1*21708Sdist /* 2*21708Sdist * Copyright (c) 1980 Regents of the University of California. 3*21708Sdist * All rights reserved. The Berkeley software License Agreement 4*21708Sdist * specifies the terms and conditions for redistribution. 5*21708Sdist */ 6*21708Sdist 7*21708Sdist #ifndef lint 8*21708Sdist static char sccsid[] = "@(#)printf.c 7.2 (Berkeley) 05/31/85"; 9*21708Sdist #endif not lint 10*21708Sdist 11558Smark /* The pwb version this is based on */ 12563Smark static char *printf_id = "@(#) printf.c:2.2 6/5/79"; 13466Smark #include "varargs.h" 14466Smark /* 15466Smark * This version of printf is compatible with the Version 7 C 16466Smark * printf. The differences are only minor except that this 17466Smark * printf assumes it is to print through putchar. Version 7 18466Smark * printf is more general (and is much larger) and includes 19466Smark * provisions for floating point. 20466Smark */ 21466Smark 22466Smark 23466Smark #define MAXOCT 11 /* Maximum octal digits in a long */ 24466Smark #define MAXINT 32767 /* largest normal length positive integer */ 25466Smark #define BIG 1000000000 /* largest power of 10 less than an unsigned long */ 26466Smark #define MAXDIGS 10 /* number of digits in BIG */ 27466Smark 28466Smark static int width, sign, fill; 29466Smark 30466Smark char *_p_dconv(); 31466Smark 32466Smark printf(va_alist) 33466Smark va_dcl 34466Smark { 35466Smark va_list ap; 36466Smark register char *fmt; 37466Smark char fcode; 38466Smark int prec; 39466Smark int length,mask1,nbits,n; 40466Smark long int mask2, num; 41466Smark register char *bptr; 42466Smark char *ptr; 43466Smark char buf[134]; 44466Smark 45466Smark va_start(ap); 46466Smark fmt = va_arg(ap,char *); 47466Smark for (;;) { 48466Smark /* process format string first */ 49466Smark while ((fcode = *fmt++)!='%') { 50466Smark /* ordinary (non-%) character */ 51466Smark if (fcode=='\0') 52466Smark return; 53466Smark putchar(fcode); 54466Smark } 55466Smark /* length modifier: -1 for h, 1 for l, 0 for none */ 56466Smark length = 0; 57466Smark /* check for a leading - sign */ 58466Smark sign = 0; 59466Smark if (*fmt == '-') { 60466Smark sign++; 61466Smark fmt++; 62466Smark } 63466Smark /* a '0' may follow the - sign */ 64466Smark /* this is the requested fill character */ 65466Smark fill = 1; 66466Smark if (*fmt == '0') { 67466Smark fill--; 68466Smark fmt++; 69466Smark } 70466Smark 71466Smark /* Now comes a digit string which may be a '*' */ 72466Smark if (*fmt == '*') { 73466Smark width = va_arg(ap, int); 74466Smark if (width < 0) { 75466Smark width = -width; 76466Smark sign = !sign; 77466Smark } 78466Smark fmt++; 79466Smark } 80466Smark else { 81466Smark width = 0; 82466Smark while (*fmt>='0' && *fmt<='9') 83466Smark width = width * 10 + (*fmt++ - '0'); 84466Smark } 85466Smark 86466Smark /* maybe a decimal point followed by more digits (or '*') */ 87466Smark if (*fmt=='.') { 88466Smark if (*++fmt == '*') { 89466Smark prec = va_arg(ap, int); 90466Smark fmt++; 91466Smark } 92466Smark else { 93466Smark prec = 0; 94466Smark while (*fmt>='0' && *fmt<='9') 95466Smark prec = prec * 10 + (*fmt++ - '0'); 96466Smark } 97466Smark } 98466Smark else 99466Smark prec = -1; 100466Smark 101466Smark /* 102466Smark * At this point, "sign" is nonzero if there was 103466Smark * a sign, "fill" is 0 if there was a leading 104466Smark * zero and 1 otherwise, "width" and "prec" 105466Smark * contain numbers corresponding to the digit 106466Smark * strings before and after the decimal point, 107466Smark * respectively, and "fmt" addresses the next 108466Smark * character after the whole mess. If there was 109466Smark * no decimal point, "prec" will be -1. 110466Smark */ 111466Smark switch (*fmt) { 112466Smark case 'L': 113466Smark case 'l': 114466Smark length = 2; 115466Smark /* no break!! */ 116466Smark case 'h': 117466Smark case 'H': 118466Smark length--; 119466Smark fmt++; 120466Smark break; 121466Smark } 122466Smark 123466Smark /* 124466Smark * At exit from the following switch, we will 125466Smark * emit the characters starting at "bptr" and 126466Smark * ending at "ptr"-1, unless fcode is '\0'. 127466Smark */ 128466Smark switch (fcode = *fmt++) { 129466Smark /* process characters and strings first */ 130466Smark case 'c': 131466Smark buf[0] = va_arg(ap, int); 132466Smark ptr = bptr = &buf[0]; 133466Smark if (buf[0] != '\0') 134466Smark ptr++; 135466Smark break; 136466Smark case 's': 137466Smark bptr = va_arg(ap,char *); 138466Smark if (bptr==0) 139466Smark bptr = "(null pointer)"; 140466Smark if (prec < 0) 141466Smark prec = MAXINT; 142466Smark for (n=0; *bptr++ && n < prec; n++) ; 143466Smark ptr = --bptr; 144466Smark bptr -= n; 145466Smark break; 146466Smark case 'O': 147466Smark length = 1; 148466Smark fcode = 'o'; 149466Smark /* no break */ 150466Smark case 'o': 151466Smark case 'X': 152466Smark case 'x': 153466Smark if (length > 0) 154466Smark num = va_arg(ap,long); 155466Smark else 156466Smark num = (unsigned)va_arg(ap,int); 157466Smark if (fcode=='o') { 158466Smark mask1 = 0x7; 159466Smark mask2 = 0x1fffffffL; 160466Smark nbits = 3; 161466Smark } 162466Smark else { 163466Smark mask1 = 0xf; 164466Smark mask2 = 0x0fffffffL; 165466Smark nbits = 4; 166466Smark } 167466Smark n = (num!=0); 168466Smark bptr = buf + MAXOCT + 3; 169466Smark /* shift and mask for speed */ 170466Smark do 171466Smark if (((int) num & mask1) < 10) 172466Smark *--bptr = ((int) num & mask1) + 060; 173466Smark else 174466Smark *--bptr = ((int) num & mask1) + 0127; 175466Smark while (num = (num >> nbits) & mask2); 176466Smark 177466Smark if (fcode=='o') { 178466Smark if (n) 179466Smark *--bptr = '0'; 180466Smark } 181466Smark else 182466Smark if (!sign && fill <= 0) { 183466Smark putchar('0'); 184466Smark putchar(fcode); 185466Smark width -= 2; 186466Smark } 187466Smark else { 188466Smark *--bptr = fcode; 189466Smark *--bptr = '0'; 190466Smark } 191466Smark ptr = buf + MAXOCT + 3; 192466Smark break; 193466Smark case 'D': 194466Smark case 'U': 195466Smark case 'I': 196466Smark length = 1; 197466Smark fcode = fcode + 'a' - 'A'; 198466Smark /* no break */ 199466Smark case 'd': 200466Smark case 'i': 201466Smark case 'u': 202466Smark if (length > 0) 203466Smark num = va_arg(ap,long); 204466Smark else { 205466Smark n = va_arg(ap,int); 206466Smark if (fcode=='u') 207466Smark num = (unsigned) n; 208466Smark else 209466Smark num = (long) n; 210466Smark } 211466Smark if (n = (fcode != 'u' && num < 0)) 212466Smark num = -num; 213466Smark /* now convert to digits */ 214466Smark bptr = _p_dconv(num, buf); 215466Smark if (n) 216466Smark *--bptr = '-'; 217466Smark if (fill == 0) 218466Smark fill = -1; 219466Smark ptr = buf + MAXDIGS + 1; 220466Smark break; 221466Smark default: 222466Smark /* not a control character, 223466Smark * print it. 224466Smark */ 225466Smark ptr = bptr = &fcode; 226466Smark ptr++; 227466Smark break; 228466Smark } 229466Smark if (fcode != '\0') 230466Smark _p_emit(bptr,ptr); 231466Smark } 232466Smark va_end(ap); 233466Smark } 234466Smark 235466Smark /* _p_dconv converts the unsigned long integer "value" to 236466Smark * printable decimal and places it in "buffer", right-justified. 237466Smark * The value returned is the address of the first non-zero character, 238466Smark * or the address of the last character if all are zero. 239466Smark * The result is NOT null terminated, and is MAXDIGS characters long, 240466Smark * starting at buffer[1] (to allow for insertion of a sign). 241466Smark * 242466Smark * This program assumes it is running on 2's complement machine 243466Smark * with reasonable overflow treatment. 244466Smark */ 245466Smark char * 246466Smark _p_dconv(value, buffer) 247466Smark long value; 248466Smark char *buffer; 249466Smark { 250466Smark register char *bp; 251466Smark register int svalue; 252466Smark int n; 253466Smark long lval; 254466Smark 255466Smark bp = buffer; 256466Smark 257466Smark /* zero is a special case */ 258466Smark if (value == 0) { 259466Smark bp += MAXDIGS; 260466Smark *bp = '0'; 261466Smark return(bp); 262466Smark } 263466Smark 264466Smark /* develop the leading digit of the value in "n" */ 265466Smark n = 0; 266466Smark while (value < 0) { 267466Smark value -= BIG; /* will eventually underflow */ 268466Smark n++; 269466Smark } 270466Smark while ((lval = value - BIG) >= 0) { 271466Smark value = lval; 272466Smark n++; 273466Smark } 274466Smark 275466Smark /* stash it in buffer[1] to allow for a sign */ 276466Smark bp[1] = n + '0'; 277466Smark /* 278466Smark * Now develop the rest of the digits. Since speed counts here, 279466Smark * we do it in two loops. The first gets "value" down until it 280466Smark * is no larger than MAXINT. The second one uses integer divides 281466Smark * rather than long divides to speed it up. 282466Smark */ 283466Smark bp += MAXDIGS + 1; 284466Smark while (value > MAXINT) { 285466Smark *--bp = (int)(value % 10) + '0'; 286466Smark value /= 10; 287466Smark } 288466Smark 289466Smark /* cannot lose precision */ 290466Smark svalue = value; 291466Smark while (svalue > 0) { 292466Smark *--bp = (svalue % 10) + '0'; 293466Smark svalue /= 10; 294466Smark } 295466Smark 296466Smark /* fill in intermediate zeroes if needed */ 297466Smark if (buffer[1] != '0') { 298466Smark while (bp > buffer + 2) 299466Smark *--bp = '0'; 300466Smark --bp; 301466Smark } 302466Smark return(bp); 303466Smark } 304466Smark 305466Smark /* 306466Smark * This program sends string "s" to putchar. The character after 307466Smark * the end of "s" is given by "send". This allows the size of the 308466Smark * field to be computed; it is stored in "alen". "width" contains the 309466Smark * user specified length. If width<alen, the width will be taken to 310466Smark * be alen. "sign" is zero if the string is to be right-justified 311466Smark * in the field, nonzero if it is to be left-justified. "fill" is 312466Smark * 0 if the string is to be padded with '0', positive if it is to be 313466Smark * padded with ' ', and negative if an initial '-' should appear before 314466Smark * any padding in right-justification (to avoid printing "-3" as 315466Smark * "000-3" where "-0003" was intended). 316466Smark */ 317466Smark _p_emit(s, send) 318466Smark register char *s; 319466Smark char *send; 320466Smark { 321466Smark char cfill; 322466Smark register int alen; 323466Smark int npad; 324466Smark 325466Smark alen = send - s; 326466Smark if (alen > width) 327466Smark width = alen; 328466Smark cfill = fill>0? ' ': '0'; 329466Smark 330466Smark /* we may want to print a leading '-' before anything */ 331466Smark if (*s == '-' && fill < 0) { 332466Smark putchar(*s++); 333466Smark alen--; 334466Smark width--; 335466Smark } 336466Smark npad = width - alen; 337466Smark 338466Smark /* emit any leading pad characters */ 339466Smark if (!sign) 340466Smark while (--npad >= 0) 341466Smark putchar(cfill); 342466Smark 343466Smark /* emit the string itself */ 344466Smark while (--alen >= 0) 345466Smark putchar(*s++); 346466Smark 347466Smark /* emit trailing pad characters */ 348466Smark if (sign) 349466Smark while (--npad >= 0) 350466Smark putchar(cfill); 351466Smark } 352