home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / sh-utils-1.12-src.tgz / tar.out / fsf / sh-utils / src / printf.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  11KB  |  532 lines

  1. /* printf - format and print data
  2.    Copyright (C) 90, 91, 92, 93, 1994 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Usage: printf format [argument...]
  19.  
  20.    A front end to the printf function that lets it be used from the shell.
  21.  
  22.    Backslash escapes:
  23.  
  24.    \" = double quote
  25.    \\ = backslash
  26.    \a = alert (bell)
  27.    \b = backspace
  28.    \c = produce no further output
  29.    \f = form feed
  30.    \n = new line
  31.    \r = carriage return
  32.    \t = horizontal tab
  33.    \v = vertical tab
  34.    \0ooo = octal number (ooo is 0 to 3 digits)
  35.    \xhhh = hexadecimal number (hhh is 1 to 3 digits)
  36.  
  37.    Additional directive:
  38.  
  39.    %b = print an argument string, interpreting backslash escapes
  40.  
  41.    The `format' argument is re-used as many times as necessary
  42.    to convert all of the given arguments.
  43.  
  44.    David MacKenzie <djm@gnu.ai.mit.edu> */
  45.  
  46. #include <config.h>
  47. #include <stdio.h>
  48. #include <sys/types.h>
  49. #include <getopt.h>
  50.  
  51. #include "system.h"
  52. #include "version.h"
  53. #include "long-options.h"
  54.  
  55. #ifndef STDC_HEADERS
  56. double strtod ();
  57. long strtol ();
  58. unsigned long strtoul ();
  59. #endif
  60.  
  61. #define isodigit(c) ((c) >= '0' && (c) <= '7')
  62. #define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
  63. #define octtobin(c) ((c) - '0')
  64.  
  65. char *xmalloc ();
  66. void error ();
  67.  
  68. static double xstrtod ();
  69. static int print_esc ();
  70. static int print_formatted ();
  71. static long xstrtol ();
  72. static unsigned long xstrtoul ();
  73. static void print_direc ();
  74. static void print_esc_char ();
  75. static void print_esc_string ();
  76. static void verify ();
  77.  
  78. /* The value to return to the calling program.  */
  79. static int exit_status;
  80.  
  81. /* The name this program was run with. */
  82. char *program_name;
  83.  
  84. static void
  85. usage (status)
  86.      int status;
  87. {
  88.   if (status != 0)
  89.     fprintf (stderr, "Try `%s --help' for more information.\n",
  90.          program_name);
  91.   else
  92.     {
  93.       printf ("\
  94. Usage: %s FORMAT [ARGUMENT]...\n\
  95.   or:  %s OPTION\n\
  96. ",
  97.           program_name, program_name);
  98.       printf ("\
  99. \n\
  100.   --help      display this help and exit\n\
  101.   --version   output version information and exit\n\
  102. \n\
  103. FORMAT controls the output as in C printf.  Interpreted sequences are:\n\
  104. \n\
  105.   \\\"      double quote\n\
  106.   \\0NNN   character with octal value NNN (0 to 3 digits)\n\
  107.   \\\\      backslash\n\
  108.   \\a      alert (BEL)\n\
  109.   \\b      backspace\n\
  110.   \\c      produce no further output\n\
  111.   \\f      form feed\n\
  112.   \\n      new line\n\
  113.   \\r      carriage return\n\
  114.   \\t      horizontal tab\n\
  115.   \\v      vertical tab\n\
  116.   \\xNNN   character with hexadecimal value NNN (1 to 3 digits)\n\
  117. \n\
  118.   %%%%      a single %%\n\
  119.   %%b      ARGUMENT as a string with `\\' escapes interpreted\n\
  120. \n\
  121. and all C format specifications ending with one of diouxXfeEgGcs, with\n\
  122. ARGUMENTs converted to proper type first.  Variable widths are handled.\n\
  123. ");
  124.     }
  125.   exit (status);
  126. }
  127.  
  128. main (argc, argv)
  129.      int argc;
  130.      char **argv;
  131. {
  132.   char *format;
  133.   int args_used;
  134.  
  135.   program_name = argv[0];
  136.   exit_status = 0;
  137.  
  138.   parse_long_options (argc, argv, "printf", version_string, usage);
  139.  
  140.   if (argc == 1)
  141.     {
  142.       fprintf (stderr, "Usage: %s format [argument...]\n", program_name);
  143.       exit (1);
  144.     }
  145.  
  146.   format = argv[1];
  147.   argc -= 2;
  148.   argv += 2;
  149.  
  150.   do
  151.     {
  152.       args_used = print_formatted (format, argc, argv);
  153.       argc -= args_used;
  154.       argv += args_used;
  155.     }
  156.   while (args_used > 0 && argc > 0);
  157.  
  158.   exit (exit_status);
  159. }
  160.  
  161. /* Print the text in FORMAT, using ARGV (with ARGC elements) for
  162.    arguments to any `%' directives.
  163.    Return the number of elements of ARGV used.  */
  164.  
  165. static int
  166. print_formatted (format, argc, argv)
  167.      char *format;
  168.      int argc;
  169.      char **argv;
  170. {
  171.   int save_argc = argc;        /* Preserve original value.  */
  172.   char *f;            /* Pointer into `format'.  */
  173.   char *direc_start;        /* Start of % directive.  */
  174.   size_t direc_length;        /* Length of % directive.  */
  175.   int field_width;        /* Arg to first '*', or -1 if none.  */
  176.   int precision;        /* Arg to second '*', or -1 if none.  */
  177.  
  178.   for (f = format; *f; ++f)
  179.     {
  180.       switch (*f)
  181.     {
  182.     case '%':
  183.       direc_start = f++;
  184.       direc_length = 1;
  185.       field_width = precision = -1;
  186.       if (*f == '%')
  187.         {
  188.           putchar ('%');
  189.           break;
  190.         }
  191.       if (*f == 'b')
  192.         {
  193.           if (argc > 0)
  194.         {
  195.           print_esc_string (*argv);
  196.           ++argv;
  197.           --argc;
  198.         }
  199.           break;
  200.         }
  201.       if (index ("-+ #", *f))
  202.         {
  203.           ++f;
  204.           ++direc_length;
  205.         }
  206.       if (*f == '*')
  207.         {
  208.           ++f;
  209.           ++direc_length;
  210.           if (argc > 0)
  211.         {
  212.           field_width = xstrtoul (*argv);
  213.           ++argv;
  214.           --argc;
  215.         }
  216.           else
  217.         field_width = 0;
  218.         }
  219.       else
  220.         while (ISDIGIT (*f))
  221.           {
  222.         ++f;
  223.         ++direc_length;
  224.           }
  225.       if (*f == '.')
  226.         {
  227.           ++f;
  228.           ++direc_length;
  229.           if (*f == '*')
  230.         {
  231.           ++f;
  232.           ++direc_length;
  233.           if (argc > 0)
  234.             {
  235.               precision = xstrtoul (*argv);
  236.               ++argv;
  237.               --argc;
  238.             }
  239.           else
  240.             precision = 0;
  241.         }
  242.           else
  243.         while (ISDIGIT (*f))
  244.           {
  245.             ++f;
  246.             ++direc_length;
  247.           }
  248.         }
  249.       if (*f == 'l' || *f == 'L' || *f == 'h')
  250.         {
  251.           ++f;
  252.           ++direc_length;
  253.         }
  254.       if (!index ("diouxXfeEgGcs", *f))
  255.         error (1, 0, "%%%c: invalid directive", *f);
  256.       ++direc_length;
  257.       if (argc > 0)
  258.         {
  259.           print_direc (direc_start, direc_length, field_width,
  260.                precision, *argv);
  261.           ++argv;
  262.           --argc;
  263.         }
  264.       else
  265.         print_direc (direc_start, direc_length, field_width,
  266.              precision, "");
  267.       break;
  268.  
  269.     case '\\':
  270.       f += print_esc (f);
  271.       break;
  272.  
  273.     default:
  274.       putchar (*f);
  275.     }
  276.     }
  277.  
  278.   return save_argc - argc;
  279. }
  280.  
  281. /* Print a \ escape sequence starting at ESCSTART.
  282.    Return the number of characters in the escape sequence
  283.    besides the backslash. */
  284.  
  285. static int
  286. print_esc (escstart)
  287.      char *escstart;
  288. {
  289.   register char *p = escstart + 1;
  290.   int esc_value = 0;        /* Value of \nnn escape. */
  291.   int esc_length;        /* Length of \nnn escape. */
  292.  
  293.   /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
  294.   if (*p == 'x')
  295.     {
  296.       for (esc_length = 0, ++p;
  297.        esc_length < 3 && ISXDIGIT (*p);
  298.        ++esc_length, ++p)
  299.     esc_value = esc_value * 16 + hextobin (*p);
  300.       if (esc_length == 0)
  301.     error (1, 0, "missing hexadecimal number in escape");
  302.       putchar (esc_value);
  303.     }
  304.   else if (*p == '0')
  305.     {
  306.       for (esc_length = 0, ++p;
  307.        esc_length < 3 && isodigit (*p);
  308.        ++esc_length, ++p)
  309.     esc_value = esc_value * 8 + octtobin (*p);
  310.       putchar (esc_value);
  311.     }
  312.   else if (index ("\"\\abcfnrtv", *p))
  313.     print_esc_char (*p++);
  314.   else
  315.     error (1, 0, "\\%c: invalid escape", *p);
  316.   return p - escstart - 1;
  317. }
  318.  
  319. /* Output a single-character \ escape.  */
  320.  
  321. static void
  322. print_esc_char (c)
  323.      char c;
  324. {
  325.   switch (c)
  326.     {
  327.     case 'a':            /* Alert. */
  328.       putchar (7);
  329.       break;
  330.     case 'b':            /* Backspace. */
  331.       putchar (8);
  332.       break;
  333.     case 'c':            /* Cancel the rest of the output. */
  334.       exit (0);
  335.       break;
  336.     case 'f':            /* Form feed. */
  337.       putchar (12);
  338.       break;
  339.     case 'n':            /* New line. */
  340.       putchar (10);
  341.       break;
  342.     case 'r':            /* Carriage return. */
  343.       putchar (13);
  344.       break;
  345.     case 't':            /* Horizontal tab. */
  346.       putchar (9);
  347.       break;
  348.     case 'v':            /* Vertical tab. */
  349.       putchar (11);
  350.       break;
  351.     default:
  352.       putchar (c);
  353.       break;
  354.     }
  355. }
  356.  
  357. /* Print string STR, evaluating \ escapes. */
  358.  
  359. static void
  360. print_esc_string (str)
  361.      char *str;
  362. {
  363.   for (; *str; str++)
  364.     if (*str == '\\')
  365.       str += print_esc (str);
  366.     else
  367.       putchar (*str);
  368. }
  369.  
  370. /* Output a % directive.  START is the start of the directive,
  371.    LENGTH is its length, and ARGUMENT is its argument.
  372.    If FIELD_WIDTH or PRECISION is non-negative, they are args for
  373.    '*' values in those fields. */
  374.  
  375. static void
  376. print_direc (start, length, field_width, precision, argument)
  377.      char *start;
  378.      size_t length;
  379.      int field_width;
  380.      int precision;
  381.      char *argument;
  382. {
  383.   char *p;        /* Null-terminated copy of % directive. */
  384.  
  385.   p = xmalloc ((unsigned) (length + 1));
  386.   strncpy (p, start, length);
  387.   p[length] = 0;
  388.  
  389.   switch (p[length - 1])
  390.     {
  391.     case 'd':
  392.     case 'i':
  393.       if (field_width < 0)
  394.     {
  395.       if (precision < 0)
  396.         printf (p, xstrtol (argument));
  397.       else
  398.         printf (p, precision, xstrtol (argument));
  399.     }
  400.       else
  401.     {
  402.       if (precision < 0)
  403.         printf (p, field_width, xstrtol (argument));
  404.       else
  405.         printf (p, field_width, precision, xstrtol (argument));
  406.     }
  407.       break;
  408.  
  409.     case 'o':
  410.     case 'u':
  411.     case 'x':
  412.     case 'X':
  413.       if (field_width < 0)
  414.     {
  415.       if (precision < 0)
  416.         printf (p, xstrtoul (argument));
  417.       else
  418.         printf (p, precision, xstrtoul (argument));
  419.     }
  420.       else
  421.     {
  422.       if (precision < 0)
  423.         printf (p, field_width, xstrtoul (argument));
  424.       else
  425.         printf (p, field_width, precision, xstrtoul (argument));
  426.     }
  427.       break;
  428.  
  429.     case 'f':
  430.     case 'e':
  431.     case 'E':
  432.     case 'g':
  433.     case 'G':
  434.       if (field_width < 0)
  435.     {
  436.       if (precision < 0)
  437.         printf (p, xstrtod (argument));
  438.       else
  439.         printf (p, precision, xstrtod (argument));
  440.     }
  441.       else
  442.     {
  443.       if (precision < 0)
  444.         printf (p, field_width, xstrtod (argument));
  445.       else
  446.         printf (p, field_width, precision, xstrtod (argument));
  447.     }
  448.       break;
  449.  
  450.     case 'c':
  451.       printf (p, *argument);
  452.       break;
  453.  
  454.     case 's':
  455.       if (field_width < 0)
  456.     {
  457.       if (precision < 0)
  458.         printf (p, argument);
  459.       else
  460.         printf (p, precision, argument);
  461.     }
  462.       else
  463.     {
  464.       if (precision < 0)
  465.         printf (p, field_width, argument);
  466.       else
  467.         printf (p, field_width, precision, argument);
  468.     }
  469.       break;
  470.     }
  471.  
  472.   free (p);
  473. }
  474.  
  475. static unsigned long
  476. xstrtoul (s)
  477.      char *s;
  478. {
  479.   char *end;
  480.   unsigned long val;
  481.  
  482.   errno = 0;
  483.   val = strtoul (s, &end, 0);
  484.   verify (s, end);
  485.   return val;
  486. }
  487.  
  488. static long
  489. xstrtol (s)
  490.      char *s;
  491. {
  492.   char *end;
  493.   long val;
  494.  
  495.   errno = 0;
  496.   val = strtol (s, &end, 0);
  497.   verify (s, end);
  498.   return val;
  499. }
  500.  
  501. static double
  502. xstrtod (s)
  503.      char *s;
  504. {
  505.   char *end;
  506.   double val;
  507.  
  508.   errno = 0;
  509.   val = strtod (s, &end);
  510.   verify (s, end);
  511.   return val;
  512. }
  513.  
  514. static void
  515. verify (s, end)
  516.      char *s, *end;
  517. {
  518.   if (errno)
  519.     {
  520.       error (0, errno, "%s", s);
  521.       exit_status = 1;
  522.     }
  523.   else if (*end)
  524.     {
  525.       if (s == end)
  526.     error (0, 0, "%s: expected a numeric value", s);
  527.       else
  528.     error (0, 0, "%s: value not completely converted", s);
  529.       exit_status = 1;
  530.     }
  531. }
  532.