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