ADDED .fossil-settings/crnl-glob Index: .fossil-settings/crnl-glob ================================================================== --- .fossil-settings/crnl-glob +++ .fossil-settings/crnl-glob @@ -0,0 +1,1 @@ +* ADDED eagle/1.0/data1.0/data.eagle Index: eagle/1.0/data1.0/data.eagle ================================================================== --- eagle/1.0/data1.0/data.eagle +++ eagle/1.0/data1.0/data.eagle @@ -0,0 +1,1139 @@ +############################################################################### +# +# data.eagle -- +# +# Extensible Adaptable Generalized Logic Engine (Eagle) +# Data Utility Package +# +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. +# +# See the file "license.terms" for information on usage and redistribution of +# this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id: $ +# +############################################################################### + +# +# NOTE: Use our own namespace here because even though we do not directly +# support namespaces ourselves, we do not want to pollute the global +# namespace if this script actually ends up being evaluated in Tcl. +# +namespace eval ::Eagle { + # + # NOTE: This procedure is used to report errors during the various data + # processing operations. In "strict" mode, these errors are always + # fatal; otherwise, the errors are kept in a global "errors" variable + # for later reporting. + # + proc report { {message ""} {data ""} {strict 0} } { + if {$strict} then { + error [list message $message data $data] + } else { + lappend ::errors [list message $message data $data] + + if {[lindex [info level -1] 0] ne "fail"} then { + if {[string length $message] > 0} then { + host result Error [appendArgs \n "message: " $message \n] + } + + if {[string length $data] > 0} then { + host result Error [appendArgs \n "data: " $data \n] + } + } + } + } + + proc fail { {error ""} } { + report $error "" 0; # NOTE: Non-strict, report only. + + if {[string length $error] > 0} then { + putsStdout $error + } + + if {[info exists ::usage]} then { + putsStdout $::usage + } + + error $error + } + + proc getArchitecture {} { + if {[info exists ::tcl_platform(machine)] && \ + [string length $::tcl_platform(machine)] > 0} then { + # + # NOTE: Check for the "amd64" (i.e. "x64") architecture. + # + if {$::tcl_platform(machine) eq "amd64"} then { + return x64 + } + + # + # NOTE: Check for the "ia32_on_win64" (i.e. "WoW64") architecture. + # + if {$::tcl_platform(machine) eq "ia32_on_win64"} then { + return x86 + } + + # + # NOTE: Check for the "ia64" architecture. + # + if {$::tcl_platform(machine) eq "ia64"} then { + return ia64 + } + + # + # NOTE: Check for the "intel" (i.e. "x86") architecture. + # + if {$::tcl_platform(machine) eq "intel"} then { + return x86 + } + + # + # NOTE: We do not support this architecture. + # + putsStdout [appendArgs "Machine \"" $::tcl_platform(machine) \ + "\" is unsupported."] + + return unknown + } + + putsStdout [appendArgs "Machine detection failed."] + + return none + } + + # + # NOTE: With the native library pre-loading feature and a proper application + # local deployment of System.Data.SQLite, as shown here: + # + # * \System.Data.SQLite.dll (managed-only core assembly) + # * \x86\SQLite.Interop.dll (x86 native interop assembly) + # * \x64\SQLite.Interop.dll (x64 native interop assembly) + # + # -OR- + # + # * \System.Data.SQLite.dll (managed-only core assembly) + # * \x86\sqlite3.dll (x86 native library) + # * \x64\sqlite3.dll (x64 native library) + # + # Using this procedure is no longer necessary. + # + proc setupForSQLite { path } { + # + # NOTE: The toolPath is the directory where the caller is running from. + # + set toolPath $path + + putsStdout [appendArgs "Tool path is \"" $toolPath "\"."] + + # + # NOTE: The externalsPath is the parent of the application root directory, + # which should be the Externals directory (i.e. the one containing + # the "sqlite3" and "System.Data.SQLite" directories). If this is + # not the case, append "Externals" to the directory and try there. + # + set externalsPath [file dirname $toolPath] + + if {[file tail $externalsPath] ne "Externals"} then { + set externalsPath [file join $externalsPath Externals] + } + + putsStdout [appendArgs "Externals path is \"" $externalsPath "\"."] + + # + # NOTE: This is the list of file names we need to copy into the + # application binary directory. Currently, this includes the + # "sqlite3.dll" and "System.Data.SQLite.dll" files, which are + # necessary when using SQLite from Eagle. + # + set fileNames [list \ + [file join $externalsPath sqlite3 [getArchitecture] sqlite3.dll] \ + [file join $externalsPath System.Data.SQLite System.Data.SQLite.dll]] + + # + # NOTE: The binaryPath is the directory where the application is running + # from. + # + set binaryPath [info binary] + + putsStdout [appendArgs "Binary path is \"" $binaryPath "\"."] + + # + # NOTE: Attempt to copy each of the files we need to the application + # binary directory. Hopefully, the CLR will be able to load them + # from there. + # + foreach fileName $fileNames { + if {![file exists $fileName]} then { + # + # NOTE: It seems the source file does not exist, skip it. + # + putsStdout [appendArgs "File \"" $fileName "\" does not exist."] + + continue + } + + set justFileName [file tail $fileName] + set newFileName [file join $binaryPath $justFileName] + + if {$justFileName eq "sqlite3.dll"} then { + set magic 0 + set error null + + if {![object invoke Eagle._Components.Private.FileOps \ + CheckPeFileArchitecture $fileName magic error]} then { + # + # NOTE: The "sqlite3.dll" file does not match the current operating + # system architecture (e.g. 32-bit DLL on 64-bit Windows). + # + fail [object invoke $error ToString] + } else { + putsStdout [appendArgs "File \"" $fileName "\" PE magic OK (" \ + [string format "0x{0:X}" $magic] ")."] + } + } + + if {![file exists $newFileName]} then { + # + # NOTE: The destination file does not exist, copy it. + # + file copy $fileName $newFileName + + putsStdout [appendArgs "Copied \"" $fileName "\" to \"" \ + $newFileName "\"."] + } else { + # + # NOTE: It seems the destination file already exists, skip it. + # + putsStdout [appendArgs "File \"" $newFileName "\" exists."] + } + } + } + + proc showTime { name script } { + putsStdout [appendArgs "\n\nStarted " $name " at " \ + [clock format [clock seconds]] .] + + set elapsed [time {uplevel 1 $script}] + + putsStdout [appendArgs "\n\nStopped " $name " at " \ + [clock format [clock seconds]] .] + + putsStdout [appendArgs "Completed in " \ + $elapsed .] + } + + proc haveChannel { name } { + if {![info exists ::haveChannel($name)]} then { + set ::haveChannel($name) \ + [expr {[lsearch -exact [file channels] $name] != -1}] + } + + return $::haveChannel($name) + } + + proc putsStdout { args } { + # + # NOTE: Is the 'stdout' channel available? + # + if {[haveChannel stdout]} then { + # + # NOTE: Do we need to emit a trailing newline? + # + if {[llength $args] == 2 && \ + [lindex $args 0] eq "-nonewline"} then { + # + # NOTE: Output the second argument with no newline. + # + catch { + puts -nonewline stdout [lindex $args 1] + flush stdout + } + } else { + # + # NOTE: Output the first argument with a newline. + # + catch { + puts stdout [lindex $args 0] + flush stdout + } + } + } else { + # + # HACK: Since there is no 'stdout' channel, this procedure is + # totally useless; therefore, we simply redefine it to do + # nothing. + # + proc putsStdout { args } {} + } + } + + proc readBadFile { fileName {readProc readFile} } { + # + # HACK: This "text" file (as exported by MySQL) has a bad mixture of + # utf-8 and windows-1252 code points in it. At a bare minimum, + # we want to change the utf-8 code points that are used in the + # data as column and row delimiters and change them to valid + # windows-1252 code points. + # + return [string map [list \xC3\xBF \xFF \xC3\xBE \xFE] \ + [$readProc $fileName]] + } + + # + # WARNING: Do not use this procedure unless you know exactly what it does. + # + proc readUtf8File { fileName } { + set file_id [open $fileName RDONLY] + fconfigure $file_id -encoding utf-8 -translation auto + set result [read $file_id] + close $file_id + return $result + } + + proc executeSQLite { fileName sql {strict 0} } { + try { + set connection [sql open -type SQLite \ + [subst {Data Source=${fileName}}]] + + if {[catch {sql execute $connection $sql} error] != 0} then { + report [appendArgs "sql statement error: " $error] \ + [list $sql] $strict + } + } finally { + if {[info exists connection]} then { + sql close $connection; unset connection + } + } + } + + proc scanAsciiChars { + fileName {readProc readFile} {startIndex 0} {skip ""} {strict 0} } { + # + # NOTE: Read all the data from the file into memory using the + # specified procedure. + # + # BUGFIX: *PERF* Use a real System.String object here (via an + # opaque object handle) and query each byte in the loop + # below as necessary. This prevents the whole string + # from being needlessly copied back and forth in memory + # repeatedly (i.e. during command invocation). + # + set data [object create -alias String [$readProc $fileName]] + set length [$data Length] + set chunk 1024 + + putsStdout [appendArgs "Scanning " $length " bytes of data (" \ + $chunk " bytes per chunk)...\n"] + + for {set index $startIndex} {$index < $length} {incr index} { + # + # NOTE: Grab the byte value of the specified "character" in + # the string from the opaque object handle. + # + set value [string ordinal [$data get_Chars $index] 0] + + if {[lsearch -integer $skip $value] != -1} then { + continue + } + + if {$value < 32 || $value == 127} then { + report [appendArgs "found control character " $value " (" \ + [string format "0x{0:X}" $value] ") at index " $index] "" \ + $strict + } elseif {$value > 126} then { + report [appendArgs "found bad character " $value " (" \ + [string format "0x{0:X}" $value] ") at index " $index] "" \ + $strict + } + + if {$index % $chunk == 0} then { + putsStdout -nonewline 0 + } + } + } + + proc importDelimited { + fileName tableName {readProc readFile} {columnDelimiter \t} + {rowDelimiter \r\n} {strict 0} } { + # + # NOTE: Read all the data from the file into memory using the + # specified procedure. + # + # BUGFIX: *PERF* Use a real System.String object here (via an + # opaque object handle) and query each byte in the loop + # below as necessary. This prevents the whole string + # from being needlessly copied back and forth in memory + # repeatedly (i.e. during command invocation). + # + set data [object create -alias String [$readProc $fileName]] + + # + # HACK: Check for a detached header file. This should contain + # exactly one row (including the trailing row delimter) + # with just the column names [separated by the column + # delimiter]. + # + set headerFileName [file join [file dirname $fileName] \ + [appendArgs header_ [file tail $fileName]]] + + if {[file exists $headerFileName]} then { + putsStdout [appendArgs "Found detached header file \"" \ + $headerFileName "\" for data file \"" $fileName \ + "\", reading..."] + + set headerData [object create -alias String [$readProc \ + $headerFileName]] + } + + # + # NOTE: Split the data using the row delimiter. We use the + # -string option here to allow for the use of a + # multi-character row delimiters. For data containing + # literal cr/lf characters, a non-cr/lf row delimiter + # must be used. + # + set rowDelimiters [object create -alias String\[\] 1] + $rowDelimiters SetValue $rowDelimiter 0 + set lines [$data -create -alias Split $rowDelimiters None] + + # + # NOTE: Determine how many rows of data there are. There must + # be more than zero to continue. + # + set rowCount [$lines Length] + + # + # NOTE: We cannot proceed if there are no rows of data. + # + if {$rowCount == 0} then { + fail "no rows of data" + } + + # + # NOTE: If we read the header data from the detached header file, + # use it; otherwise, grab the first line of the data. This + # line must be the header line (i.e. it must contain the + # column names and nothing else). + # + if {[info exists headerData]} then { + set headerLine [$headerData ToString] + + # + # NOTE: All data rows are actually data; therefore, start on + # the first row. + # + set rowIndex 0 + } else { + set headerLine [$lines GetValue 0] + + # + # NOTE: The first data row is really the header line; therefore, + # start on the second row. + # + set rowIndex 1 + } + + # + # NOTE: We cannot proceed if the header line is empty. + # + if {[string length $headerLine] == 0} then { + fail "invalid file header" + } + + putsStdout [appendArgs "\n\nAttempting to import " $rowCount \ + " rows starting at index " $rowIndex "...\n"] + + # + # NOTE: Unquote the column name (i.e. removes single and + # double quotation marks). Technically, this may + # be too liberal since it will remove all leading + # and trailing single and double quotes; however, + # these are the column names, not data, and should + # not contain any single or double quotes. + # + set unquote [list [list x] { return [string trim $x '\"] }] + + # + # NOTE: Split the header line using the column delimiter. + # We use the -string option here to allow for the + # use of a multi-character column delimiter. For + # data containing literal tab characters, a non-tab + # column delimiter must be used. + # + set headerColumns [map \ + [split $headerLine $columnDelimiter -string] \ + {apply $unquote}] + + set columnCount [llength $headerColumns] + + # + # NOTE: We cannot proceed if there are no column names. + # + if {$columnCount == 0} then { + fail "no columns in header" + } + + # + # NOTE: Using the table name, access the in-memory table + # from the calling context. By convention, the + # variable name will be "_rows". + # + upvar 1 [appendArgs $tableName _rows] table + + # + # NOTE: Set the necessary metadata used by the export + # procedure into the table array. + # + set table(columns) $headerColumns + set table(imported) 0 + set table(startIndex) $rowIndex + + # + # NOTE: There is no loop initializer here, see above. + # + for {} {$rowIndex < $rowCount} {incr rowIndex} { + # + # NOTE: Grab the previous line of data, if available. + # + if {$rowIndex > 0} then { + set previousLine [$lines GetValue [expr {$rowIndex - 1}]] + } else { + set previousLine "" + } + + # + # NOTE: Grab the current line of data. + # + set line [$lines GetValue $rowIndex] + + # + # NOTE: Is the current line of data empty? + # + if {[string length $line] == 0} then { + # + # NOTE: We do not care if the final row of data is + # empty. + # + if {$rowIndex + 1 < $rowCount} then { + # + # NOTE: An empty row of data could indicate a corrupt + # data dump; however, it is almost always safe + # to simply ignore blank lines. + # + report [appendArgs "row #" $rowIndex " is empty"] \ + [list previous $previousLine] $strict + } + + continue + } + + # + # NOTE: Split the current line using the column delimiter. + # We use the -string option here to allow for the + # use of a multi-character column delimiter. For + # data containing literal tab characters, a non-tab + # column delimiter must be used. + # + set columns [split $line $columnDelimiter -string] + set count [llength $columns] + + # + # NOTE: Did we find some columns in the current line of + # data? Given the check for an empty line of data + # above, this should almost always succeed. + # + if {$count == 0} then { + # + # NOTE: A row of data with no columns could indicate a + # corrupt data dump. + # + report [appendArgs "row #" $rowIndex " has no columns"] \ + [list previous $previousLine current $line] $strict + + continue + } + + # + # NOTE: Does the current line of data contain the correct + # number of columns (based on the header)? If the + # data dump is subtly corrupted in some way, this + # is the most likely error to be seen. + # + if {$count != $columnCount} then { + # + # NOTE: A row of data with an incorrect number of columns + # almost certainly indicates at least some level of + # data dump corruption. We can ignore it and proceed; + # however, each of these errors should be individually + # investigated at the very least. + # + report [appendArgs "row #" $rowIndex " has " $count \ + " columns, expected " $columnCount] [list current $line] $strict + + continue + } + + # + # NOTE: Process each column value for this row and add it + # to the in-memory table. + # + set columnIndex 0 + + for {} {$columnIndex < $count} {incr columnIndex} { + set columnName [lindex $headerColumns $columnIndex] + set columnValue [lindex $columns $columnIndex] + + # + # NOTE: Is the first character a single or double quote? + # + if {[string index $columnValue 0] eq "'" || \ + [string index $columnValue 0] eq "\""} then { + # + # NOTE: Ok, remove the first character. + # + set columnValue [string range $columnValue 1 end] + } + + # + # NOTE: Is the last character a single or double quote? + # + if {[string index $columnValue end] eq "'" || \ + [string index $columnValue end] eq "\""} then { + # + # NOTE: Ok, remove the last character. + # + set columnValue [string range $columnValue 0 end-1] + } + + set table($rowIndex,$columnName) $columnValue + } + + incr table(imported) + putsStdout -nonewline . + } + + set table(count) $table(imported) + } + + proc exportFixedLength { + tableName fileName fields {maps ""} {regsubs ""} {strict 0} } { + # + # NOTE: Using the table name, access the in-memory table + # from the calling context. By convention, the + # variable name will be "_rows". + # + upvar 1 [appendArgs $tableName _rows] table + + set headerColumns $table(columns) + set columnCount [llength $headerColumns] + + # + # NOTE: So far, we have not exported any rows. + # + set table(exported) 0 + + # + # NOTE: Grab the necessary metadata from the table array. + # + set rowCount $table(count) + set startIndex $table(startIndex) + set rowIndex $startIndex + + putsStdout [appendArgs "\n\nAttempting to export " $rowCount \ + " rows starting at index " $rowIndex "...\n"] + + # + # NOTE: Process each row in the passed-in array. There is no + # loop initializer here, see above. + # + set data "" + + for {} {$rowIndex < $rowCount + $startIndex} {incr rowIndex} { + # + # NOTE: Start out with an empty row value. After all the fields + # are processed, this will be added to the overall data block + # to export. + # + set rowValue "" + + # + # NOTE: Process each field in the passed-in list. + # + set fieldIndex 0 + + for {} {$fieldIndex < [llength $fields]} {incr fieldIndex} { + # + # NOTE: What is the length of this row so far? + # + set rowLength [string length $rowValue] + + # + # NOTE: Grab the field [definition] from the list. + # + set field [lindex $fields $fieldIndex] + + # + # NOTE: Make sure the field has the required elements. + # + if {[llength $field] < 3} then { + report [appendArgs \ + "field #" $fieldIndex " has " [llength $field] \ + " elements, expected at least 3"] "" $strict + + continue + } + + # + # NOTE: Extract and validate the field identifier. This element is + # always required and must be a valid integer. + # + set fieldId [string trim [lindex $field 0]] + + if {![string is integer -strict $fieldId]} then { + report [appendArgs \ + "field #" $fieldIndex " has an invalid identifier \"" \ + $fieldId \"] "" $strict + + continue + } + + # + # NOTE: Extract and validate the field name. This element is + # always required. + # + set fieldName [string trim [lindex $field 1]] + + if {[string length $fieldName] == 0} then { + report [appendArgs \ + "field #" $fieldIndex " has an empty name"] "" $strict + + continue + } + + # + # NOTE: Extract and validate the field width. This element is + # always required and must be a valid integer greater than + # zero. + # + set fieldWidth [string trim [lindex $field 2]] + + if {![string is integer -strict $fieldWidth]} then { + report [appendArgs \ + "field #" $fieldIndex " has an invalid width \"" \ + $fieldWidth \"] "" $strict + + continue + } + + # + # NOTE: The field width must be positive and greater than zero. + # + if {$fieldWidth <= 0} then { + report [appendArgs \ + "field #" $fieldIndex " has width \"" $fieldWidth \ + "\", which is less than or equal to zero"] "" $strict + + continue + } + + # + # NOTE: Extract and validate the field start. This element is + # optional; if specified, it must be an integer. + # + set fieldStart [string trim [lindex $field 3]] + + if {[string length $fieldStart] == 0} then { + set fieldStart $rowLength + } + + if {![string is integer -strict $fieldStart]} then { + report [appendArgs \ + "field #" $fieldIndex " has an invalid start \"" \ + $fieldStart \"] "" $strict + + continue + } + + # + # NOTE: The field start cannot occur before the current position in + # the row being built (i.e. fields are always processed in the + # order they occur in the list provided by the caller). + # + if {$fieldStart < $rowLength} then { + report [appendArgs \ + "field #" $fieldIndex " cannot start at \"" $fieldStart \ + "\", already beyond that point"] "" $strict + + continue + } + + # + # NOTE: Extract and validate the field alignment. This element is + # optional; if specified, it must be either "left" or "right". + # + set fieldAlignment [string trim [lindex $field 4]] + + if {[string length $fieldAlignment] == 0} then { + set fieldAlignment right + } + + if {$fieldAlignment ni [list left right]} then { + report [appendArgs \ + "field #" $fieldIndex " has an invalid alignment \"" \ + $fieldAlignment "\", must be \"left\" or \"right\""] "" \ + $strict + + continue + } + + # + # NOTE: Extract and validate the field type. This element is + # optional; if specified, it must be either "string", + # "number", or "datetime". + # + set fieldType [string trim [lindex $field 5]] + + if {[string length $fieldType] == 0} then { + set fieldType string + } + + if {$fieldType ni [list string number datetime]} then { + report [appendArgs \ + "field #" $fieldIndex " has an invalid type \"" $fieldType \ + "\", must be \"string\", \"number\", or \"datetime\""] "" \ + $strict + + continue + } + + # + # NOTE: Extract and validate the field format. This element is + # optional. + # + set fieldFormat [lindex $field 6]; # NOTE: No trim. + + # + # NOTE: Search for the column in the list of columns. If it cannot + # be found, use an empty string for the column name and value. + # We cannot simply skip the column because the format string + # may simply be a literal string that does not require the + # column value. + # + set columnIndex [lsearch -exact $headerColumns $fieldName] + + if {$columnIndex != -1} then { + set columnName [lindex $headerColumns $columnIndex] + set columnValue $table($rowIndex,$columnName) + } else { + set columnName "" + set columnValue "" + } + + # + # HACK: Perform any replacements specified by the caller. This is + # done in two phases. Typically, the first phase is used to + # escape characters (e.g. by converting them to HTML entities) + # and the second phase is [optionally] used to undo any double + # escapes that may have been created during the first phase. + # + if {[llength $maps] > 0} then { + foreach map $maps { + if {[llength $map] > 0} then { + set columnValue [string map $map $columnValue] + } + } + } + + # + # HACK: Perform any regular expression replacements specified by the + # caller. + # + if {[llength $regsubs] > 0} then { + foreach regsub $regsubs { + # + # NOTE: Each element in the list must have exactly 2 elements. + # The first element must be the regular expression. The + # second element must be the replacement pattern. + # + if {[llength $regsub] == 2} then { + regsub -all -- [lindex $regsub 0] $columnValue \ + [lindex $regsub 1] columnValue + } + } + } + + # + # NOTE: Check if an explicit format string was specified. If so, + # use the appropriate formatting command for the data type. + # + if {[string length $fieldFormat] > 0} then { + switch -exact -- $fieldType { + string { + set columnValue [object invoke String Format \ + $fieldFormat $columnValue] + } + number { + set columnValue [format $fieldFormat $columnValue] + } + datetime { + if {[string length $columnValue] > 0 && \ + ![string is integer -strict $columnValue]} then { + # + # NOTE: The value is not an integer; therefore, + # try to scan it as a date and/or time. + # + set columnValue [clock scan $columnValue] + } + + set columnValue [clock format $columnValue \ + -format $fieldFormat] + } + default { + report [appendArgs \ + "field #" $fieldIndex " has bad type \"" \ + $fieldAlignment \"] "" $strict + + continue + } + } + } + + # + # NOTE: Check the formatted column length against the field width. + # If the formatted column length is greater, it must be + # truncated. Otherwise, if the formatted column length is + # less, it must be padded according to the field alignment. + # + set columnLength [string length $columnValue] + + if {$columnLength > $fieldWidth} then { + # + # NOTE: Truncate the string; otherwise, it will not fit within + # the field. + # + if {$fieldAlignment eq "left"} then { + set columnValue [string range $columnValue \ + [expr {$columnLength - $fieldWidth}] end] + } else { + set columnValue [string range $columnValue \ + 0 [expr {$fieldWidth - 1}]] + } + + report [appendArgs \ + "column \"" $columnName "\" value at row #" $rowIndex \ + " (length " $columnLength ") exceeds width of field #" \ + $fieldIndex " (width " $fieldWidth "), truncated"] "" \ + $strict + } else { + set padding [string repeat " " \ + [expr {$fieldWidth - $columnLength}]] + + if {$fieldAlignment eq "left"} then { + set columnValue [appendArgs $columnValue $padding] + } else { + set columnValue [appendArgs $padding $columnValue] + } + } + + # + # NOTE: If this field starts at some point after the end of the + # row value built so far, pad it. + # + if {$fieldStart > $rowLength} then { + set padding [string repeat " " \ + [expr {$fieldStart - $rowLength}]] + } else { + set padding "" + } + + # + # NOTE: Append the necessary padding and the final column value + # to the row value. + # + append rowValue $padding $columnValue + } + + # + # NOTE: Append the row value to the overall data block to export. + # + append data $rowValue + + incr table(exported) + putsStdout -nonewline * + } + + writeFile $fileName $data + } + + proc exportSQLite { + tableName fileName {sql ""} {maps ""} {regsubs ""} {strict 0} } { + # + # NOTE: Export the in-memory table to the specified SQLite + # database file. + # + try { + set connection [sql open -type SQLite \ + [subst {Data Source=${fileName}}]] + + # + # NOTE: If custom SQL was specified, execute it against + # the database connection now. + # + if {[string length $sql] > 0} then { + sql execute $connection $sql + } + + # + # NOTE: Using the table name, access the in-memory table + # from the calling context. By convention, the + # variable name will be "_rows". + # + upvar 1 [appendArgs $tableName _rows] table + + set headerColumns $table(columns) + set columnCount [llength $headerColumns] + + # + # NOTE: Wraps the column name in square brackets. + # + set wrap [list [list x] { return [appendArgs \[ $x \]] }] + + # + # NOTE: Build the parameterized SQL statement to execute an + # INSERT (or UPDATE) of a single row. + # + set rowSql [appendArgs \ + "INSERT OR REPLACE INTO \[" $tableName "\] (" \ + [join [map $headerColumns {apply $wrap}] ,] ") VALUES(" \ + [join [lrepeat $columnCount ?] ,] ");"] + + # + # NOTE: Build the per-row script to evaluate for adding or + # updating a row in the database. + # + set script {eval sql execute -verbatim \ + [list $connection] [list $rowSql] $columnParams} + + # + # NOTE: So far, we have not exported any rows. + # + set table(exported) 0 + + # + # NOTE: Grab the necessary metadata from the table array. + # + set rowCount $table(count) + set startIndex $table(startIndex) + set rowIndex $startIndex + + putsStdout [appendArgs "\n\nAttempting to export " $rowCount \ + " rows starting at index " $rowIndex "...\n"] + + # + # NOTE: Process each row in the passed-in array. There is no loop + # initializer here, see above. + # + for {} {$rowIndex < $rowCount + $startIndex} {incr rowIndex} { + set columnParams [list] + + # + # NOTE: Process each column value for this row and add it to the + # list of parameters for the SQL statement to execute. + # + set columnIndex 0 + + for {} {$columnIndex < $columnCount} {incr columnIndex} { + set columnName [lindex $headerColumns $columnIndex] + set columnValue $table($rowIndex,$columnName) + + # + # HACK: Perform any replacements specified by the caller. This is + # done in two phases. Typically, the first phase is used to + # escape characters (e.g. by converting them to HTML entities) + # and the second phase is [optionally] used to undo any double + # escapes that may have been created during the first phase. + # + if {[llength $maps] > 0} then { + foreach map $maps { + if {[llength $map] > 0} then { + set columnValue [string map $map $columnValue] + } + } + } + + # + # HACK: Perform any regular expression replacements specified by the + # caller. + # + if {[llength $regsubs] > 0} then { + foreach regsub $regsubs { + # + # NOTE: Each element in the list must have exactly 2 elements. + # The first element must be the regular expression. The + # second element must be the replacement pattern. + # + if {[llength $regsub] == 2} then { + regsub -all -- [lindex $regsub 0] $columnValue \ + [lindex $regsub 1] columnValue + } + } + } + + # + # HACK: Make dates conform to the format needed by SQLite. + # + if {[regexp -- {^\d{4}/\d{1,2}/\d{1,2}$} $columnValue]} then { + set dateTime [object invoke -alias DateTime Parse $columnValue] + set columnValue [$dateTime ToString yyyy-MM-dd] + } + + # + # NOTE: Make sure to omit the parameter value if the column value + # needs to be a literal NULL. + # + set columnParam [list [appendArgs param $columnIndex] String] + + if {$columnValue ne "NULL"} then { + lappend columnParam $columnValue + } + + # + # NOTE: Add the parameter for this column to the list of parameters + # to pass to the command. + # + lappend columnParams $columnParam + } + + # + # NOTE: Evaluate the per-row script used to actually insert or + # update a row in the database. Catch and report on any + # errors we encounter. + # + if {[catch $script error] != 0} then { + report [appendArgs "sql statement error: " $error] \ + [list $rowSql $columnParams] $strict + } else { + # + # NOTE: We successfully imported another row. + # + incr table(exported) + } + + putsStdout -nonewline * + } + } finally { + if {[info exists connection]} then { + sql close $connection; unset connection + } + } + } + + # + # NOTE: Provide the Eagle library package to the interpreter. + # + package provide Eagle.Data \ + [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] +} + ADDED eagle/1.0/data1.0/data.eagle.harpy Index: eagle/1.0/data1.0/data.eagle.harpy ================================================================== --- eagle/1.0/data1.0/data.eagle.harpy +++ eagle/1.0/data1.0/data.eagle.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + b75d0391-1361-4486-bfa0-df753175f4d0 + SHA512 + Script + 2016-08-13T19:15:04.6484375Z + -1.00:00:00 + 0x2c322765603b5278 + + Y+UzAa7YnCZOYYIlbcsE8RPzewFUWniBPAcUVubl6JMpOrqfq31tEEfn3J0IVgXRSWL2iBqWCiW4 + 3xmG86Ah6AFTVXdlsXFzuDaA6v+YfDR4DYlKbieFJfLN0MW9SYuvjVYB6+qSwZ0o3Gedepc1ouRf + P9sk1f4ZTxZcRhDhZ6At7DHml13gShCujXzeUYFQXP8m53wXptyO3DlNJnHH1fiQZv4Qz24JZIX9 + 9QgF/Aweav05haV/gt8bXyqslCdnWbrEdyujYbhVkL0CX2JZ4+j0l+AerFyUMWFLSkpa8qqGl4EM + 9lK2T5c/r7Yu2/t+BtHHFAjVaiDcPRB1SvyQsWv8pHwDWpsA2sE429Lgu/Cu+GWj80NYgydd1Ub2 + pAEL73BpzkH6yp/8GfjExS/FQCYVK6XitMOMxkldjYrjzPlox3sQ0Qx3y+nmd8RveHcbgcDhhc0T + PZJC4Rb2027E4GhMWTIZbzX26TOnYc9MfiXBgrabVtmmuj2sT9PZ2zVXq1ES5xW1BiLaKya9Tgzs + A22WNiMyfFXvVUDvn61nvsZyX7tUJbUCA9FHFdeH98kPxu2R5Pk/gi8Q5JdB62MaP5jWVHoQBfkz + zkAZVhSETenG8EnjPDbf8ORkc/SfcOwyMLKloifGDbovDfRPvt/Dohw7fXndkLqmw1SCNvudxz2K + nhaetZUv9SKIwAYxcHzH4ms1mmSNq6GsjROTja9VLWymvNjBPxk8DSYEb8J5ojx3TkELvzj6JvlO + zgYQlWCu/KXQPphwbIxsPpKppYrNbyqv842E7AHjCJQF5f0vMFWFuE7lBc7wM6vz395pbvefPMaI + uAFwrLcF5mDN10f9Lt67o3UZJhX7Rp+wdNYBY1bUzyLAooqBClpTTI/GNMMA+TAAg99WqiuPQSRS + N1tadjhWZPfHDCM6LyrfMVjduCWoylakhy775+VPIr7vXHN1y0MGxhf0Ma5EQg2kTYj4GsQ42dZW + 7P4PVS5Wmv93Ydp74o81H+nesDUMgd0DrklNxB/NynVwhx2lJqRpV8Tov3UrzU8vpqhyVZNKrZ56 + XP68F/Z4QUIFFk1H9sLhF5bx6qXComO7qi8HxkiLK1XLvPBXt9BrePmZgKTm5VJHr5y8YmOGqxV3 + azlswBa4x2ifJ5si7VjB0jfVpBZgIWmclu96X3+VCJNAw50dBMZgmPmr7N5U2/zqh5ppvbZ+8Euh + BLMW5mGL9nfv9yP3dm3HdA6e6NsOO9kVDcaeMaCagHFRF7ZzQHnAtDZJR20J5K6MUOPYXk25i16B + ySOEc2SApQOfZhkQ3afLowEZqbvtrcOm9VB+NtPTx55qSVilDs4vx03XWxR3o+St1O272qUdHQ== + + ADDED eagle/1.0/data1.0/pkgIndex.eagle Index: eagle/1.0/data1.0/pkgIndex.eagle ================================================================== --- eagle/1.0/data1.0/pkgIndex.eagle +++ eagle/1.0/data1.0/pkgIndex.eagle @@ -0,0 +1,20 @@ +############################################################################### +# +# pkgIndex.eagle -- +# +# Extensible Adaptable Generalized Logic Engine (Eagle) +# Package Index File +# +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. +# +# See the file "license.terms" for information on usage and redistribution of +# this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id: $ +# +############################################################################### + +if {![package vsatisfies [package provide Tcl] 8.4]} {return} +if {![package vsatisfies [package provide Eagle] 1.0]} {return} + +package ifneeded Eagle.Data 1.0 [list source [file join $dir data.eagle]] ADDED eagle/1.0/data1.0/pkgIndex.eagle.harpy Index: eagle/1.0/data1.0/pkgIndex.eagle.harpy ================================================================== --- eagle/1.0/data1.0/pkgIndex.eagle.harpy +++ eagle/1.0/data1.0/pkgIndex.eagle.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + 617c385d-49a3-4b98-b06c-80bad63311a4 + SHA512 + Script + 2016-08-13T19:15:17.8593750Z + -1.00:00:00 + 0x2c322765603b5278 + + iqyBnSPDUq5sZ4vpnLDtMb65Q7wuukZDFgR1W8MGANA8s6vCsR8Yhs/Qc8gQxGrbyl3N+1SdBSJ2 + vNC4MJHIWYUPYWo6T0BoLb42ryhZ5g/Cp4cqtm9vJosQyKWQ304vIetxQ+sh39B1nIcK28j5dicY + /wmUXd3bL+MKeM7P96XhnhQinftc37HHGQmn9hbYSrhVX04+E+X/q4jsUDIDnDpcqdRQlhzDDGsC + R7yfXttPNQbkRzTeevW5qcZPY7hGjyplgTcXoeBVUVT8qsxlD4F4g/kb/hzKeAtH5Py2793edrKz + ujUKKBvEUNIZyf7SPLSg8yTjM5aoQu7EdjwDYVGj8BHnUIHrDc+X4kksWUvXPEwdjmg61OP7QjCI + Hpfax/SEXznCzYVRiPy8jPDagsbHt62ASx0fcvbHryUNMv/WpucDI9yfPVCmmZFf0l2rnxEBxzA+ + nPfDQCdLjY8/cCh/H7iasxDHyoI/tzUj1zQG+ltDKKLd3iLiuUMuyqN3RchHVmqQFkeUT3HKyVxa + 0B5BSHsym2VKZqlSjKQPWMTaa4fSUplgmST+fbMLjEWDwZ2oW3HHCzdf/N7OiownDC4lcCnBiyw4 + NXVqqR59trJ/26hiw+W7/FqEpNo1zaC+Y8LFsaTv0tekQWz6AG3Kfu7E4VzgssbwYrY2mMj5+oqV + 5tNVMg28fp4ZnE0y7ZE2j3T6RqrEIrF8MPdtuFjUMMAOFnqXWSmG+aCi94K7hac4Sp7ADU2Ac43/ + NP1gdkr3e/d1GzKpN9PFMrLYmHpFTpiFWnTbdDMh4EgdL4I/KpFhLC3mA+dJAmpxEktdoAcstymI + jZYV0YsOBTxtdyHrVyG6XrhKX52D5LYbjITpU061pLt84pF6JPZBFOJXgu9YeYlWqoGVypv+bREy + LgfJ4wLqrs65yJNroeW2ScdPL9wv2FtbpocJUVWj7TnvMdNC6kp8+M5cbslAz4AWUOppkVP+S31B + RVCbqwDQC7sfDz4AKdl4I72l6TyJhtI8ULRqUiBwXr84wMYwTqrjKJ6HZybEBRjU+lIyKCakUWaN + QehWuRy57FuJXvPggffEJ9Jl4xn89V7k8uvBHrjqAMuIwd5Uozzg+GEXUweH5hiim1K7geqVa4Mm + Y+TBuML2zL3Vx8A6uQVO8IS622UrdqlrFOGEBz8e1WL07HDBcugvGE3AsWLIugDWtMTwxCY67UHj + yUhVw+iUXwR+vuhLc9VkI4F/3QYH6WjYk/chiy2uooM4+VhmP5kbkSxGNqFbO7mk+Ilw0ieovuMz + vJgHrxOezOQZSuMt/ienIyGYBSflxKeAKtw6cDnnu9+jPJEBQYMelkuSSLZ9wkkVRy7NBfWdRA== + + ADDED tcl/8.4/tcllib1.15/aes/aes.tcl Index: tcl/8.4/tcllib1.15/aes/aes.tcl ================================================================== --- tcl/8.4/tcllib1.15/aes/aes.tcl +++ tcl/8.4/tcllib1.15/aes/aes.tcl @@ -0,0 +1,628 @@ +# aes.tcl - +# +# Copyright (c) 2005 Thorsten Schloermann +# Copyright (c) 2005 Pat Thoyts +# Copyright (c) 2013 Andreas Kupries +# +# A Tcl implementation of the Advanced Encryption Standard (US FIPS PUB 197) +# +# AES is a block cipher with a block size of 128 bits and a variable +# key size of 128, 192 or 256 bits. +# The algorithm works on each block as a 4x4 state array. There are 4 steps +# in each round: +# SubBytes a non-linear substitution step using a predefined S-box +# ShiftRows cyclic transposition of rows in the state matrix +# MixColumns transformation upon columns in the state matrix +# AddRoundKey application of round specific sub-key +# +# ------------------------------------------------------------------------- +# See the file "license.terms" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. +# ------------------------------------------------------------------------- + +package require Tcl 8.2 + +namespace eval ::aes { + variable version 1.1.1 + variable rcsid {$Id: aes.tcl,v 1.7 2010/07/06 19:39:00 andreas_kupries Exp $} + variable uid ; if {![info exists uid]} { set uid 0 } + + namespace export {aes} + + # constants + + # S-box + variable sbox { + 0x63 0x7c 0x77 0x7b 0xf2 0x6b 0x6f 0xc5 0x30 0x01 0x67 0x2b 0xfe 0xd7 0xab 0x76 + 0xca 0x82 0xc9 0x7d 0xfa 0x59 0x47 0xf0 0xad 0xd4 0xa2 0xaf 0x9c 0xa4 0x72 0xc0 + 0xb7 0xfd 0x93 0x26 0x36 0x3f 0xf7 0xcc 0x34 0xa5 0xe5 0xf1 0x71 0xd8 0x31 0x15 + 0x04 0xc7 0x23 0xc3 0x18 0x96 0x05 0x9a 0x07 0x12 0x80 0xe2 0xeb 0x27 0xb2 0x75 + 0x09 0x83 0x2c 0x1a 0x1b 0x6e 0x5a 0xa0 0x52 0x3b 0xd6 0xb3 0x29 0xe3 0x2f 0x84 + 0x53 0xd1 0x00 0xed 0x20 0xfc 0xb1 0x5b 0x6a 0xcb 0xbe 0x39 0x4a 0x4c 0x58 0xcf + 0xd0 0xef 0xaa 0xfb 0x43 0x4d 0x33 0x85 0x45 0xf9 0x02 0x7f 0x50 0x3c 0x9f 0xa8 + 0x51 0xa3 0x40 0x8f 0x92 0x9d 0x38 0xf5 0xbc 0xb6 0xda 0x21 0x10 0xff 0xf3 0xd2 + 0xcd 0x0c 0x13 0xec 0x5f 0x97 0x44 0x17 0xc4 0xa7 0x7e 0x3d 0x64 0x5d 0x19 0x73 + 0x60 0x81 0x4f 0xdc 0x22 0x2a 0x90 0x88 0x46 0xee 0xb8 0x14 0xde 0x5e 0x0b 0xdb + 0xe0 0x32 0x3a 0x0a 0x49 0x06 0x24 0x5c 0xc2 0xd3 0xac 0x62 0x91 0x95 0xe4 0x79 + 0xe7 0xc8 0x37 0x6d 0x8d 0xd5 0x4e 0xa9 0x6c 0x56 0xf4 0xea 0x65 0x7a 0xae 0x08 + 0xba 0x78 0x25 0x2e 0x1c 0xa6 0xb4 0xc6 0xe8 0xdd 0x74 0x1f 0x4b 0xbd 0x8b 0x8a + 0x70 0x3e 0xb5 0x66 0x48 0x03 0xf6 0x0e 0x61 0x35 0x57 0xb9 0x86 0xc1 0x1d 0x9e + 0xe1 0xf8 0x98 0x11 0x69 0xd9 0x8e 0x94 0x9b 0x1e 0x87 0xe9 0xce 0x55 0x28 0xdf + 0x8c 0xa1 0x89 0x0d 0xbf 0xe6 0x42 0x68 0x41 0x99 0x2d 0x0f 0xb0 0x54 0xbb 0x16 + } + # inverse S-box + variable xobs { + 0x52 0x09 0x6a 0xd5 0x30 0x36 0xa5 0x38 0xbf 0x40 0xa3 0x9e 0x81 0xf3 0xd7 0xfb + 0x7c 0xe3 0x39 0x82 0x9b 0x2f 0xff 0x87 0x34 0x8e 0x43 0x44 0xc4 0xde 0xe9 0xcb + 0x54 0x7b 0x94 0x32 0xa6 0xc2 0x23 0x3d 0xee 0x4c 0x95 0x0b 0x42 0xfa 0xc3 0x4e + 0x08 0x2e 0xa1 0x66 0x28 0xd9 0x24 0xb2 0x76 0x5b 0xa2 0x49 0x6d 0x8b 0xd1 0x25 + 0x72 0xf8 0xf6 0x64 0x86 0x68 0x98 0x16 0xd4 0xa4 0x5c 0xcc 0x5d 0x65 0xb6 0x92 + 0x6c 0x70 0x48 0x50 0xfd 0xed 0xb9 0xda 0x5e 0x15 0x46 0x57 0xa7 0x8d 0x9d 0x84 + 0x90 0xd8 0xab 0x00 0x8c 0xbc 0xd3 0x0a 0xf7 0xe4 0x58 0x05 0xb8 0xb3 0x45 0x06 + 0xd0 0x2c 0x1e 0x8f 0xca 0x3f 0x0f 0x02 0xc1 0xaf 0xbd 0x03 0x01 0x13 0x8a 0x6b + 0x3a 0x91 0x11 0x41 0x4f 0x67 0xdc 0xea 0x97 0xf2 0xcf 0xce 0xf0 0xb4 0xe6 0x73 + 0x96 0xac 0x74 0x22 0xe7 0xad 0x35 0x85 0xe2 0xf9 0x37 0xe8 0x1c 0x75 0xdf 0x6e + 0x47 0xf1 0x1a 0x71 0x1d 0x29 0xc5 0x89 0x6f 0xb7 0x62 0x0e 0xaa 0x18 0xbe 0x1b + 0xfc 0x56 0x3e 0x4b 0xc6 0xd2 0x79 0x20 0x9a 0xdb 0xc0 0xfe 0x78 0xcd 0x5a 0xf4 + 0x1f 0xdd 0xa8 0x33 0x88 0x07 0xc7 0x31 0xb1 0x12 0x10 0x59 0x27 0x80 0xec 0x5f + 0x60 0x51 0x7f 0xa9 0x19 0xb5 0x4a 0x0d 0x2d 0xe5 0x7a 0x9f 0x93 0xc9 0x9c 0xef + 0xa0 0xe0 0x3b 0x4d 0xae 0x2a 0xf5 0xb0 0xc8 0xeb 0xbb 0x3c 0x83 0x53 0x99 0x61 + 0x17 0x2b 0x04 0x7e 0xba 0x77 0xd6 0x26 0xe1 0x69 0x14 0x63 0x55 0x21 0x0c 0x7d + } +} + +# aes::Init -- +# +# Initialise our AES state and calculate the key schedule. An initialization +# vector is maintained in the state for modes that require one. The key must +# be binary data of the correct size and the IV must be 16 bytes. +# +# Nk: columns of the key-array +# Nr: number of rounds (depends on key-length) +# Nb: columns of the text-block, is always 4 in AES +# +proc ::aes::Init {mode key iv} { + switch -exact -- $mode { + ecb - cbc { } + cfb - ofb { + return -code error "$mode mode not implemented" + } + default { + return -code error "invalid mode \"$mode\":\ + must be one of ecb or cbc." + } + } + + set size [expr {[string length $key] << 3}] + switch -exact -- $size { + 128 {set Nk 4; set Nr 10; set Nb 4} + 192 {set Nk 6; set Nr 12; set Nb 4} + 256 {set Nk 8; set Nr 14; set Nb 4} + default { + return -code error "invalid key size \"$size\":\ + must be one of 128, 192 or 256." + } + } + + variable uid + set Key [namespace current]::[incr uid] + upvar #0 $Key state + array set state [list M $mode K $key I $iv Nk $Nk Nr $Nr Nb $Nb W {}] + ExpandKey $Key + return $Key +} + +# aes::Reset -- +# +# Reset the initialization vector for the specified key. This permits the +# key to be reused for encryption or decryption without the expense of +# re-calculating the key schedule. +# +proc ::aes::Reset {Key iv} { + upvar #0 $Key state + set state(I) $iv + return +} + +# aes::Final -- +# +# Clean up the key state +# +proc ::aes::Final {Key} { + # FRINK: nocheck + unset $Key +} + +# ------------------------------------------------------------------------- + +# 5.1 Cipher: Encipher a single block of 128 bits. +proc ::aes::EncryptBlock {Key block} { + upvar #0 $Key state + if {[binary scan $block I4 data] != 1} { + return -code error "invalid block size: blocks must be 16 bytes" + } + + if {[string equal $state(M) cbc]} { + if {[binary scan $state(I) I4 iv] != 1} { + return -code error "invalid initialization vector: must be 16 bytes" + } + for {set n 0} {$n < 4} {incr n} { + lappend data2 [expr {0xffffffff & ([lindex $data $n] ^ [lindex $iv $n])}] + } + set data $data2 + } + + set data [AddRoundKey $Key 0 $data] + for {set n 1} {$n < $state(Nr)} {incr n} { + set data [AddRoundKey $Key $n [MixColumns [ShiftRows [SubBytes $data]]]] + } + set data [AddRoundKey $Key $n [ShiftRows [SubBytes $data]]] + + # Bug 2993029: + # Force all elements of data into the 32bit range. + set res {} + foreach d $data { + lappend res [expr {$d & 0xffffffff}] + } + set data $res + + return [set state(I) [binary format I4 $data]] +} + +# 5.3: Inverse Cipher: Decipher a single 128 bit block. +proc ::aes::DecryptBlock {Key block} { + upvar #0 $Key state + if {[binary scan $block I4 data] != 1} { + return -code error "invalid block size: block must be 16 bytes" + } + + set n $state(Nr) + set data [AddRoundKey $Key $state(Nr) $data] + for {incr n -1} {$n > 0} {incr n -1} { + set data [InvMixColumns [AddRoundKey $Key $n [InvSubBytes [InvShiftRows $data]]]] + } + set data [AddRoundKey $Key $n [InvSubBytes [InvShiftRows $data]]] + + if {[string equal $state(M) cbc]} { + if {[binary scan $state(I) I4 iv] != 1} { + return -code error "invalid initialization vector: must be 16 bytes" + } + for {set n 0} {$n < 4} {incr n} { + lappend data2 [expr {0xffffffff & ([lindex $data $n] ^ [lindex $iv $n])}] + } + set data $data2 + } else { + # Bug 2993029: + # Force all elements of data into the 32bit range. + # The trimming we see above only happens for CBC mode. + set res {} + foreach d $data { + lappend res [expr {$d & 0xffffffff}] + } + set data $res + } + + set state(I) $block + return [binary format I4 $data] +} + +# 5.2: KeyExpansion +proc ::aes::ExpandKey {Key} { + upvar #0 $Key state + set Rcon [list 0x00000000 0x01000000 0x02000000 0x04000000 0x08000000 \ + 0x10000000 0x20000000 0x40000000 0x80000000 0x1b000000 \ + 0x36000000 0x6c000000 0xd8000000 0xab000000 0x4d000000] + # Split the key into Nk big-endian words + binary scan $state(K) I* W + set max [expr {$state(Nb) * ($state(Nr) + 1)}] + set i $state(Nk) + set h $state(Nk) ; incr h -1 + set j 0 + for {} {$i < $max} {incr i; incr h; incr j} { + set temp [lindex $W $h] + if {($i % $state(Nk)) == 0} { + set sub [SubWord [RotWord $temp]] + set rc [lindex $Rcon [expr {$i/$state(Nk)}]] + set temp [expr {$sub ^ $rc}] + } elseif {$state(Nk) > 6 && ($i % $state(Nk)) == 4} { + set temp [SubWord $temp] + } + lappend W [expr {[lindex $W $j] ^ $temp}] + } + set state(W) $W + return +} + +# 5.2: Key Expansion: Apply S-box to each byte in the 32 bit word +proc ::aes::SubWord {w} { + variable sbox + set s3 [lindex $sbox [expr {(($w >> 24) & 255)}]] + set s2 [lindex $sbox [expr {(($w >> 16) & 255)}]] + set s1 [lindex $sbox [expr {(($w >> 8 ) & 255)}]] + set s0 [lindex $sbox [expr {( $w & 255)}]] + return [expr {($s3 << 24) | ($s2 << 16) | ($s1 << 8) | $s0}] +} + +proc ::aes::InvSubWord {w} { + variable xobs + set s3 [lindex $xobs [expr {(($w >> 24) & 255)}]] + set s2 [lindex $xobs [expr {(($w >> 16) & 255)}]] + set s1 [lindex $xobs [expr {(($w >> 8 ) & 255)}]] + set s0 [lindex $xobs [expr {( $w & 255)}]] + return [expr {($s3 << 24) | ($s2 << 16) | ($s1 << 8) | $s0}] +} + +# 5.2: Key Expansion: Rotate a 32bit word by 8 bits +proc ::aes::RotWord {w} { + return [expr {(($w << 8) | (($w >> 24) & 0xff)) & 0xffffffff}] +} + +# 5.1.1: SubBytes() Transformation +proc ::aes::SubBytes {words} { + set r {} + foreach w $words { + lappend r [SubWord $w] + } + return $r +} + +# 5.3.2: InvSubBytes() Transformation +proc ::aes::InvSubBytes {words} { + set r {} + foreach w $words { + lappend r [InvSubWord $w] + } + return $r +} + +# 5.1.2: ShiftRows() Transformation +proc ::aes::ShiftRows {words} { + for {set n0 0} {$n0 < 4} {incr n0} { + set n1 [expr {($n0 + 1) % 4}] + set n2 [expr {($n0 + 2) % 4}] + set n3 [expr {($n0 + 3) % 4}] + lappend r [expr {( [lindex $words $n0] & 0xff000000) + | ([lindex $words $n1] & 0x00ff0000) + | ([lindex $words $n2] & 0x0000ff00) + | ([lindex $words $n3] & 0x000000ff) + }] + } + return $r +} + + +# 5.3.1: InvShiftRows() Transformation +proc ::aes::InvShiftRows {words} { + for {set n0 0} {$n0 < 4} {incr n0} { + set n1 [expr {($n0 + 1) % 4}] + set n2 [expr {($n0 + 2) % 4}] + set n3 [expr {($n0 + 3) % 4}] + lappend r [expr {( [lindex $words $n0] & 0xff000000) + | ([lindex $words $n3] & 0x00ff0000) + | ([lindex $words $n2] & 0x0000ff00) + | ([lindex $words $n1] & 0x000000ff) + }] + } + return $r +} + +# 5.1.3: MixColumns() Transformation +proc ::aes::MixColumns {words} { + set r {} + foreach w $words { + set r0 [expr {(($w >> 24) & 255)}] + set r1 [expr {(($w >> 16) & 255)}] + set r2 [expr {(($w >> 8 ) & 255)}] + set r3 [expr {( $w & 255)}] + + set s0 [expr {[GFMult2 $r0] ^ [GFMult3 $r1] ^ $r2 ^ $r3}] + set s1 [expr {$r0 ^ [GFMult2 $r1] ^ [GFMult3 $r2] ^ $r3}] + set s2 [expr {$r0 ^ $r1 ^ [GFMult2 $r2] ^ [GFMult3 $r3]}] + set s3 [expr {[GFMult3 $r0] ^ $r1 ^ $r2 ^ [GFMult2 $r3]}] + + lappend r [expr {($s0 << 24) | ($s1 << 16) | ($s2 << 8) | $s3}] + } + return $r +} + +# 5.3.3: InvMixColumns() Transformation +proc ::aes::InvMixColumns {words} { + set r {} + foreach w $words { + set r0 [expr {(($w >> 24) & 255)}] + set r1 [expr {(($w >> 16) & 255)}] + set r2 [expr {(($w >> 8 ) & 255)}] + set r3 [expr {( $w & 255)}] + + set s0 [expr {[GFMult0e $r0] ^ [GFMult0b $r1] ^ [GFMult0d $r2] ^ [GFMult09 $r3]}] + set s1 [expr {[GFMult09 $r0] ^ [GFMult0e $r1] ^ [GFMult0b $r2] ^ [GFMult0d $r3]}] + set s2 [expr {[GFMult0d $r0] ^ [GFMult09 $r1] ^ [GFMult0e $r2] ^ [GFMult0b $r3]}] + set s3 [expr {[GFMult0b $r0] ^ [GFMult0d $r1] ^ [GFMult09 $r2] ^ [GFMult0e $r3]}] + + lappend r [expr {($s0 << 24) | ($s1 << 16) | ($s2 << 8) | $s3}] + } + return $r +} + +# 5.1.4: AddRoundKey() Transformation +proc ::aes::AddRoundKey {Key round words} { + upvar #0 $Key state + set r {} + set n [expr {$round * $state(Nb)}] + foreach w $words { + lappend r [expr {$w ^ [lindex $state(W) $n]}] + incr n + } + return $r +} + +# ------------------------------------------------------------------------- +# ::aes::GFMult* +# +# some needed functions for multiplication in a Galois-field +# +proc ::aes::GFMult2 {number} { + # this is a tabular representation of xtime (multiplication by 2) + # it is used instead of calculation to prevent timing attacks + set xtime { + 0x00 0x02 0x04 0x06 0x08 0x0a 0x0c 0x0e 0x10 0x12 0x14 0x16 0x18 0x1a 0x1c 0x1e + 0x20 0x22 0x24 0x26 0x28 0x2a 0x2c 0x2e 0x30 0x32 0x34 0x36 0x38 0x3a 0x3c 0x3e + 0x40 0x42 0x44 0x46 0x48 0x4a 0x4c 0x4e 0x50 0x52 0x54 0x56 0x58 0x5a 0x5c 0x5e + 0x60 0x62 0x64 0x66 0x68 0x6a 0x6c 0x6e 0x70 0x72 0x74 0x76 0x78 0x7a 0x7c 0x7e + 0x80 0x82 0x84 0x86 0x88 0x8a 0x8c 0x8e 0x90 0x92 0x94 0x96 0x98 0x9a 0x9c 0x9e + 0xa0 0xa2 0xa4 0xa6 0xa8 0xaa 0xac 0xae 0xb0 0xb2 0xb4 0xb6 0xb8 0xba 0xbc 0xbe + 0xc0 0xc2 0xc4 0xc6 0xc8 0xca 0xcc 0xce 0xd0 0xd2 0xd4 0xd6 0xd8 0xda 0xdc 0xde + 0xe0 0xe2 0xe4 0xe6 0xe8 0xea 0xec 0xee 0xf0 0xf2 0xf4 0xf6 0xf8 0xfa 0xfc 0xfe + 0x1b 0x19 0x1f 0x1d 0x13 0x11 0x17 0x15 0x0b 0x09 0x0f 0x0d 0x03 0x01 0x07 0x05 + 0x3b 0x39 0x3f 0x3d 0x33 0x31 0x37 0x35 0x2b 0x29 0x2f 0x2d 0x23 0x21 0x27 0x25 + 0x5b 0x59 0x5f 0x5d 0x53 0x51 0x57 0x55 0x4b 0x49 0x4f 0x4d 0x43 0x41 0x47 0x45 + 0x7b 0x79 0x7f 0x7d 0x73 0x71 0x77 0x75 0x6b 0x69 0x6f 0x6d 0x63 0x61 0x67 0x65 + 0x9b 0x99 0x9f 0x9d 0x93 0x91 0x97 0x95 0x8b 0x89 0x8f 0x8d 0x83 0x81 0x87 0x85 + 0xbb 0xb9 0xbf 0xbd 0xb3 0xb1 0xb7 0xb5 0xab 0xa9 0xaf 0xad 0xa3 0xa1 0xa7 0xa5 + 0xdb 0xd9 0xdf 0xdd 0xd3 0xd1 0xd7 0xd5 0xcb 0xc9 0xcf 0xcd 0xc3 0xc1 0xc7 0xc5 + 0xfb 0xf9 0xff 0xfd 0xf3 0xf1 0xf7 0xf5 0xeb 0xe9 0xef 0xed 0xe3 0xe1 0xe7 0xe5 + } + return [lindex $xtime $number] +} + +proc ::aes::GFMult3 {number} { + # multliply by 2 (via GFMult2) and add the number again on the result (via XOR) + return [expr {$number ^ [GFMult2 $number]}] +} + +proc ::aes::GFMult09 {number} { + # 09 is: (02*02*02) + 01 + return [expr {[GFMult2 [GFMult2 [GFMult2 $number]]] ^ $number}] +} + +proc ::aes::GFMult0b {number} { + # 0b is: (02*02*02) + 02 + 01 + #return [expr [GFMult2 [GFMult2 [GFMult2 $number]]] ^ [GFMult2 $number] ^ $number] + #set g0 [GFMult2 $number] + return [expr {[GFMult09 $number] ^ [GFMult2 $number]}] +} + +proc ::aes::GFMult0d {number} { + # 0d is: (02*02*02) + (02*02) + 01 + set temp [GFMult2 [GFMult2 $number]] + return [expr {[GFMult2 $temp] ^ ($temp ^ $number)}] +} + +proc ::aes::GFMult0e {number} { + # 0e is: (02*02*02) + (02*02) + 02 + set temp [GFMult2 [GFMult2 $number]] + return [expr {[GFMult2 $temp] ^ ($temp ^ [GFMult2 $number])}] +} + +# ------------------------------------------------------------------------- + +# aes::Encrypt -- +# +# Encrypt a blocks of plain text and returns blocks of cipher text. +# The input data must be a multiple of the block size (16). +# +proc ::aes::Encrypt {Key data} { + set len [string length $data] + if {($len % 16) != 0} { + return -code error "invalid block size: AES requires 16 byte blocks" + } + + set result {} + for {set i 0} {$i < $len} {incr i 1} { + set block [string range $data $i [incr i 15]] + append result [EncryptBlock $Key $block] + } + return $result +} + +# aes::DecryptBlock -- +# +# Decrypt a blocks of cipher text and returns blocks of plain text. +# The input data must be a multiple of the block size (16). +# +proc ::aes::Decrypt {Key data} { + set len [string length $data] + if {($len % 16) != 0} { + return -code error "invalid block size: AES requires 16 byte blocks" + } + + set result {} + for {set i 0} {$i < $len} {incr i 1} { + set block [string range $data $i [incr i 15]] + append result [DecryptBlock $Key $block] + } + return $result +} + +# ------------------------------------------------------------------------- +# Fileevent handler for chunked file reading. +# +proc ::aes::Chunk {Key in {out {}} {chunksize 4096}} { + upvar #0 $Key state + + #puts ||CHUNK.X||i=$in|o=$out|c=$chunksize|eof=[eof $in] + + if {[eof $in]} { + fileevent $in readable {} + set state(reading) 0 + } + + set data [read $in $chunksize] + + #puts ||CHUNK.R||i=$in|o=$out|c=$chunksize|eof=[eof $in]||[string length $data]||$data|| + + # Do nothing when data was read at all. + if {![string length $data]} return + + if {[eof $in]} { + #puts CHUNK.Z + set data [Pad $data 16] + } + + #puts ||CHUNK.P||i=$in|o=$out|c=$chunksize|eof=[eof $in]||[string length $data]||$data|| + + if {$out == {}} { + append state(output) [$state(cmd) $Key $data] + } else { + puts -nonewline $out [$state(cmd) $Key $data] + } +} + +proc ::aes::SetOneOf {lst item} { + set ndx [lsearch -glob $lst "${item}*"] + if {$ndx == -1} { + set err [join $lst ", "] + return -code error "invalid mode \"$item\": must be one of $err" + } + return [lindex $lst $ndx] +} + +proc ::aes::CheckSize {what size thing} { + if {[string length $thing] != $size} { + return -code error "invalid value for $what: must be $size bytes long" + } + return $thing +} + +proc ::aes::Pad {data blocksize {fill \0}} { + set len [string length $data] + if {$len == 0} { + set data [string repeat $fill $blocksize] + } elseif {($len % $blocksize) != 0} { + set pad [expr {$blocksize - ($len % $blocksize)}] + append data [string repeat $fill $pad] + } + return $data +} + +proc ::aes::Pop {varname {nth 0}} { + upvar 1 $varname args + set r [lindex $args $nth] + set args [lreplace $args $nth $nth] + return $r +} + +proc ::aes::Hex {data} { + binary scan $data H* r + return $r +} + +proc ::aes::aes {args} { + array set opts {-dir encrypt -mode cbc -key {} -in {} -out {} -chunksize 4096 -hex 0} + set opts(-iv) [string repeat \0 16] + set modes {ecb cbc} + set dirs {encrypt decrypt} + while {([llength $args] > 1) && [string match -* [set option [lindex $args 0]]]} { + switch -exact -- $option { + -mode { set opts(-mode) [SetOneOf $modes [Pop args 1]] } + -dir { set opts(-dir) [SetOneOf $dirs [Pop args 1]] } + -iv { set opts(-iv) [CheckSize -iv 16 [Pop args 1]] } + -key { set opts(-key) [Pop args 1] } + -in { set opts(-in) [Pop args 1] } + -out { set opts(-out) [Pop args 1] } + -chunksize { set opts(-chunksize) [Pop args 1] } + -hex { set opts(-hex) 1 } + -- { Pop args ; break } + default { + set err [join [lsort [array names opts]] ", "] + return -code error "bad option \"$option\":\ + must be one of $err" + } + } + Pop args + } + + if {$opts(-key) == {}} { + return -code error "no key provided: the -key option is required" + } + + set r {} + if {$opts(-in) == {}} { + + if {[llength $args] != 1} { + return -code error "wrong \# args:\ + should be \"aes ?options...? -key keydata plaintext\"" + } + + set data [Pad [lindex $args 0] 16] + set Key [Init $opts(-mode) $opts(-key) $opts(-iv)] + if {[string equal $opts(-dir) "encrypt"]} { + set r [Encrypt $Key $data] + } else { + set r [Decrypt $Key $data] + } + + if {$opts(-out) != {}} { + puts -nonewline $opts(-out) $r + set r {} + } + Final $Key + + } else { + + if {[llength $args] != 0} { + return -code error "wrong \# args:\ + should be \"aes ?options...? -key keydata -in channel\"" + } + + set Key [Init $opts(-mode) $opts(-key) $opts(-iv)] + + set readcmd [list [namespace origin Chunk] \ + $Key $opts(-in) $opts(-out) \ + $opts(-chunksize)] + + upvar 1 $Key state + set state(reading) 1 + if {[string equal $opts(-dir) "encrypt"]} { + set state(cmd) Encrypt + } else { + set state(cmd) Decrypt + } + set state(output) "" + fileevent $opts(-in) readable $readcmd + if {[info commands ::tkwait] != {}} { + tkwait variable [subst $Key](reading) + } else { + vwait [subst $Key](reading) + } + if {$opts(-out) == {}} { + set r $state(output) + } + Final $Key + } + + if {$opts(-hex)} { + set r [Hex $r] + } + return $r +} + +# ------------------------------------------------------------------------- + +package provide aes $::aes::version + +# ------------------------------------------------------------------------- +# Local variables: +# mode: tcl +# indent-tabs-mode: nil +# End: ADDED tcl/8.4/tcllib1.15/aes/aes.tcl.harpy Index: tcl/8.4/tcllib1.15/aes/aes.tcl.harpy ================================================================== --- tcl/8.4/tcllib1.15/aes/aes.tcl.harpy +++ tcl/8.4/tcllib1.15/aes/aes.tcl.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + eae21bc1-3c8a-498e-afcc-409d1b85a149 + SHA512 + Script + 2016-08-13T19:13:37.4687500Z + -1.00:00:00 + 0x2c322765603b5278 + + L+xL4YJ9xs42ZDA0h53VGaXagPlf/CEO9ygwQBOjbDPZZpDxLEano0iGryqh4jSQ6xFnVcU4RnLg + MRVx5YwVsufu6BH1A5O1xpSzX8Xm55PCJlS2fSk7CUPhPsNZR1E9tYluwdjTBdv2OiVYvfpjeDgG + omBWkBgx1zGckACbzNNOZcFih1BR+XsIKxP7HbRmVjFSro7fqHnsuTUbq1UY+kYfKyrjqfvz7n5e + jFYx5hoaRSbCjoGumbUKgflZ2Hn1vy9+4+pcVwBuB4VWixQ5hecD2xbBmOZYOimwYXy/aCNnyvHf + YooV6fF0v+1JC0MX6QFhr8V9p6ol4uVAig97Dt1ww9ZqqmA7dUXczrMTcJcWFbsS39red8eeWmkm + Guy9rrsNTw7yrtOjrWagQgC3Bi2UwoUr4ALgk7TzAt4gWckMqA+nAJ5HmBf1q2iCMzeKT/rqNe9Q + YeR0o3cdRmWSNYVOGNcgmZaBGrZQjzxD1n3ATKZokdXyx5bPt1iz8JUD573uIy6ovGUY1/UfuDWu + uhnY7EPxVStgwS0v/2yPkx0wLWWqYQCBzNr7POVJZLHRFsKf39EQ+DZuqgUSKkwfXqqkijpuqYBM + InKZBGoBIiHTWPlzNfttGrdLnMInZbhA2AtcljwxEbDuzsttvgutYJX6CrGpYlKqJezu3g2Lm1dQ + 4SeD54aL4Od3GhTh4VWxBBLSpDTLYfryFgmqWtZC+wGdcom4wnQMXty9GcqeTaMPAazh0fvo2Lmc + TajmmNY+HYkPlQ9tHVWa3OiRl5L42ItiwC0WYgXnT4rLxVWT6/URnlMgFSx8NrMNkZNp+yeCdYJy + oH03nYttPoT0e6IDqj88foy7KoEHi3EOsTJdQAUSIebLlATPluDhCaaF55x0YillJ8l020neisBb + OEhf89zCLhwHxzXYvJhIKUW3quaQrkefApbYW3Y7Th7kI4HeDzn4IDEEE2uJS0eM265Ck7wTcotu + RsetH6L6JY4JM/z5rR0++dPekPHSL5F60FONE3hcut2JNNammaZ3odUQBjlXOGSjOPu8yUjQjBsD + qlInio0EUzxdV1FzqbqbFJPeM85P4769ewVGZzqyBdqdoQH3rllmQvDv1OFTBCFxvC6+TbofNgGA + aXR22N54EvyTMVA4rcOg9ZQJ2q1TtAkDoicoYuDfiDY7TtrKJKDBnsNO74aPemxKHuZAI8O5kzvi + MmG9ZHYNV+yilMYgDFiFACUuzFWrphqgrVZ7cbNM9WAb2OOEPEB91JMYRTsFU3NMg+1pcGTBWCIP + BWrRRqp1DLJXxWzeY/pESef1JNKM7Q6xfCyyss6luq90SKufagZZG1WU0ltG1eM8B5H73OsWRw== + + ADDED tcl/8.4/tcllib1.15/aes/pkgIndex.tcl Index: tcl/8.4/tcllib1.15/aes/pkgIndex.tcl ================================================================== --- tcl/8.4/tcllib1.15/aes/pkgIndex.tcl +++ tcl/8.4/tcllib1.15/aes/pkgIndex.tcl @@ -0,0 +1,5 @@ +if {![package vsatisfies [package provide Tcl] 8.2]} { + # PRAGMA: returnok + return +} +package ifneeded aes 1.1.1 [list source [file join $dir aes.tcl]] ADDED tcl/8.4/tcllib1.15/aes/pkgIndex.tcl.harpy Index: tcl/8.4/tcllib1.15/aes/pkgIndex.tcl.harpy ================================================================== --- tcl/8.4/tcllib1.15/aes/pkgIndex.tcl.harpy +++ tcl/8.4/tcllib1.15/aes/pkgIndex.tcl.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + 8a806368-bf22-47be-8bef-5600be931cb2 + SHA512 + Script + 2016-08-13T19:13:47.8515625Z + -1.00:00:00 + 0x2c322765603b5278 + + FMzWAxor84OdXxLHTUUerNdIrtmL4cHa4OshraWMKQsKCgEdIG99VekBRZhrLQjgKpvyZ8I25QW3 + dLGZeveW7J9GAlEj2iIgq215IRiCK8H4adhjKqlo/H3CpkQRxt2/uvHwqAfKuXYn8Z87y+U/ojMA + P0MLuv+ARS3QY8AC5QGG8Q3FUnEVo0EFRuQxQHD+x4WlbnoRWDE6mRDr4f6WvPvakDzIiEr5DL1r + maFWwW0CYn57o0GlAeqHoXBnS7sZq8qweCF+W3HGpWMwoh+6VKrwX6zGSwTL4oEtRTJ6Wb9+8YP+ + EftWsJ80nAWGIE5w2C4ljgAvFD2tcUSMycFxKWT3SBySsZGmkOZBxvMJJej8kF6TCJ1BXoZVY+Ja + gLDU9yQqLQC5gCPgoav4wMV9kis5g7hTs4S/i9K569DPICvly2Acfunc3gpm7ubTXNnW+1M5pH56 + OENMJ+u7HLA3EF2L5fpIsEMhWIznqlJ68EiIkC9inmfYxV/R30F58DLzkvyOqkwrjJIV8zi4mm5Q + 19u0zNoeFqHDPeD+f9YhA76Ah9Pawgv9hZvcpka1rd3VxvLhS8tdSmXJvDzVMXxO4BPoy5rV89rV + 7h06bUmLda3UHKQlzUCSQ4SpMzHKyDzMyGzVBn6XZxppjtk7AcpzJL5FIZOeue/c4ott3iECOsWb + pXgIoABA0bNbIFrkOjvcaEIWeLhTPUcbSCBYeVwUrcVi87mCiETam+kSSCnVC4jfbKm5DCL2OMsj + QyEIxNrNKKddWXS9rTEAhxCoAdYpiUtGB2Jpup88l/BdJgX1Qyi0zqMoCk2sYeHErheCmAy+KV1M + CrrL9p8ta9WxEHSX1ROl/pmnJo284wC47bNIKbxljHvT035kkUfyQuFxZ7AbV82kosCnUbzAZYf/ + i1bUp4krXH60IrMBxaYjmo8itMHMTDYEJQ6O29NNV+TXdDFjRH3ur8iUQfoPnUvwpdtPt9ZfEEN+ + OO6xTJ55tM5uCbzGV/9i8a1EHBQqkhjjnG/z6Am8yeJmLRGmxIaO1hScf82EwF27UDxutWUrTmev + w63VgokcK1WKLDOzeNx/DmRRCjNViYtwkvi3GuwGVaNqAwYjxEZyo4y9ZauPw7oFbGa3hAQjrb2o + tPAGnirvAR2ryqI3psAPfRgn5I+IKPgsLb3KN8YCmJXCtfXvEsAHvXUcRUhIQlFtPBDzOXRN4aWE + 1RXReJf66zxMDT00lIiEL3/qOHz2aE3KUPlU7fgysQ5VmH8MOv7kQ/l3+BWxhWsOWQZwl2gogc7Q + ypsWnCCu+PBGMvTWuPaHGFrSBmH56xjc75g0C+oQh9N0DHeUvO+afOa7eyl5WG/eA+uIlSIgKQ== + +