OSDN Git Service

* Rework fields used to describe positions of bitfields and
[pf3gnuchains/gcc-fork.git] / gcc / cppexp.c
index 8e65a95..4d1cf10 100644 (file)
@@ -1,5 +1,5 @@
-/* Parse C expressions for CCCP.
-   Copyright (C) 1987, 1992, 1994, 1995 Free Software Foundation.
+/* Parse C expressions for cpplib.
+   Copyright (C) 1987, 92, 94, 95, 97, 98, 1999, 2000 Free Software Foundation.
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -13,56 +13,21 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
-Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
 
  In other words, you are welcome to use, share and improve this program.
  You are forbidden to forbid anyone else to use, share and improve
  what you give them.   Help stamp out software-hoarding!
 
-Written by Per Bothner 1994. */
+Written by Per Bothner 1994.  */
 
 /* Parse a C expression from text in a string  */
    
 #include "config.h"
+#include "system.h"
 #include "cpplib.h"
-
-extern char *xmalloc PARAMS ((unsigned));
-extern char *xrealloc PARAMS ((char *, unsigned));
-
-#ifdef MULTIBYTE_CHARS
-#include <stdlib.h>
-#include <locale.h>
-#endif
-
-#include <stdio.h>
-
-/* This is used for communicating lists of keywords with cccp.c.  */
-struct arglist {
-  struct arglist *next;
-  U_CHAR *name;
-  int length;
-  int argno;
-};
-
-/* Define a generic NULL if one hasn't already been defined.  */
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-#ifndef GENERIC_PTR
-#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
-#define GENERIC_PTR void *
-#else
-#define GENERIC_PTR char *
-#endif
-#endif
-
-#ifndef NULL_PTR
-#define NULL_PTR ((GENERIC_PTR)0)
-#endif
-
-extern char *xmalloc ();
+#include "cpphash.h"
 
 #ifndef CHAR_TYPE_SIZE
 #define CHAR_TYPE_SIZE BITS_PER_UNIT
@@ -96,13 +61,34 @@ extern char *xmalloc ();
 #define MAX_WCHAR_TYPE_SIZE WCHAR_TYPE_SIZE
 #endif
 
+#define MAX_CHAR_TYPE_MASK (MAX_CHAR_TYPE_SIZE < HOST_BITS_PER_WIDEST_INT \
+                           ? (~(~(HOST_WIDEST_INT) 0 << MAX_CHAR_TYPE_SIZE)) \
+                           : ~ (HOST_WIDEST_INT) 0)
+
+#define MAX_WCHAR_TYPE_MASK (MAX_WCHAR_TYPE_SIZE < HOST_BITS_PER_WIDEST_INT \
+                            ? ~(~(HOST_WIDEST_INT) 0 << MAX_WCHAR_TYPE_SIZE) \
+                            : ~ (HOST_WIDEST_INT) 0)
+
 /* Yield nonzero if adding two numbers with A's and B's signs can yield a
    number with SUM's sign, where A, B, and SUM are all C integers.  */
 #define possible_sum_sign(a, b, sum) ((((a) ^ (b)) | ~ ((a) ^ (sum))) < 0)
 
-static void integer_overflow ();
-static long left_shift ();
-static long right_shift ();
+static void integer_overflow PARAMS ((cpp_reader *));
+static HOST_WIDEST_INT left_shift PARAMS ((cpp_reader *, HOST_WIDEST_INT,
+                                          unsigned int,
+                                          unsigned HOST_WIDEST_INT));
+static HOST_WIDEST_INT right_shift PARAMS ((cpp_reader *, HOST_WIDEST_INT,
+                                           unsigned int,
+                                           unsigned HOST_WIDEST_INT));
+static struct operation parse_number PARAMS ((cpp_reader *, U_CHAR *,
+                                             U_CHAR *));
+static struct operation parse_charconst PARAMS ((cpp_reader *, U_CHAR *,
+                                                U_CHAR *));
+static struct operation parse_defined PARAMS ((cpp_reader *));
+static HOST_WIDEST_INT parse_escape PARAMS ((cpp_reader *, U_CHAR **,
+                                            HOST_WIDEST_INT));
+static struct operation lex PARAMS ((cpp_reader *, int));
+
 
 #define ERROR 299
 #define OROR 300
@@ -120,144 +106,292 @@ static long right_shift ();
 #define LEFT_OPERAND_REQUIRED 1
 #define RIGHT_OPERAND_REQUIRED 2
 #define HAVE_VALUE 4
-/*#define UNSIGNEDP 8*/
+/* SKIP_OPERAND is set for '&&' '||' '?' and ':' when the
+   following operand should be short-circuited instead of evaluated.  */
+#define SKIP_OPERAND 8
 
