| #!/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 |
| } |