#include "c.h"
#include <float.h>
#include <errno.h>

static char rcsid[] = "$Id: lex.nw,v 2.13 1997/06/23 17:11:22 drh Exp $";

#define MAXTOKEN 32

enum { BLANK=01,  NEWLINE=02, LETTER=04,
       DIGIT=010, HEX=020,    OTHER=040 };

static unsigned char map[256] = { /* 000 nul */   0,
           /* 001 soh */   0,
           /* 002 stx */   0,
           /* 003 etx */   0,
           /* 004 eot */   0,
           /* 005 enq */   0,
           /* 006 ack */   0,
           /* 007 bel */   0,
           /* 010 bs  */   0,
           /* 011 ht  */   BLANK,
           /* 012 nl  */   NEWLINE,
           /* 013 vt  */   BLANK,
           /* 014 ff  */   BLANK,
           /* 015 cr  */   0,
           /* 016 so  */   0,
           /* 017 si  */   0,
           /* 020 dle */   0,
           /* 021 dc1 */   0,
           /* 022 dc2 */   0,
           /* 023 dc3 */   0,
           /* 024 dc4 */   0,
           /* 025 nak */   0,
           /* 026 syn */   0,
           /* 027 etb */   0,
           /* 030 can */   0,
           /* 031 em  */   0,
           /* 032 sub */   0,
           /* 033 esc */   0,
           /* 034 fs  */   0,
           /* 035 gs  */   0,
           /* 036 rs  */   0,
           /* 037 us  */   0,
           /* 040 sp  */   BLANK,
           /* 041 !   */   OTHER,
           /* 042 "   */   OTHER,
           /* 043 #   */   OTHER,
           /* 044 $   */   0,
           /* 045 %   */   OTHER,
           /* 046 &   */   OTHER,
           /* 047 '   */   OTHER,
           /* 050 (   */   OTHER,
           /* 051 )   */   OTHER,
           /* 052 *   */   OTHER,
           /* 053 +   */   OTHER,
           /* 054 ,   */   OTHER,
           /* 055 -   */   OTHER,
           /* 056 .   */   OTHER,
           /* 057 /   */   OTHER,
           /* 060 0   */   DIGIT,
           /* 061 1   */   DIGIT,
           /* 062 2   */   DIGIT,
           /* 063 3   */   DIGIT,
           /* 064 4   */   DIGIT,
           /* 065 5   */   DIGIT,
           /* 066 6   */   DIGIT,
           /* 067 7   */   DIGIT,
           /* 070 8   */   DIGIT,
           /* 071 9   */   DIGIT,
           /* 072 :   */   OTHER,
           /* 073 ;   */   OTHER,
           /* 074 <   */   OTHER,
           /* 075 =   */   OTHER,
           /* 076 >   */   OTHER,
           /* 077 ?   */   OTHER,
           /* 100 @   */   0,
           /* 101 A   */   LETTER|HEX,
           /* 102 B   */   LETTER|HEX,
           /* 103 C   */   LETTER|HEX,
           /* 104 D   */   LETTER|HEX,
           /* 105 E   */   LETTER|HEX,
           /* 106 F   */   LETTER|HEX,
           /* 107 G   */   LETTER,
           /* 110 H   */   LETTER,
           /* 111 I   */   LETTER,
           /* 112 J   */   LETTER,
           /* 113 K   */   LETTER,
           /* 114 L   */   LETTER,
           /* 115 M   */   LETTER,
           /* 116 N   */   LETTER,
           /* 117 O   */   LETTER,
           /* 120 P   */   LETTER,
           /* 121 Q   */   LETTER,
           /* 122 R   */   LETTER,
           /* 123 S   */   LETTER,
           /* 124 T   */   LETTER,
           /* 125 U   */   LETTER,
           /* 126 V   */   LETTER,
           /* 127 W   */   LETTER,
           /* 130 X   */   LETTER,
           /* 131 Y   */   LETTER,
           /* 132 Z   */   LETTER,
           /* 133 [   */   OTHER,
           /* 134 \   */   OTHER,
           /* 135 ]   */   OTHER,
           /* 136 ^   */   OTHER,
           /* 137 _   */   LETTER,
           /* 140 `   */   0,
           /* 141 a   */   LETTER|HEX,
           /* 142 b   */   LETTER|HEX,
           /* 143 c   */   LETTER|HEX,
           /* 144 d   */   LETTER|HEX,
           /* 145 e   */   LETTER|HEX,
           /* 146 f   */   LETTER|HEX,
           /* 147 g   */   LETTER,
           /* 150 h   */   LETTER,
           /* 151 i   */   LETTER,
           /* 152 j   */   LETTER,
           /* 153 k   */   LETTER,
           /* 154 l   */   LETTER,
           /* 155 m   */   LETTER,
           /* 156 n   */   LETTER,
           /* 157 o   */   LETTER,
           /* 160 p   */   LETTER,
           /* 161 q   */   LETTER,
           /* 162 r   */   LETTER,
           /* 163 s   */   LETTER,
           /* 164 t   */   LETTER,
           /* 165 u   */   LETTER,
           /* 166 v   */   LETTER,
           /* 167 w   */   LETTER,
           /* 170 x   */   LETTER,
           /* 171 y   */   LETTER,
           /* 172 z   */   LETTER,
           /* 173 {   */   OTHER,
           /* 174 |   */   OTHER,
           /* 175 }   */   OTHER,
           /* 176 ~   */   OTHER, };
