diff --git a/include/ntux/ntux.h b/include/ntux/ntux.h index 7e9ca52..12cba1e 100644 --- a/include/ntux/ntux.h +++ b/include/ntux/ntux.h @@ -32,6 +32,23 @@ extern "C" { #define NTUX_DRIVER_VERSION 0x0010 #define NTUX_DRIVER_DRY_RUN 0x0020 +#define NTUX_DRIVER_ANNOTATE_ALWAYS 0x10000000 +#define NTUX_DRIVER_ANNOTATE_NEVER 0x20000000 +#define NTUX_DRIVER_ANNOTATE_FULL 0x40000000 + +/* error flags */ +#define NTUX_ERROR_TOP_LEVEL 0x0001 +#define NTUX_ERROR_NESTED 0x0002 +#define NTUX_ERROR_CHILD 0x0004 +#define NTUX_ERROR_CUSTOM 0x0008 +#define NTUX_ERROR_NATIVE 0x0010 + +enum ntux_custom_error { + NTUX_ERR_FLOW_ERROR, + NTUX_ERR_FLEE_ERROR, + NTUX_ERR_LDSO_INIT, +}; + enum ntux_cmd { NTUX_CMD_DEFAULT, NTUX_CMD_STAT, @@ -72,6 +89,11 @@ struct ntux_driver_ctx { void * any; }; +struct ntux_unit_ctx { + const char * const * path; + void * any; +}; + /* package info */ ntux_api const struct ntux_source_version * ntux_source_version(void); @@ -82,6 +104,7 @@ ntux_api void ntux_free_driver_ctx (struct ntux_driver_ctx *); /* utility api */ ntux_api int ntux_main (int, char **, char **); +ntux_api int ntux_output_error_vector (const struct ntux_driver_ctx *); #ifdef __cplusplus } diff --git a/project/common.mk b/project/common.mk index c1491b3..1be73a6 100644 --- a/project/common.mk +++ b/project/common.mk @@ -5,9 +5,14 @@ DRIVER_SRCS = \ INTERNAL_SRCS = \ src/internal/nolibc/ntux_compiler.c \ + src/internal/ntux_errinfo_impl.c \ src/internal/ntux_memfn_impl.c \ src/internal/ntux_nolibc_impl.c \ src/internal/ntux_ntaio_impl.c \ + src/internal/ntux_strerr_impl.c \ + +OUTPUT_SRCS = \ + src/output/ntux_output_error.c \ APP_SRCS = \ src/ntux.c @@ -15,3 +20,4 @@ APP_SRCS = \ COMMON_SRCS = \ $(DRIVER_SRCS) \ $(INTERNAL_SRCS) \ + $(OUTPUT_SRCS) \ diff --git a/project/headers.mk b/project/headers.mk index c47901c..05d9d25 100644 --- a/project/headers.mk +++ b/project/headers.mk @@ -5,8 +5,10 @@ API_HEADERS = \ INTERNAL_HEADERS = \ $(SOURCE_DIR)/src/internal/argv/argv.h \ $(SOURCE_DIR)/src/internal/$(PACKAGE)_driver_impl.h \ + $(SOURCE_DIR)/src/internal/$(PACKAGE)_errinfo_impl.h \ $(SOURCE_DIR)/src/internal/$(PACKAGE)_init_impl.h \ $(SOURCE_DIR)/src/internal/$(PACKAGE)_memfn_impl.h \ $(SOURCE_DIR)/src/internal/$(PACKAGE)_nolibc_impl.h \ + $(SOURCE_DIR)/src/internal/$(PACKAGE)_strerr_impl.h \ ALL_HEADERS = $(API_HEADERS) $(INTERNAL_HEADERS) diff --git a/project/tree.mk b/project/tree.mk index 2a78989..9f0d99e 100644 --- a/project/tree.mk +++ b/project/tree.mk @@ -5,5 +5,6 @@ tree.tag: mkdir -p src/internal mkdir -p src/internal/nolibc mkdir -p src/logic + mkdir -p src/output mkdir -p src/skin touch tree.tag diff --git a/src/driver/ntux_amain.c b/src/driver/ntux_amain.c index 6b9acf4..11741c9 100644 --- a/src/driver/ntux_amain.c +++ b/src/driver/ntux_amain.c @@ -57,6 +57,7 @@ static void ntux_perform_unit_actions( static int ntux_exit(struct ntux_driver_ctx * dctx, int ret) { + ntux_output_error_vector(dctx); ntux_free_driver_ctx(dctx); return ret; } diff --git a/src/internal/ntux_driver_impl.h b/src/internal/ntux_driver_impl.h index 6c635de..38d10cc 100644 --- a/src/internal/ntux_driver_impl.h +++ b/src/internal/ntux_driver_impl.h @@ -26,11 +26,39 @@ struct ntux_driver_ctx_impl { struct ntux_common_ctx cctx; struct ntux_driver_ctx ctx; struct __psx_context xctx; + const struct ntux_unit_ctx *euctx; const char * eunit; struct ntux_error_info ** errinfp; struct ntux_error_info ** erricap; struct ntux_error_info * erriptr[64]; struct ntux_error_info erribuf[64]; + char errsbuf[28]; + int errcode; }; + +static inline struct ntux_driver_ctx_impl * ntux_get_driver_ictx(const struct ntux_driver_ctx * dctx) +{ + uintptr_t addr; + + if (dctx) { + addr = (uintptr_t)dctx - offsetof(struct ntux_driver_ctx_impl,ctx); + return (struct ntux_driver_ctx_impl *)addr; + } + + return 0; +} + +static inline void ntux_driver_set_ectx( + const struct ntux_driver_ctx * dctx, + const struct ntux_unit_ctx * uctx, + const char * unit) +{ + struct ntux_driver_ctx_impl * ictx; + + ictx = ntux_get_driver_ictx(dctx); + ictx->euctx = uctx; + ictx->eunit = unit; +} + #endif diff --git a/src/internal/ntux_errinfo_impl.c b/src/internal/ntux_errinfo_impl.c new file mode 100644 index 0000000..2c21663 --- /dev/null +++ b/src/internal/ntux_errinfo_impl.c @@ -0,0 +1,66 @@ +/***********************************************************/ +/* ntux: native translation und extension */ +/* Copyright (C) 2016--2018 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTUX. */ +/***********************************************************/ + +#include +#include "ntux_driver_impl.h" +#include "ntux_errinfo_impl.h" + +int ntux_errno(const struct ntux_driver_ctx * dctx) +{ + struct ntux_driver_ctx_impl * ictx; + ictx = ntux_get_driver_ictx(dctx); + return ictx->errcode; +} + +int ntux_errno_set( + const struct ntux_driver_ctx * dctx, + int esyscode) +{ + struct ntux_driver_ctx_impl * ictx; + + ictx = ntux_get_driver_ictx(dctx); + ictx->errcode = (esyscode < 0) + ? -esyscode + : esyscode; + + return esyscode; +} + +int ntux_record_error( + const struct ntux_driver_ctx * dctx, + int esyscode, + int elibcode, + const char * efunction, + int eline, + unsigned eflags, + void * eany) +{ + struct ntux_driver_ctx_impl * ictx; + struct ntux_error_info * erri; + + ictx = ntux_get_driver_ictx(dctx); + + if (ictx->errinfp == ictx->erricap) + return -1; + + *ictx->errinfp = &ictx->erribuf[ictx->errinfp - ictx->erriptr]; + erri = *ictx->errinfp; + + erri->euctx = ictx->euctx; + erri->eunit = ictx->eunit; + + erri->edctx = dctx; + erri->esyscode = esyscode; + erri->elibcode = elibcode; + erri->efunction = efunction; + erri->eline = eline; + erri->eflags = eflags; + erri->eany = eany; + + ictx->errinfp++; + + return -1; +} diff --git a/src/internal/ntux_errinfo_impl.h b/src/internal/ntux_errinfo_impl.h new file mode 100644 index 0000000..1d3b527 --- /dev/null +++ b/src/internal/ntux_errinfo_impl.h @@ -0,0 +1,96 @@ +/***********************************************************/ +/* ntux: native translation und extension */ +/* Copyright (C) 2016--2018 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTUX. */ +/***********************************************************/ + +#include + +int ntux_errno(const struct ntux_driver_ctx *); + +int ntux_errno_set( + const struct ntux_driver_ctx *, + int esyscode); + +int ntux_record_error( + const struct ntux_driver_ctx *, + int esyscode, + int elibcode, + const char * efunction, + int eline, + unsigned eflags, + void * eany); + +#define NTUX_SYSTEM_ERROR(dctx) \ + ntux_record_error( \ + dctx, \ + ntux_errno(dctx), \ + 0, \ + __func__, \ + __LINE__, \ + NTUX_ERROR_TOP_LEVEL, \ + 0) + +#define NTUX_BUFFER_ERROR(dctx) \ + ntux_record_error( \ + dctx, \ + ENOBUFS, \ + 0, \ + __func__, \ + __LINE__, \ + NTUX_ERROR_TOP_LEVEL, \ + 0) + +#define NTUX_SPAWN_ERROR(dctx) \ + ntux_record_error( \ + dctx, \ + ntux_errno(dctx), \ + 0, \ + __func__, \ + __LINE__, \ + NTUX_ERROR_TOP_LEVEL \ + | (ntux_errno(dctx) ? 0 \ + : NTUX_ERROR_CHILD), \ + 0) + +#define NTUX_FILE_ERROR(dctx) \ + ntux_record_error( \ + dctx, \ + EIO, \ + 0, \ + __func__, \ + __LINE__, \ + NTUX_ERROR_TOP_LEVEL, \ + 0) + +#define NTUX_CUSTOM_ERROR(dctx,elibcode) \ + ntux_record_error( \ + dctx, \ + 0, \ + elibcode, \ + __func__, \ + __LINE__, \ + NTUX_ERROR_TOP_LEVEL \ + | NTUX_ERROR_CUSTOM, \ + 0) + +#define NTUX_NATIVE_ERROR(dctx,elibcode) \ + ntux_record_error( \ + dctx, \ + 0, \ + elibcode, \ + __func__, \ + __LINE__, \ + NTUX_ERROR_TOP_LEVEL \ + | NTUX_ERROR_NATIVE, \ + 0) + +#define NTUX_NESTED_ERROR(dctx) \ + ntux_record_error( \ + dctx, \ + 0, \ + 0, \ + __func__, \ + __LINE__, \ + NTUX_ERROR_NESTED, \ + 0) diff --git a/src/internal/ntux_nolibc_impl.h b/src/internal/ntux_nolibc_impl.h index d048bd4..a12b848 100644 --- a/src/internal/ntux_nolibc_impl.h +++ b/src/internal/ntux_nolibc_impl.h @@ -5,6 +5,7 @@ #define fileno ntux_fileno #define fputs ntux_fputs +#define fflush ntux_fflush #define fprintf ntux_fprintf #define sprintf ntux_sprintf #define snprintf ntux_snprintf @@ -35,6 +36,7 @@ int ntux_sprintf(char * str, const char * fmt, ...); int ntux_snprintf(char * str, size_t n, const char * fmt, ...); int ntux_fprintf(FILE *__restrict, const char *__restrict, ...); int ntux_fputs(const char * str, FILE * file); +int ntux_fflush(FILE *); void * ntux_memcpy(void * dst, const void * src, size_t n); void * memset(void * ch, int c, size_t n); diff --git a/src/internal/ntux_ntaio_impl.c b/src/internal/ntux_ntaio_impl.c index 377a833..850a7d7 100644 --- a/src/internal/ntux_ntaio_impl.c +++ b/src/internal/ntux_ntaio_impl.c @@ -112,3 +112,8 @@ int ntux_fileno(void * any) { return (int)(intptr_t)any; } + +int ntux_fflush(FILE * file) +{ + return __sys_fsync((int)(intptr_t)file); +} diff --git a/src/internal/ntux_strerr_impl.c b/src/internal/ntux_strerr_impl.c new file mode 100644 index 0000000..6cb4853 --- /dev/null +++ b/src/internal/ntux_strerr_impl.c @@ -0,0 +1,165 @@ +/***********************************************************/ +/* ntux: native translation und extension */ +/* Copyright (C) 2016--2018 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTUX. */ +/***********************************************************/ + +#include + +#include +#include "ntux_driver_impl.h" +#include "ntux_nolibc_impl.h" + +const char * ntux_posix_error_strs[] = { + [0] = "EOK", + [EPERM] = "EPERM", + [ENOENT] = "ENOENT", + [ESRCH] = "ESRCH", + [EINTR] = "EINTR", + [EIO] = "EIO", + [ENXIO] = "ENXIO", + [E2BIG] = "E2BIG", + [ENOEXEC] = "ENOEXEC", + [EBADF] = "EBADF", + [ECHILD] = "ECHILD", + [EAGAIN] = "EAGAIN", + [ENOMEM] = "ENOMEM", + [EACCES] = "EACCES", + [EFAULT] = "EFAULT", + [ENOTBLK] = "ENOTBLK", + [EBUSY] = "EBUSY", + [EEXIST] = "EEXIST", + [EXDEV] = "EXDEV", + [ENODEV] = "ENODEV", + [ENOTDIR] = "ENOTDIR", + [EISDIR] = "EISDIR", + [EINVAL] = "EINVAL", + [ENFILE] = "ENFILE", + [EMFILE] = "EMFILE", + [ENOTTY] = "ENOTTY", + [ETXTBSY] = "ETXTBSY", + [EFBIG] = "EFBIG", + [ENOSPC] = "ENOSPC", + [ESPIPE] = "ESPIPE", + [EROFS] = "EROFS", + [EMLINK] = "EMLINK", + [EPIPE] = "EPIPE", + [EDOM] = "EDOM", + [ERANGE] = "ERANGE", + [EDEADLK] = "EDEADLK", + [ENAMETOOLONG] = "ENAMETOOLONG", + [ENOLCK] = "ENOLCK", + [ENOSYS] = "ENOSYS", + [ENOTEMPTY] = "ENOTEMPTY", + [ELOOP] = "ELOOP", + [ENOMSG] = "ENOMSG", + [EIDRM] = "EIDRM", + [ECHRNG] = "ECHRNG", + [EL2NSYNC] = "EL2NSYNC", + [EL3HLT] = "EL3HLT", + [EL3RST] = "EL3RST", + [ELNRNG] = "ELNRNG", + [EUNATCH] = "EUNATCH", + [ENOCSI] = "ENOCSI", + [EL2HLT] = "EL2HLT", + [EBADE] = "EBADE", + [EBADR] = "EBADR", + [EXFULL] = "EXFULL", + [ENOANO] = "ENOANO", + [EBADRQC] = "EBADRQC", + [EBADSLT] = "EBADSLT", + [EBFONT] = "EBFONT", + [ENOSTR] = "ENOSTR", + [ENODATA] = "ENODATA", + [ETIME] = "ETIME", + [ENOSR] = "ENOSR", + [ENONET] = "ENONET", + [ENOPKG] = "ENOPKG", + [EREMOTE] = "EREMOTE", + [ENOLINK] = "ENOLINK", + [EADV] = "EADV", + [ESRMNT] = "ESRMNT", + [ECOMM] = "ECOMM", + [EPROTO] = "EPROTO", + [EMULTIHOP] = "EMULTIHOP", + [EDOTDOT] = "EDOTDOT", + [EBADMSG] = "EBADMSG", + [EOVERFLOW] = "EOVERFLOW", + [ENOTUNIQ] = "ENOTUNIQ", + [EBADFD] = "EBADFD", + [EREMCHG] = "EREMCHG", + [ELIBACC] = "ELIBACC", + [ELIBBAD] = "ELIBBAD", + [ELIBSCN] = "ELIBSCN", + [ELIBMAX] = "ELIBMAX", + [ELIBEXEC] = "ELIBEXEC", + [EILSEQ] = "EILSEQ", + [ERESTART] = "ERESTART", + [ESTRPIPE] = "ESTRPIPE", + [EUSERS] = "EUSERS", + [ENOTSOCK] = "ENOTSOCK", + [EDESTADDRREQ] = "EDESTADDRREQ", + [EMSGSIZE] = "EMSGSIZE", + [EPROTOTYPE] = "EPROTOTYPE", + [ENOPROTOOPT] = "ENOPROTOOPT", + [EPROTONOSUPPORT] = "EPROTONOSUPPORT", + [ESOCKTNOSUPPORT] = "ESOCKTNOSUPPORT", + [EOPNOTSUPP] = "EOPNOTSUPP", + [EPFNOSUPPORT] = "EPFNOSUPPORT", + [EAFNOSUPPORT] = "EAFNOSUPPORT", + [EADDRINUSE] = "EADDRINUSE", + [EADDRNOTAVAIL] = "EADDRNOTAVAIL", + [ENETDOWN] = "ENETDOWN", + [ENETUNREACH] = "ENETUNREACH", + [ENETRESET] = "ENETRESET", + [ECONNABORTED] = "ECONNABORTED", + [ECONNRESET] = "ECONNRESET", + [ENOBUFS] = "ENOBUFS", + [EISCONN] = "EISCONN", + [ENOTCONN] = "ENOTCONN", + [ESHUTDOWN] = "ESHUTDOWN", + [ETOOMANYREFS] = "ETOOMANYREFS", + [ETIMEDOUT] = "ETIMEDOUT", + [ECONNREFUSED] = "ECONNREFUSED", + [EHOSTDOWN] = "EHOSTDOWN", + [EHOSTUNREACH] = "EHOSTUNREACH", + [EALREADY] = "EALREADY", + [EINPROGRESS] = "EINPROGRESS", + [ESTALE] = "ESTALE", + [EUCLEAN] = "EUCLEAN", + [ENOTNAM] = "ENOTNAM", + [ENAVAIL] = "ENAVAIL", + [EISNAM] = "EISNAM", + [EREMOTEIO] = "EREMOTEIO", + [EDQUOT] = "EDQUOT", + [ENOMEDIUM] = "ENOMEDIUM", + [EMEDIUMTYPE] = "EMEDIUMTYPE", + [ECANCELED] = "ECANCELED", + [ENOKEY] = "ENOKEY", + [EKEYEXPIRED] = "EKEYEXPIRED", + [EKEYREVOKED] = "EKEYREVOKED", + [EKEYREJECTED] = "EKEYREJECTED", + [EOWNERDEAD] = "EOWNERDEAD", + [ENOTRECOVERABLE] = "ENOTRECOVERABLE", + [ERFKILL] = "ERFKILL", + [EHWPOISON] = "EHWPOISON", + [EERRORS] = 0}; + +const char * ntux_strerror( + const struct ntux_driver_ctx * dctx, + int errno) +{ + struct ntux_driver_ctx_impl * ictx; + + if ((errno >= 0) && (errno < EERRORS)) + return ntux_posix_error_strs[errno]; + + ictx = ntux_get_driver_ictx(dctx); + + ntux_sprintf( + ictx->errsbuf, + "Unknown error %d", + errno); + + return ictx->errsbuf; +} diff --git a/src/internal/ntux_strerr_impl.h b/src/internal/ntux_strerr_impl.h new file mode 100644 index 0000000..3f317a7 --- /dev/null +++ b/src/internal/ntux_strerr_impl.h @@ -0,0 +1,6 @@ +#ifndef NTUX_STRERR_IMPL_H +#define NTUX_STRERR_IMPL_H + +const char * ntux_strerror(const struct ntux_driver_ctx *, int); + +#endif diff --git a/src/output/ntux_output_error.c b/src/output/ntux_output_error.c new file mode 100644 index 0000000..47858ab --- /dev/null +++ b/src/output/ntux_output_error.c @@ -0,0 +1,217 @@ +/***********************************************************/ +/* ntux: native translation und extension */ +/* Copyright (C) 2016--2018 Z. Gilboa */ +/* Released under GPLv2 and GPLv3; see COPYING.NTUX. */ +/***********************************************************/ + +#include +#include +#include +#include + +#include +#include "ntux_driver_impl.h" +#include "ntux_nolibc_impl.h" +#include "ntux_strerr_impl.h" + +static const char aclr_reset[] = "\x1b[0m"; +static const char aclr_bold[] = "\x1b[1m"; + +static const char aclr_red[] = "\x1b[31m"; +static const char aclr_green[] = "\x1b[32m"; +static const char aclr_blue[] = "\x1b[34m"; +static const char aclr_magenta[] = "\x1b[35m"; + +static const char * ntux_output_error_header(const struct ntux_error_info * erri) +{ + if (erri->eflags & NTUX_ERROR_CHILD) + return "exec error upon"; + + else if (erri->eflags & NTUX_ERROR_TOP_LEVEL) + return "error logged in"; + + else if (erri->eflags & NTUX_ERROR_NESTED) + return "< returned to >"; + + else + return "distorted state"; +} + +static const char * ntux_output_unit_header(const struct ntux_error_info * erri) +{ + if (!(erri->eflags & NTUX_ERROR_CUSTOM)) + return "while opening"; + + else + return "while querying"; +} + +static const char * ntux_output_strerror( + const struct ntux_driver_ctx * dctx, + const struct ntux_error_info * erri) +{ + struct ntux_driver_ctx_impl * ictx; + + if (erri->eflags & NTUX_ERROR_NATIVE) { + ictx = ntux_get_driver_ictx(dctx); + ntux_sprintf(ictx->errsbuf,"system error: 0x%X",erri->elibcode); + return ictx->errsbuf; + } + + if (erri->eflags & NTUX_ERROR_CUSTOM) + return "flow error: unexpected condition or other"; + + else if (erri->eflags & NTUX_ERROR_NESTED) + return ""; + + else if (erri->eflags & NTUX_ERROR_CHILD) + return "(see child process error messages)"; + + else if (erri->esyscode == ENOBUFS) + return "input error: string length exceeds buffer size."; + + else + return ntux_strerror(dctx,erri->esyscode); +} + +static int ntux_output_error_record_plain( + const struct ntux_driver_ctx * dctx, + const struct ntux_error_info * erri) +{ + const char * epath; + const char * errdesc = ntux_output_strerror(dctx,erri); + + epath = erri->euctx + ? *erri->euctx->path + : erri->eunit; + + if (epath && !(erri->eflags & NTUX_ERROR_NESTED)) + if (ntux_fprintf(stderr,"%s: [%s] '%s':\n", + dctx->program, + ntux_output_unit_header(erri), + epath) < 0) + return -1; + + if (fprintf(stderr,"%s: %s %s(), line %d%s%s.\n", + dctx->program, + ntux_output_error_header(erri), + erri->efunction, + erri->eline, + strlen(errdesc) ? ": " : "", + errdesc) < 0) + return -1; + + return fflush(stderr); +} + +static int ntux_output_error_record_annotated( + const struct ntux_driver_ctx * dctx, + const struct ntux_error_info * erri) +{ + const char * epath; + const char * errdesc = ntux_output_strerror(dctx,erri); + + epath = erri->euctx + ? *erri->euctx->path + : erri->eunit; + + if (epath && !(erri->eflags & NTUX_ERROR_NESTED)) + if (ntux_fprintf( + stderr, + "%s%s%s:%s %s[%s]%s %s%s'%s'%s:\n", + + aclr_bold,aclr_magenta, + dctx->program, + aclr_reset, + + aclr_bold, + ntux_output_unit_header(erri), + aclr_reset, + + aclr_bold,aclr_red, + epath, + aclr_reset) < 0) + return -1; + + if (fprintf( + stderr, + "%s%s%s:%s %s%s%s %s%s%s()%s, %s%sline %d%s%s%s%s%s.\n", + + aclr_bold,aclr_magenta, + dctx->program, + aclr_reset, + + aclr_bold, + ntux_output_error_header(erri), + aclr_reset, + + aclr_bold,aclr_blue, + erri->efunction, + aclr_reset, + + aclr_bold,aclr_green, + erri->eline, + aclr_reset, + strlen(errdesc) ? ": " : "", + + aclr_bold, + ntux_output_strerror(dctx,erri), + aclr_reset) < 0) + return -1; + + return fflush(stderr); +} + +int ntux_output_error_record( + const struct ntux_driver_ctx * dctx, + const struct ntux_error_info * erri) +{ + if (dctx->cctx->drvflags & NTUX_DRIVER_ANNOTATE_NEVER) + return ntux_output_error_record_plain(dctx,erri); + + else if (dctx->cctx->drvflags & NTUX_DRIVER_ANNOTATE_ALWAYS) + return ntux_output_error_record_annotated(dctx,erri); + + else if (isatty(STDERR_FILENO)) + return ntux_output_error_record_annotated(dctx,erri); + + else + return ntux_output_error_record_plain(dctx,erri); +} + +static int ntux_output_error_vector_plain(const struct ntux_driver_ctx * dctx) +{ + struct ntux_error_info ** perr; + + for (perr=dctx->errv; *perr; perr++) + if (ntux_output_error_record_plain(dctx,*perr)) + return -1; + + return 0; +} + +static int ntux_output_error_vector_annotated(const struct ntux_driver_ctx * dctx) +{ + struct ntux_error_info ** perr; + + for (perr=dctx->errv; *perr; perr++) + if (ntux_output_error_record_annotated(dctx,*perr)) + return -1; + + return 0; +} + +int ntux_output_error_vector(const struct ntux_driver_ctx * dctx) +{ + if (dctx->cctx->drvflags & NTUX_DRIVER_ANNOTATE_NEVER) + return ntux_output_error_vector_plain(dctx); + + else if (dctx->cctx->drvflags & NTUX_DRIVER_ANNOTATE_ALWAYS) + return ntux_output_error_vector_annotated(dctx); + + else if (isatty(STDERR_FILENO)) + return ntux_output_error_vector_annotated(dctx); + + else + return ntux_output_error_vector_plain(dctx); +}