fred@gyre.UUCP (Fred Blonder) (01/10/87)
Here's a handy little program which works sort of like echo, except that it does formatting in the manner of printf. In fact, it uses printf to do its formatting, so you have the full functionality of printf available at the shell level. The only problem with it is that it will run only on stack machines, and only some of them. It runs on VAXes. It doesn't run on Suns. I haven't tested it on anything else. The reason for this strangeness is that I didn't want to allow only a fixed number of arguments to the printf call, so I had to push the arguments onto the stack myself, rather than using the normal C calling mechanism. This results in some rather odd code. I suppose someday someone will write this transportably, but in the meantime you can amaze your friends by showing them the call to printf with no arguments. No makefile here, just ``cc printf.c -o printf''. . . . . . . . . . . . . . CUT ON DOTTED LINE . . . . . . . . . . . . . : Run this shell script with "sh" not "csh" PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH export PATH all=FALSE if [ x$1 = x-a ]; then all=TRUE fi echo Extracting printf.1 sed 's/^X//' <<'//go.sysin dd *' >printf.1 X.\" @(#)printf.1 8-Jan-1987 X.\" X.TH PRINTF 1 "8-Jan-1987" X.AT 3 X.SH NAME printf \- formatted print at shell command level X.SH SYNOPSIS X.B "printf <format-string>" [ X.B arg1 ] [ X.B arg2 ] ... X.SH DESCRIPTION X.I Printf duplicates \- as far as possible \- the standard C library routine of the same name, at the shell command level. It is similar to X.I echo, except that it formats its arguments according to conversion specifications given in the X.B format-string, before writing them to the standard output. XFor a thorough explanation of format specifications, see the manual entry for the printf subroutine. X.PP As a convenience, within the X.I format-string and any string or character arguments, X.I printf converts "backslash notation" \- as defined in the ANSII draft C standard \- into the appropriate control characters. X.SH EXAMPLES X.nf X.na X.sp 2 % printf 'Today is %s the %d of %s.\\n' Monday 1 April Today is Monday the 1 of April. X.sp 3 % printf 'Interesting Numbers\\n\\n\\tPie: %*.*f\\n\\tFoo: %g\\n' \\ 6 4 3.1415927 42 Interesting Numbers Pie: 3.1416 Foo: 42 X.sp 2 X.fi X.ad X.SH AUTHOR XFred Blonder <fred@Mimsy.umd.edu> X.SH "SEE ALSO" echo(1), printf(3) //go.sysin dd * if [ `wc -c < printf.1` != 1169 ]; then made=FALSE echo error transmitting printf.1 -- echo length should be 1169, not `wc -c < printf.1` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 printf.1 echo -n ' '; ls -ld printf.1 fi echo Extracting printf.c sed 's/^X//' <<'//go.sysin dd *' >printf.c #ifndef lint static char sccsid[] = "@(#)printf.c (U of Maryland) FLB 6-Jan-1987"; static char RCSid[] = "@(#)$Header: printf.c,v 1.2 87/01/09 19:10:57 fred Exp $"; #endif X/* * Printf - Duplicate the C library routine of the same name, but from * the shell command level. * * WARNING: Gross code. Extremely machine dependent. (Works on VAXen.) * * To Compile: % cc -s -O printf.c -o printf * * $Log: printf.c,v $ * Revision 1.2 87/01/09 19:10:57 fred * Fixed bug in argument-count error-checking. * Changed backslash escapes within strings to correspond to ANSII C * draft standard. (9-Jan-87 FLB) * */ #include <stdio.h> #include <sysexits.h> X/****************************************************************************/ main(argc, argv) int argc; char *argv[]; { register char *cp; register int *argp, *ep; register unsigned long *sp; char *conv_args, *sbrk(), *index(); double atof(); int conv_arg_size; register int *tp; if (argc < 2) { fprintf(stderr, "printf: Usage: printf <format-string> [ arg1 . . . ]\n"); exit(EX_USAGE); } argp = (int *)&argv[2]; /* Point at first arg (if any) beyond format string. */ ep = (int *)&argv[argc]; /* Point beyond last arg. */ conv_args = sbrk(0); /* Remember current break. */ ctrl(argv[1]); /* Change backslash notation to control chars in fmt string. */ tp = (int *)sbrk(sizeof(char *)); /* Put fmt string on arg list. */ *tp = (int)argv[1]; X/* Scan format string for conversion specifications, and do appropriate conversion on the corresponding argument. */ for (cp = argv[1]; *cp; cp++) { /* Look for next conversion spec. */ while (*cp && *cp != '%') { cp++; } if (!*cp) /* % at end of string - error - ignore */ break; for (cp++; *cp && *cp != '%' && (int)index("*.scdoxufeg0123456789", *cp); cp++) { if (argp >= ep) { fprintf(stderr, "printf: not enough args for format\n"); exit(EX_USAGE); } switch (*cp) { /* Field-width spec.: Keep scanning. */ case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; case 's': /* String: no conversion */ ctrl((char *)*argp); tp = (int *)sbrk(sizeof(char *)); *tp = *argp; argp++; goto out; case 'c': /* Char: copy it */ ctrl((char *)*argp); tp = (int *)sbrk(sizeof(int *)); *(int *)tp = *(char *)*argp; argp++; goto out; case '*': /* Dynamic field-width spec */ tp = (int *)sbrk(sizeof(int)); *tp = atoi((char *)*argp); argp++; continue; case 'd': /* Integer */ case 'o': case 'x': case 'u': tp = (int *)sbrk(sizeof(int)); *tp = atoi((char *)*argp); argp++; goto out; case 'f': /* Real */ case 'e': case 'g': tp = (int *)sbrk(sizeof(double)); *(double *)tp = atof((char *)*argp); argp++; goto out; } } out: ; } X/* Gross-out time! Copy converted argument list onto the stack. */ if (!(sp = (unsigned long *)alloca(conv_arg_size = (sbrk(0) - conv_args)))) { fprintf(stderr, "printf: Can't allocate stack space.\n"); exit(EX_OSERR); } bcopy(conv_args, sp, conv_arg_size); printf(); /* Yes, this really does work. */ exit(EX_OK); } X/****************************************************************************/ X/* Convert backslash notation to control characters, in place. */ ctrl(s) register char *s; { register char *op; static int val; for (op = s; *s; s++) if (*s == '\\') switch (*++s) { case '\0': /* End-of-string: user goofed */ goto out; case '\\': /* Backslash */ *op++ = '\\'; break; case 'n': /* newline */ *op++ = '\n'; break; case 't': /* horizontal tab */ *op++ = '\t'; break; case 'r': /* carriage-return */ *op++ = '\r'; break; case 'f': /* form-feed */ *op++ = '\f'; break; case 'b': /* backspace */ *op++ = '\b'; break; case 'v': /* vertical tab */ *op++ = '\13'; break; case 'a': /* WARNING! DANGER! DANGER! DANGER! */ *op++ = '\7'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { /* octal constant */ register int digits; val = 0; (void) sscanf(s, "%3o", &val); *op++ = val; for (digits = 3; s[1] && (int)index("01234567", s[1]) && --digits > 0; s++); } break; case 'x': /* hex constant */ s++; { register int digits; val = 0; (void) sscanf(s, "%3x", &val); *op++ = val; for (digits = 3; *s && s[1] && (int)index("0123456789abcdefABCDEF", s[1]) && --digits > 0; s++); } break; } else *op++ = *s; out: *op = '\0'; } X/****************************************************************************/ //go.sysin dd * if [ `wc -c < printf.c` != 4776 ]; then made=FALSE echo error transmitting printf.c -- echo length should be 4776, not `wc -c < printf.c` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 printf.c echo -n ' '; ls -ld printf.c fi . . . . . . . . . . . . . CUT ON DOTTED LINE . . . . . . . . . . . . . -- ----- Fred Blonder (301) 454-7690 seismo!mimsy!fred Fred@Mimsy.umd.edu , @Maryland.CSNet
fred@mimsy.UUCP (Fred Blonder) (01/10/87)
Here's a handy little program which works sort of like echo, except that it does formatting in the manner of printf. In fact, it uses printf to do its formatting, so you have the full functionality of printf available at the shell level. The only problem with it is that it will run only on stack machines, and only some of them. It runs on VAXes. It doesn't run on Suns. I haven't tested it on anything else. The reason for this strangeness is that I didn't want to allow only a fixed number of arguments to the printf call, so I had to push the arguments onto the stack myself, rather than using the normal C calling mechanism. This results in some rather odd code. I suppose someday someone will write this transportably, but in the meantime you can amaze your friends by showing them the call to printf with no arguments. No makefile here, just ``cc printf.c -o printf''. . . . . . . . . . . . . . CUT ON DOTTED LINE . . . . . . . . . . . . . : Run this shell script with "sh" not "csh" PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH export PATH all=FALSE if [ x$1 = x-a ]; then all=TRUE fi echo Extracting printf.1 sed 's/^X//' <<'//go.sysin dd *' >printf.1 X.\" @(#)printf.1 8-Jan-1987 X.\" X.TH PRINTF 1 "8-Jan-1987" X.AT 3 X.SH NAME printf \- formatted print at shell command level X.SH SYNOPSIS X.B "printf <format-string>" [ X.B arg1 ] [ X.B arg2 ] ... X.SH DESCRIPTION X.I Printf duplicates \- as far as possible \- the standard C library routine of the same name, at the shell command level. It is similar to X.I echo, except that it formats its arguments according to conversion specifications given in the X.B format-string, before writing them to the standard output. XFor a thorough explanation of format specifications, see the manual entry for the printf subroutine. X.PP As a convenience, within the X.I format-string and any string or character arguments, X.I printf converts "backslash notation" \- as defined in the ANSII draft C standard \- into the appropriate control characters. X.SH EXAMPLES X.nf X.na X.sp 2 % printf 'Today is %s the %d of %s.\\n' Monday 1 April Today is Monday the 1 of April. X.sp 3 % printf 'Interesting Numbers\\n\\n\\tPie: %*.*f\\n\\tFoo: %g\\n' \\ 6 4 3.1415927 42 Interesting Numbers Pie: 3.1416 Foo: 42 X.sp 2 X.fi X.ad X.SH AUTHOR XFred Blonder <fred@Mimsy.umd.edu> X.SH "SEE ALSO" echo(1), printf(3) //go.sysin dd * if [ `wc -c < printf.1` != 1169 ]; then made=FALSE echo error transmitting printf.1 -- echo length should be 1169, not `wc -c < printf.1` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 printf.1 echo -n ' '; ls -ld printf.1 fi echo Extracting printf.c sed 's/^X//' <<'//go.sysin dd *' >printf.c #ifndef lint static char sccsid[] = "@(#)printf.c (U of Maryland) FLB 6-Jan-1987"; static char RCSid[] = "@(#)$Header: printf.c,v 1.2 87/01/09 19:10:57 fred Exp $"; #endif X/* * Printf - Duplicate the C library routine of the same name, but from * the shell command level. * * WARNING: Gross code. Extremely machine dependent. (Works on VAXen.) * * To Compile: % cc -s -O printf.c -o printf * * $Log: printf.c,v $ * Revision 1.2 87/01/09 19:10:57 fred * Fixed bug in argument-count error-checking. * Changed backslash escapes within strings to correspond to ANSII C * draft standard. (9-Jan-87 FLB) * */ #include <stdio.h> #include <sysexits.h> X/****************************************************************************/ main(argc, argv) int argc; char *argv[]; { register char *cp; register int *argp, *ep; register unsigned long *sp; char *conv_args, *sbrk(), *index(); double atof(); int conv_arg_size; register int *tp; if (argc < 2) { fprintf(stderr, "printf: Usage: printf <format-string> [ arg1 . . . ]\n"); exit(EX_USAGE); } argp = (int *)&argv[2]; /* Point at first arg (if any) beyond format string. */ ep = (int *)&argv[argc]; /* Point beyond last arg. */ conv_args = sbrk(0); /* Remember current break. */ ctrl(argv[1]); /* Change backslash notation to control chars in fmt string. */ tp = (int *)sbrk(sizeof(char *)); /* Put fmt string on arg list. */ *tp = (int)argv[1]; X/* Scan format string for conversion specifications, and do appropriate conversion on the corresponding argument. */ for (cp = argv[1]; *cp; cp++) { /* Look for next conversion spec. */ while (*cp && *cp != '%') { cp++; } if (!*cp) /* % at end of string - error - ignore */ break; for (cp++; *cp && *cp != '%' && (int)index("*.scdoxufeg0123456789", *cp); cp++) { if (argp >= ep) { fprintf(stderr, "printf: not enough args for format\n"); exit(EX_USAGE); } switch (*cp) { /* Field-width spec.: Keep scanning. */ case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; case 's': /* String: no conversion */ ctrl((char *)*argp); tp = (int *)sbrk(sizeof(char *)); *tp = *argp; argp++; goto out; case 'c': /* Char: copy it */ ctrl((char *)*argp); tp = (int *)sbrk(sizeof(int *)); *(int *)tp = *(char *)*argp; argp++; goto out; case '*': /* Dynamic field-width spec */ tp = (int *)sbrk(sizeof(int)); *tp = atoi((char *)*argp); argp++; continue; case 'd': /* Integer */ case 'o': case 'x': case 'u': tp = (int *)sbrk(sizeof(int)); *tp = atoi((char *)*argp); argp++; goto out; case 'f': /* Real */ case 'e': case 'g': tp = (int *)sbrk(sizeof(double)); *(double *)tp = atof((char *)*argp); argp++; goto out; } } out: ; } X/* Gross-out time! Copy converted argument list onto the stack. */ if (!(sp = (unsigned long *)alloca(conv_arg_size = (sbrk(0) - conv_args)))) { fprintf(stderr, "printf: Can't allocate stack space.\n"); exit(EX_OSERR); } bcopy(conv_args, sp, conv_arg_size); printf(); /* Yes, this really does work. */ exit(EX_OK); } X/****************************************************************************/ X/* Convert backslash notation to control characters, in place. */ ctrl(s) register char *s; { register char *op; static int val; for (op = s; *s; s++) if (*s == '\\') switch (*++s) { case '\0': /* End-of-string: user goofed */ goto out; case '\\': /* Backslash */ *op++ = '\\'; break; case 'n': /* newline */ *op++ = '\n'; break; case 't': /* horizontal tab */ *op++ = '\t'; break; case 'r': /* carriage-return */ *op++ = '\r'; break; case 'f': /* form-feed */ *op++ = '\f'; break; case 'b': /* backspace */ *op++ = '\b'; break; case 'v': /* vertical tab */ *op++ = '\13'; break; case 'a': /* WARNING! DANGER! DANGER! DANGER! */ *op++ = '\7'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { /* octal constant */ register int digits; val = 0; (void) sscanf(s, "%3o", &val); *op++ = val; for (digits = 3; s[1] && (int)index("01234567", s[1]) && --digits > 0; s++); } break; case 'x': /* hex constant */ s++; { register int digits; val = 0; (void) sscanf(s, "%3x", &val); *op++ = val; for (digits = 3; *s && s[1] && (int)index("0123456789abcdefABCDEF", s[1]) && --digits > 0; s++); } break; } else *op++ = *s; out: *op = '\0'; } X/****************************************************************************/ //go.sysin dd * if [ `wc -c < printf.c` != 4776 ]; then made=FALSE echo error transmitting printf.c -- echo length should be 4776, not `wc -c < printf.c` else made=TRUE fi if [ $made = TRUE ]; then chmod 644 printf.c echo -n ' '; ls -ld printf.c fi . . . . . . . . . . . . . CUT ON DOTTED LINE . . . . . . . . . . . . . -- Fred Blonder (301) 454-7690 seismo!mimsy!fred Fred@Mimsy.umd.edu
black@masscomp.UUCP (Sam Black) (01/12/87)
In article <9@gyre.UUCP> fred@gyre.UUCP (Fred Blonder) writes: >Here's a handy little program which works sort of like echo, except >that it does formatting in the manner of printf. In fact, it uses >printf to do its formatting, so you have the full functionality of >printf available at the shell level. You can do the same thing with AWK, and it works on all machines that have AWK. With C shell, do something like: alias printf "echo ' ' | awk '{ printf(\!*) }'" You can probably define a function in Bourne shell to do the same thing, too. ---------------------------------------------- There are only two kinds of planes in the world: Fighters and targets. - 1Lt. Steven Brown, 63 TFTS ...!{decvax,allegra,harvard,seismo,unc,ihnp4}!masscomp!black ----------------------------------------------
jvb@duke.UUCP (Jack V. Briner, Jr.) (01/14/87)
In a series of articles people have been working on how to get a printf function for the shell. I would like to refine Sam Black's so that it avoids an unnecessary piped command. Try: alias printf "awk 'BEGIN {printf(\!*); exit;}'" This will have to be modified if your history character is a "!". If your history character is a "," you will have to be careful about its usage.