stevesu@copper.UUCP (03/26/87)
Mark Pulver is looking for a C version of _doprnt, so I thought I'd pass mine along. I wrote this from the ground up; it is absolutely underived from anything proprietary. This version is not complete, and has the following two key omissions: It doesn't do floating point (%f, %e, or %g). It will handle %ld (%lx, etc.) incorrectly on machines where sizeof(long) != sizeof(int). It also does not implement the %# stuff which appeared in the 4.2 documentation but which I haven't seen in any implementation yet. I believe it handles everything else correctly, although I have not tested it exhaustively. There are two "fun" additions: %b is binary, and %r is roman. You are free to use this code as you wish, but please leave the identification comment intact. I can offer no support for this code, although if I ever implement floating point or pdp11 support (I'm acutely embarrassed to admit making the typical VAX int/long equivalence assumption) I'll try to remember to post those additions. Steve Summit stevesu@copper.tek.com --------------------- cut here for doprnt.c --------------------- /* * Common code for printf et al. * * The calling routine typically takes a variable number of arguments, * and passes the address of the first one. This implementation * assumes a straightforward, stack implementation, aligned to the * machine's wordsize. Increasing addresses are assumed to point to * successive arguments (left-to-right), as is the case for a machine * with a downward-growing stack with arguments pushed right-to-left. * * To write, for example, fprintf() using this routine, the code * * fprintf(fd, format, args) * FILE *fd; * char *format; * { * _doprnt(format, &args, fd); * } * * would suffice. (This example does not handle the fprintf's "return * value" correctly, but who looks at the return value of fprintf * anyway?) * * This version implements the following printf features: * * %d decimal conversion * %u unsigned conversion * %x hexadecimal conversion * %X hexadecimal conversion with capital letters * %o octal conversion * %c character * %s string * %m.n field width, precision * %-m.n left adjustment * %0m.n zero-padding * %*.* width and precision taken from arguments * * This version does not implement %f, %e, or %g. It accepts, but * ignores, an `l' as in %ld, %lo, %lx, and %lu, and therefore will not * work correctly on machines for which sizeof(long) != sizeof(int). * It does not even parse %D, %O, or %U; you should be using %ld, %o and * %lu if you mean long conversion. * * This version implements the following nonstandard features: * * %b binary conversion * %r roman numeral conversion * %R roman numeral conversion with capital letters * * As mentioned, this version does not return any reasonable value. * * Permission is granted to use, modify, or propagate this code as * long as this notice is incorporated. * * Steve Summit 3/25/87 */ #include <stdio.h> #define TRUE 1 #define FALSE 0 #define ROMAN #define isdigit(d) ((d) >= '0' && (d) <= '9') #define Ctod(c) ((c) - '0') #define MAXBUF (sizeof(long int) * 8) /* enough for binary */ #ifdef ROMAN static tack(); static doit(); #endif _doprnt(fmt, argp, fd) register char *fmt; register int *argp; FILE *fd; { register char *p; char *p2; int size; int length; int prec; int ladjust; char padc; int n; unsigned int u; int base; char buf[MAXBUF]; int negflag; char *digs; #ifdef ROMAN char *rdigs; int d; #endif while(*fmt != '\0') { if(*fmt != '%') { putc(*fmt++, fd); continue; } fmt++; if(*fmt == 'l') fmt++; /* need to use it if sizeof(int) < sizeof(long) */ length = 0; prec = -1; ladjust = FALSE; padc = ' '; if(*fmt == '-') { ladjust = TRUE; fmt++; } if(*fmt == '0') { padc = '0'; fmt++; } if(isdigit(*fmt)) { while(isdigit(*fmt)) length = 10 * length + Ctod(*fmt++); } else if(*fmt == '*') { length = *argp++; fmt++; if(length < 0) { ladjust = !ladjust; length = -length; } } if(*fmt == '.') { fmt++; if(isdigit(*fmt)) { prec = 0; while(isdigit(*fmt)) prec = 10 * prec + Ctod(*fmt++); } else if(*fmt == '*') { prec = *argp++; fmt++; } } negflag = FALSE; digs = "0123456789abcdef"; #ifdef ROMAN rdigs = " mdclxvi"; #endif switch(*fmt) { case 'b': case 'B': u = *argp++; base = 2; goto donum; case 'c': putc(*argp++, fd); break; case 'd': case 'D': n = *argp++; if(n >= 0) u = n; else { u = -n; negflag = TRUE; } base = 10; goto donum; case 'o': case 'O': u = *argp++; base = 8; goto donum; #ifdef ROMAN case 'R': rdigs = " MDCLXVI"; case 'r': n = *argp++; p2 = &buf[MAXBUF - 1]; d = n % 10; tack(d, &rdigs[6], &p2); n = n / 10; d = n % 10; tack(d, &rdigs[4], &p2); n = n / 10; d = n % 10; tack(d, &rdigs[2], &p2); n /= 10; d = n % 10; tack(d, rdigs, &p2); p = p2; goto putpad; #endif case 's': p = (char *)(*argp++); if(p == NULL) p = "(NULL)"; if(length > 0 && !ladjust) { n = 0; p2 = p; for(; *p != '\0' && (prec == -1 || n < prec); p++) n++; p = p2; while(n < length) { putc(' ', fd); n++; } } n = 0; while(*p != '\0') { if(++n > prec && prec != -1) break; putc(*p++, fd); } if(n < length && ladjust) { while(n < length) { putc(' ', fd); n++; } } break; case 'u': case 'U': u = *argp++; base = 10; goto donum; case 'X': digs = "0123456789ABCDEF"; case 'x': u = *argp++; base = 16; donum: p = &buf[MAXBUF - 1]; do { *p-- = digs[u % base]; u /= base; } while(u != 0); if(negflag) putc('-', fd); putpad: size = &buf[MAXBUF - 1] - p; if(size < length && !ladjust) { while(length > size) { putc(padc, fd); length--; } } while(++p != &buf[MAXBUF]) putc(*p, fd); if(size < length) /* must be ladjust */ { while(length > size) { putc(padc, fd); length--; } } break; case '\0': fmt--; break; default: putc(*fmt, fd); } fmt++; } } #ifdef ROMAN static tack(d, digs, p) int d; char *digs; char **p; { if(d == 0) return; if(d >= 1 && d <= 3) { doit(d, digs[2], p); return; } if(d == 4 || d == 5) { **p = digs[1]; (*p)--; } if(d == 4) { **p = digs[2]; (*p)--; return; } if(d == 5) return; if(d >= 6 && d <= 8) { doit(d - 5, digs[2], p); **p = digs[1]; (*p)--; return; } /* d == 9 */ **p = digs[0]; (*p)--; **p = digs[2]; (*p)--; return; } static doit(d, one, p) int d; char one; char **p; { int i; for(i = 0; i < d; i++) { **p = one; (*p)--; } } #endif
metro@asi.UUCP (03/28/87)
I am not familiar with the _doprnt() routine, however I had a need to perform a similar function. I wanted to write a function of the form: fperror(fmt,args) char *fmt; int args; { } which would function much like printf(). However, depending on system parameters, would perform various logging of the error messages. The obvious way would be to use sprintf() to format the message and then pass the buffer to my routine. This is what i origionally did. However, it made the code somewhat unreadable. Then I realized that the vsprintf() routine provided with my c library took its parameters in the same form as &args of above. The function then bacame: fperror(fmt,args) char *fmt; int args; { char msgbuff[128]; vsprintf(msgbuff,fmt,&args); /* format args into msgbuff based on fmt */ . . . process the formatted arguments. } This is very likely not portable. However, on a machine where this method works, I would much rather have my parameters processed by the SAME routine as other sprintf's, printf's, and fprintf's. And save a home-brew routine for other systems. -- Metro T. Sauper, Jr. Assessment Systems, Inc. Director, Remote Systems Development 210 South Fourth Street (215) 592-8900 ..!asi!metro Philadelphia, PA 19106
allbery@ncoast.UUCP (04/04/87)
As quoted from <100@asi.UUCP> by metro@asi.UUCP (Metro T. Sauper): +--------------- | I am not familiar with the _doprnt() routine, however I had a need to perform | a similar function. I wanted to write a function of the form: >... | fperror(fmt,args) char *fmt; int args; | { | char msgbuff[128]; | | vsprintf(msgbuff,fmt,&args); /* format args into msgbuff based on fmt */ +--------------- This works on some systems, but if you have vsprintf() you canb also make these functions fully portable. Use varargs. Skeleton which appears in many programs I write (see io.c in UNaXcess Conferencing as an example): #include <varargs.h> void writef(va_alist) va_dcl { register char *fmt; static char _buf[5120]; va_list args; va_start(args); fmt = va_arg(args, char *); /* NO PARENTHESES AROUND THE TYPE! */ /* They will cause a syntax error */ (void) vsprintf(_buf, fmt, args); va_end(args); for (fmt = _buf; *fmt != '\0'; fmt++) writec(*fmt); } Note that varargs is defined such that it will work even on machines such as the Pyramid which pass arguments in registers (common on RISC machines). You MUST declare only one argument -- va_alist -- and include the type of it as "va_dcl" without a semicolon. (C compilers can then recognize this directly to do any special processing needed, as for the Pyramid.) Variables declared as "va_list" can then be passed to other functions as arguments, which is what makes the v*printf() functions work. I have seen varargs implemented under: AT&T System V Plexus Sys3 -- and possibly vanilla AT&T System III Xenix 2.x (Tandy 16a) Xenix 3.x (Tandy 6000, Altos x86) Pyramid OS/x Ultrix 1.1 (MicroVax) Moreover, UNaXcess Conferencing seems to be running on quite a few systems by means of it. (Actually, UC doesn't call vsprintf, since that function doesn't exist on all systems; it has its own format code. You're free to lift it out and use it, provided you attribute it to me and clearly state any changes you make to it so I don't get bug reports on your changes.) ++Brandon -- ____ ______________ / \ / __ __ __ \ Brandon S. Allbery | /^\ USS ___ | /__> / \ / \ aXcess Company | A A A CHALLENGER / \ | | `--, `--, 6615 Center St. #A1-105 | H V H | | \__/ \__/ \__/ Mentor, OH 44060-4101 | H . H SEVEN \____/ \______________/ +1 216 974 9210 | / | \ WHO ________________________________________________________| /___|___\ DARED As long as there is the spirit to dare new frontiers, the dream will never die. cbatt!cwruecmp!ncoast!allbery ncoast!allbery%case.CSNET@relay.CS.NET BALLBERY (UUCP) (CSNET/Internet) (MCIMail)