Fossil SCM

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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button