The often used screen name for David S. Cargo (escargo@skypoint.com and dcargo@mednetstudy.com). My home IP address is often 199.86.45.178 (from home) or 67.53.175.180 (from work), and will appear when looking at Recent Changes .
Updated slightly on 05/05/05 since it's such a funny date.
Initials? Why would I prefer to use initials with a handle like this?
I have been coding in tcl, tk, and expect since 1996.
For a few years I was a Multics [L1 ] user, and have also used (in ancient times) CP/M-80, MS-DOS, VAX VMS, TOPS-10, old Honeywell minicomputer systems, ModComp Systems.
More recently I have used CrayOS, Solaris, Microsoft Windows (starting at 3.1), and Linux.
I did manage to attend the Tcl 2002 conference in Vancouver. Very nice.
I have started a few pages, including one with an application:
and others more philosophical
For $25/yr you can be escargot@slug.org.
(That's the yearly membership for slug.org) PSE
For not much more than that I can get my own domain name. I'm not sure a snail should be consorting with slugs. They are fellow mollusks, but still....
Pet Peeves
I may wind up doing some minor editorial (copyediting [L2 ] [L3 ]) changes to wiki pages that I see that have the following problems.
I won't mark these changes since I don't think they will change the meaning.
EKB I support you in this effort, but mention that the rules for "which" vs. "that" are different in the UK and the US.
escargo I know that, so I like to check to see which kind of quotations might be used (single versus double quotation marks) to guess which kind of localization needs to be done.
My tendencies toward punctuation correction have led two different people to each give me a copy of Eats, Shoots & Leaves: The Zero Tolerance Approach to Punctuation, by Lynne Truss.
neb Would that be a 'compunctuation'... a compunction for punctuation?
My Current Puzzle
May the source be with you...
I'm trying to develop an application that can, among other functions, read and process Tcl commands from a file. I want to log operations (using the tcllib logger package). Ideally, legal Tcl would be processed in a way equivalent to the source command. However, I'm seeing interesting differences.
First, some legal operations (to be stored in a file named test.tcl):
proc requireOperation {service operation} { if {[string trim $operation] eq ""} { puts "blank operation" } return 1 } set ops [list operation1 operation2 operation3 operation4] set service someService foreach op $ops { requireOperation $service $op } # This should be the equivalent of the code above. set ops [list \ operation1 \ operation2 \ operation3 \ operation4] foreach op $ops { requireOperation $service $op }
To run this code, you must first do
package require logger set log [logger::init test] source process_file.tcl process_file $log test.tcl
This is the code that has the problem somewhere:
# process_file.tcl - This file contains procs for executing files or # lists of Tcl commands. Errors are caught and reported. Execution # is not terminated at this level. If execution is terminated, it # will happen at the logging proc level. # logObj - a logger object to use for logging # fileName - the name of the file to execute commands from # This proc reads the entire file before processing starts. # It does not finely diagnose why a file cannot be read. proc process_file {logObj fileName} { ${logObj}::info "Opening $fileName" if {[catch {open $fileName} handle]} { ${logObj}::critical "Failure opening $fileName" set result 1 } else { set result 0 set contents [read $handle [file size $fileName]] close $handle # This was added to solve one problem but it might have created another. set contents [string map [list "\\\n" ""] $contents] set listOfLines [split $contents \n] runCommands $logObj $listOfLines } return $result } # logObj - a logger object to use for logging # listOfLines - a list containing a sequence of lines to execute # The proc does not care where the lines came from. proc runCommands {logObj listOfLines} { set n [llength $listOfLines] ${logObj}::debug "runCommands passed $n lines." set command "" # Using for so that lines are counted. for {set i 0} {$i < $n} {incr i} { set line [lindex $listOfLines $i] ${logObj}::debug "Line $i: $line" append command $line # Allow manual exit of this loop without a real EOF signal. if {$command eq "EOF"} { break } elseif {[info complete $command]} { runOneCommand $logObj $command set command "" } } } # logObj - a logger object to use for logging # command - the command to execute # This proc is only supposed to be called when "info complete" # says that there is a complete command. However, that is true # for the empty string and even a list of empty strings. # Don't bother to safeguard against those for now. proc runOneCommand {logObj command} { global errorInfo # Execute command at global level. ${logObj}::info "Executing $command." if {[catch {uplevel \#0 $command} result]} { puts [set errorInfo] ${logObj}::error "Error executing $command: [set errorInfo]" } else { puts $result } }
The problem seems to result in confused tokenization of the processed code. I get messages like:
wrong # args: extra words after "else" clause in "if" command
It seems like this ought to be simple, but it's not.
RJ I think I see what may be happening. Here is what listOfLines ends up looking like when passed to the next proc:
\ proc\ requireOperation\ \{service\ operation\}\ \{ \ \ \ \ \ if\ \{\[string\ trim\ \$operation\]\ ==\ \"\"\}\ \{ { puts "blank operation"} \ \ \ \ \ \} { return 1} \ \} {} { set ops [list operation1 operation2 operation3 operation4]} {} { set service someService} \ foreach\ op\ \$ops\ \{ { requireOperation $service $op} \ \} {}
See if command page - if the if command appears on a single line and ends in a continuation "\" it expects the next line to start with 'else'. Could it be seeing the "\" chars as continuations? MG doesn't think that's entirely true. It only needs an "else" keyword and clause if there's -something- there. If it's just spaces (or no characters at all) on the lines, they're ignored totally.
MG The problem is your
append command $line
line of code. All the lines in the body of the proc are being appended into one single line (because 'info complete' doesn't return 1 until the closing } for the proc definition), so the body of the requireOperations proc is:
if {[string trim $operation] eq ""} { puts "blank operation" } return 1
which takes advantage of the fact that if doesn't need the 'then' / 'else' keywords to work, and assumes "return" is its "else" clause - and then has an extra word after the else clause, the "1". If you ended (or started) each separate command line with a semi-colon it would sort the problem, but wouldn't really be generally useable.
The same problem is likely to come up within a foreach (or an if, for that matter) when called outside a proc - if its args have multiple lines, they'll all be concatenated into one single line. Perhaps using
append command "; $line"
would fix the problem, but I'm not sure. I'll try that now... Yup, it seems it does, at least in the test case you used on this page. I'm not sure whether it'll always work, but so far...
escargo 27 Sep 2005 - Indeed, "so far" it seems to be doing the trick. I now have my logging working to my satisfaction. Thank you.
Messages to escargo:
LVwikignome - 2013-04-28 11:18:48
Good morning! I was reading one of the wiki pages on argument parsing and you mentioned developing an argument parsing strategy that worked for command line or graphical specification.
I am playing with tcl a bit, and was thinking that it would be interesting to allow:
Would one application require all of this? Maybe - if one was writing a general utility like a 7zip, or file comparison, etc. program.
Anyways, I was just curious whether your efforts had resulted in anything that was available to examine and think about.
Thank you so much for all your contributions.
AK - 2013-04-29 15:51:27
I should possibly point to the Commander now, at http://core.tcl.tk/akupries/cmdr/index Not much of documentation at the moment. Examples only in the test suite.