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