diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index a0ea5ec973..45f054e03a 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -231,7 +231,7 @@ $(3): $$(MAKEFILE_LIST) $$(bin.c-pp) $(2) CLEAN_FILES += $(3) endef -c-pp.D.64bit = -D64bit +c-pp.D.64bit = -Dbits64 # # $(call b.strip-js-emcc-bindings) @@ -497,7 +497,7 @@ WASM_CUSTOM_INSTANTIATE = 1 # in the same way we would normally do C files, but C-specific quirks # of each makes that untennable. # -# - We implemented c-pp.c (the C-Minus Pre-processor) as a custom +# - We implemented c-pp-lite.c (the C-Minus Pre-processor) as a custom # generic/file-format-agnostic preprocessor to enable us to pack # code for different target builds into the same JS files. Most # notably, some ES6 module (a.k.a. ESM) features cannot legally be @@ -506,11 +506,11 @@ WASM_CUSTOM_INSTANTIATE = 1 # of code where necessary for ESM and non-ESM (a.k.a. vanilla JS) # require different implementations. The alternative to such # preprocessing, would be to have separate source files for ES6 -# builds, which would have a higher maintenance burden than c-pp.c -# seems likely to. +# builds, which would have a higher maintenance burden than +# c-pp-lite.c seems likely to. # -# c-pp.c was written specifically for the sqlite project's JavaScript -# builds but is maintained as a standalone project: +# c-pp-lite.c was written specifically for the sqlite project's +# JavaScript builds but is maintained as a standalone project: # https://fossil.wanderinghorse.net/r/c-pp # # The SQLITE_... build flags used here have NO EFFECT on the JS/WASM @@ -518,9 +518,9 @@ WASM_CUSTOM_INSTANTIATE = 1 # # -D... flags which should be included in all invocations should be # appended to $(b.c-pp.target.flags). -bin.c-pp = ./c-pp -$(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE) - $(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \ +bin.c-pp = ./c-pp-lite +$(bin.c-pp): c-pp-lite.c $(sqlite3.c) $(MAKEFILE) + $(CC) -O0 -o $@ c-pp-lite.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \ -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_UTF16 \ -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \ -DSQLITE_TEMP_STORE=3 diff --git a/ext/wasm/api/extern-post-js.c-pp.js b/ext/wasm/api/extern-post-js.c-pp.js index 0bb511d639..606e02ae28 100644 --- a/ext/wasm/api/extern-post-js.c-pp.js +++ b/ext/wasm/api/extern-post-js.c-pp.js @@ -110,7 +110,7 @@ const toExportForESM = sIMS.scriptDir = src.join('/') + '/'; } sIMS.debugModule('extern-post-js.c-pp.js sqlite3InitModuleState =',sIMS); -//#ifnot target:es6-module +//#if not target:es6-module // Emscripten does not inject these module-loader bits in ES6 module // builds and including them here breaks JS bundlers, so elide them // from ESM builds. diff --git a/ext/wasm/api/sqlite3-api-oo1.c-pp.js b/ext/wasm/api/sqlite3-api-oo1.c-pp.js index 84a388302f..8c2f35e677 100644 --- a/ext/wasm/api/sqlite3-api-oo1.c-pp.js +++ b/ext/wasm/api/sqlite3-api-oo1.c-pp.js @@ -1,4 +1,4 @@ -//#ifnot omit-oo1 +//#if not omit-oo1 /* 2022-07-22 @@ -2347,4 +2347,4 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }); //#else /* Built with the omit-oo1 flag. */ -//#endif ifnot omit-oo1 +//#endif if not omit-oo1 diff --git a/ext/wasm/api/sqlite3-api-worker1.c-pp.js b/ext/wasm/api/sqlite3-api-worker1.c-pp.js index 55ad16185c..25262abf85 100644 --- a/ext/wasm/api/sqlite3-api-worker1.c-pp.js +++ b/ext/wasm/api/sqlite3-api-worker1.c-pp.js @@ -1,4 +1,4 @@ -//#ifnot omit-oo1 +//#if not omit-oo1 /** 2022-07-22 @@ -677,4 +677,4 @@ sqlite3.initWorker1API = function(){ }); //#else /* Built with the omit-oo1 flag. */ -//#endif ifnot omit-oo1 +//#endif if not omit-oo1 diff --git a/ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js index 5a59de9c9a..69be338b0c 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js @@ -1,4 +1,4 @@ -//#ifnot target:node +//#if not target:node /* 2023-07-14 @@ -494,7 +494,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ /* Maps SAH to an abstract File Object which contains various metadata about that handle. */ - //#mapSAHToMeta = new Map(); /** Buffer used by [sg]etAssociatedPath(). */ #apBody = new Uint8Array(HEADER_CORPUS_SIZE); diff --git a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js index a57efb4ef2..2b636460dd 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js @@ -1,4 +1,4 @@ -//#ifnot target:node +//#if not target:node /* 2022-09-18 diff --git a/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js b/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js index db10ca0f7d..1a09bf9a6a 100644 --- a/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js +++ b/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js @@ -1,4 +1,4 @@ -//#ifnot omit-oo1 +//#if not omit-oo1 /* 2022-08-24 @@ -275,7 +275,7 @@ globalThis.sqlite3Worker1Promiser.defaultConfig = { return new Worker(theJs + globalThis.location.search); //#endif } -//#ifnot target:es6-module +//#if not target:es6-module .bind({ currentScript: globalThis?.document?.currentScript }) @@ -346,4 +346,4 @@ export default sqlite3Worker1Promiser.v2; //#endif /* target:es6-module */ //#else /* Built with the omit-oo1 flag. */ -//#endif ifnot omit-oo1 +//#endif if not omit-oo1 diff --git a/ext/wasm/api/sqlite3-worker1.c-pp.js b/ext/wasm/api/sqlite3-worker1.c-pp.js index 32bb370e39..036c4c6ea3 100644 --- a/ext/wasm/api/sqlite3-worker1.c-pp.js +++ b/ext/wasm/api/sqlite3-worker1.c-pp.js @@ -1,4 +1,4 @@ -//#ifnot omit-oo1 +//#if not omit-oo1 /* 2022-05-23 @@ -53,4 +53,4 @@ import {default as sqlite3InitModule} from './sqlite3-bundler-friendly.mjs'; sqlite3InitModule().then(sqlite3 => sqlite3.initWorker1API()); //#else /* Built with the omit-oo1 flag. */ -//#endif ifnot omit-oo1 +//#endif if not omit-oo1 diff --git a/ext/wasm/c-pp.c b/ext/wasm/c-pp-lite.c similarity index 62% rename from ext/wasm/c-pp.c rename to ext/wasm/c-pp-lite.c index fecffca7f6..53a3a28cb3 100644 --- a/ext/wasm/c-pp.c +++ b/ext/wasm/c-pp-lite.c @@ -14,60 +14,15 @@ ** Why? Because C preprocessors _can_ process non-C code but generally make ** quite a mess of it. The purpose of this application is an extremely ** minimal preprocessor with only the most basic functionality of a C -** preprocessor, namely: +** preprocessor, namely. ** -** - Limited `#if`, where its one argument is a macro name or a -** name=value pair. If just the name is used, it's considered true -** if it has a non-empty value which is not '0', else it's false. If -** name=value is used then it resolves to true if the value matches, -** noting that value is treated like a glob. Likewise, `#ifnot` is -** the inverse. Includes `#else` and `#elif` and `#elifnot`. Such -** chains are terminated with `#endif`. More simply (and more -** recently) `#if` and `#elif` support two modifer words: `not` and -** `defined`, so can be used like: `#if not defined x` or -** `#if defined y`. +** The supported preprocessor directives are documented in the +** README.md hosted with this file. ** -** - `#assert` compares its arguments like `#if` but throws a fatal -** error if it's condition is falsy. Unlike `#if`, it does not -** open a new block. -** -** - `#define` accepts one or more arguments, the names or name=value -** list of macros. Each one with no explicit value defaults to a -** value of 1.. -** -** - `#undef` undefine one or more macros. -** -** - `#error` treats the rest of the line as a fatal error message. -** -** - `#include` treats its argument as a filename token (NOT quoted, -** though support for quoting may be added later). Some effort is -** made to prevent recursive inclusion, but that support is both -** somewhat fragile and possibly completely unnecessary. -** -** - `#pragma` is in place for adding "meta-commands", but it does not -** yet have any concrete list of documented commands. -** -** - `#savepoint` takes one argument: begin, commit, rollback. Each -** corresponds to the similarly-named SQLite savepoint feature. -** (What we're calling "commit" is called "release" in savepoint -** terminology.) Savepoints apply ONLY to the db-side data (namely -** #define and friends), not to content blocks. (Might that not -** be interesting, though?) -** -** - `#stderr` outputs its file name, line number, and the remainder -** of that line to stderr. -** -** - `#//` acts as a single-line comment, noting that there must be no -** space after the `//` part because `//` is (despite appearances) -** parsed like a keyword. -** -** - `#@policy NAME` sets the policy for handling `@tokens@` in -** the content parts of the input (as opposed to the keyword -** lines like this one). @ -** -** The "#" above is symbolic. The keyword delimiter is configurable -** and defaults to "##". Define CMPP_DEFAULT_DELIM to a string when -** compiling to define the default at build-time. +** Any mention of "#" in the docs, e.g. "#if", is symbolic. The +** directive delimiter is configurable and defaults to "##". Define +** CMPP_DEFAULT_DELIM to a string when compiling to define the default +** at build-time. ** ** This preprocessor has only minimal support for replacement of tokens ** which live in the "content" blocks of inputs (that is, the pieces @@ -131,8 +86,12 @@ #endif /* Fatally exits the app with the given printf-style message. */ -static CMPP_NORETURN void fatalv(char const *zFmt, va_list); -static CMPP_NORETURN void fatal(char const *zFmt, ...); +static CMPP_NORETURN void fatalv__base(char const *zFile, int line, + char const *zFmt, va_list); +static CMPP_NORETURN void fatal__base(char const *zFile, int line, + char const *zFmt, ...); +#define fatalv(...) fatalv__base(__FILE__,__LINE__,__VA_ARGS__) +#define fatal(...) fatal__base(__FILE__,__LINE__,__VA_ARGS__) /** Proxy for free(), for symmetry with cmpp_realloc(). */ static void cmpp_free(void *p); @@ -168,10 +127,10 @@ static void FILE_slurp(FILE *pFile, unsigned char **pOut, unsigned * nOut); /* -** Intended to be passed an sqlite3 result code. If it's non-0 -** then it emits a fatal error message which contains both the -** given string and the sqlite3_errmsg() from the application's -** database instance. +** Intended to be passed an sqlite3 result code. If it's a non-0 value +** other than SQLITE_ROW or SQLITE_DONE then it emits a fatal error +** message which contains both the given string and the +** sqlite3_errmsg() from the application's database instance. */ static void db_affirm_rc(int rc, const char * zMsg); @@ -186,8 +145,6 @@ static char * db_str_finish(sqlite3_str *s, int * n); */ static sqlite3_str * db_str_new(void); -/* Proxy for sqlite3_finalize(). */ -static void db_finalize(sqlite3_stmt *pStmt); /* ** Proxy for sqlite3_step() which fails fatally if the result ** is anything other than SQLITE_ROW or SQLITE_DONE. @@ -221,17 +178,39 @@ static void db_bind_textv(sqlite3_stmt *pStmt, int col, const char * zFmt, ...); ** by sqlite3_malloc(). */ static void db_free(void *m); + +/* +** Returns true if the first nKey bytes of zKey are a legal string. If +** it returns false and zErrPos is not null, *zErrPos is set to the +** position of the illegal character. If nKey is negative, strlen() is +** used to calculate it. +*/ +static int cmpp_is_legal_key(char const *zKey, int nKey, char const **zErrPos); + +/* +** Fails fatally if !cmpp_is_legal_key(zKey). +*/ +static void cmpp_affirm_legal_key(char const *zKey, int nKey); + /* ** Adds the given `#define` macro name to the list of macros, ignoring ** any duplicates. Fails fatally on error. +** +** If zVal is NULL then zKey may contain an '=', from which the value +** will be extracted. If zVal is not NULL then zKey may _not_ contain +** an '='. */ -static void db_define_add(const char * zKey); +static void db_define_add(const char * zKey, char const *zVal); /* ** Returns true if the given key is already in the `#define` list, ** else false. Fails fatally on db error. +** +** nName is the length of the key part of zName (which might have +** a following =y part. If it's negative, strlen() is used to +** calculate it. */ -static int db_define_has(const char * zName); +static int db_define_has(const char * zName, int nName); /* ** Returns true if the given key is already in the `#define` list, and @@ -296,33 +275,75 @@ static void db_prepare(sqlite3_stmt **pStmt, const char * zSql, ...); static void cmpp_process_file(const char * zName); /* - ** If the first nKey bytes of zKey contain a chEq character then: - ** - ** - Assigns *nVal to the strlen() of the part after that (may be 0). - ** - If zEq is not NULL, it is set to the position of the chEq. - ** - Returns the part one byte after that chEq. - ** - ** Else returns NULL and does not modify *len or *zEq. - ** - ** If nKey is negative then strlen() is used to calculate it. - */ -static char const * cmpp_val_part(char const *zKey, - int nKey, - char chEq, - unsigned * nVal, - char const **zEq); +** Operator policy for cmpp_kvp_parse(). +*/ +enum cmpp_key_op_e { + /* Fail if the key contains an operator. */ + cmpp_key_op_none, + /* Accept only '='. */ + cmpp_key_op_eq1 +}; +typedef enum cmpp_key_op_e cmpp_key_op_e; /* -** Returns the number newline characters between the given starting -** point and inclusive ending point. Results are undefined if zFrom is -** greater than zTo. +** Operators and operator policies for use with X=Y-format keys. */ -static unsigned count_lines(unsigned char const * zFrom, - unsigned char const *zTo); +#define cmpp_kvp_op_map(E) \ + E(none,"") \ + E(eq1,"=") \ + E(eq2,"==") \ + E(lt,"<") \ + E(le,"<=") \ + E(gt,">") \ + E(ge,">=") + +enum cmpp_kvp_op_e { +#define E(N,S) cmpp_kvp_op_ ## N, + cmpp_kvp_op_map(E) +#undef E +}; +typedef enum cmpp_kvp_op_e cmpp_kvp_op_e; + +/* +** A snippet from a string. +*/ +struct cmpp_snippet { + char const *z; + unsigned int n; +}; +typedef struct cmpp_snippet cmpp_snippet; +#define cmpp_snippet_empty_m {0,0} + +/* +** Result type for cmpp_kvp_parse(). +*/ +struct cmpp_kvp { + cmpp_snippet k; + cmpp_snippet v; + cmpp_kvp_op_e op; +}; + +typedef struct cmpp_kvp cmpp_kvp; +#define cmpp_kvp_empty_m \ + {cmpp_snippet_empty_m,cmpp_snippet_empty_m,cmpp_kvp_op_none} +static const cmpp_kvp cmpp_kvp_empty = cmpp_kvp_empty_m; + +/* +** Parses X or X=Y into p. Fails fatally on error. +** +** If nKey is negative then strlen() is used to calculate it. +** +** The third argument specifies whether/how to permit/treat the '=' +** part of X=Y. +*/ +static void cmpp_kvp_parse(cmpp_kvp * p, + char const *zKey, int nKey, + cmpp_kvp_op_e opPolicy); /* ** Wrapper around a FILE handle. */ +typedef struct FileWrapper FileWrapper; struct FileWrapper { /* File's name. */ char const *zName; @@ -332,9 +353,10 @@ struct FileWrapper { unsigned char * zContent; /* Size of this->zContent, as set by FileWrapper_slurp(). */ unsigned nContent; + /* See Global::pFiles. */ + FileWrapper * pTail; }; -typedef struct FileWrapper FileWrapper; -#define FileWrapper_empty_m {0,0,0,0} +#define FileWrapper_empty_m {0,0,0,0,0} static const FileWrapper FileWrapper_empty = FileWrapper_empty_m; /* @@ -454,20 +476,6 @@ int FileWrapper_chomp(FileWrapper * p){ return 0; } -unsigned count_lines(unsigned char const * zFrom, unsigned char const *zTo){ - unsigned ln = 0; - unsigned char const *zPos = zFrom; - assert(zFrom && zTo); - assert(zFrom <= zTo); - for(; zPos < zTo; ++zPos){ - switch(*zPos){ - case (unsigned)'\n': ++ln; break; - default: break; - } - } - return ln; -} - enum CmppParseState { TS_Start = 1, TS_If, @@ -477,27 +485,55 @@ TS_Error }; typedef enum CmppParseState CmppParseState; enum CmppTokenType { -TT_Invalid = 0, -TT_Assert, -TT_AtPolicy, -TT_Comment, -TT_Define, -TT_Elif, -TT_ElifNot, -TT_Else, -TT_EndIf, -TT_Error, -TT_If, -TT_IfNot, -TT_Include, -TT_Line, -TT_Pragma, -TT_Savepoint, -TT_Stderr, -TT_Undef + +#define CmppToken_map(E) \ + E(Invalid,0) \ + E(Assert,"assert") \ + E(AtPolicy,"@policy") \ + E(Comment,"//") \ + E(Define,"define") \ + E(Elif,"elif") \ + E(Else,"else") \ + E(Endif,"endif") \ + E(Error,"error") \ + E(If,"if") \ + E(Include,"include") \ + E(Line,0) \ + E(Opaque,0) \ + E(Pragma,"pragma") \ + E(Savepoint,"savepoint") \ + E(Stderr,"stderr") \ + E(Undef,"undef") + +#define E(N,TOK) TT_ ## N, + CmppToken_map(E) +#undef E }; typedef enum CmppTokenType CmppTokenType; +/* +** Map of directive (formerly keyword) names and their token types. +*/ +static const struct { +#define E(N,TOK) struct cmpp_snippet N; + CmppToken_map(E) +#undef E +} DStrings = { +#define E(N,TOK) .N = {TOK,sizeof(TOK)-1}, + CmppToken_map(E) +#undef E +}; + +//static +char const * TT_cstr(int tt){ + switch(tt){ +#define E(N,TOK) case TT_ ## N: return DStrings.N.z; + CmppToken_map(E) +#undef E + } + return NULL; +} + struct CmppToken { CmppTokenType ttype; /* Line number of this token in the source file. */ @@ -514,7 +550,7 @@ static const CmppToken CmppToken_empty = CmppToken_empty_m; /* ** CmppLevel represents one "level" of tokenization, starting at the ** top of the main input, incrementing once for each level of `#if`, -** and decrementing for each `#endif`. Similarly, `#include` +** and decrementing for each `#endif`. ** pushes a level. */ typedef struct CmppLevel CmppLevel; @@ -525,7 +561,7 @@ struct CmppLevel { ** should get output. */ unsigned short skipLevel; - /* The token which started this level (an 'if' or 'ifnot'). */ + /* The token which started this level (an 'if' or 'include'). */ CmppToken token; CmppParseState pstate; }; @@ -535,14 +571,16 @@ enum CmppLevel_Flags { /* Max depth of nested `#if` constructs in a single tokenizer. */ CmppLevel_Max = 10, /* Max number of keyword arguments. */ -CmppArgs_Max = 10, +CmppArgs_Max = 15, +/* Directive line buffer size */ +CmppArgs_BufSize = 1024, /* Flag indicating that output for a CmpLevel should be elided. */ CmppLevel_F_ELIDE = 0x01, /* ** Mask of CmppLevel::flags which are inherited when CmppLevel_push() ** is used. */ -CmppLevel_F_INHERIT_MASK = 0x01 +CmppLevel_F_INHERIT_MASK = CmppLevel_F_ELIDE }; typedef struct CmppTokenizer CmppTokenizer; @@ -566,10 +604,9 @@ struct CmppTokenizer { const char * zName; /* Input (file) name for error reporting */ unsigned const char * zBegin; /* start of input */ unsigned const char * zEnd; /* one-after-the-end of input */ - unsigned const char * zAnchor; /* start of input or end point of - previous token */ unsigned const char * zPos; /* current position */ unsigned int lineNo; /* line # of current pos */ + unsigned nSavepoint; CmppParseState pstate; CmppToken token; /* current token result */ struct { @@ -581,7 +618,7 @@ struct CmppTokenizer { CmppKeyword const * pKw; int argc; const unsigned char * argv[CmppArgs_Max]; - unsigned char lineBuf[1024]; + unsigned char lineBuf[CmppArgs_BufSize]; } args; }; #define CT_level(t) (t)->level.stack[(t)->level.ndx] @@ -589,17 +626,19 @@ struct CmppTokenizer { #define CT_skipLevel(t) CT_level(t).skipLevel #define CLvl_skip(lvl) ((lvl)->skipLevel || ((lvl)->flags & CmppLevel_F_ELIDE)) #define CT_skip(t) CLvl_skip(&CT_level(t)) -#define CmppTokenizer_empty_m { \ - .zName=0, .zBegin=0, .zEnd=0, .zAnchor=0, \ - .zPos=0, \ - .lineNo=1U, \ - .pstate = TS_Start, \ - .token = CmppToken_empty_m, \ - .level = {0U,{CmppLevel_empty_m}}, \ - .args = {0,0,{0},{0}} \ +#define CmppTokenizer_empty_m { \ + .zName=0, .zBegin=0, .zEnd=0, \ + .zPos=0, \ + .lineNo=1U, \ + .pstate = TS_Start, \ + .token = CmppToken_empty_m, \ + .level = {0U,{CmppLevel_empty_m}}, \ + .args = {0,0,{0},{0}} \ } static const CmppTokenizer CmppTokenizer_empty = CmppTokenizer_empty_m; +static void CmppTokenizer_cleanup(CmppTokenizer * const t); + static void cmpp_t_out(CmppTokenizer * t, void const *z, unsigned int n); /*static void cmpp_t_outf(CmppTokenizer * t, char const *zFmt, ...);*/ @@ -659,6 +698,12 @@ static struct Global { sqlite3 * db; /* Current tokenizer (for error reporting purposes). */ CmppTokenizer const * tok; + /* + ** We use a linked-list of these to keep track of our opened + ** files so that we can clean then up via atexit() in the case of + ** fatal error (to please valgrind). + */ + FileWrapper * pFiles; /* Output channel. */ FileWrapper out; struct { @@ -678,25 +723,33 @@ static struct Global { const unsigned char chAt; } delim; struct { +#define CMPP_SAVEPOINT_NAME "_cmpp_" #define GStmt_map(E) \ E(defIns,"INSERT OR REPLACE INTO def(k,v) VALUES(?,?)") \ - E(defDel,"DELETE FROM def WHERE k GLOB ?") \ - E(defHas,"SELECT 1 FROM def WHERE k GLOB ?") \ - E(defGet,"SELECT k,v FROM def WHERE k GLOB ?") \ - E(defGetBool, \ - "SELECT 1 FROM def WHERE k = ?1" \ - " AND v IS NOT NULL" \ - " AND '0'!=v AND ''!=v") \ - E(inclIns,"INSERT OR FAIL INTO incl(file,srcFile,srcLine) VALUES(?,?,?)") \ - E(inclDel,"DELETE FROM incl WHERE file=?") \ - E(inclHas,"SELECT 1 FROM incl WHERE file=?") \ - E(inclPathAdd,"INSERT OR FAIL INTO inclpath(seq,dir) VALUES(?,?)") \ - E(inclSearch, \ - "SELECT ?1 fn WHERE fileExists(fn) " \ - "UNION ALL SELECT * FROM (" \ - "SELECT replace(dir||'/'||?1, '//','/') AS fn " \ - "FROM inclpath WHERE fileExists(fn) ORDER BY seq" \ - ")") + E(defDel,"DELETE FROM def WHERE k GLOB ?") \ + E(defHas,"SELECT 1 FROM def WHERE k GLOB ?") \ + E(defGet,"SELECT k,v FROM def WHERE k GLOB ?") \ + E(defGetBool, \ + "SELECT 1 FROM def WHERE k = ?1" \ + " AND v IS NOT NULL" \ + " AND '0'!=v AND ''!=v") \ + E(defSelAll,"SELECT k,v FROM def ORDER BY k") \ + E(inclIns,"INSERT OR FAIL INTO incl(file,srcFile," \ + "srcLine) VALUES(?,?,?)") \ + E(inclDel,"DELETE FROM incl WHERE file=?") \ + E(inclHas,"SELECT 1 FROM incl WHERE file=?") \ + E(inclPathAdd,"INSERT OR FAIL INTO " \ + "inclpath(seq,dir) VALUES(?,?)") \ + E(inclSearch, \ + "SELECT ?1 fn WHERE fileExists(fn) " \ + "UNION ALL SELECT * FROM (" \ + "SELECT replace(dir||'/'||?1, '//','/') AS fn " \ + "FROM inclpath WHERE fileExists(fn) ORDER BY seq"\ + ")") \ + E(spBegin,"SAVEPOINT " CMPP_SAVEPOINT_NAME) \ + E(spRollback,"ROLLBACK TO SAVEPOINT " \ + CMPP_SAVEPOINT_NAME) \ + E(spRelease,"RELEASE SAVEPOINT " CMPP_SAVEPOINT_NAME) #define E(N,S) sqlite3_stmt * N; GStmt_map(E) @@ -717,6 +770,7 @@ static struct Global { .zArgv0 = "?", .db = 0, .tok = 0, + .pFiles = 0, .out = FileWrapper_empty_m, .delim = { .z = CMPP_DEFAULT_DELIM, @@ -748,8 +802,8 @@ static struct Global { /** Distinct IDs for each g.stmt member. */ enum GStmt_e { - GStmt_e_none = 0, -#define E(N,S) GStmt_e_ ## N, + GStmt_none = 0, +#define E(N,S) GStmt_ ## N, GStmt_map(E) #undef E }; @@ -762,10 +816,10 @@ static sqlite3_stmt * g_stmt(enum GStmt_e which){ sqlite3_stmt ** q = 0; char const * zSql = 0; switch(which){ - case GStmt_e_none: - fatal("GStmt_e_none is not a valid statement handle"); + case GStmt_none: + fatal("GStmt_none is not a valid statement handle"); return NULL; -#define E(N,S) case GStmt_e_ ## N: zSql = S; q = &g.stmt.N; break; +#define E(N,S) case GStmt_ ## N: zSql = S; q = &g.stmt.N; break; GStmt_map(E) #undef E } @@ -777,6 +831,10 @@ static sqlite3_stmt * g_stmt(enum GStmt_e which){ } return *q; } +static void g_stmt_reset(sqlite3_stmt * const q){ + sqlite3_clear_bindings(q); + sqlite3_reset(q); +} #if 0 /* @@ -795,13 +853,16 @@ void g_outf(char const *zFmt, ...){ /* Outputs n bytes from z to c-pp's global output channel. */ static void g_out(void const *z, unsigned int n); void g_out(void const *z, unsigned int n){ - if(1!=fwrite(z, n, 1, g.out.pFile)){ + if(g.out.pFile && 1!=fwrite(z, n, 1, g.out.pFile)){ int const err = errno; fatal("fwrite() output failed with errno #%d", err); } } void g_stderrv(char const *zFmt, va_list va){ + if( g.out.pFile==stdout ){ + fflush(g.out.pFile); + } vfprintf(stderr, zFmt, va); } @@ -862,17 +923,18 @@ CmppLevel * CmppLevel_get(CmppTokenizer * const t){ void db_affirm_rc(int rc, const char * zMsg){ - if(rc){ - assert( g.db ); - fatal("Db error #%d %s: %s", rc, zMsg, - sqlite3_errmsg(g.db)); + switch(rc){ + case 0: + case SQLITE_DONE: + case SQLITE_ROW: + break; + default: + assert( g.db ); + fatal("Db error #%d %s: %s", rc, zMsg, + sqlite3_errmsg(g.db)); } } -void db_finalize(sqlite3_stmt *pStmt){ - sqlite3_finalize(pStmt); -} - int db_step(sqlite3_stmt *pStmt){ int const rc = sqlite3_step(pStmt); switch( rc ){ @@ -965,32 +1027,36 @@ void db_free(void *m){ sqlite3_free(m); } -void db_define_add(const char * zKey){ - int rc; - char const * zEq = 0; - unsigned nVal = 0; - char const * zVal = cmpp_val_part(zKey, -1, '=', &nVal, &zEq); - sqlite3_stmt * const q = g_stmt(GStmt_e_defIns); - assert( q ); - //g_stderr("zKey=%s\nzVal=%s\nzEq=%s\n", zKey, zVal, zEq); - if( zEq ){ - db_bind_textn(q, 1, zKey, (zEq-zKey)); +void db_define_add(const char * zKey, char const *zVal){ + cmpp_kvp kvp = cmpp_kvp_empty; + cmpp_kvp_parse(&kvp, zKey, -1, + zVal + ? cmpp_key_op_none + : cmpp_key_op_eq1 + ); + if( kvp.v.z ){ + if( zVal ){ + assert(!"cannot happen - cmpp_key_op_none will prevent it"); + fatal("Cannot assign two values to [%.*s] [%.*s] [%s]", + kvp.k.n, kvp.k.z, kvp.v.n, kvp.v.z, zVal); + } }else{ - db_bind_text(q, 1, zKey); + kvp.v.z = zVal; + kvp.v.n = zVal ? (int)strlen(zVal) : 0; } - if( zEq ){ - if( zVal && nVal ){ - db_bind_text(q, 2, zVal); + sqlite3_stmt * const q = g_stmt(GStmt_defIns); + //g_stderr("zKey=%s\nzVal=%s\nzEq=%s\n", zKey, zVal, zEq); + db_bind_textn(q, 1, kvp.k.z, kvp.k.n); + if( kvp.v.z ){ + if( kvp.v.n ){ + db_bind_textn(q, 2, kvp.v.z, (int)kvp.v.n); }else{ db_bind_null(q, 2); } }else{ db_bind_int(q, 2, 1); } - rc = db_step(q); - if(SQLITE_DONE != rc){ - db_affirm_rc(rc, "Stepping INSERT on def"); - } + db_step(q); g_debug(2,("define: %s%s%s\n", zKey, zVal ? " with value " : "", @@ -999,20 +1065,18 @@ void db_define_add(const char * zKey){ } static void db_define_add_file(const char * zKey){ - int rc; - char const * zEq = 0; - unsigned nVal = 0; - char const * zVal = cmpp_val_part(zKey, -1, '=', &nVal, &zEq); - if( !zVal || !nVal ){ + cmpp_kvp kvp = cmpp_kvp_empty; + cmpp_kvp_parse(&kvp, zKey, -1, cmpp_kvp_op_eq1); + if( !kvp.v.z || !kvp.v.n ){ fatal("Invalid filename: %s", zKey); } sqlite3_stmt * q = 0; FileWrapper fw = FileWrapper_empty; - FileWrapper_open(&fw, zVal, "r"); + FileWrapper_open(&fw, kvp.v.z, "r"); FileWrapper_slurp(&fw); - q = g_stmt(GStmt_e_defIns); + q = g_stmt(GStmt_defIns); //g_stderr("zKey=%s\nzVal=%s\nzEq=%s\n", zKey, zVal, zEq); - db_bind_textn(q, 1, zKey, (zEq-zKey)); + db_bind_textn(q, 1, kvp.k.z, (int)kvp.k.n); if( g.flags.chompF ){ FileWrapper_chomp(&fw); } @@ -1029,22 +1093,25 @@ static void db_define_add_file(const char * zKey){ "binding empty file content"); } FileWrapper_close(&fw); - rc = db_step(q); - if(SQLITE_DONE != rc){ - db_affirm_rc(rc, "stepping INSERT on def"); - } + db_step(q); + g_stmt_reset(q); g_debug(2,("define: %s%s%s\n", - zKey, - zVal ? " with value " : "", - zVal ? zVal : "")); - sqlite3_clear_bindings(q); - sqlite3_reset(q); + kvp.k.z, + kvp.v.z ? " with value " : "", + kvp.v.z ? kvp.v.z : "")); } -int db_define_has(const char * zName){ + +static inline unsigned int cmpp_strlen(char const *z, int n){ + return n<0 ? (int)strlen(z) : (unsigned)n; +} + + +int db_define_has(const char * zName, int nName){ int rc; - sqlite3_stmt * const q = g_stmt(GStmt_e_defHas); - db_bind_text(q, 1, zName); + sqlite3_stmt * const q = g_stmt(GStmt_defHas); + nName = cmpp_strlen(zName, nName); + db_bind_textn(q, 1, zName, nName); rc = db_step(q); if(SQLITE_ROW == rc){ rc = 1; @@ -1053,15 +1120,14 @@ int db_define_has(const char * zName){ rc = 0; } g_debug(1,("defined [%s] ?= %d\n",zName, rc)); - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); return rc; } int db_define_get_bool(const char * zName, int nName){ - sqlite3_stmt * const q = g_stmt(GStmt_e_defGetBool); + sqlite3_stmt * const q = g_stmt(GStmt_defGetBool); int rc = 0; - if( nName<0 ) nName=(int)strlen(zName); + nName = cmpp_strlen(zName, nName); db_bind_textn(q, 1, zName, nName); rc = db_step(q); if(SQLITE_ROW == rc){ @@ -1073,15 +1139,14 @@ int db_define_get_bool(const char * zName, int nName){ assert(SQLITE_DONE==rc); rc = 0; } - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); return rc; } int db_define_get(const char * zName, int nName, char **zVal, unsigned int *nVal){ - sqlite3_stmt * q = g_stmt(GStmt_e_defGet); - if( nName<0 ) nName=(int)strlen(zName); + sqlite3_stmt * q = g_stmt(GStmt_defGet); + nName = cmpp_strlen(zName, nName); db_bind_textn(q, 1, zName, nName); int n = 0; int rc = db_step(q); @@ -1106,28 +1171,26 @@ int db_define_get(const char * zName, int nName, nName, zName, rc, *zVal ? n : 0, *zVal ? *zVal : "")); - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); return rc; } void db_define_rm(const char * zKey){ int rc; int n = 0; - sqlite3_stmt * const q = g_stmt(GStmt_e_defDel); + sqlite3_stmt * const q = g_stmt(GStmt_defDel); db_bind_text(q, 1, zKey); rc = db_step(q); if(SQLITE_DONE != rc){ db_affirm_rc(rc, "Stepping DELETE on def"); } g_debug(2,("undefine: %.*s\n",n, zKey)); - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); } void db_including_add(const char * zKey, const char * zSrc, int srcLine){ int rc; - sqlite3_stmt * const q = g_stmt(GStmt_e_inclIns); + sqlite3_stmt * const q = g_stmt(GStmt_inclIns); db_bind_text(q, 1, zKey); db_bind_text(q, 2, zSrc); db_bind_int(q, 3, srcLine); @@ -1136,40 +1199,37 @@ void db_including_add(const char * zKey, const char * zSrc, int srcLine){ db_affirm_rc(rc, "Stepping INSERT on incl"); } g_debug(2,("is-including-file add [%s] from [%s]:%d\n", zKey, zSrc, srcLine)); - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); } void db_include_rm(const char * zKey){ int rc; - sqlite3_stmt * const q = g_stmt(GStmt_e_inclDel); + sqlite3_stmt * const q = g_stmt(GStmt_inclDel); db_bind_text(q, 1, zKey); rc = db_step(q); if(SQLITE_DONE != rc){ db_affirm_rc(rc, "Stepping DELETE on incl"); } g_debug(2,("inclpath rm [%s]\n", zKey)); - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); } char * db_include_search(const char * zKey){ char * zName = 0; - sqlite3_stmt * const q = g_stmt(GStmt_e_inclSearch); + sqlite3_stmt * const q = g_stmt(GStmt_inclSearch); db_bind_text(q, 1, zKey); if(SQLITE_ROW==db_step(q)){ const unsigned char * z = sqlite3_column_text(q, 0); zName = z ? sqlite3_mprintf("%s", z) : 0; if(!zName) fatal("Alloc failed"); } - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); return zName; } static int db_including_has(const char * zName){ int rc; - sqlite3_stmt * const q = g_stmt(GStmt_e_inclHas); + sqlite3_stmt * const q = g_stmt(GStmt_inclHas); db_bind_text(q, 1, zName); rc = db_step(q); if(SQLITE_ROW == rc){ @@ -1179,8 +1239,7 @@ static int db_including_has(const char * zName){ rc = 0; } g_debug(2,("inclpath has [%s] = %d\n",zName, rc)); - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); return rc; } @@ -1199,7 +1258,7 @@ void db_including_check(const char * zName){ void db_include_dir_add(const char * zDir){ static int seq = 0; int rc; - sqlite3_stmt * const q = g_stmt(GStmt_e_inclPathAdd); + sqlite3_stmt * const q = g_stmt(GStmt_inclPathAdd); db_bind_int(q, 1, ++seq); db_bind_text(q, 2, zDir); rc = db_step(q); @@ -1207,16 +1266,83 @@ void db_include_dir_add(const char * zDir){ db_affirm_rc(rc, "Stepping INSERT on inclpath"); } g_debug(2,("inclpath add #%d: %s\n",seq, zDir)); - sqlite3_clear_bindings(q); - sqlite3_reset(q); + g_stmt_reset(q); +} + +void g_FileWrapper_link(FileWrapper *fp){ + assert(!fp->pTail); + fp->pTail = g.pFiles; + g.pFiles = fp; +} + +void g_FileWrapper_close(FileWrapper *fp){ + assert(fp); + assert(fp->pTail || g.pFiles==fp); + g.pFiles = fp->pTail; + fp->pTail = 0; + FileWrapper_close(fp); +} + +static void g_cleanup(int bCloseFileChain){ +#define E(N,S) sqlite3_finalize(g.stmt.N); g.stmt.N = 0; + GStmt_map(E) +#undef E + if( bCloseFileChain ){ + FileWrapper * fpNext = 0; + for( FileWrapper * fp=g.pFiles; fp; fp=fpNext ){ + fpNext = fp->pTail; + fp->pTail = 0; + FileWrapper_close(fp); + } + } + FileWrapper_close(&g.out); + if(g.db) sqlite3_close(g.db); } static void cmpp_atexit(void){ -#define E(N,S) sqlite3_finalize(g.stmt.N); - GStmt_map(E) -#undef E - FileWrapper_close(&g.out); - if(g.db) sqlite3_close(g.db); + g_cleanup(1); +} + +int cmpp_is_legal_key(char const *zKey, int nKey, char const **zAt){ + char const * z = zKey; + nKey = cmpp_strlen(zKey, nKey); + if( !nKey ){ + if( zAt ) *zAt = z; + return 0; + } + char const * const zEnd = z ? z + nKey : NULL; + for( ; z < zEnd; ++z ){ + switch( (0x80 & *z) ? 0 : *z ){ + case 0: + case '_': + continue; + case '-': + case '.': + case '/': + case ':': + case '=': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if( z==zKey ) break; + continue; + default: + if( isalpha((int)*z) ) continue; + } + if( zAt ) *zAt = z; + return 0; + } + assert( z==zEnd ); + return 1; +} + +void cmpp_affirm_legal_key(char const *zKey, int nKey){ + char const *zAt = 0; + nKey = cmpp_strlen(zKey, nKey); + if( !cmpp_is_legal_key(zKey, nKey, &zAt) ){ + assert( zAt ); + fatal("Illegal character 0x%02x in key [%.*s]\n", + (int)*zAt, nKey, zKey); + } } /* @@ -1300,7 +1426,8 @@ static void cmpp_initdb(void){ ** [zBegin,zEnd), returns g.delim.n if it is at the start of a line and ** starts with g.delim.z, else returns 0. */ -static unsigned short cmpp_is_delim(unsigned char const *zBegin, +//static +unsigned short cmpp_is_delim(unsigned char const *zBegin, unsigned char const *zEnd, unsigned char const *zPos){ assert(zEnd>zBegin); @@ -1317,23 +1444,136 @@ static unsigned short cmpp_is_delim(unsigned char const *zBegin, } } -static char const * cmpp_val_part(char const *zKey, - int nKey, - char chEq, - unsigned * len, - char const **zEq){ - const char *zPos = zKey; - if( nKey<0 ) nKey = (int)strlen(zKey); - const char * const zEnd = zPos + nKey; - for( ; *zPos && zPoszPos,t->zEnd) for a derective delimiter. Emits any + non-delimiter output found along the way. + + This updtes t->zPos and t->lineNo as it goes. + + If a delimiter is found, it updates t->token and returns 0. + On no match returns 0. +*/ +//static +int CmppTokenizer__delim_search(CmppTokenizer * const t){ + if(!t->zPos) t->zPos = t->zBegin; + if( t->zPos>=t->zEnd ){ + return 0; + } + assert( (t->zPos==t->zBegin || t->zPos[-1]=='\n') + && "Else we've mismanaged something."); + char const * const zD = g.delim.z; + unsigned short const nD = g.delim.n; + unsigned char const * const zEnd = t->zEnd; + unsigned char const * zLeft = t->zPos; + unsigned char const * z = zLeft; + + assert( 0==*zEnd && "Else we'll misinteract with strcspn()" ); + if( *zEnd ){ + fatal("Input must be NUL-terminated."); + return 0; + } +#define tflush \ + if(z>zEnd) z=zEnd; \ + if( z>zLeft ) { \ + cmpp_t_out_expand(t, zLeft, (unsigned)(z-zLeft)); \ + } zLeft = z + while(z < zEnd){ + size_t const nNl = strcspn((char const *)z, "\n"); + unsigned char const * const zNl = (z + nNl > zEnd ? zEnd : z + nNl); + if( nNl >= CmppArgs_BufSize /* too long */ + //|| '\n'!=(char)*zNl /* end of input */ + /* ^^^ we have to accept a missing trailing EOL for the + sake of -e scripts. */ + ){ + ++t->lineNo; + z = zNl + 1; + tflush; + continue; + } + assert( '\n'==*zNl || !*zNl ); + //g_stderr("input: zNl=%d z=<<<%.*s>>>\n", (int)*zNl, (zNl-z), z); + unsigned char const * const zBOL = z; + cmpp__skip_space_c(&z, zNl); + if( z+nD < zNl && 0==memcmp(z, zD, nD) ){ + if( zBOL!=z ){ + /* Do not emit space which preceeds a delimiter */ + zLeft = z; + } + tflush; + t->token.zBegin = z + nD; + t->token.zEnd = zNl; + cmpp__skip_space_c(&t->token.zBegin, t->token.zEnd); + t->token.ttype = TT_Line; + t->token.lineNo = t->lineNo++; + t->zPos = t->token.zEnd + 1; + + //g_stderr("token=%.*s\n", (zNl - t->token.zBegin), t->token.zBegin); + return 1; + } + z = zNl+1; + ++t->lineNo; + tflush; + //g_stderr0("line #%d no match\n",(int)t->lineNO); + } + tflush; + t->zPos = z; + return 0; +#undef tflush +} + +void cmpp_kvp_parse(cmpp_kvp * p, char const *zKey, int nKey, + cmpp_kvp_op_e opPolicy){ + char chEq = 0; + char opLen = 0; + *p = cmpp_kvp_empty; + p->k.z = zKey; + p->k.n = cmpp_strlen(zKey, nKey); + switch( opPolicy ){ + case cmpp_kvp_op_none: break; + case cmpp_kvp_op_eq1: + chEq = '='; + opLen = 1; + break; + default: + assert(!"don't use these yet"); + /* todo: ==, !=, <=, <, >, >= */ + chEq = '='; + opLen = 1; + break; + } + assert( chEq ); + p->op = cmpp_kvp_op_none; + const char * const zEnd = p->k.z + p->k.n; + for(const char * zPos = p->k.z ; *zPos && zPosop = cmpp_kvp_op_eq1; + p->k.n = (unsigned)(zPos - zKey); + zPos += opLen; + assert( zPos <= zEnd ); + p->v.z = zPos; + p->v.n = (unsigned)(zEnd - zPos); + break; } } - return 0; + cmpp_affirm_legal_key(p->k.z, p->k.n); } static void cmpp_t_out_expand(CmppTokenizer * const t, @@ -1421,169 +1661,117 @@ static void cmpp_t_out_expand(CmppTokenizer * const t, ** with cmpp_process_keyword(), which should then be called. */ static int cmpp_next_keyword_line(CmppTokenizer * const t){ - unsigned char const * zStart; - unsigned char const * z; CmppToken * const tok = &t->token; - unsigned short isDelim = 0; assert(t->zBegin); assert(t->zEnd > t->zBegin); if(!t->zPos) t->zPos = t->zBegin; - t->zAnchor = t->zPos; - zStart = z = t->zPos; + t->args.pKw = 0; + t->args.argc = 0; *tok = CmppToken_empty; - while(zzEnd - && 0==(isDelim = cmpp_is_delim(t->zBegin, t->zEnd, z))){ - ++z; + if( !CmppTokenizer__delim_search(t) ){ + return 0; } - if(z>zStart){ - /* We passed up content */ - //cmpp_t_out(t, zStart, (unsigned)(z - zStart)); - cmpp_t_out_expand(t, zStart, (unsigned)(z - zStart)); - } - assert(isDelim==0 || isDelim==g.delim.n); - tok->lineNo = t->lineNo += count_lines(zStart, z); - if(isDelim){ - /* Handle backslash-escaped newlines */ - int isEsc = 0, atEol = 0; - tok->zBegin = z+isDelim; - for( ++z ; zzEnd && 0==atEol; ++z ){ - switch((int)*z){ - case (int)'\\': - isEsc = 0==isEsc; break; - case (int)'\n': - atEol = 0==isEsc; - isEsc = 0; - ++t->lineNo; - break; - default: - break; - } - } - tok->zEnd = atEol ? z-1 : z; - /* Strip leading spaces */ - while(tok->zBegin < tok->zEnd && isspace((char)(*tok->zBegin))){ - ++tok->zBegin; - } - tok->ttype = TT_Line; - g_debug(2,("Keyword @ line %u: [[[%.*s]]]\n", - tok->lineNo, - (int)(tok->zEnd-tok->zBegin), tok->zBegin)); - } - t->zPos = z; - if(isDelim){ - /* Split t->token into arguments for the line's keyword */ - int i, argc = 0, prevChar = 0; - const unsigned tokLen = (unsigned)(tok->zEnd - tok->zBegin); - unsigned char * zKwd; - unsigned char * zEsc; - unsigned char * zz; + /* Split t->token into arguments for the line's keyword */ + int i, argc = 0, prevChar = 0; + const unsigned tokLen = (unsigned)(tok->zEnd - tok->zBegin); + unsigned char * zKwd; + unsigned char * zEsc; + unsigned char * zz; - assert(TT_Line==tok->ttype); - if((unsigned)sizeof(t->args.lineBuf) < tokLen + 1){ - fatal("Keyword line #%u is unreasonably long: %.*s", - tok->lineNo, tokLen, tok->zBegin); - }else if(!tokLen){ - fatal("Line #%u has no keyword after delimiter", tok->lineNo); - } - g_debug(2,("token @ line %u len=%u [[[%.*s]]]\n", - tok->lineNo, tokLen, tokLen, tok->zBegin)); - zKwd = &t->args.lineBuf[0]; - memcpy(zKwd, tok->zBegin, tokLen); - memset(zKwd + tokLen, 0, sizeof(t->args.lineBuf) - tokLen); - for( zEsc = 0, zz = zKwd; *zz; ++zz ){ - /* Convert backslash-escaped newlines to whitespace */ - switch((int)*zz){ - case (int)'\\': - if(zEsc) zEsc = 0; - else zEsc = zz; - break; - case (int)'\n': - assert(zEsc && "Should not have an unescaped newline?"); - if(zEsc==zz-1){ - *zEsc = (unsigned char)' '; - /* FIXME?: memmove() lnBuf content one byte to the left here - ** to collapse backslash and newline into a single - ** byte. Also consider collapsing all leading space on the - ** next line. */ - } - zEsc = 0; - *zz = (unsigned char)' '; - break; - default: - zEsc = 0; - break; - } - } - t->args.argv[argc++] = zKwd; - for( zz = zKwd; *zz; ++zz ){ - if(isspace(*zz)){ - *zz = 0; + assert(TT_Line==tok->ttype); + g_debug(2,("token @ line %u len=%u [[[%.*s]]]\n", + tok->lineNo, tokLen, tokLen, tok->zBegin)); + zKwd = &t->args.lineBuf[0]; + memcpy(zKwd, tok->zBegin, tokLen); + memset(zKwd + tokLen, 0, sizeof(t->args.lineBuf) - tokLen); + for( zEsc = 0, zz = zKwd; *zz; ++zz ){ + /* Convert backslash-escaped newlines to whitespace */ + switch((int)*zz){ + case (int)'\\': + if(zEsc) zEsc = 0; + else zEsc = zz; + break; + case (int)'\n': + assert(zEsc && "Should not have an unescaped newline?"); + if(zEsc==zz-1){ + *zEsc = (unsigned char)' '; + /* FIXME?: memmove() lnBuf content one byte to the left here + ** to collapse backslash and newline into a single + ** byte. Also consider collapsing all leading space on the + ** next line. (Much later: or just collapse the output as we go, + ** effectively shrinking the line.) */ + } + zEsc = 0; + *zz = (unsigned char)' '; + break; + default: + zEsc = 0; break; - } } - t->args.pKw = CmppKeyword_search((char const *)zKwd); - if(!t->args.pKw){ - fatal("Unknown keyword '%s' at line %u\n", (char const *)zKwd, - tok->lineNo); + } + t->args.argv[argc++] = zKwd; + for( zz = zKwd; *zz; ++zz ){ + if(isspace(*zz)){ + *zz = 0; + break; } - for( ++zz ; *zz && isspace(*zz); ++zz ){} - if(t->args.pKw->bTokenize){ - for( ; *zz; prevChar = *zz, ++zz ){ - /* Split string into word-shaped tokens. - ** TODO ?= quoted strings, for the sake of the - ** #error keyword. */ - if(isspace(*zz)){ - assert(zz!=zKwd && "Leading space was stripped earlier."); - *zz = 0; - }else{ - if(argc == (int)CmppArgs_Max){ - fatal("Too many arguments @ line %u: %.*s", - tok->lineNo, tokLen, tok->zBegin); - }else if(zz>zKwd && !prevChar){ - t->args.argv[argc++] = zz; - } + } + t->args.pKw = CmppKeyword_search((char const *)zKwd); + if(!t->args.pKw){ + fatal("Unknown keyword '%s' at line %u\n", (char const *)zKwd, + tok->lineNo); + } + for( ++zz ; *zz && isspace(*zz); ++zz ){} + if(t->args.pKw->bTokenize){ + for( ; *zz; prevChar = *zz, ++zz ){ + /* Split string into word-shaped tokens. + ** TODO ?= quoted strings, for the sake of the + ** #error keyword. */ + if(isspace(*zz)){ + assert(zz!=zKwd && "Leading space was stripped earlier."); + *zz = 0; + }else{ + if(argc == (int)CmppArgs_Max){ + fatal("Too many arguments @ line %u: %.*s", + tok->lineNo, tokLen, tok->zBegin); + }else if(zz>zKwd && !prevChar){ + t->args.argv[argc++] = zz; } } - }else{ - /* Treat rest of line as one token */ - if(*zz) t->args.argv[argc++] = zz; } - tok->ttype = t->args.pKw->ttype; - if(g.flags.doDebug>1){ - for(i = 0; i < argc; ++i){ - g_debug(0,("line %u arg #%d=%s\n", - tok->lineNo, i, - (char const *)t->args.argv[i])); - } - } - t->args.argc = argc; }else{ - t->args.pKw = 0; - t->args.argc = 0; + /* Treat rest of line as one token */ + if(*zz) t->args.argv[argc++] = zz; } - return isDelim; -} - -static void cmpp_kwd__err_prefix(CmppKeyword const * pKw, - CmppTokenizer const *t, - char const *zPrefix){ - g_stderr("%s%s%s @ %s line %u: ", - zPrefix ? zPrefix : "", - zPrefix ? ": " : "", - pKw->zName, t->zName, t->token.lineNo); + tok->ttype = t->args.pKw->ttype; + if(g.flags.doDebug>1){ + for(i = 0; i < argc; ++i){ + g_debug(0,("line %u arg #%d=%s\n", + tok->lineNo, i, + (char const *)t->args.argv[i])); + } + } + t->args.argc = argc; + return 1; } /* Internal error reporting helper for cmpp_keyword_f() impls. */ -static CMPP_NORETURN void cmpp_kwd__err(CmppKeyword const * pKw, - CmppTokenizer const *t, - char const *zFmt, ...){ +static CMPP_NORETURN void cmpp_kwd__err_(char const *zFile, int line, + CmppKeyword const * pKw, + CmppTokenizer const *t, + char const *zFmt, ...){ va_list va; - cmpp_kwd__err_prefix(pKw, t, "Fatal error"); + g_stderr("%s @ %s line %u:", + pKw->zName, t->zName, t->token.lineNo); va_start(va, zFmt); - fatalv(zFmt, va); + g.tok = 0 /* stop fatalv__base() from duplicating the file info */; + fatalv__base(zFile, line, zFmt, va); + /* not reached */ va_end(va); } +#define cmpp_kwd__err(...) cmpp_kwd__err_(__FILE__,__LINE__, __VA_ARGS__) +#define cmpp_t__err(T,...) cmpp_kwd__err_(__FILE__,__LINE__, (T)->args.pKw, (T), __VA_ARGS__) /* No-op cmpp_keyword_f() impl. */ static void cmpp_kwd_noop(CmppKeyword const * pKw, CmppTokenizer *t){ @@ -1598,8 +1786,7 @@ static void cmpp_kwd_error(CmppKeyword const * pKw, CmppTokenizer *t){ assert(t->args.argc < 3); const char *zBegin = t->args.argc>1 ? (const char *)t->args.argv[1] : 0; - cmpp_kwd__err_prefix(pKw, t, NULL); - fatal("%s", zBegin ? zBegin : "(no additional info)"); + cmpp_t__err(t, "%s", zBegin ? zBegin : "(no additional info)"); } } @@ -1610,77 +1797,282 @@ static void cmpp_kwd_define(CmppKeyword const * pKw, CmppTokenizer *t){ cmpp_kwd__err(pKw, t, "Expecting one or more arguments"); }else{ int i = 1; - void (*func)(const char *) = TT_Define==pKw->ttype - ? db_define_add : db_define_rm; for( ; i < t->args.argc; ++i){ - func( (char const *)t->args.argv[i] ); + char const * const zArg = (char const *)t->args.argv[i]; + cmpp_affirm_legal_key(zArg, -1); + if( TT_Define==pKw->ttype ){ + db_define_add( zArg, NULL ); + }else{ + db_define_rm( zArg ); + } } } } -/* Impl. for #if, #ifnot, #elif, #elifnot. */ -static void cmpp_kwd_if(CmppKeyword const * pKw, CmppTokenizer *t){ +static int cmpp_val_matches(char const *zGlob, char const *zRhs){ + return 0==sqlite3_strglob(zGlob, zRhs); +} + +typedef int (*cmpp_vcmp_f)(char const *zLhs, char const *zRhs); + +/* +** Accepts a key in the form X or X=Y. In the former case, it uses +** db_define_get_bool(kvp->k) to determine its truthiness, else it +** compares the kvp->v part to kvp->k's defined value to determine +** truthiness. +** +** Unless... +** +** If bCheckDefined is true is true then (A) it returns true if the +** value is defined and (B) fails fatally if given an X=Y-format key. +** +** Returns true if zKey evals to true, else false. +*/ +//static +int cmpp_kvp_truth(CmppKeyword const * const pKw, + CmppTokenizer const * const t, + cmpp_kvp const * const kvp, + int bCheckDefined){ int buul = 0; + if( kvp->v.z ){ + if( bCheckDefined ){ + cmpp_kwd__err(pKw, t, "Value part is not legal for " + "is-defined checks: %.s", + kvp->k.n, kvp->k.z); + } + char * zVal = 0; + unsigned int nVal = 0; + buul = db_define_get(kvp->k.z, (int)kvp->k.n, &zVal, &nVal); + //g_debug(0,("checking key[%.*s]=%.*s\n", (zEq-zKey), zKey, nVal, zVal)); + if( kvp->v.n && nVal ){ + /* FIXME? do this with a query */ + /*g_debug(0,("if get-define [%.*s]=[%.*s] zValPart=%s\n", + (zEq-zKey), zKey, + nVal, zVal, zValPart));*/ + buul = cmpp_val_matches(kvp->v.z, zVal); + //g_debug(0,("buul=%d\n", buul)); + }else{ + assert( 0==kvp->v.n || 0==nVal ); + buul = kvp->v.n == nVal; + } + db_free(zVal); + }else{ + if( bCheckDefined ){ + buul = db_define_has(kvp->k.z, kvp->k.n); + }else{ + buul = db_define_get_bool(kvp->k.z, kvp->k.n); + } + } + return buul; +} + +#if 0 +/* +** A thin proxy for cmpp_kvp_truth(). +*/ +static int cmpp_key_truth(CmppKeyword const * pKw, + CmppTokenizer const * t, + char const *zKey, int bCheckDefined){ + cmpp_kvp kvp = cmpp_kvp_empty; + cmpp_kvp_parse(&kvp, zKey, -1, cmpp_kvp_op_eq1); + return cmpp_kvp_truth(pKw, t, &kvp, bCheckDefined); +} +#endif + +//static +cmpp_kvp_op_e cmpp_t_is_op(CmppTokenizer const * t, int arg){ + if( t->args.argc > arg ){ + char const * const z = (char const *)t->args.argv[arg]; +#define E(N,S) if( strcmp(S,z) ) return cmpp_kvp_op_ ## N; else + cmpp_kvp_op_map(E) +#undef E + if(0) {} + } + return cmpp_kvp_op_none; +} + +/* +** A single part of an #if-type expression. They are parsed from +** CmppTokenizer::args in this form: +** +** not* defined{0,1} key[=[value]] +*/ +struct CmppExprDef { + /* The key part of the input. */ + cmpp_kvp kvp; + struct { + int ndx; + int next; + } arg; + CmppTokenizer const * tizer; + /* Set to 0 or 1 depending how many "not" are parsed. */ + unsigned char bNegated; + /* Set to 1 if "defined" is parsed. */ + unsigned char bCheckDefined; +}; +typedef struct CmppExprDef CmppExprDef; +#define CmppExprDef_empty_m {cmpp_kvp_empty_m,{0,0},0,0,0} +static const CmppExprDef CmppExprDef_empty = CmppExprDef_empty_m; + +/* +** Evaluate cep to true or false and return that value: +** +** If cep->bCheckDefined, return the result of db_define_has(). +** +** Else if cep->kvp.v.z is not NULL then fetch the define's value +** and return the result of cmpp_val_matches(cep->kvp.v.z,thatValue). +** +** Else return the result of db_define_get_bool(). +** +** The returned result accounts for cep->bNegated. +*/ +static int CmppExprDef_eval(CmppExprDef const * cep){ + int buul = 0; + + if( cep->bCheckDefined ){ + assert( !cep->kvp.v.n ); + buul = db_define_has(cep->kvp.k.z, (int)cep->kvp.k.n); + }else if( cep->kvp.v.z ){ + unsigned nVal = 0; + char * zVal = 0; + buul = db_define_get(cep->kvp.k.z, cep->kvp.k.n, &zVal, &nVal); + if( nVal ){ + buul = cmpp_val_matches(cep->kvp.v.z, zVal); + } + db_free(zVal); + }else{ + buul = db_define_get_bool(cep->kvp.k.z, cep->kvp.k.n); + } + return cep->bNegated ? !buul : buul; +} + +/* +** Expects t->args, starting at t->args.argv[startArg], to parse to +** one CmmpExprDef. It clears cep and repopulates it with info about +** the parse. Fails fatally on a parse error. +** +** Returns true if it reads one, false if it doesn't, and fails fatally +** if what it tries to parse is not empty but is not a CmppExprDef. +** +** Specifically, it parses: +** +** not+ defined? Word[=value] +** +*/ +static int CmppExprDef_read_one(CmppKeyword const * pKw, + CmppTokenizer const * t, + int startArg, CmppExprDef * cep){ + char const *zKey = 0; + *cep = CmppExprDef_empty; + cep->arg.ndx = startArg; + assert( t->args.pKw ); + assert( t->args.pKw==pKw ); + cep->tizer = t; + for(int i = startArg; !zKey && iargs.argc; ++i ){ + char const * z = (char const *)t->args.argv[i]; + if( 0==strcmp(z, "not") ){ + cep->bNegated = !cep->bNegated; + }else if( 0==strcmp(z,"defined") ){ + if( cep->bCheckDefined ){ + cmpp_kwd__err(pKw, t, + "Cannot use 'defined' more than once"); + } + cep->bCheckDefined = 1; + }else{ + assert( !zKey ); + cmpp_kvp_parse(&cep->kvp, z, -1, cmpp_kvp_op_eq1); + if( cep->bCheckDefined && cep->kvp.v.z ){ + cmpp_kwd__err(pKw, t, "Cannot use X=Y keys with 'defined'"); + cep->arg.next = ++i; + } + return 1; + } + } + return 0; +} + +/* +** Evals pStart and then proceeds to process any remaining arguments +** in t->args as RHS expressions. Returns the result of the expression +** as a bool. +** +** Specifically, it parses: +** +** and|or CmppExprDef +** +** Where CmppExprDef is the result of CmppExprDef_read_one(). +*/ +static int CmppExprDef_parse_cond(CmppKeyword const *pKw, + CmppTokenizer *t, + CmppExprDef const * pStart){ + enum { Op_none = 0, Op_And, Op_Or }; + int lhs = CmppExprDef_eval(pStart); + int op = Op_none; + int i = pStart->arg.next; + for( ; i < t->args.argc; ++i ){ + CmppExprDef eNext = CmppExprDef_empty; + char const *z = (char const *)t->args.argv[i]; + if( 0==strcmp("and",z) ){ + if( Op_none!=op ) goto multiple_ops; + op = Op_And; + continue; + }else if( 0==strcmp("or",z) ){ + if( Op_none!=op ) goto multiple_ops; + op = Op_Or; + continue; + }else if( !CmppExprDef_read_one(pKw, t, i, &eNext) ){ + if( Op_none!=op ){ + cmpp_t__err(t, "Stray operator: %s",z); + } + } + assert( eNext.kvp.k.z ); + int const rhs = CmppExprDef_eval(&eNext); + switch( op ){ + case Op_none: break; + case Op_And: lhs = lhs && rhs; break; + case Op_Or: lhs = lhs || rhs; break; + default: + assert(!"cannot happen"); + fatal("this cannot happen"); + } + op = Op_none; + } + if( Op_none!=op ){ + cmpp_t__err(t, "Extra operator at end of expression"); + }else if( i < t->args.argc ){ + assert(!"cannot happen"); + cmpp_kwd__err(t->args.pKw, t, "Unhandled extra arguments"); + }else{ + return lhs; + } + assert(!"not reached"); +multiple_ops: + cmpp_t__err(t,"Cannot have multiple operators"); + return 0 /* not reached */; +} + +/* Impl. for #if, #elif, #assert. */ +static void cmpp_kwd_if(CmppKeyword const * pKw, CmppTokenizer *t){ CmppParseState tmpState = TS_Start; - /** - TT_If: accept args: - - - "not" = negates the operation - - - "defined" == is-defined op - */ - int bCheckDefined = 0; - int bNot = 0; - char const * zKey = 0; - char const *zEq = 0; - + CmppExprDef cep = CmppExprDef_empty; + //int buul = 0; assert( TT_If==pKw->ttype - || TT_IfNot==pKw->ttype || TT_Elif==pKw->ttype - || TT_ElifNot==pKw->ttype || TT_Assert==pKw->ttype); if(t->args.argc<2){ cmpp_kwd__err(pKw, t, "Expecting an argument"); } - switch( pKw->ttype ){ - case TT_IfNot: - case TT_ElifNot: bNot = 1; - /* fall through */ - case TT_If: - case TT_Assert: - case TT_Elif: - for( int i = 1; i < t->args.argc; ++i ){ - char const * z = (char const *)t->args.argv[i]; - if( 0==strcmp(z, "not") ){ - bNot = !bNot; - }else if( 0==strcmp(z,"defined") ){ - if( bCheckDefined ){ - cmpp_kwd__err(pKw, t, - "Cannot use 'defined' more than once"); - } - bCheckDefined = 1; - }else if( !zKey ){ - zKey = (char const *)t->args.argv[i]; - }else{ - cmpp_kwd__err(pKw, t, "Unhandled argument: %s", z); - } - } - if( !zKey ){ - cmpp_kwd__err(pKw, t, "Missing key argument"); - } - break; - default: - if(t->args.argc!=2){ - cmpp_kwd__err(pKw, t, "Expecting exactly 1 argument"); - } - zKey = (char const *)t->args.argv[1]; + CmppExprDef_read_one(pKw, t, 1, &cep); + if( !cep.kvp.k.z ){ + cmpp_kwd__err(pKw, t, "Missing key argument"); } /*g_debug(0,("%s %s level %u pstate=%d bNot=%d bCheckDefined=%d\n", pKw->zName, zKey, t->level.ndx, (int)CT_pstate(t), bNot, bCheckDefined));*/ switch(pKw->ttype){ + case TT_Assert: + break; case TT_Elif: - case TT_ElifNot: switch(CT_pstate(t)){ case TS_If: break; case TS_IfPassed: CT_level(t).flags |= CmppLevel_F_ELIDE; return; @@ -1690,66 +2082,34 @@ static void cmpp_kwd_if(CmppKeyword const * pKw, CmppTokenizer *t){ } break; case TT_If: - case TT_IfNot: CmppLevel_push(t); break; - case TT_Assert: - break; default: assert(!"cannot happen"); cmpp_kwd__err(pKw, t, "Unexpected keyword token type"); break; } - unsigned nValPart = 0; - char const * zValPart = cmpp_val_part(zKey, -1, '=', &nValPart, &zEq); - /*g_debug(0,("%s %s level %u pstate=%d bNot=%d bCheckDefined=%d " - "nValPart=%u zValPart=%s\n", - pKw->zName, zKey, t->level.ndx, (int)CT_pstate(t), - bNot, bCheckDefined, nValPart, zValPart));*/ - if( zValPart ){ - if( bCheckDefined ){ - cmpp_kwd__err(pKw, t, "Value part is not legal with %s: %s", - pKw->zName, zKey); - } - unsigned nVal = 0; - char * zVal = 0; - buul = db_define_get(zKey, (zEq-zKey), &zVal, &nVal); - //g_debug(0,("checking key[%.*s]=%.*s\n", (zEq-zKey), zKey, nVal, zVal)); - if( nVal ){ - /* FIXME? do this with a query */ - /*g_debug(0,("if get-define [%.*s]=[%.*s] zValPart=%s\n", - (zEq-zKey), zKey, - nVal, zVal, zValPart));*/ - buul = 0==sqlite3_strglob(zValPart,zVal); - //g_debug(0,("buul=%d\n", buul)); - } - db_free(zVal); - }else{ - if( bCheckDefined ){ - buul = db_define_has(zKey); - }else{ - buul = db_define_get_bool(zKey, -1); - } - } - //if( bNot ) buul = !buul; - if( bNot ? !buul : buul ){ + if( CmppExprDef_parse_cond( pKw, t, &cep ) ){ CT_pstate(t) = tmpState = TS_IfPassed; CT_skipLevel(t) = 0; }else{ if( TT_Assert==pKw->ttype ){ - cmpp_kwd__err(pKw, t, "Assertion failed: %s", zKey); + cmpp_kwd__err(pKw, t, "Assertion failed: %s", + /* fixme: emit the whole line. We don't have it + handy in a readily-printable form. */ + cep.kvp.k.z); } - CT_pstate(t) = TS_If /* also for TT_IfNot, TT_Elif, TT_ElifNot */; + CT_pstate(t) = TS_If /* also for TT_Elif */; CT_skipLevel(t) = 1; g_debug(3,("setting CT_skipLevel = 1 @ level %d\n", t->level.ndx)); } - if( TT_If==pKw->ttype || TT_IfNot==pKw->ttype ){ + if( TT_If==pKw->ttype ){ unsigned const lvlIf = t->level.ndx; CmppToken const lvlToken = CT_level(t).token; while(cmpp_next_keyword_line(t)){ cmpp_process_keyword(t); if(lvlIf > t->level.ndx){ - assert(TT_EndIf == t->token.ttype); + assert(TT_Endif == t->token.ttype); break; } #if 0 @@ -1822,8 +2182,7 @@ static void cmpp_kwd_include(CmppKeyword const * pKw, CmppTokenizer *t){ ** invocation. We might want some other form of multi-include ** protection, rather than this, however. There may well be ** sensible uses for recursion. */ - cmpp_kwd__err_prefix(pKw, t, NULL); - fatal("Recursive include of file: %s", zFile); + cmpp_t__err(t, "Recursive include of file: %s", zFile); } zResolved = db_include_search(zFile); if(zResolved){ @@ -1832,11 +2191,24 @@ static void cmpp_kwd_include(CmppKeyword const * pKw, CmppTokenizer *t){ db_include_rm(zFile); db_free(zResolved); }else{ - cmpp_kwd__err_prefix(pKw, t, NULL); - fatal("file not found: %s", zFile); + cmpp_t__err(t, "file not found: %s", zFile); } } + +static void cmpp_dump_defines( FILE * fp, int bIndent ){ + sqlite3_stmt * const q = g_stmt(GStmt_defSelAll); + while( SQLITE_ROW==sqlite3_step(q) ){ + unsigned char const * zK = sqlite3_column_text(q, 0); + unsigned char const * zV = sqlite3_column_text(q, 1); + int const nK = sqlite3_column_bytes(q, 0); + int const nV = sqlite3_column_bytes(q, 1); + fprintf(fp, "%s%.*s = %.*s\n", + bIndent ? "\t" : "", nK, zK, nV, zV); + } + g_stmt_reset(q); +} + /* Impl. for #pragma. */ static void cmpp_kwd_pragma(CmppKeyword const * pKw, CmppTokenizer *t){ const char * zArg; @@ -1847,15 +2219,7 @@ static void cmpp_kwd_pragma(CmppKeyword const * pKw, CmppTokenizer *t){ zArg = (const char *)t->args.argv[1]; #define M(X) 0==strcmp(zArg,X) if(M("defines")){ - sqlite3_stmt * q = 0; - db_prepare(&q, "SELECT k FROM def ORDER BY k"); - g_stderr("cmpp defines:\n"); - while(SQLITE_ROW==db_step(q)){ - const char * z = (const char *)sqlite3_column_text(q, 0); - int const n = sqlite3_column_bytes(q, 0); - g_stderr("\t%.*s\n", n, z); - } - db_finalize(q); + cmpp_dump_defines(stderr, 1); } else if(M("chomp-F")){ g.flags.chompF = 1; @@ -1881,6 +2245,41 @@ static void cmpp_kwd_pragma(CmppKeyword const * pKw, CmppTokenizer *t){ #undef M } +static void db_step_reset(sqlite3_stmt * const q, char const * zErrTip){ + db_affirm_rc(sqlite3_step(q), zErrTip); + g_stmt_reset(q); +} + +static void cmpp_sp_begin(CmppTokenizer * const t){ + db_step_reset(g_stmt(GStmt_spBegin), "Starting savepoint"); + ++t->nSavepoint; +} + +static void cmpp_sp_rollback(CmppTokenizer * const t){ + if( !t->nSavepoint ){ + cmpp_t__err(t, "Cannot roll back: no active savepoint"); + } + db_step_reset(g_stmt(GStmt_spRollback), + "Rolling back savepoint"); + db_step_reset(g_stmt(GStmt_spRelease), + "Releasing rolled-back savepoint"); + --t->nSavepoint; +} + +static void cmpp_sp_commit(CmppTokenizer * const t){ + if( !t->nSavepoint ){ + cmpp_t__err(t, "Cannot commit: no active savepoint"); + } + db_step_reset(g_stmt(GStmt_spRelease), "Rolling back savepoint"); + --t->nSavepoint; +} + +void CmppTokenizer_cleanup(CmppTokenizer * const t){ + while( t->nSavepoint ){ + cmpp_sp_rollback(t); + } +} + /* Impl. for #savepoint. */ static void cmpp_kwd_savepoint(CmppKeyword const * pKw, CmppTokenizer *t){ const char * zArg; @@ -1889,26 +2288,13 @@ static void cmpp_kwd_savepoint(CmppKeyword const * pKw, CmppTokenizer *t){ cmpp_kwd__err(pKw, t, "Expecting one argument"); } zArg = (const char *)t->args.argv[1]; -#define SP_NAME " cmpp /*" __FILE__ "*/;" #define M(X) 0==strcmp(zArg,X) if(M("begin")){ - db_affirm_rc(sqlite3_exec(g.db, "SAVEPOINT" SP_NAME, 0, 0, 0), - "Starting a savepoint"); + cmpp_sp_begin(t); }else if(M("rollback")){ - db_affirm_rc( - sqlite3_exec(g.db, - "ROLLBACK TO SAVEPOINT" SP_NAME - "RELEASE SAVEPOINT" SP_NAME, - 0, 0, 0 - ), "Rolling back a savepoint" - ); + cmpp_sp_rollback(t); }else if(M("commit")){ - db_affirm_rc( - sqlite3_exec(g.db, - "RELEASE" SP_NAME, - 0, 0, 0 - ), "Committing a savepoint" - ); + cmpp_sp_commit(t); }else{ cmpp_kwd__err(pKw, t, "Unknown savepoint option: %s", zArg); } @@ -1953,26 +2339,26 @@ static void cmpp_kwd_todo(CmppKeyword const * pKw, CmppTokenizer *t){ CmppKeyword aKeywords[] = { /* Keep these sorted by zName */ - {"//", 2, 0, TT_Comment, cmpp_kwd_noop}, - {"@policy", 7, 1, TT_AtPolicy, cmpp_kwd_at_policy}, - {"assert", 3, 1, TT_Assert, cmpp_kwd_if}, - {"define", 6, 1, TT_Define, cmpp_kwd_define}, - {"elif", 4, 1, TT_Elif, cmpp_kwd_if}, - {"elifnot", 7, 1, TT_ElifNot, cmpp_kwd_if}, - {"else", 4, 1, TT_Else, cmpp_kwd_else}, - {"endif", 5, 0, TT_EndIf, cmpp_kwd_endif}, - {"error", 4, 0, TT_Error, cmpp_kwd_error}, - {"if", 2, 1, TT_If, cmpp_kwd_if}, - {"ifnot", 5, 1, TT_IfNot, cmpp_kwd_if}, - {"include", 7, 0, TT_Include, cmpp_kwd_include}, - {"pragma", 6, 1, TT_Pragma, cmpp_kwd_pragma}, - {"savepoint", 9, 1, TT_Savepoint, cmpp_kwd_savepoint}, - {"stderr", 6, 0, TT_Stderr, cmpp_kwd_stderr}, - {"undef", 5, 1, TT_Undef, cmpp_kwd_define}, +#define S(NAME) DStrings.NAME.z, DStrings.NAME.n + {S(Comment), 0, TT_Comment, cmpp_kwd_noop}, + {S(AtPolicy), 1, TT_AtPolicy, cmpp_kwd_at_policy}, + {S(Assert),1, TT_Assert, cmpp_kwd_if}, + {S(Define), 1, TT_Define, cmpp_kwd_define}, + {S(Elif), 1, TT_Elif, cmpp_kwd_if}, + {S(Else), 1, TT_Else, cmpp_kwd_else}, + {S(Endif), 0, TT_Endif, cmpp_kwd_endif}, + {S(Error), 0, TT_Error, cmpp_kwd_error}, + {S(If), 1, TT_If, cmpp_kwd_if}, + {S(Include), 0, TT_Include, cmpp_kwd_include}, + {S(Pragma), 1, TT_Pragma, cmpp_kwd_pragma}, + {S(Savepoint), 1, TT_Savepoint, cmpp_kwd_savepoint}, + {S(Stderr), 0, TT_Stderr, cmpp_kwd_stderr}, + {S(Undef), 1, TT_Undef, cmpp_kwd_define}, +#undef S {0,0,TT_Invalid, 0} }; -static int cmp_CmppKeyword(const void *p1, const void *p2){ +static int cmpp_CmppKeyword(const void *p1, const void *p2){ char const * zName = (const char *)p1; CmppKeyword const * kw = (CmppKeyword const *)p2; return strcmp(zName, kw->zName); @@ -1982,7 +2368,7 @@ CmppKeyword const * CmppKeyword_search(const char *zName){ return (CmppKeyword const *)bsearch(zName, &aKeywords[0], sizeof(aKeywords)/sizeof(aKeywords[0]) - 1, sizeof(aKeywords[0]), - cmp_CmppKeyword); + cmpp_CmppKeyword); } void cmpp_process_keyword(CmppTokenizer * const t){ @@ -1999,6 +2385,7 @@ void cmpp_process_file(const char * zName){ CmppTokenizer ct = CmppTokenizer_empty; g.tok = &ct; FileWrapper_open(&fw, zName, "r"); + g_FileWrapper_link(&fw); FileWrapper_slurp(&fw); g_debug(1,("Read %u byte(s) from [%s]\n", fw.nContent, fw.zName)); if( fw.zContent ){ @@ -2009,39 +2396,48 @@ void cmpp_process_file(const char * zName){ cmpp_process_keyword(&ct); } } - FileWrapper_close(&fw); + g_FileWrapper_close(&fw); if(0!=ct.level.ndx){ CmppLevel * const lv = CmppLevel_get(&ct); fatal("Input ended inside an unterminated nested construct" "opened at [%s] line %u", zName, lv->token.lineNo); } + CmppTokenizer_cleanup(&ct); g.tok = oldTok; } -void fatalv(char const *zFmt, va_list va){ +void fatalv__base(char const *zFile, int line, + char const *zFmt, va_list va){ + FILE * const fp = stderr; fflush(stdout); - fputc('\n', stderr); + fputc('\n', fp); + if( g.flags.doDebug ){ + fprintf(fp, "%s: ", g.zArgv0); + if( zFile ){ + fprintf(fp, "%s:%d ",zFile, line); + } + } if( g.tok ){ - fprintf(stderr,"%s: @%s:%d: ", - g.zArgv0, + fprintf(fp,"@%s:%d: ", (g.tok->zName && 0==strcmp("-",g.tok->zName)) ? "" : g.tok->zName, g.tok->lineNo); } if(zFmt && *zFmt){ - vfprintf(stderr, zFmt, va); + vfprintf(fp, zFmt, va); } - fputc('\n', stderr); - fflush(stderr); + fputc('\n', fp); + fflush(fp); exit(1); } -void fatal(char const *zFmt, ...){ +void fatal__base(char const *zFile, int line, + char const *zFmt, ...){ va_list va; va_start(va, zFmt); - fatalv(zFmt, va); + fatalv__base(zFile, line, zFmt, va); va_end(va); } @@ -2063,8 +2459,8 @@ static void usage(int isErr){ arg("-o|--outfile FILE","Send output to FILE (default=- (stdout)).\n" GAP "Because arguments are processed in order, this should\n" GAP "normally be given before -f."); - arg("-f|--file FILE","Read input from FILE (default=- (stdin)).\n" - " All non-flag arguments are assumed to be the input files."); + arg("-f|--file FILE","Process FILE (default=- (stdin)).\n" + GAP "All non-flag arguments are assumed to be the input files."); arg("-DXYZ[=value]","Define XYZ to the given value (default=1)."); arg("-UXYZ","Undefine all defines matching glob XYZ."); arg("-IXYZ","Add dir XYZ to the " CMPP_DEFAULT_DELIM "include path."); @@ -2074,15 +2470,15 @@ static void usage(int isErr){ GAP "Maybe it should be. Or maybe we need a new flag for that."); arg("-d|--delimiter VALUE", "Set keyword delimiter to VALUE " "(default=" CMPP_DEFAULT_DELIM ")."); - arg("--@-policy retain|elide|error|off", + arg("--@policy retain|elide|error|off", "Specifies how to handle @tokens@ (default=off).\n" GAP "off = do not look for @tokens@\n" GAP "retain = parse @tokens@ and retain any undefined ones\n" GAP "elide = parse @tokens@ and elide any undefined ones\n" - GAP "error = parse @tokens@ and error for any undefined ones" + GAP "error = parse @tokens@ and error out for any undefined ones" ); - arg("-@", "Equivalent to --@-policy=error."); - arg("-no-@", "Equivalent to --@-policy=off (the default)."); + arg("-@", "Equivalent to --@policy=error."); + arg("-no-@", "Equivalent to --@policy=off (the default)."); arg("--sql-trace", "Send a trace of all SQL to stderr."); arg("--sql-trace-x", "Like --sql-trace but expand all bound values in the SQL."); @@ -2092,11 +2488,19 @@ static void usage(int isErr){ arg("--no-chomp-F", "Disable --chomp-F (default)."); #undef arg #undef GAP - fputs("\n",fOut); + fputs("\nFlags which require a value accept either " + "--flag=value or --flag value.\n\n",fOut); } +/* +** Expects that *ndx points to the current argv entry and that it is a +** flag which expects a value. This function checks for --flag=val and +** (--flag val) forms. If a value is found then *ndx is adjusted (if +** needed) to point to the next argument after the value and *zVal is +** pointed to the value. If no value is found then it fails fatally. +*/ static void get_flag_val(int argc, char const * const * argv, int * ndx, - char const **zVal){ + char const **zVal){ char const * zEq = strchr(argv[*ndx], '='); if( zEq ){ *zVal = zEq+1; @@ -2183,7 +2587,7 @@ int main(int argc, char const * const * argv){ ++zArg; if(!*zArg) fatal("Missing key for -D"); DOIT { - db_define_add(zArg); + db_define_add(zArg, 0); } }else if('F'==*zArg){ ++zArg; @@ -2231,7 +2635,7 @@ int main(int argc, char const * const * argv){ g.flags.atPolicy = negate ? AT_OFF : AT_DEFAULT; } } - ISFLAG("@-policy"){ + ISFLAG("@policy"){ AtPolicy aup; ARGVAL; aup = AtPolicy_fromStr(zVal, 1); @@ -2277,6 +2681,13 @@ int main(int argc, char const * const * argv){ if(!g.delim.n) fatal("Keyword delimiter may not be empty."); } } + ISFLAG2("dd", "dump-defines"){ + DOIT { + FILE * const fp = stderr; + fprintf(fp, "All %sdefine entries:\n", g.delim.z); + cmpp_dump_defines(fp, 1); + } + } else{ fatal("Unhandled flag: %s", argv[i]); } @@ -2294,6 +2705,6 @@ int main(int argc, char const * const * argv){ } } end: - FileWrapper_close(&g.out); + g_cleanup(0); return rc ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/ext/wasm/demo-worker1-promiser.c-pp.js b/ext/wasm/demo-worker1-promiser.c-pp.js index bd12035d15..c129e21281 100644 --- a/ext/wasm/demo-worker1-promiser.c-pp.js +++ b/ext/wasm/demo-worker1-promiser.c-pp.js @@ -40,7 +40,7 @@ delete globalThis.sqlite3Worker1Promiser; }; const promiserConfig = { -//#ifnot target:es6-module +//#if not target:es6-module /** The v1 interfaces uses an onready function. The v2 interface optionally accepts one but does not require it. If provided, it is called _before_ diff --git a/ext/wasm/mkwasmbuilds.c b/ext/wasm/mkwasmbuilds.c index 0d4f0b515f..f16ae92324 100644 --- a/ext/wasm/mkwasmbuilds.c +++ b/ext/wasm/mkwasmbuilds.c @@ -302,7 +302,7 @@ const BuildDefs oBuildDefs = { .zEmo = "🛼64", .zBaseName = "speedtest1-64bit", .zDotWasm = 0, - .zCmppD = "-D64bit", + .zCmppD = 0, .zEmcc = "$(emcc.speedtest1)" " $(emcc.speedtest1.common)" @@ -669,7 +669,7 @@ static void mk_pre_post(char const *zBuildName, BuildDef const * pB){ "%s," "$(extern-post-js.in.js)," "$(extern-post-js.%s.js)," - "$(c-pp.D.%s) --@-policy=error -Dsqlite3.wasm=%s.wasm" + "$(c-pp.D.%s) --@policy=error -Dsqlite3.wasm=%s.wasm" "))", zBuildName, zBuildName, zBuildName, zBaseName); diff --git a/manifest b/manifest index 2ac6bf55fa..46c0d40245 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\scorrupting\sthe\sfts5\sindex\sif\sa\svalue\sthat\swas\sinserted\svia\ssqlite3_bind_blob()\sin\sa\snon-utf8\sdb\sis\sdeleted. -D 2025-10-09T14:50:27.036 +C Update\sc-pp.c\sand\srename\sit\sto\sc-pp-lite.c\sto\smatch\sits\snew\supstream\sname.\sAdapt\sthe\sJS\sfiles\sand\smkwasmbuilds.c\sfor\sc-pp\ssyntactic\schanges. +D 2025-10-09T15:28:59.026 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -575,7 +575,7 @@ F ext/session/sqlite3session.c b3de195ce668cace9b324599bf6255a70290cbfb5451e826e F ext/session/sqlite3session.h 7404723606074fcb2afdc6b72c206072cdb2b7d8ba097ca1559174a80bc26f7a F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c -F ext/wasm/GNUmakefile 8c75ec005c07c131df362596addf63a95db275d9eadc7d7b3f48b45fa331342b +F ext/wasm/GNUmakefile 0bc887cb6ed6e197359f9c8838913d44fe6077b1c3ff3aafc770daf9577f4dc0 F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a F ext/wasm/README.md 66ace67ae98a45e4116f2ca5425b716887bcee4d64febee804ff6398e1ae9ec7 F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff @@ -588,26 +588,26 @@ F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras cb4fa8842c875b6ee99381523792975 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md f4c0d67caaee21a77b8938c30b5f79667bfc9d0c95d01b51df77ea35ee773884 -F ext/wasm/api/extern-post-js.c-pp.js eaa41ddccf70c3bb3b953e4edd1c0cb82e695166d86ae8dc36b59b09631e2741 +F ext/wasm/api/extern-post-js.c-pp.js 205f55aacfc62c580985db5c790300779de3876a76a5c7e1bfb13e71c8b4506b F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41 F ext/wasm/api/post-js-footer.js 5bd7170b5e8ce7b62102702bbcf47ef7b3b49cd56ed40c043fd990aa715b74ee F ext/wasm/api/post-js-header.js 79d078aec33d93b640a19c574b504d88bb2446432f38e2fbb3bb8e36da436e70 F ext/wasm/api/pre-js.c-pp.js a876c6399dff29b6fe9e434036beb89889164cc872334e184291723ecc7cb072 F ext/wasm/api/sqlite3-api-cleanup.js a3d6b9e449aefbb8bba283c2ba9477e2333a0eeb94a7a26b5bf952736f65a6dd F ext/wasm/api/sqlite3-api-glue.c-pp.js 12f5b36775fab1e7bf5385689fded2b2a9f77360562515e9849acb5e66602e2d -F ext/wasm/api/sqlite3-api-oo1.c-pp.js db4c8ebb03bac60db32ce03f8c615b00f4e4ad53e7d5de5e63d2780cba052caa +F ext/wasm/api/sqlite3-api-oo1.c-pp.js 31dbfd470c91ffd96d77399b749bab6b69e3ba9074188833f97ac13f087cf07b F ext/wasm/api/sqlite3-api-prologue.js b5a55ae74efcdcd0aa6a143d59e34137e43ae732f02b563dcab22d735f1599a4 -F ext/wasm/api/sqlite3-api-worker1.c-pp.js 760191cd13416e6f5adfd9fcc8a97fed5645c9e0a5fbac213a2d4ce2d79a4334 +F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938 F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966 F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d -F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js f78ba02f7855355513f271d0955a01a7f86a2a8884c278053f578662b2a3b268 -F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 17f172182ff2fd4ad5dc2c2d79aef339b307cb2fa345b0521864baf20262fe6e +F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 26cb41d5a62f46a106b6371eb00fef02de3cdbfaa51338ba087a45f53028e0d0 +F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 418c33fe284739564daab3c7a7a88882fdd3c99137497900f98eddec1e409af5 F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 9097074724172e31e56ce20ccd7482259cf72a76124213cbc9469d757676da86 F ext/wasm/api/sqlite3-wasm.c 929d4c5619a321fa2fb9ef921ec8e4ccfdac27745df543062b267f9f928de3a7 -F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js a72cb1f2a4170e79a595a8d420fac02e4eec8e2a65aefa9eed081b30845c44c7 -F ext/wasm/api/sqlite3-worker1.c-pp.js fa330c5c9e14277ce85e65c0fdb5d28ee983fcf664d29e23451ac184c1771ec9 -F ext/wasm/c-pp.c 7396bfe57800d54a560702b9379bb9ede2db94af7eabf919b476e8b1d92a2d2f +F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bda1c75bd674a92a0e27cc2f3d46dbbf21e422413f8046814515a0bd7409328a +F ext/wasm/api/sqlite3-worker1.c-pp.js 802d69ead8c38dc1be52c83afbfc77e757da8a91a2e159e7ed3ecda8b8dba2e7 +F ext/wasm/c-pp-lite.c 16250600fe18723e0cee4227161101ccb54f10fa6136bd208b479cf596df0f75 w ext/wasm/c-pp.c F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f @@ -619,7 +619,7 @@ F ext/wasm/demo-123.js c7b3cca50c55841c381a9ca4f9396e5bbdc6114273d0b10a43e378e32 F ext/wasm/demo-jsstorage.html 409c4be4af5f207fb2877160724b91b33ea36a3cd8c204e8da1acb828ffe588e F ext/wasm/demo-jsstorage.js 42131ddfa18e817d0e39ac63745e9ea31553980a5ebd2222e04d4fac60c19837 F ext/wasm/demo-worker1-promiser.c-pp.html 635cf90685805e21772a5f7a35d1ace80f98a9ef7c42ff04d7a125ddca7e5db8 -F ext/wasm/demo-worker1-promiser.c-pp.js 165ca6c6b41876afc6cbcd8a1610410694f26fa27a47656d8edbb456170c22c3 +F ext/wasm/demo-worker1-promiser.c-pp.js f40ec65810048e368896be71461028bd10de01e24277208c59266edf23bb9f52 F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d F ext/wasm/demo-worker1.js 08720227e98fa5b44761cf6e219269cee3e9dd0421d8d91459535da776950314 F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f @@ -631,7 +631,7 @@ F ext/wasm/index.html 54e27db740695ab2cb296e02d42c4c66b3f11b65797340d19fa6590f5b F ext/wasm/jaccwabyt/jaccwabyt.js bbac67bc7a79dca34afe6215fd16b27768d84e22273507206f888c117e2ede7d F ext/wasm/jaccwabyt/jaccwabyt.md 167fc0b624c9bc2c477846e336de9403842d81b1a24fc4d3b24317cb9eba734f F ext/wasm/mkdist.sh 29f8a37a7aba41fa5df8e89b1fab02b83b35c43473c5cf808584872e022514b8 x -F ext/wasm/mkwasmbuilds.c 2640139178e0961d7933842e637ed6c21cde9506f59fa86194feb9c1877ce19d +F ext/wasm/mkwasmbuilds.c 0216fd7273314b9e433ca17d7692cce844a362b31a192e44d31fc13cdb27ad8c F ext/wasm/module-symbols.html e54f42112e0aac2a31f850ab33e7f2630a2ea4f63496f484a12469a2501e07e2 F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96 F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63 @@ -2169,8 +2169,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P b3741f1101d25eded57a62b4967ec1bcce532dc9937c4b7b74cb689861efb442 -R 412e48a669e146967bbdc02f8b0696ba -U dan -Z 30d36738aec1726fc6292939ee2c4886 +P 8bf26c956e199762d55c159392c2e3813a9e12b914d3ca33000bf332cd946cb0 +R de5988f0e29caae569a0c585e2f64ade +U stephan +Z a33d2ee1fb877041e46e669ea48ada3b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9c10c755fe..059e9900ad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8bf26c956e199762d55c159392c2e3813a9e12b914d3ca33000bf332cd946cb0 +bb13e46ddfcd1d3ca73845430d9a91c0ea3913762d39bbd94127783d77e4f63b