/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #if !defined(lint) && defined(DOSCCS) static char *sccsid = "@(#)printf.c 7.3 (Berkeley) 6/7/85"; /* The pwb version this is based on */ static char *printf_id = "@(#) printf.c:2.2 6/5/79"; #endif #include "varargs.h" /* * This version of printf is compatible with the Version 7 C * printf. The differences are only minor except that this * printf assumes it is to print through putchar. Version 7 * printf is more general (and is much larger) and includes * provisions for floating point. */ #define MAXOCT 11 /* Maximum octal digits in a long */ #define MAXINT 32767 /* largest normal length positive integer */ #define BIG 1000000000 /* largest power of 10 less than an unsigned long */ #define MAXDIGS 10 /* number of digits in BIG */ static int width, sign, fill; char *_p_dconv(); printf(va_alist) va_dcl { va_list ap; register char *fmt; char fcode; int prec; int length,mask1,nbits,n; long int mask2, num; register char *bptr; char *ptr; char buf[134]; va_start(ap); fmt = va_arg(ap,char *); for (;;) { /* process format string first */ while ((fcode = *fmt++)!='%') { /* ordinary (non-%) character */ if (fcode=='\0') return; putchar(fcode); } /* length modifier: -1 for h, 1 for l, 0 for none */ length = 0; /* check for a leading - sign */ sign = 0; if (*fmt == '-') { sign++; fmt++; } /* a '0' may follow the - sign */ /* this is the requested fill character */ fill = 1; if (*fmt == '0') { fill--; fmt++; } /* Now comes a digit string which may be a '*' */ if (*fmt == '*') { width = va_arg(ap, int); if (width < 0) { width = -width; sign = !sign; } fmt++; } else { width = 0; while (*fmt>='0' && *fmt<='9') width = width * 10 + (*fmt++ - '0'); } /* maybe a decimal point followed by more digits (or '*') */ if (*fmt=='.') { if (*++fmt == '*') { prec = va_arg(ap, int); fmt++; } else { prec = 0; while (*fmt>='0' && *fmt<='9') prec = prec * 10 + (*fmt++ - '0'); } } else prec = -1; /* * At this point, "sign" is nonzero if there was * a sign, "fill" is 0 if there was a leading * zero and 1 otherwise, "width" and "prec" * contain numbers corresponding to the digit * strings before and after the decimal point, * respectively, and "fmt" addresses the next * character after the whole mess. If there was * no decimal point, "prec" will be -1. */ switch (*fmt) { case 'L': case 'l': length = 2; /* no break!! */ case 'h': case 'H': length--; fmt++; break; } /* * At exit from the following switch, we will * emit the characters starting at "bptr" and * ending at "ptr"-1, unless fcode is '\0'. */ switch (fcode = *fmt++) { /* process characters and strings first */ case 'c': buf[0] = va_arg(ap, int); ptr = bptr = &buf[0]; if (buf[0] != '\0') ptr++; break; case 's': bptr = va_arg(ap,char *); if (bptr==0) bptr = "(null pointer)"; if (prec < 0) prec = MAXINT; for (n=0; *bptr++ && n < prec; n++) ; ptr = --bptr; bptr -= n; break; case 'O': length = 1; fcode = 'o'; /* no break */ case 'o': case 'X': case 'x': if (length > 0) num = va_arg(ap,long); else num = (unsigned)va_arg(ap,int); if (fcode=='o') { mask1 = 0x7; mask2 = 0x1fffffffL; nbits = 3; } else { mask1 = 0xf; mask2 = 0x0fffffffL; nbits = 4; } n = (num!=0); bptr = buf + MAXOCT + 3; /* shift and mask for speed */ do if (((int) num & mask1) < 10) *--bptr = ((int) num & mask1) + 060; else *--bptr = ((int) num & mask1) + 0127; while (num = (num >> nbits) & mask2); if (fcode=='o') { if (n) *--bptr = '0'; } else if (!sign && fill <= 0) { putchar('0'); putchar(fcode); width -= 2; } else { *--bptr = fcode; *--bptr = '0'; } ptr = buf + MAXOCT + 3; break; case 'D': case 'U': case 'I': length = 1; fcode = fcode + 'a' - 'A'; /* no break */ case 'd': case 'i': case 'u': if (length > 0) num = va_arg(ap,long); else { n = va_arg(ap,int); if (fcode=='u') num = (unsigned) n; else num = (long) n; } if (n = (fcode != 'u' && num < 0)) num = -num; /* now convert to digits */ bptr = _p_dconv(num, buf); if (n) *--bptr = '-'; if (fill == 0) fill = -1; ptr = buf + MAXDIGS + 1; break; default: /* not a control character, * print it. */ ptr = bptr = &fcode; ptr++; break; } if (fcode != '\0') _p_emit(bptr,ptr); } va_end(ap); } /* _p_dconv converts the unsigned long integer "value" to * printable decimal and places it in "buffer", right-justified. * The value returned is the address of the first non-zero character, * or the address of the last character if all are zero. * The result is NOT null terminated, and is MAXDIGS characters long, * starting at buffer[1] (to allow for insertion of a sign). * * This program assumes it is running on 2's complement machine * with reasonable overflow treatment. */ char * _p_dconv(value, buffer) long value; char *buffer; { register char *bp; register int svalue; int n; long lval; bp = buffer; /* zero is a special case */ if (value == 0) { bp += MAXDIGS; *bp = '0'; return(bp); } /* develop the leading digit of the value in "n" */ n = 0; while (value < 0) { value -= BIG; /* will eventually underflow */ n++; } while ((lval = value - BIG) >= 0) { value = lval; n++; } /* stash it in buffer[1] to allow for a sign */ bp[1] = n + '0'; /* * Now develop the rest of the digits. Since speed counts here, * we do it in two loops. The first gets "value" down until it * is no larger than MAXINT. The second one uses integer divides * rather than long divides to speed it up. */ bp += MAXDIGS + 1; while (value > MAXINT) { *--bp = (int)(value % 10) + '0'; value /= 10; } /* cannot lose precision */ svalue = value; while (svalue > 0) { *--bp = (svalue % 10) + '0'; svalue /= 10; } /* fill in intermediate zeroes if needed */ if (buffer[1] != '0') { while (bp > buffer + 2) *--bp = '0'; --bp; } return(bp); } /* * This program sends string "s" to putchar. The character after * the end of "s" is given by "send". This allows the size of the * field to be computed; it is stored in "alen". "width" contains the * user specified length. If width width) width = alen; cfill = fill>0? ' ': '0'; /* we may want to print a leading '-' before anything */ if (*s == '-' && fill < 0) { putchar(*s++); alen--; width--; } npad = width - alen; /* emit any leading pad characters */ if (!sign) while (--npad >= 0) putchar(cfill); /* emit the string itself */ while (--alen >= 0) putchar(*s++); /* emit trailing pad characters */ if (sign) while (--npad >= 0) putchar(cfill); }