47dcf5fc27 2019-05-01 1: #! /usr/bin/env tclsh
47dcf5fc27 2019-05-01 2:
47dcf5fc27 2019-05-01 3: namespace eval ::xvfs {}
47dcf5fc27 2019-05-01 4:
47dcf5fc27 2019-05-01 5: # Functions
47dcf5fc27 2019-05-01 6: proc ::xvfs::printHelp {channel {errors ""}} {
47dcf5fc27 2019-05-01 7: if {[llength $errors] != 0} {
47dcf5fc27 2019-05-01 8: foreach error $errors {
47dcf5fc27 2019-05-01 9: puts $channel "error: $error"
47dcf5fc27 2019-05-01 10: }
47dcf5fc27 2019-05-01 11: puts $channel ""
47dcf5fc27 2019-05-01 12: }
47dcf5fc27 2019-05-01 13: puts $channel "Usage: dir2c \[--help\] --directory <rootDirectory> --name <fsName>"
47dcf5fc27 2019-05-01 14: flush $channel
47dcf5fc27 2019-05-01 15: }
47dcf5fc27 2019-05-01 16:
47dcf5fc27 2019-05-01 17: proc ::xvfs::sanitizeCString {string} {
47dcf5fc27 2019-05-01 18: set output [join [lmap char [split $string ""] {
47dcf5fc27 2019-05-01 19: if {![regexp {[A-Za-z0-9./-]} $char]} {
47dcf5fc27 2019-05-01 20: binary scan $char H* char
47dcf5fc27 2019-05-01 21: set char "\\[format %03o 0x$char]"
47dcf5fc27 2019-05-01 22: }
47dcf5fc27 2019-05-01 23:
47dcf5fc27 2019-05-01 24: set char
47dcf5fc27 2019-05-01 25: }] ""]
47dcf5fc27 2019-05-01 26:
47dcf5fc27 2019-05-01 27: return $output
47dcf5fc27 2019-05-01 28: }
47dcf5fc27 2019-05-01 29:
32b55a907b 2019-05-02 30: proc ::xvfs::sanitizeCStringList {list {prefix ""} {width 80}} {
32b55a907b 2019-05-02 31: set lines [list]
32b55a907b 2019-05-02 32: set row [list]
32b55a907b 2019-05-02 33: foreach item $list {
32b55a907b 2019-05-02 34: lappend row "\"[sanitizeCString $item]\""
32b55a907b 2019-05-02 35:
32b55a907b 2019-05-02 36: set rowString [join $row {, }]
32b55a907b 2019-05-02 37: set rowString "${prefix}${rowString}"
32b55a907b 2019-05-02 38: if {[string length $rowString] > $width} {
32b55a907b 2019-05-02 39: set row [list]
32b55a907b 2019-05-02 40: lappend lines $rowString
32b55a907b 2019-05-02 41: unset rowString
32b55a907b 2019-05-02 42: }
32b55a907b 2019-05-02 43: }
32b55a907b 2019-05-02 44: if {[info exists rowString]} {
32b55a907b 2019-05-02 45: lappend lines $rowString
32b55a907b 2019-05-02 46: }
32b55a907b 2019-05-02 47:
32b55a907b 2019-05-02 48: return [join $lines "\n"]
32b55a907b 2019-05-02 49: }
32b55a907b 2019-05-02 50:
47dcf5fc27 2019-05-01 51: proc ::xvfs::binaryToCHex {binary {prefix ""} {width 10}} {
47dcf5fc27 2019-05-01 52: binary scan $binary H* binary
47dcf5fc27 2019-05-01 53: set output [list]
47dcf5fc27 2019-05-01 54:
47dcf5fc27 2019-05-01 55: set width [expr {$width * 2}]
47dcf5fc27 2019-05-01 56: set stopAt [expr {$width - 1}]
47dcf5fc27 2019-05-01 57:
47dcf5fc27 2019-05-01 58: while {$binary ne ""} {
47dcf5fc27 2019-05-01 59: set row [string range $binary 0 $stopAt]
47dcf5fc27 2019-05-01 60: set binary [string range $binary $width end]
47dcf5fc27 2019-05-01 61:
47dcf5fc27 2019-05-01 62: set rowOutput [list]
47dcf5fc27 2019-05-01 63: while {$row ne ""} {
47dcf5fc27 2019-05-01 64: set value [string range $row 0 1]
47dcf5fc27 2019-05-01 65: set row [string range $row 2 end]
47dcf5fc27 2019-05-01 66:
47dcf5fc27 2019-05-01 67: lappend rowOutput "\\x$value"
47dcf5fc27 2019-05-01 68: }
47dcf5fc27 2019-05-01 69: set rowOutput [join $rowOutput {}]
47dcf5fc27 2019-05-01 70: set rowOutput "${prefix}\"${rowOutput}\""
47dcf5fc27 2019-05-01 71: lappend output $rowOutput
47dcf5fc27 2019-05-01 72: }
47dcf5fc27 2019-05-01 73:
47dcf5fc27 2019-05-01 74: if {[llength $output] == 0} {
47dcf5fc27 2019-05-01 75: return "${prefix}\"\""
47dcf5fc27 2019-05-01 76: }
47dcf5fc27 2019-05-01 77:
47dcf5fc27 2019-05-01 78: set output [join $output "\n"]
47dcf5fc27 2019-05-01 79: }
47dcf5fc27 2019-05-01 80:
47dcf5fc27 2019-05-01 81: proc ::xvfs::processFile {fsName inputFile outputFile fileInfoDict} {
47dcf5fc27 2019-05-01 82: array set fileInfo $fileInfoDict
47dcf5fc27 2019-05-01 83:
47dcf5fc27 2019-05-01 84: switch -exact -- $fileInfo(type) {
47dcf5fc27 2019-05-01 85: "file" {
47dcf5fc27 2019-05-01 86: set type "XVFS_FILE_TYPE_REG"
47dcf5fc27 2019-05-01 87: set fd [open $inputFile]
47dcf5fc27 2019-05-01 88: fconfigure $fd -encoding binary -translation binary -blocking true
47dcf5fc27 2019-05-01 89: set data [read $fd]
47dcf5fc27 2019-05-01 90: set size [string length $data]
47dcf5fc27 2019-05-01 91: set data [string trimleft [binaryToCHex $data "\t\t\t"]]
47dcf5fc27 2019-05-01 92: close $fd
47dcf5fc27 2019-05-01 93: }
47dcf5fc27 2019-05-01 94: "directory" {
47dcf5fc27 2019-05-01 95: set type "XVFS_FILE_TYPE_DIR"
32b55a907b 2019-05-02 96: set children $fileInfo(children)
32b55a907b 2019-05-02 97: set size [llength $children]
32b55a907b 2019-05-02 98:
32b55a907b 2019-05-02 99: if {$size == 0} {
32b55a907b 2019-05-02 100: set children "NULL"
32b55a907b 2019-05-02 101: } else {
32b55a907b 2019-05-02 102: set children [string trimleft [sanitizeCStringList $children "\t\t\t"]]
32b55a907b 2019-05-02 103: # This initializes it using a C99 compound literal, C99 is required
32b55a907b 2019-05-02 104: set children "(const char *\[\]) \{$children\}"
32b55a907b 2019-05-02 105: }
47dcf5fc27 2019-05-01 106: }
47dcf5fc27 2019-05-01 107: default {
47dcf5fc27 2019-05-01 108: return -code error "Unable to process $inputFile, unknown type: $fileInfo(type)"
47dcf5fc27 2019-05-01 109: }
47dcf5fc27 2019-05-01 110: }
47dcf5fc27 2019-05-01 111:
47dcf5fc27 2019-05-01 112: puts "\t\{"
47dcf5fc27 2019-05-01 113: puts "\t\t.name = \"[sanitizeCString $outputFile]\","
47dcf5fc27 2019-05-01 114: puts "\t\t.type = $type,"
47dcf5fc27 2019-05-01 115: puts "\t\t.size = $size,"
32b55a907b 2019-05-02 116: switch -exact -- $fileInfo(type) {
32b55a907b 2019-05-02 117: "file" {
32b55a907b 2019-05-02 118: puts "\t\t.data.fileContents = (const unsigned char *) $data"
32b55a907b 2019-05-02 119: }
32b55a907b 2019-05-02 120: "directory" {
32b55a907b 2019-05-02 121: puts "\t\t.data.dirChildren = $children"
32b55a907b 2019-05-02 122: }
32b55a907b 2019-05-02 123: }
47dcf5fc27 2019-05-01 124: puts "\t\},"
47dcf5fc27 2019-05-01 125: }
47dcf5fc27 2019-05-01 126:
47dcf5fc27 2019-05-01 127: proc ::xvfs::processDirectory {fsName directory {subDirectory ""}} {
47dcf5fc27 2019-05-01 128: set subDirectories [list]
47dcf5fc27 2019-05-01 129: set outputFiles [list]
47dcf5fc27 2019-05-01 130: set workingDirectory [file join $directory $subDirectory]
47dcf5fc27 2019-05-01 131: set outputDirectory $subDirectory
47dcf5fc27 2019-05-01 132:
47dcf5fc27 2019-05-01 133: if {$subDirectory eq ""} {
47dcf5fc27 2019-05-01 134: set isTopLevel true
47dcf5fc27 2019-05-01 135: } else {
47dcf5fc27 2019-05-01 136: set isTopLevel false
47dcf5fc27 2019-05-01 137: }
47dcf5fc27 2019-05-01 138:
47dcf5fc27 2019-05-01 139: if {$isTopLevel} {
47dcf5fc27 2019-05-01 140: puts "static struct xvfs_file_data xvfs_${fsName}_data\[\] = \{"
47dcf5fc27 2019-05-01 141: }
47dcf5fc27 2019-05-01 142:
47dcf5fc27 2019-05-01 143: # XXX:TODO: Include hidden files ?
32b55a907b 2019-05-02 144: set children [list]
47dcf5fc27 2019-05-01 145: foreach file [glob -nocomplain -tails -directory $workingDirectory *] {
47dcf5fc27 2019-05-01 146: if {$file in {. ..}} {
47dcf5fc27 2019-05-01 147: continue
47dcf5fc27 2019-05-01 148: }
47dcf5fc27 2019-05-01 149:
47dcf5fc27 2019-05-01 150: set inputFile [file join $workingDirectory $file]
d99958bdd3 2019-05-03 151: set outputFile [file join $outputDirectory [encoding convertto utf-8 $file]]
47dcf5fc27 2019-05-01 152:
47dcf5fc27 2019-05-01 153: unset -nocomplain fileInfo
47dcf5fc27 2019-05-01 154: catch {
47dcf5fc27 2019-05-01 155: file lstat $inputFile fileInfo
47dcf5fc27 2019-05-01 156: }
47dcf5fc27 2019-05-01 157: if {![info exists fileInfo]} {
47dcf5fc27 2019-05-01 158: puts stderr "warning: Unable to access $inputFile, skipping"
47dcf5fc27 2019-05-01 159: }
32b55a907b 2019-05-02 160:
32b55a907b 2019-05-02 161: lappend children [file tail $file]
47dcf5fc27 2019-05-01 162:
47dcf5fc27 2019-05-01 163: if {$fileInfo(type) eq "directory"} {
47dcf5fc27 2019-05-01 164: lappend subDirectories $outputFile
32b55a907b 2019-05-02 165: continue
47dcf5fc27 2019-05-01 166: }
47dcf5fc27 2019-05-01 167:
47dcf5fc27 2019-05-01 168: processFile $fsName $inputFile $outputFile [array get fileInfo]
47dcf5fc27 2019-05-01 169: lappend outputFiles $outputFile
47dcf5fc27 2019-05-01 170: }
47dcf5fc27 2019-05-01 171:
47dcf5fc27 2019-05-01 172: foreach subDirectory $subDirectories {
47dcf5fc27 2019-05-01 173: lappend outputFiles {*}[processDirectory $fsName $directory $subDirectory]
47dcf5fc27 2019-05-01 174: }
32b55a907b 2019-05-02 175:
32b55a907b 2019-05-02 176: set inputFile $directory
32b55a907b 2019-05-02 177: set outputFile $outputDirectory
32b55a907b 2019-05-02 178: unset -nocomplain fileInfo
32b55a907b 2019-05-02 179: file stat $inputFile fileInfo
32b55a907b 2019-05-02 180: set fileInfo(children) $children
32b55a907b 2019-05-02 181:
32b55a907b 2019-05-02 182: processFile $fsName $inputFile $outputFile [array get fileInfo]
32b55a907b 2019-05-02 183: lappend outputFiles $outputFile
47dcf5fc27 2019-05-01 184:
47dcf5fc27 2019-05-01 185: if {$isTopLevel} {
47dcf5fc27 2019-05-01 186: puts "\};"
47dcf5fc27 2019-05-01 187: }
47dcf5fc27 2019-05-01 188:
47dcf5fc27 2019-05-01 189: return $outputFiles
47dcf5fc27 2019-05-01 190: }
47dcf5fc27 2019-05-01 191:
47dcf5fc27 2019-05-01 192: proc ::xvfs::main {argv} {
47dcf5fc27 2019-05-01 193: # Main entry point
47dcf5fc27 2019-05-01 194: ## 1. Parse arguments
47dcf5fc27 2019-05-01 195: if {[llength $argv] % 2 != 0} {
47dcf5fc27 2019-05-01 196: lappend argv ""
47dcf5fc27 2019-05-01 197: }
47dcf5fc27 2019-05-01 198:
47dcf5fc27 2019-05-01 199: foreach {arg val} $argv {
47dcf5fc27 2019-05-01 200: switch -exact -- $arg {
47dcf5fc27 2019-05-01 201: "--help" {
47dcf5fc27 2019-05-01 202: printHelp stdout
47dcf5fc27 2019-05-01 203: exit 0
47dcf5fc27 2019-05-01 204: }
47dcf5fc27 2019-05-01 205: "--directory" {
47dcf5fc27 2019-05-01 206: set rootDirectory $val
47dcf5fc27 2019-05-01 207: }
47dcf5fc27 2019-05-01 208: "--name" {
47dcf5fc27 2019-05-01 209: set fsName $val
47dcf5fc27 2019-05-01 210: }
47dcf5fc27 2019-05-01 211: default {
47dcf5fc27 2019-05-01 212: printHelp stderr [list "Invalid option: $arg $val"]
47dcf5fc27 2019-05-01 213: exit 1
47dcf5fc27 2019-05-01 214: }
47dcf5fc27 2019-05-01 215: }
47dcf5fc27 2019-05-01 216: }
47dcf5fc27 2019-05-01 217:
47dcf5fc27 2019-05-01 218: ## 2. Validate arguments
47dcf5fc27 2019-05-01 219: set errors [list]
47dcf5fc27 2019-05-01 220: if {![info exists rootDirectory]} {
47dcf5fc27 2019-05-01 221: lappend errors "--directory must be specified"
47dcf5fc27 2019-05-01 222: }
47dcf5fc27 2019-05-01 223: if {![info exists fsName]} {
47dcf5fc27 2019-05-01 224: lappend errors "--name must be specified"
47dcf5fc27 2019-05-01 225: }
47dcf5fc27 2019-05-01 226:
47dcf5fc27 2019-05-01 227: if {[llength $errors] != 0} {
47dcf5fc27 2019-05-01 228: printHelp stderr $errors
47dcf5fc27 2019-05-01 229: exit 1
47dcf5fc27 2019-05-01 230: }
47dcf5fc27 2019-05-01 231:
47dcf5fc27 2019-05-01 232: ## 3. Start processing directory and producing initial output
32b55a907b 2019-05-02 233: set ::xvfs::outputFiles [processDirectory $fsName $rootDirectory]
47dcf5fc27 2019-05-01 234:
47dcf5fc27 2019-05-01 235: set ::xvfs::fsName $fsName
47dcf5fc27 2019-05-01 236: set ::xvfs::rootDirectory $rootDirectory
47dcf5fc27 2019-05-01 237: }
47dcf5fc27 2019-05-01 238:
47dcf5fc27 2019-05-01 239: package provide xvfs 1