// // aegis - project change supervisor // Copyright (C) 1991-1995, 1998, 1999, 2002-2006, 2008, 2012 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // NAME // wrap - wrap s string over lines // // SYNOPSIS // void wrap(char *); // // DESCRIPTION // The wrap function is used to print error messages onto stderr // wrapping ling lines. // // CAVEATS // Line length is assumed to be 80 characters. // static void wrap(const char *s) { const char *progname; static char escapes[] = "\rr\nn\ff\bb\tt"; int page_width; int first_line; if (fflush(stdout) || ferror(stdout)) nfatal("(stdout)"); // don't use last column, many terminals are dumb page_width = 79; progname = progname_get(); first_line = 1; while (*s) { const char *ep; int ocol; // // Work out how many characters fit on the line. // if (first_line) ocol = strlen(progname) + 2; else ocol = 8; for (ep = s; *ep; ++ep) { int cw; unsigned char c; c = *ep; if (isprint((unsigned char)c)) cw = 1 + (c == '\\'); else cw = (strchr(escapes, c) ? 2 : 4); if (ocol + cw > page_width) break; ocol += cw; } // // see if there is a better place to break the line // if (*ep && *ep != ' ') { const char *mp; const char *bp_space; const char *bp_slash; bp_space = 0; for (mp = ep; mp > s; --mp) { if (mp[-1] == ' ') { bp_space = mp; break; } } bp_slash = 0; for (mp = ep; mp > s; --mp) { if (strchr("\\/", mp[-1])) { bp_slash = mp; break; } } // // We could break it at the space, and only use // the slash if there are no spaces on the line. // This can lead to large amounts of wasted // space, particularly for link commands. So, if // both breaks are possible, and the space break // is before the slash break, and the space // break is in the left half of the line, use // the slash break. // if ( bp_space && bp_slash && bp_space < bp_slash && bp_space < s + 30 ) bp_space = 0; // // use the break if available // if (bp_space) ep = bp_space; else if (bp_slash) ep = bp_slash; } // // ignore trailing blanks // while (ep > s && ep[-1] == ' ') ep--; // // print the line // char tmp[200]; char *tp = tmp; if (first_line) { tp = strendcpy(tp, progname, tmp + sizeof(tmp)); tp = strendcpy(tp, ": ", tmp + sizeof(tmp)); } else tp = strendcpy(tp, "\t", tmp + sizeof(tmp)); tp = tmp + strlen(tmp); while (s < ep) { unsigned char c = *s++; if (isprint((unsigned char)c)) { if (c == '\\') *tp++ = '\\'; *tp++ = c; } else { const char *esc; esc = strchr(escapes, c); if (esc) { *tp++ = '\\'; *tp++ = esc[1]; } else { snprintf(tp, tmp + sizeof(tmp) - tp, "\\%3.3o", c); tp += strlen(tp); } } } *tp++ = '\n'; *tp = 0; fputs(tmp, stderr); if (ferror(stderr)) break; // // skip leading spaces for subsequent lines // while (*s == ' ') s++; first_line = 0; } if (fflush(stderr) || ferror(stderr)) { static int disaster_count; // // If there is a problem with stderr, it is usually // because the tee command went away. Try to cope, so // that we can finish cleaning up, but don't try too hard. // if (disaster_count++ || !freopen("/dev/null", "w", stderr)) exit(1); quit(1); } } static void double_jeopardy(void) { int err = errno; if (err == ENOMEM) resource_usage_print(); char buffer[200]; snprintf ( buffer, sizeof(buffer), "while attempting to construct an error message: %s (fatal)", strerror(err) ); wrap(buffer); quit(1); } static char * copy_string(const char *s) { errno = 0; size_t nbytes = strlen(s) + 1; char *cp = (char *)malloc(nbytes); if (!cp) { if (!errno) errno = ENOMEM; double_jeopardy(); } memcpy(cp, s, nbytes); return cp; } // // NAME // error - place a message on the error stream // // SYNOPSIS // void error(char *fmt, ...); // // DESCRIPTION // Error places a message on the error output stream. // The first argument is a printf-like format string, // optionally followed by other arguments. // The message will be prefixed by the program name and a colon, // and will be terminated with a newline, automatically. // // CAVEAT // Things like "error(filename)" blow up if the filename // contains a '%' character. // void error_raw(const char *fmt, ...) { va_list ap; char *buffer; va_start(ap, fmt); buffer = vmprintf_errok(fmt, ap); va_end(ap); if (!buffer) double_jeopardy(); wrap(buffer); } // // NAME // nerror - place a system fault message on the error stream // // SYNOPSIS // void nerror(char *fmt, ...); // // DESCRIPTION // Nerror places a message on the error output stream. // The first argument is a printf-like format string, // optionally followed by other arguments. // The message will be prefixed by the program name and a colon, // and will be terminated with a text description of the error // indicated by the 'errno' global variable, automatically. // // CAVEAT // Things like "nerror(filename)" blow up if the filename // contains a '%' character. // void nerror(const char *fmt, ...) { char *s1; va_list ap; int n; n = errno; va_start(ap, fmt); s1 = vmprintf_errok(fmt, ap); va_end(ap); if (!s1) double_jeopardy(); s1 = copy_string(s1); if (n == ENOMEM) resource_usage_print(); error_raw("%s: %s", s1, strerror(n)); free(s1); } // // NAME // nfatal - place a system fault message on the error stream and exit // // SYNOPSIS // void nfatal(char *fmt, ...); // // DESCRIPTION // Nfatal places a message on the error output stream and exits. // The first argument is a printf-like format string, // optionally followed by other arguments. // The message will be prefixed by the program name and a colon, // and will be terminated with a text description of the error // indicated by the 'errno' global variable, automatically. // // CAVEAT // Things like "nfatal(filename)" blow up if the filename // contains a '%' character. // // This function does NOT return. // void nfatal(const char *fmt, ...) { char *s1; va_list ap; int n; n = errno; va_start(ap, fmt); s1 = vmprintf_errok(fmt, ap); va_end(ap); if (!s1) double_jeopardy(); s1 = copy_string(s1); if (n == ENOMEM) resource_usage_print(); fatal_raw("%s: %s", s1, strerror(n)); } // // NAME // fatal - place a message on the error stream and exit // // SYNOPSIS // void fatal(char *fmt, ...); // // DESCRIPTION // Fatal places a message on the error output stream and exits. // The first argument is a printf-like format string, // optionally followed by other arguments. // The message will be prefixed by the program name and a colon, // and will be terminated with a newline, automatically. // // CAVEAT // Things like "error(filename)" blow up if the filename // contains a '%' character. // // This function does NOT return. // void fatal_raw(const char *fmt, ...) { va_list ap; char *buffer; va_start(ap, fmt); buffer = vmprintf_errok(fmt, ap); va_end(ap); if (!buffer) double_jeopardy(); wrap(buffer); quit(1); } // vim: set ts=8 sw=4 et :