~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Tina5/tina-libs/scripts/makeheaders.c

Version: ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2 ** This program scans C and C++ source files and automatically generates
  3 ** appropriate header files.
  4 ** %Z% %P% %I% %G% %Z%
  5 */
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <ctype.h>
  9 #include <memory.h>
 10 #include <sys/stat.h>
 11 #include <assert.h>
 12 #ifndef WIN32
 13 # include <unistd.h>
 14 #else
 15 # include <string.h>
 16 #endif
 17 
 18 /*
 19 ** Macros for debugging.
 20 */
 21 #ifdef DEBUG
 22 static int debugMask = 0;
 23 # define debug0(F,M)       if( (F)&debugMask ){ fprintf(stderr,M); }
 24 # define debug1(F,M,A)     if( (F)&debugMask ){ fprintf(stderr,M,A); }
 25 # define debug2(F,M,A,B)   if( (F)&debugMask ){ fprintf(stderr,M,A,B); }
 26 # define debug3(F,M,A,B,C) if( (F)&debugMask ){ fprintf(stderr,M,A,B,C); }
 27 # define PARSER      0x00000001
 28 # define DECL_DUMP   0x00000002
 29 # define TOKENIZER   0x00000004
 30 #else
 31 # define debug0(Flags, Format)
 32 # define debug1(Flags, Format, A)
 33 # define debug2(Flags, Format, A, B)
 34 # define debug3(Flags, Format, A, B, C)
 35 #endif
 36 
 37 /*
 38 ** The following macros are purely for the purpose of testing this
 39 ** program on itself.  They don't really contribute to the code.
 40 */
 41 #define INTERFACE 1
 42 #define EXPORT_INTERFACE 1
 43 #define EXPORT
 44 
 45 /*
 46 ** Each token in a source file is represented by an instance of
 47 ** the following structure.  Tokens are collected onto a list.
 48 */
 49 typedef struct Token Token;
 50 struct Token {
 51   const char *zText;      /* The text of the token */
 52   int nText;              /* Number of characters in the token's text */
 53   int eType;              /* The type of this token */
 54   int nLine;              /* The line number on which the token starts */
 55   Token *pComment;        /* Most recent block comment before this token */
 56   Token *pNext;           /* Next token on the list */
 57   Token *pPrev;           /* Previous token on the list */
 58 };
 59 
 60 /*
 61 ** During tokenization, information about the state of the input
 62 ** stream is held in an instance of the following structure
 63 */
 64 typedef struct InStream InStream;
 65 struct InStream {
 66   const char *z;          /* Complete text of the input */
 67   int i;                  /* Next character to read from the input */
 68   int nLine;              /* The line number for character z[i] */
 69 };
 70 
 71 /*
 72 ** Each declaration in the C or C++ source files is parsed out and stored as
 73 ** an instance of the following structure.
 74 **
 75 ** A "forward declaration" is a declaration that an object exists that
 76 ** doesn't tell about the objects structure.  A typical forward declaration
 77 ** is:
 78 **
 79 **          struct Xyzzy;
 80 **
 81 ** Not every object has a forward declaration.  If it does, thought, the
 82 ** forward declaration will be contained in the zFwd field for C and
 83 ** the zFwdCpp for C++.  The zDecl field contains the complete 
 84 ** declaration text.  
 85 */
 86 typedef struct Decl Decl;
 87 struct Decl {
 88   char *zName;       /* Name of the object being declared.  The appearance
 89                      ** of this name is a source file triggers the declaration
 90                      ** to be added to the header for that file. */
 91   char *zFile;       /* File from which extracted.  */
 92   char *zIf;         /* Surround the declaration with this #if */
 93   char *zFwd;        /* A forward declaration.  NULL if there is none. */
 94   char *zFwdCpp;     /* Use this forward declaration for C++. */
 95   char *zDecl;       /* A full declaration of this object */
 96   struct Include *pInclude;   /* #includes that come before this declaration */
 97   int flags;         /* See the "Properties" below */
 98   Token *pComment;   /* A block comment associated with this declaration */
 99   Token tokenCode;   /* Implementation of functions and procedures */
100   Decl *pSameName;   /* Next declaration with the same "zName" */
101   Decl *pSameHash;   /* Next declaration with same hash but different zName */
102   Decl *pNext;       /* Next declaration with a different name */
103 };
104 
105 /*
106 ** Properties associated with declarations.
107 **
108 ** DP_Forward and DP_Declared are used during the generation of a single
109 ** header file in order to prevent duplicate declarations and definitions.
110 ** DP_Forward is set after the object has been given a forward declaration
111 ** and DP_Declared is set after the object gets a full declarations.
112 ** (Example:  A forward declaration is "typedef struct Abc Abc;" and the
113 ** full declaration is "struct Abc { int a; float b; };".)
114 **
115 ** The DP_Export and DP_Local flags are more permanent.  They mark objects
116 ** that have EXPORT scope and LOCAL scope respectively.  If both of these
117 ** marks are missing, then the object has library scope.  The meanings of
118 ** the scopes are as follows:
119 **
120 **    LOCAL scope         The object is only usable within the file in
121 **                        which it is declared.
122 **
123 **    library scope       The object is visible and usable within other
124 **                        files in the same project.  By if the project is
125 **                        a library, then the object is not visible to users
126 **                        of the library.  (i.e. the object does not appear
127 **                        in the output when using the -H option.)
128 **
129 **    EXPORT scope        The object is visible and usable everywhere.
130 **
131 ** The DP_Flag is a temporary use flag that is used during processing to
132 ** prevent an infinite loop.  It's use is localized.  
133 **
134 ** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent
135 ** and are used to specify what type of declaration the object requires.
136 */
137 #define DP_Forward      0x001   /* Has a forward declaration in this file */
138 #define DP_Declared     0x002   /* Has a full declaration in this file */
139 #define DP_Export       0x004   /* Export this declaration */
140 #define DP_Local        0x008   /* Declare in its home file only */
141 #define DP_Flag         0x010   /* Use to mark a subset of a Decl list
142                                 ** for special processing */
143 #define DP_Cplusplus    0x020   /* Has C++ linkage and cannot appear in a
144                                 ** C header file */
145 #define DP_ExternCReqd  0x040   /* Prepend 'extern "C"' in a C++ header.
146                                 ** Prepend nothing in a C header */
147 #define DP_ExternReqd   0x080   /* Prepend 'extern "C"' in a C++ header if
148                                 ** DP_Cplusplus is not also set. If DP_Cplusplus
149                                 ** is set or this is a C header then
150                                 ** prepend 'extern' */
151 
152 /*
153 ** Convenience macros for dealing with declaration properties
154 */
155 #define DeclHasProperty(D,P)    (((D)->flags&(P))==(P))
156 #define DeclHasAnyProperty(D,P) (((D)->flags&(P))!=0)
157 #define DeclSetProperty(D,P)    (D)->flags |= (P)
158 #define DeclClearProperty(D,P)  (D)->flags &= ~(P)
159 
160 /*
161 ** These are state properties of the parser.  Each of the values is
162 ** distinct from the DP_ values above so that both can be used in
163 ** the same "flags" field.
164 **
165 ** Be careful not to confuse PS_Export with DP_Export or
166 ** PS_Local with DP_Local.  Their names are similar, but the meanings
167 ** of these flags are very different.
168 */
169 #define PS_Extern        0x000800    /* "extern" has been seen */
170 #define PS_Export        0x001000    /* If between "#if EXPORT_INTERFACE" 
171                                      ** and "#endif" */
172 #define PS_Export2       0x002000    /* If "EXPORT" seen */
173 #define PS_Typedef       0x004000    /* If "typedef" has been seen */
174 #define PS_Static        0x008000    /* If "static" has been seen */
175 #define PS_Interface     0x010000    /* If within #if INTERFACE..#endif */
176 #define PS_Method        0x020000    /* If "::" token has been seen */
177 #define PS_Local         0x040000    /* If within #if LOCAL_INTERFACE..#endif */
178 #define PS_Local2        0x080000    /* If "LOCAL" seen. */
179 
180 /*
181 ** The following set of flags are ORed into the "flags" field of
182 ** a Decl in order to identify what type of object is being
183 ** declared.
184 */
185 #define TY_Class         0x00100000
186 #define TY_Subroutine    0x00200000
187 #define TY_Macro         0x00400000
188 #define TY_Typedef       0x00800000
189 #define TY_Variable      0x01000000
190 #define TY_Structure     0x02000000
191 #define TY_Union         0x04000000
192 #define TY_Enumeration   0x08000000
193 #define TY_Defunct       0x10000000  /* Used to erase a declaration */
194 
195 /*
196 ** Each nested #if (or #ifdef or #ifndef) is stored in a stack of 
197 ** instances of the following structure.
198 */
199 typedef struct Ifmacro Ifmacro;
200 struct Ifmacro {
201   int nLine;         /* Line number where this macro occurs */
202   char *zCondition;  /* Text of the condition for this macro */
203   Ifmacro *pNext;    /* Next down in the stack */
204   int flags;         /* Can hold PS_Export, PS_Interface or PS_Local flags */
205 };
206 
207 /*
208 ** When parsing a file, we need to keep track of what other files have
209 ** be #include-ed.  For each #include found, we create an instance of
210 ** the following structure.
211 */
212 typedef struct Include Include;
213 struct Include {
214   char *zFile;       /* The name of file include.  Includes "" or <> */
215   char *zIf;         /* If not NULL, #include should be enclosed in #if */
216   char *zLabel;      /* A unique label used to test if this #include has
217                       * appeared already in a file or not */
218   Include *pNext;    /* Previous include file, or NULL if this is the first */
219 };
220 
221 /*
222 ** Identifiers found in a source file that might be used later to provoke
223 ** the copying of a declaration into the corresponding header file are
224 ** stored in a hash table as instances of the following structure.
225 */
226 typedef struct Ident Ident;
227 struct Ident {
228   char *zName;        /* The text of this identifier */
229   Ident *pCollide;    /* Next identifier with the same hash */
230   Ident *pNext;       /* Next identifier in a list of them all */
231 };
232 
233 /*
234 ** A complete table of identifiers is stored in an instance of
235 ** the next structure.
236 */
237 #define IDENT_HASH_SIZE 2237
238 typedef struct IdentTable IdentTable;
239 struct IdentTable {
240   Ident *pList;                     /* List of all identifiers in this table */
241   Ident *apTable[IDENT_HASH_SIZE];  /* The hash table */
242 };
243 
244 /*
245 ** The following structure holds all information for a single
246 ** source file named on the command line of this program.
247 */
248 typedef struct InFile InFile;
249 struct InFile {
250   char *zSrc;              /* Name of input file */
251   char *zHdr;              /* Name of the generated .h file for this input.
252                            ** Will be NULL if input is to be scanned only */
253   int flags;               /* One or more DP_, PS_ and/or TY_ flags */
254   InFile *pNext;           /* Next input file in the list of them all */
255   IdentTable idTable;      /* All identifiers in this input file */
256 };
257 
258 /* 
259 ** An unbounded string is able to grow without limit.  We use these
260 ** to construct large in-memory strings from lots of smaller components.
261 */
262 typedef struct String String;
263 struct String {
264   int nAlloc;      /* Number of bytes allocated */
265   int nUsed;       /* Number of bytes used (not counting null terminator) */
266   char *zText;     /* Text of the string */
267 };
268 
269 /*
270 ** The following structure contains a lot of state information used
271 ** while generating a .h file.  We put the information in this structure
272 ** and pass around a pointer to this structure, rather than pass around
273 ** all of the information separately.  This helps reduce the number of
274 ** arguments to generator functions.
275 */
276 typedef struct GenState GenState;
277 struct GenState {
278   String *pStr;          /* Write output to this string */
279   IdentTable *pTable;    /* A table holding the zLabel of every #include that
280                           * has already been generated.  Used to avoid
281                           * generating duplicate #includes. */
282   const char *zIf;       /* If not NULL, then we are within a #if with
283                           * this argument. */
284   int nErr;              /* Number of errors */
285   const char *zFilename; /* Name of the source file being scanned */
286   int flags;             /* Various flags (DP_ and PS_ flags above) */
287 };
288 
289 /*
290 ** The following text line appears at the top of every file generated
291 ** by this program.  By recognizing this line, the program can be sure
292 ** never to read a file that it generated itself.
293 */
294 const char zTopLine[] = 
295   "/* \aThis file was automatically generated.  Do not edit! */\n";
296 #define nTopLine (sizeof(zTopLine)-1)
297 
298 /*
299 ** The name of the file currently being parsed.
300 */
301 static char *zFilename;
302 
303 /*
304 ** The stack of #if macros for the file currently being parsed.
305 */
306 static Ifmacro *ifStack = 0;
307 
308 /*
309 ** A list of all files that have been #included so far in a file being
310 ** parsed.
311 */
312 static Include *includeList = 0;
313 
314 /*
315 ** The last block comment seen.
316 */
317 static Token *blockComment = 0;
318 
319 /*
320 ** The following flag is set if the -doc flag appears on the
321 ** command line.
322 */
323 static int doc_flag = 0;
324 
325 /*
326 ** If the following flag is set, then makeheaders will attempt to
327 ** generate prototypes for static functions and procedures.
328 */
329 static int proto_static = 0;
330 
331 /*
332 ** A list of all declarations.  The list is held together using the
333 ** pNext field of the Decl structure.
334 */
335 static Decl *pDeclFirst;    /* First on the list */
336 static Decl *pDeclLast;     /* Last on the list */
337 
338 /*
339 ** A hash table of all declarations
340 */
341 #define DECL_HASH_SIZE 3371
342 static Decl *apTable[DECL_HASH_SIZE];
343 
344 /*
345 ** The TEST macro must be defined to something.  Make sure this is the
346 ** case.
347 */
348 #ifndef TEST
349 # define TEST 0
350 #endif
351 
352 #ifdef NOT_USED
353 /*
354 ** We do our own assertion macro so that we can have more control
355 ** over debugging.
356 */
357 #define Assert(X)    if(!(X)){ CantHappen(__LINE__); }
358 #define CANT_HAPPEN  CantHappen(__LINE__)
359 static void CantHappen(int iLine){
360   fprintf(stderr,"Assertion failed on line %d\n",iLine);
361   *(char*)1 = 0;  /* Force a core-dump */
362 }
363 #endif
364 
365 /*
366 ** Memory allocation functions that are guaranteed never to return NULL.
367 */
368 static void *SafeMalloc(int nByte){
369   void *p = malloc( nByte );
370   if( p==0 ){
371     fprintf(stderr,"Out of memory.  Can't allocate %d bytes.\n",nByte);
372     exit(1);
373   }
374   return p;
375 }
376 static void SafeFree(void *pOld){
377   if( pOld ){
378     free(pOld);
379   }
380 }
381 static void *SafeRealloc(void *pOld, int nByte){
382   void *p;
383   if( pOld==0 ){
384     p = SafeMalloc(nByte);
385   }else{
386     p = realloc(pOld, nByte);
387     if( p==0 ){
388       fprintf(stderr,
389         "Out of memory.  Can't enlarge an allocation to %d bytes\n",nByte);
390       exit(1);
391     }
392   }
393   return p;
394 }
395 static char *StrDup(const char *zSrc, int nByte){
396   char *zDest;
397   if( nByte<=0 ){
398     nByte = strlen(zSrc);
399   }
400   zDest = SafeMalloc( nByte + 1 );
401   strncpy(zDest,zSrc,nByte);
402   zDest[nByte] = 0;
403   return zDest;
404 }
405 
406 /*
407 ** Return TRUE if the character X can be part of an identifier
408 */
409 #define ISALNUM(X)  ((X)=='_' || isalnum(X))
410 
411 /*
412 ** Routines for dealing with unbounded strings.
413 */
414 static void StringInit(String *pStr){
415   pStr->nAlloc = 0;
416   pStr->nUsed = 0;
417   pStr->zText = 0;
418 }
419 static void StringReset(String *pStr){
420   SafeFree(pStr->zText);
421   StringInit(pStr);
422 }
423 static void StringAppend(String *pStr, const char *zText, int nByte){
424   if( nByte<=0 ){
425     nByte = strlen(zText);
426   }
427   if( pStr->nUsed + nByte >= pStr->nAlloc ){
428     if( pStr->nAlloc==0 ){
429       pStr->nAlloc = nByte + 100;
430       pStr->zText = SafeMalloc( pStr->nAlloc );
431     }else{
432       pStr->nAlloc = pStr->nAlloc*2 + nByte;
433       pStr->zText = SafeRealloc(pStr->zText, pStr->nAlloc);
434     }
435   }
436   strncpy(&pStr->zText[pStr->nUsed],zText,nByte);
437   pStr->nUsed += nByte;
438   pStr->zText[pStr->nUsed] = 0;
439 }
440 #define StringGet(S) ((S)->zText?(S)->zText:"")
441 
442 /*
443 ** Compute a hash on a string.  The number returned is a non-negative
444 ** value between 0 and 2**31 - 1
445 */
446 static int Hash(const char *z, int n){
447   int h = 0;
448   if( n<=0 ){
449     n = strlen(z);
450   }
451   while( n-- ){
452     h = h ^ (h<<5) ^ *z++;
453   }
454   if( h<0 ) h = -h;
455   return h;
456 }
457 
458 /*
459 ** Given an identifier name, try to find a declaration for that
460 ** identifier in the hash table.  If found, return a pointer to
461 ** the Decl structure.  If not found, return 0.
462 */
463 static Decl *FindDecl(const char *zName, int len){
464   int h;
465   Decl *p;
466 
467   if( len<=0 ){
468     len = strlen(zName);
469   }
470   h = Hash(zName,len) % DECL_HASH_SIZE;
471   p = apTable[h];
472   while( p && (strncmp(p->zName,zName,len)!=0 || p->zName[len]!=0) ){
473     p = p->pSameHash;
474   }
475   return p;
476 }
477 
478 /*
479 ** Install the given declaration both in the hash table and on
480 ** the list of all declarations.
481 */
482 static void InstallDecl(Decl *pDecl){
483   int h;
484   Decl *pOther;
485 
486   h = Hash(pDecl->zName,0) % DECL_HASH_SIZE;
487   pOther = apTable[h];
488   while( pOther && strcmp(pDecl->zName,pOther->zName)!=0 ){
489     pOther = pOther->pSameHash;
490   }
491   if( pOther ){
492     pDecl->pSameName = pOther->pSameName;
493     pOther->pSameName = pDecl;
494   }else{
495     pDecl->pSameName = 0;
496     pDecl->pSameHash = apTable[h];
497     apTable[h] = pDecl;
498   }
499   pDecl->pNext = 0;
500   if( pDeclFirst==0 ){
501     pDeclFirst = pDeclLast = pDecl;
502   }else{
503     pDeclLast->pNext = pDecl;
504     pDeclLast = pDecl;
505   }
506 }
507 
508 /*
509 ** Look at the current ifStack.  If anything declared at the current
510 ** position must be surrounded with
511 **
512 **      #if   STUFF
513 **      #endif
514 **
515 ** Then this routine computes STUFF and returns a pointer to it.  Memory
516 ** to hold the value returned is obtained from malloc().
517 */
518 static char *GetIfString(void){
519   Ifmacro *pIf;
520   char *zResult = 0;
521   int hasIf = 0;
522   String str;
523 
524   for(pIf = ifStack; pIf; pIf=pIf->pNext){
525     if( pIf->zCondition==0 || *pIf->zCondition==0 ) continue;
526     if( !hasIf ){
527       hasIf = 1;
528       StringInit(&str);
529     }else{
530       StringAppend(&str," && ",4);
531     }
532     StringAppend(&str,pIf->zCondition,0);
533   }
534   if( hasIf ){
535     zResult = StrDup(StringGet(&str),0);
536     StringReset(&str);
537   }else{
538     zResult = 0;
539   }
540   return zResult;
541 }
542 
543 /*
544 ** Create a new declaration and put it in the hash table.  Also
545 ** return a pointer to it so that we can fill in the zFwd and zDecl
546 ** fields, and so forth.
547 */
548 static Decl *CreateDecl(
549   const char *zName,       /* Name of the object being declared. */
550   int nName                /* Length of the name */
551 ){
552   Decl *pDecl;
553 
554   pDecl = SafeMalloc( sizeof(Decl) + nName + 1);
555   memset(pDecl,0,sizeof(Decl));
556   pDecl->zName = (char*)&pDecl[1];
557   sprintf(pDecl->zName,"%.*s",nName,zName);
558   pDecl->zFile = zFilename;
559   pDecl->pInclude = includeList;
560   pDecl->zIf = GetIfString();
561   InstallDecl(pDecl);
562   return pDecl;
563 }
564 
565 /*
566 ** Insert a new identifier into an table of identifiers.  Return TRUE if
567 ** a new identifier was inserted and return FALSE if the identifier was
568 ** already in the table.
569 */
570 static int IdentTableInsert(
571   IdentTable *pTable,       /* The table into which we will insert */
572   const char *zId,          /* Name of the identifiers */
573   int nId                   /* Length of the identifier name */
574 ){
575   int h;
576   Ident *pId;
577 
578   if( nId<=0 ){
579     nId = strlen(zId);
580   }
581   h = Hash(zId,nId) % IDENT_HASH_SIZE;
582   for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){
583     if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){
584       /* printf("Already in table: %.*s\n",nId,zId); */
585       return 0;
586     }
587   }
588   pId = SafeMalloc( sizeof(Ident) + nId + 1 );
589   pId->zName = (char*)&pId[1];
590   sprintf(pId->zName,"%.*s",nId,zId);
591   pId->pNext = pTable->pList;
592   pTable->pList = pId;
593   pId->pCollide = pTable->apTable[h];
594   pTable->apTable[h] = pId;
595   /* printf("Add to table: %.*s\n",nId,zId); */
596   return 1;
597 }
598 
599 /*
600 ** Check to see if the given value is in the given IdentTable.  Return
601 ** true if it is and false if it is not.
602 */
603 static int IdentTableTest(
604   IdentTable *pTable,       /* The table in which to search */
605   const char *zId,          /* Name of the identifiers */
606   int nId                   /* Length of the identifier name */
607 ){
608   int h;
609   Ident *pId;
610 
611   if( nId<=0 ){
612     nId = strlen(zId);
613   }
614   h = Hash(zId,nId) % IDENT_HASH_SIZE;
615   for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){
616     if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){
617       return 1;
618     }
619   }
620   return 0;
621 }
622 
623 /*
624 ** Remove every identifier from the given table.   Reset the table to
625 ** its initial state.
626 */
627 static void IdentTableReset(IdentTable *pTable){
628   Ident *pId, *pNext;
629 
630   for(pId = pTable->pList; pId; pId = pNext){
631     pNext = pId->pNext;
632     SafeFree(pId);
633   }
634   memset(pTable,0,sizeof(IdentTable));
635 }
636 
637 #ifdef DEBUG
638 /*
639 ** Print the name of every identifier in the given table, one per line
640 */
641 static void IdentTablePrint(IdentTable *pTable, FILE *pOut){
642   Ident *pId;
643 
644   for(pId = pTable->pList; pId; pId = pId->pNext){
645     fprintf(pOut,"%s\n",pId->zName);
646   }
647 }
648 #endif
649 
650 /*
651 ** Read an entire file into memory.  Return a pointer to the memory.
652 **
653 ** The memory is obtained from SafeMalloc and must be freed by the
654 ** calling function.
655 **
656 ** If the read fails for any reason, 0 is returned.
657 */
658 static char *ReadFile(const char *zFilename){
659   struct stat sStat;
660   FILE *pIn;
661   char *zBuf;
662   int n;
663 
664   if( stat(zFilename,&sStat)!=0 
665 #ifndef WIN32
666     || !S_ISREG(sStat.st_mode)
667 #endif
668   ){
669     return 0;
670   }
671   pIn = fopen(zFilename,"r");
672   if( pIn==0 ){
673     return 0;
674   }
675   zBuf = SafeMalloc( sStat.st_size + 1 );
676   n = fread(zBuf,1,sStat.st_size,pIn);
677   zBuf[n] = 0;
678   fclose(pIn);
679   return zBuf;
680 }
681 
682 /*
683 ** Write the contents of a string into a file.  Return the number of
684 ** errors
685 */
686 static int WriteFile(const char *zFilename, const char *zOutput){
687   FILE *pOut;
688   pOut = fopen(zFilename,"w");
689   if( pOut==0 ){
690     return 1;
691   }
692   fwrite(zOutput,1,strlen(zOutput),pOut);
693   fclose(pOut);
694   return 0;
695 }
696 
697 /*
698 ** Major token types
699 */
700 #define TT_Space           1   /* Contiguous white space */
701 #define TT_Id              2   /* An identifier */
702 #define TT_Preprocessor    3   /* Any C preprocessor directive */
703 #define TT_Comment         4   /* Either C or C++ style comment */
704 #define TT_Number          5   /* Any numeric constant */
705 #define TT_String          6   /* String or character constants. ".." or '.' */
706 #define TT_Braces          7   /* All text between { and a matching } */
707 #define TT_EOF             8   /* End of file */
708 #define TT_Error           9   /* An error condition */
709 #define TT_BlockComment    10  /* A C-Style comment at the left margin that
710                                 * spans multple lines */
711 #define TT_Other           0   /* None of the above */
712 
713 /*
714 ** Get a single low-level token from the input file.  Update the
715 ** file pointer so that it points to the first character beyond the
716 ** token.
717 **
718 ** A "low-level token" is any token except TT_Braces.  A TT_Braces token
719 ** consists of many smaller tokens and is assembled by a routine that
720 ** calls this one.
721 **
722 ** The function returns the number of errors.  An error is an
723 ** unterminated string or character literal or an unterminated
724 ** comment.
725 **
726 ** Profiling shows that this routine consumes about half the
727 ** CPU time on a typical run of makeheaders.
728 */
729 static int GetToken(InStream *pIn, Token *pToken){
730   int i;
731   const char *z;
732   int cStart;
733   int c;
734   int startLine;   /* Line on which a structure begins */
735   int nlisc = 0;   /* True if there is a new-line in a ".." or '..' */
736   int nErr = 0;    /* Number of errors seen */
737 
738   z = pIn->z;
739   i = pIn->i;
740   pToken->nLine = pIn->nLine;
741   pToken->zText = &z[i];
742   switch( z[i] ){
743     case 0:
744       pToken->eType = TT_EOF;
745       pToken->nText = 0;
746       break;
747 
748     case '#':
749       if( i==0 || z[i-1]=='\n' || (i>1 && z[i-1]=='\r' && z[i-2]=='\n')){
750         /* We found a preprocessor statement */
751         pToken->eType = TT_Preprocessor;
752         i++;
753         while( z[i]!=0 && z[i]!='\n' ){
754           if( z[i]=='\\' ){
755             i++;
756             if( z[i]=='\n' ) pIn->nLine++;
757           }
758           i++;
759         }
760         pToken->nText = i - pIn->i;
761       }else{
762         /* Just an operator */
763         pToken->eType = TT_Other;
764         pToken->nText = 1;
765       }
766       break;
767 
768     case ' ':
769     case '\t':
770     case '\r':
771     case '\f':
772     case '\n':
773       while( isspace(z[i]) ){
774         if( z[i]=='\n' ) pIn->nLine++;
775         i++;
776       }
777       pToken->eType = TT_Space;
778       pToken->nText = i - pIn->i;
779       break;
780 
781     case '\\':
782       pToken->nText = 2;
783       pToken->eType = TT_Other;
784       if( z[i+1]=='\n' ){
785         pIn->nLine++;
786         pToken->eType = TT_Space;
787       }else if( z[i+1]==0 ){
788         pToken->nText = 1;
789       }
790       break;
791 
792     case '\'':
793     case '\"':
794       cStart = z[i];
795       startLine = pIn->nLine;
796       do{
797         i++;
798         c = z[i];
799         if( c=='\n' ){
800           if( !nlisc ){
801             fprintf(stderr,
802               "%s:%d: (warning) Newline in string or character literal.\n",
803               zFilename, pIn->nLine);
804             nlisc = 1;
805           }
806           pIn->nLine++;
807         }
808         if( c=='\\' ){
809           i++;
810           c = z[i];
811           if( c=='\n' ){
812             pIn->nLine++;
813           }
814         }else if( c==cStart ){
815           i++;
816           c = 0;
817         }else if( c==0 ){
818           fprintf(stderr, "%s:%d: Unterminated string or character literal.\n",
819              zFilename, startLine);
820           nErr++;
821         }
822       }while( c );
823       pToken->eType = TT_String;
824       pToken->nText = i - pIn->i;
825       break;
826 
827     case '/':
828       if( z[i+1]=='/' ){
829         /* C++ style comment */
830         while( z[i] && z[i]!='\n' ){ i++; }
831         pToken->eType = TT_Comment;
832         pToken->nText = i - pIn->i;
833       }else if( z[i+1]=='*' ){
834         /* C style comment */
835         int isBlockComment = i==0 || z[i-1]=='\n';
836         i += 2;
837         startLine = pIn->nLine;
838         while( z[i] && (z[i]!='*' || z[i+1]!='/') ){
839           if( z[i]=='\n' ){
840             pIn->nLine++;
841             if( isBlockComment ){
842               if( z[i+1]=='*' || z[i+2]=='*' ){
843                  isBlockComment = 2;
844               }else{
845                  isBlockComment = 0;
846               }
847             }
848           }
849           i++;
850         }
851         if( z[i] ){ 
852           i += 2; 
853         }else{
854           isBlockComment = 0;
855           fprintf(stderr,"%s:%d: Unterminated comment\n",
856             zFilename, startLine);
857           nErr++;
858         }
859         pToken->eType = isBlockComment==2 ? TT_BlockComment : TT_Comment;
860         pToken->nText = i - pIn->i;
861       }else{
862         /* A divide operator */
863         pToken->eType = TT_Other;
864         pToken->nText = 1;
865       }
866       break;
867 
868     case '': 
869       if( z[i+1]=='x' || z[i+1]=='X' ){
870         /* A hex constant */
871         i += 2;
872         while( isxdigit(z[i]) ){ i++; }
873       }else{
874         /* An octal constant */
875         while( isdigit(z[i]) ){ i++; }
876       }
877       pToken->eType = TT_Number;
878       pToken->nText = i - pIn->i;
879       break;
880 
881     case '1': case '2': case '3': case '4':
882     case '5': case '6': case '7': case '8': case '9':
883       while( isdigit(z[i]) ){ i++; }
884       if( (c=z[i])=='.' ){
885          i++;
886          while( isdigit(z[i]) ){ i++; }
887          c = z[i];
888          if( c=='e' || c=='E' ){
889            i++;
890            if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; }
891            while( isdigit(z[i]) ){ i++; }
892            c = z[i];
893          }
894          if( c=='f' || c=='F' || c=='l' || c=='L' ){ i++; }
895       }else if( c=='e' || c=='E' ){
896          i++;
897          if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; }
898          while( isdigit(z[i]) ){ i++; }
899       }else if( c=='L' || c=='l' ){
900          i++;
901          c = z[i];
902          if( c=='u' || c=='U' ){ i++; }
903       }else if( c=='u' || c=='U' ){
904          i++;
905          c = z[i];
906          if( c=='l' || c=='L' ){ i++; }
907       }
908       pToken->eType = TT_Number;
909       pToken->nText = i - pIn->i;
910       break;
911 
912     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
913     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
914     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
915     case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
916     case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
917     case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P':
918     case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W':
919     case 'X': case 'Y': case 'Z': case '_':
920       while( isalnum(z[i]) || z[i]=='_' ){ i++; };
921       pToken->eType = TT_Id;
922       pToken->nText = i - pIn->i;
923       break;
924 
925     default:
926       pToken->eType = TT_Other;
927       pToken->nText = 1;
928       break;
929   }
930   pIn->i += pToken->nText;
931   return nErr;
932 }
933 
934 /*
935 ** This routine recovers the next token from the input file which is
936 ** not a space or a comment or any text between an "#if 0" and "#endif".
937 **
938 ** This routine returns the number of errors encountered.  An error
939 ** is an unterminated token or unmatched "#if 0".
940 **
941 ** Profiling shows that this routine uses about a quarter of the
942 ** CPU time in a typical run.
943 */
944 static int GetNonspaceToken(InStream *pIn, Token *pToken){
945   int nIf = 0;
946   int inZero = 0;
947   const char *z;
948   int value;
949   int startLine;
950   int nErr = 0;
951 
952   startLine = pIn->nLine;
953   while( 1 ){
954     nErr += GetToken(pIn,pToken);
955     /* printf("%04d: Type=%d nIf=%d [%.*s]\n",
956        pToken->nLine,pToken->eType,nIf,pToken->nText,
957        pToken->eType!=TT_Space ? pToken->zText : "<space>"); */
958     pToken->pComment = blockComment;
959     switch( pToken->eType ){
960       case TT_Comment:
961       case TT_Space:
962         break;
963 
964       case TT_BlockComment:
965         if( doc_flag ){
966           blockComment = SafeMalloc( sizeof(Token) );
967           *blockComment = *pToken;
968         }
969         break;
970 
971       case TT_EOF:
972         if( nIf ){
973           fprintf(stderr,"%s:%d: Unterminated \"#if\"\n",
974              zFilename, startLine);
975           nErr++;
976         }
977         return nErr;
978 
979       case TT_Preprocessor:
980         z = &pToken->zText[1];
981         while( *z==' ' || *z=='\t' ) z++;
982         if( sscanf(z,"if %d",&value)==1 && value==0 ){
983           nIf++;
984           inZero = 1;
985         }else if( inZero ){
986           if( strncmp(z,"if",2)==0 ){
987             nIf++;
988           }else if( strncmp(z,"endif",5)==0 ){
989             nIf--;
990             if( nIf==0 ) inZero = 0;
991           }
992         }else{
993           return nErr;
994         }
995         break;
996 
997       default:
998         if( !inZero ){
999           return nErr;
1000         }
1001         break;
1002     }
1003   }
1004   /* NOT REACHED */
1005 }
1006 
1007 /* 
1008 ** This routine looks for identifiers (strings of contiguous alphanumeric
1009 ** characters) within a preprocessor directive and adds every such string
1010 ** found to the given identifier table
1011 */
1012 static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){
1013   Token sToken;
1014   InStream sIn;
1015   int go = 1;
1016 
1017   sIn.z = pToken->zText;
1018   sIn.i = 1;
1019   sIn.nLine = 1;
1020   while( go && sIn.i < pToken->nText ){
1021     GetToken(&sIn,&sToken);
1022     switch( sToken.eType ){
1023       case TT_Id:
1024         IdentTableInsert(pTable,sToken.zText,sToken.nText);
1025         break;
1026 
1027       case TT_EOF:
1028         go = 0;
1029         break;
1030 
1031       default:
1032         break;
1033     }
1034   }
1035 }
1036 
1037 /*
1038 ** This routine gets the next token.  Everything contained within
1039 ** {...} is collapsed into a single TT_Braces token.  Whitespace is
1040 ** omitted.
1041 **
1042 ** If pTable is not NULL, then insert every identifier seen into the
1043 ** IdentTable.  This includes any identifiers seen inside of {...}.
1044 **
1045 ** The number of errors encountered is returned.  An error is an
1046 ** unterminated token.
1047 */
1048 static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable){
1049   const char *z, *zStart;
1050   int iStart;
1051   int nBrace;
1052   int c;
1053   int nLine;
1054   int nErr;
1055 
1056   nErr = GetNonspaceToken(pIn,pToken);
1057   switch( pToken->eType ){
1058     case TT_Id:
1059       if( pTable!=0 ){
1060         IdentTableInsert(pTable,pToken->zText,pToken->nText);
1061       }
1062       return nErr;
1063 
1064     case TT_Preprocessor:
1065       if( pTable!=0 ){
1066         FindIdentifiersInMacro(pToken,pTable);
1067       }
1068       return nErr;
1069 
1070     case TT_Other:
1071       if( pToken->zText[0]=='{' ) break;
1072       return nErr;
1073 
1074     default:
1075       return nErr;
1076   }
1077 
1078   z = pIn->z;
1079   iStart = pIn->i;
1080   zStart = pToken->zText;
1081   nLine = pToken->nLine;
1082   nBrace = 1;
1083   while( nBrace ){
1084     nErr += GetNonspaceToken(pIn,pToken);
1085     /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace,
1086        pToken->nText,pToken->zText); */
1087     switch( pToken->eType ){
1088       case TT_EOF:
1089         fprintf(stderr,"%s:%d: Unterminated \"{\"\n",
1090            zFilename, nLine);
1091         nErr++;
1092         pToken->eType = TT_Error;
1093         return nErr;
1094 
1095       case TT_Id:
1096         if( pTable ){
1097           IdentTableInsert(pTable,pToken->zText,pToken->nText);
1098         }
1099         break;
1100   
1101       case TT_Preprocessor:
1102         if( pTable!=0 ){
1103           FindIdentifiersInMacro(pToken,pTable);
1104         }
1105         break;
1106 
1107       case TT_Other:
1108         if( (c = pToken->zText[0])=='{' ){
1109           nBrace++;
1110         }else if( c=='}' ){
1111           nBrace--;
1112         }
1113         break;
1114 
1115       default:
1116         break;
1117     }
1118   }
1119   pToken->eType = TT_Braces;
1120   pToken->nText = 1 + pIn->i - iStart;
1121   pToken->zText = zStart;
1122   pToken->nLine = nLine;
1123   return nErr;
1124 }
1125 
1126 /*
1127 ** This routine frees up a list of Tokens.  The pComment tokens are
1128 ** not cleared by this.  So we leak a little memory when using the -doc
1129 ** option.  So what.
1130 */
1131 static void FreeTokenList(Token *pList){
1132   Token *pNext;
1133   while( pList ){
1134     pNext = pList->pNext;
1135     SafeFree(pList);
1136     pList = pNext;
1137   }
1138 }
1139 
1140 /*
1141 ** Tokenize an entire file.  Return a pointer to the list of tokens.
1142 **
1143 ** Space for each token is obtained from a separate malloc() call.  The
1144 ** calling function is responsible for freeing this space.
1145 **
1146 ** If pTable is not NULL, then fill the table with all identifiers seen in
1147 ** the input file.
1148 */
1149 static Token *TokenizeFile(const char *zFile, IdentTable *pTable){
1150   InStream sIn;
1151   Token *pFirst = 0, *pLast = 0, *pNew;
1152   int nErr = 0;
1153 
1154   sIn.z = zFile;
1155   sIn.i = 0;
1156   sIn.nLine = 1;
1157   blockComment = 0;
1158 
1159   while( sIn.z[sIn.i]!=0 ){
1160     pNew = SafeMalloc( sizeof(Token) );
1161     nErr += GetBigToken(&sIn,pNew,pTable);
1162     debug3(TOKENIZER, "Token on line %d: [%.*s]\n",
1163        pNew->nLine, pNew->nText<50 ? pNew->nText : 50, pNew->zText);
1164     if( pFirst==0 ){
1165       pFirst = pLast = pNew;
1166       pNew->pPrev = 0;
1167     }else{
1168       pLast->pNext = pNew;
1169       pNew->pPrev = pLast;
1170       pLast = pNew;
1171     }
1172     if( pNew->eType==TT_EOF ) break;
1173   }
1174   if( pLast ) pLast->pNext = 0;
1175   blockComment = 0;
1176   if( nErr ){
1177     FreeTokenList(pFirst);
1178     pFirst = 0;
1179   }
1180 
1181   return pFirst;
1182 }
1183 
1184 #if TEST==1
1185 /*
1186 ** Use the following routine to test or debug the tokenizer.
1187 */
1188 void main(int argc, char **argv){
1189   char *zFile;
1190   Token *pList, *p;
1191   IdentTable sTable;
1192 
1193   if( argc!=2 ){
1194     fprintf(stderr,"Usage: %s filename\n",*argv);
1195     exit(1);
1196   }
1197   memset(&sTable,0,sizeof(sTable));
1198   zFile = ReadFile(argv[1]);
1199   if( zFile==0 ){
1200     fprintf(stderr,"Can't read file \"%s\"\n",argv[1]);
1201     exit(1);
1202   }
1203   pList = TokenizeFile(zFile,&sTable);
1204   for(p=pList; p; p=p->pNext){
1205     int j;
1206     switch( p->eType ){ 
1207       case TT_Space:
1208         printf("%4d: Space\n",p->nLine);
1209         break;
1210       case TT_Id:
1211         printf("%4d: Id           %.*s\n",p->nLine,p->nText,p->zText);
1212         break;
1213       case TT_Preprocessor:
1214         printf("%4d: Preprocessor %.*s\n",p->nLine,p->nText,p->zText);
1215         break;
1216       case TT_Comment:
1217         printf("%4d: Comment\n",p->nLine);
1218         break;
1219       case TT_BlockComment:
1220         printf("%4d: Block Comment\n",p->nLine);
1221         break;
1222       case TT_Number:
1223         printf("%4d: Number       %.*s\n",p->nLine,p->nText,p->zText);
1224         break;
1225       case TT_String:
1226         printf("%4d: String       %.*s\n",p->nLine,p->nText,p->zText);
1227         break;
1228       case TT_Other:
1229         printf("%4d: Other        %.*s\n",p->nLine,p->nText,p->zText);
1230         break;
1231       case TT_Braces:
1232         for(j=0; j<p->nText && j<30 && p->zText[j]!='\n'; j++){}
1233         printf("%4d: Braces       %.*s...}\n",p->nLine,j,p->zText);
1234         break;
1235       case TT_EOF:
1236         printf("%4d: End of file\n",p->nLine);
1237         break;
1238       default:
1239         printf("%4d: type %d\n",p->nLine,p->eType);
1240         break;
1241     }
1242   }
1243   FreeTokenList(pList);
1244   SafeFree(zFile);
1245   IdentTablePrint(&sTable,stdout);
1246 }
1247 #endif
1248 
1249 #ifdef DEBUG
1250 /*
1251 ** For debugging purposes, write out a list of tokens.
1252 */
1253 static void PrintTokens(Token *pFirst, Token *pLast){
1254   int needSpace = 0;
1255   int c;
1256 
1257   pLast = pLast->pNext;
1258   while( pFirst!=pLast ){
1259     switch( pFirst->eType ){
1260       case TT_Preprocessor:
1261         printf("\n%.*s\n",pFirst->nText,pFirst->zText);
1262         needSpace = 0;
1263         break;
1264 
1265       case TT_Id:
1266       case TT_Number:
1267         printf("%s%.*s", needSpace ? " " : "", pFirst->nText, pFirst->zText);
1268         needSpace = 1;
1269         break;
1270 
1271       default:
1272         c = pFirst->zText[0];
1273         printf("%s%.*s", 
1274           (needSpace && (c=='*' || c=='{')) ? " " : "",
1275           pFirst->nText, pFirst->zText);
1276         needSpace = pFirst->zText[0]==',';
1277         break;
1278     }
1279     pFirst = pFirst->pNext;
1280   }
1281 }
1282 #endif
1283 
1284 /*
1285 ** Convert a sequence of tokens into a string and return a pointer
1286 ** to that string.  Space to hold the string is obtained from malloc()
1287 ** and must be freed by the calling function.
1288 **
1289 ** The characters ";\n" are always appended.
1290 */
1291 static char *TokensToString(Token *pFirst, Token *pLast){
1292   char *zReturn;
1293   String str;
1294   int needSpace = 0;
1295   int c;
1296 
1297   StringInit(&str);
1298   pLast = pLast->pNext;
1299   while( pFirst!=pLast ){
1300     switch( pFirst->eType ){
1301       case TT_Preprocessor:
1302         StringAppend(&str,"\n",1);
1303         StringAppend(&str,pFirst->zText,pFirst->nText);
1304         StringAppend(&str,"\n",1);
1305         needSpace = 0;
1306         break;
1307 
1308       case TT_Id:
1309         if( pFirst->nText==6 && pFirst->zText[0]=='E' 
1310         && strncmp(pFirst->zText,"EXPORT",6)==0 ){
1311           break;
1312         }
1313         /* Fall thru to the next case */
1314       case TT_Number:
1315         if( needSpace ){
1316           StringAppend(&str," ",1);
1317         }
1318         StringAppend(&str,pFirst->zText,pFirst->nText);
1319         needSpace = 1;
1320         break;
1321 
1322       default:
1323         c = pFirst->zText[0];
1324         if( needSpace && (c=='*' || c=='{') ){
1325           StringAppend(&str," ",1);
1326         }
1327         StringAppend(&str,pFirst->zText,pFirst->nText);
1328         /* needSpace = pFirst->zText[0]==','; */
1329         needSpace = 0;
1330         break;
1331     }
1332     pFirst = pFirst->pNext;
1333   }
1334   StringAppend(&str,";\n",2);
1335   zReturn = StrDup(StringGet(&str),0);
1336   StringReset(&str);
1337   return zReturn;
1338 }
1339 
1340 /*
1341 ** This routine is called when we see one of the keywords "struct",
1342 ** "enum", "union" or "class".  This might be the beginning of a
1343 ** type declaration.  This routine will process the declaration and
1344 ** remove the declaration tokens from the input stream.
1345 **
1346 ** If this is a type declaration that is immediately followed by a
1347 ** semicolon (in other words it isn't also a variable definition)
1348 ** then set *pReset to ';'.  Otherwise leave *pReset at 0.  The
1349 ** *pReset flag causes the parser to skip ahead to the next token
1350 ** that begins with the value placed in the *pReset flag, if that
1351 ** value is different from 0.
1352 */
1353 static int ProcessTypeDecl(Token *pList, int flags, int *pReset){
1354   Token *pName, *pEnd;
1355   Decl *pDecl;
1356   String str;
1357   int need_to_collapse = 1;
1358 
1359   *pReset = 0;
1360   if( pList==0 || pList->pNext==0 || pList->pNext->eType!=TT_Id ){
1361     return 0;
1362   }
1363   pName = pList->pNext;
1364   for(pEnd=pName->pNext; pEnd && pEnd->eType!=TT_Braces; pEnd=pEnd->pNext){
1365     switch( pEnd->zText[0] ){
1366       case '(':
1367       case '*':
1368       case '[':
1369       case '=':
1370       case ';':
1371         return 0;
1372     }
1373   }
1374   if( pEnd==0 ){
1375     return 0;
1376   }
1377 
1378   /*
1379   ** At this point, we know we have a type declaration that is bounded
1380   ** by pList and pEnd and has the name pName.
1381   */
1382 
1383   /*
1384   ** If the braces are followed immedately by a semicolon, then we are
1385   ** dealing a type declaration only.  There is not variable definition
1386   ** following the type declaration.  So reset...
1387   */
1388   if( pEnd->pNext==0 || pEnd->pNext->zText[0]==';' ){
1389     *pReset = ';';
1390     need_to_collapse = 0;
1391   }else{
1392     need_to_collapse = 1;
1393   }
1394 
1395   if( proto_static==0 && (flags & (PS_Local|PS_Export|PS_Interface))==0 ){
1396     /* Ignore these objects unless they are explicitly declared as interface,
1397     ** or unless the "-local" command line option was specified. */
1398     *pReset = ';';
1399     return 0;
1400   }
1401 
1402 #ifdef DEBUG
1403   if( debugMask & PARSER ){
1404     printf("**** Found type: %.*s %.*s...\n",
1405       pList->nText, pList->zText, pName->nText, pName->zText);
1406     PrintTokens(pList,pEnd);
1407     printf(";\n");
1408   }
1409 #endif
1410   pDecl = CreateDecl(pName->zText,pName->nText);
1411   if( (flags & PS_Static) || !(flags & (PS_Interface|PS_Export)) ){
1412     DeclSetProperty(pDecl,DP_Local);
1413   }
1414   switch( *pList->zText ){
1415     case 'c':  DeclSetProperty(pDecl,TY_Class);       break;
1416     case 's':  DeclSetProperty(pDecl,TY_Structure);   break;
1417     case 'e':  DeclSetProperty(pDecl,TY_Enumeration); break;
1418     case 'u':  DeclSetProperty(pDecl,TY_Union);       break;
1419     default:     /* Can't Happen */  break;
1420   }
1421 
1422   /* The object has a full declaration only if it is contained within
1423   ** "#if INTERFACE...#endif" or "#if EXPORT_INTERFACE...#endif" or
1424   ** "#if LOCAL_INTERFACE...#endif".  Otherwise, we only give it a
1425   ** forward declaration.
1426   */
1427   if( flags & (PS_Local | PS_Export | PS_Interface)  ){
1428     pDecl->zDecl = TokensToString(pList,pEnd);
1429   }else{
1430     pDecl->zDecl = 0;
1431   }
1432   pDecl->pComment = pList->pComment;
1433   StringInit(&str);
1434   StringAppend(&str,"typedef ",0);
1435   StringAppend(&str,pList->zText,pList->nText);
1436   StringAppend(&str," ",0);
1437   StringAppend(&str,pName->zText,pName->nText);
1438   StringAppend(&str," ",0);
1439   StringAppend(&str,pName->zText,pName->nText);
1440   StringAppend(&str,";\n",2);
1441   pDecl->zFwd = StrDup(StringGet(&str),0);
1442   StringReset(&str);
1443   StringInit(&str);
1444   StringAppend(&str,pList->zText,pList->nText);
1445   StringAppend(&str," ",0);
1446   StringAppend(&str,pName->zText,pName->nText);
1447   StringAppend(&str,";\n",2);
1448   pDecl->zFwdCpp = StrDup(StringGet(&str),0);
1449   StringReset(&str);
1450   if( flags & PS_Export ){
1451     DeclSetProperty(pDecl,DP_Export);
1452   }else if( flags & PS_Local ){
1453     DeclSetProperty(pDecl,DP_Local);
1454   }
1455 
1456   /* Here's something weird.  ANSI-C doesn't allow a forward declaration
1457   ** of an enumeration.  So we have to build the typedef into the
1458   ** definition.
1459   */
1460   if( pDecl->zDecl && DeclHasProperty(pDecl, TY_Enumeration) ){
1461     StringInit(&str);
1462     StringAppend(&str,pDecl->zDecl,0);
1463     StringAppend(&str,pDecl->zFwd,0);
1464     SafeFree(pDecl->zDecl);
1465     SafeFree(pDecl->zFwd);
1466     pDecl->zFwd = 0;
1467     pDecl->zDecl = StrDup(StringGet(&str),0);
1468     StringReset(&str);
1469   }
1470 
1471   if( pName->pNext->zText[0]==':' ){
1472     DeclSetProperty(pDecl,DP_Cplusplus);
1473   }
1474   if( pName->nText==5 && strncmp(pName->zText,"class",5)==0 ){
1475     DeclSetProperty(pDecl,DP_Cplusplus);
1476   }
1477 
1478   /*
1479   ** Remove all but pList and pName from the input stream.
1480   */
1481   if( need_to_collapse ){
1482     while( pEnd!=pName ){
1483       Token *pPrev = pEnd->pPrev;
1484       pPrev->pNext = pEnd->pNext;
1485       pEnd->pNext->pPrev = pPrev;
1486       SafeFree(pEnd);
1487       pEnd = pPrev;
1488     }
1489   }
1490   return 0;
1491 }
1492 
1493 /*
1494 ** Given a list of tokens that declare something (a function, procedure,
1495 ** variable or typedef) find the token which contains the name of the
1496 ** thing being declared.
1497 **
1498 ** Algorithm:
1499 **
1500 **   The name is:
1501 **
1502 **     1.  The first identifier that is followed by a "[", or
1503 **
1504 **     2.  The first identifier that is followed by a "(" where the
1505 **         "(" is followed by another identifier, or
1506 **
1507 **     3.  The first identifier followed by "::", or
1508 **
1509 **     4.  If none of the above, then the last identifier.
1510 **
1511 **   In all of the above, certain reserved words (like "char") are
1512 **   not considered identifiers.
1513 */
1514 static Token *FindDeclName(Token *pFirst, Token *pLast){
1515   Token *pName = 0;
1516   Token *p;
1517   int c;
1518 
1519   if( pFirst==0 || pLast==0 ){
1520     return 0;
1521   }
1522   pLast = pLast->pNext;
1523   for(p=pFirst; p && p!=pLast; p=p->pNext){
1524     if( p->eType==TT_Id ){
1525       static IdentTable sReserved;
1526       static int isInit = 0;
1527       static char *aWords[] = { "char", "class", 
1528        "const", "double", "enum", "extern", "EXPORT", "ET_PROC", 
1529        "float", "int", "long", 
1530        "register", "static", "struct", "sizeof", "signed", "typedef", 
1531        "union", "volatile", "virtual", "void", };
1532   
1533       if( !isInit ){
1534         int i;
1535         for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){
1536           IdentTableInsert(&sReserved,aWords[i],0);
1537         }
1538         isInit = 1;
1539       }
1540       if( !IdentTableTest(&sReserved,p->zText,p->nText) ){
1541         pName = p;
1542       }
1543     }else if( p==pFirst ){
1544       continue;
1545     }else if( (c=p->zText[0])=='[' && pName ){
1546       break;
1547     }else if( c=='(' && p->pNext && p->pNext->eType==TT_Id && pName ){
1548       break;
1549     }else if( c==':' && p->zText[1]==':' && pName ){
1550       break;
1551     }
1552   }
1553   return pName;
1554 }
1555 
1556 /*
1557 ** This routine is called when we see a function or procedure definition.
1558 ** We make an entry in the declaration table that is a prototype for this
1559 ** function or procedure.
1560 */
1561 static int ProcessProcedureDef(Token *pFirst, Token *pLast, int flags){
1562   Token *pName;
1563   Decl *pDecl;
1564   Token *pCode;
1565 
1566   if( pFirst==0 || pLast==0 ){
1567     return 0;
1568   }
1569   if( flags & PS_Method ){
1570     return 0;
1571   }
1572   if( (flags & PS_Static)!=0 && !proto_static ){
1573     return 0;
1574   }
1575   pCode = pLast;
1576   while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){
1577     pLast = pLast->pPrev;
1578   }
1579   if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){
1580     fprintf(stderr,"%s:%d: Unrecognized syntax.\n", 
1581       zFilename, pFirst->nLine);
1582     return 1;
1583   }
1584   if( flags & (PS_Interface|PS_Export|PS_Local) ){
1585     fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n",
1586       zFilename, pFirst->nLine);
1587     return 1;
1588   }
1589   pName = FindDeclName(pFirst,pLast);
1590   if( pName==0 ){
1591     fprintf(stderr,"%s:%d: Malformed function or procedure definition.\n",
1592       zFilename, pFirst->nLine);
1593     return 1;
1594   }
1595 
1596   /*
1597   ** At this point we've isolated a procedure declaration between pFirst
1598   ** and pLast with the name pName.
1599   */
1600 #ifdef DEBUG
1601   if( debugMask & PARSER ){
1602     printf("**** Found routine: %.*s on line %d...\n", pName->nText,
1603        pName->zText, pFirst->nLine);
1604     PrintTokens(pFirst,pLast);
1605     printf(";\n");
1606   }
1607 #endif
1608   pDecl = CreateDecl(pName->zText,pName->nText);
1609   pDecl->pComment = pFirst->pComment;
1610   if( pCode && pCode->eType==TT_Braces ){
1611     pDecl->tokenCode = *pCode;
1612   }
1613   DeclSetProperty(pDecl,TY_Subroutine);
1614   pDecl->zDecl = TokensToString(pFirst,pLast);
1615   if( (flags & (PS_Static|PS_Local2))!=0 ){
1616     DeclSetProperty(pDecl,DP_Local);
1617   }else if( (flags & (PS_Export2))!=0 ){
1618     DeclSetProperty(pDecl,DP_Export);
1619   }
1620 
1621   if( flags & DP_Cplusplus ){
1622     DeclSetProperty(pDecl,DP_Cplusplus);
1623   }else{
1624     DeclSetProperty(pDecl,DP_ExternCReqd);
1625   }
1626 
1627   return 0;
1628 }
1629 
1630 /*
1631 ** This routine is called whenever we see the "inline" keyword.  We
1632 ** need to seek-out the inline function or procedure and make a
1633 ** declaration out of the entire definition.
1634 */
1635 static int ProcessInlineProc(Token *pFirst, int flags, int *pReset){
1636   Token *pName;
1637   Token *pEnd;
1638   Decl *pDecl;
1639 
1640   for(pEnd=pFirst; pEnd; pEnd = pEnd->pNext){
1641     if( pEnd->zText[0]=='{' || pEnd->zText[0]==';' ){
1642       *pReset = pEnd->zText[0];
1643       break;
1644     }
1645   }
1646   if( pEnd==0 ){
1647     *pReset = ';';
1648     fprintf(stderr,"%s:%d: incomplete inline procedure definition\n",
1649       zFilename, pFirst->nLine);
1650     return 1;
1651   }
1652   pName = FindDeclName(pFirst,pEnd);
1653   if( pName==0 ){
1654     fprintf(stderr,"%s:%d: malformed inline procedure definition\n",
1655       zFilename, pFirst->nLine);
1656     return 1;
1657   }
1658 
1659 #ifdef DEBUG
1660   if( debugMask & PARSER ){
1661     printf("**** Found inline routine: %.*s on line %d...\n", 
1662        pName->nText, pName->zText, pFirst->nLine);
1663     PrintTokens(pFirst,pEnd);
1664     printf("\n");
1665   }
1666 #endif
1667   pDecl = CreateDecl(pName->zText,pName->nText);
1668   pDecl->pComment = pFirst->pComment;
1669   DeclSetProperty(pDecl,TY_Subroutine);
1670   pDecl->zDecl = TokensToString(pFirst,pEnd);
1671   if( (flags & (PS_Static|PS_Local|PS_Local2)) ){
1672     DeclSetProperty(pDecl,DP_Local);
1673   }else if( flags & (PS_Export|PS_Export2) ){
1674     DeclSetProperty(pDecl,DP_Export);
1675   }
1676 
1677   if( flags & DP_Cplusplus ){
1678     DeclSetProperty(pDecl,DP_Cplusplus);
1679   }else{
1680     DeclSetProperty(pDecl,DP_ExternCReqd);
1681   }
1682 
1683   return 0;
1684 }
1685 
1686 /*
1687 ** Determine if the tokens between pFirst and pEnd form a variable
1688 ** definition or a function prototype.  Return TRUE if we are dealing
1689 ** with a variable defintion and FALSE for a prototype.
1690 **
1691 ** pEnd is the token that ends the object.  It can be either a ';' or
1692 ** a '='.  If it is '=', then assume we have a variable definition.
1693 **
1694 ** If pEnd is ';', then the determination is more difficult.  We have
1695 ** to search for an occurance of an ID followed immediately by '('.
1696 ** If found, we have a prototype.  Otherwise we are dealing with a
1697 ** variable definition.
1698 */
1699 static int isVariableDef(Token *pFirst, Token *pEnd){
1700   if( pEnd && pEnd->zText[0]=='=' ){
1701     return 1;
1702   }
1703   while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){
1704     if( pFirst->eType==TT_Id && pFirst->pNext->zText[0]=='(' ){
1705       return 0;
1706     }
1707     pFirst = pFirst->pNext;
1708   }
1709   return 1;
1710 }
1711 
1712 
1713 /*
1714 ** This routine is called whenever we encounter a ";" or "=".  The stuff
1715 ** between pFirst and pLast constitutes either a typedef or a global
1716 ** variable definition.  Do the right thing.
1717 */
1718 static int ProcessDecl(Token *pFirst, Token *pEnd, int flags){
1719   Token *pName;
1720   Decl *pDecl;
1721   int isLocal = 0;
1722   int isVar;
1723   int nErr = 0;
1724 
1725   if( pFirst==0 || pEnd==0 ){
1726     return 0;
1727   }
1728   if( flags & PS_Typedef ){
1729     if( (flags & (PS_Export2|PS_Local2))!=0 ){
1730       fprintf(stderr,"%s:%d: \"EXPORT\" or \"LOCAL\" ignored before typedef.\n",
1731         zFilename, pFirst->nLine);
1732       nErr++;
1733     }
1734     if( (flags & (PS_Interface|PS_Export|PS_Local|DP_Cplusplus))==0 ){
1735       /* It is illegal to duplicate a typedef in C (but OK in C++).
1736       ** So don't record typedefs that aren't within a C++ file or
1737       ** within #if INTERFACE..#endif */
1738       return nErr;
1739     }
1740     if( (flags & (PS_Interface|PS_Export|PS_Local))==0 && proto_static==0 ){
1741       /* Ignore typedefs that are not with "#if INTERFACE..#endif" unless
1742       ** the "-local" command line option is used. */
1743       return nErr;
1744     }
1745     if( (flags & (PS_Interface|PS_Export))==0 ){
1746       /* typedefs are always local, unless within #if INTERFACE..#endif */
1747       isLocal = 1;
1748     }
1749   }else if( flags & (PS_Static|PS_Local2) ){
1750     if( proto_static==0 && (flags & PS_Local2)==0 ){
1751       /* Don't record static variables unless the "-local" command line
1752       ** option was specified or the "LOCAL" keyword is used. */
1753       return nErr;
1754     }
1755     while( pFirst!=0 && pFirst->pNext!=pEnd &&
1756        ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0)
1757         || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0))
1758     ){
1759       /* Lose the initial "static" or local from local variables. 
1760       ** We'll prepend "extern" later. */
1761       pFirst = pFirst->pNext;
1762       isLocal = 1;
1763     }
1764     if( pFirst==0 || !isLocal ){
1765       return nErr;
1766     }
1767   }else if( flags & PS_Method ){
1768     /* Methods are declared by their class.  Don't declare separately. */
1769     return nErr;
1770   }
1771   isVar =  (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd);
1772   if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 
1773   && (flags & PS_Extern)==0 ){
1774     fprintf(stderr,"%s:%d: Can't define a variable in this context\n",
1775       zFilename, pFirst->nLine);
1776     nErr++;
1777   }
1778   pName = FindDeclName(pFirst,pEnd->pPrev);
1779   if( pName==0 ){
1780     fprintf(stderr,"%s:%d: Can't find a name for the object declared here.\n",
1781       zFilename, pFirst->nLine);
1782     return nErr+1;
1783   }
1784 
1785 #ifdef DEBUG
1786   if( debugMask & PARSER ){
1787     if( flags & PS_Typedef ){
1788       printf("**** Found typedef %.*s at line %d...\n",
1789         pName->nText, pName->zText, pName->nLine);
1790     }else if( isVar ){
1791       printf("**** Found variable %.*s at line %d...\n",
1792         pName->nText, pName->zText, pName->nLine);
1793     }else{
1794       printf("**** Found prototype %.*s at line %d...\n",
1795         pName->nText, pName->zText, pName->nLine);
1796     }
1797     PrintTokens(pFirst,pEnd->pPrev);
1798     printf(";\n");
1799   }
1800 #endif
1801 
1802   pDecl = CreateDecl(pName->zText,pName->nText);
1803   if( (flags & PS_Typedef) ){
1804     DeclSetProperty(pDecl, TY_Typedef);
1805   }else if( isVar ){
1806     DeclSetProperty(pDecl,DP_ExternReqd | TY_Variable);
1807     if( !(flags & DP_Cplusplus) ){
1808       DeclSetProperty(pDecl,DP_ExternCReqd);
1809     }
1810   }else{
1811     DeclSetProperty(pDecl, TY_Subroutine);
1812     if( !(flags & DP_Cplusplus) ){
1813       DeclSetProperty(pDecl,DP_ExternCReqd);
1814     }
1815   }
1816   pDecl->pComment = pFirst->pComment;
1817   pDecl->zDecl = TokensToString(pFirst,pEnd->pPrev);
1818   if( isLocal || (flags & (PS_Local||PS_Local2))!=0 ){
1819     DeclSetProperty(pDecl,DP_Local);
1820   }else if( flags & (PS_Export|PS_Export2) ){
1821     DeclSetProperty(pDecl,DP_Export);
1822   }
1823   if( flags & DP_Cplusplus ){
1824     DeclSetProperty(pDecl,DP_Cplusplus);
1825   }
1826   return nErr;
1827 }
1828 
1829 /*
1830 ** Push an if condition onto the if stack
1831 */
1832 static void PushIfMacro(
1833   const char *zPrefix,      /* A prefix, like "define" or "!" */
1834   const char *zText,        /* The condition */
1835   int nText,                /* Number of characters in zText */
1836   int nLine,                /* Line number where this macro occurs */
1837   int flags                 /* Either 0, PS_Interface, PS_Export or PS_Local */
1838 ){
1839   Ifmacro *pIf;
1840   int nByte;
1841 
1842   nByte = sizeof(Ifmacro);
1843   if( zText ){
1844     if( zPrefix ){
1845       nByte += strlen(zPrefix) + 2;
1846     }
1847     nByte += nText + 1;
1848   }
1849   pIf = SafeMalloc( nByte );
1850   if( zText ){
1851     pIf->zCondition = (char*)&pIf[1];
1852     if( zPrefix ){
1853       sprintf(pIf->zCondition,"%s(%.*s)",zPrefix,nText,zText);
1854     }else{
1855       sprintf(pIf->zCondition,"%.*s",nText,zText);
1856     }
1857   }else{
1858     pIf->zCondition = 0;
1859   }
1860   pIf->nLine = nLine;
1861   pIf->flags = flags;
1862   pIf->pNext = ifStack;
1863   ifStack = pIf;
1864 }
1865 
1866 /*
1867 ** This routine is called to handle all preprocessor directives.
1868 **
1869 ** This routine will recompute the value of *pPresetFlags to be the
1870 ** logical or of all flags on all nested #ifs.  The #ifs that set flags
1871 ** are as follows:
1872 **
1873 **        conditional                   flag set
1874 **        ------------------------      --------------------
1875 **        #if INTERFACE                 PS_Interface
1876 **        #if EXPORT_INTERFACE          PS_Export
1877 **        #if LOCAL_INTERFACE           PS_Local
1878 **
1879 ** For example, if after processing the preprocessor token given
1880 ** by pToken there is an "#if INTERFACE" on the preprocessor
1881 ** stack, then *pPresetFlags will be set to PS_Interface.
1882 */
1883 static int ParsePreprocessor(Token *pToken, int flags, int *pPresetFlags){
1884   const char *zCmd;
1885   int nCmd;
1886   const char *zArg;
1887   int nArg;
1888   int nErr = 0;
1889   Ifmacro *pIf;
1890 
1891   zCmd = &pToken->zText[1];
1892   while( isspace(*zCmd) && *zCmd!='\n' ){
1893     zCmd++;
1894   }
1895   if( !isalpha(*zCmd) ){
1896     return 0;
1897   }
1898   nCmd = 1;
1899   while( isalpha(zCmd[nCmd]) ){
1900     nCmd++;
1901   }
1902 
1903   if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){
1904     /*
1905     ** Pop the if stack 
1906     */
1907     pIf = ifStack;
1908     if( pIf==0 ){
1909       fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine);
1910       return 1;
1911     }
1912     ifStack = pIf->pNext;
1913     SafeFree(pIf);
1914   }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){
1915     /*
1916     ** Record a #define if we are in PS_Interface or PS_Export 
1917     */
1918     Decl *pDecl;
1919     if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; }
1920     zArg = &zCmd[6];
1921     while( *zArg && isspace(*zArg) && *zArg!='\n' ){
1922       zArg++;
1923     }
1924     if( *zArg==0 || *zArg=='\n' ){ return 0; }
1925     for(nArg=0; ISALNUM(zArg[nArg]); nArg++){}
1926     if( nArg==0 ){ return 0; }
1927     pDecl = CreateDecl(zArg,nArg);
1928     pDecl->pComment = pToken->pComment;
1929     DeclSetProperty(pDecl,TY_Macro);
1930     pDecl->zDecl = SafeMalloc( pToken->nText + 2 );
1931     sprintf(pDecl->zDecl,"%.*s\n",pToken->nText,pToken->zText);
1932     if( flags & PS_Export ){
1933       DeclSetProperty(pDecl,DP_Export);
1934     }else if( flags & PS_Local ){
1935       DeclSetProperty(pDecl,DP_Local);
1936     }
1937   }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){
1938     /*
1939     ** Record an #include if we are in PS_Interface or PS_Export 
1940     */
1941     Include *pInclude;
1942     char *zIf;
1943 
1944     if( !(flags & (PS_Interface|PS_Export)) ){ return 0; }
1945     zArg = &zCmd[7];
1946     while( *zArg && isspace(*zArg) ){ zArg++; }
1947     for(nArg=0; !isspace(zArg[nArg]); nArg++){}
1948     if( (zArg[0]=='"' && zArg[nArg-1]!='"')
1949       ||(zArg[0]=='<' && zArg[nArg-1]!='>')
1950     ){
1951       fprintf(stderr,"%s:%d: malformed #include statement.\n",
1952         zFilename,pToken->nLine);
1953       return 1;
1954     }
1955     zIf = GetIfString();
1956     if( zIf ){
1957       pInclude = SafeMalloc( sizeof(Include) + nArg*2 + strlen(zIf) + 10 );
1958       pInclude->zFile = (char*)&pInclude[1];
1959       pInclude->zLabel = &pInclude->zFile[nArg+1];
1960       sprintf(pInclude->zFile,"%.*s",nArg,zArg);
1961       sprintf(pInclude->zLabel,"%.*s:%s",nArg,zArg,zIf);
1962       pInclude->zIf = &pInclude->zLabel[nArg+1];
1963       SafeFree(zIf);
1964     }else{
1965       pInclude = SafeMalloc( sizeof(Include) + nArg + 1 );
1966       pInclude->zFile = (char*)&pInclude[1];
1967       sprintf(pInclude->zFile,"%.*s",nArg,zArg);
1968       pInclude->zIf = 0;
1969       pInclude->zLabel = pInclude->zFile;
1970     }
1971     pInclude->pNext = includeList;
1972     includeList = pInclude;
1973   }else if( nCmd==2 && strncmp(zCmd,"if",2)==0 ){
1974     /*
1975     ** Push an #if.  Watch for the special cases of INTERFACE
1976     ** and EXPORT_INTERFACE and LOCAL_INTERFACE
1977     */
1978     zArg = &zCmd[2];
1979     while( *zArg && isspace(*zArg) && *zArg!='\n' ){
1980       zArg++;
1981     }
1982     if( *zArg==0 || *zArg=='\n' ){ return 0; }
1983     nArg = pToken->nText + (int)pToken->zText - (int)zArg;
1984     if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
1985       PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
1986     }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
1987       PushIfMacro(0,0,0,pToken->nLine,PS_Export);
1988     }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
1989       PushIfMacro(0,0,0,pToken->nLine,PS_Local);
1990     }else{
1991       PushIfMacro(0,zArg,nArg,pToken->nLine,0);
1992     }
1993   }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
1994     /* 
1995     ** Push an #ifdef.
1996     */
1997     zArg = &zCmd[5];
1998     while( *zArg && isspace(*zArg) && *zArg!='\n' ){
1999       zArg++;
2000     }
2001     if( *zArg==0 || *zArg=='\n' ){ return 0; }
2002     nArg = pToken->nText + (int)pToken->zText - (int)zArg;
2003     PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
2004   }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
2005     /*
2006     ** Push an #ifndef.
2007     */
2008     zArg = &zCmd[6];
2009     while( *zArg && isspace(*zArg) && *zArg!='\n' ){
2010       zArg++;
2011     }
2012     if( *zArg==0 || *zArg=='\n' ){ return 0; }
2013     nArg = pToken->nText + (int)pToken->zText - (int)zArg;
2014     PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
2015   }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
2016     /*
2017     ** Invert the #if on the top of the stack 
2018     */
2019     if( ifStack==0 ){
2020       fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename,
2021          pToken->nLine);
2022       return 1;
2023     }
2024     pIf = ifStack;
2025     if( pIf->zCondition ){
2026       ifStack = ifStack->pNext;
2027       PushIfMacro("!",pIf->zCondition,strlen(pIf->zCondition),pIf->nLine,0);
2028       SafeFree(pIf);
2029     }else{
2030       pIf->flags = 0;
2031     }
2032   }else{
2033     /*
2034     ** This directive can be safely ignored 
2035     */
2036     return 0;
2037   }
2038 
2039   /* 
2040   ** Recompute the preset flags 
2041   */
2042   *pPresetFlags = 0;
2043   for(pIf = ifStack; pIf; pIf=pIf->pNext){
2044     *pPresetFlags |= pIf->flags;
2045   }
2046     
2047   return nErr;
2048 }
2049 
2050 /*
2051 ** Parse an entire file.  Return the number of errors.
2052 **
2053 ** pList is a list of tokens in the file.  Whitespace tokens have been
2054 ** eliminated, and text with {...} has been collapsed into a
2055 ** single TT_Brace token.
2056 ** 
2057 ** initFlags are a set of parse flags that should always be set for this
2058 ** file.  For .c files this is normally 0.  For .h files it is PS_Interface.
2059 */
2060 static int ParseFile(Token *pList, int initFlags){
2061   int nErr = 0;
2062   Token *pStart = 0;
2063   int flags = initFlags;
2064   int presetFlags = initFlags;
2065   int resetFlag = 0;
2066 
2067   includeList = 0;
2068   while( pList ){
2069     switch( pList->eType ){
2070     case TT_EOF:
2071       goto end_of_loop;
2072 
2073     case TT_Preprocessor:
2074       nErr += ParsePreprocessor(pList,flags,&presetFlags);
2075       pStart = 0;
2076       presetFlags |= initFlags;
2077       flags = presetFlags;
2078       break;
2079 
2080     case TT_Other:
2081       switch( pList->zText[0] ){
2082       case ';':
2083         nErr += ProcessDecl(pStart,pList,flags);
2084         pStart = 0;
2085         flags = presetFlags;
2086         break;
2087 
2088       case '=':
2089         nErr += ProcessDecl(pStart,pList,flags);
2090         pStart = 0;
2091         while( pList && pList->zText[0]!=';' ){
2092           pList = pList->pNext;
2093         }
2094         if( pList==0 ) goto end_of_loop;
2095         flags = presetFlags;
2096         break;
2097 
2098       case ':':
2099         if( pList->zText[1]==':' ){
2100           flags |= PS_Method;
2101         }
2102         break;
2103 
2104       default:
2105         break;
2106       }
2107       break;
2108 
2109     case TT_Braces:
2110       nErr += ProcessProcedureDef(pStart,pList,flags);
2111       pStart = 0;
2112       flags = presetFlags;
2113       break;
2114 
2115     case TT_Id:
2116        if( pStart==0 ){
2117           pStart = pList;
2118           flags = presetFlags;
2119        }
2120        resetFlag = 0;
2121        switch( pList->zText[0] ){
2122        case 'c':
2123          if( pList->nText==5 && strncmp(pList->zText,"class",5)==0 ){
2124            nErr += ProcessTypeDecl(pList,flags,&resetFlag);
2125          }
2126          break;
2127 
2128        case 'E':
2129          if( pList->nText==6 && strncmp(pList->zText,"EXPORT",6)==0 ){
2130            flags |= PS_Export2;
2131            /* pStart = 0; */
2132          }
2133          break;
2134 
2135        case 'e':
2136          if( pList->nText==4 && strncmp(pList->zText,"enum",4)==0 ){
2137            if( pList->pNext && pList->pNext->eType==TT_Braces ){
2138              pList = pList->pNext;
2139            }else{
2140              nErr += ProcessTypeDecl(pList,flags,&resetFlag);
2141            }
2142          }else if( pList->nText==6 && strncmp(pList->zText,"extern",6)==0 ){
2143            pList = pList->pNext;
2144            if( pList && pList->nText==3 && strncmp(pList->zText,"\"C\"",3)==0 ){
2145              pList = pList->pNext;
2146              flags &= ~DP_Cplusplus;
2147            }else{
2148              flags |= PS_Extern;
2149            }
2150            pStart = pList;
2151          }
2152          break;
2153 
2154        case 'i':
2155          if( pList->nText==6 && strncmp(pList->zText,"inline",6)==0 ){
2156            nErr += ProcessInlineProc(pList,flags,&resetFlag);
2157          }
2158          break;
2159 
2160        case 'L':
2161          if( pList->nText==5 && strncmp(pList->zText,"LOCAL",5)==0 ){
2162            flags |= PS_Local2;
2163            pStart = pList;
2164          }
2165          break;
2166 
2167        case 's':
2168          if( pList->nText==6 && strncmp(pList->zText,"struct",6)==0 ){
2169            if( pList->pNext && pList->pNext->eType==TT_Braces ){
2170              pList = pList->pNext;
2171            }else{
2172              nErr += ProcessTypeDecl(pList,flags,&resetFlag);
2173            }
2174          }else if( pList->nText==6 && strncmp(pList->zText,"static",6)==0 ){
2175            flags |= PS_Static;
2176          }
2177          break;
2178 
2179        case 't':
2180          if( pList->nText==7 && strncmp(pList->zText,"typedef",7)==0 ){
2181            flags |= PS_Typedef;
2182          }
2183          break;
2184 
2185        case 'u':
2186          if( pList->nText==5 && strncmp(pList->zText,"union",5)==0 ){
2187            if( pList->pNext && pList->pNext->eType==TT_Braces ){
2188              pList = pList->pNext;
2189            }else{
2190              nErr += ProcessTypeDecl(pList,flags,&resetFlag);
2191            }
2192          }
2193          break;
2194 
2195        default:
2196          break;
2197        }
2198        if( resetFlag!=0 ){
2199          while( pList && pList->zText[0]!=resetFlag ){
2200            pList = pList->pNext;
2201          }
2202          if( pList==0 ) goto end_of_loop;
2203          pStart = 0;
2204          flags = presetFlags;
2205        }
2206        break;
2207 
2208     case TT_Number:
2209        break;
2210 
2211     default:
2212        pStart = pList;
2213        flags = presetFlags;
2214        break;
2215     }
2216     pList = pList->pNext;
2217   }
2218   end_of_loop:
2219 
2220   /* Verify that all #ifs have a matching "#endif" */
2221   while( ifStack ){
2222     Ifmacro *pIf = ifStack;
2223     ifStack = pIf->pNext;
2224     fprintf(stderr,"%s:%d: This '#if' has no '#endif'\n",zFilename,
2225       pIf->nLine);
2226     SafeFree(pIf);
2227   }
2228 
2229   return nErr;
2230 }
2231 
2232 /*
2233 ** Reset the DP_Forward and DP_Declared flags on all Decl structures.
2234 ** Set both flags for anything that is tagged as local and isn't 
2235 ** in the file zFilename so that it won't be printing in other files.
2236 */
2237 static void ResetDeclFlags(char *zFilename){
2238   Decl *pDecl;
2239 
2240   for(pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext){
2241     DeclClearProperty(pDecl,DP_Forward|DP_Declared);
2242     if( DeclHasProperty(pDecl,DP_Local) && pDecl->zFile!=zFilename ){
2243       DeclSetProperty(pDecl,DP_Forward|DP_Declared);
2244     }
2245   }
2246 }
2247 
2248 /*
2249 ** Forward declaration of the ScanText() function.
2250 */
2251 static void ScanText(const char*, GenState *pState);
2252 
2253 /*
2254 ** The output in pStr is currently within an #if CONTEXT where context
2255 ** is equal to *pzIf.  (*pzIf might be NULL to indicate that we are
2256 ** not within any #if at the moment.)  We are getting ready to output
2257 ** some text that needs to be within the context of "#if NEW" where
2258 ** NEW is zIf.  Make an appropriate change to the context.
2259 */
2260 static void ChangeIfContext(
2261   const char *zIf,       /* The desired #if context */
2262   GenState *pState       /* Current state of the code generator */
2263 ){
2264   if( zIf==0 ){
2265     if( pState->zIf==0 ) return;
2266     StringAppend(pState->pStr,"#endif\n",0);
2267     pState->zIf = 0;
2268   }else{
2269     if( pState->zIf ){
2270       if( strcmp(zIf,pState->zIf)==0 ) return;
2271       StringAppend(pState->pStr,"#endif\n",0);
2272       pState->zIf = 0;
2273     }
2274     ScanText(zIf, pState);
2275     if( pState->zIf!=0 ){
2276       StringAppend(pState->pStr,"#endif\n",0);
2277     }
2278     StringAppend(pState->pStr,"#if ",0);
2279     StringAppend(pState->pStr,zIf,0);
2280     StringAppend(pState->pStr,"\n",0);
2281     pState->zIf = zIf;
2282   }
2283 }
2284 
2285 /*
2286 ** Add to the string pStr a #include of every file on the list of
2287 ** include files pInclude.  The table pTable contains all files that
2288 ** have already been #included at least once.  Don't add any
2289 ** duplicates.  Update pTable with every new #include that is added.
2290 */
2291 static void AddIncludes(
2292   Include *pInclude,       /* Write every #include on this list */
2293   GenState *pState         /* Current state of the code generator */
2294 ){
2295   if( pInclude ){
2296     if( pInclude->pNext ){
2297       AddIncludes(pInclude->pNext,pState);
2298     }
2299     if( IdentTableInsert(pState->pTable,pInclude->zLabel,0) ){
2300       ChangeIfContext(pInclude->zIf,pState);
2301       StringAppend(pState->pStr,"#include ",0);
2302       StringAppend(pState->pStr,pInclude->zFile,0);
2303       StringAppend(pState->pStr,"\n",1);
2304     }
2305   }
2306 }
2307 
2308 /*
2309 ** Add to the string pStr a declaration for the object described
2310 ** in pDecl.
2311 **
2312 ** If pDecl has already been declared in this file, detect that
2313 ** fact and abort early.  Do not duplicate a declaration.
2314 **
2315 ** If the needFullDecl flag is false and this object has a forward
2316 ** declaration, then supply the forward declaration only.  A later
2317 ** call to CompleteForwardDeclarations() will finish the declaration
2318 ** for us.  But if needFullDecl is true, we must supply the full
2319 ** declaration now.  Some objects do not have a forward declaration.
2320 ** For those objects, we must print the full declaration now.
2321 **
2322 ** Because it is illegal to duplicate a typedef in C, care is taken
2323 ** to insure that typedefs for the same identifier are only issued once.
2324 */
2325 static void DeclareObject(
2326   Decl *pDecl,        /* The thing to be declared */
2327   GenState *pState,   /* Current state of the code generator */
2328   int needFullDecl    /* Must have the full declaration.  A forward
2329                        * declaration isn't enough */
2330 ){
2331   Decl *p;               /* The object to be declared */
2332   int flag;
2333   int isCpp;             /* True if generating C++ */
2334   int doneTypedef = 0;   /* True if a typedef has been done for this object */
2335 
2336   /* 
2337   ** For any object that has a forward declaration, go ahead and do the
2338   ** forward declaration first.
2339   */
2340   isCpp = (pState->flags & DP_Cplusplus) != 0;
2341   for(p=pDecl; p; p=p->pSameName){
2342     if( p->zFwd ){
2343       if( !DeclHasProperty(p,DP_Forward) ){
2344         DeclSetProperty(p,DP_Forward);
2345         if( strncmp(p->zFwd,"typedef",7)==0 ){
2346           if( doneTypedef ) continue;
2347           doneTypedef = 1;
2348         }
2349         ChangeIfContext(p->zIf,pState);
2350         StringAppend(pState->pStr,isCpp ? p->zFwdCpp : p->zFwd,0);
2351       }
2352     }
2353   }
2354 
2355   /*
2356   ** Early out if everything is already suitably declared.
2357   **
2358   ** This is a very important step because it prevents us from
2359   ** executing the code the follows in a recursive call to this
2360   ** function with the same value for pDecl.
2361   */
2362   flag = needFullDecl ? DP_Declared|DP_Forward : DP_Forward;
2363   for(p=pDecl; p; p=p->pSameName){
2364     if( !DeclHasProperty(p,flag) ) break;
2365   }
2366   if( p==0 ){
2367     return;
2368   }
2369 
2370   /*
2371   ** Make sure we have all necessary #includes
2372   */
2373   for(p=pDecl; p; p=p->pSameName){
2374     AddIncludes(p->pInclude,pState);
2375   }
2376 
2377   /*
2378   ** Go ahead an mark everything as being declared, to prevent an
2379   ** infinite loop thru the ScanText() function.  At the same time,
2380   ** we decide which objects need a full declaration and mark them
2381   ** with the DP_Flag bit.  We are only able to use DP_Flag in this
2382   ** way because we know we'll never execute this far into this
2383   ** function on a recursive call with the same pDecl.  Hence, recursive
2384   ** calls to this function (through ScanText()) can never change the
2385   ** value of DP_Flag out from under us.
2386   */
2387   for(p=pDecl; p; p=p->pSameName){
2388     if( !DeclHasProperty(p,DP_Declared) 
2389      && (p->zFwd==0 || needFullDecl) 
2390      && p->zDecl!=0
2391     ){
2392       DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag);
2393     }else{
2394       DeclClearProperty(p,DP_Flag);
2395     }
2396   }
2397 
2398   /*
2399   ** Call ScanText() recusively (this routine is called from ScanText())
2400   ** to include declarations required to come before these declarations.
2401   */
2402   for(p=pDecl; p; p=p->pSameName){
2403     if( DeclHasProperty(p,DP_Flag) ){
2404       if( p->zDecl[0]=='#' ){
2405         ScanText(&p->zDecl[1],pState);
2406       }else{
2407         ScanText(p->zDecl,pState);
2408       }
2409     }
2410   }
2411 
2412   /*
2413   ** Output the declarations.  Do this in two passes.  First
2414   ** output everything that isn't a typedef.  Then go back and
2415   ** get the typedefs by the same name.
2416   */
2417   for(p=pDecl; p; p=p->pSameName){
2418     if( DeclHasProperty(p,DP_Flag) && !DeclHasProperty(p,TY_Typedef) ){
2419       if( DeclHasAnyProperty(p,TY_Enumeration) ){
2420         if( doneTypedef ) continue;
2421         doneTypedef = 1;
2422       }
2423       ChangeIfContext(p->zIf,pState);
2424       if( !isCpp && DeclHasAnyProperty(p,DP_ExternReqd) ){
2425         StringAppend(pState->pStr,"extern ",0);
2426       }else if( isCpp && DeclHasProperty(p,DP_Cplusplus|DP_ExternReqd) ){
2427         StringAppend(pState->pStr,"extern ",0);
2428       }else if( isCpp && DeclHasAnyProperty(p,DP_ExternCReqd|DP_ExternReqd) ){
2429         StringAppend(pState->pStr,"extern \"C\" ",0);
2430       }
2431       StringAppend(pState->pStr,p->zDecl,0);
2432       if( !isCpp && DeclHasProperty(p,DP_Cplusplus) ){
2433         fprintf(stderr,
2434           "%s: C code ought not reference the C++ object \"%s\"\n",
2435           pState->zFilename, p->zName);
2436         pState->nErr++;
2437       }
2438       DeclClearProperty(p,DP_Flag);
2439     }
2440   }
2441   for(p=pDecl; p && !doneTypedef; p=p->pSameName){
2442     if( DeclHasProperty(p,DP_Flag) ){
2443       /* This has to be a typedef */
2444       doneTypedef = 1;
2445       ChangeIfContext(p->zIf,pState);
2446       StringAppend(pState->pStr,p->zDecl,0);
2447     }
2448   }
2449 }
2450 
2451 /*
2452 ** This routine scans the input text given, and appends to the
2453 ** string in pState->pStr the text of any declarations that must
2454 ** occur before the text in zText.
2455 **
2456 ** If an identifier in zText is immediately followed by '*', then
2457 ** only forward declarations are needed for that identifier.  If the
2458 ** identifier name is not followed immediately by '*', we must supply
2459 ** a full declaration.
2460 */
2461 static void ScanText(
2462   const char *zText,    /* The input text to be scanned */
2463   GenState *pState      /* Current state of the code generator */
2464 ){
2465   int nextValid = 0;    /* True is sNext contains valid data */
2466   InStream sIn;         /* The input text */
2467   Token sToken;         /* The current token being examined */
2468   Token sNext;          /* The next non-space token */
2469 
2470   sIn.z = zText;
2471   sIn.i = 0;
2472   sIn.nLine = 1;
2473   while( sIn.z[sIn.i]!=0 ){
2474     if( nextValid ){
2475       sToken = sNext;
2476       nextValid = 0;
2477     }else{
2478       GetNonspaceToken(&sIn,&sToken);
2479     }
2480     if( sToken.eType==TT_Id ){
2481       int needFullDecl;   /* True if we need to provide the full declaration,
2482                           ** not just the forward declaration */
2483       Decl *pDecl;        /* The declaration having the name in sToken */
2484 
2485       /*
2486       ** See if there is a declaration in the database with the name given
2487       ** by sToken.
2488       */
2489       pDecl = FindDecl(sToken.zText,sToken.nText);
2490       if( pDecl==0 ) continue;
2491 
2492       /* 
2493       ** If we get this far, we've found an identifier that has a 
2494       ** declaration in the database.  Now see if we the full declaration
2495       ** or just a forward declaration.
2496       */
2497       GetNonspaceToken(&sIn,&sNext);
2498       if( sNext.zText[0]=='*' ){
2499         needFullDecl = 0;
2500       }else{
2501         needFullDecl = 1;
2502         nextValid = sNext.eType==TT_Id;
2503       }
2504 
2505       /*
2506       ** Generate the needed declaration.
2507       */
2508       DeclareObject(pDecl,pState,needFullDecl);
2509     }else if( sToken.eType==TT_Preprocessor ){
2510       sIn.i -= sToken.nText - 1;
2511     }
2512   }
2513 }
2514 
2515 /*
2516 ** Provide a full declaration to any object which so far has had only
2517 ** a foward declaration.
2518 */
2519 static void CompleteForwardDeclarations(GenState *pState){
2520   Decl *pDecl;
2521   int progress;
2522 
2523   do{
2524     progress = 0;
2525     for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){
2526       if( DeclHasProperty(pDecl,DP_Forward) 
2527        && !DeclHasProperty(pDecl,DP_Declared) 
2528       ){
2529         DeclareObject(pDecl,pState,1);
2530         progress = 1;
2531         assert( DeclHasProperty(pDecl,DP_Declared) );
2532       }
2533     }
2534   }while( progress );
2535 }
2536 
2537 /*
2538 ** Generate an include file for the given source file.  Return the number
2539 ** of errors encountered.
2540 **
2541 ** if nolocal_flag is true, then we do not generate declarations for
2542 ** objected marked DP_Local.
2543 */
2544 static int MakeHeader(InFile *pFile, FILE *report, int nolocal_flag){
2545   int nErr = 0;
2546   GenState sState;
2547   String outStr;
2548   IdentTable includeTable;
2549   Ident *pId;
2550   char *zNewVersion;
2551   char *zOldVersion;
2552 
2553   if( pFile->zHdr==0 || *pFile->zHdr==0 ) return 0;
2554   sState.pStr = &outStr;
2555   StringInit(&outStr);
2556   StringAppend(&outStr,zTopLine,nTopLine);
2557   sState.pTable = &includeTable;
2558   memset(&includeTable,0,sizeof(includeTable));
2559   sState.zIf = 0;
2560   sState.nErr = 0;
2561   sState.zFilename = pFile->zSrc;
2562   sState.flags = pFile->flags & DP_Cplusplus;
2563   ResetDeclFlags(nolocal_flag ? "no" : pFile->zSrc);
2564   for(pId = pFile->idTable.pList; pId; pId=pId->pNext){
2565     Decl *pDecl = FindDecl(pId->zName,0);
2566     if( pDecl ){
2567       DeclareObject(pDecl,&sState,1);
2568     }
2569   }
2570   CompleteForwardDeclarations(&sState);
2571   ChangeIfContext(0,&sState);
2572   nErr += sState.nErr;
2573   zOldVersion = ReadFile(pFile->zHdr);
2574   zNewVersion = StringGet(&outStr);
2575   if( report ) fprintf(report,"%s: ",pFile->zHdr);
2576   if( zOldVersion==0 ){
2577     if( report ) fprintf(report,"updated\n");
2578     if( WriteFile(pFile->zHdr,zNewVersion) ){
2579       fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
2580       nErr++;
2581     }
2582   }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
2583     if( report ) fprintf(report,"error!\n");
2584     fprintf(stderr,
2585        "%s: Can't overwrite this file because it wasn't previously\n"
2586        "%*s  generated by 'makeheaders'.\n",
2587        pFile->zHdr, strlen(pFile->zHdr), "");
2588     nErr++;
2589   }else if( strcmp(zOldVersion,zNewVersion)!=0 ){
2590     if( report ) fprintf(report,"updated\n");
2591     if( WriteFile(pFile->zHdr,zNewVersion) ){
2592       fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
2593       nErr++;
2594     }
2595   }else if( report ){
2596     fprintf(report,"unchanged\n");
2597   }
2598   SafeFree(zOldVersion); 
2599   IdentTableReset(&includeTable);
2600   StringReset(&outStr);
2601   return nErr;
2602 }
2603 
2604 /*
2605 ** Generate a global header file -- a header file that contains all
2606 ** declarations.  If the forExport flag is true, then only those
2607 ** objects that are exported are included in the header file.
2608 */
2609 static int MakeGlobalHeader(int forExport){
2610   GenState sState;
2611   String outStr;
2612   IdentTable includeTable;
2613   Decl *pDecl;
2614 
2615   sState.pStr = &outStr;
2616   StringInit(&outStr);
2617   /* StringAppend(&outStr,zTopLine,nTopLine); */
2618   sState.pTable = &includeTable;
2619   memset(&includeTable,0,sizeof(includeTable));
2620   sState.zIf = 0;
2621   sState.nErr = 0;
2622   sState.zFilename = "(all)";
2623   sState.flags = 0;
2624   ResetDeclFlags(0);
2625   for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){
2626     if( forExport==0 || DeclHasProperty(pDecl,DP_Export) ){
2627       DeclareObject(pDecl,&sState,1);
2628     }
2629   }
2630   ChangeIfContext(0,&sState);
2631   printf("%s",StringGet(&outStr));
2632   IdentTableReset(&includeTable);
2633   StringReset(&outStr);
2634   return 0;  
2635 }
2636 
2637 #ifdef DEBUG
2638 /*
2639 ** Return the number of characters in the given string prior to the
2640 ** first newline.
2641 */
2642 static int ClipTrailingNewline(char *z){
2643   int n = strlen(z);
2644   while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ){ n--; }
2645   return n;
2646 }
2647 
2648 /*
2649 ** Dump the entire declaration list for debugging purposes
2650 */
2651 static void DumpDeclList(void){
2652   Decl *pDecl;
2653 
2654   for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){
2655     printf("**** %s from file %s ****\n",pDecl->zName,pDecl->zFile);
2656     if( pDecl->zIf ){
2657       printf("If: [%.*s]\n",ClipTrailingNewline(pDecl->zIf),pDecl->zIf);
2658     }
2659     if( pDecl->zFwd ){
2660       printf("Decl: [%.*s]\n",ClipTrailingNewline(pDecl->zFwd),pDecl->zFwd);
2661     }
2662     if( pDecl->zDecl ){
2663       printf("Def: [%.*s]\n",ClipTrailingNewline(pDecl->zDecl),pDecl->zDecl);
2664     }
2665     if( pDecl->flags ){
2666       static struct {
2667         int mask;
2668         char *desc;
2669       } flagSet[] = {
2670         { TY_Class,       "class" },
2671         { TY_Enumeration, "enum" },
2672         { TY_Structure,   "struct" },
2673         { TY_Union,       "union" },
2674         { TY_Variable,    "variable" },
2675         { TY_Subroutine,  "function" },
2676         { TY_Typedef,     "typedef" },
2677         { TY_Macro,       "macro" },
2678         { DP_Export,      "export" },
2679         { DP_Local,       "local" },
2680         { DP_Cplusplus,   "C++" },
2681       };
2682       int i;
2683       printf("flags:");
2684       for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){
2685         if( flagSet[i].mask & pDecl->flags ){
2686           printf(" %s", flagSet[i].desc);
2687         }
2688       }
2689       printf("\n");
2690     }
2691     if( pDecl->pInclude ){
2692       Include *p;
2693       printf("includes:");
2694       for(p=pDecl->pInclude; p; p=p->pNext){
2695         printf(" %s",p->zFile);
2696       }
2697       printf("\n");
2698     }
2699   }
2700 }
2701 #endif
2702 
2703 /*
2704 ** When the "-doc" command-line option is used, this routine is called
2705 ** to print all of the database information to standard output.
2706 */
2707 static void DocumentationDump(void){
2708   Decl *pDecl;
2709   static struct {
2710     int mask;
2711     char flag;
2712   } flagSet[] = {
2713     { TY_Class,       'c' },
2714     { TY_Enumeration, 'e' },
2715     { TY_Structure,   's' },
2716     { TY_Union,       'u' },
2717     { TY_Variable,    'v' },
2718     { TY_Subroutine,  'f' },
2719     { TY_Typedef,     't' },
2720     { TY_Macro,       'm' },
2721     { DP_Export,      'x' },
2722     { DP_Local,       'l' },
2723     { DP_Cplusplus,   '+' },
2724   };
2725 
2726   for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){
2727     int i;
2728     int nLabel = 0;
2729     char *zDecl;
2730     char zLabel[50];
2731     for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){
2732       if( DeclHasProperty(pDecl,flagSet[i].mask) ){
2733         zLabel[nLabel++] = flagSet[i].flag;
2734       }
2735     }
2736     if( nLabel==0 ) continue;
2737     zLabel[nLabel] = 0;
2738     zDecl = pDecl->zDecl;
2739     if( zDecl==0 ) zDecl = pDecl->zFwd;
2740     printf("%s %s %s %d %d %d %d %d %d\n",
2741        pDecl->zName,
2742        zLabel,
2743        pDecl->zFile,
2744        pDecl->pComment ? (int)pDecl->pComment/sizeof(Token) : 0,
2745        pDecl->pComment ? pDecl->pComment->nText+1 : 0,
2746        pDecl->zIf ? strlen(pDecl->zIf)+1 : 0,
2747        zDecl ? strlen(zDecl) : 0,
2748        pDecl->pComment ? pDecl->pComment->nLine : 0,
2749        pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
2750     );
2751     if( pDecl->pComment ){
2752       printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
2753     }
2754     if( pDecl->zIf ){
2755       printf("%s\n",pDecl->zIf);
2756     }
2757     if( zDecl ){
2758       printf("%s",zDecl);
2759     }
2760     if( pDecl->tokenCode.nText ){
2761       printf("%.*s\n",pDecl->tokenCode.nText, pDecl->tokenCode.zText);
2762     }
2763   }
2764 }
2765 
2766 /*
2767 ** Given the complete text of an input file, this routine prints a
2768 ** documentation record for the header comment at the beginning of the
2769 ** file (if the file has a header comment.)
2770 */
2771 void PrintModuleRecord(const char *zFile, const char *zFilename){
2772   int i;
2773   static int addr = 5;
2774   while( isspace(*zFile) ){ zFile++; }
2775   if( *zFile!='/' || zFile[1]!='*' ) return;
2776   for(i=2; zFile[i] && (zFile[i-1]!='/' || zFile[i-2]!='*'); i++){}
2777   if( zFile[i]==0 ) return;
2778   printf("%s M %s %d %d 0 0 0\n%.*s\n",
2779     zFilename, zFilename, addr, i+1, i, zFile);
2780   addr += 4;
2781 }
2782 
2783 
2784 /*
2785 ** Given an input argument to the program, construct a new InFile
2786 ** object.
2787 */
2788 static InFile *CreateInFile(char *zArg, int *pnErr){
2789   int nSrc;
2790   char *zSrc;
2791   InFile *pFile;
2792   int i;
2793 
2794   /* 
2795   ** Get the name of the input file to be scanned
2796   */
2797   zSrc = zArg;
2798   for(nSrc=0; zSrc[nSrc] && zArg[nSrc]!=':'; nSrc++){}
2799   pFile = SafeMalloc( sizeof(InFile) );
2800   memset(pFile,0,sizeof(InFile));
2801   pFile->zSrc = StrDup(zSrc,nSrc);
2802 
2803   /* Figure out if we are dealing with C or C++ code.  Assume any
2804   ** file with ".c" or ".h" is C code and all else is C++.
2805   */
2806   if( nSrc>2 && zSrc[nSrc-2]=='.' && (zSrc[nSrc-1]=='c' || zSrc[nSrc-1]=='h')){
2807     pFile->flags &= ~DP_Cplusplus;
2808   }else{
2809     pFile->flags |= DP_Cplusplus;
2810   }
2811 
2812   /*
2813   ** If a separate header file is specified, use it
2814   */
2815   if( zSrc[nSrc]==':' ){
2816     int nHdr;
2817     char *zHdr;
2818     zHdr = &zSrc[nSrc+1];
2819     for(nHdr=0; zHdr[nHdr] && zHdr[nHdr]!=':'; nHdr++){}
2820     pFile->zHdr = StrDup(zHdr,nHdr);
2821   }
2822 
2823   /* Look for any 'c' or 'C' in the suffix of the file name and change
2824   ** that character to 'h' or 'H' respectively.  If no 'c' or 'C' is found,
2825   ** then assume we are dealing with a header.
2826   */
2827   else{
2828     int foundC = 0;
2829     pFile->zHdr = StrDup(zSrc,nSrc);
2830     for(i = nSrc-1; i>0 && pFile->zHdr[i]!='.'; i--){
2831       if( pFile->zHdr[i]=='c' ){
2832         foundC = 1;
2833         pFile->zHdr[i] = 'h';
2834       }else if( pFile->zHdr[i]=='C' ){
2835         foundC = 1;
2836         pFile->zHdr[i] = 'H';
2837       }
2838     }
2839     if( !foundC ){
2840       SafeFree(pFile->zHdr);
2841       pFile->zHdr = 0;
2842     }
2843   }
2844 
2845   /*
2846   ** If pFile->zSrc contains no 'c' or 'C' in its extension, it
2847   ** must be a header file.   In that case, we need to set the 
2848   ** PS_Interface flag.
2849   */
2850   pFile->flags |= PS_Interface;
2851   for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){
2852     if( zSrc[i]=='c' || zSrc[i]=='C' ){
2853       pFile->flags &= ~PS_Interface;
2854       break;
2855     }
2856   }
2857 
2858   /* Done! 
2859   */
2860   return pFile;
2861 }
2862 
2863 /* MS-Windows and MS-DOS both have the following serious OS bug:  the
2864 ** length of a command line is severely restricted.  But this program
2865 ** occasionally requires long command lines.  Hence the following
2866 ** work around.
2867 **
2868 ** If the parameters "-f FILENAME" appear anywhere on the command line,
2869 ** then the named file is scanned for additional command line arguments.
2870 ** These arguments are substituted in place of the "FILENAME" argument
2871 ** in the original argument list.
2872 **
2873 ** This first parameter to this routine is the index of the "-f"
2874 ** parameter in the argv[] array.  The argc and argv are passed by
2875 ** pointer so that they can be changed.
2876 **
2877 ** Parsing of the parameters in the file is very simple.  Parameters
2878 ** can be separated by any amount of white-space (including newlines
2879 ** and carriage returns.)  There are now quoting characters of any
2880 ** kind.  The length of a token is limited to about 1000 characters.
2881 */
2882 static void AddParameters(int index, int *pArgc, char ***pArgv){
2883   int argc = *pArgc;      /* The original argc value */
2884   char **argv = *pArgv;   /* The original argv value */
2885   int newArgc;            /* Value for argc after inserting new arguments */
2886   char **zNew;            /* The new argv after this routine is done */
2887   char *zFile;            /* Name of the input file */
2888   int nNew = 0;           /* Number of new entries in the argv[] file */
2889   int nAlloc = 0;         /* Space allocated for zNew[] */
2890   int i;                  /* Loop counter */
2891   int n;                  /* Number of characters in a new argument */
2892   int c;                  /* Next character of input */
2893   int startOfLine = 1;    /* True if we are where '#' can start a comment */
2894   FILE *in;               /* The input file */
2895   char zBuf[1000];        /* A single argument is accumulated here */
2896 
2897   if( index+1==argc ) return;
2898   zFile = argv[index+1];
2899   in = fopen(zFile,"r");
2900   if( in==0 ){
2901     fprintf(stderr,"Can't open input file \"%s\"\n",zFile);
2902     exit(1);
2903   }
2904   c = ' ';
2905   while( c!=EOF ){
2906     while( c!=EOF && isspace(c) ){
2907       if( c=='\n' ){
2908         startOfLine = 1;
2909       }
2910       c = getc(in); 
2911       if( startOfLine && c=='#' ){
2912         while( c!=EOF && c!='\n' ){
2913           c = getc(in);
2914         }
2915       }
2916     }
2917     n = 0;
2918     while( c!=EOF && !isspace(c) ){
2919       if( n<sizeof(zBuf)-1 ){ zBuf[n++] = c; }
2920       startOfLine = 0;
2921       c = getc(in);
2922     }
2923     zBuf[n] = 0;
2924     if( n>0 ){
2925       nNew++;
2926       if( nNew + argc > nAlloc ){
2927         if( nAlloc==0 ){
2928           nAlloc = 100 + argc;
2929           zNew = malloc( sizeof(char*) * nAlloc );
2930         }else{
2931           nAlloc *= 2;
2932           zNew = realloc( zNew, sizeof(char*) * nAlloc );  
2933         }
2934       }
2935       if( zNew ){
2936         int j = nNew + index;
2937         zNew[j] = malloc( n + 1 );
2938         if( zNew[j] ){
2939           strcpy( zNew[j], zBuf );
2940         }
2941       }
2942     }
2943   }
2944   newArgc = argc + nNew - 1;
2945   for(i=0; i<=index; i++){
2946     zNew[i] = argv[i];
2947   }
2948   for(i=nNew + index + 1; i<newArgc; i++){
2949     zNew[i] = argv[i + 1 - nNew];
2950   }
2951   zNew[newArgc] = 0;
2952   *pArgc = newArgc;
2953   *pArgv = zNew;
2954 }
2955 
2956 #ifdef NOT_USED
2957 /*
2958 ** Return the time that the given file was last modified.  If we can't
2959 ** locate the file (because, for example, it doesn't exist), then
2960 ** return 0.
2961 */
2962 static unsigned int ModTime(const char *zFilename){
2963   unsigned int mTime = 0;
2964   struct stat sStat;
2965   if( stat(zFilename,&sStat)==0 ){
2966     mTime = sStat.st_mtime;
2967   }
2968   return mTime;
2969 }
2970 #endif
2971 
2972 /*
2973 ** Print a usage comment for this program.
2974 */
2975 static void Usage(const char *argv0, const char *argvN){
2976   fprintf(stderr,"%s: Illegal argument \"%s\"\n",argv0,argvN);
2977   fprintf(stderr,"Usage: %s [options] filename...\n"
2978     "Options:\n"
2979     "  -h          Generate a single .h to standard output.\n"
2980     "  -H          Like -h, but only output EXPORT declarations.\n"
2981     "  -v          (verbose) Write status information to the screen.\n"
2982     "  -doc        Generate no header files.  Instead, output information\n"
2983     "              that can be used by an automatic program documentation\n"
2984     "              and cross-reference generator.\n"
2985     "  -local      Generate prototypes for \"static\" functions and\n"
2986     "              procedures.\n"
2987     "  -f FILE     Read additional command-line arguments from the file named\n"
2988     "              \"FILE\".\n"
2989 #ifdef DEBUG
2990     "  -! MASK     Set the debugging mask to the number \"MASK\".\n"
2991 #endif
2992     "  --          Treat all subsequent comment-line parameters as filenames,\n"
2993     "              even if they begin with \"-\".\n",
2994     argv0
2995   );
2996 }
2997 
2998 /*
2999 ** The following text contains a few simple #defines that we want
3000 ** to be available to every file.
3001 */
3002 static char zInit[] = 
3003   "#define INTERFACE 0\n"
3004   "#define EXPORT_INTERFACE 0\n"
3005   "#define LOCAL_INTERFACE 0\n"
3006   "#define EXPORT\n"
3007   "#define LOCAL static\n"
3008 ;
3009 
3010 #if TEST==0
3011 int main(int argc, char **argv){
3012   int i;                /* Loop counter */
3013   int nErr = 0;         /* Number of errors encountered */
3014   Token *pList;         /* List of input tokens for one file */
3015   InFile *pFileList = 0;/* List of all input files */
3016   InFile *pTail;        /* Last file on the list */
3017   InFile *pFile;        /* for looping over the file list */
3018   int h_flag = 0;       /* True if -h is present.  Output unified header */
3019   int H_flag = 0;       /* True if -H is present.  Output EXPORT header */
3020   int v_flag = 0;       /* Verbose */
3021   int noMoreFlags;      /* True if -- has been seen. */
3022   FILE *report;         /* Send progress reports to this, if not NULL */
3023 
3024   noMoreFlags = 0;
3025   for(i=1; i<argc; i++){
3026     if( argv[i][0]=='-' && !noMoreFlags ){
3027       switch( argv[i][1] ){
3028         case 'h':   h_flag = 1;   break;
3029         case 'H':   H_flag = 1;   break;
3030         case 'v':   v_flag = 1;   break;
3031         case 'd':   doc_flag = 1; proto_static = 1; break;
3032         case 'l':   proto_static = 1; break;
3033         case 'f':   AddParameters(i, &argc, &argv); break;
3034         case '-':   noMoreFlags = 1;   break;
3035 #ifdef DEBUG
3036         case '!':   i++;  debugMask = strtol(argv[i],0,0); break;
3037 #endif
3038         default:    Usage(argv[0],argv[i]); return 1;
3039       }
3040     }else{
3041       pFile = CreateInFile(argv[i],&nErr);
3042       if( pFile ){
3043         if( pFileList ){
3044           pTail->pNext = pFile;
3045           pTail = pFile;
3046         }else{
3047           pFileList = pTail = pFile;
3048         }
3049       }
3050     }
3051   }
3052   if( h_flag && H_flag ){
3053     h_flag = 0;
3054   }
3055   if( v_flag ){
3056     report = (h_flag || H_flag) ? stderr : stdout;
3057   }else{
3058     report = 0;
3059   }
3060   if( nErr>0 ){
3061     return nErr;
3062   }
3063   for(pFile=pFileList; pFile; pFile=pFile->pNext){
3064     char *zFile;
3065 
3066     zFilename = pFile->zSrc;
3067     if( zFilename==0 ) continue;
3068     zFile = ReadFile(zFilename);
3069     if( zFile==0 ){
3070       fprintf(stderr,"Can't read input file \"%s\"\n",zFilename);
3071       nErr++;
3072       continue;
3073     }
3074     if( strncmp(zFile,zTopLine,nTopLine)==0 ){
3075       pFile->zSrc = 0;
3076     }else{
3077       if( report ) fprintf(report,"Reading %s...\n",zFilename);
3078       pList = TokenizeFile(zFile,&pFile->idTable);
3079       if( pList ){
3080         nErr += ParseFile(pList,pFile->flags);
3081         FreeTokenList(pList);
3082       }else if( zFile[0]==0 ){
3083         fprintf(stderr,"Input file \"%s\" is empty.\n", zFilename);
3084         nErr++;
3085       }else{
3086         fprintf(stderr,"Errors while processing \"%s\"\n", zFilename);
3087         nErr++;
3088       }
3089     }
3090     if( !doc_flag ) SafeFree(zFile);
3091     if( doc_flag ) PrintModuleRecord(zFile,zFilename);
3092   }
3093   if( nErr>0 ){
3094     return nErr;
3095   }
3096 #ifdef DEBUG
3097   if( debugMask & DECL_DUMP ){
3098     DumpDeclList();
3099     return nErr;
3100   }
3101 #endif
3102   if( doc_flag ){
3103     DocumentationDump();
3104     return nErr;
3105   }
3106   zFilename = "--internal--";
3107   pList = TokenizeFile(zInit,0);
3108   if( pList==0 ){
3109     return nErr+1;
3110   }
3111   ParseFile(pList,PS_Interface);
3112   FreeTokenList(pList);
3113   if( h_flag || H_flag ){
3114     nErr += MakeGlobalHeader(H_flag);
3115   }else{
3116     for(pFile=pFileList; pFile; pFile=pFile->pNext){
3117       if( pFile->zSrc==0 ) continue;
3118       nErr += MakeHeader(pFile,report,0);
3119     }
3120   }
3121   return nErr;
3122 }
3123 #endif
3124 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.