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