Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: data/trunk/Media/tcl8.5/platform/shell.tcl @ 5220

Last change on this file since 5220 was 5180, checked in by dafrick, 16 years ago
File size: 5.7 KB
Line 
1# -*- tcl -*-
2# ### ### ### ######### ######### #########
3## Overview
4
5# Higher-level commands which invoke the functionality of this package
6# for an arbitrary tcl shell (tclsh, wish, ...). This is required by a
7# repository as while the tcl shell executing packages uses the same
8# platform in general as a repository application there can be
9# differences in detail (i.e. 32/64 bit builds).
10
11# ### ### ### ######### ######### #########
12## Requirements
13
14package require platform
15namespace eval ::platform::shell {}
16
17# ### ### ### ######### ######### #########
18## Implementation
19
20# -- platform::shell::generic
21
22proc ::platform::shell::generic {shell} {
23    # Argument is the path to a tcl shell.
24
25    CHECK $shell
26    LOCATE base out
27
28    set     code {}
29    # Forget any pre-existing platform package, it might be in
30    # conflict with this one.
31    lappend code {package forget platform}
32    # Inject our platform package
33    lappend code [list source $base]
34    # Query and print the architecture
35    lappend code {puts [platform::generic]}
36    # And done
37    lappend code {exit 0}
38
39    set arch [RUN $shell [join $code \n]]
40
41    if {$out} {file delete -force $base}
42    return $arch
43}
44
45# -- platform::shell::identify
46
47proc ::platform::shell::identify {shell} {
48    # Argument is the path to a tcl shell.
49
50    CHECK $shell
51    LOCATE base out
52
53    set     code {}
54    # Forget any pre-existing platform package, it might be in
55    # conflict with this one.
56    lappend code {package forget platform}
57    # Inject our platform package
58    lappend code [list source $base]
59    # Query and print the architecture
60    lappend code {puts [platform::identify]}
61    # And done
62    lappend code {exit 0}
63
64    set arch [RUN $shell [join $code \n]]
65
66    if {$out} {file delete -force $base}
67    return $arch
68}
69
70# -- platform::shell::platform
71
72proc ::platform::shell::platform {shell} {
73    # Argument is the path to a tcl shell.
74
75    CHECK $shell
76
77    set     code {}
78    lappend code {puts $tcl_platform(platform)}
79    lappend code {exit 0}
80
81    return [RUN $shell [join $code \n]]
82}
83
84# ### ### ### ######### ######### #########
85## Internal helper commands.
86
87proc ::platform::shell::CHECK {shell} {
88    if {![file exists $shell]} {
89        return -code error "Shell \"$shell\" does not exist"
90    }
91    if {![file executable $shell]} {
92        return -code error "Shell \"$shell\" is not executable (permissions)"
93    }
94    return
95}
96
97proc ::platform::shell::LOCATE {bv ov} {
98    upvar 1 $bv base $ov out
99
100    # Locate the platform package for injection into the specified
101    # shell. We are using package management to find it, whereever it
102    # is, instead of using hardwired relative paths. This allows us to
103    # install the two packages as TMs without breaking the code
104    # here. If the found package is wrapped we copy the code somewhere
105    # where the spawned shell will be able to read it.
106
107    set pl [package ifneeded platform [package require platform]]
108    foreach {cmd base} $pl break
109
110    set out 0
111    if {[lindex [file system $base]] ne "native"} {
112        set temp [TEMP]
113        file copy -force $base $temp
114        set base $temp
115        set out 1
116    }
117    return
118}
119
120proc ::platform::shell::RUN {shell code} {
121    set     c [TEMP]
122    set    cc [open $c w]
123    puts  $cc $code
124    close $cc
125
126    set e [TEMP]
127
128    set code [catch {
129        exec $shell $c 2> $e
130    } res]
131
132    file delete $c
133
134    if {$code} {
135        append res \n[read [set chan [open $e r]]][close $chan]
136        file delete $e
137        return -code error "Shell \"$shell\" is not executable ($res)"
138    }
139
140    file delete $e
141    return $res
142}
143
144proc ::platform::shell::TEMP {} {
145    set prefix platform
146
147    # This code is copied out of Tcllib's fileutil package.
148    # (TempFile/tempfile)
149
150    set tmpdir [DIR]
151
152    set chars "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
153    set nrand_chars 10
154    set maxtries 10
155    set access [list RDWR CREAT EXCL TRUNC]
156    set permission 0600
157    set channel ""
158    set checked_dir_writable 0
159    set mypid [pid]
160    for {set i 0} {$i < $maxtries} {incr i} {
161        set newname $prefix
162        for {set j 0} {$j < $nrand_chars} {incr j} {
163            append newname [string index $chars \
164                    [expr {int(rand()*62)}]]
165        }
166        set newname [file join $tmpdir $newname]
167        if {[file exists $newname]} {
168            after 1
169        } else {
170            if {[catch {open $newname $access $permission} channel]} {
171                if {!$checked_dir_writable} {
172                    set dirname [file dirname $newname]
173                    if {![file writable $dirname]} {
174                        return -code error "Directory $dirname is not writable"
175                    }
176                    set checked_dir_writable 1
177                }
178            } else {
179                # Success
180                close $channel
181                return [file normalize $newname]
182            }
183        }
184    }
185    if {[string compare $channel ""]} {
186        return -code error "Failed to open a temporary file: $channel"
187    } else {
188        return -code error "Failed to find an unused temporary file name"
189    }
190}
191
192proc ::platform::shell::DIR {} {
193    # This code is copied out of Tcllib's fileutil package.
194    # (TempDir/tempdir)
195
196    global tcl_platform env
197
198    set attempdirs [list]
199
200    foreach tmp {TMPDIR TEMP TMP} {
201        if { [info exists env($tmp)] } {
202            lappend attempdirs $env($tmp)
203        }
204    }
205
206    switch $tcl_platform(platform) {
207        windows {
208            lappend attempdirs "C:\\TEMP" "C:\\TMP" "\\TEMP" "\\TMP"
209        }
210        macintosh {
211            set tmpdir $env(TRASH_FOLDER)  ;# a better place?
212        }
213        default {
214            lappend attempdirs \
215                [file join / tmp] \
216                [file join / var tmp] \
217                [file join / usr tmp]
218        }
219    }
220
221    lappend attempdirs [pwd]
222
223    foreach tmp $attempdirs {
224        if { [file isdirectory $tmp] && [file writable $tmp] } {
225            return [file normalize $tmp]
226        }
227    }
228
229    # Fail if nothing worked.
230    return -code error "Unable to determine a proper directory for temporary files"
231}
232
233# ### ### ### ######### ######### #########
234## Ready
235
236package provide platform::shell 1.1.3
Note: See TracBrowser for help on using the repository browser.