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
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.