chiark / gitweb /
debian/control: Fix architectures for x86-model.
[misc] / splitconf.in
1 #! @TCLSH@
2
3 #----- Miscellaneous utilities ----------------------------------------------
4
5 # die MSG
6 #
7 # Something didn't work.  Exit right now.
8
9 proc die {msg} {
10   global argv0
11   puts stderr "$argv0: $msg"
12   exit 1
13 }
14
15 # usage FILE
16 #
17 # Write a usage message to FILE, which is a file handle.
18
19 proc usage {file} {
20   global argv0
21   puts $file "Usage: \n\t$argv0 \[-s\] FILE\n\t$argv0 -u OUTPUT FILE FILE ..."
22 }
23
24 # clear-arrays ARRAY ...
25 #
26 # Make each named ARRAY exist and be empty.
27
28 proc clear-arrays {args} {
29   foreach i $args {
30     upvar 1 $i v
31     unset i
32     array set v {}
33   }
34 }
35
36 #------ Write-safe ----------------------------------------------------------
37
38 # write-safe STUFF [TIDY]
39 #
40 # Do some safe I/O.  If STUFF succeeds, do TIDY and commit the modifications;
41 # otherwise, do TIDY and back out all the changes.  See also write-safe-open,
42 # write-safe-file and write-safe-delete.
43
44 proc write-safe {stuff {tidy {}}} {
45   global _ws_close _ws_del _ws_new
46   clear-arrays _ws_del _ws_new
47   set _ws_close {}
48
49   if {[set rc [catch {
50     uplevel 1 $stuff
51   } err]]} {
52     foreach f $_ws_close { catch { close $f } }
53     foreach f [array names _ws_new] { catch { file delete -- $f.new } }
54     catch { uplevel 1 $tidy }
55     return -code $rc $err
56   }
57   foreach f $_ws_close { catch { close $f } }
58   clear-arrays all
59   foreach f [concat [array names _ws_old] [array names _ws_del]] {
60     set all($f) 0
61   }
62   if {[set rc [catch {
63     foreach f [array names all] {
64       if {[file exists $f]} {
65         file delete -- $f.old
66         file copy -force -- $f $f.old
67       }
68       set old($f) 0
69     }
70     foreach f [array names _ws_new] { file rename -force -- $f.new $f }
71     foreach f [array names _ws_del] { file delete -- $f }
72   } err]]} {
73     foreach f [array names _ws_new] { catch { file delete -- $f.new } }
74     foreach f [array names old] { file rename -force -- $f.old $f }
75     catch { uplevel 1 $tidy }
76     return -code $rc $err
77   }
78   foreach i [array names all] { catch { file delete -- $i.old } }
79   catch { uplevel 1 $tidy }
80   return {}
81 }
82
83 # write-safe-open NAME [TRANS]
84 #
85 # Open file NAME for writing, with the translation mode TRANS (default is
86 # `auto'); return the file handle.  The file NAME is not destroyed until the
87 # changes are committed by an enclosing write-safe completing.  You can close
88 # the file handle if you like; write-safe will close it automatically anyway.
89
90 proc write-safe-open {name {trans auto}} {
91   global _ws_close _ws_new
92   if {[file isdirectory $name]} { error "`$name' is a directory" }
93   set f [open $name.new w]
94   fconfigure $f -translation $trans
95   lappend _ws_close $f
96   set _ws_new($name) 0
97   return $f
98 }
99
100 # write-safe-delete NAME
101 #
102 # Delete file NAME.  The file isn't actually removed until the enclosing
103 # write-safe completes.
104
105 proc write-safe-delete {name} {
106   global _ws_del
107   set _ws_del($name) 0
108 }
109
110 # write-safe-file NAME CONTENTS [TRANS]
111 #
112 # Write CONTENTS to FILE, using translation mode TRANS (default `auto').  The
113 # file isn't actually replaced until the changes are committed by an
114 # enclosing write-safe completing.
115
116 proc write-safe-file {name contents {trans auto}} {
117   set f [write-safe-open $name $trans]
118   puts -nonewline $f $contents
119   close $f
120 }
121
122 # read-file NAME [TRANS]
123 #
124 # Evaluates to the contents of the file NAME under translation mode TRANS
125 # (default `auto').
126
127 proc read-file {name {trans auto}} {
128   set f [open $name]
129   fconfigure $f -translation $trans
130   set c [read $f]
131   close $f
132   return $c
133 }
134
135 #----- Splitconf-specific stuff ---------------------------------------------
136
137 # write-safe-manifest F L
138 #
139 # Writes the list of filenames L to the manifest file associated with config
140 # file F.
141
142 proc write-safe-manifest {f l} {
143   set f [write-safe-open $f.files]
144   foreach i $l { puts $f $i }
145   close $f
146 }
147
148 # old-files CONF
149 #
150 # Returns the filenames in the current manifest of the config file CONF.
151
152 proc old-files {conf} {
153   set old {}
154   if {[file exists $conf.files]} {
155     set f [open $conf.files]
156     while {[gets $f line] >= 0} { lappend old $line }
157     close $f
158   }
159   return $old
160 }
161
162 #----- Main code ------------------------------------------------------------
163
164 set job "split"
165 while {[llength $argv]} {
166   switch -glob -- [lindex $argv 0] {
167     "-u" - "--unsplit" {
168       set job "unsplit"
169       if {[llength $argv] < 2} { die "option `-u' needs an argument" }
170       set output [lindex $argv 1]
171       set argv [lrange $argv 1 end]
172     }
173     "-d" - "--delete" { set job "delete" }
174     "-s" - "--split" { set job "split" }
175     "-h" - "--help" { usage stdout; exit 0 }
176     "-" { break }
177     "--" { set argv [lrange $argv 1 end]; break }
178     "-*" { die "unknown option `[lindex $argv 0]'"; exit 1 }
179     default { break }
180   }
181   set argv [lrange $argv 1 end]
182 }
183
184 set rc 0
185 clear-arrays opt
186 array set opt {
187   prefix ""
188   before ""
189   after ""
190 }
191 switch $job {
192   "unsplit" {
193     set f "\#\# automatically generated by splitconf\n\n"
194     set ff {}
195     foreach i $argv {
196       if {[catch {
197         set c [read-file $i]
198         append f "\[$i\]\n$c\n"
199         lappend ff $i
200       } msg]} {
201         set rc 1
202       }
203     }
204     write-safe {
205       write-safe-file $output $f
206       write-safe-manifest $output $ff
207     }
208   }
209   "delete" {
210     if {[llength $argv] != 1} { die "need exactly one filename" }
211     set conf [lindex $argv 0]
212     set old [old-files $conf]
213     write-safe {
214       foreach i $old { write-safe-delete $i }
215       write-safe-delete $conf.files
216     }
217   }
218   "split" {
219     if {[llength $argv] != 1} { die "need exactly one filename" }
220     set conf [lindex $argv 0]
221     set old [old-files $conf]
222     set c [open $conf r]
223     catch { unset o }
224     set spill ""
225     set donebefore 0
226     array set new {}
227     write-safe {
228       while {[gets $c line] >= 0} {
229         if {[regexp -- {^\[(.*)\]\s*$} $line . name]} {
230           if {[info exists o]} { close $o }
231           if {[string equal $name ""]} {
232             catch { unset o }
233           } else {
234             if {!$donebefore} {
235               exec "sh" "-c" $opt(before) <@ stdin >@ stdout 2>@ stderr
236               set donebefore 1
237             }
238             set name "$opt(prefix)$name"
239             set o [write-safe-open $name]
240             set new($name) 1
241             set spill ""
242           }
243         } elseif {[info exists o]} {
244           switch -regexp -- $line {
245             {^\s*$} { append spill "$line\n" }
246             {^\#\#} { }
247             {^\!} {
248               puts -nonewline $o "$spill[string range $line 1 end]\n"
249               set spill ""
250             }
251             default { puts -nonewline $o "$spill$line\n"; set spill "" }
252           }
253         } elseif {[regexp -- {^\s*(\#|$)} $line]} {
254           continue
255         } elseif {[regexp -- \
256                        {^\s*([-./\w]+)\s*=\s*(.*\S|)\s*$} $line . k v]} {
257           if {![info exists opt($k)]} {
258             error "unknown configuration option `$k'"
259           } else {
260             set opt($k) $v
261           }
262         } elseif {[regexp -- \
263                        {^\s*([-./\w]+)\s*:\s*(.*\S|)\s*$} $line . name d]} {
264           if {!$donebefore} {
265             exec "sh" "-c" $opt(before) <@ stdin >@ stdout 2>@ stderr
266             set donebefore 1
267           }
268           set name "$opt(prefix)$name"
269           set new($name) 1
270           write-safe-file $name "$d\n"
271         } else {
272           error "unknown preamble directive"
273         }
274       }
275       if {[info exists o]} {
276         close $o
277       }
278       close $c
279       foreach i $old {
280         if {![info exists new($i)]} { write-safe-delete $i }
281       }
282       write-safe-manifest $conf [array names new]
283     } {
284       if {$donebefore} {
285         exec "sh" "-c" $opt(after) <@ stdin >@ stdout 2>@ stderr
286       }
287     }
288   }
289 }
290
291 #----- That's all, folks ----------------------------------------------------