summaryrefslogtreecommitdiffstats
path: root/tkhtml1/tools/makeheaders.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkhtml1/tools/makeheaders.c')
-rw-r--r--tkhtml1/tools/makeheaders.c3129
1 files changed, 3129 insertions, 0 deletions
diff --git a/tkhtml1/tools/makeheaders.c b/tkhtml1/tools/makeheaders.c
new file mode 100644
index 0000000..6d05935
--- /dev/null
+++ b/tkhtml1/tools/makeheaders.c
@@ -0,0 +1,3129 @@
+/*
+** This program scans C and C++ source files and automatically generates
+** appropriate header files.
+** %Z% %P% %I% %G% %Z%
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <assert.h>
+#ifndef WIN32
+# include <unistd.h>
+#else
+# include <string.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. */
+ 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 */
+ 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. */
+
+/*
+** 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 null 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.
+*/
+const char zTopLine[] =
+ "/* \aThis file was automatically generated. Do not edit! */\n";
+#define nTopLine (sizeof(zTopLine)-1)
+
+/*
+** The name of the file currently being parsed.
+*/
+static 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++;
+ }
+ if( h<0 ) h = -h;
+ return h;
+}
+
+/*
+** 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 multple 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;
+ }
+ 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;
+
+ 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:
+ 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 *z, *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;
+ }
+
+ z = pIn->z;
+ 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.
+**
+** The characters ";\n" are always appended.
+*/
+static char *TokensToString(Token *pFirst, Token *pLast){
+ char *zReturn;
+ String str;
+ int needSpace = 0;
+ int c;
+
+ StringInit(&str);
+ pLast = pLast->pNext;
+ while( pFirst!=pLast ){
+ 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:
+ if( pFirst->nText==6 && pFirst->zText[0]=='E'
+ && strncmp(pFirst->zText,"EXPORT",6)==0 ){
+ break;
+ }
+ /* 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;
+ }
+ StringAppend(&str,";\n",2);
+ 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;
+
+ *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 ';':
+ 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 immedately 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
+ pDecl = CreateDecl(pName->zText,pName->nText);
+ if( (flags & PS_Static) || !(flags & (PS_Interface|PS_Export)) ){
+ DeclSetProperty(pDecl,DP_Local);
+ }
+ switch( *pList->zText ){
+ case 'c': DeclSetProperty(pDecl,TY_Class); break;
+ case 's': DeclSetProperty(pDecl,TY_Structure); break;
+ case 'e': DeclSetProperty(pDecl,TY_Enumeration); break;
+ case 'u': DeclSetProperty(pDecl,TY_Union); break;
+ default: /* Can't Happen */ break;
+ }
+
+ /* 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);
+ }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 char *aWords[] = { "char", "class",
+ "const", "double", "enum", "extern", "EXPORT", "ET_PROC",
+ "float", "int", "long",
+ "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 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 ){
+ 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;
+ }
+
+ /*
+ ** 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);
+ 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);
+ 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 occurance 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]=='=' ){
+ 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;
+}
+
+
+/*
+** 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;
+ }
+ 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 ){
+ 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);
+ 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 - (int)zArg;
+ 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{
+ 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 - (int)zArg;
+ 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 - (int)zArg;
+ 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 '=':
+ 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 ){
+ 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 '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_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;
+}
+
+/*
+** 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 */
+
+ /*
+ ** 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() recusively (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{
+ 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);
+ }
+ 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);
+ 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 */
+
+ 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;
+ }
+ }
+}
+
+/*
+** Provide a full declaration to any object which so far has had only
+** a foward 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"
+ "generated by 'makeheaders'.\n", 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 ){
+ 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;
+ zDecl = pDecl->zDecl;
+ if( zDecl==0 ) zDecl = pDecl->zFwd;
+ printf("%s %s %s %d %d %d %d %d %d\n",
+ pDecl->zName,
+ zLabel,
+ pDecl->zFile,
+ (int)(pDecl->pComment ? (int)pDecl->pComment/sizeof(Token) : 0),
+ (int)(pDecl->pComment ? pDecl->pComment->nText+1 : 0),
+ (int)(pDecl->zIf ? strlen(pDecl->zIf)+1 : 0),
+ (int)(zDecl ? strlen(zDecl) : 0),
+ (int)(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\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
+ */
+ zSrc = zArg;
+ for(nSrc=0; 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] && 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; /* 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 );
+ }
+ }
+ }
+ }
+ 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 char zInit[] =
+ "#define INTERFACE 0\n"
+ "#define EXPORT_INTERFACE 0\n"
+ "#define LOCAL_INTERFACE 0\n"
+ "#define EXPORT\n"
+ "#define LOCAL static\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; /* 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