rmtodd@uokmax.UUCP (06/30/87)
Here is a revised version of the doprintf.c file. It contains one bug fix (a problem with printing the long number 0x80000000 in decimal format), and several enhancements. This version supports the %ld, %lu, %lx, and %lo formats for long integers in addition to the already-existing %D,%X,%O formats. The %ld, %lu, %lx, %lo formats are the way almost every other compiler's library uses to specify the printing of a long integer. This means the next time you port a program to MINIX you won't have to go find every last occurence of printf("%ld",...) and change it to printf("%D", ...) . This version also supports variable field sizes in the same way that most other printf()s do (at least, how the BSD4.2 and Aztec printf() functions do it): printf("%*d", size, value); prints the argument "value" in a field whose size is determined by the integer "size". The precision value of the format can also be variable--i.e. you can have printf("%*.*s", width, precision, string) . This is also helpful in porting some programs to MINIX. (I would have posted context diffs between the original version and mine, but the diff turned out to be twice as long as the file :-(). -------------------------------------------------------------------------- Richard Todd USSnail:820 Annie Court,Norman OK 73069 UUCP: {allegra!cbosgd|ihnp4}!okstate!uokmax!rmtodd --------------------- cut here ----------------------------------------- #include "../include/stdio.h" #define MAXDIGITS 12 #define PRIVATE static /* ** _doprintf(fp, format, args) -- function called by fprintf, printf, sprintf ** Revised 06/21/87 by rmtodd@uokmax to add %ld, %lx, %lu, %lo formats ** Revised 06/28/87 by rmtodd@uokmax to add variable-length formats ** (e.g. printf("%*s",width,string); */ /* This is the same as varargs , on BSD systems */ #define GET_ARG(arglist,mode) ((mode *)(arglist += sizeof(mode)))[-1] _doprintf(fp, format, args) FILE *fp; register char *format; int args; { register char *vl; int long_flag; int unsign_flag; int r, w1, w2, sign; long l; char c; char *s; char padchar; char a[MAXDIGITS]; vl = (char *) args; while ( *format != '\0') { if ( *format != '%' ) { putc( *format++, fp ); continue; } w1 = 0; w2 = 0; sign = 1; long_flag = unsign_flag = 0; padchar = ' '; format++; /* handle variable-size formats */ if ( *format == '*') { w1 = (int) GET_ARG(vl, int); format++; } else { if ( *format == '-' ) { sign = -1; format++; } if ( *format == '0') { padchar = '0'; format++; } while ( *format >='0' && *format <='9' ){ w1 = 10 * w1 + sign * ( *format - '0' ); format++; } } if ( *format == '.'){ format++; if ( *format == '*') { w2 = (int) GET_ARG(vl, int); format++; } else { while ( *format >='0' && *format <='9' ){ w2 = 10 * w2 + ( *format - '0' ); format++; } } } if (*format == 'l') { long_flag = 1; ++format; } switch (*format) { case 'd': if (long_flag) { l = (long) GET_ARG(vl, long); } else { l = (long) GET_ARG(vl, int); } r = 10; break; case 'u': if (long_flag) { l = (long) GET_ARG(vl, long); } else { l = (long) GET_ARG(vl, int); l = l & 0xFFFF; } unsign_flag = 1; r = 10; break; case 'o': if (long_flag) { l = (long) GET_ARG(vl, long); } else { l = (long) GET_ARG(vl, int); if (l < 0) l = l & 0xFFFF; } unsign_flag = 1; r = 8; break; case 'x': if (long_flag) { l = (long) GET_ARG(vl, long); } else { l = (long) GET_ARG(vl, int); if (l < 0) l = l & 0xFFFF; } unsign_flag = 1; r = 16; break; case 'D': l = (long) GET_ARG(vl, long); r = 10; break; case 'O': l = (long) GET_ARG(vl, long); r = 8; break; case 'X': l = (long) GET_ARG(vl, long); r = 16; break; case 'c': c = (char) GET_ARG(vl, int); /* char's are casted back to int's */ putc( c, fp); format++; continue; case 's': s = GET_ARG(vl, char *); _printit(s,w1,w2,padchar,strlen(s),fp); format++; continue; default: putc(*format++,fp); continue; } _bintoascii (l, r, a, unsign_flag); _printit(a,w1,w2,padchar,strlen(a),fp); format++; } } PRIVATE _bintoascii (num, radix, a, unsg) unsigned long num; int radix; char *a; int unsg; /* 1 if unsigned number output */ { char b[MAXDIGITS]; int hit, negative; register int n, i; long temp; negative = 0; if (num == 0) { a[0] = '0'; a[1] = '\0'; return; } if (radix == 10 && !unsg) { temp = num; if (temp < 0) {num = -temp; negative++;} } for (n = 0; n < MAXDIGITS; n++) b[n] = 0; n = 0; do { if (radix == 10) { b[n] = num % 10; num = (num - b[n]) / 10; } if (radix == 8) { b[n] = num & 0x7; num = (num >> 3) & 0x1FFFFFFF; } if (radix == 16) { b[n] = num & 0xF; num = (num >> 4) & 0x0FFFFFFF; } n++; } while (num != 0); /* Convert to ASCII. */ hit = 0; for (i = n - 1; i >= 0; i--) { if (b[i] == 0 && hit == 0) { b[i] = ' '; } else { if (b[i] < 10) b[i] += '0'; else b[i] += 'A' - 10; hit++; } } if (negative) b[n++] = '-'; for(i = n - 1 ; i >= 0 ; i-- ) *a++ = b[i]; *a = '\0'; } PRIVATE _printit(str, w1, w2, padchar, length, file) char *str; int w1, w2; char padchar; int length; FILE *file; { int len2 = length; int temp; if ( w2 > 0 && length > w2) len2 = w2; temp = len2; if ( w1 > 0 ) while ( w1 > len2 ){ --w1; putc(padchar,file); } while ( *str && ( len2-- != 0 )) putc(*str++, file); if ( w1 < 0 ) if ( padchar == '0' ){ putc('.',file); w1++; } while ( w1 < -temp ){ w1++; putc(padchar,file); } }