/* Copyright 2001,2002,2004 Oswald Buddenhagen Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the copyright holder. */ /* * xdm - display manager daemon * Author: Keith Packard, MIT X Consortium * * printf.c - working horse of error.c */ /* * NOTE: this file is meant to be included, not linked, * so it can be used in the helper programs without much voodoo. */ /* ########## printf core implementation with some extensions ########## */ /* * How to use the extensions: * - put ' or " in the flags field to quote a string with this char and * escape special characters (only available, if PRINT_QUOTES is defined) * - put \\ in the flags field to quote special characters and leading and * trailing spaces (only available, if PRINT_QUOTES is defined) * - arrays (only available, if PRINT_ARRAYS is defined) * - the array modifier [ comes after the maximal field width specifier * - the array length can be specified literally, with the '*' modifier * (in which case an argument is expected) or will be automatically * determined (stop values are -1 for ints and 0 for strings) * - these modifiers expect their argument to be an in-line string quoted * with an arbitrary character: * - (, ) -> array pre-/suf-fix; default "" * - <, > -> element pre-/suf-fix; default "" * - | -> element separator; default " " * - these modifiers expect no argument: * - : -> print ': ' before an array * - , -> short for |',' * - { -> short for ('{')' }'<' '|'' * - the pointer to the array is the last argument to the format * - the %m conversion from syslog() is supported */ /************************************************************** * Partially stolen from OpenSSH's OpenBSD compat directory. * (C) Patrick Powell, Brandon Long, Thomas Roessler, * Michael Elkins, Ben Lindstrom **************************************************************/ #include #include #include /* format flags - Bits */ #define DP_F_MINUS (1 << 0) #define DP_F_PLUS (1 << 1) #define DP_F_SPACE (1 << 2) #define DP_F_NUM (1 << 3) #define DP_F_ZERO (1 << 4) #define DP_F_UPCASE (1 << 5) #define DP_F_UNSIGNED (1 << 6) #define DP_F_SQUOTE (1 << 7) #define DP_F_DQUOTE (1 << 8) #define DP_F_BACKSL (1 << 9) #define DP_F_ARRAY (1 << 10) #define DP_F_COLON (1 << 11) /* Conversion Flags */ #define DP_C_INT 0 #define DP_C_BYTE 1 #define DP_C_SHORT 2 #define DP_C_LONG 3 #define DP_C_STR 10 typedef void (*OutCh)( void *bp, char c ); static void fmtint( OutCh dopr_outch, void *bp, long value, int base, int min, int max, int flags ) { const char *ctab; unsigned long uvalue; int signvalue = 0; int place = 0; int spadlen = 0; /* amount to space pad */ int zpadlen = 0; /* amount to zero pad */ char convert[20]; if (max < 0) max = 0; uvalue = value; if (!(flags & DP_F_UNSIGNED)) { if (value < 0) { signvalue = '-'; uvalue = -value; } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ signvalue = '+'; else if (flags & DP_F_SPACE) signvalue = ' '; } ctab = (flags & DP_F_UPCASE) ? "0123456789ABCDEF" : "0123456789abcdef"; do { convert[place++] = ctab[uvalue % (unsigned)base]; uvalue = uvalue / (unsigned)base; } while (uvalue); zpadlen = max - place; spadlen = min - (max > place ? max : place) - (signvalue ? 1 : 0) - ((flags & DP_F_NUM) ? 2 : 0); if (zpadlen < 0) zpadlen = 0; if (spadlen < 0) spadlen = 0; if (flags & DP_F_ZERO) { zpadlen = zpadlen > spadlen ? zpadlen : spadlen; spadlen = 0; } if (flags & DP_F_MINUS) spadlen = -spadlen; /* Left Justifty */ /* Spaces */ while (spadlen > 0) { dopr_outch( bp, ' ' ); --spadlen; } /* Sign */ if (signvalue) dopr_outch( bp, signvalue ); /* Prefix */ if (flags & DP_F_NUM) { dopr_outch( bp, '0' ); dopr_outch( bp, 'x' ); } /* Zeros */ if (zpadlen > 0) while (zpadlen > 0) { dopr_outch( bp, '0' ); --zpadlen; } /* Digits */ while (place > 0) dopr_outch( bp, convert[--place] ); /* Left Justified spaces */ while (spadlen < 0) { dopr_outch( bp, ' ' ); ++spadlen; } } typedef struct { const char *str; size_t len; } str_t; static void putstr( OutCh dopr_outch, void *bp, str_t *st ) { size_t pt; for (pt = 0; pt < st->len; pt++) dopr_outch( bp, st->str[pt] ); } static str_t _null_parents = { "(null)", 6 }; #ifdef PRINT_ARRAYS static str_t _null_dparents = { "((null))", 8 }; #endif #if defined(PRINT_QUOTES) || defined(PRINT_ARRAYS) static str_t _null_caps = { "NULL", 4 }; #endif static void fmtstr( OutCh dopr_outch, void *bp, const char *value, int flags, int min, int max ) { int padlen, strln, curcol; #ifdef PRINT_QUOTES int lastcol = 0; #endif char ch; if (!value) { #ifdef PRINT_QUOTES if (flags & (DP_F_SQUOTE | DP_F_DQUOTE)) putstr( dopr_outch, bp, &_null_caps ); else #endif putstr( dopr_outch, bp, &_null_parents ); return; } for (strln = 0; (unsigned)strln < (unsigned)max && value[strln]; strln++); padlen = min - strln; if (padlen < 0) padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justify */ for (; padlen > 0; padlen--) dopr_outch( bp, ' ' ); #ifdef PRINT_QUOTES # if 0 /* gcc's flow analyzer is not the smartest ... */ lastcol = 0; # endif if (flags & DP_F_SQUOTE) dopr_outch( bp, '\'' ); else if (flags & DP_F_DQUOTE) dopr_outch( bp, '"'); else if (flags & DP_F_BACKSL) for (lastcol = strln; lastcol && value[lastcol - 1] == ' '; lastcol--); #endif for (curcol = 0; curcol < strln; curcol++) { ch = value[curcol]; #ifdef PRINT_QUOTES if (flags & (DP_F_SQUOTE | DP_F_DQUOTE | DP_F_BACKSL)) { switch (ch) { case '\r': ch = 'r'; break; case '\n': ch = 'n'; break; case '\t': ch = 't'; break; case '\a': ch = 'a'; break; case '\b': ch = 'b'; break; case '\v': ch = 'v'; break; case '\f': ch = 'f'; break; default: if (ch < 32 || ((unsigned char)ch >= 0x7f && (unsigned char)ch < 0xa0)) { dopr_outch( bp, '\\' ); fmtint( dopr_outch, bp, (unsigned char)ch, 8, 3, 3, DP_F_ZERO ); continue; } else { if ((ch == '\'' && (flags & DP_F_SQUOTE)) || (ch == '"' && (flags & DP_F_DQUOTE) ) || (ch == ' ' && (flags & DP_F_BACKSL) && (!curcol || curcol >= lastcol)) || ch == '\\') dopr_outch( bp, '\\' ); dopr_outch( bp, ch ); continue; } } dopr_outch( bp, '\\' ); } #endif dopr_outch( bp, ch ); } #ifdef PRINT_QUOTES if (flags & DP_F_SQUOTE) dopr_outch( bp, '\'' ); else if (flags & DP_F_DQUOTE) dopr_outch( bp, '"' ); #endif for (; padlen < 0; padlen++) dopr_outch( bp, ' ' ); } static void DoPr( OutCh dopr_outch, void *bp, const char *format, va_list args ) { const char *strvalue; #ifdef PRINT_ARRAYS str_t arpr, arsf, arepr, aresf, aresp, *arp; void *arptr; #endif unsigned long value; int radix = 0, min, max, flags, cflags, errn; #ifdef PRINT_ARRAYS int arlen = 0; unsigned aridx; char sch; #endif char ch; #define NCHR if (!(ch = *format++)) return #if 0 /* gcc's flow analyzer is not the smartest ... */ # ifdef PRINT_ARRAYS arlen = 0; # endif radix = 0; #endif errn = errno; for (;;) { for (;;) { NCHR; if (ch == '%') break; dopr_outch (bp, ch); } flags = cflags = min = 0; max = -1; for (;;) { NCHR; switch (ch) { case '#': flags |= DP_F_NUM; continue; case '-': flags |= DP_F_MINUS; continue; case '+': flags |= DP_F_PLUS; continue; case ' ': flags |= DP_F_SPACE; continue; case '0': flags |= DP_F_ZERO; continue; #ifdef PRINT_QUOTES case '"': flags |= DP_F_DQUOTE; continue; case '\'': flags |= DP_F_SQUOTE; continue; case '\\': flags |= DP_F_BACKSL; continue; #endif } break; } for (;;) { if (isdigit( (unsigned char)ch )) { min = 10 * min + (ch - '0'); NCHR; continue; } else if (ch == '*') { min = va_arg( args, int ); NCHR; } break; } if (ch == '.') { max = 0; for (;;) { NCHR; if (isdigit( (unsigned char)ch )) { max = 10 * max + (ch - '0'); continue; } else if (ch == '*') { max = va_arg( args, int ); NCHR; } break; } } #ifdef PRINT_ARRAYS if (ch == '[') { flags |= DP_F_ARRAY; arlen = -1; arpr.len = arsf.len = arepr.len = aresf.len = 0; aresp.len = 1, aresp.str = " "; for (;;) { NCHR; if (isdigit( (unsigned char)ch )) { arlen = 0; for (;;) { arlen += (ch - '0'); NCHR; if (!isdigit( (unsigned char)ch )) break; arlen *= 10; } } switch (ch) { case ':': flags |= DP_F_COLON; continue; case '*': arlen = va_arg( args, int ); continue; case '(': arp = &arpr; goto rar; case ')': arp = &arsf; goto rar; case '<': arp = &arepr; goto rar; case '>': arp = &aresf; goto rar; case '|': arp = &aresp; rar: NCHR; sch = ch; arp->str = format; do { NCHR; } while (ch != sch); arp->len = format - arp->str - 1; continue; case ',': aresp.len = 1, aresp.str = ","; continue; case '{': aresp.len = 0, arpr.len = arepr.len = 1, arsf.len = 2; arpr.str = "{", arepr.str = " ", arsf.str = " }"; continue; } break; } } #endif for (;;) { switch (ch) { case 'h': cflags = DP_C_SHORT; NCHR; if (ch == 'h') { cflags = DP_C_BYTE; NCHR; } continue; case 'l': cflags = DP_C_LONG; NCHR; continue; } break; } switch (ch) { case '%': dopr_outch( bp, ch ); break; case 'm': fmtstr( dopr_outch, bp, strerror( errn ), flags, min, max ); break; case 'c': dopr_outch( bp, va_arg( args, int ) ); break; case 's': #ifdef PRINT_ARRAYS cflags = DP_C_STR; goto printit; #else strvalue = va_arg( args, char * ); fmtstr( dopr_outch, bp, strvalue, flags, min, max ); break; #endif case 'u': flags |= DP_F_UNSIGNED; case 'd': case 'i': radix = 10; goto printit; case 'X': flags |= DP_F_UPCASE; case 'x': flags |= DP_F_UNSIGNED; radix = 16; printit: #ifdef PRINT_ARRAYS if (flags & DP_F_ARRAY) { if (!(arptr = va_arg( args, void * ))) putstr( dopr_outch, bp, arpr.len ? &_null_caps : &_null_dparents ); else { if (arlen == -1) { arlen = 0; switch (cflags) { case DP_C_STR: while (((char **)arptr)[arlen]) arlen++; break; case DP_C_BYTE: while (((unsigned char *)arptr)[arlen] != (unsigned char)-1) arlen++; break; case DP_C_SHORT: while (((unsigned short int *)arptr)[arlen] != (unsigned short int)-1) arlen++; break; case DP_C_LONG: while (((unsigned long int *)arptr)[arlen] != (unsigned long int)-1) arlen++; break; default: while (((unsigned int *)arptr)[arlen] != (unsigned int)-1) arlen++; break; } } if (flags & DP_F_COLON) { fmtint( dopr_outch, bp, (long)arlen, 10, 0, -1, DP_F_UNSIGNED ); dopr_outch( bp, ':' ); dopr_outch( bp, ' ' ); } putstr( dopr_outch, bp, &arpr ); for (aridx = 0; aridx < (unsigned)arlen; aridx++) { if (aridx) putstr( dopr_outch, bp, &aresp ); putstr( dopr_outch, bp, &arepr ); if (cflags == DP_C_STR) { strvalue = ((char **)arptr)[aridx]; fmtstr( dopr_outch, bp, strvalue, flags, min, max ); } else { if (flags & DP_F_UNSIGNED) { switch (cflags) { case DP_C_BYTE: value = ((unsigned char *)arptr)[aridx]; break; case DP_C_SHORT: value = ((unsigned short int *)arptr)[aridx]; break; case DP_C_LONG: value = ((unsigned long int *)arptr)[aridx]; break; default: value = ((unsigned int *)arptr)[aridx]; break; } } else { switch (cflags) { case DP_C_BYTE: value = ((signed char *)arptr)[aridx]; break; case DP_C_SHORT: value = ((short int *)arptr)[aridx]; break; case DP_C_LONG: value = ((long int *)arptr)[aridx]; break; default: value = ((int *)arptr)[aridx]; break; } } fmtint( dopr_outch, bp, value, radix, min, max, flags ); } putstr( dopr_outch, bp, &aresf ); } putstr( dopr_outch, bp, &arsf ); } } else { if (cflags == DP_C_STR) { strvalue = va_arg( args, char * ); fmtstr( dopr_outch, bp, strvalue, flags, min, max ); } else { #endif if (flags & DP_F_UNSIGNED) { switch (cflags) { case DP_C_LONG: value = va_arg( args, unsigned long int ); break; default: value = va_arg( args, unsigned int ); break; } } else { switch (cflags) { case DP_C_LONG: value = va_arg( args, long int ); break; default: value = va_arg( args, int ); break; } } fmtint( dopr_outch, bp, value, radix, min, max, flags ); #ifdef PRINT_ARRAYS } } #endif break; case 'p': value = (long)va_arg( args, void * ); fmtint( dopr_outch, bp, value, 16, sizeof(long) * 2 + 2, max, flags | DP_F_UNSIGNED | DP_F_ZERO | DP_F_NUM ); break; } } } /* ########## end of printf core implementation ########## */ /* * Logging function for xdm and helper programs. */ #ifndef NO_LOGGER #include #include #ifdef USE_SYSLOG # include # ifdef LOG_NAME # define InitLog() openlog(LOG_NAME, LOG_PID, LOG_DAEMON) # else # define InitLog() openlog(prog, LOG_PID, LOG_DAEMON) # endif static int lognums[] = { LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT }; #else # define InitLog() while(0) #endif static const char *lognams[] = { "debug", "info", "warning", "error", "panic" }; static void logTime( char *dbuf ) { time_t tim; (void)time( &tim ); strftime( dbuf, 20, "%b %e %H:%M:%S", localtime( &tim ) ); } #if defined(LOG_DEBUG_MASK) || defined(USE_SYSLOG) STATIC int debugLevel; #endif #define OOMSTR "Out of memory. Expect problems.\n" STATIC void LogOutOfMem( void ) { static time_t last; time_t tnow; time( &tnow ); if (last + 100 > tnow) { /* don't log bursts */ last = tnow; return; } last = tnow; #ifdef USE_SYSLOG if (!(debugLevel & DEBUG_NOSYSLOG)) syslog( LOG_CRIT, OOMSTR ); else #endif { int el; char dbuf[24], sbuf[128]; logTime( dbuf ); el = sprintf( sbuf, "%s " #ifdef LOG_NAME LOG_NAME "[%ld]: " OOMSTR, dbuf, #else "%s[%ld]: " OOMSTR, dbuf, prog, #endif (long)getpid() ); write( 2, sbuf, el ); } } typedef struct { char *buf; int clen, blen, type; char lmbuf[128]; } OCLBuf; static void OutChLFlush( OCLBuf *oclbp ) { if (oclbp->clen) { #ifdef USE_SYSLOG if (!(debugLevel & DEBUG_NOSYSLOG)) syslog( lognums[oclbp->type], "%.*s", oclbp->clen, oclbp->buf ); else #endif { oclbp->buf[oclbp->clen] = '\n'; write( 2, oclbp->buf, oclbp->clen + 1 ); } oclbp->clen = 0; } } static void OutChL( void *bp, char c ) { OCLBuf *oclbp = (OCLBuf *)bp; char *nbuf; int nlen; if (c == '\n') OutChLFlush( oclbp ); else { if (oclbp->clen >= oclbp->blen - 1) { if (oclbp->buf == oclbp->lmbuf) { OutChLFlush( oclbp ); oclbp->buf = 0; oclbp->blen = 0; } nlen = oclbp->blen * 3 / 2 + 128; nbuf = Realloc( oclbp->buf, nlen ); if (nbuf) { oclbp->buf = nbuf; oclbp->blen = nlen; } else { OutChLFlush( oclbp ); oclbp->buf = oclbp->lmbuf; oclbp->blen = sizeof(oclbp->lmbuf); } } #ifdef USE_SYSLOG if (!oclbp->clen && (debugLevel & DEBUG_NOSYSLOG)) { #else if (!oclbp->clen) { #endif char dbuf[24]; logTime( dbuf ); oclbp->clen = sprintf( oclbp->buf, "%s " #ifdef LOG_NAME LOG_NAME "[%ld] %s: ", dbuf, #else "%s[%ld] %s: ", dbuf, prog, #endif (long)getpid(), lognams[oclbp->type] ); } oclbp->buf[oclbp->clen++] = c; } } static void Logger( int type, const char *fmt, va_list args ) { OCLBuf oclb; oclb.buf = 0; oclb.blen = oclb.clen = 0; oclb.type = type; DoPr( OutChL, &oclb, fmt, args ); /* no flush, every message is supposed to be \n-terminated */ if (oclb.buf && oclb.buf != oclb.lmbuf) free( oclb.buf ); } #ifdef LOG_DEBUG_MASK STATIC void Debug( const char *fmt, ... ) { if (debugLevel & LOG_DEBUG_MASK) { va_list args; int olderrno = errno; va_start( args, fmt ); Logger( DM_DEBUG, fmt, args ); va_end( args ); errno = olderrno; } } #endif #ifndef LOG_NO_INFO STATIC void LogInfo( const char *fmt, ... ) { va_list args; va_start( args, fmt ); Logger( DM_INFO, fmt, args ); va_end( args ); } #endif #ifndef LOG_NO_WARN STATIC void LogWarn( const char *fmt, ... ) { va_list args; va_start( args, fmt ); Logger( DM_WARN, fmt, args ); va_end( args ); } #endif #ifndef LOG_NO_ERROR STATIC void LogError( const char *fmt, ... ) { va_list args; va_start( args, fmt ); Logger( DM_ERR, fmt, args ); va_end( args ); } #endif #ifdef LOG_PANIC_EXIT STATIC void LogPanic( const char *fmt, ... ) { va_list args; va_start( args, fmt ); Logger( DM_PANIC, fmt, args ); va_end( args ); exit( LOG_PANIC_EXIT ); } #endif #endif /* NO_LOGGER */ #ifdef NEED_FDPRINTF typedef struct { char *buf; int clen, blen, tlen; } OCFBuf; static void OutCh_OCF( void *bp, char c ) { OCFBuf *ocfbp = (OCFBuf *)bp; char *nbuf; int nlen; ocfbp->tlen++; if (ocfbp->clen >= ocfbp->blen) { if (ocfbp->blen < 0) return; nlen = ocfbp->blen * 3 / 2 + 100; nbuf = Realloc( ocfbp->buf, nlen ); if (!nbuf) { free( ocfbp->buf ); ocfbp->blen = -1; ocfbp->buf = 0; ocfbp->clen = 0; return; } ocfbp->blen = nlen; ocfbp->buf = nbuf; } ocfbp->buf[ocfbp->clen++] = c; } STATIC int FdPrintf( int fd, const char *fmt, ... ) { va_list args; OCFBuf ocfb = { 0, 0, 0, -1 }; va_start( args, fmt ); DoPr( OutCh_OCF, &ocfb, fmt, args ); va_end( args ); if (ocfb.buf) { Debug( "FdPrintf %\".*s to %d\n", ocfb.clen, ocfb.buf, fd ); (void)write( fd, ocfb.buf, ocfb.clen ); free( ocfb.buf ); } return ocfb.tlen; } #endif /* NEED_FDPRINTF */ #ifdef NEED_ASPRINTF typedef struct { char *buf; int clen, blen, tlen; } OCABuf; static void OutCh_OCA( void *bp, char c ) { OCABuf *ocabp = (OCABuf *)bp; char *nbuf; int nlen; ocabp->tlen++; if (ocabp->clen >= ocabp->blen) { if (ocabp->blen < 0) return; nlen = ocabp->blen * 3 / 2 + 100; nbuf = Realloc( ocabp->buf, nlen ); if (!nbuf) { free( ocabp->buf ); ocabp->blen = -1; ocabp->buf = 0; ocabp->clen = 0; return; } ocabp->blen = nlen; ocabp->buf = nbuf; } ocabp->buf[ocabp->clen++] = c; } STATIC int VASPrintf( char **strp, const char *fmt, va_list args ) { OCABuf ocab = { 0, 0, 0, -1 }; DoPr( OutCh_OCA, &ocab, fmt, args ); OutCh_OCA( &ocab, 0 ); *strp = Realloc( ocab.buf, ocab.clen ); if (!*strp) *strp = ocab.buf; return ocab.tlen; } STATIC int ASPrintf( char **strp, const char *fmt, ... ) { va_list args; int len; va_start( args, fmt ); len = VASPrintf( strp, fmt, args ); va_end( args ); return len; } #endif /* NEED_ASPRINTF */