./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_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"
** 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/>
**
-** 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)
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 *);
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(),
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
- balanced in the face of certain error-handling paths.
+ balanced.
cmpp_undef() will _not_ undefine an entry added through this
interface.
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.
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);
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.
*/
/**
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
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
- 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()).
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);
/**
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
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;
/**
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_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,
- 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) \
/**
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);
/**
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.
-
- 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.
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.
/**
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 {
/**
- 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 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().
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
- 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.
*/
-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
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.
Record IDs for/from cmpp_[un]define_shadow().
*/
struct {
- /** ID for __FILE__. */
+ /** Shadow-define rowid for __FILE__. */
int64_t sidFile;
- /** Rowid for #include path entry. */
+ /** Shadow-define rowid for #include path entry. */
int64_t ridInclPath;
} shadow;
bool nextIsCall;
} flags;
};
+
/**
Initializes or resets a. Returns non-0 on OOM.
*/
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; \
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; \
"(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.
*/
-#define CMPP_SAVEPOINT_NAME "_cmpp_"
#define CmppStmt_map(E) \
E(sdefIns, \
"INSERT INTO " \
"ATTACH ?1 AS ?2") \
E(dbDetach, \
"DETACH ?1") \
- E(spBegin, "SAVEPOINT " CMPP_SAVEPOINT_NAME) \
+ E(spBegin, "SAVEPOINT " CMPP__SAVEPOINT_NAME) \
E(spRollback, \
- "ROLLBACK TO SAVEPOINT " CMPP_SAVEPOINT_NAME) \
+ "ROLLBACK TO SAVEPOINT " CMPP__SAVEPOINT_NAME) \
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, \
- /* sqlite.org/forum/forumpost/840c98a8e87c2207 */ \
+ /* sqlite.org/forum/forumpost/840c98a8e87c2207 */ \
"WITH path(basename, sep, ext, path) AS (\n" \
" select\n" \
" ?1 basename,\n" \
"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;
enum cmpp_b_list_e bufSort;
/**
Head of the free-list.
- */
+ */
cmpp_args_pimpl * argPimpl;
} recycler;
};
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...
- cmpp_outputer_swap(pp, &oOld, &oMine);
+ cmpp__outputer_swap(pp, &oOld, &oMine);
```
Because this involves bitwise copying, care must be taken with
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);
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__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
*/
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;
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){
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 ? ": " : "",
/**
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,
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
++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;
arg = aAppend;
break;
}
+ handle_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;
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 ){
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. */;
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);
}
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,
- 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));
}
}
+/* 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;
"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, '#');
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));
cmpp_b_reuse(bSep),
cmpp_arg_to_b_F_BRACE_CALL);
arg = arg->next;
+ /* FIXME: unescape bSep */
continue;
}
//FLAG("-nl"){ addNl=true; 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 ){
- usage:
cmpp_dx_err_set(dx, CMPP_RC_MISUSE,
"Usage: LHS RHS");
goto end;
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,
}
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
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;
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 );
+ 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 ){