4 ** In place of a legal notice, here is a blessing:
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.
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`.
61 ** - Stephan Beal <https://wanderinghorse.net/home/stephan/>
74 #if defined(_WIN32) || defined(WIN32)
78 # define access(f,m) _access((f),(m))
84 #ifndef CMPP_DEFAULT_DELIM
85 #define CMPP_DEFAULT_DELIM "##"
89 # define CMPP_NORETURN __attribute__((noreturn))
91 # define CMPP_NORETURN
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
);
103 /** A malloc() proxy which dies fatally on allocation error. */
104 static void * cmpp_malloc(unsigned n
);
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
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
,
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
);
158 ** Proxy for sqlite3_bind_null() which fails fatally on error.
160 static void db_bind_null(sqlite3_stmt
*pStmt
, int col
);
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
);
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
, ...);
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
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
236 static unsigned count_lines(unsigned char const * zFrom
,
237 unsigned char const *zTo
);
240 ** Wrapper around a FILE handle.
247 /* Where FileWrapper_slurp() stores the file's contents. */
248 unsigned char * zContent
;
249 /* Size of this->zContent, as set by FileWrapper_slurp(). */
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
){
277 vfprintf(stderr
, zFmt
, va
);
283 void fatal(char const *zFmt
, ...){
290 void cmpp_free(void *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
);
301 void * cmpp_malloc(unsigned n
){
302 void * const rc
= malloc(n
);
303 if(!rc
) fatal("malloc(%u) failed", n
);
308 FILE * FILE_open(char const *zName
, const char * zMode
){
310 if('-'==zName
[0] && 0==zName
[1]){
311 p
= strstr(zMode
,"w") ? stdout
: stdin
;
313 p
= fopen(zName
, zMode
);
314 if(!p
) fatal("Cannot open file [%s] with mode [%s]", zName
, zMode
);
319 void FILE_close(FILE *p
){
320 if(p
&& p
!=stdout
&& p
!=stderr
){
325 void FILE_slurp(FILE *pFile
, unsigned char **pOut
,
327 unsigned char zBuf
[1024 * 8];
328 unsigned char * pDest
= 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
);
337 if(nAlloc
< nOff
+ n
+ 1){
338 nAlloc
= nOff
+ n
+ 1;
339 pDest
= cmpp_realloc(pDest
, nAlloc
);
341 memcpy(pDest
+ nOff
, zBuf
, n
);
345 if(pDest
) pDest
[nOff
] = 0;
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
,
358 FileWrapper_close(p
);
359 p
->pFile
= FILE_open(zName
, zMode
);
363 void FileWrapper_slurp(FileWrapper
* p
){
364 assert(!p
->zContent
);
366 FILE_slurp(p
->pFile
, &p
->zContent
, &p
->nContent
);
369 unsigned count_lines(unsigned char const * zFrom
, unsigned char const *zTo
){
371 unsigned char const *zPos
= zFrom
;
372 assert(zFrom
&& zTo
);
373 assert(zFrom
<= zTo
);
374 for(; zPos
< zTo
; ++zPos
){
376 case (unsigned)'\n': ++ln
; break;
383 enum CmppParseState
{
390 typedef enum CmppParseState CmppParseState
;
408 typedef enum CmppTokenType CmppTokenType
;
412 /* Line number of this token in the source file. */
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
;
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'). */
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. */
445 /* Max number of keyword arguments. */
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()
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
);
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
479 unsigned const char * zPos
; /* current position */
480 unsigned int lineNo
; /* line # of current pos */
481 CmppParseState pstate
;
482 CmppToken token
; /* current token result */
485 CmppLevel stack
[CmppLevel_Max
];
487 /* Args for use in cmpp_keyword_f() impls. */
489 CmppKeyword
const * pKw
;
491 const unsigned char * argv
[CmppArgs_Max
];
492 unsigned char lineBuf
[1024];
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*/, \
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
516 static void CmppLevel_push(CmppTokenizer
* const t
);
518 ** Pops a level from the tokenizer. Fails fatally if the top
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]. */
533 ** Bytes of the keyword delimiter/prefix. Owned
537 /* Byte length of this->zDelim. */
538 unsigned short nDelim
;
539 /* If true, enables certain debugging output. */
541 /* App's db instance. */
543 /* Output channel. */
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
;
557 CMPP_DEFAULT_DELIM
/*zDelim*/,
558 (unsigned short) sizeof(CMPP_DEFAULT_DELIM
)-1/*nDelim*/,
561 FileWrapper_empty_m
/*out*/,
563 0/*defIns*/, 0/*defDel*/, 0/*defHas*/,
564 0/*inclIns*/, 0/*inclDel*/, 0/*inclHas*/,
572 ** Outputs a printf()-formatted message to c-pp's global output
575 static void g_outf(char const *zFmt
, ...);
576 void g_outf(char const *zFmt
, ...){
579 vfprintf(g
.out
.pFile
, zFmt
, va
);
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
);
595 void g_stderrv(char const *zFmt
, va_list va
){
596 vfprintf(stderr
, zFmt
, va
);
599 void g_stderr(char const *zFmt
, ...){
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
)));
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
){
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
;
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
){
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
){
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()");
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()");
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()");
687 void db_prepare(sqlite3_stmt
**pStmt
, const char * zSql
, ...){
689 sqlite3_str
* str
= db_str_new();
693 if(!str
) fatal("sqlite3_str_new() failed");
695 sqlite3_str_vappendf(str
, zSql
, 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
);
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()");
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()");
718 void db_bind_textn(sqlite3_stmt
*pStmt
, int col
,
719 const char * zStr
, int n
){
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
,
728 db_bind_textn(pStmt
, col
, zStr
, -1);
732 void db_bind_textv(sqlite3_stmt
*pStmt
, int col
,
733 const char * zFmt
, ...){
735 sqlite3_str
* str
= db_str_new();
740 sqlite3_str_vappendf(str
, zFmt
, 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()");
748 void db_free(void *m
){
752 void db_define_add(const char * zKey
){
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
){
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
){
777 assert(SQLITE_DONE
==rc
);
780 g_debug(1,("defined [%s] ?= %d\n",zName
, rc
));
781 sqlite3_clear_bindings(g
.stmt
.defHas
);
782 sqlite3_reset(g
.stmt
.defHas
);
787 void db_define_rm(const char * zKey
){
790 const char *zPos
= zKey
;
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
){
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
){
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
){
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"
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
);
859 static int db_including_has(const char * zName
){
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
){
869 assert(SQLITE_DONE
==rc
);
872 g_debug(2,("inclpath has [%s] = %d\n",zName
, rc
));
873 sqlite3_clear_bindings(g
.stmt
.inclHas
);
874 sqlite3_reset(g
.stmt
.inclHas
);
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
);
890 void db_include_dir_add(const char * zDir
){
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
);
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
,
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){
938 const char * zSchema
=
940 "k TEXT PRIMARY KEY NOT NULL"
941 /*"v INTEGER DEFAULT 1"*/
945 "file TEXT PRIMARY KEY NOT NULL,"
946 "srcFile TEXT DEFAULT NULL,"
947 "srcLine INTEGER DEFAULT 0"
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 */
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
){
978 assert(zPos
>=zBegin
);
981 || ((unsigned)(zEnd
- zPos
) <= g
.nDelim
))){
983 }else if(0==memcmp(zPos
, g
.zDelim
, g
.nDelim
)){
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;
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
;
1011 && 0==(isDelim
= cmpp_is_delim(t
->zBegin
, t
->zEnd
, z
))){
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
);
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
){
1027 isEsc
= 0==isEsc
; break;
1037 tok
->zEnd
= atEol
? z
-1 : z
;
1038 /* Strip leading spaces */
1039 while(tok
->zBegin
< tok
->zEnd
&& isspace((char)(*tok
->zBegin
))){
1042 tok
->ttype
= TT_Line
;
1043 g_debug(2,("Keyword @ line %u: [[[%.*s]]]\n",
1045 (int)(tok
->zEnd
-tok
->zBegin
), tok
->zBegin
));
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
;
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
);
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 */
1076 assert(zEsc
&& "Should not have an unescaped newline?");
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
1085 *zz
= (unsigned char)' ';
1092 t
->args
.argv
[argc
++] = zKwd
;
1093 for( zz
= zKwd
; *zz
; ++zz
){
1099 t
->args
.pKw
= CmppKeyword_search((char const *)zKwd
);
1101 fatal("Unknown keyword '%s' at line %u\n", (char const *)zKwd
,
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. */
1111 assert(zz
!=zKwd
&& "Leading space was stripped earlier.");
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
;
1123 /* Treat rest of line as one token */
1124 if(*zz
) t
->args
.argv
[argc
++] = zz
;
1126 tok
->ttype
= t
->args
.pKw
->ttype
;
1128 for(i
= 0; i
< argc
; ++i
){
1129 g_debug(0,("line %u arg #%d=%s\n",
1131 (char const *)t
->args
.argv
[i
]));
1134 t
->args
.argc
= argc
;
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
,
1153 char const *zFmt
, ...){
1155 cmpp_kwd__err_prefix(pKw
, t
, "Fatal error");
1161 /* No-op cmpp_keyword_f() impl. */
1162 static void cmpp_kwd_noop(CmppKeyword
const * pKw
, CmppTokenizer
*t
){
1163 if(t
|| pKw
){/*unused*/}
1167 static void cmpp_kwd_error(CmppKeyword
const * pKw
, CmppTokenizer
*t
){
1168 if(CT_skip(t
)) return;
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;
1182 cmpp_kwd__misuse(pKw
, t
, "Expecting one or more arguments");
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
){
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)));*/
1206 switch(CT_pstate(t
)){
1208 case TS_IfPassed
: CT_level(t
).flags
|= CmppLevel_F_ELIDE
; return;
1209 default: goto misuse
;
1217 cmpp_kwd__misuse(pKw
, t
, "Unpexected keyword token type");
1220 buul
= db_define_has((char const *)t
->args
.argv
[1]);
1221 if(TT_IfNot
==pKw
->ttype
|| TT_ElifNot
==pKw
->ttype
) buul
= !buul
;
1223 CT_pstate(t
) = tmpState
= TS_IfPassed
;
1224 CT_skipLevel(t
) = 0;
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
);
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
));
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
);
1256 cmpp_kwd__misuse(pKw
, t
, "'%s' used out of context",
1260 /* Impl. for #else. */
1261 static void cmpp_kwd_else(CmppKeyword
const * pKw
, CmppTokenizer
*t
){
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;
1269 cmpp_kwd__misuse(pKw
, t
, "'%s' with no matching 'if'",
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:
1284 ** in a manner which does not require a specific comment style */
1285 switch(CT_pstate(t
)){
1291 cmpp_kwd__misuse(pKw
, t
, "'%s' with no matching 'if'",
1297 /* Impl. for #include. */
1298 static void cmpp_kwd_include(CmppKeyword
const * pKw
, CmppTokenizer
*t
){
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
);
1318 db_including_add(zFile
, t
->zName
, t
->token
.lineNo
);
1319 cmpp_process_file(zResolved
);
1320 db_include_rm(zFile
);
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
){
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)
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
);
1348 cmpp_kwd__misuse(pKw
, t
, "Unknown pragma");
1354 static void cmpp_kwd_stderr(CmppKeyword
const * pKw
, CmppTokenizer
*t
){
1355 if(CT_skip(t
)) return;
1357 const char *zBegin
= t
->args
.argc
>1
1358 ? (const char *)t
->args
.argv
[1] : 0;
1360 g_stderr("%s:%u: %s\n", t
->zName
, t
->token
.lineNo
, zBegin
);
1362 g_stderr("%s:%u: (no %.*s%s argument)\n",
1363 t
->zName
, t
->token
.lineNo
,
1364 g
.nDelim
, g
.zDelim
, pKw
->zName
);
1370 /* Impl. for dummy placeholder. */
1371 static void cmpp_kwd_todo(CmppKeyword
const * pKw
, CmppTokenizer
*t
){
1373 g_debug(0,("TODO: keyword handler for %s\n", pKw
->zName
));
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
},
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]),
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
);
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
));
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
;
1440 "Usage: %s [flags] [infile]\n"
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
")");
1457 int main(int argc
, char const * const * argv
){
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))
1466 if(i+1>=argc) fatal("Missing value for flag '%s'", zArg); \
1469 atexit(cmpp_atexit
);
1471 for(i
= 1; i
< argc
; ++i
){
1472 char const * zArg
= argv
[i
];
1473 while('-'==*zArg
) ++zArg
;
1474 if(M("?") || M("help")) {
1477 }else if('D'==*zArg
){
1479 if(!*zArg
) fatal("Missing key for -D");
1480 db_define_add(zArg
);
1481 }else if('U'==*zArg
){
1483 if(!*zArg
) fatal("Missing key for -U");
1485 }else if('I'==*zArg
){
1487 if(!*zArg
) fatal("Missing directory for -I");
1488 db_include_dir_add(zArg
);
1491 ISFLAG2("o","outfile"){
1493 if(g
.out
.zName
) fatal("Cannot use -o more than once.");
1496 ISFLAG2("f","file"){
1499 if(zInfile
) fatal("Cannot use -i more than once.");
1502 ISFLAG2("d","delimiter"){
1505 g
.nDelim
= (unsigned short)strlen(zArg
);
1506 if(!g
.nDelim
) fatal("Keyword delimiter may not be empty.");
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
);
1523 return rc
? EXIT_FAILURE
: EXIT_SUCCESS
;