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