home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / io / prscanf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  18.2 KB  |  638 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  * 
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  * 
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * Scan functions for NSPR types
  21.  *
  22.  * Author: Wan-Teh Chang
  23.  *
  24.  * Acknowledgment: The implementation is inspired by the source code
  25.  * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992.
  26.  */
  27.  
  28. #include <limits.h>
  29. #include <ctype.h>
  30. #include <string.h>
  31. #include <stdlib.h>
  32. #include "prprf.h"
  33. #include "prdtoa.h"
  34. #include "prlog.h"
  35. #include "prerror.h"
  36.  
  37. /*
  38.  * A function that reads a character from 'stream'.
  39.  * Returns the character read, or EOF if end of stream is reached.
  40.  */
  41. typedef int (*_PRGetCharFN)(void *stream);
  42.  
  43. /*
  44.  * A function that pushes the character 'ch' back to 'stream'.
  45.  */
  46. typedef void (*_PRUngetCharFN)(void *stream, int ch); 
  47.  
  48. /*
  49.  * The size specifier for the integer and floating point number
  50.  * conversions in format control strings.
  51.  */
  52. typedef enum {
  53.     _PR_size_none,  /* No size specifier is given */
  54.     _PR_size_h,     /* The 'h' specifier, suggesting "short" */
  55.     _PR_size_l,     /* The 'l' specifier, suggesting "long" */
  56.     _PR_size_L,     /* The 'L' specifier, meaning a 'long double' */
  57.     _PR_size_ll     /* The 'll' specifier, suggesting "long long" */
  58. } _PRSizeSpec;
  59.  
  60. /*
  61.  * The collection of data that is passed between the scan function
  62.  * and its subordinate functions.  The fields of this structure
  63.  * serve as the input or output arguments for these functions.
  64.  */
  65. typedef struct {
  66.     _PRGetCharFN get;        /* get a character from input stream */
  67.     _PRUngetCharFN unget;    /* unget (push back) a character */
  68.     void *stream;            /* argument for get and unget */
  69.     va_list ap;              /* the variable argument list */
  70.     int nChar;               /* number of characters read from 'stream' */
  71.  
  72.     PRBool assign;           /* assign, or suppress assignment? */
  73.     int width;               /* field width */
  74.     _PRSizeSpec sizeSpec;    /* 'h', 'l', 'L', or 'll' */
  75.  
  76.     PRBool converted;        /* is the value actually converted? */
  77. } ScanfState;
  78.  
  79. #define GET(state) ((state)->nChar++, (state)->get((state)->stream))
  80. #define UNGET(state, ch) \
  81.         ((state)->nChar--, (state)->unget((state)->stream, ch))
  82.  
  83. /*
  84.  * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH,
  85.  * are always used together.
  86.  *
  87.  * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return
  88.  * value to 'ch' only if we have not exceeded the field width of
  89.  * 'state'.  Therefore, after GET_IF_WITHIN_WIDTH, the value of
  90.  * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true.
  91.  */
  92.  
  93. #define GET_IF_WITHIN_WIDTH(state, ch) \
  94.         if (--(state)->width >= 0) { \
  95.             (ch) = GET(state); \
  96.         }
  97. #define WITHIN_WIDTH(state) ((state)->width >= 0)
  98.  
  99. /*
  100.  * _pr_strtoull:
  101.  *     Convert a string to an unsigned 64-bit integer.  The string
  102.  *     'str' is assumed to be a representation of the integer in
  103.  *     base 'base'.
  104.  *
  105.  * Warning: 
  106.  *     - Only handle base 8, 10, and 16.
  107.  *     - No overflow checking.
  108.  */
  109.  
  110. static PRUint64
  111. _pr_strtoull(const char *str, char **endptr, int base)
  112. {
  113.     static const int BASE_MAX = 16;
  114.     static const char digits[] = "0123456789abcdef";
  115.     char *digitPtr;
  116.     PRUint64 x;    /* return value */
  117.     PRInt64 base64;
  118.     const char *cPtr;
  119.     PRBool negative;
  120.     const char *digitStart;
  121.  
  122.     PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16);
  123.     if (base < 0 || base == 1 || base > BASE_MAX) {
  124.         if (endptr) {
  125.             *endptr = (char *) str;
  126.             return LL_ZERO;
  127.         }
  128.     }
  129.  
  130.     cPtr = str;
  131.     while (isspace(*cPtr)) {
  132.         ++cPtr;
  133.     }
  134.  
  135.     negative = PR_FALSE;
  136.     if (*cPtr == '-') {
  137.         negative = PR_TRUE;
  138.         cPtr++;
  139.     } else if (*cPtr == '+') {
  140.         cPtr++;
  141.     }
  142.  
  143.     if (base == 16) {
  144.         if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) {
  145.             cPtr += 2;
  146.         }
  147.     } else if (base == 0) {
  148.         if (*cPtr != '0') {
  149.             base = 10;
  150.         } else if (cPtr[1] == 'x' || cPtr[1] == 'X') {
  151.             base = 16;
  152.             cPtr += 2;
  153.         } else {
  154.             base = 8;
  155.         } 
  156.     }
  157.     PR_ASSERT(base != 0);
  158.     LL_I2L(base64, base);
  159.     digitStart = cPtr;
  160.  
  161.     /* Skip leading zeros */
  162.     while (*cPtr == '0') {
  163.         cPtr++;
  164.     }
  165.  
  166.     LL_I2L(x, 0);
  167.     while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) {
  168.         PRUint64 d;
  169.  
  170.         LL_I2L(d, (digitPtr - digits));
  171.         LL_MUL(x, x, base64);
  172.         LL_ADD(x, x, d);
  173.         cPtr++;
  174.     }
  175.  
  176.     if (cPtr == digitStart) {
  177.         if (endptr) {
  178.             *endptr = (char *) str;
  179.         }
  180.         return LL_ZERO;
  181.     }
  182.  
  183.     if (negative) {
  184.         LL_NEG(x, x);
  185.     }
  186.  
  187.     if (endptr) {
  188.         *endptr = (char *) cPtr;
  189.     }
  190.     return x;
  191. }
  192.  
  193. /*
  194.  * The maximum field width (in number of characters) that is enough
  195.  * (may be more than necessary) to represent a 64-bit integer or
  196.  * floating point number.
  197.  */
  198. #define FMAX 31
  199. #define DECIMAL_POINT '.'
  200.  
  201. static PRStatus
  202. GetInt(ScanfState *state, int code)
  203. {
  204.     char buf[FMAX + 1], *p;
  205.     int ch;
  206.     static const char digits[] = "0123456789abcdefABCDEF";
  207.     PRBool seenDigit = PR_FALSE;
  208.     int base;
  209.     int dlen;
  210.  
  211.     switch (code) {
  212.         case 'd': case 'u':
  213.             base = 10;
  214.             break;
  215.         case 'i':
  216.             base = 0;
  217.             break;
  218.         case 'x': case 'X': case 'p':
  219.             base = 16;
  220.             break;
  221.         case 'o':
  222.             base = 8;
  223.             break;
  224.         default:
  225.             return PR_FAILURE;
  226.     }
  227.     if (state->width == 0 || state->width > FMAX) {
  228.         state->width = FMAX;
  229.     }
  230.     p = buf;
  231.     GET_IF_WITHIN_WIDTH(state, ch);
  232.     if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
  233.         *p++ = ch;
  234.         GET_IF_WITHIN_WIDTH(state, ch);
  235.     }
  236.     if (WITHIN_WIDTH(state) && ch == '0') {
  237.         seenDigit = PR_TRUE;
  238.         *p++ = ch;
  239.         GET_IF_WITHIN_WIDTH(state, ch);
  240.         if (WITHIN_WIDTH(state)
  241.                 && (ch == 'x' || ch == 'X')
  242.                 && (base == 0 || base == 16)) {
  243.             base = 16;
  244.             *p++ = ch;
  245.             GET_IF_WITHIN_WIDTH(state, ch);
  246.         } else if (base == 0) {
  247.             base = 8;
  248.         }
  249.     }
  250.     if (base == 0 || base == 10) {
  251.         dlen = 10;
  252.     } else if (base == 8) {
  253.         dlen = 8;
  254.     } else {
  255.         PR_ASSERT(base == 16);
  256.         dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */
  257.     }
  258.     while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) {
  259.         *p++ = ch;
  260.         GET_IF_WITHIN_WIDTH(state, ch);
  261.         seenDigit = PR_TRUE;
  262.     }
  263.     if (WITHIN_WIDTH(state)) {
  264.         UNGET(state, ch);
  265.     }
  266.     if (!seenDigit) {
  267.         return PR_FAILURE;
  268.     }
  269.     *p = '\0';
  270.     if (state->assign) {
  271.         if (code == 'd' || code == 'i') {
  272.             if (state->sizeSpec == _PR_size_ll) {
  273.                 PRInt64 llval = _pr_strtoull(buf, NULL, base);
  274.                 *va_arg(state->ap, PRInt64 *) = llval;
  275.             } else {
  276.                 long lval = strtol(buf, NULL, base);
  277.  
  278.                 if (state->sizeSpec == _PR_size_none) {
  279.                     *va_arg(state->ap, PRIntn *) = lval;
  280.                 } else if (state->sizeSpec == _PR_size_h) {
  281.                     *va_arg(state->ap, PRInt16 *) = (PRInt16)lval;
  282.                 } else if (state->sizeSpec == _PR_size_l) {
  283.                     *va_arg(state->ap, PRInt32 *) = lval;
  284.                 } else {
  285.                     return PR_FAILURE;
  286.                 }
  287.             }
  288.         } else {
  289.             if (state->sizeSpec == _PR_size_ll) {
  290.                 PRUint64 llval = _pr_strtoull(buf, NULL, base);
  291.                 *va_arg(state->ap, PRUint64 *) = llval;
  292.             } else {
  293.                 unsigned long lval = strtoul(buf, NULL, base);
  294.  
  295.                 if (state->sizeSpec == _PR_size_none) {
  296.                     *va_arg(state->ap, PRUintn *) = lval;
  297.                 } else if (state->sizeSpec == _PR_size_h) {
  298.                     *va_arg(state->ap, PRUint16 *) = (PRUint16)lval;
  299.                 } else if (state->sizeSpec == _PR_size_l) {
  300.                     *va_arg(state->ap, PRUint32 *) = lval;
  301.                 } else {
  302.                     return PR_FAILURE;
  303.                 }
  304.             }
  305.         }
  306.         state->converted = PR_TRUE;
  307.     }
  308.     return PR_SUCCESS;
  309. }
  310.  
  311. static PRStatus
  312. GetFloat(ScanfState *state)
  313. {
  314.     char buf[FMAX + 1], *p;
  315.     int ch;
  316.     PRBool seenDigit = PR_FALSE;
  317.  
  318.     if (state->width == 0 || state->width > FMAX) {
  319.         state->width = FMAX;
  320.     }
  321.     p = buf;
  322.     GET_IF_WITHIN_WIDTH(state, ch);
  323.     if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
  324.         *p++ = ch;
  325.         GET_IF_WITHIN_WIDTH(state, ch);
  326.     }
  327.     while (WITHIN_WIDTH(state) && isdigit(ch)) {
  328.         *p++ = ch;
  329.         GET_IF_WITHIN_WIDTH(state, ch);
  330.         seenDigit = PR_TRUE;
  331.     }
  332.     if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) {
  333.         *p++ = ch;
  334.         GET_IF_WITHIN_WIDTH(state, ch);
  335.         while (WITHIN_WIDTH(state) && isdigit(ch)) {
  336.             *p++ = ch;
  337.             GET_IF_WITHIN_WIDTH(state, ch);
  338.             seenDigit = PR_TRUE;
  339.         }
  340.     }
  341.  
  342.     /*
  343.      * This is not robust.  For example, "1.2e+" would confuse
  344.      * the code below to read 'e' and '+', only to realize that
  345.      * it should have stopped at "1.2".  But we can't push back
  346.      * more than one character, so there is nothing I can do.
  347.      */
  348.  
  349.     /* Parse exponent */
  350.     if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) {
  351.         *p++ = ch;
  352.         GET_IF_WITHIN_WIDTH(state, ch);
  353.         if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
  354.             *p++ = ch;
  355.             GET_IF_WITHIN_WIDTH(state, ch);
  356.         }
  357.         while (WITHIN_WIDTH(state) && isdigit(ch)) {
  358.             *p++ = ch;
  359.             GET_IF_WITHIN_WIDTH(state, ch);
  360.         }
  361.     }
  362.     if (WITHIN_WIDTH(state)) {
  363.         UNGET(state, ch);
  364.     }
  365.     if (!seenDigit) {
  366.         return PR_FAILURE;
  367.     }
  368.     *p = '\0';
  369.     if (state->assign) {
  370.         PRFloat64 dval = PR_strtod(buf, NULL);
  371.  
  372.         state->converted = PR_TRUE;
  373.         if (state->sizeSpec == _PR_size_l) {
  374.             *va_arg(state->ap, PRFloat64 *) = dval;
  375.         } else if (state->sizeSpec == _PR_size_L) {
  376. #if defined(OSF1) || defined(IRIX)
  377.             *va_arg(state->ap, double *) = dval;
  378. #else
  379.             *va_arg(state->ap, long double *) = dval;
  380. #endif
  381.         } else {
  382.             *va_arg(state->ap, float *) = dval;
  383.         }
  384.     }
  385.     return PR_SUCCESS;
  386. }
  387.  
  388. /*
  389.  * Convert, and return the end of the conversion spec.
  390.  */
  391.  
  392. static const char *
  393. Convert(ScanfState *state, const char *fmt)
  394. {
  395.     const char *cPtr;
  396.     int ch;
  397.     char *cArg;
  398.  
  399.     state->converted = PR_FALSE;
  400.     cPtr = fmt;
  401.     if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') {
  402.         do {
  403.             ch = GET(state);
  404.         } while (isspace(ch));
  405.         UNGET(state, ch);
  406.     }
  407.     switch (*cPtr) {
  408.         case 'c':
  409.             if (state->assign) {
  410.                 cArg = va_arg(state->ap, char *);
  411.             }
  412.             if (state->width == 0) {
  413.                 state->width = 1;
  414.             }
  415.             for (; state->width > 0; state->width--) {
  416.                 ch = GET(state);
  417.                 if (ch == EOF) {
  418.                     return NULL;
  419.                 } else if (state->assign) {
  420.                     *cArg++ = ch;
  421.                 }
  422.             }
  423.             if (state->assign) {
  424.                 state->converted = PR_TRUE;
  425.             }
  426.             break;
  427.         case 'p':
  428.         case 'd': case 'i': case 'o':
  429.         case 'u': case 'x': case 'X':
  430.             if (GetInt(state, *cPtr) == PR_FAILURE) {
  431.                 return NULL;
  432.             }
  433.             break;
  434.         case 'e': case 'E': case 'f':
  435.         case 'g': case 'G':
  436.             if (GetFloat(state) == PR_FAILURE) {
  437.                 return NULL;
  438.             }
  439.             break;
  440.         case 'n':
  441.             /* do not consume any input */
  442.             if (state->assign) {
  443.                 switch (state->sizeSpec) {
  444.                     case _PR_size_none:
  445.                         *va_arg(state->ap, PRIntn *) = state->nChar;
  446.                         break;
  447.                     case _PR_size_h:
  448.                         *va_arg(state->ap, PRInt16 *) = state->nChar;
  449.                         break;
  450.                     case _PR_size_l:
  451.                         *va_arg(state->ap, PRInt32 *) = state->nChar;
  452.                         break;
  453.                     case _PR_size_ll:
  454.                         LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar);
  455.                         break;
  456.                     default:
  457.                         PR_ASSERT(0);
  458.                 }
  459.             }
  460.             break;
  461.         case 's':
  462.             if (state->width == 0) {
  463.                 state->width = INT_MAX;
  464.             }
  465.             if (state->assign) {
  466.                 cArg = va_arg(state->ap, char *);
  467.             }
  468.             for (; state->width > 0; state->width--) {
  469.                 ch = GET(state);
  470.                 if ((ch == EOF) || isspace(ch)) {
  471.                     UNGET(state, ch);
  472.                     break;
  473.                 }
  474.                 if (state->assign) {
  475.                     *cArg++ = ch;
  476.                 }
  477.             }
  478.             if (state->assign) {
  479.                 *cArg = '\0';
  480.                 state->converted = PR_TRUE;
  481.             }
  482.             break;
  483.         case '%':
  484.             ch = GET(state);
  485.             if (ch != '%') {
  486.                 UNGET(state, ch);
  487.                 return NULL;
  488.             }
  489.             break;
  490.         case '[':
  491.             {
  492.                 PRBool complement = PR_FALSE;
  493.                 const char *closeBracket;
  494.                 size_t n;
  495.  
  496.                 if (*++cPtr == '^') {
  497.                     complement = PR_TRUE;
  498.                     cPtr++;
  499.                 }
  500.                 closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']');
  501.                 if (closeBracket == NULL) {
  502.                     return NULL;
  503.                 }
  504.                 n = closeBracket - cPtr;
  505.                 if (state->width == 0) {
  506.                     state->width = INT_MAX;
  507.                 }
  508.                 if (state->assign) {
  509.                     cArg = va_arg(state->ap, char *);
  510.                 }
  511.                 for (; state->width > 0; state->width--) {
  512.                     ch = GET(state);
  513.                     if ((ch == EOF) 
  514.                             || (!complement && !memchr(cPtr, ch, n))
  515.                             || (complement && memchr(cPtr, ch, n))) {
  516.                         UNGET(state, ch);
  517.                         break;
  518.                     }
  519.                     if (state->assign) {
  520.                         *cArg++ = ch;
  521.                     }
  522.                 }
  523.                 if (state->assign) {
  524.                     *cArg = '\0';
  525.                     state->converted = PR_TRUE;
  526.                 }
  527.                 cPtr = closeBracket;
  528.             }
  529.             break;
  530.         default:
  531.             return NULL;
  532.     }
  533.     return cPtr;
  534. }
  535.  
  536. static PRInt32
  537. DoScanf(ScanfState *state, const char *fmt)
  538. {
  539.     PRInt32 nConverted = 0;
  540.     const char *cPtr;
  541.     int ch;
  542.  
  543.     state->nChar = 0;
  544.     cPtr = fmt;
  545.     while (1) {
  546.         if (isspace(*cPtr)) {
  547.             do {
  548.                 cPtr++;
  549.             } while (isspace(*cPtr));
  550.             do {
  551.                 ch = GET(state);
  552.             } while (isspace(ch));
  553.             UNGET(state, ch);
  554.         } else if (*cPtr != '%') {
  555.             if (*cPtr == '\0') {
  556.                 return nConverted;
  557.             }
  558.             ch = GET(state);
  559.             if (ch != *cPtr) {
  560.                 UNGET(state, ch);
  561.                 return nConverted;
  562.             }
  563.             cPtr++;
  564.         } else {
  565.             cPtr++;
  566.             state->assign = PR_TRUE;
  567.             if (*cPtr == '*') {
  568.                 state->assign = PR_FALSE;
  569.             }
  570.             for (state->width = 0; isdigit(*cPtr); cPtr++) {
  571.                 state->width = state->width * 10 + *cPtr - '0';
  572.             }
  573.             state->sizeSpec = _PR_size_none;
  574.             if (*cPtr == 'h') {
  575.                 cPtr++;
  576.                 state->sizeSpec = _PR_size_h;
  577.             } else if (*cPtr == 'l') {
  578.                 cPtr++;
  579.                 if (*cPtr == 'l') {
  580.                     cPtr++;
  581.                     state->sizeSpec = _PR_size_ll;
  582.                 } else {
  583.                     state->sizeSpec = _PR_size_l;
  584.                 }
  585.             } else if (*cPtr == 'L') {
  586.                 cPtr++;
  587.                 state->sizeSpec = _PR_size_L;
  588.             }
  589.             cPtr = Convert(state, cPtr);
  590.             if (cPtr == NULL) {
  591.                 return (nConverted > 0 ? nConverted : EOF);
  592.             }
  593.             if (state->converted) {
  594.                 nConverted++;
  595.             }
  596.             cPtr++;
  597.         }
  598.     }
  599. }
  600.  
  601. static int
  602. StringGetChar(void *stream)
  603. {
  604.     char *cPtr = *((char **) stream);
  605.  
  606.     if (*cPtr == '\0') {
  607.         return EOF;
  608.     } else {
  609.         *((char **) stream) = cPtr + 1;
  610.         return *cPtr;
  611.     }
  612. }
  613.  
  614. static void
  615. StringUngetChar(void *stream, int ch)
  616. {
  617.     char *cPtr = *((char **) stream);
  618.  
  619.     if (ch != EOF) {
  620.         *((char **) stream) = cPtr - 1;
  621.     }
  622. }
  623.  
  624. PR_IMPLEMENT(PRInt32)
  625. PR_sscanf(const char *buf, const char *fmt, ...)
  626. {
  627.     PRInt32 rv;
  628.     ScanfState state;
  629.  
  630.     state.get = &StringGetChar;
  631.     state.unget = &StringUngetChar;
  632.     state.stream = (void *) &buf;
  633.     va_start(state.ap, fmt);
  634.     rv = DoScanf(&state, fmt);
  635.     va_end(state.ap);
  636.     return rv;
  637. }
  638.