svn update: 57412 (latest:57412)
authorMyungjae Lee <mjae.lee@samsung.com>
Wed, 9 Mar 2011 02:07:18 +0000 (11:07 +0900)
committerMyungjae Lee <mjae.lee@samsung.com>
Wed, 9 Mar 2011 02:07:18 +0000 (11:07 +0900)
Change-Id: I9bdd3511e2486b3ba7851d32c36e9c47ac625b7e

26 files changed:
AUTHORS
ChangeLog
autogen.sh
configure.ac
doc/edje.dox.in
edje.spec.in
src/Makefile.am
src/bin/Makefile.am
src/bin/edje_cc_handlers.c
src/bin/edje_cc_parse.c
src/bin/epp/Makefile.am [new file with mode: 0644]
src/bin/epp/cppalloc.c [new file with mode: 0644]
src/bin/epp/cpperror.c [new file with mode: 0644]
src/bin/epp/cppexp.c [new file with mode: 0644]
src/bin/epp/cpphash.c [new file with mode: 0644]
src/bin/epp/cpplib.c [new file with mode: 0644]
src/bin/epp/cppmain.c [new file with mode: 0644]
src/lib/edje_cache.c
src/lib/edje_calc.c
src/lib/edje_data.c
src/lib/edje_entry.c
src/lib/edje_load.c
src/lib/edje_program.c
src/lib/edje_smart.c
src/lib/edje_text.c
src/lib/edje_util.c

diff --git a/AUTHORS b/AUTHORS
index fde96ed..074162a 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,3 +12,6 @@ Jihoon Kim <jihoon48.kim@samsung.com>
 Tiago Falcão <tiago@profusion.mobi>
 Davide Andreoli <dave@gurumeditation.it>
 Sebastian Dransfeld <sd@tango.flipp.net>
+Tom Hacohen <tom@stosb.com>
+Aharon Hillel <a.hillel@partner.samsung.com>
+Mike Blumenkrantz <mike@zentific.com>
index e69de29..6f6b21e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -0,0 +1,50 @@
+2011-01-29  Carsten Haitzler (The Rasterman)
+
+        1.0.0 release
+
+2011-01-30  Tom Hacohen (TAsn)
+
+        * Added "UNDER" cursor mode.
+        * Added auto-alignment to text parts, just set text.alignment to -1 to
+          activate it. It's only relevant to TEXT parts, textblock objects
+          already have that by default.
+
+2011-02-01  Tom Hacohen (TAsn)
+
+        * Added Mirroring support to edje objects.
+
+2011-02-08  Cedric BAIL
+
+       * Fix invalid memory access by edje alias code.
+
+2011-02-10  Cedric BAIL
+
+       * Fix use of recursive alias.
+
+2011-02-21  WooHyun Jung
+
+        * Fix missing cursor changed signale on backspace
+
+2011-02-21  Mike Blumenkrantz
+
+        * Add built-in cpp (epp) to provide consistent output on all archs/OSes
+
+2011-02-10  Cedric BAIL
+
+       * Fix propagation of recursive events on existing part.
+
+2011-02-24  Cedric BAIL
+
+       * fix signal comming from box/table item to include their
+       index or name correctly.
+
+2011-02-25  Jihoon Kim
+
+       * Add edje_object_part_text_cursor_pos_{set,get} API
+
+2011-02-27  Tom Hacohen (TAsn)
+
+        * Added size_range property to TEXT parts in edje files.
+       This lets you limit the size of the font that will be used.
+       Especially useful when using fit.
+
index 995ff2f..6499736 100755 (executable)
@@ -1,8 +1,5 @@
 #!/bin/sh
 
-rm -rf autom4te.cache
-rm -f aclocal.m4 ltmain.sh
-
 touch README
 
 echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS -I m4 || exit 1
@@ -12,5 +9,5 @@ echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --au
 echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1
 
 if [ -z "$NOCONFIGURE" ]; then
-       ./configure "$@"
+       ./configure -C "$@"
 fi
index ebdfd32..0f377ed 100644 (file)
@@ -2,11 +2,11 @@
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_maj], [1])
 m4_define([v_min], [0])
-m4_define([v_mic], [0])
+m4_define([v_mic], [999])
 m4_define([v_rev], m4_esyscmd([(svnversion "${SVN_REPO_PATH:-.}" | grep -v export || echo 0) | awk -F : '{printf("%s\n", $1);}' | tr -d ' :MSP\n']))
 m4_if(v_rev, [0], [m4_define([v_rev], m4_esyscmd([git log 2> /dev/null | (grep -m1 git-svn-id || echo 0) | sed -e 's/.*@\([0-9]*\).*/\1/' | tr -d '\n']))])
 ##--   When released, remove the dnl on the below line
-m4_undefine([v_rev])
+dnl m4_undefine([v_rev])
 ##--   When doing snapshots - change soname. remove dnl on below line
 dnl m4_define([relname], [ver-pre-svn-07])
 dnl m4_define([v_rel], [-release relname])
@@ -18,7 +18,7 @@ m4_define([lt_age], v_min)
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 
-AC_INIT([edje], [v_ver.beta3], [enlightenment-devel@lists.sourceforge.net])
+AC_INIT([edje], [v_ver], [enlightenment-devel@lists.sourceforge.net])
 AC_PREREQ([2.52])
 AC_CONFIG_SRCDIR([configure.ac])
 AC_CONFIG_MACRO_DIR([m4])
@@ -63,6 +63,7 @@ AC_DEFINE_UNQUOTED([SHARED_LIB_SUFFIX], ["$shrext_cmds"], [Suffix for shared obj
 AC_CANONICAL_BUILD
 AC_CANONICAL_HOST
 
+
 case "$host_os" in
    mingw32ce* | cegcc*)
       MODULE_ARCH="$host_os-$host_cpu"
@@ -70,7 +71,7 @@ case "$host_os" in
       have_edje_cc="no"
       ;;
    *)
-      MODULE_ARCH="$host_os-$host_cpu-v_maj.v_min.v_mic"
+      MODULE_ARCH="$host_os-$host_cpu-v_maj.0.0"
       install_vim="yes"
       have_edje_cc="yes"
       ;;
@@ -295,6 +296,8 @@ if test "x$have_edje_cc" = "xyes"; then
    EDJE_CC_LIBS="${EDJE_CC_LIBS} ${LUA_LIBS} ${lua_libs}"
 fi
 
+AM_CONDITIONAL([BUILD_EPP], [test "x$have_edje_cc" = "xyes"])
+
 if test "x$have_edje_decc" = "xyes"; then
    PKG_CHECK_MODULES([EDJE_DECC], [ecore-file >= 1.0.0 ecore-evas >= 1.0.0 ecore >= 1.0.0 evas >= 1.0.0 eet >= 1.0.0 eina >= 1.0.0])
 fi
@@ -381,6 +384,7 @@ doc/edje.dox
 src/Makefile
 src/lib/Makefile
 src/bin/Makefile
+src/bin/epp/Makefile
 utils/Makefile
 ])
 
index fecec26..739a513 100644 (file)
@@ -10,8 +10,23 @@ These routines are used for Edje.
 @mainpage Edje Library Documentation
 @image html  e.png
 @version @PACKAGE_VERSION@
-@author Carsten Haitzler <raster\@rasterman.com>
-@date 2003-2010
+@author Carsten Haitzler <raster@@rasterman.com>
+@author Tilman Sauerbeck (tilman at code-monkey de)
+@author ZigsMcKenzie <zigsmckenzie@@gmail.com>
+@author Cedric BAIL <cedric.bail@@free.fr>
+@author Brian Mattern <rephorm@@rephorm.com>
+@author Mathieu Taillefumier <mathieu.taillefumier@@free.fr>
+@author Tristan <blunderer@@gmail.com>
+@author Gustavo Lima Chaves <glima@@profusion.mobi>
+@author Bruno Dilly <bdilly@@profusion.mobi>
+@author Fabiano Fidêncio <fidencio@@profusion.mobi>
+@author Jihoon Kim <jihoon48.kim@@samsung.com>
+@author Tiago Falcão <tiago@@profusion.mobi>
+@author Davide Andreoli <dave@@gurumeditation.it>
+@author Sebastian Dransfeld <sd@@tango.flipp.net>
+@author Tom Hacohen <tom@@stosb.com>
+@author Aharon Hillel <a.hillel@@partner.samsung.com>
+@date 2003-2011
 
 
 
index 94c83c5..1d6e2a7 100644 (file)
@@ -91,7 +91,6 @@ test "x$RPM_BUILD_ROOT" != "x/" && rm -rf $RPM_BUILD_ROOT
 %files bin
 %defattr(-, root, root)
 %{_bindir}/edje_cc
-%{_bindir}/edje_convert
 %{_bindir}/edje_decc
 %{_bindir}/edje_recc
 %{_bindir}/edje_player
index 7b45af6..e950aed 100644 (file)
@@ -1,3 +1,4 @@
 ## Process this file with automake to produce Makefile.in
 
 SUBDIRS = lib bin
+MAINTAINERCLEANFILES = Makefile.in
index b8f0fc9..e559f7f 100644 (file)
@@ -1,6 +1,9 @@
-
 MAINTAINERCLEANFILES = Makefile.in
 
+if BUILD_EPP
+  SUBDIRS = epp
+endif
+
 bin_SCRIPTS = @EDJE_RECC_PRG@
 
 bin_PROGRAMS = @EDJE_CC_PRG@ @EDJE_DECC_PRG@ @EDJE_PLAYER_PRG@ @EDJE_INSPECTOR_PRG@ @EDJE_EXTERNAL_INSPECTOR_PRG@
@@ -22,6 +25,7 @@ edje_cc_CPPFLAGS = \
 -DPACKAGE_BIN_DIR=\"$(bindir)\" \
 -DPACKAGE_LIB_DIR=\"$(libdir)\" \
 -DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+-DEPP_DIR=\"$(libdir)/$(PACKAGE)/utils\" \
 @EDJE_CFLAGS@ @EDJE_CC_CFLAGS@ @EVIL_CFLAGS@
 edje_cc_LDADD = $(top_builddir)/src/lib/libedje.la @EDJE_CC_LIBS@ @EVIL_LIBS@ -lm
 edje_cc_LDFLAGS = @lt_enable_auto_import@
index 4817548..358d58f 100644 (file)
@@ -102,6 +102,7 @@ static void st_collections_group_alias(void);
 static void st_collections_group_min(void);
 static void st_collections_group_max(void);
 static void st_collections_group_data_item(void);
+static void st_collections_group_orientation(void);
 
 static void ob_collections_group_script(void);
 static void ob_collections_group_lua_script(void);
@@ -128,6 +129,7 @@ static void st_collections_group_parts_part_source5(void);
 static void st_collections_group_parts_part_source6(void);
 static void st_collections_group_parts_part_entry_mode(void);
 static void st_collections_group_parts_part_select_mode(void);
+static void st_collections_group_parts_part_cursor_mode(void);
 static void st_collections_group_parts_part_multiline(void);
 static void st_collections_group_parts_part_dragable_x(void);
 static void st_collections_group_parts_part_dragable_y(void);
@@ -196,6 +198,7 @@ static void st_collections_group_parts_part_description_text_font(void);
 static void st_collections_group_parts_part_description_text_style(void);
 static void st_collections_group_parts_part_description_text_repch(void);
 static void st_collections_group_parts_part_description_text_size(void);
+static void st_collections_group_parts_part_description_text_size_range(void);
 static void st_collections_group_parts_part_description_text_fit(void);
 static void st_collections_group_parts_part_description_text_min(void);
 static void st_collections_group_parts_part_description_text_max(void);
@@ -289,6 +292,7 @@ New_Statement_Handler statement_handlers[] =
      {"collections.group.alias", st_collections_group_alias},
      {"collections.group.min", st_collections_group_min},
      {"collections.group.max", st_collections_group_max},
+     {"collections.group.orientation", st_collections_group_orientation},
      {"collections.group.data.item", st_collections_group_data_item},
      {"collections.group.externals.external", st_externals_external}, /* dup */
      {"collections.group.image", st_images_image}, /* dup */
@@ -350,6 +354,7 @@ New_Statement_Handler statement_handlers[] =
      {"collections.group.parts.part.dragable.events", st_collections_group_parts_part_dragable_events},
      {"collections.group.parts.part.entry_mode", st_collections_group_parts_part_entry_mode},
      {"collections.group.parts.part.select_mode", st_collections_group_parts_part_select_mode},
+     {"collections.group.parts.part.cursor_mode", st_collections_group_parts_part_cursor_mode},
      {"collections.group.parts.part.multiline", st_collections_group_parts_part_multiline},
      {"collections.group.parts.part.image", st_images_image}, /* dup */
      {"collections.group.parts.part.set.name", st_images_set_name},
@@ -445,6 +450,7 @@ New_Statement_Handler statement_handlers[] =
      {"collections.group.parts.part.description.text.style", st_collections_group_parts_part_description_text_style},
      {"collections.group.parts.part.description.text.repch", st_collections_group_parts_part_description_text_repch},
      {"collections.group.parts.part.description.text.size", st_collections_group_parts_part_description_text_size},
+     {"collections.group.parts.part.description.text.size_range", st_collections_group_parts_part_description_text_size_range},
      {"collections.group.parts.part.description.text.fit", st_collections_group_parts_part_description_text_fit},
      {"collections.group.parts.part.description.text.min", st_collections_group_parts_part_description_text_min},
      {"collections.group.parts.part.description.text.max", st_collections_group_parts_part_description_text_max},
@@ -454,6 +460,7 @@ New_Statement_Handler statement_handlers[] =
      {"collections.group.parts.part.description.text.font", st_fonts_font}, /* dup */
      {"collections.group.parts.part.description.text.fonts.font", st_fonts_font}, /* dup */
      {"collections.group.parts.part.description.text.elipsis", st_collections_group_parts_part_description_text_elipsis},
+     {"collections.group.parts.part.description.text.ellipsis", st_collections_group_parts_part_description_text_elipsis},
      {"collections.group.parts.part.description.box.layout", st_collections_group_parts_part_description_box_layout},
      {"collections.group.parts.part.description.box.align", st_collections_group_parts_part_description_box_align},
      {"collections.group.parts.part.description.box.padding", st_collections_group_parts_part_description_box_padding},
@@ -1997,6 +2004,35 @@ st_collections_group_data_item(void)
 
 /**
     @page edcref
+    @property
+        orientation
+    @parameters
+    enum AUTO, LTR, RTL
+    @effect
+        This defines GROUP orientation.
+        This is useful if you want match interface orientation with language.
+        AUTO  - Follow system defs.
+        LTR  - suitable for Left To Right Languages (latin)
+        RTL - suitable for Right To Left Languages (Hebrew, Arabic interface)
+    @endproperty
+*/
+static void
+st_collections_group_orientation(void)
+{
+   Edje_Part_Collection *pc;
+
+   check_arg_count(1);
+
+   pc = eina_list_data_get(eina_list_last(edje_collections));
+   pc->prop.orientation = parse_enum(0,
+         "AUTO", EDJE_ORIENTATION_AUTO,
+         "LTR", EDJE_ORIENTATION_LTR,
+         "RTL", EDJE_ORIENTATION_RTL,
+         NULL);
+}
+
+/**
+    @page edcref
     @block
         parts
     @context
@@ -2693,6 +2729,38 @@ st_collections_group_parts_part_select_mode(void)
 /**
     @page edcref
     @property
+        cursor_mode
+    @parameters
+        [MODE]
+    @effect
+        Sets the cursor mode for a textblock part to one of:
+        @li UNDER
+        @li BEFORE
+        UNDER cursor mode means the cursor will draw below the character pointed
+        at. That's the default.
+        BEFORE cursor mode means the cursor is drawn as a vertical line before
+        the current character, just like many other GUI toolkits handle it.
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_cursor_mode(void)
+{
+   Edje_Part_Collection *pc;
+   Edje_Part *ep;
+
+   check_arg_count(1);
+
+   pc = eina_list_data_get(eina_list_last(edje_collections));
+   ep = pc->parts[pc->parts_count - 1];
+   ep->cursor_mode = parse_enum(0,
+                                "UNDER", EDJE_ENTRY_CURSOR_MODE_UNDER,
+                                "BEFORE", EDJE_ENTRY_CURSOR_MODE_BEFORE,
+                                NULL);
+}
+
+/**
+    @page edcref
+    @property
         multiline
     @parameters
         [1 or 0]
@@ -5022,7 +5090,7 @@ st_collections_group_parts_part_description_fill_size_offset(void)
                     align:        X-axis     Y-axis;
                     source:      "part_name";
                     text_source: "text_part_name";
-                    elipsis:      0.0-1.0;
+                    ellipsis:     0.0-1.0;
                     style:       "stylename";
                 }
                 ..
@@ -5281,6 +5349,54 @@ st_collections_group_parts_part_description_text_size(void)
     @page edcref
 
     @property
+        size_range
+    @parameters
+        [font min size in points (pt)] [font max size in points (pt)]
+    @effect
+        Sets the allowed font size for the text part. Setting min and max to 0
+        means we won't restrict the sizing (default).
+    @endproperty
+    @since 1.1.0
+*/
+static void
+st_collections_group_parts_part_description_text_size_range(void)
+{
+   Edje_Part_Collection *pc;
+   Edje_Part *ep;
+   Edje_Part_Description_Text *ed;
+
+   check_arg_count(2);
+
+   pc = eina_list_data_get(eina_list_last(edje_collections));
+   ep = pc->parts[pc->parts_count - 1];
+
+   if ((ep->type != EDJE_PART_TYPE_TEXT) &&
+       (ep->type != EDJE_PART_TYPE_TEXTBLOCK))
+     {
+       ERR("%s: Error. parse error %s:%i. "
+           "text attributes in non-TEXT part.",
+           progname, file_in, line - 1);
+       exit(-1);
+     }
+
+   ed = (Edje_Part_Description_Text*) ep->default_desc;
+   if (ep->other.desc_count) ed = (Edje_Part_Description_Text*)  ep->other.desc[ep->other.desc_count - 1];
+
+   ed->text.size_range_min = parse_int_range(0, 0, 255);
+   ed->text.size_range_max = parse_int_range(1, 0, 255);
+   if (ed->text.size_range_min > ed->text.size_range_max)
+     {
+       ERR("%s: Error. parse error %s:%i. "
+           "min size is bigger than max size.",
+           progname, file_in, line - 1);
+       exit(-1);
+     }
+}
+
+/**
+    @page edcref
+
+    @property
         fit
     @parameters
         [horizontal] [vertical]
@@ -5435,7 +5551,7 @@ st_collections_group_parts_part_description_text_align(void)
    ed = (Edje_Part_Description_Text*) ep->default_desc;
    if (ep->other.desc_count) ed = (Edje_Part_Description_Text*)  ep->other.desc[ep->other.desc_count - 1];
 
-   ed->text.align.x = FROM_DOUBLE(parse_float_range(0, 0.0, 1.0));
+   ed->text.align.x = FROM_DOUBLE(parse_float_range(0, -1.0, 1.0));
    ed->text.align.y = FROM_DOUBLE(parse_float_range(1, 0.0, 1.0));
 }
 
@@ -5533,7 +5649,7 @@ st_collections_group_parts_part_description_text_text_source(void)
     @page edcref
 
     @property
-        elipsis
+        ellipsis
     @parameters
         [point of balance]
     @effect
index 98b922e..40a457a 100644 (file)
@@ -692,7 +692,13 @@ compile(void)
        /*
         * Run the input through the C pre-processor.
         */
-
+        ret = -1;
+        if (ecore_file_exists(EPP_DIR"/epp"))
+          {
+             snprintf(buf, sizeof(buf), EPP_DIR"/epp %s -I%s %s -o %s",
+                      file_in, inc, def, tmpn);
+             ret = system(buf);
+          }
        /*
         * On OpenSolaris, the default cpp is located in different places.
         * Alan Coppersmith told me to do what xorg does: using /usr/ccs/lib/cpp
@@ -700,8 +706,7 @@ compile(void)
         * Also, that preprocessor is not managing C++ comments, so pass the
         * sun cc preprocessor just after.
         */
-        ret = -1;
-        if (ecore_file_exists("/usr/ccs/lib/cpp"))
+        else if (ecore_file_exists("/usr/ccs/lib/cpp"))
           {
              snprintf(buf, sizeof(buf), "/usr/ccs/lib/cpp -I%s %s %s %s",
                       inc, def, file_in, tmpn);
diff --git a/src/bin/epp/Makefile.am b/src/bin/epp/Makefile.am
new file mode 100644 (file)
index 0000000..2d3f3aa
--- /dev/null
@@ -0,0 +1,58 @@
+# Makefile for GNU C compiler.
+#   Copyright (C) 1987, 88, 90-94, 1995 Free Software Foundation, Inc.
+
+#This file is part of GNU CC.
+
+#GNU CC is free software; you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation; either version 2, or (at your option)
+#any later version.
+
+#GNU CC is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with GNU CC; see the file COPYING.  If not, write to
+#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+MAINTAINERCLEANFILES = Makefile.in
+
+eppdir = @libdir@/@PACKAGE@/utils
+
+epp_PROGRAMS = epp
+
+epp_SOURCES = \
+cpplib.h \
+cpphash.h \
+cppalloc.c \
+cpperror.c \
+cppexp.c \
+cpphash.c \
+cpplib.c \
+cppmain.c
+
+epp_CPPFLAGS = \
+-I$(top_builddir) \
+$(CWARNFLAGS)
+
+DEFS= \
+-DHAVE_STRERROR \
+-DFATAL_EXIT_CODE=1 \
+-DSUCCESS_EXIT_CODE=1 \
+-DGCC_INCLUDE_DIR=\"/usr/include\" \
+-DGPLUSPLUS_INCLUDE_DIR=\"/usr/include\" \
+-DTOOL_INCLUDE_DIR=\"/usr/bin\" \
+-DHOST_BITS_PER_LONG=32 \
+-DBITS_PER_UNIT=8 \
+-DHOST_BITS_PER_INT=32 \
+-DBITS_PER_WORD=16 \
+-DTARGET_BELL=7 \
+-DTARGET_BS=8 \
+-DTARGET_FF=12 \
+-DTARGET_NEWLINE=10 \
+-DTARGET_CR=13 \
+-DTARGET_TAB=9 \
+-DTARGET_VT=11
+
diff --git a/src/bin/epp/cppalloc.c b/src/bin/epp/cppalloc.c
new file mode 100644 (file)
index 0000000..2106e1c
--- /dev/null
@@ -0,0 +1,66 @@
+/* Part of CPP library.  (memory allocation - xmalloc etc)
+ * Copyright (C) 1986, 87, 89, 92, 93, 94, 1995 Free Software Foundation, Inc.
+ * Written by Per Bothner, 1994.
+ * Based on CCCP program by by Paul Rubin, June 1986
+ * Adapted to ANSI C, Richard Stallman, Jan 1987
+ * Copyright (C) 2003-2011 Kim Woelders
+ * 
+ * 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
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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.
+ * 
+ * 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!  */
+
+#include <stdlib.h>
+#include "config.h"
+#include "cpplib.h"
+
+static void
+memory_full(void)
+{
+   cpp_fatal("Memory exhausted.");
+}
+
+void               *
+xmalloc(unsigned size)
+{
+   char               *ptr = (char *)malloc(size);
+
+   if (ptr)
+      return (ptr);
+   memory_full();
+   /*NOTREACHED */
+   return 0;
+}
+
+void               *
+xrealloc(void *old, unsigned size)
+{
+   char               *ptr = (char *)realloc(old, size);
+
+   if (!ptr)
+      memory_full();
+   return ptr;
+}
+
+void               *
+xcalloc(unsigned number, unsigned size)
+{
+   char               *ptr = (char *)calloc(number, size);
+
+   if (!ptr)
+      memory_full();
+   return ptr;
+}
diff --git a/src/bin/epp/cpperror.c b/src/bin/epp/cpperror.c
new file mode 100644 (file)
index 0000000..caaa513
--- /dev/null
@@ -0,0 +1,143 @@
+/* Default error handlers for CPP Library.
+ * Copyright (C) 1986, 87, 89, 92, 93, 94, 1995 Free Software Foundation, Inc.
+ * Written by Per Bothner, 1994.
+ * Based on CCCP program by by Paul Rubin, June 1986
+ * Adapted to ANSI C, Richard Stallman, Jan 1987
+ * Copyright (C) 2003-2011 Kim Woelders
+ * 
+ * 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
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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.
+ * 
+ * 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!  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "config.h"
+#include "cpplib.h"
+
+/* Print the file names and line numbers of the #include
+ * commands which led to the current file.  */
+
+void
+cpp_print_containing_files(cpp_reader * pfile)
+{
+   cpp_buffer         *ip;
+   int                 first = 1;
+
+   /* If stack of files hasn't changed since we last printed
+    * this info, don't repeat it.  */
+   if (pfile->input_stack_listing_current)
+      return;
+
+   ip = cpp_file_buffer(pfile);
+
+   /* Give up if we don't find a source file.  */
+   if (!ip)
+      return;
+
+   /* Find the other, outer source files.  */
+   while ((ip = CPP_PREV_BUFFER(ip)), ip != CPP_NULL_BUFFER(pfile))
+     {
+       long                line, col;
+
+       cpp_buf_line_and_col(ip, &line, &col);
+       if (ip->fname)
+         {
+            if (first)
+              {
+                 first = 0;
+                 fprintf(stderr, "In file included");
+              }
+            else
+               fprintf(stderr, ",\n                ");
+         }
+     }
+   if (!first)
+      fprintf(stderr, ":\n");
+
+   /* Record we have printed the status as of this time.  */
+   pfile->input_stack_listing_current = 1;
+}
+
+void
+cpp_file_line_for_message(cpp_reader * pfile __UNUSED__, const char *filename,
+                         int line, int column)
+{
+   if (column > 0)
+     {
+       fprintf(stderr, "%s:%d:%d: ", filename, line, column);
+     }
+   else
+     {
+       fprintf(stderr, "%s:%d: ", filename, line);
+     }
+}
+
+/* IS_ERROR is 1 for error, 0 for warning */
+void
+cpp_message_v(cpp_reader * pfile, int is_error, const char *msg, va_list args)
+{
+   if (is_error)
+      pfile->errors++;
+   else
+      fprintf(stderr, "warning: ");
+   vfprintf(stderr, msg, args);
+   fprintf(stderr, "\n");
+}
+
+void
+cpp_message(cpp_reader * pfile, int is_error, const char *msg, ...)
+{
+   va_list             args;
+
+   va_start(args, msg);
+
+   cpp_message_v(pfile, is_error, msg, args);
+
+   va_end(args);
+}
+
+static void
+cpp_fatal_v(const char *msg, va_list args)
+{
+   fprintf(stderr, "%s: ", progname);
+   vfprintf(stderr, msg, args);
+   fprintf(stderr, "\n");
+   exit(FATAL_EXIT_CODE);
+}
+
+void
+cpp_fatal(const char *msg, ...)
+{
+   va_list             args;
+
+   va_start(args, msg);
+
+   cpp_fatal_v(msg, args);
+
+   va_end(args);
+}
+
+void
+cpp_pfatal_with_name(cpp_reader * pfile, const char *name)
+{
+   cpp_perror_with_name(pfile, name);
+#ifdef VMS
+   exit(vaxc$errno);
+#else
+   exit(FATAL_EXIT_CODE);
+#endif
+}
diff --git a/src/bin/epp/cppexp.c b/src/bin/epp/cppexp.c
new file mode 100644 (file)
index 0000000..507fec3
--- /dev/null
@@ -0,0 +1,1087 @@
+/* Parse C expressions for CCCP.
+ * Copyright (C) 1987, 1992, 1994, 1995 Free Software Foundation.
+ * Copyright (C) 2003-2011 Kim Woelders
+ * 
+ * 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
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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.
+ * 
+ * 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. */
+
+/* Parse a C expression from text in a string  */
+
+#include "config.h"
+#include "cpplib.h"
+#include "cpphash.h"
+
+#ifdef __EMX__
+#include <strings.h>
+#endif
+
+#ifdef MULTIBYTE_CHARS
+#include <locale.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* This is used for communicating lists of keywords with cccp.c.  */
+struct arglist {
+   struct arglist     *next;
+   unsigned 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
+
+#ifndef CHAR_TYPE_SIZE
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+#endif
+
+#ifndef INT_TYPE_SIZE
+#define INT_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef LONG_TYPE_SIZE
+#define LONG_TYPE_SIZE BITS_PER_WORD
+#endif
+
+#ifndef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
+#endif
+
+#ifndef MAX_CHAR_TYPE_SIZE
+#define MAX_CHAR_TYPE_SIZE CHAR_TYPE_SIZE
+#endif
+
+#ifndef MAX_INT_TYPE_SIZE
+#define MAX_INT_TYPE_SIZE INT_TYPE_SIZE
+#endif
+
+#ifndef MAX_LONG_TYPE_SIZE
+#define MAX_LONG_TYPE_SIZE LONG_TYPE_SIZE
+#endif
+
+#ifndef MAX_WCHAR_TYPE_SIZE
+#define MAX_WCHAR_TYPE_SIZE WCHAR_TYPE_SIZE
+#endif
+
+/* 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)
+
+#define ERROR 299
+#define OROR 300
+#define ANDAND 301
+#define EQUAL 302
+#define NOTEQUAL 303
+#define LEQ 304
+#define GEQ 305
+#define LSH 306
+#define RSH 307
+#define NAME 308
+#define INT 309
+#define CHAR 310
+
+#define LEFT_OPERAND_REQUIRED 1
+#define RIGHT_OPERAND_REQUIRED 2
+#define HAVE_VALUE 4
+/* SKIP_OPERAND is set for '&&' '||' '?' and ':' when the
+ * following operand should be short-circuited instead of evaluated. */
+#define SKIP_OPERAND 8
+/*#define UNSIGNEDP 16 */
+
+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. */
+};
+
+/* 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 */
+
+static void
+parse_number(struct operation *op, cpp_reader * pfile, const char *start,
+            int olen)
+{
+   const char         *p = start;
+   int                 c;
+   unsigned long       n = 0, nd, ULONG_MAX_over_base;
+   int                 base = 10;
+   int                 len = olen;
+   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;
+       }
+   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;
+
+                 if (--len == 0)
+                    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 |= (ULONG_MAX_over_base < n) | (nd < n);
+       n = nd;
+     }
+
+   if (len != 0)
+     {
+       cpp_error(pfile, "Invalid number in #if expression");
+       op->op = ERROR;
+       return;
+     }
+   if (base <= largest_digit)
+      cpp_warning(pfile, "integer constant contains digits beyond the radix");
+
+   if (overflow)
+      cpp_warning(pfile, "integer constant out of range");
+
+   /* If too big to be signed, consider it unsigned.  */
+   if ((long)n < 0 && !op->unsignedp)
+     {
+       if (base == 10)
+          cpp_warning(pfile,
+                      "integer constant is so large that it is unsigned");
+       op->unsignedp = 1;
+     }
+   op->value = n;
+   op->op = INT;
+}
+
+struct token {
+   const char         *oper;
+   int                 token;
+};
+
+static struct token tokentab2[] = {
+   {"&&", ANDAND},
+   {"||", OROR},
+   {"<<", LSH},
+   {">>", RSH},
+   {"==", EQUAL},
+   {"!=", NOTEQUAL},
+   {"<=", LEQ},
+   {">=", GEQ},
+   {"++", ERROR},
+   {"--", ERROR},
+   {NULL, ERROR}
+};
+
+/* Read one token. */
+
+static void
+cpp_lex(struct operation *op, cpp_reader * pfile)
+{
+   int                 c;
+   struct token       *toktab;
+   enum cpp_token      token;
+   unsigned char      *tok_start, *tok_end;
+   int                 old_written;
+
+   op->value = 0;
+   op->unsignedp = 0;
+
+ retry:
+
+   old_written = CPP_WRITTEN(pfile);
+   cpp_skip_hspace(pfile);
+   c = CPP_BUF_PEEK(CPP_BUFFER(pfile));
+   if (c == '#')
+     {
+       parse_number(op, pfile, cpp_read_check_assertion(pfile) ? "1" : "0", 1);
+       return;
+     }
+
+   if (c == '\n')
+     {
+       op->op = 0;
+       return;
+     }
+   token = cpp_get_token(pfile);
+   tok_start = pfile->token_buffer + old_written;
+   tok_end = CPP_PWRITTEN(pfile);
+   pfile->limit = tok_start;
+   switch (token)
+     {
+     case CPP_EOF:             /* Should not happen ... */
+       op->op = 0;
+       break;
+
+     case CPP_VSPACE:
+     case CPP_POP:
+       if (CPP_BUFFER(pfile)->fname)
+         {
+            op->op = 0;
+            break;
+         }
+       goto retry;
+
+     case CPP_HSPACE:
+     case CPP_COMMENT:
+       goto retry;
+
+     case CPP_NUMBER:
+       parse_number(op, pfile, (char *)tok_start, tok_end - tok_start);
+       break;
+
+     case CPP_STRING:
+       cpp_error(pfile, "string constants not allowed in #if expressions");
+       op->op = ERROR;
+       break;
+
+     case CPP_CHAR:
+       /* This code for reading a character constant
+        * handles multicharacter constants and wide characters.
+        * It is mostly copied from c-lex.c.  */
+       {
+          int                 result = 0;
+          int                 num_chars = 0;
+          unsigned            width = MAX_CHAR_TYPE_SIZE;
+          int                 wide_flag = 0;
+          int                 max_chars;
+          char               *ptr = (char *)tok_start;
+
+#ifdef MULTIBYTE_CHARS
+          char                token_buffer[MAX_LONG_TYPE_SIZE /
+                                           MAX_CHAR_TYPE_SIZE + MB_CUR_MAX];
+#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 >= (char *)CPP_PWRITTEN(pfile) || (c = *ptr++) == '\'')
+                  break;
+
+               if (c == '\\')
+                 {
+                    c = cpp_parse_escape(pfile, &ptr);
+                    if (width < HOST_BITS_PER_INT
+                        && (unsigned)c >= (unsigned)(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;
+#ifdef MULTIBYTE_CHARS
+                    token_buffer[num_chars - 1] = c;
+#endif
+                 }
+            }
+
+#ifdef MULTIBYTE_CHARS
+          token_buffer[num_chars] = 0;
+#endif
+
+          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");
+
+          /* If char type is signed, sign-extend the constant.  */
+          if (!wide_flag)
+            {
+               int                 num_bits = num_chars * width;
+
+               if (cpp_lookup("__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;
+            }
+       }
+
+       /* This is always a signed type.  */
+       op->unsignedp = 0;
+       op->op = CHAR;
+       break;
+
+     case CPP_NAME:
+       parse_number(op, pfile, "0", 0);
+       break;
+
+     case CPP_OTHER:
+       /* See if it is a special token of length 2.  */
+       if (tok_start + 2 == tok_end)
+         {
+            for (toktab = tokentab2; toktab->oper; toktab++)
+               if (tok_start[0] == toktab->oper[0]
+                   && tok_start[1] == toktab->oper[1])
+                  break;
+            if (toktab->token == ERROR)
+              {
+                 char               *buf = (char *)malloc(40);
+
+                 memset(buf, 0, 40);
+
+                 sprintf(buf, "`%s' not allowed in operand of `#if'",
+                         tok_start);
+                 cpp_error(pfile, buf);
+                 free(buf);
+              }
+            op->op = toktab->token;
+            break;
+         }
+       /* fall through */
+     default:
+       op->op = *tok_start;
+       break;
+     }
+}
+
+/* Parse a C escape sequence.  STRING_PTR points to a variable
+ * containing a pointer to the string to parse.  That pointer
+ * is updated past the characters we use.  The value of the
+ * escape sequence is returned.
+ * 
+ * A negative value means the sequence \ newline was seen,
+ * which is supposed to be equivalent to nothing at all.
+ * 
+ * If \ is followed by a null character, we return a negative
+ * value and leave the string pointer pointing at the null character.
+ * 
+ * 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(cpp_reader * pfile, char **string_ptr)
+{
+   int                 c = *(*string_ptr)++;
+
+   switch (c)
+     {
+     case 'a':
+       return TARGET_BELL;
+     case 'b':
+       return TARGET_BS;
+     case 'e':
+     case 'E':
+       if (CPP_PEDANTIC(pfile))
+          cpp_pedwarn(pfile, "non-ANSI-standard escape sequence, `\\%c'", c);
+       return 033;
+     case 'f':
+       return TARGET_FF;
+     case 'n':
+       return TARGET_NEWLINE;
+     case 'r':
+       return TARGET_CR;
+     case 't':
+       return TARGET_TAB;
+     case 'v':
+       return TARGET_VT;
+     case '\n':
+       return -2;
+     case 0:
+       (*string_ptr)--;
+       return 0;
+
+     case '0':
+     case '1':
+     case '2':
+     case '3':
+     case '4':
+     case '5':
+     case '6':
+     case '7':
+       {
+          int                 i = c - '0';
+          int                 count = 0;
+
+          while (++count < 3)
+            {
+               c = *(*string_ptr)++;
+               if (c >= '0' && c <= '7')
+                  i = (i << 3) + c - '0';
+               else
+                 {
+                    (*string_ptr)--;
+                    break;
+                 }
+            }
+          if ((i & ~((1 << MAX_CHAR_TYPE_SIZE) - 1)) != 0)
+            {
+               i &= (1 << MAX_CHAR_TYPE_SIZE) - 1;
+               cpp_warning(pfile,
+                           "octal character constant does not fit in a byte");
+            }
+          return i;
+       }
+     case 'x':
+       {
+          unsigned            i = 0, overflow = 0, digits_found = 0, digit;
+
+          for (;;)
+            {
+               c = *(*string_ptr)++;
+               if (c >= '0' && c <= '9')
+                  digit = c - '0';
+               else if (c >= 'a' && c <= 'f')
+                  digit = c - 'a' + 10;
+               else if (c >= 'A' && c <= 'F')
+                  digit = c - 'A' + 10;
+               else
+                 {
+                    (*string_ptr)--;
+                    break;
+                 }
+               overflow |= i ^ (i << 4 >> 4);
+               i = (i << 4) + digit;
+               digits_found = 1;
+            }
+          if (!digits_found)
+             cpp_error(pfile, "\\x used with no following hex digits");
+          if (overflow | (i & ~((1 << BITS_PER_UNIT) - 1)))
+            {
+               i &= (1 << BITS_PER_UNIT) - 1;
+               cpp_warning(pfile,
+                           "hex character constant does not fit in a byte");
+            }
+          return i;
+       }
+     default:
+       return c;
+     }
+}
+
+static void
+integer_overflow(cpp_reader * pfile)
+{
+   if (CPP_PEDANTIC(pfile))
+      cpp_pedwarn(pfile, "integer overflow in preprocessor expression");
+}
+
+static long
+left_shift(cpp_reader * pfile, long a, int unsignedp, unsigned long b)
+{
+   if (b >= HOST_BITS_PER_LONG)
+     {
+       if (!unsignedp && a != 0)
+          integer_overflow(pfile);
+       return 0;
+     }
+   else if (unsignedp)
+      return (unsigned long)a << b;
+   else
+     {
+       long                l = a << b;
+
+       if (l >> b != a)
+          integer_overflow(pfile);
+       return l;
+     }
+}
+
+static long
+right_shift(cpp_reader * pfile __UNUSED__, long a, int unsignedp,
+           unsigned long b)
+{
+   if (b >= HOST_BITS_PER_LONG)
+     {
+       return unsignedp ? 0 : a >> (HOST_BITS_PER_LONG - 1);
+     }
+   else if (unsignedp)
+     {
+       return (unsigned long)a >> b;
+     }
+   else
+     {
+       return a >> b;
+     }
+}
+
+/* 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)
+#define OROR_PRIO (COND_PRIO+2)
+#define ANDAND_PRIO (OROR_PRIO+2)
+#define OR_PRIO (ANDAND_PRIO+2)
+#define XOR_PRIO (OR_PRIO+2)
+#define AND_PRIO (XOR_PRIO+2)
+#define EQUAL_PRIO (AND_PRIO+2)
+#define LESS_PRIO (EQUAL_PRIO+2)
+#define SHIFT_PRIO (LESS_PRIO+2)
+#define PLUS_PRIO (SHIFT_PRIO+2)
+#define MUL_PRIO (PLUS_PRIO+2)
+#define UNARY_PRIO (MUL_PRIO+2)
+#define PAREN_OUTER_PRIO (UNARY_PRIO+2)
+
+#define COMPARE(OP) \
+  top->unsignedp = 0;\
+  top->value = (unsigned1 || unsigned2) ? (unsigned long) v1 OP (unsigned long) v2 : (v1 OP v2)
+
+/* Parse and evaluate a C expression, reading from PFILE.
+ * Returns the value of the expression.  */
+
+HOST_WIDE_INT
+cpp_parse_expr(cpp_reader * pfile)
+{
+   /* The implementation is an operator precedence parser,
+    * i.e. a bottom-up parser, using a stack for not-yet-reduced tokens.
+    * 
+    * The stack base is 'stack', and the current stack pointer is 'top'.
+    * There is a stack element for each operator (only),
+    * and the most recently pushed operator is 'top->op'.
+    * An operand (value) is stored in the 'value' field of the stack
+    * element of the operator that precedes it.
+    * In that case the 'flags' field has the HAVE_VALUE flag set.  */
+
+#define INIT_STACK_SIZE 20
+   struct operation    init_stack[INIT_STACK_SIZE];
+   struct operation   *stack = init_stack;
+   struct operation   *limit = stack + INIT_STACK_SIZE;
+   struct operation   *top = stack;
+   int                 lprio = 0, rprio = 0;
+   int                 skip_evaluation = 0;
+
+   top->rprio = 0;
+   top->flags = 0;
+   for (;;)
+     {
+       struct operation    op;
+       char                flags = 0;
+
+       /* Read a token */
+       cpp_lex(&op, pfile);
+
+       /* 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. */
+
+       switch (op.op)
+         {
+         case NAME:
+            top->value = 0, top->unsignedp = 0;
+            goto set_value;
+         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;
+         case '!':
+         case '~':
+            flags = RIGHT_OPERAND_REQUIRED;
+            rprio = UNARY_PRIO;
+            lprio = rprio + 1;
+            goto maybe_reduce;
+         case '*':
+         case '/':
+         case '%':
+            lprio = MUL_PRIO;
+            goto binop;
+         case '<':
+         case '>':
+         case LEQ:
+         case GEQ:
+            lprio = LESS_PRIO;
+            goto binop;
+         case EQUAL:
+         case NOTEQUAL:
+            lprio = EQUAL_PRIO;
+            goto binop;
+         case LSH:
+         case RSH:
+            lprio = SHIFT_PRIO;
+            goto binop;
+         case '&':
+            lprio = AND_PRIO;
+            goto binop;
+         case '^':
+            lprio = XOR_PRIO;
+            goto binop;
+         case '|':
+            lprio = OR_PRIO;
+            goto binop;
+         case ANDAND:
+            lprio = ANDAND_PRIO;
+            goto binop;
+         case OROR:
+            lprio = OROR_PRIO;
+            goto binop;
+         case ',':
+            lprio = COMMA_PRIO;
+            goto binop;
+         case '(':
+            lprio = PAREN_OUTER_PRIO;
+            rprio = PAREN_INNER_PRIO;
+            goto maybe_reduce;
+         case ')':
+            lprio = PAREN_INNER_PRIO;
+            rprio = PAREN_OUTER_PRIO;
+            goto maybe_reduce;
+         case ':':
+            lprio = COND_PRIO;
+            rprio = COND_PRIO;
+            goto maybe_reduce;
+         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;
+         default:
+            cpp_error(pfile, "invalid character in #if");
+            goto syntax_error;
+         }
+
+      set_value:
+       /* Push a value onto the stack. */
+       if (top->flags & HAVE_VALUE)
+         {
+            cpp_error(pfile, "syntax error in #if");
+            goto syntax_error;
+         }
+       top->flags |= HAVE_VALUE;
+       continue;
+
+      maybe_reduce:
+       /* 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;
+
+            top--;
+            if ((top[1].flags & LEFT_OPERAND_REQUIRED)
+                && !(top[0].flags & HAVE_VALUE))
+              {
+                 cpp_error(pfile, "syntax error - missing left operand");
+                 goto syntax_error;
+              }
+            if ((top[1].flags & RIGHT_OPERAND_REQUIRED)
+                && !(top[1].flags & HAVE_VALUE))
+              {
+                 cpp_error(pfile, "syntax error - missing right operand");
+                 goto syntax_error;
+              }
+            /* top[0].value = (top[1].op)(v1, v2); */
+            switch (top[1].op)
+              {
+              case '+':
+                 if (!(top->flags & HAVE_VALUE))
+                   {           /* Unary '+' */
+                      top->value = v2;
+                      top->unsignedp = unsigned2;
+                      top->flags |= HAVE_VALUE;
+                   }
+                 else
+                   {
+                      top->value = v1 + v2;
+                      top->unsignedp = unsigned1 || unsigned2;
+                      if (!top->unsignedp && !skip_evaluation
+                          && !possible_sum_sign(v1, v2, top->value))
+                         integer_overflow(pfile);
+                   }
+                 break;
+              case '-':
+                 if (skip_evaluation); /* do nothing */
+                 else if (!(top->flags & HAVE_VALUE))
+                   {           /* Unary '-' */
+                      top->value = -v2;
+                      if ((top->value & v2) < 0 && !unsigned2)
+                         integer_overflow(pfile);
+                      top->unsignedp = unsigned2;
+                      top->flags |= HAVE_VALUE;
+                   }
+                 else
+                   {           /* Binary '-' */
+                      top->value = v1 - v2;
+                      top->unsignedp = unsigned1 || unsigned2;
+                      if (!top->unsignedp
+                          && !possible_sum_sign(top->value, v2, v1))
+                         integer_overflow(pfile);
+                   }
+                 break;
+              case '*':
+                 top->unsignedp = unsigned1 || unsigned2;
+                 if (top->unsignedp)
+                    top->value = (unsigned long)v1 *v2;
+
+                 else if (!skip_evaluation)
+                   {
+                      top->value = v1 * v2;
+                      if (v1
+                          && (top->value / v1 != v2
+                              || (top->value & v1 & v2) < 0))
+                         integer_overflow(pfile);
+                   }
+                 break;
+              case '/':
+                 if (skip_evaluation)
+                    break;
+                 if (v2 == 0)
+                   {
+                      cpp_error(pfile, "division by zero in #if");
+                      v2 = 1;
+                   }
+                 top->unsignedp = unsigned1 || unsigned2;
+                 if (top->unsignedp)
+                    top->value = (unsigned long)v1 / v2;
+                 else
+                   {
+                      top->value = v1 / v2;
+                      if ((top->value & v1 & v2) < 0)
+                         integer_overflow(pfile);
+                   }
+                 break;
+              case '%':
+                 if (skip_evaluation)
+                    break;
+                 if (v2 == 0)
+                   {
+                      cpp_error(pfile, "division by zero in #if");
+                      v2 = 1;
+                   }
+                 top->unsignedp = unsigned1 || unsigned2;
+                 if (top->unsignedp)
+                    top->value = (unsigned long)v1 % v2;
+                 else
+                    top->value = v1 % v2;
+                 break;
+              case '!':
+                 if (top->flags & HAVE_VALUE)
+                   {
+                      cpp_error(pfile, "syntax error");
+                      goto syntax_error;
+                   }
+                 top->value = !v2;
+                 top->unsignedp = 0;
+                 top->flags |= HAVE_VALUE;
+                 break;
+              case '~':
+                 if (top->flags & HAVE_VALUE)
+                   {
+                      cpp_error(pfile, "syntax error");
+                      goto syntax_error;
+                   }
+                 top->value = ~v2;
+                 top->unsignedp = unsigned2;
+                 top->flags |= HAVE_VALUE;
+                 break;
+              case '<':
+                 COMPARE(<);
+                 break;
+              case '>':
+                 COMPARE(>);
+                 break;
+              case LEQ:
+                 COMPARE(<=);
+                 break;
+              case GEQ:
+                 COMPARE(>=);
+                 break;
+              case EQUAL:
+                 top->value = (v1 == v2);
+                 top->unsignedp = 0;
+                 break;
+              case NOTEQUAL:
+                 top->value = (v1 != v2);
+                 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);
+                 else
+                    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);
+                 else
+                    top->value = right_shift(pfile, v1, unsigned1, v2);
+                 break;
+#define LOGICAL(OP) \
+             top->value = v1 OP v2;\
+             top->unsignedp = unsigned1 || unsigned2;
+              case '&':
+                 LOGICAL(&);
+                 break;
+              case '^':
+                 LOGICAL(^);
+                 break;
+              case '|':
+                 LOGICAL(|);
+                 break;
+              case ANDAND:
+                 top->value = v1 && v2;
+                 top->unsignedp = 0;
+                 if (!v1)
+                    skip_evaluation--;
+                 break;
+              case OROR:
+                 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'");
+                 top->value = v2;
+                 top->unsignedp = unsigned2;
+                 break;
+              case '(':
+              case '?':
+                 cpp_error(pfile, "syntax error in #if");
+                 goto syntax_error;
+              case ':':
+                 if (top[0].op != '?')
+                   {
+                      cpp_error(pfile,
+                                "syntax error ':' without preceding '?'");
+                      goto syntax_error;
+                   }
+                 else if (!(top[1].flags & HAVE_VALUE)
+                          || !(top[-1].flags & HAVE_VALUE)
+                          || !(top[0].flags & HAVE_VALUE))
+                   {
+                      cpp_error(pfile, "bad syntax for ?: operator");
+                      goto syntax_error;
+                   }
+                 else
+                   {
+                      top--;
+                      if (top->value)
+                         skip_evaluation--;
+                      top->value = top->value ? v1 : v2;
+                      top->unsignedp = unsigned1 || unsigned2;
+                   }
+                 break;
+              case ')':
+                 if ((top[1].flags & HAVE_VALUE)
+                     || !(top[0].flags & HAVE_VALUE)
+                     || top[0].op != '(' || (top[-1].flags & HAVE_VALUE))
+                   {
+                      cpp_error(pfile, "mismatched parentheses in #if");
+                      goto syntax_error;
+                   }
+                 else
+                   {
+                      top--;
+                      top->value = v1;
+                      top->unsignedp = unsigned1;
+                      top->flags |= HAVE_VALUE;
+                   }
+                 break;
+              default:
+                 fprintf(stderr,
+                         top[1].op >= ' ' && top[1].op <= '~'
+                         ? "unimplemented operator '%c'\n"
+                         : "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;
+         }
+       top++;
+
+       /* Check for and handle stack overflow. */
+       if (top == limit)
+         {
+            struct operation   *new_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);
+            else
+              {
+                 new_stack = (struct operation *)xmalloc(new_size);
+                 memcpy((char *)new_stack, (char *)stack, old_size);
+              }
+            stack = new_stack;
+            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:
+   if (stack != init_stack)
+      free(stack);
+   skip_rest_of_line(pfile);
+   return 0;
+}
diff --git a/src/bin/epp/cpphash.c b/src/bin/epp/cpphash.c
new file mode 100644 (file)
index 0000000..8545b9a
--- /dev/null
@@ -0,0 +1,195 @@
+/* Part of CPP library.  (Macro hash table support.)
+ * Copyright (C) 1986, 87, 89, 92, 93, 94, 1995 Free Software Foundation, Inc.
+ * Written by Per Bothner, 1994.
+ * Based on CCCP program by by Paul Rubin, June 1986
+ * Adapted to ANSI C, Richard Stallman, Jan 1987
+ * Copyright (C) 2003-2011 Kim Woelders
+ * 
+ * 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
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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.
+ * 
+ * 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!  */
+
+#include "config.h"
+#include "cpplib.h"
+#include "cpphash.h"
+
+static HASHNODE    *hashtab[HASHSIZE];
+
+#include <string.h>
+#include <stdlib.h>
+
+#define IS_IDCHAR(ch) is_idchar[(unsigned char)(ch)]
+
+/*
+ * return hash function on name.  must be compatible with the one
+ * computed a step at a time, elsewhere
+ */
+int
+hashf(const char *name, int len, int hashsize)
+{
+   int                 r = 0;
+
+   while (len--)
+      r = HASHSTEP(r, *name++);
+
+   return MAKE_POS(r) % hashsize;
+}
+
+/*
+ * find the most recent hash node for name name (ending with first
+ * non-identifier char) installed by install
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+HASHNODE           *
+cpp_lookup(const char *name, int len, int hash)
+{
+   const char         *bp;
+   HASHNODE           *bucket;
+
+   if (len < 0)
+     {
+       for (bp = name; IS_IDCHAR(*bp); bp++)
+          ;
+       len = bp - name;
+     }
+   if (hash < 0)
+      hash = hashf(name, len, HASHSIZE);
+
+   bucket = hashtab[hash];
+   while (bucket)
+     {
+       if (bucket->length == len
+           && strncmp((const char *)bucket->name, name, len) == 0)
+          return bucket;
+       bucket = bucket->next;
+     }
+   return (HASHNODE *) 0;
+}
+
+/*
+ * Delete a hash node.  Some weirdness to free junk from macros.
+ * More such weirdness will have to be added if you define more hash
+ * types that need it.
+ */
+
+/* Note that the DEFINITION of a macro is removed from the hash table
+ * but its storage is not freed.  This would be a storage leak
+ * except that it is not reasonable to keep undefining and redefining
+ * large numbers of macros many times.
+ * In any case, this is necessary, because a macro can be #undef'd
+ * in the middle of reading the arguments to a call to it.
+ * If #undef freed the DEFINITION, that would crash.  */
+
+void
+delete_macro(HASHNODE * hp)
+{
+
+   if (hp->prev)
+      hp->prev->next = hp->next;
+   if (hp->next)
+      hp->next->prev = hp->prev;
+
+   /* make sure that the bucket chain header that
+    * the deleted guy was on points to the right thing afterwards. */
+   if (hp == *hp->bucket_hdr)
+      *hp->bucket_hdr = hp->next;
+
+   if (hp->type == T_MACRO)
+     {
+       DEFINITION         *d = hp->value.defn;
+       struct reflist     *ap, *nextap;
+
+       for (ap = d->pattern; ap; ap = nextap)
+         {
+            nextap = ap->next;
+            free(ap);
+         }
+       if (d->nargs >= 0)
+          free(d->args.argnames);
+       free(d);
+     }
+   free(hp);
+}
+/*
+ * install a name in the main hash table, even if it is already there.
+ *   name stops with first non alphanumeric, except leading '#'.
+ * caller must check against redefinition if that is desired.
+ * delete_macro () removes things installed by install () in fifo order.
+ * this is important because of the `defined' special symbol used
+ * in #if, and also if pushdef/popdef directives are ever implemented.
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+HASHNODE           *
+install(const char *name, int len, enum node_type type, int ivalue, char *value,
+       int hash)
+{
+   HASHNODE           *hp;
+   int                 i, bucket;
+   const char         *p;
+
+   if (len < 0)
+     {
+       p = name;
+       while (IS_IDCHAR(*p))
+          p++;
+       len = p - name;
+     }
+   if (hash < 0)
+      hash = hashf(name, len, HASHSIZE);
+
+   i = sizeof(HASHNODE) + len + 1;
+   hp = (HASHNODE *) xmalloc(i);
+   bucket = hash;
+   hp->bucket_hdr = &hashtab[bucket];
+   hp->next = hashtab[bucket];
+   hashtab[bucket] = hp;
+   hp->prev = NULL;
+   if (hp->next)
+      hp->next->prev = hp;
+   hp->type = type;
+   hp->length = len;
+   if (hp->type == T_CONST)
+      hp->value.ival = ivalue;
+   else
+      hp->value.cpval = value;
+   hp->name = ((char *)hp) + sizeof(HASHNODE);
+   memcpy(hp->name, name, len);
+   hp->name[len] = 0;
+   return hp;
+}
+
+void
+cpp_hash_cleanup(cpp_reader * pfile __UNUSED__)
+{
+   int                 i;
+
+   for (i = HASHSIZE; --i >= 0;)
+     {
+       while (hashtab[i])
+          delete_macro(hashtab[i]);
+     }
+}
diff --git a/src/bin/epp/cpplib.c b/src/bin/epp/cpplib.c
new file mode 100644 (file)
index 0000000..cb9e094
--- /dev/null
@@ -0,0 +1,7398 @@
+/* CPP Library.
+ * Copyright (C) 1986, 87, 89, 92, 93, 94, 1995 Free Software Foundation, Inc.
+ * Written by Per Bothner, 1994-95.
+ * Based on CCCP program by by Paul Rubin, June 1986
+ * Adapted to ANSI C, Richard Stallman, Jan 1987
+ * Copyright (C) 2003-2011 Kim Woelders
+ * 
+ * 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
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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.
+ * 
+ * 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!  */
+
+const char         *version_string = "0.0.0";
+
+#include "config.h"
+
+#ifdef __EMX__
+#include <strings.h>
+#endif
+
+#ifndef STANDARD_INCLUDE_DIR
+#define STANDARD_INCLUDE_DIR "/usr/include"
+#endif
+
+#ifndef LOCAL_INCLUDE_DIR
+#define LOCAL_INCLUDE_DIR "/usr/local/include"
+#endif
+
+#include "cpplib.h"
+#include "cpphash.h"
+
+#ifndef STDC_VALUE
+#define STDC_VALUE 1
+#endif
+
+/* By default, colon separates directories in a path.  */
+#ifndef PATH_SEPARATOR
+#define PATH_SEPARATOR ':'
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#ifndef VMS
+#ifndef USG
+#include <time.h>
+#include <sys/time.h>          /* for __DATE__ and __TIME__ */
+#include <sys/resource.h>
+#else
+#include <sys/param.h>         /* CYGNUS LOCAL: shebs -noquiet */
+#include <sys/times.h>
+#include <time.h>
+#include <fcntl.h>
+#endif /* USG */
+#endif /* not VMS */
+
+/* This defines "errno" properly for VMS, and gives us EACCES. */
+#include <errno.h>
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+
+#undef MIN
+#undef MAX
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+/* Define a generic NULL if one hasn't already been defined.  */
+
+#ifndef GENERIC_PTR
+#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
+#define GENERIC_PTR void *
+#else
+#define GENERIC_PTR char *
+#endif
+#endif
+
+#ifndef INCLUDE_LEN_FUDGE
+#define INCLUDE_LEN_FUDGE 0
+#endif
+
+#define USE_FILE_NAME_MAPS 0
+
+/* Symbols to predefine.  */
+
+#ifdef CPP_PREDEFINES
+static const char  *predefs = CPP_PREDEFINES;
+
+#else
+static const char  *predefs = "";
+
+#endif
+
+/* We let tm.h override the types used here, to handle trivial differences
+ * such as the choice of unsigned int or long unsigned int for size_t.
+ * When machines start needing nontrivial differences in the size type,
+ * it would be best to do something here to figure out automatically
+ * from other information what type to use.  */
+
+/* The string value for __SIZE_TYPE__.  */
+
+#ifndef SIZE_TYPE
+#define SIZE_TYPE "long unsigned int"
+#endif
+
+/* The string value for __PTRDIFF_TYPE__.  */
+
+#ifndef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "long int"
+#endif
+
+/* The string value for __WCHAR_TYPE__.  */
+
+#ifndef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+#endif
+#define CPP_WCHAR_TYPE(PFILE) \
+       (CPP_OPTIONS (PFILE)->cplusplus ? "__wchar_t" : WCHAR_TYPE)
+
+/* The string value for __USER_LABEL_PREFIX__ */
+
+#ifndef USER_LABEL_PREFIX
+#define USER_LABEL_PREFIX ""
+#endif
+
+/* The string value for __REGISTER_PREFIX__ */
+
+#ifndef REGISTER_PREFIX
+#define REGISTER_PREFIX ""
+#endif
+
+struct directive {
+   int                 length;
+   int                 (*func) (cpp_reader * pfile, struct directive * keyword,
+                               unsigned char *buf, unsigned char *limit);
+   const char         *name;
+   enum node_type      type;
+   char                command_reads_line;
+   char                traditional_comments;
+   char                pass_thru;
+};
+
+/* In the definition of a #assert name, this structure forms
+ * a list of the individual values asserted.
+ * Each value is itself a list of "tokens".
+ * These are strings that are compared by name.  */
+
+struct tokenlist_list {
+   struct tokenlist_list *next;
+   struct arglist     *tokens;
+};
+
+struct assertion_hashnode {
+   struct assertion_hashnode *next;    /* double links for easy deletion */
+   struct assertion_hashnode *prev;
+   /* also, a back pointer to this node's hash
+    * chain is kept, in case the node is the head
+    * of the chain and gets deleted. */
+   struct assertion_hashnode **bucket_hdr;
+   int                 length; /* length of token, for quick comparison */
+   char               *name;   /* the actual name */
+   /* List of token-sequences.  */
+   struct tokenlist_list *value;
+};
+
+#define SKIP_WHITE_SPACE(p) do { while (is_hor_space[(unsigned char)(*p)]) p++; } while (0)
+#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[(unsigned char)(*p)]) p++; } while (0)
+
+#define PEEKN(N) (CPP_BUFFER (pfile)->rlimit - CPP_BUFFER (pfile)->cur >= (N) ? CPP_BUFFER (pfile)->cur[N] : EOF)
+#define FORWARD(N) CPP_FORWARD (CPP_BUFFER (pfile), (N))
+#define GETC() CPP_BUF_GET (CPP_BUFFER (pfile))
+#define PEEKC() CPP_BUF_PEEK (CPP_BUFFER (pfile))
+/* CPP_IS_MACRO_BUFFER is true if the buffer contains macro expansion.
+ * (Note that it is false while we're expanding marco *arguments*.) */
+#define CPP_IS_MACRO_BUFFER(PBUF) ((PBUF)->cleanup == macro_cleanup)
+
+/* Move all backslash-newline pairs out of embarrassing places.
+ * Exchange all such pairs following BP
+ * with any potentially-embarrassing characters that follow them.
+ * Potentially-embarrassing characters are / and *
+ * (because a backslash-newline inside a comment delimiter
+ * would cause it not to be recognized).  */
+
+#define NEWLINE_FIX \
+  do {while (PEEKC() == '\\' && PEEKN(1) == '\n') FORWARD(2); } while(0)
+
+/* Same, but assume we've already read the potential '\\' into C. */
+#define NEWLINE_FIX1(C) do { \
+    while ((C) == '\\' && PEEKC() == '\n') { FORWARD(1); (C) = GETC(); }\
+  } while(0)
+
+/* Name under which this program was invoked.  */
+
+char               *progname;
+
+struct cpp_pending {
+   struct cpp_pending *next;
+   const char         *cmd;
+   const char         *arg;
+};
+
+/* Structure returned by create_definition */
+typedef struct {
+   struct definition  *defn;
+   char               *symnam;
+   int                 symlen;
+} MACRODEF;
+
+/* Forward declarations.  */
+typedef struct file_name_list file_name_list;
+
+static void         add_import(cpp_reader * pfile, int fd, char *fname);
+static int          finclude(cpp_reader * pfile, int f, const char *fname,
+                            int system_header_p, file_name_list * dirptr);
+static void         validate_else(cpp_reader * pfile, const char *directive);
+static int          comp_def_part(int first, unsigned char *beg1, int len1,
+                                 unsigned char *beg2, int len2, int last);
+static int          lookup_import(cpp_reader * pfile, char *filename,
+                                 file_name_list * searchptr);
+static int          redundant_include_p(cpp_reader * pfile, char *name);
+
+static int          is_system_include(cpp_reader * pfile, char *filename);
+
+static int          open_include_file(cpp_reader * pfile, char *filename,
+                                     file_name_list * searchptr);
+static int          check_macro_name(cpp_reader * pfile, unsigned char *symname,
+                                    const char *usage);
+
+static int          compare_token_lists(struct arglist *l1, struct arglist *l2);
+static HOST_WIDE_INT eval_if_expression(cpp_reader * pfile, unsigned char *buf,
+                                       int length);
+
+static int          file_size_and_mode(int fd, int *mode_pointer,
+                                      long int *size_pointer);
+static struct arglist *read_token_list(cpp_reader * pfile, int *error_flag);
+static void         free_token_list(struct arglist *tokens);
+static int          safe_read(int desc, char *ptr, int len);
+static void         push_macro_expansion(cpp_reader * pfile,
+                                        unsigned char *x,
+                                        int xbuf_len, HASHNODE * hp);
+
+static struct cpp_pending *nreverse_pending(struct cpp_pending *list);
+static char        *savestring(const char *input);
+
+static void         conditional_skip(cpp_reader * pfile, int skip,
+                                    enum node_type type,
+                                    unsigned char *control_macro);
+static void         skip_if_group(cpp_reader * pfile, int any);
+
+static void         cpp_error_with_line(cpp_reader * pfile, int line,
+                                       int column, const char *msg);
+static void         cpp_pedwarn_with_line(cpp_reader * pfile, int line,
+                                         int column, const char *msg);
+static void         cpp_pedwarn_with_file_and_line(cpp_reader * pfile,
+                                                  const char *file, int line,
+                                                  const char *msg,
+                                                  const char *arg1,
+                                                  const char *arg2,
+                                                  const char *arg3);
+static void         cpp_error_from_errno(cpp_reader * pfile, const char *name);
+
+static cpp_buffer  *cpp_push_buffer(cpp_reader * pfile, unsigned char *buffer,
+                                   long length);
+static cpp_buffer  *cpp_pop_buffer(cpp_reader * pfile);
+
+/* Last arg to output_line_command.  */
+enum file_change_code {
+   same_file, enter_file, leave_file
+};
+
+/* These functions are declared to return int instead of void since they
+ * are going to be placed in a table and some old compilers have trouble with
+ * pointers to functions returning void.  */
+
+static int          do_define(cpp_reader * pfile, struct directive *keyword,
+                             unsigned char *buf, unsigned char *limit);
+
+static int          do_line(cpp_reader * pfile, struct directive *keyword,
+                           unsigned char *unused1, unsigned char *unused2);
+
+static int          do_include(cpp_reader * pfile, struct directive *keyword,
+                              unsigned char *unused1, unsigned char *unused2);
+
+static int          do_undef(cpp_reader * pfile, struct directive *keyword,
+                            unsigned char *buf, unsigned char *limit);
+
+static int          do_error(cpp_reader * pfile, struct directive *keyword,
+                            unsigned char *buf, unsigned char *limit);
+
+static int          do_pragma(cpp_reader * pfile, struct directive *keyword,
+                             unsigned char *buf, unsigned char *limit);
+
+static int          do_ident(cpp_reader * pfile, struct directive *keyword,
+                            unsigned char *buf, unsigned char *limit);
+
+static int          do_if(cpp_reader * pfile, struct directive *keyword,
+                         unsigned char *buf, unsigned char *limit);
+
+static int          do_xifdef(cpp_reader * pfile, struct directive *keyword,
+                             unsigned char *buf, unsigned char *limit);
+
+static int          do_else(cpp_reader * pfile, struct directive *keyword,
+                           unsigned char *buf, unsigned char *limit);
+
+static int          do_elif(cpp_reader * pfile, struct directive *keyword,
+                           unsigned char *buf, unsigned char *limit);
+
+static int          do_endif(cpp_reader * pfile, struct directive *keyword,
+                            unsigned char *buf, unsigned char *limit);
+
+static int          do_assert(cpp_reader * pfile, struct directive *keyword,
+                             unsigned char *buf, unsigned char *limit);
+
+static int          do_unassert(cpp_reader * pfile, struct directive *keyword,
+                               unsigned char *buf, unsigned char *limit);
+
+static int          do_warning(cpp_reader * pfile, struct directive *keyword,
+                              unsigned char *buf, unsigned char *limit);
+
+struct arglist     *reverse_token_list(struct arglist *tokens);
+
+static int          parse_name(cpp_reader * pfile, int c);
+
+static void         parse_set_mark(struct parse_marker *pmark,
+                                  cpp_reader * pfile);
+static void         parse_clear_mark(struct parse_marker *pmark);
+static void         parse_goto_mark(struct parse_marker *pmark,
+                                   cpp_reader * pfile);
+static void         parse_move_mark(struct parse_marker *pmark,
+                                   cpp_reader * pfile);
+
+struct file_name_list {
+   file_name_list     *next;
+   char               *fname;
+   /* If the following is nonzero, it is a macro name.
+    * Don't include the file again if that macro is defined.  */
+   unsigned char      *control_macro;
+   /* If the following is nonzero, it is a C-language system include
+    * directory.  */
+   int                 c_system_include_path;
+   /* Mapping of file names for this directory.  */
+   struct file_name_map *name_map;
+   /* Non-zero if name_map is valid.  */
+   int                 got_name_map;
+};
+
+/* If a buffer's dir field is SELF_DIR_DUMMY, it means the file was found
+ * via the same directory as the file that #included it. */
+#define SELF_DIR_DUMMY ((file_name_list*)(~0))
+
+/* #include "file" looks in source file dir, then stack. */
+/* #include <file> just looks in the stack. */
+/* -I directories are added to the end, then the defaults are added. */
+/* The */
+static struct default_include {
+   const char         *fname;  /* The name of the directory.  */
+   int                 cplusplus;      /* Only look here if we're compiling C++.  */
+   int                 cxx_aware;      /* Includes in this directory don't need to
+                                        * be wrapped in extern "C" when compiling
+                                        * C++.  */
+} include_defaults_array[]
+#ifdef INCLUDE_DEFAULTS
+   = INCLUDE_DEFAULTS;
+
+#else
+   =
+{
+   /* Pick up GNU C++ specific include files.  */
+   {
+   GPLUSPLUS_INCLUDE_DIR, 1, 1}
+   ,
+#ifdef CROSS_COMPILE
+      /* This is the dir for fixincludes.  Put it just before
+       * the files that we fix.  */
+   {
+   GCC_INCLUDE_DIR, 0, 0}
+   ,
+      /* For cross-compilation, this dir name is generated
+       * automatically in Makefile.in.  */
+   {
+   CROSS_INCLUDE_DIR, 0, 0}
+   ,
+      /* This is another place that the target system's headers might be.  */
+   {
+   TOOL_INCLUDE_DIR, 0, 1}
+   ,
+#else /* not CROSS_COMPILE */
+      /* This should be /usr/local/include and should come before
+       * the fixincludes-fixed header files.  */
+   {
+   LOCAL_INCLUDE_DIR, 0, 1}
+   ,
+      /* This is here ahead of GCC_INCLUDE_DIR because assert.h goes here.
+       * Likewise, behind LOCAL_INCLUDE_DIR, where glibc puts its assert.h.  */
+   {
+   TOOL_INCLUDE_DIR, 0, 1}
+   ,
+      /* This is the dir for fixincludes.  Put it just before
+       * the files that we fix.  */
+   {
+   GCC_INCLUDE_DIR, 0, 0}
+   ,
+      /* Some systems have an extra dir of include files.  */
+#ifdef SYSTEM_INCLUDE_DIR
+   {
+   SYSTEM_INCLUDE_DIR, 0, 0}
+   ,
+#endif
+   {
+   STANDARD_INCLUDE_DIR, 0, 0}
+   ,
+#endif /* not CROSS_COMPILE */
+   {
+   0, 0, 0}
+};
+
+#endif /* no INCLUDE_DEFAULTS */
+
+/* Here is the actual list of #-directives, most-often-used first.
+ * The initialize_builtins function assumes #define is the very first.  */
+
+static struct directive directive_table[] = {
+   {6, do_define, "define", T_DEFINE, 0, 1, 0},
+   {5, do_xifdef, "ifdef", T_IFDEF, 1, 0, 0},
+   {6, do_xifdef, "ifndef", T_IFNDEF, 1, 0, 0},
+   {7, do_include, "include", T_INCLUDE, 1, 0, 0},
+   {12, do_include, "include_next", T_INCLUDE_NEXT, 1, 0, 0},
+   {6, do_include, "import", T_IMPORT, 1, 0, 0},
+   {5, do_endif, "endif", T_ENDIF, 1, 0, 0},
+   {4, do_else, "else", T_ELSE, 1, 0, 0},
+   {2, do_if, "if", T_IF, 1, 0, 0},
+   {4, do_elif, "elif", T_ELIF, 1, 0, 0},
+   {5, do_undef, "undef", T_UNDEF, 0, 0, 0},
+   {5, do_error, "error", T_ERROR, 0, 0, 0},
+   {7, do_warning, "warning", T_WARNING, 0, 0, 0},
+   {6, do_pragma, "pragma", T_PRAGMA, 0, 0, 1},
+   {4, do_line, "line", T_LINE, 1, 0, 0},
+   {5, do_ident, "ident", T_IDENT, 1, 0, 1},
+#ifdef SCCS_DIRECTIVE
+   {4, do_sccs, "sccs", T_SCCS, 0, 0, 0},
+#endif
+   {6, do_assert, "assert", T_ASSERT, 1, 0, 0},
+   {8, do_unassert, "unassert", T_UNASSERT, 1, 0, 0},
+   {-1, 0, "", T_UNUSED, 0, 0, 0},
+};
+
+/* table to tell if char can be part of a C identifier. */
+unsigned char       is_idchar[256];
+
+/* table to tell if char can be first char of a c identifier. */
+unsigned char       is_idstart[256];
+
+/* table to tell if c is horizontal space.  */
+unsigned char       is_hor_space[256];
+
+/* table to tell if c is horizontal or vertical space.  */
+static unsigned char is_space[256];
+
+/* Initialize syntactic classifications of characters.  */
+
+static void
+initialize_char_syntax(struct cpp_options *opts)
+{
+   int                 i;
+
+   /*
+    * Set up is_idchar and is_idstart tables.  These should be
+    * faster than saying (is_alpha (c) || c == '_'), etc.
+    * Set up these things before calling any routines tthat
+    * refer to them.
+    */
+   for (i = 'a'; i <= 'z'; i++)
+     {
+       is_idchar[i - 'a' + 'A'] = 1;
+       is_idchar[i] = 1;
+       is_idstart[i - 'a' + 'A'] = 1;
+       is_idstart[i] = 1;
+     }
+   for (i = '0'; i <= '9'; i++)
+      is_idchar[i] = 1;
+   is_idchar[(unsigned char)'_'] = 1;
+   is_idstart[(unsigned char)'_'] = 1;
+   is_idchar[(unsigned char)'$'] = opts->dollars_in_ident;
+   is_idstart[(unsigned char)'$'] = opts->dollars_in_ident;
+
+   /* horizontal space table */
+   is_hor_space[(unsigned char)' '] = 1;
+   is_hor_space[(unsigned char)'\t'] = 1;
+   is_hor_space[(unsigned char)'\v'] = 1;
+   is_hor_space[(unsigned char)'\f'] = 1;
+   is_hor_space[(unsigned char)'\r'] = 1;
+
+   is_space[(unsigned char)' '] = 1;
+   is_space[(unsigned char)'\t'] = 1;
+   is_space[(unsigned char)'\v'] = 1;
+   is_space[(unsigned char)'\f'] = 1;
+   is_space[(unsigned char)'\n'] = 1;
+   is_space[(unsigned char)'\r'] = 1;
+}
+
+/* Place into PFILE a quoted string representing the string SRC.
+ * Caller must reserve enough space in pfile->token_buffer. */
+static void
+quote_string(cpp_reader * pfile, const char *src)
+{
+   unsigned char       c;
+
+   CPP_PUTC_Q(pfile, '\"');
+   for (;;)
+      switch ((c = *src++))
+       {
+       default:
+          if (isprint(c))
+             CPP_PUTC_Q(pfile, c);
+          else
+            {
+               sprintf((char *)CPP_PWRITTEN(pfile), "\\%03o", c);
+               CPP_ADJUST_WRITTEN(pfile, 4);
+            }
+          break;
+
+       case '\"':
+       case '\\':
+          CPP_PUTC_Q(pfile, '\\');
+          CPP_PUTC_Q(pfile, c);
+          break;
+
+       case '\0':
+          CPP_PUTC_Q(pfile, '\"');
+          CPP_NUL_TERMINATE_Q(pfile);
+          return;
+       }
+}
+
+/* Make sure PFILE->token_buffer will hold at least N more chars. */
+
+void
+cpp_grow_buffer(cpp_reader * pfile, long n)
+{
+   long                old_written = CPP_WRITTEN(pfile);
+
+   pfile->token_buffer_size = n + 2 * pfile->token_buffer_size;
+   pfile->token_buffer =
+      (unsigned char *)xrealloc(pfile->token_buffer, pfile->token_buffer_size);
+   CPP_SET_WRITTEN(pfile, old_written);
+}
+
+/*
+ * process a given definition string, for initialization
+ * If STR is just an identifier, define it with value 1.
+ * If STR has anything after the identifier, then it should
+ * be identifier=definition.
+ */
+
+void
+cpp_define(cpp_reader * pfile, unsigned char *str)
+{
+   unsigned char      *buf, *p;
+
+   buf = str;
+   p = str;
+   if (!is_idstart[*p])
+     {
+       cpp_error(pfile, "malformed option `-D %s'", str);
+       return;
+     }
+   while (is_idchar[*++p])
+      ;
+   if (*p == 0)
+     {
+       buf = (unsigned char *)alloca(p - buf + 4);
+       strcpy((char *)buf, (const char *)str);
+       strcat((char *)buf, " 1");
+     }
+   else if (*p != '=')
+     {
+       cpp_error(pfile, "malformed option `-D %s'", str);
+       return;
+     }
+   else
+     {
+       unsigned char      *q;
+
+       /* Copy the entire option so we can modify it.  */
+       buf = (unsigned char *)alloca(2 * strlen((char *)str) + 1);
+       strncpy((char *)buf, (const char *)str, p - str);
+       /* Change the = to a space.  */
+       buf[p - str] = ' ';
+       /* Scan for any backslash-newline and remove it.  */
+       p++;
+       q = &buf[p - str];
+       while (*p)
+         {
+            if (*p == '\\' && p[1] == '\n')
+               p += 2;
+            else
+               *q++ = *p++;
+         }
+       *q = 0;
+     }
+
+   do_define(pfile, NULL, buf, buf + strlen((char *)buf));
+}
+
+/* Process the string STR as if it appeared as the body of a #assert.
+ * OPTION is the option name for which STR was the argument.  */
+
+static void
+make_assertion(cpp_reader * pfile, const char *option, const char *str)
+{
+   unsigned char      *buf, *p, *q;
+
+   /* Copy the entire option so we can modify it.  */
+   buf = (unsigned char *)alloca(strlen((char *)str) + 1);
+   strcpy((char *)buf, (const char *)str);
+   /* Scan for any backslash-newline and remove it.  */
+   p = q = buf;
+   while (*p)
+     {
+       *q++ = *p++;
+     }
+   *q = 0;
+
+   p = buf;
+   if (!is_idstart[*p])
+     {
+       cpp_error(pfile, "malformed option `%s %s'", option, str);
+       return;
+     }
+   while (is_idchar[*++p])
+      ;
+   while (*p == ' ' || *p == '\t')
+      p++;
+   if (!(*p == 0 || *p == '('))
+     {
+       cpp_error(pfile, "malformed option `%s %s'", option, str);
+       return;
+     }
+   cpp_push_buffer(pfile, buf, strlen((char *)buf));
+   do_assert(pfile, NULL, NULL, NULL);
+   cpp_pop_buffer(pfile);
+}
+
+/* Append a chain of `file_name_list's
+ * to the end of the main include chain.
+ * FIRST is the beginning of the chain to append, and LAST is the end.  */
+
+static void
+append_include_chain(cpp_reader * pfile, file_name_list * first,
+                    file_name_list * last)
+{
+   struct cpp_options *opts = CPP_OPTIONS(pfile);
+   file_name_list     *dir;
+
+   if (!first || !last)
+      return;
+
+   if (!opts->include)
+      opts->include = first;
+   else
+      opts->last_include->next = first;
+
+   if (!opts->first_bracket_include)
+      opts->first_bracket_include = first;
+
+   for (dir = first;; dir = dir->next)
+     {
+       int                 len = strlen(dir->fname) + INCLUDE_LEN_FUDGE;
+
+       if (len > pfile->max_include_len)
+          pfile->max_include_len = len;
+       if (dir == last)
+          break;
+     }
+
+   last->next = NULL;
+   opts->last_include = last;
+}
+
+/* Add output to `deps_buffer' for the -M switch.
+ * STRING points to the text to be output.
+ * SPACER is ':' for targets, ' ' for dependencies, zero for text
+ * to be inserted literally.  */
+
+static void
+deps_output(cpp_reader * pfile, const char *string, int spacer)
+{
+   int                 size = strlen(string);
+
+   if (size == 0)
+      return;
+
+#ifndef MAX_OUTPUT_COLUMNS
+#define MAX_OUTPUT_COLUMNS 72
+#endif
+   if (spacer
+       && pfile->deps_column > 0
+       && (pfile->deps_column + size) > MAX_OUTPUT_COLUMNS)
+     {
+       deps_output(pfile, " \\\n  ", 0);
+       pfile->deps_column = 0;
+     }
+   if (pfile->deps_size + size + 8 > pfile->deps_allocated_size)
+     {
+       pfile->deps_allocated_size = (pfile->deps_size + size + 50) * 2;
+       pfile->deps_buffer = (char *)xrealloc(pfile->deps_buffer,
+                                             pfile->deps_allocated_size);
+     }
+   if (spacer == ' ' && pfile->deps_column > 0)
+      pfile->deps_buffer[pfile->deps_size++] = ' ';
+   memcpy(&pfile->deps_buffer[pfile->deps_size], string, size);
+   pfile->deps_size += size;
+   pfile->deps_column += size;
+   if (spacer == ':')
+      pfile->deps_buffer[pfile->deps_size++] = ':';
+   pfile->deps_buffer[pfile->deps_size] = 0;
+}
+
+/* Given a colon-separated list of file names PATH,
+ * add all the names to the search path for include files.  */
+
+static void
+path_include(cpp_reader * pfile, char *path)
+{
+   char               *p;
+
+   p = path;
+
+   if (*p)
+      while (1)
+       {
+          char               *q = p;
+          char               *name;
+          file_name_list     *dirtmp;
+
+          /* Find the end of this name.  */
+          while (*q != 0 && *q != PATH_SEPARATOR)
+             q++;
+          if (p == q)
+            {
+               /* An empty name in the path stands for the current directory.  */
+               name = (char *)xmalloc(2);
+               name[0] = '.';
+               name[1] = 0;
+            }
+          else
+            {
+               /* Otherwise use the directory that is named.  */
+               name = (char *)xmalloc(q - p + 1);
+               memcpy(name, p, q - p);
+               name[q - p] = 0;
+            }
+
+          dirtmp = (file_name_list *) xmalloc(sizeof(file_name_list));
+
+          dirtmp->next = 0;    /* New one goes on the end */
+          dirtmp->control_macro = 0;
+          dirtmp->c_system_include_path = 0;
+          dirtmp->fname = name;
+          dirtmp->got_name_map = 0;
+          append_include_chain(pfile, dirtmp, dirtmp);
+
+          /* Advance past this name.  */
+          p = q;
+          if (*p == 0)
+             break;
+          /* Skip the colon.  */
+          p++;
+       }
+}
+
+void
+init_parse_options(struct cpp_options *opts)
+{
+   memset((char *)opts, 0, sizeof *opts);
+   opts->in_fname = NULL;
+   opts->out_fname = NULL;
+
+   /* Initialize is_idchar to allow $.  */
+   opts->dollars_in_ident = 1;
+   initialize_char_syntax(opts);
+   opts->dollars_in_ident = DOLLARS_IN_IDENTIFIERS > 0;
+
+   opts->no_line_commands = 0;
+   opts->no_trigraphs = 1;
+   opts->put_out_comments = 0;
+   opts->print_include_names = 0;
+   opts->dump_macros = dump_none;
+   opts->no_output = 0;
+   opts->cplusplus = 0;
+   opts->cplusplus_comments = 1;
+
+   opts->verbose = 0;
+   opts->objc = 0;
+   opts->lang_asm = 0;
+   opts->for_lint = 0;
+   opts->chill = 0;
+   opts->pedantic_errors = 0;
+   opts->inhibit_warnings = 0;
+   opts->warn_comments = 0;
+   opts->warn_import = 1;
+   opts->warnings_are_errors = 0;
+}
+
+static enum cpp_token
+null_underflow(cpp_reader * pfile __UNUSED__)
+{
+   return CPP_EOF;
+}
+
+static int
+null_cleanup(cpp_buffer * pbuf __UNUSED__, cpp_reader * pfile __UNUSED__)
+{
+   return 0;
+}
+
+static int
+macro_cleanup(cpp_buffer * pbuf, cpp_reader * pfile __UNUSED__)
+{
+   HASHNODE           *macro = (HASHNODE *) pbuf->data;
+
+   if (macro->type == T_DISABLED)
+      macro->type = T_MACRO;
+   if (macro->type != T_MACRO || pbuf->buf != macro->value.defn->expansion)
+      free(pbuf->buf);
+   return 0;
+}
+
+static int
+file_cleanup(cpp_buffer * pbuf, cpp_reader * pfile __UNUSED__)
+{
+   if (pbuf->buf)
+     {
+       free(pbuf->buf);
+       pbuf->buf = 0;
+     }
+   return 0;
+}
+
+/* Assuming we have read '/'.
+ * If this is the start of a comment (followed by '*' or '/'),
+ * skip to the end of the comment, and return ' '.
+ * Return EOF if we reached the end of file before the end of the comment.
+ * If not the start of a comment, return '/'. */
+
+static int
+skip_comment(cpp_reader * pfile, long *linep)
+{
+   int                 c = 0;
+
+   while (PEEKC() == '\\' && PEEKN(1) == '\n')
+     {
+       if (linep)
+          (*linep)++;
+       FORWARD(2);
+     }
+   if (PEEKC() == '*')
+     {
+       FORWARD(1);
+       for (;;)
+         {
+            int                 prev_c = c;
+
+            c = GETC();
+            if (c == EOF)
+               return EOF;
+            while (c == '\\' && PEEKC() == '\n')
+              {
+                 if (linep)
+                    (*linep)++;
+                 FORWARD(1), c = GETC();
+              }
+            if (prev_c == '*' && c == '/')
+               return ' ';
+            if (c == '\n' && linep)
+               (*linep)++;
+         }
+     }
+   else if (PEEKC() == '/' && CPP_OPTIONS(pfile)->cplusplus_comments)
+     {
+       FORWARD(1);
+       for (;;)
+         {
+            c = GETC();
+            if (c == EOF)
+               return ' ';     /* Allow // to be terminated by EOF. */
+            while (c == '\\' && PEEKC() == '\n')
+              {
+                 FORWARD(1);
+                 c = GETC();
+                 if (linep)
+                    (*linep)++;
+              }
+            if (c == '\n')
+              {
+                 /* Don't consider final '\n' to be part of comment. */
+                 FORWARD(-1);
+                 return ' ';
+              }
+         }
+     }
+   else
+      return '/';
+}
+
+/* Skip whitespace \-newline and comments.  Does not macro-expand.  */
+void
+cpp_skip_hspace(cpp_reader * pfile)
+{
+   while (1)
+     {
+       int                 c = PEEKC();
+
+       if (c == EOF)
+          return;              /* FIXME */
+       if (is_hor_space[c])
+         {
+            if ((c == '\f' || c == '\v') && CPP_PEDANTIC(pfile))
+               cpp_pedwarn(pfile, "%s in preprocessing directive",
+                           c == '\f' ? "formfeed" : "vertical tab");
+            FORWARD(1);
+         }
+       else if (c == '/')
+         {
+            FORWARD(1);
+            c = skip_comment(pfile, NULL);
+            if (c == '/')
+               FORWARD(-1);
+            if (c == EOF || c == '/')
+               return;
+         }
+       else if (c == '\\' && PEEKN(1) == '\n')
+         {
+            FORWARD(2);
+         }
+       else if (c == '@' && CPP_BUFFER(pfile)->has_escapes
+                && is_hor_space[PEEKN(1)])
+          FORWARD(2);
+       else
+          return;
+     }
+}
+
+/* Read the rest of the current line.
+ * The line is appended to PFILE's output buffer. */
+
+static void
+copy_rest_of_line(cpp_reader * pfile)
+{
+   struct cpp_options *opts = CPP_OPTIONS(pfile);
+
+   for (;;)
+     {
+       int                 c = GETC();
+       int                 nextc;
+
+       switch (c)
+         {
+         case EOF:
+            goto end_directive;
+         case '\\':
+            if (PEEKC() == '\n')
+              {
+                 FORWARD(1);
+                 continue;
+              }
+         case '\'':
+         case '\"':
+            goto scan_directive_token;
+            break;
+         case '/':
+            nextc = PEEKC();
+            if (nextc == '*' || (opts->cplusplus_comments && nextc == '*'))
+               goto scan_directive_token;
+            break;
+         case '\f':
+         case '\v':
+            if (CPP_PEDANTIC(pfile))
+               cpp_pedwarn(pfile, "%s in preprocessing directive",
+                           c == '\f' ? "formfeed" : "vertical tab");
+            break;
+
+         case '\n':
+            FORWARD(-1);
+            goto end_directive;
+          scan_directive_token:
+            FORWARD(-1);
+            cpp_get_token(pfile);
+            continue;
+         }
+       CPP_PUTC(pfile, c);
+     }
+ end_directive:;
+   CPP_NUL_TERMINATE(pfile);
+}
+
+void
+skip_rest_of_line(cpp_reader * pfile)
+{
+   long                old = CPP_WRITTEN(pfile);
+
+   copy_rest_of_line(pfile);
+   CPP_SET_WRITTEN(pfile, old);
+}
+
+/* Handle a possible # directive.
+ * '#' has already been read.  */
+
+static int
+handle_directive(cpp_reader * pfile)
+{
+   int                 c;
+   struct directive   *kt;
+   int                 ident_length;
+   long                after_ident = 0;
+   unsigned char      *ident, *line_end;
+   long                old_written = CPP_WRITTEN(pfile);
+
+   cpp_skip_hspace(pfile);
+
+   c = PEEKC();
+   if (c >= '0' && c <= '9')
+     {
+       /* Handle # followed by a line number.  */
+       if (CPP_PEDANTIC(pfile))
+          cpp_pedwarn(pfile, "`#' followed by integer");
+       do_line(pfile, NULL, NULL, NULL);
+       goto done_a_directive;
+     }
+   /* Now find the directive name. */
+   CPP_PUTC(pfile, '#');
+   parse_name(pfile, GETC());
+   ident = pfile->token_buffer + old_written + 1;
+   ident_length = CPP_PWRITTEN(pfile) - ident;
+   if (ident_length == 0 && PEEKC() == '\n')
+     {
+       /* A line of just `#' becomes blank.  */
+       goto done_a_directive;
+     }
+   /*
+    * Decode the keyword and call the appropriate expansion
+    * routine, after moving the input pointer up to the next line.
+    */
+   for (kt = directive_table;; kt++)
+     {
+       if (kt->length <= 0)
+          goto not_a_directive;
+       if (kt->length == ident_length
+           && !strncmp(kt->name, (const char *)ident, ident_length))
+          break;
+     }
+
+   if (!kt->command_reads_line)
+     {
+       /* Nonzero means do not delete comments within the directive.
+        * #define needs this when -traditional.  */
+       int                 comments = CPP_TRADITIONAL(pfile)
+          && kt->traditional_comments;
+       int                 save_put_out_comments =
+          CPP_OPTIONS(pfile)->put_out_comments;
+
+       CPP_OPTIONS(pfile)->put_out_comments = comments;
+       after_ident = CPP_WRITTEN(pfile);
+       copy_rest_of_line(pfile);
+       CPP_OPTIONS(pfile)->put_out_comments = save_put_out_comments;
+     }
+   /* For #pragma and #define, we may want to pass through the directive.
+    * Other directives may create output, but we don't want the directive
+    * itself out, so we pop it now.  For example #include may write a #line
+    * command (see comment in do_include), and conditionals may emit
+    * #failed ... #endfailed stuff.  But note that popping the buffer
+    * means the parameters to kt->func may point after pfile->limit
+    * so these parameters are invalid as soon as something gets appended
+    * to the token_buffer.  */
+
+   line_end = CPP_PWRITTEN(pfile);
+   if (!kt->pass_thru && kt->type != T_DEFINE)
+      CPP_SET_WRITTEN(pfile, old_written);
+
+   (*kt->func) (pfile, kt, pfile->token_buffer + after_ident, line_end);
+   if (kt->pass_thru
+       || (kt->type == T_DEFINE
+          && CPP_OPTIONS(pfile)->dump_macros == dump_definitions))
+     {
+       /* Just leave the entire #define in the output stack. */
+     }
+   else if (kt->type == T_DEFINE
+           && CPP_OPTIONS(pfile)->dump_macros == dump_names)
+     {
+       unsigned char      *p = pfile->token_buffer + old_written + 7;  /* Skip "#define". */
+
+       SKIP_WHITE_SPACE(p);
+       while (is_idchar[*p])
+          p++;
+       pfile->limit = p;
+       CPP_PUTC(pfile, '\n');
+     }
+   else if (kt->type == T_DEFINE)
+      CPP_SET_WRITTEN(pfile, old_written);
+ done_a_directive:
+   return 1;
+
+ not_a_directive:
+   return 0;
+}
+
+/* Pass a directive through to the output file.
+ * BUF points to the contents of the directive, as a contiguous string.
+ * LIMIT points to the first character past the end of the directive.
+ * KEYWORD is the keyword-table entry for the directive.  */
+
+static void
+pass_thru_directive(char *buf, char *limit, cpp_reader * pfile,
+                   struct directive *keyword)
+{
+   unsigned            keyword_length = keyword->length;
+
+   CPP_RESERVE(pfile, 1 + keyword_length + (limit - buf));
+   CPP_PUTC_Q(pfile, '#');
+   CPP_PUTS_Q(pfile, keyword->name, keyword_length);
+   if (limit != buf && buf[0] != ' ')
+      CPP_PUTC_Q(pfile, ' ');
+   CPP_PUTS_Q(pfile, buf, limit - buf);
+}
+
+/* The arglist structure is built by do_define to tell
+ * collect_definition where the argument names begin.  That
+ * is, for a define like "#define f(x,y,z) foo+x-bar*y", the arglist
+ * would contain pointers to the strings x, y, and z.
+ * Collect_definition would then build a DEFINITION node,
+ * with reflist nodes pointing to the places x, y, and z had
+ * appeared.  So the arglist is just convenience data passed
+ * between these two routines.  It is not kept around after
+ * the current #define has been processed and entered into the
+ * hash table. */
+
+struct arglist {
+   struct arglist     *next;
+   char               *name;
+   int                 length;
+   int                 argno;
+   char                rest_args;
+};
+
+/* Read a replacement list for a macro with parameters.
+ * Build the DEFINITION structure.
+ * Reads characters of text starting at BUF until END.
+ * ARGLIST specifies the formal parameters to look for
+ * in the text of the definition; NARGS is the number of args
+ * in that list, or -1 for a macro name that wants no argument list.
+ * MACRONAME is the macro name itself (so we can avoid recursive expansion)
+ * and NAMELEN is its length in characters.
+ * 
+ * Note that comments, backslash-newlines, and leading white space
+ * have already been deleted from the argument.  */
+
+static DEFINITION  *
+collect_expansion(cpp_reader * pfile, unsigned char *buf, unsigned char *limit,
+                 int nargs, struct arglist *arglist)
+{
+   DEFINITION         *defn;
+   unsigned char      *p, *lastp, *exp_p;
+   reflist            *endpat = NULL;
+
+   /* Pointer to first nonspace after last ## seen.  */
+   unsigned char      *concat = 0;
+
+   /* Pointer to first nonspace after last single-# seen.  */
+   unsigned char      *stringify = 0;
+   int                 maxsize;
+   int                 expected_delimiter = '\0';
+
+   /* Scan thru the replacement list, ignoring comments and quoted
+    * strings, picking up on the macro calls.  It does a linear search
+    * thru the arg list on every potential symbol.  Profiling might say
+    * that something smarter should happen. */
+
+   if (limit < buf)
+      abort();
+
+   /* Find the beginning of the trailing whitespace.  */
+   p = buf;
+   while (p < limit && is_space[limit[-1]])
+      limit--;
+
+   /* Allocate space for the text in the macro definition.
+    * Leading and trailing whitespace chars need 2 bytes each.
+    * Each other input char may or may not need 1 byte,
+    * so this is an upper bound.  The extra 5 are for invented
+    * leading and trailing newline-marker and final null.  */
+   maxsize = (sizeof(DEFINITION) + (limit - p) + 5);
+   /* Occurrences of '@' get doubled, so allocate extra space for them. */
+   while (p < limit)
+      if (*p++ == '@')
+        maxsize++;
+   defn = (DEFINITION *) xcalloc(1, maxsize);
+
+   defn->nargs = nargs;
+   exp_p = defn->expansion = (unsigned char *)defn + sizeof(DEFINITION);
+   lastp = exp_p;
+
+   p = buf;
+
+   /* Add one initial space escape-marker to prevent accidental
+    * token-pasting (often removed by macroexpand). */
+   *exp_p++ = '@';
+   *exp_p++ = ' ';
+
+   if (limit - p >= 2 && p[0] == '#' && p[1] == '#')
+     {
+       cpp_error(pfile, "`##' at start of macro definition");
+       p += 2;
+     }
+   /* Process the main body of the definition.  */
+   while (p < limit)
+     {
+       int                 skipped_arg = 0;
+       unsigned char       c = *p++;
+
+       *exp_p++ = c;
+
+       if (!CPP_TRADITIONAL(pfile))
+         {
+            switch (c)
+              {
+              case '\'':
+              case '\"':
+                 if (expected_delimiter != '\0')
+                   {
+                      if (c == expected_delimiter)
+                         expected_delimiter = '\0';
+                   }
+                 else
+                    expected_delimiter = c;
+                 break;
+
+              case '\\':
+                 if (p < limit && expected_delimiter)
+                   {
+                      /* In a string, backslash goes through
+                       * and makes next char ordinary.  */
+                      *exp_p++ = *p++;
+                   }
+                 break;
+
+              case '@':
+                 /* An '@' in a string or character constant stands for itself,
+                  * and does not need to be escaped. */
+                 if (!expected_delimiter)
+                    *exp_p++ = c;
+                 break;
+
+              case '#':
+                 /* # is ordinary inside a string.  */
+                 if (expected_delimiter)
+                    break;
+                 if (p < limit && *p == '#')
+                   {
+                      /* ##: concatenate preceding and following tokens.  */
+                      /* Take out the first #, discard preceding whitespace.  */
+                      exp_p--;
+                      while (exp_p > lastp && is_hor_space[exp_p[-1]])
+                         --exp_p;
+                      /* Skip the second #.  */
+                      p++;
+                      /* Discard following whitespace.  */
+                      SKIP_WHITE_SPACE(p);
+                      concat = p;
+                      if (p == limit)
+                         cpp_error(pfile, "`##' at end of macro definition");
+                   }
+                 else if (nargs >= 0)
+                   {
+                      /* Single #: stringify following argument ref.
+                       * Don't leave the # in the expansion.  */
+                      exp_p--;
+                      SKIP_WHITE_SPACE(p);
+                      if (p == limit || !is_idstart[*p])
+                         cpp_error(pfile,
+                                   "`#' operator is not followed by a macro argument name");
+                      else
+                         stringify = p;
+                   }
+                 break;
+              }
+         }
+       else
+         {
+            /* In -traditional mode, recognize arguments inside strings and
+             * and character constants, and ignore special properties of #.
+             * Arguments inside strings are considered "stringified", but no
+             * extra quote marks are supplied.  */
+            switch (c)
+              {
+              case '\'':
+              case '\"':
+                 if (expected_delimiter != '\0')
+                   {
+                      if (c == expected_delimiter)
+                         expected_delimiter = '\0';
+                   }
+                 else
+                    expected_delimiter = c;
+                 break;
+
+              case '\\':
+                 /* Backslash quotes delimiters and itself, but not macro args.  */
+                 if (expected_delimiter != 0 && p < limit
+                     && (*p == expected_delimiter || *p == '\\'))
+                   {
+                      *exp_p++ = *p++;
+                      continue;
+                   }
+                 break;
+
+              case '/':
+                 if (expected_delimiter != '\0')       /* No comments inside strings.  */
+                    break;
+                 if (*p == '*')
+                   {
+                      /* If we find a comment that wasn't removed by handle_directive,
+                       * this must be -traditional.  So replace the comment with
+                       * nothing at all.  */
+                      exp_p--;
+                      p += 1;
+                      while (p < limit && !(p[-2] == '*' && p[-1] == '/'))
+                         p++;
+                   }
+                 break;
+              }
+         }
+
+       /* Handle the start of a symbol.  */
+       if (is_idchar[c] && nargs > 0)
+         {
+            unsigned char      *id_beg = p - 1;
+            int                 id_len;
+
+            --exp_p;
+            while (p != limit && is_idchar[*p])
+               p++;
+            id_len = p - id_beg;
+
+            if (is_idstart[c])
+              {
+                 struct arglist     *arg;
+
+                 for (arg = arglist; arg; arg = arg->next)
+                   {
+                      reflist            *tpat;
+
+                      if (arg->name[0] == c
+                          && arg->length == id_len
+                          && strncmp((const char *)arg->name,
+                                     (const char *)id_beg, id_len) == 0)
+                        {
+                           if (expected_delimiter
+                               && CPP_OPTIONS(pfile)->warn_stringify)
+                             {
+                                if (CPP_TRADITIONAL(pfile))
+                                  {
+                                     cpp_warning(pfile,
+                                                 "macro argument `%.*s' is stringified.",
+                                                 id_len, arg->name);
+                                  }
+                                else
+                                  {
+                                     cpp_warning(pfile,
+                                                 "macro arg `%.*s' would be stringified with -traditional.",
+                                                 id_len, arg->name);
+                                  }
+                             }
+                           /* If ANSI, don't actually substitute inside a string.  */
+                           if (!CPP_TRADITIONAL(pfile) && expected_delimiter)
+                              break;
+                           /* make a pat node for this arg and append it to the end of
+                            * the pat list */
+                           tpat = (reflist *) xmalloc(sizeof(reflist));
+
+                           tpat->next = NULL;
+                           tpat->raw_before = concat == id_beg;
+                           tpat->raw_after = 0;
+                           tpat->rest_args = arg->rest_args;
+                           tpat->stringify = (CPP_TRADITIONAL(pfile)
+                                              ? expected_delimiter != '\0'
+                                              : stringify == id_beg);
+
+                           if (!endpat)
+                              defn->pattern = tpat;
+                           else
+                              endpat->next = tpat;
+                           endpat = tpat;
+
+                           tpat->argno = arg->argno;
+                           tpat->nchars = exp_p - lastp;
+                           {
+                              unsigned char      *p1 = p;
+
+                              SKIP_WHITE_SPACE(p1);
+                              if (p1 + 2 <= limit && p1[0] == '#'
+                                  && p1[1] == '#')
+                                 tpat->raw_after = 1;
+                           }
+                           lastp = exp_p;      /* place to start copying from next time */
+                           skipped_arg = 1;
+                           break;
+                        }
+                   }
+              }
+            /* If this was not a macro arg, copy it into the expansion.  */
+            if (!skipped_arg)
+              {
+                 unsigned char      *lim1 = p;
+
+                 p = id_beg;
+                 while (p != lim1)
+                    *exp_p++ = *p++;
+                 if (stringify == id_beg)
+                    cpp_error(pfile,
+                              "`#' operator should be followed by a macro argument name");
+              }
+         }
+     }
+
+   if (!CPP_TRADITIONAL(pfile) && expected_delimiter == 0)
+     {
+       /* If ANSI, put in a "@ " marker to prevent token pasting.
+        * But not if "inside a string" (which in ANSI mode
+        * happens only for -D option).  */
+       *exp_p++ = '@';
+       *exp_p++ = ' ';
+     }
+   *exp_p = '\0';
+
+   defn->length = exp_p - defn->expansion;
+
+   /* Crash now if we overrun the allocated size.  */
+   if (defn->length + 1 > maxsize)
+      abort();
+
+   return defn;
+}
+
+/*
+ * special extension string that can be added to the last macro argument to 
+ * allow it to absorb the "rest" of the arguments when expanded.  Ex:
+ *              #define wow(a, b...)            process (b, a, b)
+ *              { wow (1, 2, 3); }      ->      { process (2, 3, 1, 2, 3); }
+ *              { wow (one, two); }     ->      { process (two, one, two); }
+ * if this "rest_arg" is used with the concat token '##' and if it is not
+ * supplied then the token attached to with ## will not be outputted.  Ex:
+ *              #define wow (a, b...)           process (b ## , a, ## b)
+ *              { wow (1, 2); }         ->      { process (2, 1, 2); }
+ *              { wow (one); }          ->      { process (one); {
+ */
+static char         rest_extension[] = "...";
+
+#define REST_EXTENSION_LENGTH  (sizeof (rest_extension) - 1)
+
+/* Create a DEFINITION node from a #define directive.  Arguments are 
+ * as for do_define. */
+static void
+create_definition(MACRODEF * mdef, unsigned char *buf, unsigned char *limit,
+                 cpp_reader * pfile, int predefinition)
+{
+   unsigned char      *bp;     /* temp ptr into input buffer */
+   unsigned char      *symname;        /* remember where symbol name starts */
+   int                 sym_length;     /* and how long it is */
+   int                 rest_args = 0;
+   long                line, col;
+   const char         *file =
+      CPP_BUFFER(pfile) ? CPP_BUFFER(pfile)->nominal_fname : "";
+   DEFINITION         *defn;
+   int                 arglengths = 0; /* Accumulate lengths of arg names
+                                        * plus number of args.  */
+
+   cpp_buf_line_and_col(CPP_BUFFER(pfile), &line, &col);
+
+   bp = buf;
+
+   while (is_hor_space[*bp])
+      bp++;
+
+   symname = bp;               /* remember where it starts */
+
+   sym_length = check_macro_name(pfile, bp, "macro");
+   bp += sym_length;
+
+   /* Lossage will occur if identifiers or control keywords are broken
+    * across lines using backslash.  This is not the right place to take
+    * care of that. */
+
+   if (*bp == '(')
+     {
+       struct arglist     *arg_ptrs = NULL;
+       int                 argno = 0;
+
+       bp++;                   /* skip '(' */
+       SKIP_WHITE_SPACE(bp);
+
+       /* Loop over macro argument names.  */
+       while (*bp != ')')
+         {
+            struct arglist     *temp;
+
+            temp = (struct arglist *)alloca(sizeof(struct arglist));
+
+            temp->name = (char *)bp;
+            temp->next = arg_ptrs;
+            temp->argno = argno++;
+            temp->rest_args = 0;
+            arg_ptrs = temp;
+
+            if (rest_args)
+               cpp_pedwarn(pfile, "another parameter follows `%s'",
+                           rest_extension);
+
+            if (!is_idstart[*bp])
+               cpp_pedwarn(pfile, "invalid character in macro parameter name");
+
+            /* Find the end of the arg name.  */
+            while (is_idchar[*bp])
+              {
+                 bp++;
+                 /* do we have a "special" rest-args extension here? */
+                 if ((unsigned)(limit - bp) > REST_EXTENSION_LENGTH &&
+                     strncmp(rest_extension, (const char *)bp,
+                             REST_EXTENSION_LENGTH) == 0)
+                   {
+                      rest_args = 1;
+                      temp->rest_args = 1;
+                      break;
+                   }
+              }
+            temp->length = (char *)bp - temp->name;
+            if (rest_args == 1)
+               bp += REST_EXTENSION_LENGTH;
+            arglengths += temp->length + 2;
+            SKIP_WHITE_SPACE(bp);
+            if (temp->length == 0 || (*bp != ',' && *bp != ')'))
+              {
+                 cpp_error(pfile,
+                           "badly punctuated parameter list in `#define'");
+                 goto nope;
+              }
+            if (*bp == ',')
+              {
+                 bp++;
+                 SKIP_WHITE_SPACE(bp);
+              }
+            if (bp >= limit)
+              {
+                 cpp_error(pfile, "unterminated parameter list in `#define'");
+                 goto nope;
+              }
+            {
+               struct arglist     *otemp;
+
+               for (otemp = temp->next; otemp; otemp = otemp->next)
+                  if (temp->length == otemp->length &&
+                      strncmp((const char *)temp->name,
+                              (const char *)otemp->name, temp->length) == 0)
+                    {
+                       char               *name;
+
+                       name = (char *)alloca(temp->length + 1);
+                       strncpy(name, (const char *)temp->name, temp->length);
+                       name[temp->length] = '\0';
+                       cpp_error(pfile,
+                                 "duplicate argument name `%s' in `#define'",
+                                 name);
+                       goto nope;
+                    }
+            }
+         }
+
+       ++bp;                   /* skip paren */
+       SKIP_WHITE_SPACE(bp);
+       /* now everything from bp before limit is the definition. */
+       defn = collect_expansion(pfile, bp, limit, argno, arg_ptrs);
+       defn->rest_args = rest_args;
+
+       /* Now set defn->args.argnames to the result of concatenating
+        * the argument names in reverse order
+        * with comma-space between them.  */
+       defn->args.argnames = (unsigned char *)xmalloc(arglengths + 1);
+       {
+          struct arglist     *temp;
+          int                 i = 0;
+
+          for (temp = arg_ptrs; temp; temp = temp->next)
+            {
+               memcpy(&defn->args.argnames[i], temp->name, temp->length);
+               i += temp->length;
+               if (temp->next)
+                 {
+                    defn->args.argnames[i++] = ',';
+                    defn->args.argnames[i++] = ' ';
+                 }
+            }
+          defn->args.argnames[i] = 0;
+       }
+     }
+   else
+     {
+       /* Simple expansion or empty definition.  */
+
+       if (bp < limit)
+         {
+            if (is_hor_space[*bp])
+              {
+                 bp++;
+                 SKIP_WHITE_SPACE(bp);
+              }
+            else
+              {
+                 switch (*bp)
+                   {
+                   case '!':
+                   case '"':
+                   case '#':
+                   case '%':
+                   case '&':
+                   case '\'':
+                   case ')':
+                   case '*':
+                   case '+':
+                   case ',':
+                   case '-':
+                   case '.':
+                   case '/':
+                   case ':':
+                   case ';':
+                   case '<':
+                   case '=':
+                   case '>':
+                   case '?':
+                   case '[':
+                   case '\\':
+                   case ']':
+                   case '^':
+                   case '{':
+                   case '|':
+                   case '}':
+                   case '~':
+                      cpp_warning(pfile,
+                                  "missing white space after `#define %.*s'",
+                                  sym_length, symname);
+                      break;
+
+                   default:
+                      cpp_pedwarn(pfile,
+                                  "missing white space after `#define %.*s'",
+                                  sym_length, symname);
+                      break;
+                   }
+              }
+         }
+       /* now everything from bp before limit is the definition. */
+       defn = collect_expansion(pfile, bp, limit, -1, NULL);
+       defn->args.argnames = (unsigned char *)"";
+     }
+
+   defn->line = line;
+   defn->file = file;
+
+   /* OP is null if this is a predefinition */
+   defn->predefined = predefinition;
+   mdef->defn = defn;
+   mdef->symnam = (char *)symname;
+   mdef->symlen = sym_length;
+
+   return;
+
+ nope:
+   mdef->defn = 0;
+}
+
+/* Check a purported macro name SYMNAME, and yield its length.
+ * USAGE is the kind of name this is intended for.  */
+
+static int
+check_macro_name(cpp_reader * pfile, unsigned char *symname, const char *usage)
+{
+   unsigned char      *p;
+   int                 sym_length;
+
+   for (p = symname; is_idchar[*p]; p++)
+      ;
+   sym_length = p - symname;
+   if (sym_length == 0)
+     {
+       cpp_error(pfile, "invalid %s name", usage);
+     }
+   else if (!is_idstart[*symname])
+     {
+       unsigned char      *msg;        /* what pain... */
+
+       msg = (unsigned char *)alloca(sym_length + 1);
+       memcpy(msg, symname, sym_length);
+       msg[sym_length] = 0;
+       cpp_error(pfile, "invalid %s name `%s'", usage, msg);
+     }
+   else
+     {
+       if (!strncmp((const char *)symname, "defined", 7) && sym_length == 7)
+          cpp_error(pfile, "invalid %s name `defined'", usage);
+     }
+   return sym_length;
+}
+
+/*
+ * return zero if two DEFINITIONs are isomorphic
+ */
+static int
+compare_defs(DEFINITION * d1, DEFINITION * d2)
+{
+   reflist            *a1, *a2;
+   unsigned char      *p1 = d1->expansion;
+   unsigned char      *p2 = d2->expansion;
+   int                 first = 1;
+
+   if (d1->nargs != d2->nargs)
+      return 1;
+   if (strcmp((char *)d1->args.argnames, (char *)d2->args.argnames))
+      return 1;
+   for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
+       a1 = a1->next, a2 = a2->next)
+     {
+       if (!
+           ((a1->nchars == a2->nchars
+             && !strncmp((const char *)p1, (const char *)p2, a1->nchars))
+            || !comp_def_part(first, p1, a1->nchars, p2, a2->nchars, 0))
+           || a1->argno != a2->argno || a1->stringify != a2->stringify
+           || a1->raw_before != a2->raw_before
+           || a1->raw_after != a2->raw_after)
+          return 1;
+       first = 0;
+       p1 += a1->nchars;
+       p2 += a2->nchars;
+     }
+   if (a1 != a2)
+      return 1;
+   if (comp_def_part(first, p1, d1->length - (p1 - d1->expansion),
+                    p2, d2->length - (p2 - d2->expansion), 1))
+      return 1;
+   return 0;
+}
+
+/* Return 1 if two parts of two macro definitions are effectively different.
+ * One of the parts starts at BEG1 and has LEN1 chars;
+ * the other has LEN2 chars at BEG2.
+ * Any sequence of whitespace matches any other sequence of whitespace.
+ * FIRST means these parts are the first of a macro definition;
+ * so ignore leading whitespace entirely.
+ * LAST means these parts are the last of a macro definition;
+ * so ignore trailing whitespace entirely.  */
+
+static int
+comp_def_part(int first, unsigned char *beg1, int len1,
+             unsigned char *beg2, int len2, int last)
+{
+   unsigned char      *end1 = beg1 + len1;
+   unsigned char      *end2 = beg2 + len2;
+
+   if (first)
+     {
+       while (beg1 != end1 && is_space[*beg1])
+          beg1++;
+       while (beg2 != end2 && is_space[*beg2])
+          beg2++;
+     }
+   if (last)
+     {
+       while (beg1 != end1 && is_space[end1[-1]])
+          end1--;
+       while (beg2 != end2 && is_space[end2[-1]])
+          end2--;
+     }
+   while (beg1 != end1 && beg2 != end2)
+     {
+       if (is_space[*beg1] && is_space[*beg2])
+         {
+            while (beg1 != end1 && is_space[*beg1])
+               beg1++;
+            while (beg2 != end2 && is_space[*beg2])
+               beg2++;
+         }
+       else if (*beg1 == *beg2)
+         {
+            beg1++;
+            beg2++;
+         }
+       else
+          break;
+     }
+   return (beg1 != end1) || (beg2 != end2);
+}
+
+/* Process a #define command.
+ * BUF points to the contents of the #define command, as a contiguous string.
+ * LIMIT points to the first character past the end of the definition.
+ * KEYWORD is the keyword-table entry for #define,
+ * or NULL for a "predefined" macro.  */
+
+static int
+do_define(cpp_reader * pfile, struct directive *keyword,
+         unsigned char *buf, unsigned char *limit)
+{
+   int                 hashcode;
+   MACRODEF            mdef;
+   HASHNODE           *hp;
+
+   create_definition(&mdef, buf, limit, pfile, !keyword);
+   if (!mdef.defn)
+      return 1;
+
+   hashcode = hashf(mdef.symnam, mdef.symlen, HASHSIZE);
+
+   if ((hp = cpp_lookup(mdef.symnam, mdef.symlen, hashcode)))
+     {
+       int                 ok = 0;
+
+       /* Redefining a precompiled key is ok.  */
+       if (hp->type == T_PCSTRING)
+          ok = 1;
+       /* Redefining a macro is ok if the definitions are the same.  */
+       else if (hp->type == T_MACRO)
+          ok = !compare_defs(mdef.defn, hp->value.defn);
+       /* Redefining a constant is ok with -D.  */
+       else if (hp->type == T_CONST)
+          ok = !CPP_OPTIONS(pfile)->done_initializing;
+       /* Print the warning if it's not ok.  */
+       if (!ok)
+         {
+            char               *msg;   /* what pain... */
+
+            /* If we are passing through #define and #undef directives, do
+             * that for this re-definition now.  */
+            if (CPP_OPTIONS(pfile)->debug_output && keyword)
+               pass_thru_directive((char *)buf, (char *)limit, pfile, keyword);
+
+            msg = (char *)alloca(mdef.symlen + 22);
+            *msg = '`';
+            memcpy(msg + 1, mdef.symnam, mdef.symlen);
+            strcpy((msg + mdef.symlen + 1), "' redefined");
+            cpp_pedwarn(pfile, msg);
+            if (hp->type == T_MACRO)
+               cpp_pedwarn_with_file_and_line(pfile, hp->value.defn->file,
+                                              hp->value.defn->line,
+                                              "this is the location of the previous definition",
+                                              NULL, NULL, NULL);
+         }
+       /* Replace the old definition.  */
+       hp->type = T_MACRO;
+       hp->value.defn = mdef.defn;
+     }
+   else
+     {
+       /* If we are passing through #define and #undef directives, do
+        * that for this new definition now.  */
+       if (CPP_OPTIONS(pfile)->debug_output && keyword)
+          pass_thru_directive((char *)buf, (char *)limit, pfile, keyword);
+       install(mdef.symnam, mdef.symlen, T_MACRO, 0,
+               (char *)mdef.defn, hashcode);
+     }
+
+   return 0;
+}
+
+/* This structure represents one parsed argument in a macro call.
+ * `raw' points to the argument text as written (`raw_length' is its length).
+ * `expanded' points to the argument's macro-expansion
+ * (its length is `expand_length').
+ * `stringified_length' is the length the argument would have
+ * if stringified.
+ * `use_count' is the number of times this macro arg is substituted
+ * into the macro.  If the actual use count exceeds 10, 
+ * the value stored is 10. */
+
+/* raw and expanded are relative to ARG_BASE */
+#define ARG_BASE ((pfile)->token_buffer)
+
+struct argdata {
+   /* Strings relative to pfile->token_buffer */
+   long                raw, expanded, stringified;
+   int                 raw_length, expand_length;
+   int                 stringified_length;
+   char                newlines;
+   char                use_count;
+};
+
+cpp_buffer         *
+cpp_push_buffer(cpp_reader * pfile, unsigned char *buffer, long length)
+{
+#ifdef STATIC_BUFFERS
+   cpp_buffer         *buf = CPP_BUFFER(pfile);
+
+   if (buf == pfile->buffer_stack)
+      cpp_fatal("macro or `#include' recursion too deep");
+   buf--;
+   memset((char *)buf, 0, sizeof(cpp_buffer));
+   CPP_BUFFER(pfile) = buf;
+#else
+   cpp_buffer         *buf = (cpp_buffer *) xmalloc(sizeof(cpp_buffer));
+
+   memset((char *)buf, 0, sizeof(cpp_buffer));
+   CPP_PREV_BUFFER(buf) = CPP_BUFFER(pfile);
+   CPP_BUFFER(pfile) = buf;
+#endif
+   buf->if_stack = pfile->if_stack;
+   buf->cleanup = null_cleanup;
+   buf->underflow = null_underflow;
+   buf->buf = buf->cur = buffer;
+   buf->alimit = buf->rlimit = buffer + length;
+
+   return buf;
+}
+
+static cpp_buffer  *
+cpp_pop_buffer(cpp_reader * pfile)
+{
+   cpp_buffer         *buf = CPP_BUFFER(pfile);
+
+#ifdef STATIC_BUFFERS
+   (*buf->cleanup) (buf, pfile);
+   return ++CPP_BUFFER(pfile);
+#else
+   cpp_buffer         *next_buf = CPP_PREV_BUFFER(buf);
+
+   (*buf->cleanup) (buf, pfile);
+   CPP_BUFFER(pfile) = next_buf;
+   free(buf);
+   return next_buf;
+#endif
+}
+
+/* Scan until CPP_BUFFER (PFILE) is exhausted into PFILE->token_buffer.
+ * Pop the buffer when done. */
+
+static void
+cpp_scan_buffer(cpp_reader * pfile)
+{
+   cpp_buffer         *buffer = CPP_BUFFER(pfile);
+
+   for (;;)
+     {
+       enum cpp_token      token = cpp_get_token(pfile);
+
+       if (token == CPP_EOF)   /* Should not happen ... */
+          break;
+       if (token == CPP_POP && CPP_BUFFER(pfile) == buffer)
+         {
+            cpp_pop_buffer(pfile);
+            break;
+         }
+     }
+}
+
+/*
+ * Rescan a string (which may have escape marks) into pfile's buffer.
+ * Place the result in pfile->token_buffer.
+ *
+ * The input is copied before it is scanned, so it is safe to pass
+ * it something from the token_buffer that will get overwritten
+ * (because it follows CPP_WRITTEN).  This is used by do_include.
+ */
+
+static void
+cpp_expand_to_buffer(cpp_reader * pfile, unsigned char *buf, int length)
+{
+   cpp_buffer         *ip;
+   unsigned char      *limit = buf + length;
+   unsigned char      *buf1;
+
+   if (length < 0)
+      abort();
+
+   /* Set up the input on the input stack.  */
+
+   buf1 = (unsigned char *)alloca(length + 1);
+   {
+      unsigned char      *p1 = buf;
+      unsigned char      *p2 = buf1;
+
+      while (p1 != limit)
+        *p2++ = *p1++;
+   }
+   buf1[length] = 0;
+
+   ip = cpp_push_buffer(pfile, buf1, length);
+   ip->has_escapes = 1;
+
+   /* Scan the input, create the output.  */
+   cpp_scan_buffer(pfile);
+
+   CPP_NUL_TERMINATE(pfile);
+}
+
+static void
+adjust_position(unsigned char *buf, unsigned char *limit, long *linep,
+               long *colp)
+{
+   while (buf < limit)
+     {
+       unsigned char       ch = *buf++;
+
+       if (ch == '\n')
+          (*linep)++, (*colp) = 1;
+       else
+          (*colp)++;
+     }
+}
+
+/* Move line_base forward, updating lineno and colno. */
+
+static void
+update_position(cpp_buffer * pbuf)
+{
+   unsigned char      *old_pos = pbuf->buf + pbuf->line_base;
+   unsigned char      *new_pos = pbuf->cur;
+   struct parse_marker *mark;
+
+   for (mark = pbuf->marks; mark; mark = mark->next)
+     {
+       if (pbuf->buf + mark->position < new_pos)
+          new_pos = pbuf->buf + mark->position;
+     }
+   pbuf->line_base += new_pos - old_pos;
+   adjust_position(old_pos, new_pos, &pbuf->lineno, &pbuf->colno);
+}
+
+void
+cpp_buf_line_and_col(cpp_buffer * pbuf, long *linep, long *colp)
+{
+   long                dummy;
+
+   if (!colp)
+      colp = &dummy;
+   if (pbuf)
+     {
+       *linep = pbuf->lineno;
+       *colp = pbuf->colno;
+       adjust_position(pbuf->buf + pbuf->line_base, pbuf->cur, linep, colp);
+     }
+   else
+     {
+       *linep = 0;
+       *colp = 0;
+     }
+}
+
+/* Return the cpp_buffer that corresponds to a file (not a macro). */
+
+cpp_buffer         *
+cpp_file_buffer(cpp_reader * pfile)
+{
+   cpp_buffer         *ip = CPP_BUFFER(pfile);
+
+   for (; ip != CPP_NULL_BUFFER(pfile); ip = CPP_PREV_BUFFER(ip))
+      if (ip->fname)
+        return ip;
+   return NULL;
+}
+
+static long
+count_newlines(unsigned char *buf, unsigned char *limit)
+{
+   long                count = 0;
+
+   while (buf < limit)
+     {
+       unsigned char       ch = *buf++;
+
+       if (ch == '\n')
+          count++;
+     }
+   return count;
+}
+
+/*
+ * write out a #line command, for instance, after an #include file.
+ * If CONDITIONAL is nonzero, we can omit the #line if it would
+ * appear to be a no-op, and we can output a few newlines instead
+ * if we want to increase the line number by a small amount.
+ * FILE_CHANGE says whether we are entering a file, leaving, or neither.
+ */
+
+static void
+output_line_command(cpp_reader * pfile, int conditional,
+                   enum file_change_code file_change)
+{
+   long                line, col;
+   cpp_buffer         *ip = CPP_BUFFER(pfile);
+
+   if (CPP_OPTIONS(pfile)->no_line_commands
+       || !ip->fname || CPP_OPTIONS(pfile)->no_output)
+     {
+       return;
+     }
+   update_position(ip);
+   line = CPP_BUFFER(pfile)->lineno;
+   col = CPP_BUFFER(pfile)->colno;
+   adjust_position(CPP_LINE_BASE(ip), ip->cur, &line, &col);
+
+   if (conditional)
+     {
+       if (line == pfile->lineno)
+          return;
+
+       /* If the inherited line number is a little too small,
+        * output some newlines instead of a #line command.  */
+       if (line > pfile->lineno && line < pfile->lineno + 8)
+         {
+            CPP_RESERVE(pfile, 20);
+            while (line > pfile->lineno)
+              {
+                 CPP_PUTC_Q(pfile, '\n');
+                 pfile->lineno++;
+              }
+            return;
+         }
+     }
+
+   CPP_RESERVE(pfile, 4 * strlen(ip->nominal_fname) + 50);
+   {
+#ifdef OUTPUT_LINE_COMMANDS
+      static char         sharp_line[] = "#line ";
+
+#else
+      static char         sharp_line[] = "# ";
+
+#endif
+      CPP_PUTS_Q(pfile, sharp_line, sizeof(sharp_line) - 1);
+   }
+
+   sprintf((char *)CPP_PWRITTEN(pfile), "%d ", (int)line);
+   CPP_ADJUST_WRITTEN(pfile, strlen((char *)CPP_PWRITTEN(pfile)));
+
+   quote_string(pfile, ip->nominal_fname);
+   if (file_change != same_file)
+     {
+       CPP_PUTC_Q(pfile, ' ');
+       CPP_PUTC_Q(pfile, file_change == enter_file ? '1' : '2');
+     }
+   /* Tell cc1 if following text comes from a system header file.  */
+   if (ip->system_header_p)
+     {
+       CPP_PUTC_Q(pfile, ' ');
+       CPP_PUTC_Q(pfile, '3');
+     }
+#ifndef NO_IMPLICIT_EXTERN_C
+   /* Tell cc1plus if following text should be treated as C.  */
+   if (ip->system_header_p == 2 && CPP_OPTIONS(pfile)->cplusplus)
+     {
+       CPP_PUTC_Q(pfile, ' ');
+       CPP_PUTC_Q(pfile, '4');
+     }
+#endif
+   CPP_PUTC_Q(pfile, '\n');
+   pfile->lineno = line;
+}
+
+/*
+ * Parse a macro argument and append the info on PFILE's token_buffer.
+ * REST_ARGS means to absorb the rest of the args.
+ * Return nonzero to indicate a syntax error.
+ */
+
+static enum cpp_token
+macarg(cpp_reader * pfile, int rest_args)
+{
+   int                 paren = 0;
+   enum cpp_token      token;
+   char                save_put_out_comments =
+      CPP_OPTIONS(pfile)->put_out_comments;
+
+   CPP_OPTIONS(pfile)->put_out_comments = 0;
+
+   token = CPP_OTHER;
+   /* Try to parse as much of the argument as exists at this
+    * input stack level.  */
+   pfile->no_macro_expand++;
+   for (;;)
+     {
+       token = cpp_get_token(pfile);
+       switch (token)
+         {
+         case CPP_EOF:
+            goto done;
+         case CPP_POP:
+            /* If we've hit end of file, it's an error (reported by caller).
+             * Ditto if it's the end of cpp_expand_to_buffer text.
+             * If we've hit end of macro, just continue.  */
+            if (!CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile)))
+               goto done;
+            break;
+         case CPP_LPAREN:
+            paren++;
+            break;
+         case CPP_RPAREN:
+            if (--paren < 0)
+               goto found;
+            break;
+         case CPP_COMMA:
+            /* if we've returned to lowest level and
+             * we aren't absorbing all args */
+            if (paren == 0 && rest_args == 0)
+               goto found;
+            break;
+          found:
+            /* Remove ',' or ')' from argument buffer. */
+            CPP_ADJUST_WRITTEN(pfile, -1);
+            goto done;
+         default:;
+         }
+     }
+
+ done:
+   CPP_OPTIONS(pfile)->put_out_comments = save_put_out_comments;
+   pfile->no_macro_expand--;
+
+   return token;
+}
+
+/* Turn newlines to spaces in the string of length LENGTH at START,
+ * except inside of string constants.
+ * The string is copied into itself with its beginning staying fixed.  */
+
+static int
+change_newlines(unsigned char *start, int length)
+{
+   unsigned char      *ibp;
+   unsigned char      *obp;
+   unsigned char      *limit;
+   int                 c;
+
+   ibp = start;
+   limit = start + length;
+   obp = start;
+
+   while (ibp < limit)
+     {
+       *obp++ = c = *ibp++;
+       switch (c)
+         {
+
+         case '\'':
+         case '\"':
+            /* Notice and skip strings, so that we don't delete newlines in them.  */
+            {
+               int                 quotec = c;
+
+               while (ibp < limit)
+                 {
+                    *obp++ = c = *ibp++;
+                    if (c == quotec)
+                       break;
+                    if (c == '\n' && quotec == '\'')
+                       break;
+                 }
+            }
+            break;
+         }
+     }
+
+   return obp - start;
+}
+
+static struct tm   *
+timestamp(cpp_reader * pfile)
+{
+   if (!pfile->timebuf)
+     {
+       time_t              t = time((time_t *) 0);
+
+       pfile->timebuf = localtime(&t);
+     }
+   return pfile->timebuf;
+}
+
+static const char  *monthnames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+};
+
+/*
+ * expand things like __FILE__.  Place the expansion into the output
+ * buffer *without* rescanning.
+ */
+
+static void
+special_symbol(HASHNODE * hp, cpp_reader * pfile)
+{
+   const char         *buf;
+   char               *bufx;
+   int                 len;
+   int                 true_indepth;
+   cpp_buffer         *ip = NULL;
+   struct tm          *timebuf;
+
+   int                 paren = 0;      /* For special `defined' keyword */
+
+   for (ip = CPP_BUFFER(pfile);; ip = CPP_PREV_BUFFER(ip))
+     {
+       if (!ip)
+         {
+            cpp_error(pfile, "cccp error: not in any file?!");
+            return;            /* the show must go on */
+         }
+       if (ip->fname)
+          break;
+     }
+
+   switch (hp->type)
+     {
+     case T_FILE:
+     case T_BASE_FILE:
+       {
+          const char         *string;
+
+          if (hp->type == T_BASE_FILE)
+            {
+               while (CPP_PREV_BUFFER(ip))
+                  ip = CPP_PREV_BUFFER(ip);
+            }
+          string = ip->nominal_fname;
+
+          if (!string)
+             string = "";
+          CPP_RESERVE(pfile, 3 + 4 * strlen(string));
+          quote_string(pfile, string);
+          return;
+       }
+
+     case T_INCLUDE_LEVEL:
+       true_indepth = 0;
+       for (ip = CPP_BUFFER(pfile); ip; ip = CPP_PREV_BUFFER(ip))
+          if (ip->fname)
+             true_indepth++;
+
+       bufx = (char *)alloca(8);       /* Eight bytes ought to be more than enough */
+       sprintf(bufx, "%d", true_indepth - 1);
+       buf = bufx;
+       break;
+
+     case T_VERSION:
+       bufx = (char *)alloca(3 + strlen(version_string));
+       sprintf(bufx, "\"%s\"", version_string);
+       buf = bufx;
+       break;
+
+#ifndef NO_BUILTIN_SIZE_TYPE
+     case T_SIZE_TYPE:
+       buf = SIZE_TYPE;
+       break;
+#endif
+
+#ifndef NO_BUILTIN_PTRDIFF_TYPE
+     case T_PTRDIFF_TYPE:
+       buf = PTRDIFF_TYPE;
+       break;
+#endif
+
+     case T_WCHAR_TYPE:
+       buf = CPP_WCHAR_TYPE(pfile);
+       break;
+
+     case T_USER_LABEL_PREFIX_TYPE:
+       buf = USER_LABEL_PREFIX;
+       break;
+
+     case T_REGISTER_PREFIX_TYPE:
+       buf = REGISTER_PREFIX;
+       break;
+
+     case T_CONST:
+       bufx = (char *)alloca(4 * sizeof(int));
+       sprintf(bufx, "%d", hp->value.ival);
+       buf = bufx;
+       break;
+
+     case T_SPECLINE:
+       {
+          long                line = ip->lineno;
+          long                col = ip->colno;
+
+          adjust_position(CPP_LINE_BASE(ip), ip->cur, &line, &col);
+
+          bufx = (char *)alloca(10);
+          sprintf(bufx, "%d", (int)line);
+          buf = bufx;
+       }
+       break;
+
+     case T_DATE:
+     case T_TIME:
+       bufx = (char *)alloca(20);
+       timebuf = timestamp(pfile);
+       if (hp->type == T_DATE)
+          sprintf(bufx, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon],
+                  timebuf->tm_mday, timebuf->tm_year + 1900);
+       else
+          sprintf(bufx, "\"%02d:%02d:%02d\"", timebuf->tm_hour,
+                  timebuf->tm_min, timebuf->tm_sec);
+       buf = bufx;
+       break;
+
+     case T_SPEC_DEFINED:
+       buf = " 0 ";            /* Assume symbol is not defined */
+       ip = CPP_BUFFER(pfile);
+       SKIP_WHITE_SPACE(ip->cur);
+       if (*ip->cur == '(')
+         {
+            paren++;
+            ip->cur++;         /* Skip over the paren */
+            SKIP_WHITE_SPACE(ip->cur);
+         }
+       if (!is_idstart[*ip->cur])
+          goto oops;
+       if ((hp = cpp_lookup((const char *)ip->cur, -1, -1)))
+         {
+            buf = " 1 ";
+         }
+       while (is_idchar[*ip->cur])
+          ++ip->cur;
+       SKIP_WHITE_SPACE(ip->cur);
+       if (paren)
+         {
+            if (*ip->cur != ')')
+               goto oops;
+            ++ip->cur;
+         }
+       break;
+
+      oops:
+
+       cpp_error(pfile, "`defined' without an identifier");
+       break;
+
+     default:
+       cpp_error(pfile, "cccp error: invalid special hash type");      /* time for gdb */
+       abort();
+     }
+   len = strlen(buf);
+   CPP_RESERVE(pfile, len + 1);
+   CPP_PUTS_Q(pfile, buf, len);
+   CPP_NUL_TERMINATE_Q(pfile);
+
+   return;
+}
+
+/* Initialize the built-in macros.  */
+
+static void
+initialize_builtins(cpp_reader * pfile)
+{
+   install("__LINE__", -1, T_SPECLINE, 0, 0, -1);
+   install("__DATE__", -1, T_DATE, 0, 0, -1);
+   install("__FILE__", -1, T_FILE, 0, 0, -1);
+   install("__BASE_FILE__", -1, T_BASE_FILE, 0, 0, -1);
+   install("__INCLUDE_LEVEL__", -1, T_INCLUDE_LEVEL, 0, 0, -1);
+   install("__VERSION__", -1, T_VERSION, 0, 0, -1);
+#ifndef NO_BUILTIN_SIZE_TYPE
+   install("__SIZE_TYPE__", -1, T_SIZE_TYPE, 0, 0, -1);
+#endif
+#ifndef NO_BUILTIN_PTRDIFF_TYPE
+   install("__PTRDIFF_TYPE__ ", -1, T_PTRDIFF_TYPE, 0, 0, -1);
+#endif
+   install("__WCHAR_TYPE__", -1, T_WCHAR_TYPE, 0, 0, -1);
+   install("__USER_LABEL_PREFIX__", -1, T_USER_LABEL_PREFIX_TYPE, 0, 0, -1);
+   install("__REGISTER_PREFIX__", -1, T_REGISTER_PREFIX_TYPE, 0, 0, -1);
+   install("__TIME__", -1, T_TIME, 0, 0, -1);
+   if (!CPP_TRADITIONAL(pfile))
+      install("__STDC__", -1, T_CONST, STDC_VALUE, 0, -1);
+   if (CPP_OPTIONS(pfile)->objc)
+      install("__OBJC__", -1, T_CONST, 1, 0, -1);
+/*  This is supplied using a -D by the compiler driver
+ * so that it is present only when truly compiling with GNU C.  */
+/*  install ("__GNUC__", -1, T_CONST, 2, 0, -1);  */
+
+   if (CPP_OPTIONS(pfile)->debug_output)
+     {
+       char                directive[2048];
+       struct directive   *dp = &directive_table[0];
+       struct tm          *timebuf = timestamp(pfile);
+       cpp_buffer         *pbuffer = CPP_BUFFER(pfile);
+
+       while (CPP_PREV_BUFFER(pbuffer))
+          pbuffer = CPP_PREV_BUFFER(pbuffer);
+       sprintf(directive, " __BASE_FILE__ \"%s\"\n", pbuffer->nominal_fname);
+       output_line_command(pfile, 0, same_file);
+       pass_thru_directive(directive, &directive[strlen(directive)], pfile,
+                           dp);
+
+       sprintf(directive, " __VERSION__ \"%s\"\n", version_string);
+       output_line_command(pfile, 0, same_file);
+       pass_thru_directive(directive, &directive[strlen(directive)], pfile,
+                           dp);
+
+#ifndef NO_BUILTIN_SIZE_TYPE
+       sprintf(directive, " __SIZE_TYPE__ %s\n", SIZE_TYPE);
+       output_line_command(pfile, 0, same_file);
+       pass_thru_directive(directive, &directive[strlen(directive)], pfile,
+                           dp);
+#endif
+
+#ifndef NO_BUILTIN_PTRDIFF_TYPE
+       sprintf(directive, " __PTRDIFF_TYPE__ %s\n", PTRDIFF_TYPE);
+       output_line_command(pfile, 0, same_file);
+       pass_thru_directive(directive, &directive[strlen(directive)], pfile,
+                           dp);
+#endif
+
+       sprintf(directive, " __WCHAR_TYPE__ %s\n", CPP_WCHAR_TYPE(pfile));
+       output_line_command(pfile, 0, same_file);
+       pass_thru_directive(directive, &directive[strlen(directive)], pfile,
+                           dp);
+
+       sprintf(directive, " __DATE__ \"%s %2d %4d\"\n",
+               monthnames[timebuf->tm_mon],
+               timebuf->tm_mday, timebuf->tm_year + 1900);
+       output_line_command(pfile, 0, same_file);
+       pass_thru_directive(directive, &directive[strlen(directive)], pfile,
+                           dp);
+
+       sprintf(directive, " __TIME__ \"%02d:%02d:%02d\"\n",
+               timebuf->tm_hour, timebuf->tm_min, timebuf->tm_sec);
+       output_line_command(pfile, 0, same_file);
+       pass_thru_directive(directive, &directive[strlen(directive)], pfile,
+                           dp);
+
+       if (!CPP_TRADITIONAL(pfile))
+         {
+            sprintf(directive, " __STDC__ 1");
+            output_line_command(pfile, 0, same_file);
+            pass_thru_directive(directive, &directive[strlen(directive)],
+                                pfile, dp);
+         }
+       if (CPP_OPTIONS(pfile)->objc)
+         {
+            sprintf(directive, " __OBJC__ 1");
+            output_line_command(pfile, 0, same_file);
+            pass_thru_directive(directive, &directive[strlen(directive)],
+                                pfile, dp);
+         }
+     }
+}
+
+/* Return 1 iff a token ending in C1 followed directly by a token C2
+ * could cause mis-tokenization. */
+
+static int
+unsafe_chars(int c1, int c2)
+{
+   switch (c1)
+     {
+     case '+':
+     case '-':
+       if (c2 == c1 || c2 == '=')
+          return 1;
+       goto letter;
+     case '.':
+     case '0':
+     case '1':
+     case '2':
+     case '3':
+     case '4':
+     case '5':
+     case '6':
+     case '7':
+     case '8':
+     case '9':
+     case 'e':
+     case 'E':
+       if (c2 == '-' || c2 == '+')
+          return 1;            /* could extend a pre-processing number */
+       goto letter;
+     case 'L':
+       if (c2 == '\'' || c2 == '\"')
+          return 1;            /* Could turn into L"xxx" or L'xxx'. */
+       goto letter;
+      letter:
+     case '_':
+     case 'a':
+     case 'b':
+     case 'c':
+     case 'd':
+     case 'f':
+     case 'g':
+     case 'h':
+     case 'i':
+     case 'j':
+     case 'k':
+     case 'l':
+     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':
+     case 'A':
+     case 'B':
+     case 'C':
+     case 'D':
+     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':
+       /* We're in the middle of either a name or a pre-processing number. */
+       return (is_idchar[c2] || c2 == '.');
+     case '<':
+     case '>':
+     case '!':
+     case '%':
+     case '#':
+     case ':':
+     case '^':
+     case '&':
+     case '|':
+     case '*':
+     case '/':
+     case '=':
+       return (c2 == c1 || c2 == '=');
+     }
+   return 0;
+}
+
+/* Expand a macro call.
+ * HP points to the symbol that is the macro being called.
+ * Put the result of expansion onto the input stack
+ * so that subsequent input by our caller will use it.
+ * 
+ * If macro wants arguments, caller has already verified that
+ * an argument list follows; arguments come from the input stack.  */
+
+static void
+macroexpand(cpp_reader * pfile, HASHNODE * hp)
+{
+   int                 nargs;
+   DEFINITION         *defn = hp->value.defn;
+   unsigned char      *xbuf;
+   long                start_line, start_column;
+   int                 xbuf_len;
+   struct argdata     *args;
+   long                old_written = CPP_WRITTEN(pfile);
+
+   int                 rest_args, rest_zero;
+   int                 i;
+
+   pfile->output_escapes++;
+   cpp_buf_line_and_col(cpp_file_buffer(pfile), &start_line, &start_column);
+
+   rest_zero = 0;
+   args = NULL;
+   nargs = defn->nargs;
+
+   if (nargs >= 0)
+     {
+       enum cpp_token      token;
+
+       token = CPP_OTHER;
+
+       args = (struct argdata *)alloca((nargs + 1) * sizeof(struct argdata));
+
+       for (i = 0; i < nargs; i++)
+         {
+            args[i].raw = args[i].expanded = 0;
+            args[i].raw_length = 0;
+            args[i].expand_length = args[i].stringified_length = -1;
+            args[i].use_count = 0;
+         }
+
+       /* Parse all the macro args that are supplied.  I counts them.
+        * The first NARGS args are stored in ARGS.
+        * The rest are discarded.  If rest_args is set then we assume
+        * macarg absorbed the rest of the args. */
+       i = 0;
+       rest_args = 0;
+       rest_args = 0;
+       FORWARD(1);             /* Discard the open-parenthesis before the first arg.  */
+       do
+         {
+            if (rest_args)
+               continue;
+            if (i < nargs || (nargs == 0 && i == 0))
+              {
+                 /* if we are working on last arg which absorbs rest of args... */
+                 if (i == nargs - 1 && defn->rest_args)
+                    rest_args = 1;
+                 args[i].raw = CPP_WRITTEN(pfile);
+                 token = macarg(pfile, rest_args);
+                 args[i].raw_length = CPP_WRITTEN(pfile) - args[i].raw;
+                 args[i].newlines = 0; /* FIXME */
+              }
+            else
+               token = macarg(pfile, 0);
+            if (token == CPP_EOF || token == CPP_POP)
+              {
+                 cpp_error_with_line(pfile, start_line, start_column,
+                                     "unterminated macro call");
+                 return;
+              }
+            i++;
+         }
+       while (token == CPP_COMMA);
+
+       /* If we got one arg but it was just whitespace, call that 0 args.  */
+       if (i == 1)
+         {
+            unsigned char      *bp = ARG_BASE + args[0].raw;
+            unsigned char      *lim = bp + args[0].raw_length;
+
+            /* cpp.texi says for foo ( ) we provide one argument.
+             * However, if foo wants just 0 arguments, treat this as 0.  */
+            if (nargs == 0)
+               while (bp != lim && is_space[*bp])
+                  bp++;
+            if (bp == lim)
+               i = 0;
+         }
+       /* Don't output an error message if we have already output one for
+        * a parse error above.  */
+       if (nargs == 0 && i > 0)
+         {
+            cpp_error(pfile, "arguments given to macro `%s'", hp->name);
+         }
+       else if (i < nargs)
+         {
+            /* traditional C allows foo() if foo wants one argument.  */
+            if (nargs == 1 && i == 0 && CPP_TRADITIONAL(pfile));
+            /* the rest args token is allowed to absorb 0 tokens */
+            else if (i == nargs - 1 && defn->rest_args)
+               rest_zero = 1;
+            else if (i == 0)
+               cpp_error(pfile, "macro `%s' used without args", hp->name);
+            else if (i == 1)
+               cpp_error(pfile, "macro `%s' used with just one arg", hp->name);
+            else
+               cpp_error(pfile, "macro `%s' used with only %d args",
+                         hp->name, i);
+         }
+       else if (i > nargs)
+         {
+            cpp_error(pfile,
+                      "macro `%s' used with too many (%d) args", hp->name, i);
+         }
+     }
+   /* If macro wants zero args, we parsed the arglist for checking only.
+    * Read directly from the macro definition.  */
+   if (nargs <= 0)
+     {
+       xbuf = defn->expansion;
+       xbuf_len = defn->length;
+     }
+   else
+     {
+       unsigned char      *exp = defn->expansion;
+       int                 offset;     /* offset in expansion,
+                                        * copied a piece at a time */
+       int                 totlen;     /* total amount of exp buffer filled so far */
+       reflist            *ap, *last_ap;
+
+       /* Macro really takes args.  Compute the expansion of this call.  */
+
+       /* Compute length in characters of the macro's expansion.
+        * Also count number of times each arg is used.  */
+       xbuf_len = defn->length;
+       for (ap = defn->pattern; ap; ap = ap->next)
+         {
+            if (ap->stringify)
+              {
+                 struct argdata     *arg = &args[ap->argno];
+
+                 /* Stringify it it hasn't already been */
+                 if (arg->stringified_length < 0)
+                   {
+                      int                 arglen = arg->raw_length;
+                      int                 escaped = 0;
+                      int                 in_string = 0;
+                      int                 c;
+
+                      /* Initially need_space is -1.  Otherwise, 1 means the
+                       * previous character was a space, but we suppressed it;
+                       * 0 means the previous character was a non-space. */
+                      int                 need_space = -1;
+
+                      i = 0;
+                      arg->stringified = CPP_WRITTEN(pfile);
+                      if (!CPP_TRADITIONAL(pfile))
+                         CPP_PUTC(pfile, '\"');        /* insert beginning quote */
+                      for (; i < arglen; i++)
+                        {
+                           c = (ARG_BASE + arg->raw)[i];
+
+                           if (!in_string)
+                             {
+                                /* Internal sequences of whitespace are replaced by
+                                 * one space except within an string or char token. */
+                                if (is_space[c])
+                                  {
+                                     if (CPP_WRITTEN(pfile) > arg->stringified
+                                         && (CPP_PWRITTEN(pfile))[-1] == '@')
+                                       {
+                                          /* "@ " escape markers are removed */
+                                          CPP_ADJUST_WRITTEN(pfile, -1);
+                                          continue;
+                                       }
+                                     if (need_space == 0)
+                                        need_space = 1;
+                                     continue;
+                                  }
+                                else if (need_space > 0)
+                                   CPP_PUTC(pfile, ' ');
+                                need_space = 0;
+                             }
+                           if (escaped)
+                              escaped = 0;
+                           else
+                             {
+                                if (c == '\\')
+                                   escaped = 1;
+                                if (in_string)
+                                  {
+                                     if (c == in_string)
+                                        in_string = 0;
+                                  }
+                                else if (c == '\"' || c == '\'')
+                                   in_string = c;
+                             }
+
+                           /* Escape these chars */
+                           if (c == '\"' || (in_string && c == '\\'))
+                              CPP_PUTC(pfile, '\\');
+                           if (isprint(c))
+                              CPP_PUTC(pfile, c);
+                           else
+                             {
+                                CPP_RESERVE(pfile, 4);
+                                sprintf((char *)CPP_PWRITTEN(pfile), "\\%03o",
+                                        (unsigned int)c);
+                                CPP_ADJUST_WRITTEN(pfile, 4);
+                             }
+                        }
+                      if (!CPP_TRADITIONAL(pfile))
+                         CPP_PUTC(pfile, '\"');        /* insert ending quote */
+                      arg->stringified_length
+                         = CPP_WRITTEN(pfile) - arg->stringified;
+                   }
+                 xbuf_len += args[ap->argno].stringified_length;
+              }
+            else if (ap->raw_before || ap->raw_after || CPP_TRADITIONAL(pfile))
+               /* Add 4 for two newline-space markers to prevent
+                * token concatenation.  */
+               xbuf_len += args[ap->argno].raw_length + 4;
+            else
+              {
+                 /* We have an ordinary (expanded) occurrence of the arg.
+                  * So compute its expansion, if we have not already.  */
+                 if (args[ap->argno].expand_length < 0)
+                   {
+                      args[ap->argno].expanded = CPP_WRITTEN(pfile);
+                      cpp_expand_to_buffer(pfile,
+                                           ARG_BASE + args[ap->argno].raw,
+                                           args[ap->argno].raw_length);
+
+                      args[ap->argno].expand_length
+                         = CPP_WRITTEN(pfile) - args[ap->argno].expanded;
+                   }
+                 /* Add 4 for two newline-space markers to prevent
+                  * token concatenation.  */
+                 xbuf_len += args[ap->argno].expand_length + 4;
+              }
+            if (args[ap->argno].use_count < 10)
+               args[ap->argno].use_count++;
+         }
+
+       xbuf = (unsigned char *)xmalloc(xbuf_len + 1);
+
+       /* Generate in XBUF the complete expansion
+        * with arguments substituted in.
+        * TOTLEN is the total size generated so far.
+        * OFFSET is the index in the definition
+        * of where we are copying from.  */
+       offset = totlen = 0;
+       for (last_ap = NULL, ap = defn->pattern; ap;
+            last_ap = ap, ap = ap->next)
+         {
+            struct argdata     *arg = &args[ap->argno];
+            int                 count_before = totlen;
+
+            /* Add chars to XBUF.  */
+            for (i = 0; i < ap->nchars; i++, offset++)
+               xbuf[totlen++] = exp[offset];
+
+            /* If followed by an empty rest arg with concatenation,
+             * delete the last run of nonwhite chars.  */
+            if (rest_zero && totlen > count_before
+                && ((ap->rest_args && ap->raw_before)
+                    || (last_ap && last_ap->rest_args && last_ap->raw_after)))
+              {
+                 /* Delete final whitespace.  */
+                 while (totlen > count_before && is_space[xbuf[totlen - 1]])
+                    totlen--;
+
+                 /* Delete the nonwhites before them.  */
+                 while (totlen > count_before && !is_space[xbuf[totlen - 1]])
+                    totlen--;
+              }
+            if (ap->stringify != 0)
+              {
+                 memcpy(xbuf + totlen, ARG_BASE + arg->stringified,
+                        arg->stringified_length);
+                 totlen += arg->stringified_length;
+              }
+            else if (ap->raw_before || ap->raw_after || CPP_TRADITIONAL(pfile))
+              {
+                 unsigned char      *p1 = ARG_BASE + arg->raw;
+                 unsigned char      *l1 = p1 + arg->raw_length;
+
+                 if (ap->raw_before)
+                   {
+                      while (p1 != l1 && is_space[*p1])
+                         p1++;
+                      while (p1 != l1 && is_idchar[*p1])
+                         xbuf[totlen++] = *p1++;
+                   }
+                 if (ap->raw_after)
+                   {
+                      /* Arg is concatenated after: delete trailing whitespace,
+                       * whitespace markers, and no-reexpansion markers.  */
+                      while (p1 != l1)
+                        {
+                           if (is_space[l1[-1]])
+                              l1--;
+                           else if (l1[-1] == '-')
+                             {
+                                unsigned char      *p2 = l1 - 1;
+
+                                /* If a `-' is preceded by an odd number of newlines then it
+                                 * and the last newline are a no-reexpansion marker.  */
+                                while (p2 != p1 && p2[-1] == '\n')
+                                   p2--;
+                                if ((l1 - 1 - p2) & 1)
+                                  {
+                                     l1 -= 2;
+                                  }
+                                else
+                                   break;
+                             }
+                           else
+                              break;
+                        }
+                   }
+                 memcpy(xbuf + totlen, p1, l1 - p1);
+                 totlen += l1 - p1;
+              }
+            else
+              {
+                 unsigned char      *expanded = ARG_BASE + arg->expanded;
+
+                 if (!ap->raw_before && totlen > 0 && arg->expand_length
+                     && !CPP_TRADITIONAL(pfile)
+                     && unsafe_chars(xbuf[totlen - 1], expanded[0]))
+                   {
+                      xbuf[totlen++] = '@';
+                      xbuf[totlen++] = ' ';
+                   }
+                 memcpy(xbuf + totlen, expanded, arg->expand_length);
+                 totlen += arg->expand_length;
+
+                 if (!ap->raw_after && totlen > 0 && offset < defn->length
+                     && !CPP_TRADITIONAL(pfile)
+                     && unsafe_chars(xbuf[totlen - 1], exp[offset]))
+                   {
+                      xbuf[totlen++] = '@';
+                      xbuf[totlen++] = ' ';
+                   }
+                 /* If a macro argument with newlines is used multiple times,
+                  * then only expand the newlines once.  This avoids creating
+                  * output lines which don't correspond to any input line,
+                  * which confuses gdb and gcov.  */
+                 if (arg->use_count > 1 && arg->newlines > 0)
+                   {
+                      /* Don't bother doing change_newlines for subsequent
+                       * uses of arg.  */
+                      arg->use_count = 1;
+                      arg->expand_length
+                         = change_newlines(expanded, arg->expand_length);
+                   }
+              }
+
+            if (totlen > xbuf_len)
+               abort();
+         }
+
+       /* if there is anything left of the definition
+        * after handling the arg list, copy that in too. */
+
+       for (i = offset; i < defn->length; i++)
+         {
+            /* if we've reached the end of the macro */
+            if (exp[i] == ')')
+               rest_zero = 0;
+            if (!(rest_zero && last_ap && last_ap->rest_args
+                  && last_ap->raw_after))
+               xbuf[totlen++] = exp[i];
+         }
+
+       xbuf[totlen] = 0;
+       xbuf_len = totlen;
+
+     }
+
+   pfile->output_escapes--;
+
+   /* Now put the expansion on the input stack
+    * so our caller will commence reading from it.  */
+   push_macro_expansion(pfile, xbuf, xbuf_len, hp);
+   CPP_BUFFER(pfile)->has_escapes = 1;
+
+   /* Pop the space we've used in the token_buffer for argument expansion. */
+   CPP_SET_WRITTEN(pfile, old_written);
+
+   /* Recursive macro use sometimes works traditionally.
+    * #define foo(x,y) bar (x (y,0), y)
+    * foo (foo, baz)  */
+
+   if (!CPP_TRADITIONAL(pfile))
+      hp->type = T_DISABLED;
+}
+
+static void
+push_macro_expansion(cpp_reader * pfile, unsigned char *xbuf, int xbuf_len,
+                    HASHNODE * hp)
+{
+   cpp_buffer         *mbuf = cpp_push_buffer(pfile, xbuf, xbuf_len);
+
+   mbuf->cleanup = macro_cleanup;
+   mbuf->data = hp;
+
+   /* The first chars of the expansion should be a "@ " added by
+    * collect_expansion.  This is to prevent accidental token-pasting
+    * between the text preceding the macro invocation, and the macro
+    * expansion text.
+    * 
+    * We would like to avoid adding unneeded spaces (for the sake of
+    * tools that use cpp, such as imake).  In some common cases we can
+    * tell that it is safe to omit the space.
+    * 
+    * The character before the macro invocation cannot have been an
+    * idchar (or else it would have been pasted with the idchars of
+    * the macro name).  Therefore, if the first non-space character
+    * of the expansion is an idchar, we do not need the extra space
+    * to prevent token pasting.
+    * 
+    * Also, we don't need the extra space if the first char is '(',
+    * or some other (less common) characters.  */
+
+   if (xbuf[0] == '@' && xbuf[1] == ' '
+       && (is_idchar[xbuf[2]] || xbuf[2] == '(' || xbuf[2] == '\''
+          || xbuf[2] == '\"'))
+      mbuf->cur += 2;
+}
+
+/* Like cpp_get_token, except that it does not read past end-of-line.
+ * Also, horizontal space is skipped, and macros are popped.  */
+
+static enum cpp_token
+get_directive_token(cpp_reader * pfile)
+{
+   for (;;)
+     {
+       long                old_written = CPP_WRITTEN(pfile);
+       enum cpp_token      token;
+
+       cpp_skip_hspace(pfile);
+       if (PEEKC() == '\n')
+          return CPP_VSPACE;
+       token = cpp_get_token(pfile);
+       switch (token)
+         {
+         case CPP_POP:
+            if (!CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile)))
+               return token;
+            /* ... else fall though ... */
+         case CPP_HSPACE:
+         case CPP_COMMENT:
+            CPP_SET_WRITTEN(pfile, old_written);
+            break;
+         default:
+            return token;
+         }
+     }
+}
+
+/* Handle #include and #import.
+ * This function expects to see "fname" or <fname> on the input.
+ * 
+ * The input is normally in part of the output_buffer following
+ * CPP_WRITTEN, and will get overwritten by output_line_command.
+ * I.e. in input file specification has been popped by handle_directive.
+ * This is safe. */
+
+static int
+do_include(cpp_reader * pfile, struct directive *keyword,
+          unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__)
+{
+   int                 importing = (keyword->type == T_IMPORT);
+   int                 skip_dirs = (keyword->type == T_INCLUDE_NEXT);
+   char               *fname;  /* Dynamically allocated fname buffer */
+   unsigned char      *fbeg, *fend;    /* Beginning and end of fname */
+   enum cpp_token      token;
+
+   /* Chain of dirs to search */
+   file_name_list     *search_start = CPP_OPTIONS(pfile)->include;
+   file_name_list      dsp[1]; /* First in chain, if #include "..." */
+   file_name_list     *searchptr = 0;
+   long                old_written = CPP_WRITTEN(pfile);
+   int                 flen;
+   int                 f;      /* file number */
+   int                 angle_brackets = 0;     /* 0 for "...", 1 for <...> */
+
+   f = -1;                     /* JF we iz paranoid! */
+
+   if (importing && CPP_OPTIONS(pfile)->warn_import
+       && !CPP_OPTIONS(pfile)->inhibit_warnings
+       && !CPP_BUFFER(pfile)->system_header_p && !pfile->import_warning)
+     {
+       pfile->import_warning = 1;
+       cpp_warning(pfile, "using `#import' is not recommended");
+       fprintf(stderr,
+               "The fact that a certain header file need not be processed more than once\n");
+       fprintf(stderr,
+               "should be indicated in the header file, not where it is used.\n");
+       fprintf(stderr,
+               "The best way to do this is with a conditional of this form:\n\n");
+       fprintf(stderr, "  #ifndef _FOO_H_INCLUDED\n");
+       fprintf(stderr, "  #define _FOO_H_INCLUDED\n");
+       fprintf(stderr, "  ... <real contents of file> ...\n");
+       fprintf(stderr, "  #endif /* Not _FOO_H_INCLUDED */\n\n");
+       fprintf(stderr, "Then users can use `#include' any number of times.\n");
+       fprintf(stderr,
+               "GNU C automatically avoids processing the file more than once\n");
+       fprintf(stderr, "when it is equipped with such a conditional.\n");
+     }
+   pfile->parsing_include_directive++;
+   token = get_directive_token(pfile);
+   pfile->parsing_include_directive--;
+
+   if (token == CPP_STRING)
+     {
+       /* FIXME - check no trailing garbage */
+       fbeg = pfile->token_buffer + old_written + 1;
+       fend = CPP_PWRITTEN(pfile) - 1;
+       if (fbeg[-1] == '<')
+         {
+            angle_brackets = 1;
+            /* If -I-, start with the first -I dir after the -I-.  */
+            if (CPP_OPTIONS(pfile)->first_bracket_include)
+               search_start = CPP_OPTIONS(pfile)->first_bracket_include;
+         }
+       /* If -I- was specified, don't search current dir, only spec'd ones. */
+       else if (!CPP_OPTIONS(pfile)->ignore_srcdir)
+         {
+            cpp_buffer         *fp;
+
+            /* We have "filename".  Figure out directory this source
+             * file is coming from and put it on the front of the list. */
+
+            for (fp = CPP_BUFFER(pfile); fp; fp = CPP_PREV_BUFFER(fp))
+              {
+                 int                 n;
+                 const char         *ep, *nam;
+
+                 if ((nam = fp->nominal_fname))
+                   {
+                      /* Found a named file.  Figure out dir of the file,
+                       * and put it in front of the search list.  */
+                      dsp[0].next = search_start;
+                      search_start = dsp;
+#ifndef VMS
+                      ep = strrchr(nam, '/');
+#else /* VMS */
+                      ep = strrchr(nam, ']');
+                      if (!ep)
+                         ep = strrchr(nam, '>');
+                      if (!ep)
+                         ep = strrchr(nam, ':');
+                      if (ep)
+                         ep++;
+#endif /* VMS */
+                      if (ep)
+                        {
+                           n = ep - nam;
+                           dsp[0].fname = (char *)alloca(n + 1);
+                           strncpy(dsp[0].fname, nam, n);
+                           dsp[0].fname[n] = '\0';
+                           if (n + INCLUDE_LEN_FUDGE > pfile->max_include_len)
+                              pfile->max_include_len = n + INCLUDE_LEN_FUDGE;
+                        }
+                      else
+                        {
+                           dsp[0].fname = 0;   /* Current directory */
+                        }
+                      dsp[0].got_name_map = 0;
+                      break;
+                   }
+              }
+         }
+     }
+   else
+     {
+       cpp_error(pfile,
+                 "`#%s' expects \"FILENAME\" or <FILENAME>", keyword->name);
+       CPP_SET_WRITTEN(pfile, old_written);
+       skip_rest_of_line(pfile);
+       return 0;
+     }
+
+   *fend = 0;
+
+   token = get_directive_token(pfile);
+   if (token != CPP_VSPACE)
+     {
+       cpp_error(pfile, "junk at end of `#include'");
+       while (token != CPP_VSPACE && token != CPP_EOF && token != CPP_POP)
+          token = get_directive_token(pfile);
+     }
+   /* For #include_next, skip in the search path
+    * past the dir in which the containing file was found.  */
+   if (skip_dirs)
+     {
+       cpp_buffer         *fp;
+
+       for (fp = CPP_BUFFER(pfile); fp; fp = CPP_PREV_BUFFER(fp))
+          if (fp->fname)
+            {
+               /* fp->dir is null if the containing file was specified with
+                * an absolute file name.  In that case, don't skip anything.  */
+               if (fp->dir == SELF_DIR_DUMMY)
+                  search_start = CPP_OPTIONS(pfile)->include;
+               else if (fp->dir)
+                  search_start = fp->dir->next;
+               break;
+            }
+     }
+   CPP_SET_WRITTEN(pfile, old_written);
+
+   flen = fend - fbeg;
+
+   if (flen == 0)
+     {
+       cpp_error(pfile, "empty file name in `#%s'", keyword->name);
+       return 0;
+     }
+   /* Allocate this permanently, because it gets stored in the definitions
+    * of macros.  */
+   fname = (char *)xmalloc(pfile->max_include_len + flen + 4);
+   /* + 2 above for slash and terminating null.  */
+   /* + 2 added for '.h' on VMS (to support '#include filename') */
+
+   /* If specified file name is absolute, just open it.  */
+
+#ifndef __EMX__
+   if (*fbeg == '/')
+#else
+   if (_fnisabs(fbeg))
+#endif
+     {
+       strncpy(fname, (const char *)fbeg, flen);
+       fname[flen] = 0;
+       if (redundant_include_p(pfile, fname))
+          return 0;
+       if (importing)
+          f = lookup_import(pfile, fname, NULL);
+       else
+          f = open_include_file(pfile, fname, NULL);
+       if (f == -2)
+          return 0;            /* Already included this file */
+     }
+   else
+     {
+       /* Search directory path, trying to open the file.
+        * Copy each filename tried into FNAME.  */
+
+       for (searchptr = search_start; searchptr; searchptr = searchptr->next)
+         {
+            if (searchptr->fname)
+              {
+                 /* The empty string in a search path is ignored.
+                  * This makes it possible to turn off entirely
+                  * a standard piece of the list.  */
+                 if (searchptr->fname[0] == 0)
+                    continue;
+                 strcpy(fname, searchptr->fname);
+                 strcat(fname, "/");
+                 fname[strlen(fname) + flen] = 0;
+              }
+            else
+              {
+                 fname[0] = 0;
+              }
+            strncat(fname, (const char *)fbeg, flen);
+#ifdef VMS
+            /* Change this 1/2 Unix 1/2 VMS file specification into a
+             * full VMS file specification */
+            if (searchptr->fname && (searchptr->fname[0] != 0))
+              {
+                 /* Fix up the filename */
+                 hack_vms_include_specification(fname);
+              }
+            else
+              {
+                 /* This is a normal VMS filespec, so use it unchanged.  */
+                 strncpy(fname, fbeg, flen);
+                 fname[flen] = 0;
+                 /* if it's '#include filename', add the missing .h */
+                 if (!strchr(fname, '.'))
+                   {
+                      strcat(fname, ".h");
+                   }
+              }
+#endif /* VMS */
+            /* ??? There are currently 3 separate mechanisms for avoiding processing
+             * of redundant include files: #import, #pragma once, and
+             * redundant_include_p.  It would be nice if they were unified.  */
+            if (redundant_include_p(pfile, fname))
+               return 0;
+            if (importing)
+               f = lookup_import(pfile, fname, searchptr);
+            else
+               f = open_include_file(pfile, fname, searchptr);
+            if (f == -2)
+               return 0;       /* Already included this file */
+#ifdef EACCES
+            else if (f == -1 && errno == EACCES)
+               cpp_warning(pfile, "Header file %s exists, but is not readable",
+                           fname);
+#endif
+            if (f >= 0)
+               break;
+         }
+     }
+
+   if (f < 0)
+     {
+       /* A file that was not found.  */
+       strncpy(fname, (const char *)fbeg, flen);
+       fname[flen] = 0;
+       /* If generating dependencies and -MG was specified, we assume missing
+        * files are leaf files, living in the same directory as the source file
+        * or other similar place; these missing files may be generated from
+        * other files and may not exist yet (eg: y.tab.h).  */
+
+       if (CPP_OPTIONS(pfile)->print_deps_missing_files
+           && CPP_PRINT_DEPS(pfile)
+           > (angle_brackets || (pfile->system_include_depth > 0)))
+         {
+            /* If it was requested as a system header file,
+             * then assume it belongs in the first place to look for such.  */
+            if (angle_brackets)
+              {
+                 for (searchptr = search_start; searchptr;
+                      searchptr = searchptr->next)
+                   {
+                      if (searchptr->fname)
+                        {
+                           char               *p;
+
+                           if (searchptr->fname[0] == 0)
+                              continue;
+                           p = (char *)alloca(strlen(searchptr->fname)
+                                              + strlen(fname) + 2);
+                           strcpy(p, searchptr->fname);
+                           strcat(p, "/");
+                           strcat(p, fname);
+                           deps_output(pfile, p, ' ');
+                           break;
+                        }
+                   }
+              }
+            else
+              {
+                 /* Otherwise, omit the directory, as if the file existed
+                  * in the directory with the source.  */
+                 deps_output(pfile, fname, ' ');
+              }
+         }
+       /* If -M was specified, and this header file won't be added to the
+        * dependency list, then don't count this as an error, because we can
+        * still produce correct output.  Otherwise, we can't produce correct
+        * output, because there may be dependencies we need inside the missing
+        * file, and we don't know what directory this missing file exists in. */
+       else if (CPP_PRINT_DEPS(pfile)
+                && (CPP_PRINT_DEPS(pfile)
+                    <= (angle_brackets || (pfile->system_include_depth > 0))))
+          cpp_warning(pfile, "No include path in which to find %s", fname);
+       else if (search_start)
+          cpp_error_from_errno(pfile, fname);
+       else
+          cpp_error(pfile, "No include path in which to find %s", fname);
+     }
+   else
+     {
+       /* Check to see if this include file is a once-only include file.
+        * If so, give up.  */
+
+       file_name_list     *ptr;
+
+       for (ptr = pfile->dont_repeat_files; ptr; ptr = ptr->next)
+         {
+            if (!strcmp(ptr->fname, fname))
+              {
+                 close(f);
+                 return 0;     /* This file was once'd. */
+              }
+         }
+
+       for (ptr = pfile->all_include_files; ptr; ptr = ptr->next)
+         {
+            if (!strcmp(ptr->fname, fname))
+               break;          /* This file was included before. */
+         }
+
+       if (!ptr)
+         {
+            /* This is the first time for this file.  */
+            /* Add it to list of files included.  */
+
+            ptr = (file_name_list *) xmalloc(sizeof(file_name_list));
+
+            ptr->control_macro = 0;
+            ptr->c_system_include_path = 0;
+            ptr->next = pfile->all_include_files;
+            pfile->all_include_files = ptr;
+            ptr->fname = savestring(fname);
+            ptr->got_name_map = 0;
+
+            /* For -M, add this file to the dependencies.  */
+            if (CPP_PRINT_DEPS(pfile)
+                > (angle_brackets || (pfile->system_include_depth > 0)))
+               deps_output(pfile, fname, ' ');
+         }
+       /* Handle -H option.  */
+       if (CPP_OPTIONS(pfile)->print_include_names)
+         {
+            cpp_buffer         *buf = CPP_BUFFER(pfile);
+
+            while ((buf = CPP_PREV_BUFFER(buf)))
+               putc('.', stderr);
+            fprintf(stderr, "%s\n", fname);
+         }
+       if (angle_brackets)
+          pfile->system_include_depth++;
+
+       /* Actually process the file.  */
+
+       /* Record file on "seen" list for #import. */
+       add_import(pfile, f, fname);
+
+       /* Actually process the file */
+       cpp_push_buffer(pfile, NULL, 0);
+       if (finclude(pfile, f, fname, is_system_include(pfile, fname),
+                    searchptr != dsp ? searchptr : SELF_DIR_DUMMY))
+         {
+            output_line_command(pfile, 0, enter_file);
+            pfile->only_seen_white = 2;
+         }
+       if (angle_brackets)
+          pfile->system_include_depth--;
+     }
+   return 0;
+}
+
+/* Return nonzero if there is no need to include file NAME
+ * because it has already been included and it contains a conditional
+ * to make a repeated include do nothing.  */
+
+static int
+redundant_include_p(cpp_reader * pfile, char *name)
+{
+   file_name_list     *l = pfile->all_include_files;
+
+   for (; l; l = l->next)
+      if (!strcmp(name, l->fname)
+         && l->control_macro
+         && cpp_lookup((const char *)l->control_macro, -1, -1))
+        return 1;
+   return 0;
+}
+
+/* Return nonzero if the given FILENAME is an absolute pathname which
+ * designates a file within one of the known "system" include file
+ * directories.  We assume here that if the given FILENAME looks like
+ * it is the name of a file which resides either directly in a "system"
+ * include file directory, or within any subdirectory thereof, then the
+ * given file must be a "system" include file.  This function tells us
+ * if we should suppress pedantic errors/warnings for the given FILENAME.
+ * 
+ * The value is 2 if the file is a C-language system header file
+ * for which C++ should (on most systems) assume `extern "C"'.  */
+
+static int
+is_system_include(cpp_reader * pfile, char *filename)
+{
+   file_name_list     *searchptr;
+
+   for (searchptr = CPP_OPTIONS(pfile)->first_system_include; searchptr;
+       searchptr = searchptr->next)
+      if (searchptr->fname)
+       {
+          char               *sys_dir = searchptr->fname;
+          unsigned            length = strlen(sys_dir);
+
+          if (!strncmp(sys_dir, filename, length) && filename[length] == '/')
+            {
+               if (searchptr->c_system_include_path)
+                  return 2;
+               else
+                  return 1;
+            }
+       }
+   return 0;
+}
+
+/*
+ * Install a name in the assertion hash table.
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+static ASSERTION_HASHNODE *
+assertion_install(cpp_reader * pfile, const char *name, int len, int hash)
+{
+   ASSERTION_HASHNODE *hp;
+   int                 i, bucket;
+
+   i = sizeof(ASSERTION_HASHNODE) + len + 1;
+   hp = (ASSERTION_HASHNODE *) xmalloc(i);
+   bucket = hash;
+   hp->bucket_hdr = &pfile->assertion_hashtab[bucket];
+   hp->next = pfile->assertion_hashtab[bucket];
+   pfile->assertion_hashtab[bucket] = hp;
+   hp->prev = NULL;
+   if (hp->next)
+      hp->next->prev = hp;
+   hp->length = len;
+   hp->value = 0;
+   hp->name = ((char *)hp) + sizeof(ASSERTION_HASHNODE);
+   memcpy(hp->name, name, len);
+   hp->name[len] = 0;
+   return hp;
+}
+/*
+ * find the most recent hash node for name name (ending with first
+ * non-identifier char) installed by install
+ *
+ * If LEN is >= 0, it is the length of the name.
+ * Otherwise, compute the length by scanning the entire name.
+ *
+ * If HASH is >= 0, it is the precomputed hash code.
+ * Otherwise, compute the hash code.
+ */
+
+static ASSERTION_HASHNODE *
+assertion_lookup(cpp_reader * pfile, const char *name, int len, int hash)
+{
+   ASSERTION_HASHNODE *bucket;
+
+   bucket = pfile->assertion_hashtab[hash];
+   while (bucket)
+     {
+       if (bucket->length == len && strncmp(bucket->name, name, len) == 0)
+          return bucket;
+       bucket = bucket->next;
+     }
+   return NULL;
+}
+
+static void
+delete_assertion(ASSERTION_HASHNODE * hp)
+{
+   struct tokenlist_list *tail;
+
+   if (hp->prev)
+      hp->prev->next = hp->next;
+   if (hp->next)
+      hp->next->prev = hp->prev;
+
+   for (tail = hp->value; tail;)
+     {
+       struct tokenlist_list *next = tail->next;
+
+       free_token_list(tail->tokens);
+       free(tail);
+       tail = next;
+     }
+
+   /* make sure that the bucket chain header that
+    * the deleted guy was on points to the right thing afterwards. */
+   if (hp == *hp->bucket_hdr)
+      *hp->bucket_hdr = hp->next;
+
+   free(hp);
+}
+
+/* Convert a character string literal into a nul-terminated string.
+ * The input string is [IN ... LIMIT).
+ * The result is placed in RESULT.  RESULT can be the same as IN.
+ * The value returned in the end of the string written to RESULT,
+ * or NULL on error.  */
+
+static char        *
+convert_string(cpp_reader * pfile, char *result, char *in, char *limit,
+              int handle_escapes)
+{
+   unsigned char       c;
+
+   c = *in++;
+   if (c != '\"')
+      return NULL;
+   while (in < limit)
+     {
+       c = *in++;
+
+       switch (c)
+         {
+         case '\0':
+            return NULL;
+         case '\"':
+            limit = in;
+            break;
+         case '\\':
+            if (handle_escapes)
+              {
+                 char               *bpc = in;
+                 int                 i =
+                    (unsigned char)cpp_parse_escape(pfile, &bpc);
+
+                 in = bpc;
+                 if (i >= 0)
+                    *result++ = (unsigned char)c;
+                 break;
+              }
+            /* else fall through */
+         default:
+            *result++ = c;
+         }
+     }
+   *result = 0;
+   return result;
+}
+
+/*
+ * interpret #line command.  Remembers previously seen fnames
+ * in its very own hash table.
+ */
+#define FNAME_HASHSIZE 37
+
+static int
+do_line(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+       unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__)
+{
+   cpp_buffer         *ip = CPP_BUFFER(pfile);
+   int                 new_lineno;
+   long                old_written = CPP_WRITTEN(pfile);
+   enum file_change_code file_change = same_file;
+   enum cpp_token      token;
+
+   token = get_directive_token(pfile);
+
+   if (token != CPP_NUMBER || !isdigit(pfile->token_buffer[old_written]))
+     {
+       cpp_error(pfile, "invalid format `#line' command");
+       goto bad_line_directive;
+     }
+   /* The Newline at the end of this line remains to be processed.
+    * To put the next line at the specified line number,
+    * we must store a line number now that is one less.  */
+   new_lineno = atoi((char *)(pfile->token_buffer + old_written)) - 1;
+   CPP_SET_WRITTEN(pfile, old_written);
+
+   /* NEW_LINENO is one less than the actual line number here.  */
+   if (CPP_PEDANTIC(pfile) && new_lineno < 0)
+      cpp_pedwarn(pfile, "line number out of range in `#line' command");
+
+   token = get_directive_token(pfile);
+
+   if (token == CPP_STRING)
+     {
+       char               *fname = (char *)pfile->token_buffer + old_written;
+       char               *end_name;
+       static HASHNODE    *fname_table[FNAME_HASHSIZE];
+       HASHNODE           *hp, **hash_bucket;
+       unsigned char      *p;
+       long                num_start;
+       int                 fname_length;
+
+       /* Turn the file name, which is a character string literal,
+        * into a null-terminated string.  Do this in place.  */
+       end_name =
+          convert_string(pfile, fname, fname, (char *)CPP_PWRITTEN(pfile), 1);
+       if (!end_name)
+         {
+            cpp_error(pfile, "invalid format `#line' command");
+            goto bad_line_directive;
+         }
+       fname_length = end_name - fname;
+
+       num_start = CPP_WRITTEN(pfile);
+       token = get_directive_token(pfile);
+       if (token != CPP_VSPACE && token != CPP_EOF && token != CPP_POP)
+         {
+            p = pfile->token_buffer + num_start;
+            if (CPP_PEDANTIC(pfile))
+               cpp_pedwarn(pfile, "garbage at end of `#line' command");
+
+            if (token != CPP_NUMBER || *p < '0' || *p > '4' || p[1] != '\0')
+              {
+                 cpp_error(pfile, "invalid format `#line' command");
+                 goto bad_line_directive;
+              }
+            if (*p == '1')
+               file_change = enter_file;
+            else if (*p == 2)
+               file_change = leave_file;
+            else if (*p == 3)
+               ip->system_header_p = 1;
+            else               /* if (*p == 4) */
+               ip->system_header_p = 2;
+
+            CPP_SET_WRITTEN(pfile, num_start);
+            token = get_directive_token(pfile);
+            p = pfile->token_buffer + num_start;
+            if (token == CPP_NUMBER && p[1] == '\0'
+                && (*p == '3' || *p == '4'))
+              {
+                 ip->system_header_p = *p == 3 ? 1 : 2;
+                 token = get_directive_token(pfile);
+              }
+            if (token != CPP_VSPACE)
+              {
+                 cpp_error(pfile, "invalid format `#line' command");
+                 goto bad_line_directive;
+              }
+         }
+       hash_bucket = &fname_table[hashf(fname, fname_length, FNAME_HASHSIZE)];
+       for (hp = *hash_bucket; hp; hp = hp->next)
+          if (hp->length == fname_length &&
+              strncmp(hp->value.cpval, fname, fname_length) == 0)
+            {
+               ip->nominal_fname = hp->value.cpval;
+               break;
+            }
+       if (!hp)
+         {
+            /* Didn't find it; cons up a new one.  */
+            hp = (HASHNODE *) xcalloc(1, sizeof(HASHNODE) + fname_length + 1);
+            hp->next = *hash_bucket;
+            *hash_bucket = hp;
+
+            hp->length = fname_length;
+            ip->nominal_fname = hp->value.cpval =
+               ((char *)hp) + sizeof(HASHNODE);
+            memcpy(hp->value.cpval, fname, fname_length);
+         }
+     }
+   else if (token != CPP_VSPACE && token != CPP_EOF)
+     {
+       cpp_error(pfile, "invalid format `#line' command");
+       goto bad_line_directive;
+     }
+   ip->lineno = new_lineno;
+ bad_line_directive:
+   skip_rest_of_line(pfile);
+   CPP_SET_WRITTEN(pfile, old_written);
+   output_line_command(pfile, 0, file_change);
+   return 0;
+}
+
+/*
+ * remove the definition of a symbol from the symbol table.
+ * according to un*x /lib/cpp, it is not an error to undef
+ * something that has no definitions, so it isn't one here either.
+ */
+
+static int
+do_undef(cpp_reader * pfile, struct directive *keyword, unsigned char *buf,
+        unsigned char *limit)
+{
+   int                 sym_length;
+   HASHNODE           *hp;
+   unsigned char      *orig_buf = buf;
+
+   SKIP_WHITE_SPACE(buf);
+   sym_length = check_macro_name(pfile, buf, "macro");
+
+   while ((hp = cpp_lookup((const char *)buf, sym_length, -1)))
+     {
+       /* If we are generating additional info for debugging (with -g) we
+        * need to pass through all effective #undef commands.  */
+       if (CPP_OPTIONS(pfile)->debug_output && keyword)
+          pass_thru_directive((char *)orig_buf, (char *)limit, pfile, keyword);
+       if (hp->type != T_MACRO)
+          cpp_warning(pfile, "undefining `%s'", hp->name);
+       delete_macro(hp);
+     }
+
+   if (CPP_PEDANTIC(pfile))
+     {
+       buf += sym_length;
+       SKIP_WHITE_SPACE(buf);
+       if (buf != limit)
+          cpp_pedwarn(pfile, "garbage after `#undef' directive");
+     }
+   return 0;
+}
+
+/*
+ * Report an error detected by the program we are processing.
+ * Use the text of the line in the error message.
+ * (We use error because it prints the filename & line#.)
+ */
+
+static int
+do_error(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+        unsigned char *buf, unsigned char *limit)
+{
+   int                 length = limit - buf;
+   unsigned char      *copy = (unsigned char *)xmalloc(length + 1);
+
+   memcpy(copy, buf, length);
+   copy[length] = 0;
+   SKIP_WHITE_SPACE(copy);
+   cpp_error(pfile, "#error %s", copy);
+   return 0;
+}
+
+/*
+ * Report a warning detected by the program we are processing.
+ * Use the text of the line in the warning message, then continue.
+ * (We use error because it prints the filename & line#.)
+ */
+
+static int
+do_warning(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+          unsigned char *buf, unsigned char *limit)
+{
+   int                 length = limit - buf;
+   unsigned char      *copy = (unsigned char *)xmalloc(length + 1);
+
+   memcpy(copy, buf, length);
+   copy[length] = 0;
+   SKIP_WHITE_SPACE(copy);
+   cpp_warning(pfile, "#warning %s", copy);
+   return 0;
+}
+
+/* Remember the name of the current file being read from so that we can
+ * avoid ever including it again.  */
+
+static int
+do_once(cpp_reader * pfile)
+{
+   cpp_buffer         *ip = NULL;
+   file_name_list     *new_;
+
+   for (ip = CPP_BUFFER(pfile);; ip = CPP_PREV_BUFFER(ip))
+     {
+       if (!ip)
+          return 0;
+       if (ip->fname)
+          break;
+     }
+
+   new_ = (file_name_list *) xmalloc(sizeof(file_name_list));
+
+   new_->next = pfile->dont_repeat_files;
+   pfile->dont_repeat_files = new_;
+   new_->fname = savestring(ip->fname);
+   new_->control_macro = 0;
+   new_->got_name_map = 0;
+   new_->c_system_include_path = 0;
+
+   return 0;
+}
+
+/* #ident has already been copied to the output file, so just ignore it.  */
+
+static int
+do_ident(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+        unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
+{
+/*  long old_written = CPP_WRITTEN (pfile); */
+
+   /* Allow #ident in system headers, since that's not user's fault.  */
+   if (CPP_PEDANTIC(pfile) && !CPP_BUFFER(pfile)->system_header_p)
+      cpp_pedwarn(pfile, "ANSI C does not allow `#ident'");
+
+   /* Leave rest of line to be read by later calls to cpp_get_token. */
+
+   return 0;
+}
+
+/* #pragma and its argument line have already been copied to the output file.
+ * Just check for some recognized pragmas that need validation here.  */
+
+static int
+do_pragma(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+         unsigned char *buf, unsigned char *limit __UNUSED__)
+{
+   while (*buf == ' ' || *buf == '\t')
+      buf++;
+
+   if (!strncmp((const char *)buf, "once", 4))
+     {
+       /* Allow #pragma once in system headers, since that's not the user's
+        * fault.  */
+       if (!CPP_BUFFER(pfile)->system_header_p)
+          cpp_warning(pfile, "`#pragma once' is obsolete");
+       do_once(pfile);
+     }
+   if (!strncmp((const char *)buf, "implementation", 14))
+     {
+       /* Be quiet about `#pragma implementation' for a file only if it hasn't
+        * been included yet.  */
+       file_name_list     *ptr;
+       char               *p = (char *)buf + 14, *fname, *inc_fname;
+       int                 fname_len;
+
+       SKIP_WHITE_SPACE(p);
+       if (*p == '\n' || *p != '\"')
+          return 0;
+
+       fname = p + 1;
+       p = strchr(fname, '\"');
+       fname_len = (p) ? (int)(p - fname) : (int)strlen(fname);
+
+       for (ptr = pfile->all_include_files; ptr; ptr = ptr->next)
+         {
+            inc_fname = strrchr(ptr->fname, '/');
+            inc_fname = inc_fname ? inc_fname + 1 : (char *)ptr->fname;
+            if (inc_fname && !strncmp(inc_fname, fname, fname_len))
+               cpp_warning(pfile,
+                           "`#pragma implementation' for `%s' appears after file is included",
+                           fname);
+         }
+     }
+   return 0;
+}
+
+/* Just ignore #sccs, on systems where we define it at all.  */
+
+/*
+ * handle #if command by
+ *   1) inserting special `defined' keyword into the hash table
+ *      that gets turned into 0 or 1 by special_symbol (thus,
+ *      if the luser has a symbol called `defined' already, it won't
+ *      work inside the #if command)
+ *   2) rescan the input into a temporary output buffer
+ *   3) pass the output buffer to the yacc parser and collect a value
+ *   4) clean up the mess left from steps 1 and 2.
+ *   5) call conditional_skip to skip til the next #endif (etc.),
+ *      or not, depending on the value from step 3.
+ */
+
+static int
+do_if(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+      unsigned char *buf, unsigned char *limit)
+{
+   HOST_WIDE_INT       value = eval_if_expression(pfile, buf, limit - buf);
+
+   conditional_skip(pfile, value == 0, T_IF, NULL);
+   return 0;
+}
+
+/*
+ * handle a #elif directive by not changing  if_stack  either.
+ * see the comment above do_else.
+ */
+
+static int
+do_elif(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+       unsigned char *buf, unsigned char *limit)
+{
+   if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack)
+     {
+       cpp_error(pfile, "`#elif' not within a conditional");
+       return 0;
+     }
+   else
+     {
+       if (pfile->if_stack->type != T_IF && pfile->if_stack->type != T_ELIF)
+         {
+            cpp_error(pfile, "`#elif' after `#else'");
+            if (pfile->if_stack->fname
+                && CPP_BUFFER(pfile)->fname
+                && strcmp(pfile->if_stack->fname,
+                          CPP_BUFFER(pfile)->nominal_fname) != 0)
+               fprintf(stderr, ", file %s", pfile->if_stack->fname);
+            fprintf(stderr, ")\n");
+         }
+       pfile->if_stack->type = T_ELIF;
+     }
+
+   if (pfile->if_stack->if_succeeded)
+      skip_if_group(pfile, 0);
+   else
+     {
+       HOST_WIDE_INT       value = eval_if_expression(pfile, buf, limit - buf);
+
+       if (value == 0)
+          skip_if_group(pfile, 0);
+       else
+         {
+            ++pfile->if_stack->if_succeeded;   /* continue processing input */
+            output_line_command(pfile, 1, same_file);
+         }
+     }
+   return 0;
+}
+
+/*
+ * evaluate a #if expression in BUF, of length LENGTH,
+ * then parse the result as a C expression and return the value as an int.
+ */
+static              HOST_WIDE_INT
+eval_if_expression(cpp_reader * pfile, unsigned char *buf __UNUSED__,
+                  int length __UNUSED__)
+{
+   HASHNODE           *save_defined;
+   HOST_WIDE_INT       value;
+   long                old_written = CPP_WRITTEN(pfile);
+
+   save_defined = install("defined", -1, T_SPEC_DEFINED, 0, 0, -1);
+   pfile->pcp_inside_if = 1;
+
+   value = cpp_parse_expr(pfile);
+   pfile->pcp_inside_if = 0;
+   delete_macro(save_defined); /* clean up special symbol */
+
+   CPP_SET_WRITTEN(pfile, old_written);        /* Pop */
+
+   return value;
+}
+
+/*
+ * routine to handle ifdef/ifndef.  Try to look up the symbol,
+ * then do or don't skip to the #endif/#else/#elif depending
+ * on what directive is actually being processed.
+ */
+
+static int
+do_xifdef(cpp_reader * pfile, struct directive *keyword,
+         unsigned char *unused1 __UNUSED__, unsigned char *unused2 __UNUSED__)
+{
+   int                 skip;
+   cpp_buffer         *ip = CPP_BUFFER(pfile);
+   char               *ident;
+   int                 ident_length;
+   enum cpp_token      token;
+   int                 start_of_file = 0;
+   unsigned char      *control_macro = 0;
+   int                 old_written = CPP_WRITTEN(pfile);
+
+   /* Detect a #ifndef at start of file (not counting comments).  */
+   if (ip->fname != 0 && keyword->type == T_IFNDEF)
+      start_of_file = pfile->only_seen_white == 2;
+
+   pfile->no_macro_expand++;
+   token = get_directive_token(pfile);
+   pfile->no_macro_expand--;
+
+   ident = (char *)pfile->token_buffer + old_written;
+   ident_length = CPP_WRITTEN(pfile) - old_written;
+   CPP_SET_WRITTEN(pfile, old_written);        /* Pop */
+
+   if (token == CPP_VSPACE || token == CPP_POP || token == CPP_EOF)
+     {
+       skip = (keyword->type == T_IFDEF);
+       if (!CPP_TRADITIONAL(pfile))
+          cpp_pedwarn(pfile, "`#%s' with no argument", keyword->name);
+     }
+   else if (token == CPP_NAME)
+     {
+       HASHNODE           *hp = cpp_lookup(ident, ident_length, -1);
+
+       skip = (!hp) ^ (keyword->type == T_IFNDEF);
+       if (start_of_file && !skip)
+         {
+            control_macro = (unsigned char *)xmalloc(ident_length + 1);
+            memcpy(control_macro, ident, ident_length + 1);
+         }
+     }
+   else
+     {
+       skip = (keyword->type == T_IFDEF);
+       if (!CPP_TRADITIONAL(pfile))
+          cpp_error(pfile, "`#%s' with invalid argument", keyword->name);
+     }
+
+   if (!CPP_TRADITIONAL(pfile))
+     {
+       int                 c;
+
+       cpp_skip_hspace(pfile);
+       c = PEEKC();
+       if (c != EOF && c != '\n')
+          cpp_pedwarn(pfile, "garbage at end of `#%s' argument",
+                      keyword->name);
+     }
+   skip_rest_of_line(pfile);
+
+   conditional_skip(pfile, skip, T_IF, control_macro);
+   return 0;
+}
+
+/* Push TYPE on stack; then, if SKIP is nonzero, skip ahead.
+ * If this is a #ifndef starting at the beginning of a file,
+ * CONTROL_MACRO is the macro name tested by the #ifndef.
+ * Otherwise, CONTROL_MACRO is 0.  */
+
+static void
+conditional_skip(cpp_reader * pfile, int skip, enum node_type type,
+                unsigned char *control_macro)
+{
+   IF_STACK_FRAME     *temp;
+
+   temp = (IF_STACK_FRAME *) xcalloc(1, sizeof(IF_STACK_FRAME));
+   temp->fname = CPP_BUFFER(pfile)->nominal_fname;
+   temp->next = pfile->if_stack;
+   temp->control_macro = control_macro;
+   pfile->if_stack = temp;
+
+   pfile->if_stack->type = type;
+
+   if (skip != 0)
+     {
+       skip_if_group(pfile, 0);
+       return;
+     }
+   else
+     {
+       ++pfile->if_stack->if_succeeded;
+       output_line_command(pfile, 1, same_file);
+     }
+}
+
+/*
+ * skip to #endif, #else, or #elif.  adjust line numbers, etc.
+ * leaves input ptr at the sharp sign found.
+ * If ANY is nonzero, return at next directive of any sort.
+ */
+
+static void
+skip_if_group(cpp_reader * pfile, int any)
+{
+   int                 c;
+   struct directive   *kt;
+   IF_STACK_FRAME     *save_if_stack = pfile->if_stack;        /* don't pop past here */
+
+   int                 ident_length;
+   char               *ident;
+   struct parse_marker line_start_mark;
+
+   parse_set_mark(&line_start_mark, pfile);
+
+   if (CPP_OPTIONS(pfile)->output_conditionals)
+     {
+       static char         failed[] = "#failed\n";
+
+       CPP_PUTS(pfile, failed, sizeof(failed) - 1);
+       pfile->lineno++;
+       output_line_command(pfile, 1, same_file);
+     }
+ beg_of_line:
+   if (CPP_OPTIONS(pfile)->output_conditionals)
+     {
+       cpp_buffer         *pbuf = CPP_BUFFER(pfile);
+       unsigned char      *start_line = pbuf->buf + line_start_mark.position;
+
+       CPP_PUTS(pfile, start_line, pbuf->cur - start_line);
+     }
+   parse_move_mark(&line_start_mark, pfile);
+   if (!CPP_TRADITIONAL(pfile))
+      cpp_skip_hspace(pfile);
+   c = GETC();
+   if (c == '#')
+     {
+       int                 old_written = CPP_WRITTEN(pfile);
+
+       cpp_skip_hspace(pfile);
+
+       parse_name(pfile, GETC());
+       ident_length = CPP_WRITTEN(pfile) - old_written;
+       ident = (char *)pfile->token_buffer + old_written;
+       pfile->limit = (unsigned char *)ident;
+
+       for (kt = directive_table; kt->length >= 0; kt++)
+         {
+            IF_STACK_FRAME     *temp;
+
+            if (ident_length == kt->length
+                && strncmp(ident, kt->name, kt->length) == 0)
+              {
+                 /* If we are asked to return on next directive, do so now.  */
+                 if (any)
+                    goto done;
+
+                 switch (kt->type)
+                   {
+                   case T_IF:
+                   case T_IFDEF:
+                   case T_IFNDEF:
+                      temp
+                         =
+                         (IF_STACK_FRAME *) xcalloc(1, sizeof(IF_STACK_FRAME));
+                      temp->next = pfile->if_stack;
+                      pfile->if_stack = temp;
+                      temp->fname = CPP_BUFFER(pfile)->nominal_fname;
+                      temp->type = kt->type;
+                      break;
+                   case T_ELSE:
+                   case T_ENDIF:
+                      if (CPP_PEDANTIC(pfile)
+                          && pfile->if_stack != save_if_stack)
+                         validate_else(pfile,
+                                       kt->type ==
+                                       T_ELSE ? "#else" : "#endif");
+                   case T_ELIF:
+                      if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack)
+                        {
+                           cpp_error(pfile,
+                                     "`#%s' not within a conditional",
+                                     kt->name);
+                           break;
+                        }
+                      else if (pfile->if_stack == save_if_stack)
+                         goto done;    /* found what we came for */
+
+                      if (kt->type != T_ENDIF)
+                        {
+                           if (pfile->if_stack->type == T_ELSE)
+                              cpp_error(pfile,
+                                        "`#else' or `#elif' after `#else'");
+                           pfile->if_stack->type = kt->type;
+                           break;
+                        }
+                      temp = pfile->if_stack;
+                      pfile->if_stack = temp->next;
+                      free(temp);
+                      break;
+                   default:;
+                   }
+                 break;
+              }
+            /* Don't let erroneous code go by.  */
+            if (kt->length < 0 && !CPP_OPTIONS(pfile)->lang_asm
+                && CPP_PEDANTIC(pfile))
+               cpp_pedwarn(pfile, "invalid preprocessor directive name");
+         }
+       c = GETC();
+     }
+   /* We're in the middle of a line.  Skip the rest of it. */
+   for (;;)
+     {
+       switch (c)
+         {
+            long                old;
+
+         case EOF:
+            goto done;
+         case '/':             /* possible comment */
+            c = skip_comment(pfile, NULL);
+            if (c == EOF)
+               goto done;
+            break;
+         case '\"':
+         case '\'':
+            FORWARD(-1);
+            old = CPP_WRITTEN(pfile);
+            cpp_get_token(pfile);
+            CPP_SET_WRITTEN(pfile, old);
+            break;
+         case '\\':
+            /* Char after backslash loses its special meaning.  */
+            if (PEEKC() == '\n')
+               FORWARD(1);
+            break;
+         case '\n':
+            goto beg_of_line;
+            break;
+         }
+       c = GETC();
+     }
+ done:
+   if (CPP_OPTIONS(pfile)->output_conditionals)
+     {
+       static char         end_failed[] = "#endfailed\n";
+
+       CPP_PUTS(pfile, end_failed, sizeof(end_failed) - 1);
+       pfile->lineno++;
+     }
+   pfile->only_seen_white = 1;
+   parse_goto_mark(&line_start_mark, pfile);
+   parse_clear_mark(&line_start_mark);
+}
+
+/*
+ * handle a #else directive.  Do this by just continuing processing
+ * without changing  if_stack ;  this is so that the error message
+ * for missing #endif's etc. will point to the original #if.  It
+ * is possible that something different would be better.
+ */
+
+static int
+do_else(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+       unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
+{
+   cpp_buffer         *ip = CPP_BUFFER(pfile);
+
+   if (CPP_PEDANTIC(pfile))
+      validate_else(pfile, "#else");
+   skip_rest_of_line(pfile);
+
+   if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack)
+     {
+       cpp_error(pfile, "`#else' not within a conditional");
+       return 0;
+     }
+   else
+     {
+       /* #ifndef can't have its special treatment for containing the whole file
+        * if it has a #else clause.  */
+       pfile->if_stack->control_macro = 0;
+
+       if (pfile->if_stack->type != T_IF && pfile->if_stack->type != T_ELIF)
+         {
+            cpp_error(pfile, "`#else' after `#else'");
+            fprintf(stderr, " (matches line %d", pfile->if_stack->lineno);
+            if (strcmp(pfile->if_stack->fname, ip->nominal_fname) != 0)
+               fprintf(stderr, ", file %s", pfile->if_stack->fname);
+            fprintf(stderr, ")\n");
+         }
+       pfile->if_stack->type = T_ELSE;
+     }
+
+   if (pfile->if_stack->if_succeeded)
+      skip_if_group(pfile, 0);
+   else
+     {
+       ++pfile->if_stack->if_succeeded;        /* continue processing input */
+       output_line_command(pfile, 1, same_file);
+     }
+   return 0;
+}
+
+/*
+ * unstack after #endif command
+ */
+
+static int
+do_endif(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+        unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
+{
+   if (CPP_PEDANTIC(pfile))
+      validate_else(pfile, "#endif");
+   skip_rest_of_line(pfile);
+
+   if (pfile->if_stack == CPP_BUFFER(pfile)->if_stack)
+     {
+       cpp_error(pfile, "unbalanced `#endif'");
+     }
+   else
+     {
+       IF_STACK_FRAME     *temp = pfile->if_stack;
+
+       pfile->if_stack = temp->next;
+       if (temp->control_macro)
+         {
+            /* This #endif matched a #ifndef at the start of the file.
+             * See if it is at the end of the file.  */
+            struct parse_marker start_mark;
+            int                 c;
+
+            parse_set_mark(&start_mark, pfile);
+
+            for (;;)
+              {
+                 cpp_skip_hspace(pfile);
+                 c = GETC();
+                 if (c != '\n')
+                    break;
+              }
+            parse_goto_mark(&start_mark, pfile);
+            parse_clear_mark(&start_mark);
+
+            if (c == EOF)
+              {
+                 /* If we get here, this #endif ends a #ifndef
+                  * that contains all of the file (aside from whitespace).
+                  * Arrange not to include the file again
+                  * if the macro that was tested is defined.
+                  * 
+                  * Do not do this for the top-level file in a -include or any
+                  * file in a -imacros.  */
+                 {
+                    file_name_list     *ifile = pfile->all_include_files;
+
+                    for (; ifile; ifile = ifile->next)
+                      {
+                         if (!strcmp(ifile->fname, CPP_BUFFER(pfile)->fname))
+                           {
+                              ifile->control_macro = temp->control_macro;
+                              break;
+                           }
+                      }
+                 }
+              }
+         }
+       free(temp);
+       output_line_command(pfile, 1, same_file);
+     }
+   return 0;
+}
+
+/* When an #else or #endif is found while skipping failed conditional,
+ * if -pedantic was specified, this is called to warn about text after
+ * the command name.  P points to the first char after the command name.  */
+
+static void
+validate_else(cpp_reader * pfile, const char *directive)
+{
+   int                 c;
+
+   cpp_skip_hspace(pfile);
+   c = PEEKC();
+   if (c != EOF && c != '\n')
+      cpp_pedwarn(pfile,
+                 "text following `%s' violates ANSI standard", directive);
+}
+
+/* Get the next token, and add it to the text in pfile->token_buffer.
+ * Return the kind of token we got. */
+
+enum cpp_token
+cpp_get_token(cpp_reader * pfile)
+{
+   int                 c, c2, c3;
+   long                old_written = 0;
+   long                start_line, start_column;
+   enum cpp_token      token;
+   struct cpp_options *opts = CPP_OPTIONS(pfile);
+
+   CPP_BUFFER(pfile)->prev = CPP_BUFFER(pfile)->cur;
+ get_next:
+   c = GETC();
+   if (c == EOF)
+     {
+      handle_eof:
+       if (CPP_BUFFER(pfile)->seen_eof)
+         {
+            if (cpp_pop_buffer(pfile) != CPP_NULL_BUFFER(pfile))
+               goto get_next;
+            else
+               return CPP_EOF;
+         }
+       else
+         {
+            cpp_buffer         *next_buf = CPP_PREV_BUFFER(CPP_BUFFER(pfile));
+
+            CPP_BUFFER(pfile)->seen_eof = 1;
+            if (CPP_BUFFER(pfile)->nominal_fname && next_buf)
+              {
+                 /* We're about to return from an #include file.
+                  * Emit #line information now (as part of the CPP_POP) result.
+                  * But the #line refers to the file we will pop to. */
+                 cpp_buffer         *cur_buffer = CPP_BUFFER(pfile);
+
+                 CPP_BUFFER(pfile) = next_buf;
+                 pfile->input_stack_listing_current = 0;
+                 output_line_command(pfile, 0, leave_file);
+                 CPP_BUFFER(pfile) = cur_buffer;
+              }
+            return CPP_POP;
+         }
+     }
+   else
+     {
+       switch (c)
+         {
+            long                newlines;
+            struct parse_marker start_mark;
+
+         case '/':
+            if (PEEKC() == '=')
+               goto op2;
+            if (opts->put_out_comments)
+               parse_set_mark(&start_mark, pfile);
+            newlines = 0;
+            cpp_buf_line_and_col(cpp_file_buffer(pfile),
+                                 &start_line, &start_column);
+            c = skip_comment(pfile, &newlines);
+            if (opts->put_out_comments && (c == '/' || c == EOF))
+               parse_clear_mark(&start_mark);
+            if (c == '/')
+               goto randomchar;
+            if (c == EOF)
+              {
+                 cpp_error_with_line(pfile, start_line, start_column,
+                                     "unterminated comment");
+                 goto handle_eof;
+              }
+            c = '/';           /* Initial letter of comment. */
+          return_comment:
+            /* Comments are equivalent to spaces.
+             * For -traditional, a comment is equivalent to nothing.  */
+            if (opts->put_out_comments)
+              {
+                 cpp_buffer         *pbuf = CPP_BUFFER(pfile);
+                 unsigned char      *start = pbuf->buf + start_mark.position;
+                 int                 len = pbuf->cur - start;
+
+                 CPP_RESERVE(pfile, 1 + len);
+                 CPP_PUTC_Q(pfile, c);
+                 CPP_PUTS_Q(pfile, start, len);
+                 pfile->lineno += newlines;
+                 parse_clear_mark(&start_mark);
+                 return CPP_COMMENT;
+              }
+            else if (CPP_TRADITIONAL(pfile))
+              {
+                 return CPP_COMMENT;
+              }
+            else
+              {
+                 CPP_RESERVE(pfile, 1);
+                 CPP_PUTC_Q(pfile, ' ');
+                 return CPP_HSPACE;
+              }
+
+         case '#':
+            if (!pfile->only_seen_white)
+               goto randomchar;
+            if (handle_directive(pfile))
+               return CPP_DIRECTIVE;
+            pfile->only_seen_white = 0;
+            return CPP_OTHER;
+
+         case '\"':
+         case '\'':
+            /* A single quoted string is treated like a double -- some
+             * programs (e.g., troff) are perverse this way */
+            cpp_buf_line_and_col(cpp_file_buffer(pfile),
+                                 &start_line, &start_column);
+            old_written = CPP_WRITTEN(pfile);
+          string:
+            CPP_PUTC(pfile, c);
+            while (1)
+              {
+                 int                 cc = GETC();
+
+                 if (cc == EOF)
+                   {
+                      if (CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile)))
+                        {
+                           /* try harder: this string crosses a macro expansion
+                            * boundary.  This can happen naturally if -traditional.
+                            * Otherwise, only -D can make a macro with an unmatched
+                            * quote.  */
+                           cpp_buffer         *next_buf
+                              = CPP_PREV_BUFFER(CPP_BUFFER(pfile));
+
+                           (*CPP_BUFFER(pfile)->cleanup)
+                              (CPP_BUFFER(pfile), pfile);
+                           CPP_BUFFER(pfile) = next_buf;
+                           continue;
+                        }
+                      if (!CPP_TRADITIONAL(pfile))
+                        {
+                           cpp_error_with_line(pfile, start_line, start_column,
+                                               "unterminated string or character constant");
+                           if (pfile->multiline_string_line != start_line
+                               && pfile->multiline_string_line != 0)
+                              cpp_error_with_line(pfile,
+                                                  pfile->multiline_string_line,
+                                                  -1,
+                                                  "possible real start of unterminated constant");
+                           pfile->multiline_string_line = 0;
+                        }
+                      break;
+                   }
+                 CPP_PUTC(pfile, cc);
+                 switch (cc)
+                   {
+                   case '\n':
+                      /* Traditionally, end of line ends a string constant with
+                       * no error.  So exit the loop and record the new line.  */
+                      if (CPP_TRADITIONAL(pfile))
+                         goto while2end;
+                      if (c == '\'')
+                        {
+                           cpp_error_with_line(pfile, start_line, start_column,
+                                               "unterminated character constant");
+                           goto while2end;
+                        }
+                      if (CPP_PEDANTIC(pfile)
+                          && pfile->multiline_string_line == 0)
+                        {
+                           cpp_pedwarn_with_line(pfile, start_line,
+                                                 start_column,
+                                                 "string constant runs past end of line");
+                        }
+                      if (pfile->multiline_string_line == 0)
+                         pfile->multiline_string_line = start_line;
+                      break;
+
+                   case '\\':
+                      cc = GETC();
+                      if (cc == '\n')
+                        {
+                           /* Backslash newline is replaced by nothing at all. */
+                           CPP_ADJUST_WRITTEN(pfile, -1);
+                           pfile->lineno++;
+                        }
+                      else
+                        {
+                           /* ANSI stupidly requires that in \\ the second \
+                            * is *not* prevented from combining with a newline.  */
+                           NEWLINE_FIX1(cc);
+                           if (cc != EOF)
+                              CPP_PUTC(pfile, cc);
+                        }
+                      break;
+
+                   case '\"':
+                   case '\'':
+                      if (cc == c)
+                         goto while2end;
+                      break;
+                   }
+              }
+          while2end:
+            pfile->lineno += count_newlines(pfile->token_buffer + old_written,
+                                            CPP_PWRITTEN(pfile));
+            pfile->only_seen_white = 0;
+            return c == '\'' ? CPP_CHAR : CPP_STRING;
+
+         case '$':
+            if (!opts->dollars_in_ident)
+               goto randomchar;
+            goto letter;
+
+         case ':':
+            if (opts->cplusplus && PEEKC() == ':')
+               goto op2;
+            goto randomchar;
+
+         case '&':
+         case '+':
+         case '|':
+            NEWLINE_FIX;
+            c2 = PEEKC();
+            if (c2 == c || c2 == '=')
+               goto op2;
+            goto randomchar;
+
+         case '*':
+         case '!':
+         case '%':
+         case '=':
+         case '^':
+            NEWLINE_FIX;
+            if (PEEKC() == '=')
+               goto op2;
+            goto randomchar;
+
+         case '-':
+            NEWLINE_FIX;
+            c2 = PEEKC();
+            if (c2 == '-' && opts->chill)
+              {
+                 /* Chill style comment */
+                 if (opts->put_out_comments)
+                    parse_set_mark(&start_mark, pfile);
+                 FORWARD(1);   /* Skip second '-'. */
+                 for (;;)
+                   {
+                      c = GETC();
+                      if (c == EOF)
+                         break;
+                      if (c == '\n')
+                        {
+                           /* Don't consider final '\n' to be part of comment. */
+                           FORWARD(-1);
+                           break;
+                        }
+                   }
+                 c = '-';
+                 goto return_comment;
+              }
+            if (c2 == '-' || c2 == '=' || c2 == '>')
+               goto op2;
+            goto randomchar;
+
+         case '<':
+            if (pfile->parsing_include_directive)
+              {
+                 for (;;)
+                   {
+                      CPP_PUTC(pfile, c);
+                      if (c == '>')
+                         break;
+                      c = GETC();
+                      NEWLINE_FIX1(c);
+                      if (c == '\n' || c == EOF)
+                        {
+                           cpp_error(pfile,
+                                     "missing '>' in `#include <FILENAME>'");
+                           break;
+                        }
+                   }
+                 return CPP_STRING;
+              }
+            /* else fall through */
+         case '>':
+            NEWLINE_FIX;
+            c2 = PEEKC();
+            if (c2 == '=')
+               goto op2;
+            if (c2 != c)
+               goto randomchar;
+            FORWARD(1);
+            CPP_RESERVE(pfile, 4);
+            CPP_PUTC(pfile, c);
+            CPP_PUTC(pfile, c2);
+            NEWLINE_FIX;
+            c3 = PEEKC();
+            if (c3 == '=')
+               CPP_PUTC_Q(pfile, GETC());
+            CPP_NUL_TERMINATE_Q(pfile);
+            pfile->only_seen_white = 0;
+            return CPP_OTHER;
+
+         case '@':
+            if (CPP_BUFFER(pfile)->has_escapes)
+              {
+                 c = GETC();
+                 if (c == '-')
+                   {
+                      if (pfile->output_escapes)
+                         CPP_PUTS(pfile, "@-", 2);
+                      parse_name(pfile, GETC());
+                      return CPP_NAME;
+                   }
+                 else if (is_space[c])
+                   {
+                      CPP_RESERVE(pfile, 2);
+                      if (pfile->output_escapes)
+                         CPP_PUTC_Q(pfile, '@');
+                      CPP_PUTC_Q(pfile, c);
+                      return CPP_HSPACE;
+                   }
+              }
+            if (pfile->output_escapes)
+              {
+                 CPP_PUTS(pfile, "@@", 2);
+                 return CPP_OTHER;
+              }
+            goto randomchar;
+
+         case '.':
+            NEWLINE_FIX;
+            c2 = PEEKC();
+            if (isdigit(c2))
+              {
+                 CPP_RESERVE(pfile, 2);
+                 CPP_PUTC_Q(pfile, '.');
+                 c = GETC();
+                 goto number;
+              }
+            /* FIXME - misses the case "..\\\n." */
+            if (c2 == '.' && PEEKN(1) == '.')
+              {
+                 CPP_RESERVE(pfile, 4);
+                 CPP_PUTC_Q(pfile, '.');
+                 CPP_PUTC_Q(pfile, '.');
+                 CPP_PUTC_Q(pfile, '.');
+                 FORWARD(2);
+                 CPP_NUL_TERMINATE_Q(pfile);
+                 pfile->only_seen_white = 0;
+                 return CPP_3DOTS;
+              }
+            goto randomchar;
+
+          op2:
+            token = CPP_OTHER;
+            pfile->only_seen_white = 0;
+          op2any:
+            CPP_RESERVE(pfile, 3);
+            CPP_PUTC_Q(pfile, c);
+            CPP_PUTC_Q(pfile, GETC());
+            CPP_NUL_TERMINATE_Q(pfile);
+            return token;
+
+         case 'L':
+            NEWLINE_FIX;
+            c2 = PEEKC();
+            if ((c2 == '\'' || c2 == '\"') && !CPP_TRADITIONAL(pfile))
+              {
+                 CPP_PUTC(pfile, c);
+                 c = GETC();
+                 goto string;
+              }
+            goto letter;
+
+         case '0':
+         case '1':
+         case '2':
+         case '3':
+         case '4':
+         case '5':
+         case '6':
+         case '7':
+         case '8':
+         case '9':
+          number:
+            c2 = '.';
+            for (;;)
+              {
+                 CPP_RESERVE(pfile, 2);
+                 CPP_PUTC_Q(pfile, c);
+                 NEWLINE_FIX;
+                 c = PEEKC();
+                 if (c == EOF)
+                    break;
+                 if (!is_idchar[c] && c != '.'
+                     && ((c2 != 'e' && c2 != 'E') || (c != '+' && c != '-')))
+                    break;
+                 FORWARD(1);
+                 c2 = c;
+              }
+            CPP_NUL_TERMINATE_Q(pfile);
+            pfile->only_seen_white = 0;
+            return CPP_NUMBER;
+         case 'b':
+         case 'c':
+         case 'd':
+         case 'h':
+         case 'o':
+         case 'B':
+         case 'C':
+         case 'D':
+         case 'H':
+         case 'O':
+            if (opts->chill && PEEKC() == '\'')
+              {
+                 pfile->only_seen_white = 0;
+                 CPP_RESERVE(pfile, 2);
+                 CPP_PUTC_Q(pfile, c);
+                 CPP_PUTC_Q(pfile, '\'');
+                 FORWARD(1);
+                 for (;;)
+                   {
+                      c = GETC();
+                      if (c == EOF)
+                         goto chill_number_eof;
+                      if (!is_idchar[c])
+                        {
+                           if (c == '\\' && PEEKC() == '\n')
+                             {
+                                FORWARD(2);
+                                continue;
+                             }
+                           break;
+                        }
+                      CPP_PUTC(pfile, c);
+                   }
+                 if (c == '\'')
+                   {
+                      CPP_RESERVE(pfile, 2);
+                      CPP_PUTC_Q(pfile, c);
+                      CPP_NUL_TERMINATE_Q(pfile);
+                      return CPP_STRING;
+                   }
+                 else
+                   {
+                      FORWARD(-1);
+                    chill_number_eof:
+                      CPP_NUL_TERMINATE(pfile);
+                      return CPP_NUMBER;
+                   }
+              }
+            else
+               goto letter;
+         case '_':
+         case 'a':
+         case 'e':
+         case 'f':
+         case 'g':
+         case 'i':
+         case 'j':
+         case 'k':
+         case 'l':
+         case 'm':
+         case 'n':
+         case 'p':
+         case 'q':
+         case 'r':
+         case 's':
+         case 't':
+         case 'u':
+         case 'v':
+         case 'w':
+         case 'x':
+         case 'y':
+         case 'z':
+         case 'A':
+         case 'E':
+         case 'F':
+         case 'G':
+         case 'I':
+         case 'J':
+         case 'K':
+         case 'M':
+         case 'N':
+         case 'P':
+         case 'Q':
+         case 'R':
+         case 'S':
+         case 'T':
+         case 'U':
+         case 'V':
+         case 'W':
+         case 'X':
+         case 'Y':
+         case 'Z':
+          letter:
+            {
+               HASHNODE           *hp;
+               char               *ident;
+               int                 before_name_written = CPP_WRITTEN(pfile);
+               int                 ident_len;
+
+               parse_name(pfile, c);
+               pfile->only_seen_white = 0;
+               if (pfile->no_macro_expand)
+                  return CPP_NAME;
+               ident = (char *)pfile->token_buffer + before_name_written;
+               ident_len = CPP_PWRITTEN(pfile) - (unsigned char *)ident;
+               hp = cpp_lookup(ident, ident_len, -1);
+               if (!hp)
+                  return CPP_NAME;
+               if (hp->type == T_DISABLED)
+                 {
+                    if (pfile->output_escapes)
+                      {        /* Return "@-IDENT", followed by '\0'. */
+                         int                 i;
+
+                         CPP_RESERVE(pfile, 3);
+                         ident =
+                            (char *)pfile->token_buffer + before_name_written;
+                         CPP_ADJUST_WRITTEN(pfile, 2);
+                         for (i = ident_len; i >= 0; i--)
+                            ident[i + 2] = ident[i];
+                         ident[0] = '@';
+                         ident[1] = '-';
+                      }
+                    return CPP_NAME;
+                 }
+               /* If macro wants an arglist, verify that a '(' follows.
+                * first skip all whitespace, copying it to the output
+                * after the macro name.  Then, if there is no '(',
+                * decide this is not a macro call and leave things that way.  */
+               if (hp->type == T_MACRO && hp->value.defn->nargs >= 0)
+                 {
+                    struct parse_marker macro_mark;
+                    int                 is_macro_call;
+
+                    while (CPP_IS_MACRO_BUFFER(CPP_BUFFER(pfile)))
+                      {
+                         cpp_buffer         *next_buf;
+
+                         cpp_skip_hspace(pfile);
+                         if (PEEKC() != EOF)
+                            break;
+                         next_buf = CPP_PREV_BUFFER(CPP_BUFFER(pfile));
+                         (*CPP_BUFFER(pfile)->cleanup) (CPP_BUFFER(pfile),
+                                                        pfile);
+                         CPP_BUFFER(pfile) = next_buf;
+                      }
+                    parse_set_mark(&macro_mark, pfile);
+                    for (;;)
+                      {
+                         cpp_skip_hspace(pfile);
+                         c = PEEKC();
+                         is_macro_call = c == '(';
+                         if (c != '\n')
+                            break;
+                         FORWARD(1);
+                      }
+                    if (!is_macro_call)
+                       parse_goto_mark(&macro_mark, pfile);
+                    parse_clear_mark(&macro_mark);
+                    if (!is_macro_call)
+                       return CPP_NAME;
+                 }
+               /* This is now known to be a macro call. */
+
+               /* it might not actually be a macro.  */
+               if (hp->type != T_MACRO)
+                 {
+                    int                 xbuf_len;
+                    unsigned char      *xbuf;
+
+                    CPP_SET_WRITTEN(pfile, before_name_written);
+                    special_symbol(hp, pfile);
+                    xbuf_len = CPP_WRITTEN(pfile) - before_name_written;
+                    xbuf = (unsigned char *)xmalloc(xbuf_len + 1);
+                    CPP_SET_WRITTEN(pfile, before_name_written);
+                    memcpy(xbuf, CPP_PWRITTEN(pfile), xbuf_len + 1);
+                    push_macro_expansion(pfile, xbuf, xbuf_len, hp);
+                 }
+               else
+                 {
+                    /* Expand the macro, reading arguments as needed,
+                     * and push the expansion on the input stack.  */
+                    macroexpand(pfile, hp);
+                    CPP_SET_WRITTEN(pfile, before_name_written);
+                 }
+
+               /* An extra "@ " is added to the end of a macro expansion
+                * to prevent accidental token pasting.  We prefer to avoid
+                * unneeded extra spaces (for the sake of cpp-using tools like
+                * imake).  Here we remove the space if it is safe to do so. */
+               if (pfile->buffer->rlimit - pfile->buffer->cur >= 3
+                   && pfile->buffer->rlimit[-2] == '@'
+                   && pfile->buffer->rlimit[-1] == ' ')
+                 {
+                    int                 c1 = pfile->buffer->rlimit[-3];
+
+                    c2 = CPP_BUF_PEEK(CPP_PREV_BUFFER(CPP_BUFFER(pfile)));
+
+                    if (c2 == EOF || !unsafe_chars(c1, c2))
+                       pfile->buffer->rlimit -= 2;
+                 }
+            }
+            goto get_next;
+
+         case ' ':
+         case '\t':
+         case '\v':
+         case '\r':
+            for (;;)
+              {
+                 CPP_PUTC(pfile, c);
+                 c = PEEKC();
+                 if (c == EOF || !is_hor_space[c])
+                    break;
+                 FORWARD(1);
+              }
+            return CPP_HSPACE;
+
+         case '\\':
+            c2 = PEEKC();
+            if (c2 != '\n')
+               goto randomchar;
+            token = CPP_HSPACE;
+            goto op2any;
+
+         case '\n':
+            CPP_PUTC(pfile, c);
+            if (pfile->only_seen_white == 0)
+               pfile->only_seen_white = 1;
+            pfile->lineno++;
+            output_line_command(pfile, 1, same_file);
+            return CPP_VSPACE;
+
+         case '(':
+            token = CPP_LPAREN;
+            goto char1;
+         case ')':
+            token = CPP_RPAREN;
+            goto char1;
+         case '{':
+            token = CPP_LBRACE;
+            goto char1;
+         case '}':
+            token = CPP_RBRACE;
+            goto char1;
+         case ',':
+            token = CPP_COMMA;
+            goto char1;
+         case ';':
+            token = CPP_SEMICOLON;
+            goto char1;
+
+          randomchar:
+         default:
+            token = CPP_OTHER;
+          char1:
+            pfile->only_seen_white = 0;
+            CPP_PUTC(pfile, c);
+            return token;
+         }
+     }
+}
+
+#if 0                          /* Unused */
+/* Like cpp_get_token, but skip spaces and comments. */
+enum cpp_token
+cpp_get_non_space_token(cpp_reader * pfile)
+{
+   int                 old_written = CPP_WRITTEN(pfile);
+
+   for (;;)
+     {
+       enum cpp_token      token = cpp_get_token(pfile);
+
+       if (token != CPP_COMMENT && token != CPP_POP
+           && token != CPP_HSPACE && token != CPP_VSPACE)
+          return token;
+       CPP_SET_WRITTEN(pfile, old_written);
+     }
+}
+#endif
+
+/* Parse an identifier starting with C. */
+
+int
+parse_name(cpp_reader * pfile, int c)
+{
+   for (;;)
+     {
+       if (!is_idchar[c])
+         {
+            if (c == '\\' && PEEKC() == '\n')
+              {
+                 FORWARD(2);
+                 continue;
+              }
+            FORWARD(-1);
+            break;
+         }
+       CPP_RESERVE(pfile, 2);  /* One more for final NUL. */
+       CPP_PUTC_Q(pfile, c);
+       c = GETC();
+       if (c == EOF)
+          break;
+     }
+   CPP_NUL_TERMINATE_Q(pfile);
+   return 1;
+}
+
+/* Maintain and search list of included files, for #import.  */
+
+/* Hash a file name for import_hash_table.  */
+
+static int
+import_hash(char *f)
+{
+   int                 val = 0;
+
+   while (*f)
+      val += *f++;
+   return (val % IMPORT_HASH_SIZE);
+}
+
+/* Search for file FILENAME in import_hash_table.
+ * Return -2 if found, either a matching name or a matching inode.
+ * Otherwise, open the file and return a file descriptor if successful
+ * or -1 if unsuccessful.  */
+
+static int
+lookup_import(cpp_reader * pfile, char *filename, file_name_list * searchptr)
+{
+   struct import_file *i;
+   int                 h;
+   int                 hashval;
+   struct stat         sb;
+   int                 fd;
+
+   hashval = import_hash(filename);
+
+   /* Attempt to find file in list of already included files */
+   i = pfile->import_hash_table[hashval];
+
+   while (i)
+     {
+       if (!strcmp(filename, i->name))
+          return -2;           /* return found */
+       i = i->next;
+     }
+   /* Open it and try a match on inode/dev */
+   fd = open_include_file(pfile, filename, searchptr);
+   if (fd < 0)
+      return fd;
+   fstat(fd, &sb);
+   for (h = 0; h < IMPORT_HASH_SIZE; h++)
+     {
+       i = pfile->import_hash_table[h];
+       while (i)
+         {
+            /* Compare the inode and the device.
+             * Supposedly on some systems the inode is not a scalar.  */
+            if (!memcmp
+                ((char *)&i->inode, (char *)&sb.st_ino, sizeof(sb.st_ino))
+                && i->dev == sb.st_dev)
+              {
+                 close(fd);
+                 return -2;    /* return found */
+              }
+            i = i->next;
+         }
+     }
+   return fd;                  /* Not found, return open file */
+}
+
+/* Add the file FNAME, open on descriptor FD, to import_hash_table.  */
+
+static void
+add_import(cpp_reader * pfile, int fd, char *fname)
+{
+   struct import_file *i;
+   int                 hashval;
+   struct stat         sb;
+
+   hashval = import_hash(fname);
+   fstat(fd, &sb);
+   i = (struct import_file *)xmalloc(sizeof(struct import_file));
+
+   i->name = (char *)xmalloc(strlen(fname) + 1);
+   strcpy(i->name, fname);
+   memcpy((char *)&i->inode, (char *)&sb.st_ino, sizeof(sb.st_ino));
+   i->dev = sb.st_dev;
+   i->next = pfile->import_hash_table[hashval];
+   pfile->import_hash_table[hashval] = i;
+}
+
+/* The file_name_map structure holds a mapping of file names for a
+ * particular directory.  This mapping is read from the file named
+ * FILE_NAME_MAP_FILE in that directory.  Such a file can be used to
+ * map filenames on a file system with severe filename restrictions,
+ * such as DOS.  The format of the file name map file is just a series
+ * of lines with two tokens on each line.  The first token is the name
+ * to map, and the second token is the actual name to use.  */
+
+struct file_name_map {
+   struct file_name_map *map_next;
+   char               *map_from;
+   char               *map_to;
+};
+
+#if USE_FILE_NAME_MAPS
+
+#define FILE_NAME_MAP_FILE "header.gcc"
+
+/* Read a space delimited string of unlimited length from a stdio
+ * file.  */
+
+static char        *
+read_filename_string(int ch, FILE * f)
+{
+   char               *alloc, *set;
+   int                 len;
+
+   len = 20;
+   set = alloc = (char *)xmalloc(len + 1);
+   if (!is_space[ch])
+     {
+       *set++ = ch;
+       while ((ch = getc(f)) != EOF && !is_space[ch])
+         {
+            if (set - alloc == len)
+              {
+                 len *= 2;
+                 alloc = (char *)xrealloc(alloc, len + 1);
+                 set = alloc + len / 2;
+              }
+            *set++ = ch;
+         }
+     }
+   *set = '\0';
+   ungetc(ch, f);
+   return alloc;
+}
+
+/* This structure holds a linked list of file name maps, one per directory. */
+struct file_name_map_list {
+   struct file_name_map_list *map_list_next;
+   char               *map_list_name;
+   struct file_name_map *map_list_map;
+};
+
+/* Read the file name map file for DIRNAME.  */
+
+static struct file_name_map *
+read_name_map(cpp_reader * pfile, const char *dirname)
+{
+   struct file_name_map_list *map_list_ptr;
+   char               *name;
+   FILE               *f;
+
+   for (map_list_ptr = CPP_OPTIONS(pfile)->map_list; map_list_ptr;
+       map_list_ptr = map_list_ptr->map_list_next)
+      if (!strcmp(map_list_ptr->map_list_name, dirname))
+        return map_list_ptr->map_list_map;
+
+   map_list_ptr =
+      ((struct file_name_map_list *)xmalloc(sizeof(struct file_name_map_list)));
+
+   map_list_ptr->map_list_name = savestring(dirname);
+   map_list_ptr->map_list_map = NULL;
+
+   name = (char *)alloca(strlen(dirname) + strlen(FILE_NAME_MAP_FILE) + 2);
+   strcpy(name, dirname);
+   if (*dirname)
+      strcat(name, "/");
+   strcat(name, FILE_NAME_MAP_FILE);
+#ifndef __EMX__
+   f = fopen(name, "r");
+#else
+   f = fopen(name, "rt");
+#endif
+   if (!f)
+      map_list_ptr->map_list_map = NULL;
+   else
+     {
+       int                 ch;
+       int                 dirlen = strlen(dirname);
+
+       while ((ch = getc(f)) != EOF)
+         {
+            char               *from, *to;
+            struct file_name_map *ptr;
+
+            if (is_space[ch])
+               continue;
+            from = read_filename_string(ch, f);
+            while ((ch = getc(f)) != EOF && is_hor_space[ch]);
+            to = read_filename_string(ch, f);
+
+            ptr =
+               ((struct file_name_map *)xmalloc(sizeof(struct file_name_map)));
+
+            ptr->map_from = from;
+
+            /* Make the real filename absolute.  */
+            if (*to == '/')
+               ptr->map_to = to;
+            else
+              {
+                 ptr->map_to = (char *)xmalloc(dirlen + strlen(to) + 2);
+                 strcpy(ptr->map_to, dirname);
+                 ptr->map_to[dirlen] = '/';
+                 strcpy(ptr->map_to + dirlen + 1, to);
+                 free(to);
+              }
+
+            ptr->map_next = map_list_ptr->map_list_map;
+            map_list_ptr->map_list_map = ptr;
+
+            while ((ch = getc(f)) != '\n')
+               if (ch == EOF)
+                  break;
+         }
+       fclose(f);
+     }
+
+   map_list_ptr->map_list_next = CPP_OPTIONS(pfile)->map_list;
+   CPP_OPTIONS(pfile)->map_list = map_list_ptr;
+
+   return map_list_ptr->map_list_map;
+}
+
+/* Try to open include file FILENAME.  SEARCHPTR is the directory
+ * being tried from the include file search path.  This function maps
+ * filenames on file systems based on information read by
+ * read_name_map.  */
+
+static int
+open_include_file(cpp_reader * pfile, char *filename,
+                 file_name_list * searchptr)
+{
+   struct file_name_map *map;
+   const char         *from;
+   const char         *p, *dir;
+
+   if (searchptr && !searchptr->got_name_map)
+     {
+       searchptr->name_map = read_name_map(pfile,
+                                           searchptr->fname
+                                           ? searchptr->fname : ".");
+       searchptr->got_name_map = 1;
+     }
+   /* First check the mapping for the directory we are using.  */
+   if (searchptr && searchptr->name_map)
+     {
+       from = filename;
+       if (searchptr->fname)
+          from += strlen(searchptr->fname) + 1;
+       for (map = searchptr->name_map; map; map = map->map_next)
+         {
+            if (!strcmp(map->map_from, from))
+              {
+                 /* Found a match.  */
+                 return open(map->map_to, O_RDONLY, 0666);
+              }
+         }
+     }
+   /* Try to find a mapping file for the particular directory we are
+    * looking in.  Thus #include <sys/types.h> will look up sys/types.h
+    * in /usr/include/header.gcc and look up types.h in
+    * /usr/include/sys/header.gcc.  */
+   p = strrchr(filename, '/');
+   if (!p)
+      p = filename;
+   if (searchptr
+       && searchptr->fname
+       && strlen(searchptr->fname) == (unsigned)(p - filename)
+       && !strncmp(searchptr->fname, filename, p - filename))
+     {
+       /* FILENAME is in SEARCHPTR, which we've already checked.  */
+       return open(filename, O_RDONLY, 0666);
+     }
+   if (p == filename)
+     {
+       dir = ".";
+       from = filename;
+     }
+   else
+     {
+       char               *s;
+
+       s = (char *)alloca(p - filename + 1);
+       memcpy(s, filename, p - filename);
+       s[p - filename] = '\0';
+       from = p + 1;
+       dir = s;
+     }
+   for (map = read_name_map(pfile, dir); map; map = map->map_next)
+      if (!strcmp(map->map_from, from))
+        return open(map->map_to, O_RDONLY, 0666);
+
+   return open(filename, O_RDONLY, 0666);
+}
+
+#else
+
+static int
+open_include_file(cpp_reader * pfile __UNUSED__, char *filename,
+                 file_name_list * searchptr __UNUSED__)
+{
+   return open(filename, O_RDONLY, 0666);
+}
+
+#endif /* USE_FILE_NAME_MAPS */
+
+/* Process the contents of include file FNAME, already open on descriptor F,
+ * with output to OP.
+ * SYSTEM_HEADER_P is 1 if this file resides in any one of the known
+ * "system" include directories (as decided by the `is_system_include'
+ * function above).
+ * DIRPTR is the link in the dir path through which this file was found,
+ * or 0 if the file name was absolute or via the current directory.
+ * Return 1 on success, 0 on failure.
+ * 
+ * The caller is responsible for the cpp_push_buffer.  */
+
+static int
+finclude(cpp_reader * pfile, int f, const char *fname, int system_header_p,
+        file_name_list * dirptr)
+{
+   int                 st_mode;
+   long                st_size;
+   long                i;
+   int                 length;
+   cpp_buffer         *fp;     /* For input stack frame */
+
+   if (file_size_and_mode(f, &st_mode, &st_size) < 0)
+     {
+       cpp_perror_with_name(pfile, fname);
+       close(f);
+       cpp_pop_buffer(pfile);
+       return 0;
+     }
+   fp = CPP_BUFFER(pfile);
+   fp->nominal_fname = fp->fname = fname;
+   fp->dir = dirptr;
+   fp->system_header_p = system_header_p;
+   fp->lineno = 1;
+   fp->colno = 1;
+   fp->cleanup = file_cleanup;
+
+   if (S_ISREG(st_mode))
+     {
+       fp->buf = (unsigned char *)xmalloc(st_size + 2);
+       fp->alimit = fp->buf + st_size + 2;
+       fp->cur = fp->buf;
+
+       /* Read the file contents, knowing that st_size is an upper bound
+        * on the number of bytes we can read.  */
+       length = safe_read(f, (char *)fp->buf, st_size);
+       fp->rlimit = fp->buf + length;
+       if (length < 0)
+          goto nope;
+     }
+   else if (S_ISDIR(st_mode))
+     {
+       cpp_error(pfile, "directory `%s' specified in #include", fname);
+       close(f);
+       return 0;
+     }
+   else
+     {
+       /* Cannot count its file size before reading.
+        * First read the entire file into heap and
+        * copy them into buffer on stack. */
+
+       int                 bsize = 2000;
+
+       st_size = 0;
+       fp->buf = (unsigned char *)xmalloc(bsize + 2);
+
+       for (;;)
+         {
+            i = safe_read(f, (char *)(fp->buf + st_size), bsize - st_size);
+            if (i < 0)
+               goto nope;      /* error! */
+            st_size += i;
+            if (st_size != bsize)
+               break;          /* End of file */
+            bsize *= 2;
+            fp->buf = (unsigned char *)xrealloc(fp->buf, bsize + 2);
+         }
+       length = st_size;
+     }
+
+   if ((length > 0 && fp->buf[length - 1] != '\n')
+       /* Backslash-newline at end is not good enough.  */
+       || (length > 1 && fp->buf[length - 2] == '\\'))
+     {
+       fp->buf[length++] = '\n';
+     }
+   fp->buf[length] = '\0';
+   fp->rlimit = fp->buf + length;
+
+   /* Close descriptor now, so nesting does not use lots of descriptors.  */
+   close(f);
+
+   /* Must do this before calling trigraph_pcp, so that the correct file name
+    * will be printed in warning messages.  */
+
+   pfile->input_stack_listing_current = 0;
+
+   return 1;
+
+ nope:
+
+   cpp_perror_with_name(pfile, fname);
+   close(f);
+   free(fp->buf);
+   return 1;
+}
+
+int
+push_parse_file(cpp_reader * pfile, const char *fname)
+{
+   struct cpp_options *opts = CPP_OPTIONS(pfile);
+   struct cpp_pending *pend;
+   char               *p;
+   int                 f;
+   cpp_buffer         *fp;
+
+   /* The code looks at the defaults through this pointer, rather than through
+    * the constant structure above.  This pointer gets changed if an environment
+    * variable specifies other defaults.  */
+   struct default_include *include_defaults = include_defaults_array;
+
+   /* Add dirs from CPATH after dirs from -I.  */
+   /* There seems to be confusion about what CPATH should do,
+    * so for the moment it is not documented.  */
+   /* Some people say that CPATH should replace the standard include dirs,
+    * but that seems pointless: it comes before them, so it overrides them
+    * anyway.  */
+   p = (char *)getenv("CPATH");
+   if (p && !opts->no_standard_includes)
+      path_include(pfile, p);
+
+   /* Now that dollars_in_ident is known, initialize is_idchar.  */
+   initialize_char_syntax(opts);
+
+   /* Do partial setup of input buffer for the sake of generating
+    * early #line directives (when -g is in effect).  */
+   fp = cpp_push_buffer(pfile, NULL, 0);
+   if (!opts->in_fname)
+      opts->in_fname = "";
+   fp->nominal_fname = fp->fname = opts->in_fname;
+   fp->lineno = 0;
+
+   /* Install __LINE__, etc.  Must follow initialize_char_syntax
+    * and option processing.  */
+   initialize_builtins(pfile);
+
+   /* Do standard #defines and assertions
+    * that identify system and machine type.  */
+
+   if (!opts->inhibit_predefs)
+     {
+       p = (char *)alloca(strlen(predefs) + 1);
+
+       strcpy(p, predefs);
+       while (*p)
+         {
+            char               *q;
+
+            while (*p == ' ' || *p == '\t')
+               p++;
+            /* Handle -D options.  */
+            if (p[0] == '-' && p[1] == 'D')
+              {
+                 q = &p[2];
+                 while (*p && *p != ' ' && *p != '\t')
+                    p++;
+                 if (*p != 0)
+                    *p++ = 0;
+                 if (opts->debug_output)
+                    output_line_command(pfile, 0, same_file);
+                 cpp_define(pfile, (unsigned char *)q);
+                 while (*p == ' ' || *p == '\t')
+                    p++;
+              }
+            else if (p[0] == '-' && p[1] == 'A')
+              {
+                 /* Handle -A options (assertions).  */
+                 char               *assertion;
+                 char               *past_name;
+                 char               *value;
+                 char               *past_value;
+                 char               *termination;
+                 int                 save_char;
+
+                 assertion = &p[2];
+                 past_name = assertion;
+                 /* Locate end of name.  */
+                 while (*past_name && *past_name != ' '
+                        && *past_name != '\t' && *past_name != '(')
+                    past_name++;
+                 /* Locate `(' at start of value.  */
+                 value = past_name;
+                 while (*value && (*value == ' ' || *value == '\t'))
+                    value++;
+                 if (*value++ != '(')
+                    abort();
+                 while (*value && (*value == ' ' || *value == '\t'))
+                    value++;
+                 past_value = value;
+                 /* Locate end of value.  */
+                 while (*past_value && *past_value != ' '
+                        && *past_value != '\t' && *past_value != ')')
+                    past_value++;
+                 termination = past_value;
+                 while (*termination
+                        && (*termination == ' ' || *termination == '\t'))
+                    termination++;
+                 if (*termination++ != ')')
+                    abort();
+                 if (*termination && *termination != ' '
+                     && *termination != '\t')
+                    abort();
+                 /* Temporarily null-terminate the value.  */
+                 save_char = *termination;
+                 *termination = '\0';
+                 /* Install the assertion.  */
+                 make_assertion(pfile, "-A", assertion);
+                 *termination = (char)save_char;
+                 p = termination;
+                 while (*p == ' ' || *p == '\t')
+                    p++;
+              }
+            else
+              {
+                 abort();
+              }
+         }
+     }
+   /* Now handle the command line options.  */
+
+   /* Do -U's, -D's and -A's in the order they were seen.  */
+   /* First reverse the list. */
+   opts->pending = nreverse_pending(opts->pending);
+
+   for (pend = opts->pending; pend; pend = pend->next)
+     {
+       if (pend->cmd && pend->cmd[0] == '-')
+         {
+            switch (pend->cmd[1])
+              {
+              case 'U':
+                 if (opts->debug_output)
+                    output_line_command(pfile, 0, same_file);
+                 do_undef(pfile, NULL, (unsigned char *)pend->arg,
+                          (unsigned char *)pend->arg + strlen(pend->arg));
+                 break;
+              case 'D':
+                 if (opts->debug_output)
+                    output_line_command(pfile, 0, same_file);
+                 cpp_define(pfile, (unsigned char *)pend->arg);
+                 break;
+              case 'A':
+                 make_assertion(pfile, "-A", pend->arg);
+                 break;
+              }
+         }
+     }
+
+   opts->done_initializing = 1;
+
+   {                           /* read the appropriate environment variable and if it exists
+                                * replace include_defaults with the listed path. */
+      char               *epath = 0;
+
+      switch ((opts->objc << 1) + opts->cplusplus)
+       {
+       case 0:
+          epath = getenv("C_INCLUDE_PATH");
+          break;
+       case 1:
+          epath = getenv("CPLUS_INCLUDE_PATH");
+          break;
+       case 2:
+          epath = getenv("OBJC_INCLUDE_PATH");
+          break;
+       case 3:
+          epath = getenv("OBJCPLUS_INCLUDE_PATH");
+          break;
+       }
+      /* If the environment var for this language is set,
+       * add to the default list of include directories.  */
+      if (epath)
+       {
+          char               *nstore = (char *)alloca(strlen(epath) + 2);
+          int                 num_dirs;
+          char               *startp, *endp;
+
+          for (num_dirs = 1, startp = epath; *startp; startp++)
+             if (*startp == PATH_SEPARATOR)
+                num_dirs++;
+          include_defaults
+             = (struct default_include *)xmalloc((num_dirs
+                                                  *
+                                                  sizeof(struct
+                                                         default_include)) +
+                                                 sizeof
+                                                 (include_defaults_array));
+
+          startp = endp = epath;
+          num_dirs = 0;
+          while (1)
+            {
+               /* Handle cases like c:/usr/lib:d:/gcc/lib */
+               if ((*endp == PATH_SEPARATOR) || *endp == 0)
+                 {
+                    strncpy(nstore, startp, endp - startp);
+                    if (endp == startp)
+                       strcpy(nstore, ".");
+                    else
+                       nstore[endp - startp] = '\0';
+
+                    include_defaults[num_dirs].fname = savestring(nstore);
+                    include_defaults[num_dirs].cplusplus = opts->cplusplus;
+                    include_defaults[num_dirs].cxx_aware = 1;
+                    num_dirs++;
+                    if (*endp == '\0')
+                       break;
+                    endp = startp = endp + 1;
+                 }
+               else
+                  endp++;
+            }
+          /* Put the usual defaults back in at the end.  */
+          memcpy((char *)&include_defaults[num_dirs],
+                 (char *)include_defaults_array,
+                 sizeof(include_defaults_array));
+       }
+   }
+
+   append_include_chain(pfile, opts->before_system, opts->last_before_system);
+   opts->first_system_include = opts->before_system;
+
+   /* Unless -fnostdinc,
+    * tack on the standard include file dirs to the specified list */
+   if (!opts->no_standard_includes)
+     {
+       struct default_include *di = include_defaults;
+       char               *specd_prefix = opts->include_prefix;
+       char               *default_prefix = savestring(GCC_INCLUDE_DIR);
+       int                 default_len = 0;
+
+       /* Remove the `include' from /usr/local/lib/gcc.../include.  */
+       if (!strcmp(default_prefix + strlen(default_prefix) - 8, "/include"))
+         {
+            default_len = strlen(default_prefix) - 7;
+            default_prefix[default_len] = 0;
+         }
+       /* Search "translated" versions of GNU directories.
+        * These have /usr/local/lib/gcc... replaced by specd_prefix.  */
+       if (specd_prefix && default_len != 0)
+          for (di = include_defaults; di->fname; di++)
+            {
+               /* Some standard dirs are only for C++.  */
+               if (!di->cplusplus
+                   || (opts->cplusplus
+                       && !opts->no_standard_cplusplus_includes))
+                 {
+                    /* Does this dir start with the prefix?  */
+                    if (!strncmp(di->fname, default_prefix, default_len))
+                      {
+                         /* Yes; change prefix and add to search list.  */
+                         file_name_list     *new_
+                            =
+                            (file_name_list *) xmalloc(sizeof(file_name_list));
+                         int                 this_len =
+                            strlen(specd_prefix) + strlen(di->fname) -
+                            default_len;
+                         char               *str =
+                            (char *)xmalloc(this_len + 1);
+
+                         strcpy(str, specd_prefix);
+                         strcat(str, di->fname + default_len);
+                         new_->fname = str;
+                         new_->control_macro = 0;
+                         new_->c_system_include_path = !di->cxx_aware;
+                         new_->got_name_map = 0;
+                         append_include_chain(pfile, new_, new_);
+                         if (!opts->first_system_include)
+                            opts->first_system_include = new_;
+                      }
+                 }
+            }
+       /* Search ordinary names for GNU include directories.  */
+       for (di = include_defaults; di->fname; di++)
+         {
+            /* Some standard dirs are only for C++.  */
+            if (!di->cplusplus
+                || (opts->cplusplus && !opts->no_standard_cplusplus_includes))
+              {
+                 file_name_list     *new_
+                    = (file_name_list *) xmalloc(sizeof(file_name_list));
+
+                 new_->control_macro = 0;
+                 new_->c_system_include_path = !di->cxx_aware;
+                 new_->fname = (char *)di->fname;
+                 new_->got_name_map = 0;
+                 append_include_chain(pfile, new_, new_);
+                 if (!opts->first_system_include)
+                    opts->first_system_include = new_;
+              }
+         }
+     }
+   /* Tack the after_include chain at the end of the include chain.  */
+   append_include_chain(pfile, opts->after_include, opts->last_after_include);
+   if (!opts->first_system_include)
+      opts->first_system_include = opts->after_include;
+
+   /* With -v, print the list of dirs to search.  */
+   if (opts->verbose)
+     {
+       file_name_list     *fl;
+
+       fprintf(stderr, "#include \"...\" search starts here:\n");
+       for (fl = opts->include; fl; fl = fl->next)
+         {
+            if (fl == opts->first_bracket_include)
+               fprintf(stderr, "#include <...> search starts here:\n");
+            fprintf(stderr, " %s\n", fl->fname);
+         }
+       fprintf(stderr, "End of search list.\n");
+     }
+   /* Scan the -imacros files before the main input.
+    * Much like #including them, but with no_output set
+    * so that only their macro definitions matter.  */
+
+   opts->no_output++;
+   pfile->no_record_file++;
+   for (pend = opts->pending; pend; pend = pend->next)
+     {
+       if (pend->cmd && strcmp(pend->cmd, "-imacros") == 0)
+         {
+            int                 fd = open(pend->arg, O_RDONLY, 0666);
+
+            if (fd < 0)
+              {
+                 cpp_perror_with_name(pfile, pend->arg);
+                 return FATAL_EXIT_CODE;
+              }
+            cpp_push_buffer(pfile, NULL, 0);
+            finclude(pfile, fd, pend->arg, 0, NULL);
+            cpp_scan_buffer(pfile);
+         }
+     }
+   opts->no_output--;
+   pfile->no_record_file--;
+
+   /* Copy the entire contents of the main input file into
+    * the stacked input buffer previously allocated for it.  */
+   if (!fname || *fname == 0)
+     {
+       fname = "";
+       f = 0;
+     }
+   else if ((f = open(fname, O_RDONLY, 0666)) < 0)
+      cpp_pfatal_with_name(pfile, fname);
+
+   /* -MG doesn't select the form of output and must be specified with one of
+    * -M or -MM.  -MG doesn't make sense with -MD or -MMD since they don't
+    * inhibit compilation.  */
+   if (opts->print_deps_missing_files
+       && (opts->print_deps == 0 || !opts->no_output))
+      cpp_fatal("-MG must be specified with one of -M or -MM");
+
+   /* Either of two environment variables can specify output of deps.
+    * Its value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET",
+    * where OUTPUT_FILE is the file to write deps info to
+    * and DEPS_TARGET is the target to mention in the deps.  */
+
+   if (opts->print_deps == 0
+       && (getenv("SUNPRO_DEPENDENCIES") != 0
+          || getenv("DEPENDENCIES_OUTPUT") != 0))
+     {
+       char               *spec = getenv("DEPENDENCIES_OUTPUT");
+       char               *s;
+       char               *output_file;
+
+       if (!spec)
+         {
+            spec = getenv("SUNPRO_DEPENDENCIES");
+            opts->print_deps = 2;
+         }
+       else
+          opts->print_deps = 1;
+
+       s = spec;
+       /* Find the space before the DEPS_TARGET, if there is one.  */
+       /* This should use index.  (mrs) */
+       while (*s != 0 && *s != ' ')
+          s++;
+       if (*s != 0)
+         {
+            opts->deps_target = s + 1;
+            output_file = (char *)xmalloc(s - spec + 1);
+            memcpy(output_file, spec, s - spec);
+            output_file[s - spec] = 0;
+         }
+       else
+         {
+            opts->deps_target = 0;
+            output_file = spec;
+         }
+
+       opts->deps_file = output_file;
+       opts->print_deps_append = 1;
+     }
+   /* For -M, print the expected object file name
+    * as the target of this Make-rule.  */
+   if (opts->print_deps)
+     {
+       pfile->deps_allocated_size = 200;
+       pfile->deps_buffer = (char *)xmalloc(pfile->deps_allocated_size);
+       pfile->deps_buffer[0] = 0;
+       pfile->deps_size = 0;
+       pfile->deps_column = 0;
+
+       if (opts->deps_target)
+          deps_output(pfile, opts->deps_target, ':');
+       else if (*opts->in_fname == 0)
+          deps_output(pfile, "-", ':');
+       else
+         {
+            char               *q;
+            int                 len;
+
+            /* Discard all directory prefixes from filename.  */
+            if ((q = (char *)strrchr(opts->in_fname, '/'))
+#ifdef DIR_SEPARATOR
+                && (q = strrchr(opts->in_fname, DIR_SEPARATOR))
+#endif
+               )
+               ++q;
+            else
+               q = (char *)opts->in_fname;
+
+            /* Copy remainder to mungable area.  */
+            p = (char *)alloca(strlen(q) + 8);
+            strcpy(p, q);
+
+            /* Output P, but remove known suffixes.  */
+            len = strlen(p);
+            q = p + len;
+            if (len >= 2 && p[len - 2] == '.' && strchr("cCsSm", p[len - 1]))
+               q = p + (len - 2);
+            else if (len >= 3
+                     && p[len - 3] == '.'
+                     && p[len - 2] == 'c' && p[len - 1] == 'c')
+               q = p + (len - 3);
+            else if (len >= 4
+                     && p[len - 4] == '.'
+                     && p[len - 3] == 'c'
+                     && p[len - 2] == 'x' && p[len - 1] == 'x')
+               q = p + (len - 4);
+            else if (len >= 4
+                     && p[len - 4] == '.'
+                     && p[len - 3] == 'c'
+                     && p[len - 2] == 'p' && p[len - 1] == 'p')
+               q = p + (len - 4);
+
+            /* Supply our own suffix.  */
+            strcpy(q, ".o");
+
+            deps_output(pfile, p, ':');
+            deps_output(pfile, opts->in_fname, ' ');
+         }
+     }
+
+   /* Scan the -include files before the main input.
+    * We push these in reverse order, so that the first one is handled first.  */
+
+   pfile->no_record_file++;
+   opts->pending = nreverse_pending(opts->pending);
+   for (pend = opts->pending; pend; pend = pend->next)
+     {
+       if (pend->cmd && strcmp(pend->cmd, "-include") == 0)
+         {
+            int                 fd = open(pend->arg, O_RDONLY, 0666);
+
+            if (fd < 0)
+              {
+                 cpp_perror_with_name(pfile, pend->arg);
+                 return FATAL_EXIT_CODE;
+              }
+            cpp_push_buffer(pfile, NULL, 0);
+            finclude(pfile, fd, pend->arg, 0, NULL);
+         }
+     }
+   pfile->no_record_file--;
+
+   /* Free the pending list. */
+   for (pend = opts->pending; pend;)
+     {
+       struct cpp_pending *next = pend->next;
+
+       free(pend);
+       pend = next;
+     }
+   opts->pending = NULL;
+
+   if (finclude(pfile, f, fname, 0, NULL))
+      output_line_command(pfile, 0, same_file);
+   return SUCCESS_EXIT_CODE;
+}
+
+void
+init_parse_file(cpp_reader * pfile)
+{
+   memset((char *)pfile, 0, sizeof(cpp_reader));
+   pfile->get_token = cpp_get_token;
+
+   pfile->token_buffer_size = 200;
+   pfile->token_buffer = (unsigned char *)xmalloc(pfile->token_buffer_size);
+   CPP_SET_WRITTEN(pfile, 0);
+
+   pfile->system_include_depth = 0;
+   pfile->dont_repeat_files = 0;
+   pfile->all_include_files = 0;
+   pfile->max_include_len = 0;
+   pfile->timebuf = NULL;
+   pfile->only_seen_white = 1;
+   pfile->buffer = CPP_NULL_BUFFER(pfile);
+}
+
+static struct cpp_pending *
+nreverse_pending(struct cpp_pending *list)
+{
+   struct cpp_pending *prev = 0, *next, *pend;
+
+   for (pend = list; pend; pend = next)
+     {
+       next = pend->next;
+       pend->next = prev;
+       prev = pend;
+     }
+   return prev;
+}
+
+static void
+push_pending(cpp_reader * pfile, const char *cmd, const char *arg)
+{
+   struct cpp_pending *pend
+      = (struct cpp_pending *)xmalloc(sizeof(struct cpp_pending));
+
+   pend->cmd = cmd;
+   pend->arg = arg;
+   pend->next = CPP_OPTIONS(pfile)->pending;
+   CPP_OPTIONS(pfile)->pending = pend;
+}
+
+/* Handle command-line options in (argc, argv).
+ * Can be called multiple times, to handle multiple sets of options.
+ * Returns if an unrecognized option is seen.
+ * Returns number of handled arguments.  */
+
+int
+cpp_handle_options(cpp_reader * pfile, int argc, char **argv)
+{
+   int                 i;
+   struct cpp_options *opts = CPP_OPTIONS(pfile);
+
+   for (i = 0; i < argc; i++)
+     {
+       if (argv[i][0] != '-')
+         {
+            if (opts->out_fname)
+               cpp_fatal("Usage: %s [switches] input output", argv[0]);
+            else if (opts->in_fname)
+               opts->out_fname = argv[i];
+            else
+               opts->in_fname = argv[i];
+         }
+       else
+         {
+            switch (argv[i][1])
+              {
+
+              case 'i':
+                 if (!strcmp(argv[i], "-include")
+                     || !strcmp(argv[i], "-imacros"))
+                   {
+                      if (i + 1 == argc)
+                         cpp_fatal("Filename missing after `%s' option",
+                                   argv[i]);
+                      else
+                         push_pending(pfile, argv[i], argv[i + 1]), i++;
+                   }
+                 if (!strcmp(argv[i], "-iprefix"))
+                   {
+                      if (i + 1 == argc)
+                         cpp_fatal("Filename missing after `-iprefix' option");
+                      else
+                         opts->include_prefix = argv[++i];
+                   }
+                 if (!strcmp(argv[i], "-ifoutput"))
+                   {
+                      opts->output_conditionals = 1;
+                   }
+                 if (!strcmp(argv[i], "-isystem"))
+                   {
+                      file_name_list     *dirtmp;
+
+                      if (i + 1 == argc)
+                         cpp_fatal("Filename missing after `-isystem' option");
+
+                      dirtmp =
+                         (file_name_list *) xmalloc(sizeof(file_name_list));
+
+                      dirtmp->next = 0;
+                      dirtmp->control_macro = 0;
+                      dirtmp->c_system_include_path = 1;
+                      dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1]) + 1);
+                      strcpy(dirtmp->fname, argv[++i]);
+                      dirtmp->got_name_map = 0;
+
+                      if (!opts->before_system)
+                         opts->before_system = dirtmp;
+                      else
+                         opts->last_before_system->next = dirtmp;
+                      opts->last_before_system = dirtmp;       /* Tail follows the last one */
+                   }
+                 /* Add directory to end of path for includes,
+                  * with the default prefix at the front of its name.  */
+                 if (!strcmp(argv[i], "-iwithprefix"))
+                   {
+                      file_name_list     *dirtmp;
+                      char               *prefix;
+
+                      if (opts->include_prefix)
+                         prefix = opts->include_prefix;
+                      else
+                        {
+                           prefix = savestring(GCC_INCLUDE_DIR);
+                           /* Remove the `include' from /usr/local/lib/gcc.../include.  */
+                           if (!strcmp
+                               (prefix + strlen(prefix) - 8, "/include"))
+                              prefix[strlen(prefix) - 7] = 0;
+                        }
+
+                      dirtmp =
+                         (file_name_list *) xmalloc(sizeof(file_name_list));
+
+                      dirtmp->next = 0;        /* New one goes on the end */
+                      dirtmp->control_macro = 0;
+                      dirtmp->c_system_include_path = 0;
+                      if (i + 1 == argc)
+                         cpp_fatal
+                            ("Directory name missing after `-iwithprefix' option");
+
+                      dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1])
+                                                      + strlen(prefix) + 1);
+                      strcpy(dirtmp->fname, prefix);
+                      strcat(dirtmp->fname, argv[++i]);
+                      dirtmp->got_name_map = 0;
+
+                      if (!opts->after_include)
+                         opts->after_include = dirtmp;
+                      else
+                         opts->last_after_include->next = dirtmp;
+                      opts->last_after_include = dirtmp;       /* Tail follows the last one */
+                   }
+                 /* Add directory to main path for includes,
+                  * with the default prefix at the front of its name.  */
+                 if (!strcmp(argv[i], "-iwithprefixbefore"))
+                   {
+                      file_name_list     *dirtmp;
+                      char               *prefix;
+
+                      if (opts->include_prefix)
+                         prefix = opts->include_prefix;
+                      else
+                        {
+                           prefix = savestring(GCC_INCLUDE_DIR);
+                           /* Remove the `include' from /usr/local/lib/gcc.../include.  */
+                           if (!strcmp
+                               (prefix + strlen(prefix) - 8, "/include"))
+                              prefix[strlen(prefix) - 7] = 0;
+                        }
+
+                      dirtmp =
+                         (file_name_list *) xmalloc(sizeof(file_name_list));
+
+                      dirtmp->next = 0;        /* New one goes on the end */
+                      dirtmp->control_macro = 0;
+                      dirtmp->c_system_include_path = 0;
+                      if (i + 1 == argc)
+                         cpp_fatal
+                            ("Directory name missing after `-iwithprefixbefore' option");
+
+                      dirtmp->fname = (char *)xmalloc(strlen(argv[i + 1])
+                                                      + strlen(prefix) + 1);
+                      strcpy(dirtmp->fname, prefix);
+                      strcat(dirtmp->fname, argv[++i]);
+                      dirtmp->got_name_map = 0;
+
+                      append_include_chain(pfile, dirtmp, dirtmp);
+                   }
+                 /* Add directory to end of path for includes.  */
+                 if (!strcmp(argv[i], "-idirafter"))
+                   {
+                      file_name_list     *dirtmp;
+
+                      dirtmp =
+                         (file_name_list *) xmalloc(sizeof(file_name_list));
+
+                      dirtmp->next = 0;        /* New one goes on the end */
+                      dirtmp->control_macro = 0;
+                      dirtmp->c_system_include_path = 0;
+                      if (i + 1 == argc)
+                         cpp_fatal
+                            ("Directory name missing after `-idirafter' option");
+                      else
+                         dirtmp->fname = argv[++i];
+                      dirtmp->got_name_map = 0;
+
+                      if (!opts->after_include)
+                         opts->after_include = dirtmp;
+                      else
+                         opts->last_after_include->next = dirtmp;
+                      opts->last_after_include = dirtmp;       /* Tail follows the last one */
+                   }
+                 break;
+
+              case 'o':
+                 if (opts->out_fname)
+                    cpp_fatal("Output filename specified twice");
+                 if (i + 1 == argc)
+                    cpp_fatal("Filename missing after -o option");
+                 opts->out_fname = argv[++i];
+                 if (!strcmp(opts->out_fname, "-"))
+                    opts->out_fname = "";
+                 break;
+
+              case 'p':
+                 if (!strcmp(argv[i], "-pedantic"))
+                    CPP_PEDANTIC(pfile) = 1;
+                 else if (!strcmp(argv[i], "-pedantic-errors"))
+                   {
+                      CPP_PEDANTIC(pfile) = 1;
+                      opts->pedantic_errors = 1;
+                   }
+                 break;
+
+              case 't':
+                 if (!strcmp(argv[i], "-traditional"))
+                   {
+                      opts->traditional = 1;
+                      if (opts->dollars_in_ident > 0)
+                         opts->dollars_in_ident = 1;
+                   }
+                 else if (!strcmp(argv[i], "-trigraphs"))
+                   {
+                      if (!opts->chill)
+                         opts->no_trigraphs = 0;
+                   }
+                 break;
+
+              case 'l':
+                 if (!strcmp(argv[i], "-lang-c"))
+                    opts->cplusplus = 0, opts->cplusplus_comments =
+                       0, opts->objc = 0;
+                 if (!strcmp(argv[i], "-lang-c++"))
+                    opts->cplusplus = 1, opts->cplusplus_comments =
+                       1, opts->objc = 0;
+                 if (!strcmp(argv[i], "-lang-c-c++-comments"))
+                    opts->cplusplus = 0, opts->cplusplus_comments =
+                       1, opts->objc = 0;
+                 if (!strcmp(argv[i], "-lang-objc"))
+                    opts->objc = 1, opts->cplusplus =
+                       0, opts->cplusplus_comments = 1;
+                 if (!strcmp(argv[i], "-lang-objc++"))
+                    opts->objc = 1, opts->cplusplus =
+                       1, opts->cplusplus_comments = 1;
+                 if (!strcmp(argv[i], "-lang-asm"))
+                    opts->lang_asm = 1;
+                 if (!strcmp(argv[i], "-lint"))
+                    opts->for_lint = 1;
+                 if (!strcmp(argv[i], "-lang-chill"))
+                    opts->objc = 0, opts->cplusplus = 0, opts->chill = 1,
+                       opts->traditional = 1, opts->no_trigraphs = 1;
+                 break;
+
+              case '+':
+                 opts->cplusplus = 1, opts->cplusplus_comments = 1;
+                 break;
+
+              case 'w':
+                 opts->inhibit_warnings = 1;
+                 break;
+
+              case 'W':
+                 if (!strcmp(argv[i], "-Wtrigraphs"))
+                    opts->warn_trigraphs = 1;
+                 else if (!strcmp(argv[i], "-Wno-trigraphs"))
+                    opts->warn_trigraphs = 0;
+                 else if (!strcmp(argv[i], "-Wcomment"))
+                    opts->warn_comments = 1;
+                 else if (!strcmp(argv[i], "-Wno-comment"))
+                    opts->warn_comments = 0;
+                 else if (!strcmp(argv[i], "-Wcomments"))
+                    opts->warn_comments = 1;
+                 else if (!strcmp(argv[i], "-Wno-comments"))
+                    opts->warn_comments = 0;
+                 else if (!strcmp(argv[i], "-Wtraditional"))
+                    opts->warn_stringify = 1;
+                 else if (!strcmp(argv[i], "-Wno-traditional"))
+                    opts->warn_stringify = 0;
+                 else if (!strcmp(argv[i], "-Wimport"))
+                    opts->warn_import = 1;
+                 else if (!strcmp(argv[i], "-Wno-import"))
+                    opts->warn_import = 0;
+                 else if (!strcmp(argv[i], "-Werror"))
+                    opts->warnings_are_errors = 1;
+                 else if (!strcmp(argv[i], "-Wno-error"))
+                    opts->warnings_are_errors = 0;
+                 else if (!strcmp(argv[i], "-Wall"))
+                   {
+                      opts->warn_trigraphs = 1;
+                      opts->warn_comments = 1;
+                   }
+                 break;
+
+              case 'M':
+                 /* The style of the choices here is a bit mixed.
+                  * The chosen scheme is a hybrid of keeping all options in one string
+                  * and specifying each option in a separate argument:
+                  * -M|-MM|-MD file|-MMD file [-MG].  An alternative is:
+                  * -M|-MM|-MD file|-MMD file|-MG|-MMG; or more concisely:
+                  * -M[M][G][D file].  This is awkward to handle in specs, and is not
+                  * as extensible.  */
+                 /* ??? -MG must be specified in addition to one of -M or -MM.
+                  * This can be relaxed in the future without breaking anything.
+                  * The converse isn't true.  */
+
+                 /* -MG isn't valid with -MD or -MMD.  This is checked for later.  */
+                 if (!strcmp(argv[i], "-MG"))
+                   {
+                      opts->print_deps_missing_files = 1;
+                      break;
+                   }
+                 if (!strcmp(argv[i], "-M"))
+                    opts->print_deps = 2;
+                 else if (!strcmp(argv[i], "-MM"))
+                    opts->print_deps = 1;
+                 else if (!strcmp(argv[i], "-MD"))
+                    opts->print_deps = 2;
+                 else if (!strcmp(argv[i], "-MMD"))
+                    opts->print_deps = 1;
+                 /* For -MD and -MMD options, write deps on file named by next arg.  */
+                 if (!strcmp(argv[i], "-MD") || !strcmp(argv[i], "-MMD"))
+                   {
+                      if (i + 1 == argc)
+                         cpp_fatal("Filename missing after %s option",
+                                   argv[i]);
+                      opts->deps_file = argv[++i];
+                   }
+                 else
+                   {
+                      /* For -M and -MM, write deps on standard output
+                       * and suppress the usual output.  */
+                      opts->no_output = 1;
+                   }
+                 break;
+
+              case 'd':
+                 {
+                    char               *p = argv[i] + 2;
+                    char                c;
+
+                    while ((c = *p++) != 0)
+                      {
+                         /* Arg to -d specifies what parts of macros to dump */
+                         switch (c)
+                           {
+                           case 'M':
+                              opts->dump_macros = dump_only;
+                              opts->no_output = 1;
+                              break;
+                           case 'N':
+                              opts->dump_macros = dump_names;
+                              break;
+                           case 'D':
+                              opts->dump_macros = dump_definitions;
+                              break;
+                           }
+                      }
+                 }
+                 break;
+
+              case 'g':
+                 if (argv[i][2] == '3')
+                    opts->debug_output = 1;
+                 break;
+
+              case 'v':
+                 fprintf(stderr, "GNU CPP version %s", version_string);
+#ifdef TARGET_VERSION
+                 TARGET_VERSION;
+#endif
+                 fprintf(stderr, "\n");
+                 opts->verbose = 1;
+                 break;
+
+              case 'H':
+                 opts->print_include_names = 1;
+                 break;
+
+              case 'D':
+                 if (argv[i][2] != 0)
+                    push_pending(pfile, "-D", argv[i] + 2);
+                 else if (i + 1 == argc)
+                    cpp_fatal("Macro name missing after -D option");
+                 else
+                    i++, push_pending(pfile, "-D", argv[i]);
+                 break;
+
+              case 'A':
+                 {
+                    char               *p = NULL;
+
+                    if (argv[i][2] != 0)
+                       p = argv[i] + 2;
+                    else if (i + 1 == argc)
+                       cpp_fatal("Assertion missing after -A option");
+                    else
+                       p = argv[++i];
+
+                    if (!strcmp(p, "-"))
+                      {
+                         struct cpp_pending **ptr;
+
+                         /* -A- eliminates all predefined macros and assertions.
+                          * Let's include also any that were specified earlier
+                          * on the command line.  That way we can get rid of any
+                          * that were passed automatically in from GCC.  */
+
+                         opts->inhibit_predefs = 1;
+                         for (ptr = &opts->pending; *ptr;)
+                           {
+                              struct cpp_pending *pend = *ptr;
+
+                              if (pend->cmd && pend->cmd[0] == '-'
+                                  && (pend->cmd[1] == 'D'
+                                      || pend->cmd[1] == 'A'))
+                                {
+                                   *ptr = pend->next;
+                                   free(pend);
+                                }
+                              else
+                                 ptr = &pend->next;
+                           }
+                      }
+                    else
+                      {
+                         push_pending(pfile, "-A", p);
+                      }
+                 }
+                 break;
+
+              case 'U':        /* JF #undef something */
+                 if (argv[i][2] != 0)
+                    push_pending(pfile, "-U", argv[i] + 2);
+                 else if (i + 1 == argc)
+                    cpp_fatal("Macro name missing after -U option");
+                 else
+                    push_pending(pfile, "-U", argv[i + 1]), i++;
+                 break;
+
+              case 'C':
+                 opts->put_out_comments = 1;
+                 break;
+
+              case 'E':        /* -E comes from cc -E; ignore it.  */
+                 break;
+
+              case 'P':
+                 opts->no_line_commands = 1;
+                 break;
+
+              case '$':        /* Don't include $ in identifiers.  */
+                 opts->dollars_in_ident = 0;
+                 break;
+
+              case 'I':        /* Add directory to path for includes.  */
+                 {
+                    file_name_list     *dirtmp;
+
+                    if (!CPP_OPTIONS(pfile)->ignore_srcdir
+                        && !strcmp(argv[i] + 2, "-"))
+                      {
+                         CPP_OPTIONS(pfile)->ignore_srcdir = 1;
+                         /* Don't use any preceding -I directories for #include <...>.  */
+                         CPP_OPTIONS(pfile)->first_bracket_include = 0;
+                      }
+                    else
+                      {
+                         dirtmp =
+                            (file_name_list *) xmalloc(sizeof(file_name_list));
+
+                         dirtmp->next = 0;     /* New one goes on the end */
+                         dirtmp->control_macro = 0;
+                         dirtmp->c_system_include_path = 0;
+                         if (argv[i][2] != 0)
+                            dirtmp->fname = argv[i] + 2;
+                         else if (i + 1 == argc)
+                            cpp_fatal
+                               ("Directory name missing after -I option");
+                         else
+                            dirtmp->fname = argv[++i];
+                         dirtmp->got_name_map = 0;
+                         append_include_chain(pfile, dirtmp, dirtmp);
+                      }
+                 }
+                 break;
+
+              case 'n':
+                 if (!strcmp(argv[i], "-nostdinc"))
+                    /* -nostdinc causes no default include directories.
+                     * You must specify all include-file directories with -I.  */
+                    opts->no_standard_includes = 1;
+                 else if (!strcmp(argv[i], "-nostdinc++"))
+                    /* -nostdinc++ causes no default C++-specific include directories. */
+                    opts->no_standard_cplusplus_includes = 1;
+                 break;
+
+              case 'u':
+                 /* Sun compiler passes undocumented switch "-undef".
+                  * Let's assume it means to inhibit the predefined symbols.  */
+                 opts->inhibit_predefs = 1;
+                 break;
+
+              case '\0':       /* JF handle '-' as file name meaning stdin or stdout */
+                 if (!opts->in_fname)
+                   {
+                      opts->in_fname = "";
+                      break;
+                   }
+                 else if (!opts->out_fname)
+                   {
+                      opts->out_fname = "";
+                      break;
+                   }           /* else fall through into error */
+              default:
+                 return i;
+              }
+         }
+     }
+   return i;
+}
+
+void
+cpp_finish(cpp_reader * pfile)
+{
+   struct cpp_options *opts = CPP_OPTIONS(pfile);
+
+   if (opts->print_deps)
+     {
+       /* Stream on which to print the dependency information.  */
+       FILE               *deps_stream;
+
+       /* Don't actually write the deps file if compilation has failed.  */
+       if (pfile->errors == 0)
+         {
+            const char         *deps_mode =
+               opts->print_deps_append ? "a" : "w";
+
+            if (!opts->deps_file)
+               deps_stream = stdout;
+            else if (!(deps_stream = fopen(opts->deps_file, deps_mode)))
+               cpp_pfatal_with_name(pfile, opts->deps_file);
+            fputs(pfile->deps_buffer, deps_stream);
+            putc('\n', deps_stream);
+            if (opts->deps_file)
+              {
+                 if (ferror(deps_stream) || fclose(deps_stream) != 0)
+                    cpp_fatal("I/O error on output");
+              }
+         }
+     }
+}
+
+static int
+do_assert(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+         unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
+{
+   long                symstart;       /* remember where symbol name starts */
+   int                 c;
+   int                 sym_length;     /* and how long it is */
+   struct arglist     *tokens = NULL;
+
+   if (CPP_PEDANTIC(pfile) && CPP_OPTIONS(pfile)->done_initializing
+       && !CPP_BUFFER(pfile)->system_header_p)
+      cpp_pedwarn(pfile, "ANSI C does not allow `#assert'");
+
+   cpp_skip_hspace(pfile);
+   symstart = CPP_WRITTEN(pfile);      /* remember where it starts */
+   parse_name(pfile, GETC());
+   sym_length = check_macro_name(pfile, pfile->token_buffer + symstart,
+                                "assertion");
+
+   cpp_skip_hspace(pfile);
+   if (PEEKC() != '(')
+     {
+       cpp_error(pfile, "missing token-sequence in `#assert'");
+       goto error;
+     }
+   {
+      int                 error_flag = 0;
+
+      tokens = read_token_list(pfile, &error_flag);
+      if (error_flag)
+        goto error;
+      if (!tokens)
+       {
+          cpp_error(pfile, "empty token-sequence in `#assert'");
+          goto error;
+       }
+      cpp_skip_hspace(pfile);
+      c = PEEKC();
+      if (c != EOF && c != '\n')
+        cpp_pedwarn(pfile, "junk at end of `#assert'");
+      skip_rest_of_line(pfile);
+   }
+
+   /* If this name isn't already an assertion name, make it one.
+    * Error if it was already in use in some other way.  */
+
+   {
+      ASSERTION_HASHNODE *hp;
+      const char         *symname = (char *)pfile->token_buffer + symstart;
+      int                 hashcode =
+        hashf(symname, sym_length, ASSERTION_HASHSIZE);
+      struct tokenlist_list *value =
+        (struct tokenlist_list *)xmalloc(sizeof(struct tokenlist_list));
+
+      hp = assertion_lookup(pfile, symname, sym_length, hashcode);
+      if (!hp)
+       {
+          if (sym_length == 7 && !strncmp(symname, "defined", sym_length))
+             cpp_error(pfile, "`defined' redefined as assertion");
+          hp = assertion_install(pfile, symname, sym_length, hashcode);
+       }
+      /* Add the spec'd token-sequence to the list of such.  */
+      value->tokens = tokens;
+      value->next = hp->value;
+      hp->value = value;
+   }
+   CPP_SET_WRITTEN(pfile, symstart);   /* Pop */
+   return 0;
+ error:
+   CPP_SET_WRITTEN(pfile, symstart);   /* Pop */
+   skip_rest_of_line(pfile);
+   return 1;
+}
+
+static int
+do_unassert(cpp_reader * pfile, struct directive *keyword __UNUSED__,
+           unsigned char *buf __UNUSED__, unsigned char *limit __UNUSED__)
+{
+   long                symstart;       /* remember where symbol name starts */
+   int                 sym_length;     /* and how long it is */
+   int                 c;
+
+   struct arglist     *tokens = NULL;
+   int                 tokens_specified = 0;
+
+   if (CPP_PEDANTIC(pfile) && CPP_OPTIONS(pfile)->done_initializing
+       && !CPP_BUFFER(pfile)->system_header_p)
+      cpp_pedwarn(pfile, "ANSI C does not allow `#unassert'");
+
+   cpp_skip_hspace(pfile);
+
+   symstart = CPP_WRITTEN(pfile);      /* remember where it starts */
+   parse_name(pfile, GETC());
+   sym_length = check_macro_name(pfile, pfile->token_buffer + symstart,
+                                "assertion");
+
+   cpp_skip_hspace(pfile);
+   if (PEEKC() == '(')
+     {
+       int                 error_flag = 0;
+
+       tokens = read_token_list(pfile, &error_flag);
+       if (error_flag)
+          goto error;
+       if (!tokens)
+         {
+            cpp_error(pfile, "empty token list in `#unassert'");
+            goto error;
+         }
+       tokens_specified = 1;
+     }
+   cpp_skip_hspace(pfile);
+   c = PEEKC();
+   if (c != EOF && c != '\n')
+      cpp_error(pfile, "junk at end of `#unassert'");
+   skip_rest_of_line(pfile);
+
+   {
+      ASSERTION_HASHNODE *hp;
+      const char         *symname = (char *)pfile->token_buffer + symstart;
+      int                 hashcode =
+        hashf(symname, sym_length, ASSERTION_HASHSIZE);
+      struct tokenlist_list *tail, *prev;
+
+      hp = assertion_lookup(pfile, symname, sym_length, hashcode);
+      if (!hp)
+        return 1;
+
+      /* If no token list was specified, then eliminate this assertion
+       * entirely.  */
+      if (!tokens_specified)
+        delete_assertion(hp);
+      else
+       {
+          /* If a list of tokens was given, then delete any matching list.  */
+
+          tail = hp->value;
+          prev = 0;
+          while (tail)
+            {
+               struct tokenlist_list *next = tail->next;
+
+               if (compare_token_lists(tail->tokens, tokens))
+                 {
+                    if (prev)
+                       prev->next = next;
+                    else
+                       hp->value = tail->next;
+                    free_token_list(tail->tokens);
+                    free(tail);
+                 }
+               else
+                 {
+                    prev = tail;
+                 }
+               tail = next;
+            }
+       }
+   }
+
+   CPP_SET_WRITTEN(pfile, symstart);   /* Pop */
+   return 0;
+ error:
+   CPP_SET_WRITTEN(pfile, symstart);   /* Pop */
+   skip_rest_of_line(pfile);
+   return 1;
+}
+
+/* Test whether there is an assertion named NAME
+ * and optionally whether it has an asserted token list TOKENS.
+ * NAME is not null terminated; its length is SYM_LENGTH.
+ * If TOKENS_SPECIFIED is 0, then don't check for any token list.  */
+
+static int
+check_assertion(cpp_reader * pfile, const char *name, int sym_length,
+               int tokens_specified, struct arglist *tokens)
+{
+   ASSERTION_HASHNODE *hp;
+   int                 hashcode = hashf(name, sym_length, ASSERTION_HASHSIZE);
+
+   if (CPP_PEDANTIC(pfile) && !CPP_BUFFER(pfile)->system_header_p)
+      cpp_pedwarn(pfile, "ANSI C does not allow testing assertions");
+
+   hp = assertion_lookup(pfile, name, sym_length, hashcode);
+   if (!hp)
+      /* It is not an assertion; just return false.  */
+      return 0;
+
+   /* If no token list was specified, then value is 1.  */
+   if (!tokens_specified)
+      return 1;
+
+   {
+      struct tokenlist_list *tail;
+
+      tail = hp->value;
+
+      /* If a list of tokens was given,
+       * then succeed if the assertion records a matching list.  */
+
+      while (tail)
+       {
+          if (compare_token_lists(tail->tokens, tokens))
+             return 1;
+          tail = tail->next;
+       }
+
+      /* Fail if the assertion has no matching list.  */
+      return 0;
+   }
+}
+
+/* Compare two lists of tokens for equality including order of tokens.  */
+
+static int
+compare_token_lists(struct arglist *l1, struct arglist *l2)
+{
+   while (l1 && l2)
+     {
+       if (l1->length != l2->length)
+          return 0;
+       if (strncmp(l1->name, l2->name, l1->length))
+          return 0;
+       l1 = l1->next;
+       l2 = l2->next;
+     }
+
+   /* Succeed if both lists end at the same time.  */
+   return l1 == l2;
+}
+
+struct arglist     *
+reverse_token_list(struct arglist *tokens)
+{
+   struct arglist     *prev = 0, *cur, *next;
+
+   for (cur = tokens; cur; cur = next)
+     {
+       next = cur->next;
+       cur->next = prev;
+       prev = cur;
+     }
+   return prev;
+}
+
+/* Read a space-separated list of tokens ending in a close parenthesis.
+ * Return a list of strings, in the order they were written.
+ * (In case of error, return 0 and store -1 in *ERROR_FLAG.) */
+
+static struct arglist *
+read_token_list(cpp_reader * pfile, int *error_flag)
+{
+   struct arglist     *token_ptrs = 0;
+   int                 depth = 1;
+   int                 length;
+
+   *error_flag = 0;
+   FORWARD(1);                 /* Skip '(' */
+
+   /* Loop over the assertion value tokens.  */
+   while (depth > 0)
+     {
+       struct arglist     *temp;
+       long                name_written = CPP_WRITTEN(pfile);
+       int                 c;
+
+       cpp_skip_hspace(pfile);
+
+       c = GETC();
+
+       /* Find the end of the token.  */
+       if (c == '(')
+         {
+            CPP_PUTC(pfile, c);
+            depth++;
+         }
+       else if (c == ')')
+         {
+            depth--;
+            if (depth == 0)
+               break;
+            CPP_PUTC(pfile, c);
+         }
+       else if (c == '"' || c == '\'')
+         {
+            FORWARD(-1);
+            cpp_get_token(pfile);
+         }
+       else if (c == '\n')
+          break;
+       else
+         {
+            while (c != EOF && !is_space[c] && c != '(' && c != ')'
+                   && c != '"' && c != '\'')
+              {
+                 CPP_PUTC(pfile, c);
+                 c = GETC();
+              }
+            if (c != EOF)
+               FORWARD(-1);
+         }
+
+       length = CPP_WRITTEN(pfile) - name_written;
+       temp = (struct arglist *)xmalloc(sizeof(struct arglist) + length + 1);
+
+       temp->name = (char *)(temp + 1);
+       memcpy(temp->name, (char *)(pfile->token_buffer + name_written),
+              length);
+       temp->name[length] = 0;
+       temp->next = token_ptrs;
+       token_ptrs = temp;
+       temp->length = length;
+
+       CPP_ADJUST_WRITTEN(pfile, -length);     /* pop */
+
+       if (c == EOF || c == '\n')
+         {                     /* FIXME */
+            cpp_error(pfile,
+                      "unterminated token sequence following  `#' operator");
+            return 0;
+         }
+     }
+
+   /* We accumulated the names in reverse order.
+    * Now reverse them to get the proper order.  */
+   return reverse_token_list(token_ptrs);
+}
+
+static void
+free_token_list(struct arglist *tokens)
+{
+   while (tokens)
+     {
+       struct arglist     *next = tokens->next;
+
+       free(tokens->name);
+       free(tokens);
+       tokens = next;
+     }
+}
+
+/* Get the file-mode and data size of the file open on FD
+ * and store them in *MODE_POINTER and *SIZE_POINTER.  */
+
+static int
+file_size_and_mode(int fd, int *mode_pointer, long int *size_pointer)
+{
+   struct stat         sbuf;
+
+   if (fstat(fd, &sbuf) < 0)
+      return (-1);
+   if (mode_pointer)
+      *mode_pointer = sbuf.st_mode;
+   if (size_pointer)
+      *size_pointer = sbuf.st_size;
+   return 0;
+}
+
+/* Read LEN bytes at PTR from descriptor DESC, for file FILENAME,
+ * retrying if necessary.  Return a negative value if an error occurs,
+ * otherwise return the actual number of bytes read,
+ * which must be LEN unless end-of-file was reached.  */
+
+static int
+safe_read(int desc, char *ptr, int len)
+{
+   int                 left = len;
+
+   while (left > 0)
+     {
+       int                 nchars = read(desc, ptr, left);
+
+       if (nchars < 0)
+         {
+#ifdef EINTR
+            if (errno == EINTR)
+               continue;
+#endif
+            return nchars;
+         }
+       if (nchars == 0)
+          break;
+       ptr += nchars;
+       left -= nchars;
+     }
+   return len - left;
+}
+
+static char        *
+savestring(const char *input)
+{
+   unsigned            size = strlen(input);
+   char               *output = (char *)xmalloc(size + 1);
+
+   strcpy(output, input);
+   return output;
+}
+
+/* Initialize PMARK to remember the current position of PFILE. */
+void
+parse_set_mark(struct parse_marker *pmark, cpp_reader * pfile)
+{
+   cpp_buffer         *pbuf = CPP_BUFFER(pfile);
+
+   pmark->next = pbuf->marks;
+   pbuf->marks = pmark;
+   pmark->buf = pbuf;
+   pmark->position = pbuf->cur - pbuf->buf;
+}
+
+/* Cleanup PMARK - we no longer need it. */
+static void
+parse_clear_mark(struct parse_marker *pmark)
+{
+   struct parse_marker **pp = &pmark->buf->marks;
+
+   for (;; pp = &(*pp)->next)
+     {
+       if (!*pp)
+          cpp_fatal("internal error", "in parse_set_mark");
+       if (*pp == pmark)
+          break;
+     }
+   *pp = pmark->next;
+}
+
+/* Backup the current position of PFILE to that saved in PMARK. */
+
+static void
+parse_goto_mark(struct parse_marker *pmark, cpp_reader * pfile)
+{
+   cpp_buffer         *pbuf = CPP_BUFFER(pfile);
+
+   if (pbuf != pmark->buf)
+      cpp_fatal("internal error %s", "parse_goto_mark");
+   pbuf->cur = pbuf->buf + pmark->position;
+}
+
+/* Reset PMARK to point to the current position of PFILE.  (Same
+ * as parse_clear_mark (PMARK), parse_set_mark (PMARK, PFILE) but faster. */
+
+static void
+parse_move_mark(struct parse_marker *pmark, cpp_reader * pfile)
+{
+   cpp_buffer         *pbuf = CPP_BUFFER(pfile);
+
+   if (pbuf != pmark->buf)
+      cpp_fatal("internal error %s", "parse_move_mark");
+   pmark->position = pbuf->cur - pbuf->buf;
+}
+
+int
+cpp_read_check_assertion(cpp_reader * pfile)
+{
+   int                 name_start = CPP_WRITTEN(pfile);
+   int                 name_length, name_written;
+   int                 result;
+
+   FORWARD(1);                 /* Skip '#' */
+   cpp_skip_hspace(pfile);
+   parse_name(pfile, GETC());
+   name_written = CPP_WRITTEN(pfile);
+   name_length = name_written - name_start;
+   cpp_skip_hspace(pfile);
+   if (CPP_BUF_PEEK(CPP_BUFFER(pfile)) == '(')
+     {
+       int                 error_flag;
+       struct arglist     *token_ptrs = read_token_list(pfile, &error_flag);
+
+       result = check_assertion(pfile,
+                                (char *)pfile->token_buffer + name_start,
+                                name_length, 1, token_ptrs);
+     }
+   else
+      result = check_assertion(pfile,
+                              (char *)pfile->token_buffer + name_start,
+                              name_length, 0, NULL);
+   CPP_ADJUST_WRITTEN(pfile, -name_length);    /* pop */
+   return result;
+}
+
+static void
+cpp_print_file_and_line(cpp_reader * pfile)
+{
+   cpp_buffer         *ip = cpp_file_buffer(pfile);
+
+   if (ip)
+     {
+       long                line, col;
+
+       cpp_buf_line_and_col(ip, &line, &col);
+       cpp_file_line_for_message(pfile, ip->nominal_fname,
+                                 line, pfile->show_column ? col : -1);
+     }
+}
+
+static void
+cpp_error_v(cpp_reader * pfile, const char *msg, va_list args)
+{
+   cpp_print_containing_files(pfile);
+   cpp_print_file_and_line(pfile);
+   cpp_message_v(pfile, 1, msg, args);
+}
+
+void
+cpp_error(cpp_reader * pfile, const char *msg, ...)
+{
+   va_list             args;
+
+   va_start(args, msg);
+
+   cpp_error_v(pfile, msg, args);
+
+   va_end(args);
+}
+
+/* Print error message but don't count it.  */
+
+static void
+cpp_warning_v(cpp_reader * pfile, const char *msg, va_list args)
+{
+   if (CPP_OPTIONS(pfile)->inhibit_warnings)
+      return;
+
+   if (CPP_OPTIONS(pfile)->warnings_are_errors)
+      pfile->errors++;
+
+   cpp_print_containing_files(pfile);
+   cpp_print_file_and_line(pfile);
+   cpp_message_v(pfile, 0, msg, args);
+}
+
+void
+cpp_warning(cpp_reader * pfile, const char *msg, ...)
+{
+   va_list             args;
+
+   va_start(args, msg);
+
+   cpp_warning_v(pfile, msg, args);
+
+   va_end(args);
+}
+
+/* Print an error message and maybe count it.  */
+
+void
+cpp_pedwarn(cpp_reader * pfile, const char *msg, ...)
+{
+   va_list             args;
+
+   va_start(args, msg);
+
+   if (CPP_OPTIONS(pfile)->pedantic_errors)
+      cpp_error_v(pfile, msg, args);
+   else
+      cpp_warning_v(pfile, msg, args);
+
+   va_end(args);
+}
+
+static void
+cpp_error_with_line(cpp_reader * pfile, int line, int column, const char *msg)
+{
+   cpp_buffer         *ip = cpp_file_buffer(pfile);
+
+   cpp_print_containing_files(pfile);
+
+   if (ip)
+      cpp_file_line_for_message(pfile, ip->nominal_fname, line, column);
+
+   cpp_message(pfile, 1, msg, NULL, NULL, NULL);
+}
+
+static void
+cpp_warning_with_line(cpp_reader * pfile, int line, int column, const char *msg)
+{
+   cpp_buffer         *ip;
+
+   if (CPP_OPTIONS(pfile)->inhibit_warnings)
+      return;
+
+   if (CPP_OPTIONS(pfile)->warnings_are_errors)
+      pfile->errors++;
+
+   cpp_print_containing_files(pfile);
+
+   ip = cpp_file_buffer(pfile);
+
+   if (ip)
+      cpp_file_line_for_message(pfile, ip->nominal_fname, line, column);
+
+   cpp_message(pfile, 0, msg, NULL, NULL, NULL);
+}
+
+static void
+cpp_pedwarn_with_line(cpp_reader * pfile, int line, int column, const char *msg)
+{
+   if (CPP_OPTIONS(pfile)->pedantic_errors)
+      cpp_error_with_line(pfile, column, line, msg);
+   else
+      cpp_warning_with_line(pfile, line, column, msg);
+}
+
+/* Report a warning (or an error if pedantic_errors)
+ * giving specified file name and line number, not current.  */
+
+void
+cpp_pedwarn_with_file_and_line(cpp_reader * pfile,
+                              const char *file, int line,
+                              const char *msg, const char *arg1,
+                              const char *arg2, const char *arg3)
+{
+   if (!CPP_OPTIONS(pfile)->pedantic_errors
+       && CPP_OPTIONS(pfile)->inhibit_warnings)
+      return;
+   if (file)
+      cpp_file_line_for_message(pfile, file, line, -1);
+   cpp_message(pfile, CPP_OPTIONS(pfile)->pedantic_errors,
+              msg, arg1, arg2, arg3);
+}
+
+/* This defines "errno" properly for VMS, and gives us EACCES. */
+#include <errno.h>
+#ifndef errno
+extern int          errno;
+
+#endif
+
+#ifndef HAVE_STRERROR
+extern int          sys_nerr;
+
+#if defined(bsd4_4)
+extern const char  *const sys_errlist[];
+
+#else
+extern char        *sys_errlist[];
+
+#endif
+#endif /* HAVE_STRERROR */
+
+/*
+ * my_strerror - return the descriptive text associated with an `errno' code.
+ */
+
+static const char  *
+my_strerror(int errnum)
+{
+   const char         *result;
+
+#ifndef HAVE_STRERROR
+   result = ((errnum < sys_nerr) ? sys_errlist[errnum] : 0);
+#else
+   result = strerror(errnum);
+#endif
+
+   if (!result)
+      result = "undocumented I/O error";
+
+   return result;
+}
+
+/* Error including a message from `errno'.  */
+
+static void
+cpp_error_from_errno(cpp_reader * pfile, const char *name)
+{
+   cpp_buffer         *ip = cpp_file_buffer(pfile);
+
+   cpp_print_containing_files(pfile);
+
+   if (ip)
+      cpp_file_line_for_message(pfile, ip->nominal_fname, ip->lineno, -1);
+
+   cpp_message(pfile, 1, "%s: %s", name, my_strerror(errno), NULL);
+}
+
+void
+cpp_perror_with_name(cpp_reader * pfile, const char *name)
+{
+   cpp_message(pfile, 1, "%s: %s: %s", progname, name, my_strerror(errno));
+}
+
+/* TODO:
+ * No pre-compiled header file support.
+ *
+ * Possibly different enum token codes for each C/C++ token.
+ *
+ * Should clean up remaining directives to that do_XXX functions
+ *   only take two arguments and all have command_reads_line.
+ *
+ * Find and cleanup remaining uses of static variables,
+ *
+ * Support for trigraphs.
+ *
+ * Support -dM flag (dump_all_macros).
+ *
+ * Support for_lint flag.
+ */
diff --git a/src/bin/epp/cppmain.c b/src/bin/epp/cppmain.c
new file mode 100644 (file)
index 0000000..0cf2d94
--- /dev/null
@@ -0,0 +1,142 @@
+/* CPP main program, using CPP Library.
+ * Copyright (C) 1995 Free Software Foundation, Inc.
+ * Written by Per Bothner, 1994-95.
+ * Copyright (C) 2003-2011 Kim Woelders
+ * 
+ * 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
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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.
+ * 
+ * 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!  */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "cpplib.h"
+
+#define EPP_DEBUG 0
+
+cpp_reader          parse_in;
+cpp_options         options;
+
+int
+main(int argc, char **argv)
+{
+   char               *p;
+   int                 i;
+   int                 argi = 1;       /* Next argument to handle. */
+   struct cpp_options *opts = &options;
+   enum cpp_token      kind;
+   int                 got_text;
+
+   p = argv[0] + strlen(argv[0]);
+#ifndef __EMX__
+   while (p != argv[0] && p[-1] != '/')
+#else
+   while (p != argv[0] && p[-1] != '/' && p[-1] != '\\')
+#endif
+      --p;
+   progname = p;
+
+   init_parse_file(&parse_in);
+   parse_in.data = opts;
+
+   init_parse_options(opts);
+
+   argi += cpp_handle_options(&parse_in, argc - argi, argv + argi);
+   if (argi < argc)
+      cpp_fatal("Invalid option `%s'", argv[argi]);
+   parse_in.show_column = 1;
+
+   i = push_parse_file(&parse_in, opts->in_fname);
+   if (i != SUCCESS_EXIT_CODE)
+      return i;
+
+   /* Now that we know the input file is valid, open the output.  */
+
+   if (!opts->out_fname || !strcmp(opts->out_fname, ""))
+      opts->out_fname = "stdout";
+   else if (!freopen(opts->out_fname, "w", stdout))
+      cpp_pfatal_with_name(&parse_in, opts->out_fname);
+
+   got_text = 0;
+   for (i = 0;; i++)
+     {
+       kind = cpp_get_token(&parse_in);
+#if EPP_DEBUG
+       fprintf(stderr, "%03d: kind=%d len=%d out=%d text=%d\n", i,
+               kind, CPP_WRITTEN(&parse_in), !opts->no_output, got_text);
+#endif
+       switch (kind)
+         {
+         case CPP_EOF:
+            goto done;
+
+         case CPP_HSPACE:
+            continue;
+
+         case CPP_VSPACE:
+            if (!got_text)
+               goto next;
+            break;
+
+         default:
+         case CPP_OTHER:
+         case CPP_NAME:
+         case CPP_NUMBER:
+         case CPP_CHAR:
+         case CPP_STRING:
+         case CPP_LPAREN:
+         case CPP_RPAREN:
+         case CPP_LBRACE:
+         case CPP_RBRACE:
+         case CPP_COMMA:
+         case CPP_SEMICOLON:
+         case CPP_3DOTS:
+            got_text = 1;
+            continue;
+
+         case CPP_COMMENT:
+         case CPP_DIRECTIVE:
+         case CPP_POP:
+            continue;
+         }
+#if EPP_DEBUG
+       fprintf(stderr, "'");
+       fwrite(parse_in.token_buffer, 1, CPP_WRITTEN(&parse_in), stderr);
+       fprintf(stderr, "'\n");
+#endif
+       if (!opts->no_output)
+         {
+            size_t              n;
+
+            n = CPP_WRITTEN(&parse_in);
+            if (fwrite(parse_in.token_buffer, 1, n, stdout) != n)
+               exit(FATAL_EXIT_CODE);
+         }
+      next:
+       parse_in.limit = parse_in.token_buffer;
+       got_text = 0;
+     }
+
+ done:
+   cpp_finish(&parse_in);
+
+   if (parse_in.errors)
+      exit(FATAL_EXIT_CODE);
+   exit(SUCCESS_EXIT_CODE);
+}
index b2a0f8c..6368ba0 100644 (file)
@@ -46,21 +46,26 @@ _edje_file_coll_open(Edje_File *edf, const char *coll)
    id = ce->id;
    if (id < 0) return NULL;
 
-#define INIT_EMP(Tp, Sz, Ce)                                           \
-   buffer = alloca(strlen(ce->entry) + strlen(#Tp) + 2);               \
-   sprintf(buffer, "%s/%s", ce->entry, #Tp);                           \
+#define INIT_EMP(Tp, Sz, Ce)                                           \
+   buffer = alloca(strlen(ce->entry) + strlen(#Tp) + 2);               \
+   sprintf(buffer, "%s/%s", ce->entry, #Tp);                           \
    Ce->mp.Tp = eina_mempool_add("one_big", buffer, NULL, sizeof (Sz), Ce->count.Tp); \
    _emp_##Tp = Ce->mp.Tp;
 
-   INIT_EMP(RECTANGLE, Edje_Part_Description_Common, ce);
-   INIT_EMP(TEXT, Edje_Part_Description_Text, ce);
-   INIT_EMP(IMAGE, Edje_Part_Description_Image, ce);
-   INIT_EMP(SWALLOW, Edje_Part_Description_Common, ce);
-   INIT_EMP(TEXTBLOCK, Edje_Part_Description_Text, ce);
-   INIT_EMP(GROUP, Edje_Part_Description_Common, ce);
-   INIT_EMP(BOX, Edje_Part_Description_Box, ce);
-   INIT_EMP(TABLE, Edje_Part_Description_Table, ce);
-   INIT_EMP(EXTERNAL, Edje_Part_Description_External, ce);
+#define INIT_EMP_BOTH(Tp, Sz, Ce)                                       \
+   INIT_EMP(Tp, Sz, Ce)                                                 \
+   Ce->mp_rtl.Tp = eina_mempool_add("one_big", buffer, NULL,            \
+         sizeof (Sz), Ce->count.Tp);
+
+   INIT_EMP_BOTH(RECTANGLE, Edje_Part_Description_Common, ce);
+   INIT_EMP_BOTH(TEXT, Edje_Part_Description_Text, ce);
+   INIT_EMP_BOTH(IMAGE, Edje_Part_Description_Image, ce);
+   INIT_EMP_BOTH(SWALLOW, Edje_Part_Description_Common, ce);
+   INIT_EMP_BOTH(TEXTBLOCK, Edje_Part_Description_Text, ce);
+   INIT_EMP_BOTH(GROUP, Edje_Part_Description_Common, ce);
+   INIT_EMP_BOTH(BOX, Edje_Part_Description_Box, ce);
+   INIT_EMP_BOTH(TABLE, Edje_Part_Description_Table, ce);
+   INIT_EMP_BOTH(EXTERNAL, Edje_Part_Description_External, ce);
    INIT_EMP(part, Edje_Part, ce);
 
    snprintf(buf, sizeof(buf), "edje/collections/%i", id);
index 712ba3a..db1f45b 100644 (file)
@@ -5,6 +5,9 @@
 #define FLAG_Y    0x02
 #define FLAG_XY   (FLAG_X | FLAG_Y)
 
+static void _edje_part_make_rtl(Edje_Part_Description_Common *desc);
+static Edje_Part_Description_Common *_edje_get_description_by_orientation(Edje *ed, Edje_Part_Description_Common *src, Edje_Part_Description_Common **dst, unsigned char type);
+
 static void _edje_part_recalc_single(Edje *ed, Edje_Real_Part *ep,
                                     Edje_Part_Description_Common *desc, Edje_Part_Description_Common *chosen_desc,
                                     Edje_Real_Part *rel1_to_x, Edje_Real_Part *rel1_to_y,
@@ -60,31 +63,172 @@ _edje_part_pos_set(Edje *ed, Edje_Real_Part *ep, int mode, FLOAT_T pos)
 #endif
 }
 
+
+/**
+ * Returns part description
+ *
+ * @internal
+ *
+ * Converts part description to RTL-desc.
+ *
+ * @param desc Pointer to desc buffer.
+ *
+ **/
+static void
+_edje_part_make_rtl(Edje_Part_Description_Common *desc)
+{
+   double t;
+   int i;
+
+   if(!desc)
+     return;
+
+   /* This makes alignment right-oriented */
+   desc->align.x = 1.0 - desc->align.x;
+
+   /* same as above for relative components */
+   t = desc->rel1.relative_x;
+   desc->rel1.relative_x = 1.0 - desc->rel2.relative_x;
+   desc->rel2.relative_x = 1.0 - t;
+
+   /* +1 and +1 are because how edje works with right
+    * side borders - nothing is printed beyond that limit
+    *
+    * rel2 is now to the left of rel1, and Edje assumes
+    * the opposite so we switch corners on x-axis to define
+    * offset from right to left */
+   i = desc->rel1.offset_x;
+   desc->rel1.offset_x = -(desc->rel2.offset_x + 1);
+   desc->rel2.offset_x = -(i + 1);
+
+   i = desc->rel1.id_x;
+   desc->rel1.id_x = desc->rel2.id_x;
+   desc->rel2.id_x = i;
+}
+
+/**
+ * Returns part description
+ *
+ * @internal
+ *
+ * Returns part description according to object orientation.
+ * When object is in RTL-orientation (RTL flag is set)
+ * this returns the RTL-desc of it.
+ * RTL-desc would be allocated if was not created by a previous call.
+ * The dst pointer is updated in case of an allocation.
+ *
+ * @param ed Edje object.
+ * @param src The Left To Right (LTR), original desc.
+ * @param dst Pointer to Right To Left (RTL) desc-list.
+ * @param type name of dec type. Example: "default".
+ *
+ * @return Edje part description.
+ *
+ **/
+static Edje_Part_Description_Common *
+_edje_get_description_by_orientation(Edje *ed, Edje_Part_Description_Common *src, Edje_Part_Description_Common **dst, unsigned char type)
+{
+   Edje_Part_Description_Common *desc_rtl = NULL;
+   Edje_Part_Collection_Directory_Entry *ce;
+   size_t memsize = 0;
+
+   /* RTL flag is not set, return original description */
+   if(!edje_object_mirrored_get(ed->obj))
+      return src;
+
+   if(*dst)
+     return *dst; /* Was allocated before and we should use it */
+
+#define EDIT_ALLOC_POOL_RTL(Short, Type, Name)                         \
+        case EDJE_PART_TYPE_##Short:                                   \
+          {                                                            \
+             Edje_Part_Description_##Type *Name;                       \
+             Name = eina_mempool_malloc(ce->mp_rtl.Short,              \
+                    sizeof (Edje_Part_Description_##Type));             \
+             memset(Name, 0, sizeof(Edje_Part_Description_##Type));    \
+             desc_rtl = &Name->common;                                 \
+              memsize = sizeof(Edje_Part_Description_##Type);           \
+             break;                                                    \
+          }
+
+   ce = eina_hash_find(ed->file->collection, ed->group);
+
+   switch (type)
+     {
+      case EDJE_PART_TYPE_RECTANGLE:
+        desc_rtl = eina_mempool_malloc(ce->mp_rtl.RECTANGLE,
+               sizeof (Edje_Part_Description_Common));
+        ce->count.RECTANGLE++;
+         memsize = sizeof(Edje_Part_Description_Common);
+        break;
+      case EDJE_PART_TYPE_SWALLOW:
+        desc_rtl = eina_mempool_malloc(ce->mp_rtl.SWALLOW,
+               sizeof (Edje_Part_Description_Common));
+        ce->count.SWALLOW++;
+         memsize = sizeof(Edje_Part_Description_Common);
+        break;
+      case EDJE_PART_TYPE_GROUP:
+        desc_rtl = eina_mempool_malloc(ce->mp_rtl.GROUP,
+               sizeof (Edje_Part_Description_Common));
+        ce->count.GROUP++;
+         memsize = sizeof(Edje_Part_Description_Common);
+         break;
+        EDIT_ALLOC_POOL_RTL(TEXT, Text, text);
+        EDIT_ALLOC_POOL_RTL(TEXTBLOCK, Text, text);
+        EDIT_ALLOC_POOL_RTL(IMAGE, Image, image);
+        EDIT_ALLOC_POOL_RTL(BOX, Box, box);
+        EDIT_ALLOC_POOL_RTL(TABLE, Table, table);
+        EDIT_ALLOC_POOL_RTL(EXTERNAL, External, external_params);
+     }
+
+   if(desc_rtl)
+      memcpy(desc_rtl, src, memsize);
+
+   _edje_part_make_rtl(desc_rtl);
+
+   *dst = desc_rtl;
+   return desc_rtl;
+}
+
 Edje_Part_Description_Common *
-_edje_part_description_find(Edje *ed __UNUSED__, Edje_Real_Part *rp, const char *name,
+_edje_part_description_find(Edje *ed, Edje_Real_Part *rp, const char *name,
                             double val)
 {
    Edje_Part *ep = rp->part;
    Edje_Part_Description_Common *ret = NULL;
    Edje_Part_Description_Common *d;
+
    double min_dst = 99999.0;
    unsigned int i;
 
+   /* RTL flag is set, return RTL description */
+   if(edje_object_mirrored_get(ed->obj))
+     if(!ep->other.desc_rtl)
+       ep->other.desc_rtl = (Edje_Part_Description_Common **)
+          calloc(ep->other.desc_count,
+                sizeof (Edje_Part_Description_Common *));
+
    if (!strcmp(name, "default") && val == 0.0)
-     return ep->default_desc;
+     return _edje_get_description_by_orientation(ed,
+           ep->default_desc, &ep->default_desc_rtl, ep->type);
 
    if (!strcmp(name, "custom"))
-     return rp->custom ? rp->custom->description : NULL;
+     return rp->custom ?
+        _edje_get_description_by_orientation(ed, rp->custom->description,
+              &rp->custom->description_rtl, ep->type) : NULL;
 
    if (!strcmp(name, "default"))
      {
-       ret = ep->default_desc;
+       ret = _edje_get_description_by_orientation(ed, ep->default_desc,
+              &ep->default_desc_rtl, ep->type);
+
        min_dst = ABS(ep->default_desc->state.value - val);
      }
+
    for (i = 0; i < ep->other.desc_count; ++i)
      {
        d = ep->other.desc[i];
-       
+
        if (d->state.name && !strcmp(d->state.name, name))
          {
             double dst;
@@ -92,7 +236,8 @@ _edje_part_description_find(Edje *ed __UNUSED__, Edje_Real_Part *rp, const char
             dst = ABS(d->state.value - val);
             if (dst < min_dst)
               {
-                 ret = d;
+                 ret = _edje_get_description_by_orientation(ed, d,
+                        &ep->other.desc_rtl[i], ep->type);
                  min_dst = dst;
               }
          }
@@ -265,7 +410,7 @@ _edje_recalc_do(Edje *ed)
 
        ep = ed->table_parts[i];
        if (ep->calculated != FLAG_XY)
-         _edje_part_recalc(ed, ep, (~ep->calculated) & FLAG_XY);
+          _edje_part_recalc(ed, ep, (~ep->calculated) & FLAG_XY);
      }
    if (!ed->calc_only) ed->recalc = 0;
 #ifdef EDJE_CALC_CACHE
@@ -760,10 +905,12 @@ _edje_part_recalc_single_textblock(FLOAT_T sc,
             if (chosen_desc->text.max_x)
               {
                  if (mw > *maxw) *maxw = mw;
+                  if (*maxw < *minw) *maxw = *minw;
               }
             if (chosen_desc->text.max_y)
               {
                  if (mh > *maxw) *maxh = mh;
+                  if (*maxh < *minh) *maxh = *minh;
               }
          }
      }
index 28fe86d..23881b7 100644 (file)
@@ -563,6 +563,8 @@ _edje_edd_init(void)
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.text_class", text.text_class, EET_T_STRING);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.style", text.style, EET_T_STRING);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.font", text.font, EET_T_STRING);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.size_range_min", text.size_range_min, EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.size_range_max", text.size_range_max, EET_T_INT);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.repch", text.repch, EET_T_STRING);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.size", text.size, EET_T_INT);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_text, Edje_Part_Description_Text, "text.fit_x", text.fit_x, EET_T_UCHAR);
@@ -593,6 +595,8 @@ _edje_edd_init(void)
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.font", text.font, EET_T_STRING);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.repch", text.repch, EET_T_STRING);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.size", text.size, EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.size_range_min", text.size_range_min, EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.size_range_max", text.size_range_max, EET_T_INT);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.fit_x", text.fit_x, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.fit_y", text.fit_y, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_textblock, Edje_Part_Description_Text, "text.min_x", text.min_x, EET_T_UCHAR);
@@ -755,6 +759,7 @@ _edje_edd_init(void)
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part, Edje_Part, "pointer_mode", pointer_mode, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part, Edje_Part, "entry_mode", entry_mode, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part, Edje_Part, "select_mode", select_mode, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part, Edje_Part, "cursor_mode", cursor_mode, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part, Edje_Part, "multiline", multiline, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part, Edje_Part, "api.name", api.name, EET_T_STRING);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part, Edje_Part, "api.description", api.description, EET_T_STRING);
@@ -784,4 +789,5 @@ _edje_edd_init(void)
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_collection, Edje_Part_Collection, "part", part, EET_T_STRING);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_collection, Edje_Part_Collection, "script_only", script_only, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_collection, Edje_Part_Collection, "lua_script_only", lua_script_only, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_collection, Edje_Part_Collection, "prop.orientation", prop.orientation, EET_T_INT);
 }
index 7cf33a4..44aede7 100644 (file)
@@ -326,6 +326,7 @@ _edje_focus_in_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, v
    ecore_imf_context_focus_in(en->imf_context);
 
    _input_panel_show(en);
+   _caps_mode_check(en);
 #endif /* HAVE_ECORE_IMF */
 }
 
@@ -434,8 +435,19 @@ static void
 _curs_update_from_curs(Evas_Textblock_Cursor *c, Evas_Object *o __UNUSED__, Entry *en)
 {
    Evas_Coord cx, cy, cw, ch;
+   Evas_Textblock_Cursor_Type cur_type;
    if (c != en->cursor) return;
-   evas_textblock_cursor_geometry_get(c, &cx, &cy, &cw, &ch, NULL, EVAS_TEXTBLOCK_CURSOR_UNDER);
+   switch (en->rp->part->cursor_mode)
+     {
+      case EDJE_ENTRY_CURSOR_MODE_BEFORE:
+         cur_type = EVAS_TEXTBLOCK_CURSOR_BEFORE;
+         break;
+      case EDJE_ENTRY_CURSOR_MODE_UNDER:
+         /* no break for a resaon */
+      default:
+         cur_type = EVAS_TEXTBLOCK_CURSOR_UNDER;
+     }
+   evas_textblock_cursor_geometry_get(c, &cx, &cy, &cw, &ch, NULL, cur_type);
    en->cx = cx + (cw / 2);
    en->cy = cy + (ch / 2);
 }
@@ -1708,8 +1720,8 @@ _edje_key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, v
           }
      }
    if ((old_cur_pos != evas_textblock_cursor_pos_get(en->cursor)) && (!cursor_changed))
-      _edje_emit(ed, "cursor,changed", rp->part->name);  
-    
+      _edje_emit(ed, "cursor,changed", rp->part->name);
+
 #ifdef HAVE_ECORE_IMF
    if (en->imf_context)
      {
@@ -2753,14 +2765,25 @@ _edje_entry_real_part_configure(Edje_Real_Part *rp)
 {
    Evas_Coord x, y, w, h, xx, yy, ww, hh;
    Entry *en = rp->entry_data;
+   Evas_Textblock_Cursor_Type cur_type;
    if (!en) return;
+   switch (rp->part->cursor_mode)
+     {
+      case EDJE_ENTRY_CURSOR_MODE_BEFORE:
+         cur_type = EVAS_TEXTBLOCK_CURSOR_BEFORE;
+         break;
+      case EDJE_ENTRY_CURSOR_MODE_UNDER:
+         /* no break for a resaon */
+      default:
+         cur_type = EVAS_TEXTBLOCK_CURSOR_UNDER;
+     }
 
    _sel_update(en->cursor, rp->object, en);
    _anchors_update(en->cursor, rp->object, en);
    x = y = w = h = -1;
    xx = yy = ww = hh = -1;
    evas_object_geometry_get(rp->object, &x, &y, &w, &h);
-   evas_textblock_cursor_geometry_get(en->cursor, &xx, &yy, &ww, &hh, NULL, EVAS_TEXTBLOCK_CURSOR_UNDER);
+   evas_textblock_cursor_geometry_get(en->cursor, &xx, &yy, &ww, &hh, NULL, cur_type);
    if (ww < 1) ww = 1;
    if (hh < 1) ww = 1;
    if (en->cursor_bg)
@@ -3040,12 +3063,23 @@ _edje_entry_cursor_geometry_get(Edje_Real_Part *rp, Evas_Coord *cx, Evas_Coord *
 {
    Evas_Coord x, y, w, h, xx, yy, ww, hh;
    Entry *en = rp->entry_data;
+   Evas_Textblock_Cursor_Type cur_type;
    if (!en) return;
+   switch (rp->part->cursor_mode)
+     {
+      case EDJE_ENTRY_CURSOR_MODE_BEFORE:
+         cur_type = EVAS_TEXTBLOCK_CURSOR_BEFORE;
+         break;
+      case EDJE_ENTRY_CURSOR_MODE_UNDER:
+         /* no break for a resaon */
+      default:
+         cur_type = EVAS_TEXTBLOCK_CURSOR_UNDER;
+     }
 
    x = y = w = h = -1;
    xx = yy = ww = hh = -1;
    evas_object_geometry_get(rp->object, &x, &y, &w, &h);
-   evas_textblock_cursor_geometry_get(en->cursor, &xx, &yy, &ww, &hh, NULL, EVAS_TEXTBLOCK_CURSOR_UNDER);
+   evas_textblock_cursor_geometry_get(en->cursor, &xx, &yy, &ww, &hh, NULL, cur_type);
    if (ww < 1) ww = 1;
    if (hh < 1) ww = 1;
    if (cx) *cx = x + xx;
@@ -3499,6 +3533,37 @@ _edje_entry_cursor_content_get(Edje_Real_Part *rp, Edje_Cursor cur)
    return s;
 }
 
+void
+_edje_entry_cursor_pos_set(Edje_Real_Part *rp, Edje_Cursor cur, int pos)
+{
+   Entry *en = rp->entry_data;
+   Evas_Textblock_Cursor *c = _cursor_get(rp, cur);
+   if (!c) return;
+   evas_textblock_cursor_pos_set(c, pos);
+   _curs_update_from_curs(c, rp->object, rp->entry_data);
+   _sel_update(c, rp->object, rp->entry_data);
+
+#ifdef HAVE_ECORE_IMF
+   if (en->imf_context)
+     {
+        ecore_imf_context_reset(en->imf_context);
+        ecore_imf_context_cursor_position_set(en->imf_context,
+                                              evas_textblock_cursor_pos_get(en->cursor));
+     }
+#endif
+
+   _edje_emit(rp->edje, "cursor,changed", rp->part->name);
+   _edje_entry_real_part_configure(rp);
+}
+
+int
+_edje_entry_cursor_pos_get(Edje_Real_Part *rp, Edje_Cursor cur)
+{
+   Evas_Textblock_Cursor *c = _cursor_get(rp, cur);
+   if (!c) return 0;
+   return evas_textblock_cursor_pos_get(c);
+}
+
 #ifdef HAVE_ECORE_IMF
 static Eina_Bool
 _edje_entry_imf_retrieve_surrounding_cb(void *data, Ecore_IMF_Context *ctx __UNUSED__, char **text, int *cursor_pos)
index a75ed49..fef632d 100644 (file)
@@ -25,12 +25,15 @@ static Eina_List *_edje_swallows_collect(Edje *ed);
 EAPI Eina_Bool
 edje_object_file_set(Evas_Object *obj, const char *file, const char *group)
 {
+   Eina_Bool ret;
    Edje *ed;
 
    ed = _edje_fetch(obj);
    if (!ed)
      return EINA_FALSE;
-   return ed->api->file_set(obj, file, group);
+   ret = ed->api->file_set(obj, file, group);
+   _edje_object_orientation_inform(obj);
+   return ret;
 }
 
 /* FIXDOC: Verify/expand doc. */
@@ -373,6 +376,10 @@ _edje_object_file_set_internal(Evas_Object *obj, const char *file, const char *g
 
    if (ed->collection)
      {
+       if (ed->collection->prop.orientation != EDJE_ORIENTATION_AUTO)
+          ed->is_rtl = (ed->collection->prop.orientation ==
+                EDJE_ORIENTATION_RTL);
+
        if (ed->collection->script_only)
          {
             ed->load_error = EDJE_LOAD_ERROR_NONE;
@@ -446,7 +453,8 @@ _edje_object_file_set_internal(Evas_Object *obj, const char *file, const char *g
                  _edje_ref(rp->edje);
                  rp->part = ep;
                  parts = eina_list_append(parts, rp);
-                 rp->param1.description = ep->default_desc;
+                 rp->param1.description = _edje_part_description_find(ed,
+                        rp, "default", 0.0);
                  rp->chosen_description = rp->param1.description;
                  if (!rp->param1.description)
                    ERR("no default part description!");
@@ -777,8 +785,8 @@ _edje_object_file_set_internal(Evas_Object *obj, const char *file, const char *g
                            return 0;
                         }
                       child_ed = _edje_fetch(child_obj);
-                      child_ed->parent = eina_stringshare_add(rp->part->name);
-                      
+                       child_ed->parent = eina_stringshare_add(rp->part->name);
+
                       group_path = eina_list_remove(group_path, group_path_entry);
                       eina_stringshare_del(group_path_entry);
 
@@ -790,10 +798,13 @@ _edje_object_file_set_internal(Evas_Object *obj, const char *file, const char *g
                         }
                       else
                         {
+                            pack_it->parent = rp;
+
                            _edje_object_pack_item_hints_set(child_obj, pack_it);
                            evas_object_show(child_obj);
                            if (pack_it->name)
-                             evas_object_name_set(child_obj, pack_it->name);
+                                 evas_object_name_set(child_obj, pack_it->name);
+
                            if (rp->part->type == EDJE_PART_TYPE_BOX)
                              {
                                 _edje_real_part_box_append(rp, child_obj);
@@ -1204,6 +1215,10 @@ _edje_collection_free(Edje_File *edf, Edje_Part_Collection *ec, Edje_Part_Collec
          _edje_collection_free_part_description_clean(ep->type, ep->other.desc[j], edf->free_strings);
 
        free(ep->other.desc);
+        /* Alloc for RTL objects in edje_calc.c:_edje_part_description_find() */
+        if(ep->other.desc_rtl)
+          free(ep->other.desc_rtl);
+
        free(ep->items);
 // technically need this - but we ASSUME we use "one_big" so everything gets
 // freed in one go lower down when we del the mempool... but what if pool goes
@@ -1251,6 +1266,16 @@ _edje_collection_free(Edje_File *edf, Edje_Part_Collection *ec, Edje_Part_Collec
    eina_mempool_del(ce->mp.part);
    memset(&ce->mp, 0, sizeof (ce->mp));
 
+   eina_mempool_del(ce->mp_rtl.RECTANGLE);
+   eina_mempool_del(ce->mp_rtl.TEXT);
+   eina_mempool_del(ce->mp_rtl.IMAGE);
+   eina_mempool_del(ce->mp_rtl.SWALLOW);
+   eina_mempool_del(ce->mp_rtl.TEXTBLOCK);
+   eina_mempool_del(ce->mp_rtl.GROUP);
+   eina_mempool_del(ce->mp_rtl.BOX);
+   eina_mempool_del(ce->mp_rtl.TABLE);
+   eina_mempool_del(ce->mp_rtl.EXTERNAL);
+   memset(&ce->mp_rtl, 0, sizeof (ce->mp_rtl));
    free(ec);
    ce->ref = NULL;
 }
@@ -1399,26 +1424,77 @@ _edje_find_alias(Eina_Hash *aliased, char *src, int *length)
 static void
 _cb_signal_repeat(void *data, Evas_Object *obj, const char *signal, const char *source)
 {
+   Edje_Pack_Element *pack_it;
    Evas_Object *parent;
    Edje                *ed;
    Edje         *ed_parent;
    char                 new_src[4096]; /* XXX is this max reasonable? */
    size_t       length_parent = 0;
+   size_t        length_index = 0;
    size_t       length_source;
+   int           i = 0;
    const char   *alias = NULL;
 
    parent = data;
    ed = _edje_fetch(obj);
    if (!ed) return;
+
+   pack_it = evas_object_data_get(obj, "\377 edje.box_item");
+   if (!pack_it) pack_it = evas_object_data_get(obj, "\377 edje.table_item");
+   if (pack_it)
+     {
+        if (!pack_it->name)
+          {
+             Eina_List *child = NULL;
+             Evas_Object *o;
+
+             if (pack_it->parent->part->type == EDJE_PART_TYPE_BOX)
+               {
+                  child = evas_object_box_children_get(pack_it->parent->object);
+               }
+             else if (pack_it->parent->part->type == EDJE_PART_TYPE_TABLE)
+               {
+                  child = evas_object_table_children_get(pack_it->parent->object);
+               }
+
+             EINA_LIST_FREE(child, o)
+               {
+                  if (o == obj) break;
+                  i++;
+               }
+
+             eina_list_free(child);
+
+             length_index = 12;
+          }
+        else
+          {
+             length_index = strlen(pack_it->name) + 2;
+          }
+     }
+
    /* Replace snprint("%s%c%s") == memcpy + *new_src + memcat */
    if (ed->parent)
      length_parent = strlen(ed->parent);
    length_source = strlen(source);
-   if (length_source + length_parent + 2 > sizeof(new_src))
+   if (length_source + length_parent + 2 + length_index > sizeof(new_src))
      return;
 
    if (ed->parent)
      memcpy(new_src, ed->parent, length_parent);
+   if (ed->parent && length_index)
+     {
+        new_src[length_parent++] = EDJE_PART_PATH_SEPARATOR_INDEXL;
+        if (length_index == 12)
+          length_parent += eina_convert_itoa(i, new_src + length_parent);
+        else
+          {
+             memcpy(new_src + length_parent, pack_it->name, length_index);
+             length_parent += length_index - 2;
+          }
+        new_src[length_parent++] = EDJE_PART_PATH_SEPARATOR_INDEXR;
+     }
+
    new_src[length_parent] = EDJE_PART_PATH_SEPARATOR;
    memcpy(new_src + length_parent + 1, source, length_source + 1);
 
index d85464e..cab227b 100644 (file)
@@ -1008,83 +1008,60 @@ _edje_emit(Edje *ed, const char *sig, const char *src)
 
    if (ed->delete_me) return;
 
-   sep = strchr(sig, EDJE_PART_PATH_SEPARATOR);
+   sep = strrchr(sig, EDJE_PART_PATH_SEPARATOR);
 
    /* If we are not sending the signal to a part of the child, the
     * signal if for ourself
     */
    if (sep)
      {
-        char *idx, *newsig;
+        Edje_Real_Part *rp = NULL;
+        Edje *ed2;
+        char *newsig;
         size_t length;
         char *part;
-        unsigned int i;
 
-        /* the signal contains a colon, split the signal into "part:signal",
-         * and deliver it to "part" (if there is a GROUP or EXTERNAL part named "part")
+        /* the signal contains a colon, split the signal into "parts:signal",
+         * use _edje_real_part_recursive_get_helper to find the real part.
          */
         length = strlen(sig) + 1;
         part = alloca(length);
         memcpy(part, sig, length);
 
-        /* The part contain a [index], retrieve it */
-        idx = strchr(sig, EDJE_PART_PATH_SEPARATOR_INDEXL);
-        if (!idx || sep < idx) newsig = part + (sep - sig);
-        else newsig = part + (idx - sig);
+        newsig = part + (sep - sig);
 
         *newsig = '\0';
         newsig++;
 
-        for (i = 0; i < ed->table_parts_size; i++)
+        ed2 = _edje_recursive_get(ed, part, &rp);
+        if (ed2 && ed2 != ed)
+          {
+             _edje_emit(ed2, newsig, src);
+          }
+        else if (rp)
           {
-             Edje_Real_Part *rp = ed->table_parts[i];
-             if ((((rp->part->type == EDJE_PART_TYPE_GROUP
-                    || rp->part->type == EDJE_PART_TYPE_EXTERNAL)
-                   && (rp->swallowed_object))
-                  || rp->part->type == EDJE_PART_TYPE_BOX || rp->part->type == EDJE_PART_TYPE_TABLE) &&
-                 (rp->part) && (rp->part->name) &&
-                 (strcmp(rp->part->name, part) == 0))
+             switch (rp->part->type)
                {
-                  if (rp->part->type == EDJE_PART_TYPE_GROUP)
-                    {
-                       Edje *ed2 = _edje_fetch(rp->swallowed_object);
-                       if (ed2) _edje_emit(ed2, newsig, src);
-                       return; /* stop processing.
-                                * XXX maybe let signal be processed anyway?
-                                * XXX in this case, just comment this line
-                                */
-                    }
-                  else if (rp->part->type == EDJE_PART_TYPE_EXTERNAL)
-                    {
-                       _edje_external_signal_emit(rp->swallowed_object, newsig, src);
-                       return;
-                    }
-                  else if (rp->part->type == EDJE_PART_TYPE_BOX
-                           || rp->part->type == EDJE_PART_TYPE_TABLE)
-                    {
-                       const char *partid;
-                       Evas_Object *child;
-                       Edje *ed2 = NULL;
-
-                       idx = strchr(newsig, EDJE_PART_PATH_SEPARATOR_INDEXR);
-
-                       if (!idx) return ;
-                       if (idx[1] != ':') return ;
-                       if (!rp->object) return;
-
-                       partid = newsig;
-                       newsig = idx;
-
-                       *newsig = '\0';
-                       newsig += 2; /* we jump over ']' and ':' */
-
-                       child = _edje_children_get(rp, partid);
-
-                       if (child) ed2 = _edje_fetch(child);
-                       if (ed2) _edje_emit(ed2, newsig, src);
-
-                       return;
-                    }
+                case EDJE_PART_TYPE_EXTERNAL:
+                   if (!rp->swallowed_object) break ;
+
+                   _edje_external_signal_emit(rp->swallowed_object, newsig, src);
+                   break ;
+                case EDJE_PART_TYPE_BOX:
+                case EDJE_PART_TYPE_TABLE:
+                   _edje_emit(rp->edje, newsig, src);
+                   break ;
+                case EDJE_PART_TYPE_GROUP:
+                   if (!rp->swallowed_object) break;
+
+                   ed2 = _edje_fetch(rp->swallowed_object);
+                   if (!ed2) break;
+
+                   _edje_emit(ed2, newsig, src);
+                   break ;
+                default:
+                   fprintf(stderr, "SPANK SPANK SPANK !!!\nYou should never be here !\n");
+                   break;
                }
           }
      }
@@ -1095,7 +1072,7 @@ _edje_emit(Edje *ed, const char *sig, const char *src)
    EINA_LIST_FOREACH(ed->subobjs, l, obj)
      {
         Edje *ed2;
-        
+
         ed2 = _edje_fetch(obj);
         if (!ed2) continue;
         if (ed2->delete_me) continue;
index 4964a75..358e6b6 100644 (file)
@@ -30,12 +30,18 @@ Eina_List *_edje_edjes = NULL;
 EAPI Evas_Object *
 edje_object_add(Evas *evas)
 {
+   Edje *ed;
+   Evas_Object *e;
    if (!_edje_smart)
      {
        _edje_object_smart_set(&_edje_smart_class);
        _edje_smart = evas_smart_class_new((Evas_Smart_Class *)&_edje_smart_class);
      }
-   return evas_object_smart_add(evas, _edje_smart);
+
+   e = evas_object_smart_add(evas, _edje_smart);
+   ed = _edje_fetch(e);
+
+   return e;
 }
 
 void
@@ -103,6 +109,7 @@ _edje_smart_add(Evas_Object *obj)
    evas_object_move(ed->clipper, -10000, -10000);
    evas_object_resize(ed->clipper, 20000, 20000);
    evas_object_pass_events_set(ed->clipper, 1);
+   ed->is_rtl = EINA_FALSE;
    ed->have_objects = 1;
    ed->references = 1;
 
index 25882e5..ba9ecb8 100644 (file)
@@ -1,5 +1,5 @@
 #include "edje_private.h"
-
+#define _ELLIP_STR "\xE2\x80\xA6"
 
 /* returns with and height for this part.
  *
@@ -86,12 +86,12 @@ _edje_text_fit_set(char *buf, const char *text, int c1, int c2)
 
    if (c1 >= 0)
      {
-       strcpy(buf, "...");
+       strcpy(buf, _ELLIP_STR);
 
        if (c2 >= 0)
          {
             strncat(buf, text + c1, c2 - c1);
-            strcat(buf, "...");
+            strcat(buf, _ELLIP_STR);
          }
        else
          strcat(buf, text + c1);
@@ -102,7 +102,7 @@ _edje_text_fit_set(char *buf, const char *text, int c1, int c2)
          {
             strncpy(buf, text, c2);
             buf[c2] = 0;
-            strcat(buf, "...");
+            strcat(buf, _ELLIP_STR);
          }
        else
          strcpy(buf, text);
@@ -193,6 +193,8 @@ _edje_text_fit_x(Edje *ed, Edje_Real_Part *ep,
           }
      }
 
+   buf[0] = '\0';
+
    while (((c1 >= 0) || (c2 >= 0)) && (tw > sw))
      {
        loop++;
@@ -321,7 +323,8 @@ _edje_text_recalc_apply(Edje *ed, Edje_Real_Part *ep,
                        Edje_Calc_Params *params,
                        Edje_Part_Description_Text *chosen_desc)
 {
-   const char  *text, *str;
+   const char  *text = NULL;
+   const char   *str;
    const char  *font;
    char                *font2 = NULL;
    char         *sfont = NULL;
@@ -425,39 +428,32 @@ _edje_text_recalc_apply(Edje *ed, Edje_Real_Part *ep,
        evas_object_text_font_set(ep->object, font, size);
        evas_object_text_text_set(ep->object, text);
        part_get_geometry(ep, &tw, &th);
-       if (tw > sw)
-         {
-            while ((tw > sw) && (size > 0) && (tw != 0))
-              {
-                 int psize = size;
-                 size = (size * sw) / tw;
-                 if ((psize - size) <= 0) size = psize - 1;
-                 if (inlined_font) evas_object_text_font_source_set(ep->object, ed->path);
-                 else evas_object_text_font_source_set(ep->object, NULL);
+        /* Find the wanted font size */
+       if ((tw > sw) && (size > 0) && (tw != 0))
+          {
+             size = (size * sw) / tw;
 
-                 if (ep->part->scale) evas_object_scale_set(ep->object, TO_DOUBLE(sc));
-                 evas_object_text_font_set(ep->object, font, size);
-                 part_get_geometry(ep, &tw, &th);
-                 if ((size > 0) && (tw == 0)) break;
-              }
-         }
-       else if (tw < sw)
-         {
-            while ((tw < sw) && (size > 0) && (tw != 0))
-              {
-                 int psize = size;
-                 size = (size * sw) / tw;
-                 /* fprintf(stderr, "size = %i (%i, %i)\n", size, sw, tw); */
-                 if ((psize - size) >= 0) size = psize + 1;
-                 if (inlined_font) evas_object_text_font_source_set(ep->object, ed->path);
-                 else evas_object_text_font_source_set(ep->object, NULL);
+             if (inlined_font) evas_object_text_font_source_set(ep->object, ed->path);
+             else evas_object_text_font_source_set(ep->object, NULL);
 
-                 if (ep->part->scale) evas_object_scale_set(ep->object, TO_DOUBLE(sc));
-                 evas_object_text_font_set(ep->object, font, size);
-                 part_get_geometry(ep, &tw, &th);
-                 if ((size > 0) && (tw == 0)) break;
-              }
+             if (ep->part->scale) evas_object_scale_set(ep->object, TO_DOUBLE(sc));
+             evas_object_text_font_set(ep->object, font, size);
+             part_get_geometry(ep, &tw, &th);
          }
+       else if ((tw < sw) && (size > 0) && (tw != 0))
+          {
+             size = (size * sw) / tw;
+
+             if (inlined_font) evas_object_text_font_source_set(ep->object, ed->path);
+             else evas_object_text_font_source_set(ep->object, NULL);
+
+             if (ep->part->scale) evas_object_scale_set(ep->object, TO_DOUBLE(sc));
+             evas_object_text_font_set(ep->object, font, size);
+             part_get_geometry(ep, &tw, &th);
+          }
+        /* FIXME: This should possibly be replaced by more proper handling,
+         * but it's still way better than what was here before. */
+        if (tw > sw) size--;
      }
    if (chosen_desc->text.fit_y && (ep->text.cache.in_str && eina_stringshare_strlen(ep->text.cache.in_str) > 0))
      {
@@ -543,9 +539,17 @@ _edje_text_recalc_apply(Edje *ed, Edje_Real_Part *ep,
             size = current - 1;
          }
      }
-   if (size < 1) size = 1;
 
-   if (!chosen_desc->text.fit_x)
+   /* Make sure the size is in range */
+   if (size < 1)
+      size = 1;
+   else if ((size > chosen_desc->text.size_range_max) &&
+            (chosen_desc->text.size_range_max > 0))
+      size = chosen_desc->text.size_range_max;
+   else if (size < chosen_desc->text.size_range_min)
+      size = chosen_desc->text.size_range_min;
+
+   /* Handle ellipsis */
      {
        if (inlined_font) evas_object_text_font_source_set(ep->object, ed->path);
        else evas_object_text_font_source_set(ep->object, NULL);
@@ -573,8 +577,28 @@ _edje_text_recalc_apply(Edje *ed, Edje_Real_Part *ep,
    evas_object_text_font_set(ep->object, font, size);
    evas_object_text_text_set(ep->object, text);
    part_get_geometry(ep, &tw, &th);
-   ep->text.offset.x = TO_INT(SCALE(params->type.text.align.x, (sw - tw)));
-   ep->text.offset.y = TO_INT(SCALE(params->type.text.align.y, (sh - th)));
+   /* Handle alignment */
+     {
+        double align_x;
+        if (params->type.text.align.x < 0.0)
+          {
+             if (evas_object_text_direction_get(ep->object) ==
+                   EVAS_BIDI_DIRECTION_RTL)
+               {
+                  align_x = 1.0;
+               }
+             else
+               {
+                  align_x = 0.0;
+               }
+          }
+        else
+          {
+             align_x = params->type.text.align.x;
+          }
+        ep->text.offset.x = TO_INT(SCALE(align_x, (sw - tw)));
+        ep->text.offset.y = TO_INT(SCALE(params->type.text.align.y, (sh - th)));
+     }
 
    evas_object_move(ep->object,
                    ed->x + params->x + ep->text.offset.x,
index 2eb2b84..94e9f12 100644 (file)
@@ -41,7 +41,8 @@ static Eina_Bool _edje_text_class_list_foreach(const Eina_Hash *hash, const void
 static void _edje_object_image_preload_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
 static void _edje_object_signal_preload_cb(void *data, Evas_Object *obj, const char *emission, const char *source);
 
-Edje_Real_Part *_edje_real_part_recursive_get_helper(Edje *ed, char **path);
+Edje_Real_Part *_edje_real_part_recursive_get_helper(const Edje *ed, char **path);
+static Edje *_edje_recursive_get_helper(Edje *ed, char **path, Edje_Real_Part **orp);
 
 /************************** API Routines **************************/
 
@@ -265,6 +266,73 @@ edje_object_scale_get(const Evas_Object *obj)
 }
 
 /**
+ * Get the RTL orientation for this object.
+ *
+ * You can RTL orientation explicitly with edje_object_mirrored_set.
+ *
+ * @param obj the smart object
+ * @return if flag is set or not.
+ * @since 1.1.0
+ */
+EAPI Eina_Bool
+edje_object_mirrored_get(const Evas_Object *obj)
+{
+   Edje *ed;
+
+   ed = _edje_fetch(obj);
+   if (!ed) return EINA_FALSE;
+
+   return ed->is_rtl;
+}
+
+void
+_edje_object_orientation_inform(Evas_Object *obj)
+{
+   if (edje_object_mirrored_get(obj))
+     edje_object_signal_emit(obj, "edje,state,rtl", "edje");
+   else
+     edje_object_signal_emit(obj, "edje,state,ltr", "edje");
+}
+
+/**
+ * Set the RTL orientation for this object.
+ *
+ * @param obj the smart object
+ * @rtl new value of flag EINA_TRUE/EINA_FALSE
+ * @since 1.1.0
+ */
+EAPI void
+edje_object_mirrored_set(Evas_Object *obj, Eina_Bool rtl)
+{
+   Edje *ed;
+   unsigned int i;
+
+   ed = _edje_fetch(obj);
+   if (!ed) return;
+   if (ed->is_rtl == rtl) return;
+
+   ed->is_rtl = rtl;
+
+   for (i = 0 ; i < ed->table_parts_size ; i++)
+     {
+       Edje_Real_Part *ep;
+        const char *s;
+        double v;
+
+       ep = ed->table_parts[i];
+       s = ep->param1.description->state.name,
+       v = ep->param1.description->state.value;
+        _edje_part_description_apply(ed, ep, s, v , NULL, 0.0);
+        ep->chosen_description = ep->param1.description;
+     }
+   _edje_recalc_do(ed);
+
+   _edje_object_orientation_inform(obj);
+
+   return;
+}
+
+/**
  * @brief Set the edje object's global input panel usage.
  *
  * @param obj The edje object's reference.
@@ -2368,6 +2436,57 @@ edje_object_part_text_cursor_content_get(const Evas_Object *obj, const char *par
 }
 
 /**
+ * @brief Sets the cursor position to the given value
+ *
+ * @param obj A valid Evas_Object handle
+ * @param part The part name
+ * @param cur The cursor to move
+ * @param pos the position of the cursor
+ * @since 1.1.0
+ */
+EAPI void
+edje_object_part_text_cursor_pos_set(Evas_Object *obj, const char *part, Edje_Cursor cur, int pos)
+{
+   Edje *ed;
+   Edje_Real_Part *rp;
+
+   ed = _edje_fetch(obj);
+   if ((!ed) || (!part)) return;
+   rp = _edje_real_part_recursive_get(ed, part);
+   if (!rp) return;
+   if (rp->part->entry_mode > EDJE_ENTRY_EDIT_MODE_NONE)
+     {
+        _edje_entry_cursor_pos_set(rp, cur, pos);
+     }
+}
+
+/**
+ * @brief Retrieves the current position of the cursor
+ *
+ * @param obj A valid Evas_Object handle
+ * @param part The part name
+ * @param cur The cursor to get the position
+ * @return The cursor position
+ * @since 1.1.0
+ */
+EAPI int
+edje_object_part_text_cursor_pos_get(const Evas_Object *obj, const char *part, Edje_Cursor cur)
+{
+   Edje *ed;
+   Edje_Real_Part *rp;
+
+   ed = _edje_fetch(obj);
+   if ((!ed) || (!part)) return 0;
+   rp = _edje_real_part_recursive_get(ed, part);
+   if (!rp) return 0;
+   if (rp->part->entry_mode > EDJE_ENTRY_EDIT_MODE_NONE)
+     {
+        return _edje_entry_cursor_pos_get(rp, cur);
+     }
+   return 0;
+}
+
+/**
  * Add a filter function for newly inserted text.
  *
  * Whenever text is inserted (not the same as set) into the given @p part,
@@ -4579,7 +4698,7 @@ _edje_real_part_table_clear(Edje_Real_Part *rp, Eina_Bool clear)
 }
 
 Edje_Real_Part *
-_edje_real_part_recursive_get(Edje *ed, const char *part)
+_edje_real_part_recursive_get(const Edje *ed, const char *part)
 {
    Edje_Real_Part *rp;
    char **path;
@@ -4587,7 +4706,6 @@ _edje_real_part_recursive_get(Edje *ed, const char *part)
    path = eina_str_split(part, EDJE_PART_PATH_SEPARATOR_STRING, 0);
    if (!path) return NULL;
 
-   //printf("recursive get: %s\n", part);
    rp = _edje_real_part_recursive_get_helper(ed, path);
 
    free(*path);
@@ -4595,6 +4713,22 @@ _edje_real_part_recursive_get(Edje *ed, const char *part)
    return rp;
 }
 
+Edje *
+_edje_recursive_get(Edje *ed, const char *part, Edje_Real_Part **orp)
+{
+   Edje *oed;
+   char **path;
+
+   path = eina_str_split(part, EDJE_PART_PATH_SEPARATOR_STRING, 0);
+   if (!path) return NULL;
+
+   oed = _edje_recursive_get_helper(ed, path, orp);
+
+   free(*path);
+   free(path);
+   return oed;
+}
+
 Evas_Object *
 _edje_children_get(Edje_Real_Part *rp, const char *partid)
 {
@@ -4643,17 +4777,60 @@ _edje_children_get(Edje_Real_Part *rp, const char *partid)
    return child;
 }
 
+/* rebuild alternative path */
+char *
+_edje_merge_path(const char *alias, char * const *path)
+{
+   char *tmp;
+   unsigned int length = 1;
+   unsigned int alias_length;
+   unsigned int i;
+
+   if (!alias) return NULL;
+
+   alias_length = strlen(alias);
+
+   for (i = 0; path[i]; i++)
+     length += strlen(path[i]) + 1;
+
+   tmp = malloc(sizeof (char) * (length + alias_length + 2));
+   memcpy(tmp, alias, alias_length);
+   tmp[alias_length] = '\0';
+
+   for (i = 0; path[i]; i++)
+     {
+        strcat(tmp, EDJE_PART_PATH_SEPARATOR_STRING);
+        strcat(tmp, path[i]);
+     }
+
+   return tmp;
+}
+
+
 Edje_Real_Part *
-_edje_real_part_recursive_get_helper(Edje *ed, char **path)
+_edje_real_part_recursive_get_helper(const Edje *ed, char **path)
 {
    Edje_Real_Part *rp;
    Evas_Object *child;
-   const char *alias = NULL;
    char *idx = NULL;
 
+   if (!path[0])
+     return NULL;
+
+   if (ed->collection && ed->collection->alias)
+     {
+        char *alias;
+
+        alias = _edje_merge_path(eina_hash_find(ed->collection->alias, path[0]), path + 1);
+        if (alias) {
+           rp = _edje_real_part_recursive_get(ed, alias);
+           free(alias);
+           return rp;
+        }
+     }
+
    //printf("  lookup: %s on %s\n", path[0], ed->parent ? ed->parent : "-");
-   if (path[0])
-     idx = strchr(path[0], EDJE_PART_PATH_SEPARATOR_INDEXL);
+   idx = strchr(path[0], EDJE_PART_PATH_SEPARATOR_INDEXL);
    if (idx)
      {
        char *end;
@@ -4667,19 +4844,82 @@ _edje_real_part_recursive_get_helper(Edje *ed, char **path)
          }
      }
 
+   rp = _edje_real_part_get(ed, path[0]);
+   if (!path[1] && !idx) return rp;
+   if (!rp) return NULL;
+
+   switch (rp->part->type)
+     {
+      case EDJE_PART_TYPE_GROUP:
+        if (!rp->swallowed_object) return NULL;
+        ed = _edje_fetch(rp->swallowed_object);
+        if (!ed) return NULL;
+        path++;
+        return _edje_real_part_recursive_get_helper(ed, path);
+      case EDJE_PART_TYPE_BOX:
+      case EDJE_PART_TYPE_TABLE:
+      case EDJE_PART_TYPE_EXTERNAL:
+        if (!idx) return rp;
+        path++;
+
+        child = _edje_children_get(rp, idx);
+
+         ed = _edje_fetch(child);
+
+        if (!ed) return NULL;
+        return _edje_real_part_recursive_get_helper(ed, path);
+      default:
+        return NULL;
+     }
+}
+
+/* Private Routines */
+static Edje *
+_edje_recursive_get_helper(Edje *ed, char **path, Edje_Real_Part **orp)
+{
+   Evas_Object *child;
+   Edje_Real_Part *rp;
+   char *idx = NULL;
+
+   if (!path[0])
+     return NULL;
+
    if (ed->collection && ed->collection->alias)
-     alias = eina_hash_find(ed->collection->alias, path[0]);
-   if (alias)
      {
-       rp = _edje_real_part_recursive_get(ed, alias);
-       if (!path[1]) return rp;
-       if (!rp) return NULL;
+        char *alias;
+
+        alias = _edje_merge_path(eina_hash_find(ed->collection->alias, path[0]), path + 1);
+        if (alias) {
+           Edje *tmp;
+
+           tmp = _edje_recursive_get(ed, alias, orp);
+           free(alias);
+           return tmp;
+        }
      }
-   else
+
+   //printf("  lookup: %s on %s\n", path[0], ed->parent ? ed->parent : "-");
+   idx = strchr(path[0], EDJE_PART_PATH_SEPARATOR_INDEXL);
+   if (idx)
      {
-       rp = _edje_real_part_get(ed, path[0]);
-       if (!path[1]) return rp;
-       if (!rp) return NULL;
+       char *end;
+
+       end = strchr(idx + 1, EDJE_PART_PATH_SEPARATOR_INDEXR);
+       if (end)
+         {
+            *end = '\0';
+            *idx = '\0';
+            idx++;
+         }
+     }
+
+   rp = _edje_real_part_get(ed, path[0]);
+   if (!rp) return NULL;
+
+   if (!path[1] && !idx)
+     {
+        *orp = rp;
+        return rp->edje;
      }
 
    switch (rp->part->type)
@@ -4689,31 +4929,32 @@ _edje_real_part_recursive_get_helper(Edje *ed, char **path)
         ed = _edje_fetch(rp->swallowed_object);
         if (!ed) return NULL;
         path++;
-        return _edje_real_part_recursive_get_helper(ed, path);
+
+         if (!path[0]) return ed;
+        return _edje_recursive_get_helper(ed, path, orp);
       case EDJE_PART_TYPE_BOX:
       case EDJE_PART_TYPE_TABLE:
       case EDJE_PART_TYPE_EXTERNAL:
-        if (!idx) return rp;
+        if (!idx)
+           {
+              *orp = rp;
+              return NULL;
+           }
         path++;
 
         child = _edje_children_get(rp, idx);
 
         ed = _edje_fetch(child);
         if (!ed) return NULL;
-        if ((rp = _edje_real_part_recursive_get_helper(ed, path)))
-          return rp;
-
-        return NULL;
+         if (!path[0]) return ed;
+        return _edje_recursive_get_helper(ed, path, orp);
       default:
         return NULL;
      }
 }
 
-
-/* Private Routines */
-
 Edje_Real_Part *
-_edje_real_part_get(Edje *ed, const char *part)
+_edje_real_part_get(const Edje *ed, const char *part)
 {
    unsigned int i;