~ubuntu-branches/ubuntu/utopic/usb-modeswitch/utopic-proposed

« back to all changes in this revision

Viewing changes to usb_modeswitch.tcl

  • Committer: Bazaar Package Importer
  • Author(s): Didier Raboud
  • Date: 2010-05-03 11:37:08 UTC
  • mfrom: (1.2.1 upstream) (8.1.1 sid)
  • Revision ID: james.westby@ubuntu.com-20100503113708-y9s2bbgvr264bmmb
Tags: 1.1.2-2
Add 03_filter_undesired_rules.patch to filter out undesired files
(Closes: #579981)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/bin/sh
 
2
# next lines for bash, ignored by tclsh, restarting in background\
 
3
export PATH=/bin:/usr/bin; \
 
4
if [ ! -e "/usr/bin/tclsh" ]; then \
 
5
        logger -p syslog.error "usb_modeswitch: tcl shell not found, install tcl package!"; \
 
6
fi; \
 
7
/usr/bin/tclsh "$0" "$@" &>/dev/null & \
 
8
sleep 1; \
 
9
exit
 
10
 
 
11
 
 
12
# Wrapper (tcl) for usb_modeswitch, called from
 
13
# /lib/udev/rules.d/40-usb_modeswitch.rules
 
14
# (part of data pack "usb-modeswitch-data")
 
15
#
 
16
# Does ID check on hotplugged USB devices and calls the
 
17
# mode switching program with the matching parameter file
 
18
# from /etc/usb_modeswitch.d
 
19
#
 
20
# Part of usb-modeswitch-1.1.2 package
 
21
# (C) Josua Dietze 2009, 2010
 
22
 
 
23
 
 
24
# Setting of the following switches is done in an external config
 
25
# file (/etc/usb_modeswitch.conf)
 
26
 
 
27
set logging 0
 
28
set noswitching 0
 
29
 
 
30
 
 
31
 
 
32
set env(PATH) "/bin:/usr/bin"
 
33
 
 
34
# Execution starts at file bottom
 
35
 
 
36
proc {Main} {argc argv} {
 
37
 
 
38
global scsi usb match wc device logging noswitching
 
39
 
 
40
# The facility to add a symbolic link pointing to the
 
41
# ttyUSB port which provides interrupt transfer, i.e.
 
42
# the port to connect through; returns a symlink name
 
43
# for udev and exits
 
44
 
 
45
if {[lindex $argv 0] == "symlink"} {
 
46
        puts [SymLinkName [lindex $argv 2]]
 
47
        exit
 
48
}
 
49
eval file delete d [glob -nocomplain /tmp/gsmmodem_*]
 
50
 
 
51
set dbdir       /etc/usb_modeswitch.d
 
52
set bindir      /usr/sbin
 
53
 
 
54
set devList1 {}
 
55
set devList2 {}
 
56
 
 
57
 
 
58
# argv contains the values provided from the udev rule
 
59
# separated by "/"
 
60
 
 
61
set argList [split [lindex $argv 0] /]
 
62
 
 
63
if [string length [lindex $argList 1]] {
 
64
        set device [lindex $argList 1]
 
65
} else {
 
66
        set device "noname"
 
67
}
 
68
 
 
69
ParseConfigFile
 
70
 
 
71
Log "raw args from udev: $argv"
 
72
 
 
73
if {$device == "noname"} {
 
74
        Log "No data from udev. Exiting"
 
75
        SafeExit
 
76
}
 
77
 
 
78
if {![string match *0 $device]} {
 
79
        Log "Interface is not 0. Not the install storage. Exiting"
 
80
        SafeExit
 
81
}
 
82
 
 
83
 
 
84
# arg 0: the bus id for the device (udev: %b)
 
85
# arg 1: the "kernel name" for the device (udev: %k)
 
86
#
 
87
# Both together give the top directory where the path
 
88
# to the SCSI attributes can be determined (further down)
 
89
# Addendum: older kernel/udev version seem to differ in
 
90
# providing these attributes - or not. So more probing
 
91
# is needed
 
92
 
 
93
if {[string length [lindex $argList 0]] == 0} {
 
94
        if {[string length [lindex $argList 1]] == 0} {
 
95
                Log "No device number values given from udev! Exiting"
 
96
                SafeExit
 
97
        } else {
 
98
                Log "Bus ID for device not given by udev."
 
99
                Log " Trying to determine it from kernel name ([lindex $argList 1]) ..."
 
100
                if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} {
 
101
                        Log "Could not determine top device dir from udev values! Exiting"
 
102
                        SafeExit
 
103
                }
 
104
        }
 
105
} else {
 
106
        set dev_top [lindex $argList 0]
 
107
        regexp {(.*?):} $dev_top d dev_top
 
108
}
 
109
 
 
110
 
 
111
set devdir /sys/bus/usb/devices/$dev_top
 
112
if {![file isdirectory $devdir]} {
 
113
        Log "Top sysfs directory not found ($devdir)! Exiting"
 
114
        SafeExit
 
115
}
 
116
 
 
117
 
 
118
# Mapping of the short string identifiers (in the config
 
119
# file names) to the long name used here
 
120
#
 
121
# If we need them it's a snap to add new attributes here!
 
122
 
 
123
set match(sVe) scsi(vendor)
 
124
set match(sMo) scsi(model)
 
125
set match(sRe) scsi(rev)
 
126
set match(uMa) usb(manufacturer)
 
127
set match(uPr) usb(product)
 
128
set match(uSe) usb(serial)
 
129
 
 
130
 
 
131
# Now reading the USB attributes
 
132
 
 
133
ReadUSBAttrs $devdir
 
134
 
 
135
if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
 
136
        Log "USB IDs not found in sysfs tree. Exiting"
 
137
        SafeExit
 
138
}
 