static struct symbol tval;
Coordinate src;         /* current source coordinate */
int t;
char *token;            /* current token */
Symbol tsym;            /* symbol table entry for current token */

static int backslash(int q);
static Symbol fcon(void);
static Symbol icon(unsigned long, int, int);
static void ppnumber(char *);

int gettok(void) {
  for (;;) {
    register unsigned char *rcp = cp;
    while (map[*rcp]&BLANK)
      rcp++;
    if (limit - rcp < MAXTOKEN) {
      cp = rcp;
      fillbuf();
      rcp = cp;
    }
    src.file = file;
    src.x = (char *)rcp - line;
    src.y = lineno;
    cp = rcp + 1;
    switch (*rcp++) {
    case '/': if (*rcp == '*') {
          int c = 0;
          for (rcp++; *rcp != '/' || c != '*'; )
            if (map[*rcp]&NEWLINE) {
              if (rcp < limit)
                c = *rcp;
              cp = rcp + 1;
              nextline();
              rcp = cp;
              if (rcp == limit)
                break;
            } else
              c = *rcp++;
          if (rcp < limit)
            rcp++;
          else
            error("unclosed comment\n");
          cp = rcp;
          continue;
        }
        return '/';
    case 'L': if (*rcp == '\'') {
          int t = gettok();
          assert(t == ICON);
          src.x--;
          tval.type = unsignedchar;
          tval.u.c.v.u = (unsigned char)tval.u.c.v.i;
          return t;
        }
        if (*rcp != '"')
          goto id;
        cp = rcp + 1;
        goto scon;
    case '<':
      if (*rcp == '=') return cp++, LEQ;
      if (*rcp == '<') return cp++, LSHIFT;
      return '<';
    case '>':
      if (*rcp == '=') return cp++, GEQ;
      if (*rcp == '>') return cp++, RSHIFT;
      return '>';
    case '-':
      if (*rcp == '>') return cp++, DEREF;
      if (*rcp == '-') return cp++, DECR;
      return '-';
    case '=': return *rcp == '=' ? cp++, EQL    : '=';
    case '!': return *rcp == '=' ? cp++, NEQ    : '!';
    case '|': return *rcp == '|' ? cp++, OROR   : '|';
    case '&': return *rcp == '&' ? cp++, ANDAND : '&';
    case '+': return *rcp == '+' ? cp++, INCR   : '+';
    case ';': case ',': case ':':
    case '*': case '~': case '%': case '^': case '?':
    case '[': case ']': case '{': case '}': case '(': case ')':
      return rcp[-1];
    case '\n': case '\v': case '\r': case '\f':
      nextline();
      if (cp == limit) {
        tsym = NULL;
        return EOI;
      }
      continue;

    case 'i':
      if (rcp[0] == 'f'
      && !(map[rcp[1]]&(DIGIT|LETTER))) {
        cp = rcp + 1;
        return IF;
      }
      if (rcp[0] == 'n'
      &&  rcp[1] == 't'
      && !(map[rcp[2]]&(DIGIT|LETTER))) {
        cp = rcp + 2;
        tsym = inttype->u.sym;
        return INT;
      }
      goto id;
    case 'h': case 'j': case 'k': case 'm': case 'n': case 'o':
    case 'p': case 'q': case 'x': case 'y': case 'z':
    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
    case 'G': case 'H': case 'I': case 'J': case 'K':
    case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
    case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
    case 'Y': case 'Z':
    id:
      if (limit - rcp < MAXLINE) {
        cp = rcp - 1;
        fillbuf();
        rcp = ++cp;
      }
      assert(cp == rcp);
      token = (char *)rcp - 1;
      while (map[*rcp]&(DIGIT|LETTER))
        rcp++;
      token = stringn(token, (char *)rcp - token);
      tsym = lookup(token, identifiers);
      cp = rcp;
      return ID;
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9': {
      unsigned long n = 0;
      if (limit - rcp < MAXLINE) {
        cp = rcp - 1;
        fillbuf();
        rcp = ++cp;
      }
      assert(cp == rcp);
      token = (char *)rcp - 1;
      if (*token == '0' && (*rcp == 'x' || *rcp == 'X')) {
        int d, overflow = 0;
        while (*++rcp) {
          if (map[*rcp]&DIGIT)
            d = *rcp - '0';
          else if (*rcp >= 'a' && *rcp <= 'f')
            d = *rcp - 'a' + 10;
          else if (*rcp >= 'A' && *rcp <= 'F')
            d = *rcp - 'A' + 10;
          else
            break;
          if (n&~(~0UL >> 4))
            overflow = 1;
          else
            n = (n<<4) + d;
        }
        if ((char *)rcp - token <= 2)
          error("invalid hexadecimal constant `%S'\n", token, (char *)rcp-token);
        cp = rcp;
        tsym = icon(n, overflow, 16);
      } else if (*token == '0') {
        int err = 0, overflow = 0;
        for ( ; map[*rcp]&DIGIT; rcp++) {
          if (*rcp == '8' || *rcp == '9')
            err = 1;
          if (n&~(~0UL >> 3))
            overflow = 1;
          else
            n = (n<<3) + (*rcp - '0');
        }
        if (*rcp == '.' || *rcp == 'e' || *rcp == 'E') {
          cp = rcp;
          tsym = fcon();
          return FCON;
        }
        cp = rcp;
        tsym = icon(n, overflow, 8);
        if (err)
          error("invalid octal constant `%S'\n", token, (char*)cp-token);
      } else {
        int overflow = 0;
        for (n = *token - '0'; map[*rcp]&DIGIT; ) {
          int d = *rcp++ - '0';
          if (n > (ULONG_MAX - d)/10)
            overflow = 1;
          else
            n = 10*n + d;
        }
        if (*rcp == '.' || *rcp == 'e' || *rcp == 'E') {
          cp = rcp;
          tsym = fcon();
          return FCON;
        }
        cp = rcp;
        tsym = icon(n, overflow, 10);
      }
      return ICON;
    }
    case '.':
      if (rcp[0] == '.' && rcp[1] == '.') {
        cp += 2;
        return ELLIPSIS;
      }
      if ((map[*rcp]&DIGIT) == 0)
        return '.';
      if (limit - rcp < MAXLINE) {
        cp = rcp - 1;
        fillbuf();
        rcp = ++cp;
      }
      assert(cp == rcp);
      cp = rcp - 1;
      token = (char *)cp;
      tsym = fcon();
      return FCON;
    scon:
    case '\'': case '"': {
      static char cbuf[BUFSIZE+1];
      char *s = cbuf;
      int nbad = 0;
      *s++ = *--cp;
      do {
        cp++;
        while (*cp != cbuf[0]) {
          int c;
          if (map[*cp]&NEWLINE) {
            if (cp < limit)
              break;
            cp++;
            nextline();
            if (cp == limit)
              break;
            continue;
          }
          c = *cp++;
          if (c == '\\') {
            if (map[*cp]&NEWLINE) {
              if (cp < limit)
                break;
              cp++;
              nextline();
            }
            if (limit - cp < MAXTOKEN)
              fillbuf();
            c = backslash(cbuf[0]);
          } else if (map[c] == 0)
            nbad++;
          if (s < &cbuf[sizeof cbuf] - 1)
            *s++ = c;
        }
        if (*cp == cbuf[0])
          cp++;
        else
          error("missing %c\n", cbuf[0]);
      } while (cbuf[0] == '"' && getchr() == '"');
      *s++ = 0;
      if (s >= &cbuf[sizeof cbuf])
        error("%s literal too long\n",
          cbuf[0] == '"' ? "string" : "character");
      if (Aflag >= 2 && cbuf[0] == '"' && s - cbuf - 1 > 509)
        warning("more than 509 characters in a string literal\n");
      if (Aflag >= 2 && nbad)
        warning("%s literal contains non-portable characters\n",
          cbuf[0] == '"' ? "string" : "character");
      token = cbuf;
      tsym = &tval;
      if (cbuf[0] == '"') {
        tval.type = array(chartype, s - cbuf - 1, 0);
        tval.u.c.v.p = cbuf + 1;
        return SCON;
      } else {
        if (s - cbuf > 3)
          warning("excess characters in multibyte character literal `%S' ignored\n", token, (char*)cp-token);

        else if (s - cbuf <= 2)
          error("missing '\n");
        tval.type = inttype;
        tval.u.c.v.i = cbuf[1];
        return ICON;
      }
    }
    case 'a':
      if (rcp[0] == 'u'
      &&  rcp[1] == 't'
      &&  rcp[2] == 'o'
      && !(map[rcp[3]]&(DIGIT|LETTER))) {
        cp = rcp + 3;
        return AUTO;
      }
      goto id;
    case 'b':
      if (rcp[0] == 'r'
      &&  rcp[1] == 'e'
      &&  rcp[2] == 'a'
      &&  rcp[3] == 'k'
      && !(map[rcp[4]]&(DIGIT|LETTER))) {
        cp = rcp + 4;
        return BREAK;
      }
      goto id;
    case 'c':
      if (rcp[0] == 'a'
      &&  rcp[1] == 's'
      &&  rcp[2] == 'e'
      && !(map[rcp[3]]&(DIGIT|LETTER))) {
        cp = rcp + 3;
        return CASE;
      }
      if (rcp[0] == 'h'
      &&  rcp[1] == 'a'
      &&  rcp[2] == 'r'
      && !(map[rcp[3]]&(DIGIT|LETTER))) {
        cp = rcp + 3;
        tsym = chartype->u.sym;
        return CHAR;
      }
      if (rcp[0] == 'o'
      &&  rcp[1] == 'n'
      &&  rcp[2] == 's'
      &&  rcp[3] == 't'
      && !(map[rcp[4]]&(DIGIT|LETTER))) {
        cp = rcp + 4;
        return CONST;
      }
      if (rcp[0] == 'o'
      &&  rcp[1] == 'n'
      &&  rcp[2] == 't'
      &&  rcp[3] == 'i'
      &&  rcp[4] == 'n'
      &&  rcp[5] == 'u'
      &&  rcp[6] == 'e'
      && !(map[rcp[7]]&(DIGIT|LETTER))) {
        cp = rcp + 7;
        return CONTINUE;
      }
      goto id;
    case 'd':
      if (rcp[0] == 'e'
      &&  rcp[1] == 'f'
      &&  rcp[2] == 'a'
      &&  rcp[3] == 'u'
      &&  rcp[4] == 'l'
      &&  rcp[5] == 't'
      && !(map[rcp[6]]&(DIGIT|LETTER))) {
        cp = rcp + 6;
        return DEFAULT;
      }
      if (rcp[0] == 'o'
      &&  rcp[1] == 'u'
      &&  rcp[2] == 'b'
      &&  rcp[3] == 'l'
      &&  rcp[4] == 'e'
      && !(map[rcp[5]]&(DIGIT|LETTER))) {
        cp = rcp + 5;
        tsym = doubletype->u.sym;
        return DOUBLE;
      }
      if (rcp[0] == 'o'
      && !(map[rcp[1]]&(DIGIT|LETTER))) {
        cp = rcp + 1;
        return DO;
      }
      goto id;
    case 'e':
      if (rcp[0] == 'l'
      &&  rcp[1] == 's'
      &&  rcp[2] == 'e'
      && !(map[rcp[3]]&(DIGIT|LETTER))) {
        cp = rcp + 3;
        return ELSE;
      }
      if (rcp[0] == 'n'
      &&  rcp[1] == 'u'
      &&  rcp[2] == 'm'
      && !(map[rcp[3]]&(DIGIT|LETTER))) {
        cp = rcp + 3;
        return ENUM;
      }
      if (rcp[0] == 'x'
      &&  rcp[1] == 't'
      &&  rcp[2] == 'e'
      &&  rcp[3] == 'r'
      &&  rcp[4] == 'n'
      && !(map[rcp[5]]&(DIGIT|LETTER))) {
        cp = rcp + 5;
        return EXTERN;
      }
      goto id;
    case 'f':
      if (rcp[0] == 'l'
      &&  rcp[1] == 'o'
      &&  rcp[2] == 'a'
      &&  rcp[3] == 't'
      && !(map[rcp[4]]&(DIGIT|LETTER))) {
        cp = rcp + 4;
        tsym = floattype->u.sym;
        return FLOAT;
      }
      if (rcp[0] == 'o'
      &&  rcp[1] == 'r'
      && !(map[rcp[2]]&(DIGIT|LETTER))) {
        cp = rcp + 2;
        return FOR;
      }
      goto id;
    case 'g':
      if (rcp[0] == 'o'
      &&  rcp[1] == 't'
      &&  rcp[2] == 'o'
      && !(map[rcp[3]]&(DIGIT|LETTER))) {
        cp = rcp + 3;
        return GOTO;
      }
      goto id;
    case 'l':
      if (rcp[0] == 'o'
      &&  rcp[1] == 'n'
      &&  rcp[2] == 'g'
      && !(map[rcp[3]]&(DIGIT|LETTER))) {
        cp = rcp + 3;
        tsym = longtype->u.sym;
        return LONG;
      }
      goto id;
    case 'r':
      if (rcp[0] == 'e'
      &&  rcp[1] == 'g'
      &&  rcp[2] == 'i'
      &&  rcp[3] == 's'
      &&  rcp[4] == 't'
      &&  rcp[5] == 'e'
      &&  rcp[6] == 'r'
      && !(map[rcp[7]]&(DIGIT|LETTER))) {
        cp = rcp + 7;
        return REGISTER;
      }
      if (rcp[0] == 'e'
      &&  rcp[1] == 't'
      &&  rcp[2] == 'u'
      &&  rcp[3] == 'r'
      &&  rcp[4] == 'n'
      && !(map[rcp[5]]&(DIGIT|LETTER))) {
        cp = rcp + 5;
        return RETURN;
      }
      goto id;
    case 's':
      if (rcp[0] == 'h'
      &&  rcp[1] == 'o'
      &&  rcp[2] == 'r'
      &&  rcp[3] == 't'
      && !(map[rcp[4]]&(DIGIT|LETTER))) {
        cp = rcp + 4;
        tsym = shorttype->u.sym;
        return SHORT;
      }
      if (rcp[0] == 'i'
      &&  rcp[1] == 'g'
      &&  rcp[2] == 'n'
      &&  rcp[3] == 'e'
      &&  rcp[4] == 'd'
      && !(map[rcp[5]]&(DIGIT|LETTER))) {
        cp = rcp + 5;
        return SIGNED;
      }
      if (rcp[0] == 'i'
      &&  rcp[1] == 'z'
      &&  rcp[2] == 'e'
      &&  rcp[3] == 'o'
      &&  rcp[4] == 'f'
      && !(map[rcp[5]]&(DIGIT|LETTER))) {
        cp = rcp + 5;
        return SIZEOF;
      }
      if (rcp[0] == 't'
      &&  rcp[1] == 'a'
      &&  rcp[2] == 't'
      &&  rcp[3] == 'i'
      &&  rcp[4] == 'c'
      && !(map[rcp[5]]&(DIGIT|LETTER))) {
        cp = rcp + 5;
        return STATIC;
      }
      if (rcp[0] == 't'
      &&  rcp[1] == 'r'
      &&  rcp[2] == 'u'
      &&  rcp[3] == 'c'
      &&  rcp[4] == 't'
      && !(map[rcp[5]]&(DIGIT|LETTER))) {
        cp = rcp + 5;
        return STRUCT;
      }
      if (rcp[0] == 'w'
      &&  rcp[1] == 'i'
      &&  rcp[2] == 't'
      &&  rcp[3] == 'c'
      &&  rcp[4] == 'h'
      && !(map[rcp[5]]&(DIGIT|LETTER))) {
        cp = rcp + 5;
        return SWITCH;
      }
      goto id;
    case 't':
      if (rcp[0] == 'y'
      &&  rcp[1] == 'p'
      &&  rcp[2] == 'e'
      &&  rcp[3] == 'd'
      &&  rcp[4] == 'e'
      &&  rcp[5] == 'f'
      && !(map[rcp[6]]&(DIGIT|LETTER))) {
        cp = rcp + 6;
        return TYPEDEF;
      }
      goto id;
    case 'u':
      if (rcp[0] == 'n'
      &&  rcp[1] == 'i'
      &&  rcp[2] == 'o'
      &&  rcp[3] == 'n'
      && !(map[rcp[4]]&(DIGIT|LETTER))) {
        cp = rcp + 4;
        return UNION;
      }
      if (rcp[0] == 'n'
      &&  rcp[1] == 's'
      &&  rcp[2] == 'i'
      &&  rcp[3] == 'g'
      &&  rcp[4] == 'n'
      &&  rcp[5] == 'e'
      &&  rcp[6] == 'd'
      && !(map[rcp[7]]&(DIGIT|LETTER))) {
        cp = rcp + 7;
        return UNSIGNED;
      }
      goto id;
    case 'v':
      if (rcp[0] == 'o'
      &&  rcp[1] == 'i'
      &&  rcp[2] == 'd'
      && !(map[rcp[3]]&(DIGIT|LETTER))) {
        cp = rcp + 3;
        tsym = voidtype->u.sym;
        return VOID;
      }
      if (rcp[0] == 'o'
      &&  rcp[1] == 'l'
      &&  rcp[2] == 'a'
      &&  rcp[3] == 't'
      &&  rcp[4] == 'i'
      &&  rcp[5] == 'l'
      &&  rcp[6] == 'e'
      && !(map[rcp[7]]&(DIGIT|LETTER))) {
        cp = rcp + 7;
        return VOLATILE;
      }
      goto id;
    case 'w':
      if (rcp[0] == 'h'
      &&  rcp[1] == 'i'
      &&  rcp[2] == 'l'
      &&  rcp[3] == 'e'
      && !(map[rcp[4]]&(DIGIT|LETTER))) {
        cp = rcp + 4;
        return WHILE;
      }
      goto id;
    case '_':
      if (rcp[0] == '_'
      &&  rcp[1] == 't'
      &&  rcp[2] == 'y'
      &&  rcp[3] == 'p'
      &&  rcp[4] == 'e'
      &&  rcp[5] == 'c'
      &&  rcp[6] == 'o'
      &&  rcp[7] == 'd'
      &&  rcp[8] == 'e'
      && !(map[rcp[9]]&(DIGIT|LETTER))) {
        cp = rcp + 9;
        return TYPECODE;
      }
      if (rcp[0] == '_'
      &&  rcp[1] == 'f'
      &&  rcp[2] == 'i'
      &&  rcp[3] == 'r'
      &&  rcp[4] == 's'
      &&  rcp[5] == 't'
      &&  rcp[6] == 'a'
      &&  rcp[7] == 'r'
      &&  rcp[8] == 'g'
      && !(map[rcp[9]]&(DIGIT|LETTER))) {
        cp = rcp + 9;
        return FIRSTARG;
      }
      goto id;
    default:
      if ((map[cp[-1]]&BLANK) == 0)
        if (cp[-1] < ' ' || cp[-1] >= 0177)
          error("illegal character `\\0%o'\n", cp[-1]);
        else
          error("illegal character `%c'\n", cp[-1]);
    }
  }
}
static Symbol icon(unsigned long n, int overflow, int base) {
  if ((*cp=='u'||*cp=='U') && (cp[1]=='l'||cp[1]=='L')
  ||  (*cp=='l'||*cp=='L') && (cp[1]=='u'||cp[1]=='U')) {
    tval.type = unsignedlong;
    cp += 2;
  } else if (*cp == 'u' || *cp == 'U') {
    if (overflow || n > unsignedtype->u.sym->u.limits.max.i)
      tval.type = unsignedlong;
    else
      tval.type = unsignedtype;
    cp += 1;
  } else if (*cp == 'l' || *cp == 'L') {
    if (overflow || n > longtype->u.sym->u.limits.max.i)
      tval.type = unsignedlong;
    else
      tval.type = longtype;
    cp += 1;
  } else if (overflow || n > longtype->u.sym->u.limits.max.i)
    tval.type = unsignedlong;
  else if (n > inttype->u.sym->u.limits.max.i)
    tval.type = longtype;
  else if (base != 10 && n > inttype->u.sym->u.limits.max.i)
    tval.type = unsignedtype;
  else
    tval.type = inttype;
  switch (tval.type->op) {
  case INT:
    if (overflow || n > tval.type->u.sym->u.limits.max.i) {
      warning("overflow in constant `%S'\n", token,
        (char*)cp - token);
      tval.u.c.v.i = tval.type->u.sym->u.limits.max.i;
    } else
      tval.u.c.v.i = n;
    break;
  case UNSIGNED:
    if (overflow || n > tval.type->u.sym->u.limits.max.u) {
      warning("overflow in constant `%S'\n", token,
        (char*)cp - token);
      tval.u.c.v.u = tval.type->u.sym->u.limits.max.u;
    } else
      tval.u.c.v.u = n;
    break;
  default: assert(0);
  }
  ppnumber("integer");
  return &tval;
}
static void ppnumber(char *which) {
  unsigned char *rcp = cp--;

  for ( ; (map[*cp]&(DIGIT|LETTER)) || *cp == '.'; cp++)
    if ((cp[0] == 'E' || cp[0] == 'e')
    &&  (cp[1] == '-' || cp[1] == '+'))
      cp++;
  if (cp > rcp)
    error("`%S' is a preprocessing number but an invalid %s constant\n", token,

      (char*)cp-token, which);
}
static Symbol fcon(void) {
  if (*cp == '.')
    do
      cp++;
    while (map[*cp]&DIGIT);
  if (*cp == 'e' || *cp == 'E') {
    if (*++cp == '-' || *cp == '+')
      cp++;
    if (map[*cp]&DIGIT)
      do
        cp++;
      while (map[*cp]&DIGIT);
    else
      error("invalid floating constant `%S'\n", token,
        (char*)cp - token);
  }

  errno = 0;
  tval.u.c.v.d = strtod(token, NULL);
  if (errno == ERANGE)
    warning("overflow in floating constant `%S'\n", token,
      (char*)cp - token);
  if (*cp == 'f' || *cp == 'F') {
    ++cp;
    if (tval.u.c.v.d > floattype->u.sym->u.limits.max.d)
      warning("overflow in floating constant `%S'\n", token,
        (char*)cp - token);
    tval.type = floattype;
  } else if (*cp == 'l' || *cp == 'L') {
    cp++;
    tval.type = longdouble;
  } else {
    if (tval.u.c.v.d > doubletype->u.sym->u.limits.max.d)
      warning("overflow in floating constant `%S'\n", token,
        (char*)cp - token);
    tval.type = doubletype;
  }
  ppnumber("floating");
  return &tval;
}

