/* #define TESTING */ /* * 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. Internal A test routine may be conditionally compiled. #endif #include #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 #define NEG 2 /* Signal for output of '-' */ #ifdef M68000 ROM_SECT(rom00); #endif /* * 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, argp, fildes) char *format; /* Format string */ register int *argp; /* Argument vector pointer */ 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 */ #ifdef decus unsigned value; /* Current argument value */ #else unsigned long value; #endif int temp; /* Set if '-' needed. */ short radix; /* Conversion radix */ while ((c = *format++) != EOS) { /* * Check for conversion specifications. */ if (c == '%') { /* * Check for various options. */ c = *format++; ljust = 0; /* No left adjustment */ 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 = *argp++; c = *format++; } else { /* %n field width */ width = 0; while (isdigit(c)) { width *= 10; width += (c - '0'); c = *format++; } } if (c == '.') { /* %n.n precision */ c = *format++; /* %n.* (from arg) */ if (c == '?' || c == '*') { prec = *argp++; c = *format++; } else { prec = 0; while (isdigit(c)) { prec *= 10; prec += (c - '0'); c = *format++; } } } else { prec = 32767; /* No prec. specified */ } /* * 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. */ radix = p.fmt->f_radix; temp = p.fmt->f_width; p.result = &work[WORKSIZE]; *--p.result = EOS; /* Terminate result */ if (temp == INT) { if (c == 'd' && *argp < 0) { value = -*argp++; temp = NEG; /* Remember signal */ } else value = (unsigned) *argp++; } else { value = *((long *) argp)++; } #ifdef TESTING fprintf(stderr, "radix %d, value = %d (signed), %u (unsigned), 0x%x\n", radix, value, value, value); #endif if (value == 0) *--p.result = '0'; else { /* * Convert an unsigned non-zero number. */ do { c = value % radix; *--p.result = c + ((c < 10) ? '0' : ('a' - 10)); } while ((value /= radix) != 0); if (temp == NEG) *--p.result = '-'; } #ifdef TESTING fprintf(stderr, "result \"%s\"\n", p.result); #endif } 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 = *argp++; 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 */ argp = (int *) *argp; format = (char *) *argp++; continue; /* No print here */ case 's': /* String */ if ((p.result = (char *) *argp++) == NULL) p.result = "{NULL}"; /* Bug hack */ break; case 'c': /* Character */ c = *argp++; /* * 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 */ #ifdef TESTING fprintf(stderr, "width %d, field length %d, precision %d\n", width, slen, prec); #endif 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); } } if ((fildes->_flag & _IOSTRG) != 0) putc(EOS, fildes); return; } #ifdef TESTING char format[133]; char inputline[133]; int v[3]; main() { extern char *prompt(); for (;;) { if (prompt("Format") == NULL) break; strcpy(format, inputline); v[0] = rdint("v1", 1, 0, 0); v[1] = rdint("v2", 1, 0, 0); v[2] = rdint("v3", 1, 0, 0); printf("\""); c_doprnt(format, v, stdout); printf("\"\n"); } } char * prompt(info) char *info; /* * Prompt, read text from console to input line. * Return inputline or NULL at eof. */ { register char *result; extern char *gets(); clearerr(stdin); if (info != NULL) fprintf(stderr, "%s: ", info); fflush(stderr); inputline[0] = EOS; result = gets(inputline); return (result); } int rdint(info, low, high, def_value) char *info; /* Prompt text */ int low; /* Minimum acceptable value */ int high; /* Maximum acceptable value */ int def_value; /* Default value */ /* * Prompt and read an integer. Note: * info must be present * low > high any value acceptable */ { register int value; register char *lp; for (;;) { if (low <= high) { fprintf(stderr, "%s <%d..%d> [%d]? ", info, low, high, def_value); } else { fprintf(stderr, "%s [%d]? ", info, def_value); } if (prompt(NULL) == NULL || inputline[0] == EOS) sprintf(inputline, "%d", def_value); value = atoi(inputline); if (low > high || (value >= low && value <= high)) break; fprintf(stderr, "Illegal value %d, range is %d..%d, please try again.\n", value, low, high); } return (value); } #endif