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