int getchr(void) {
  for (;;) {
    while (map[*cp]&BLANK)
      cp++;
    if (!(map[*cp]&NEWLINE))
      return *cp;
    cp++;
    nextline();
    if (cp == limit)
      return EOI;
  }
}
static int backslash(int q) {
  int c;

  switch (*cp++) {
  case 'a': return 7;
  case 'b': return '\b';
  case 'f': return '\f';
  case 'n': return '\n';
  case 'r': return '\r';
  case 't': return '\t';
  case 'v': return '\v';
  case '\'': case '"': case '\\': case '\?': break;
  case 'x': {
    int overflow = 0;
    if ((map[*cp]&(DIGIT|HEX)) == 0) {
      if (*cp < ' ' || *cp == 0177)
        error("ill-formed hexadecimal escape sequence\n");
      else
        error("ill-formed hexadecimal escape sequence `\\x%c'\n", *cp);
      if (*cp != q)
        cp++;
      return 0;
    }
    for (c = 0; map[*cp]&(DIGIT|HEX); cp++) {
      if (c&~(~0UL >> 4))
        overflow++;
      if (map[*cp]&DIGIT)
        c = (c<<4) + *cp - '0';
      else
        c = (c<<4) + (*cp&~040) - 'A' + 10;
    }
    if (c&~0377 || overflow)
      warning("overflow in hexadecimal escape sequence\n");
    return c&0377;
    }
  case '0': case '1': case '2': case '3':
  case '4': case '5': case '6': case '7':
    c = *(cp-1) - '0';
    if (*cp >= '0' && *cp <= '7') {
      c = (c<<3) + *cp++ - '0';
      if (*cp >= '0' && *cp <= '7')
        c = (c<<3) + *cp++ - '0';
    }
    if (c&~0377)
      warning("overflow in octal escape sequence\n");
    return c&0377;
  default:
    if (cp[-1] < ' ' || cp[-1] >= 0177)
      warning("unrecognized character escape sequence\n");
    else
      warning("unrecognized character escape sequence `\\%c'\n", cp[-1]);
  }
  return cp[-1];
}
