Add a test for the fixes on this branch.
[sqlite.git] / ext / wasm / c-pp.c
blobc439a0d091b910150e2a0c1665d6bb00c9bd4354
1 /*
2 ** 2022-11-12:
3 **
4 ** In place of a legal notice, here is a blessing:
5 **
6 ** * May you do good and not evil.
7 ** * May you find forgiveness for yourself and forgive others.
8 ** * May you share freely, never taking more than you give.
9 **
10 ************************************************************************
12 ** The C-minus Preprocessor: a truly minimal C-like preprocessor.
13 ** Why? Because C preprocessors _can_ process non-C code but generally make
14 ** quite a mess of it. The purpose of this application is an extremely
15 ** minimal preprocessor with only the most basic functionality of a C
16 ** preprocessor, namely:
18 ** - Limited `#if`, where its one argument is a macro name which
19 ** resolves to true if it's defined, false if it's not. Likewise,
20 ** `#ifnot` is the inverse. Includes `#else` and `#elif` and
21 ** `#elifnot`. Such chains are terminated with `#endif`.
23 ** - `#define` accepts one or more arguments, the names of
24 ** macros. Each one is implicitly true.
26 ** - `#undef` undefine one or more macros.
28 ** - `#error` treats the rest of the line as a fatal error message.
30 ** - `#include` treats its argument as a filename token (NOT quoted,
31 ** though support for quoting may be added later). Some effort is
32 ** made to prevent recursive inclusion, but that support is both
33 ** somewhat fragile and possibly completely unnecessary.
35 ** - `#pragma` is in place for adding "meta-commands", but it does not
36 ** yet have any concrete list of documented commands.
38 * - `#stderr` outputs its file name, line number, and the remaininder
39 ** of that line to stderr.
41 ** - `#//` acts as a single-line comment, noting that there must be as
42 ** space after the `//` part because `//` is (despite appearances)
43 ** parsed like a keyword.
45 ** Note that "#" above is symbolic. The keyword delimiter is
46 ** configurable and defaults to "##". Define CMPP_DEFAULT_DELIM to a
47 ** string when compiling to define the default at build-time.
49 ** This preprocessor does no expansion of content except within the
50 ** bounds of its `#keywords`.
52 ** Design note: this code makes use of sqlite3. Though not _strictly_
53 ** needed in order to implement it, this tool was specifically created
54 ** for use with the sqlite3 project's own JavaScript code, so there's
55 ** no reason not to make use of it to do some of the heavy lifting. It
56 ** does not require any cutting-edge sqlite3 features and should be
57 ** usable with any version which supports `WITHOUT ROWID`.
59 ** Author(s):
61 ** - Stephan Beal <https://wanderinghorse.net/home/stephan/>
64 #include <stdlib.h>
65 #include <stdio.h>
66 #include <errno.h>
67 #include <string.h>
68 #include <stdarg.h>
69 #include <assert.h>
70 #include <ctype.h>
72 #include "sqlite3.h"
74 #if defined(_WIN32) || defined(WIN32)
75 # include <io.h>
76 # include <fcntl.h>
77 # ifndef access
78 # define access(f,m) _access((f),(m))
79 # endif
80 #else
81 # include <unistd.h>
82 #endif
84 #ifndef CMPP_DEFAULT_DELIM
85 #define CMPP_DEFAULT_DELIM "##"
86 #endif
88 #if 1
89 # define CMPP_NORETURN __attribute__((noreturn))
90 #else
91 # define CMPP_NORETURN
92 #endif
94 /* Fatally exits the app with the given printf-style message. */
95 static CMPP_NORETURN void fatalv(char const *zFmt, va_list);
96 static CMPP_NORETURN void fatal(char const *zFmt, ...);
98 /** Proxy for free(), for symmetry with cmpp_realloc(). */
99 static void cmpp_free(void *p);
100 /** A realloc() proxy which dies fatally on allocation error. */
101 static void * cmpp_realloc(void * p, unsigned n);
102 #if 0
103 /** A malloc() proxy which dies fatally on allocation error. */
104 static void * cmpp_malloc(unsigned n);
105 #endif
108 ** If p is stdin or stderr then this is a no-op, else it is a
109 ** proxy for fclose(). This is a no-op if p is NULL.
111 static void FILE_close(FILE *p);
113 ** Works like fopen() but accepts the special name "-" to mean either
114 ** stdin (if zMode indicates a real-only mode) or stdout. Fails
115 ** fatally on error.
117 static FILE * FILE_open(char const *zName, const char * zMode);
119 ** Reads the entire contents of the given file, allocating it in a
120 ** buffer which gets assigned to `*pOut`. `*nOut` gets assigned the
121 ** length of the output buffer. Fails fatally on error.
123 static void FILE_slurp(FILE *pFile, unsigned char **pOut,
124 unsigned * nOut);
127 ** Intended to be passed an sqlite3 result code. If it's non-0
128 ** then it emits a fatal error message which contains both the
129 ** given string and the sqlite3_errmsg() from the application's
130 ** database instance.
132 static void db_affirm_rc(int rc, const char * zMsg);
135 ** Proxy for sqlite3_str_finish() which fails fatally if that
136 ** routine returns NULL.
138 static char * db_str_finish(sqlite3_str *s, int * n);
140 ** Proxy for sqlite3_str_new() which fails fatally if that
141 ** routine returns NULL.
143 static sqlite3_str * db_str_new(void);
145 /* Proxy for sqlite3_finalize(). */
146 static void db_finalize(sqlite3_stmt *pStmt);
148 ** Proxy for sqlite3_step() which fails fatally if the result
149 ** is anything other than SQLITE_ROW or SQLITE_DONE.
151 static int db_step(sqlite3_stmt *pStmt);
153 ** Proxy for sqlite3_bind_int() which fails fatally on error.
155 static void db_bind_int(sqlite3_stmt *pStmt, int col, int val);
156 #if 0
158 ** Proxy for sqlite3_bind_null() which fails fatally on error.
160 static void db_bind_null(sqlite3_stmt *pStmt, int col);
161 #endif
163 ** Proxy for sqlite3_bind_text() which fails fatally on error.
165 static void db_bind_text(sqlite3_stmt *pStmt, int col, const char * zStr);
167 ** Proxy for sqlite3_bind_text() which fails fatally on error.
169 static void db_bind_textn(sqlite3_stmt *pStmt, int col, const char * zStr, int len);
170 #if 0
172 ** Proxy for sqlite3_bind_text() which fails fatally on error. It uses
173 ** sqlite3_str_vappendf() so supports all of its formatting options.
175 static void db_bind_textv(sqlite3_stmt *pStmt, int col, const char * zFmt, ...);
176 #endif
178 ** Proxy for sqlite3_free(), to be passed any memory which is allocated
179 ** by sqlite3_malloc().
181 static void db_free(void *m);
183 ** Adds the given `#define` macro name to the list of macros, ignoring
184 ** any duplicates. Fails fatally on error.
186 static void db_define_add(const char * zKey);
188 ** Returns true if the given key is already in the `#define` list,
189 ** else false. Fails fatally on db error.
191 static int db_define_has(const char * zName);
193 ** Removes the given `#define` macro name from the list of
194 ** macros. Fails fatally on error.
196 static void db_define_rm(const char * zKey);
198 ** Adds the given filename to the list of being-`#include`d files,
199 ** using the given source file name and line number of error reporting
200 ** purposes. If recursion is later detected.
202 static void db_including_add(const char * zKey, const char * zSrc, int srcLine);
204 ** Adds the given dir to the list of includes. They are checked in the
205 ** order they are added.
207 static void db_include_dir_add(const char * zKey);
209 ** Returns a resolved path of PREFIX+'/'+zKey, where PREFIX is one of
210 ** the `#include` dirs (db_include_dir_add()). If no file match is
211 ** found, NULL is returned. Memory must eventually be passed to
212 ** db_free() to free it.
214 static char * db_include_search(const char * zKey);
216 ** Removes the given key from the `#include` list.
218 static void db_include_rm(const char * zKey);
220 ** A proxy for sqlite3_prepare() which fails fatally on error.
222 static void db_prepare(sqlite3_stmt **pStmt, const char * zSql, ...);
225 ** Opens the given file and processes its contents as c-pp, sending
226 ** all output to the global c-pp output channel. Fails fatally on
227 ** error.
229 static void cmpp_process_file(const char * zName);
232 ** Returns the number newline characters between the given starting
233 ** point and inclusive ending point. Results are undefined if zFrom is
234 ** greater than zTo.
236 static unsigned count_lines(unsigned char const * zFrom,
237 unsigned char const *zTo);
240 ** Wrapper around a FILE handle.
242 struct FileWrapper {
243 /* File's name. */
244 char const *zName;
245 /* FILE handle. */
246 FILE * pFile;
247 /* Where FileWrapper_slurp() stores the file's contents. */
248 unsigned char * zContent;
249 /* Size of this->zContent, as set by FileWrapper_slurp(). */
250 unsigned nContent;
252 typedef struct FileWrapper FileWrapper;
253 #define FileWrapper_empty_m {0,0,0,0}
254 static const FileWrapper FileWrapper_empty = FileWrapper_empty_m;
256 /* Proxy for FILE_close(). */
257 static void FileWrapper_close(FileWrapper * p);
258 /* Proxy for FILE_open(). */
259 static void FileWrapper_open(FileWrapper * p, const char * zName, const char *zMode);
260 /* Proxy for FILE_slurp(). */
261 static void FileWrapper_slurp(FileWrapper * p);
264 ** Outputs a printf()-formatted message to stderr.
266 static void g_stderr(char const *zFmt, ...);
268 ** Outputs a printf()-formatted message to stderr.
270 static void g_stderrv(char const *zFmt, va_list);
271 #define g_debug(lvl,pfexpr) \
272 if(lvl<=g.doDebug) g_stderr("%s @ %s:%d: ",g.zArgv0,__FILE__,__LINE__); \
273 if(lvl<=g.doDebug) g_stderr pfexpr
275 void fatalv(char const *zFmt, va_list va){
276 if(zFmt && *zFmt){
277 vfprintf(stderr, zFmt, va);
279 fputc('\n', stderr);
280 exit(1);
283 void fatal(char const *zFmt, ...){
284 va_list va;
285 va_start(va, zFmt);
286 fatalv(zFmt, va);
287 va_end(va);
290 void cmpp_free(void *p){
291 free(p);
294 void * cmpp_realloc(void * p, unsigned n){
295 void * const rc = realloc(p, n);
296 if(!rc) fatal("realloc(P,%u) failed", n);
297 return rc;
300 #if 0
301 void * cmpp_malloc(unsigned n){
302 void * const rc = malloc(n);
303 if(!rc) fatal("malloc(%u) failed", n);
304 return rc;
306 #endif
308 FILE * FILE_open(char const *zName, const char * zMode){
309 FILE * p;
310 if('-'==zName[0] && 0==zName[1]){
311 p = strstr(zMode,"w") ? stdout : stdin;
312 }else{
313 p = fopen(zName, zMode);
314 if(!p) fatal("Cannot open file [%s] with mode [%s]", zName, zMode);
316 return p;
319 void FILE_close(FILE *p){
320 if(p && p!=stdout && p!=stderr){
321 fclose(p);
325 void FILE_slurp(FILE *pFile, unsigned char **pOut,
326 unsigned * nOut){
327 unsigned char zBuf[1024 * 8];
328 unsigned char * pDest = 0;
329 unsigned nAlloc = 0;
330 unsigned nOff = 0;
331 /* Note that this needs to be able to work on non-seekable streams,
332 ** thus we read in chunks instead of doing a single alloc and
333 ** filling it in one go. */
334 while( !feof(pFile) ){
335 size_t const n = fread(zBuf, 1, sizeof(zBuf), pFile);
336 if(n>0){
337 if(nAlloc < nOff + n + 1){
338 nAlloc = nOff + n + 1;
339 pDest = cmpp_realloc(pDest, nAlloc);
341 memcpy(pDest + nOff, zBuf, n);
342 nOff += n;
345 if(pDest) pDest[nOff] = 0;
346 *pOut = pDest;
347 *nOut = nOff;
350 void FileWrapper_close(FileWrapper * p){
351 if(p->pFile) FILE_close(p->pFile);
352 if(p->zContent) cmpp_free(p->zContent);
353 *p = FileWrapper_empty;
356 void FileWrapper_open(FileWrapper * p, const char * zName,
357 const char * zMode){
358 FileWrapper_close(p);
359 p->pFile = FILE_open(zName, zMode);
360 p->zName = zName;
363 void FileWrapper_slurp(FileWrapper * p){
364 assert(!p->zContent);
365 assert(p->pFile);
366 FILE_slurp(p->pFile, &p->zContent, &p->nContent);
369 unsigned count_lines(unsigned char const * zFrom, unsigned char const *zTo){
370 unsigned ln = 0;
371 unsigned char const *zPos = zFrom;
372 assert(zFrom && zTo);
373 assert(zFrom <= zTo);
374 for(; zPos < zTo; ++zPos){
375 switch(*zPos){
376 case (unsigned)'\n': ++ln; break;
377 default: break;
380 return ln;
383 enum CmppParseState {
384 TS_Start = 1,
385 TS_If,
386 TS_IfPassed,
387 TS_Else,
388 TS_Error
390 typedef enum CmppParseState CmppParseState;
391 enum CmppTokenType {
392 TT_Invalid = 0,
393 TT_Comment,
394 TT_Define,
395 TT_Elif,
396 TT_ElifNot,
397 TT_Else,
398 TT_EndIf,
399 TT_Error,
400 TT_If,
401 TT_IfNot,
402 TT_Include,
403 TT_Line,
404 TT_Pragma,
405 TT_Stderr,
406 TT_Undef
408 typedef enum CmppTokenType CmppTokenType;
410 struct CmppToken {
411 CmppTokenType ttype;
412 /* Line number of this token in the source file. */
413 unsigned lineNo;
414 /* Start of the token. */
415 unsigned char const * zBegin;
416 /* One-past-the-end byte of the token. */
417 unsigned char const * zEnd;
419 typedef struct CmppToken CmppToken;
420 #define CmppToken_empty_m {TT_Invalid,0,0,0}
421 static const CmppToken CmppToken_empty = CmppToken_empty_m;
424 ** CmppLevel represents one "level" of tokenization, starting at the
425 ** top of the main input, incrementing once for each level of `#if`,
426 ** and decrementing for each `#endif`.
428 typedef struct CmppLevel CmppLevel;
429 struct CmppLevel {
430 unsigned short flags;
432 ** Used for controlling which parts of an if/elif/...endif chain
433 ** should get output.
435 unsigned short skipLevel;
436 /* The token which started this level (an 'if' or 'ifnot'). */
437 CmppToken token;
438 CmppParseState pstate;
440 #define CmppLevel_empty_m {0U,0U,CmppToken_empty_m,TS_Start}
441 static const CmppLevel CmppLevel_empty = CmppLevel_empty_m;
442 enum CmppLevel_Flags {
443 /* Max depth of nested `#if` constructs in a single tokenizer. */
444 CmppLevel_Max = 10,
445 /* Max number of keyword arguments. */
446 CmppArgs_Max = 10,
447 /* Flag indicating that output for a CmpLevel should be elided. */
448 CmppLevel_F_ELIDE = 0x01,
450 ** Mask of CmppLevel::flags which are inherited when CmppLevel_push()
451 ** is used.
453 CmppLevel_F_INHERIT_MASK = 0x01
456 typedef struct CmppTokenizer CmppTokenizer;
457 typedef struct CmppKeyword CmppKeyword;
458 typedef void (*cmpp_keyword_f)(CmppKeyword const * pKw, CmppTokenizer * t);
459 struct CmppKeyword {
460 const char *zName;
461 unsigned nName;
462 int bTokenize;
463 CmppTokenType ttype;
464 cmpp_keyword_f xCall;
467 static CmppKeyword const * CmppKeyword_search(const char *zName);
468 static void cmpp_process_keyword(CmppTokenizer * const t);
471 ** Tokenizer for c-pp input files.
473 struct CmppTokenizer {
474 const char * zName; /* Input (file) name for error reporting */
475 unsigned const char * zBegin; /* start of input */
476 unsigned const char * zEnd; /* one-after-the-end of input */
477 unsigned const char * zAnchor; /* start of input or end point of
478 previous token */
479 unsigned const char * zPos; /* current position */
480 unsigned int lineNo; /* line # of current pos */
481 CmppParseState pstate;
482 CmppToken token; /* current token result */
483 struct {
484 unsigned ndx;
485 CmppLevel stack[CmppLevel_Max];
486 } level;
487 /* Args for use in cmpp_keyword_f() impls. */
488 struct {
489 CmppKeyword const * pKw;
490 int argc;
491 const unsigned char * argv[CmppArgs_Max];
492 unsigned char lineBuf[1024];
493 } args;
495 #define CT_level(t) (t)->level.stack[(t)->level.ndx]
496 #define CT_pstate(t) CT_level(t).pstate
497 #define CT_skipLevel(t) CT_level(t).skipLevel
498 #define CLvl_skip(lvl) ((lvl)->skipLevel || ((lvl)->flags & CmppLevel_F_ELIDE))
499 #define CT_skip(t) CLvl_skip(&CT_level(t))
500 #define CmppTokenizer_empty_m { \
501 0,0,0,0,0,1U/*lineNo*/, \
502 TS_Start, \
503 CmppToken_empty_m, \
504 {/*level*/0U,{CmppLevel_empty_m}}, \
505 {/*args*/0,0,{0},{0}} \
507 static const CmppTokenizer CmppTokenizer_empty = CmppTokenizer_empty_m;
509 static void cmpp_t_out(CmppTokenizer * t, void const *z, unsigned int n);
510 /*static void cmpp_t_outf(CmppTokenizer * t, char const *zFmt, ...);*/
513 ** Pushes a new level into the given tokenizer. Fails fatally if
514 ** it's too deep.
516 static void CmppLevel_push(CmppTokenizer * const t);
518 ** Pops a level from the tokenizer. Fails fatally if the top
519 ** level is popped.
521 static void CmppLevel_pop(CmppTokenizer * const t);
523 ** Returns the current level object.
525 static CmppLevel * CmppLevel_get(CmppTokenizer * const t);
528 ** Global app state singleton. */
529 static struct Global {
530 /* main()'s argv[0]. */
531 const char * zArgv0;
533 ** Bytes of the keyword delimiter/prefix. Owned
534 ** elsewhere.
536 const char * zDelim;
537 /* Byte length of this->zDelim. */
538 unsigned short nDelim;
539 /* If true, enables certain debugging output. */
540 int doDebug;
541 /* App's db instance. */
542 sqlite3 * db;
543 /* Output channel. */
544 FileWrapper out;
545 struct {
546 sqlite3_stmt * defIns;
547 sqlite3_stmt * defDel;
548 sqlite3_stmt * defHas;
549 sqlite3_stmt * inclIns;
550 sqlite3_stmt * inclDel;
551 sqlite3_stmt * inclHas;
552 sqlite3_stmt * inclPathAdd;
553 sqlite3_stmt * inclSearch;
554 } stmt;
555 } g = {
556 "?",
557 CMPP_DEFAULT_DELIM/*zDelim*/,
558 (unsigned short) sizeof(CMPP_DEFAULT_DELIM)-1/*nDelim*/,
559 0/*doDebug*/,
560 0/*db*/,
561 FileWrapper_empty_m/*out*/,
562 {/*stmt*/
563 0/*defIns*/, 0/*defDel*/, 0/*defHas*/,
564 0/*inclIns*/, 0/*inclDel*/, 0/*inclHas*/,
565 0/*inclPathAdd*/
570 #if 0
572 ** Outputs a printf()-formatted message to c-pp's global output
573 ** channel.
575 static void g_outf(char const *zFmt, ...);
576 void g_outf(char const *zFmt, ...){
577 va_list va;
578 va_start(va, zFmt);
579 vfprintf(g.out.pFile, zFmt, va);
580 va_end(va);
582 #endif
584 #if 0
585 /* Outputs n bytes from z to c-pp's global output channel. */
586 static void g_out(void const *z, unsigned int n);
587 void g_out(void const *z, unsigned int n){
588 if(1!=fwrite(z, n, 1, g.out.pFile)){
589 int const err = errno;
590 fatal("fwrite() output failed with errno #%d", err);
593 #endif
595 void g_stderrv(char const *zFmt, va_list va){
596 vfprintf(stderr, zFmt, va);
599 void g_stderr(char const *zFmt, ...){
600 va_list va;
601 va_start(va, zFmt);
602 g_stderrv(zFmt, va);
603 va_end(va);
606 void cmpp_t_out(CmppTokenizer * t, void const *z, unsigned int n){
607 g_debug(3,("CT_skipLevel() ?= %d\n",CT_skipLevel(t)));
608 g_debug(3,("CT_skip() ?= %d\n",CT_skip(t)));
609 if(!CT_skip(t)){
610 if(1!=fwrite(z, n, 1, g.out.pFile)){
611 int const err = errno;
612 fatal("fwrite() output failed with errno #%d", err);
617 void CmppLevel_push(CmppTokenizer * const t){
618 CmppLevel * pPrev;
619 CmppLevel * p;
620 if(t->level.ndx+1 == (unsigned)CmppLevel_Max){
621 fatal("%sif nesting level is too deep. Max=%d\n",
622 g.zDelim, CmppLevel_Max);
624 pPrev = &CT_level(t);
625 g_debug(3,("push from tokenizer level=%u flags=%04x\n", t->level.ndx, pPrev->flags));
626 p = &t->level.stack[++t->level.ndx];
627 *p = CmppLevel_empty;
628 p->token = t->token;
629 p->flags = (CmppLevel_F_INHERIT_MASK & pPrev->flags);
630 if(CLvl_skip(pPrev)) p->flags |= CmppLevel_F_ELIDE;
631 g_debug(3,("push to tokenizer level=%u flags=%04x\n", t->level.ndx, p->flags));
634 void CmppLevel_pop(CmppTokenizer * const t){
635 if(!t->level.ndx){
636 fatal("Internal error: CmppLevel_pop() at the top of the stack");
638 g_debug(3,("pop from tokenizer level=%u, flags=%04x skipLevel?=%d\n", t->level.ndx,
639 t->level.stack[t->level.ndx].flags, CT_skipLevel(t)));
640 g_debug(3,("CT_skipLevel() ?= %d\n",CT_skipLevel(t)));
641 g_debug(3,("CT_skip() ?= %d\n",CT_skip(t)));
642 t->level.stack[t->level.ndx--] = CmppLevel_empty;
643 g_debug(3,("pop to tokenizer level=%u, flags=%04x\n", t->level.ndx,
644 t->level.stack[t->level.ndx].flags));
645 g_debug(3,("CT_skipLevel() ?= %d\n",CT_skipLevel(t)));
646 g_debug(3,("CT_skip() ?= %d\n",CT_skip(t)));
649 CmppLevel * CmppLevel_get(CmppTokenizer * const t){
650 return &t->level.stack[t->level.ndx];
654 void db_affirm_rc(int rc, const char * zMsg){
655 if(rc){
656 fatal("Db error #%d %s: %s", rc, zMsg, sqlite3_errmsg(g.db));
660 void db_finalize(sqlite3_stmt *pStmt){
661 sqlite3_finalize(pStmt);
664 int db_step(sqlite3_stmt *pStmt){
665 int const rc = sqlite3_step(pStmt);
666 if(SQLITE_ROW!=rc && SQLITE_DONE!=rc){
667 db_affirm_rc(rc, "from db_step()");
669 return rc;
672 static sqlite3_str * db_str_new(void){
673 sqlite3_str * rc = sqlite3_str_new(g.db);
674 if(!rc) fatal("Alloc failed for sqlite3_str_new()");
675 return rc;
678 static char * db_str_finish(sqlite3_str *s, int * n){
679 int const rc = sqlite3_str_errcode(s);
680 if(rc) fatal("Error #%d from sqlite3_str_errcode()", rc);
681 if(n) *n = sqlite3_str_length(s);
682 char * z = sqlite3_str_finish(s);
683 if(!z) fatal("Alloc failed for sqlite3_str_new()");
684 return z;
687 void db_prepare(sqlite3_stmt **pStmt, const char * zSql, ...){
688 int rc;
689 sqlite3_str * str = db_str_new();
690 char * z = 0;
691 int n = 0;
692 va_list va;
693 if(!str) fatal("sqlite3_str_new() failed");
694 va_start(va, zSql);
695 sqlite3_str_vappendf(str, zSql, va);
696 va_end(va);
697 rc = sqlite3_str_errcode(str);
698 if(rc) fatal("sqlite3_str_errcode() = %d", rc);
699 z = db_str_finish(str, &n);
700 rc = sqlite3_prepare_v2(g.db, z, n, pStmt, 0);
701 if(rc) fatal("Error #%d (%s) preparing: %s",
702 rc, sqlite3_errmsg(g.db), z);
703 sqlite3_free(z);
706 void db_bind_int(sqlite3_stmt *pStmt, int col, int val){
707 int const rc = sqlite3_bind_int(pStmt, col, val);
708 db_affirm_rc(rc,"from db_bind_int()");
711 #if 0
712 void db_bind_null(sqlite3_stmt *pStmt, int col){
713 int const rc = sqlite3_bind_null(pStmt, col);
714 db_affirm_rc(rc,"from db_bind_null()");
716 #endif
718 void db_bind_textn(sqlite3_stmt *pStmt, int col,
719 const char * zStr, int n){
720 int const rc = zStr
721 ? sqlite3_bind_text(pStmt, col, zStr, n, SQLITE_TRANSIENT)
722 : sqlite3_bind_null(pStmt, col);
723 db_affirm_rc(rc,"from db_bind_textn()");
726 void db_bind_text(sqlite3_stmt *pStmt, int col,
727 const char * zStr){
728 db_bind_textn(pStmt, col, zStr, -1);
731 #if 0
732 void db_bind_textv(sqlite3_stmt *pStmt, int col,
733 const char * zFmt, ...){
734 int rc;
735 sqlite3_str * str = db_str_new();
736 int n = 0;
737 char * z;
738 va_list va;
739 va_start(va,zFmt);
740 sqlite3_str_vappendf(str, zFmt, va);
741 va_end(va);
742 z = db_str_finish(str, &n);
743 rc = sqlite3_bind_text(pStmt, col, z, n, sqlite3_free);
744 db_affirm_rc(rc,"from db_bind_textv()");
746 #endif
748 void db_free(void *m){
749 sqlite3_free(m);
752 void db_define_add(const char * zKey){
753 int rc;
754 if(!g.stmt.defIns){
755 db_prepare(&g.stmt.defIns,
756 "INSERT OR REPLACE INTO def(k) VALUES(?)");
758 db_bind_text(g.stmt.defIns, 1, zKey);
759 rc = db_step(g.stmt.defIns);
760 if(SQLITE_DONE != rc){
761 db_affirm_rc(rc, "Stepping INSERT on def");
763 g_debug(2,("define: %s\n",zKey));
764 sqlite3_reset(g.stmt.defIns);
767 int db_define_has(const char * zName){
768 int rc;
769 if(!g.stmt.defHas){
770 db_prepare(&g.stmt.defHas, "SELECT 1 FROM def WHERE k=?");
772 db_bind_text(g.stmt.defHas, 1, zName);
773 rc = db_step(g.stmt.defHas);
774 if(SQLITE_ROW == rc){
775 rc = 1;
776 }else{
777 assert(SQLITE_DONE==rc);
778 rc = 0;
780 g_debug(1,("defined [%s] ?= %d\n",zName, rc));
781 sqlite3_clear_bindings(g.stmt.defHas);
782 sqlite3_reset(g.stmt.defHas);
783 return rc;
787 void db_define_rm(const char * zKey){
788 int rc;
789 int n = 0;
790 const char *zPos = zKey;
791 if(!g.stmt.defDel){
792 db_prepare(&g.stmt.defDel, "DELETE FROM def WHERE k=?");
794 for( ; *zPos && '='!=*zPos; ++n, ++zPos) {}
795 db_bind_text(g.stmt.defDel, 1, zKey);
796 rc = db_step(g.stmt.defDel);
797 if(SQLITE_DONE != rc){
798 db_affirm_rc(rc, "Stepping DELETE on def");
800 g_debug(2,("undefine: %.*s\n",n, zKey));
801 sqlite3_clear_bindings(g.stmt.defDel);
802 sqlite3_reset(g.stmt.defDel);
805 void db_including_add(const char * zKey, const char * zSrc, int srcLine){
806 int rc;
807 if(!g.stmt.inclIns){
808 db_prepare(&g.stmt.inclIns,
809 "INSERT OR FAIL INTO incl(file,srcFile,srcLine) VALUES(?,?,?)");
811 db_bind_text(g.stmt.inclIns, 1, zKey);
812 db_bind_text(g.stmt.inclIns, 2, zSrc);
813 db_bind_int(g.stmt.inclIns, 3, srcLine);
814 rc = db_step(g.stmt.inclIns);
815 if(SQLITE_DONE != rc){
816 db_affirm_rc(rc, "Stepping INSERT on incl");
818 g_debug(2,("inclpath add [%s] from [%s]:%d\n", zKey, zSrc, srcLine));
819 sqlite3_clear_bindings(g.stmt.inclIns);
820 sqlite3_reset(g.stmt.inclIns);
823 void db_include_rm(const char * zKey){
824 int rc;
825 if(!g.stmt.inclDel){
826 db_prepare(&g.stmt.inclDel, "DELETE FROM incl WHERE file=?");
828 db_bind_text(g.stmt.inclDel, 1, zKey);
829 rc = db_step(g.stmt.inclDel);
830 if(SQLITE_DONE != rc){
831 db_affirm_rc(rc, "Stepping DELETE on incl");
833 g_debug(2,("inclpath rm [%s]\n", zKey));
834 sqlite3_clear_bindings(g.stmt.inclDel);
835 sqlite3_reset(g.stmt.inclDel);
838 char * db_include_search(const char * zKey){
839 char * zName = 0;
840 if(!g.stmt.inclSearch){
841 db_prepare(&g.stmt.inclSearch,
842 "SELECT ?1 fn WHERE fileExists(fn) "
843 "UNION ALL SELECT * FROM ("
844 "SELECT replace(dir||'/'||?1, '//','/') AS fn "
845 "FROM inclpath WHERE fileExists(fn) ORDER BY seq"
846 ")");
848 db_bind_text(g.stmt.inclSearch, 1, zKey);
849 if(SQLITE_ROW==db_step(g.stmt.inclSearch)){
850 const unsigned char * z = sqlite3_column_text(g.stmt.inclSearch, 0);
851 zName = z ? sqlite3_mprintf("%s", z) : 0;
852 if(!zName) fatal("Alloc failed");
854 sqlite3_clear_bindings(g.stmt.inclSearch);
855 sqlite3_reset(g.stmt.inclSearch);
856 return zName;
859 static int db_including_has(const char * zName){
860 int rc;
861 if(!g.stmt.inclHas){
862 db_prepare(&g.stmt.inclHas, "SELECT 1 FROM incl WHERE file=?");
864 db_bind_text(g.stmt.inclHas, 1, zName);
865 rc = db_step(g.stmt.inclHas);
866 if(SQLITE_ROW == rc){
867 rc = 1;
868 }else{
869 assert(SQLITE_DONE==rc);
870 rc = 0;
872 g_debug(2,("inclpath has [%s] = %d\n",zName, rc));
873 sqlite3_clear_bindings(g.stmt.inclHas);
874 sqlite3_reset(g.stmt.inclHas);
875 return rc;
878 #if 0
880 ** Fails fatally if the `#include` list contains the given key.
882 static void db_including_check(const char * zKey);
883 void db_including_check(const char * zName){
884 if(db_including_has(zName)){
885 fatal("Recursive include detected: %s\n", zName);
888 #endif
890 void db_include_dir_add(const char * zDir){
891 static int seq = 0;
892 int rc;
893 if(!g.stmt.inclPathAdd){
894 db_prepare(&g.stmt.inclPathAdd,
895 "INSERT OR FAIL INTO inclpath(seq,dir) VALUES(?,?)");
897 db_bind_int(g.stmt.inclPathAdd, 1, ++seq);
898 db_bind_text(g.stmt.inclPathAdd, 2, zDir);
899 rc = db_step(g.stmt.inclPathAdd);
900 if(SQLITE_DONE != rc){
901 db_affirm_rc(rc, "Stepping INSERT on inclpath");
903 g_debug(2,("inclpath add #%d: %s\n",seq, zDir));
904 sqlite3_clear_bindings(g.stmt.inclPathAdd);
905 sqlite3_reset(g.stmt.inclPathAdd);
908 static void cmpp_atexit(void){
909 #define FINI(M) if(g.stmt.M) sqlite3_finalize(g.stmt.M)
910 FINI(defIns); FINI(defDel); FINI(defHas);
911 FINI(inclIns); FINI(inclDel); FINI(inclHas);
912 FINI(inclPathAdd); FINI(inclSearch);
913 #undef FINI
914 FileWrapper_close(&g.out);
915 if(g.db) sqlite3_close(g.db);
919 ** sqlite3 UDF which returns true if its argument refers to an
920 ** accessible file, else false.
922 static void udf_file_exists(
923 sqlite3_context *context,
924 int argc,
925 sqlite3_value **argv
927 const char *zName;
928 (void)(argc); /* Unused parameter */
929 zName = (const char*)sqlite3_value_text(argv[0]);
930 if( zName==0 ) return;
931 sqlite3_result_int(context, 0==access(zName, 0));
934 /* Initialize g.db, failing fatally on error. */
935 static void cmpp_initdb(void){
936 int rc;
937 char * zErr = 0;
938 const char * zSchema =
939 "CREATE TABLE def("
940 "k TEXT PRIMARY KEY NOT NULL"
941 /*"v INTEGER DEFAULT 1"*/
942 ") WITHOUT ROWID;"
943 /* ^^^ defines */
944 "CREATE TABLE incl("
945 "file TEXT PRIMARY KEY NOT NULL,"
946 "srcFile TEXT DEFAULT NULL,"
947 "srcLine INTEGER DEFAULT 0"
948 ") WITHOUT ROWID;"
949 /* ^^^ files currently being included */
950 "CREATE TABLE inclpath("
951 "seq INTEGER UNIQUE, "
952 "dir TEXT PRIMARY KEY NOT NULL ON CONFLICT IGNORE"
954 /* ^^^ include path */
956 assert(0==g.db);
957 if(g.db) return;
958 rc = sqlite3_open_v2(":memory:", &g.db, SQLITE_OPEN_READWRITE, 0);
959 if(rc) fatal("Error opening :memory: db.");
960 rc = sqlite3_exec(g.db, zSchema, 0, 0, &zErr);
961 if(rc) fatal("Error initializing database: %s", zErr);
962 rc = sqlite3_create_function(g.db, "fileExists", 1,
963 SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
964 udf_file_exists, 0, 0);
965 db_affirm_rc(rc, "UDF registration failed.");
969 ** For position zPos, which must be in the half-open range
970 ** [zBegin,zEnd), returns g.nDelim if it is at the start of a line and
971 ** starts with g.zDelim, else returns 0.
973 static unsigned short cmpp_is_delim(unsigned char const *zBegin,
974 unsigned char const *zEnd,
975 unsigned char const *zPos){
976 assert(zEnd>zBegin);
977 assert(zPos<zEnd);
978 assert(zPos>=zBegin);
979 if(zPos>zBegin &&
980 ('\n'!=*(zPos - 1)
981 || ((unsigned)(zEnd - zPos) <= g.nDelim))){
982 return 0;
983 }else if(0==memcmp(zPos, g.zDelim, g.nDelim)){
984 return g.nDelim;
985 }else{
986 return 0;
991 ** Scans t to the next keyword line, emitting all input before that
992 ** which is _not_ a keyword line unless it's elided due to being
993 ** inside a block which elides its content. Returns 0 if no keyword
994 ** line was found, in which case the end of the input has been
995 ** reached, else returns a truthy value and sets up t's state for use
996 ** with cmpp_process_keyword(), which should then be called.
998 static int cmpp_next_keyword_line(CmppTokenizer * const t){
999 unsigned char const * zStart;
1000 unsigned char const * z;
1001 CmppToken * const tok = &t->token;
1002 unsigned short isDelim = 0;
1004 assert(t->zBegin);
1005 assert(t->zEnd > t->zBegin);
1006 if(!t->zPos) t->zPos = t->zBegin;
1007 t->zAnchor = t->zPos;
1008 zStart = z = t->zPos;
1009 *tok = CmppToken_empty;
1010 while(z<t->zEnd
1011 && 0==(isDelim = cmpp_is_delim(t->zBegin, t->zEnd, z))){
1012 ++z;
1014 if(z>zStart){
1015 /* We passed up content */
1016 cmpp_t_out(t, zStart, (unsigned)(z - zStart));
1018 assert(isDelim==0 || isDelim==g.nDelim);
1019 tok->lineNo = t->lineNo += count_lines(zStart, z);
1020 if(isDelim){
1021 /* Handle backslash-escaped newlines */
1022 int isEsc = 0, atEol = 0;
1023 tok->zBegin = z+isDelim;
1024 for( ++z ; z<t->zEnd && 0==atEol; ++z ){
1025 switch((int)*z){
1026 case (int)'\\':
1027 isEsc = 0==isEsc; break;
1028 case (int)'\n':
1029 atEol = 0==isEsc;
1030 isEsc = 0;
1031 ++t->lineNo;
1032 break;
1033 default:
1034 break;
1037 tok->zEnd = atEol ? z-1 : z;
1038 /* Strip leading spaces */
1039 while(tok->zBegin < tok->zEnd && isspace((char)(*tok->zBegin))){
1040 ++tok->zBegin;
1042 tok->ttype = TT_Line;
1043 g_debug(2,("Keyword @ line %u: [[[%.*s]]]\n",
1044 tok->lineNo,
1045 (int)(tok->zEnd-tok->zBegin), tok->zBegin));
1047 t->zPos = z;
1048 if(isDelim){
1049 /* Split t->token into arguments for the line's keyword */
1050 int i, argc = 0, prevChar = 0;
1051 const unsigned tokLen = (unsigned)(tok->zEnd - tok->zBegin);
1052 unsigned char * zKwd;
1053 unsigned char * zEsc;
1054 unsigned char * zz;
1056 assert(TT_Line==tok->ttype);
1057 if((unsigned)sizeof(t->args.lineBuf) < tokLen + 1){
1058 fatal("Keyword line is unreasonably long: %.*s",
1059 tokLen, tok->zBegin);
1060 }else if(!tokLen){
1061 fatal("Line #%u has no keyword after delimiter", tok->lineNo);
1063 g_debug(2,("token @ line %u len=%u [[[%.*s]]]\n",
1064 tok->lineNo, tokLen, tokLen, tok->zBegin));
1065 zKwd = &t->args.lineBuf[0];
1066 memcpy(zKwd, tok->zBegin, tokLen);
1067 memset(zKwd + tokLen, 0, sizeof(t->args.lineBuf) - tokLen);
1068 for( zEsc = 0, zz = zKwd; *zz; ++zz ){
1069 /* Convert backslash-escaped newlines to whitespace */
1070 switch((int)*zz){
1071 case (int)'\\':
1072 if(zEsc) zEsc = 0;
1073 else zEsc = zz;
1074 break;
1075 case (int)'\n':
1076 assert(zEsc && "Should not have an unescaped newline?");
1077 if(zEsc==zz-1){
1078 *zEsc = (unsigned char)' ';
1079 /* FIXME?: memmove() lnBuf content one byte to the left here
1080 ** to collapse backslash and newline into a single
1081 ** byte. Also consider collapsing all leading space on the
1082 ** next line. */
1084 zEsc = 0;
1085 *zz = (unsigned char)' ';
1086 break;
1087 default:
1088 zEsc = 0;
1089 break;
1092 t->args.argv[argc++] = zKwd;
1093 for( zz = zKwd; *zz; ++zz ){
1094 if(isspace(*zz)){
1095 *zz = 0;
1096 break;
1099 t->args.pKw = CmppKeyword_search((char const *)zKwd);
1100 if(!t->args.pKw){
1101 fatal("Unknown keyword '%s' at line %u\n", (char const *)zKwd,
1102 tok->lineNo);
1104 for( ++zz ; *zz && isspace(*zz); ++zz ){}
1105 if(t->args.pKw->bTokenize){
1106 for( ; *zz; prevChar = *zz, ++zz ){
1107 /* Split string into word-shaped tokens.
1108 ** TODO ?= quoted strings, for the sake of the
1109 ** #error keyword. */
1110 if(isspace(*zz)){
1111 assert(zz!=zKwd && "Leading space was stripped earlier.");
1112 *zz = 0;
1113 }else{
1114 if(argc == (int)CmppArgs_Max){
1115 fatal("Too many arguments @ line %u: %.*s",
1116 tok->lineNo, tokLen, tok->zBegin);
1117 }else if(zz>zKwd && !prevChar){
1118 t->args.argv[argc++] = zz;
1122 }else{
1123 /* Treat rest of line as one token */
1124 if(*zz) t->args.argv[argc++] = zz;
1126 tok->ttype = t->args.pKw->ttype;
1127 if(g.doDebug>1){
1128 for(i = 0; i < argc; ++i){
1129 g_debug(0,("line %u arg #%d=%s\n",
1130 tok->lineNo, i,
1131 (char const *)t->args.argv[i]));
1134 t->args.argc = argc;
1135 }else{
1136 t->args.pKw = 0;
1137 t->args.argc = 0;
1139 return isDelim;
1142 static void cmpp_kwd__err_prefix(CmppKeyword const * pKw, CmppTokenizer *t,
1143 char const *zPrefix){
1144 g_stderr("%s%s%s @ %s line %u: ",
1145 zPrefix ? zPrefix : "",
1146 zPrefix ? ": " : "",
1147 pKw->zName, t->zName, t->token.lineNo);
1150 /* Internal error reporting helper for cmpp_keyword_f() impls. */
1151 static CMPP_NORETURN void cmpp_kwd__misuse(CmppKeyword const * pKw,
1152 CmppTokenizer *t,
1153 char const *zFmt, ...){
1154 va_list va;
1155 cmpp_kwd__err_prefix(pKw, t, "Fatal error");
1156 va_start(va, zFmt);
1157 fatalv(zFmt, va);
1158 va_end(va);
1161 /* No-op cmpp_keyword_f() impl. */
1162 static void cmpp_kwd_noop(CmppKeyword const * pKw, CmppTokenizer *t){
1163 if(t || pKw){/*unused*/}
1166 /* #error impl. */
1167 static void cmpp_kwd_error(CmppKeyword const * pKw, CmppTokenizer *t){
1168 if(CT_skip(t)) return;
1169 else{
1170 assert(t->args.argc < 3);
1171 const char *zBegin = t->args.argc>1
1172 ? (const char *)t->args.argv[1] : 0;
1173 cmpp_kwd__err_prefix(pKw, t, NULL);
1174 fatal("%s", zBegin ? zBegin : "(no additional info)");
1178 /* Impl. for #define, #undef */
1179 static void cmpp_kwd_define(CmppKeyword const * pKw, CmppTokenizer *t){
1180 if(CT_skip(t)) return;
1181 if(t->args.argc<2){
1182 cmpp_kwd__misuse(pKw, t, "Expecting one or more arguments");
1183 }else{
1184 int i = 1;
1185 void (*func)(const char *) = TT_Define==pKw->ttype
1186 ? db_define_add : db_define_rm;
1187 for( ; i < t->args.argc; ++i){
1188 func( (char const *)t->args.argv[i] );
1193 /* Impl. for #if, #ifnot, #elif, #elifnot. */
1194 static void cmpp_kwd_if(CmppKeyword const * pKw, CmppTokenizer *t){
1195 int buul;
1196 CmppParseState tmpState = TS_Start;
1197 if(t->args.argc!=2){
1198 cmpp_kwd__misuse(pKw, t, "Expecting exactly 1 argument");
1200 /*g_debug(0,("%s %s level %u pstate=%d\n", pKw->zName,
1201 (char const *)t->args.argv[1],
1202 t->level.ndx, (int)CT_pstate(t)));*/
1203 switch(pKw->ttype){
1204 case TT_Elif:
1205 case TT_ElifNot:
1206 switch(CT_pstate(t)){
1207 case TS_If: break;
1208 case TS_IfPassed: CT_level(t).flags |= CmppLevel_F_ELIDE; return;
1209 default: goto misuse;
1211 break;
1212 case TT_If:
1213 case TT_IfNot:
1214 CmppLevel_push(t);
1215 break;
1216 default:
1217 cmpp_kwd__misuse(pKw, t, "Unpexected keyword token type");
1218 break;
1220 buul = db_define_has((char const *)t->args.argv[1]);
1221 if(TT_IfNot==pKw->ttype || TT_ElifNot==pKw->ttype) buul = !buul;
1222 if(buul){
1223 CT_pstate(t) = tmpState = TS_IfPassed;
1224 CT_skipLevel(t) = 0;
1225 }else{
1226 CT_pstate(t) = TS_If /* also for TT_IfNot, TT_Elif, TT_ElifNot */;
1227 CT_skipLevel(t) = 1;
1228 g_debug(3,("setting CT_skipLevel = 1 @ level %d\n", t->level.ndx));
1230 if(TT_If==pKw->ttype || TT_IfNot==pKw->ttype){
1231 unsigned const lvlIf = t->level.ndx;
1232 CmppToken const lvlToken = CT_level(t).token;
1233 while(cmpp_next_keyword_line(t)){
1234 cmpp_process_keyword(t);
1235 if(lvlIf > t->level.ndx){
1236 assert(TT_EndIf == t->token.ttype);
1237 break;
1239 #if 0
1240 if(TS_IfPassed==tmpState){
1241 tmpState = TS_Start;
1242 t->level.stack[lvlIf].flags |= CmppLevel_F_ELIDE;
1243 g_debug(1,("Setting ELIDE for TS_IfPassed @ lv %d (lvlIf=%d)\n", t->level.ndx, lvlIf));
1245 #endif
1247 if(lvlIf <= t->level.ndx){
1248 cmpp_kwd__err_prefix(pKw, t, NULL);
1249 fatal("Input ended inside an unterminated %sif "
1250 "opened at [%s] line %u",
1251 g.zDelim, t->zName, lvlToken.lineNo);
1254 return;
1255 misuse:
1256 cmpp_kwd__misuse(pKw, t, "'%s' used out of context",
1257 pKw->zName);
1260 /* Impl. for #else. */
1261 static void cmpp_kwd_else(CmppKeyword const * pKw, CmppTokenizer *t){
1262 if(t->args.argc>1){
1263 cmpp_kwd__misuse(pKw, t, "Expecting no arguments");
1265 switch(CT_pstate(t)){
1266 case TS_IfPassed: CT_skipLevel(t) = 1; break;
1267 case TS_If: CT_skipLevel(t) = 0; break;
1268 default:
1269 cmpp_kwd__misuse(pKw, t, "'%s' with no matching 'if'",
1270 pKw->zName);
1272 /*g_debug(0,("else flags=0x%02x skipLevel=%u\n",
1273 CT_level(t).flags, CT_level(t).skipLevel));*/
1274 CT_pstate(t) = TS_Else;
1277 /* Impl. for #endif. */
1278 static void cmpp_kwd_endif(CmppKeyword const * pKw, CmppTokenizer *t){
1279 /* Maintenance reminder: we ignore all arguments after the endif
1280 ** to allow for constructs like:
1282 ** #endif // foo
1284 ** in a manner which does not require a specific comment style */
1285 switch(CT_pstate(t)){
1286 case TS_Else:
1287 case TS_If:
1288 case TS_IfPassed:
1289 break;
1290 default:
1291 cmpp_kwd__misuse(pKw, t, "'%s' with no matching 'if'",
1292 pKw->zName);
1294 CmppLevel_pop(t);
1297 /* Impl. for #include. */
1298 static void cmpp_kwd_include(CmppKeyword const * pKw, CmppTokenizer *t){
1299 char const * zFile;
1300 char * zResolved;
1301 if(CT_skip(t)) return;
1302 else if(t->args.argc!=2){
1303 cmpp_kwd__misuse(pKw, t, "Expecting exactly 1 filename argument");
1305 zFile = (const char *)t->args.argv[1];
1306 if(db_including_has(zFile)){
1307 /* Note that different spellings of the same filename
1308 ** will elude this check, but that seems okay, as different
1309 ** spellings means that we're not re-running the exact same
1310 ** invocation. We might want some other form of multi-include
1311 ** protection, rather than this, however. There may well be
1312 ** sensible uses for recursion. */
1313 cmpp_kwd__err_prefix(pKw, t, NULL);
1314 fatal("Recursive include of file: %s", zFile);
1316 zResolved = db_include_search(zFile);
1317 if(zResolved){
1318 db_including_add(zFile, t->zName, t->token.lineNo);
1319 cmpp_process_file(zResolved);
1320 db_include_rm(zFile);
1321 db_free(zResolved);
1322 }else{
1323 cmpp_kwd__err_prefix(pKw, t, NULL);
1324 fatal("file not found: %s", zFile);
1328 /* Impl. for #pragma. */
1329 static void cmpp_kwd_pragma(CmppKeyword const * pKw, CmppTokenizer *t){
1330 const char * zArg;
1331 if(CT_skip(t)) return;
1332 else if(t->args.argc!=2){
1333 cmpp_kwd__misuse(pKw, t, "Expecting one argument");
1335 zArg = (const char *)t->args.argv[1];
1336 #define M(X) 0==strcmp(zArg,X)
1337 if(M("defines")){
1338 sqlite3_stmt * q = 0;
1339 db_prepare(&q, "SELECT k FROM def ORDER BY k");
1340 g_stderr("cmpp defines:\n");
1341 while(SQLITE_ROW==db_step(q)){
1342 int const n = sqlite3_column_bytes(q, 0);
1343 const char * z = (const char *)sqlite3_column_text(q, 0);
1344 g_stderr("\t%.*s\n", n, z);
1346 db_finalize(q);
1347 }else{
1348 cmpp_kwd__misuse(pKw, t, "Unknown pragma");
1350 #undef M
1353 /* #stder impl. */
1354 static void cmpp_kwd_stderr(CmppKeyword const * pKw, CmppTokenizer *t){
1355 if(CT_skip(t)) return;
1356 else{
1357 const char *zBegin = t->args.argc>1
1358 ? (const char *)t->args.argv[1] : 0;
1359 if(zBegin){
1360 g_stderr("%s:%u: %s\n", t->zName, t->token.lineNo, zBegin);
1361 }else{
1362 g_stderr("%s:%u: (no %.*s%s argument)\n",
1363 t->zName, t->token.lineNo,
1364 g.nDelim, g.zDelim, pKw->zName);
1369 #if 0
1370 /* Impl. for dummy placeholder. */
1371 static void cmpp_kwd_todo(CmppKeyword const * pKw, CmppTokenizer *t){
1372 if(t){/*unused*/}
1373 g_debug(0,("TODO: keyword handler for %s\n", pKw->zName));
1375 #endif
1377 CmppKeyword aKeywords[] = {
1378 /* Keep these sorted by zName */
1379 {"//", 2, 0, TT_Comment, cmpp_kwd_noop},
1380 {"define", 6, 1, TT_Define, cmpp_kwd_define},
1381 {"elif", 4, 1, TT_Elif, cmpp_kwd_if},
1382 {"elifnot", 7, 1, TT_ElifNot, cmpp_kwd_if},
1383 {"else", 4, 1, TT_Else, cmpp_kwd_else},
1384 {"endif", 5, 0, TT_EndIf, cmpp_kwd_endif},
1385 {"error", 4, 0, TT_Error, cmpp_kwd_error},
1386 {"if", 2, 1, TT_If, cmpp_kwd_if},
1387 {"ifnot", 5, 1, TT_IfNot, cmpp_kwd_if},
1388 {"include", 7, 0, TT_Include, cmpp_kwd_include},
1389 {"pragma", 6, 1, TT_Pragma, cmpp_kwd_pragma},
1390 {"stderr", 6, 0, TT_Stderr, cmpp_kwd_stderr},
1391 {"undef", 5, 1, TT_Undef, cmpp_kwd_define},
1392 {0,0,TT_Invalid, 0}
1395 static int cmp_CmppKeyword(const void *p1, const void *p2){
1396 char const * zName = (const char *)p1;
1397 CmppKeyword const * kw = (CmppKeyword const *)p2;
1398 return strcmp(zName, kw->zName);
1401 CmppKeyword const * CmppKeyword_search(const char *zName){
1402 return (CmppKeyword const *)bsearch(zName, &aKeywords[0],
1403 sizeof(aKeywords)/sizeof(aKeywords[0]) - 1,
1404 sizeof(aKeywords[0]),
1405 cmp_CmppKeyword);
1408 void cmpp_process_keyword(CmppTokenizer * const t){
1409 assert(t->args.pKw);
1410 assert(t->args.argc);
1411 t->args.pKw->xCall(t->args.pKw, t);
1412 t->args.pKw = 0;
1413 t->args.argc = 0;
1416 void cmpp_process_file(const char * zName){
1417 FileWrapper fw = FileWrapper_empty;
1418 CmppTokenizer ct = CmppTokenizer_empty;
1420 FileWrapper_open(&fw, zName, "r");
1421 FileWrapper_slurp(&fw);
1422 g_debug(1,("Read %u byte(s) from [%s]\n", fw.nContent, fw.zName));
1423 ct.zName = zName;
1424 ct.zBegin = fw.zContent;
1425 ct.zEnd = fw.zContent + fw.nContent;
1426 while(cmpp_next_keyword_line(&ct)){
1427 cmpp_process_keyword(&ct);
1429 FileWrapper_close(&fw);
1430 if(0!=ct.level.ndx){
1431 CmppLevel * const lv = CmppLevel_get(&ct);
1432 fatal("Input ended inside an unterminated nested construct"
1433 "opened at [%s] line %u", zName, lv->token.lineNo);
1437 static void usage(int isErr){
1438 FILE * const fOut = isErr ? stderr : stdout;
1439 fprintf(fOut,
1440 "Usage: %s [flags] [infile]\n"
1441 "Flags:\n",
1442 g.zArgv0);
1443 #define arg(F,D) fprintf(fOut," %s\n %s\n",F, D)
1444 arg("-f|--file FILE","Read input from FILE (default=- (stdin)).\n"
1445 " Alternately, the first non-flag argument is assumed to "
1446 "be the input file.");
1447 arg("-o|--outfile FILE","Send output to FILE (default=- (stdout))");
1448 arg("-DXYZ","Define XYZ to true");
1449 arg("-UXYZ","Undefine XYZ (equivalent to false)");
1450 arg("-IXYZ","Add dir XYZ to include path");
1451 arg("-d|--delimiter VALUE", "Set keyword delimiter to VALUE "
1452 "(default=" CMPP_DEFAULT_DELIM ")");
1453 #undef arg
1454 fputs("",fOut);
1457 int main(int argc, char const * const * argv){
1458 int rc = 0;
1459 int i;
1460 int inclCount = 0;
1461 const char * zInfile = 0;
1462 #define M(X) (0==strcmp(X,zArg))
1463 #define ISFLAG(X) else if(M(X))
1464 #define ISFLAG2(X,Y) else if(M(X) || M(Y))
1465 #define ARGVAL \
1466 if(i+1>=argc) fatal("Missing value for flag '%s'", zArg); \
1467 zArg = argv[++i]
1468 g.zArgv0 = argv[0];
1469 atexit(cmpp_atexit);
1470 cmpp_initdb();
1471 for(i = 1; i < argc; ++i){
1472 char const * zArg = argv[i];
1473 while('-'==*zArg) ++zArg;
1474 if(M("?") || M("help")) {
1475 usage(0);
1476 goto end;
1477 }else if('D'==*zArg){
1478 ++zArg;
1479 if(!*zArg) fatal("Missing key for -D");
1480 db_define_add(zArg);
1481 }else if('U'==*zArg){
1482 ++zArg;
1483 if(!*zArg) fatal("Missing key for -U");
1484 db_define_rm(zArg);
1485 }else if('I'==*zArg){
1486 ++zArg;
1487 if(!*zArg) fatal("Missing directory for -I");
1488 db_include_dir_add(zArg);
1489 ++inclCount;
1491 ISFLAG2("o","outfile"){
1492 ARGVAL;
1493 if(g.out.zName) fatal("Cannot use -o more than once.");
1494 g.out.zName = zArg;
1496 ISFLAG2("f","file"){
1497 ARGVAL;
1498 do_infile:
1499 if(zInfile) fatal("Cannot use -i more than once.");
1500 zInfile = zArg;
1502 ISFLAG2("d","delimiter"){
1503 ARGVAL;
1504 g.zDelim = zArg;
1505 g.nDelim = (unsigned short)strlen(zArg);
1506 if(!g.nDelim) fatal("Keyword delimiter may not be empty.");
1508 ISFLAG("debug"){
1509 ++g.doDebug;
1510 }else if(!zInfile){
1511 goto do_infile;
1512 }else{
1513 fatal("Unhandled flag: %s", argv[i]);
1516 if(!zInfile) zInfile = "-";
1517 if(!g.out.zName) g.out.zName = "-";
1518 if(!inclCount) db_include_dir_add(".");
1519 FileWrapper_open(&g.out, g.out.zName, "w");
1520 cmpp_process_file(zInfile);
1521 FileWrapper_close(&g.out);
1522 end:
1523 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
1526 #undef CT_level
1527 #undef CT_pstate
1528 #undef CT_skipLevel
1529 #undef CT_skip
1530 #undef CLvl_skip