chris@sol.UUCP (07/10/86)
*** REPLACE THIS LINE WITH YOUR MESSAGE *** Here are the routines that I talked about in net.lang.c. They are barely commented, and tested on XENIX on an IBM-PC/AT, ULTRIX-11 on a PDP-11 and UNIX system V on a Microforce 1a. Do with as ye may. I do ask that if you make changes, please do not distribute them as different versions will confuse people. Notify me, and if the changes are appropriate, I will post a new version. Christopher M. Caldwell ...decvax!sii!dmcnh!sol!chris 16 Columbia Drive, VIBRAC Corporation, Amherst, NH 03031, (603)882-6777 13B Bobby's Lane, Milford, NH 03055, (603)673-2249 : This is a shell archive. Delete everything above this line. : To unpack this archive, type '/bin/sh <this_file'. : If the message 'shar complete.' does not appear at the end, this : file is not complete. echo "Extracting files archived Wed Jul 9 17:53:00 1986 by chris." echo "Extracting format.c (13553 characters)." sed 's/^X//' << SHARMARKER > format.c X/**************************************************************************/ X/*** format.c: format routines ***/ X/*** Author: Christopher M. Caldwell of IO Software, Inc. ***/ X/*** Created: 09-Jul-86 ***/ X/*** "Do with as ye may" ***/ X/**************************************************************************/ X X/* XThis package is a set of routines for formatting data in a more flexible Xway than UNIX's printf routines. In the simplest case: X X format("This is a test.\n"); X Xworks the way X X printf("This is a test.\n"); X Xdoes. However, printf uses percents (%) to denote substitutions. Format Xencloses substitutions with braces ({ and }). I.E. {s} denotes "substitute Xthe next argument as a string." After the letter denoting the substitution Xtype, modifiers may be supplied. {sl5} means print a string left justified Xto 5 characters. If the value is not supplied, it is taken from the Xargument list. The EBNF for this is: X X '{' <type-letter> { <modifier-character> [ <modifier-value> ] } '}' X XAvailable type letters so far are: X X s string (Char *) X i int X l long X c character X f float (double) X S Another format string (I smell recursion) X XAvailable modifers (and their defaults) are: X X l(0) Number of columns to take up, value will be left justified X r(0) Number of columns to take up, value will be right justified X c(0) Number of columns to take up, value will be centered X m(0) Maximum number of columns display of data X .(6) Number of places to display to the right of the decimal point X b(10) Base of number X p(32) Ascii value of pad character for l, r or c. X u(0) Unsigned status (signed=0, unsigned!=0) X n(1) Count of times to repeat string X XIf a modifer value is followed by a string of digits then that string is Xconverted to an integer and used as the modifier's value, else the value will Xbe taken from the next argument to format. X XSome ridiculous variable type/modifer combinations exist: X What is an unsigned string (or character) of base 8 with 4 trailing X decimal places? (All three modifiers are ignored) X Do you really want to see floating point variables in base 3? (Yes!) X What does centering a left or right justifed variable look like? X (The last justification character is the one used) X XNow, an example program: X X main() X { X int i; X X format("Left justified integer: '{il10}'\n",1234); X format("###{f.1c}###\n",123.4,20); X format("Octal: {lb8r11pu1}\n",01234L,'0'); X format("Truncate this: {sm10}\n", "This is a test" ); X format("Print 5 arfs: {sn5}\n","arf"); X i = 1; X format("Do it {i} time{sn}.\n",i,"s",i!=1); X i = 2; X format("Do it {i} time{sn}.\n",i,"s",i!=1); X format("Center this: ###{Sc20p}###\n","Answer={i}",'$',1234); X exit(0); X } X Xand its standard output: X X Left justified integer: '1234 ' X ### 123.4 ### X Octal: 00000001234 X Truncate this: This is a X Print 5 arfs: arfarfarfarfarf X Do it 1 time. X Do it 2 times. X Center this: ###$$$$$Answer=1234$$$$### X XThe following flavors of format are available: X X format( formatstring, args... ) Send to standard out X fformat( stream, formatstring, args... )Send to specified stream X i = cformat( formatstring, args... ) Calculate number of chars X sformat( buf, formatstring, args... ) Send to the character array buf X ptr = mformat( formatstring, args... ) Malloc enough space for result X (and trailing 0), put the X result in malloced space, and X return pointer to it X Also, as a kluge: X ptr = sformat( NULL, formatstring, args... ) X Xmformat (and sformat with a NULL array), perform the format twice, Xonce to find out how many characters needed to malloc, and the next Xtime to fill up the array. X XAdvantages: X Easier to understand justification X Centering available X More flexible padding (instead of just spaces or zeros) X Arbitrary base output (2 to 36) X Complex formats available ("S") X Arbitrarily lengthed everything (up do what you can malloc) X And from my point of view, I have the source! X*/ X X/**************************************************************************/ X X#include <stdio.h> X#include <ctype.h> X X#define PF_COUNT 0 X#define PF_ARRAY 1 X#define PF_PUTC 2 X#define then X#define TRUE 1 X#define FALSE 0 X Xstatic char *al; Xextern char *malloc(); X Xint lcase(c) char c; { return( isupper(c) ? tolower(c) : c ); } Xint ucase(c) char c; { return( islower(c) ? toupper(c) : c ); } Xint digtobin(c) char c; { return( c<='9' ? c-'0' : lcase(c)-'a'+10 ); } Xchar bintodig(i)int i; { return( i<=9 ? i+'0' : i+'a'-10 ); } X Xint isbase( c, b ) X/* Return TRUE if character c is digit of base b. Similar to isdigit, X but allows bases with letters and won't except "9" in base 8, etc. X*/ X char c; X int b; X { X if( !isdigit(c) && !isalpha(c) ) X then return FALSE; X else return( c<='9' ? (c-'0'<b) : (lcase(c)-'a'<b-10) ); X } X Xint cformat(fmt,arglist) char *fmt; int arglist; X/* Return number of characters generated by "printing" fmt. Used by X things that need to malloc space for strings. X*/ X { X int pf_count; X al = (char *)&arglist; X pf( fmt, PF_COUNT, &pf_count, NULL, NULL ); X return pf_count; X } X Xint cxformat(fmt,arglist) char *fmt, *arglist; X/* Same as above but argument list is pointed to by second argument */ X { X int pf_count; X al = arglist; X pf( fmt, PF_COUNT, &pf_count, NULL, NULL ); X return pf_count; X } X Xchar *mformat(fmt,arglist) char *fmt; int arglist; X/* Return pointer to malloced array of characters */ X { X int pf_count; X char *res; X al = (char *)&arglist; X pf( fmt, PF_COUNT, &pf_count, NULL, NULL ); X if( (res=malloc(pf_count+1)) == NULL ) then return NULL; X al = (char *)&arglist; X pf( fmt, PF_ARRAY, NULL, res, NULL ); X return res; X } X Xchar *mxformat(fmt,arglist) char *fmt, *arglist; X/* Same as above but argument list is pointed to by second argument */ X { X int pf_count; X char *res; X al = arglist; X pf( fmt, PF_COUNT, &pf_count, NULL, NULL ); X if( (res=malloc(pf_count+1)) == NULL ) then return NULL; X al = arglist; X pf( fmt, PF_ARRAY, NULL, res, NULL ); X return res; X } X Xchar *sformat(res,fmt,arglist) char *fmt, *res; int arglist; X/* Return pointer to malloced array of characters if res==NULL, X else fill array res with characters. X*/ X { X int pf_count; X if( res==NULL ) X then X { X al = (char *)&arglist; X pf( fmt, PF_COUNT, &pf_count, NULL, NULL ); X if( (res=malloc(pf_count+1)) == NULL ) then return NULL; X } X al = (char *)&arglist; X pf( fmt, PF_ARRAY, NULL, res, NULL ); X return res; X } X Xchar *sxformat(res,fmt,arglist) char *fmt, *res, *arglist; X/* Same as above but argument list is pointed to by second argument */ X { X int pf_count; X if( res==NULL ) X then X { X al = arglist; X pf( fmt, PF_COUNT, &pf_count, NULL, NULL ); X if( (res=malloc(pf_count+1)) == NULL ) then return NULL; X } X al = arglist; X pf( fmt, PF_ARRAY, NULL, res, NULL ); X return res; X } X Xint format(fmt,arglist) char *fmt; int arglist; X/* Print characters to standard out. */ X { X al = (char *)&arglist; X return pf( fmt, PF_PUTC, NULL, NULL, stdout ); X } X Xint xformat(fmt,arglist) char *fmt, *arglist; X/* Same as above but argument list is pointed to by second argument */ X { X al = arglist; X return pf( fmt, PF_PUTC, NULL, NULL, stdout ); X } X Xint fformat(outfile,fmt,arglist) FILE *outfile; char *fmt; int arglist; X/* Print characters to specified file stream. */ X { X al = (char *)&arglist; X return pf( fmt, PF_PUTC, NULL, NULL, outfile ); X } X Xint fxformat(outfile,fmt,arglist) FILE *outfile; char *fmt, *arglist; X/* Same as above but argument list is pointed to by second argument */ X { X al = arglist; X return pf( fmt, PF_PUTC, NULL, NULL, outfile ); X } X X#define NEXT(mode) ((mode *)(al += sizeof(mode)))[-1] X Xstatic int pf( fs, pf_func, pf_count, pf_addr, pf_file ) X/* This routine is called to parse the format string. If pf_func X is PF_COUNT, that would be generated is returned. If pf_func X is PF_ARRAY, an array is filled. If pf_func is PF_FILE, characters X are sent to the stream pf_file. X*/ X char *fs; X int pf_func; X int *pf_count; X char *pf_addr; X FILE *pf_file; X { X int pf_right, pf_left, pf_center, pf_dec, pf_max; X int pf_base, pf_iter, pf_pad, pf_unsigned; X char *pf_string, pf_char; X int pf_int; X long pf_long; X double pf_double; X double pnum; X int ind; X char t, c, buf[512], *cp; X int bufcnt; X int pbase; X int nm; X char *saveal; X X while( c = *fs++ ) X if( c != '{' || (t = *fs++) == '{' ) X then X switch( pf_func ) X { X case PF_COUNT: (*pf_count)++; break; X case PF_ARRAY: *pf_addr++ = c; break; X case PF_PUTC: if( putc(c,pf_file) == EOF ) X then return EOF; X else break; X } X else X { X pf_right = 0; X pf_left = 0; X pf_center = 0; X pf_dec = 6; X pf_max = 0; X pf_base = 10; X pf_iter = 1; X pf_pad = ' '; X pf_unsigned = FALSE; X switch( t ) X { X case 'S': pf_string = NEXT(char *); break; X case 's': pf_string = NEXT(char *); break; X case 'c': pf_char = NEXT(int); break; X case 'i': pf_int = NEXT(int); break; X case 'l': pf_long = NEXT(long); break; X case 'f': pf_double = NEXT(double); break; X break; X } X while( (c = *fs++) != '}' ) X { X nm = 0; X pbase = 10; X if( !isbase(*fs,pbase) ) X then nm = NEXT(int); X else X { X while( TRUE ) X { X while( isbase(*fs,pbase) ) X nm = pbase*nm + digtobin(*fs++); X if( nm<2 || nm>36 || *fs!='_' ) then break; X pbase = nm; X fs++; X } X } X switch( c ) X { X case 'r': pf_right = nm; break; X case 'l': pf_left = nm; break; X case 'c': pf_center = nm; break; X case '.': pf_dec = nm; break; X case 'm': pf_max = nm; break; X case 'b': pf_base = nm; break; X case 'n': pf_iter = nm; break; X case 'p': pf_pad = nm; break; X case 'u': pf_unsigned = nm; break; X } X } X bufcnt = 0; X switch( t ) X { X case 's': if( pf_string == NULL ) X then cp = "(null)"; X else cp = pf_string; X bufcnt = strlen( cp ); X break; X X case 'S': saveal = al; X pf(pf_string,PF_COUNT,&bufcnt,NULL,NULL); X al = saveal; X cp = malloc( bufcnt+1 ); X pf(pf_string,PF_ARRAY,NULL,cp,NULL); X break; X X case 'c': buf[bufcnt++] = pf_char; X cp = buf; X break; X X case 'i': pf_char = ( pf_int < 0 ); X if( pf_int >= 0 || !pf_unsigned ) X then X { X do { X buf[100-(++bufcnt)] X = bintodig( abs(pf_int%pf_base) ); X pf_int /= pf_base; X } while( pf_int != 0 ); X if(pf_char) then buf[100-(++bufcnt)]='-'; X cp = buf; X cp += (100 - bufcnt); X } X else X { X c = pf_int & 1; X pf_int >>= 1; X pf_int &= (1<<(sizeof(pf_int)-1)); X c = c + ((pf_int%(pf_base>>1)) << 1); X pf_int /= (pf_base>>1); X buf[100-(++bufcnt)] = bintodig( c ); X while( pf_int != 0 ) X { X buf[100-(++bufcnt)] X = bintodig( abs(pf_int%pf_base) ); X pf_int /= pf_base; X } X cp = buf; X cp += (100 - bufcnt); X } X break; X X case 'l': pf_char = ( pf_long < 0 ); X if( pf_long >= 0 || !pf_unsigned ) X then X { X do { X buf[100-(++bufcnt)] = bintodig( X abs((int)(pf_long%pf_base)) ); X pf_long /= pf_base; X } while( pf_long != 0 ); X if(pf_char) then buf[100-(++bufcnt)]='-'; X cp = buf; X cp += (100 - bufcnt); X } X else X { X c = pf_long & 1; X pf_long >>= 1; X pf_long &= (1<<(sizeof(pf_long)-1)); X c = c + ((pf_long%(pf_base>>1)) << 1); X pf_long /= (pf_base>>1); X buf[100-(++bufcnt)] = bintodig( c ); X while( pf_long != 0 ) X { X buf[100-(++bufcnt)] = bintodig( X abs((int)(pf_long%pf_base)) ); X pf_long /= pf_base; X } X cp = buf; X cp += (100 - bufcnt); X } X break; X X case 'f': if( pf_double < 0 ) X then X { X buf[bufcnt++] = '-'; X pf_double = -pf_double; X } X ind = 0; X for(pnum=1.0; pnum<=pf_double; pnum*=pf_base) X ind--; X pnum /= pf_base; X do { X if( ind++ == 0 ) then buf[bufcnt++]='.'; X c = (int)(pf_double/pnum); X buf[bufcnt++] = bintodig( c ); X pf_double -= (pnum*c); X pnum /= pf_base; X } while( ind < pf_dec ); X cp = buf; X break; X } X if( bufcnt > pf_max && pf_max > 0 ) then bufcnt = pf_max; X if( pf_center > 0 ) X then X { X pf_left = ( pf_center - bufcnt ) / 2; X pf_right = pf_center - pf_left - bufcnt; X } X else X { X pf_left -= bufcnt; X pf_right -= bufcnt; X } X while( pf_iter-- > 0 ) X { X for( ind=0; ind<pf_right; ind++ ) X switch( pf_func ) X { X case PF_COUNT: (*pf_count)++; break; X case PF_ARRAY: *pf_addr++ = pf_pad; break; X case PF_PUTC: if( putc(pf_pad,pf_file) == EOF ) X then return EOF; X else break; X } X for( ind=0; ind<bufcnt; ind++ ) X switch( pf_func ) X { X case PF_COUNT: (*pf_count)++; break; X case PF_ARRAY: *pf_addr++ = cp[ind]; break; X case PF_PUTC: if( putc(cp[ind],pf_file) == EOF ) X then return EOF; X else break; X } X for( ind=0; ind<pf_left; ind++ ) X switch( pf_func ) X { X case PF_COUNT: (*pf_count)++; break; X case PF_ARRAY: *pf_addr++ = pf_pad; break; X case PF_PUTC: if( putc(pf_pad,pf_file) == EOF ) X then return EOF; X else break; X } X } X if( t == 'S' ) then free( cp ); X } X if( pf_func == PF_ARRAY ) then *pf_addr = 0; X return 0; X } SHARMARKER echo "Shar complete."