home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / glibc-1.06 / stdio / __vfscanf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-05  |  11.7 KB  |  532 lines

  1. /* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3.  
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public License as
  6. published by the Free Software Foundation; either version 2 of the
  7. License, or (at your option) any later version.
  8.  
  9. The GNU C Library 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 GNU
  12. Library General Public License for more details.
  13.  
  14. You should have received a copy of the GNU Library General Public
  15. License along with the GNU C Library; see the file COPYING.LIB.  If
  16. not, write to the Free Software Foundation, Inc., 675 Mass Ave,
  17. Cambridge, MA 02139, USA.  */
  18.  
  19. #include <ansidecl.h>
  20. #include <localeinfo.h>
  21. #include <errno.h>
  22. #include <limits.h>
  23. #include <ctype.h>
  24. #include <stdarg.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28.  
  29.  
  30. #ifdef    __GNUC__
  31. #define    HAVE_LONGLONG
  32. #define    LONGLONG    long long
  33. #else
  34. #define    LONGLONG    long
  35. #endif
  36.  
  37.  
  38. #define    inchar()    ((c = getc(s)) == EOF ? EOF : (++read_in, c))
  39. #define    conv_error()    return ((c == EOF || ungetc(c, s)), done)
  40. #define input_error()    return (done == 0 ? EOF : done)
  41. #define    memory_error()    return ((errno = ENOMEM), EOF)
  42.  
  43.  
  44. /* Read formatted input from S according to the format string
  45.    FORMAT, using the argument list in ARG.
  46.    Return the number of assignments made, or -1 for an input error.  */
  47. int
  48. DEFUN(__vfscanf, (s, format, arg),
  49.       FILE *s AND CONST char *format AND va_list argptr)
  50. {
  51.   va_list arg = (va_list) argptr;
  52.  
  53.   register CONST char *f = format;
  54.   register char fc;        /* Current character of the format.  */
  55.   register size_t done = 0;    /* Assignments done.  */
  56.   register size_t read_in = 0;    /* Chars read in.  */
  57.   register int c;        /* Last char read.  */
  58.   register int do_assign;    /* Whether to do an assignment.  */
  59.   register int width;        /* Maximum field width.  */
  60.  
  61.   /* Type modifiers.  */
  62.   char is_short, is_long, is_long_double;
  63. #ifdef    HAVE_LONGLONG
  64.   /* We use the `L' modifier for `long long int'.  */
  65. #define    is_longlong    is_long_double
  66. #else
  67. #define    is_longlong    0
  68. #endif
  69.   int malloc_string;        /* Args are char ** to be filled in.  */
  70.   /* Status for reading F-P nums.  */
  71.   char got_dot, got_e;
  72.   /* If a [...] is a [^...].  */
  73.   char not_in;
  74.   /* Base for integral numbers.  */
  75.   int base;
  76.   /* Integral holding variables.  */
  77.   long int num;
  78.   unsigned long int unum;
  79.   /* Floating-point holding variable.  */
  80.   LONG_DOUBLE fp_num;
  81.   /* Character-buffer pointer.  */
  82.   register char *str, **strptr;
  83.   size_t strsize;
  84.   /* Workspace.  */
  85.   char work[200];
  86.   char *w;            /* Pointer into WORK.  */
  87.   wchar_t decimal;        /* Decimal point character.  */
  88.  
  89.   if (!__validfp(s) || !s->__mode.__read || format == NULL)
  90.     {
  91.       errno = EINVAL;
  92.       return EOF;
  93.     }
  94.  
  95.   /* Figure out the decimal point character.  */
  96.   if (mbtowc(&decimal, _numeric_info->decimal_point,
  97.          strlen(_numeric_info->decimal_point)) <= 0)
  98.     decimal = (wchar_t) *_numeric_info->decimal_point;
  99.  
  100.   c = inchar();
  101.  
  102.   /* Run through the format string.  */
  103.   while (*f != '\0')
  104.     {
  105.       if (!isascii(*f))
  106.     {
  107.       /* Non-ASCII, may be a multibyte.  */
  108.       int len = mblen(f, strlen(f));
  109.       if (len > 0)
  110.         {
  111.           while (len-- > 0)
  112.         if (c == EOF)
  113.           input_error();
  114.         else if (c == *f++)
  115.           (void) inchar();
  116.         else
  117.           conv_error();
  118.           continue;
  119.         }
  120.     }
  121.  
  122.       fc = *f++;
  123.       if (fc != '%')
  124.     {
  125.       /* Characters other than format specs must just match.  */
  126.       if (c == EOF)
  127.         input_error();
  128.       if (isspace(fc))
  129.         {
  130.           /* Whitespace characters match any amount of whitespace.  */
  131.           while (isspace (c))
  132.         inchar ();
  133.           continue;
  134.         }
  135.       else if (c == fc)
  136.         (void) inchar();
  137.       else
  138.         conv_error();
  139.       continue;
  140.     }
  141.  
  142.       /* Check for the assignment-suppressant.  */
  143.       if (*f == '*')
  144.     {
  145.       do_assign = 0;
  146.       ++f;
  147.     }
  148.       else
  149.     do_assign = 1;
  150.         
  151.       /* Find the maximum field width.  */
  152.       width = 0;
  153.       while (isdigit(*f))
  154.     {
  155.       width *= 10;
  156.       width += *f++ - '0';
  157.     }
  158.       if (width == 0)
  159.     width = -1;
  160.  
  161.       /* Check for type modifiers.  */
  162.       is_short = is_long = is_long_double = malloc_string = 0;
  163.       while (*f == 'h' || *f == 'l' || *f == 'L')
  164.     switch (*f++)
  165.       {
  166.       case 'h':
  167.         /* int's are short int's.  */
  168.         is_short = 1;
  169.         break;
  170.       case 'l':
  171.         if (is_long)
  172.           /* A double `l' is equivalent to an `L'.  */
  173.           is_longlong = 1;
  174.         else
  175.           /* int's are long int's.  */
  176.           is_long = 1;
  177.         break;
  178.       case 'L':
  179.         /* double's are long double's, and int's are long long int's.  */
  180.         is_long_double = 1;
  181.         break;
  182.       case 'a':
  183.         /* String conversions (%s, %[) take a `char **'
  184.            arg and fill it in with a malloc'd pointer.  */
  185.         malloc_string = 1;
  186.         break;
  187.       }
  188.  
  189.       /* End of the format string?  */
  190.       if (*f == '\0')
  191.     conv_error();
  192.  
  193.       /* Find the conversion specifier.  */
  194.       w = work;
  195.       fc = *f++;
  196.       if (fc != '[' && fc != 'c' && fc != 'n')
  197.     /* Eat whitespace.  */
  198.     while (isspace(c))
  199.       (void) inchar();
  200.       switch (fc)
  201.     {
  202.     case '%':    /* Must match a literal '%'.  */
  203.       if (c != fc)
  204.         conv_error();
  205.       break;
  206.  
  207.     case 'n':    /* Answer number of assignments done.  */
  208.       if (do_assign)
  209.         *va_arg(arg, int *) = read_in;
  210.       break;
  211.  
  212.     case 'c':    /* Match characters.  */
  213.       if (do_assign)
  214.         {
  215.           str = va_arg (arg, char *);
  216.           if (str == NULL)
  217.         conv_error ();
  218.         }
  219.  
  220.       if (c == EOF)
  221.         input_error();
  222.  
  223.       if (width == -1)
  224.         width = 1;
  225.  
  226.       if (do_assign)
  227.         {
  228.           do
  229.         *str++ = c;
  230.           while (inchar() != EOF && --width > 0);
  231.         }
  232.       else
  233.         while (inchar() != EOF && width > 0)
  234.           --width;
  235.  
  236.       if (do_assign)
  237.         ++done;
  238.  
  239.       break;
  240.  
  241.     case 's':        /* Read a string.  */
  242. #define STRING_ARG                                  \
  243.       if (do_assign)                              \
  244.         {                                      \
  245.           if (malloc_string)                          \
  246.         {                                  \
  247.           /* The string is to be stored in a malloc'd buffer.  */     \
  248.           strptr = va_arg (arg, char **);                  \
  249.           if (strptr == NULL)                          \
  250.             conv_error ();                          \
  251.           /* Allocate an initial buffer.  */                  \
  252.           strsize = 100;                          \
  253.           *strptr = str = malloc (strsize);                  \
  254.         }                                  \
  255.           else                                  \
  256.         str = va_arg (arg, char *);                      \
  257.           if (str == NULL)                              \
  258.         conv_error ();                              \
  259.         }
  260.       STRING_ARG;
  261.  
  262.       if (c == EOF)
  263.         input_error ();
  264.  
  265.       do
  266.         {
  267.           if (isspace (c))
  268.         break;
  269. #define    STRING_ADD_CHAR(c)                              \
  270.           if (do_assign)                              \
  271.         {                                  \
  272.           *str++ = c;                              \
  273.           if (malloc_string && str == *strptr + strsize)          \
  274.             {                                  \
  275.               /* Enlarge the buffer.  */                  \
  276.               str = realloc (*strptr, strsize * 2);              \
  277.               if (str == NULL)                          \
  278.             {                              \
  279.               /* Can't allocate that much.  Last-ditch effort.  */\
  280.               str = realloc (*strptr, strsize + 1);              \
  281.               if (str == NULL)                      \
  282.                 {                              \
  283.                   /* We lose.  Oh well.                  \
  284.                  Terminate the string and stop converting,    \
  285.                  so at least we don't swallow any input.  */  \
  286.                   (*strptr)[strsize] = '\0';              \
  287.                   ++done;                          \
  288.                   conv_error ();                      \
  289.                 }                              \
  290.               else                              \
  291.                 {                              \
  292.                   *strptr = str;                      \
  293.                   str += strsize;                      \
  294.                   ++strsize;                      \
  295.                 }                              \
  296.             }                              \
  297.               else                              \
  298.             {                              \
  299.               *strptr = str;                      \
  300.               str += strsize;                      \
  301.               strsize *= 2;                          \
  302.             }                              \
  303.             }                                  \
  304.         }
  305.           STRING_ADD_CHAR (c);
  306.         } while (inchar () != EOF && (width <= 0 || --width > 0));
  307.  
  308.       if (do_assign)
  309.         {
  310.           *str = '\0';
  311.           ++done;
  312.         }
  313.       break;
  314.  
  315.     case 'x':    /* Hexadecimal integer.  */
  316.     case 'X':    /* Ditto.  */ 
  317.       base = 16;
  318.       goto number;
  319.  
  320.     case 'o':    /* Octal integer.  */
  321.       base = 8;
  322.       goto number;
  323.  
  324.     case 'u':    /* Decimal integer.  */
  325.     case 'd':    /* Ditto.  */
  326.       base = 10;
  327.       goto number;
  328.  
  329.     case 'i':    /* Generic number.  */
  330.       base = 0;
  331.  
  332.     number:;
  333.       if (c == EOF)
  334.         input_error();
  335.  
  336.       /* Check for a sign.  */
  337.       if (c == '-' || c == '+')
  338.         {
  339.           *w++ = c;
  340.           if (width > 0)
  341.         --width;
  342.           (void) inchar();
  343.         }
  344.  
  345.       /* Look for a leading indication of base.  */
  346.       if (c == '0')
  347.         {
  348.           if (width > 0)
  349.         --width;
  350.           *w++ = '0';
  351.  
  352.           (void) inchar();
  353.  
  354.           if (tolower(c) == 'x')
  355.         {
  356.           if (base == 0)
  357.             base = 16;
  358.           if (base == 16)
  359.             {
  360.               if (width > 0)
  361.             --width;
  362.               (void) inchar();
  363.             }
  364.         }
  365.           else if (base == 0)
  366.         base = 8;
  367.         }
  368.  
  369.       if (base == 0)
  370.         base = 10;
  371.  
  372.       /* Read the number into WORK.  */
  373.       do
  374.         {
  375.           if (base == 16 ? !isxdigit(c) :
  376.           (!isdigit(c) || c - '0' >= base))
  377.         break;
  378.           *w++ = c;
  379.           if (width > 0)
  380.         --width;
  381.         } while (inchar() != EOF && width != 0);
  382.  
  383.       if (w == work ||
  384.           (w - work == 1 && (work[0] == '+' || work[0] == '-')))
  385.         /* There was on number.  */
  386.         conv_error();
  387.  
  388.       /* Convert the number.  */
  389.       *w = '\0';
  390.       num = strtol(work, &w, base);
  391.       if (w == work)
  392.         conv_error();
  393.  
  394.       if (do_assign)
  395.         {
  396.           if (is_longlong)
  397.         *va_arg(arg, LONGLONG int *) = num;
  398.           else if (is_long)
  399.         *va_arg(arg, long int *) = num;
  400.           else if (is_short)
  401.         *va_arg(arg, short int *) = (short int) num;
  402.           else
  403.         *va_arg(arg, int *) = (int) num;
  404.           ++done;
  405.         }
  406.       break;
  407.  
  408.     case 'e':    /* Floating-point numbers.  */
  409.     case 'E':
  410.     case 'f':
  411.     case 'g':
  412.     case 'G':
  413.       if (c == EOF)
  414.         input_error();
  415.  
  416.       /* Check for a sign.  */
  417.       if (c == '-' || c == '+')
  418.         {
  419.           *w++ = c;
  420.           if (inchar() == EOF)
  421.         /* EOF is only an input error before we read any chars.  */
  422.         conv_error();
  423.           if (width > 0)
  424.         --width;
  425.         }
  426.  
  427.       got_dot = got_e = 0;
  428.       do
  429.         {
  430.           if (isdigit(c))
  431.         *w++ = c;
  432.           else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
  433.         *w++ = c;
  434.           else if (!got_e && tolower(c) == 'e')
  435.         {
  436.           *w++ = 'e';
  437.           got_e = got_dot = 1;
  438.         }
  439.           else if (c == decimal && !got_dot)
  440.         {
  441.           *w++ = c;
  442.           got_dot = 1;
  443.         }
  444.           else
  445.         break;
  446.           if (width > 0)
  447.         --width;
  448.         } while (inchar() != EOF && width != 0);
  449.  
  450.       if (w == work)
  451.         conv_error();
  452.       if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
  453.         conv_error();
  454.  
  455.       /* Convert the number.  */
  456.       *w = '\0';
  457.       fp_num = strtod(work, &w);
  458.       if (w == work)
  459.         conv_error();
  460.  
  461.       if (do_assign)
  462.         {
  463.           if (is_long_double)
  464.         *va_arg(arg, LONG_DOUBLE *) = fp_num;
  465.           else if (is_long)
  466.         *va_arg(arg, double *) = (double) fp_num;
  467.           else
  468.         *va_arg(arg, float *) = (float) fp_num;
  469.           ++done;
  470.         }
  471.       break;
  472.  
  473.     case '[':    /* Character class.  */
  474.       STRING_ARG;
  475.  
  476.       if (c == EOF)
  477.         input_error();
  478.  
  479.       if (*f == '^')
  480.         {
  481.           ++f;
  482.           not_in = 1;
  483.         }
  484.       else
  485.         not_in = 0;
  486.  
  487.       while ((fc = *f++) != '\0' && fc != ']')
  488.         {
  489.           if (fc == '-' && *f != '\0' && *f != ']' &&
  490.           w > work && w[-1] <= *f)
  491.         /* Add all characters from the one before the '-'
  492.            up to (but not including) the next format char.  */
  493.         for (fc = w[-1] + 1; fc < *f; ++fc)
  494.           *w++ = fc;
  495.           else
  496.         /* Add the character to the list.  */
  497.         *w++ = fc;
  498.         }
  499.       if (fc == '\0')
  500.         conv_error();
  501.  
  502.       *w = '\0';
  503.       unum = read_in;
  504.       do
  505.         {
  506.           if ((strchr (work, c) == NULL) != not_in)
  507.         break;
  508.           STRING_ADD_CHAR (c);
  509.           if (width > 0)
  510.         --width;
  511.         } while (inchar () != EOF && width != 0);
  512.       if (read_in == unum)
  513.         conv_error ();
  514.  
  515.       if (do_assign)
  516.         {
  517.           *str = '\0';
  518.           ++done;
  519.         }
  520.       break;
  521.  
  522.     case 'p':    /* Generic pointer.  */
  523.       base = 16;
  524.       /* A PTR must be the same size as a `long int'.  */
  525.       is_long = 1;
  526.       goto number;
  527.     }
  528.     }
  529.  
  530.   conv_error();
  531. }
  532.