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