#!/usr/bin/tclsh # # Use this script to build C-language source code for a program that uses # tclsqlite.c together with custom TCL scripts and/or C extensions for # either SQLite or TCL. # # Usage example: # # tclsh mkccode.tcl -DENABLE_FEATURE_XYZ demoapp.c.in >demoapp.c # # The demoapp.c.in file contains a mixture of C code, TCL script, and # processing directives used by mktclsqliteprog.tcl to build the final C-code # output file. Most lines of demoapp.c.in are copied straight through into # the output. The following control directives are recognized: # # BEGIN_STRING # # This marks the beginning of large string literal - usually a TCL # script of some kind. Subsequent lines of text through the first # line that begins with END_STRING are converted into a C-language # string literal. # # INCLUDE path # # The path argument is the name of a file to be inserted in place of # the INCLUDE line. The path can begin with $ROOT to signify the # root of the SQLite source tree, or $HOME to signify the directory # that contains the demoapp.c.in input script itself. If the path does # not begin with either $ROOT or $HOME, then it is interpreted relative # to the current working directory. # # If the INCLUDE occurs in the middle of BEGIN_STRING...END_STRING # then all of the text in the input file is converted into C-language # string literals. # # IFDEF macro # IFNDEF macro # ELSE # ENDIF # # The text from "IFDEF macro" down to the next ELSE or ENDIF is # included only if -Dmacro appears as a command-line argument. # The "IFNDEF macro" simply inverts the initial test. # # None of the control directives described above will nest. Only the # top-level input file ("demoapp.c.in" in the example) is interpreted. # referenced files are copied verbatim. # proc usage {} { puts stderr "Usage: $::argv0 \[OPTIONS\] TEMPLATE >OUTPUT" exit 1 } set infile {} foreach ax $argv { if {[string match -D* $ax]} { if {[string match *=* $ax]} { regexp -- {-D([^=]+)=(.*)} $ax all name value set DEF($name) $value } else { set DEF([string range $ax 2 end]) 1 } continue } if {[string match -* $ax]} { puts stderr "$::argv0: Unknown option \"$ax\"" usage } if {$infile!=""} { puts stderr "$::argv0: Surplus argument: \"$ax\"" usage } set infile $ax } set ROOT [file normalize [file dir $argv0]/..] set HOME [file normalize [file dir $infile]] set in [open $infile rb] puts [subst {/* DO NOT EDIT ** ** This file was generated by \"$argv0 $argv\". ** To make changes, edit $infile then rerun the generator ** command. */}] set instr 0 set omit {} set nomit 0 set ln 0 while {1} { set line [gets $in] incr ln if {[eof $in]} break if {[regexp {^INCLUDE (.*)} $line all path]} { if {$nomit>0 && [string match *1* $omit]} continue if {0} { # https://github.com/msteveb/jimtcl/issues/320 regsub {^\$ROOT\y} $path $ROOT path regsub {^\$HOME\y} $path $HOME path } else { set path [string map "\$ROOT $ROOT" $path] set path [string map "\$HOME $HOME" $path] # or: set path [string map "\$HOME $HOME \$ROOT $ROOT" $path] } set in2 [open $path rb] puts "/* INCLUDE $path */" if {$instr} { while {1} { set line [gets $in2] if {[eof $in2]} break set x [string map "\\\\ \\\\\\\\ \\\" \\\\\"" $line] puts "\"$x\\n\"" } } else { puts [read $in2] } puts "/* END $path */" close $in2 continue } if {[regexp {^BEGIN_STRING} $line]} { set instr 1 puts "/* BEGIN_STRING */" continue } if {[regexp {^END_STRING} $line]} { set instr 0 puts "/* END_STRING */" continue } if {[regexp {^IFNDEF +([A-Za-z_0-9]+)} $line all name]} { set omit $omit[info exists DEF($name)] incr nomit continue } if {[regexp {^IFDEF +([A-Za-z_0-9]+)} $line all name]} { set omit $omit[expr {![info exists DEF($name)]}] incr nomit continue } if {[regexp {^ELSE} $line]} { if {!$nomit} { puts stderr "$infile:$ln: ELSE without a prior IFDEF" exit 1 } set omit [string range $omit 0 end-1][expr {![string index $omit end]}] continue } if {[regexp {^ENDIF} $line]} { if {!$nomit} { puts stderr "$infile:$ln: ENDIF without a prior IFDEF" exit 1 } incr nomit -1 set omit [string range $omit 0 [expr {$nomit-1}]] continue } if {$nomit>0 && [string match *1* $omit]} { # noop } elseif {$instr} { set x [string map "\\\\ \\\\\\\\ \\\" \\\\\"" $line] puts "\"$x\\n\"" } else { puts $line } } if {$nomit} { puts stderr "$infile:$ln: One or more unterminated IFDEFs" exit 1 }