diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2019-01-07 19:34:50 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2019-01-07 19:34:50 (GMT) |
commit | 488dcf87e8e9f24a6d0e7955b7be56d972480ca4 (patch) | |
tree | fb96917e1f49ca7bcef7efc1107c1306f604a471 /tkhtml1/tools | |
parent | 0dd09186b4455ddd6c9dc09a2e27ea961a185784 (diff) | |
download | blt-488dcf87e8e9f24a6d0e7955b7be56d972480ca4.zip blt-488dcf87e8e9f24a6d0e7955b7be56d972480ca4.tar.gz blt-488dcf87e8e9f24a6d0e7955b7be56d972480ca4.tar.bz2 |
update TEA 3.13
Diffstat (limited to 'tkhtml1/tools')
-rw-r--r-- | tkhtml1/tools/getpage.c | 171 | ||||
-rw-r--r-- | tkhtml1/tools/httpget.c | 188 | ||||
-rw-r--r-- | tkhtml1/tools/makeheaders.c | 3129 | ||||
-rw-r--r-- | tkhtml1/tools/maketokens.tcl | 94 | ||||
-rw-r--r-- | tkhtml1/tools/sgmlparse.c | 207 | ||||
-rw-r--r-- | tkhtml1/tools/url.c | 268 |
6 files changed, 0 insertions, 4057 deletions
diff --git a/tkhtml1/tools/getpage.c b/tkhtml1/tools/getpage.c deleted file mode 100644 index 2b2bc56..0000000 --- a/tkhtml1/tools/getpage.c +++ /dev/null @@ -1,171 +0,0 @@ -/* -** This is a simple program used to retrieve an HTML document using -** HTTP. The program also fetches all images that the document -** references. -*/ -#include <stdio.h> -#include <stdlib.h> -#include "getpage.h" - -#define stricmp strcasecmp - - -/* -** Each image to be loaded is an instance of the following structure. -*/ -typedef struct Image Image; -struct Image { - char *zUrl; /* The URL for this image */ - char *zLocal; /* The local filename */ - Image *pNext; /* Next in a list of them all */ -}; - -static FILE *html; /* Html output to this file. */ -static int nImage = 0; /* Number of images loaded so far */ -static Image *pImage; /* List of all images */ -static global_nErr = 0; /* System wide errors */ -static char baseUrl[1000];/* The base URL */ -static int quiet = 0; /* The quiet flag */ - -/* -** Make sure the given URL is loaded as a local file. Return the -** name of the local file. -*/ -static char *GetImage(char *zUrl){ - Image *p; - for(p=pImage; p; p=p->pNext){ - if( strcmp(p->zUrl,zUrl)==0 ){ - return p->zLocal; - } - } - p = malloc( sizeof(*p) + strlen(zUrl) + 100 ); - p->zUrl = (char*)&p[1]; - strcpy(p->zUrl, zUrl); - p->zLocal = &p->zUrl[strlen(zUrl)+1]; - sprintf(p->zLocal,"image%d", ++nImage); - p->pNext = pImage; - pImage = p; - HttpFetch(zUrl, p->zLocal, quiet, 0, 0); - return p->zLocal; -} - -/* -** Print a usage comment and exit -*/ -void usage(char *argv0){ - fprintf(stderr,"Usage: %s URL\n",argv0); - exit(1); -} - -/* -** Handle anything that isn't markup -*/ -static void WordHandler(const char *zText, void *notUsed){ - fprintf(html, zText); -} - -/* -** Handle all markup that we don't care about. -*/ -static void DefaultMarkup(int argc, const char **argv, void *notUsed){ - int i; - fprintf(html,"<%s",argv[0]); - for(i=1; i<argc-1; i+=2){ - fprintf(html," %s=\"%s\"", argv[i], argv[i+1]); - } - fprintf(html,">"); -} - -/* -** Handler for <IMG> markup -*/ -static void ImageMarkup(int argc, const char **argv, void *notUsed){ - int i; - for(i=1; i<argc-1; i+=2){ - if( stricmp(argv[i],"src")==0 ){ - const char *azUrl[2]; - char *zResolved; - azUrl[0] = argv[i+1]; - azUrl[1] = 0; - zResolved = ResolveUrl(baseUrl, azUrl); - if( !quiet ){ - printf("Resolved: (%s) (%s) -> (%s)\n",baseUrl, azUrl[0], zResolved); - } - argv[i+1] = GetImage(zResolved); - /* printf("%s -> %s -> argv[i+1]\n",argv[i+1], zResolved); */ - free(zResolved); - } - } - DefaultMarkup(argc, argv, 0); -} - -/* -** Handler for <BASE> markup -*/ -static void BaseMarkup(int argc, const char **argv, void *notUsed){ - int i; - for(i=1; i<argc-1; i+=2){ - if( stricmp(argv[i],"href")==0 ){ - if( !quiet ){ - printf("Base Href=%s\n",argv[i+1]); - } - sprintf(baseUrl,"%.*s", sizeof(baseUrl), argv[i+1]); - } - } -} - -/* -** Name of a temporary file -*/ -static char zTemp[] = "index.html.orig"; - -/* -** The main routine -*/ -int main(int argc, char **argv){ - int i; /* Loop counter */ - int nErr; /* Number of errors */ - int rc; /* Result code */ - char *zUrl = 0; /* The URL */ - FILE *in; /* For reading the raw html */ - - if( argc<2 ) usage(argv[0]); - zUrl = 0; - for(i=1; i<argc; i++){ - if( strcmp(argv[i],"-quiet")==0 ){ - quiet = 1; - }else if( argv[i][0]=='-' ){ - usage(argv[0]); - }else{ - zUrl = argv[i]; - } - } - if( zUrl==0 ) usage(argv[0]); - rc = HttpFetch(zUrl, zTemp, quiet, sizeof(baseUrl), baseUrl); - if( rc!=200 ){ - unlink(zTemp); - fprintf(stderr,"Unable to fetch base page %s\n", zUrl); - exit(1); - } - in = fopen(zTemp,"r"); - /* unlink(zTemp); */ - if( in==0 ){ - perror("can't reopen temporary file!"); - exit(1); - } - html = fopen("index.html","w"); - if( html==0 ){ - perror("can't open output file \"index.html\""); - exit(1); - } - SgmlWordHandler(WordHandler); - SgmlSpaceHandler(WordHandler); - SgmlCommentHandler(WordHandler); - SgmlDefaultMarkupHandler(DefaultMarkup); - SgmlHandler("img", ImageMarkup); - SgmlHandler("base", BaseMarkup); - SgmlParse(in, 0); - fclose(in); - fclose(html); - return global_nErr; -} diff --git a/tkhtml1/tools/httpget.c b/tkhtml1/tools/httpget.c deleted file mode 100644 index 3532147..0000000 --- a/tkhtml1/tools/httpget.c +++ /dev/null @@ -1,188 +0,0 @@ -/* -** This file contains code to fetch a single URL into a local file. -*/ -#include <stdio.h> -#include <netdb.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <linux/in.h> -#include "httpget.h" - -#define strnicmp strncasecmp - -/* -** Get a URL using HTTP. Return the result code. If a Location: field -** appears in the header, write it into zLocation[]. Location[] should -** be at least 200 characters in size. -*/ -static int -HttpTryOnce(char *zUrl, char *zLocalFile, int quiet, char *zLocation){ - int i, j; - int nErr = 0; /* Number of errors */ - char *zPath; /* Pathname to send as second argument to GET */ - int iPort; /* TCP port for the server */ - struct hostent *pHost; /* Name information */ - int s; /* The main communications socket */ - int c; /* A character read from the remote side */ - int n; /* Number of characters in header */ - int rc = 200; /* The result code */ - FILE *sock; /* FILE corresponding to file descriptor s */ - FILE *out; /* Write output here */ - int last_was_nl; /* TRUE if last character received was '\n' */ - struct sockaddr_in addr; /* The address structure */ - int nByte = 0; - char zIpAddr[400]; /* The IP address of the server to print */ - char zMsg[1000]; /* Space to hold error messages */ - char zLine[1000]; /* A single line of the header */ - - out = fopen(zLocalFile, "w"); - if( out==0 ){ - sprintf(zMsg, "can't open output fule \"%.100s\"", zLocalFile); - perror(zMsg); - return 1; - } - - i = 0; - if( strnicmp(zUrl,"http:",5)==0 ){ i = 5; } - while( zUrl[i]=='/' ){ i++; } - j = 0; - while( zUrl[i] && zUrl[i]!=':' && zUrl[i]!='/' ){ - if( j<sizeof(zIpAddr)-1 ){ zIpAddr[j++] = zUrl[i]; } - i++; - } - zIpAddr[j] = 0; - if( zUrl[i]==':' ){ - iPort = 0; - i++; - while( isdigit(zUrl[i]) ){ - iPort = iPort*10 + zUrl[i] - '0'; - i++; - } - }else{ - iPort = 80; - } - zPath = &zUrl[i]; - - addr.sin_family = AF_INET; - addr.sin_port = htons(iPort); - *(int*)&addr.sin_addr = inet_addr(zIpAddr); - if( -1 == *(int*)&addr.sin_addr ){ - pHost = gethostbyname(zIpAddr); - if( pHost==0 ){ - fprintf(stderr,"can't resolve host name: %s\n",zIpAddr); - return 1; - } - memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); - if( !quiet ){ - fprintf(stderr,"Address resolution: %s -> %d.%d.%d.%d\n",zIpAddr, - pHost->h_addr_list[0][0]&0xff, - pHost->h_addr_list[0][1]&0xff, - pHost->h_addr_list[0][2]&0xff, - pHost->h_addr_list[0][3]&0xff); - } - } - s = socket(AF_INET,SOCK_STREAM,0); - if( s<0 ){ - sprintf(zMsg,"can't open socket to %.100s", zIpAddr); - perror(zMsg); - fclose(out); - return 1; - } - if( connect(s,(struct sockaddr*)&addr,sizeof(addr))<0 ){ - sprintf(zMsg,"can't connect to host %.100s", zIpAddr); - perror(zMsg); - fclose(out); - return 1; - } - sock = fdopen(s,"r+"); - if( *zPath==0 ) zPath = "/"; - fprintf(sock,"GET %s HTTP/1.0\r\n",zPath); - fprintf(sock,"User-Agent: Mozilla/2.0 (X11; U; Linux 0.99p17 i486)\r\n"); - if( iPort!=80 ){ - fprintf(sock,"Host: %s:%d\r\n", zIpAddr, iPort); - }else{ - fprintf(sock,"Host: %s\r\n", zIpAddr); - } - fprintf(sock,"Accept: image/gif, image/x-xbitmap, image/jpeg, */*\r\n"); - fprintf(sock,"\r\n"); - fflush(sock); - n = 0; - rc = 0; - while( (c=getc(sock))!=EOF && (c!='\n' || !last_was_nl) ){ - if( c=='\r' ) continue; - last_was_nl = (c=='\n'); - if( last_was_nl ){ - zLine[n] = 0; - if( strncmp(zLine,"Location:",9)==0 && zLocation ){ - int j, k; - for(j=9; isspace(zLine[j]); j++){} - k = 0; - while( zLine[j] && !isspace(zLine[j]) && k<199 ){ - zLocation[k++] = zLine[j++]; - } - zLocation[k] = 0; - if( !quiet ) fprintf(stderr,"Location: %s\n", zLocation); - }else if( rc==0 ){ - sscanf(zLine,"HTTP/%*d.%*d %d ",&rc); - if( !quiet ) fprintf(stderr,"Status: %d\n", rc); - } - } - if( n<sizeof(zLine)-1 ){ zLine[n++] = c; } - if( last_was_nl ){ n = 0; } - } - if( rc==0 ) rc = 200; - if( !quiet ){ - fprintf(stderr, "Reading %s...", zUrl); - } - while( (c=getc(sock))!=EOF ){ - nByte++; - putc(c,out); - } - if( !quiet ){ - fprintf(stderr, " %d bytes\n", nByte); - } - fclose(sock); - fclose(out); - return rc; -} - -/* -** Get the file. Take up to 7 redirects. -*/ -int HttpFetch( - char *zUrl, /* Fetch this URL */ - char *zLocalFile, /* Write to this file */ - int quiet, /* Be quiet if true */ - int nActual, /* Size of zActual[] */ - char *zActual /* Write actual URL retrieved here */ -){ - int i; - int rc; - char *zOriginalUrl = zUrl; - char zLocation[300]; - - for(i=0; i<7; i++){ - if( !quiet ) fprintf(stderr,"HTTP: %s -> %s\n", zUrl, zLocalFile); - rc = HttpTryOnce(zUrl, zLocalFile, quiet, zLocation); - if( rc==301 || rc==302 ){ - char *z; - const char *az[2]; - az[0] = zLocation; - az[1] = 0; - z = ResolveUrl(zUrl, az); - if( zUrl!=zOriginalUrl ){ - free(zUrl); - } - zUrl = z; - }else{ - break; - } - } - if( nActual>0 && zActual!=0 ){ - sprintf(zActual, "%.*s", nActual, zUrl); - } - if( zUrl!=zOriginalUrl ){ - free(zUrl); - } - return rc; -} diff --git a/tkhtml1/tools/makeheaders.c b/tkhtml1/tools/makeheaders.c deleted file mode 100644 index 6d05935..0000000 --- a/tkhtml1/tools/makeheaders.c +++ /dev/null @@ -1,3129 +0,0 @@ -/* -** This program scans C and C++ source files and automatically generates -** appropriate header files. -** %Z% %P% %I% %G% %Z% -*/ -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> -#include <memory.h> -#include <sys/stat.h> -#include <assert.h> -#ifndef WIN32 -# include <unistd.h> -#else -# include <string.h> -#endif - -/* -** Macros for debugging. -*/ -#ifdef DEBUG -static int debugMask = 0; -# define debug0(F,M) if( (F)&debugMask ){ fprintf(stderr,M); } -# define debug1(F,M,A) if( (F)&debugMask ){ fprintf(stderr,M,A); } -# define debug2(F,M,A,B) if( (F)&debugMask ){ fprintf(stderr,M,A,B); } -# define debug3(F,M,A,B,C) if( (F)&debugMask ){ fprintf(stderr,M,A,B,C); } -# define PARSER 0x00000001 -# define DECL_DUMP 0x00000002 -# define TOKENIZER 0x00000004 -#else -# define debug0(Flags, Format) -# define debug1(Flags, Format, A) -# define debug2(Flags, Format, A, B) -# define debug3(Flags, Format, A, B, C) -#endif - -/* -** The following macros are purely for the purpose of testing this -** program on itself. They don't really contribute to the code. -*/ -#define INTERFACE 1 -#define EXPORT_INTERFACE 1 -#define EXPORT - -/* -** Each token in a source file is represented by an instance of -** the following structure. Tokens are collected onto a list. -*/ -typedef struct Token Token; -struct Token { - const char *zText; /* The text of the token */ - int nText; /* Number of characters in the token's text */ - int eType; /* The type of this token */ - int nLine; /* The line number on which the token starts */ - Token *pComment; /* Most recent block comment before this token */ - Token *pNext; /* Next token on the list */ - Token *pPrev; /* Previous token on the list */ -}; - -/* -** During tokenization, information about the state of the input -** stream is held in an instance of the following structure -*/ -typedef struct InStream InStream; -struct InStream { - const char *z; /* Complete text of the input */ - int i; /* Next character to read from the input */ - int nLine; /* The line number for character z[i] */ -}; - -/* -** Each declaration in the C or C++ source files is parsed out and stored as -** an instance of the following structure. -** -** A "forward declaration" is a declaration that an object exists that -** doesn't tell about the objects structure. A typical forward declaration -** is: -** -** struct Xyzzy; -** -** Not every object has a forward declaration. If it does, thought, the -** forward declaration will be contained in the zFwd field for C and -** the zFwdCpp for C++. The zDecl field contains the complete -** declaration text. -*/ -typedef struct Decl Decl; -struct Decl { - char *zName; /* Name of the object being declared. The appearance - ** of this name is a source file triggers the declaration - ** to be added to the header for that file. */ - char *zFile; /* File from which extracted. */ - char *zIf; /* Surround the declaration with this #if */ - char *zFwd; /* A forward declaration. NULL if there is none. */ - char *zFwdCpp; /* Use this forward declaration for C++. */ - char *zDecl; /* A full declaration of this object */ - struct Include *pInclude; /* #includes that come before this declaration */ - int flags; /* See the "Properties" below */ - Token *pComment; /* A block comment associated with this declaration */ - Token tokenCode; /* Implementation of functions and procedures */ - Decl *pSameName; /* Next declaration with the same "zName" */ - Decl *pSameHash; /* Next declaration with same hash but different zName */ - Decl *pNext; /* Next declaration with a different name */ -}; - -/* -** Properties associated with declarations. -** -** DP_Forward and DP_Declared are used during the generation of a single -** header file in order to prevent duplicate declarations and definitions. -** DP_Forward is set after the object has been given a forward declaration -** and DP_Declared is set after the object gets a full declarations. -** (Example: A forward declaration is "typedef struct Abc Abc;" and the -** full declaration is "struct Abc { int a; float b; };".) -** -** The DP_Export and DP_Local flags are more permanent. They mark objects -** that have EXPORT scope and LOCAL scope respectively. If both of these -** marks are missing, then the object has library scope. The meanings of -** the scopes are as follows: -** -** LOCAL scope The object is only usable within the file in -** which it is declared. -** -** library scope The object is visible and usable within other -** files in the same project. By if the project is -** a library, then the object is not visible to users -** of the library. (i.e. the object does not appear -** in the output when using the -H option.) -** -** EXPORT scope The object is visible and usable everywhere. -** -** The DP_Flag is a temporary use flag that is used during processing to -** prevent an infinite loop. It's use is localized. -** -** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent -** and are used to specify what type of declaration the object requires. -*/ -#define DP_Forward 0x001 /* Has a forward declaration in this file */ -#define DP_Declared 0x002 /* Has a full declaration in this file */ -#define DP_Export 0x004 /* Export this declaration */ -#define DP_Local 0x008 /* Declare in its home file only */ -#define DP_Flag 0x010 /* Use to mark a subset of a Decl list - ** for special processing */ -#define DP_Cplusplus 0x020 /* Has C++ linkage and cannot appear in a - ** C header file */ -#define DP_ExternCReqd 0x040 /* Prepend 'extern "C"' in a C++ header. - ** Prepend nothing in a C header */ -#define DP_ExternReqd 0x080 /* Prepend 'extern "C"' in a C++ header if - ** DP_Cplusplus is not also set. If DP_Cplusplus - ** is set or this is a C header then - ** prepend 'extern' */ - -/* -** Convenience macros for dealing with declaration properties -*/ -#define DeclHasProperty(D,P) (((D)->flags&(P))==(P)) -#define DeclHasAnyProperty(D,P) (((D)->flags&(P))!=0) -#define DeclSetProperty(D,P) (D)->flags |= (P) -#define DeclClearProperty(D,P) (D)->flags &= ~(P) - -/* -** These are state properties of the parser. Each of the values is -** distinct from the DP_ values above so that both can be used in -** the same "flags" field. -** -** Be careful not to confuse PS_Export with DP_Export or -** PS_Local with DP_Local. Their names are similar, but the meanings -** of these flags are very different. -*/ -#define PS_Extern 0x000800 /* "extern" has been seen */ -#define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE" - ** and "#endif" */ -#define PS_Export2 0x002000 /* If "EXPORT" seen */ -#define PS_Typedef 0x004000 /* If "typedef" has been seen */ -#define PS_Static 0x008000 /* If "static" has been seen */ -#define PS_Interface 0x010000 /* If within #if INTERFACE..#endif */ -#define PS_Method 0x020000 /* If "::" token has been seen */ -#define PS_Local 0x040000 /* If within #if LOCAL_INTERFACE..#endif */ -#define PS_Local2 0x080000 /* If "LOCAL" seen. */ - -/* -** The following set of flags are ORed into the "flags" field of -** a Decl in order to identify what type of object is being -** declared. -*/ -#define TY_Class 0x00100000 -#define TY_Subroutine 0x00200000 -#define TY_Macro 0x00400000 -#define TY_Typedef 0x00800000 -#define TY_Variable 0x01000000 -#define TY_Structure 0x02000000 -#define TY_Union 0x04000000 -#define TY_Enumeration 0x08000000 -#define TY_Defunct 0x10000000 /* Used to erase a declaration */ - -/* -** Each nested #if (or #ifdef or #ifndef) is stored in a stack of -** instances of the following structure. -*/ -typedef struct Ifmacro Ifmacro; -struct Ifmacro { - int nLine; /* Line number where this macro occurs */ - char *zCondition; /* Text of the condition for this macro */ - Ifmacro *pNext; /* Next down in the stack */ - int flags; /* Can hold PS_Export, PS_Interface or PS_Local flags */ -}; - -/* -** When parsing a file, we need to keep track of what other files have -** be #include-ed. For each #include found, we create an instance of -** the following structure. -*/ -typedef struct Include Include; -struct Include { - char *zFile; /* The name of file include. Includes "" or <> */ - char *zIf; /* If not NULL, #include should be enclosed in #if */ - char *zLabel; /* A unique label used to test if this #include has - * appeared already in a file or not */ - Include *pNext; /* Previous include file, or NULL if this is the first */ -}; - -/* -** Identifiers found in a source file that might be used later to provoke -** the copying of a declaration into the corresponding header file are -** stored in a hash table as instances of the following structure. -*/ -typedef struct Ident Ident; -struct Ident { - char *zName; /* The text of this identifier */ - Ident *pCollide; /* Next identifier with the same hash */ - Ident *pNext; /* Next identifier in a list of them all */ -}; - -/* -** A complete table of identifiers is stored in an instance of -** the next structure. -*/ -#define IDENT_HASH_SIZE 2237 -typedef struct IdentTable IdentTable; -struct IdentTable { - Ident *pList; /* List of all identifiers in this table */ - Ident *apTable[IDENT_HASH_SIZE]; /* The hash table */ -}; - -/* -** The following structure holds all information for a single -** source file named on the command line of this program. -*/ -typedef struct InFile InFile; -struct InFile { - char *zSrc; /* Name of input file */ - char *zHdr; /* Name of the generated .h file for this input. - ** Will be NULL if input is to be scanned only */ - int flags; /* One or more DP_, PS_ and/or TY_ flags */ - InFile *pNext; /* Next input file in the list of them all */ - IdentTable idTable; /* All identifiers in this input file */ -}; - -/* -** An unbounded string is able to grow without limit. We use these -** to construct large in-memory strings from lots of smaller components. -*/ -typedef struct String String; -struct String { - int nAlloc; /* Number of bytes allocated */ - int nUsed; /* Number of bytes used (not counting null terminator) */ - char *zText; /* Text of the string */ -}; - -/* -** The following structure contains a lot of state information used -** while generating a .h file. We put the information in this structure -** and pass around a pointer to this structure, rather than pass around -** all of the information separately. This helps reduce the number of -** arguments to generator functions. -*/ -typedef struct GenState GenState; -struct GenState { - String *pStr; /* Write output to this string */ - IdentTable *pTable; /* A table holding the zLabel of every #include that - * has already been generated. Used to avoid - * generating duplicate #includes. */ - const char *zIf; /* If not NULL, then we are within a #if with - * this argument. */ - int nErr; /* Number of errors */ - const char *zFilename; /* Name of the source file being scanned */ - int flags; /* Various flags (DP_ and PS_ flags above) */ -}; - -/* -** The following text line appears at the top of every file generated -** by this program. By recognizing this line, the program can be sure -** never to read a file that it generated itself. -*/ -const char zTopLine[] = - "/* \aThis file was automatically generated. Do not edit! */\n"; -#define nTopLine (sizeof(zTopLine)-1) - -/* -** The name of the file currently being parsed. -*/ -static char *zFilename; - -/* -** The stack of #if macros for the file currently being parsed. -*/ -static Ifmacro *ifStack = 0; - -/* -** A list of all files that have been #included so far in a file being -** parsed. -*/ -static Include *includeList = 0; - -/* -** The last block comment seen. -*/ -static Token *blockComment = 0; - -/* -** The following flag is set if the -doc flag appears on the -** command line. -*/ -static int doc_flag = 0; - -/* -** If the following flag is set, then makeheaders will attempt to -** generate prototypes for static functions and procedures. -*/ -static int proto_static = 0; - -/* -** A list of all declarations. The list is held together using the -** pNext field of the Decl structure. -*/ -static Decl *pDeclFirst; /* First on the list */ -static Decl *pDeclLast; /* Last on the list */ - -/* -** A hash table of all declarations -*/ -#define DECL_HASH_SIZE 3371 -static Decl *apTable[DECL_HASH_SIZE]; - -/* -** The TEST macro must be defined to something. Make sure this is the -** case. -*/ -#ifndef TEST -# define TEST 0 -#endif - -#ifdef NOT_USED -/* -** We do our own assertion macro so that we can have more control -** over debugging. -*/ -#define Assert(X) if(!(X)){ CantHappen(__LINE__); } -#define CANT_HAPPEN CantHappen(__LINE__) -static void CantHappen(int iLine){ - fprintf(stderr,"Assertion failed on line %d\n",iLine); - *(char*)1 = 0; /* Force a core-dump */ -} -#endif - -/* -** Memory allocation functions that are guaranteed never to return NULL. -*/ -static void *SafeMalloc(int nByte){ - void *p = malloc( nByte ); - if( p==0 ){ - fprintf(stderr,"Out of memory. Can't allocate %d bytes.\n",nByte); - exit(1); - } - return p; -} -static void SafeFree(void *pOld){ - if( pOld ){ - free(pOld); - } -} -static void *SafeRealloc(void *pOld, int nByte){ - void *p; - if( pOld==0 ){ - p = SafeMalloc(nByte); - }else{ - p = realloc(pOld, nByte); - if( p==0 ){ - fprintf(stderr, - "Out of memory. Can't enlarge an allocation to %d bytes\n",nByte); - exit(1); - } - } - return p; -} -static char *StrDup(const char *zSrc, int nByte){ - char *zDest; - if( nByte<=0 ){ - nByte = strlen(zSrc); - } - zDest = SafeMalloc( nByte + 1 ); - strncpy(zDest,zSrc,nByte); - zDest[nByte] = 0; - return zDest; -} - -/* -** Return TRUE if the character X can be part of an identifier -*/ -#define ISALNUM(X) ((X)=='_' || isalnum(X)) - -/* -** Routines for dealing with unbounded strings. -*/ -static void StringInit(String *pStr){ - pStr->nAlloc = 0; - pStr->nUsed = 0; - pStr->zText = 0; -} -static void StringReset(String *pStr){ - SafeFree(pStr->zText); - StringInit(pStr); -} -static void StringAppend(String *pStr, const char *zText, int nByte){ - if( nByte<=0 ){ - nByte = strlen(zText); - } - if( pStr->nUsed + nByte >= pStr->nAlloc ){ - if( pStr->nAlloc==0 ){ - pStr->nAlloc = nByte + 100; - pStr->zText = SafeMalloc( pStr->nAlloc ); - }else{ - pStr->nAlloc = pStr->nAlloc*2 + nByte; - pStr->zText = SafeRealloc(pStr->zText, pStr->nAlloc); - } - } - strncpy(&pStr->zText[pStr->nUsed],zText,nByte); - pStr->nUsed += nByte; - pStr->zText[pStr->nUsed] = 0; -} -#define StringGet(S) ((S)->zText?(S)->zText:"") - -/* -** Compute a hash on a string. The number returned is a non-negative -** value between 0 and 2**31 - 1 -*/ -static int Hash(const char *z, int n){ - int h = 0; - if( n<=0 ){ - n = strlen(z); - } - while( n-- ){ - h = h ^ (h<<5) ^ *z++; - } - if( h<0 ) h = -h; - return h; -} - -/* -** Given an identifier name, try to find a declaration for that -** identifier in the hash table. If found, return a pointer to -** the Decl structure. If not found, return 0. -*/ -static Decl *FindDecl(const char *zName, int len){ - int h; - Decl *p; - - if( len<=0 ){ - len = strlen(zName); - } - h = Hash(zName,len) % DECL_HASH_SIZE; - p = apTable[h]; - while( p && (strncmp(p->zName,zName,len)!=0 || p->zName[len]!=0) ){ - p = p->pSameHash; - } - return p; -} - -/* -** Install the given declaration both in the hash table and on -** the list of all declarations. -*/ -static void InstallDecl(Decl *pDecl){ - int h; - Decl *pOther; - - h = Hash(pDecl->zName,0) % DECL_HASH_SIZE; - pOther = apTable[h]; - while( pOther && strcmp(pDecl->zName,pOther->zName)!=0 ){ - pOther = pOther->pSameHash; - } - if( pOther ){ - pDecl->pSameName = pOther->pSameName; - pOther->pSameName = pDecl; - }else{ - pDecl->pSameName = 0; - pDecl->pSameHash = apTable[h]; - apTable[h] = pDecl; - } - pDecl->pNext = 0; - if( pDeclFirst==0 ){ - pDeclFirst = pDeclLast = pDecl; - }else{ - pDeclLast->pNext = pDecl; - pDeclLast = pDecl; - } -} - -/* -** Look at the current ifStack. If anything declared at the current -** position must be surrounded with -** -** #if STUFF -** #endif -** -** Then this routine computes STUFF and returns a pointer to it. Memory -** to hold the value returned is obtained from malloc(). -*/ -static char *GetIfString(void){ - Ifmacro *pIf; - char *zResult = 0; - int hasIf = 0; - String str; - - for(pIf = ifStack; pIf; pIf=pIf->pNext){ - if( pIf->zCondition==0 || *pIf->zCondition==0 ) continue; - if( !hasIf ){ - hasIf = 1; - StringInit(&str); - }else{ - StringAppend(&str," && ",4); - } - StringAppend(&str,pIf->zCondition,0); - } - if( hasIf ){ - zResult = StrDup(StringGet(&str),0); - StringReset(&str); - }else{ - zResult = 0; - } - return zResult; -} - -/* -** Create a new declaration and put it in the hash table. Also -** return a pointer to it so that we can fill in the zFwd and zDecl -** fields, and so forth. -*/ -static Decl *CreateDecl( - const char *zName, /* Name of the object being declared. */ - int nName /* Length of the name */ -){ - Decl *pDecl; - - pDecl = SafeMalloc( sizeof(Decl) + nName + 1); - memset(pDecl,0,sizeof(Decl)); - pDecl->zName = (char*)&pDecl[1]; - sprintf(pDecl->zName,"%.*s",nName,zName); - pDecl->zFile = zFilename; - pDecl->pInclude = includeList; - pDecl->zIf = GetIfString(); - InstallDecl(pDecl); - return pDecl; -} - -/* -** Insert a new identifier into an table of identifiers. Return TRUE if -** a new identifier was inserted and return FALSE if the identifier was -** already in the table. -*/ -static int IdentTableInsert( - IdentTable *pTable, /* The table into which we will insert */ - const char *zId, /* Name of the identifiers */ - int nId /* Length of the identifier name */ -){ - int h; - Ident *pId; - - if( nId<=0 ){ - nId = strlen(zId); - } - h = Hash(zId,nId) % IDENT_HASH_SIZE; - for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){ - if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){ - /* printf("Already in table: %.*s\n",nId,zId); */ - return 0; - } - } - pId = SafeMalloc( sizeof(Ident) + nId + 1 ); - pId->zName = (char*)&pId[1]; - sprintf(pId->zName,"%.*s",nId,zId); - pId->pNext = pTable->pList; - pTable->pList = pId; - pId->pCollide = pTable->apTable[h]; - pTable->apTable[h] = pId; - /* printf("Add to table: %.*s\n",nId,zId); */ - return 1; -} - -/* -** Check to see if the given value is in the given IdentTable. Return -** true if it is and false if it is not. -*/ -static int IdentTableTest( - IdentTable *pTable, /* The table in which to search */ - const char *zId, /* Name of the identifiers */ - int nId /* Length of the identifier name */ -){ - int h; - Ident *pId; - - if( nId<=0 ){ - nId = strlen(zId); - } - h = Hash(zId,nId) % IDENT_HASH_SIZE; - for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){ - if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){ - return 1; - } - } - return 0; -} - -/* -** Remove every identifier from the given table. Reset the table to -** its initial state. -*/ -static void IdentTableReset(IdentTable *pTable){ - Ident *pId, *pNext; - - for(pId = pTable->pList; pId; pId = pNext){ - pNext = pId->pNext; - SafeFree(pId); - } - memset(pTable,0,sizeof(IdentTable)); -} - -#ifdef DEBUG -/* -** Print the name of every identifier in the given table, one per line -*/ -static void IdentTablePrint(IdentTable *pTable, FILE *pOut){ - Ident *pId; - - for(pId = pTable->pList; pId; pId = pId->pNext){ - fprintf(pOut,"%s\n",pId->zName); - } -} -#endif - -/* -** Read an entire file into memory. Return a pointer to the memory. -** -** The memory is obtained from SafeMalloc and must be freed by the -** calling function. -** -** If the read fails for any reason, 0 is returned. -*/ -static char *ReadFile(const char *zFilename){ - struct stat sStat; - FILE *pIn; - char *zBuf; - int n; - - if( stat(zFilename,&sStat)!=0 -#ifndef WIN32 - || !S_ISREG(sStat.st_mode) -#endif - ){ - return 0; - } - pIn = fopen(zFilename,"r"); - if( pIn==0 ){ - return 0; - } - zBuf = SafeMalloc( sStat.st_size + 1 ); - n = fread(zBuf,1,sStat.st_size,pIn); - zBuf[n] = 0; - fclose(pIn); - return zBuf; -} - -/* -** Write the contents of a string into a file. Return the number of -** errors -*/ -static int WriteFile(const char *zFilename, const char *zOutput){ - FILE *pOut; - pOut = fopen(zFilename,"w"); - if( pOut==0 ){ - return 1; - } - fwrite(zOutput,1,strlen(zOutput),pOut); - fclose(pOut); - return 0; -} - -/* -** Major token types -*/ -#define TT_Space 1 /* Contiguous white space */ -#define TT_Id 2 /* An identifier */ -#define TT_Preprocessor 3 /* Any C preprocessor directive */ -#define TT_Comment 4 /* Either C or C++ style comment */ -#define TT_Number 5 /* Any numeric constant */ -#define TT_String 6 /* String or character constants. ".." or '.' */ -#define TT_Braces 7 /* All text between { and a matching } */ -#define TT_EOF 8 /* End of file */ -#define TT_Error 9 /* An error condition */ -#define TT_BlockComment 10 /* A C-Style comment at the left margin that - * spans multple lines */ -#define TT_Other 0 /* None of the above */ - -/* -** Get a single low-level token from the input file. Update the -** file pointer so that it points to the first character beyond the -** token. -** -** A "low-level token" is any token except TT_Braces. A TT_Braces token -** consists of many smaller tokens and is assembled by a routine that -** calls this one. -** -** The function returns the number of errors. An error is an -** unterminated string or character literal or an unterminated -** comment. -** -** Profiling shows that this routine consumes about half the -** CPU time on a typical run of makeheaders. -*/ -static int GetToken(InStream *pIn, Token *pToken){ - int i; - const char *z; - int cStart; - int c; - int startLine; /* Line on which a structure begins */ - int nlisc = 0; /* True if there is a new-line in a ".." or '..' */ - int nErr = 0; /* Number of errors seen */ - - z = pIn->z; - i = pIn->i; - pToken->nLine = pIn->nLine; - pToken->zText = &z[i]; - switch( z[i] ){ - case 0: - pToken->eType = TT_EOF; - pToken->nText = 0; - break; - - case '#': - if( i==0 || z[i-1]=='\n' || (i>1 && z[i-1]=='\r' && z[i-2]=='\n')){ - /* We found a preprocessor statement */ - pToken->eType = TT_Preprocessor; - i++; - while( z[i]!=0 && z[i]!='\n' ){ - if( z[i]=='\\' ){ - i++; - if( z[i]=='\n' ) pIn->nLine++; - } - i++; - } - pToken->nText = i - pIn->i; - }else{ - /* Just an operator */ - pToken->eType = TT_Other; - pToken->nText = 1; - } - break; - - case ' ': - case '\t': - case '\r': - case '\f': - case '\n': - while( isspace(z[i]) ){ - if( z[i]=='\n' ) pIn->nLine++; - i++; - } - pToken->eType = TT_Space; - pToken->nText = i - pIn->i; - break; - - case '\\': - pToken->nText = 2; - pToken->eType = TT_Other; - if( z[i+1]=='\n' ){ - pIn->nLine++; - pToken->eType = TT_Space; - }else if( z[i+1]==0 ){ - pToken->nText = 1; - } - break; - - case '\'': - case '\"': - cStart = z[i]; - startLine = pIn->nLine; - do{ - i++; - c = z[i]; - if( c=='\n' ){ - if( !nlisc ){ - fprintf(stderr, - "%s:%d: (warning) Newline in string or character literal.\n", - zFilename, pIn->nLine); - nlisc = 1; - } - pIn->nLine++; - } - if( c=='\\' ){ - i++; - c = z[i]; - if( c=='\n' ){ - pIn->nLine++; - } - }else if( c==cStart ){ - i++; - c = 0; - }else if( c==0 ){ - fprintf(stderr, "%s:%d: Unterminated string or character literal.\n", - zFilename, startLine); - nErr++; - } - }while( c ); - pToken->eType = TT_String; - pToken->nText = i - pIn->i; - break; - - case '/': - if( z[i+1]=='/' ){ - /* C++ style comment */ - while( z[i] && z[i]!='\n' ){ i++; } - pToken->eType = TT_Comment; - pToken->nText = i - pIn->i; - }else if( z[i+1]=='*' ){ - /* C style comment */ - int isBlockComment = i==0 || z[i-1]=='\n'; - i += 2; - startLine = pIn->nLine; - while( z[i] && (z[i]!='*' || z[i+1]!='/') ){ - if( z[i]=='\n' ){ - pIn->nLine++; - if( isBlockComment ){ - if( z[i+1]=='*' || z[i+2]=='*' ){ - isBlockComment = 2; - }else{ - isBlockComment = 0; - } - } - } - i++; - } - if( z[i] ){ - i += 2; - }else{ - isBlockComment = 0; - fprintf(stderr,"%s:%d: Unterminated comment\n", - zFilename, startLine); - nErr++; - } - pToken->eType = isBlockComment==2 ? TT_BlockComment : TT_Comment; - pToken->nText = i - pIn->i; - }else{ - /* A divide operator */ - pToken->eType = TT_Other; - pToken->nText = 1; - } - break; - - case '0': - if( z[i+1]=='x' || z[i+1]=='X' ){ - /* A hex constant */ - i += 2; - while( isxdigit(z[i]) ){ i++; } - }else{ - /* An octal constant */ - while( isdigit(z[i]) ){ i++; } - } - pToken->eType = TT_Number; - pToken->nText = i - pIn->i; - break; - - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - while( isdigit(z[i]) ){ i++; } - if( (c=z[i])=='.' ){ - i++; - while( isdigit(z[i]) ){ i++; } - c = z[i]; - if( c=='e' || c=='E' ){ - i++; - if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; } - while( isdigit(z[i]) ){ i++; } - c = z[i]; - } - if( c=='f' || c=='F' || c=='l' || c=='L' ){ i++; } - }else if( c=='e' || c=='E' ){ - i++; - if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; } - while( isdigit(z[i]) ){ i++; } - }else if( c=='L' || c=='l' ){ - i++; - c = z[i]; - if( c=='u' || c=='U' ){ i++; } - }else if( c=='u' || c=='U' ){ - i++; - c = z[i]; - if( c=='l' || c=='L' ){ i++; } - } - pToken->eType = TT_Number; - pToken->nText = i - pIn->i; - break; - - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': - case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': - case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': - case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': - case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': - case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': - case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': - case 'X': case 'Y': case 'Z': case '_': - while( isalnum(z[i]) || z[i]=='_' ){ i++; }; - pToken->eType = TT_Id; - pToken->nText = i - pIn->i; - break; - - default: - pToken->eType = TT_Other; - pToken->nText = 1; - break; - } - pIn->i += pToken->nText; - return nErr; -} - -/* -** This routine recovers the next token from the input file which is -** not a space or a comment or any text between an "#if 0" and "#endif". -** -** This routine returns the number of errors encountered. An error -** is an unterminated token or unmatched "#if 0". -** -** Profiling shows that this routine uses about a quarter of the -** CPU time in a typical run. -*/ -static int GetNonspaceToken(InStream *pIn, Token *pToken){ - int nIf = 0; - int inZero = 0; - const char *z; - int value; - int startLine; - int nErr = 0; - - startLine = pIn->nLine; - while( 1 ){ - nErr += GetToken(pIn,pToken); - /* printf("%04d: Type=%d nIf=%d [%.*s]\n", - pToken->nLine,pToken->eType,nIf,pToken->nText, - pToken->eType!=TT_Space ? pToken->zText : "<space>"); */ - pToken->pComment = blockComment; - switch( pToken->eType ){ - case TT_Comment: - case TT_Space: - break; - - case TT_BlockComment: - if( doc_flag ){ - blockComment = SafeMalloc( sizeof(Token) ); - *blockComment = *pToken; - } - break; - - case TT_EOF: - if( nIf ){ - fprintf(stderr,"%s:%d: Unterminated \"#if\"\n", - zFilename, startLine); - nErr++; - } - return nErr; - - case TT_Preprocessor: - z = &pToken->zText[1]; - while( *z==' ' || *z=='\t' ) z++; - if( sscanf(z,"if %d",&value)==1 && value==0 ){ - nIf++; - inZero = 1; - }else if( inZero ){ - if( strncmp(z,"if",2)==0 ){ - nIf++; - }else if( strncmp(z,"endif",5)==0 ){ - nIf--; - if( nIf==0 ) inZero = 0; - } - }else{ - return nErr; - } - break; - - default: - if( !inZero ){ - return nErr; - } - break; - } - } - /* NOT REACHED */ -} - -/* -** This routine looks for identifiers (strings of contiguous alphanumeric -** characters) within a preprocessor directive and adds every such string -** found to the given identifier table -*/ -static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){ - Token sToken; - InStream sIn; - int go = 1; - - sIn.z = pToken->zText; - sIn.i = 1; - sIn.nLine = 1; - while( go && sIn.i < pToken->nText ){ - GetToken(&sIn,&sToken); - switch( sToken.eType ){ - case TT_Id: - IdentTableInsert(pTable,sToken.zText,sToken.nText); - break; - - case TT_EOF: - go = 0; - break; - - default: - break; - } - } -} - -/* -** This routine gets the next token. Everything contained within -** {...} is collapsed into a single TT_Braces token. Whitespace is -** omitted. -** -** If pTable is not NULL, then insert every identifier seen into the -** IdentTable. This includes any identifiers seen inside of {...}. -** -** The number of errors encountered is returned. An error is an -** unterminated token. -*/ -static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable){ - const char *z, *zStart; - int iStart; - int nBrace; - int c; - int nLine; - int nErr; - - nErr = GetNonspaceToken(pIn,pToken); - switch( pToken->eType ){ - case TT_Id: - if( pTable!=0 ){ - IdentTableInsert(pTable,pToken->zText,pToken->nText); - } - return nErr; - - case TT_Preprocessor: - if( pTable!=0 ){ - FindIdentifiersInMacro(pToken,pTable); - } - return nErr; - - case TT_Other: - if( pToken->zText[0]=='{' ) break; - return nErr; - - default: - return nErr; - } - - z = pIn->z; - iStart = pIn->i; - zStart = pToken->zText; - nLine = pToken->nLine; - nBrace = 1; - while( nBrace ){ - nErr += GetNonspaceToken(pIn,pToken); - /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace, - pToken->nText,pToken->zText); */ - switch( pToken->eType ){ - case TT_EOF: - fprintf(stderr,"%s:%d: Unterminated \"{\"\n", - zFilename, nLine); - nErr++; - pToken->eType = TT_Error; - return nErr; - - case TT_Id: - if( pTable ){ - IdentTableInsert(pTable,pToken->zText,pToken->nText); - } - break; - - case TT_Preprocessor: - if( pTable!=0 ){ - FindIdentifiersInMacro(pToken,pTable); - } - break; - - case TT_Other: - if( (c = pToken->zText[0])=='{' ){ - nBrace++; - }else if( c=='}' ){ - nBrace--; - } - break; - - default: - break; - } - } - pToken->eType = TT_Braces; - pToken->nText = 1 + pIn->i - iStart; - pToken->zText = zStart; - pToken->nLine = nLine; - return nErr; -} - -/* -** This routine frees up a list of Tokens. The pComment tokens are -** not cleared by this. So we leak a little memory when using the -doc -** option. So what. -*/ -static void FreeTokenList(Token *pList){ - Token *pNext; - while( pList ){ - pNext = pList->pNext; - SafeFree(pList); - pList = pNext; - } -} - -/* -** Tokenize an entire file. Return a pointer to the list of tokens. -** -** Space for each token is obtained from a separate malloc() call. The -** calling function is responsible for freeing this space. -** -** If pTable is not NULL, then fill the table with all identifiers seen in -** the input file. -*/ -static Token *TokenizeFile(const char *zFile, IdentTable *pTable){ - InStream sIn; - Token *pFirst = 0, *pLast = 0, *pNew; - int nErr = 0; - - sIn.z = zFile; - sIn.i = 0; - sIn.nLine = 1; - blockComment = 0; - - while( sIn.z[sIn.i]!=0 ){ - pNew = SafeMalloc( sizeof(Token) ); - nErr += GetBigToken(&sIn,pNew,pTable); - debug3(TOKENIZER, "Token on line %d: [%.*s]\n", - pNew->nLine, pNew->nText<50 ? pNew->nText : 50, pNew->zText); - if( pFirst==0 ){ - pFirst = pLast = pNew; - pNew->pPrev = 0; - }else{ - pLast->pNext = pNew; - pNew->pPrev = pLast; - pLast = pNew; - } - if( pNew->eType==TT_EOF ) break; - } - if( pLast ) pLast->pNext = 0; - blockComment = 0; - if( nErr ){ - FreeTokenList(pFirst); - pFirst = 0; - } - - return pFirst; -} - -#if TEST==1 -/* -** Use the following routine to test or debug the tokenizer. -*/ -void main(int argc, char **argv){ - char *zFile; - Token *pList, *p; - IdentTable sTable; - - if( argc!=2 ){ - fprintf(stderr,"Usage: %s filename\n",*argv); - exit(1); - } - memset(&sTable,0,sizeof(sTable)); - zFile = ReadFile(argv[1]); - if( zFile==0 ){ - fprintf(stderr,"Can't read file \"%s\"\n",argv[1]); - exit(1); - } - pList = TokenizeFile(zFile,&sTable); - for(p=pList; p; p=p->pNext){ - int j; - switch( p->eType ){ - case TT_Space: - printf("%4d: Space\n",p->nLine); - break; - case TT_Id: - printf("%4d: Id %.*s\n",p->nLine,p->nText,p->zText); - break; - case TT_Preprocessor: - printf("%4d: Preprocessor %.*s\n",p->nLine,p->nText,p->zText); - break; - case TT_Comment: - printf("%4d: Comment\n",p->nLine); - break; - case TT_BlockComment: - printf("%4d: Block Comment\n",p->nLine); - break; - case TT_Number: - printf("%4d: Number %.*s\n",p->nLine,p->nText,p->zText); - break; - case TT_String: - printf("%4d: String %.*s\n",p->nLine,p->nText,p->zText); - break; - case TT_Other: - printf("%4d: Other %.*s\n",p->nLine,p->nText,p->zText); - break; - case TT_Braces: - for(j=0; j<p->nText && j<30 && p->zText[j]!='\n'; j++){} - printf("%4d: Braces %.*s...}\n",p->nLine,j,p->zText); - break; - case TT_EOF: - printf("%4d: End of file\n",p->nLine); - break; - default: - printf("%4d: type %d\n",p->nLine,p->eType); - break; - } - } - FreeTokenList(pList); - SafeFree(zFile); - IdentTablePrint(&sTable,stdout); -} -#endif - -#ifdef DEBUG -/* -** For debugging purposes, write out a list of tokens. -*/ -static void PrintTokens(Token *pFirst, Token *pLast){ - int needSpace = 0; - int c; - - pLast = pLast->pNext; - while( pFirst!=pLast ){ - switch( pFirst->eType ){ - case TT_Preprocessor: - printf("\n%.*s\n",pFirst->nText,pFirst->zText); - needSpace = 0; - break; - - case TT_Id: - case TT_Number: - printf("%s%.*s", needSpace ? " " : "", pFirst->nText, pFirst->zText); - needSpace = 1; - break; - - default: - c = pFirst->zText[0]; - printf("%s%.*s", - (needSpace && (c=='*' || c=='{')) ? " " : "", - pFirst->nText, pFirst->zText); - needSpace = pFirst->zText[0]==','; - break; - } - pFirst = pFirst->pNext; - } -} -#endif - -/* -** Convert a sequence of tokens into a string and return a pointer -** to that string. Space to hold the string is obtained from malloc() -** and must be freed by the calling function. -** -** The characters ";\n" are always appended. -*/ -static char *TokensToString(Token *pFirst, Token *pLast){ - char *zReturn; - String str; - int needSpace = 0; - int c; - - StringInit(&str); - pLast = pLast->pNext; - while( pFirst!=pLast ){ - switch( pFirst->eType ){ - case TT_Preprocessor: - StringAppend(&str,"\n",1); - StringAppend(&str,pFirst->zText,pFirst->nText); - StringAppend(&str,"\n",1); - needSpace = 0; - break; - - case TT_Id: - if( pFirst->nText==6 && pFirst->zText[0]=='E' - && strncmp(pFirst->zText,"EXPORT",6)==0 ){ - break; - } - /* Fall thru to the next case */ - case TT_Number: - if( needSpace ){ - StringAppend(&str," ",1); - } - StringAppend(&str,pFirst->zText,pFirst->nText); - needSpace = 1; - break; - - default: - c = pFirst->zText[0]; - if( needSpace && (c=='*' || c=='{') ){ - StringAppend(&str," ",1); - } - StringAppend(&str,pFirst->zText,pFirst->nText); - /* needSpace = pFirst->zText[0]==','; */ - needSpace = 0; - break; - } - pFirst = pFirst->pNext; - } - StringAppend(&str,";\n",2); - zReturn = StrDup(StringGet(&str),0); - StringReset(&str); - return zReturn; -} - -/* -** This routine is called when we see one of the keywords "struct", -** "enum", "union" or "class". This might be the beginning of a -** type declaration. This routine will process the declaration and -** remove the declaration tokens from the input stream. -** -** If this is a type declaration that is immediately followed by a -** semicolon (in other words it isn't also a variable definition) -** then set *pReset to ';'. Otherwise leave *pReset at 0. The -** *pReset flag causes the parser to skip ahead to the next token -** that begins with the value placed in the *pReset flag, if that -** value is different from 0. -*/ -static int ProcessTypeDecl(Token *pList, int flags, int *pReset){ - Token *pName, *pEnd; - Decl *pDecl; - String str; - int need_to_collapse = 1; - - *pReset = 0; - if( pList==0 || pList->pNext==0 || pList->pNext->eType!=TT_Id ){ - return 0; - } - pName = pList->pNext; - - /* Catch the case of "struct Foo;" and skip it. */ - if( pName->pNext && pName->pNext->zText[0]==';' ){ - *pReset = ';'; - return 0; - } - - for(pEnd=pName->pNext; pEnd && pEnd->eType!=TT_Braces; pEnd=pEnd->pNext){ - switch( pEnd->zText[0] ){ - case '(': - case '*': - case '[': - case '=': - case ';': - return 0; - } - } - if( pEnd==0 ){ - return 0; - } - - /* - ** At this point, we know we have a type declaration that is bounded - ** by pList and pEnd and has the name pName. - */ - - /* - ** If the braces are followed immedately by a semicolon, then we are - ** dealing a type declaration only. There is not variable definition - ** following the type declaration. So reset... - */ - if( pEnd->pNext==0 || pEnd->pNext->zText[0]==';' ){ - *pReset = ';'; - need_to_collapse = 0; - }else{ - need_to_collapse = 1; - } - - if( proto_static==0 && (flags & (PS_Local|PS_Export|PS_Interface))==0 ){ - /* Ignore these objects unless they are explicitly declared as interface, - ** or unless the "-local" command line option was specified. */ - *pReset = ';'; - return 0; - } - -#ifdef DEBUG - if( debugMask & PARSER ){ - printf("**** Found type: %.*s %.*s...\n", - pList->nText, pList->zText, pName->nText, pName->zText); - PrintTokens(pList,pEnd); - printf(";\n"); - } -#endif - pDecl = CreateDecl(pName->zText,pName->nText); - if( (flags & PS_Static) || !(flags & (PS_Interface|PS_Export)) ){ - DeclSetProperty(pDecl,DP_Local); - } - switch( *pList->zText ){ - case 'c': DeclSetProperty(pDecl,TY_Class); break; - case 's': DeclSetProperty(pDecl,TY_Structure); break; - case 'e': DeclSetProperty(pDecl,TY_Enumeration); break; - case 'u': DeclSetProperty(pDecl,TY_Union); break; - default: /* Can't Happen */ break; - } - - /* The object has a full declaration only if it is contained within - ** "#if INTERFACE...#endif" or "#if EXPORT_INTERFACE...#endif" or - ** "#if LOCAL_INTERFACE...#endif". Otherwise, we only give it a - ** forward declaration. - */ - if( flags & (PS_Local | PS_Export | PS_Interface) ){ - pDecl->zDecl = TokensToString(pList,pEnd); - }else{ - pDecl->zDecl = 0; - } - pDecl->pComment = pList->pComment; - StringInit(&str); - StringAppend(&str,"typedef ",0); - StringAppend(&str,pList->zText,pList->nText); - StringAppend(&str," ",0); - StringAppend(&str,pName->zText,pName->nText); - StringAppend(&str," ",0); - StringAppend(&str,pName->zText,pName->nText); - StringAppend(&str,";\n",2); - pDecl->zFwd = StrDup(StringGet(&str),0); - StringReset(&str); - StringInit(&str); - StringAppend(&str,pList->zText,pList->nText); - StringAppend(&str," ",0); - StringAppend(&str,pName->zText,pName->nText); - StringAppend(&str,";\n",2); - pDecl->zFwdCpp = StrDup(StringGet(&str),0); - StringReset(&str); - if( flags & PS_Export ){ - DeclSetProperty(pDecl,DP_Export); - }else if( flags & PS_Local ){ - DeclSetProperty(pDecl,DP_Local); - } - - /* Here's something weird. ANSI-C doesn't allow a forward declaration - ** of an enumeration. So we have to build the typedef into the - ** definition. - */ - if( pDecl->zDecl && DeclHasProperty(pDecl, TY_Enumeration) ){ - StringInit(&str); - StringAppend(&str,pDecl->zDecl,0); - StringAppend(&str,pDecl->zFwd,0); - SafeFree(pDecl->zDecl); - SafeFree(pDecl->zFwd); - pDecl->zFwd = 0; - pDecl->zDecl = StrDup(StringGet(&str),0); - StringReset(&str); - } - - if( pName->pNext->zText[0]==':' ){ - DeclSetProperty(pDecl,DP_Cplusplus); - } - if( pName->nText==5 && strncmp(pName->zText,"class",5)==0 ){ - DeclSetProperty(pDecl,DP_Cplusplus); - } - - /* - ** Remove all but pList and pName from the input stream. - */ - if( need_to_collapse ){ - while( pEnd!=pName ){ - Token *pPrev = pEnd->pPrev; - pPrev->pNext = pEnd->pNext; - pEnd->pNext->pPrev = pPrev; - SafeFree(pEnd); - pEnd = pPrev; - } - } - return 0; -} - -/* -** Given a list of tokens that declare something (a function, procedure, -** variable or typedef) find the token which contains the name of the -** thing being declared. -** -** Algorithm: -** -** The name is: -** -** 1. The first identifier that is followed by a "[", or -** -** 2. The first identifier that is followed by a "(" where the -** "(" is followed by another identifier, or -** -** 3. The first identifier followed by "::", or -** -** 4. If none of the above, then the last identifier. -** -** In all of the above, certain reserved words (like "char") are -** not considered identifiers. -*/ -static Token *FindDeclName(Token *pFirst, Token *pLast){ - Token *pName = 0; - Token *p; - int c; - - if( pFirst==0 || pLast==0 ){ - return 0; - } - pLast = pLast->pNext; - for(p=pFirst; p && p!=pLast; p=p->pNext){ - if( p->eType==TT_Id ){ - static IdentTable sReserved; - static int isInit = 0; - static char *aWords[] = { "char", "class", - "const", "double", "enum", "extern", "EXPORT", "ET_PROC", - "float", "int", "long", - "register", "static", "struct", "sizeof", "signed", "typedef", - "union", "volatile", "virtual", "void", }; - - if( !isInit ){ - int i; - for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){ - IdentTableInsert(&sReserved,aWords[i],0); - } - isInit = 1; - } - if( !IdentTableTest(&sReserved,p->zText,p->nText) ){ - pName = p; - } - }else if( p==pFirst ){ - continue; - }else if( (c=p->zText[0])=='[' && pName ){ - break; - }else if( c=='(' && p->pNext && p->pNext->eType==TT_Id && pName ){ - break; - }else if( c==':' && p->zText[1]==':' && pName ){ - break; - } - } - return pName; -} - -/* -** This routine is called when we see a function or procedure definition. -** We make an entry in the declaration table that is a prototype for this -** function or procedure. -*/ -static int ProcessProcedureDef(Token *pFirst, Token *pLast, int flags){ - Token *pName; - Decl *pDecl; - Token *pCode; - - if( pFirst==0 || pLast==0 ){ - return 0; - } - if( flags & PS_Method ){ - return 0; - } - if( (flags & PS_Static)!=0 && !proto_static ){ - return 0; - } - pCode = pLast; - while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){ - pLast = pLast->pPrev; - } - if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){ - fprintf(stderr,"%s:%d: Unrecognized syntax.\n", - zFilename, pFirst->nLine); - return 1; - } - if( flags & (PS_Interface|PS_Export|PS_Local) ){ - fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n", - zFilename, pFirst->nLine); - return 1; - } - pName = FindDeclName(pFirst,pLast); - if( pName==0 ){ - fprintf(stderr,"%s:%d: Malformed function or procedure definition.\n", - zFilename, pFirst->nLine); - return 1; - } - - /* - ** At this point we've isolated a procedure declaration between pFirst - ** and pLast with the name pName. - */ -#ifdef DEBUG - if( debugMask & PARSER ){ - printf("**** Found routine: %.*s on line %d...\n", pName->nText, - pName->zText, pFirst->nLine); - PrintTokens(pFirst,pLast); - printf(";\n"); - } -#endif - pDecl = CreateDecl(pName->zText,pName->nText); - pDecl->pComment = pFirst->pComment; - if( pCode && pCode->eType==TT_Braces ){ - pDecl->tokenCode = *pCode; - } - DeclSetProperty(pDecl,TY_Subroutine); - pDecl->zDecl = TokensToString(pFirst,pLast); - if( (flags & (PS_Static|PS_Local2))!=0 ){ - DeclSetProperty(pDecl,DP_Local); - }else if( (flags & (PS_Export2))!=0 ){ - DeclSetProperty(pDecl,DP_Export); - } - - if( flags & DP_Cplusplus ){ - DeclSetProperty(pDecl,DP_Cplusplus); - }else{ - DeclSetProperty(pDecl,DP_ExternCReqd); - } - - return 0; -} - -/* -** This routine is called whenever we see the "inline" keyword. We -** need to seek-out the inline function or procedure and make a -** declaration out of the entire definition. -*/ -static int ProcessInlineProc(Token *pFirst, int flags, int *pReset){ - Token *pName; - Token *pEnd; - Decl *pDecl; - - for(pEnd=pFirst; pEnd; pEnd = pEnd->pNext){ - if( pEnd->zText[0]=='{' || pEnd->zText[0]==';' ){ - *pReset = pEnd->zText[0]; - break; - } - } - if( pEnd==0 ){ - *pReset = ';'; - fprintf(stderr,"%s:%d: incomplete inline procedure definition\n", - zFilename, pFirst->nLine); - return 1; - } - pName = FindDeclName(pFirst,pEnd); - if( pName==0 ){ - fprintf(stderr,"%s:%d: malformed inline procedure definition\n", - zFilename, pFirst->nLine); - return 1; - } - -#ifdef DEBUG - if( debugMask & PARSER ){ - printf("**** Found inline routine: %.*s on line %d...\n", - pName->nText, pName->zText, pFirst->nLine); - PrintTokens(pFirst,pEnd); - printf("\n"); - } -#endif - pDecl = CreateDecl(pName->zText,pName->nText); - pDecl->pComment = pFirst->pComment; - DeclSetProperty(pDecl,TY_Subroutine); - pDecl->zDecl = TokensToString(pFirst,pEnd); - if( (flags & (PS_Static|PS_Local|PS_Local2)) ){ - DeclSetProperty(pDecl,DP_Local); - }else if( flags & (PS_Export|PS_Export2) ){ - DeclSetProperty(pDecl,DP_Export); - } - - if( flags & DP_Cplusplus ){ - DeclSetProperty(pDecl,DP_Cplusplus); - }else{ - DeclSetProperty(pDecl,DP_ExternCReqd); - } - - return 0; -} - -/* -** Determine if the tokens between pFirst and pEnd form a variable -** definition or a function prototype. Return TRUE if we are dealing -** with a variable defintion and FALSE for a prototype. -** -** pEnd is the token that ends the object. It can be either a ';' or -** a '='. If it is '=', then assume we have a variable definition. -** -** If pEnd is ';', then the determination is more difficult. We have -** to search for an occurance of an ID followed immediately by '('. -** If found, we have a prototype. Otherwise we are dealing with a -** variable definition. -*/ -static int isVariableDef(Token *pFirst, Token *pEnd){ - if( pEnd && pEnd->zText[0]=='=' ){ - return 1; - } - while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){ - if( pFirst->eType==TT_Id && pFirst->pNext->zText[0]=='(' ){ - return 0; - } - pFirst = pFirst->pNext; - } - return 1; -} - - -/* -** This routine is called whenever we encounter a ";" or "=". The stuff -** between pFirst and pLast constitutes either a typedef or a global -** variable definition. Do the right thing. -*/ -static int ProcessDecl(Token *pFirst, Token *pEnd, int flags){ - Token *pName; - Decl *pDecl; - int isLocal = 0; - int isVar; - int nErr = 0; - - if( pFirst==0 || pEnd==0 ){ - return 0; - } - if( flags & PS_Typedef ){ - if( (flags & (PS_Export2|PS_Local2))!=0 ){ - fprintf(stderr,"%s:%d: \"EXPORT\" or \"LOCAL\" ignored before typedef.\n", - zFilename, pFirst->nLine); - nErr++; - } - if( (flags & (PS_Interface|PS_Export|PS_Local|DP_Cplusplus))==0 ){ - /* It is illegal to duplicate a typedef in C (but OK in C++). - ** So don't record typedefs that aren't within a C++ file or - ** within #if INTERFACE..#endif */ - return nErr; - } - if( (flags & (PS_Interface|PS_Export|PS_Local))==0 && proto_static==0 ){ - /* Ignore typedefs that are not with "#if INTERFACE..#endif" unless - ** the "-local" command line option is used. */ - return nErr; - } - if( (flags & (PS_Interface|PS_Export))==0 ){ - /* typedefs are always local, unless within #if INTERFACE..#endif */ - isLocal = 1; - } - }else if( flags & (PS_Static|PS_Local2) ){ - if( proto_static==0 && (flags & PS_Local2)==0 ){ - /* Don't record static variables unless the "-local" command line - ** option was specified or the "LOCAL" keyword is used. */ - return nErr; - } - while( pFirst!=0 && pFirst->pNext!=pEnd && - ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0) - || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0)) - ){ - /* Lose the initial "static" or local from local variables. - ** We'll prepend "extern" later. */ - pFirst = pFirst->pNext; - isLocal = 1; - } - if( pFirst==0 || !isLocal ){ - return nErr; - } - }else if( flags & PS_Method ){ - /* Methods are declared by their class. Don't declare separately. */ - return nErr; - } - isVar = (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd); - if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 - && (flags & PS_Extern)==0 ){ - fprintf(stderr,"%s:%d: Can't define a variable in this context\n", - zFilename, pFirst->nLine); - nErr++; - } - pName = FindDeclName(pFirst,pEnd->pPrev); - if( pName==0 ){ - fprintf(stderr,"%s:%d: Can't find a name for the object declared here.\n", - zFilename, pFirst->nLine); - return nErr+1; - } - -#ifdef DEBUG - if( debugMask & PARSER ){ - if( flags & PS_Typedef ){ - printf("**** Found typedef %.*s at line %d...\n", - pName->nText, pName->zText, pName->nLine); - }else if( isVar ){ - printf("**** Found variable %.*s at line %d...\n", - pName->nText, pName->zText, pName->nLine); - }else{ - printf("**** Found prototype %.*s at line %d...\n", - pName->nText, pName->zText, pName->nLine); - } - PrintTokens(pFirst,pEnd->pPrev); - printf(";\n"); - } -#endif - - pDecl = CreateDecl(pName->zText,pName->nText); - if( (flags & PS_Typedef) ){ - DeclSetProperty(pDecl, TY_Typedef); - }else if( isVar ){ - DeclSetProperty(pDecl,DP_ExternReqd | TY_Variable); - if( !(flags & DP_Cplusplus) ){ - DeclSetProperty(pDecl,DP_ExternCReqd); - } - }else{ - DeclSetProperty(pDecl, TY_Subroutine); - if( !(flags & DP_Cplusplus) ){ - DeclSetProperty(pDecl,DP_ExternCReqd); - } - } - pDecl->pComment = pFirst->pComment; - pDecl->zDecl = TokensToString(pFirst,pEnd->pPrev); - if( isLocal || (flags & (PS_Local|PS_Local2))!=0 ){ - DeclSetProperty(pDecl,DP_Local); - }else if( flags & (PS_Export|PS_Export2) ){ - DeclSetProperty(pDecl,DP_Export); - } - if( flags & DP_Cplusplus ){ - DeclSetProperty(pDecl,DP_Cplusplus); - } - return nErr; -} - -/* -** Push an if condition onto the if stack -*/ -static void PushIfMacro( - const char *zPrefix, /* A prefix, like "define" or "!" */ - const char *zText, /* The condition */ - int nText, /* Number of characters in zText */ - int nLine, /* Line number where this macro occurs */ - int flags /* Either 0, PS_Interface, PS_Export or PS_Local */ -){ - Ifmacro *pIf; - int nByte; - - nByte = sizeof(Ifmacro); - if( zText ){ - if( zPrefix ){ - nByte += strlen(zPrefix) + 2; - } - nByte += nText + 1; - } - pIf = SafeMalloc( nByte ); - if( zText ){ - pIf->zCondition = (char*)&pIf[1]; - if( zPrefix ){ - sprintf(pIf->zCondition,"%s(%.*s)",zPrefix,nText,zText); - }else{ - sprintf(pIf->zCondition,"%.*s",nText,zText); - } - }else{ - pIf->zCondition = 0; - } - pIf->nLine = nLine; - pIf->flags = flags; - pIf->pNext = ifStack; - ifStack = pIf; -} - -/* -** This routine is called to handle all preprocessor directives. -** -** This routine will recompute the value of *pPresetFlags to be the -** logical or of all flags on all nested #ifs. The #ifs that set flags -** are as follows: -** -** conditional flag set -** ------------------------ -------------------- -** #if INTERFACE PS_Interface -** #if EXPORT_INTERFACE PS_Export -** #if LOCAL_INTERFACE PS_Local -** -** For example, if after processing the preprocessor token given -** by pToken there is an "#if INTERFACE" on the preprocessor -** stack, then *pPresetFlags will be set to PS_Interface. -*/ -static int ParsePreprocessor(Token *pToken, int flags, int *pPresetFlags){ - const char *zCmd; - int nCmd; - const char *zArg; - int nArg; - int nErr = 0; - Ifmacro *pIf; - - zCmd = &pToken->zText[1]; - while( isspace(*zCmd) && *zCmd!='\n' ){ - zCmd++; - } - if( !isalpha(*zCmd) ){ - return 0; - } - nCmd = 1; - while( isalpha(zCmd[nCmd]) ){ - nCmd++; - } - - if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){ - /* - ** Pop the if stack - */ - pIf = ifStack; - if( pIf==0 ){ - fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine); - return 1; - } - ifStack = pIf->pNext; - SafeFree(pIf); - }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){ - /* - ** Record a #define if we are in PS_Interface or PS_Export - */ - Decl *pDecl; - if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; } - zArg = &zCmd[6]; - while( *zArg && isspace(*zArg) && *zArg!='\n' ){ - zArg++; - } - if( *zArg==0 || *zArg=='\n' ){ return 0; } - for(nArg=0; ISALNUM(zArg[nArg]); nArg++){} - if( nArg==0 ){ return 0; } - pDecl = CreateDecl(zArg,nArg); - pDecl->pComment = pToken->pComment; - DeclSetProperty(pDecl,TY_Macro); - pDecl->zDecl = SafeMalloc( pToken->nText + 2 ); - sprintf(pDecl->zDecl,"%.*s\n",pToken->nText,pToken->zText); - if( flags & PS_Export ){ - DeclSetProperty(pDecl,DP_Export); - }else if( flags & PS_Local ){ - DeclSetProperty(pDecl,DP_Local); - } - }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){ - /* - ** Record an #include if we are in PS_Interface or PS_Export - */ - Include *pInclude; - char *zIf; - - if( !(flags & (PS_Interface|PS_Export)) ){ return 0; } - zArg = &zCmd[7]; - while( *zArg && isspace(*zArg) ){ zArg++; } - for(nArg=0; !isspace(zArg[nArg]); nArg++){} - if( (zArg[0]=='"' && zArg[nArg-1]!='"') - ||(zArg[0]=='<' && zArg[nArg-1]!='>') - ){ - fprintf(stderr,"%s:%d: malformed #include statement.\n", - zFilename,pToken->nLine); - return 1; - } - zIf = GetIfString(); - if( zIf ){ - pInclude = SafeMalloc( sizeof(Include) + nArg*2 + strlen(zIf) + 10 ); - pInclude->zFile = (char*)&pInclude[1]; - pInclude->zLabel = &pInclude->zFile[nArg+1]; - sprintf(pInclude->zFile,"%.*s",nArg,zArg); - sprintf(pInclude->zLabel,"%.*s:%s",nArg,zArg,zIf); - pInclude->zIf = &pInclude->zLabel[nArg+1]; - SafeFree(zIf); - }else{ - pInclude = SafeMalloc( sizeof(Include) + nArg + 1 ); - pInclude->zFile = (char*)&pInclude[1]; - sprintf(pInclude->zFile,"%.*s",nArg,zArg); - pInclude->zIf = 0; - pInclude->zLabel = pInclude->zFile; - } - pInclude->pNext = includeList; - includeList = pInclude; - }else if( nCmd==2 && strncmp(zCmd,"if",2)==0 ){ - /* - ** Push an #if. Watch for the special cases of INTERFACE - ** and EXPORT_INTERFACE and LOCAL_INTERFACE - */ - zArg = &zCmd[2]; - while( *zArg && isspace(*zArg) && *zArg!='\n' ){ - zArg++; - } - if( *zArg==0 || *zArg=='\n' ){ return 0; } - nArg = pToken->nText + (int)pToken->zText - (int)zArg; - if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){ - PushIfMacro(0,0,0,pToken->nLine,PS_Interface); - }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){ - PushIfMacro(0,0,0,pToken->nLine,PS_Export); - }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){ - PushIfMacro(0,0,0,pToken->nLine,PS_Local); - }else{ - PushIfMacro(0,zArg,nArg,pToken->nLine,0); - } - }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){ - /* - ** Push an #ifdef. - */ - zArg = &zCmd[5]; - while( *zArg && isspace(*zArg) && *zArg!='\n' ){ - zArg++; - } - if( *zArg==0 || *zArg=='\n' ){ return 0; } - nArg = pToken->nText + (int)pToken->zText - (int)zArg; - PushIfMacro("defined",zArg,nArg,pToken->nLine,0); - }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){ - /* - ** Push an #ifndef. - */ - zArg = &zCmd[6]; - while( *zArg && isspace(*zArg) && *zArg!='\n' ){ - zArg++; - } - if( *zArg==0 || *zArg=='\n' ){ return 0; } - nArg = pToken->nText + (int)pToken->zText - (int)zArg; - PushIfMacro("!defined",zArg,nArg,pToken->nLine,0); - }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){ - /* - ** Invert the #if on the top of the stack - */ - if( ifStack==0 ){ - fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename, - pToken->nLine); - return 1; - } - pIf = ifStack; - if( pIf->zCondition ){ - ifStack = ifStack->pNext; - PushIfMacro("!",pIf->zCondition,strlen(pIf->zCondition),pIf->nLine,0); - SafeFree(pIf); - }else{ - pIf->flags = 0; - } - }else{ - /* - ** This directive can be safely ignored - */ - return 0; - } - - /* - ** Recompute the preset flags - */ - *pPresetFlags = 0; - for(pIf = ifStack; pIf; pIf=pIf->pNext){ - *pPresetFlags |= pIf->flags; - } - - return nErr; -} - -/* -** Parse an entire file. Return the number of errors. -** -** pList is a list of tokens in the file. Whitespace tokens have been -** eliminated, and text with {...} has been collapsed into a -** single TT_Brace token. -** -** initFlags are a set of parse flags that should always be set for this -** file. For .c files this is normally 0. For .h files it is PS_Interface. -*/ -static int ParseFile(Token *pList, int initFlags){ - int nErr = 0; - Token *pStart = 0; - int flags = initFlags; - int presetFlags = initFlags; - int resetFlag = 0; - - includeList = 0; - while( pList ){ - switch( pList->eType ){ - case TT_EOF: - goto end_of_loop; - - case TT_Preprocessor: - nErr += ParsePreprocessor(pList,flags,&presetFlags); - pStart = 0; - presetFlags |= initFlags; - flags = presetFlags; - break; - - case TT_Other: - switch( pList->zText[0] ){ - case ';': - nErr += ProcessDecl(pStart,pList,flags); - pStart = 0; - flags = presetFlags; - break; - - case '=': - nErr += ProcessDecl(pStart,pList,flags); - pStart = 0; - while( pList && pList->zText[0]!=';' ){ - pList = pList->pNext; - } - if( pList==0 ) goto end_of_loop; - flags = presetFlags; - break; - - case ':': - if( pList->zText[1]==':' ){ - flags |= PS_Method; - } - break; - - default: - break; - } - break; - - case TT_Braces: - nErr += ProcessProcedureDef(pStart,pList,flags); - pStart = 0; - flags = presetFlags; - break; - - case TT_Id: - if( pStart==0 ){ - pStart = pList; - flags = presetFlags; - } - resetFlag = 0; - switch( pList->zText[0] ){ - case 'c': - if( pList->nText==5 && strncmp(pList->zText,"class",5)==0 ){ - nErr += ProcessTypeDecl(pList,flags,&resetFlag); - } - break; - - case 'E': - if( pList->nText==6 && strncmp(pList->zText,"EXPORT",6)==0 ){ - flags |= PS_Export2; - /* pStart = 0; */ - } - break; - - case 'e': - if( pList->nText==4 && strncmp(pList->zText,"enum",4)==0 ){ - if( pList->pNext && pList->pNext->eType==TT_Braces ){ - pList = pList->pNext; - }else{ - nErr += ProcessTypeDecl(pList,flags,&resetFlag); - } - }else if( pList->nText==6 && strncmp(pList->zText,"extern",6)==0 ){ - pList = pList->pNext; - if( pList && pList->nText==3 && strncmp(pList->zText,"\"C\"",3)==0 ){ - pList = pList->pNext; - flags &= ~DP_Cplusplus; - }else{ - flags |= PS_Extern; - } - pStart = pList; - } - break; - - case 'i': - if( pList->nText==6 && strncmp(pList->zText,"inline",6)==0 ){ - nErr += ProcessInlineProc(pList,flags,&resetFlag); - } - break; - - case 'L': - if( pList->nText==5 && strncmp(pList->zText,"LOCAL",5)==0 ){ - flags |= PS_Local2; - pStart = pList; - } - break; - - case 's': - if( pList->nText==6 && strncmp(pList->zText,"struct",6)==0 ){ - if( pList->pNext && pList->pNext->eType==TT_Braces ){ - pList = pList->pNext; - }else{ - nErr += ProcessTypeDecl(pList,flags,&resetFlag); - } - }else if( pList->nText==6 && strncmp(pList->zText,"static",6)==0 ){ - flags |= PS_Static; - } - break; - - case 't': - if( pList->nText==7 && strncmp(pList->zText,"typedef",7)==0 ){ - flags |= PS_Typedef; - } - break; - - case 'u': - if( pList->nText==5 && strncmp(pList->zText,"union",5)==0 ){ - if( pList->pNext && pList->pNext->eType==TT_Braces ){ - pList = pList->pNext; - }else{ - nErr += ProcessTypeDecl(pList,flags,&resetFlag); - } - } - break; - - default: - break; - } - if( resetFlag!=0 ){ - while( pList && pList->zText[0]!=resetFlag ){ - pList = pList->pNext; - } - if( pList==0 ) goto end_of_loop; - pStart = 0; - flags = presetFlags; - } - break; - - case TT_Number: - break; - - default: - pStart = pList; - flags = presetFlags; - break; - } - pList = pList->pNext; - } - end_of_loop: - - /* Verify that all #ifs have a matching "#endif" */ - while( ifStack ){ - Ifmacro *pIf = ifStack; - ifStack = pIf->pNext; - fprintf(stderr,"%s:%d: This '#if' has no '#endif'\n",zFilename, - pIf->nLine); - SafeFree(pIf); - } - - return nErr; -} - -/* -** Reset the DP_Forward and DP_Declared flags on all Decl structures. -** Set both flags for anything that is tagged as local and isn't -** in the file zFilename so that it won't be printing in other files. -*/ -static void ResetDeclFlags(char *zFilename){ - Decl *pDecl; - - for(pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext){ - DeclClearProperty(pDecl,DP_Forward|DP_Declared); - if( DeclHasProperty(pDecl,DP_Local) && pDecl->zFile!=zFilename ){ - DeclSetProperty(pDecl,DP_Forward|DP_Declared); - } - } -} - -/* -** Forward declaration of the ScanText() function. -*/ -static void ScanText(const char*, GenState *pState); - -/* -** The output in pStr is currently within an #if CONTEXT where context -** is equal to *pzIf. (*pzIf might be NULL to indicate that we are -** not within any #if at the moment.) We are getting ready to output -** some text that needs to be within the context of "#if NEW" where -** NEW is zIf. Make an appropriate change to the context. -*/ -static void ChangeIfContext( - const char *zIf, /* The desired #if context */ - GenState *pState /* Current state of the code generator */ -){ - if( zIf==0 ){ - if( pState->zIf==0 ) return; - StringAppend(pState->pStr,"#endif\n",0); - pState->zIf = 0; - }else{ - if( pState->zIf ){ - if( strcmp(zIf,pState->zIf)==0 ) return; - StringAppend(pState->pStr,"#endif\n",0); - pState->zIf = 0; - } - ScanText(zIf, pState); - if( pState->zIf!=0 ){ - StringAppend(pState->pStr,"#endif\n",0); - } - StringAppend(pState->pStr,"#if ",0); - StringAppend(pState->pStr,zIf,0); - StringAppend(pState->pStr,"\n",0); - pState->zIf = zIf; - } -} - -/* -** Add to the string pStr a #include of every file on the list of -** include files pInclude. The table pTable contains all files that -** have already been #included at least once. Don't add any -** duplicates. Update pTable with every new #include that is added. -*/ -static void AddIncludes( - Include *pInclude, /* Write every #include on this list */ - GenState *pState /* Current state of the code generator */ -){ - if( pInclude ){ - if( pInclude->pNext ){ - AddIncludes(pInclude->pNext,pState); - } - if( IdentTableInsert(pState->pTable,pInclude->zLabel,0) ){ - ChangeIfContext(pInclude->zIf,pState); - StringAppend(pState->pStr,"#include ",0); - StringAppend(pState->pStr,pInclude->zFile,0); - StringAppend(pState->pStr,"\n",1); - } - } -} - -/* -** Add to the string pStr a declaration for the object described -** in pDecl. -** -** If pDecl has already been declared in this file, detect that -** fact and abort early. Do not duplicate a declaration. -** -** If the needFullDecl flag is false and this object has a forward -** declaration, then supply the forward declaration only. A later -** call to CompleteForwardDeclarations() will finish the declaration -** for us. But if needFullDecl is true, we must supply the full -** declaration now. Some objects do not have a forward declaration. -** For those objects, we must print the full declaration now. -** -** Because it is illegal to duplicate a typedef in C, care is taken -** to insure that typedefs for the same identifier are only issued once. -*/ -static void DeclareObject( - Decl *pDecl, /* The thing to be declared */ - GenState *pState, /* Current state of the code generator */ - int needFullDecl /* Must have the full declaration. A forward - * declaration isn't enough */ -){ - Decl *p; /* The object to be declared */ - int flag; - int isCpp; /* True if generating C++ */ - int doneTypedef = 0; /* True if a typedef has been done for this object */ - - /* - ** For any object that has a forward declaration, go ahead and do the - ** forward declaration first. - */ - isCpp = (pState->flags & DP_Cplusplus) != 0; - for(p=pDecl; p; p=p->pSameName){ - if( p->zFwd ){ - if( !DeclHasProperty(p,DP_Forward) ){ - DeclSetProperty(p,DP_Forward); - if( strncmp(p->zFwd,"typedef",7)==0 ){ - if( doneTypedef ) continue; - doneTypedef = 1; - } - ChangeIfContext(p->zIf,pState); - StringAppend(pState->pStr,isCpp ? p->zFwdCpp : p->zFwd,0); - } - } - } - - /* - ** Early out if everything is already suitably declared. - ** - ** This is a very important step because it prevents us from - ** executing the code the follows in a recursive call to this - ** function with the same value for pDecl. - */ - flag = needFullDecl ? DP_Declared|DP_Forward : DP_Forward; - for(p=pDecl; p; p=p->pSameName){ - if( !DeclHasProperty(p,flag) ) break; - } - if( p==0 ){ - return; - } - - /* - ** Make sure we have all necessary #includes - */ - for(p=pDecl; p; p=p->pSameName){ - AddIncludes(p->pInclude,pState); - } - - /* - ** Go ahead an mark everything as being declared, to prevent an - ** infinite loop thru the ScanText() function. At the same time, - ** we decide which objects need a full declaration and mark them - ** with the DP_Flag bit. We are only able to use DP_Flag in this - ** way because we know we'll never execute this far into this - ** function on a recursive call with the same pDecl. Hence, recursive - ** calls to this function (through ScanText()) can never change the - ** value of DP_Flag out from under us. - */ - for(p=pDecl; p; p=p->pSameName){ - if( !DeclHasProperty(p,DP_Declared) - && (p->zFwd==0 || needFullDecl) - && p->zDecl!=0 - ){ - DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag); - }else{ - DeclClearProperty(p,DP_Flag); - } - } - - /* - ** Call ScanText() recusively (this routine is called from ScanText()) - ** to include declarations required to come before these declarations. - */ - for(p=pDecl; p; p=p->pSameName){ - if( DeclHasProperty(p,DP_Flag) ){ - if( p->zDecl[0]=='#' ){ - ScanText(&p->zDecl[1],pState); - }else{ - ScanText(p->zDecl,pState); - } - } - } - - /* - ** Output the declarations. Do this in two passes. First - ** output everything that isn't a typedef. Then go back and - ** get the typedefs by the same name. - */ - for(p=pDecl; p; p=p->pSameName){ - if( DeclHasProperty(p,DP_Flag) && !DeclHasProperty(p,TY_Typedef) ){ - if( DeclHasAnyProperty(p,TY_Enumeration) ){ - if( doneTypedef ) continue; - doneTypedef = 1; - } - ChangeIfContext(p->zIf,pState); - if( !isCpp && DeclHasAnyProperty(p,DP_ExternReqd) ){ - StringAppend(pState->pStr,"extern ",0); - }else if( isCpp && DeclHasProperty(p,DP_Cplusplus|DP_ExternReqd) ){ - StringAppend(pState->pStr,"extern ",0); - }else if( isCpp && DeclHasAnyProperty(p,DP_ExternCReqd|DP_ExternReqd) ){ - StringAppend(pState->pStr,"extern \"C\" ",0); - } - StringAppend(pState->pStr,p->zDecl,0); - if( !isCpp && DeclHasProperty(p,DP_Cplusplus) ){ - fprintf(stderr, - "%s: C code ought not reference the C++ object \"%s\"\n", - pState->zFilename, p->zName); - pState->nErr++; - } - DeclClearProperty(p,DP_Flag); - } - } - for(p=pDecl; p && !doneTypedef; p=p->pSameName){ - if( DeclHasProperty(p,DP_Flag) ){ - /* This has to be a typedef */ - doneTypedef = 1; - ChangeIfContext(p->zIf,pState); - StringAppend(pState->pStr,p->zDecl,0); - } - } -} - -/* -** This routine scans the input text given, and appends to the -** string in pState->pStr the text of any declarations that must -** occur before the text in zText. -** -** If an identifier in zText is immediately followed by '*', then -** only forward declarations are needed for that identifier. If the -** identifier name is not followed immediately by '*', we must supply -** a full declaration. -*/ -static void ScanText( - const char *zText, /* The input text to be scanned */ - GenState *pState /* Current state of the code generator */ -){ - int nextValid = 0; /* True is sNext contains valid data */ - InStream sIn; /* The input text */ - Token sToken; /* The current token being examined */ - Token sNext; /* The next non-space token */ - - sIn.z = zText; - sIn.i = 0; - sIn.nLine = 1; - while( sIn.z[sIn.i]!=0 ){ - if( nextValid ){ - sToken = sNext; - nextValid = 0; - }else{ - GetNonspaceToken(&sIn,&sToken); - } - if( sToken.eType==TT_Id ){ - int needFullDecl; /* True if we need to provide the full declaration, - ** not just the forward declaration */ - Decl *pDecl; /* The declaration having the name in sToken */ - - /* - ** See if there is a declaration in the database with the name given - ** by sToken. - */ - pDecl = FindDecl(sToken.zText,sToken.nText); - if( pDecl==0 ) continue; - - /* - ** If we get this far, we've found an identifier that has a - ** declaration in the database. Now see if we the full declaration - ** or just a forward declaration. - */ - GetNonspaceToken(&sIn,&sNext); - if( sNext.zText[0]=='*' ){ - needFullDecl = 0; - }else{ - needFullDecl = 1; - nextValid = sNext.eType==TT_Id; - } - - /* - ** Generate the needed declaration. - */ - DeclareObject(pDecl,pState,needFullDecl); - }else if( sToken.eType==TT_Preprocessor ){ - sIn.i -= sToken.nText - 1; - } - } -} - -/* -** Provide a full declaration to any object which so far has had only -** a foward declaration. -*/ -static void CompleteForwardDeclarations(GenState *pState){ - Decl *pDecl; - int progress; - - do{ - progress = 0; - for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ - if( DeclHasProperty(pDecl,DP_Forward) - && !DeclHasProperty(pDecl,DP_Declared) - ){ - DeclareObject(pDecl,pState,1); - progress = 1; - assert( DeclHasProperty(pDecl,DP_Declared) ); - } - } - }while( progress ); -} - -/* -** Generate an include file for the given source file. Return the number -** of errors encountered. -** -** if nolocal_flag is true, then we do not generate declarations for -** objected marked DP_Local. -*/ -static int MakeHeader(InFile *pFile, FILE *report, int nolocal_flag){ - int nErr = 0; - GenState sState; - String outStr; - IdentTable includeTable; - Ident *pId; - char *zNewVersion; - char *zOldVersion; - - if( pFile->zHdr==0 || *pFile->zHdr==0 ) return 0; - sState.pStr = &outStr; - StringInit(&outStr); - StringAppend(&outStr,zTopLine,nTopLine); - sState.pTable = &includeTable; - memset(&includeTable,0,sizeof(includeTable)); - sState.zIf = 0; - sState.nErr = 0; - sState.zFilename = pFile->zSrc; - sState.flags = pFile->flags & DP_Cplusplus; - ResetDeclFlags(nolocal_flag ? "no" : pFile->zSrc); - for(pId = pFile->idTable.pList; pId; pId=pId->pNext){ - Decl *pDecl = FindDecl(pId->zName,0); - if( pDecl ){ - DeclareObject(pDecl,&sState,1); - } - } - CompleteForwardDeclarations(&sState); - ChangeIfContext(0,&sState); - nErr += sState.nErr; - zOldVersion = ReadFile(pFile->zHdr); - zNewVersion = StringGet(&outStr); - if( report ) fprintf(report,"%s: ",pFile->zHdr); - if( zOldVersion==0 ){ - if( report ) fprintf(report,"updated\n"); - if( WriteFile(pFile->zHdr,zNewVersion) ){ - fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr); - nErr++; - } - }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){ - if( report ) fprintf(report,"error!\n"); - fprintf(stderr, - "%s: Can't overwrite this file because it wasn't previously\n" - "generated by 'makeheaders'.\n", pFile->zHdr); - nErr++; - }else if( strcmp(zOldVersion,zNewVersion)!=0 ){ - if( report ) fprintf(report,"updated\n"); - if( WriteFile(pFile->zHdr,zNewVersion) ){ - fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr); - nErr++; - } - }else if( report ){ - fprintf(report,"unchanged\n"); - } - SafeFree(zOldVersion); - IdentTableReset(&includeTable); - StringReset(&outStr); - return nErr; -} - -/* -** Generate a global header file -- a header file that contains all -** declarations. If the forExport flag is true, then only those -** objects that are exported are included in the header file. -*/ -static int MakeGlobalHeader(int forExport){ - GenState sState; - String outStr; - IdentTable includeTable; - Decl *pDecl; - - sState.pStr = &outStr; - StringInit(&outStr); - /* StringAppend(&outStr,zTopLine,nTopLine); */ - sState.pTable = &includeTable; - memset(&includeTable,0,sizeof(includeTable)); - sState.zIf = 0; - sState.nErr = 0; - sState.zFilename = "(all)"; - sState.flags = 0; - ResetDeclFlags(0); - for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ - if( forExport==0 || DeclHasProperty(pDecl,DP_Export) ){ - DeclareObject(pDecl,&sState,1); - } - } - ChangeIfContext(0,&sState); - printf("%s",StringGet(&outStr)); - IdentTableReset(&includeTable); - StringReset(&outStr); - return 0; -} - -#ifdef DEBUG -/* -** Return the number of characters in the given string prior to the -** first newline. -*/ -static int ClipTrailingNewline(char *z){ - int n = strlen(z); - while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ){ n--; } - return n; -} - -/* -** Dump the entire declaration list for debugging purposes -*/ -static void DumpDeclList(void){ - Decl *pDecl; - - for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){ - printf("**** %s from file %s ****\n",pDecl->zName,pDecl->zFile); - if( pDecl->zIf ){ - printf("If: [%.*s]\n",ClipTrailingNewline(pDecl->zIf),pDecl->zIf); - } - if( pDecl->zFwd ){ - printf("Decl: [%.*s]\n",ClipTrailingNewline(pDecl->zFwd),pDecl->zFwd); - } - if( pDecl->zDecl ){ - printf("Def: [%.*s]\n",ClipTrailingNewline(pDecl->zDecl),pDecl->zDecl); - } - if( pDecl->flags ){ - static struct { - int mask; - char *desc; - } flagSet[] = { - { TY_Class, "class" }, - { TY_Enumeration, "enum" }, - { TY_Structure, "struct" }, - { TY_Union, "union" }, - { TY_Variable, "variable" }, - { TY_Subroutine, "function" }, - { TY_Typedef, "typedef" }, - { TY_Macro, "macro" }, - { DP_Export, "export" }, - { DP_Local, "local" }, - { DP_Cplusplus, "C++" }, - }; - int i; - printf("flags:"); - for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){ - if( flagSet[i].mask & pDecl->flags ){ - printf(" %s", flagSet[i].desc); - } - } - printf("\n"); - } - if( pDecl->pInclude ){ - Include *p; - printf("includes:"); - for(p=pDecl->pInclude; p; p=p->pNext){ - printf(" %s",p->zFile); - } - printf("\n"); - } - } -} -#endif - -/* -** When the "-doc" command-line option is used, this routine is called -** to print all of the database information to standard output. -*/ -static void DocumentationDump(void){ - Decl *pDecl; - static struct { - int mask; - char flag; - } flagSet[] = { - { TY_Class, 'c' }, - { TY_Enumeration, 'e' }, - { TY_Structure, 's' }, - { TY_Union, 'u' }, - { TY_Variable, 'v' }, - { TY_Subroutine, 'f' }, - { TY_Typedef, 't' }, - { TY_Macro, 'm' }, - { DP_Export, 'x' }, - { DP_Local, 'l' }, - { DP_Cplusplus, '+' }, - }; - - for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){ - int i; - int nLabel = 0; - char *zDecl; - char zLabel[50]; - for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){ - if( DeclHasProperty(pDecl,flagSet[i].mask) ){ - zLabel[nLabel++] = flagSet[i].flag; - } - } - if( nLabel==0 ) continue; - zLabel[nLabel] = 0; - zDecl = pDecl->zDecl; - if( zDecl==0 ) zDecl = pDecl->zFwd; - printf("%s %s %s %d %d %d %d %d %d\n", - pDecl->zName, - zLabel, - pDecl->zFile, - (int)(pDecl->pComment ? (int)pDecl->pComment/sizeof(Token) : 0), - (int)(pDecl->pComment ? pDecl->pComment->nText+1 : 0), - (int)(pDecl->zIf ? strlen(pDecl->zIf)+1 : 0), - (int)(zDecl ? strlen(zDecl) : 0), - (int)(pDecl->pComment ? pDecl->pComment->nLine : 0), - pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0 - ); - if( pDecl->pComment ){ - printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText); - } - if( pDecl->zIf ){ - printf("%s\n",pDecl->zIf); - } - if( zDecl ){ - printf("%s",zDecl); - } - if( pDecl->tokenCode.nText ){ - printf("%.*s\n",pDecl->tokenCode.nText, pDecl->tokenCode.zText); - } - } -} - -/* -** Given the complete text of an input file, this routine prints a -** documentation record for the header comment at the beginning of the -** file (if the file has a header comment.) -*/ -void PrintModuleRecord(const char *zFile, const char *zFilename){ - int i; - static int addr = 5; - while( isspace(*zFile) ){ zFile++; } - if( *zFile!='/' || zFile[1]!='*' ) return; - for(i=2; zFile[i] && (zFile[i-1]!='/' || zFile[i-2]!='*'); i++){} - if( zFile[i]==0 ) return; - printf("%s M %s %d %d 0 0 0\n%.*s\n", - zFilename, zFilename, addr, i+1, i, zFile); - addr += 4; -} - - -/* -** Given an input argument to the program, construct a new InFile -** object. -*/ -static InFile *CreateInFile(char *zArg, int *pnErr){ - int nSrc; - char *zSrc; - InFile *pFile; - int i; - - /* - ** Get the name of the input file to be scanned - */ - zSrc = zArg; - for(nSrc=0; zSrc[nSrc] && zArg[nSrc]!=':'; nSrc++){} - pFile = SafeMalloc( sizeof(InFile) ); - memset(pFile,0,sizeof(InFile)); - pFile->zSrc = StrDup(zSrc,nSrc); - - /* Figure out if we are dealing with C or C++ code. Assume any - ** file with ".c" or ".h" is C code and all else is C++. - */ - if( nSrc>2 && zSrc[nSrc-2]=='.' && (zSrc[nSrc-1]=='c' || zSrc[nSrc-1]=='h')){ - pFile->flags &= ~DP_Cplusplus; - }else{ - pFile->flags |= DP_Cplusplus; - } - - /* - ** If a separate header file is specified, use it - */ - if( zSrc[nSrc]==':' ){ - int nHdr; - char *zHdr; - zHdr = &zSrc[nSrc+1]; - for(nHdr=0; zHdr[nHdr] && zHdr[nHdr]!=':'; nHdr++){} - pFile->zHdr = StrDup(zHdr,nHdr); - } - - /* Look for any 'c' or 'C' in the suffix of the file name and change - ** that character to 'h' or 'H' respectively. If no 'c' or 'C' is found, - ** then assume we are dealing with a header. - */ - else{ - int foundC = 0; - pFile->zHdr = StrDup(zSrc,nSrc); - for(i = nSrc-1; i>0 && pFile->zHdr[i]!='.'; i--){ - if( pFile->zHdr[i]=='c' ){ - foundC = 1; - pFile->zHdr[i] = 'h'; - }else if( pFile->zHdr[i]=='C' ){ - foundC = 1; - pFile->zHdr[i] = 'H'; - } - } - if( !foundC ){ - SafeFree(pFile->zHdr); - pFile->zHdr = 0; - } - } - - /* - ** If pFile->zSrc contains no 'c' or 'C' in its extension, it - ** must be a header file. In that case, we need to set the - ** PS_Interface flag. - */ - pFile->flags |= PS_Interface; - for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){ - if( zSrc[i]=='c' || zSrc[i]=='C' ){ - pFile->flags &= ~PS_Interface; - break; - } - } - - /* Done! - */ - return pFile; -} - -/* MS-Windows and MS-DOS both have the following serious OS bug: the -** length of a command line is severely restricted. But this program -** occasionally requires long command lines. Hence the following -** work around. -** -** If the parameters "-f FILENAME" appear anywhere on the command line, -** then the named file is scanned for additional command line arguments. -** These arguments are substituted in place of the "FILENAME" argument -** in the original argument list. -** -** This first parameter to this routine is the index of the "-f" -** parameter in the argv[] array. The argc and argv are passed by -** pointer so that they can be changed. -** -** Parsing of the parameters in the file is very simple. Parameters -** can be separated by any amount of white-space (including newlines -** and carriage returns.) There are now quoting characters of any -** kind. The length of a token is limited to about 1000 characters. -*/ -static void AddParameters(int index, int *pArgc, char ***pArgv){ - int argc = *pArgc; /* The original argc value */ - char **argv = *pArgv; /* The original argv value */ - int newArgc; /* Value for argc after inserting new arguments */ - char **zNew; /* The new argv after this routine is done */ - char *zFile; /* Name of the input file */ - int nNew = 0; /* Number of new entries in the argv[] file */ - int nAlloc = 0; /* Space allocated for zNew[] */ - int i; /* Loop counter */ - int n; /* Number of characters in a new argument */ - int c; /* Next character of input */ - int startOfLine = 1; /* True if we are where '#' can start a comment */ - FILE *in; /* The input file */ - char zBuf[1000]; /* A single argument is accumulated here */ - - if( index+1==argc ) return; - zFile = argv[index+1]; - in = fopen(zFile,"r"); - if( in==0 ){ - fprintf(stderr,"Can't open input file \"%s\"\n",zFile); - exit(1); - } - c = ' '; - while( c!=EOF ){ - while( c!=EOF && isspace(c) ){ - if( c=='\n' ){ - startOfLine = 1; - } - c = getc(in); - if( startOfLine && c=='#' ){ - while( c!=EOF && c!='\n' ){ - c = getc(in); - } - } - } - n = 0; - while( c!=EOF && !isspace(c) ){ - if( n<sizeof(zBuf)-1 ){ zBuf[n++] = c; } - startOfLine = 0; - c = getc(in); - } - zBuf[n] = 0; - if( n>0 ){ - nNew++; - if( nNew + argc > nAlloc ){ - if( nAlloc==0 ){ - nAlloc = 100 + argc; - zNew = malloc( sizeof(char*) * nAlloc ); - }else{ - nAlloc *= 2; - zNew = realloc( zNew, sizeof(char*) * nAlloc ); - } - } - if( zNew ){ - int j = nNew + index; - zNew[j] = malloc( n + 1 ); - if( zNew[j] ){ - strcpy( zNew[j], zBuf ); - } - } - } - } - newArgc = argc + nNew - 1; - for(i=0; i<=index; i++){ - zNew[i] = argv[i]; - } - for(i=nNew + index + 1; i<newArgc; i++){ - zNew[i] = argv[i + 1 - nNew]; - } - zNew[newArgc] = 0; - *pArgc = newArgc; - *pArgv = zNew; -} - -#ifdef NOT_USED -/* -** Return the time that the given file was last modified. If we can't -** locate the file (because, for example, it doesn't exist), then -** return 0. -*/ -static unsigned int ModTime(const char *zFilename){ - unsigned int mTime = 0; - struct stat sStat; - if( stat(zFilename,&sStat)==0 ){ - mTime = sStat.st_mtime; - } - return mTime; -} -#endif - -/* -** Print a usage comment for this program. -*/ -static void Usage(const char *argv0, const char *argvN){ - fprintf(stderr,"%s: Illegal argument \"%s\"\n",argv0,argvN); - fprintf(stderr,"Usage: %s [options] filename...\n" - "Options:\n" - " -h Generate a single .h to standard output.\n" - " -H Like -h, but only output EXPORT declarations.\n" - " -v (verbose) Write status information to the screen.\n" - " -doc Generate no header files. Instead, output information\n" - " that can be used by an automatic program documentation\n" - " and cross-reference generator.\n" - " -local Generate prototypes for \"static\" functions and\n" - " procedures.\n" - " -f FILE Read additional command-line arguments from the file named\n" - " \"FILE\".\n" -#ifdef DEBUG - " -! MASK Set the debugging mask to the number \"MASK\".\n" -#endif - " -- Treat all subsequent comment-line parameters as filenames,\n" - " even if they begin with \"-\".\n", - argv0 - ); -} - -/* -** The following text contains a few simple #defines that we want -** to be available to every file. -*/ -static char zInit[] = - "#define INTERFACE 0\n" - "#define EXPORT_INTERFACE 0\n" - "#define LOCAL_INTERFACE 0\n" - "#define EXPORT\n" - "#define LOCAL static\n" -; - -#if TEST==0 -int main(int argc, char **argv){ - int i; /* Loop counter */ - int nErr = 0; /* Number of errors encountered */ - Token *pList; /* List of input tokens for one file */ - InFile *pFileList = 0;/* List of all input files */ - InFile *pTail; /* Last file on the list */ - InFile *pFile; /* for looping over the file list */ - int h_flag = 0; /* True if -h is present. Output unified header */ - int H_flag = 0; /* True if -H is present. Output EXPORT header */ - int v_flag = 0; /* Verbose */ - int noMoreFlags; /* True if -- has been seen. */ - FILE *report; /* Send progress reports to this, if not NULL */ - - noMoreFlags = 0; - for(i=1; i<argc; i++){ - if( argv[i][0]=='-' && !noMoreFlags ){ - switch( argv[i][1] ){ - case 'h': h_flag = 1; break; - case 'H': H_flag = 1; break; - case 'v': v_flag = 1; break; - case 'd': doc_flag = 1; proto_static = 1; break; - case 'l': proto_static = 1; break; - case 'f': AddParameters(i, &argc, &argv); break; - case '-': noMoreFlags = 1; break; -#ifdef DEBUG - case '!': i++; debugMask = strtol(argv[i],0,0); break; -#endif - default: Usage(argv[0],argv[i]); return 1; - } - }else{ - pFile = CreateInFile(argv[i],&nErr); - if( pFile ){ - if( pFileList ){ - pTail->pNext = pFile; - pTail = pFile; - }else{ - pFileList = pTail = pFile; - } - } - } - } - if( h_flag && H_flag ){ - h_flag = 0; - } - if( v_flag ){ - report = (h_flag || H_flag) ? stderr : stdout; - }else{ - report = 0; - } - if( nErr>0 ){ - return nErr; - } - for(pFile=pFileList; pFile; pFile=pFile->pNext){ - char *zFile; - - zFilename = pFile->zSrc; - if( zFilename==0 ) continue; - zFile = ReadFile(zFilename); - if( zFile==0 ){ - fprintf(stderr,"Can't read input file \"%s\"\n",zFilename); - nErr++; - continue; - } - if( strncmp(zFile,zTopLine,nTopLine)==0 ){ - pFile->zSrc = 0; - }else{ - if( report ) fprintf(report,"Reading %s...\n",zFilename); - pList = TokenizeFile(zFile,&pFile->idTable); - if( pList ){ - nErr += ParseFile(pList,pFile->flags); - FreeTokenList(pList); - }else if( zFile[0]==0 ){ - fprintf(stderr,"Input file \"%s\" is empty.\n", zFilename); - nErr++; - }else{ - fprintf(stderr,"Errors while processing \"%s\"\n", zFilename); - nErr++; - } - } - if( !doc_flag ) SafeFree(zFile); - if( doc_flag ) PrintModuleRecord(zFile,zFilename); - } - if( nErr>0 ){ - return nErr; - } -#ifdef DEBUG - if( debugMask & DECL_DUMP ){ - DumpDeclList(); - return nErr; - } -#endif - if( doc_flag ){ - DocumentationDump(); - return nErr; - } - zFilename = "--internal--"; - pList = TokenizeFile(zInit,0); - if( pList==0 ){ - return nErr+1; - } - ParseFile(pList,PS_Interface); - FreeTokenList(pList); - if( h_flag || H_flag ){ - nErr += MakeGlobalHeader(H_flag); - }else{ - for(pFile=pFileList; pFile; pFile=pFile->pNext){ - if( pFile->zSrc==0 ) continue; - nErr += MakeHeader(pFile,report,0); - } - } - return nErr; -} -#endif diff --git a/tkhtml1/tools/maketokens.tcl b/tkhtml1/tools/maketokens.tcl deleted file mode 100644 index 95cd34e..0000000 --- a/tkhtml1/tools/maketokens.tcl +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/sh -# This script is a replacement for the maketokens.sh shell script. -# The shell script required GNU awk. This script should work with -# any old version of tclsh. -# \ -exec tclsh "$0" ${1+"$@"} - -if {$argc!=1} { - puts stderr "Usage: $argv0 tokenlist.txt >htmltokens.c" - exit 1 -} -if {[catch {open [lindex $argv 0] r} f]} { - puts stderr "$argv0: can not open \"[lindex $argv 0]\": $f" - exit 1 -} -set tokenlist {} -while {![eof $f]} { - set line [string trim [gets $f]] - if {$line==""} continue - if {[string index $line 0]=="#"} continue - if {[llength $line]!=2 && [llength $line]!=3} continue - lappend tokenlist [lindex $line 0] - lappend tokenlist [lindex $line 1] - lappend tokenlist [lindex $line 2] -} -close $f - -global tcl_platform -if {$tcl_platform(platform) == "windows"} { - fconfigure stdout -translation lf -} - -puts {/* DO NOT EDIT -** The code in this file was automatically generated. -*/ -#include <tk.h> -#include "htmltokens.h" -#if INTERFACE -struct HtmlTokenMap { - char *zName; /* Name of a markup */ - Html_16 type; /* Markup type code */ - Html_16 extra; /* Extra space needed above HtmlBaseElement */ - HtmlTokenMap *pCollide; /* Hash table collision chain */ -}; -#define Html_Text 1 -#define Html_Space 2 -#define Html_Unknown 3 -#define Html_Block 4 -#define HtmlIsMarkup(X) ((X)->base.type>Html_Block) -} - -set count 5 -set fmt {#define %-20s %d} - -foreach {name start end} $tokenlist { - set upr [string toupper $name] - puts [format $fmt Html_$upr $count] - incr count - if {$end!=""} { - puts [format $fmt Html_End$upr $count] - incr count - } -} - -puts [format $fmt Html_TypeCount [expr $count-1]] -puts "#define HTML_MARKUP_HASH_SIZE [expr $count+11]" -puts "#define HTML_MARKUP_COUNT [expr $count-5]" -puts "#endif /* INTERFACE */" -puts "HtmlTokenMap HtmlMarkupMap\[\] = {" - -set fmt " { %-15s %-25s %-30s }," - -foreach {name start end} $tokenlist { - set upr [string toupper $name] - set nm "\"$name\"," - set val Html_$upr, - if {$start=="0"} { - set size "0," - } else { - set size "sizeof($start)," - } - puts [format $fmt $nm $val $size] - if {$end==""} continue - set nm "\"/$name\"," - set val Html_End$upr, - if {$end=="0"} { - set size "0," - } else { - set size "sizeof($end)," - } - puts [format $fmt $nm $val $size] -} - -puts "};" diff --git a/tkhtml1/tools/sgmlparse.c b/tkhtml1/tools/sgmlparse.c deleted file mode 100644 index d85806e..0000000 --- a/tkhtml1/tools/sgmlparse.c +++ /dev/null @@ -1,207 +0,0 @@ -/* -** This file contains code used to tokenize SGML. -*/ -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <stdlib.h> -#include <assert.h> -#include "sgmlparse.h" - -#define stricmp strcasecmp - -/* These three pointers define certain special handlers. All whitespace -** is sent to xSpaceHandler. Non-whitespace is given to xWordHandler. -** Any markup that isn't specifically directed elsewhere is given -** to xDefaultMarkupHandlers. -*/ -static void (*xSpaceHandler)(const char*,void*); -static void (*xWordHandler)(const char*,void*); -static void (*xCommentHandler)(const char*,void*); -static void (*xDefaultMarkupHandler)(int, const char**, void*); - -/* Each handler is stored in a hash table as an instance of the -** following structure. -*/ -typedef struct sgHandler Handler; -struct sgHandler { - char *zName; /* Name of markup to handle */ - void (*xHandler)(int, const char**, void*); /* Routine to do the work */ - Handler *pCollide; /* Next handler with same hash */ -}; - -/* The size of the handler hash table. -** For best results, this should be a prime number which is larger than -** the number of markups in the hash table. -*/ -#define SGML_HASH_SIZE 203 - -/* The handler hash table */ -static Handler *apHandler[SGML_HASH_SIZE]; - -/* Hash a handler name */ -static int SgmlHash(const char *zName){ - int h = 0; - char c; - while( (c=*zName)!=0 ){ - if( isupper(c) ) c = tolower(c); - h = h<<5 ^ h ^ c; - zName++; - } - if( h<0 ) h = -h; - return h % SGML_HASH_SIZE; -} - -/* Given a pointer to an input file, read and parse that file -** as if it were SGML. -** -** This is not a true SGML parser because it handles some unusual -** cases differently, and ignores the & operator completely. -*/ -void SgmlParse(FILE *in, void *pArg){ - int c; - int i, j; - int argc; - Handler *pHandler; - char *argv[100]; - char zBuf[10000]; - - c = getc(in); - while( c!=EOF ){ - if( isspace(c) ){ - /* Case 1: spaces */ - zBuf[0] = c; - i = 1; - while( i<sizeof(zBuf)-2 && (c=getc(in))!=EOF && isspace(c) ){ - zBuf[i++] = c; - } - zBuf[i] = 0; - /* Dispose of space */ - if( xSpaceHandler ){ - (*xSpaceHandler)(zBuf,pArg); - } - }else if( c=='<' ){ - int cQuote = 0; - i = 0; - zBuf[i++] = c; - while( (c=getc(in))!=EOF && (cQuote || c!='>') ){ - if( i<sizeof(zBuf)-3 ) zBuf[i++] = c; - if( cQuote ){ - if( cQuote==c ) cQuote = 0; - }else if( c=='"' || c=='\'' ){ - cQuote = c; - } - } - if( c=='>' ) c = getc(in); - zBuf[i] = 0; - if( strncmp(zBuf,"<!--",4)==0 ){ - zBuf[i++] = '>'; - zBuf[i] = 0; - if( xCommentHandler ){ - (*xCommentHandler)(zBuf,pArg); - } - continue; - } - argc = 0; - argv[0] = &zBuf[1]; - for(j=1; zBuf[j] && !isspace(zBuf[j]); j++){} - if( zBuf[j] ){ - zBuf[j++] = 0; - while( argc<(sizeof(argv)/sizeof(argv[0])) - 4 && zBuf[j] ){ - while( isspace(zBuf[j]) ) j++; - argv[++argc] = &zBuf[j]; - while( zBuf[j] && !isspace(zBuf[j]) && zBuf[j]!='=' ) j++; - if( zBuf[j]!='=' ){ - argv[argc+1] = argv[argc]; - argc++; - if( zBuf[j] ) zBuf[j++] = 0; - continue; - } - zBuf[j++] = 0; - if( zBuf[j]=='"' || zBuf[j]=='\'' ){ - cQuote = zBuf[j++]; - }else{ - cQuote = 0; - } - argv[++argc] = &zBuf[j]; - if( cQuote ){ - while( zBuf[j] && zBuf[j]!=cQuote ) j++; - }else{ - while( zBuf[j] && !isspace(zBuf[j]) ) j++; - } - if( zBuf[j] ) zBuf[j++] = 0; - } - } - argv[++argc] = 0; - /* Despose of a markup */ - pHandler = apHandler[SgmlHash(argv[0])]; - while( pHandler && stricmp(pHandler->zName,argv[0])!=0 ){ - pHandler = pHandler->pCollide; - } - if( pHandler ){ - if( pHandler->xHandler ){ - (*pHandler->xHandler)(argc,(const char**)argv,pArg); - } - }else if( xDefaultMarkupHandler ){ - (*xDefaultMarkupHandler)(argc,(const char**)argv,pArg); - } - }else{ - zBuf[0] = c; - i = 1; - while( i<sizeof(zBuf)-2 && (c=getc(in))!=EOF && c!='<' && !isspace(c) ){ - zBuf[i++] = c; - } - zBuf[i] = 0; - /* Dispose of a word */ - if( xWordHandler ){ - (*xWordHandler)(zBuf,pArg); - } - } - } -} - -/* -** Clear out the handler hash table -*/ -void SgmlHandlerReset(void){ - Handler *pHandler; - int i; - - for(i=0; i<SGML_HASH_SIZE; i++){ - Handler *pNext; - for(pHandler=apHandler[i]; pHandler; pHandler=pNext){ - pNext = pHandler->pCollide; - free(pHandler); - } - apHandler[i] = 0; - } -} - -/* Install a new handler -*/ -void SgmlHandler(const char *zName, void (*xFunc)(int,const char**,void*)){ - int h = SgmlHash(zName); - extern void *malloc(); - Handler *pNew = malloc( sizeof(Handler) + strlen(zName) + 1 ); - if( pNew==0 ) return; - pNew->zName = (char*)&pNew[1]; - strcpy(pNew->zName,zName); - pNew->pCollide = apHandler[h]; - pNew->xHandler = xFunc; - apHandler[h] = pNew; -} - -/* Install the default handlers -*/ -void SgmlWordHandler(void (*xWord)(const char*,void*)){ - xWordHandler = xWord; -} -void SgmlSpaceHandler(void (*xSpace)(const char*,void*)){ - xSpaceHandler = xSpace; -} -void SgmlCommentHandler(void (*xComment)(const char*,void*)){ - xCommentHandler = xComment; -} -void SgmlDefaultMarkupHandler(void (*xMarkup)(int,const char**,void*)){ - xDefaultMarkupHandler = xMarkup; -} diff --git a/tkhtml1/tools/url.c b/tkhtml1/tools/url.c deleted file mode 100644 index 677cbba..0000000 --- a/tkhtml1/tools/url.c +++ /dev/null @@ -1,268 +0,0 @@ -/* -** This file contains code use for resolving relative URLs -*/ -#include <stdlib.h> -#include "url.h" - -#if INTERFACE -/* -** A parsed URI is held in an instance of the following structure. -** Each component is recorded in memory obtained from malloc(). -** -** The examples are from the URI -** -** http://192.168.1.1:8080/cgi-bin/printenv?name=xyzzy&addr=none#frag -*/ -typedef struct Url Url; -struct Url { - char *zScheme; /* Ex: "http" */ - char *zAuthority; /* Ex: "192.168.1.1:8080" */ - char *zPath; /* Ex: "cgi-bin/printenv" */ - char *zQuery; /* Ex: "name=xyzzy&addr=none" */ - char *zFragment; /* Ex: "frag" */ -}; -#endif - -/* -** Return the length of the next component of the URL in z[] given -** that the component starts at z[0]. The initial sequence of the -** component must be zInit[]. The component is terminated by any -** character in zTerm[]. The length returned is 0 if the component -** doesn't exist. The length includes the zInit[] string, but not -** the termination character. -** -** Component zInit zTerm -** ---------- ------- ------- -** scheme "" ":/?#" -** authority "//" "/?#" -** path "/" "?#" -** query "?" "#" -** fragment "#" "" -*/ -static int ComponentLength(const char *z, const char *zInit, const char *zTerm){ - int i, n; - for(n=0; zInit[n]; n++){ - if( zInit[n]!=z[n] ) return 0; - } - while( z[n] ){ - for(i=0; zTerm[i]; i++){ - if( z[n]==zTerm[i] ) return n; - } - n++; - } - return n; -} - -/* -** Duplicate a string of length n. -*/ -static char *StrNDup(const char *z, int n){ - char *zResult; - if( n<=0 ){ - n = strlen(z); - } - zResult = malloc( n + 1 ); - if( zResult ){ - memcpy(zResult, z, n); - zResult[n] = 0; - } - return zResult; -} - -/* -** Parse a text URI into an Url structure. -*/ -Url *ParseUrl(const char *zUri){ - Url *p; - int n; - - p = malloc( sizeof(*p) ); - if( p==0 ) return 0; - memset(p, 0, sizeof(*p)); - if( zUri==0 || zUri[0]==0 ) return p; - while( isspace(zUri[0]) ){ zUri++; } - n = ComponentLength(zUri, "", ":/?# "); - if( n>0 && zUri[n]==':' ){ - p->zScheme = StrNDup(zUri, n); - zUri += n+1; - } - n = ComponentLength(zUri, "//", "/?# "); - if( n>0 ){ - p->zAuthority = StrNDup(&zUri[2], n-2); - zUri += n; - } - n = ComponentLength(zUri, "", "?# "); - if( n>0 ){ - p->zPath = StrNDup(zUri, n); - zUri += n; - } - n = ComponentLength(zUri, "?", "# "); - if( n>0 ){ - p->zQuery = StrNDup(&zUri[1], n-1); - zUri += n; - } - n = ComponentLength(zUri, "#", " "); - if( n>0 ){ - p->zFragment = StrNDup(&zUri[1], n-1); - } - return p; -} - -/* -** Delete an Url structure. -*/ -void FreeUrl(Url *p){ - if( p==0 ) return; - if( p->zScheme ) free(p->zScheme); - if( p->zAuthority ) free(p->zAuthority); - if( p->zPath ) free(p->zPath); - if( p->zQuery ) free(p->zQuery); - if( p->zFragment ) free(p->zFragment); - free(p); -} - -/* -** Create a string to hold the given URI. Memory to hold the string -** is obtained from malloc() and must be freed by the calling -** function. -*/ -char *BuildUrl(Url *p){ - int n = 1; - char *z; - if( p->zScheme ) n += strlen(p->zScheme)+1; - if( p->zAuthority ) n += strlen(p->zAuthority)+2; - if( p->zPath ) n += strlen(p->zPath)+1; - if( p->zQuery ) n += strlen(p->zQuery)+1; - if( p->zFragment ) n += strlen(p->zFragment)+1; - z = malloc( n ); - if( z==0 ) return 0; - n = 0; - if( p->zScheme ){ - sprintf(z, "%s:", p->zScheme); - n = strlen(z); - } - if( p->zAuthority ){ - sprintf(&z[n], "//%s", p->zAuthority); - n += strlen(&z[n]); - } - if( p->zPath ){ - sprintf(&z[n], "%s", p->zPath); - n += strlen(&z[n]); - } - if( p->zQuery ){ - sprintf(&z[n], "?%s", p->zQuery); - n += strlen(&z[n]); - } - if( p->zFragment ){ - sprintf(&z[n], "#%s", p->zFragment); - } - return z; -} - -/* -** Replace the string in *pzDest with the string in zSrc -*/ -static void ReplaceStr(char **pzDest, const char *zSrc){ - if( *pzDest!=0 ) free(*pzDest); - if( zSrc==0 ){ - *pzDest = 0; - }else{ - *pzDest = StrNDup(zSrc, -1); - } -} - -/* -** Remove leading and trailing spaces from the given string. Return -** a new string obtained from malloc(). -*/ -static char *Trim(char *z){ - int i; - char *zNew; - while( isspace(*z) ) z++; - i = strlen(z); - zNew = malloc( i+1 ); - if( zNew==0 ) return 0; - strcpy(zNew, z); - if( i>0 && isspace(zNew[i-1]) ){ - i--; - zNew[i] = 0; - } - return zNew; -} - -/* -** Resolve a sequence of URLs. Return the result in space obtained -** from malloc(). -*/ -char *ResolveUrl( - char *zBase, /* The base URL */ - const char **azSeries /* A list of relatives. NULL terminated */ -){ - Url *base; - Url *term; - char *z; - - base = ParseUrl(zBase); - while( azSeries[0] ){ - term = ParseUrl(azSeries[0]); - azSeries++; - if( term->zScheme==0 && term->zAuthority==0 && term->zPath==0 - && term->zQuery==0 && term->zFragment ){ - ReplaceStr(&base->zFragment, term->zFragment); - }else if( term->zScheme ){ - Url temp; - temp = *term; - *term = *base; - *base = temp; - }else if( term->zAuthority ){ - ReplaceStr(&base->zAuthority, term->zAuthority); - ReplaceStr(&base->zPath, term->zPath); - ReplaceStr(&base->zQuery, term->zQuery); - ReplaceStr(&base->zFragment, term->zFragment); - }else if( term->zPath && term->zPath[0]=='/' ){ - ReplaceStr(&base->zPath, term->zPath); - ReplaceStr(&base->zQuery, term->zQuery); - ReplaceStr(&base->zFragment, term->zFragment); - }else if( term->zPath && base->zPath ){ - char *zBuf; - int i, j; - zBuf = malloc( strlen(base->zPath) + strlen(term->zPath) + 2 ); - if( zBuf ){ - sprintf(zBuf,"%s", base->zPath); - for(i=strlen(zBuf)-1; i>=0 && zBuf[i]!='/'; i--){ zBuf[i] = 0; } - strcat(zBuf, term->zPath); - for(i=0; zBuf[i]; i++){ - if( zBuf[i]=='/' && zBuf[i+1]=='.' && zBuf[i+2]=='/' ){ - strcpy(&zBuf[i+1], &zBuf[i+3]); - i--; - continue; - } - if( zBuf[i]=='/' && zBuf[i+1]=='.' && zBuf[i+2]==0 ){ - zBuf[i+1] = 0; - continue; - } - if( i>0 && zBuf[i]=='/' && zBuf[i+1]=='.' && zBuf[i+2]=='.' - && (zBuf[i+3]=='/' || zBuf[i+3]==0) ){ - for(j=i-1; j>=0 && zBuf[j]!='/'; j--){} - if( zBuf[i+3] ){ - strcpy(&zBuf[j+1], &zBuf[i+4]); - }else{ - zBuf[j+1] = 0; - } - i = j-1; - if( i<-1 ) i = -1; - continue; - } - } - free(base->zPath); - base->zPath = zBuf; - } - ReplaceStr(&base->zQuery, term->zQuery); - ReplaceStr(&base->zFragment, term->zFragment); - } - FreeUrl(term); - } - z = BuildUrl(base); - FreeUrl(base); - return z; -} |