home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume9 / printf < prev    next >
Text File  |  1987-03-11  |  12KB  |  594 lines

  1. Subject:  v09i023:  Printf(1), for shell scripts
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.TMC.COM
  4.  
  5. Submitted by: Fred Blonder <fred@mimsy.umd.edu>
  6. Mod.sources: Volume 9, Issue 23
  7. Archive-name: printf
  8.  
  9. Here's a transportable version of the ``printf'' program.
  10. [  I wrote the Makefile and spliced it into the shar.  This program isn't
  11.    as much fun as Fred's original one, which ended with a call to
  12.    "printf()" but it is more transportable....  --r$  ]
  13.  
  14. ================================ CUT HERE ===============================
  15. : Run this shell script with "sh" not "csh"
  16. PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH
  17. export PATH
  18. all=FALSE
  19. sed 's/^X//' <<'//go.sysin dd *' >Makefile
  20. # Quickie makefile
  21. CFLAGS    = -C
  22. printf:        printf.c
  23.     $(CC) $(CFLAGS) -o printf printf.c
  24. # Edit appropriately
  25. DESTPROG=/usr/local/bin/printf
  26. DESTMAN =/usr/man/man1/printf.1
  27. install:    printf
  28.     cp printf $(DESTPROG)
  29.     chmod 755 $(DESTPROG)
  30.     strip $(DESTPROG)
  31.     cp printf.1 $(DESTMAN)
  32.     chmod 444 $(DESTMAN)
  33. //go.sysin dd *
  34. if [ x$1 = x-a ]; then
  35.     all=TRUE
  36. fi
  37. echo Extracting printf.1
  38. sed 's/^X//' <<'//go.sysin dd *' >printf.1
  39. X.\"    @(#)printf.1    8-Jan-1987
  40. X.\"
  41. X.TH PRINTF 1 "8-Jan-1987"
  42. X.AT 3
  43. X.SH NAME
  44. printf \- formatted print at shell command level
  45. X.SH SYNOPSIS
  46. X.B "printf <format-string>"
  47. [
  48. X.B arg1
  49. ] [
  50. X.B arg2
  51. ] ...
  52. X.SH DESCRIPTION
  53. X.I Printf
  54. duplicates \- as far as possible \- the standard C library routine of the
  55. same name, at the shell command level. It is similar to
  56. X.I echo,
  57. except that it formats its arguments according to conversion specifications
  58. given in the
  59. X.B format-string,
  60. before writing them to the standard output.
  61. XFor a thorough explanation of format specifications, see the manual entry
  62. for the printf subroutine.
  63. X.PP
  64. XFor the sake of perversity,
  65. X.I printf
  66. implements one format conversion
  67. X.B not
  68. supported by the standard printf subroutine: the %r conversion, which prints
  69. integers as Roman numerals.
  70. X.PP
  71. As a convenience, within the
  72. X.I format-string
  73. and any string or character arguments,
  74. X.I printf
  75. converts "backslash notation" \- as defined in the ANSII draft C
  76. standard \- into the appropriate control characters.
  77. X.SH EXAMPLES
  78. X.nf
  79. X.na
  80. X.sp 2
  81. % printf 'Today is %s the %d of %s.\\n' Monday 1 April
  82. Today is Monday the 1 of April.
  83. X.sp 3
  84. % printf 'Interesting Numbers\\n\\n\\tPie: %*.*f\\n\\tFoo: %g\\n' \\
  85.     6 4 3.1415927 42
  86. Interesting Numbers
  87.  
  88.     Pie: 3.1416
  89.     Foo: 42
  90. X.sp 3
  91. % printf '%s %d, %r\\n' July 4 1776
  92. July 4, MCCCCCCCLXXVI
  93. X.sp 2
  94. X.fi
  95. X.ad
  96. X.SH AUTHOR
  97. XFred Blonder <fred@Mimsy.umd.edu>
  98. X.SH "SEE ALSO"
  99. echo(1), printf(3)
  100.  
  101. //go.sysin dd *
  102. if [ `wc -c < printf.1` != 1420 ]; then
  103.     made=FALSE
  104.     echo error transmitting printf.1 --
  105.     echo length should be 1420, not `wc -c < printf.1`
  106. else
  107.     made=TRUE
  108. fi
  109. if [ $made = TRUE ]; then
  110.     chmod 644 printf.1
  111.     echo -n '    '; ls -ld printf.1
  112. fi
  113. echo Extracting printf.c
  114. sed 's/^X//' <<'//go.sysin dd *' >printf.c
  115. #ifndef lint
  116. static char sccsid[] = "@(#)printf.c    (U of Maryland) FLB 6-Jan-1987";
  117. static char RCSid[] = "@(#)$Header: printf.c,v 1.4 87/01/29 20:52:30 fred Exp $";
  118. #endif
  119.  
  120. X/*
  121.  * Printf - Duplicate the C library routine of the same name, but from
  122.  *        the shell command level.
  123.  *
  124.  * Fred Blonder <fred@Mimsy.umd.edu>
  125.  *
  126.  * To Compile:
  127.  %    cc -s -O printf.c -o printf
  128.  *
  129.  * $Log:    printf.c,v $
  130.  * Revision 1.4  87/01/29  20:52:30  fred
  131.  * Re-installed backslash-notation conversion for string & char arguments.
  132.  * 
  133.  * Revision 1.3  87/01/29  20:44:23  fred
  134.  * Converted to portable algorithm.
  135.  * Added Roman format for integers.
  136.  *     29-Jan-87  FLB
  137.  * 
  138.  * Revision 1.2  87/01/09  19:10:57  fred
  139.  * Fixed bug in argument-count error-checking.
  140.  * Changed backslash escapes within strings to correspond to ANSII C
  141.  *     draft standard.  (9-Jan-87 FLB)
  142.  * 
  143.  */
  144.  
  145. #include <stdio.h>
  146. #include <sysexits.h>
  147.  
  148. X/****************************************************************************/
  149.  
  150. main(argc, argv)
  151. int argc;
  152. char *argv[];
  153. {
  154. register char *cp, *conv_spec, **argp, **ep;
  155. char *strncpy(), *index(), *ctor();
  156. double atof();
  157.  
  158. if (argc < 2) {
  159.     fprintf(stderr,
  160.         "printf: Usage: printf <format-string> [ arg1 . . . ]\n");
  161.     exit(EX_USAGE);
  162.     }
  163.  
  164. argp = &argv[2];    /* Point at first arg (if any) beyond format string. */
  165. ep = &argv[argc];    /* Point beyond last arg. */
  166.  
  167. ctrl(argv[1]);    /* Change backslash notation to control chars in fmt string. */
  168.  
  169. X/* Scan format string for conversion specifications, and do appropriate
  170.    conversion on the corresponding argument. */
  171. for (cp = argv[1]; *cp; cp++) {
  172. register int dynamic_count;
  173.  
  174.     /* Look for next conversion spec. */
  175.     while (*cp && *cp != '%') {
  176.         putchar(*cp++);
  177.         }
  178.  
  179.     if (!*cp)    /* End of format string */
  180.         break;
  181.         
  182.     dynamic_count = 0;    /* Begin counting dynamic field width specs. */
  183.     conv_spec = cp++;    /* Remember where this conversion begins. */
  184.  
  185.     for (;*cp; cp++) {    /* Scan until conversion character. */
  186.         char conv_buf[BUFSIZ];    /* Save conversion string here. */
  187.         register int conv_len;    /* Length of ``conv_buf''. */
  188.  
  189.         switch (*cp) {    /* Field-width spec.: Keep scanning. */
  190.             case '.': case '0': case '1': case '2': case '3':
  191.             case '4': case '5': case '6': case '7': case '8':
  192.             case '9':
  193.                 continue;
  194.  
  195.             case '*':    /* Dynamic field-width spec */
  196.                 dynamic_count++;
  197.                 continue;
  198.  
  199.             case 's':    /* String */
  200.                 if (&argp[dynamic_count] >= ep) {
  201.                     fprintf(stderr,
  202.                     "printf: Not enough args for format.\n"
  203.                         );
  204.                     exit(EX_USAGE);
  205.                     }
  206.  
  207.                 (void) strncpy(conv_buf, conv_spec,
  208.                     conv_len = cp - conv_spec + 1);
  209.                 conv_buf[conv_len] = '\0';
  210.  
  211.                 switch (dynamic_count) {
  212.                     case 0:
  213.                         ctrl(*argp);
  214.                         printf(conv_buf, *argp++);
  215.                         break;
  216.  
  217.                     case 1:
  218.                         {
  219.                         register int a1;
  220.  
  221.                         a1 = atoi(*argp++);
  222.                         ctrl(*argp);
  223.                         printf(conv_buf, a1, *argp++);
  224.                         }
  225.                         break;
  226.  
  227.                     case 2:
  228.                         {
  229.                         register int a1, a2;
  230.  
  231.                         a1 = atoi(*argp++);
  232.                         a2 = atoi(*argp++);
  233.                         ctrl(*argp);
  234.                         printf(conv_buf, a1, a2, *argp++);
  235.                         }
  236.                         break;
  237.  
  238.                     }
  239.                 goto out;
  240.  
  241.             case 'c':    /* Char */
  242.                 if (&argp[dynamic_count] >= ep) {
  243.                     fprintf(stderr,
  244.                     "printf: Not enough args for format.\n"
  245.                         );
  246.                     exit(EX_USAGE);
  247.                     }
  248.  
  249.                 (void) strncpy(conv_buf, conv_spec,
  250.                     conv_len = cp - conv_spec + 1);
  251.                 conv_buf[conv_len] = '\0';
  252.  
  253.                 switch (dynamic_count) {
  254.                     case 0:
  255.                         ctrl(*argp);
  256.                         printf(conv_buf, **argp++);
  257.                         break;
  258.  
  259.                     case 1:
  260.                         {
  261.                         register int a1;
  262.  
  263.                         a1 = atoi(*argp++);
  264.                         ctrl(*argp);
  265.                         printf(conv_buf, a1, **argp++);
  266.                         }
  267.                         break;
  268.  
  269.                     case 2:
  270.                         {
  271.                         register int a1, a2;
  272.  
  273.                         a1 = atoi(*argp++);
  274.                         a2 = atoi(*argp++);
  275.                         ctrl(*argp);
  276.                         printf(conv_buf, a1, a2, **argp++);
  277.                         }
  278.                         break;
  279.                     }
  280.                 goto out;
  281.  
  282.             case 'd':    /* Integer */
  283.             case 'o':
  284.             case 'x':
  285.             case 'u':
  286.                 if (&argp[dynamic_count] >= ep) {
  287.                     fprintf(stderr,
  288.                     "printf: Not enough args for format.\n"
  289.                         );
  290.                     exit(EX_USAGE);
  291.                     }
  292.  
  293.                 (void) strncpy(conv_buf, conv_spec,
  294.                     conv_len = cp - conv_spec + 1);
  295.                 conv_buf[conv_len] = '\0';
  296.  
  297.                 switch (dynamic_count) {
  298.                     case 0:
  299.                         printf(conv_buf, atoi(*argp++));
  300.                         break;
  301.  
  302.                     case 1:
  303.                         {
  304.                         register int a1;
  305.  
  306.                         a1 = atoi(*argp++);
  307.                         printf(conv_buf, a1, atoi(*argp++));
  308.                         }
  309.                         break;
  310.  
  311.                     case 2:
  312.                         {
  313.                         register int a1, a2;
  314.  
  315.                         a1 = atoi(*argp++);
  316.                         a2 = atoi(*argp++);
  317.                         printf(conv_buf, a1, a2, atoi(*argp++));
  318.                         }
  319.                         break;
  320.  
  321.                     }
  322.                 goto out;
  323.  
  324.             case 'f':    /* Real */
  325.             case 'e':
  326.             case 'g':
  327.                 if (&argp[dynamic_count] >= ep) {
  328.                     fprintf(stderr,
  329.                     "printf: Not enough args for format.\n"
  330.                         );
  331.                     exit(EX_USAGE);
  332.                     }
  333.  
  334.                 (void) strncpy(conv_buf, conv_spec,
  335.                     conv_len = cp - conv_spec + 1);
  336.                 conv_buf[conv_len] = '\0';
  337.  
  338.                 switch (dynamic_count) {
  339.                     case 0:
  340.                         printf(conv_buf, atof(*argp++));
  341.                         break;
  342.  
  343.                     case 1:
  344.                         {
  345.                         register int a1;
  346.  
  347.                         a1 = atoi(*argp++);
  348.                         printf(conv_buf, a1, atof(*argp++));
  349.                         }
  350.                         break;
  351.  
  352.                     case 2:
  353.                         {
  354.                         register int a1, a2;
  355.  
  356.                         a1 = atoi(*argp++);
  357.                         a2 = atoi(*argp++);
  358.                         printf(conv_buf, a1, a2, atof(*argp++));
  359.                         }
  360.                         break;
  361.  
  362.                     }
  363.                 goto out;
  364.  
  365.             case 'r':    /* Roman (Well, why not?) */
  366.                 if (&argp[dynamic_count] >= ep) {
  367.                     fprintf(stderr,
  368.                     "printf: Not enough args for format.\n"
  369.                         );
  370.                     exit(EX_USAGE);
  371.                     }
  372.  
  373.                 (void) strncpy(conv_buf, conv_spec,
  374.                     conv_len = cp - conv_spec + 1);
  375.                 conv_buf[conv_len] = '\0';
  376.                 conv_buf[conv_len - 1] = 's';
  377.  
  378.                 switch (dynamic_count) {
  379.                     case 0:
  380.                         printf(conv_buf,
  381.                             ctor(atoi(*argp++)));
  382.                         break;
  383.  
  384.                     case 1:
  385.                         {
  386.                         register int a1;
  387.  
  388.                         a1 = atoi(*argp++);
  389.                         printf(conv_buf, a1,
  390.                             ctor(atoi(*argp++)));
  391.                         }
  392.                         break;
  393.  
  394.                     case 2:
  395.                         {
  396.                         register int a1, a2;
  397.  
  398.                         a1 = atoi(*argp++);
  399.                         a2 = atoi(*argp++);
  400.                         printf(conv_buf, a1, a2,
  401.                             ctor(atoi(*argp++)));
  402.                         }
  403.                         break;
  404.  
  405.                     }
  406.                 goto out;
  407.  
  408.             case '%':    /* Boring */
  409.                 putchar('%');
  410.                 break;
  411.  
  412.             default:    /* Probably an error, but let user
  413.                        have his way. */
  414.                 continue;
  415.             }
  416.         }
  417.     out: ;
  418.     }
  419.  
  420. exit(EX_OK);
  421. }
  422.  
  423. X/****************************************************************************/
  424.  
  425. X/* Convert backslash notation to control characters, in place. */
  426.  
  427. ctrl(s)
  428. register char *s;
  429. {
  430. register char *op;
  431. static int val;
  432.  
  433. for (op = s; *s; s++)
  434.     if (*s == '\\')
  435.         switch (*++s) {
  436.             case '\0':    /* End-of-string: user goofed */
  437.                 goto out;
  438.  
  439.             case '\\':    /* Backslash */
  440.                 *op++ = '\\';
  441.                 break;
  442.  
  443.             case 'n':    /* newline */
  444.                 *op++ = '\n';
  445.                 break;
  446.  
  447.             case 't':    /* horizontal tab */
  448.                 *op++ = '\t';
  449.                 break;
  450.  
  451.             case 'r':    /* carriage-return */
  452.                 *op++ = '\r';
  453.                 break;
  454.  
  455.             case 'f':    /* form-feed */
  456.                 *op++ = '\f';
  457.                 break;
  458.  
  459.             case 'b':    /* backspace */
  460.                 *op++ = '\b';
  461.                 break;
  462.  
  463.             case 'v':    /* vertical tab */
  464.                 *op++ = '\13';
  465.                 break;
  466.  
  467.             case 'a':    /* WARNING! DANGER! DANGER! DANGER! */
  468.                 *op++ = '\7';
  469.                 break;
  470.  
  471.             case '0': case '1': case '2': case '3':
  472.             case '4': case '5': case '6': case '7':
  473.                 {    /* octal constant */
  474.                 register int digits;
  475.  
  476.                 val = 0;
  477.                 (void) sscanf(s, "%3o", &val);
  478.                 *op++ = val;
  479.                 for (digits = 3; s[1] &&
  480.                     (int)index("01234567", s[1])
  481.                     && --digits > 0;
  482.                         s++);
  483.                 }
  484.                 break;
  485.  
  486.             case 'x':    /* hex constant */
  487.                 s++;
  488.                 {
  489.                 register int digits;
  490.  
  491.                 val = 0;
  492.                 (void) sscanf(s, "%3x", &val);
  493.                 *op++ = val;
  494.                 for (digits = 3; *s && s[1] &&
  495.                     (int)index("0123456789abcdefABCDEF",
  496.                                     s[1])
  497.                     && --digits > 0;
  498.                         s++);
  499.                 }
  500.                 break;
  501.  
  502.             }
  503.     else
  504.         *op++ = *s;
  505.  
  506. out:
  507.  
  508. *op = '\0';
  509. }
  510.  
  511. X/****************************************************************************/
  512.  
  513. X/* Convert integer to Roman Numerals. (Have have you survived without it?) */
  514.  
  515. struct roman {
  516.     unsigned r_mag;
  517.     char r_units, r_fives;
  518.     } roman[] = {
  519.         1000, 'M', '\0',
  520.          100, 'C', '\0',
  521.           10, 'X', 'L',
  522.            1, 'I', 'V'
  523.         };
  524.  
  525. char *
  526. ctor(x)
  527. register int x;
  528. {
  529. register struct roman *mp;
  530. static char buf[BUFSIZ];
  531. register char *cp = buf;
  532.  
  533. X/* I've never actually seen a roman numeral with a minus-sign.
  534.    Probably ought to print out some appropriate latin phrase instead. */
  535. if (x < 0) {
  536.     *cp++ = '-';
  537.     x = -x;
  538.     }
  539.  
  540. for (mp = roman; x; mp++) {
  541.     register unsigned units;
  542.  
  543.     units = x / mp->r_mag;
  544.     x = x % mp->r_mag;
  545.  
  546.     if (cp > &buf[BUFSIZ-2])
  547.         return "???";
  548.  
  549.     if (units == 9 && mp > roman) {    /* Do inverse notation: Eg: ``IX''. */
  550.         *cp++ = mp->r_units;
  551.         *cp++ = mp[-1].r_units;
  552.         }
  553.     else if (units == 4 && mp->r_fives) {
  554.         /* Inverse notation for half-decades: Eg: ``IV'' */
  555.         *cp++ = mp->r_units;
  556.         *cp++ = mp->r_fives;
  557.         }
  558.     else {    /* Additive notation */
  559.         if (units >= 5 && mp->r_fives) {
  560.             *cp++ = mp->r_fives;
  561.             units -= 5;
  562.             }
  563.         while (units--) {
  564.             *cp++ = mp->r_units;
  565.             if (cp > &buf[BUFSIZ-5])
  566.                 return "???";
  567.             }
  568.         }
  569.     }
  570.  
  571. *cp = '\0';
  572.  
  573. return buf;
  574. }
  575.  
  576. X/****************************************************************************/
  577. //go.sysin dd *
  578. if [ `wc -c < printf.c` != 9057 ]; then
  579.     made=FALSE
  580.     echo error transmitting printf.c --
  581.     echo length should be 9057, not `wc -c < printf.c`
  582. else
  583.     made=TRUE
  584. fi
  585. if [ $made = TRUE ]; then
  586.     chmod 644 printf.c
  587.     echo -n '    '; ls -ld printf.c
  588. fi
  589. -- 
  590.                     Fred Blonder (301) 454-7690
  591.                     seismo!mimsy!fred
  592.                     Fred@Mimsy.umd.edu
  593.  
  594.