1*466Smark /* char printf_id[] = "@(#) printf.c:2.2 6/5/79";*/ 2*466Smark #include "varargs.h" 3*466Smark /* 4*466Smark * This version of printf is compatible with the Version 7 C 5*466Smark * printf. The differences are only minor except that this 6*466Smark * printf assumes it is to print through putchar. Version 7 7*466Smark * printf is more general (and is much larger) and includes 8*466Smark * provisions for floating point. 9*466Smark */ 10*466Smark 11*466Smark 12*466Smark #define MAXOCT 11 /* Maximum octal digits in a long */ 13*466Smark #define MAXINT 32767 /* largest normal length positive integer */ 14*466Smark #define BIG 1000000000 /* largest power of 10 less than an unsigned long */ 15*466Smark #define MAXDIGS 10 /* number of digits in BIG */ 16*466Smark 17*466Smark static int width, sign, fill; 18*466Smark 19*466Smark char *_p_dconv(); 20*466Smark 21*466Smark printf(va_alist) 22*466Smark va_dcl 23*466Smark { 24*466Smark va_list ap; 25*466Smark register char *fmt; 26*466Smark char fcode; 27*466Smark int prec; 28*466Smark int length,mask1,nbits,n; 29*466Smark long int mask2, num; 30*466Smark register char *bptr; 31*466Smark char *ptr; 32*466Smark char buf[134]; 33*466Smark 34*466Smark va_start(ap); 35*466Smark fmt = va_arg(ap,char *); 36*466Smark for (;;) { 37*466Smark /* process format string first */ 38*466Smark while ((fcode = *fmt++)!='%') { 39*466Smark /* ordinary (non-%) character */ 40*466Smark if (fcode=='\0') 41*466Smark return; 42*466Smark putchar(fcode); 43*466Smark } 44*466Smark /* length modifier: -1 for h, 1 for l, 0 for none */ 45*466Smark length = 0; 46*466Smark /* check for a leading - sign */ 47*466Smark sign = 0; 48*466Smark if (*fmt == '-') { 49*466Smark sign++; 50*466Smark fmt++; 51*466Smark } 52*466Smark /* a '0' may follow the - sign */ 53*466Smark /* this is the requested fill character */ 54*466Smark fill = 1; 55*466Smark if (*fmt == '0') { 56*466Smark fill--; 57*466Smark fmt++; 58*466Smark } 59*466Smark 60*466Smark /* Now comes a digit string which may be a '*' */ 61*466Smark if (*fmt == '*') { 62*466Smark width = va_arg(ap, int); 63*466Smark if (width < 0) { 64*466Smark width = -width; 65*466Smark sign = !sign; 66*466Smark } 67*466Smark fmt++; 68*466Smark } 69*466Smark else { 70*466Smark width = 0; 71*466Smark while (*fmt>='0' && *fmt<='9') 72*466Smark width = width * 10 + (*fmt++ - '0'); 73*466Smark } 74*466Smark 75*466Smark /* maybe a decimal point followed by more digits (or '*') */ 76*466Smark if (*fmt=='.') { 77*466Smark if (*++fmt == '*') { 78*466Smark prec = va_arg(ap, int); 79*466Smark fmt++; 80*466Smark } 81*466Smark else { 82*466Smark prec = 0; 83*466Smark while (*fmt>='0' && *fmt<='9') 84*466Smark prec = prec * 10 + (*fmt++ - '0'); 85*466Smark } 86*466Smark } 87*466Smark else 88*466Smark prec = -1; 89*466Smark 90*466Smark /* 91*466Smark * At this point, "sign" is nonzero if there was 92*466Smark * a sign, "fill" is 0 if there was a leading 93*466Smark * zero and 1 otherwise, "width" and "prec" 94*466Smark * contain numbers corresponding to the digit 95*466Smark * strings before and after the decimal point, 96*466Smark * respectively, and "fmt" addresses the next 97*466Smark * character after the whole mess. If there was 98*466Smark * no decimal point, "prec" will be -1. 99*466Smark */ 100*466Smark switch (*fmt) { 101*466Smark case 'L': 102*466Smark case 'l': 103*466Smark length = 2; 104*466Smark /* no break!! */ 105*466Smark case 'h': 106*466Smark case 'H': 107*466Smark length--; 108*466Smark fmt++; 109*466Smark break; 110*466Smark } 111*466Smark 112*466Smark /* 113*466Smark * At exit from the following switch, we will 114*466Smark * emit the characters starting at "bptr" and 115*466Smark * ending at "ptr"-1, unless fcode is '\0'. 116*466Smark */ 117*466Smark switch (fcode = *fmt++) { 118*466Smark /* process characters and strings first */ 119*466Smark case 'c': 120*466Smark buf[0] = va_arg(ap, int); 121*466Smark ptr = bptr = &buf[0]; 122*466Smark if (buf[0] != '\0') 123*466Smark ptr++; 124*466Smark break; 125*466Smark case 's': 126*466Smark bptr = va_arg(ap,char *); 127*466Smark if (bptr==0) 128*466Smark bptr = "(null pointer)"; 129*466Smark if (prec < 0) 130*466Smark prec = MAXINT; 131*466Smark for (n=0; *bptr++ && n < prec; n++) ; 132*466Smark ptr = --bptr; 133*466Smark bptr -= n; 134*466Smark break; 135*466Smark case 'O': 136*466Smark length = 1; 137*466Smark fcode = 'o'; 138*466Smark /* no break */ 139*466Smark case 'o': 140*466Smark case 'X': 141*466Smark case 'x': 142*466Smark if (length > 0) 143*466Smark num = va_arg(ap,long); 144*466Smark else 145*466Smark num = (unsigned)va_arg(ap,int); 146*466Smark if (fcode=='o') { 147*466Smark mask1 = 0x7; 148*466Smark mask2 = 0x1fffffffL; 149*466Smark nbits = 3; 150*466Smark } 151*466Smark else { 152*466Smark mask1 = 0xf; 153*466Smark mask2 = 0x0fffffffL; 154*466Smark nbits = 4; 155*466Smark } 156*466Smark n = (num!=0); 157*466Smark bptr = buf + MAXOCT + 3; 158*466Smark /* shift and mask for speed */ 159*466Smark do 160*466Smark if (((int) num & mask1) < 10) 161*466Smark *--bptr = ((int) num & mask1) + 060; 162*466Smark else 163*466Smark *--bptr = ((int) num & mask1) + 0127; 164*466Smark while (num = (num >> nbits) & mask2); 165*466Smark 166*466Smark if (fcode=='o') { 167*466Smark if (n) 168*466Smark *--bptr = '0'; 169*466Smark } 170*466Smark else 171*466Smark if (!sign && fill <= 0) { 172*466Smark putchar('0'); 173*466Smark putchar(fcode); 174*466Smark width -= 2; 175*466Smark } 176*466Smark else { 177*466Smark *--bptr = fcode; 178*466Smark *--bptr = '0'; 179*466Smark } 180*466Smark ptr = buf + MAXOCT + 3; 181*466Smark break; 182*466Smark case 'D': 183*466Smark case 'U': 184*466Smark case 'I': 185*466Smark length = 1; 186*466Smark fcode = fcode + 'a' - 'A'; 187*466Smark /* no break */ 188*466Smark case 'd': 189*466Smark case 'i': 190*466Smark case 'u': 191*466Smark if (length > 0) 192*466Smark num = va_arg(ap,long); 193*466Smark else { 194*466Smark n = va_arg(ap,int); 195*466Smark if (fcode=='u') 196*466Smark num = (unsigned) n; 197*466Smark else 198*466Smark num = (long) n; 199*466Smark } 200*466Smark if (n = (fcode != 'u' && num < 0)) 201*466Smark num = -num; 202*466Smark /* now convert to digits */ 203*466Smark bptr = _p_dconv(num, buf); 204*466Smark if (n) 205*466Smark *--bptr = '-'; 206*466Smark if (fill == 0) 207*466Smark fill = -1; 208*466Smark ptr = buf + MAXDIGS + 1; 209*466Smark break; 210*466Smark default: 211*466Smark /* not a control character, 212*466Smark * print it. 213*466Smark */ 214*466Smark ptr = bptr = &fcode; 215*466Smark ptr++; 216*466Smark break; 217*466Smark } 218*466Smark if (fcode != '\0') 219*466Smark _p_emit(bptr,ptr); 220*466Smark } 221*466Smark va_end(ap); 222*466Smark } 223*466Smark 224*466Smark /* _p_dconv converts the unsigned long integer "value" to 225*466Smark * printable decimal and places it in "buffer", right-justified. 226*466Smark * The value returned is the address of the first non-zero character, 227*466Smark * or the address of the last character if all are zero. 228*466Smark * The result is NOT null terminated, and is MAXDIGS characters long, 229*466Smark * starting at buffer[1] (to allow for insertion of a sign). 230*466Smark * 231*466Smark * This program assumes it is running on 2's complement machine 232*466Smark * with reasonable overflow treatment. 233*466Smark */ 234*466Smark char * 235*466Smark _p_dconv(value, buffer) 236*466Smark long value; 237*466Smark char *buffer; 238*466Smark { 239*466Smark register char *bp; 240*466Smark register int svalue; 241*466Smark int n; 242*466Smark long lval; 243*466Smark 244*466Smark bp = buffer; 245*466Smark 246*466Smark /* zero is a special case */ 247*466Smark if (value == 0) { 248*466Smark bp += MAXDIGS; 249*466Smark *bp = '0'; 250*466Smark return(bp); 251*466Smark } 252*466Smark 253*466Smark /* develop the leading digit of the value in "n" */ 254*466Smark n = 0; 255*466Smark while (value < 0) { 256*466Smark value -= BIG; /* will eventually underflow */ 257*466Smark n++; 258*466Smark } 259*466Smark while ((lval = value - BIG) >= 0) { 260*466Smark value = lval; 261*466Smark n++; 262*466Smark } 263*466Smark 264*466Smark /* stash it in buffer[1] to allow for a sign */ 265*466Smark bp[1] = n + '0'; 266*466Smark /* 267*466Smark * Now develop the rest of the digits. Since speed counts here, 268*466Smark * we do it in two loops. The first gets "value" down until it 269*466Smark * is no larger than MAXINT. The second one uses integer divides 270*466Smark * rather than long divides to speed it up. 271*466Smark */ 272*466Smark bp += MAXDIGS + 1; 273*466Smark while (value > MAXINT) { 274*466Smark *--bp = (int)(value % 10) + '0'; 275*466Smark value /= 10; 276*466Smark } 277*466Smark 278*466Smark /* cannot lose precision */ 279*466Smark svalue = value; 280*466Smark while (svalue > 0) { 281*466Smark *--bp = (svalue % 10) + '0'; 282*466Smark svalue /= 10; 283*466Smark } 284*466Smark 285*466Smark /* fill in intermediate zeroes if needed */ 286*466Smark if (buffer[1] != '0') { 287*466Smark while (bp > buffer + 2) 288*466Smark *--bp = '0'; 289*466Smark --bp; 290*466Smark } 291*466Smark return(bp); 292*466Smark } 293*466Smark 294*466Smark /* 295*466Smark * This program sends string "s" to putchar. The character after 296*466Smark * the end of "s" is given by "send". This allows the size of the 297*466Smark * field to be computed; it is stored in "alen". "width" contains the 298*466Smark * user specified length. If width<alen, the width will be taken to 299*466Smark * be alen. "sign" is zero if the string is to be right-justified 300*466Smark * in the field, nonzero if it is to be left-justified. "fill" is 301*466Smark * 0 if the string is to be padded with '0', positive if it is to be 302*466Smark * padded with ' ', and negative if an initial '-' should appear before 303*466Smark * any padding in right-justification (to avoid printing "-3" as 304*466Smark * "000-3" where "-0003" was intended). 305*466Smark */ 306*466Smark _p_emit(s, send) 307*466Smark register char *s; 308*466Smark char *send; 309*466Smark { 310*466Smark char cfill; 311*466Smark register int alen; 312*466Smark int npad; 313*466Smark 314*466Smark alen = send - s; 315*466Smark if (alen > width) 316*466Smark width = alen; 317*466Smark cfill = fill>0? ' ': '0'; 318*466Smark 319*466Smark /* we may want to print a leading '-' before anything */ 320*466Smark if (*s == '-' && fill < 0) { 321*466Smark putchar(*s++); 322*466Smark alen--; 323*466Smark width--; 324*466Smark } 325*466Smark npad = width - alen; 326*466Smark 327*466Smark /* emit any leading pad characters */ 328*466Smark if (!sign) 329*466Smark while (--npad >= 0) 330*466Smark putchar(cfill); 331*466Smark 332*466Smark /* emit the string itself */ 333*466Smark while (--alen >= 0) 334*466Smark putchar(*s++); 335*466Smark 336*466Smark /* emit trailing pad characters */ 337*466Smark if (sign) 338*466Smark while (--npad >= 0) 339*466Smark putchar(cfill); 340*466Smark } 341