-#ifndef HOST_BITS_PER_WIDE_INT
-
-#if HOST_BITS_PER_LONG > HOST_BITS_PER_INT
-#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG
-#define HOST_WIDE_INT long
-#else
-#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT
-#define HOST_WIDE_INT int
-#endif
-
-#endif
-
-struct operation {
-    short op;
-    char rprio; /* Priority of op (relative to it right operand). */
-    char flags;
-    char unsignedp;    /* true if value should be treated as unsigned */
-    HOST_WIDE_INT value;        /* The value logically "right" of op. */
+struct operation
+{
+  short op;
+  U_CHAR rprio; /* Priority of op (relative to it right operand).  */
+  U_CHAR flags;
+  U_CHAR unsignedp;    /* true if value should be treated as unsigned */
+  HOST_WIDEST_INT value;        /* The value logically "right" of op.  */
 };
-\f
-/* Take care of parsing a number (anything that starts with a digit).
-   LEN is the number of characters in it.  */
 
-/* maybe needs to actually deal with floating point numbers */
+/* Parse and convert an integer for #if.  Accepts decimal, hex, or octal
+   with or without size suffixes.  */
 
-struct operation
-parse_number (pfile, start, olen)
+static struct operation
+parse_number (pfile, start, end)
      cpp_reader *pfile;
-     char *start;
-     int olen;
+     U_CHAR *start;
+     U_CHAR *end;
 {
   struct operation op;
-  register char *p = start;
-  register int c;
-  register unsigned long n = 0, nd, ULONG_MAX_over_base;
-  register int base = 10;
-  register int len = olen;
-  register int overflow = 0;
-  register int digit, largest_digit = 0;
+  U_CHAR *p = start;
+  int c;
+  unsigned HOST_WIDEST_INT n = 0, nd, MAX_over_base;
+  int base = 10;
+  int overflow = 0;
+  int digit, largest_digit = 0;
   int spec_long = 0;
 
   op.unsignedp = 0;
 
-  for (c = 0; c < len; c++)
-    if (p[c] == '.') {
-      /* It's a float since it contains a point.  */
-      cpp_error (pfile,
-                "floating point numbers not allowed in #if expressions");
-      op.op = ERROR;
-      return op;
+  if (p[0] == '0')
+    {
+      if (end - start >= 3 && (p[1] == 'x' || p[1] == 'X'))
+       {
+         p += 2;
+         base = 16;
+       }
+      else
+       {
+         p += 1;
+         base = 8;
+       }
     }
 
-  if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
-    p += 2;
-    base = 16;
-    len -= 2;
-  }
-  else if (*p == '0')
-    base = 8;
-
-  /* Some buggy compilers (e.g. MPW C) seem to need both casts. */
-  ULONG_MAX_over_base = ((unsigned long) -1) / ((unsigned long) base);
-
-  for (; len > 0; len--) {
-    c = *p++;
-
-    if (c >= '0' && c <= '9')
-      digit = c - '0';
-    else if (base == 16 && c >= 'a' && c <= 'f')
-      digit = c - 'a' + 10;
-    else if (base == 16 && c >= 'A' && c <= 'F')
-      digit = c - 'A' + 10;
-    else {
-      /* `l' means long, and `u' means unsigned.  */
-      while (1) {
-       if (c == 'l' || c == 'L')
-         {
-           if (spec_long)
-             cpp_error (pfile, "two `l's in integer constant");
-           spec_long = 1;
-         }
-       else if (c == 'u' || c == 'U')
-         {
-           if (op.unsignedp)
-             cpp_error (pfile, "two `u's in integer constant");
-           op.unsignedp = 1;
-         }
-       else
-         break;
+  /* Some buggy compilers (e.g. MPW C) seem to need both casts.  */
+  MAX_over_base = (((unsigned HOST_WIDEST_INT) -1)
+                  / ((unsigned HOST_WIDEST_INT) base));
 
-       if (--len == 0)
+  while (p < end)
+    {
+      c = *p++;
+
+      if (c >= '0' && c <= '9')
+       digit = c - '0';
+      else if (base == 16 && c >= 'a' && c <= 'f') /* FIXME: assumes ASCII */
+       digit = c - 'a' + 10;
+      else if (base == 16 && c >= 'A' && c <= 'F')
+       digit = c - 'A' + 10;
+      else if (c == '.')
+       {
+         /* It's a float since it contains a point.  */
+         cpp_error (pfile,
+               "floating point numbers are not allowed in #if expressions");
+         goto error;
+       }
+      else
+       {
+         /* `l' means long, and `u' means unsigned.  */
+         for (;;)
+           {
+             if (c == 'l' || c == 'L')
+                 spec_long++;
+             else if (c == 'u' || c == 'U')
+                 op.unsignedp++;
+             else
+               {
+                 /* Decrement p here so that the error for an invalid number
+                    will be generated below in the case where this is the
+                    last character in the buffer.  */
+                 p--;
+                 break;
+               }
+             if (p == end)
+               break;
+             c = *p++;
+           }
+         /* Don't look for any more digits after the suffixes.  */
          break;
-       c = *p++;
-      }
-      /* Don't look for any more digits after the suffixes.  */
-      break;
+       }
+      
+      if (largest_digit < digit)
+       largest_digit = digit;
+      nd = n * base + digit;
+      overflow |= MAX_over_base < n || nd < n;
+      n = nd;
     }
-    if (largest_digit < digit)
-      largest_digit = digit;
-    nd = n * base + digit;
-    overflow |= ULONG_MAX_over_base < n | nd < n;
-    n = nd;
-  }
 
-  if (len != 0)
+  if (p != end)
     {
-      cpp_error (pfile, "Invalid number in #if expression");
-      op.op = ERROR;
-      return op;
+      cpp_error (pfile, "invalid number in #if expression");
+      goto error;
     }
-
+  else if (spec_long > (CPP_OPTIONS (pfile)->c89 ? 1 : 2))
+    {
+      cpp_error (pfile, "too many `l' suffixes in integer constant");
+      goto error;
+    }
+  else if (op.unsignedp > 1)
+    {
+      cpp_error (pfile, "too many `u' suffixes in integer constant");
+      goto error;
+    }
+  
   if (base <= largest_digit)
-    cpp_warning (pfile, "integer constant contains digits beyond the radix");
+    cpp_pedwarn (pfile, "integer constant contains digits beyond the radix");
 
   if (overflow)
-    cpp_warning (pfile, "integer constant out of range");
+    cpp_pedwarn (pfile, "integer constant out of range");
 
   /* If too big to be signed, consider it unsigned.  */
-  if ((long) n < 0 && ! op.unsignedp)
+  else if ((HOST_WIDEST_INT) n < 0 && ! op.unsignedp)
     {
       if (base == 10)
-       cpp_warning (pfile, "integer constant is so large that it is unsigned");
+       cpp_warning (pfile,
+                    "integer constant is so large that it is unsigned");
       op.unsignedp = 1;
     }
 
   op.value = n;
   op.op = INT;
   return op;
+
+ error:
+  op.op = ERROR;
+  return op;
+}
+
+/* Parse and convert a character constant for #if.  Understands backslash
+   escapes (\n, \031) and multibyte characters (if so configured).  */
+static struct operation
+parse_charconst (pfile, start, end)
+     cpp_reader *pfile;
+     U_CHAR *start;
+     U_CHAR *end;
+{
+  struct operation op;
+  HOST_WIDEST_INT result = 0;
+  int num_chars = 0;
+  int num_bits;
+  unsigned int width = MAX_CHAR_TYPE_SIZE, mask = MAX_CHAR_TYPE_MASK;
+  int max_chars;
+  U_CHAR *ptr = start;
+
+  int c = -1;
+
+  if (*ptr == 'L')
+    {
+      ++ptr;
+      width = MAX_WCHAR_TYPE_SIZE, mask = MAX_WCHAR_TYPE_MASK;
+    }
+  max_chars = MAX_LONG_TYPE_SIZE / width;
+
+  ++ptr;  /* skip initial quote */
+
+  while (ptr < end)
+    {
+      c = *ptr++;
+      if (c == '\'' || c == '\0')
+       break;
+      else if (c == '\\')
+       {
+         c = parse_escape (pfile, &ptr, mask);
+         if (width < HOST_BITS_PER_INT
+             && (unsigned int) c >= (unsigned int)(1 << width))
+           cpp_pedwarn (pfile, "escape sequence out of range for character");
+       }
+         
+      /* Merge character into result; ignore excess chars.  */
+      if (++num_chars <= max_chars)
+       {
+         if (width < HOST_BITS_PER_INT)
+           result = (result << width) | (c & ((1 << width) - 1));
+         else
+           result = c;
+       }
+    }
+
+  if (num_chars == 0)
+    {
+      cpp_error (pfile, "empty character constant");
+      goto error;
+    }
+  else if (c != '\'')
+    {
+      /* cpp_get_token has already emitted an error if !traditional. */
+      if (! CPP_TRADITIONAL (pfile))
+       cpp_error (pfile, "malformatted character constant");
+      goto error;
+    }
+  else if (num_chars > max_chars)
+    {
+      cpp_error (pfile, "character constant too long");
+      goto error;
+    }
+  else if (num_chars != 1 && ! CPP_TRADITIONAL (pfile))
+    cpp_warning (pfile, "multi-character character constant");
+
+  /* If char type is signed, sign-extend the constant.  */
+  num_bits = num_chars * width;
+      
+  if (cpp_defined (pfile, (const U_CHAR *)"__CHAR_UNSIGNED__",
+                  sizeof ("__CHAR_UNSIGNED__")-1)
+      || ((result >> (num_bits - 1)) & 1) == 0)
+    op.value = result & ((unsigned HOST_WIDEST_INT) ~0
+                        >> (HOST_BITS_PER_WIDEST_INT - num_bits));
+  else
+    op.value = result | ~((unsigned HOST_WIDEST_INT) ~0
+                         >> (HOST_BITS_PER_WIDEST_INT - num_bits));
+
+  /* This is always a signed type.  */
+  op.unsignedp = 0;
+  op.op = CHAR;
+  return op;
+
+ error:
+  op.op = ERROR;
+  return op;
 }
 
+static struct operation
+parse_defined (pfile)
+     cpp_reader *pfile;
+{
+  int paren = 0, len;
+  U_CHAR *tok;
+  enum cpp_token token;
+  struct operation op;
+  long old_written = CPP_WRITTEN (pfile);
+
+  op.unsignedp = 0;
+  op.op = INT;
+
+  pfile->no_macro_expand++;
+  token = _cpp_get_directive_token (pfile);
+  if (token == CPP_LPAREN)
+    {
+      paren++;
+      CPP_SET_WRITTEN (pfile, old_written);
+      token = _cpp_get_directive_token (pfile);
+    }
+
+  if (token != CPP_NAME)
+    goto oops;
+
+  tok = pfile->token_buffer + old_written;
+  len = CPP_PWRITTEN (pfile) - tok;
+  op.value = cpp_defined (pfile, tok, len);
+
+  if (paren)
+    {
+      if (_cpp_get_directive_token (pfile) != CPP_RPAREN)
+       goto oops;
+    }
+  CPP_SET_WRITTEN (pfile, old_written);
+  pfile->no_macro_expand--;
+  return op;
+
+ oops:
+  CPP_SET_WRITTEN (pfile, old_written);
+  pfile->no_macro_expand--;
+  cpp_error (pfile, "`defined' without an identifier");
+
+  op.op = ERROR;
+  return op;
+}
+
+
 struct token {
-  char *operator;
+  const char *operator;
   int token;
 };
 
-static struct token tokentab2[] = {
+static const struct token tokentab2[] = {
   {"&&", ANDAND},
   {"||", OROR},
   {"<<", LSH},
@@ -271,177 +405,62 @@ static struct token tokentab2[] = {
   {NULL, ERROR}
 };
 
-/* Read one token. */
+/* Read one token.  */
 
-struct operation
-cpp_lex (pfile)
-cpp_reader *pfile;
+static struct operation
+lex (pfile, skip_evaluation)
+     cpp_reader *pfile;
+     int skip_evaluation;
 {
-  register int c;
-  register int namelen;
-  register struct token *toktab;
+  const struct token *toktab;
   enum cpp_token token;
   struct operation op;
   U_CHAR *tok_start, *tok_end;
-  int old_written;
-
- retry:
-
-  c = CPP_BUF_PEEK (CPP_BUFFER (pfile));
-  if (c == '#')
-    return parse_number (pfile,
-                        cpp_read_check_assertion (pfile) ? "1" : "0", 1);
+  long old_written;
 
   old_written = CPP_WRITTEN (pfile);
-  cpp_skip_hspace (pfile);
-  if (c == '\n')
-    {
-      op.op = 0;
-      return op;
-    }
+  token = _cpp_get_directive_token (pfile);
 
-  token = cpp_get_token (pfile);
   tok_start = pfile->token_buffer + old_written;
   tok_end = CPP_PWRITTEN (pfile);
-  pfile->limit = tok_start;
+  CPP_SET_WRITTEN (pfile, old_written);
   switch (token)
-  {
-    case CPP_EOF: /* Should not happen ... */
+    {
+    case CPP_EOF: /* Should not happen ...  */
+    case CPP_VSPACE:
       op.op = 0;
       return op;
-    case CPP_VSPACE:
-    case CPP_POP:
-      if (CPP_BUFFER (pfile)->fname != NULL)
-       {
-         op.op = 0;
-         return op;
-       }
-      goto retry;
-    case CPP_HSPACE:   case CPP_COMMENT: 
-      goto retry;
     case CPP_NUMBER:
-      return parse_number (pfile, tok_start, tok_end - tok_start);
+      return parse_number (pfile, tok_start, tok_end);
     case CPP_STRING:
-      cpp_error (pfile, "string constants not allowed in #if expressions");
+    case CPP_WSTRING:
+      cpp_error (pfile, "string constants are not allowed in #if expressions");
       op.op = ERROR;
       return op;
-    case CPP_CHAR:
-      /* This code for reading a character constant
-        handles multicharacter constants and wide characters.
-        It is mostly copied from c-lex.c.  */
-      {
-        register int result = 0;
-       register num_chars = 0;
-       unsigned width = MAX_CHAR_TYPE_SIZE;
-       int wide_flag = 0;
-       int max_chars;
-       U_CHAR *ptr = tok_start;
-#ifdef MULTIBYTE_CHARS
-       char token_buffer[MAX_LONG_TYPE_SIZE/MAX_CHAR_TYPE_SIZE + MB_CUR_MAX];
-#else
-       char token_buffer[MAX_LONG_TYPE_SIZE/MAX_CHAR_TYPE_SIZE + 1];
-#endif
-
-       if (*ptr == 'L')
-         {
-           ptr++;
-           wide_flag = 1;
-           width = MAX_WCHAR_TYPE_SIZE;
-#ifdef MULTIBYTE_CHARS
-           max_chars = MB_CUR_MAX;
-#else
-           max_chars = 1;
-#endif
-         }
-       else
-           max_chars = MAX_LONG_TYPE_SIZE / width;
-
-       while (1)
-         {
-           if (ptr >= CPP_PWRITTEN (pfile) || (c = *ptr++) == '\'')
-             break;
 
-           if (c == '\\')
-             {
-               c = cpp_parse_escape (pfile, &ptr);
-               if (width < HOST_BITS_PER_INT
-                 && (unsigned) c >= (1 << width))
-                   cpp_pedwarn (pfile,
-                                "escape sequence out of range for character");
-             }
-
-           num_chars++;
-
-           /* Merge character into result; ignore excess chars.  */
-           if (num_chars < max_chars + 1)
-             {
-               if (width < HOST_BITS_PER_INT)
-                 result = (result << width) | (c & ((1 << width) - 1));
-               else
-                 result = c;
-               token_buffer[num_chars - 1] = c;
-             }
-         }
-
-       token_buffer[num_chars] = 0;
+    case CPP_CHAR:
+    case CPP_WCHAR:
+      return parse_charconst (pfile, tok_start, tok_end);
 
-       if (c != '\'')
-         cpp_error (pfile, "malformatted character constant");
-       else if (num_chars == 0)
-         cpp_error (pfile, "empty character constant");
-       else if (num_chars > max_chars)
-         {
-           num_chars = max_chars;
-           cpp_error (pfile, "character constant too long");
-         }
-       else if (num_chars != 1 && ! CPP_TRADITIONAL (pfile))
-         cpp_warning (pfile, "multi-character character constant");
+    case CPP_NAME:
+      if (!strcmp (tok_start, "defined"))
+       return parse_defined (pfile);
 
-       /* If char type is signed, sign-extend the constant.  */
-       if (! wide_flag)
-         {
-           int num_bits = num_chars * width;
+      op.op = INT;
+      op.unsignedp = 0;
+      op.value = 0;
 
-           if (cpp_lookup (pfile, "__CHAR_UNSIGNED__",
-                           sizeof ("__CHAR_UNSIGNED__")-1, -1)
-               || ((result >> (num_bits - 1)) & 1) == 0)
-               op.value
-                   = result & ((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits));
-           else
-               op.value
-                   = result | ~((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits));
-         }
-       else
-         {
-#ifdef MULTIBYTE_CHARS
-           /* Set the initial shift state and convert the next sequence.  */
-             result = 0;
-             /* In all locales L'\0' is zero and mbtowc will return zero,
-                so don't use it.  */
-             if (num_chars > 1
-                 || (num_chars == 1 && token_buffer[0] != '\0'))
-               {
-                 wchar_t wc;
-                 (void) mbtowc (NULL_PTR, NULL_PTR, 0);
-                 if (mbtowc (& wc, token_buffer, num_chars) == num_chars)
-                   result = wc;
-                 else
-                   cpp_warning (pfile,"Ignoring invalid multibyte character");
-               }
-#endif
-             op.value = result;
-           }
-        }
+      if (CPP_OPTIONS (pfile)->warn_undef && !skip_evaluation)
+       cpp_warning (pfile, "`%.*s' is not defined",
+                    (int) (tok_end - tok_start), tok_start);
+      return op;
 
-      /* This is always a signed type.  */
+    case CPP_ASSERTION:
+      op.op = INT;
       op.unsignedp = 0;
-      op.op = CHAR;
-    
+      op.value = cpp_defined (pfile, tok_start, tok_end - tok_start);
       return op;
 
-    case CPP_NAME:
-      return parse_number (pfile, "0", 0);
-
     case CPP_OTHER:
       /* See if it is a special token of length 2.  */
       if (tok_start + 2 == tok_end)
@@ -451,11 +470,8 @@ cpp_reader *pfile;
                && tok_start[1] == toktab->operator[1])
                break;
          if (toktab->token == ERROR)
-           {
-             char *buf = (char *) alloca (40);
-             sprintf (buf, "`%s' not allowed in operand of `#if'", tok_start);
-             cpp_error (pfile, buf);
-           }
+           cpp_error (pfile, "`%s' not allowed in operand of `#if'",
+                      tok_start);
          op.op = toktab->token; 
          return op;
        }
@@ -481,10 +497,11 @@ cpp_reader *pfile;
    If \ is followed by 000, we return 0 and leave the string pointer
    after the zeros.  A value of 0 does not mean end of string.  */
 
-int
-cpp_parse_escape (pfile, string_ptr)
+static HOST_WIDEST_INT
+parse_escape (pfile, string_ptr, result_mask)
      cpp_reader *pfile;
-     char **string_ptr;
+     U_CHAR **string_ptr;
+     HOST_WIDEST_INT result_mask;
 {
   register int c = *(*string_ptr)++;
   switch (c)
@@ -495,9 +512,9 @@ cpp_parse_escape (pfile, string_ptr)
       return TARGET_BS;
     case 'e':
     case 'E':
-      if (CPP_PEDANTIC (pfile))
+      if (CPP_OPTIONS (pfile)->pedantic)
        cpp_pedwarn (pfile, "non-ANSI-standard escape sequence, `\\%c'", c);
-      return 033;
+      return TARGET_ESC;
     case 'f':
       return TARGET_FF;
     case 'n':
@@ -523,7 +540,7 @@ cpp_parse_escape (pfile, string_ptr)
     case '6':
     case '7':
       {
-       register int i = c - '0';
+       register HOST_WIDEST_INT i = c - '0';
        register int count = 0;
        while (++count < 3)
          {
@@ -536,17 +553,17 @@ cpp_parse_escape (pfile, string_ptr)
                break;
              }
          }
-       if ((i & ~((1 << MAX_CHAR_TYPE_SIZE) - 1)) != 0)
+       if (i != (i & result_mask))
          {
-           i &= (1 << MAX_CHAR_TYPE_SIZE) - 1;
-           cpp_warning (pfile,
-                         "octal character constant does not fit in a byte");
+           i &= result_mask;
+           cpp_pedwarn (pfile, "octal escape sequence out of range");
          }
        return i;
       }
     case 'x':
       {
-       register unsigned i = 0, overflow = 0, digits_found = 0, digit;
+       register unsigned HOST_WIDEST_INT i = 0, overflow = 0;
+       register int digits_found = 0, digit;
        for (;;)
          {
            c = *(*string_ptr)++;
@@ -567,11 +584,10 @@ cpp_parse_escape (pfile, string_ptr)
          }
        if (!digits_found)
          cpp_error (pfile, "\\x used with no following hex digits");
-       if (overflow | (i & ~((1 << BITS_PER_UNIT) - 1)))
+       if (overflow | (i != (i & result_mask)))
          {
-           i &= (1 << BITS_PER_UNIT) - 1;
-           cpp_warning (pfile,
-                        "hex character constant does not fit in a byte");
+           i &= result_mask;
+           cpp_pedwarn (pfile, "hex escape sequence out of range");
          }
        return i;
       }
@@ -588,46 +604,46 @@ integer_overflow (pfile)
     cpp_pedwarn (pfile, "integer overflow in preprocessor expression");
 }
 
-static long
+static HOST_WIDEST_INT
 left_shift (pfile, a, unsignedp, b)
      cpp_reader *pfile;
-     long a;
-     int unsignedp;
-     unsigned long b;
+     HOST_WIDEST_INT a;
+     unsigned int unsignedp;
+     unsigned HOST_WIDEST_INT b;
 {
-  if (b >= HOST_BITS_PER_LONG)
+  if (b >= HOST_BITS_PER_WIDEST_INT)
     {
       if (! unsignedp && a != 0)
        integer_overflow (pfile);
       return 0;
     }
   else if (unsignedp)
-    return (unsigned long) a << b;
+    return (unsigned HOST_WIDEST_INT) a << b;
   else
     {
-      long l = a << b;
+      HOST_WIDEST_INT l = a << b;
       if (l >> b != a)
        integer_overflow (pfile);
       return l;
     }
 }
 
-static long
+static HOST_WIDEST_INT
 right_shift (pfile, a, unsignedp, b)
-     cpp_reader *pfile;
-     long a;
-     int unsignedp;
-     unsigned long b;
+     cpp_reader *pfile ATTRIBUTE_UNUSED;
+     HOST_WIDEST_INT a;
+     unsigned int unsignedp;
+     unsigned HOST_WIDEST_INT b;
 {
-  if (b >= HOST_BITS_PER_LONG)
-    return unsignedp ? 0 : a >> (HOST_BITS_PER_LONG - 1);
+  if (b >= HOST_BITS_PER_WIDEST_INT)
+    return unsignedp ? 0 : a >> (HOST_BITS_PER_WIDEST_INT - 1);
   else if (unsignedp)
-    return (unsigned long) a >> b;
+    return (unsigned HOST_WIDEST_INT) a >> b;
   else
     return a >> b;
 }
 \f
-/* These priorities are all even, so we can handle associatively. */
+/* These priorities are all even, so we can handle associatively.  */
 #define PAREN_INNER_PRIO 0
 #define COMMA_PRIO 4
 #define COND_PRIO (COMMA_PRIO+2)
@@ -646,13 +662,14 @@ right_shift (pfile, a, unsignedp, b)
 
 #define COMPARE(OP) \
   top->unsignedp = 0;\
-  top->value = (unsigned1 || unsigned2) ? (unsigned long) v1 OP v2 : (v1 OP v2)
+  top->value = (unsigned1 || unsigned2) \
+  ? (unsigned HOST_WIDEST_INT) v1 OP (unsigned HOST_WIDEST_INT) v2 : (v1 OP v2)
 
 /* Parse and evaluate a C expression, reading from PFILE.
-   Returns the value of the expression.  */
+   Returns the truth value of the expression.  */
 
-HOST_WIDE_INT
-cpp_parse_expr (pfile)
+int
+_cpp_parse_expr (pfile)
      cpp_reader *pfile;
 {
   /* The implementation is an operator precedence parser,
@@ -670,37 +687,42 @@ cpp_parse_expr (pfile)
   struct operation *stack = init_stack;
   struct operation *limit = stack + INIT_STACK_SIZE;
   register struct operation *top = stack;
-  int lprio, rprio;
+  unsigned int lprio, rprio = 0;
+  int skip_evaluation = 0;
+  long old_written = CPP_WRITTEN (pfile);
+  int result;
 
+  pfile->parsing_if_directive++;
   top->rprio = 0;
   top->flags = 0;
   for (;;)
     {
       struct operation op;
-      char flags = 0;
+      U_CHAR flags = 0;
 
       /* Read a token */
-      op =  cpp_lex (pfile);
+      op =  lex (pfile, skip_evaluation);
 
       /* See if the token is an operand, in which case go to set_value.
         If the token is an operator, figure out its left and right
-        priorities, and then goto maybe_reduce. */
+        priorities, and then goto maybe_reduce.  */
 
       switch (op.op)
        {
        case NAME:
-         top->value = 0, top->unsignedp = 0;
-         goto set_value;
+         cpp_ice (pfile, "lex returns a NAME");
+         goto syntax_error;
        case INT:  case CHAR:
-         top->value = op.value;
-         top->unsignedp = op.unsignedp;
          goto set_value;
        case 0:
          lprio = 0;  goto maybe_reduce;
        case '+':  case '-':
-         /* Is this correct if unary ? FIXME */
-         flags = RIGHT_OPERAND_REQUIRED;
-         lprio = PLUS_PRIO;  rprio = lprio + 1;  goto maybe_reduce;
+         if (top->flags & HAVE_VALUE)
+           {
+             lprio = PLUS_PRIO;
+             goto binop;
+           }
+         /* else fall through */
        case '!':  case '~':
          flags = RIGHT_OPERAND_REQUIRED;
          rprio = UNARY_PRIO;  lprio = rprio + 1;  goto maybe_reduce;
@@ -731,31 +753,36 @@ cpp_parse_expr (pfile)
         case '?':
          lprio = COND_PRIO + 1;  rprio = COND_PRIO;
          goto maybe_reduce;
-       binop:
-         flags = LEFT_OPERAND_REQUIRED|RIGHT_OPERAND_REQUIRED;
-         rprio = lprio + 1;
-         goto maybe_reduce;
+       case ERROR:
+         goto syntax_error;
        default:
          cpp_error (pfile, "invalid character in #if");
          goto syntax_error;
        }
 
     set_value:
-      /* Push a value onto the stack. */
+      /* Push a value onto the stack.  */
       if (top->flags & HAVE_VALUE)
        {
          cpp_error (pfile, "syntax error in #if");
          goto syntax_error;
        }
+      top->value = op.value;
+      top->unsignedp = op.unsignedp;
       top->flags |= HAVE_VALUE;
       continue;
 
+    binop:
+      flags = LEFT_OPERAND_REQUIRED|RIGHT_OPERAND_REQUIRED;
+      rprio = lprio + 1;
+
     maybe_reduce:
-      /* Push an operator, and check if we can reduce now. */
+      /* Push an operator, and check if we can reduce now.  */
       while (top->rprio > lprio)
        {
-         long v1 = top[-1].value, v2 = top[0].value;
-         int unsigned1 = top[-1].unsignedp, unsigned2 = top[0].unsignedp;
+         HOST_WIDEST_INT v1 = top[-1].value, v2 = top[0].value;
+         unsigned int unsigned1 = top[-1].unsignedp;
+         unsigned int unsigned2 = top[0].unsignedp;
          top--;
          if ((top[1].flags & LEFT_OPERAND_REQUIRED)
              && ! (top[0].flags & HAVE_VALUE))
@@ -783,7 +810,7 @@ cpp_parse_expr (pfile)
                {
                  top->value = v1 + v2;
                  top->unsignedp = unsigned1 || unsigned2;
-                 if (! top->unsignedp
+                 if (! top->unsignedp && ! skip_evaluation
                      && ! possible_sum_sign (v1, v2, top->value))
                    integer_overflow (pfile);
                }
@@ -792,7 +819,7 @@ cpp_parse_expr (pfile)
              if (!(top->flags & HAVE_VALUE))
                { /* Unary '-' */
                  top->value = - v2;
-                 if ((top->value & v2) < 0 && ! unsigned2)
+                 if (!skip_evaluation && (top->value & v2) < 0 && !unsigned2)
                    integer_overflow (pfile);
                  top->unsignedp = unsigned2;
                  top->flags |= HAVE_VALUE;
@@ -801,7 +828,7 @@ cpp_parse_expr (pfile)
                { /* Binary '-' */
                  top->value = v1 - v2;
                  top->unsignedp = unsigned1 || unsigned2;
-                 if (! top->unsignedp
+                 if (! top->unsignedp && ! skip_evaluation
                      && ! possible_sum_sign (top->value, v2, v1))
                    integer_overflow (pfile);
                }
@@ -809,8 +836,8 @@ cpp_parse_expr (pfile)
            case '*':
              top->unsignedp = unsigned1 || unsigned2;
              if (top->unsignedp)
-               top->value = (unsigned long) v1 * v2;
-             else
+               top->value = (unsigned HOST_WIDEST_INT) v1 * v2;
+             else if (!skip_evaluation)
                {
                  top->value = v1 * v2;
                  if (v1
@@ -820,6 +847,8 @@ cpp_parse_expr (pfile)
                }
              break;
            case '/':
+             if (skip_evaluation)
+               break;
              if (v2 == 0)
                {
                  cpp_error (pfile, "division by zero in #if");
@@ -827,7 +856,7 @@ cpp_parse_expr (pfile)
                }
              top->unsignedp = unsigned1 || unsigned2;
              if (top->unsignedp)
-               top->value = (unsigned long) v1 / v2;
+               top->value = (unsigned HOST_WIDEST_INT) v1 / v2;
              else
                {
                  top->value = v1 / v2;
@@ -836,6 +865,8 @@ cpp_parse_expr (pfile)
                }
              break;
            case '%':
+             if (skip_evaluation)
+               break;
              if (v2 == 0)
                {
                  cpp_error (pfile, "division by zero in #if");
@@ -843,7 +874,7 @@ cpp_parse_expr (pfile)
                }
              top->unsignedp = unsigned1 || unsigned2;
              if (top->unsignedp)
-               top->value = (unsigned long) v1 % v2;
+               top->value = (unsigned HOST_WIDEST_INT) v1 % v2;
              else
                top->value = v1 % v2;
              break;
@@ -880,6 +911,8 @@ cpp_parse_expr (pfile)
              top->unsignedp = 0;
              break;
            case LSH:
+             if (skip_evaluation)
+               break;
              top->unsignedp = unsigned1;
              if (v2 < 0 && ! unsigned2)
                top->value = right_shift (pfile, v1, unsigned1, -v2);
@@ -887,6 +920,8 @@ cpp_parse_expr (pfile)
                top->value = left_shift (pfile, v1, unsigned1, v2);
              break;
            case RSH:
+             if (skip_evaluation)
+               break;
              top->unsignedp = unsigned1;
              if (v2 < 0 && ! unsigned2)
                top->value = left_shift (pfile, v1, unsigned1, -v2);
@@ -900,9 +935,13 @@ cpp_parse_expr (pfile)
            case '^':  LOGICAL(^);  break;
            case '|':  LOGICAL(|);  break;
            case ANDAND:
-             top->value = v1 && v2;  top->unsignedp = 0;  break;
+             top->value = v1 && v2;  top->unsignedp = 0;
+             if (!v1) skip_evaluation--;
+             break;
            case OROR:
-             top->value = v1 || v2;  top->unsignedp = 0;  break;
+             top->value = v1 || v2;  top->unsignedp = 0;
+             if (v1) skip_evaluation--;
+             break;
            case ',':
              if (CPP_PEDANTIC (pfile))
                cpp_pedwarn (pfile, "comma operator in operand of `#if'");
@@ -929,6 +968,7 @@ cpp_parse_expr (pfile)
              else
                {
                  top--;
+                 if (top->value) skip_evaluation--;
                  top->value = top->value ? v1 : v2;
                  top->unsignedp = unsigned1 || unsigned2;
                }
@@ -951,48 +991,64 @@ cpp_parse_expr (pfile)
                }
              break;
            default:
-             fprintf (stderr,
-                      top[1].op >= ' ' && top[1].op <= '~'
-                      ? "unimplemented operator '%c'\n"
-                      : "unimplemented operator '\\%03o'\n",
-                      top[1].op);
+             if (ISGRAPH (top[1].op))
+               cpp_error (pfile, "unimplemented operator '%c'\n", top[1].op);
+             else
+               cpp_error (pfile, "unimplemented operator '\\%03o'\n",
+                          top[1].op);
            }
        }
       if (op.op == 0)
        {
          if (top != stack)
-           cpp_error (pfile, "internal error in #if expression");
-         if (stack != init_stack)
-           free (stack);
-         return top->value;
+           cpp_ice (pfile, "unbalanced stack in #if expression");
+         result = (top->value != 0);
+         goto done;
        }
       top++;
       
-      /* Check for and handle stack overflow. */
+      /* Check for and handle stack overflow.  */
       if (top == limit)
        {
          struct operation *new_stack;
-         int old_size = (char*)limit - (char*)stack;
+         int old_size = (char *) limit - (char *) stack;
          int new_size = 2 * old_size;
          if (stack != init_stack)
-           new_stack = (struct operation*) xrealloc (stack, new_size);
+           new_stack = (struct operation *) xrealloc (stack, new_size);
          else
            {
-             new_stack = (struct operation*) xmalloc (new_size);
-             bcopy ((char *) stack, (char *) new_stack, old_size);
+             new_stack = (struct operation *) xmalloc (new_size);
+             memcpy (new_stack, stack, old_size);
            }
          stack = new_stack;
-         top = (struct operation*)((char*) new_stack + old_size);
-         limit = (struct operation*)((char*) new_stack + new_size);
+         top = (struct operation *) ((char *) new_stack + old_size);
+         limit = (struct operation *) ((char *) new_stack + new_size);
        }
       
       top->flags = flags;
       top->rprio = rprio;
       top->op = op.op;
+      if ((op.op == OROR && top[-1].value)
+         || (op.op == ANDAND && !top[-1].value)
+         || (op.op == '?' && !top[-1].value))
+       {
+         skip_evaluation++;
+       }
+      else if (op.op == ':')
+       {
+         if (top[-2].value) /* Was condition true? */
+           skip_evaluation++;
+         else
+           skip_evaluation--;
+       }
     }
  syntax_error:
+  _cpp_skip_rest_of_line (pfile);
+  result = 0;
+ done:
+  pfile->parsing_if_directive--;
+  CPP_SET_WRITTEN (pfile, old_written);
   if (stack != init_stack)
     free (stack);
-  skip_rest_of_line (pfile);
-  return 0;
+  return result;
 }