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