139
 
 
140
Log "----------------\nUSB values from sysfs:"
 
141
foreach attr {manufacturer product serial} {
 
142
        Log "  $attr\t$usb($attr)"
 
143
}
 
144
Log "----------------"
 
145
 
 
146
if $noswitching {
 
147
        Log "\nSwitching globally disabled. Exiting\n"
 
148
        catch {exec logger -p syslog.notice "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)"}
 
149
        SafeExit
 
150
}
 
151
 
 
152
# Special ZTE check
 
153
if {"$usb(idVendor)$usb(idProduct)" == "19d22000"} {
 
154
        foreach dir {/etc/udev/rules.d /lib/udev/rules.d} {
 
155
                catch {eval exec grep {"19d2.*2000.*eject"} [glob -nocomplain $dir/*]} result
 
156
                if [regexp {(.*?):.*19d2} $result d ruleFile] {
 
157
                        Log "\nExisting ZTE rule found in $ruleFile. Exiting\n"
 
158
                        SafeExit
 
159
                }
 
160
        }
 
161
}
 
162
 
 
163
# Check if there is more than one config file for this USB ID,
 
164
# which would point to a possible ambiguity. If so, check if
 
165
# SCSI values are needed
 
166
 
 
167
# The glob matches $idVendor:$idProduct without postfix and with :spec=value postfixes
 
168
# This allows to filter out .dpkg-old files
 
169
set configList [glob -nocomplain $dbdir/$usb(idVendor):$usb(idProduct){,\[:a-Z0-9=\]*}]
 
170
if {[llength $configList] == 0} {
 
171
        Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exiting"
 
172
        SafeExit
 
173
}
 
174
 
 
175
set scsiNeeded false
 
176
if {[llength $configList] > 1} {
 
177
        if [regexp {:s} $configList] {
 
178
                set scsiNeeded true
 
179
        }
 
180
}
 
181
if {!$scsiNeeded} {
 
182
        Log "SCSI attributes not needed, moving on"
 
183
}
 
184
 
 
185
 
 
186
# Getting the SCSI values via libusb results in a detached
 
187
# usb-storage driver. Not good for devices that want to be
 
188
# left alone. Fortunately, the sysfs tree provides the values
 
189
# too without need for direct access
 
190
 
 
191
# First we wait until the SCSI data is ready - or timeout.
 
192
# Timeout means: no storage driver was bound to the device.
 
193
# We run 20 times max, every half second (max. 10 seconds
 
194
# total)
 
195
 
 
196
# We also check if the device itself changes, probably
 
197
# because it was switched by the kernel (or even unplugged).
 
198
# Then we do simply nothing and exit quietly ...
 
199
 
 
200
set counter 0
 
201
while {$scsiNeeded && $counter < 20} {
 
202
        after 500
 
203
        incr counter
 
204
        Log "waiting for storage tree in sysfs"
 
205
 
 
206
        set sysdir $devdir/[lindex $argList 1]
 
207
 
 
208
        if {![file isdirectory $sysdir]} {
 
209
                # Device is gone. Unplugged? Switched by kernel?
 
210
                Log "sysfs device tree is gone; exiting"
 
211
                SafeExit
 
212
        }
 
213
        set rc [open $devdir/product r]
 
214
        set newproduct [read -nonewline $rc]
 
215
        close $rc
 
216
        if {![string match $newproduct $usb(product)]} {
 
217
                # Device has just changed. Switched by someone else?
 
218
                Log "device has changed; exiting"
 
219
                SafeExit
 
220
        }
 
221
 
 
222
        # Searching the storage/SCSI tree; might take a while
 
223
        if {[set dirList [glob -nocomplain $sysdir/host*]] != ""} {
 
224
                set sysdir [lindex $dirList 0]
 
225
                if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} {
 
226
                        set sysdir [lindex $dirList 0]
 
227
                        regexp {.*target(.*)} $sysdir d subdir
 
228
                        if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} {
 
229
                                set sysdir [lindex $dirList 0]
 
230
                                if [file exists $sysdir/vendor] {
 
231
                                        # Finally SCSI structure is ready, get the values
 
232
                                        ReadSCSIAttrs $sysdir
 
233
                                        Log "SCSI values read"
 
234
                                        break
 
235
                                }
 
236
                        }
 
237
                }
 
238
        }
 
239
}
 
240
if $scsiNeeded {
 
241
        if {$counter == 20 && [string length $scsi(vendor)] == 0} {
 
242
                Log "SCSI tree not found; you may want to check if this path/file exists:"
 
243
                Log "$sysdir/vendor\n"
 
244
        } else {
 
245
                Log "----------------\nSCSI values from sysfs:"
 
246
                foreach attr {vendor model rev} {
 
247
                        Log " $attr\t$scsi($attr)"
 
248
                }
 
249
                Log "----------------"
 
250
        }
 
251
        Log "Waiting 3 secs. after SCSI device was added"
 
252
        after 3000
 
253
} else {
 
254
        after 500
 
255
}
 
256
 
 
257
# If SCSI tree in sysfs was not identified, try and get the values
 
258
# from a (nonswitching) call of usb_modeswitch; this detaches the
 
259
# storage driver, so it's just the last resort
 
260
 
 
261
if {$scsiNeeded && $scsi(vendor)==""} {
 
262
        set testSCSI [exec $bindir/usb_modeswitch -v 0x$usb(idVendor) -p 0x$usb(idProduct)]
 
263
        regexp {  Vendor String: (.*?)\n} $testSCSI d scsi(vendor)
 
264
        regexp {   Model String: (.*?)\n} $testSCSI d scsi(model)
 
265
        regexp {Revision String: (.*?)\n} $testSCSI d scsi(rev)
 
266
        Log "SCSI values from usb_modeswitch:"
 
267
        foreach attr {vendor model rev} {
 
268
                Log " $attr\t$scsi($attr)"
 
269
        }
 
270
}
 
271
 
 
272
# If we don't have the SCSI values by now, we just
 
273
# leave the variables empty; they won't match anything
 
274
 
 
275
# Time to check for a matching config file.
 
276
# Matching itself is done by MatchDevice
 
277
#
 
278
# Sorting the configuration file names reverse so that
 
279
# the ones with matching additions are tried first; the
 
280
# common configs without match attributes are used at the
 
281
# end and provide a fallback
 
282
 
 
283
set report {}
 
284
# The glob matches $idVendor:$idProduct without postfix and with :spec=value postfixes
 
285
# This allows to filter out .dpkg-old files
 
286
set configList [glob -nocomplain $dbdir/$usb(idVendor):$usb(idProduct){,\[:a-Z0-9=\]*}]
 
287
foreach configuration [lsort -decreasing $configList] {
 
288
        Log "checking config: $configuration"
 
289
        if [MatchDevice $configuration] {
 
290
                set switch_config $configuration
 
291
                set devList1 [glob -nocomplain /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS*]
 
292
                Log "! matched, now switching"
 
293
                set tc [open /tmp/gsmmodem_$dev_top w]
 
294
                close $tc
 
295
                if $logging {
 
296
                        Log " (running command: $bindir/usb_modeswitch -I -W -c $configuration)"
 
297
                        set report [exec $bindir/usb_modeswitch -I -W -D -c $configuration 2>@ stdout]
 
298
                } else {
 
299
                        set report [exec $bindir/usb_modeswitch -I -Q -D -c $configuration]
 
300
                }
 
301
                Log "\nverbose output of usb_modeswitch:"
 
302
                Log "--------------------------------"
 
303
                Log $report
 
304
                Log "--------------------------------"
 
305
                Log "(end of usb_modeswitch output)\n"
 
306
                break
 
307
        } else {
 
308
                Log "* no match, not switching with this config"
 
309
        }
 
310
}
 
311
 
 
312
# We're finished with switching; success checking
 
313
# was done by usb_modeswitch and logged via syslog.
 
314
#
 
315
# If switching was OK we now check for drivers by
 
316
# simply recounting serial devices under /dev
 
317
 
 
318
# If target ID given, driver shall be loaded
 
319
if [regexp -nocase {ok:[0-9a-f]{4}:[0-9a-f]{4}} $report] {
 
320
 
 
321
        # For general driver loading; TODO: add respective device names.
 
322
        # Presently only useful for HSO devices (which are recounted now)
 
323
        set driverModule ""
 
324
        set driverIDPath ""
 
325
        set rc [open $configuration r]
 
326
        set lineList [split [read $rc] \n]
 
327
        close $rc
 
328
        foreach line $lineList {
 
329
                regexp {DriverModule[[:blank:]]*=[[:blank:]]*"?(\w+)"?} $line d driverModule
 
330
                regexp {DriverIDPath[[:blank:]]*=[[:blank:]]*?"?([/\-\w]+)"?} $line d driverIDPath
 
331
        }
 
332
        if {$driverModule == ""} {
 
333
                set driverModule "option"
 
334
                set driverIDPath "/sys/bus/usb-serial/drivers/option1"
 
335
        } else {
 
336
                if {$driverIDPath == ""} {
 
337
                        set driverIDPath "/sys/bus/usb/drivers/$driverModule"
 
338
                }
 
339
        }
 
340
        Log "Driver module is \"$driverModule\", ID path is $driverIDPath\n"
 
341
 
 
342
        # some settle time in ms
 
343
        after 500
 
344
 
 
345
        Log "Now checking for newly created serial devices ..."
 
346
        set devList2 [glob -nocomplain /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS*]
 
347
 
 
348
        if {[llength $devList1] >= [llength $devList2]} {
 
349
                Log " no new serial devices found"
 
350
 
 
351
                if {![file isdirectory $devdir]} {
 
352
                        Log "Device directory in sysfs is gone! Something went wrong, aborting"
 
353
                        SafeExit
 
354
                }
 
355
 
 
356
                # Give the device annother second if it's not fully back yet
 
357
                if {![file exists $devdir/idProduct]} {
 
358
                        after 1000
 
359
                }
 
360
 
 
361
                ReadUSBAttrs $devdir
 
362
                if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
 
363
                        regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)
 
364
                }
 
365
                set t "$usb(idVendor)$usb(idProduct)"
 
366
                if {[string length $t] == 8 && [string trim $t 0] != ""} {
 
367
                        set idfile $driverIDPath/new_id
 
368
                        if {![file exists $idfile]} {
 
369
                                Log "\nTrying to load driver \"$driverModule\""
 
370
                                set loader /sbin/modprobe
 
371
                                Log " loader is: $loader"
 
372
                                if [file exists $loader] {
 
373
                                        if [catch {set result [exec $loader -v $driverModule]} err] {
 
374
                                                Log " Running \"$loader $driverModule\" gave an error:\n  $err"
 
375
                                        }
 
376
                                } else {
 
377
                                        Log " /sbin/modprobe not found"
 
378
                                }
 
379
                        }
 
380
                        if [file exists $idfile] {
 
381
                                Log "Trying to add ID to driver \"$driverModule\""
 
382
                                catch {exec logger -p syslog.notice "usb_modeswitch: adding device ID $usb(idVendor):$usb(idProduct) to driver \"$driverModule\""}
 
383
                                catch {exec echo "$usb(idVendor) $usb(idProduct)" >$idfile}
 
384
                                after 600
 
385
                                set devList2 [glob -nocomplain /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS*]
 
386
                                if {[llength $devList1] >= [llength $devList2]} {
 
387
                                        Log " still no new serial devices found"
 
388
                                } else {
 
389
                                        Log " driver successfully bound"
 
390
                                }
 
391
                        } else {
 
392
                                Log " \"$idfile\" not found, can't add ID"
 
393
                        }
 
394
                }
 
395
        } else {
 
396
                Log " new serial devices found, driver has bound"
 
397
        }
 
398
}
 
399
 
 
400
 
 
401
 
 
402
if [regexp {ok:$} $report] {
 
403
        Log "Doing no driver checking or binding for this device"
 
404
}
 
405
 
 
406
# In newer kernels there is a switch to avoid the use of a device
 
407
# reset (e.g. from usb-storage) which would likely switch back
 
408
# a mode-switching device
 
409
if [regexp {ok:} $report] {
 
410
        Log "Checking for AVOID_RESET_QUIRK attribute"
 
411
        if [file exists $devdir/avoid_reset_quirk] {
 
412
                if [catch {exec echo "1" >$devdir/avoid_reset_quirk} err] {
 
413
                        Log " Error setting the attribute: $err"
 
414
                } else {
 
415
                        Log " AVOID_RESET_QUIRK activated"
 
416
                }
 
417
        } else {
 
418
                Log " AVOID_RESET_QUIRK not present"
 
419
        }
 
420
}
 
421
 
 
422
Log "\nAll done, exiting\n"
 
423
SafeExit
 
424
 
 
425
}
 
426
# end of proc {Main}
 
427
 
 
428
 
 
429
proc {ReadSCSIAttrs} {dir} {
 
430
 
 
431
global scsi
 
432
Log "SCSI dir exists: $dir"
 
433
 
 
434
foreach attr {vendor model rev} {
 
435
        if [file exists $dir/$attr] {
 
436
                set rc [open $dir/$attr r]
 
437
                set scsi($attr) [read -nonewline $rc]
 
438
                close $rc
 
439
        } else {
 
440
                set scsi($attr) ""
 
441
                Log "Warning: SCSI attribute \"$attr\" not found."
 
442
        }
 
443
}
 
444
 
 
445
}
 
446
# end of proc {ReadSCSIAttrs}
 
447
 
 
448
 
 
449
proc {ReadUSBAttrs} {dir} {
 
450
 
 
451
global usb
 
452
Log "USB dir exists: $dir"
 
453
 
 
454
foreach attr {idVendor idProduct manufacturer product serial} {
 
455
        if [file exists $dir/$attr] {
 
456
                set rc [open $dir/$attr r]
 
457
                set usb($attr) [read -nonewline $rc]
 
458
                close $rc
 
459
        } else {
 
460
                set usb($attr) ""
 
461
                Log "Warning: USB attribute \"$attr\" not found."
 
462
        }
 
463
}
 
464
 
 
465
}
 
466
# end of proc {ReadUSBAttrs}
 
467
 
 
468
 
 
469
proc {MatchDevice} {config} {
 
470
 
 
471
global scsi usb match
 
472
 
 
473
set devinfo [file tail $config]
 
474
set infoList [split $devinfo :]
 
475
set stringList [lrange $infoList 2 end]
 
476
if {[llength $stringList] == 0} {return 1}
 
477
 
 
478
foreach teststring $stringList {
 
479
        if {$teststring == "?"} {return 0}
 
480
        set tokenList [split $teststring =]
 
481
        set id [lindex $tokenList 0]
 
482
        set matchstring [lindex $tokenList 1]
 
483
        set blankstring ""
 
484
        regsub -all {_} $matchstring { } blankstring
 
485
        Log "matching $match($id)"
 
486
        Log "  match string1: $matchstring"
 
487
        Log "  match string2: $blankstring"
 
488
        Log " device string: [set $match($id)]"
 
489
        if {!([string match $matchstring* [set $match($id)]] || [string match $blankstring* [set $match($id)]])} {
 
490
                return 0
 
491
        }
 
492
}
 
493
return 1
 
494
 
 
495
}
 
496
# end of proc {MatchDevice}
 
497
 
 
498
 
 
499
proc {ParseConfigFile} {} {
 
500
 
 
501
global logging noswitching
 
502
 
 
503
set configFile ""
 
504
set places [list /etc/usb_modeswitch.conf /etc/sysconfig/usb_modeswitch /etc/default/usb_modeswitch]
 
505
foreach cfg $places {
 
506
        if [file exists $cfg] {
 
507
                set configFile $cfg
 
508
                break
 
509
        }
 
510
}
 
511
 
 
512
if {$configFile == ""} {return}
 
513
 
 
514
set rc [open $configFile r]
 
515
while {![eof $rc]} {
 
516
        gets $rc line
 
517
        if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] {
 
518
                if [regexp -nocase {1|yes|true} $val] {
 
519
                        set noswitching 1
 
520
                }
 
521
        }
 
522
        if [regexp {EnableLogging\s*=\s*([^\s]+)} $line d val] {
 
523
                if [regexp -nocase {1|yes|true} $val] {
 
524
                        set logging 1
 
525
                }
 
526
        }
 
527
 
 
528
}
 
529
Log "Using global config file: $configFile"
 
530
 
 
531
}
 
532
# end of proc {ParseConfigFile}
 
533
 
 
534
 
 
535
proc {Log} {msg} {
 
536
 
 
537
global wc logging device
 
538
if {$logging == 0} {return}
 
539
if {![info exists wc]} {
 
540
        set wc [open /var/log/usb_modeswitch_$device a]
 
541
        puts $wc "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n"
 
542
}
 
543
puts $wc $msg
 
544
 
 
545
}
 
546
# end of proc {Log}
 
547
 
 
548
 
 
549
 
 
550
# Checking for interrupt endpoint in ttyUSB port; if found,
 
551
# check for unused "gsmmodem[n]" name.
 
552
# First link will be "gsmmodem", then "gsmmodem2" and up
 
553
 
 
554
proc {SymLinkName} {path} {
 
555
 
 
556
# HACK ... /tmp/gsmmodem_* was generated by a switching run before;
 
557
# no way found to signal annother instance in the udev environment
 
558
set tmpname [lindex [glob -nocomplain /tmp/gsmmodem_*] 0]
 
559
set dev_top [lindex [split $tmpname _] 1]
 
560
 
 
561
set dirList [split $path /]
 
562
set idx -1
 
563
set idx [lsearch -regexp $dirList {\d+-\d+:\d+\.\d+}]
 
564
if {$idx == -1} {
 
565
        return ""
 
566
}
 
567
set ifDir /sys[join [lrange $dirList 0 $idx] /]
 
568
set port [lindex $dirList end]
 
569
 
 
570
if {![regexp "/$dev_top/" $path]} {
 
571
        return ""
 
572
}
 
573
 
 
574
set symlinkName ""
 
575
foreach epDir [glob -nocomplain $ifDir/ep_*] {
 
576
        if [file exists $epDir/type] {
 
577
                set rc [open $epDir/type r]
 
578
                set type [read $rc]
 
579
                close $rc
 
580
                if [regexp {Interrupt} $type] {
 
581
                        set symlinkName "gsmmodem"
 
582
                        break
 
583
                }
 
584
        }
 
585
}
 
586
cd /dev
 
587
set idx 2
 
588
set trunkName $symlinkName
 
589
while {$idx < 256} {
 
590
        if {![file exists $symlinkName]} {
 
591
                break
 
592
        }
 
593
        set symlinkName $trunkName$idx
 
594
        incr idx
 
595
}
 
596
 
 
597
return $symlinkName
 
598
 
 
599
}
 
600
# end of proc {SymLinkName}
 
601
 
 
602
 
 
603
proc {SafeExit} {} {
 
604
global wc
 
605
if [info exists wc] {
 
606
        catch {close $wc}
 
607
}
 
608
exit
 
609
 
 
610
}
 
611
# end of proc {SafeExit}
 
612
 
 
613
 
 
614
# The actual entry point
 
615
Main $argc $argv
 
616