]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Latest upstream ext/wasm/libcmpp.c, primarily to make it filcc-legal (only the final... master
authorstephan <stephan@noemail.net>
Tue, 21 Jul 2026 10:44:28 +0000 (10:44 +0000)
committerstephan <stephan@noemail.net>
Tue, 21 Jul 2026 10:44:28 +0000 (10:44 +0000)
FossilOrigin-Name: 06f3bbc5bddc2e763042f8b487a3deedf47c54b7fa785ba6fd6abd524b4cbe3a

ext/wasm/libcmpp.c
manifest
manifest.uuid

index 717f56ea93cfdf412c604222c0d9d7e22fa86005..2450b7e3974533d818e010fbb625217bee0919f1 100644 (file)
 
   ./c-pp -I. -I./src -Dsrcdir=./src -Dsed=/usr/bin/sed -o libcmpp.h ./tool/libcmpp.c-pp.h -o libcmpp.c ./tool/libcmpp.c-pp.c
 
 
   ./c-pp -I. -I./src -Dsrcdir=./src -Dsed=/usr/bin/sed -o libcmpp.h ./tool/libcmpp.c-pp.h -o libcmpp.c ./tool/libcmpp.c-pp.c
 
-  with libcmpp 2.0.x c02f3e3e2d3f3573a9a33c1474c2e52fc48e52c70730404a90d0ae51517e7d37 @ 2026-03-08 14:50:35.123 UTC
+  with libcmpp 2.0.x a53a63923506aba565e91571c0f6bdb678e7dcc05dc9f7e281a17e68bf95aa46 @ 2026-07-21 10:38:56.344 UTC
 */
 #define CMPP_PACKAGE_NAME "libcmpp"
 #define CMPP_LIB_VERSION "2.0.x"
 */
 #define CMPP_PACKAGE_NAME "libcmpp"
 #define CMPP_LIB_VERSION "2.0.x"
-#define CMPP_LIB_VERSION_HASH "c02f3e3e2d3f3573a9a33c1474c2e52fc48e52c70730404a90d0ae51517e7d37"
-#define CMPP_LIB_VERSION_TIMESTAMP "2026-03-08 14:50:35.123 UTC"
-#define CMPP_LIB_CONFIG_TIMESTAMP "2026-03-08 15:32 GMT"
+#define CMPP_LIB_VERSION_HASH "a53a63923506aba565e91571c0f6bdb678e7dcc05dc9f7e281a17e68bf95aa46"
+#define CMPP_LIB_VERSION_TIMESTAMP "2026-07-21 10:38:56.344 UTC"
+#define CMPP_LIB_CONFIG_TIMESTAMP "2026-07-21 10:39 GMT"
 #define CMPP_VERSION CMPP_LIB_VERSION " " CMPP_LIB_VERSION_HASH " @ " CMPP_LIB_VERSION_TIMESTAMP
 #define CMPP_PLATFORM_EXT_DLL ".so"
 #define CMPP_MODULE_PATH ".:/usr/local/lib/cmpp"
 #define CMPP_VERSION CMPP_LIB_VERSION " " CMPP_LIB_VERSION_HASH " @ " CMPP_LIB_VERSION_TIMESTAMP
 #define CMPP_PLATFORM_EXT_DLL ".so"
 #define CMPP_MODULE_PATH ".:/usr/local/lib/cmpp"
 ** at build-time.
 **
 ** This API is presented as a library but was evolved from a
 ** at build-time.
 **
 ** This API is presented as a library but was evolved from a
-** monolithic app. Thus is library interface is likely still missing
+** monolithic app. Thus the library interface is likely still missing
 ** some pieces needed to make it more readily usable as a library.
 **
 ** Author(s):
 **
 ** - Stephan Beal <https://wanderinghorse.net/home/stephan/>
 **
 ** some pieces needed to make it more readily usable as a library.
 **
 ** Author(s):
 **
 ** - Stephan Beal <https://wanderinghorse.net/home/stephan/>
 **
-** Canonical homes:
+** Canonical home:
 **
 **
-** - https://fossil.wanderinghorse.net/r/c-pp
-** - https://sqlite.org/src/file/ext/wasm/c-pp-lite.c
-**
-** With the former hosting this app's SCM and the latter being the
-** original deployment of c-pp.c, from which this library
-** evolved. SQLite uses a "lite" version of c-pp, whereas _this_ copy
-** is its much-heavier-weight fork.
+** - <https://fossil.wanderinghorse.net/r/c-pp>
 */
 
 #if defined(CMPP_HAVE_AUTOCONFIG_H)
 */
 
 #if defined(CMPP_HAVE_AUTOCONFIG_H)
@@ -404,8 +398,9 @@ typedef struct cmpp_ctor_cfg cmpp_ctor_cfg;
    it failed). In either case, the caller must eventually pass *pp to
    cmpp_dtor() to free it.
 
    it failed). In either case, the caller must eventually pass *pp to
    cmpp_dtor() to free it.
 
-   If the library is built with the symbol CMPP_CTOR_INSTANCE_INIT
-   defined, it must refer to a function with this signature:
+   If the library is built with the preprocessor symbol
+   CMPP_CTOR_INSTANCE_INIT defined, it must refer to a function with
+   this signature:
 
    int CMPP_CTOR_INSTANCE_INIT(cmpp *);
 
 
    int CMPP_CTOR_INSTANCE_INIT(cmpp *);
 
@@ -576,13 +571,9 @@ CMPP_EXPORT int cmpp_undef(cmpp *pp, const char * zKey,
                            unsigned int *nRemoved);
 
 /**
                            unsigned int *nRemoved);
 
 /**
-   This works similarly to cmpp_define_v2() except that:
-
-   - It does not permit its zKey argument to contain the value
-     part like that function does.
-
-   - The new define "shadows", rather than overwrites, an existing
-     define with the same name.
+   This works similarly to cmpp_define_v2() except that the new define
+   "shadows", rather than overwrites, an existing define with the same
+   name.
 
    All APIs which look up define keys will get the value of the shadow
    define.  The shadow can be uninstalled with cmpp_define_unshadow(),
 
    All APIs which look up define keys will get the value of the shadow
    define.  The shadow can be uninstalled with cmpp_define_unshadow(),
@@ -590,9 +581,9 @@ CMPP_EXPORT int cmpp_undef(cmpp *pp, const char * zKey,
    should be called one time for each call to this one, passing the
    same key to each call.  A given key may be shadowed any number of
    times by this routine. Each one saves the internal ID of the shadow
    should be called one time for each call to this one, passing the
    same key to each call.  A given key may be shadowed any number of
    times by this routine. Each one saves the internal ID of the shadow
-   into *pId (and pId must not be NULL). That value must be passed to
+   into *pId (which must not be NULL). That value must be passed to
    cmpp_define_unshadow() to ensure that the "shadow stack" stays
    cmpp_define_unshadow() to ensure that the "shadow stack" stays
-   balanced in the face of certain error-handling paths.
+   balanced.
 
    cmpp_undef() will _not_ undefine an entry added through this
    interface.
 
    cmpp_undef() will _not_ undefine an entry added through this
    interface.
@@ -610,9 +601,9 @@ CMPP_EXPORT int cmpp_define_shadow(cmpp *pp, char const *zKey,
                                    int64_t * pId);
 
 /**
                                    int64_t * pId);
 
 /**
-   Removes the most shadow define matching the zKey and id values
-   which where previously passed to cmpp_define_shadow().  It is not
-   an error if no match is found, in which case this function has no
+   Removes the shadow define matching the zKey and id values which
+   where previously passed to cmpp_define_shadow().  It is not an
+   error if no match is found, in which case this function has no
    visible side-effects.
 
    Unlike cmpp_undef(), zKey is matched precisely, not against a glob.
    visible side-effects.
 
    Unlike cmpp_undef(), zKey is matched precisely, not against a glob.
@@ -930,8 +921,8 @@ CMPP_EXPORT bool cmpp_is_safemode(cmpp const * pp);
    Starts a new SAVEPOINT in the database. Returns non-0, and updates
    pp's persistent error state, on failure.
 
    Starts a new SAVEPOINT in the database. Returns non-0, and updates
    pp's persistent error state, on failure.
 
-   If this returns 0, the caller is obligated to later call either
-   cmpp_sp_commit() or cmpp_sp_rollback() later.
+   If this returns 0, the caller is obligated to later pass pp to one
+   of cmpp_sp_commit() or cmpp_sp_rollback().
 */
 CMPP_EXPORT int cmpp_sp_begin(cmpp *pp);
 
 */
 CMPP_EXPORT int cmpp_sp_begin(cmpp *pp);
 
