Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!utcs!mnetor!seismo!lll-crg!topaz!ll-xn!mit-amt!mit-eddie!genrad!decvax!savax!sii!dmcnh!sol!chris From: chris@sol.UUCP Newsgroups: net.sources Subject: format (printf like routines) Message-ID: <125@sol.UUCP> Date: Wed, 9-Jul-86 17:57:39 EDT Article-I.D.: sol.125 Posted: Wed Jul 9 17:57:39 1986 Date-Received: Fri, 11-Jul-86 23:11:48 EDT Distribution: net Organization: VIBRAC Corp., Amherst, NH Lines: 516 *** 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 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 '{' { [ ] } '}' 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 X#include 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'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