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