@@ -1033,7 +1024,7 @@ CMPP_EXPORT int cmpp_stream(cmpp_input_f inF, void * inState,
    and *nOut to the number of bytes read (which will be fewer than are
    allocated). It guarantees that on success it NUL-terminates the
    buffer at one byte after the returned size, with one exception: if
    and *nOut to the number of bytes read (which will be fewer than are
    allocated). It guarantees that on success it NUL-terminates the
    buffer at one byte after the returned size, with one exception: if
-   the string has no input, both *pOut and *nOut will be set to 0.
+   the stream has no input, both *pOut and *nOut will be set to 0.
 
    On error it returns whatever code xIn() returns.
 */
 
    On error it returns whatever code xIn() returns.
 */
@@ -1173,7 +1164,7 @@ CMPP_EXPORT void cmpp_outputer_cleanup_f_FILE(cmpp_outputer *self);
 /**
    Sets pp's current directive delimiter to a copy of the
    NUL-terminated zDelim. The delimiter is the sequence which starts
 /**
    Sets pp's current directive delimiter to a copy of the
    NUL-terminated zDelim. The delimiter is the sequence which starts
-   line and distinguishes cmpp directives from other input, in the
+   lines and distinguishes cmpp directives from other input, in the
    same way that C preprocessors use '#' as a delimiter.
 
    If zDelim is NULL then the default delimiter is used. The default
    same way that C preprocessors use '#' as a delimiter.
 
    If zDelim is NULL then the default delimiter is used. The default
@@ -1193,9 +1184,9 @@ CMPP_EXPORT void cmpp_outputer_cleanup_f_FILE(cmpp_outputer *self);
    the delimiter.
 
    Returns 0 on success. Returns non-0 if called when the delimiter
    the delimiter.
 
    Returns 0 on success. Returns non-0 if called when the delimiter
-   stack is empty, if it cannot copy the string or zDelim is deemed
+   stack is empty, if it cannot copy the string, or zDelim is deemed
    unsuitable for use as a delimiter. Calling this when the stack is
    unsuitable for use as a delimiter. Calling this when the stack is
-   empty represents a serious API misuse (indicating that
+   empty represents a serious API misuse (possibly indicating that
    cmpp_delimiter_pop() was used out of scope) and will trigger an
    assert() in debug builds.  Except for that last case, errors from
    this function are recoverable (see cmpp_err_set()).
    cmpp_delimiter_pop() was used out of scope) and will trigger an
    assert() in debug builds.  Except for that last case, errors from
    this function are recoverable (see cmpp_err_set()).
@@ -1209,8 +1200,9 @@ CMPP_EXPORT int cmpp_delimiter_set(cmpp *pp, char const *zDelim);
 
    If, by some odd usage constellation, this is called after an
    allocation of the delimiter stack has failed, this will set *zDelim
 
    If, by some odd usage constellation, this is called after an
    allocation of the delimiter stack has failed, this will set *zDelim
-   to the compile-time-default delimiter. That "cannot happen" in normal use because such a failure
-   would have been reacted to and this would not be called.
+   to the compile-time-default delimiter. That "cannot happen" in
+   normal use because such a failure would have been reacted to and
+   this would not be called.
 */
 CMPP_EXPORT void cmpp_delimiter_get(cmpp const *pp, char const **zDelim);
 
 */
 CMPP_EXPORT void cmpp_delimiter_get(cmpp const *pp, char const **zDelim);
 
@@ -1244,8 +1236,8 @@ CMPP_EXPORT bool cmpp_chomp(unsigned char * z, cmpp_size_t * n);
 
 /**
    A basic memory buffer class. This is primarily used with
 
 /**
    A basic memory buffer class. This is primarily used with
-   cmpp_outputer_b to capture arbitrary output for later use.
-   It's also used for incrementally creating dynamic strings.
+   cmpp_outputer_b to capture arbitrary output for later use and for
+   incrementally creating dynamic strings.
 
    TODO: add the heuristic that an nAlloc of 0 with a non-NULL z
    refers to externally-owned memory. This would change the
 
    TODO: add the heuristic that an nAlloc of 0 with a non-NULL z
    refers to externally-owned memory. This would change the
@@ -1262,6 +1254,9 @@ struct cmpp_b {
 
      If this pointer is taken away from this object then it must
      eventually be passed to cmpp_mfree().
 
      If this pointer is taken away from this object then it must
      eventually be passed to cmpp_mfree().
+
+     This class's APIs strive to keep valid buffers NUL-terminated at
+     byte this->z[this->n].
   */
   unsigned char * z;
   /**
   */
   unsigned char * z;
   /**
@@ -1330,8 +1325,9 @@ CMPP_EXPORT void cmpp_b_swap(cmpp_b * l, cmpp_b * r);
 CMPP_EXPORT int cmpp_b_reserve(cmpp_b *s, cmpp_size_t n);
 
 /**
 CMPP_EXPORT int cmpp_b_reserve(cmpp_b *s, cmpp_size_t n);
 
 /**
-   Works just like cmpp_b_reserve() but on allocation error it
-   updates pp's error state.
+   Works just like cmpp_b_reserve() but on allocation error it updates
+   pp's error state. If called when pp has error state, that code is
+   returned and this function has no side-effects.
 */
 CMPP_EXPORT int cmpp_b_reserve3(cmpp * pp, cmpp_b * os, cmpp_size_t n);
 
 */
 CMPP_EXPORT int cmpp_b_reserve3(cmpp * pp, cmpp_b * os, cmpp_size_t n);
 
@@ -1346,8 +1342,9 @@ CMPP_EXPORT int cmpp_b_append(cmpp_b * os, void const *src,
                               cmpp_size_t n);
 
 /**
                               cmpp_size_t n);
 
 /**
-   Works just like cmpp_b_append() but on allocation error it
-   updates pp's error state.
+   Works just like cmpp_b_append() but on allocation error it updates
+   pp's error state. If called when pp has error state, that code is
+   returned and this function has no side-effects.
 */
 CMPP_EXPORT int cmpp_b_append4(cmpp * pp,
                                cmpp_b * os,
 */
 CMPP_EXPORT int cmpp_b_append4(cmpp * pp,
                                cmpp_b * os,
@@ -1448,10 +1445,10 @@ enum cmpp_tt {
 
    - GroupParen, GroupBrace, GroupSquiggly: (), [], and {}
 
 
    - GroupParen, GroupBrace, GroupSquiggly: (), [], and {}
 
-   - All which start with D_ are directives. D_Line is a transitional
-     state between "unparsed" and another D_... value.
+   - RawLine: the token contains the raw, unparsed arguments line for
+     a directive.
 */
 */
-#define cmpp_tt_map(E)    \
+#define cmpp_tt_map(E)          \
   E(None,         0)            \
   E(RawLine,      0)            \
   E(Unknown,      0)            \
   E(None,         0)            \
   E(RawLine,      0)            \
   E(Unknown,      0)            \
@@ -1496,7 +1493,7 @@ typedef enum cmpp_tt cmpp_tt;
 
 /**
    For all of the cmpp_tt enum entries, returns a string form of the
 
 /**
    For all of the cmpp_tt enum entries, returns a string form of the
-   enum entry name, e.g. "cmpp_TT_D_If". Returns NULL for any other
+   enum entry name, e.g. "cmpp_TT_String". Returns NULL for any other
    values
 */
 CMPP_EXPORT char const * cmpp_tt_cstr(int tt);
    values
 */
 CMPP_EXPORT char const * cmpp_tt_cstr(int tt);
@@ -1624,14 +1621,13 @@ enum cmpp_d_e {
 
   /**
      Indicates that the direction should not be available if the cmpp
 
   /**
      Indicates that the direction should not be available if the cmpp
-     instance is configured with any of the cmpp_ctor_F_SAFEMODE flags.
-     All directives when do any of the following are obligated to
-     set this flag:
+     instance is configured with the cmpp_ctor_F_SAFEMODE flag.  All
+     directives which do any of the following are obligated to set
+     this flag:
 
      - Filesystem or network access.
      - Invoking external processes.
 
      - Filesystem or network access.
      - Invoking external processes.
-
-     Or anything else which might be deamed "security-relevant".
+     - Anything else which might be deamed "security-relevant".
 
      When registering a directive which has both opener and closer
      implementations, it is sufficient to set this only on the opener.
 
      When registering a directive which has both opener and closer
      implementations, it is sufficient to set this only on the opener.
@@ -1653,7 +1649,7 @@ enum cmpp_d_e {
   cmpp_d_F_CALL_ONLY        = 0x08,
   /**
      Indicates that the directive is incapable of working in a [call]
   cmpp_d_F_CALL_ONLY        = 0x08,
   /**
      Indicates that the directive is incapable of working in a [call]
-     context and an error should be trigger if it is. _Most_
+     context and an error should be triggered if it is. _Most_
      directives which have a closing directive should have this
      flag. The exceptions are directives which only conditionally use
      a closing directive, like #query.
      directives which have a closing directive should have this
      flag. The exceptions are directives which only conditionally use
      a closing directive, like #query.
@@ -1701,15 +1697,10 @@ typedef void (*cmpp_finalizer_f)(void *);
 
 /**
    State specific to concrete cmpp_d implementations.
 
 /**
    State specific to concrete cmpp_d implementations.
-
-   TODO: move this, except for the state pointer, out of cmpp_d
-   so that directives cannot invoke these callbacks directly. Getting
-   that to work requires moving the builtin directives into the
-   dynamic directives list.
 */
 struct cmpp_d_impl {
   /**
 */
 struct cmpp_d_impl {
   /**
-     Callback func. If any API other othan cmpp_dx_process() invokes
+     Callback func. If any API other than cmpp_dx_process() invokes
      this, behavior is undefined.
   */
   cmpp_dx_f callback;
      this, behavior is undefined.
   */
   cmpp_dx_f callback;
@@ -2525,7 +2516,7 @@ CMPP_EXPORT char const * cmpp_dx_delim(cmpp_dx const *dx);
    This transfers ownership of the buffer to the caller, who is
    obligated to eventually do ONE of the following:
 
    This transfers ownership of the buffer to the caller, who is
    obligated to eventually do ONE of the following:
 
-   - Pass it to cmpp_b_return() with the same dx argument.
+   - Pass it to cmpp_b_return() with the same pp argument.
 
    - Pass it to cmpp_b_clear() then cmpp_mfree().
 
 
    - Pass it to cmpp_b_clear() then cmpp_mfree().
 
@@ -2538,19 +2529,19 @@ CMPP_EXPORT char const * cmpp_dx_delim(cmpp_dx const *dx);
    about the buffer usage. e.g. argument-conversion buffers are
    normally small but block content buffers can be arbitrarily large.
 */
    about the buffer usage. e.g. argument-conversion buffers are
    normally small but block content buffers can be arbitrarily large.
 */
-CMPP_EXPORT cmpp_b * cmpp_b_borrow(cmpp *dx);
+CMPP_EXPORT cmpp_b * cmpp_b_borrow(cmpp *pp);
 
 /**
    Returns a buffer borrowed from cmpp_b_borrow(), transferring
    ownership back to pp. Passing a non-NULL b which was not returned
 
 /**
    Returns a buffer borrowed from cmpp_b_borrow(), transferring
    ownership back to pp. Passing a non-NULL b which was not returned
-   by cmpp_b_borrow() invoked undefined behavior (possibly delayed
+   by cmpp_b_borrow() invokes undefined behavior (possibly delayed
    until the list is cleaned up). To simplify usage, b may be NULL.
 
    After calling this, b must be considered "freed" - it must not be
    used again. This function is free (as it were) to immediately free
    the object's memory instead of recycling it.
 */
    until the list is cleaned up). To simplify usage, b may be NULL.
 
    After calling this, b must be considered "freed" - it must not be
    used again. This function is free (as it were) to immediately free
    the object's memory instead of recycling it.
 */
-CMPP_EXPORT void cmpp_b_return(cmpp *dx, cmpp_b *b);
+CMPP_EXPORT void cmpp_b_return(cmpp *pp, cmpp_b *b);
 
 /**
    If NUL-terminated z matches one of the strings listed below, its
 
 /**
    If NUL-terminated z matches one of the strings listed below, its
@@ -4520,6 +4511,7 @@ struct cmpp_args_pimpl {
      We need(?) a (cmpp*) here for finalization/recycling purposes.
   */
   cmpp *pp;
      We need(?) a (cmpp*) here for finalization/recycling purposes.
   */
   cmpp *pp;
+  /** True if these are the arguments for [call...] syntax. */
   bool isCall;
   /**
      Next entry in the free-list.
   bool isCall;
   /**
      Next entry in the free-list.
@@ -4596,9 +4588,9 @@ struct cmpp_dx_pimpl {
      Record IDs for/from cmpp_[un]define_shadow().
   */
   struct {
      Record IDs for/from cmpp_[un]define_shadow().
   */
   struct {
-    /** ID for __FILE__. */
+    /** Shadow-define rowid for __FILE__. */
     int64_t sidFile;
     int64_t sidFile;
-    /** Rowid for #include path entry. */
+    /** Shadow-define rowid for #include path entry. */
     int64_t ridInclPath;
   } shadow;
 
     int64_t ridInclPath;
   } shadow;
 
@@ -4614,6 +4606,7 @@ struct cmpp_dx_pimpl {
     bool nextIsCall;
   } flags;
 };
     bool nextIsCall;
   } flags;
 };
+
 /**
    Initializes or resets a. Returns non-0 on OOM.
 */
 /**
    Initializes or resets a. Returns non-0 on OOM.
 */
@@ -4737,7 +4730,7 @@ CMPP_PRIVATE int CmppKvp_parse(cmpp *pp, CmppKvp * p,
    Stack of POD values. Intended for use with cmpp at-token and
    undefined key policies.
 */
    Stack of POD values. Intended for use with cmpp at-token and
    undefined key policies.
 */
-#define cmpp__PodList_decl(ST,ET)         \
+#define cmpp__PodList_decl(ST,ET)          \
   struct ST {                              \
     /* current stack index */              \
     cmpp_size_t n;                         \
   struct ST {                              \
     /* current stack index */              \
     cmpp_size_t n;                         \
@@ -4751,7 +4744,7 @@ CMPP_PRIVATE int CmppKvp_parse(cmpp *pp, CmppKvp * p,
   void ST ## _pop(ST *s);                  \
   int ST ## _reserve(cmpp *, ST *, cmpp_size_t min)
 
   void ST ## _pop(ST *s);                  \
   int ST ## _reserve(cmpp *, ST *, cmpp_size_t min)
 
-#define cmpp__PodList_impl(ST,ET)                               \
+#define cmpp__PodList_impl(ST,ET)                                \
   void ST ## _wipe(ST * const s, ET v){                          \
     if( s->na ) memset(s->stack, (int)v, sizeof(ET)*s->na);      \
     s->n = 0;                                                    \
   void ST ## _wipe(ST * const s, ET v){                          \
     if( s->na ) memset(s->stack, (int)v, sizeof(ET)*s->na);      \
     s->n = 0;                                                    \
@@ -4933,11 +4926,13 @@ struct cmpp_pimpl {
     "(SELECT v FROM " CMPP__DB_MAIN_NAME ".vdef WHERE k=?" #N \
     " ORDER BY source LIMIT 1)"
 
     "(SELECT v FROM " CMPP__DB_MAIN_NAME ".vdef WHERE k=?" #N \
     " ORDER BY source LIMIT 1)"
 
+    /** Name of library-generated savepoints. */
+#define CMPP__SAVEPOINT_NAME "_cmpp_"
+
     /**
        One entry for each distinct query used by cmpp: E(X,SQL), where
        X is the member's name and SQL is its SQL.
     */
     /**
        One entry for each distinct query used by cmpp: E(X,SQL), where
        X is the member's name and SQL is its SQL.
     */
-#define CMPP_SAVEPOINT_NAME "_cmpp_"
 #define CmppStmt_map(E)                                \
     E(sdefIns,                                         \
       "INSERT INTO "                                   \
 #define CmppStmt_map(E)                                \
     E(sdefIns,                                         \
       "INSERT INTO "                                   \
@@ -5019,16 +5014,16 @@ struct cmpp_pimpl {
       "ATTACH ?1 AS ?2")                               \
     E(dbDetach,                                        \
       "DETACH ?1")                                     \
       "ATTACH ?1 AS ?2")                               \
     E(dbDetach,                                        \
       "DETACH ?1")                                     \
-    E(spBegin, "SAVEPOINT " CMPP_SAVEPOINT_NAME)       \
+    E(spBegin, "SAVEPOINT " CMPP__SAVEPOINT_NAME)      \
     E(spRollback,                                      \
     E(spRollback,                                      \
-      "ROLLBACK TO SAVEPOINT " CMPP_SAVEPOINT_NAME)    \
+      "ROLLBACK TO SAVEPOINT " CMPP__SAVEPOINT_NAME)   \
     E(spRelease,                                       \
     E(spRelease,                                       \
-      "RELEASE SAVEPOINT " CMPP_SAVEPOINT_NAME)        \
+      "RELEASE SAVEPOINT " CMPP__SAVEPOINT_NAME)       \
     E(insTtype,                                        \
       "INSERT INTO " CMPP__DB_MAIN_NAME ".ttype"       \
       "(t,n,s) VALUES(?1,?2,?3)")                      \
     E(selPathSearch,                                   \
     E(insTtype,                                        \
       "INSERT INTO " CMPP__DB_MAIN_NAME ".ttype"       \
       "(t,n,s) VALUES(?1,?2,?3)")                      \
     E(selPathSearch,                                   \
-   /* sqlite.org/forum/forumpost/840c98a8e87c2207 */   \
+      /* sqlite.org/forum/forumpost/840c98a8e87c2207 */ \
       "WITH path(basename, sep, ext, path) AS (\n"     \
       "  select\n"                                     \
       "  ?1 basename,\n"                               \
       "WITH path(basename, sep, ext, path) AS (\n"     \
       "  select\n"                                     \
       "  ?1 basename,\n"                               \
@@ -5067,8 +5062,7 @@ struct cmpp_pimpl {
       "where r<>'' and cmpp_file_exists(fn)\n"         \
       "order by i\n"                                   \
       "limit 1;")
       "where r<>'' and cmpp_file_exists(fn)\n"         \
       "order by i\n"                                   \
       "limit 1;")
-
-    /* trivia: selPathSearch (^^^) was generated using
+    /* trivia: selPathSearch (^^^) was initially generated using
        cmpp's #c-code directive. */
 
 #define E(N,S) sqlite3_stmt * N;
        cmpp's #c-code directive. */
 
 #define E(N,S) sqlite3_stmt * N;
@@ -5200,7 +5194,7 @@ struct cmpp_pimpl {
     enum cmpp_b_list_e bufSort;
     /**
        Head of the free-list.
     enum cmpp_b_list_e bufSort;
     /**
        Head of the free-list.
-     */
+    */
     cmpp_args_pimpl * argPimpl;
   } recycler;
 };
     cmpp_args_pimpl * argPimpl;
   } recycler;
 };
@@ -5697,9 +5691,9 @@ char const * cmpp__unpol_name(cmpp *pp, cmpp_unpol_e p);
    cmpp_b bMine = cmpp_b_empty;
    cmpp_outputer oOld = {0};
    oMine.state = &bMine;
    cmpp_b bMine = cmpp_b_empty;
    cmpp_outputer oOld = {0};
    oMine.state = &bMine;
-   cmpp_outputer_swap(pp, &myOut, &oOld);
+   cmpp__outputer_swap(pp, &oMine, &oOld);
    ...do some work then ALWAYS do...
    ...do some work then ALWAYS do...
-   cmpp_outputer_swap(pp, &oOld, &oMine);
+   cmpp__outputer_swap(pp, &oOld, &oMine);
    ```
 
    Because this involves bitwise copying, care must be taken with
    ```
 
    Because this involves bitwise copying, care must be taken with
@@ -6942,7 +6936,6 @@ int cmpp__bind_textv(cmpp*pp, sqlite3_stmt *pStmt, int col,
     int n = 0;
     char * z;
     va_list va;
     int n = 0;
     char * z;
     va_list va;
-    if( !str ) return ppCode;
     va_start(va,zFmt);
     sqlite3_str_vappendf(str, zFmt, va);
     va_end(va);
     va_start(va,zFmt);
     sqlite3_str_vappendf(str, zFmt, va);
     va_end(va);
@@ -8208,7 +8201,7 @@ CMPP__EXPORT(int, cmpp_dx_next)(cmpp_dx * const dx, bool * pGotOne){
   assert( !pimpl->args.pimpl->argOut.n );
   assert( !pimpl->args.pimpl->argli.n );
   assert( dx->args.z );
   assert( !pimpl->args.pimpl->argOut.n );
   assert( !pimpl->args.pimpl->argli.n );
   assert( dx->args.z );
-  if( //1 || //pleases valgrind. Well, it did at one point.
+  if( //pleases valgrind. Well, it did at one point.
       !cmpp_dx_is_eliding(dx) || 0!=(cmpp_d_F_FLOW_CONTROL & dx->d->flags) ){
     if( cmpp_d_F_ARGS_LIST & dx->d->flags ){
       cmpp_dx_args_parse(dx, &pimpl->args);
       !cmpp_dx_is_eliding(dx) || 0!=(cmpp_d_F_FLOW_CONTROL & dx->d->flags) ){
     if( cmpp_d_F_ARGS_LIST & dx->d->flags ){
       cmpp_dx_args_parse(dx, &pimpl->args);
@@ -8993,7 +8986,7 @@ int cmpp_process_file(cmpp *pp, const char * zName){
 }
 
 CMPP__EXPORT(int, cmpp_process_stream)(cmpp *pp, const char * zName,
 }
 
 CMPP__EXPORT(int, cmpp_process_stream)(cmpp *pp, const char * zName,
-                        cmpp_input_f src, void * srcState){
+                                       cmpp_input_f src, void * srcState){
   if( 0==ppCode ){
     cmpp_b * const os = cmpp_b_borrow(pp);
     int const rc = os
   if( 0==ppCode ){
     cmpp_b * const os = cmpp_b_borrow(pp);
     int const rc = os
@@ -10233,8 +10226,9 @@ const cmpp_outputer cmpp_outputer_obuf = {
 */
 static int cmpp__prepare(cmpp *pp, sqlite3_stmt **pStmt,
                          const char * zSql, ...){
 */
 static int cmpp__prepare(cmpp *pp, sqlite3_stmt **pStmt,
                          const char * zSql, ...){
-  /* We need for pp->pimpl->stmt.sp* to work regardless of pending errors so
-     that we can, when appropriate, create the rollback statements. */
+  /* We need for pp->pimpl->stmt.sp* to work regardless of pending
+     errors so that we can, when appropriate, create the rollback
+     statements. Thus we don't check ppCode before starting. */
   sqlite3_str * str = sqlite3_str_new(pp->pimpl->db.dbh);
   char * z = 0;
   int n = 0;
   sqlite3_str * str = sqlite3_str_new(pp->pimpl->db.dbh);
   char * z = 0;
   int n = 0;
@@ -10305,8 +10299,7 @@ int cmpp__step(cmpp * const pp, sqlite3_stmt * const q, bool resetIt){
    from cmpp_rc_e. It specifically treats SQLITE_ROW and SQLITE_DONE
    as non-errors, returning 0 for those.
 */
    from cmpp_rc_e. It specifically treats SQLITE_ROW and SQLITE_DONE
    as non-errors, returning 0 for those.
 */
-static int cmpp__db_errcode(sqlite3 * const db, int sqliteCode);
-int cmpp__db_errcode(sqlite3 * const db, int sqliteCode){
+static int cmpp__db_errcode(sqlite3 * const db, int sqliteCode){
   (void)db;
   int rc = 0;
   switch(sqliteCode & 0xff){
   (void)db;
   int rc = 0;
   switch(sqliteCode & 0xff){
@@ -10345,7 +10338,7 @@ int cmpp__db_rc(cmpp *pp, int dbRc, char const *zMsg){
         pp, cmpp__db_errcode(pp->pimpl->db.dbh, dbRc),
         "SQLite error #%d: %s%s%s",
         dbRc,
         pp, cmpp__db_errcode(pp->pimpl->db.dbh, dbRc),
         "SQLite error #%d: %s%s%s",
         dbRc,
-                       pp->pimpl->db.dbh
+        pp->pimpl->db.dbh
         ? sqlite3_errmsg(pp->pimpl->db.dbh)
         : "<no db handle>",
         zMsg ? ": " : "",
         ? sqlite3_errmsg(pp->pimpl->db.dbh)
         : "<no db handle>",
         zMsg ? ": " : "",
@@ -10357,7 +10350,10 @@ int cmpp__db_rc(cmpp *pp, int dbRc, char const *zMsg){
 /**
    The base "define" impl.  Requires q to be an INSERT for one of the
    define tables and have the (t,k,v) columns set up to bind to ?1,
 /**
    The base "define" impl.  Requires q to be an INSERT for one of the
    define tables and have the (t,k,v) columns set up to bind to ?1,
-   ?2, and ?3.
+   ?2, and ?3. nVal must be the number of bytes of zVal to bind. If
+   zVal is NULL then (zVal="1", nVal=1) is assumed, else if nVal is 0
+   then zVal is bound as SQL NULL. That's wonky, but it's what we
+   currently have.
 */
 static
 int cmpp__define_impl(cmpp * const pp,
 */
 static
 int cmpp__define_impl(cmpp * const pp,
@@ -10963,6 +10959,7 @@ static void cmpp_dx_f_define(cmpp_dx *dx){
   unsigned char acHeredoc[128] = {0} /* TODO: cmpp_args_clone() */;
   bool ifNotDefined = false /* true if '?' arg */;
   cmpp_arg const *aAppend = 0;
   unsigned char acHeredoc[128] = {0} /* TODO: cmpp_args_clone() */;
   bool ifNotDefined = false /* true if '?' arg */;
   cmpp_arg const *aAppend = 0;
+  bool raw = false;
 #define checkIsDefined(ARG) \
     if(ifNotDefined && (cmpp_has(dx->pp, (char const*)ARG->z, ARG->n) \
                         || dxppCode)) break
 #define checkIsDefined(ARG) \
     if(ifNotDefined && (cmpp_has(dx->pp, (char const*)ARG->z, ARG->n) \
                         || dxppCode)) break
@@ -11016,6 +11013,10 @@ static void cmpp_dx_f_define(cmpp_dx *dx){
           ++nChomp;
           break;
         }
           ++nChomp;
           break;
         }
+        if( cmpp_arg_isflag(arg,"-raw") ){
+          raw = true;
+          break;
+        }
         if( cmpp_arg_isflag(arg,"-append")
             || cmpp_arg_isflag(arg,"-a") ){
           aAppend = arg->next;
         if( cmpp_arg_isflag(arg,"-append")
             || cmpp_arg_isflag(arg,"-a") ){
           aAppend = arg->next;
@@ -11027,8 +11028,15 @@ static void cmpp_dx_f_define(cmpp_dx *dx){
           arg = aAppend;
           break;
         }
           arg = aAppend;
           break;
         }
+      handle_value:
         if( aKey ){
           /* This is the second arg - the value */
         if( aKey ){
           /* This is the second arg - the value */
+          if( arg->next ){
+            cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
+                            "Unexpected token after value: %s",
+                            arg->next->z);
+            break;
+          }
           checkIsDefined(aKey);
           cmpp_b * const os = cmpp_b_borrow(dx->pp);
           cmpp_b * const ba = aAppend ? cmpp_b_borrow(dx->pp) : 0;
           checkIsDefined(aKey);
           cmpp_b * const os = cmpp_b_borrow(dx->pp);
           cmpp_b * const ba = aAppend ? cmpp_b_borrow(dx->pp) : 0;
@@ -11048,6 +11056,8 @@ static void cmpp_dx_f_define(cmpp_dx *dx){
               if( ba->n ) cmpp_b_append4(dx->pp, ba, aAppend->z, aAppend->n);
               cmpp_b_append4(dx->pp, ba, os->z, os->n);
             }
               if( ba->n ) cmpp_b_append4(dx->pp, ba, aAppend->z, aAppend->n);
               cmpp_b_append4(dx->pp, ba, os->z, os->n);
             }
+            int n = nChomp;
+            while( n-- && cmpp_b_chomp(which) ){}
             cmpp__define2(dx->pp, aKey->z, aKey->n, which->z, which->n,
                           arg->ttype);
             if( 0 ){
             cmpp__define2(dx->pp, aKey->z, aKey->n, which->z, which->n,
                           arg->ttype);
             if( 0 ){
@@ -11073,7 +11083,10 @@ static void cmpp_dx_f_define(cmpp_dx *dx){
         break;
       case cmpp_TT_GroupSquiggly:
         assert( !acHeredoc[0] );
         break;
       case cmpp_TT_GroupSquiggly:
         assert( !acHeredoc[0] );
-        if( (ifNotDefined ? argNdx>1 : argNdx>0) || arg->next ){
+        if( aKey ){
+          /* Treat this as a value */
+          goto handle_value;
+        }else if( (ifNotDefined ? argNdx>1 : argNdx>0) || arg->next ){
           cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
                           "{...} must be the only argument.")
             /* This is for simplicity's sake. */;
           cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
                           "{...} must be the only argument.")
             /* This is for simplicity's sake. */;
@@ -11117,6 +11130,8 @@ static void cmpp_dx_f_define(cmpp_dx *dx){
         checkIsDefined(aKey);
         cmpp_b * const b = cmpp_b_borrow(dx->pp);
         if( b && 0==cmpp_call_str(dx->pp, arg->z, arg->n, b, 0) ){
         checkIsDefined(aKey);
         cmpp_b * const b = cmpp_b_borrow(dx->pp);
         if( b && 0==cmpp_call_str(dx->pp, arg->z, arg->n, b, 0) ){
+          int n = nChomp;
+          while( n-- && cmpp_b_chomp(b) ){}
           cmpp__define2(dx->pp, aKey->z, aKey->n,
                         b->z, b->n, cmpp_TT_AnyType);
         }
           cmpp__define2(dx->pp, aKey->z, aKey->n,
                         b->z, b->n, cmpp_TT_AnyType);
         }
@@ -11131,17 +11146,15 @@ static void cmpp_dx_f_define(cmpp_dx *dx){
         break;
     }
   }
         break;
     }
   }
-  if( 0==nHeredoc && nChomp ){
-    cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
-                    "-chomp can only be used with <<.");
-  }
   if( 0==dxppCode && nHeredoc ){
     // Process (#define KEY <<)
     cmpp_b * const os = cmpp_b_borrow(dx->pp);
     assert( dx->d->closer );
     if( os &&
         0==cmpp_dx_consume_b(dx, os, &dx->d->closer, 1,
   if( 0==dxppCode && nHeredoc ){
     // Process (#define KEY <<)
     cmpp_b * const os = cmpp_b_borrow(dx->pp);
     assert( dx->d->closer );
     if( os &&
         0==cmpp_dx_consume_b(dx, os, &dx->d->closer, 1,
-                             cmpp_dx_consume_F_PROCESS_OTHER_D) ){
+                             raw
+                             ? cmpp_dx_consume_F_RAW
+                             : cmpp_dx_consume_F_PROCESS_OTHER_D) ){
       while( nChomp-- && cmpp_b_chomp(os) ){}
       g_debug(dx->pp,2,("define  heredoc: [%s]=[%.*s]\n",
                        acHeredoc, (int)os->n, os->z));
       while( nChomp-- && cmpp_b_chomp(os) ){}
       g_debug(dx->pp,2,("define  heredoc: [%s]=[%.*s]\n",
                        acHeredoc, (int)os->n, os->z));
@@ -11191,6 +11204,153 @@ static void cmpp_dx_f_undef(cmpp_dx *dx){
   }
 }
 
   }
 }
 
+/* Impl. for #expand. */
+static void cmpp_dx_f_expand(cmpp_dx *dx){
+  cmpp_d const * const d = dx->d;
+  assert(d);
+  if( !dx->args.arg0 ){
+    cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
+                    "#expand requires one or two arguments");
+    return;
+  }
+  cmpp_arg const * argKav = 0 /* {key -> value ...} */;
+  cmpp_arg const * argToB = 0 /* one of X or Y from: #expand X Y */;
+  cmpp_arg const * aLhs = 0;
+  cmpp_arg const * aRhs = 0;
+  cmpp_arg const * aErr = 0;
+  cmpp_args args = cmpp_args_empty /* need a clone b/c of recursion */;
+  cmpp_b * const osIn = cmpp_b_borrow(dx->pp);
+  int nChomp = 0    /* Strip nChomp newlines from result */;
+  int unChomp = 0   /* Add unChomp newlines to result */;
+  bool raw = false  /* interpret block content as raw or
+                       preprocessor-ready? */;
+  bool inSp = false /* started a local savepoint? */;
+  if( !osIn || cmpp_dx_args_clone(dx, &args) ) goto end;
+  for( cmpp_arg const * arg = args.arg0;
+       0==dxppCode && arg && !aRhs;
+       arg = arg->next ){
+    //g_warn("arg=%s", arg->z);
+    aErr = arg;
+    switch( arg->ttype ){
+      case cmpp_TT_Word:
+        if( cmpp_arg_isflag(arg,"-chomp") ){
+          ++nChomp;
+          continue;
+        }
+        if( cmpp_arg_isflag(arg,"-unchomp") ){
+          ++unChomp;
+          continue;
+        }
+        if( cmpp_arg_isflag(arg,"-raw") ){
+          raw = true;
+          continue;
+        }
+        if( !aLhs ){
+          aLhs = arg;
+          continue;
+        }
+        goto unexpected_arg;
+      default:
+        assert( !aRhs );
+        if( aLhs ){
+          if( (aErr = arg->next) ){
+            assert( arg!=aLhs );
+            assert( !aRhs );
+            goto unexpected_arg;
+          }
+          aRhs = arg;
+        }else{
+          aLhs = arg;
+        }
+        continue;
+    }
+  }
+  if( !aLhs ){
+    cmpp_dx_err_set(dx, CMPP_RC_MISUSE, "Expecting one or two arguments.");
+    goto end;
+  }
+  if( aRhs ){
+    if( cmpp_TT_GroupSquiggly!=aRhs->ttype ){
+      aErr = aRhs;
+      goto unexpected_arg;
+    }
+    argKav = aRhs;
+    argToB = aLhs;
+  }else{
+    if( cmpp_TT_GroupSquiggly==aLhs->ttype ){
+      argKav = aLhs;
+    }else{
+      argToB = aLhs;
+    }
+  }
+  assert( argKav || argToB );
+  if( argToB ){
+    if( cmpp_arg_to_b(dx, argToB, osIn, cmpp_arg_to_b_F_BRACE_CALL) ){
+      goto end;
+    }
+  }
+  if( argKav ){
+    assert(!argKav->next);
+    if( (aErr = argKav->next) ){
+      goto unexpected_arg;
+    }
+    if( cmpp_sp_begin(dx->pp) ) goto end;
+    inSp = true;
+    if( cmpp_kav_each(dx, argKav->z, argKav->n,
+                      cmpp_kav_each_f_define__group,
+                      0,
+                      cmpp_kav_each_F_EXPAND_VAL
+                      | cmpp_kav_each_F_CALL_VAL
+                      | cmpp_kav_each_F_PARENS_EXPR) ){
+      goto end;
+    }
+  }
+  if( argToB && cmpp_TT_GroupSquiggly==argToB->ttype ){
+    cmpp_outputer xout = cmpp_outputer_b;
+    cmpp_outputer old = {0};
+    cmpp_b * const b = cmpp_b_borrow(dx->pp);
+    if( !(xout.state = b) ) goto end;
+    cmpp__outputer_swap(dx->pp, &xout, &old);
+    cmpp_process_string(dx->pp, "#expand buffer", osIn->z, osIn->n);
+    cmpp__outputer_swap(dx->pp, &old, &xout);
+    cmpp_b_swap(b, osIn);
+    cmpp_b_return(dx->pp, b);
+    if( dxppCode ) goto end;
+  }
+  if( !argToB ){
+    assert(!osIn->n);
+    if( cmpp_dx_is_call(dx) ){
+      cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
+                      "#expand.../#expand is not legal for [call].");
+      goto end;
+    }
+    if( cmpp_dx_consume_b(dx, osIn, &dx->d->closer, 1,
+                          raw
+                          ? cmpp_dx_consume_F_RAW
+                          : cmpp_dx_consume_F_PROCESS_OTHER_D) ){
+      goto end;
+    }
+  }
+  while( nChomp-- && cmpp_b_chomp(osIn) ){}
+  while( unChomp-- && cmpp_b_append_ch(osIn, '\n') ){}
+  //MARKER(("Emitting %d bytes: %.*s\n", (int)osIn->n, (int)osIn->n, osIn->z));
+  cmpp_dx_out_expand(dx, NULL, osIn->z, osIn->n, cmpp_atpol_CURRENT);
+
+end:
+  if( inSp ){
+    cmpp_sp_rollback(dx->pp);
+  }
+  cmpp_b_return(dx->pp, osIn);
+  cmpp_args_cleanup(&args);
+  return;
+unexpected_arg:
+  assert( aErr );
+  cmpp_dx_err_set(dx, CMPP_RC_MISUSE, "Unexpected argument: %.*s",
+                  (int)aErr->n, aErr->z);
+  goto end;
+}
+
+
 /* Impl. for #once. */
 static void cmpp_dx_f_once(cmpp_dx *dx){
   cmpp_d const * const d = dx->d;
 /* Impl. for #once. */
 static void cmpp_dx_f_once(cmpp_dx *dx){
   cmpp_d const * const d = dx->d;
@@ -11201,7 +11361,6 @@ static void cmpp_dx_f_once(cmpp_dx *dx){
                     "Expecting no arguments");
     return;
   }
                     "Expecting no arguments");
     return;
   }
-  cmpp_dx_pimpl * const dxp = dx->pimpl;
   cmpp_b * const b = cmpp_b_borrow(dx->pp);
   if( !b ) return;
   cmpp_b_append_ch(b, '#');
   cmpp_b * const b = cmpp_b_borrow(dx->pp);
   if( !b ) return;
   cmpp_b_append_ch(b, '#');
@@ -11212,7 +11371,7 @@ static void cmpp_dx_f_once(cmpp_dx *dx){
   if( b->errCode
       || dxppCode
       || cmpp_b_append_ch(b, ':')
   if( b->errCode
       || dxppCode
       || cmpp_b_append_ch(b, ':')
-      || cmpp_b_append_i32(b, (int)dxp->pos.lineNo) ){
+      || cmpp_b_append_i32(b, (int)dx->pimpl->pos.lineNo) ){
     goto end;
   }
   //g_debug(dx->pp,1,("#once key: %s", b->z));
     goto end;
   }
   //g_debug(dx->pp,1,("#once key: %s", b->z));
@@ -12896,6 +13055,7 @@ static void cmpp_dx_f_join(cmpp_dx *dx){
                     cmpp_b_reuse(bSep),
                     cmpp_arg_to_b_F_BRACE_CALL);
       arg = arg->next;
                     cmpp_b_reuse(bSep),
                     cmpp_arg_to_b_F_BRACE_CALL);
       arg = arg->next;
+      /* FIXME: unescape bSep */
       continue;
     }
     //FLAG("-nl"){ addNl=true; continue; }
       continue;
     }
     //FLAG("-nl"){ addNl=true; continue; }
@@ -13026,12 +13186,11 @@ static void cmpp_dx_f_cmp(cmpp_dx *dx){
       cmpp_arg_to_b(dx, arg, bR, a2bFlags);
       continue;
     }
       cmpp_arg_to_b(dx, arg, bR, a2bFlags);
       continue;
     }
-    goto usage;
+    break;
   }
 
   if( cmpp_dx_err_check(dx) ) goto end;
   if( !bL->z || !bR->z ){
   }
 
   if( cmpp_dx_err_check(dx) ) goto end;
   if( !bL->z || !bR->z ){
-  usage:
     cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
                     "Usage: LHS RHS");
     goto end;
     cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
                     "Usage: LHS RHS");
     goto end;
@@ -13156,6 +13315,8 @@ int cmpp__d_delayed_load(cmpp *pp, char const *zName){
   M_IF_CORE("delimiter",        cmpp_dx_f_delimiter,    F_A_LIST,
                                 cmpp_dx_f_dangling_closer, 0);
   M_IF_CORE("error",            cmpp_dx_f_error,        F_A_RAW, 0, 0);
   M_IF_CORE("delimiter",        cmpp_dx_f_delimiter,    F_A_LIST,
                                 cmpp_dx_f_dangling_closer, 0);
   M_IF_CORE("error",            cmpp_dx_f_error,        F_A_RAW, 0, 0);
+  M_IF_CORE("expand",           cmpp_dx_f_expand,       F_A_LIST,
+                                cmpp_dx_f_dangling_closer, 0);
   M_IF_CORE("expr",             cmpp_dx_f_expr,         F_EXPR, 0, 0);
   M_IF_CORE("join",             cmpp_dx_f_join,         F_A_LIST, 0, 0);
   M_IF_CORE("once",             cmpp_dx_f_once,         F_A_LIST | F_NC,
   M_IF_CORE("expr",             cmpp_dx_f_expr,         F_EXPR, 0, 0);
   M_IF_CORE("join",             cmpp_dx_f_join,         F_A_LIST, 0, 0);
   M_IF_CORE("once",             cmpp_dx_f_once,         F_A_LIST | F_NC,
@@ -14034,7 +14195,6 @@ int cmpp_args_parse(cmpp_dx * const dx,
   }
   unsigned char * zOut = buffer->z;
   unsigned char const * const zOutEnd = zOut + buffer->nAlloc - 1;
   }
   unsigned char * zOut = buffer->z;
   unsigned char const * const zOutEnd = zOut + buffer->nAlloc - 1;
-  cmpp_arg * prevArg = 0;
 #if !defined(NDEBUG)
   unsigned char const * const zReallocCheck = buffer->z;
 #endif
 #if !defined(NDEBUG)
   unsigned char const * const zReallocCheck = buffer->z;
 #endif
@@ -14044,7 +14204,6 @@ int cmpp_args_parse(cmpp_dx * const dx,
   pArgs->arg0 = NULL;
   pArgs->argc = 0;
   for( int i = 0; zPos<zInEnd; ++i){
   pArgs->arg0 = NULL;
   pArgs->argc = 0;
   for( int i = 0; zPos<zInEnd; ++i){
-    //g_stderr("i=%d prevArg=%p\n",i, prevArg);
     cmpp_arg * const arg =
       CmppArgList_append(dx->pp, &pArgs->pimpl->argli);
     if( !arg ) return dxppCode;
     cmpp_arg * const arg =
       CmppArgList_append(dx->pp, &pArgs->pimpl->argli);
     if( !arg ) return dxppCode;
@@ -14086,16 +14245,21 @@ int cmpp_args_parse(cmpp_dx * const dx,
       default: break;
     }
     if( dxppCode ) break;
       default: break;
     }
     if( dxppCode ) break;
-    if( prevArg ){
-      assert( !prevArg->next );
-      prevArg->next = arg;
-    }
-    prevArg = arg;
   }/*foreach input char*/
   //g_stderr("rc=%s argc=%d\n", cmpp_rc_cstr(dxppCode), pArgs->args.n);
   if( 0==dxppCode ){
     pArgs->argc = pArgs->pimpl->argli.n;
     assert( !pArgs->arg0 );
   }/*foreach input char*/
   //g_stderr("rc=%s argc=%d\n", cmpp_rc_cstr(dxppCode), pArgs->args.n);
   if( 0==dxppCode ){
     pArgs->argc = pArgs->pimpl->argli.n;
     assert( !pArgs->arg0 );
+    for( unsigned int i = 0; i < pArgs->argc; ++i ){
+      /* We have to link arg->next after the loop because
+         CmppArgList_append() can realloc, invalidating
+         the links. */
+      if( i ){
+        cmpp_arg * const a = &pArgs->pimpl->argli.list[i];
+        assert( ! a->next );
+        pArgs->pimpl->argli.list[i-1].next = a;
+      }
+    }
     if( pArgs->argc ) pArgs->arg0 = pArgs->pimpl->argli.list;
     if( zOut<zInEnd ) *zOut = 0;
     if( 0 ){
     if( pArgs->argc ) pArgs->arg0 = pArgs->pimpl->argli.list;
     if( zOut<zInEnd ) *zOut = 0;
     if( 0 ){
index a3e79cc5f34334844db502710e43e0c24fc967f8..178c4de68e03866edc513236f782e779de5c30e3 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improve\sperformance\sand\srobustness\sof\sinternal\sroutine\sjsonBlobExpand().
-D 2026-07-20T21:03:27.436
+C Latest\supstream\sext/wasm/libcmpp.c,\sprimarily\sto\smake\sit\sfilcc-legal\s(only\sthe\sfinal\schunk\sin\sthe\sdiff\s-\sthe\srest\sis\sminor\schanges\saccumulated\ssince\sthe\slast\supdate).
+D 2026-07-21T10:44:28.794
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -636,7 +636,7 @@ F ext/wasm/index-dist.html db23748044e286773f2768eec287669501703b5d5f72755e8db73
 F ext/wasm/index.html 5bf6cf1b0a3c8b9f5f54d77f2219d7ae87a15162055ce308109c49b1dcab4239
 F ext/wasm/jaccwabyt/jaccwabyt.js 4e2b797dc170851c9c530c3567679f4aa509eec0fab73b466d945b00b356574b
 F ext/wasm/jaccwabyt/jaccwabyt.md 6aa90fa1a973d0ad10d077088bea163b241d8470c75eafdef87620a1de1dea41
 F ext/wasm/index.html 5bf6cf1b0a3c8b9f5f54d77f2219d7ae87a15162055ce308109c49b1dcab4239
 F ext/wasm/jaccwabyt/jaccwabyt.js 4e2b797dc170851c9c530c3567679f4aa509eec0fab73b466d945b00b356574b
 F ext/wasm/jaccwabyt/jaccwabyt.md 6aa90fa1a973d0ad10d077088bea163b241d8470c75eafdef87620a1de1dea41
-F ext/wasm/libcmpp.c 0e2cec7de7c9cbe7941e6a4dc0c123cec736578a3eeae7828ac18706acd3cea6
+F ext/wasm/libcmpp.c e8ad0f9213af8edb19859e2b584ab6d5801a0e5e19fd715c9add269dfa720ee3
 F ext/wasm/mkdist.sh f8883b077a2ca47cf92e6f0ce305fbf72ca648c3501810125056c4b09c2d5554 x
 F ext/wasm/mkwasmbuilds.c 60706fb66db7f1e24d876e3cb2493484cfb27e7b103c7741c384505dd678769c
 F ext/wasm/module-symbols.html e54f42112e0aac2a31f850ab33e7f2630a2ea4f63496f484a12469a2501e07e2
 F ext/wasm/mkdist.sh f8883b077a2ca47cf92e6f0ce305fbf72ca648c3501810125056c4b09c2d5554 x
 F ext/wasm/mkwasmbuilds.c 60706fb66db7f1e24d876e3cb2493484cfb27e7b103c7741c384505dd678769c
 F ext/wasm/module-symbols.html e54f42112e0aac2a31f850ab33e7f2630a2ea4f63496f484a12469a2501e07e2
@@ -2217,8 +2217,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
 F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 6384b4b8720e35682c3ec01e7de6f0fcaec58026d5e07dfaf989f5e5851d9810
-R dca0e86070ed3e75ecf3f945925d415f
-U drh
-Z 8f5fa16b637811435deedb5237193d8e
+P 099af17c6f82d0a1357f4d3ec5967fb5c664ec532ddfa674017cb4d8041d45c3
+R 1b770928cd957473d7d5119d2d70cf68
+U stephan
+Z 98cce33760d403d951c7ab42f002d518
 # Remove this line to create a well-formed Fossil manifest.
 # Remove this line to create a well-formed Fossil manifest.
index b2195da65dc708eefd3937677a55f37f621b6cd4..55033a6931a68fc90f28540320e7b99711c596a7 100644 (file)
@@ -1 +1 @@
-099af17c6f82d0a1357f4d3ec5967fb5c664ec532ddfa674017cb4d8041d45c3
+06f3bbc5bddc2e763042f8b487a3deedf47c54b7fa785ba6fd6abd524b4cbe3a