@@ -1,9 +1,9 @@ #! /usr/bin/env tclsh if {[llength $argv] != 2} { - puts stderr "Usage: dir2c " + puts stderr "Usage: kitdll " exit 1 } set hashkey [lindex $argv 0] @@ -23,11 +23,11 @@ } proc recursive_glob {dir} { set children [glob -nocomplain -directory $dir *] - set ret [list] + set ret [list $dir] foreach child $children { unset -nocomplain childinfo catch { file stat $child childinfo } @@ -39,12 +39,10 @@ if {$childinfo(type) == "directory"} { foreach add [recursive_glob $child] { lappend ret $add } - lappend ret $child - continue } if {$childinfo(type) != "file"} { continue @@ -54,30 +52,12 @@ } return $ret } -proc dir2c_hash {path} { - set h 0 - set g 0 - - for {set idx 0} {$idx < [string length $path]} {incr idx} { - binary scan [string index $path $idx] H* char - set char "0x$char" - - set h [expr {($h << 4) + $char}] - set g [expr {$h & 0xf0000000}] - if {$g != 0} { - set h [expr {($h & 0xffffffff) ^ ($g >> 24)}] - } - - set h [expr {$h & ((~$g) & 0xffffffff)}] - } - - return $h -} - +# Convert a string into a C-style binary string +## XXX: This function needs to be optimized proc stringify {data} { set ret "\"" for {set idx 0} {$idx < [string length $data]} {incr idx} { binary scan [string index $data $idx] H* char @@ -93,36 +73,76 @@ set ret "\"$ret\"" return $ret } +# This function must be kept in-sync with the generated C function below +proc kitdll_hash {path} { + set h 0 + set g 0 + + for {set idx 0} {$idx < [string length $path]} {incr idx} { + binary scan [string index $path $idx] H* char + set char "0x$char" + + set h [expr {($h << 4) + $char}] + set g [expr {$h & 0xf0000000}] + if {$g != 0} { + set h [expr {($h & 0xffffffff) ^ ($g >> 24)}] + } + + set h [expr {$h & ((~$g) & 0xffffffff)}] + } + + return $h +} + +# Generate list of files to include in output set files [recursive_glob $startdir] -set cpp_tag "DIR2C_[string toupper $hashkey]" -set code_tag "dir2c_[string tolower $hashkey]" +# Insert dummy entry cooresponding to C dummy entry +set files [linsert $files 0 "__DUMMY__"] + +# Produce C89 compatible header +set cpp_tag "KITDLL_[string toupper $hashkey]" +set code_tag "kitdll_[string tolower $hashkey]" puts "#ifndef $cpp_tag" puts "# define $cpp_tag 1" -puts {# include +puts { +# ifdef HAVE_STDC +# ifndef HAVE_UNISTD_H +# define HAVE_UNISTD_H 1 +# endif +# ifndef HAVE_STRING_H +# define HAVE_STRING_H 1 +# endif +# endif +# ifdef HAVE_UNISTD_H +# include +# endif +# ifdef HAVE_STRING_H +# include +# endif -# ifndef LOADED_DIR2C_COMMON -# define LOADED_DIR2C_COMMON 1 +# ifndef LOADED_KITDLL_COMMON +# define LOADED_KITDLL_COMMON 1 typedef enum { - DIR2C_FILETYPE_FILE, - DIR2C_FILETYPE_DIR -} dir2c_filetype_t; - -struct dir2c_data { - const char *name; - unsigned long index; - unsigned long size; - dir2c_filetype_t type; - const unsigned char *data; + KITDLL_FILETYPE_FILE, + KITDLL_FILETYPE_DIR +} kitdll_filetype_t; + +struct kitdll_data { + const char * name; + unsigned long index; + unsigned long size; + kitdll_filetype_t type; + const unsigned char * data; }; -static unsigned long dir2c_hash(const unsigned char *path) { +static unsigned long kitdll_hash(const unsigned char *path) { unsigned long i, h = 0, g = 0; for (i = 0; path[i]; i++) { h = (h << 4) + path[i]; g = h & 0xf0000000; @@ -133,101 +153,201 @@ } return(h); } -# endif /* !LOADED_DIR2C_COMMON */} +# endif /* !LOADED_KITDLL_COMMON */} puts "" -puts "static struct dir2c_data ${code_tag}_data\[\] = {" +puts "static struct kitdll_data ${code_tag}_data\[\] = {" puts "\t{" puts "\t\t.name = NULL," puts "\t\t.index = 0," puts "\t\t.type = 0," puts "\t\t.size = 0," puts "\t\t.data = NULL," puts "\t}," -puts "\t{" -puts "\t\t.name = \"\"," -puts "\t\t.index = 1," -puts "\t\t.type = DIR2C_FILETYPE_DIR," -puts "\t\t.size = 0," -puts "\t\t.data = NULL," -puts "\t}," -for {set idx 0} {$idx < [llength $files]} {incr idx} { +for {set idx 1} {$idx < [llength $files]} {incr idx} { set file [lindex $files $idx] set shortfile [shorten_file $startdir $file] unset -nocomplain finfo type file stat $file finfo switch -- $finfo(type) { "file" { - set type "DIR2C_FILETYPE_FILE" + set type "KITDLL_FILETYPE_FILE" set size $finfo(size) set fd [open $file] fconfigure $fd -translation binary set data [read $fd] close $fd - set data [stringify $data] + set data "(unsigned char *) [stringify $data]" } "directory" { - set type "DIR2C_FILETYPE_DIR" + set type "KITDLL_FILETYPE_DIR" set data "NULL" set size 0 } } puts "\t{" puts "\t\t.name = \"$shortfile\"," - puts "\t\t.index = [expr $idx + 2]," + puts "\t\t.index = $idx," puts "\t\t.type = $type," puts "\t\t.size = $size," puts "\t\t.data = $data," puts "\t}," } puts "};" puts "" puts "static unsigned long ${code_tag}_lookup_index(const char *path) {" -puts "\tswitch (dir2c_hash(path)) {" -puts "\t\tcase [dir2c_hash {}]:" -puts "\t\t\treturn(1);" +puts "\tswitch (kitdll_hash((unsigned char *) path)) {" -set seenhashes [list] -for {set idx 0} {$idx < [llength $files]} {incr idx} { +for {set idx 1} {$idx < [llength $files]} {incr idx} { set file [lindex $files $idx] set shortfile [shorten_file $startdir $file] - set hash [dir2c_hash $shortfile] - - if {[lsearch -exact $seenhashes $hash] != -1} { - puts stderr "ERROR: Duplicate hash seen: $file ($hash), aborting" - - exit 1 - } - - lappend seenhashes $hash - + set hash [kitdll_hash $shortfile] + + lappend indexes_per_hash($hash) [list $shortfile $idx] +} + +foreach {hash idx_list} [array get indexes_per_hash] { puts "\t\tcase $hash:" - puts "\t\t\treturn([expr $idx + 2]);" + + if {[llength $idx_list] == 1} { + set idx [lindex $idx_list 0 1] + + puts "\t\t\treturn($idx);" + } else { + foreach idx_ent $idx_list { + set shortfile [lindex $idx_ent 0] + set idx [lindex $idx_ent 1] + + puts "\t\t\tif (strcmp(path, \"$shortfile\") == 0) return($idx);" + } + puts "\t\t\tbreak;" + } } puts "\t}" puts "\treturn(0);" puts "}" puts "" -puts "static struct dir2c_data *${code_tag}_getData(const char *path) {" -puts "\tunsigned long index;" +puts "static struct kitdll_data *${code_tag}_getData(const char *path, unsigned long index) {" +puts "\tif (path != NULL) {" +puts "\t\tindex = ${code_tag}_lookup_index(path);" +puts "\t}" puts "" -puts "\tindex = ${code_tag}_lookup_index(path);" puts "\tif (index == 0) {" puts "\t\treturn(NULL);" puts "\t}" +puts "" +puts "\tif (path != NULL) {" +puts "\t\tif (strcmp(path, ${code_tag}_data\[index\].name) != 0) {" +puts "\t\t\treturn(NULL);" +puts "\t\t}" +puts "\t}" puts "" puts "\treturn(&${code_tag}_data\[index\]);" puts "}" puts "" + +puts "static unsigned long ${code_tag}_getChildren(const char *path, unsigned long *outbuf, unsigned long outbuf_count) {" +puts "\tunsigned long index;" +puts "\tunsigned long num_children = 0;" +puts "" +puts "\tindex = ${code_tag}_lookup_index(path);" +puts "\tif (index == 0) {" +puts "\t\treturn(0);" +puts "\t}" +puts "" +puts "\tif (${code_tag}_data\[index\].type != KITDLL_FILETYPE_DIR) {" +puts "\t\treturn(0);" +puts "\t}" +puts "" +puts "\tif (strcmp(path, ${code_tag}_data\[index\].name) != 0) {" +puts "\t\treturn(0);" +puts "\t}" +puts "" +puts "\tswitch (index) {" + +unset -nocomplain children +array set children [list] +for {set idx 1} {$idx < [llength $files]} {incr idx} { + set file [lindex $files $idx] + set shortfile [shorten_file $startdir $file] + + unset -nocomplain finfo type + file stat $file finfo + + if {$finfo(type) != "directory"} { + continue; + } + + # Determine which children are under this directory + ## Convert the current pathname to a regular expression that matches exactly + set file_regexp [string map [list "\\" "\\\\" "." "\\." "\{" "\\\{" "\}" "\\\}" "*" "\\*"] $file] + + ## Search for pathnames which start with exactly our name, followed by a slash + ## followed by no more slashes (direct descendants) + set child_idx_list [lsearch -regexp -all $files "^${file_regexp}/\[^/\]*$"] + + set children($idx) $child_idx_list + + puts "\t\tcase $idx:" + puts "\t\t\tnum_children = [llength $child_idx_list];" + puts "\t\t\tbreak;" + +} + +puts "\t}" +puts "" +puts "\tif (outbuf == NULL) {" +puts "\t\treturn(num_children);" +puts "\t}" +puts "" +puts "\tif (num_children > outbuf_count) {" +puts "\t\tnum_children = outbuf_count;" +puts "\t}" +puts "" +puts "\tif (num_children == 0) {" +puts "\t\treturn(num_children);" +puts "\t}" +puts "" +puts "\tif (outbuf_count > num_children) {" +puts "\t\toutbuf_count = num_children;" +puts "\t}" +puts "" +puts "\tswitch (index) {" + +foreach {idx child_idx_list} [array get children] { + if {[llength $child_idx_list] == 0} { + continue + } + + puts "\t\tcase $idx:" + puts "\t\t\tswitch(outbuf_count) {" + + for {set child_idx_idx [expr {[llength $child_idx_list] - 1}]} {$child_idx_idx >= 0} {incr child_idx_idx -1} { + set child_idx [lindex $child_idx_list $child_idx_idx] + + puts "\t\t\t\tcase [expr {$child_idx_idx + 1}]:" + puts "\t\t\t\t\toutbuf\[$child_idx_idx\] = $child_idx;" + } + + puts "\t\t\t}" + + puts "\t\t\tbreak;" +} + +puts "\t}" +puts "" +puts "\treturn(num_children);" +puts "}" +puts "" puts "#endif /* !$cpp_tag */"