/* * d o p r n t . c * */ /*)LIBRARY */ #ifdef DOCUMENTATION title Output Formatter index Output formatter internal synopsis .s.nf c_doprnt(format, argvec, iov) char *format; int *argvec[]; FILE *iov; .s.f Description c_doprnt() performs all formatting operations for printf(), sprintf(), and fprintf(). The formatting options are described in the documentation of printf(). Bugs c_doprnt() is functionally identical to the _doprnt() module in Unix and Vax-11 C libraries. Unfortunately, the initial '_' conflicts with RSX FCS library conventions. #endif #include #define EOS ('\0') #define FALSE 0 #define TRUE 1 #ifdef PDP10 #define BITS_PER_CHAR 36 #else #define BITS_PER_CHAR 8 #endif /* * WORKSIZE is the dimension of work[] which must hold enough characters * to store a long integer (expanded using the %b format) + '-' and EOS */ #define WORKSIZE (((sizeof (long)) * BITS_PER_CHAR) + 2) struct fmtbuf { char f_type; /* Format type ('d', etc.) */ char f_width; /* Argument width (INT or LONG) */ char f_radix; /* Argument radix (8, 10, etc.) */ }; #define INT 0 #define LONG 1 /* * formatinfo[] stores information about the "value" formats */ static struct fmtbuf formatinfo[] = { { 'b', INT, 2 }, { 'd', INT, 10 }, { 'o', INT, 8 }, { 'u', INT, 10 }, { 'x', INT, 16 }, { 'B', LONG, 2 }, { 'D', LONG, 10 }, { 'O', LONG, 8 }, { 'U', LONG, 10 }, { 'X', LONG, 16 }, { EOS, 0, 0 }, }; c_doprnt(format, arg, fildes) char *format; /* Format string */ register union { char **s; int *i; unsigned int *u; long *l; char *c; } arg; FILE *fildes; /* File descriptor */ { register int c; register union { struct fmtbuf *fmt; char *result; } p; char ljust; /* TRUE for left-justification */ char work[WORKSIZE]; /* Number buffer */ char fill; /* '0' or ' ' for fill */ int prec; /* Precision */ int slen; /* Field length */ int width; /* Argument width */ long value; /* Current argument value */ int temp; /* Set if '-' needed. */ int radix; /* Conversion radix */ while ((c = *format++) != EOS) { /* * Check for conversion specifications. */ if (c == '%') { /* * Check for various options. */ c = *format++; ljust = 0; /* No left adjustment */ prec = 0; /* Print all the arg. */ fill = ' '; /* Fill with spaces */ if (c == '-') { /* %- left justify */ ljust++; c = *format++; } if (c == '0') { /* %0d zero fill arg. */ fill = c; c = *format++; } if (c == '?' || c == '*') { /* %* width from arg */ width = *arg.i++; c = *format++; } else { /* %n field width */ width = 0; while (isdigit(c)) { width *= 10; width += (c - '0'); c = *format++; } } prec = 32767; if (c == '.') { /* %n.n precision */ c = *format++; /* %n.* (from arg) */ if (c == '?' || c == '*') { prec = *arg.i++; c = *format++; } else { while (isdigit(c)) { prec *= 10; prec += (c - '0'); c = *format++; } } } /* * Process conversion chars, understanding longs. */ if (c == 'l' || c == 'L') { switch (c = *format++) { case 'b': case 'd': case 'o': case 'u': case 'x': c = toupper(c); /* Make %lb %B */ case 'B': case 'D': case 'O': case 'U': case 'X': break; default: /* Here on %Lfoo == %Ufoo */ c = 'U'; format--; break; } } /* * Search the numeric format structure for this conversion. */ for (p.fmt = formatinfo; (p.fmt->f_type != c && p.fmt->f_type != EOS); p.fmt++) ; if (p.fmt->f_type != EOS) { /* * A numeric conversion was found. * Get the value and expand it into the work area. */ if (p.fmt->f_width == INT) { if (c == 'd') value = *arg.i++; else value = *arg.u++; } else { value = *arg.l++; } radix = p.fmt->f_radix; p.result = &work[WORKSIZE]; *--p.result = EOS; /* Terminate result */ if (value == 0) *--p.result = '0'; else { /* * Convert a signed non-zero number. * temp will signal whether a '-' is needed. */ if ((temp = (value < 0 && c == 'd' || c == 'D'))) value = (-value); /* * The sign (if needed) is stored in temp. */ while (value > 0) { c = value % radix; *--p.result = c + ((c < 10) ? '0' : ('a' - 10)); value /= radix; } if (temp) *--p.result = '-'; } } else { /* * String or something */ switch (c) { case 'q': /* Funny int value */ /* * Convert a word as a pair of octal bytes. */ c = 16384; p.result = work; temp = *arg.i++; while (c > 0) { *p.result++ = (temp / c) + '0'; temp %= c; if (c == 256) { c = 64; *p.result++ = '.'; } else c >>= 3; } *p.result = EOS; p.result = work; break; case 'r': /* Remote format */ format = *arg.s++; continue; /* No print here */ case 's': /* String */ if ((p.result = *arg.s++) == NULL) p.result = "{NULL}"; /* Bug hack */ break; case 'c': /* Character */ c = *arg.c++; /* * Fall through */ default: /* %% or whatever */ p.result = work; *p.result = c; work[1] = EOS; break; } } /* * p.result -> first byte of string to output. * Reuse c as a register temp. */ c = strlen(p.result); /* True result length */ slen = (c > prec) ? prec : c; /* Field length */ if (!ljust) { /* Right justify? */ while (width-- > slen) { putc(fill, fildes); } } /* * Output the string (up to "prec" bytes) */ for (c = prec; *p.result != EOS && --c >= 0;) { putc(*p.result++, fildes); } if (ljust) { /* Left justify? */ while (width-- > slen) { putc(' ', fildes); } } } else { /* * Not in a % thing, just output the byte. */ putc(c, fildes); } } return; }