format (printf like routines)
chris at sol.UUCP
chris at sol.UUCP
Thu Jul 10 07:57:39 AEST 1986
*** 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."
More information about the Comp.sources.unix
mailing list