1*34226Sbostic From stevesu@copper.UUCP Wed Mar 25 23:35:32 1987 2*34226Sbostic Path: seismo!ut-sally!husc6!bacchus!mit-eddie!genrad!decvax!tektronix!teklds!copper!stevesu 3*34226Sbostic From: stevesu@copper.TEK.COM (Steve Summit) 4*34226Sbostic Newsgroups: net.sources 5*34226Sbostic Subject: Public Domain _doprnt in C 6*34226Sbostic Message-ID: <938@copper.TEK.COM> 7*34226Sbostic Date: 26 Mar 87 04:35:32 GMT 8*34226Sbostic Reply-To: stevesu@copper.UUCP (Steve Summit) 9*34226Sbostic Distribution: world 10*34226Sbostic Organization: Tektronix, Inc., Beaverton, OR. 11*34226Sbostic Lines: 420 12*34226Sbostic 13*34226Sbostic Mark Pulver is looking for a C version of _doprnt, so I thought 14*34226Sbostic I'd pass mine along. I wrote this from the ground up; it is 15*34226Sbostic absolutely underived from anything proprietary. 16*34226Sbostic 17*34226Sbostic This version is not complete, and has the following two key 18*34226Sbostic omissions: 19*34226Sbostic 20*34226Sbostic It doesn't do floating point (%f, %e, or %g). 21*34226Sbostic 22*34226Sbostic It will handle %ld (%lx, etc.) incorrectly on machines 23*34226Sbostic where sizeof(long) != sizeof(int). 24*34226Sbostic 25*34226Sbostic It also does not implement the %# stuff which appeared in the 4.2 26*34226Sbostic documentation but which I haven't seen in any implementation yet. 27*34226Sbostic 28*34226Sbostic I believe it handles everything else correctly, although I have 29*34226Sbostic not tested it exhaustively. 30*34226Sbostic 31*34226Sbostic There are two "fun" additions: %b is binary, and %r is roman. 32*34226Sbostic 33*34226Sbostic You are free to use this code as you wish, but please leave the 34*34226Sbostic identification comment intact. I can offer no support for this 35*34226Sbostic code, although if I ever implement floating point or pdp11 36*34226Sbostic support (I'm acutely embarrassed to admit making the typical VAX 37*34226Sbostic int/long equivalence assumption) I'll try to remember to post 38*34226Sbostic those additions. 39*34226Sbostic 40*34226Sbostic Steve Summit 41*34226Sbostic stevesu@copper.tek.com 42*34226Sbostic 43*34226Sbostic --------------------- cut here for doprnt.c --------------------- 44*34226Sbostic /* 45*34226Sbostic * Common code for printf et al. 46*34226Sbostic * 47*34226Sbostic * The calling routine typically takes a variable number of arguments, 48*34226Sbostic * and passes the address of the first one. This implementation 49*34226Sbostic * assumes a straightforward, stack implementation, aligned to the 50*34226Sbostic * machine's wordsize. Increasing addresses are assumed to point to 51*34226Sbostic * successive arguments (left-to-right), as is the case for a machine 52*34226Sbostic * with a downward-growing stack with arguments pushed right-to-left. 53*34226Sbostic * 54*34226Sbostic * To write, for example, fprintf() using this routine, the code 55*34226Sbostic * 56*34226Sbostic * fprintf(fd, format, args) 57*34226Sbostic * FILE *fd; 58*34226Sbostic * char *format; 59*34226Sbostic * { 60*34226Sbostic * _doprnt(format, &args, fd); 61*34226Sbostic * } 62*34226Sbostic * 63*34226Sbostic * would suffice. (This example does not handle the fprintf's "return 64*34226Sbostic * value" correctly, but who looks at the return value of fprintf 65*34226Sbostic * anyway?) 66*34226Sbostic * 67*34226Sbostic * This version implements the following printf features: 68*34226Sbostic * 69*34226Sbostic * %d decimal conversion 70*34226Sbostic * %u unsigned conversion 71*34226Sbostic * %x hexadecimal conversion 72*34226Sbostic * %X hexadecimal conversion with capital letters 73*34226Sbostic * %o octal conversion 74*34226Sbostic * %c character 75*34226Sbostic * %s string 76*34226Sbostic * %m.n field width, precision 77*34226Sbostic * %-m.n left adjustment 78*34226Sbostic * %0m.n zero-padding 79*34226Sbostic * %*.* width and precision taken from arguments 80*34226Sbostic * 81*34226Sbostic * This version does not implement %f, %e, or %g. It accepts, but 82*34226Sbostic * ignores, an `l' as in %ld, %lo, %lx, and %lu, and therefore will not 83*34226Sbostic * work correctly on machines for which sizeof(long) != sizeof(int). 84*34226Sbostic * It does not even parse %D, %O, or %U; you should be using %ld, %o and 85*34226Sbostic * %lu if you mean long conversion. 86*34226Sbostic * 87*34226Sbostic * This version implements the following nonstandard features: 88*34226Sbostic * 89*34226Sbostic * %b binary conversion 90*34226Sbostic * %r roman numeral conversion 91*34226Sbostic * %R roman numeral conversion with capital letters 92*34226Sbostic * 93*34226Sbostic * As mentioned, this version does not return any reasonable value. 94*34226Sbostic * 95*34226Sbostic * Permission is granted to use, modify, or propagate this code as 96*34226Sbostic * long as this notice is incorporated. 97*34226Sbostic * 98*34226Sbostic * Steve Summit 3/25/87 99*34226Sbostic */ 100*34226Sbostic 101*34226Sbostic #include <stdio.h> 102*34226Sbostic 103*34226Sbostic #define TRUE 1 104*34226Sbostic #define FALSE 0 105*34226Sbostic 106*34226Sbostic #define ROMAN 107*34226Sbostic 108*34226Sbostic #define isdigit(d) ((d) >= '0' && (d) <= '9') 109*34226Sbostic #define Ctod(c) ((c) - '0') 110*34226Sbostic 111*34226Sbostic #define MAXBUF (sizeof(long int) * 8) /* enough for binary */ 112*34226Sbostic 113*34226Sbostic #ifdef ROMAN 114*34226Sbostic static tack(); 115*34226Sbostic static doit(); 116*34226Sbostic #endif 117*34226Sbostic 118*34226Sbostic _doprnt(fmt, argp, fd) 119*34226Sbostic register char *fmt; 120*34226Sbostic register int *argp; 121*34226Sbostic FILE *fd; 122*34226Sbostic { 123*34226Sbostic register char *p; 124*34226Sbostic char *p2; 125*34226Sbostic int size; 126*34226Sbostic int length; 127*34226Sbostic int prec; 128*34226Sbostic int ladjust; 129*34226Sbostic char padc; 130*34226Sbostic int n; 131*34226Sbostic unsigned int u; 132*34226Sbostic int base; 133*34226Sbostic char buf[MAXBUF]; 134*34226Sbostic int negflag; 135*34226Sbostic char *digs; 136*34226Sbostic #ifdef ROMAN 137*34226Sbostic char *rdigs; 138*34226Sbostic int d; 139*34226Sbostic #endif 140*34226Sbostic 141*34226Sbostic while(*fmt != '\0') 142*34226Sbostic { 143*34226Sbostic if(*fmt != '%') 144*34226Sbostic { 145*34226Sbostic putc(*fmt++, fd); 146*34226Sbostic continue; 147*34226Sbostic } 148*34226Sbostic 149*34226Sbostic fmt++; 150*34226Sbostic 151*34226Sbostic if(*fmt == 'l') 152*34226Sbostic fmt++; /* need to use it if sizeof(int) < sizeof(long) */ 153*34226Sbostic 154*34226Sbostic length = 0; 155*34226Sbostic prec = -1; 156*34226Sbostic ladjust = FALSE; 157*34226Sbostic padc = ' '; 158*34226Sbostic 159*34226Sbostic if(*fmt == '-') 160*34226Sbostic { 161*34226Sbostic ladjust = TRUE; 162*34226Sbostic fmt++; 163*34226Sbostic } 164*34226Sbostic 165*34226Sbostic if(*fmt == '0') 166*34226Sbostic { 167*34226Sbostic padc = '0'; 168*34226Sbostic fmt++; 169*34226Sbostic } 170*34226Sbostic 171*34226Sbostic if(isdigit(*fmt)) 172*34226Sbostic { 173*34226Sbostic while(isdigit(*fmt)) 174*34226Sbostic length = 10 * length + Ctod(*fmt++); 175*34226Sbostic } 176*34226Sbostic else if(*fmt == '*') 177*34226Sbostic { 178*34226Sbostic length = *argp++; 179*34226Sbostic fmt++; 180*34226Sbostic if(length < 0) 181*34226Sbostic { 182*34226Sbostic ladjust = !ladjust; 183*34226Sbostic length = -length; 184*34226Sbostic } 185*34226Sbostic } 186*34226Sbostic 187*34226Sbostic if(*fmt == '.') 188*34226Sbostic { 189*34226Sbostic fmt++; 190*34226Sbostic if(isdigit(*fmt)) 191*34226Sbostic { 192*34226Sbostic prec = 0; 193*34226Sbostic while(isdigit(*fmt)) 194*34226Sbostic prec = 10 * prec + Ctod(*fmt++); 195*34226Sbostic } 196*34226Sbostic else if(*fmt == '*') 197*34226Sbostic { 198*34226Sbostic prec = *argp++; 199*34226Sbostic fmt++; 200*34226Sbostic } 201*34226Sbostic } 202*34226Sbostic 203*34226Sbostic negflag = FALSE; 204*34226Sbostic digs = "0123456789abcdef"; 205*34226Sbostic #ifdef ROMAN 206*34226Sbostic rdigs = " mdclxvi"; 207*34226Sbostic #endif 208*34226Sbostic 209*34226Sbostic switch(*fmt) 210*34226Sbostic { 211*34226Sbostic case 'b': 212*34226Sbostic case 'B': 213*34226Sbostic u = *argp++; 214*34226Sbostic base = 2; 215*34226Sbostic goto donum; 216*34226Sbostic 217*34226Sbostic case 'c': 218*34226Sbostic putc(*argp++, fd); 219*34226Sbostic break; 220*34226Sbostic 221*34226Sbostic case 'd': 222*34226Sbostic case 'D': 223*34226Sbostic n = *argp++; 224*34226Sbostic 225*34226Sbostic if(n >= 0) 226*34226Sbostic u = n; 227*34226Sbostic else { 228*34226Sbostic u = -n; 229*34226Sbostic negflag = TRUE; 230*34226Sbostic } 231*34226Sbostic 232*34226Sbostic base = 10; 233*34226Sbostic 234*34226Sbostic goto donum; 235*34226Sbostic 236*34226Sbostic case 'o': 237*34226Sbostic case 'O': 238*34226Sbostic u = *argp++; 239*34226Sbostic base = 8; 240*34226Sbostic goto donum; 241*34226Sbostic #ifdef ROMAN 242*34226Sbostic case 'R': 243*34226Sbostic rdigs = " MDCLXVI"; 244*34226Sbostic case 'r': 245*34226Sbostic n = *argp++; 246*34226Sbostic p2 = &buf[MAXBUF - 1]; 247*34226Sbostic 248*34226Sbostic d = n % 10; 249*34226Sbostic tack(d, &rdigs[6], &p2); 250*34226Sbostic n = n / 10; 251*34226Sbostic 252*34226Sbostic d = n % 10; 253*34226Sbostic tack(d, &rdigs[4], &p2); 254*34226Sbostic n = n / 10; 255*34226Sbostic 256*34226Sbostic d = n % 10; 257*34226Sbostic tack(d, &rdigs[2], &p2); 258*34226Sbostic n /= 10; 259*34226Sbostic 260*34226Sbostic d = n % 10; 261*34226Sbostic tack(d, rdigs, &p2); 262*34226Sbostic 263*34226Sbostic p = p2; 264*34226Sbostic 265*34226Sbostic goto putpad; 266*34226Sbostic #endif 267*34226Sbostic case 's': 268*34226Sbostic p = (char *)(*argp++); 269*34226Sbostic 270*34226Sbostic if(p == NULL) 271*34226Sbostic p = "(NULL)"; 272*34226Sbostic 273*34226Sbostic if(length > 0 && !ladjust) 274*34226Sbostic { 275*34226Sbostic n = 0; 276*34226Sbostic p2 = p; 277*34226Sbostic 278*34226Sbostic for(; *p != '\0' && 279*34226Sbostic (prec == -1 || n < prec); p++) 280*34226Sbostic n++; 281*34226Sbostic 282*34226Sbostic p = p2; 283*34226Sbostic 284*34226Sbostic while(n < length) 285*34226Sbostic { 286*34226Sbostic putc(' ', fd); 287*34226Sbostic n++; 288*34226Sbostic } 289*34226Sbostic } 290*34226Sbostic 291*34226Sbostic n = 0; 292*34226Sbostic 293*34226Sbostic while(*p != '\0') 294*34226Sbostic { 295*34226Sbostic if(++n > prec && prec != -1) 296*34226Sbostic break; 297*34226Sbostic 298*34226Sbostic putc(*p++, fd); 299*34226Sbostic } 300*34226Sbostic 301*34226Sbostic if(n < length && ladjust) 302*34226Sbostic { 303*34226Sbostic while(n < length) 304*34226Sbostic { 305*34226Sbostic putc(' ', fd); 306*34226Sbostic n++; 307*34226Sbostic } 308*34226Sbostic } 309*34226Sbostic 310*34226Sbostic break; 311*34226Sbostic 312*34226Sbostic case 'u': 313*34226Sbostic case 'U': 314*34226Sbostic u = *argp++; 315*34226Sbostic base = 10; 316*34226Sbostic goto donum; 317*34226Sbostic 318*34226Sbostic case 'X': 319*34226Sbostic digs = "0123456789ABCDEF"; 320*34226Sbostic case 'x': 321*34226Sbostic u = *argp++; 322*34226Sbostic base = 16; 323*34226Sbostic 324*34226Sbostic donum: p = &buf[MAXBUF - 1]; 325*34226Sbostic 326*34226Sbostic do { 327*34226Sbostic *p-- = digs[u % base]; 328*34226Sbostic u /= base; 329*34226Sbostic } while(u != 0); 330*34226Sbostic 331*34226Sbostic if(negflag) 332*34226Sbostic putc('-', fd); 333*34226Sbostic putpad: 334*34226Sbostic size = &buf[MAXBUF - 1] - p; 335*34226Sbostic 336*34226Sbostic if(size < length && !ladjust) 337*34226Sbostic { 338*34226Sbostic while(length > size) 339*34226Sbostic { 340*34226Sbostic putc(padc, fd); 341*34226Sbostic length--; 342*34226Sbostic } 343*34226Sbostic } 344*34226Sbostic 345*34226Sbostic while(++p != &buf[MAXBUF]) 346*34226Sbostic putc(*p, fd); 347*34226Sbostic 348*34226Sbostic if(size < length) /* must be ladjust */ 349*34226Sbostic { 350*34226Sbostic while(length > size) 351*34226Sbostic { 352*34226Sbostic putc(padc, fd); 353*34226Sbostic length--; 354*34226Sbostic } 355*34226Sbostic } 356*34226Sbostic 357*34226Sbostic break; 358*34226Sbostic 359*34226Sbostic case '\0': 360*34226Sbostic fmt--; 361*34226Sbostic break; 362*34226Sbostic 363*34226Sbostic default: 364*34226Sbostic putc(*fmt, fd); 365*34226Sbostic } 366*34226Sbostic fmt++; 367*34226Sbostic } 368*34226Sbostic } 369*34226Sbostic 370*34226Sbostic #ifdef ROMAN 371*34226Sbostic 372*34226Sbostic static 373*34226Sbostic tack(d, digs, p) 374*34226Sbostic int d; 375*34226Sbostic char *digs; 376*34226Sbostic char **p; 377*34226Sbostic { 378*34226Sbostic if(d == 0) return; 379*34226Sbostic if(d >= 1 && d <= 3) 380*34226Sbostic { 381*34226Sbostic doit(d, digs[2], p); 382*34226Sbostic return; 383*34226Sbostic } 384*34226Sbostic 385*34226Sbostic if(d == 4 || d == 5) 386*34226Sbostic { 387*34226Sbostic **p = digs[1]; 388*34226Sbostic (*p)--; 389*34226Sbostic } 390*34226Sbostic 391*34226Sbostic if(d == 4) 392*34226Sbostic { 393*34226Sbostic **p = digs[2]; 394*34226Sbostic (*p)--; 395*34226Sbostic return; 396*34226Sbostic } 397*34226Sbostic 398*34226Sbostic if(d == 5) return; 399*34226Sbostic 400*34226Sbostic if(d >= 6 && d <= 8) 401*34226Sbostic { 402*34226Sbostic doit(d - 5, digs[2], p); 403*34226Sbostic **p = digs[1]; 404*34226Sbostic (*p)--; 405*34226Sbostic return; 406*34226Sbostic } 407*34226Sbostic 408*34226Sbostic /* d == 9 */ 409*34226Sbostic 410*34226Sbostic **p = digs[0]; 411*34226Sbostic (*p)--; 412*34226Sbostic **p = digs[2]; 413*34226Sbostic (*p)--; 414*34226Sbostic return; 415*34226Sbostic } 416*34226Sbostic 417*34226Sbostic static 418*34226Sbostic doit(d, one, p) 419*34226Sbostic int d; 420*34226Sbostic char one; 421*34226Sbostic char **p; 422*34226Sbostic { 423*34226Sbostic int i; 424*34226Sbostic 425*34226Sbostic for(i = 0; i < d; i++) 426*34226Sbostic { 427*34226Sbostic **p = one; 428*34226Sbostic (*p)--; 429*34226Sbostic } 430*34226Sbostic } 431*34226Sbostic 432*34226Sbostic #endif 433