-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.tcl
283 lines (254 loc) · 6.19 KB
/
utils.tcl
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
# -*- mode: tcl; tab-width: 8; coding: utf-8 -*-
namespace eval ::sshcomm {}
namespace eval ::sshcomm::utils {
apply {cn {
if {[info commands $cn] ne ""} {
uplevel 1 $cn
}
}} ::sshcomm::register-plugin
proc lines-of args {
split [uplevel 1 $args] \n
}
proc default {varName {default ""}} {
upvar 1 $varName var
if {[info exists var]} {
set var
} else {
set default
}
}
proc dict-default {dict key {default ""}} {
if {[dict exists $dict $key]} {
dict get $dict $key
} else {
set default
}
}
proc dict-cut {dictVar key args} {
upvar 1 $dictVar dict
if {[dict exists $dict $key]} {
set res [dict get $dict $key]
dict unset dict $key
set res
} elseif {[llength $args]} {
lindex $args 0
} else {
error "No such key: $key"
}
}
# dictA - (dictB items which found in dictA too)
# (useful to check [file attributes] difference)
proc dict-left-difference {dictA dictB} {
set difference {}
foreach key [dict keys $dictA] {
if {[set val [dict get $dictA $key]] ne [dict get $dictB $key]} {
lappend difference $key $val
}
}
set difference
}
proc dict-compare {dictA dictB} {
set diffA {}
set diffB {}
foreach k [dict keys $dictA] {
if {![dict exists $dictB $k]
|| [dict get $dictB $k] ne [dict get $dictA $k]} {
dict set diffA $k [dict get $dictA $k]
}
}
foreach k [dict keys $dictB] {
if {![dict exists $dictA $k]
|| [dict get $dictA $k] ne [dict get $dictB $k]} {
dict set diffB $k [dict get $dictB $k]
}
}
if {$diffA eq "" && $diffB eq ""} {
return ""
} else {
list $diffA $diffB
}
}
proc is-empty str {
expr {$str eq ""}
}
proc lgrep {pattern list {cmdOrArgs ""} {apply ""}} {
set res {}
if {$cmdOrArgs eq "" && $apply eq ""} {
foreach i $list {
if {![regexp $pattern $i]} continue
lappend res $i
}
} else {
set cmd [if {$apply ne ""} {
list apply [list $cmdOrArgs $apply]
} else {
list $cmdOrArgs
}]
foreach i $list {
if {![llength [set m [regexp -inline $pattern $i]]]} continue
lappend res [{*}$cmd {*}$m]
}
}
set res
}
proc lsearch-and-get {list value {offset 0}} {
set mypos [lsearch $list $value]
if {$mypos < 0} {
error "No such value: $value"
}
lindex $list [expr {$mypos + $offset}]
}
proc file-has {pattern fn args} {
llength [filelist-having $pattern $fn {*}$args]
}
proc filelist-having {pattern fn args} {
set found {}
foreach fn [linsert $args 0 $fn] {
set fh [open $fn]
scope_guard fh [list close $fh]
for-chan-line line $fh {
if {![regexp $pattern $line]} continue
lappend found $fn
break
}
unset fh
}
set found
}
proc for-chan-line {lineVar chan command} {
upvar $lineVar line
while {[gets $chan line] >= 0} {
uplevel 1 $command
}
}
proc read_file {fn args} {
set fh [open $fn]
scope_guard fh [list close $fh]
if {[llength $args]} {
fconfigure $fh {*}$args
}
read $fh
}
proc read_file_lines {fn args} {
set fh [open $fn]
scope_guard fh [list close $fh]
if {[llength $args]} {
fconfigure $fh {*}$args
}
set lines {}
while {[gets $fh line] >= 0} {
lappend lines $line
}
set lines
}
proc shell-quote-string string {
# XXX: Is this enough for /bin/sh's "...string..." quoting?
# $
# backslash
# `
# "
# !
regsub -all {[$\\`\"!]} $string {\\&}
}
proc text-of-list-of-list {ll {sep " "} {eos "\n"}} {
set list {}
foreach i $ll {
lappend list [join $i $sep]
}
return [join $list \n]$eos
}
proc append_file {fn data args} {
write_file $fn $data {*}$args -access a
}
proc write_file_lines {fn list args} {
write_file $fn [join $list \n] {*}$args
}
proc write_file {fn data args} {
set data [string trim $data]
regsub {\n*\Z} $data \n data
write_file_raw $fn $data {*}$args
}
proc write_file_raw {fn data args} {
set access [dict-cut args -access w]
if {![regexp {^[wa]} $access]} {
error "Invalid access flag to write_file $fn: $access"
}
set attlist {}
set rest {}
if {[set perm [dict-cut args -permissions ""]] ne ""} {
if {[string is integer $perm]} {
lappend rest $perm
} else {
lappend attlist -permissions $perm
}
}
foreach att [list -group -owner] {
if {[set val [dict-cut args $att ""]] ne ""} {
lappend attlist $att $val
}
}
set fh [open $fn $access {*}$rest]
if {$attlist ne ""} {
file attributes $fn {*}$attlist
}
scope_guard fh [list close $fh]
if {[llength $args]} {
fconfigure $fh {*}$args
}
puts -nonewline $fh $data
set fn
}
proc scope_guard {varName command} {
upvar 1 $varName var
uplevel 1 [list trace add variable $varName unset \
[list apply [list args $command]]]
}
proc catch-exec args {
set rc [catch [list exec {*}$args] result]
set result
}
proc catch-exec-noerror args {
set rc [catch [list exec {*}$args] result]
expr {! $rc}
}
proc askpass {{title "Password"} {w .askpass}} {
catch {destroy $w}
toplevel $w -borderwidth 10
wm title $w $title
label $w.p -text $title
entry $w.pass -show * -textvar _password
label $w.dummy -text ""
button $w.ok -text OK -command {set _res $_password}
button $w.cancel -text Cancel -command {set _res {}}
grid $w.p $w.pass - -sticky wns
grid $w.dummy x x
grid x $w.ok $w.cancel -sticky news
bind $w <Return> [list $w.ok invoke]
bind $w <Escape> [list $w.cancel invoke]
raise $w
grab set $w
vwait _res
destroy $w
unset ::_password
return $::_res
}
}
# More specific commands
namespace eval ::sshcomm::utils {
# To use this, you must disable "requiretty" by visudo.
proc create-echopass {password {setenv SUDO_ASKPASS}} {
set uid [exec id -u]
set rand [format %x [expr {int(rand() * 1000000)}]]
set path /run/user/$uid/echopass-[pid]-$rand.sh
write_file $path [join [list #![info nameofexecutable] \
[list puts $password]] \n] \
-permissions 0700
if {$setenv ne ""} {
set ::env($setenv) $path
}
set path
}
}
namespace eval ::sshcomm::utils {
namespace export *
}