-
Notifications
You must be signed in to change notification settings - Fork 122
/
hammerdbcli
executable file
·330 lines (323 loc) · 11.1 KB
/
hammerdbcli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#!/bin/sh
#########################################################################
## \
export LD_LIBRARY_PATH="./lib:$LD_LIBRARY_PATH"
## \
export PATH="./bin:$PATH"
## \
export PYTHONPATH="./lib/tclpy0.4:$PYTHONPATH"
## \
exec ./bin/tclsh8.6 "$0" ${1+"$@"}
########################################################################
# HammerDB
# Copyright (C) 2003-2024 Steve Shaw
# Author contact information at: http://www.hammerdb.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; If not, see <https://www.gnu.org/licenses/>
########################################################################
global hdb_version
set hdb_version "v4.12"
puts "HammerDB CLI $hdb_version"
puts "Copyright (C) 2003-2024 Steve Shaw"
if { $argc eq 0 } {
set argv0 "" } else { set argv0 [ string tolower [lindex $argv 0 ]] }
if { $argv0 == "" || $argv0 == "tcl" || $argv0 == "auto" } {
set lang "tcl"
set extension ".tcl"
puts "Type \"help\" for a list of commands"
} elseif { $argv0 == "py" || $argv0 == "python" } {
set lang "python"
set extension ".py"
puts "Type \"help()\" for a list of commands"
} else {
puts {Usage: hammerdbcli [ tcl|python [ auto [ script_to_autoload.[ tcl|py ] ] ] ]}
exit
}
namespace eval autostart {
set autostartap "false"
if { $argv0 == "tcl" || $argv0 == "py" || $argv0 == "python" } {
set argv [ lreplace $argv 0 0 ]
set argc [ expr {$argc - 1} ]
}
if { $argc eq 0 } { ; } else {
if {$argc != 2 || [lindex $argv 0] != "auto" } {
puts {Usage: hammerdbcli [ tcl|python [ auto [ script_to_autoload.[ tcl|py ] ] ] ]}
exit
} else {
set autostartap "true"
set autoloadscript [lindex $argv 1]
if { [ file exists $autoloadscript ] && [ file isfile $autoloadscript ] && [ file extension $autoloadscript ] eq $extension } {
;# autostart selected and tcl or py file exists
} else {
if { [ file exists $autoloadscript ] && [ file isfile $autoloadscript ] && [ file extension $autoloadscript ] != $extension } {
puts "Error: incorrect file extension for $lang"
if { $lang eq "tcl" } {
puts {Usage: hammerdbcli [ tcl [ auto [ script_to_autoload.[ tcl ] ] ]}
} else {
puts {Usage: hammerdbcli [ python [ auto [ script_to_autoload.[ py ] ] ]}
}
exit
} else {
puts {Usage: hammerdbcli [ tcl|python [ auto [ script_to_autoload.[ tcl|py ] ] ] ]}
}
}
}
}
}
#Common CLI initialisation between Tcl and Python
set cli_common_init {set UserDefaultDir [ file dirname [ info script ] ]
::tcl::tm::path add "$UserDefaultDir/modules"
append modulelist { Thread msgcat xml comm tclreadline task http reformat_tcl sqlite3 huddle jobs ticklecharts }
for { set modcount 0 } { $modcount < [llength $modulelist] } { incr modcount } {
set m [lindex $modulelist $modcount]
set loadtext $m
if [catch { package require $m }] {
puts stderr "While loading module\
\"$m\"...\n$errorInfo"
exit 1
}
}
append loadlist { genvu.tcl gentpcc.tcl gentpch.tcl gengen.tcl genxml.tcl genmodes.tcl genmetricscli.tcl gentccmn.tcl gentccli.tcl geninitcli.tcl gencli.tcl genhelp.tcl genstep.tcl }
for { set loadcount 0 } { $loadcount < [llength $loadlist] } { incr loadcount } {
set f [lindex $loadlist $loadcount]
set loadtext $f
if [catch {source [ file join $UserDefaultDir src generic $f ]}] {
puts stderr "While loading component file\
\"$f\"...\n$errorInfo"
exit 1
}
}
for { set dbsrccount 0 } { $dbsrccount < [llength $dbsrclist] } { incr dbsrccount } {
set f [lindex $dbsrclist $dbsrccount]
set loadtext $f
if [catch {source [ file join $UserDefaultDir src $f ]}] {
puts stderr "Error:loading database source files/$f"
}
}
init_job_tables
}
#In Tcl only either source file or do Tclreadline::interact
set cli_tcl_append {
if { $autostart::autostartap == "true" } {
source $autostart::autoloadscript
} else {
TclReadLine::interact
}}
#In Python on Linux use expect on Windows use Tclreadline::interact
set cli_py_append {
rename putscli _putscli
proc putscli { output } {
puts "$output\r"
}}
#Initialise CLI in Tcl
if { $lang == "tcl" } {
append cli_common_init $cli_tcl_append
eval $cli_common_init
#Initialise CLI in Python
} elseif { $lang eq "python" } {
proc pythonVersion {{pythonExecutable "python3"}} {
if {[string match windows $::tcl_platform(platform)]} { set pythonExecutable "python" }
if {![catch {exec $pythonExecutable --version}] || [lindex $::errorCode 0] eq "NONE"} {
} else {
puts "Error: Failed to find $::tcl_platform(platform) executable \"$pythonExecutable\""
return -1
}
set info [exec $pythonExecutable --version 2>@1]
if {[regexp {^Python ([\d.]+)$} $info --> version]} {
return $version
}
puts "Error: Failed to parse output of $pythonExecutable --version: '$info'"
return -1
}
if {[string match windows $::tcl_platform(platform)]} {
#Windows
proc winpause {} { after 3000 }
proc bgerror {message} { puts $message }
#Check python library versions are compatible
if {[catch {package require tclpy} message]} {
set pyver [pythonVersion]
if { $pyver != -1 } {
puts "Error: Python installation [pythonVersion] detected but at incorrect Python version, see HammerDB documentation"
} else {
puts "Error: Unable to detect $::tcl_platform(platform) Python installation"
}
winpause
exit
} else {
catch {package forget tclpy}
}
set UserDefaultDir [ file dirname [ info script ] ]
::tcl::tm::path add "$UserDefaultDir/modules"
package require tclreadline
regsub -all -line {tclreadline} $cli_common_init {} cli_common_init
append cli_common_init $cli_py_append
set py_init "import os
import sys
tcl_dll_location = os.getcwd() + r'\\bin'
os.add_dll_directory(tcl_dll_location)
sys.path.append(r'.\\lib\\tclpy0.4')
import tclpy
tclpy.eval('global hdb_version')
tclpy.eval('set hdb_version $hdb_version')
init_tcl = (r'''\n$cli_common_init\n''')
tclpy.eval(init_tcl)
from hammerdb import \*
sys.stdout.flush()
sys.ps1 = '>>>'"
proc piperead_interact {pipe echo} {
if {![eof $pipe]} {
set got [ read -nonewline $pipe ]
if {!$echo} { ; } else {
TclReadLine::pyclearline
if { [ string equal ">>>" $got ] } {
TclReadLine::prompt ""
} else {
if { [ string match "*>>>" $got ] } {
set got [ string trim [ string trimright $got "\n+>>>" ]]
puts "\r$got\n"
TclReadLine::prompt ""
} else {
set got [ string trim [regsub -all {\n(?:\s*\n)+} $got \n] \n ]
puts "$got"
}
}
}
} else {
catch {close $pipe}
exit
}
}
proc piperead_script {pipe} {
upvar script_end script_end
if {![eof $pipe]} {
set got [ read -nonewline $pipe ]
if { [ string match "*>>>" $got ] } {
puts "PYTHON SCRIPT END"
after 2000
set script_end 1
}
set got [ string trim [regsub -all {\n(?:\s*\n)+} $got \n] \n ]
puts "$got"
} else {
catch {close $pipe}
set script_end 1
}
}
set pipe [open "|[ auto_execok python] -uiq 2>@1" r+]
fconfigure $pipe -buffering line -blocking false -encoding cp1252
fileevent $pipe readable [list piperead_interact $pipe false]
set pywait [list]
foreach pyline [split $py_init \n] {
# send init line to the pipe
puts $pipe $pyline
# need some delay for the pipe
after 60 [list append pywait ""]
vwait pywait
}
if { $autostart::autostartap == "true" } {
set pywait [ list ]
puts $pipe "runscript(1)"
after 50 [ list append pywait ""]
vwait pywait
fileevent $pipe readable [list piperead_script $pipe]
puts $pipe "exec(open('$autostart::autoloadscript').read())"
vwait script_end
} else {
fileevent $pipe readable [ list piperead_interact $pipe true ]
puts $pipe "runscript(0)"
TclReadLine::interactpy $pipe
}
winpause
exit
} else {
#Linux
set syspath [ string trim $env(PYTHONPATH) ":*" ]
if { ![ file isdirectory $syspath ] } {
puts "Error:Cannot find HammerDB Python libraries $env(PYTHONPATH) is not a directory"
exit
}
if {[catch {package require Expect} ]} {
puts "Error:Failed to load Expect package to run Python"
exit
}
#Check python library versions are compatible
if {[catch {package require tclpy} message]} {
regexp {libpython([0-9]+.[0-9]+).so} $message matched pyver
if { [ info exists pyver ] && $pyver != [pythonVersion] } {
puts "Error: Python version $pyver required, version [pythonVersion] is installed"
exit
} else {
puts "Error: Unable to detect $::tcl_platform(platform) Python installation"
exit
}
} else {
catch {package forget tclpy}
}
#Remove tclreadline module from common initialisation in Python
regsub -all -line {tclreadline} $cli_common_init {} cli_common_init
append cli_common_init $cli_py_append
set timeout 10
log_user 0
spawn -noecho python3
expect ">>>"
send "import sys\x0D"
expect ">>>"
send "sys.path.append('$syspath')\x0D"
expect ">>>"
send "import tclpy\x0D"
expect ">>>"
send "tclpy.eval('global hdb_version')\x0D"
expect ">>>"
send "tclpy.eval('set hdb_version $hdb_version')\x0D"
expect ">>>"
send "init_tcl = (r'''\n$cli_common_init\n''')\x0D"
expect ">>>"
send "tclpy.eval(init_tcl)\x0D"
expect ">>>"
send "from hammerdb import *\x0D"
expect ">>>"
if { $autostart::autostartap == "true" } { send "runscript(1)\x0D" } else { send "runscript(0)\x0D" }
send "sys.stdout.flush()\x0D"
send "sys.ps1 = 'hammerdb>>>'\x0D"
#If timeout is set to a +ve value auto scripts will terminate before completion
set timeout -1
if { $autostart::autostartap == "true" } {
#run python script
trap {
send "\x03"
send_user "^C\n"
exit
} SIGINT
expect "hammerdb>>>"
send "exec(open('$autostart::autoloadscript').read())\x0D"
log_user 1
expect {"hammerdb>>>"}
} else {
expect "hammerdb>>>"
log_user 1
#-----------------------------#
#run interactive python shell
#-----------------------------#
interact {
\x03 {
#send Ctrl-C to Python
send "\x03"
send_user "^C"
return
}
}
}
exit
}
}