plz tell me if there are any fixes or anything I have to do in it. and also why it gives errors in CI / win test (8) (pull_request) test. On Thu, 13 Oct 2022 at 22:10, skrab-sah via GitGitGadget <gitgitgadget@xxxxxxxxx> wrote: > > From: skrab-sah <skrab.sah@xxxxxxxxx> > > 1. We don't need to commit the file. > 2. Added routin for abspath.c in Makefile. > 3. Added tool support for makeheaders. > > Signed-off-by: skrab-sah <skrab.sah@xxxxxxxxx> > --- > abspath.h file is generated by makeheaders tool > > 1. We don't need to commit the file. > 2. Added routin for abspath.c in Makefile. > 3. Added tool support for makeheaders. > > Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1353%2Fskrab-sah%2Fmaster-v2 > Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1353/skrab-sah/master-v2 > Pull-Request: https://github.com/git/git/pull/1353 > > Range-diff vs v1: > > 1: 9406707f5c3 ! 1: c61cad7c8c7 abspath.h file is generated by makeheaders tool > @@ .gitignore: Release/ > ## Makefile ## > @@ > # The default target of this Makefile is... > --all:: > -+all:: hdr > -+ > -+ > -+# In parallel mode things goes up and down. > -+.NOTPARALLEL: > -+ > + all:: > + > +# compile header > +.PHONY: hdr > -+hdr:: makeheaders > +hdr:: abspath.h > + > +makeheaders: tools/makeheaders.c > + $(CC) -o $@ $< > + > -+abspath.h: abspath.c > ++abspath.h: abspath.c makeheaders > + ./makeheaders $< > + > - > # Import tree-wide shared Makefile behavior and libraries > include shared.mak > -@@ Makefile: $(SP_OBJ): %.sp: %.c %.o > - >$@ > > - .PHONY: sparse > --sparse: $(SP_OBJ) > -+sparse: hdr $(SP_OBJ) > +@@ Makefile: missing_compdb_dir = > + compdb_args = > + endif > > - EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/% > - ifndef NETTLE_SHA256 > -@@ Makefile: $(COCCI_TEST_RES_GEN): .build/contrib/coccinelle/tests/%.res : contrib/coccinell > - .PHONY: coccicheck-test > - coccicheck-test: $(COCCI_TEST_RES_GEN) > +-$(OBJECTS): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir) > ++$(OBJECTS): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir) hdr > + $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $< > > --coccicheck: coccicheck-test > -+coccicheck: hdr coccicheck-test > - coccicheck: $(addsuffix .patch,$(filter-out %.pending.cocci,$(wildcard contrib/coccinelle/*.cocci))) > + %.s: %.c GIT-CFLAGS FORCE > +@@ Makefile: HCC = $(HCO:hco=hcc) > + @echo '#include "git-compat-util.h"' >$@ > + @echo '#include "$<"' >>$@ > > - # See contrib/coccinelle/README > -@@ Makefile: ifneq ($(INCLUDE_DLLS_IN_ARTIFACTS),) > - OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll) > - endif > +-$(HCO): %.hco: %.hcc FORCE > ++$(HCO): %.hco: %.hcc hdr FORCE > + $(QUIET_HDR)$(CC) $(ALL_CFLAGS) -o /dev/null -c -xc $< > > -+artifacts-tar:: hdr > - artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \ > - GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \ > - $(MOFILES) > + .PHONY: hdr-check $(HCO) > @@ Makefile: cocciclean: > $(RM) contrib/coccinelle/*.cocci.patch* > > @@ cache.h: static inline int is_absolute_path(const char *path) > int normalize_path_copy_len(char *dst, const char *src, int *prefix_len); > > ## contrib/buildsystems/CMakeLists.txt ## > -@@ contrib/buildsystems/CMakeLists.txt: endif() > - #git > - parse_makefile_for_sources(git_SOURCES "BUILTIN_OBJS") > +@@ contrib/buildsystems/CMakeLists.txt: cmake_minimum_required(VERSION 3.14) > + #set the source directory to root of git > + set(CMAKE_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) > > + > +# makeheaders > @@ contrib/buildsystems/CMakeLists.txt: endif() > + makeheaders ${CMAKE_SOURCE_DIR}/abspath.c > +) > + > -+add_dependencies(libgit makeheaders abspath.h) > + > -+ > - list(TRANSFORM git_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") > - add_executable(git ${CMAKE_SOURCE_DIR}/git.c ${git_SOURCES}) > - target_link_libraries(git common-main) > + option(USE_VCPKG "Whether or not to use vcpkg for obtaining dependencies. Only applicable to Windows platforms" ON) > + if(NOT WIN32) > + set(USE_VCPKG OFF CACHE BOOL "" FORCE) > +@@ contrib/buildsystems/CMakeLists.txt: parse_makefile_for_sources(libgit_SOURCES "LIB_OBJS") > + list(TRANSFORM libgit_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") > + list(TRANSFORM compat_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") > + add_library(libgit ${libgit_SOURCES} ${compat_SOURCES}) > ++add_dependencies(libgit abspath.h) > + > + #libxdiff > + parse_makefile_for_sources(libxdiff_SOURCES "XDIFF_OBJS") > +@@ contrib/buildsystems/CMakeLists.txt: parse_makefile_for_sources(reftable_SOURCES "REFTABLE_OBJS") > + > + list(TRANSFORM reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") > + add_library(reftable STATIC ${reftable_SOURCES}) > ++add_dependencies(reftable abspath.h) > + > + if(WIN32) > + if(NOT MSVC)#use windres when compiling with gcc and clang > +@@ contrib/buildsystems/CMakeLists.txt: target_link_libraries(scalar common-main) > + > + if(CURL_FOUND) > + add_library(http_obj OBJECT ${CMAKE_SOURCE_DIR}/http.c) > ++ add_dependencies(http_obj abspath.h) > + > + add_executable(git-imap-send ${CMAKE_SOURCE_DIR}/imap-send.c) > + target_link_libraries(git-imap-send http_obj common-main ${CURL_LIBRARIES}) > > ## tools/makeheaders.c (new) ## > @@ > > > .gitignore | 2 + > Makefile | 16 +- > abspath.c | 10 + > cache.h | 21 +- > contrib/buildsystems/CMakeLists.txt | 15 + > tools/makeheaders.c | 3815 +++++++++++++++++++++++++++ > 6 files changed, 3857 insertions(+), 22 deletions(-) > create mode 100644 tools/makeheaders.c > > diff --git a/.gitignore b/.gitignore > index b3dcafcb331..8bc5e53ce9d 100644 > --- a/.gitignore > +++ b/.gitignore > @@ -249,3 +249,5 @@ Release/ > /git.VC.db > *.dSYM > /contrib/buildsystems/out > +/makeheaders > +/abspath.h > diff --git a/Makefile b/Makefile > index cac3452edb9..e1136e96283 100644 > --- a/Makefile > +++ b/Makefile > @@ -1,6 +1,16 @@ > # The default target of this Makefile is... > all:: > > +# compile header > +.PHONY: hdr > +hdr:: abspath.h > + > +makeheaders: tools/makeheaders.c > + $(CC) -o $@ $< > + > +abspath.h: abspath.c makeheaders > + ./makeheaders $< > + > # Import tree-wide shared Makefile behavior and libraries > include shared.mak > > @@ -2598,7 +2608,7 @@ missing_compdb_dir = > compdb_args = > endif > > -$(OBJECTS): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir) > +$(OBJECTS): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir) hdr > $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $< > > %.s: %.c GIT-CFLAGS FORCE > @@ -3115,7 +3125,7 @@ HCC = $(HCO:hco=hcc) > @echo '#include "git-compat-util.h"' >$@ > @echo '#include "$<"' >>$@ > > -$(HCO): %.hco: %.hcc FORCE > +$(HCO): %.hco: %.hcc hdr FORCE > $(QUIET_HDR)$(CC) $(ALL_CFLAGS) -o /dev/null -c -xc $< > > .PHONY: hdr-check $(HCO) > @@ -3450,6 +3460,8 @@ cocciclean: > $(RM) contrib/coccinelle/*.cocci.patch* > > clean: profile-clean coverage-clean cocciclean > + $(RM) -r makeheaders > + $(RM) -r abspath.h > $(RM) -r .build > $(RM) po/git.pot po/git-core.pot > $(RM) git.res > diff --git a/abspath.c b/abspath.c > index 39e06b58486..1c163bbe651 100644 > --- a/abspath.c > +++ b/abspath.c > @@ -262,6 +262,16 @@ char *absolute_pathdup(const char *path) > return strbuf_detach(&sb, NULL); > } > > +/* > + * Concatenate "prefix" (if len is non-zero) and "path", with no > + * connecting characters (so "prefix" should end with a "/"). > + * Unlike prefix_path, this should be used if the named file does > + * not have to interact with index entry; i.e. name of a random file > + * on the filesystem. > + * > + * The return value is always a newly allocated string (even if the > + * prefix was empty). > + */ > char *prefix_filename(const char *pfx, const char *arg) > { > struct strbuf path = STRBUF_INIT; > diff --git a/cache.h b/cache.h > index 26ed03bd6de..e226dbcc7d5 100644 > --- a/cache.h > +++ b/cache.h > @@ -646,18 +646,6 @@ const char *setup_git_directory(void); > char *prefix_path(const char *prefix, int len, const char *path); > char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path); > > -/* > - * Concatenate "prefix" (if len is non-zero) and "path", with no > - * connecting characters (so "prefix" should end with a "/"). > - * Unlike prefix_path, this should be used if the named file does > - * not have to interact with index entry; i.e. name of a random file > - * on the filesystem. > - * > - * The return value is always a newly allocated string (even if the > - * prefix was empty). > - */ > -char *prefix_filename(const char *prefix, const char *path); > - > int check_filename(const char *prefix, const char *name); > void verify_filename(const char *prefix, > const char *name, > @@ -1299,14 +1287,7 @@ static inline int is_absolute_path(const char *path) > { > return is_dir_sep(path[0]) || has_dos_drive_prefix(path); > } > -int is_directory(const char *); > -char *strbuf_realpath(struct strbuf *resolved, const char *path, > - int die_on_error); > -char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path, > - int die_on_error); > -char *real_pathdup(const char *path, int die_on_error); > -const char *absolute_path(const char *path); > -char *absolute_pathdup(const char *path); > +#include "abspath.h" > const char *remove_leading_path(const char *in, const char *prefix); > const char *relative_path(const char *in, const char *prefix, struct strbuf *sb); > int normalize_path_copy_len(char *dst, const char *src, int *prefix_len); > diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt > index ea2a531be87..9d03aa682c9 100644 > --- a/contrib/buildsystems/CMakeLists.txt > +++ b/contrib/buildsystems/CMakeLists.txt > @@ -52,6 +52,18 @@ cmake_minimum_required(VERSION 3.14) > #set the source directory to root of git > set(CMAKE_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) > > + > +# makeheaders > +add_executable(makeheaders ${CMAKE_SOURCE_DIR}/tools/makeheaders.c) > + > +add_custom_target(abspath.h > + DEPENDS > + "${CMAKE_SOURCE_DIR}/abspath.c" "makeheaders" > + COMMAND > + makeheaders ${CMAKE_SOURCE_DIR}/abspath.c > +) > + > + > option(USE_VCPKG "Whether or not to use vcpkg for obtaining dependencies. Only applicable to Windows platforms" ON) > if(NOT WIN32) > set(USE_VCPKG OFF CACHE BOOL "" FORCE) > @@ -676,6 +688,7 @@ parse_makefile_for_sources(libgit_SOURCES "LIB_OBJS") > list(TRANSFORM libgit_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") > list(TRANSFORM compat_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") > add_library(libgit ${libgit_SOURCES} ${compat_SOURCES}) > +add_dependencies(libgit abspath.h) > > #libxdiff > parse_makefile_for_sources(libxdiff_SOURCES "XDIFF_OBJS") > @@ -688,6 +701,7 @@ parse_makefile_for_sources(reftable_SOURCES "REFTABLE_OBJS") > > list(TRANSFORM reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") > add_library(reftable STATIC ${reftable_SOURCES}) > +add_dependencies(reftable abspath.h) > > if(WIN32) > if(NOT MSVC)#use windres when compiling with gcc and clang > @@ -762,6 +776,7 @@ target_link_libraries(scalar common-main) > > if(CURL_FOUND) > add_library(http_obj OBJECT ${CMAKE_SOURCE_DIR}/http.c) > + add_dependencies(http_obj abspath.h) > > add_executable(git-imap-send ${CMAKE_SOURCE_DIR}/imap-send.c) > target_link_libraries(git-imap-send http_obj common-main ${CURL_LIBRARIES}) > diff --git a/tools/makeheaders.c b/tools/makeheaders.c > new file mode 100644 > index 00000000000..80f646e8b32 > --- /dev/null > +++ b/tools/makeheaders.c > @@ -0,0 +1,3815 @@ > +/* > +** This program is free software; you can redistribute it and/or > +** modify it under the terms of the Simplified BSD License (also > +** known as the "2-Clause License" or "FreeBSD License".) > +** > +** Copyright 1993 D. Richard Hipp. All rights reserved. > +** > +** Redistribution and use in source and binary forms, with or > +** without modification, are permitted provided that the following > +** conditions are met: > +** > +** 1. Redistributions of source code must retain the above copyright > +** notice, this list of conditions and the following disclaimer. > +** > +** 2. Redistributions in binary form must reproduce the above copyright > +** notice, this list of conditions and the following disclaimer in > +** the documentation and/or other materials provided with the > +** distribution. > +** > +** This software is provided "as is" and any express or implied warranties, > +** including, but not limited to, the implied warranties of merchantability > +** and fitness for a particular purpose are disclaimed. In no event shall > +** the author or contributors be liable for any direct, indirect, incidental, > +** special, exemplary, or consequential damages (including, but not limited > +** to, procurement of substitute goods or services; loss of use, data or > +** profits; or business interruption) however caused and on any theory of > +** liability, whether in contract, strict liability, or tort (including > +** negligence or otherwise) arising in any way out of the use of this > +** software, even if advised of the possibility of such damage. > +** > +** 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. > +*/ > +#include <stdio.h> > +#include <stdlib.h> > +#include <ctype.h> > +#include <memory.h> > +#include <sys/stat.h> > +#include <assert.h> > +#include <string.h> > + > +#if defined(__MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || \ > + defined(__POCC__) > +#ifndef WIN32 > +#define WIN32 > +#endif > +#else > +#include <unistd.h> > +#endif > + > +/* > +** Macros for debugging. > +*/ > +#ifdef DEBUG > +static int debugMask = 0; > +#define debug0(F, M) \ > + if ((F)&debugMask) { \ > + fprintf(stderr, M); \ > + } > +#define debug1(F, M, A) \ > + if ((F)&debugMask) { \ > + fprintf(stderr, M, A); \ > + } > +#define debug2(F, M, A, B) \ > + if ((F)&debugMask) { \ > + fprintf(stderr, M, A, B); \ > + } > +#define debug3(F, M, A, B, C) \ > + if ((F)&debugMask) { \ > + fprintf(stderr, M, A, B, C); \ > + } > +#define PARSER 0x00000001 > +#define DECL_DUMP 0x00000002 > +#define TOKENIZER 0x00000004 > +#else > +#define debug0(Flags, Format) > +#define debug1(Flags, Format, A) > +#define debug2(Flags, Format, A, B) > +#define debug3(Flags, Format, A, B, C) > +#endif > + > +/* > +** The following macros are purely for the purpose of testing this > +** program on itself. They don't really contribute to the code. > +*/ > +#define INTERFACE 1 > +#define EXPORT_INTERFACE 1 > +#define EXPORT > + > +/* > +** Each token in a source file is represented by an instance of > +** the following structure. Tokens are collected onto a list. > +*/ > +typedef struct Token Token; > +struct Token { > + const char *zText; /* The text of the token */ > + int nText; /* Number of characters in the token's text */ > + int eType; /* The type of this token */ > + int nLine; /* The line number on which the token starts */ > + Token *pComment; /* Most recent block comment before this token */ > + Token *pNext; /* Next token on the list */ > + Token *pPrev; /* Previous token on the list */ > +}; > + > +/* > +** During tokenization, information about the state of the input > +** stream is held in an instance of the following structure > +*/ > +typedef struct InStream InStream; > +struct InStream { > + const char *z; /* Complete text of the input */ > + int i; /* Next character to read from the input */ > + int nLine; /* The line number for character z[i] */ > +}; > + > +/* > +** Each declaration in the C or C++ source files is parsed out and stored as > +** an instance of the following structure. > +** > +** A "forward declaration" is a declaration that an object exists that > +** doesn't tell about the objects structure. A typical forward declaration > +** is: > +** > +** struct Xyzzy; > +** > +** Not every object has a forward declaration. If it does, thought, the > +** forward declaration will be contained in the zFwd field for C and > +** the zFwdCpp for C++. The zDecl field contains the complete > +** declaration text. > +*/ > +typedef struct Decl Decl; > +struct Decl { > + char *zName; /* Name of the object being declared. The appearance > + ** of this name is a source file triggers the declaration > + ** to be added to the header for that file. */ > + const char *zFile; /* File from which extracted. */ > + char *zIf; /* Surround the declaration with this #if */ > + char *zFwd; /* A forward declaration. NULL if there is none. */ > + char *zFwdCpp; /* Use this forward declaration for C++. */ > + char *zDecl; /* A full declaration of this object */ > + char *zExtra; /* Extra declaration text inserted into class objects */ > + int extraType; /* Last public:, protected: or private: in zExtraDecl */ > + struct Include *pInclude; /* #includes that come before this declaration > + */ > + int flags; /* See the "Properties" below */ > + Token *pComment; /* A block comment associated with this declaration */ > + Token tokenCode; /* Implementation of functions and procedures */ > + Decl *pSameName; /* Next declaration with the same "zName" */ > + Decl *pSameHash; /* Next declaration with same hash but different zName > + */ > + Decl *pNext; /* Next declaration with a different name */ > +}; > + > +/* > +** Properties associated with declarations. > +** > +** DP_Forward and DP_Declared are used during the generation of a single > +** header file in order to prevent duplicate declarations and definitions. > +** DP_Forward is set after the object has been given a forward declaration > +** and DP_Declared is set after the object gets a full declarations. > +** (Example: A forward declaration is "typedef struct Abc Abc;" and the > +** full declaration is "struct Abc { int a; float b; };".) > +** > +** The DP_Export and DP_Local flags are more permanent. They mark objects > +** that have EXPORT scope and LOCAL scope respectively. If both of these > +** marks are missing, then the object has library scope. The meanings of > +** the scopes are as follows: > +** > +** LOCAL scope The object is only usable within the file in > +** which it is declared. > +** > +** library scope The object is visible and usable within other > +** files in the same project. By if the project is > +** a library, then the object is not visible to users > +** of the library. (i.e. the object does not appear > +** in the output when using the -H option.) > +** > +** EXPORT scope The object is visible and usable everywhere. > +** > +** The DP_Flag is a temporary use flag that is used during processing to > +** prevent an infinite loop. It's use is localized. > +** > +** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent > +** and are used to specify what type of declaration the object requires. > +*/ > +#define DP_Forward 0x001 /* Has a forward declaration in this file */ > +#define DP_Declared 0x002 /* Has a full declaration in this file */ > +#define DP_Export 0x004 /* Export this declaration */ > +#define DP_Local 0x008 /* Declare in its home file only */ > +#define DP_Flag \ > + 0x010 /* Use to mark a subset of a Decl list \ > + ** for special processing */ > +#define DP_Cplusplus \ > + 0x020 /* Has C++ linkage and cannot appear in a \ > + ** C header file */ > +#define DP_ExternCReqd \ > + 0x040 /* Prepend 'extern "C"' in a C++ header. \ > + ** Prepend nothing in a C header */ > +#define DP_ExternReqd \ > + 0x080 /* Prepend 'extern "C"' in a C++ header if \ > + ** DP_Cplusplus is not also set. If DP_Cplusplus \ > + ** is set or this is a C header then \ > + ** prepend 'extern' */ > + > +/* > +** Convenience macros for dealing with declaration properties > +*/ > +#define DeclHasProperty(D, P) (((D)->flags & (P)) == (P)) > +#define DeclHasAnyProperty(D, P) (((D)->flags & (P)) != 0) > +#define DeclSetProperty(D, P) (D)->flags |= (P) > +#define DeclClearProperty(D, P) (D)->flags &= ~(P) > + > +/* > +** These are state properties of the parser. Each of the values is > +** distinct from the DP_ values above so that both can be used in > +** the same "flags" field. > +** > +** Be careful not to confuse PS_Export with DP_Export or > +** PS_Local with DP_Local. Their names are similar, but the meanings > +** of these flags are very different. > +*/ > +#define PS_Extern 0x000800 /* "extern" has been seen */ > +#define PS_Export \ > + 0x001000 /* If between "#if EXPORT_INTERFACE" \ > + ** and "#endif" */ > +#define PS_Export2 0x002000 /* If "EXPORT" seen */ > +#define PS_Typedef 0x004000 /* If "typedef" has been seen */ > +#define PS_Static 0x008000 /* If "static" has been seen */ > +#define PS_Interface 0x010000 /* If within #if INTERFACE..#endif */ > +#define PS_Method 0x020000 /* If "::" token has been seen */ > +#define PS_Local 0x040000 /* If within #if LOCAL_INTERFACE..#endif */ > +#define PS_Local2 0x080000 /* If "LOCAL" seen. */ > +#define PS_Public 0x100000 /* If "PUBLIC" seen. */ > +#define PS_Protected 0x200000 /* If "PROTECTED" seen. */ > +#define PS_Private 0x400000 /* If "PRIVATE" seen. */ > +#define PS_PPP 0x700000 /* If any of PUBLIC, PRIVATE, PROTECTED */ > + > +/* > +** The following set of flags are ORed into the "flags" field of > +** a Decl in order to identify what type of object is being > +** declared. > +*/ > +#define TY_Class 0x00100000 > +#define TY_Subroutine 0x00200000 > +#define TY_Macro 0x00400000 > +#define TY_Typedef 0x00800000 > +#define TY_Variable 0x01000000 > +#define TY_Structure 0x02000000 > +#define TY_Union 0x04000000 > +#define TY_Enumeration 0x08000000 > +#define TY_Defunct 0x10000000 /* Used to erase a declaration */ > + > +/* > +** Each nested #if (or #ifdef or #ifndef) is stored in a stack of > +** instances of the following structure. > +*/ > +typedef struct Ifmacro Ifmacro; > +struct Ifmacro { > + int nLine; /* Line number where this macro occurs */ > + char *zCondition; /* Text of the condition for this macro */ > + Ifmacro *pNext; /* Next down in the stack */ > + int flags; /* Can hold PS_Export, PS_Interface or PS_Local flags */ > +}; > + > +/* > +** When parsing a file, we need to keep track of what other files have > +** be #include-ed. For each #include found, we create an instance of > +** the following structure. > +*/ > +typedef struct Include Include; > +struct Include { > + char *zFile; /* The name of file include. Includes "" or <> */ > + char *zIf; /* If not NULL, #include should be enclosed in #if */ > + char *zLabel; /* A unique label used to test if this #include has > + * appeared already in a file or not */ > + Include *pNext; /* Previous include file, or NULL if this is the first > + */ > +}; > + > +/* > +** Identifiers found in a source file that might be used later to provoke > +** the copying of a declaration into the corresponding header file are > +** stored in a hash table as instances of the following structure. > +*/ > +typedef struct Ident Ident; > +struct Ident { > + char *zName; /* The text of this identifier */ > + Ident *pCollide; /* Next identifier with the same hash */ > + Ident *pNext; /* Next identifier in a list of them all */ > +}; > + > +/* > +** A complete table of identifiers is stored in an instance of > +** the next structure. > +*/ > +#define IDENT_HASH_SIZE 2237 > +typedef struct IdentTable IdentTable; > +struct IdentTable { > + Ident *pList; /* List of all identifiers in this table */ > + Ident *apTable[IDENT_HASH_SIZE]; /* The hash table */ > +}; > + > +/* > +** The following structure holds all information for a single > +** source file named on the command line of this program. > +*/ > +typedef struct InFile InFile; > +struct InFile { > + char *zSrc; /* Name of input file */ > + char *zHdr; /* Name of the generated .h file for this input. > + ** Will be NULL if input is to be scanned only */ > + int flags; /* One or more DP_, PS_ and/or TY_ flags */ > + InFile *pNext; /* Next input file in the list of them all */ > + IdentTable idTable; /* All identifiers in this input file */ > +}; > + > +/* > +** An unbounded string is able to grow without limit. We use these > +** to construct large in-memory strings from lots of smaller components. > +*/ > +typedef struct String String; > +struct String { > + int nAlloc; /* Number of bytes allocated */ > + int nUsed; /* Number of bytes used (not counting nul terminator) */ > + char *zText; /* Text of the string */ > +}; > + > +/* > +** The following structure contains a lot of state information used > +** while generating a .h file. We put the information in this structure > +** and pass around a pointer to this structure, rather than pass around > +** all of the information separately. This helps reduce the number of > +** arguments to generator functions. > +*/ > +typedef struct GenState GenState; > +struct GenState { > + String *pStr; /* Write output to this string */ > + IdentTable *pTable; /* A table holding the zLabel of every #include that > + * has already been generated. Used to avoid > + * generating duplicate #includes. */ > + const char *zIf; /* If not NULL, then we are within a #if with > + * this argument. */ > + int nErr; /* Number of errors */ > + const char *zFilename; /* Name of the source file being scanned */ > + int flags; /* Various flags (DP_ and PS_ flags above) */ > +}; > + > +/* > +** The following text line appears at the top of every file generated > +** by this program. By recognizing this line, the program can be sure > +** never to read a file that it generated itself. > +** > +** The "#undef INTERFACE" part is a hack to work around a name collision > +** in MSVC 2008. > +*/ > +const char zTopLine[] = > + "/* \aThis file was automatically generated. Do not edit! */\n" > + "#undef INTERFACE\n"; > +#define nTopLine (sizeof(zTopLine) - 1) > + > +/* > +** The name of the file currently being parsed. > +*/ > +static const char *zFilename; > + > +/* > +** The stack of #if macros for the file currently being parsed. > +*/ > +static Ifmacro *ifStack = 0; > + > +/* > +** A list of all files that have been #included so far in a file being > +** parsed. > +*/ > +static Include *includeList = 0; > + > +/* > +** The last block comment seen. > +*/ > +static Token *blockComment = 0; > + > +/* > +** The following flag is set if the -doc flag appears on the > +** command line. > +*/ > +static int doc_flag = 0; > + > +/* > +** If the following flag is set, then makeheaders will attempt to > +** generate prototypes for static functions and procedures. > +*/ > +static int proto_static = 0; > + > +/* > +** A list of all declarations. The list is held together using the > +** pNext field of the Decl structure. > +*/ > +static Decl *pDeclFirst; /* First on the list */ > +static Decl *pDeclLast; /* Last on the list */ > + > +/* > +** A hash table of all declarations > +*/ > +#define DECL_HASH_SIZE 3371 > +static Decl *apTable[DECL_HASH_SIZE]; > + > +/* > +** The TEST macro must be defined to something. Make sure this is the > +** case. > +*/ > +#ifndef TEST > +#define TEST 0 > +#endif > + > +#ifdef NOT_USED > +/* > +** We do our own assertion macro so that we can have more control > +** over debugging. > +*/ > +#define Assert(X) \ > + if (!(X)) { \ > + CantHappen(__LINE__); \ > + } > +#define CANT_HAPPEN CantHappen(__LINE__) > +static void CantHappen(int iLine) > +{ > + fprintf(stderr, "Assertion failed on line %d\n", iLine); > + *(char *)1 = 0; /* Force a core-dump */ > +} > +#endif > + > +/* > +** Memory allocation functions that are guaranteed never to return NULL. > +*/ > +static void *SafeMalloc(int nByte) > +{ > + void *p = malloc(nByte); > + if (p == 0) { > + fprintf(stderr, "Out of memory. Can't allocate %d bytes.\n", > + nByte); > + exit(1); > + } > + return p; > +} > +static void SafeFree(void *pOld) > +{ > + if (pOld) { > + free(pOld); > + } > +} > +static void *SafeRealloc(void *pOld, int nByte) > +{ > + void *p; > + if (pOld == 0) { > + p = SafeMalloc(nByte); > + } else { > + p = realloc(pOld, nByte); > + if (p == 0) { > + fprintf(stderr, > + "Out of memory. Can't enlarge an allocation to %d bytes\n", > + nByte); > + exit(1); > + } > + } > + return p; > +} > +static char *StrDup(const char *zSrc, int nByte) > +{ > + char *zDest; > + if (nByte <= 0) { > + nByte = strlen(zSrc); > + } > + zDest = SafeMalloc(nByte + 1); > + strncpy(zDest, zSrc, nByte); > + zDest[nByte] = 0; > + return zDest; > +} > + > +/* > +** Return TRUE if the character X can be part of an identifier > +*/ > +#define ISALNUM(X) ((X) == '_' || isalnum(X)) > + > +/* > +** Routines for dealing with unbounded strings. > +*/ > +static void StringInit(String *pStr) > +{ > + pStr->nAlloc = 0; > + pStr->nUsed = 0; > + pStr->zText = 0; > +} > +static void StringReset(String *pStr) > +{ > + SafeFree(pStr->zText); > + StringInit(pStr); > +} > +static void StringAppend(String *pStr, const char *zText, int nByte) > +{ > + if (nByte <= 0) { > + nByte = strlen(zText); > + } > + if (pStr->nUsed + nByte >= pStr->nAlloc) { > + if (pStr->nAlloc == 0) { > + pStr->nAlloc = nByte + 100; > + pStr->zText = SafeMalloc(pStr->nAlloc); > + } else { > + pStr->nAlloc = pStr->nAlloc * 2 + nByte; > + pStr->zText = SafeRealloc(pStr->zText, pStr->nAlloc); > + } > + } > + strncpy(&pStr->zText[pStr->nUsed], zText, nByte); > + pStr->nUsed += nByte; > + pStr->zText[pStr->nUsed] = 0; > +} > +#define StringGet(S) ((S)->zText ? (S)->zText : "") > + > +/* > +** Compute a hash on a string. The number returned is a non-negative > +** value between 0 and 2**31 - 1 > +*/ > +static int Hash(const char *z, int n) > +{ > + int h = 0; > + if (n <= 0) { > + n = strlen(z); > + } > + while (n--) { > + h = h ^ (h << 5) ^ *z++; > + } > + return h & 0x7fffffff; > +} > + > +/* > +** Given an identifier name, try to find a declaration for that > +** identifier in the hash table. If found, return a pointer to > +** the Decl structure. If not found, return 0. > +*/ > +static Decl *FindDecl(const char *zName, int len) > +{ > + int h; > + Decl *p; > + > + if (len <= 0) { > + len = strlen(zName); > + } > + h = Hash(zName, len) % DECL_HASH_SIZE; > + p = apTable[h]; > + while (p && > + (strncmp(p->zName, zName, len) != 0 || p->zName[len] != 0)) { > + p = p->pSameHash; > + } > + return p; > +} > + > +/* > +** Install the given declaration both in the hash table and on > +** the list of all declarations. > +*/ > +static void InstallDecl(Decl *pDecl) > +{ > + int h; > + Decl *pOther; > + > + h = Hash(pDecl->zName, 0) % DECL_HASH_SIZE; > + pOther = apTable[h]; > + while (pOther && strcmp(pDecl->zName, pOther->zName) != 0) { > + pOther = pOther->pSameHash; > + } > + if (pOther) { > + pDecl->pSameName = pOther->pSameName; > + pOther->pSameName = pDecl; > + } else { > + pDecl->pSameName = 0; > + pDecl->pSameHash = apTable[h]; > + apTable[h] = pDecl; > + } > + pDecl->pNext = 0; > + if (pDeclFirst == 0) { > + pDeclFirst = pDeclLast = pDecl; > + } else { > + pDeclLast->pNext = pDecl; > + pDeclLast = pDecl; > + } > +} > + > +/* > +** Look at the current ifStack. If anything declared at the current > +** position must be surrounded with > +** > +** #if STUFF > +** #endif > +** > +** Then this routine computes STUFF and returns a pointer to it. Memory > +** to hold the value returned is obtained from malloc(). > +*/ > +static char *GetIfString(void) > +{ > + Ifmacro *pIf; > + char *zResult = 0; > + int hasIf = 0; > + String str; > + > + for (pIf = ifStack; pIf; pIf = pIf->pNext) { > + if (pIf->zCondition == 0 || *pIf->zCondition == 0) > + continue; > + if (!hasIf) { > + hasIf = 1; > + StringInit(&str); > + } else { > + StringAppend(&str, " && ", 4); > + } > + StringAppend(&str, pIf->zCondition, 0); > + } > + if (hasIf) { > + zResult = StrDup(StringGet(&str), 0); > + StringReset(&str); > + } else { > + zResult = 0; > + } > + return zResult; > +} > + > +/* > +** Create a new declaration and put it in the hash table. Also > +** return a pointer to it so that we can fill in the zFwd and zDecl > +** fields, and so forth. > +*/ > +static Decl *CreateDecl(const char *zName, /* Name of the object being declared. > + */ > + int nName /* Length of the name */ > +) > +{ > + Decl *pDecl; > + > + pDecl = SafeMalloc(sizeof(Decl) + nName + 1); > + memset(pDecl, 0, sizeof(Decl)); > + pDecl->zName = (char *)&pDecl[1]; > + sprintf(pDecl->zName, "%.*s", nName, zName); > + pDecl->zFile = zFilename; > + pDecl->pInclude = includeList; > + pDecl->zIf = GetIfString(); > + InstallDecl(pDecl); > + return pDecl; > +} > + > +/* > +** Insert a new identifier into an table of identifiers. Return TRUE if > +** a new identifier was inserted and return FALSE if the identifier was > +** already in the table. > +*/ > +static int IdentTableInsert(IdentTable *pTable, /* The table into which we will > + insert */ > + const char *zId, /* Name of the identifiers */ > + int nId /* Length of the identifier name */ > +) > +{ > + int h; > + Ident *pId; > + > + if (nId <= 0) { > + nId = strlen(zId); > + } > + h = Hash(zId, nId) % IDENT_HASH_SIZE; > + for (pId = pTable->apTable[h]; pId; pId = pId->pCollide) { > + if (strncmp(zId, pId->zName, nId) == 0 && > + pId->zName[nId] == 0) { > + /* printf("Already in table: %.*s\n",nId,zId); */ > + return 0; > + } > + } > + pId = SafeMalloc(sizeof(Ident) + nId + 1); > + pId->zName = (char *)&pId[1]; > + sprintf(pId->zName, "%.*s", nId, zId); > + pId->pNext = pTable->pList; > + pTable->pList = pId; > + pId->pCollide = pTable->apTable[h]; > + pTable->apTable[h] = pId; > + /* printf("Add to table: %.*s\n",nId,zId); */ > + return 1; > +} > + > +/* > +** Check to see if the given value is in the given IdentTable. Return > +** true if it is and false if it is not. > +*/ > +static int IdentTableTest(IdentTable *pTable, /* The table in which to search */ > + const char *zId, /* Name of the identifiers */ > + int nId /* Length of the identifier name */ > +) > +{ > + int h; > + Ident *pId; > + > + if (nId <= 0) { > + nId = strlen(zId); > + } > + h = Hash(zId, nId) % IDENT_HASH_SIZE; > + for (pId = pTable->apTable[h]; pId; pId = pId->pCollide) { > + if (strncmp(zId, pId->zName, nId) == 0 && > + pId->zName[nId] == 0) { > + return 1; > + } > + } > + return 0; > +} > + > +/* > +** Remove every identifier from the given table. Reset the table to > +** its initial state. > +*/ > +static void IdentTableReset(IdentTable *pTable) > +{ > + Ident *pId, *pNext; > + > + for (pId = pTable->pList; pId; pId = pNext) { > + pNext = pId->pNext; > + SafeFree(pId); > + } > + memset(pTable, 0, sizeof(IdentTable)); > +} > + > +#ifdef DEBUG > +/* > +** Print the name of every identifier in the given table, one per line > +*/ > +static void IdentTablePrint(IdentTable *pTable, FILE *pOut) > +{ > + Ident *pId; > + > + for (pId = pTable->pList; pId; pId = pId->pNext) { > + fprintf(pOut, "%s\n", pId->zName); > + } > +} > +#endif > + > +/* > +** Read an entire file into memory. Return a pointer to the memory. > +** > +** The memory is obtained from SafeMalloc and must be freed by the > +** calling function. > +** > +** If the read fails for any reason, 0 is returned. > +*/ > +static char *ReadFile(const char *zFilename) > +{ > + struct stat sStat; > + FILE *pIn; > + char *zBuf; > + int n; > + > + if (stat(zFilename, &sStat) != 0 > +#ifndef WIN32 > + || !S_ISREG(sStat.st_mode) > +#endif > + ) { > + return 0; > + } > + pIn = fopen(zFilename, "r"); > + if (pIn == 0) { > + return 0; > + } > + zBuf = SafeMalloc(sStat.st_size + 1); > + n = fread(zBuf, 1, sStat.st_size, pIn); > + zBuf[n] = 0; > + fclose(pIn); > + return zBuf; > +} > + > +/* > +** Write the contents of a string into a file. Return the number of > +** errors > +*/ > +static int WriteFile(const char *zFilename, const char *zOutput) > +{ > + FILE *pOut; > + pOut = fopen(zFilename, "w"); > + if (pOut == 0) { > + return 1; > + } > + fwrite(zOutput, 1, strlen(zOutput), pOut); > + fclose(pOut); > + return 0; > +} > + > +/* > +** Major token types > +*/ > +#define TT_Space 1 /* Contiguous white space */ > +#define TT_Id 2 /* An identifier */ > +#define TT_Preprocessor 3 /* Any C preprocessor directive */ > +#define TT_Comment 4 /* Either C or C++ style comment */ > +#define TT_Number 5 /* Any numeric constant */ > +#define TT_String 6 /* String or character constants. ".." or '.' */ > +#define TT_Braces 7 /* All text between { and a matching } */ > +#define TT_EOF 8 /* End of file */ > +#define TT_Error 9 /* An error condition */ > +#define TT_BlockComment \ > + 10 /* A C-Style comment at the left margin that \ > + * spans multiple lines */ > +#define TT_Other 0 /* None of the above */ > + > +/* > +** Get a single low-level token from the input file. Update the > +** file pointer so that it points to the first character beyond the > +** token. > +** > +** A "low-level token" is any token except TT_Braces. A TT_Braces token > +** consists of many smaller tokens and is assembled by a routine that > +** calls this one. > +** > +** The function returns the number of errors. An error is an > +** unterminated string or character literal or an unterminated > +** comment. > +** > +** Profiling shows that this routine consumes about half the > +** CPU time on a typical run of makeheaders. > +*/ > +static int GetToken(InStream *pIn, Token *pToken) > +{ > + int i; > + const char *z; > + int cStart; > + int c; > + int startLine; /* Line on which a structure begins */ > + int nlisc = 0; /* True if there is a new-line in a ".." or '..' */ > + int nErr = 0; /* Number of errors seen */ > + > + z = pIn->z; > + i = pIn->i; > + pToken->nLine = pIn->nLine; > + pToken->zText = &z[i]; > + switch (z[i]) { > + case 0: > + pToken->eType = TT_EOF; > + pToken->nText = 0; > + break; > + > + case '#': > + if (i == 0 || z[i - 1] == '\n' || > + (i > 1 && z[i - 1] == '\r' && z[i - 2] == '\n')) { > + /* We found a preprocessor statement */ > + pToken->eType = TT_Preprocessor; > + i++; > + while (z[i] != 0 && z[i] != '\n') { > + if (z[i] == '\\') { > + i++; > + if (z[i] == '\n') > + pIn->nLine++; > + } > + i++; > + } > + pToken->nText = i - pIn->i; > + } else { > + /* Just an operator */ > + pToken->eType = TT_Other; > + pToken->nText = 1; > + } > + break; > + > + case ' ': > + case '\t': > + case '\r': > + case '\f': > + case '\n': > + while (isspace(z[i])) { > + if (z[i] == '\n') > + pIn->nLine++; > + i++; > + } > + pToken->eType = TT_Space; > + pToken->nText = i - pIn->i; > + break; > + > + case '\\': > + pToken->nText = 2; > + pToken->eType = TT_Other; > + if (z[i + 1] == '\n') { > + pIn->nLine++; > + pToken->eType = TT_Space; > + } else if (z[i + 1] == 0) { > + pToken->nText = 1; > + } > + break; > + > + case '\'': > + case '\"': > + cStart = z[i]; > + startLine = pIn->nLine; > + do { > + i++; > + c = z[i]; > + if (c == '\n') { > + if (!nlisc) { > + fprintf(stderr, > + "%s:%d: (warning) Newline in string or character literal.\n", > + zFilename, pIn->nLine); > + nlisc = 1; > + } > + pIn->nLine++; > + } > + if (c == '\\') { > + i++; > + c = z[i]; > + if (c == '\n') { > + pIn->nLine++; > + } > + } else if (c == cStart) { > + i++; > + c = 0; > + } else if (c == 0) { > + fprintf(stderr, > + "%s:%d: Unterminated string or character literal.\n", > + zFilename, startLine); > + nErr++; > + } > + } while (c); > + pToken->eType = TT_String; > + pToken->nText = i - pIn->i; > + break; > + > + case '/': > + if (z[i + 1] == '/') { > + /* C++ style comment */ > + while (z[i] && z[i] != '\n') { > + i++; > + } > + pToken->eType = TT_Comment; > + pToken->nText = i - pIn->i; > + } else if (z[i + 1] == '*') { > + /* C style comment */ > + int isBlockComment = i == 0 || z[i - 1] == '\n'; > + i += 2; > + startLine = pIn->nLine; > + while (z[i] && (z[i] != '*' || z[i + 1] != '/')) { > + if (z[i] == '\n') { > + pIn->nLine++; > + if (isBlockComment) { > + if (z[i + 1] == '*' || > + z[i + 2] == '*') { > + isBlockComment = 2; > + } else { > + isBlockComment = 0; > + } > + } > + } > + i++; > + } > + if (z[i]) { > + i += 2; > + } else { > + isBlockComment = 0; > + fprintf(stderr, "%s:%d: Unterminated comment\n", > + zFilename, startLine); > + nErr++; > + } > + pToken->eType = isBlockComment == 2 ? TT_BlockComment : > + TT_Comment; > + pToken->nText = i - pIn->i; > + } else { > + /* A divide operator */ > + pToken->eType = TT_Other; > + pToken->nText = 1 + (z[i + 1] == '+'); > + } > + break; > + > + case '0': > + if (z[i + 1] == 'x' || z[i + 1] == 'X') { > + /* A hex constant */ > + i += 2; > + while (isxdigit(z[i])) { > + i++; > + } > + } else { > + /* An octal constant */ > + while (isdigit(z[i])) { > + i++; > + } > + } > + pToken->eType = TT_Number; > + pToken->nText = i - pIn->i; > + break; > + > + case '1': > + case '2': > + case '3': > + case '4': > + case '5': > + case '6': > + case '7': > + case '8': > + case '9': > + while (isdigit(z[i])) { > + i++; > + } > + if ((c = z[i]) == '.') { > + i++; > + while (isdigit(z[i])) { > + i++; > + } > + c = z[i]; > + if (c == 'e' || c == 'E') { > + i++; > + if (((c = z[i]) == '+' || c == '-') && > + isdigit(z[i + 1])) { > + i++; > + } > + while (isdigit(z[i])) { > + i++; > + } > + c = z[i]; > + } > + if (c == 'f' || c == 'F' || c == 'l' || c == 'L') { > + i++; > + } > + } else if (c == 'e' || c == 'E') { > + i++; > + if (((c = z[i]) == '+' || c == '-') && > + isdigit(z[i + 1])) { > + i++; > + } > + while (isdigit(z[i])) { > + i++; > + } > + } else if (c == 'L' || c == 'l') { > + i++; > + c = z[i]; > + if (c == 'u' || c == 'U') { > + i++; > + } > + } else if (c == 'u' || c == 'U') { > + i++; > + c = z[i]; > + if (c == 'l' || c == 'L') { > + i++; > + } > + } > + pToken->eType = TT_Number; > + pToken->nText = i - pIn->i; > + break; > + > + case 'a': > + case 'b': > + case 'c': > + case 'd': > + case 'e': > + 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 'E': > + 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 '_': > + while (isalnum(z[i]) || z[i] == '_') { > + i++; > + }; > + pToken->eType = TT_Id; > + pToken->nText = i - pIn->i; > + break; > + > + case ':': > + pToken->eType = TT_Other; > + pToken->nText = 1 + (z[i + 1] == ':'); > + break; > + > + case '=': > + case '<': > + case '>': > + case '+': > + case '-': > + case '*': > + case '%': > + case '^': > + case '&': > + case '|': > + pToken->eType = TT_Other; > + pToken->nText = 1 + (z[i + 1] == '='); > + break; > + > + default: > + pToken->eType = TT_Other; > + pToken->nText = 1; > + break; > + } > + pIn->i += pToken->nText; > + return nErr; > +} > + > +/* > +** This routine recovers the next token from the input file which is > +** not a space or a comment or any text between an "#if 0" and "#endif". > +** > +** This routine returns the number of errors encountered. An error > +** is an unterminated token or unmatched "#if 0". > +** > +** Profiling shows that this routine uses about a quarter of the > +** CPU time in a typical run. > +*/ > +static int GetNonspaceToken(InStream *pIn, Token *pToken) > +{ > + int nIf = 0; > + int inZero = 0; > + const char *z; > + int value; > + int startLine; > + int nErr = 0; > + > + startLine = pIn->nLine; > + while (1) { > + nErr += GetToken(pIn, pToken); > + /* printf("%04d: Type=%d nIf=%d [%.*s]\n", > + pToken->nLine,pToken->eType,nIf,pToken->nText, > + pToken->eType!=TT_Space ? pToken->zText : "<space>"); */ > + pToken->pComment = blockComment; > + switch (pToken->eType) { > + case TT_Comment: /*0123456789 12345678 */ > + if (strncmp(pToken->zText, "/*MAKEHEADERS-STOP", 18) == > + 0) > + return nErr; > + break; > + > + case TT_Space: > + break; > + > + case TT_BlockComment: > + if (doc_flag) { > + blockComment = SafeMalloc(sizeof(Token)); > + *blockComment = *pToken; > + } > + break; > + > + case TT_EOF: > + if (nIf) { > + fprintf(stderr, "%s:%d: Unterminated \"#if\"\n", > + zFilename, startLine); > + nErr++; > + } > + return nErr; > + > + case TT_Preprocessor: > + z = &pToken->zText[1]; > + while (*z == ' ' || *z == '\t') > + z++; > + if (sscanf(z, "if %d", &value) == 1 && value == 0) { > + nIf++; > + inZero = 1; > + } else if (inZero) { > + if (strncmp(z, "if", 2) == 0) { > + nIf++; > + } else if (strncmp(z, "endif", 5) == 0) { > + nIf--; > + if (nIf == 0) > + inZero = 0; > + } > + } else { > + return nErr; > + } > + break; > + > + default: > + if (!inZero) { > + return nErr; > + } > + break; > + } > + } > + /* NOT REACHED */ > +} > + > +/* > +** This routine looks for identifiers (strings of contiguous alphanumeric > +** characters) within a preprocessor directive and adds every such string > +** found to the given identifier table > +*/ > +static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable) > +{ > + Token sToken; > + InStream sIn; > + int go = 1; > + > + sIn.z = pToken->zText; > + sIn.i = 1; > + sIn.nLine = 1; > + while (go && sIn.i < pToken->nText) { > + GetToken(&sIn, &sToken); > + switch (sToken.eType) { > + case TT_Id: > + IdentTableInsert(pTable, sToken.zText, sToken.nText); > + break; > + > + case TT_EOF: > + go = 0; > + break; > + > + default: > + break; > + } > + } > +} > + > +/* > +** This routine gets the next token. Everything contained within > +** {...} is collapsed into a single TT_Braces token. Whitespace is > +** omitted. > +** > +** If pTable is not NULL, then insert every identifier seen into the > +** IdentTable. This includes any identifiers seen inside of {...}. > +** > +** The number of errors encountered is returned. An error is an > +** unterminated token. > +*/ > +static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable) > +{ > + const char *zStart; > + int iStart; > + int nBrace; > + int c; > + int nLine; > + int nErr; > + > + nErr = GetNonspaceToken(pIn, pToken); > + switch (pToken->eType) { > + case TT_Id: > + if (pTable != 0) { > + IdentTableInsert(pTable, pToken->zText, pToken->nText); > + } > + return nErr; > + > + case TT_Preprocessor: > + if (pTable != 0) { > + FindIdentifiersInMacro(pToken, pTable); > + } > + return nErr; > + > + case TT_Other: > + if (pToken->zText[0] == '{') > + break; > + return nErr; > + > + default: > + return nErr; > + } > + > + iStart = pIn->i; > + zStart = pToken->zText; > + nLine = pToken->nLine; > + nBrace = 1; > + while (nBrace) { > + nErr += GetNonspaceToken(pIn, pToken); > + /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace, > + pToken->nText,pToken->zText); */ > + switch (pToken->eType) { > + case TT_EOF: > + fprintf(stderr, "%s:%d: Unterminated \"{\"\n", > + zFilename, nLine); > + nErr++; > + pToken->eType = TT_Error; > + return nErr; > + > + case TT_Id: > + if (pTable) { > + IdentTableInsert(pTable, pToken->zText, > + pToken->nText); > + } > + break; > + > + case TT_Preprocessor: > + if (pTable != 0) { > + FindIdentifiersInMacro(pToken, pTable); > + } > + break; > + > + case TT_Other: > + if ((c = pToken->zText[0]) == '{') { > + nBrace++; > + } else if (c == '}') { > + nBrace--; > + } > + break; > + > + default: > + break; > + } > + } > + pToken->eType = TT_Braces; > + pToken->nText = 1 + pIn->i - iStart; > + pToken->zText = zStart; > + pToken->nLine = nLine; > + return nErr; > +} > + > +/* > +** This routine frees up a list of Tokens. The pComment tokens are > +** not cleared by this. So we leak a little memory when using the -doc > +** option. So what. > +*/ > +static void FreeTokenList(Token *pList) > +{ > + Token *pNext; > + while (pList) { > + pNext = pList->pNext; > + SafeFree(pList); > + pList = pNext; > + } > +} > + > +/* > +** Tokenize an entire file. Return a pointer to the list of tokens. > +** > +** Space for each token is obtained from a separate malloc() call. The > +** calling function is responsible for freeing this space. > +** > +** If pTable is not NULL, then fill the table with all identifiers seen in > +** the input file. > +*/ > +static Token *TokenizeFile(const char *zFile, IdentTable *pTable) > +{ > + InStream sIn; > + Token *pFirst = 0, *pLast = 0, *pNew; > + int nErr = 0; > + > + sIn.z = zFile; > + sIn.i = 0; > + sIn.nLine = 1; > + blockComment = 0; > + > + while (sIn.z[sIn.i] != 0) { > + pNew = SafeMalloc(sizeof(Token)); > + nErr += GetBigToken(&sIn, pNew, pTable); > + debug3(TOKENIZER, "Token on line %d: [%.*s]\n", pNew->nLine, > + pNew->nText < 50 ? pNew->nText : 50, pNew->zText); > + if (pFirst == 0) { > + pFirst = pLast = pNew; > + pNew->pPrev = 0; > + } else { > + pLast->pNext = pNew; > + pNew->pPrev = pLast; > + pLast = pNew; > + } > + if (pNew->eType == TT_EOF) > + break; > + } > + if (pLast) > + pLast->pNext = 0; > + blockComment = 0; > + if (nErr) { > + FreeTokenList(pFirst); > + pFirst = 0; > + } > + > + return pFirst; > +} > + > +#if TEST == 1 > +/* > +** Use the following routine to test or debug the tokenizer. > +*/ > +void main(int argc, char **argv) > +{ > + char *zFile; > + Token *pList, *p; > + IdentTable sTable; > + > + if (argc != 2) { > + fprintf(stderr, "Usage: %s filename\n", *argv); > + exit(1); > + } > + memset(&sTable, 0, sizeof(sTable)); > + zFile = ReadFile(argv[1]); > + if (zFile == 0) { > + fprintf(stderr, "Can't read file \"%s\"\n", argv[1]); > + exit(1); > + } > + pList = TokenizeFile(zFile, &sTable); > + for (p = pList; p; p = p->pNext) { > + int j; > + switch (p->eType) { > + case TT_Space: > + printf("%4d: Space\n", p->nLine); > + break; > + case TT_Id: > + printf("%4d: Id %.*s\n", p->nLine, p->nText, > + p->zText); > + break; > + case TT_Preprocessor: > + printf("%4d: Preprocessor %.*s\n", p->nLine, p->nText, > + p->zText); > + break; > + case TT_Comment: > + printf("%4d: Comment\n", p->nLine); > + break; > + case TT_BlockComment: > + printf("%4d: Block Comment\n", p->nLine); > + break; > + case TT_Number: > + printf("%4d: Number %.*s\n", p->nLine, p->nText, > + p->zText); > + break; > + case TT_String: > + printf("%4d: String %.*s\n", p->nLine, p->nText, > + p->zText); > + break; > + case TT_Other: > + printf("%4d: Other %.*s\n", p->nLine, p->nText, > + p->zText); > + break; > + case TT_Braces: > + for (j = 0; > + j < p->nText && j < 30 && p->zText[j] != '\n'; > + j++) { > + } > + printf("%4d: Braces %.*s...}\n", p->nLine, j, > + p->zText); > + break; > + case TT_EOF: > + printf("%4d: End of file\n", p->nLine); > + break; > + default: > + printf("%4d: type %d\n", p->nLine, p->eType); > + break; > + } > + } > + FreeTokenList(pList); > + SafeFree(zFile); > + IdentTablePrint(&sTable, stdout); > +} > +#endif > + > +#ifdef DEBUG > +/* > +** For debugging purposes, write out a list of tokens. > +*/ > +static void PrintTokens(Token *pFirst, Token *pLast) > +{ > + int needSpace = 0; > + int c; > + > + pLast = pLast->pNext; > + while (pFirst != pLast) { > + switch (pFirst->eType) { > + case TT_Preprocessor: > + printf("\n%.*s\n", pFirst->nText, pFirst->zText); > + needSpace = 0; > + break; > + > + case TT_Id: > + case TT_Number: > + printf("%s%.*s", needSpace ? " " : "", pFirst->nText, > + pFirst->zText); > + needSpace = 1; > + break; > + > + default: > + c = pFirst->zText[0]; > + printf("%s%.*s", > + (needSpace && (c == '*' || c == '{')) ? " " : "", > + pFirst->nText, pFirst->zText); > + needSpace = pFirst->zText[0] == ','; > + break; > + } > + pFirst = pFirst->pNext; > + } > +} > +#endif > + > +/* > +** Convert a sequence of tokens into a string and return a pointer > +** to that string. Space to hold the string is obtained from malloc() > +** and must be freed by the calling function. > +** > +** Certain keywords (EXPORT, PRIVATE, PUBLIC, PROTECTED) are always > +** skipped. > +** > +** If pSkip!=0 then skip over nSkip tokens beginning with pSkip. > +** > +** If zTerm!=0 then append the text to the end. > +*/ > +static char *TokensToString(Token *pFirst, /* First token in the string */ > + Token *pLast, /* Last token in the string */ > + char *zTerm, /* Terminate the string with this text > + if not NULL */ > + Token *pSkip, /* Skip this token if not NULL */ > + int nSkip /* Skip a total of this many tokens */ > +) > +{ > + char *zReturn; > + String str; > + int needSpace = 0; > + int c; > + int iSkip = 0; > + int skipOne = 0; > + > + StringInit(&str); > + pLast = pLast->pNext; > + while (pFirst != pLast) { > + if (pFirst == pSkip) { > + iSkip = nSkip; > + } > + if (iSkip > 0) { > + iSkip--; > + pFirst = pFirst->pNext; > + continue; > + } > + switch (pFirst->eType) { > + case TT_Preprocessor: > + StringAppend(&str, "\n", 1); > + StringAppend(&str, pFirst->zText, pFirst->nText); > + StringAppend(&str, "\n", 1); > + needSpace = 0; > + break; > + > + case TT_Id: > + switch (pFirst->zText[0]) { > + case 'E': > + if (pFirst->nText == 6 && > + strncmp(pFirst->zText, "EXPORT", 6) == 0) { > + skipOne = 1; > + } > + break; > + case 'P': > + switch (pFirst->nText) { > + case 6: > + skipOne = !strncmp(pFirst->zText, > + "PUBLIC", 6); > + break; > + case 7: > + skipOne = !strncmp(pFirst->zText, > + "PRIVATE", 7); > + break; > + case 9: > + skipOne = !strncmp(pFirst->zText, > + "PROTECTED", 9); > + break; > + default: > + break; > + } > + break; > + default: > + break; > + } > + if (skipOne) { > + pFirst = pFirst->pNext; > + skipOne = 0; > + continue; > + } > + /* Fall thru to the next case */ > + case TT_Number: > + if (needSpace) { > + StringAppend(&str, " ", 1); > + } > + StringAppend(&str, pFirst->zText, pFirst->nText); > + needSpace = 1; > + break; > + > + default: > + c = pFirst->zText[0]; > + if (needSpace && (c == '*' || c == '{')) { > + StringAppend(&str, " ", 1); > + } > + StringAppend(&str, pFirst->zText, pFirst->nText); > + /* needSpace = pFirst->zText[0]==','; */ > + needSpace = 0; > + break; > + } > + pFirst = pFirst->pNext; > + } > + if (zTerm && *zTerm) { > + StringAppend(&str, zTerm, strlen(zTerm)); > + } > + zReturn = StrDup(StringGet(&str), 0); > + StringReset(&str); > + return zReturn; > +} > + > +/* > +** This routine is called when we see one of the keywords "struct", > +** "enum", "union" or "class". This might be the beginning of a > +** type declaration. This routine will process the declaration and > +** remove the declaration tokens from the input stream. > +** > +** If this is a type declaration that is immediately followed by a > +** semicolon (in other words it isn't also a variable definition) > +** then set *pReset to ';'. Otherwise leave *pReset at 0. The > +** *pReset flag causes the parser to skip ahead to the next token > +** that begins with the value placed in the *pReset flag, if that > +** value is different from 0. > +*/ > +static int ProcessTypeDecl(Token *pList, int flags, int *pReset) > +{ > + Token *pName, *pEnd; > + Decl *pDecl; > + String str; > + int need_to_collapse = 1; > + int type = 0; > + > + *pReset = 0; > + if (pList == 0 || pList->pNext == 0 || pList->pNext->eType != TT_Id) { > + return 0; > + } > + pName = pList->pNext; > + > + /* Catch the case of "struct Foo;" and skip it. */ > + if (pName->pNext && pName->pNext->zText[0] == ';') { > + *pReset = ';'; > + return 0; > + } > + > + for (pEnd = pName->pNext; pEnd && pEnd->eType != TT_Braces; > + pEnd = pEnd->pNext) { > + switch (pEnd->zText[0]) { > + case '(': > + case ')': > + case '*': > + case '[': > + case '=': > + case ';': > + return 0; > + } > + } > + if (pEnd == 0) { > + return 0; > + } > + > + /* > + ** At this point, we know we have a type declaration that is bounded > + ** by pList and pEnd and has the name pName. > + */ > + > + /* > + ** If the braces are followed immediately by a semicolon, then we are > + ** dealing a type declaration only. There is not variable definition > + ** following the type declaration. So reset... > + */ > + if (pEnd->pNext == 0 || pEnd->pNext->zText[0] == ';') { > + *pReset = ';'; > + need_to_collapse = 0; > + } else { > + need_to_collapse = 1; > + } > + > + if (proto_static == 0 && > + (flags & (PS_Local | PS_Export | PS_Interface)) == 0) { > + /* Ignore these objects unless they are explicitly declared as > + *interface, > + ** or unless the "-local" command line option was specified. */ > + *pReset = ';'; > + return 0; > + } > + > +#ifdef DEBUG > + if (debugMask & PARSER) { > + printf("**** Found type: %.*s %.*s...\n", pList->nText, > + pList->zText, pName->nText, pName->zText); > + PrintTokens(pList, pEnd); > + printf(";\n"); > + } > +#endif > + > + /* > + ** Create a new Decl object for this definition. Actually, if this > + ** is a C++ class definition, then the Decl object might already exist, > + ** so check first for that case before creating a new one. > + */ > + switch (*pList->zText) { > + case 'c': > + type = TY_Class; > + break; > + case 's': > + type = TY_Structure; > + break; > + case 'e': > + type = TY_Enumeration; > + break; > + case 'u': > + type = TY_Union; > + break; > + default: /* Can't Happen */ > + break; > + } > + if (type != TY_Class) { > + pDecl = 0; > + } else { > + pDecl = FindDecl(pName->zText, pName->nText); > + if (pDecl && (pDecl->flags & type) != type) > + pDecl = 0; > + } > + if (pDecl == 0) { > + pDecl = CreateDecl(pName->zText, pName->nText); > + } > + if ((flags & PS_Static) || !(flags & (PS_Interface | PS_Export))) { > + DeclSetProperty(pDecl, DP_Local); > + } > + DeclSetProperty(pDecl, type); > + > + /* The object has a full declaration only if it is contained within > + ** "#if INTERFACE...#endif" or "#if EXPORT_INTERFACE...#endif" or > + ** "#if LOCAL_INTERFACE...#endif". Otherwise, we only give it a > + ** forward declaration. > + */ > + if (flags & (PS_Local | PS_Export | PS_Interface)) { > + pDecl->zDecl = TokensToString(pList, pEnd, ";\n", 0, 0); > + } else { > + pDecl->zDecl = 0; > + } > + pDecl->pComment = pList->pComment; > + StringInit(&str); > + StringAppend(&str, "typedef ", 0); > + StringAppend(&str, pList->zText, pList->nText); > + StringAppend(&str, " ", 0); > + StringAppend(&str, pName->zText, pName->nText); > + StringAppend(&str, " ", 0); > + StringAppend(&str, pName->zText, pName->nText); > + StringAppend(&str, ";\n", 2); > + pDecl->zFwd = StrDup(StringGet(&str), 0); > + StringReset(&str); > + StringInit(&str); > + StringAppend(&str, pList->zText, pList->nText); > + StringAppend(&str, " ", 0); > + StringAppend(&str, pName->zText, pName->nText); > + StringAppend(&str, ";\n", 2); > + pDecl->zFwdCpp = StrDup(StringGet(&str), 0); > + StringReset(&str); > + if (flags & PS_Export) { > + DeclSetProperty(pDecl, DP_Export); > + } else if (flags & PS_Local) { > + DeclSetProperty(pDecl, DP_Local); > + } > + > + /* Here's something weird. ANSI-C doesn't allow a forward declaration > + ** of an enumeration. So we have to build the typedef into the > + ** definition. > + */ > + if (pDecl->zDecl && DeclHasProperty(pDecl, TY_Enumeration)) { > + StringInit(&str); > + StringAppend(&str, pDecl->zDecl, 0); > + StringAppend(&str, pDecl->zFwd, 0); > + SafeFree(pDecl->zDecl); > + SafeFree(pDecl->zFwd); > + pDecl->zFwd = 0; > + pDecl->zDecl = StrDup(StringGet(&str), 0); > + StringReset(&str); > + } > + > + if (pName->pNext->zText[0] == ':') { > + DeclSetProperty(pDecl, DP_Cplusplus); > + } > + if (pName->nText == 5 && strncmp(pName->zText, "class", 5) == 0) { > + DeclSetProperty(pDecl, DP_Cplusplus); > + } > + > + /* > + ** Remove all but pList and pName from the input stream. > + */ > + if (need_to_collapse) { > + while (pEnd != pName) { > + Token *pPrev = pEnd->pPrev; > + pPrev->pNext = pEnd->pNext; > + pEnd->pNext->pPrev = pPrev; > + SafeFree(pEnd); > + pEnd = pPrev; > + } > + } > + return 0; > +} > + > +/* > +** Given a list of tokens that declare something (a function, procedure, > +** variable or typedef) find the token which contains the name of the > +** thing being declared. > +** > +** Algorithm: > +** > +** The name is: > +** > +** 1. The first identifier that is followed by a "[", or > +** > +** 2. The first identifier that is followed by a "(" where the > +** "(" is followed by another identifier, or > +** > +** 3. The first identifier followed by "::", or > +** > +** 4. If none of the above, then the last identifier. > +** > +** In all of the above, certain reserved words (like "char") are > +** not considered identifiers. > +*/ > +static Token *FindDeclName(Token *pFirst, Token *pLast) > +{ > + Token *pName = 0; > + Token *p; > + int c; > + > + if (pFirst == 0 || pLast == 0) { > + return 0; > + } > + pLast = pLast->pNext; > + for (p = pFirst; p && p != pLast; p = p->pNext) { > + if (p->eType == TT_Id) { > + static IdentTable sReserved; > + static int isInit = 0; > + static const char *aWords[] = { > + "char", "class", "const", "double", > + "enum", "extern", "EXPORT", "ET_PROC", > + "float", "int", "long", "PRIVATE", > + "PROTECTED", "PUBLIC", "register", "static", > + "struct", "sizeof", "signed", "typedef", > + "union", "volatile", "virtual", "void", > + }; > + > + if (!isInit) { > + int i; > + for (i = 0; > + i < sizeof(aWords) / sizeof(aWords[0]); > + i++) { > + IdentTableInsert(&sReserved, aWords[i], > + 0); > + } > + isInit = 1; > + } > + if (!IdentTableTest(&sReserved, p->zText, p->nText)) { > + pName = p; > + } > + } else if (p == pFirst) { > + continue; > + } else if ((c = p->zText[0]) == '[' && pName) { > + break; > + } else if (c == '(' && p->pNext && p->pNext->eType == TT_Id && > + pName) { > + break; > + } else if (c == ':' && p->zText[1] == ':' && pName) { > + break; > + } > + } > + return pName; > +} > + > +/* > +** This routine is called when we see a method for a class that begins > +** with the PUBLIC, PRIVATE, or PROTECTED keywords. Such methods are > +** added to their class definitions. > +*/ > +static int ProcessMethodDef(Token *pFirst, Token *pLast, int flags) > +{ > + Token *pClass; > + char *zDecl; > + Decl *pDecl; > + String str; > + int type; > + > + pLast = pLast->pPrev; > + while (pFirst->zText[0] == 'P') { > + int rc = 1; > + switch (pFirst->nText) { > + case 6: > + rc = strncmp(pFirst->zText, "PUBLIC", 6); > + break; > + case 7: > + rc = strncmp(pFirst->zText, "PRIVATE", 7); > + break; > + case 9: > + rc = strncmp(pFirst->zText, "PROTECTED", 9); > + break; > + default: > + break; > + } > + if (rc) > + break; > + pFirst = pFirst->pNext; > + } > + pClass = FindDeclName(pFirst, pLast); > + if (pClass == 0) { > + fprintf(stderr, > + "%s:%d: Unable to find the class name for this method\n", > + zFilename, pFirst->nLine); > + return 1; > + } > + pDecl = FindDecl(pClass->zText, pClass->nText); > + if (pDecl == 0 || (pDecl->flags & TY_Class) != TY_Class) { > + pDecl = CreateDecl(pClass->zText, pClass->nText); > + DeclSetProperty(pDecl, TY_Class); > + } > + StringInit(&str); > + if (pDecl->zExtra) { > + StringAppend(&str, pDecl->zExtra, 0); > + SafeFree(pDecl->zExtra); > + pDecl->zExtra = 0; > + } > + type = flags & PS_PPP; > + if (pDecl->extraType != type) { > + if (type & PS_Public) { > + StringAppend(&str, "public:\n", 0); > + pDecl->extraType = PS_Public; > + } else if (type & PS_Protected) { > + StringAppend(&str, "protected:\n", 0); > + pDecl->extraType = PS_Protected; > + } else if (type & PS_Private) { > + StringAppend(&str, "private:\n", 0); > + pDecl->extraType = PS_Private; > + } > + } > + StringAppend(&str, " ", 0); > + zDecl = TokensToString(pFirst, pLast, ";\n", pClass, 2); > + if (strncmp(zDecl, pClass->zText, pClass->nText) == 0) { > + /* If member initializer list is found after a constructor, > + ** skip that part. */ > + char *colon = strchr(zDecl, ':'); > + if (colon != 0 && colon[1] != 0) { > + *colon++ = ';'; > + *colon++ = '\n'; > + *colon = 0; > + } > + } > + StringAppend(&str, zDecl, 0); > + SafeFree(zDecl); > + pDecl->zExtra = StrDup(StringGet(&str), 0); > + StringReset(&str); > + return 0; > +} > + > +/* > +** This routine is called when we see a function or procedure definition. > +** We make an entry in the declaration table that is a prototype for this > +** function or procedure. > +*/ > +static int ProcessProcedureDef(Token *pFirst, Token *pLast, int flags) > +{ > + Token *pName; > + Decl *pDecl; > + Token *pCode; > + > + if (pFirst == 0 || pLast == 0) { > + return 0; > + } > + if (flags & PS_Method) { > + if (flags & PS_PPP) { > + return ProcessMethodDef(pFirst, pLast, flags); > + } else { > + return 0; > + } > + } > + if ((flags & PS_Static) != 0 && !proto_static) { > + return 0; > + } > + pCode = pLast; > + while (pLast && pLast != pFirst && pLast->zText[0] != ')') { > + pLast = pLast->pPrev; > + } > + if (pLast == 0 || pLast == pFirst || pFirst->pNext == pLast) { > + fprintf(stderr, "%s:%d: Unrecognized syntax.\n", zFilename, > + pFirst->nLine); > + return 1; > + } > + if (flags & (PS_Interface | PS_Export | PS_Local)) { > + fprintf(stderr, > + "%s:%d: Missing \"inline\" on function or procedure.\n", > + zFilename, pFirst->nLine); > + return 1; > + } > + pName = FindDeclName(pFirst, pLast); > + if (pName == 0) { > + fprintf(stderr, > + "%s:%d: Malformed function or procedure definition.\n", > + zFilename, pFirst->nLine); > + return 1; > + } > + if (strncmp(pName->zText, "main", pName->nText) == 0) { > + /* skip main() decl. */ > + return 0; > + } > + /* > + ** At this point we've isolated a procedure declaration between pFirst > + ** and pLast with the name pName. > + */ > +#ifdef DEBUG > + if (debugMask & PARSER) { > + printf("**** Found routine: %.*s on line %d...\n", pName->nText, > + pName->zText, pFirst->nLine); > + PrintTokens(pFirst, pLast); > + printf(";\n"); > + } > +#endif > + pDecl = CreateDecl(pName->zText, pName->nText); > + pDecl->pComment = pFirst->pComment; > + if (pCode && pCode->eType == TT_Braces) { > + pDecl->tokenCode = *pCode; > + } > + DeclSetProperty(pDecl, TY_Subroutine); > + pDecl->zDecl = TokensToString(pFirst, pLast, ";\n", 0, 0); > + if ((flags & (PS_Static | PS_Local2)) != 0) { > + DeclSetProperty(pDecl, DP_Local); > + } else if ((flags & (PS_Export2)) != 0) { > + DeclSetProperty(pDecl, DP_Export); > + } > + > + if (flags & DP_Cplusplus) { > + DeclSetProperty(pDecl, DP_Cplusplus); > + } else { > + DeclSetProperty(pDecl, DP_ExternCReqd); > + } > + > + return 0; > +} > + > +/* > +** This routine is called whenever we see the "inline" keyword. We > +** need to seek-out the inline function or procedure and make a > +** declaration out of the entire definition. > +*/ > +static int ProcessInlineProc(Token *pFirst, int flags, int *pReset) > +{ > + Token *pName; > + Token *pEnd; > + Decl *pDecl; > + > + for (pEnd = pFirst; pEnd; pEnd = pEnd->pNext) { > + if (pEnd->zText[0] == '{' || pEnd->zText[0] == ';') { > + *pReset = pEnd->zText[0]; > + break; > + } > + } > + if (pEnd == 0) { > + *pReset = ';'; > + fprintf(stderr, > + "%s:%d: incomplete inline procedure definition\n", > + zFilename, pFirst->nLine); > + return 1; > + } > + pName = FindDeclName(pFirst, pEnd); > + if (pName == 0) { > + fprintf(stderr, > + "%s:%d: malformed inline procedure definition\n", > + zFilename, pFirst->nLine); > + return 1; > + } > + > +#ifdef DEBUG > + if (debugMask & PARSER) { > + printf("**** Found inline routine: %.*s on line %d...\n", > + pName->nText, pName->zText, pFirst->nLine); > + PrintTokens(pFirst, pEnd); > + printf("\n"); > + } > +#endif > + pDecl = CreateDecl(pName->zText, pName->nText); > + pDecl->pComment = pFirst->pComment; > + DeclSetProperty(pDecl, TY_Subroutine); > + pDecl->zDecl = TokensToString(pFirst, pEnd, ";\n", 0, 0); > + if ((flags & (PS_Static | PS_Local | PS_Local2))) { > + DeclSetProperty(pDecl, DP_Local); > + } else if (flags & (PS_Export | PS_Export2)) { > + DeclSetProperty(pDecl, DP_Export); > + } > + > + if (flags & DP_Cplusplus) { > + DeclSetProperty(pDecl, DP_Cplusplus); > + } else { > + DeclSetProperty(pDecl, DP_ExternCReqd); > + } > + > + return 0; > +} > + > +/* > +** Determine if the tokens between pFirst and pEnd form a variable > +** definition or a function prototype. Return TRUE if we are dealing > +** with a variable defintion and FALSE for a prototype. > +** > +** pEnd is the token that ends the object. It can be either a ';' or > +** a '='. If it is '=', then assume we have a variable definition. > +** > +** If pEnd is ';', then the determination is more difficult. We have > +** to search for an occurrence of an ID followed immediately by '('. > +** If found, we have a prototype. Otherwise we are dealing with a > +** variable definition. > +*/ > +static int isVariableDef(Token *pFirst, Token *pEnd) > +{ > + if (pEnd && pEnd->zText[0] == '=' && > + (pEnd->pPrev->nText != 8 || > + strncmp(pEnd->pPrev->zText, "operator", 8) != 0)) { > + return 1; > + } > + while (pFirst && pFirst != pEnd && pFirst->pNext && > + pFirst->pNext != pEnd) { > + if (pFirst->eType == TT_Id && pFirst->pNext->zText[0] == '(') { > + return 0; > + } > + pFirst = pFirst->pNext; > + } > + return 1; > +} > + > +/* > +** Return TRUE if pFirst is the first token of a static assert. > +*/ > +static int isStaticAssert(Token *pFirst) > +{ > + if ((pFirst->nText == 13 && > + strncmp(pFirst->zText, "static_assert", 13) == 0) || > + (pFirst->nText == 14 && > + strncmp(pFirst->zText, "_Static_assert", 14) == 0)) { > + return 1; > + } else { > + return 0; > + } > +} > + > +/* > +** This routine is called whenever we encounter a ";" or "=". The stuff > +** between pFirst and pLast constitutes either a typedef or a global > +** variable definition. Do the right thing. > +*/ > +static int ProcessDecl(Token *pFirst, Token *pEnd, int flags) > +{ > + Token *pName; > + Decl *pDecl; > + int isLocal = 0; > + int isVar; > + int nErr = 0; > + > + if (pFirst == 0 || pEnd == 0) { > + return 0; > + } > + if (flags & PS_Typedef) { > + if ((flags & (PS_Export2 | PS_Local2)) != 0) { > + fprintf(stderr, > + "%s:%d: \"EXPORT\" or \"LOCAL\" ignored before typedef.\n", > + zFilename, pFirst->nLine); > + nErr++; > + } > + if ((flags & (PS_Interface | PS_Export | PS_Local | > + DP_Cplusplus)) == 0) { > + /* It is illegal to duplicate a typedef in C (but OK in > + *C++). > + ** So don't record typedefs that aren't within a C++ > + *file or > + ** within #if INTERFACE..#endif */ > + return nErr; > + } > + if ((flags & (PS_Interface | PS_Export | PS_Local)) == 0 && > + proto_static == 0) { > + /* Ignore typedefs that are not with "#if > + *INTERFACE..#endif" unless > + ** the "-local" command line option is used. */ > + return nErr; > + } > + if ((flags & (PS_Interface | PS_Export)) == 0) { > + /* typedefs are always local, unless within #if > + * INTERFACE..#endif */ > + isLocal = 1; > + } > + } else if (flags & (PS_Static | PS_Local2)) { > + if (proto_static == 0 && (flags & PS_Local2) == 0) { > + /* Don't record static variables unless the "-local" > + *command line > + ** option was specified or the "LOCAL" keyword is used. > + */ > + return nErr; > + } > + while (pFirst != 0 && pFirst->pNext != pEnd && > + ((pFirst->nText == 6 && > + strncmp(pFirst->zText, "static", 6) == 0) || > + (pFirst->nText == 5 && > + strncmp(pFirst->zText, "LOCAL", 6) == 0))) { > + /* Lose the initial "static" or local from local > + *variables. > + ** We'll prepend "extern" later. */ > + pFirst = pFirst->pNext; > + isLocal = 1; > + } > + if (pFirst == 0 || !isLocal) { > + return nErr; > + } > + } else if (flags & PS_Method) { > + /* Methods are declared by their class. Don't declare > + * separately. */ > + return nErr; > + } else if (isStaticAssert(pFirst)) { > + return 0; > + } > + isVar = (flags & (PS_Typedef | PS_Method)) == 0 && > + isVariableDef(pFirst, pEnd); > + if (isVar && (flags & (PS_Interface | PS_Export | PS_Local)) != 0 && > + (flags & PS_Extern) == 0) { > + fprintf(stderr, > + "%s:%d: Can't define a variable in this context\n", > + zFilename, pFirst->nLine); > + nErr++; > + } > + pName = FindDeclName(pFirst, pEnd->pPrev); > + if (pName == 0) { > + if (pFirst->nText == 4 && > + strncmp(pFirst->zText, "enum", 4) == 0) { > + /* Ignore completely anonymous enums. See documentation > + * section 3.8.1. */ > + return nErr; > + } else { > + fprintf(stderr, > + "%s:%d: Can't find a name for the object declared here.\n", > + zFilename, pFirst->nLine); > + return nErr + 1; > + } > + } > + > +#ifdef DEBUG > + if (debugMask & PARSER) { > + if (flags & PS_Typedef) { > + printf("**** Found typedef %.*s at line %d...\n", > + pName->nText, pName->zText, pName->nLine); > + } else if (isVar) { > + printf("**** Found variable %.*s at line %d...\n", > + pName->nText, pName->zText, pName->nLine); > + } else { > + printf("**** Found prototype %.*s at line %d...\n", > + pName->nText, pName->zText, pName->nLine); > + } > + PrintTokens(pFirst, pEnd->pPrev); > + printf(";\n"); > + } > +#endif > + > + pDecl = CreateDecl(pName->zText, pName->nText); > + if ((flags & PS_Typedef)) { > + DeclSetProperty(pDecl, TY_Typedef); > + } else if (isVar) { > + DeclSetProperty(pDecl, DP_ExternReqd | TY_Variable); > + if (!(flags & DP_Cplusplus)) { > + DeclSetProperty(pDecl, DP_ExternCReqd); > + } > + } else { > + DeclSetProperty(pDecl, TY_Subroutine); > + if (!(flags & DP_Cplusplus)) { > + DeclSetProperty(pDecl, DP_ExternCReqd); > + } > + } > + pDecl->pComment = pFirst->pComment; > + pDecl->zDecl = TokensToString(pFirst, pEnd->pPrev, ";\n", 0, 0); > + if (isLocal || (flags & (PS_Local | PS_Local2)) != 0) { > + DeclSetProperty(pDecl, DP_Local); > + } else if (flags & (PS_Export | PS_Export2)) { > + DeclSetProperty(pDecl, DP_Export); > + } > + if (flags & DP_Cplusplus) { > + DeclSetProperty(pDecl, DP_Cplusplus); > + } > + return nErr; > +} > + > +/* > +** Push an if condition onto the if stack > +*/ > +static void PushIfMacro(const char *zPrefix, /* A prefix, like "define" or "!" > + */ > + const char *zText, /* The condition */ > + int nText, /* Number of characters in zText */ > + int nLine, /* Line number where this macro occurs */ > + int flags /* Either 0, PS_Interface, PS_Export or > + PS_Local */ > +) > +{ > + Ifmacro *pIf; > + int nByte; > + > + nByte = sizeof(Ifmacro); > + if (zText) { > + if (zPrefix) { > + nByte += strlen(zPrefix) + 2; > + } > + nByte += nText + 1; > + } > + pIf = SafeMalloc(nByte); > + if (zText) { > + pIf->zCondition = (char *)&pIf[1]; > + if (zPrefix) { > + sprintf(pIf->zCondition, "%s(%.*s)", zPrefix, nText, > + zText); > + } else { > + sprintf(pIf->zCondition, "%.*s", nText, zText); > + } > + } else { > + pIf->zCondition = 0; > + } > + pIf->nLine = nLine; > + pIf->flags = flags; > + pIf->pNext = ifStack; > + ifStack = pIf; > +} > + > +/* > +** This routine is called to handle all preprocessor directives. > +** > +** This routine will recompute the value of *pPresetFlags to be the > +** logical or of all flags on all nested #ifs. The #ifs that set flags > +** are as follows: > +** > +** conditional flag set > +** ------------------------ -------------------- > +** #if INTERFACE PS_Interface > +** #if EXPORT_INTERFACE PS_Export > +** #if LOCAL_INTERFACE PS_Local > +** > +** For example, if after processing the preprocessor token given > +** by pToken there is an "#if INTERFACE" on the preprocessor > +** stack, then *pPresetFlags will be set to PS_Interface. > +*/ > +static int ParsePreprocessor(Token *pToken, int flags, int *pPresetFlags) > +{ > + const char *zCmd; > + int nCmd; > + const char *zArg; > + int nArg; > + int nErr = 0; > + Ifmacro *pIf; > + > + zCmd = &pToken->zText[1]; > + while (isspace(*zCmd) && *zCmd != '\n') { > + zCmd++; > + } > + if (!isalpha(*zCmd)) { > + return 0; > + } > + nCmd = 1; > + while (isalpha(zCmd[nCmd])) { > + nCmd++; > + } > + > + if (nCmd == 5 && strncmp(zCmd, "endif", 5) == 0) { > + /* > + ** Pop the if stack > + */ > + pIf = ifStack; > + if (pIf == 0) { > + fprintf(stderr, "%s:%d: extra '#endif'.\n", zFilename, > + pToken->nLine); > + return 1; > + } > + ifStack = pIf->pNext; > + SafeFree(pIf); > + } else if (nCmd == 6 && strncmp(zCmd, "define", 6) == 0) { > + /* > + ** Record a #define if we are in PS_Interface or PS_Export > + */ > + Decl *pDecl; > + if (!(flags & (PS_Local | PS_Interface | PS_Export))) { > + return 0; > + } > + zArg = &zCmd[6]; > + while (*zArg && isspace(*zArg) && *zArg != '\n') { > + zArg++; > + } > + if (*zArg == 0 || *zArg == '\n') { > + return 0; > + } > + for (nArg = 0; ISALNUM(zArg[nArg]); nArg++) { > + } > + if (nArg == 0) { > + return 0; > + } > + pDecl = CreateDecl(zArg, nArg); > + pDecl->pComment = pToken->pComment; > + DeclSetProperty(pDecl, TY_Macro); > + pDecl->zDecl = SafeMalloc(pToken->nText + 2); > + sprintf(pDecl->zDecl, "%.*s\n", pToken->nText, pToken->zText); > + if (flags & PS_Export) { > + DeclSetProperty(pDecl, DP_Export); > + } else if (flags & PS_Local) { > + DeclSetProperty(pDecl, DP_Local); > + } > + } else if (nCmd == 7 && strncmp(zCmd, "include", 7) == 0) { > + /* > + ** Record an #include if we are in PS_Interface or PS_Export > + */ > + Include *pInclude; > + char *zIf; > + > + if (!(flags & (PS_Interface | PS_Export))) { > + return 0; > + } > + zArg = &zCmd[7]; > + while (*zArg && isspace(*zArg)) { > + zArg++; > + } > + for (nArg = 0; !isspace(zArg[nArg]); nArg++) { > + } > + if ((zArg[0] == '"' && zArg[nArg - 1] != '"') || > + (zArg[0] == '<' && zArg[nArg - 1] != '>')) { > + fprintf(stderr, > + "%s:%d: malformed #include statement.\n", > + zFilename, pToken->nLine); > + return 1; > + } > + zIf = GetIfString(); > + if (zIf) { > + pInclude = SafeMalloc(sizeof(Include) + nArg * 2 + > + strlen(zIf) + 10); > + pInclude->zFile = (char *)&pInclude[1]; > + pInclude->zLabel = &pInclude->zFile[nArg + 1]; > + sprintf(pInclude->zFile, "%.*s", nArg, zArg); > + sprintf(pInclude->zLabel, "%.*s:%s", nArg, zArg, zIf); > + pInclude->zIf = &pInclude->zLabel[nArg + 1]; > + SafeFree(zIf); > + } else { > + pInclude = SafeMalloc(sizeof(Include) + nArg + 1); > + pInclude->zFile = (char *)&pInclude[1]; > + sprintf(pInclude->zFile, "%.*s", nArg, zArg); > + pInclude->zIf = 0; > + pInclude->zLabel = pInclude->zFile; > + } > + pInclude->pNext = includeList; > + includeList = pInclude; > + } else if (nCmd == 2 && strncmp(zCmd, "if", 2) == 0) { > + /* > + ** Push an #if. Watch for the special cases of INTERFACE > + ** and EXPORT_INTERFACE and LOCAL_INTERFACE > + */ > + zArg = &zCmd[2]; > + while (*zArg && isspace(*zArg) && *zArg != '\n') { > + zArg++; > + } > + if (*zArg == 0 || *zArg == '\n') { > + return 0; > + } > + nArg = pToken->nText + (int)(pToken->zText - zArg); > + if (pToken->zText[pToken->nText - 1] == '\r') { > + nArg--; > + } > + if (nArg == 9 && strncmp(zArg, "INTERFACE", 9) == 0) { > + PushIfMacro(0, 0, 0, pToken->nLine, PS_Interface); > + } else if (nArg == 16 && > + strncmp(zArg, "EXPORT_INTERFACE", 16) == 0) { > + PushIfMacro(0, 0, 0, pToken->nLine, PS_Export); > + } else if (nArg == 15 && > + strncmp(zArg, "LOCAL_INTERFACE", 15) == 0) { > + PushIfMacro(0, 0, 0, pToken->nLine, PS_Local); > + } else if (nArg == 15 && > + strncmp(zArg, "MAKEHEADERS_STOPLOCAL_INTERFACE", > + 15) == 0) { > + PushIfMacro(0, 0, 0, pToken->nLine, PS_Local); > + } else { > + PushIfMacro(0, zArg, nArg, pToken->nLine, 0); > + } > + } else if (nCmd == 5 && strncmp(zCmd, "ifdef", 5) == 0) { > + /* > + ** Push an #ifdef. > + */ > + zArg = &zCmd[5]; > + while (*zArg && isspace(*zArg) && *zArg != '\n') { > + zArg++; > + } > + if (*zArg == 0 || *zArg == '\n') { > + return 0; > + } > + nArg = pToken->nText + (int)(pToken->zText - zArg); > + if (pToken->zText[pToken->nText - 1] == '\r') { > + nArg--; > + } > + PushIfMacro("defined", zArg, nArg, pToken->nLine, 0); > + } else if (nCmd == 6 && strncmp(zCmd, "ifndef", 6) == 0) { > + /* > + ** Push an #ifndef. > + */ > + zArg = &zCmd[6]; > + while (*zArg && isspace(*zArg) && *zArg != '\n') { > + zArg++; > + } > + if (*zArg == 0 || *zArg == '\n') { > + return 0; > + } > + nArg = pToken->nText + (int)(pToken->zText - zArg); > + if (pToken->zText[pToken->nText - 1] == '\r') { > + nArg--; > + } > + PushIfMacro("!defined", zArg, nArg, pToken->nLine, 0); > + } else if (nCmd == 4 && strncmp(zCmd, "else", 4) == 0) { > + /* > + ** Invert the #if on the top of the stack > + */ > + if (ifStack == 0) { > + fprintf(stderr, "%s:%d: '#else' without an '#if'\n", > + zFilename, pToken->nLine); > + return 1; > + } > + pIf = ifStack; > + if (pIf->zCondition) { > + ifStack = ifStack->pNext; > + PushIfMacro("!", pIf->zCondition, > + strlen(pIf->zCondition), pIf->nLine, 0); > + SafeFree(pIf); > + } else { > + pIf->flags = 0; > + } > + } else { > + /* > + ** This directive can be safely ignored > + */ > + return 0; > + } > + > + /* > + ** Recompute the preset flags > + */ > + *pPresetFlags = 0; > + for (pIf = ifStack; pIf; pIf = pIf->pNext) { > + *pPresetFlags |= pIf->flags; > + } > + > + return nErr; > +} > + > +/* > +** Parse an entire file. Return the number of errors. > +** > +** pList is a list of tokens in the file. Whitespace tokens have been > +** eliminated, and text with {...} has been collapsed into a > +** single TT_Brace token. > +** > +** initFlags are a set of parse flags that should always be set for this > +** file. For .c files this is normally 0. For .h files it is PS_Interface. > +*/ > +static int ParseFile(Token *pList, int initFlags) > +{ > + int nErr = 0; > + Token *pStart = 0; > + int flags = initFlags; > + int presetFlags = initFlags; > + int resetFlag = 0; > + > + includeList = 0; > + while (pList) { > + switch (pList->eType) { > + case TT_EOF: > + goto end_of_loop; > + > + case TT_Preprocessor: > + nErr += ParsePreprocessor(pList, flags, &presetFlags); > + pStart = 0; > + presetFlags |= initFlags; > + flags = presetFlags; > + break; > + > + case TT_Other: > + switch (pList->zText[0]) { > + case ';': > + nErr += ProcessDecl(pStart, pList, flags); > + pStart = 0; > + flags = presetFlags; > + break; > + > + case '=': > + if (pList->pPrev->nText == 8 && > + strncmp(pList->pPrev->zText, "operator", > + 8) == 0) { > + break; > + } > + nErr += ProcessDecl(pStart, pList, flags); > + pStart = 0; > + while (pList && pList->zText[0] != ';') { > + pList = pList->pNext; > + } > + if (pList == 0) > + goto end_of_loop; > + flags = presetFlags; > + break; > + > + case ':': > + if (pList->zText[1] == ':') { > + flags |= PS_Method; > + } > + break; > + > + default: > + break; > + } > + break; > + > + case TT_Braces: > + nErr += ProcessProcedureDef(pStart, pList, flags); > + pStart = 0; > + flags = presetFlags; > + break; > + > + case TT_Id: > + if (pStart == 0) { > + pStart = pList; > + flags = presetFlags; > + } > + resetFlag = 0; > + switch (pList->zText[0]) { > + case 'c': > + if (pList->nText == 5 && > + strncmp(pList->zText, "class", 5) == 0) { > + nErr += ProcessTypeDecl(pList, flags, > + &resetFlag); > + } > + break; > + > + case 'E': > + if (pList->nText == 6 && > + strncmp(pList->zText, "EXPORT", 6) == 0) { > + flags |= PS_Export2; > + /* pStart = 0; */ > + } > + break; > + > + case 'e': > + if (pList->nText == 4 && > + strncmp(pList->zText, "enum", 4) == 0) { > + if (pList->pNext && > + pList->pNext->eType == TT_Braces) { > + pList = pList->pNext; > + } else { > + nErr += ProcessTypeDecl( > + pList, flags, > + &resetFlag); > + } > + } else if (pList->nText == 6 && > + strncmp(pList->zText, "extern", 6) == > + 0) { > + pList = pList->pNext; > + if (pList && pList->nText == 3 && > + strncmp(pList->zText, "\"C\"", 3) == > + 0) { > + pList = pList->pNext; > + flags &= ~DP_Cplusplus; > + } else { > + flags |= PS_Extern; > + } > + pStart = pList; > + } > + break; > + > + case 'i': > + if (pList->nText == 6 && > + strncmp(pList->zText, "inline", 6) == 0 && > + (flags & PS_Static) == 0) { > + nErr += ProcessInlineProc(pList, flags, > + &resetFlag); > + } > + break; > + > + case 'L': > + if (pList->nText == 5 && > + strncmp(pList->zText, "LOCAL", 5) == 0) { > + flags |= PS_Local2; > + pStart = pList; > + } > + break; > + > + case 'P': > + if (pList->nText == 6 && > + strncmp(pList->zText, "PUBLIC", 6) == 0) { > + flags |= PS_Public; > + pStart = pList; > + } else if (pList->nText == 7 && > + strncmp(pList->zText, "PRIVATE", > + 7) == 0) { > + flags |= PS_Private; > + pStart = pList; > + } else if (pList->nText == 9 && > + strncmp(pList->zText, "PROTECTED", > + 9) == 0) { > + flags |= PS_Protected; > + pStart = pList; > + } > + break; > + > + case 's': > + if (pList->nText == 6 && > + strncmp(pList->zText, "struct", 6) == 0) { > + if (pList->pNext && > + pList->pNext->eType == TT_Braces) { > + pList = pList->pNext; > + } else { > + nErr += ProcessTypeDecl( > + pList, flags, > + &resetFlag); > + } > + } else if (pList->nText == 6 && > + strncmp(pList->zText, "static", 6) == > + 0) { > + flags |= PS_Static; > + } > + break; > + > + case 't': > + if (pList->nText == 7 && > + strncmp(pList->zText, "typedef", 7) == 0) { > + flags |= PS_Typedef; > + } > + break; > + > + case 'u': > + if (pList->nText == 5 && > + strncmp(pList->zText, "union", 5) == 0) { > + if (pList->pNext && > + pList->pNext->eType == TT_Braces) { > + pList = pList->pNext; > + } else { > + nErr += ProcessTypeDecl( > + pList, flags, > + &resetFlag); > + } > + } > + break; > + > + default: > + break; > + } > + if (resetFlag != 0) { > + while (pList && pList->zText[0] != resetFlag) { > + pList = pList->pNext; > + } > + if (pList == 0) > + goto end_of_loop; > + pStart = 0; > + flags = presetFlags; > + } > + break; > + > + case TT_String: > + case TT_Number: > + break; > + > + default: > + pStart = pList; > + flags = presetFlags; > + break; > + } > + pList = pList->pNext; > + } > +end_of_loop: > + > + /* Verify that all #ifs have a matching "#endif" */ > + while (ifStack) { > + Ifmacro *pIf = ifStack; > + ifStack = pIf->pNext; > + fprintf(stderr, "%s:%d: This '#if' has no '#endif'\n", > + zFilename, pIf->nLine); > + SafeFree(pIf); > + } > + > + return nErr; > +} > + > +/* > +** If the given Decl object has a non-null zExtra field, then the text > +** of that zExtra field needs to be inserted in the middle of the > +** zDecl field before the last "}" in the zDecl. This routine does that. > +** If the zExtra is NULL, this routine is a no-op. > +** > +** zExtra holds extra method declarations for classes. The declarations > +** have to be inserted into the class definition. > +*/ > +static void InsertExtraDecl(Decl *pDecl) > +{ > + int i; > + String str; > + > + if (pDecl == 0 || pDecl->zExtra == 0 || pDecl->zDecl == 0) > + return; > + i = strlen(pDecl->zDecl) - 1; > + while (i > 0 && pDecl->zDecl[i] != '}') { > + i--; > + } > + StringInit(&str); > + StringAppend(&str, pDecl->zDecl, i); > + StringAppend(&str, pDecl->zExtra, 0); > + StringAppend(&str, &pDecl->zDecl[i], 0); > + SafeFree(pDecl->zDecl); > + SafeFree(pDecl->zExtra); > + pDecl->zDecl = StrDup(StringGet(&str), 0); > + StringReset(&str); > + pDecl->zExtra = 0; > +} > + > +/* > +** Reset the DP_Forward and DP_Declared flags on all Decl structures. > +** Set both flags for anything that is tagged as local and isn't > +** in the file zFilename so that it won't be printing in other files. > +*/ > +static void ResetDeclFlags(char *zFilename) > +{ > + Decl *pDecl; > + > + for (pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext) { > + DeclClearProperty(pDecl, DP_Forward | DP_Declared); > + if (DeclHasProperty(pDecl, DP_Local) && > + pDecl->zFile != zFilename) { > + DeclSetProperty(pDecl, DP_Forward | DP_Declared); > + } > + } > +} > + > +/* > +** Forward declaration of the ScanText() function. > +*/ > +static void ScanText(const char *, GenState *pState); > + > +/* > +** The output in pStr is currently within an #if CONTEXT where context > +** is equal to *pzIf. (*pzIf might be NULL to indicate that we are > +** not within any #if at the moment.) We are getting ready to output > +** some text that needs to be within the context of "#if NEW" where > +** NEW is zIf. Make an appropriate change to the context. > +*/ > +static void ChangeIfContext(const char *zIf, /* The desired #if context */ > + GenState *pState /* Current state of the code > + generator */ > +) > +{ > + if (zIf == 0) { > + if (pState->zIf == 0) > + return; > + StringAppend(pState->pStr, "#endif\n", 0); > + pState->zIf = 0; > + } else { > + if (pState->zIf) { > + if (strcmp(zIf, pState->zIf) == 0) > + return; > + StringAppend(pState->pStr, "#endif\n", 0); > + pState->zIf = 0; > + } > + ScanText(zIf, pState); > + if (pState->zIf != 0) { > + StringAppend(pState->pStr, "#endif\n", 0); > + } > + StringAppend(pState->pStr, "#if ", 0); > + StringAppend(pState->pStr, zIf, 0); > + StringAppend(pState->pStr, "\n", 0); > + pState->zIf = zIf; > + } > +} > + > +/* > +** Add to the string pStr a #include of every file on the list of > +** include files pInclude. The table pTable contains all files that > +** have already been #included at least once. Don't add any > +** duplicates. Update pTable with every new #include that is added. > +*/ > +static void AddIncludes(Include *pInclude, /* Write every #include on this list > + */ > + GenState *pState /* Current state of the code generator > + */ > +) > +{ > + if (pInclude) { > + if (pInclude->pNext) { > + AddIncludes(pInclude->pNext, pState); > + } > + if (IdentTableInsert(pState->pTable, pInclude->zLabel, 0)) { > + ChangeIfContext(pInclude->zIf, pState); > + StringAppend(pState->pStr, "#include ", 0); > + StringAppend(pState->pStr, pInclude->zFile, 0); > + StringAppend(pState->pStr, "\n", 1); > + } > + } > +} > + > +/* > +** Add to the string pStr a declaration for the object described > +** in pDecl. > +** > +** If pDecl has already been declared in this file, detect that > +** fact and abort early. Do not duplicate a declaration. > +** > +** If the needFullDecl flag is false and this object has a forward > +** declaration, then supply the forward declaration only. A later > +** call to CompleteForwardDeclarations() will finish the declaration > +** for us. But if needFullDecl is true, we must supply the full > +** declaration now. Some objects do not have a forward declaration. > +** For those objects, we must print the full declaration now. > +** > +** Because it is illegal to duplicate a typedef in C, care is taken > +** to insure that typedefs for the same identifier are only issued once. > +*/ > +static void DeclareObject(Decl *pDecl, /* The thing to be declared */ > + GenState *pState, /* Current state of the code > + generator */ > + int needFullDecl /* Must have the full declaration. A > + * forward declaration isn't enough > + */ > +) > +{ > + Decl *p; /* The object to be declared */ > + int flag; > + int isCpp; /* True if generating C++ */ > + int doneTypedef = 0; /* True if a typedef has been done for this object > + */ > + > + /* printf("BEGIN %s of > + * %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/ > + /* > + ** For any object that has a forward declaration, go ahead and do the > + ** forward declaration first. > + */ > + isCpp = (pState->flags & DP_Cplusplus) != 0; > + for (p = pDecl; p; p = p->pSameName) { > + if (p->zFwd) { > + if (!DeclHasProperty(p, DP_Forward)) { > + DeclSetProperty(p, DP_Forward); > + if (strncmp(p->zFwd, "typedef", 7) == 0) { > + if (doneTypedef) > + continue; > + doneTypedef = 1; > + } > + ChangeIfContext(p->zIf, pState); > + StringAppend(pState->pStr, > + isCpp ? p->zFwdCpp : p->zFwd, 0); > + } > + } > + } > + > + /* > + ** Early out if everything is already suitably declared. > + ** > + ** This is a very important step because it prevents us from > + ** executing the code the follows in a recursive call to this > + ** function with the same value for pDecl. > + */ > + flag = needFullDecl ? DP_Declared | DP_Forward : DP_Forward; > + for (p = pDecl; p; p = p->pSameName) { > + if (!DeclHasProperty(p, flag)) > + break; > + } > + if (p == 0) { > + return; > + } > + > + /* > + ** Make sure we have all necessary #includes > + */ > + for (p = pDecl; p; p = p->pSameName) { > + AddIncludes(p->pInclude, pState); > + } > + > + /* > + ** Go ahead an mark everything as being declared, to prevent an > + ** infinite loop thru the ScanText() function. At the same time, > + ** we decide which objects need a full declaration and mark them > + ** with the DP_Flag bit. We are only able to use DP_Flag in this > + ** way because we know we'll never execute this far into this > + ** function on a recursive call with the same pDecl. Hence, recursive > + ** calls to this function (through ScanText()) can never change the > + ** value of DP_Flag out from under us. > + */ > + for (p = pDecl; p; p = p->pSameName) { > + if (!DeclHasProperty(p, DP_Declared) && > + (p->zFwd == 0 || needFullDecl) && p->zDecl != 0) { > + DeclSetProperty(p, DP_Forward | DP_Declared | DP_Flag); > + } else { > + DeclClearProperty(p, DP_Flag); > + } > + } > + > + /* > + ** Call ScanText() recursively (this routine is called from ScanText()) > + ** to include declarations required to come before these declarations. > + */ > + for (p = pDecl; p; p = p->pSameName) { > + if (DeclHasProperty(p, DP_Flag)) { > + if (p->zDecl[0] == '#') { > + ScanText(&p->zDecl[1], pState); > + } else { > + InsertExtraDecl(p); > + ScanText(p->zDecl, pState); > + } > + } > + } > + > + /* > + ** Output the declarations. Do this in two passes. First > + ** output everything that isn't a typedef. Then go back and > + ** get the typedefs by the same name. > + */ > + for (p = pDecl; p; p = p->pSameName) { > + if (DeclHasProperty(p, DP_Flag) && > + !DeclHasProperty(p, TY_Typedef)) { > + if (DeclHasAnyProperty(p, TY_Enumeration)) { > + if (doneTypedef) > + continue; > + doneTypedef = 1; > + } > + ChangeIfContext(p->zIf, pState); > + if (!isCpp && DeclHasAnyProperty(p, DP_ExternReqd)) { > + StringAppend(pState->pStr, "extern ", 0); > + } else if (isCpp && > + DeclHasProperty(p, DP_Cplusplus | > + DP_ExternReqd)) { > + StringAppend(pState->pStr, "extern ", 0); > + } else if (isCpp && > + DeclHasAnyProperty( > + p, DP_ExternCReqd | DP_ExternReqd)) { > + StringAppend(pState->pStr, "extern \"C\" ", 0); > + } > + InsertExtraDecl(p); > + StringAppend(pState->pStr, p->zDecl, 0); > + if (!isCpp && DeclHasProperty(p, DP_Cplusplus)) { > + fprintf(stderr, > + "%s: C code ought not reference the C++ object \"%s\"\n", > + pState->zFilename, p->zName); > + pState->nErr++; > + } > + DeclClearProperty(p, DP_Flag); > + } > + } > + for (p = pDecl; p && !doneTypedef; p = p->pSameName) { > + if (DeclHasProperty(p, DP_Flag)) { > + /* This has to be a typedef */ > + doneTypedef = 1; > + ChangeIfContext(p->zIf, pState); > + InsertExtraDecl(p); > + StringAppend(pState->pStr, p->zDecl, 0); > + } > + } > +} > + > +/* > +** This routine scans the input text given, and appends to the > +** string in pState->pStr the text of any declarations that must > +** occur before the text in zText. > +** > +** If an identifier in zText is immediately followed by '*', then > +** only forward declarations are needed for that identifier. If the > +** identifier name is not followed immediately by '*', we must supply > +** a full declaration. > +*/ > +static void ScanText(const char *zText, /* The input text to be scanned */ > + GenState *pState /* Current state of the code generator */ > +) > +{ > + int nextValid = 0; /* True is sNext contains valid data */ > + InStream sIn; /* The input text */ > + Token sToken; /* The current token being examined */ > + Token sNext; /* The next non-space token */ > + > + /* printf("BEGIN SCAN TEXT on %s\n", zText); */ > + > + sIn.z = zText; > + sIn.i = 0; > + sIn.nLine = 1; > + while (sIn.z[sIn.i] != 0) { > + if (nextValid) { > + sToken = sNext; > + nextValid = 0; > + } else { > + GetNonspaceToken(&sIn, &sToken); > + } > + if (sToken.eType == TT_Id) { > + int needFullDecl; /* True if we need to provide the full > + *declaration, > + ** not just the forward declaration */ > + Decl *pDecl; /* The declaration having the name in > + sToken */ > + > + /* > + ** See if there is a declaration in the database with > + *the name given > + ** by sToken. > + */ > + pDecl = FindDecl(sToken.zText, sToken.nText); > + if (pDecl == 0) > + continue; > + > + /* > + ** If we get this far, we've found an identifier that > + *has a > + ** declaration in the database. Now see if we the full > + *declaration > + ** or just a forward declaration. > + */ > + GetNonspaceToken(&sIn, &sNext); > + if (sNext.zText[0] == '*') { > + needFullDecl = 0; > + } else { > + needFullDecl = 1; > + nextValid = sNext.eType == TT_Id; > + } > + > + /* > + ** Generate the needed declaration. > + */ > + DeclareObject(pDecl, pState, needFullDecl); > + } else if (sToken.eType == TT_Preprocessor) { > + sIn.i -= sToken.nText - 1; > + } > + } > + /* printf("END SCANTEXT\n"); */ > +} > + > +/* > +** Provide a full declaration to any object which so far has had only > +** a forward declaration. > +*/ > +static void CompleteForwardDeclarations(GenState *pState) > +{ > + Decl *pDecl; > + int progress; > + > + do { > + progress = 0; > + for (pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext) { > + if (DeclHasProperty(pDecl, DP_Forward) && > + !DeclHasProperty(pDecl, DP_Declared)) { > + DeclareObject(pDecl, pState, 1); > + progress = 1; > + assert(DeclHasProperty(pDecl, DP_Declared)); > + } > + } > + } while (progress); > +} > + > +/* > +** Generate an include file for the given source file. Return the number > +** of errors encountered. > +** > +** if nolocal_flag is true, then we do not generate declarations for > +** objected marked DP_Local. > +*/ > +static int MakeHeader(InFile *pFile, FILE *report, int nolocal_flag) > +{ > + int nErr = 0; > + GenState sState; > + String outStr; > + IdentTable includeTable; > + Ident *pId; > + char *zNewVersion; > + char *zOldVersion; > + > + if (pFile->zHdr == 0 || *pFile->zHdr == 0) > + return 0; > + sState.pStr = &outStr; > + StringInit(&outStr); > + StringAppend(&outStr, zTopLine, nTopLine); > + sState.pTable = &includeTable; > + memset(&includeTable, 0, sizeof(includeTable)); > + sState.zIf = 0; > + sState.nErr = 0; > + sState.zFilename = pFile->zSrc; > + sState.flags = pFile->flags & DP_Cplusplus; > + ResetDeclFlags(nolocal_flag ? "no" : pFile->zSrc); > + for (pId = pFile->idTable.pList; pId; pId = pId->pNext) { > + Decl *pDecl = FindDecl(pId->zName, 0); > + if (pDecl) { > + DeclareObject(pDecl, &sState, 1); > + } > + } > + CompleteForwardDeclarations(&sState); > + ChangeIfContext(0, &sState); > + nErr += sState.nErr; > + zOldVersion = ReadFile(pFile->zHdr); > + zNewVersion = StringGet(&outStr); > + if (report) > + fprintf(report, "%s: ", pFile->zHdr); > + if (zOldVersion == 0) { > + if (report) > + fprintf(report, "updated\n"); > + if (WriteFile(pFile->zHdr, zNewVersion)) { > + fprintf(stderr, "%s: Can't write to file\n", > + pFile->zHdr); > + nErr++; > + } > + } else if (strncmp(zOldVersion, zTopLine, nTopLine) != 0) { > + if (report) > + fprintf(report, "error!\n"); > + fprintf(stderr, > + "%s: Can't overwrite this file because it wasn't previously\n" > + "%*s generated by 'makeheaders'.\n", > + pFile->zHdr, (int)strlen(pFile->zHdr), ""); > + nErr++; > + } else if (strcmp(zOldVersion, zNewVersion) != 0) { > + if (report) > + fprintf(report, "updated\n"); > + if (WriteFile(pFile->zHdr, zNewVersion)) { > + fprintf(stderr, "%s: Can't write to file\n", > + pFile->zHdr); > + nErr++; > + } > + } else if (report) { > + fprintf(report, "unchanged\n"); > + } > + SafeFree(zOldVersion); > + IdentTableReset(&includeTable); > + StringReset(&outStr); > + return nErr; > +} > + > +/* > +** Generate a global header file -- a header file that contains all > +** declarations. If the forExport flag is true, then only those > +** objects that are exported are included in the header file. > +*/ > +static int MakeGlobalHeader(int forExport) > +{ > + GenState sState; > + String outStr; > + IdentTable includeTable; > + Decl *pDecl; > + > + sState.pStr = &outStr; > + StringInit(&outStr); > + /* StringAppend(&outStr,zTopLine,nTopLine); */ > + sState.pTable = &includeTable; > + memset(&includeTable, 0, sizeof(includeTable)); > + sState.zIf = 0; > + sState.nErr = 0; > + sState.zFilename = "(all)"; > + sState.flags = 0; > + ResetDeclFlags(0); > + for (pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext) { > + if (forExport == 0 || DeclHasProperty(pDecl, DP_Export)) { > + DeclareObject(pDecl, &sState, 1); > + } > + } > + ChangeIfContext(0, &sState); > + printf("%s", StringGet(&outStr)); > + IdentTableReset(&includeTable); > + StringReset(&outStr); > + return 0; > +} > + > +#ifdef DEBUG > +/* > +** Return the number of characters in the given string prior to the > +** first newline. > +*/ > +static int ClipTrailingNewline(char *z) > +{ > + int n = strlen(z); > + while (n > 0 && (z[n - 1] == '\n' || z[n - 1] == '\r')) { > + n--; > + } > + return n; > +} > + > +/* > +** Dump the entire declaration list for debugging purposes > +*/ > +static void DumpDeclList(void) > +{ > + Decl *pDecl; > + > + for (pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext) { > + printf("**** %s from file %s ****\n", pDecl->zName, > + pDecl->zFile); > + if (pDecl->zIf) { > + printf("If: [%.*s]\n", ClipTrailingNewline(pDecl->zIf), > + pDecl->zIf); > + } > + if (pDecl->zFwd) { > + printf("Decl: [%.*s]\n", > + ClipTrailingNewline(pDecl->zFwd), pDecl->zFwd); > + } > + if (pDecl->zDecl) { > + InsertExtraDecl(pDecl); > + printf("Def: [%.*s]\n", > + ClipTrailingNewline(pDecl->zDecl), pDecl->zDecl); > + } > + if (pDecl->flags) { > + static struct { > + int mask; > + char *desc; > + } flagSet[] = { > + { TY_Class, "class" }, > + { TY_Enumeration, "enum" }, > + { TY_Structure, "struct" }, > + { TY_Union, "union" }, > + { TY_Variable, "variable" }, > + { TY_Subroutine, "function" }, > + { TY_Typedef, "typedef" }, > + { TY_Macro, "macro" }, > + { DP_Export, "export" }, > + { DP_Local, "local" }, > + { DP_Cplusplus, "C++" }, > + }; > + int i; > + printf("flags:"); > + for (i = 0; i < sizeof(flagSet) / sizeof(flagSet[0]); > + i++) { > + if (flagSet[i].mask & pDecl->flags) { > + printf(" %s", flagSet[i].desc); > + } > + } > + printf("\n"); > + } > + if (pDecl->pInclude) { > + Include *p; > + printf("includes:"); > + for (p = pDecl->pInclude; p; p = p->pNext) { > + printf(" %s", p->zFile); > + } > + printf("\n"); > + } > + } > +} > +#endif > + > +/* > +** When the "-doc" command-line option is used, this routine is called > +** to print all of the database information to standard output. > +*/ > +static void DocumentationDump(void) > +{ > + Decl *pDecl; > + static struct { > + int mask; > + char flag; > + } flagSet[] = { > + { TY_Class, 'c' }, { TY_Enumeration, 'e' }, > + { TY_Structure, 's' }, { TY_Union, 'u' }, > + { TY_Variable, 'v' }, { TY_Subroutine, 'f' }, > + { TY_Typedef, 't' }, { TY_Macro, 'm' }, > + { DP_Export, 'x' }, { DP_Local, 'l' }, > + { DP_Cplusplus, '+' }, > + }; > + > + for (pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext) { > + int i; > + int nLabel = 0; > + char *zDecl; > + char zLabel[50]; > + for (i = 0; i < sizeof(flagSet) / sizeof(flagSet[0]); i++) { > + if (DeclHasProperty(pDecl, flagSet[i].mask)) { > + zLabel[nLabel++] = flagSet[i].flag; > + } > + } > + if (nLabel == 0) > + continue; > + zLabel[nLabel] = 0; > + InsertExtraDecl(pDecl); > + zDecl = pDecl->zDecl; > + if (zDecl == 0) > + zDecl = pDecl->zFwd; > + printf("%s %s %s %p %d %d %d %d %d\n", pDecl->zName, zLabel, > + pDecl->zFile, pDecl->pComment, > + pDecl->pComment ? pDecl->pComment->nText + 1 : 0, > + pDecl->zIf ? (int)strlen(pDecl->zIf) + 1 : 0, > + zDecl ? (int)strlen(zDecl) : 0, > + pDecl->pComment ? pDecl->pComment->nLine : 0, > + pDecl->tokenCode.nText ? pDecl->tokenCode.nText + 1 : 0); > + if (pDecl->pComment) { > + printf("%.*s\n", pDecl->pComment->nText, > + pDecl->pComment->zText); > + } > + if (pDecl->zIf) { > + printf("%s\n", pDecl->zIf); > + } > + if (zDecl) { > + printf("%s", zDecl); > + } > + if (pDecl->tokenCode.nText) { > + printf("%.*s\n", pDecl->tokenCode.nText, > + pDecl->tokenCode.zText); > + } > + } > +} > + > +/* > +** Given the complete text of an input file, this routine prints a > +** documentation record for the header comment at the beginning of the > +** file (if the file has a header comment.) > +*/ > +void PrintModuleRecord(const char *zFile, const char *zFilename) > +{ > + int i; > + static int addr = 5; > + while (isspace(*zFile)) { > + zFile++; > + } > + if (*zFile != '/' || zFile[1] != '*') > + return; > + for (i = 2; zFile[i] && (zFile[i - 1] != '/' || zFile[i - 2] != '*'); > + i++) { > + } > + if (zFile[i] == 0) > + return; > + printf("%s M %s %d %d 0 0 0 0\n%.*s\n", zFilename, zFilename, addr, > + i + 1, i, zFile); > + addr += 4; > +} > + > +/* > +** Given an input argument to the program, construct a new InFile > +** object. > +*/ > +static InFile *CreateInFile(char *zArg, int *pnErr) > +{ > + int nSrc; > + char *zSrc; > + InFile *pFile; > + int i; > + > + /* > + ** Get the name of the input file to be scanned. The input file is > + ** everything before the first ':' or the whole file if no ':' is seen. > + ** > + ** Except, on windows, ignore any ':' that occurs as the second > + *character > + ** since it might be part of the drive specifier. So really, the ":' > + *has > + ** to be the 3rd or later character in the name. This precludes > + *1-character > + ** file names, which really should not be a problem. > + */ > + zSrc = zArg; > + for (nSrc = 2; zSrc[nSrc] && zArg[nSrc] != ':'; nSrc++) { > + } > + pFile = SafeMalloc(sizeof(InFile)); > + memset(pFile, 0, sizeof(InFile)); > + pFile->zSrc = StrDup(zSrc, nSrc); > + > + /* Figure out if we are dealing with C or C++ code. Assume any > + ** file with ".c" or ".h" is C code and all else is C++. > + */ > + if (nSrc > 2 && zSrc[nSrc - 2] == '.' && > + (zSrc[nSrc - 1] == 'c' || zSrc[nSrc - 1] == 'h')) { > + pFile->flags &= ~DP_Cplusplus; > + } else { > + pFile->flags |= DP_Cplusplus; > + } > + > + /* > + ** If a separate header file is specified, use it > + */ > + if (zSrc[nSrc] == ':') { > + int nHdr; > + char *zHdr; > + zHdr = &zSrc[nSrc + 1]; > + for (nHdr = 0; zHdr[nHdr]; nHdr++) { > + } > + pFile->zHdr = StrDup(zHdr, nHdr); > + } > + > + /* Look for any 'c' or 'C' in the suffix of the file name and change > + ** that character to 'h' or 'H' respectively. If no 'c' or 'C' is > + *found, > + ** then assume we are dealing with a header. > + */ > + else { > + int foundC = 0; > + pFile->zHdr = StrDup(zSrc, nSrc); > + for (i = nSrc - 1; i > 0 && pFile->zHdr[i] != '.'; i--) { > + if (pFile->zHdr[i] == 'c') { > + foundC = 1; > + pFile->zHdr[i] = 'h'; > + } else if (pFile->zHdr[i] == 'C') { > + foundC = 1; > + pFile->zHdr[i] = 'H'; > + } > + } > + if (!foundC) { > + SafeFree(pFile->zHdr); > + pFile->zHdr = 0; > + } > + } > + > + /* > + ** If pFile->zSrc contains no 'c' or 'C' in its extension, it > + ** must be a header file. In that case, we need to set the > + ** PS_Interface flag. > + */ > + pFile->flags |= PS_Interface; > + for (i = nSrc - 1; i > 0 && zSrc[i] != '.'; i--) { > + if (zSrc[i] == 'c' || zSrc[i] == 'C') { > + pFile->flags &= ~PS_Interface; > + break; > + } > + } > + > + /* Done! > + */ > + return pFile; > +} > + > +/* MS-Windows and MS-DOS both have the following serious OS bug: the > +** length of a command line is severely restricted. But this program > +** occasionally requires long command lines. Hence the following > +** work around. > +** > +** If the parameters "-f FILENAME" appear anywhere on the command line, > +** then the named file is scanned for additional command line arguments. > +** These arguments are substituted in place of the "FILENAME" argument > +** in the original argument list. > +** > +** This first parameter to this routine is the index of the "-f" > +** parameter in the argv[] array. The argc and argv are passed by > +** pointer so that they can be changed. > +** > +** Parsing of the parameters in the file is very simple. Parameters > +** can be separated by any amount of white-space (including newlines > +** and carriage returns.) There are now quoting characters of any > +** kind. The length of a token is limited to about 1000 characters. > +*/ > +static void AddParameters(int index, int *pArgc, char ***pArgv) > +{ > + int argc = *pArgc; /* The original argc value */ > + char **argv = *pArgv; /* The original argv value */ > + int newArgc; /* Value for argc after inserting new arguments */ > + char **zNew = 0; /* The new argv after this routine is done */ > + char *zFile; /* Name of the input file */ > + int nNew = 0; /* Number of new entries in the argv[] file */ > + int nAlloc = 0; /* Space allocated for zNew[] */ > + int i; /* Loop counter */ > + int n; /* Number of characters in a new argument */ > + int c; /* Next character of input */ > + int startOfLine = 1; /* True if we are where '#' can start a comment */ > + FILE *in; /* The input file */ > + char zBuf[1000]; /* A single argument is accumulated here */ > + > + if (index + 1 == argc) > + return; > + zFile = argv[index + 1]; > + in = fopen(zFile, "r"); > + if (in == 0) { > + fprintf(stderr, "Can't open input file \"%s\"\n", zFile); > + exit(1); > + } > + c = ' '; > + while (c != EOF) { > + while (c != EOF && isspace(c)) { > + if (c == '\n') { > + startOfLine = 1; > + } > + c = getc(in); > + if (startOfLine && c == '#') { > + while (c != EOF && c != '\n') { > + c = getc(in); > + } > + } > + } > + n = 0; > + while (c != EOF && !isspace(c)) { > + if (n < sizeof(zBuf) - 1) { > + zBuf[n++] = c; > + } > + startOfLine = 0; > + c = getc(in); > + } > + zBuf[n] = 0; > + if (n > 0) { > + nNew++; > + if (nNew + argc > nAlloc) { > + if (nAlloc == 0) { > + nAlloc = 100 + argc; > + zNew = malloc(sizeof(char *) * nAlloc); > + } else { > + nAlloc *= 2; > + zNew = realloc(zNew, > + sizeof(char *) * nAlloc); > + } > + } > + if (zNew) { > + int j = nNew + index; > + zNew[j] = malloc(n + 1); > + if (zNew[j]) { > + strcpy(zNew[j], zBuf); > + } > + } > + } > + } > + fclose(in); > + newArgc = argc + nNew - 1; > + for (i = 0; i <= index; i++) { > + zNew[i] = argv[i]; > + } > + for (i = nNew + index + 1; i < newArgc; i++) { > + zNew[i] = argv[i + 1 - nNew]; > + } > + zNew[newArgc] = 0; > + *pArgc = newArgc; > + *pArgv = zNew; > +} > + > +#ifdef NOT_USED > +/* > +** Return the time that the given file was last modified. If we can't > +** locate the file (because, for example, it doesn't exist), then > +** return 0. > +*/ > +static unsigned int ModTime(const char *zFilename) > +{ > + unsigned int mTime = 0; > + struct stat sStat; > + if (stat(zFilename, &sStat) == 0) { > + mTime = sStat.st_mtime; > + } > + return mTime; > +} > +#endif > + > +/* > +** Print a usage comment for this program. > +*/ > +static void Usage(const char *argv0, const char *argvN) > +{ > + fprintf(stderr, "%s: Illegal argument \"%s\"\n", argv0, argvN); > + fprintf(stderr, > + "Usage: %s [options] filename...\n" > + "Options:\n" > + " -h Generate a single .h to standard output.\n" > + " -H Like -h, but only output EXPORT declarations.\n" > + " -v (verbose) Write status information to the screen.\n" > + " -doc Generate no header files. Instead, output information\n" > + " that can be used by an automatic program documentation\n" > + " and cross-reference generator.\n" > + " -local Generate prototypes for \"static\" functions and\n" > + " procedures.\n" > + " -f FILE Read additional command-line arguments from the file named\n" > + " \"FILE\".\n" > +#ifdef DEBUG > + " -! MASK Set the debugging mask to the number \"MASK\".\n" > +#endif > + " -- Treat all subsequent comment-line parameters as filenames,\n" > + " even if they begin with \"-\".\n", > + argv0); > +} > + > +/* > +** The following text contains a few simple #defines that we want > +** to be available to every file. > +*/ > +static const char zInit[] = "#define INTERFACE 0\n" > + "#define EXPORT_INTERFACE 0\n" > + "#define LOCAL_INTERFACE 0\n" > + "#define EXPORT\n" > + "#define LOCAL static\n" > + "#define PUBLIC\n" > + "#define PRIVATE\n" > + "#define PROTECTED\n"; > + > +#if TEST == 0 > +int main(int argc, char **argv) > +{ > + int i; /* Loop counter */ > + int nErr = 0; /* Number of errors encountered */ > + Token *pList; /* List of input tokens for one file */ > + InFile *pFileList = 0; /* List of all input files */ > + InFile *pTail = 0; /* Last file on the list */ > + InFile *pFile; /* for looping over the file list */ > + int h_flag = 0; /* True if -h is present. Output unified header */ > + int H_flag = 0; /* True if -H is present. Output EXPORT header */ > + int v_flag = 0; /* Verbose */ > + int noMoreFlags; /* True if -- has been seen. */ > + FILE *report; /* Send progress reports to this, if not NULL */ > + > + noMoreFlags = 0; > + for (i = 1; i < argc; i++) { > + if (argv[i][0] == '-' && !noMoreFlags) { > + switch (argv[i][1]) { > + case 'h': > + h_flag = 1; > + break; > + case 'H': > + H_flag = 1; > + break; > + case 'v': > + v_flag = 1; > + break; > + case 'd': > + doc_flag = 1; > + proto_static = 1; > + break; > + case 'l': > + proto_static = 1; > + break; > + case 'f': > + AddParameters(i, &argc, &argv); > + break; > + case '-': > + noMoreFlags = 1; > + break; > +#ifdef DEBUG > + case '!': > + i++; > + debugMask = strtol(argv[i], 0, 0); > + break; > +#endif > + default: > + Usage(argv[0], argv[i]); > + return 1; > + } > + } else { > + pFile = CreateInFile(argv[i], &nErr); > + if (pFile) { > + if (pFileList) { > + pTail->pNext = pFile; > + pTail = pFile; > + } else { > + pFileList = pTail = pFile; > + } > + } > + } > + } > + if (h_flag && H_flag) { > + h_flag = 0; > + } > + if (v_flag) { > + report = (h_flag || H_flag) ? stderr : stdout; > + } else { > + report = 0; > + } > + if (nErr > 0) { > + return nErr; > + } > + for (pFile = pFileList; pFile; pFile = pFile->pNext) { > + char *zFile; > + > + zFilename = pFile->zSrc; > + if (zFilename == 0) > + continue; > + zFile = ReadFile(zFilename); > + if (zFile == 0) { > + fprintf(stderr, "Can't read input file \"%s\"\n", > + zFilename); > + nErr++; > + continue; > + } > + if (strncmp(zFile, zTopLine, nTopLine) == 0) { > + pFile->zSrc = 0; > + } else { > + if (report) > + fprintf(report, "Reading %s...\n", zFilename); > + pList = TokenizeFile(zFile, &pFile->idTable); > + if (pList) { > + nErr += ParseFile(pList, pFile->flags); > + FreeTokenList(pList); > + } else if (zFile[0] == 0) { > + fprintf(stderr, "Input file \"%s\" is empty.\n", > + zFilename); > + nErr++; > + } else { > + fprintf(stderr, > + "Errors while processing \"%s\"\n", > + zFilename); > + nErr++; > + } > + } > + if (!doc_flag) > + SafeFree(zFile); > + if (doc_flag) > + PrintModuleRecord(zFile, zFilename); > + } > + if (nErr > 0) { > + return nErr; > + } > +#ifdef DEBUG > + if (debugMask & DECL_DUMP) { > + DumpDeclList(); > + return nErr; > + } > +#endif > + if (doc_flag) { > + DocumentationDump(); > + return nErr; > + } > + zFilename = "--internal--"; > + pList = TokenizeFile(zInit, 0); > + if (pList == 0) { > + return nErr + 1; > + } > + ParseFile(pList, PS_Interface); > + FreeTokenList(pList); > + if (h_flag || H_flag) { > + nErr += MakeGlobalHeader(H_flag); > + } else { > + for (pFile = pFileList; pFile; pFile = pFile->pNext) { > + if (pFile->zSrc == 0) > + continue; > + nErr += MakeHeader(pFile, report, 0); > + } > + } > + return nErr; > +} > +#endif > > base-commit: bcd6bc478adc4951d57ec597c44b12ee74bc88fb > -- > gitgitgadget