scan.c
上传用户:lengbin
上传日期:2010-03-31
资源大小:121k
文件大小:38k
开发平台:

C/C++

  1. /*----------------------------------------------------------------------
  2.   File    : scan.c
  3.   Contents: scanner (lexical analysis of a character stream)
  4.   Author  : Christian Borgelt
  5.   History : 16.01.1996 file created
  6.             21.02.1996 identifier recognition made more flexible
  7.             17.03.1996 keyword tokens removed
  8.             15.04.1996 duplicate state removed from sc_next
  9.             29.07.1997 < and > declared active (for decision trees)
  10.             08.09.1997 escape sequences in strings made possible
  11.             11.09.1997 single characters stored also in scn->value
  12.             08.02.1998 recover and error message functions added
  13.             09.02.1998 bug in state S_NUMPT concerning "-." removed
  14.             13.02.1998 token T_RGT ('->') added
  15.             04.03.1998 returned tokens changed for some states
  16.             17.04.1998 token T_LFT ('<-') added
  17.             27.05.1998 token T_CMP (two char comparison operator) added
  18.             31.05.1998 token conversion to number removed
  19.             08.02.1999 reading from standard input made possible
  20.             29.04.1999 quoted string parsing improved
  21.             13.11.1999 token string length stored in scn->len
  22.             23.11.2000 functions sc_fmtlen and sc_format added
  23.             15.07.2001 scanner made an object, state definitions added
  24.             16.07.2001 characters with code > 127 made printable
  25.                        look ahead functionality added (sc_back)
  26. ----------------------------------------------------------------------*/
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <stdarg.h>
  31. #include <assert.h>
  32. #include "scan.h"
  33. #ifdef STORAGE
  34. #include "storage.h"
  35. #endif
  36. /*----------------------------------------------------------------------
  37.   Preprocessor Definitions
  38. ----------------------------------------------------------------------*/
  39. #ifdef SC_SCAN
  40. /* --- character classes --- */
  41. #define C_ILLEGAL    0          /* illegal character */
  42. #define C_SPACE      1          /* white space, e.g. ' ' 't' 'n' */
  43. #define C_LETTER     2          /* letter or underscore '_' */
  44. #define C_DIGIT      3          /* digit */
  45. #define C_POINT      4          /* point, '.' */
  46. #define C_SIGN       5          /* sign,  '+' or '-' */
  47. #define C_SLASH      6          /* slash, '/' */
  48. #define C_QUOTE      7          /* quote, e.g. '"' '`' */
  49. #define C_CMPOP      8          /* comparison operator, e.g. '<' */
  50. #define C_ACTIVE     9          /* active characters, e.g. ',' '(' */
  51. /* --- scanner states --- */
  52. #define S_SPACE      0          /* skipping white space */
  53. #define S_ID         1          /* reading identifier */
  54. #define S_NUMDIG     2          /* reading number, digit */
  55. #define S_NUMPT      3          /* reading number, decimal point */
  56. #define S_FRAC       4          /* reading number, digit and point */
  57. #define S_EXPIND     5          /* reading exponent, indicator */
  58. #define S_EXPSGN     6          /* reading exponent, sign */
  59. #define S_EXPDIG     7          /* reading exponent, digit */
  60. #define S_SIGN       8          /* sign read */
  61. #define S_CMPOP      9          /* reading comparison operator */
  62. #define S_STRING    10          /* reading quoted string */
  63. #define S_ESC       11          /* reading escaped character */
  64. #define S_OCT1      12          /* reading octal  number, 1 digit */
  65. #define S_OCT2      13          /* reading octal  number, 2 digits */
  66. #define S_HEX1      14          /* reading hexad. number, 1 digit */
  67. #define S_HEX2      15          /* reading hexad. number, 2 digits */
  68. #define S_SLASH     16          /* slash read */
  69. #define S_CPPCOM    17          /* reading C++ comment */
  70. #define S_CCOM1     18          /* reading C comment */
  71. #define S_CCOM2     19          /* reading C comment, possible end */
  72. #define S_CCOM3     20          /* reading C comment, possible start */
  73. /* --- functions --- */
  74. #define UNGETC(s,c)  do { if ((c) ==  EOF) break; 
  75.                           if ((c) == 'n') (s)->line--; 
  76.                           ungetc(c, (s)->file); } while (0)
  77. /* --- additional error codes --- */
  78. #define E_UNKNOWN   (-11)       /* unknown error */
  79. #define MSGOFFSET   (-16)       /* offset for add. error messages */
  80. /* --- texts --- */
  81. #ifdef GERMAN                   /* deutsche Texte */
  82. #define FILETXT     "Datei"
  83. #define LINETXT     "Zeile"
  84. #else                           /* English texts */
  85. #define FILETXT     "file"
  86. #define LINETXT     "line"
  87. #endif  /* #ifdef GERMAN .. #else .. */
  88. #endif  /* #ifdef SC_SCAN */
  89. /*----------------------------------------------------------------------
  90.   Constants
  91. ----------------------------------------------------------------------*/
  92. static const char _scftab[256] = {  /* scanable form classes */
  93.         /* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
  94. /* 00 */    2,   2,   2,   2,   2,   2,   2,  'a',
  95.         /*  BS   HT   LF   VT   FF   CR   SO   SI */
  96.            'b', 't', 'n', 'v', 'f', 'r',  2,   2,
  97.         /* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
  98. /* 10 */    2,   2,   2,   2,   2,   2,   2,   2,
  99.         /* CAN   EM  SUB  ESC   FS   GS   RS   US */
  100.             2,   2,   2,   2,   2,   2,   2,   2,
  101.         /* ' '  '!'  '"'  '#'  '$'  '%'  '&'  ''' */
  102. /* 20 */    1,   1,  '"',  1,   1,   1,   1,   1,
  103.         /* '('  ')'  '*'  '+'  ','  '-'  '.'  '/' */
  104.             1,   1,   1,   0,   1,   0,   0,   1,
  105.         /* '0'  '1'  '2'  '3'  '4'  '5'  '6'  '7' */
  106. /* 30 */    0,   0,   0,   0,   0,   0,   0,   0,
  107.         /* '8'  '9'  ':'  ';'  '<'  '='  '>'  '?' */
  108.             0,   0,   1,   1,   1,   1,   1,   1,
  109.         /* '@'  'A'  'B'  'C'  'D'  'E'  'F'  'G' */
  110. /* 40 */    1,   0,   0,   0,   0,   0,   0,   0,
  111.         /* 'H'  'I'  'J'  'K'  'L'  'M'  'N'  'O' */
  112.             0,   0,   0,   0,   0,   0,   0,   0,
  113.         /* 'P'  'Q'  'R'  'S'  'T'  'U'  'V'  'W' */
  114. /* 50 */    0,   0,   0,   0,   0,   0,   0,   0,
  115.         /* 'X'  'Y'  'Z'  '['  ''  ']'  '^'  '_' */
  116.             0,   0,   0,   1, '\',  1,   1,   0,
  117.         /* '`'  'a'  'b'  'c'  'd'  'e'  'f'  'g' */
  118. /* 60 */    1,   0,   0,   0,   0,   0,   0,   0,
  119.         /* 'h'  'i'  'j'  'k'  'l'  'm'  'n'  'o' */
  120.             0,   0,   0,   0,   0,   0,   0,   0,
  121.         /* 'p'  'q'  'r'  's'  't'  'u'  'v'  'w' */
  122. /* 70 */    0,   0,   0,   0,   0,   0,   0,   0,
  123.         /* 'x'  'y'  'z'  '{'  '|'  '}'  '~'  DEL */
  124.             0,   0,   0,   1,   1,   1,   1,   2,
  125. /* 80 */    1,   1,   1,   1,   1,   1,   1,   1,
  126.             1,   1,   1,   1,   1,   1,   1,   1,
  127. /* 90 */    1,   1,   1,   1,   1,   1,   1,   1,
  128.             1,   1,   1,   1,   1,   1,   1,   1,
  129. /* a0 */    1,   1,   1,   1,   1,   1,   1,   1,
  130.             1,   1,   1,   1,   1,   1,   1,   1,
  131. /* b0 */    1,   1,   1,   1,   1,   1,   1,   1,
  132.             1,   1,   1,   1,   1,   1,   1,   1,
  133. /* c0 */    1,   1,   1,   1,   1,   1,   1,   1,
  134.             1,   1,   1,   1,   1,   1,   1,   1,
  135. /* d0 */    1,   1,   1,   1,   1,   1,   1,   1,
  136.             1,   1,   1,   1,   1,   1,   1,   1,
  137. /* e0 */    1,   1,   1,   1,   1,   1,   1,   1,
  138.             1,   1,   1,   1,   1,   1,   1,   1,
  139. /* f0 */    1,   1,   1,   1,   1,   1,   1,   1,
  140.             1,   1,   1,   1,   1,   1,   1,   1 };
  141. #ifdef SC_SCAN
  142. static const char _ccltab[256] = {  /* character classes */
  143.         /* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
  144. /* 00 */    0,   0,   0,   0,   0,   0,   0,   0,
  145.         /*  BS   HT   LF   VT   FF   CR   SO   SI */
  146.             0,   1,   1,   1,   1,   1,   0,   0,
  147.         /* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
  148. /* 10 */    0,   0,   0,   0,   0,   0,   0,   0,
  149.         /* CAN   EM  SUB  ESC   FS   GS   RS   US */
  150.             0,   0,   0,   0,   0,   0,   0,   0,
  151.         /* ' '  '!'  '"'  '#'  '$'  '%'  '&'  ''' */
  152. /* 20 */    1,   8,   7,   9,   9,   9,   9,   7,
  153.         /* '('  ')'  '*'  '+'  ','  '-'  '.'  '/' */
  154.             9,   9,   9,   5,   9,   5,   4,   6,
  155.         /* '0'  '1'  '2'  '3'  '4'  '5'  '6'  '7' */
  156. /* 30 */    3,   3,   3,   3,   3,   3,   3,   3,
  157.         /* '8'  '9'  ':'  ';'  '<'  '='  '>'  '?' */
  158.             3,   3,   9,   9,   8,   8,   8,   9,
  159.         /* '@'  'A'  'B'  'C'  'D'  'E'  'F'  'G' */
  160. /* 40 */    0,   2,   2,   2,   2,   2,   2,   2,
  161.         /* 'H'  'I'  'J'  'K'  'L'  'M'  'N'  'O' */
  162.             2,   2,   2,   2,   2,   2,   2,   2,
  163.         /* 'P'  'Q'  'R'  'S'  'T'  'U'  'V'  'W' */
  164. /* 50 */    2,   2,   2,   2,   2,   2,   2,   2,
  165.         /* 'X'  'Y'  'Z'  '['  ''  ']'  '^'  '_' */
  166.             2,   2,   2,   9,   9,   9,   9,   2,
  167.         /* '`'  'a'  'b'  'c'  'd'  'e'  'f'  'g' */
  168. /* 60 */    7,   2,   2,   2,   2,   2,   2,   2,
  169.         /* 'h'  'i'  'j'  'k'  'l'  'm'  'n'  'o' */
  170.             2,   2,   2,   2,   2,   2,   2,   2,
  171.         /* 'p'  'q'  'r'  's'  't'  'u'  'v'  'w' */
  172. /* 70 */    2,   2,   2,   2,   2,   2,   2,   2,
  173.         /* 'x'  'y'  'z'  '{'  '|'  '}'  '~'  DEL */
  174.             2,   2,   2,   9,   9,   9,   9,   0,
  175. /* 80 */    0,   0,   0,   0,   0,   0,   0,   0,
  176.             0,   0,   0,   0,   0,   0,   0,   0,
  177. /* 90 */    0,   0,   0,   0,   0,   0,   0,   0,
  178.             0,   0,   0,   0,   0,   0,   0,   0,
  179. /* a0 */    0,   0,   0,   0,   0,   0,   0,   0,
  180.             0,   0,   0,   0,   0,   0,   0,   0,
  181. /* b0 */    0,   0,   0,   0,   0,   0,   0,   0,
  182.             0,   0,   0,   0,   0,   0,   0,   0,
  183. /* c0 */    0,   0,   0,   0,   0,   0,   0,   0,
  184.             0,   0,   0,   0,   0,   0,   0,   0,
  185. /* d0 */    0,   0,   0,   0,   0,   0,   0,   0,
  186.             0,   0,   0,   0,   0,   0,   0,   0,
  187. /* e0 */    0,   0,   0,   0,   0,   0,   0,   0,
  188.             0,   0,   0,   0,   0,   0,   0,   0,
  189. /* f0 */    0,   0,   0,   0,   0,   0,   0,   0,
  190.             0,   0,   0,   0,   0,   0,   0,   0 };
  191. #ifdef GERMAN                      /* deutsche Texte */
  192. static const char *_errmsgs[] = {  /* error messages */
  193.   /* E_NONE      0 */  "kein Fehler",
  194.   /* E_NOMEM    -1 */  "nicht genug Speicher",
  195.   /* E_FOPEN    -2 */  "謋fnen fehlgeschlagen",
  196.   /* E_FREAD    -3 */  "Lesefehler",
  197.   /* E_FWRITE   -4 */  "Schreibfehler",
  198.   /* E_ILLCHR   -5 */  "ung黮tiges Zeichen '%c' (0x%02x)",
  199.   /* E_BUFOVF   -6 */  "Puffer黚erlauf",
  200.   /* E_UNTSTR   -7 */  "unbeendete Zeichenkette",
  201.   /* E_UNTCOM   -8 */  "unerwartetes Dateiende in Kommentar "
  202.                        "(Anfang in Zeile %d)",
  203.   /* E_STATE    -9 */  "ung黮tiger Scannerzustand",
  204.   /* E_GARBAGE -10 */  "ung黮tiger Text am Dateiende",
  205.   /* E_UNKNOWN -11 */  "unbekannter Fehler"
  206. };
  207. #else                              /* English texts */
  208. static const char *_errmsgs[] = {  /* error messages */
  209.   /* E_NONE      0 */  "no error",
  210.   /* E_NOMEM    -1 */  "not enough memory",
  211.   /* E_FOPEN    -2 */  "file open failed",
  212.   /* E_FREAD    -3 */  "file read failed",
  213.   /* E_FWRITE   -4 */  "file write failed",
  214.   /* E_ILLCHR   -5 */  "illegal character '%c' (0x%02x)",
  215.   /* E_BUFOVF   -6 */  "scan buffer overflow",
  216.   /* E_UNTSTR   -7 */  "unterminated string",
  217.   /* E_UNTCOM   -8 */  "unexpected end of file in comment "
  218.                        "started on line %d",
  219.   /* E_STATE    -9 */  "illegal scanner state",
  220.   /* E_GARBAGE -10 */  "garbage at end of file",
  221.   /* E_UNKNOWN -11 */  "unknown error"
  222. };
  223. #endif  /* #ifdef GERMAN .. #else .. */
  224. #endif  /* #ifdef SC_SCAN */
  225. /*----------------------------------------------------------------------
  226.   Auxiliary Functions
  227. ----------------------------------------------------------------------*/
  228. #ifdef SC_SCAN
  229. static int _swap (SCAN *scan)
  230. {                               /* --- swap token information */
  231.   int t;                        /* swap buffer */
  232.   if (scan->value == scan->buf[0]) scan->value = scan->buf[1];
  233.   else                             scan->value = scan->buf[0];
  234.   t = scan->plen;   scan->plen   = scan->len;   scan->len   = t;
  235.   t = scan->pline;  scan->pline  = scan->line;  scan->line  = t;
  236.   t = scan->ptoken; scan->ptoken = scan->token; scan->token = t;
  237.   return t;                     /* return the new token */
  238. }  /* _swap() */
  239. #endif
  240. /*----------------------------------------------------------------------
  241.   Main Functions
  242. ----------------------------------------------------------------------*/
  243. int sc_fmtlen (const char *s, int *len)
  244. {                               /* --- length of a formatted name */
  245.   int n = 0, k = 0;             /* number of (additional) characters */
  246.   int q = 0;                    /* quote flag (default: no quotes) */
  247.   assert(s);                    /* check the function arguments */
  248.   while (*s) {                  /* while not at end of name */
  249.     n++;                        /* count character */
  250.     switch (_scftab[(unsigned char)*s++]) {
  251.       case  0:                break;
  252.       case  1:         q = 2; break;
  253.       case  2: k += 3; q = 2; break;
  254.       default: k += 1; q = 2; break;
  255.     }                           /* sum additional characters and */
  256.   }                             /* set quote flag (if necessary) */
  257.   if (len) *len = n;            /* store normal length and */
  258.   return n +k +q;               /* return length of scanable form */
  259. }  /* sc_fmtlen() */
  260. /*--------------------------------------------------------------------*/
  261. int sc_format (char *dst, const char *src, int quotes)
  262. {                               /* --- format name in scanable form */
  263.   char *d; const char *s;       /* to traverse buffer and name */
  264.   int  c, cls;                  /* character and character class */
  265.   int  t;                       /* temporary buffer */
  266.   assert(dst && src);           /* check the function arguments */
  267.   if (!*src) quotes = 1;        /* an empty name needs quotes */
  268.   if (!quotes) {                /* if quotes are not mandatory, */
  269.     for (s = src; *s; )         /* traverse the string to convert */
  270.       if (_scftab[(unsigned char)*s++] != 0) {
  271.         quotes = 1; break; }    /* if a character needs quotes, */
  272.   }                             /* set the quotes flag and abort */
  273.   d = dst;                      /* get the destination and */
  274.   if (quotes) *d++ = '"';       /* store a quote if necessary */
  275.   while (*src) {                /* traverse the characters */
  276.     c   = (unsigned char)*src++;/* get the next character */
  277.     cls = _scftab[c];           /* and its character class */
  278.     if      (cls < 2)           /* if it is a normal character, */
  279.       *d++ = c;                 /* just store it */
  280.     else if (cls > 2) {         /* if it is an ANSI escape character, */
  281.       *d++ = '\'; *d++ = cls;} /* store it as 'c' */
  282.     else {                      /* if it is any other character */
  283.       *d++ = '\'; *d++ = 'x';
  284.       t = c >> 4;  *d++ = (t > 9) ? (t -10 +'a') : (t +'0');
  285.       t = c & 0xf; *d++ = (t > 9) ? (t -10 +'a') : (t +'0');
  286.     }                           /* store the character code */
  287.   }                             /* as a hexadecimal number */
  288.   if (quotes) *d++ = '"';       /* store the closing quote */
  289.   *d = '';                    /* and terminate the string */
  290.   return (int)(d -dst);         /* return the length of the result */
  291. }  /* sc_format() */
  292. /*--------------------------------------------------------------------*/
  293. #ifdef SC_SCAN
  294. SCAN* sc_create (const char *fname)
  295. {                               /* --- create a scanner */
  296.   const char *fn = fname;       /* buffer for filename */
  297.   SCAN       *scan;             /* created scanner */
  298.   if (!fn || !*fn) fname = "<stdin>";
  299.   scan = (SCAN*)malloc(sizeof(SCAN) +strlen(fname));
  300.   if (!scan) return NULL;       /* allocate memory for a scanner */
  301.   strcpy(scan->fname, fname);   /* and note the file name */
  302.   if (!fn || !*fn)              /* if no file name is given, */
  303.     scan->file = stdin;         /* read from standard input */
  304.   else {                        /* if a  file name is given, */
  305.     scan->file = fopen(fn,"r"); /* open the file for reading */
  306.     if (!scan->file) { free(scan); return NULL; }
  307.   }
  308.   scan->line    = 1;            /* initialize the fields */
  309.   scan->token   = scan->len   = scan->start = 0;
  310.   scan->value   = scan->buf[0]; scan->buf[0][0] = '';
  311.   scan->back    = 0;
  312.   scan->errfile = stderr;
  313.   scan->msgcnt  = scan->lncnt = 0;
  314.   scan->msgs    = NULL;
  315.   return scan;                  /* return created scanner */
  316. }  /* sc_create() */
  317. /*--------------------------------------------------------------------*/
  318. void sc_delete (SCAN *scan)
  319. {                               /* --- delete a scanner */
  320.   if (scan->file != stdin) fclose(scan->file);
  321.   free(scan);                   /* close the input file and */
  322. }  /* sc_delete() */            /* delete the scanner structure */
  323. /*--------------------------------------------------------------------*/
  324. int sc_next (SCAN *scan)
  325. {                               /* --- get next token */
  326.   int  c, ccl;                  /* character and character class */
  327.   int  quote = 0;               /* quote at the start of a string */
  328.   int  ec    = 0;               /* escaped character */
  329.   int  state = 0;               /* state of automaton */
  330.   int  level = 0;               /* comment nesting level */
  331.   char *p;                      /* to traverse the scan buffer */
  332.   char *end;                    /* end of the scan buffer */
  333.   if (scan->back) {             /* if a step backwards has been made, */
  334.     scan->back = 0;             /* clear the corresponding flag, */
  335.     return _swap(scan);         /* swap back the token information, */
  336.   }                             /* and return the current token */
  337.   scan->pline  = scan->line;    /* note the relevant information */
  338.   scan->ptoken = scan->token;   /* of the current token */
  339.   scan->plen   = scan->len;     /* and swap scan buffers */
  340.   if (scan->value == scan->buf[0]) scan->value = p = scan->buf[1];
  341.   else                             scan->value = p = scan->buf[0];
  342.   end = p +SC_BUFSIZE -1;       /* get the end of the scan buffer */
  343.   while (1) {                   /* read loop */
  344.     c   = getc(scan->file);     /* get character and character class */
  345.     ccl = (c < 0) ? EOF : _ccltab[c];
  346.     if (c == 'n') scan->line++; /* count the line */
  347.     switch (state) {            /* evaluate state of automaton */
  348.       case S_SPACE:             /* --- skip white space */
  349.         switch (ccl) {          /* evaluate character category */
  350.           case C_SPACE : /* do nothing */             break;
  351.           case C_LETTER: *p++  = c; state = S_ID;     break;
  352.           case C_DIGIT : *p++  = c; state = S_NUMDIG; break;
  353.           case C_POINT : *p++  = c; state = S_NUMPT;  break;
  354.           case C_SIGN  : *p++  = c; state = S_SIGN;   break;
  355.           case C_CMPOP : *p++  = c; state = S_CMPOP;  break;
  356.           case C_QUOTE : quote = c; state = S_STRING; break;
  357.           case C_SLASH :            state = S_SLASH;  break;
  358.           case C_ACTIVE: *p++  = c; *p = ''; scan->len = 1;
  359.                          return scan->token = c;
  360.           case EOF     : strcpy(p, "<eof>");   scan->len = 4;
  361.                          return scan->token = (ferror(scan->file))
  362.                                            ? E_FREAD : T_EOF;
  363.           default      : *p++  = c; *p = ''; scan->len = 1;
  364.                          return scan->token = E_ILLCHR;
  365.         } break;
  366.       case S_ID:                /* --- identifier (letter read) */
  367.         if ((ccl == C_LETTER)   /* if another letter */
  368.         ||  (ccl == C_DIGIT)    /* or a digit */
  369.         ||  (ccl == C_POINT)    /* or a decimal point */
  370.         ||  (ccl == C_SIGN)) {  /* or a sign follows */
  371.           if (p >= end) return scan->token = E_BUFOVF;
  372.           *p++ = c; break;      /* buffer character */
  373.         }                       /* otherwise */
  374.         UNGETC(scan, c);        /* put back last character, */
  375.         *p = '';              /* terminate string in buffer */
  376.         scan->len = (int)(p -scan->value); /* set string length */
  377.         return scan->token = T_ID;   /* and return 'identifier' */
  378.       case S_NUMDIG:            /* --- number (digit read) */
  379.         if (p < end) *p++ = c;  /* buffer character */
  380.         else return scan->token = E_BUFOVF;
  381.         if  (ccl == C_DIGIT)    /* if another digit follows, */
  382.           break;                /* do nothing */
  383.         if  (ccl == C_POINT) {  /* if a decimal point follows, */
  384.           state = S_FRAC;   break; } /* go to 'fraction' state */
  385.         if ((c == 'e')          /* if an exponent indicator follows */
  386.         ||  (c == 'E')) {       /* (lower- or uppercase), */
  387.           state = S_EXPIND; break; } /* go to 'exponent' state */
  388.         if ((ccl == C_LETTER)   /* if a letter */
  389.         ||  (ccl == C_SIGN)) {  /* or a sign follows, */
  390.           state = S_ID; break;  /* go to 'identifier' state */
  391.         }                       /* otherwise */
  392.         UNGETC(scan, c);        /* put back last character, */
  393.         *--p = '';            /* terminate string in buffer */
  394.         scan->len = (int)(p -scan->value); /* set string length */
  395.         return scan->token = T_NUM;      /* and return 'number' */
  396.       case S_NUMPT:             /* --- number (point read) */
  397.         if (p < end) *p++ = c;  /* buffer character */
  398.         else return scan->token = E_BUFOVF;
  399.         if  (ccl == C_DIGIT) {       /* if a digit follows, */
  400.           state = S_FRAC; break; }   /* go to 'fraction' state */
  401.         if ((ccl == C_LETTER)   /* if a letter */
  402.         ||  (ccl == C_POINT)    /* or a decimal point */
  403.         ||  (ccl == C_SIGN)) {  /* or a sign follows */
  404.           state = S_ID; break;  /* go to 'identifier' state */
  405.         }                       /* otherwise */
  406.         UNGETC(scan, c);        /* put back last character, */
  407.         *--p = '';            /* terminate string in buffer */
  408.         scan->len = (int)(p -scan->value); /* set string length */
  409.         return scan->token = T_ID;   /* and return 'identifier' */
  410.       case S_FRAC:              /* --- number (digit & point read) */
  411.         if (p < end) *p++ = c;  /* buffer character */
  412.         else return scan->token = E_BUFOVF;
  413.         if  (ccl == C_DIGIT)    /* if another digit follows, */
  414.           break;                /* do nothing else */
  415.         if ((c == 'e')          /* if an exponent indicator follows, */
  416.         ||  (c == 'E')) {       /* (lower- or uppercase), */
  417.           state = S_EXPIND; break; } /* go to exponent state */
  418.         if ((ccl == C_LETTER)   /* if a letter */
  419.         ||  (ccl == C_POINT)    /* or a decimal point */
  420.         ||  (ccl == C_SIGN)) {  /* or a sign follows, */
  421.           state = S_ID; break;  /* go to 'identifier' state */
  422.         }                       /* otherwise */
  423.         UNGETC(scan, c);        /* put back last character, */
  424.         *--p = '';            /* terminate string in buffer */
  425.         scan->len = (int)(p -scan->value); /* set string length */
  426.         return scan->token = T_NUM;      /* and return 'number' */
  427.       case S_EXPIND:            /* --- exponent (indicator read) */
  428.         if (p < end) *p++ = c;  /* buffer character */
  429.         else return scan->token = E_BUFOVF;
  430.         if  (ccl == C_SIGN) {        /* if a sign follows, */
  431.           state = S_EXPSGN; break; } /* go to 2nd 'exponent' state */
  432.         if  (ccl == C_DIGIT) {       /* if a digit follows, */
  433.           state = S_EXPDIG; break; } /* go to 3rd 'exponent' state */
  434.         if ((ccl == C_LETTER)   /* if a letter */
  435.         ||  (ccl == C_POINT)) { /* or a decimal point follows */
  436.           state = S_ID; break;  /* go to 'identifier' state */
  437.         }                       /* otherwise */
  438.         UNGETC(scan, c);        /* put back last character, */
  439.         *--p = '';            /* terminate string in buffer */
  440.         scan->len = (int)(p -scan->value); /* set string length */
  441.         return scan->token = T_ID;   /* and return 'identifier' */
  442.       case S_EXPSGN:            /* --- exponent (sign read) */
  443.         if (p < end) *p++ = c;  /* buffer character */
  444.         else return scan->token = E_BUFOVF;
  445.         if  (ccl == C_DIGIT) {      /* if a digit follows, */
  446.           state = S_EXPDIG; break;} /* do nothing else */
  447.         if ((ccl == C_LETTER)   /* if a letter */
  448.         ||  (ccl == C_POINT)    /* or a decimal point */
  449.         ||  (ccl == C_SIGN)) {  /* or a sign follows */
  450.           state = S_ID; break;  /* go to 'identifier' state */
  451.         }                       /* otherwise */
  452.         UNGETC(scan, c);        /* put back last character, */
  453.         *--p = '';            /* terminate string in buffer */
  454.         scan->len = (int)(p -scan->value); /* set string length */
  455.         return scan->token = T_ID;   /* and return 'identifier' */
  456.       case S_EXPDIG:            /* --- exponent (digit read) */
  457.         if (p < end) *p++ = c;  /* buffer character */
  458.         else return scan->token = E_BUFOVF;
  459.         if  (ccl == C_DIGIT)    /* if another digit follows, */
  460.           break;                /* do nothing else */
  461.         if ((ccl == C_LETTER)   /* if a letter */
  462.         ||  (ccl == C_POINT)    /* or a decimal point */
  463.         ||  (ccl == C_SIGN)) {  /* or a sign follows, */
  464.           state = S_ID; break;  /* go to 'identifier' state */
  465.         }                       /* otherwise */
  466.         UNGETC(scan, c);        /* put back last character, */
  467.         *--p = '';            /* terminate string in buffer */
  468.         scan->len = (int)(p -scan->value); /* set string length */
  469.         return scan->token = T_NUM;      /* and return 'number' */
  470.       case S_SIGN:              /* --- number (sign read) */
  471.         *p++ = c;               /* buffer character */
  472.         if  (ccl == C_DIGIT) {       /* if a digit follows, */
  473.           state = S_NUMDIG; break; } /* go to 'number' state */
  474.         if  (ccl == C_POINT) {       /* if a decimal point follows, */
  475.           state = S_NUMPT; break; }  /* go to fraction state */
  476.         if ((ccl == C_LETTER)        /* if a letter */
  477.         ||  (ccl == C_SIGN)) {       /* or a sign follows, */
  478.           state = S_ID; break; }     /* go to 'identifier' state */
  479.         if ((c == '>')          /* if a '>' follows and previous */
  480.         &&  (scan->value[0] == '-')) {   /* char was a minus sign */
  481.           *p = ''; scan->len = 2; return scan->token = T_RGT; }
  482.         UNGETC(scan, c);        /* otherwise put back last character, */
  483.         *--p = '';            /* terminate string in buffer */
  484.         scan->len = (int)(p -scan->value); /* set string length */
  485.         return scan->token = T_ID;   /* and return 'identifier' */
  486.       case S_CMPOP:             /* --- comparison operator read */
  487.         if ((c == '-')          /* if a minus sign follows and */
  488.         &&  (scan->value[0] == '<')) {  /* prev. char was a '<' */
  489.           *p++ = '-';       scan->token = T_LFT; }
  490.         else if (c == '=') {    /* if an equal sign follows */
  491.           *p++ = '=';       scan->token = T_CMP; }
  492.         else {                  /* if anything else follows */
  493.           UNGETC(scan, c);  scan->token = scan->value[0]; }
  494.         *p = '';              /* terminate string in buffer */
  495.         scan->len = (int)(p -scan->value); /* set string length */
  496.         return scan->token;        /* and return the token read */
  497.       case S_STRING:            /* --- quoted string */
  498.         if ((c == 'n') || (c == EOF))  /* if end of line or file, */
  499.           return scan->token = E_UNTSTR;   /* string is unterminated */
  500.         if (c != quote) {       /* if not at end of string */
  501.           if (p >= end) return scan->token = E_BUFOVF;
  502.           if (c == '\') {      /* if escaped character follows, */
  503.             state = S_ESC; break; }  /* go to escaped char state */
  504.           *p++ = c; break;      /* otherwise buffer character */
  505.         }                       /* if at end of string, */
  506.         *p = '';              /* terminate string in buffer */
  507.         scan->len = (int)(p -scan->value); /* set string length */
  508.         return scan->token = T_ID;   /* and return 'identifier' */
  509.       case S_ESC:               /* --- after '' in quoted string */
  510.         if ((c >= '0') && (c <= '7')) {        /* if octal digit, */
  511.           ec = c -'0'; state = S_OCT1; break; }/* evaluate digit  */
  512.         if (c == 'x') {         /* if hexadecimal character code, */
  513.           state = S_HEX1; break;} /* go to hexadecimal evaluation */
  514.         switch (c) {            /* evaluate character after '' */
  515.           case  'a': c = 'a'; break;
  516.           case  'b': c = 'b'; break;
  517.           case  'f': c = 'f'; break;
  518.           case  'n': c = 'n'; break;
  519.           case  'r': c = 'r'; break;
  520.           case  't': c = 't'; break;
  521.           case  'v': c = 'v'; break;
  522.           case 'n': c = -1;   break;
  523.           default  :           break;
  524.         }                       /* get escaped character */
  525.         if (c >= 0) *p++ = c;   /* and store it, then */
  526.         state = S_STRING; break;/* return to quoted string state */
  527.       case S_OCT1:              /* --- escaped octal number 1 */
  528.         if ((c >= '0')          /* if an octal digit follows, */
  529.         &&  (c <= '7')) {       /* evaluate it */
  530.           ec = ec *8 +c -'0'; state = S_OCT2; break; }
  531.         UNGETC(scan, c);        /* otherwise put back last character */
  532.         *p++  = ec;             /* store escaped character and */
  533.         state = S_STRING; break;/* return to quoted string state */
  534.       case S_OCT2:              /* --- escaped octal number 2 */
  535.         if ((c >= '0') || (c <= '7'))
  536.           ec = ec *8 +c -'0';   /* if octal digit, evaluate it */
  537.         else UNGETC(scan, c);   /* otherwise put back last character */
  538.         *p++  = ec;             /* store escaped character and */
  539.         state = S_STRING; break;/* return to quoted string state */
  540.       case S_HEX1:              /* --- escaped hexadecimal number 1 */
  541.         if (ccl == C_DIGIT) {   /* if hexadecimal digit, evaluate it */
  542.           ec = c -'0';     state = S_HEX2; break; }
  543.         if ((c >= 'a') && (c <= 'f')) {
  544.           ec = c -'a' +10; state = S_HEX2; break; }
  545.         if ((c >= 'A') && (c <= 'F')) {
  546.           ec = c -'A' +10; state = S_HEX2; break; }
  547.         UNGETC(scan, c);        /* otherwise put back last character */
  548.         *p++  = 'x';            /* store escaped character ('x') and */
  549.         state = S_STRING; break;/* return to quoted string state */
  550.       case S_HEX2:              /* --- escaped hexadecimal number 2 */
  551.         if (ccl == C_DIGIT)     /* if hexadecimal digit, evaluate it */
  552.           ec = ec*16 +c -'0';
  553.         else if ((c >= 'a') && (c <= 'f'))
  554.           ec = ec*16 +c -'a' +10;
  555.         else if ((c >= 'A') && (c <= 'F'))
  556.           ec = ec*16 +c -'A' +10;
  557.         else UNGETC(scan, c);   /* otherwise put back last character */
  558.         *p++  = ec;             /* store escaped character and */
  559.         state = S_STRING; break;/* return to quoted string state */
  560.       case S_SLASH:             /* --- slash '/' */
  561.         if (c == '/') {         /* if C++ style comment, then */
  562.           state = S_CPPCOM; break; }   /* skip to end of line */
  563.         if (c == '*') {         /* if C style comment */
  564.           scan->start = scan->line; level = 1;
  565.           state = S_CCOM1; break;    /* note start line, init. level */
  566.         }                       /* and go to first 'comment' state */
  567.         UNGETC(scan, c);        /* otherwise put back last character */
  568.         *p++ = '/'; *p = '';  /* store character in buffer */
  569.         scan->len = 1;          /* set string length and */
  570.         return scan->token = '/';  /* return `character' */
  571.       case S_CPPCOM:            /* --- C++ style comment */
  572.         if ((c == 'n')         /* if at end of line */
  573.         ||  (c == EOF))         /* or at end of file */
  574.           state = S_SPACE;      /* return to white space skipping */
  575.         break;                  /* (skip to end of line) */
  576.       case S_CCOM1:             /* --- C style comment 1 */
  577.         if      (c == EOF)      /* if end of file, abort */
  578.           return scan->token = E_UNTCOM;
  579.         if      (c == '*')      /* if possibly 'end of comment', */
  580.           state = S_CCOM2;      /* go to 2nd 'comment' state */
  581.         else if (c == '/')      /* if possibly 'start of comment', */
  582.           state = S_CCOM3;      /* go to 3rd 'comment' state */
  583.         break;
  584.       case S_CCOM2:             /* --- C style comment 2 */
  585.         if      (c == EOF)      /* if end of file, abort */
  586.           return scan->token = E_UNTCOM;
  587.         if      (c == '/') {    /* if end of comment found */
  588.           if (--level <= 0) state = S_SPACE;
  589.           else              state = S_CCOM1; }
  590.         else if (c != '*')      /* if end of comment impossible */
  591.           state = S_CCOM1;      /* return to comment skipping */
  592.         break;                  /* (possible start of comment) */
  593.       case S_CCOM3:             /* --- C style comment 3 */
  594.         if      (c == EOF)      /* if end of file, abort */
  595.           return scan->token = E_UNTCOM;
  596.         if      (c == '*') {    /* if start of comment found */
  597.           level++; state = S_CCOM1; }
  598.         else if (c != '/')      /* if start of comment impossible */
  599.           state = S_CCOM1;      /* return to comment skipping */
  600.         break;                  /* (possible end of comment) */
  601.       default:                  /* if state is illegal, abort */
  602.         return scan->token = E_STATE;
  603.     }  /* switch() */
  604.   }  /* while(1) */
  605. }  /* sc_next() */
  606. /*--------------------------------------------------------------------*/
  607. int sc_nexter (SCAN *scan)
  608. {                               /* --- get next token error reporting */
  609.   if (sc_next(scan) < 0) return sc_error(scan, scan->token);
  610.   return scan->token;           /* get next token, report error, */
  611. }  /* sc_nexter() */            /* and return next token */
  612. /*--------------------------------------------------------------------*/
  613. int sc_back (SCAN *scan)
  614. {                               /* --- go back one token */
  615.   if (scan->back)               /* a second step backwards */
  616.     return scan->token;         /* is impossible, so do nothing */
  617.   scan->back = -1;              /* set the step backward flag */
  618.   return _swap(scan);           /* swap the token information */
  619. }  /* sc_back() */              /* and return the previous token */
  620. /*--------------------------------------------------------------------*/
  621. int sc_eof (SCAN *scan)
  622. {                               /* --- check for end of file */
  623.   if (scan->token == T_EOF) return 1;
  624.   sc_error(scan, E_GARBAGE);    /* check for end of file */
  625.   return 0;                     /* and report an error */
  626. }  /* sc_eof() */               /* if it is not reached */
  627. /*--------------------------------------------------------------------*/
  628. int sc_recover (SCAN *scan, int stop, int beg, int end, int level)
  629. {                               /* --- recover from an error */
  630.   while ((scan->token != stop)     /* while at stop token */
  631.   &&     (scan->token != T_EOF)) { /* and not at end of file */
  632.     if       (scan->token == beg)  /* if begin level token found, */
  633.       level++;                     /* increment level counter */
  634.     else if ((scan->token == end)  /* if end level token found */
  635.     &&       (--level    <= 0))    /* and on level to return to, */
  636.       break;                       /* abort loop */
  637.     if (sc_next(scan) < 0) return scan->token;
  638.   }                             /* consume token */
  639.   if (scan->token != T_EOF)     /* if not at end of file, */
  640.     sc_next(scan);              /* consume token (stop or end) */
  641.   return scan->token;           /* return the next token */
  642. }  /* sc_recover() */
  643. /*--------------------------------------------------------------------*/
  644. void sc_errfile (SCAN *scan, FILE *file, int lncnt)
  645. {                               /* --- set file for error output */
  646.   assert(scan);                 /* check the function arguments */
  647.   scan->errfile = (file) ? file : stderr;
  648.   scan->lncnt   = lncnt;        /* set file and line count */
  649. }  /* sc_errfile() */
  650. /*--------------------------------------------------------------------*/
  651. void sc_errmsgs (SCAN *scan, const char *msgs[], int cnt)
  652. {                                /* --- set additonal error messages */
  653.   assert(scan);                  /* check the function arguments */
  654.   scan->msgs   = msgs;           /* note error message vector */
  655.   scan->msgcnt = cnt;            /* and number of error messages */
  656. }  /* sc_errmsgs() */
  657. /*--------------------------------------------------------------------*/
  658. int sc_error (SCAN *scan, int code, ...)
  659. {                               /* --- print an error message */
  660.   va_list    args;              /* variable argument list */
  661.   const char *msg;              /* error message */
  662.   int        c, pc;             /* the illegal character */
  663.   int        tmp;               /* temporary buffer */
  664.   assert(scan);                  /* check the function arguments */
  665.   if (scan->lncnt <= 0)         /* if line count is zero or negative, */
  666.     putc('n', scan->errfile);  /* start a new output line */
  667.   fprintf(scan->errfile, FILETXT" %s", scan->fname);
  668.                                 /* print the file name */
  669.   if ((code != E_NONE)          /* if an error occurred, */
  670.   &&  (code != E_FOPEN)         /* but not 'file open failed' */
  671.   &&  (code != E_UNTCOM)) {     /* and not 'unterminated comment' */
  672.     fputs((scan->lncnt > 2) ? ",n" : ", ", scan->errfile);
  673.     fprintf(scan->errfile, LINETXT" %d", scan->line);
  674.   }                             /* print line number */
  675.   fputs((scan->lncnt > 1) ? ":n" : ": ", scan->errfile);
  676.   if (code >= 0) code = E_NONE; /* check error code and */
  677.   tmp = MSGOFFSET -code;        /* get error message text */
  678.   if      (code > E_UNKNOWN)    msg = _errmsgs[-code];
  679.   else if (tmp  < scan->msgcnt) msg = scan->msgs[tmp];
  680.   else                          msg = NULL;
  681.   if (!msg) msg = _errmsgs[-(code = E_UNKNOWN)];
  682.   switch (code) {               /* special error handling */
  683.     case E_ILLCHR: c = pc = (unsigned char)scan->value[0];
  684.                    if (c < ' ') pc = ' ';
  685.                    fprintf (scan->errfile, msg, pc, c);       break;
  686.     case E_UNTCOM: fprintf (scan->errfile, msg, scan->start); break;
  687.     default      : va_start(args, code); /* get variable arguments */
  688.                    vfprintf(scan->errfile, msg, args);
  689.                    va_end(args); break;  /* print error message and */
  690.   }                             /* end variable argument evaluation */
  691.   if (scan->lncnt > 0)          /* if line count is positive, */
  692.     putc('n', scan->errfile);  /* terminate output line */
  693.   return code;                  /* return error code */
  694. }  /* sc_error() */
  695. #endif