| 1 | #!/usr/bin/env tclsh |
|---|
| 2 | |
|---|
| 3 | ############################################################################### |
|---|
| 4 | # |
|---|
| 5 | # tolsh.tcl -- |
|---|
| 6 | # |
|---|
| 7 | ############################################################################### |
|---|
| 8 | |
|---|
| 9 | package provide tolsh 1.0 |
|---|
| 10 | |
|---|
| 11 | ############################################################################### |
|---|
| 12 | # |
|---|
| 13 | # Tol_HciWriter -- |
|---|
| 14 | # |
|---|
| 15 | # This file will be invoked by TOL if HCI output is enabled |
|---|
| 16 | # |
|---|
| 17 | ############################################################################### |
|---|
| 18 | proc Tol_HciWriter { msg } { |
|---|
| 19 | puts [string trim $msg] |
|---|
| 20 | } |
|---|
| 21 | |
|---|
| 22 | ############################################################################### |
|---|
| 23 | # |
|---|
| 24 | # namespace tolsh -- encapsulate tolsh functionality |
|---|
| 25 | # |
|---|
| 26 | ############################################################################### |
|---|
| 27 | namespace eval ::tolsh { |
|---|
| 28 | variable options |
|---|
| 29 | |
|---|
| 30 | # By default load initLibrary |
|---|
| 31 | # |
|---|
| 32 | set options(lang) 0 |
|---|
| 33 | set options(initlib) 1 |
|---|
| 34 | set options(compile) "" |
|---|
| 35 | set options(vmode) "" |
|---|
| 36 | set options(out,enabled) no |
|---|
| 37 | set options(runmode) "" |
|---|
| 38 | } |
|---|
| 39 | |
|---|
| 40 | ############################################################################### |
|---|
| 41 | # |
|---|
| 42 | # getoptions -- process the command line |
|---|
| 43 | # |
|---|
| 44 | # The command line accepted by tolsh is: |
|---|
| 45 | # |
|---|
| 46 | # -i --> do not include initLibrary |
|---|
| 47 | # -c"..." --> compile a tol expression |
|---|
| 48 | # -c "..." --> compile a tol expression |
|---|
| 49 | # -v?A? --> enable all output |
|---|
| 50 | # -vE --> enable error output |
|---|
| 51 | # -vS --> enable system output |
|---|
| 52 | # -vU --> enable user output |
|---|
| 53 | # -vT --> enable trace output |
|---|
| 54 | # -m?A? --> disable all output |
|---|
| 55 | # -mE --> disable error output |
|---|
| 56 | # -mS --> disable system output |
|---|
| 57 | # -mU --> disable user output |
|---|
| 58 | # -mT --> disable trace output |
|---|
| 59 | # -d --> start in dialog mode |
|---|
| 60 | # -e?n? --> use english language |
|---|
| 61 | # -s?p? --> use spanish language |
|---|
| 62 | # -server [port] --> start tol as a server tolsvc.<pid>.log will be the log |
|---|
| 63 | # [port] is the port number where the serve will be listening. |
|---|
| 64 | # |
|---|
| 65 | # Any other value will be considered a file to compile |
|---|
| 66 | # |
|---|
| 67 | # Arguments: |
|---|
| 68 | # |
|---|
| 69 | ############################################################################### |
|---|
| 70 | proc ::tolsh::getoptions { cmdline } { |
|---|
| 71 | variable options |
|---|
| 72 | |
|---|
| 73 | logtmp "entering ::tolsh::getoptions $cmdline" |
|---|
| 74 | |
|---|
| 75 | # cmdline could be a bad list so the next sentence could fail. |
|---|
| 76 | # try to test it in front of bugs situations. |
|---|
| 77 | set num [llength $cmdline] |
|---|
| 78 | set delayed_msg "" |
|---|
| 79 | set i 0 |
|---|
| 80 | while {$i < $num} { |
|---|
| 81 | set item [lindex $cmdline $i] |
|---|
| 82 | if {[string index $item 0] eq "-"} { |
|---|
| 83 | set opt [string range $item 1 end] |
|---|
| 84 | if {$opt eq "e" || $opt eq "en"} { |
|---|
| 85 | set $options(lang) 0 |
|---|
| 86 | } elseif {$opt eq "s" || $opt eq "sp"} { |
|---|
| 87 | set $options(lang) 1 |
|---|
| 88 | } elseif {$opt eq "i"} { |
|---|
| 89 | # initLibrary should not be loaded |
|---|
| 90 | # |
|---|
| 91 | set options(initlib) 0 |
|---|
| 92 | } elseif {[string index $opt 0] eq "c"} { |
|---|
| 93 | if {[string length $opt]>1} { |
|---|
| 94 | # The tol expression is part of the option |
|---|
| 95 | # |
|---|
| 96 | set expr [string range $opt 1 end] |
|---|
| 97 | } else { |
|---|
| 98 | # Next argument is the expression to compile |
|---|
| 99 | # |
|---|
| 100 | incr i |
|---|
| 101 | set expr [lindex $cmdline $i] |
|---|
| 102 | } |
|---|
| 103 | # Put expr in the list of "things" to compile |
|---|
| 104 | # |
|---|
| 105 | lappend options(compile) [list expr $expr] |
|---|
| 106 | } elseif {[string index $opt 0] eq "v"} { |
|---|
| 107 | if {[regexp {^[A|S|U|T|E|W]*$} [string range $opt 1 end] mode]} { |
|---|
| 108 | if {$mode eq ""} { |
|---|
| 109 | set mode A |
|---|
| 110 | } |
|---|
| 111 | if {[regexp {[A|S]} $mode]} { |
|---|
| 112 | set options(out,enabled) yes |
|---|
| 113 | } |
|---|
| 114 | append options(vmode) "v$mode " |
|---|
| 115 | } else { |
|---|
| 116 | append delayed_msg "\nignoring unknown verbose mode: '$opt'" |
|---|
| 117 | } |
|---|
| 118 | } elseif {[string index $opt 0] eq "m"} { |
|---|
| 119 | if {[regexp {^[A|S|U|T|E|W]*$} [string range $opt 1 end] mode]} { |
|---|
| 120 | if {$mode eq ""} { |
|---|
| 121 | set mode A |
|---|
| 122 | } |
|---|
| 123 | if {[regexp {[A|S]} $mode]} { |
|---|
| 124 | set options(out,enabled) no |
|---|
| 125 | } |
|---|
| 126 | append options(vmode) "m$mode " |
|---|
| 127 | } else { |
|---|
| 128 | append delayed_msg "\nignoring unknown mute mode: '$opt'" |
|---|
| 129 | } |
|---|
| 130 | } elseif {$opt eq "d"} { |
|---|
| 131 | if {$options(runmode) ne "" && $options(runmode) ne "interactive"} { |
|---|
| 132 | puts "Replacing previous run mode '$options(runmode)' with 'interactive'" |
|---|
| 133 | } |
|---|
| 134 | set options(runmode) "interactive" |
|---|
| 135 | } elseif {$opt eq "server"} { |
|---|
| 136 | # check if next argument is the port number |
|---|
| 137 | set port [lindex $cmdline [expr {$i+1}]] |
|---|
| 138 | if {[string is integer $port]} { |
|---|
| 139 | # $i+1 is an integer, should be considered as a port number |
|---|
| 140 | incr i |
|---|
| 141 | if {$port <= 0} { |
|---|
| 142 | puts "Could not start as a server: invalid port number '${port}'" |
|---|
| 143 | exit |
|---|
| 144 | } |
|---|
| 145 | if {$options(runmode) ne "" && $options(runmode) ne "server"} { |
|---|
| 146 | puts "Replacing previous run mode '$options(runmode)' with 'server'" |
|---|
| 147 | } |
|---|
| 148 | set options(runmode) "server" |
|---|
| 149 | set options(server,port) $port |
|---|
| 150 | } else { |
|---|
| 151 | puts "Could not start as a server: invalid port number '${port}'" |
|---|
| 152 | exit |
|---|
| 153 | } |
|---|
| 154 | } else { |
|---|
| 155 | append delayed_msg "\nignoring unknown option: '$item'" |
|---|
| 156 | } |
|---|
| 157 | } else { |
|---|
| 158 | # Consider this option as a tol file to compile. |
|---|
| 159 | # |
|---|
| 160 | if {![file exists $item] || ![file readable $item]} { |
|---|
| 161 | puts "file $item does not exist or is not readable" |
|---|
| 162 | } else { |
|---|
| 163 | lappend options(compile) [list file $item] |
|---|
| 164 | } |
|---|
| 165 | } |
|---|
| 166 | incr i |
|---|
| 167 | } |
|---|
| 168 | if {$delayed_msg ne ""} { |
|---|
| 169 | std $delayed_msg |
|---|
| 170 | } |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | ############################################################################### |
|---|
| 174 | # |
|---|
| 175 | # something_to_compile -- tell us if there is something to do |
|---|
| 176 | # |
|---|
| 177 | # Depending on the command line processing some tol expression or |
|---|
| 178 | # files needs to be compiled. All this is stored in options(compile) |
|---|
| 179 | # |
|---|
| 180 | # Return: |
|---|
| 181 | # |
|---|
| 182 | # true value if there is something to compile in $options(compile) |
|---|
| 183 | # false if $options(compile) is empty |
|---|
| 184 | # |
|---|
| 185 | ############################################################################### |
|---|
| 186 | proc ::tolsh::something_to_compile { } { |
|---|
| 187 | variable options |
|---|
| 188 | |
|---|
| 189 | llength $options(compile) |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | ############################################################################### |
|---|
| 193 | # |
|---|
| 194 | # show_usage -- show how to invoke tolsh |
|---|
| 195 | # |
|---|
| 196 | # This proc print into stdout a message that list the valid options accepted. |
|---|
| 197 | # |
|---|
| 198 | ############################################################################### |
|---|
| 199 | proc ::tolsh::show_usage { } { |
|---|
| 200 | puts "[get_app_name] \[options] filename ...\noptions:" |
|---|
| 201 | puts { |
|---|
| 202 | -i file --> include a file but not initLibrary |
|---|
| 203 | -c"..." --> compile a tol expression |
|---|
| 204 | -c "..." --> compile a tol expression |
|---|
| 205 | -v?A? --> enable all output |
|---|
| 206 | -vE --> enable error output |
|---|
| 207 | -vS --> enable standard output |
|---|
| 208 | -vU --> enable user output |
|---|
| 209 | -vT --> enable trace output |
|---|
| 210 | -m?A? --> disable all output |
|---|
| 211 | -mE --> disable error output |
|---|
| 212 | -mS --> disable standard output |
|---|
| 213 | -mU --> disable user output |
|---|
| 214 | -mT --> disable trace output |
|---|
| 215 | -d --> start in dialog mode |
|---|
| 216 | -e?n? --> use english language |
|---|
| 217 | -s?p? --> use spanish language |
|---|
| 218 | -server [port] --> start tol as a server tolsvc.<pid>.log will be the log |
|---|
| 219 | [port] is the port number where the serve will be listening. |
|---|
| 220 | |
|---|
| 221 | Multiple verbose mode is accepted in one option, for instance, |
|---|
| 222 | |
|---|
| 223 | -vUE |
|---|
| 224 | } |
|---|
| 225 | } |
|---|
| 226 | |
|---|
| 227 | ############################################################################### |
|---|
| 228 | # |
|---|
| 229 | # getArgv0 -- return the name of the app |
|---|
| 230 | # |
|---|
| 231 | ############################################################################### |
|---|
| 232 | proc ::tolsh::get_app_name {} { |
|---|
| 233 | global argv0 |
|---|
| 234 | |
|---|
| 235 | set name [file tail $argv0] |
|---|
| 236 | return [file rootname $name] |
|---|
| 237 | } |
|---|
| 238 | |
|---|
| 239 | ############################################################################### |
|---|
| 240 | # |
|---|
| 241 | # append_message -- save a message |
|---|
| 242 | # |
|---|
| 243 | ############################################################################### |
|---|
| 244 | proc ::tolsh::std { msg } { |
|---|
| 245 | variable options |
|---|
| 246 | |
|---|
| 247 | if {$options(out,enabled)} { |
|---|
| 248 | puts $msg |
|---|
| 249 | } |
|---|
| 250 | } |
|---|
| 251 | |
|---|
| 252 | proc ::tolsh::setup_pkg { pkg } { |
|---|
| 253 | if {[lsearch [package names] $pkg]==-1} { |
|---|
| 254 | if {[namespace exist ::starkit]} { |
|---|
| 255 | set bindir [file dir $starkit::topdir] |
|---|
| 256 | set libdir [file join $bindir .. lib] |
|---|
| 257 | set pkg_dirs [glob -nocomplain -dir $libdir ${pkg}*] |
|---|
| 258 | if {[llength $pkg_dirs]} { |
|---|
| 259 | lappend ::auto_path [file normalize $libdir] |
|---|
| 260 | } else { |
|---|
| 261 | set tclsh [auto_execok tclsh] |
|---|
| 262 | if {![catch { |
|---|
| 263 | exec echo puts {$::auto_path} | $tclsh |
|---|
| 264 | } ext_auto_path]} { |
|---|
| 265 | foreach p $ext_auto_path { |
|---|
| 266 | if {[lsearch $::auto_path $p]==-1} { |
|---|
| 267 | lappend ::auto_path $p |
|---|
| 268 | } |
|---|
| 269 | } |
|---|
| 270 | } |
|---|
| 271 | } |
|---|
| 272 | tclPkgUnknown ${pkg}+1 "" |
|---|
| 273 | } else { |
|---|
| 274 | set pkg_dev_dir [file normalize [file join .. $pkg]] |
|---|
| 275 | if {[file exists $pkg_dev_dir] && [file isdir $pkg_dev_dir]} { |
|---|
| 276 | lappend ::auto_path $pkg_dev_dir |
|---|
| 277 | } |
|---|
| 278 | } |
|---|
| 279 | } |
|---|
| 280 | } |
|---|
| 281 | |
|---|
| 282 | ############################################################################### |
|---|
| 283 | # |
|---|
| 284 | # setup_autopath -- |
|---|
| 285 | # |
|---|
| 286 | # Set auto_path in order to find correctly the following packages: |
|---|
| 287 | # |
|---|
| 288 | # - toltcl |
|---|
| 289 | # - rmtps_client |
|---|
| 290 | # |
|---|
| 291 | ############################################################################### |
|---|
| 292 | proc ::tolsh::setup_autopath { } { |
|---|
| 293 | logtmp "entering ::tolsh::setup_autopath" |
|---|
| 294 | |
|---|
| 295 | setup_pkg toltcl |
|---|
| 296 | setup_pkg tolcomm |
|---|
| 297 | setup_pkg tlogger |
|---|
| 298 | |
|---|
| 299 | logtmp "auto_path = $::auto_path" |
|---|
| 300 | logtmp "leaving ::tolsh::setup_autopath" |
|---|
| 301 | } |
|---|
| 302 | |
|---|
| 303 | ############################################################################### |
|---|
| 304 | # |
|---|
| 305 | # main -- entry point for tolsh application |
|---|
| 306 | # |
|---|
| 307 | ############################################################################### |
|---|
| 308 | proc ::tolsh::run { cmdline } { |
|---|
| 309 | variable options |
|---|
| 310 | |
|---|
| 311 | logtmp "user : [ exec whoami ]" |
|---|
| 312 | logtmp "entering run with cmdline = $cmdline" |
|---|
| 313 | # Process command line options |
|---|
| 314 | # |
|---|
| 315 | getoptions $cmdline |
|---|
| 316 | logtmp "options(compile): $options(compile)" |
|---|
| 317 | logtmp "options(runmode): $options(runmode)" |
|---|
| 318 | if {![something_to_compile] && ($options(runmode) eq "")} { |
|---|
| 319 | show_usage |
|---|
| 320 | return |
|---|
| 321 | } |
|---|
| 322 | |
|---|
| 323 | logtmp "pase getoptions" |
|---|
| 324 | |
|---|
| 325 | # Ensure Toltcl is found |
|---|
| 326 | # |
|---|
| 327 | setup_autopath |
|---|
| 328 | |
|---|
| 329 | # Load Toltcl (+tol) |
|---|
| 330 | # |
|---|
| 331 | package require -exact Toltcl 2.0.1 |
|---|
| 332 | |
|---|
| 333 | logtmp "after require Toltcl" |
|---|
| 334 | |
|---|
| 335 | tol::initkernel $options(lang) $options(vmode) |
|---|
| 336 | |
|---|
| 337 | # Load initLibrary if required |
|---|
| 338 | # |
|---|
| 339 | if {$options(initlib)} { |
|---|
| 340 | tol::initlibrary |
|---|
| 341 | } |
|---|
| 342 | |
|---|
| 343 | set appdata [string trim \ |
|---|
| 344 | [lindex [::tol::info var [list Text TolAppDataPath]] 2] \"] |
|---|
| 345 | |
|---|
| 346 | logtmp "appdata = $appdata" |
|---|
| 347 | |
|---|
| 348 | set tolcomm_dir [file join $appdata tolcomm] |
|---|
| 349 | file mkdir $tolcomm_dir |
|---|
| 350 | |
|---|
| 351 | # load rmtps_client |
|---|
| 352 | # |
|---|
| 353 | if {[catch {package require rmtps_client} msg]} { |
|---|
| 354 | std "could not load rmtps_client: $msg" |
|---|
| 355 | } else { |
|---|
| 356 | ::rmtps_client::init [file join $tolcomm_dir "rmtps_client.log"] |
|---|
| 357 | if {[catch rmtps_client::define_TOLAPI msg]} { |
|---|
| 358 | std "could not load tol code for rmtps_client: $msg" |
|---|
| 359 | } |
|---|
| 360 | } |
|---|
| 361 | |
|---|
| 362 | # load tolclient |
|---|
| 363 | # |
|---|
| 364 | if {[catch {package require tolclient} msg]} { |
|---|
| 365 | std "could not load tolclient: $msg" |
|---|
| 366 | } else { |
|---|
| 367 | ::tolclient::init [file join $tolcomm_dir "tolclient.log"] |
|---|
| 368 | if {[catch ::tolclient::define_TOLAPI msg]} { |
|---|
| 369 | std "could not load tol code for rmtps_client: $msg" |
|---|
| 370 | } |
|---|
| 371 | } |
|---|
| 372 | |
|---|
| 373 | # Compile expression and tol file provided in the command line |
|---|
| 374 | # |
|---|
| 375 | foreach item $options(compile) { |
|---|
| 376 | if {[lindex $item 0] eq "expr"} { |
|---|
| 377 | tol::console eval [lindex $item 1] |
|---|
| 378 | } elseif {[lindex $item 0] eq "file"} { |
|---|
| 379 | tol::include [lindex $item 1] |
|---|
| 380 | } |
|---|
| 381 | } |
|---|
| 382 | |
|---|
| 383 | if {$options(runmode) eq "interactive"} { |
|---|
| 384 | # assign our event handler for stdin |
|---|
| 385 | fileevent stdin readable ::tolsh::stdin_handler |
|---|
| 386 | # send a startup message and initial prompt |
|---|
| 387 | puts stdout "TOL interactive shell activated..." |
|---|
| 388 | console_prompt |
|---|
| 389 | # enter the tcl event loop |
|---|
| 390 | vwait __forever__ |
|---|
| 391 | } elseif {$options(runmode) eq "server"} { |
|---|
| 392 | package require tolserver |
|---|
| 393 | tolserver::start -port $options(server,port) \ |
|---|
| 394 | -logfile [file join $tolcomm_dir "tolserver.log"] |
|---|
| 395 | vwait __forever__ |
|---|
| 396 | } |
|---|
| 397 | } |
|---|
| 398 | |
|---|
| 399 | proc ::tolsh::console_cmd { cmd } { |
|---|
| 400 | if {$cmd eq "END" || $cmd eq "QUIT" || $cmd eq "EXIT"} { |
|---|
| 401 | exit |
|---|
| 402 | } else { |
|---|
| 403 | puts stdout "unknown console command: '$cmd'" |
|---|
| 404 | } |
|---|
| 405 | } |
|---|
| 406 | |
|---|
| 407 | proc ::tolsh::console_eval { tol_stms } { |
|---|
| 408 | set con_objs0 [tol::console stack list] |
|---|
| 409 | tol::console eval $tol_stms |
|---|
| 410 | set con_objs1 [tol::console stack list] |
|---|
| 411 | if {[llength $con_objs1]>[llength $con_objs0]} { |
|---|
| 412 | puts stdout [lindex [lindex $con_objs1 end] 2] |
|---|
| 413 | } |
|---|
| 414 | } |
|---|
| 415 | |
|---|
| 416 | proc ::tolsh::console_prompt {} { |
|---|
| 417 | puts -nonewline stdout \ |
|---|
| 418 | "[clock format [clock seconds] -format %H:%M:%S] TOL> " |
|---|
| 419 | flush stdout |
|---|
| 420 | } |
|---|
| 421 | |
|---|
| 422 | proc ::tolsh::stdin_handler {} { |
|---|
| 423 | # get the data from stdin |
|---|
| 424 | # |
|---|
| 425 | gets stdin data |
|---|
| 426 | if {[string index $data 0] eq "\\"} { |
|---|
| 427 | console_cmd [string toupper [string range $data 1 end]] |
|---|
| 428 | } else { |
|---|
| 429 | console_eval $data |
|---|
| 430 | } |
|---|
| 431 | # return a new pseudo-prompt |
|---|
| 432 | console_prompt |
|---|
| 433 | return |
|---|
| 434 | } |
|---|
| 435 | |
|---|
| 436 | proc logtmp { msg } { |
|---|
| 437 | return |
|---|
| 438 | set user [ exec whoami ] |
|---|
| 439 | set fd [ open "/tmp/tolsh.${user}.log" a ] |
|---|
| 440 | puts $fd $msg |
|---|
| 441 | flush $fd |
|---|
| 442 | close $fd |
|---|
| 443 | } |
|---|
| 444 | |
|---|
| 445 | ::tolsh::run $argv |
|---|