home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / gettext-0.10.24-src.tgz / tar.out / fsf / gettext / lib / printf-parse.h < prev    next >
C/C++ Source or Header  |  1996-09-28  |  10KB  |  421 lines

  1. /* Internal header for parsing printf format strings.
  2.    Copyright (C) 1995, 1996 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  17.  
  18. /* We use some extension so define this here.  */
  19. #define _GNU_SOURCE    1
  20.  
  21. #include <ctype.h>
  22. #include <printf.h>
  23. #ifdef STDC_HEADERS
  24. # include <stddef.h>
  25. #endif
  26.  
  27. #if STDC_HEADERS || HAVE_STRING_H
  28. # include <string.h>
  29. #else
  30. # include <strings.h>
  31. #endif
  32.  
  33. #if defined __GNUC__ && __GNUC__ >= 2
  34. # define long_long_int long long int
  35. # define long_double long double
  36. #else
  37. # define long_long_int long
  38. # define long_double double
  39. #endif
  40.  
  41. #ifndef MB_CUR_MAX
  42. # define MB_CUR_MAX (sizeof (long))
  43. #endif
  44.  
  45. #define NDEBUG 1
  46. #include <assert.h>
  47.  
  48. #ifndef MAX
  49. # if defined __GNU__ && __GNUC__ >= 2
  50. #  define MAX(a,b)    ({typeof(a) _a = (a); typeof(b) _b = (b);          \
  51.               _a > _b ? _a : _b; })
  52. # else
  53. #  define MAX(a,b)      ((a) > (b) ? (a) : (b))
  54. # endif
  55. #endif
  56. #ifndef MIN
  57. # if defined __GNU__ && __GNUC__ >= 2
  58. #  define MIN(a,b)    ({typeof(a) _a = (a); typeof(b) _b = (b);          \
  59.               _a < _b ? _a : _b; })
  60. # else
  61. #  define MIN(a,b)      ((a) < (b) ? (a) : (b))
  62. # endif
  63. #endif
  64.  
  65. struct printf_spec
  66.   {
  67.     /* Information parsed from the format spec.  */
  68.     struct printf_info info;
  69.  
  70.     /* Pointers into the format string for the end of this format
  71.        spec and the next (or to the end of the string if no more).  */
  72.     const char *end_of_fmt, *next_fmt;
  73.  
  74.     /* Position of arguments for precision and width, or -1 if `info' has
  75.        the constant value.  */
  76.     int prec_arg, width_arg;
  77.  
  78.     int data_arg;        /* Position of data argument.  */
  79.     int data_arg_type;        /* Type of first argument.  */
  80.     /* Number of arguments consumed by this format specifier.  */
  81.     size_t ndata_args;
  82.   };
  83.  
  84.  
  85. /* The various kinds off arguments that can be passed to printf.  */
  86. union printf_arg
  87.   {
  88.     unsigned char pa_char;
  89.     short int pa_short_int;
  90.     int pa_int;
  91.     long int pa_long_int;
  92.     long_long_int pa_long_long_int;
  93.     unsigned short int pa_u_short_int;
  94.     unsigned int pa_u_int;
  95.     unsigned long int pa_u_long_int;
  96.     unsigned long_long_int pa_u_long_long_int;
  97.     float pa_float;
  98.     double pa_double;
  99.     long_double pa_long_double;
  100.     const char *pa_string;
  101.     void *pa_pointer;
  102.   };
  103.  
  104.  
  105. /* Prototype for local function.  */
  106. static unsigned int read_int PARAMS ((const char * *pstr));
  107. static const char *find_spec PARAMS ((const char *format));
  108. static inline size_t parse_one_spec PARAMS ((const char *format, size_t posn,
  109.                          struct printf_spec *spec,
  110.                          size_t *max_ref_arg));
  111.  
  112.  
  113. /* Read a simple integer from a string and update the string pointer.
  114.    It is assumed that the first character is a digit.  */
  115. static inline unsigned int
  116. read_int (pstr)
  117.      const char * *pstr;
  118. {
  119.   unsigned int retval = **pstr - '0';
  120.  
  121.   while (isdigit (*++(*pstr)))
  122.     {
  123.       retval *= 10;
  124.       retval += **pstr - '0';
  125.     }
  126.  
  127.   return retval;
  128. }
  129.  
  130.  
  131.  
  132. /* Find the next spec in FORMAT, or the end of the string.  Returns
  133.    a pointer into FORMAT, to a '%' or a '\0'.  */
  134. static inline const char *
  135. find_spec (format)
  136.      const char *format;
  137. {
  138.   while (*format != '\0' && *format != '%')
  139.     {
  140.       int len;
  141.  
  142. #ifdef HAVE_MBLEN
  143.       if (isascii (*format) || (len = mblen (format, MB_CUR_MAX)) <= 0)
  144.     ++format;
  145.       else
  146.     format += len;
  147. #else
  148.       ++format;
  149. #endif
  150.     }
  151.   return format;
  152. }
  153.  
  154.  
  155. /* FORMAT must point to a '%' at the beginning of a spec.  Fills in *SPEC
  156.    with the parsed details.  POSN is the number of arguments already
  157.    consumed.  At most MAXTYPES - POSN types are filled in TYPES.  Return
  158.    the number of args consumed by this spec; *MAX_REF_ARG is updated so it
  159.    remains the highest argument index used.  */
  160. static inline size_t
  161. parse_one_spec (format, posn, spec, max_ref_arg)
  162.      const char *format;
  163.      size_t posn;
  164.      struct printf_spec *spec;
  165.      size_t *max_ref_arg;
  166. {
  167.   unsigned int n;
  168.   size_t nargs = 0;
  169.  
  170.   /* Skip the '%'.  */
  171.   ++format;
  172.  
  173.   /* Clear information structure.  */
  174.   spec->data_arg = -1;
  175.   spec->info.alt = 0;
  176.   spec->info.space = 0;
  177.   spec->info.left = 0;
  178.   spec->info.showsign = 0;
  179.   spec->info.group = 0;
  180.   spec->info.pad = ' ';
  181.  
  182.   /* Test for positional argument.  */
  183.   if (isdigit (*format))
  184.     {
  185.       const char *begin = format;
  186.  
  187.       n = read_int (&format);
  188.  
  189.       if (n > 0 && *format == '$')
  190.     /* Is positional parameter.  */
  191.     {
  192.       ++format;        /* Skip the '$'.  */
  193.       spec->data_arg = n - 1;
  194.       *max_ref_arg = MAX (*max_ref_arg, n);
  195.     }
  196.       else
  197.     /* Oops; that was actually the width and/or 0 padding flag.
  198.        Step back and read it again.  */
  199.     format = begin;
  200.     }
  201.  
  202.   /* Check for spec modifiers.  */
  203.   while (*format == ' ' || *format == '+' || *format == '-' ||
  204.      *format == '#' || *format == '0' || *format == '\'')
  205.     switch (*format++)
  206.       {
  207.       case ' ':
  208.     /* Output a space in place of a sign, when there is no sign.  */
  209.     spec->info.space = 1;
  210.     break;
  211.       case '+':
  212.     /* Always output + or - for numbers.  */
  213.     spec->info.showsign = 1;
  214.     break;
  215.       case '-':
  216.     /* Left-justify things.  */
  217.     spec->info.left = 1;
  218.     break;
  219.       case '#':
  220.     /* Use the "alternate form":
  221.        Hex has 0x or 0X, FP always has a decimal point.  */
  222.     spec->info.alt = 1;
  223.     break;
  224.       case '0':
  225.     /* Pad with 0s.  */
  226.     spec->info.pad = '0';
  227.     break;
  228.       case '\'':
  229.     /* Show grouping in numbers if the locale information
  230.        indicates any.  */
  231.     spec->info.group = 1;
  232.     break;
  233.       }
  234.   if (spec->info.left)
  235.     spec->info.pad = ' ';
  236.  
  237.   /* Get the field width.  */
  238.   spec->width_arg = -1;
  239.   spec->info.width = 0;
  240.   if (*format == '*')
  241.     {
  242.       /* The field width is given in an argument.
  243.      A negative field width indicates left justification.  */
  244.       const char *begin = ++format;
  245.  
  246.       if (isdigit (*format))
  247.     {
  248.       /* The width argument might be found in a positional parameter.  */
  249.       n = read_int (&format);
  250.  
  251.       if (n > 0 && *format == '$')
  252.         {
  253.           spec->width_arg = n - 1;
  254.           *max_ref_arg = MAX (*max_ref_arg, n);
  255.           ++format;        /* Skip '$'.  */
  256.         }
  257.     }
  258.  
  259.       if (spec->width_arg < 0)
  260.     {
  261.       /* Not in a positional parameter.  Consume one argument.  */
  262.       spec->width_arg = posn++;
  263.       ++nargs;
  264.       format = begin;    /* Step back and reread.  */
  265.     }
  266.     }
  267.   else if (isdigit (*format))
  268.     /* Constant width specification.  */
  269.     spec->info.width = read_int (&format);
  270.  
  271.   /* Get the precision.  */
  272.   spec->prec_arg = -1;
  273.   /* -1 means none given; 0 means explicit 0.  */
  274.   spec->info.prec = -1;
  275.   if (*format == '.')
  276.     {
  277.       ++format;
  278.       if (*format == '*')
  279.     {
  280.       /* The precision is given in an argument.  */
  281.       const char *begin = ++format;
  282.  
  283.       if (isdigit (*format))
  284.         {
  285.           n = read_int (&format);
  286.  
  287.           if (n > 0 && *format == '$')
  288.         {
  289.           spec->prec_arg = n - 1;
  290.           *max_ref_arg = MAX (*max_ref_arg, n);
  291.           ++format;
  292.         }
  293.         }
  294.  
  295.       if (spec->prec_arg < 0)
  296.         {
  297.           /* Not in a positional parameter.  */
  298.           spec->prec_arg = posn++;
  299.           ++nargs;
  300.           format = begin;
  301.         }
  302.     }
  303.       else if (isdigit (*format))
  304.     spec->info.prec = read_int (&format);
  305.       else
  306.     /* "%.?" is treated like "%.0?".  */
  307.     spec->info.prec = 0;
  308.     }
  309.  
  310.   /* Check for type modifiers.  */
  311. #define is_longlong is_long_double
  312.   spec->info.is_long_double = 0;
  313.   spec->info.is_short = 0;
  314.   spec->info.is_long = 0;
  315.  
  316.   while (*format == 'h' || *format == 'l' || *format == 'L' ||
  317.      *format == 'Z' || *format == 'q')
  318.     switch (*format++)
  319.       {
  320.       case 'h':
  321.     /* int's are short int's.  */
  322.     spec->info.is_short = 1;
  323.     break;
  324.       case 'l':
  325.     if (spec->info.is_long)
  326.       /* A double `l' is equivalent to an `L'.  */
  327.       spec->info.is_longlong = 1;
  328.     else
  329.       /* int's are long int's.  */
  330.       spec->info.is_long = 1;
  331.     break;
  332.       case 'L':
  333.     /* double's are long double's, and int's are long long int's.  */
  334.     spec->info.is_long_double = 1;
  335.     break;
  336.       case 'Z':
  337.     /* int's are size_t's.  */
  338.     assert (sizeof(size_t) <= sizeof(unsigned long_long_int));
  339.     spec->info.is_longlong = sizeof(size_t) > sizeof(unsigned long int);
  340.     spec->info.is_long = sizeof(size_t) > sizeof(unsigned int);
  341.     break;
  342.       case 'q':
  343.     /* 4.4 uses this for long long.  */
  344.     spec->info.is_longlong = 1;
  345.     break;
  346.       }
  347.  
  348.   /* Get the format specification.  */
  349.   spec->info.spec = *format++;
  350.   /* Find the data argument types of a built-in spec.  */
  351.   spec->ndata_args = 1;
  352.  
  353.   switch (spec->info.spec)
  354.     {
  355.     case 'i':
  356.     case 'd':
  357.     case 'u':
  358.     case 'o':
  359.     case 'X':
  360.     case 'x':
  361.       if (spec->info.is_longlong)
  362.     spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG;
  363.       else if (spec->info.is_long)
  364.     spec->data_arg_type = PA_INT|PA_FLAG_LONG;
  365.       else if (spec->info.is_short)
  366.     spec->data_arg_type = PA_INT|PA_FLAG_SHORT;
  367.       else
  368.     spec->data_arg_type = PA_INT;
  369.       break;
  370.     case 'e':
  371.     case 'E':
  372.     case 'f':
  373.     case 'g':
  374.     case 'G':
  375.       if (spec->info.is_long_double)
  376.     spec->data_arg_type = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
  377.       else
  378.     spec->data_arg_type = PA_DOUBLE;
  379.       break;
  380.     case 'c':
  381.       spec->data_arg_type = PA_CHAR;
  382.       break;
  383.     case 's':
  384.       spec->data_arg_type = PA_STRING;
  385.       break;
  386.     case 'p':
  387.       spec->data_arg_type = PA_POINTER|PA_FLAG_PTR;
  388.       break;
  389.     case 'n':
  390.       spec->data_arg_type = PA_INT|PA_FLAG_PTR;
  391.       break;
  392.  
  393.     case 'm':
  394.     default:
  395.       /* An unknown spec will consume no args.  */
  396.       spec->ndata_args = 0;
  397.       break;
  398.     }
  399.  
  400.   if (spec->data_arg == -1 && spec->ndata_args > 0)
  401.     {
  402.       /* There are args consumed, but no positional spec.
  403.      Use the next sequential arg position.  */
  404.       spec->data_arg = posn;
  405.       posn += spec->ndata_args;
  406.       nargs += spec->ndata_args;
  407.     }
  408.  
  409.   if (spec->info.spec == '\0')
  410.     /* Format ended before this spec was complete.  */
  411.     spec->end_of_fmt = spec->next_fmt = format - 1;
  412.   else
  413.     {
  414.       /* Find the next format spec.  */
  415.       spec->end_of_fmt = format;
  416.       spec->next_fmt = find_spec (format);
  417.     }
  418.  
  419.   return nargs;
  420. }
  421.