]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Update c-pp.c and rename it to c-pp-lite.c to match its new upstream name. Adapt...
authorstephan <stephan@noemail.net>
Thu, 9 Oct 2025 15:28:59 +0000 (15:28 +0000)
committerstephan <stephan@noemail.net>
Thu, 9 Oct 2025 15:28:59 +0000 (15:28 +0000)
FossilOrigin-Name: bb13e46ddfcd1d3ca73845430d9a91c0ea3913762d39bbd94127783d77e4f63b

13 files changed:
ext/wasm/GNUmakefile
ext/wasm/api/extern-post-js.c-pp.js
ext/wasm/api/sqlite3-api-oo1.c-pp.js
ext/wasm/api/sqlite3-api-worker1.c-pp.js
ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js
ext/wasm/api/sqlite3-vfs-opfs.c-pp.js
ext/wasm/api/sqlite3-worker1-promiser.c-pp.js
ext/wasm/api/sqlite3-worker1.c-pp.js
ext/wasm/c-pp-lite.c [moved from ext/wasm/c-pp.c with 62% similarity]
ext/wasm/demo-worker1-promiser.c-pp.js
ext/wasm/mkwasmbuilds.c
manifest
manifest.uuid

index a0ea5ec9730c7c2f2a66cb652600eee34777d281..45f054e03a912eac36409e09848171305b733197 100644 (file)
@@ -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
index 0bb511d6398d3419648842648ba1f070c6a6a05d..606e02ae28238deeaf3653af2e6b11f08682a1a0 100644 (file)
@@ -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.
index 84a388302f49694058ff777b94e356c8c0a51d12..8c2f35e67789a8ba99008a6e8bffb6a16f6b6afe 100644 (file)
@@ -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
index 55ad16185cea7673741bf095363f4b933d21cb1b..25262abf85d11596a6551267ed5257ae61dc7da2 100644 (file)
@@ -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
index 5a59de9c9ab9dcc63edf8e73c3f78fe34143e904..69be338b0c16c5afeda27d42360200585c107df2 100644 (file)
@@ -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);
index a57efb4ef2429d0fbeb95f8980705c1d6076eda8..2b636460dd7d8b55942afeadde504e340b481306 100644 (file)
@@ -1,4 +1,4 @@
-//#ifnot target:node
+//#if not target:node
 /*
   2022-09-18
 
index db10ca0f7ddbc741f21686547a1dfcecd4f7ed79..1a09bf9a6ad79b463256de5dd1c21226cf921e61 100644 (file)
@@ -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
index 32bb370e39c154bcc06d041dbf794e9c312ac9c0..036c4c6ea3ceb4d86fe3f06d9c8926e0cd6cfb43 100644 (file)
@@ -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
similarity index 62%
rename from ext/wasm/c-pp.c
rename to ext/wasm/c-pp-lite.c
index fecffca7f6540ea4c2399413a9b424fdbb45b105..53a3a28cb3933e5baf99e8abb9a820abe8c9899c 100644 (file)
 ** 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
 #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 : ""));
+}
+
+
+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 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 : "<NULL>"));
-  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,18 +1266,85 @@ 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);
 }
 
-static void cmpp_atexit(void){
-#define E(N,S) sqlite3_finalize(g.stmt.N);
+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){
+  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);
+  }
+}
+
 /*
 ** sqlite3 UDF which returns true if its argument refers to an
 ** accessible file, else false.
@@ -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 && zPos<zEnd ; ++zPos) {
-    if( chEq==*zPos ){
-      if( zEq ) *zEq = zPos;
-      ++zPos;
-      *len = (unsigned)(zEnd - zPos);
-      return zPos;
+static void cmpp_t_out_expand(CmppTokenizer * const t,
+                              unsigned char const * zFrom,
+                              unsigned int n);
+
+static inline int cmpp__isspace(int ch){
+  return ' '==ch || '\n'==ch || '\t'==ch || '\r'==ch;
+}
+
+static inline void cmpp__skip_space_c( unsigned char const **p,
+                                       unsigned char const *zEnd ){
+  unsigned char const * z = *p;
+  while( z<zEnd && cmpp__isspace(*z) ) ++z;
+  *p = z;
+}
+
+/**
+   Scan [t->zPos,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 && zPos<zEnd ; ++zPos) {
+    if( chEq==*zPos ){
+      if( cmpp_kvp_op_none==opPolicy ){
+        fatal("Illegal operator in key: %s", zKey);
+      }
+      p->op = 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;
+    }
+  }
+  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(z<t->zEnd
-        && 0==(isDelim = cmpp_is_delim(t->zBegin, t->zEnd, z))){
-    ++z;
-  }
-  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 ; z<t->zEnd && 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));
+  if( !CmppTokenizer__delim_search(t) ){
+    return 0;
   }
-  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;
-
-    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);
+  /* 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);
+  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;
     }
-    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;
+      break;
     }
-    t->args.argv[argc++] = zKwd;
-    for( zz = zKwd; *zz; ++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;
-        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);
-    }
-    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{
+        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;
+  }
+  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]));
+    }
   }
-  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);
+  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;
-  CmppParseState tmpState = TS_Start;
-  /**
-     TT_If: accept args:
+  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;
+}
 
-     - "not" = negates the operation
+#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
 
-     - "defined" == is-defined op
-  */
-  int bCheckDefined = 0;
-  int bNot = 0;
-  char const * zKey = 0;
-  char const *zEq = 0;
+//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 && i<t->args.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;
+  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,9 +2191,22 @@ 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. */
@@ -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))
             ? "<stdin>"
             : 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;
 }
index bd12035d153fc8c0d22e3b43533cfa909c386c76..c129e212818f5bef0818d2a67a01eba64f2206dd 100644 (file)
@@ -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_
index 0d4f0b515f77138336d0dd45e8d1dbe4b2d0b306..f16ae92324be7b6429cf22f5c45439af31de7cae 100644 (file)
@@ -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);
index 2ac6bf55fa00efa3997187648894e75e7235e88e..46c0d40245131a13ef36ea42d630c321f3a5a9c4 100644 (file)
--- 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.
index 9c10c755fea0c54d7b5c6a3194f98ce684d25242..059e9900adb41fc46721b923a53dd04089da4112 100644 (file)
@@ -1 +1 @@
-8bf26c956e199762d55c159392c2e3813a9e12b914d3ca33000bf332cd946cb0
+bb13e46ddfcd1d3ca73845430d9a91c0ea3913762d39bbd94127783d77e4f63b