shtool 49.7 KB
Newer Older
1
2
3
#!/bin/sh
##
##  GNU shtool -- The GNU Portable Shell Tool
4
##  Copyright (c) 1994-2008 Ralf S. Engelschall <rse@engelschall.com>
5
6
7
8
##
##  See http://www.gnu.org/software/shtool/ for more information.
##  See ftp://ftp.gnu.org/gnu/shtool/ for latest version.
##
9
##  Version:  2.0.8 (18-Jul-2008)
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
##  Contents: 6/19 available modules
##

##
##  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 2 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, write to the Free Software
##  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
##  USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
##
##  NOTICE: Given that you include this file verbatim into your own
##  source tree, you are justified in saying that it remains separate
##  from your package, and that this way you are simply just using GNU
##  shtool. So, in this situation, there is no requirement that your
##  package itself is licensed under the GNU General Public License in
##  order to take advantage of GNU shtool.
##

##
##  Usage: shtool [<options>] [<cmd-name> [<cmd-options>] [<cmd-args>]]
##
##  Available commands:
##    echo       Print string with optional construct expansion
##    move       Move files with simultaneous substitution
##    install    Install a program, script or datafile
##    mkdir      Make one or more directories
##    mkln       Make link with calculation of relative paths
##    subst      Apply sed(1) substitution operations
##
##  Not available commands (because module was not built-in):
##    mdate      Pretty-print modification time of a file or dir
##    table      Pretty-print a field-separated list as a table
##    prop       Display progress with a running propeller
##    mkshadow   Make a shadow tree through symbolic links
##    fixperm    Fix file permissions inside a source tree
##    rotate     Logfile rotation
##    tarball    Roll distribution tarballs
Kurt Zeilenga's avatar
Kurt Zeilenga committed
56
##    platform   Platform Identification Utility
57
58
59
60
61
62
63
##    arx        Extended archive command
##    slo        Separate linker options by library class
##    scpp       Sharing C Pre-Processor
##    version    Maintain a version information file
##    path       Deal with program paths
##

Kurt Zeilenga's avatar
Kurt Zeilenga committed
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
#   maximum Bourne-Shell compatibility
if [ ".$ZSH_VERSION" != . ] && (emulate sh) >/dev/null 2>&1; then
    #   reconfigure zsh(1)
    emulate sh
    NULLCMD=:
    alias -g '${1+"$@"}'='"$@"'
elif [ ".$BASH_VERSION" != . ] && (set -o posix) >/dev/null 2>&1; then
    #   reconfigure bash(1)
    set -o posix
fi

#   maximum independence of NLS nuisances
for var in \
    LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
    LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
    LC_TELEPHONE LC_TIME
do
    if (set +x; test -z "`(eval $var=C; export $var) 2>&1`"); then
        eval $var=C; export $var
    else
        unset $var
    fi
done

#   initial command line handling
89
90
91
92
93
if [ $# -eq 0 ]; then
    echo "$0:Error: invalid command line" 1>&2
    echo "$0:Hint:  run \`$0 -h' for usage" 1>&2
    exit 1
fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
94
if [ ".$1" = ".-h" ] || [ ".$1" = ".--help" ]; then
95
96
    echo "This is GNU shtool, version 2.0.8 (18-Jul-2008)"
    echo 'Copyright (c) 1994-2008 Ralf S. Engelschall <rse@engelschall.com>'
Kurt Zeilenga's avatar
Kurt Zeilenga committed
97
    echo 'Report bugs to <bug-shtool@gnu.org>'
98
    echo ''
Kurt Zeilenga's avatar
Kurt Zeilenga committed
99
    echo 'Usage: shtool [<options>] [<cmd-name> [<cmd-options>] [<cmd-args>]]'
100
101
102
103
104
105
106
107
    echo ''
    echo 'Available global <options>:'
    echo '  -v, --version   display shtool version information'
    echo '  -h, --help      display shtool usage help page (this one)'
    echo '  -d, --debug     display shell trace information'
    echo '  -r, --recreate  recreate this shtool script via shtoolize'
    echo ''
    echo 'Available <cmd-name> [<cmd-options>] [<cmd-args>]:'
Kurt Zeilenga's avatar
Kurt Zeilenga committed
108
    echo '  echo     [-n|--newline] [-e|--expand] [<string> ...]'
109
110
    echo '  move     [-v|--verbose] [-t|--trace] [-e|--expand] [-p|--preserve]'
    echo '           <src-file> <dst-file>'
Kurt Zeilenga's avatar
Kurt Zeilenga committed
111
112
113
114
    echo '  install  [-v|--verbose] [-t|--trace] [-d|--mkdir] [-c|--copy]'
    echo '           [-C|--compare-copy] [-s|--strip] [-m|--mode <mode>]'
    echo '           [-o|--owner <owner>] [-g|--group <group>] [-e|--exec'
    echo '           <sed-cmd>] <file> [<file> ...] <path>'
115
    echo '  mkdir    [-t|--trace] [-f|--force] [-p|--parents] [-m|--mode'
Kurt Zeilenga's avatar
Kurt Zeilenga committed
116
117
    echo '           <mode>] [-o|--owner <owner>] [-g|--group <group>] <dir>'
    echo '           [<dir> ...]'
118
119
    echo '  mkln     [-t|--trace] [-f|--force] [-s|--symbolic] <src-path>'
    echo '           [<src-path> ...] <dst-path>'
Kurt Zeilenga's avatar
Kurt Zeilenga committed
120
121
122
123
    echo '  subst    [-v|--verbose] [-t|--trace] [-n|--nop] [-w|--warning]'
    echo '           [-q|--quiet] [-s|--stealth] [-i|--interactive] [-b|--backup'
    echo '           <ext>] [-e|--exec <cmd>] [-f|--file <cmd-file>] [<file>]'
    echo '           [...]'
124
125
126
127
128
129
130
131
132
133
    echo ''
    echo 'Not available <cmd-name> (because module was not built-in):'
    echo '  mdate    [-n|--newline] [-z|--zero] [-s|--shorten] [-d|--digits]'
    echo '           [-f|--field-sep <str>] [-o|--order <spec>] <path>'
    echo '  table    [-F|--field-sep <sep>] [-w|--width <width>] [-c|--columns'
    echo '           <cols>] [-s|--strip <strip>] <str><sep><str>...'
    echo '  prop     [-p|--prefix <str>]'
    echo '  mkshadow [-v|--verbose] [-t|--trace] [-a|--all] <src-dir> <dst-dir>'
    echo '  fixperm  [-v|--verbose] [-t|--trace] <path> [<path> ...]'
    echo '  rotate   [-v|--verbose] [-t|--trace] [-f|--force] [-n|--num-files'
Kurt Zeilenga's avatar
Kurt Zeilenga committed
134
    echo '           <count>] [-s|--size <size>] [-c|--copy] [-r|--remove]'
135
    echo '           [-a|--archive-dir <dir>] [-z|--compress [<tool>:]<level>]'
Kurt Zeilenga's avatar
Kurt Zeilenga committed
136
137
    echo '           [-b|--background] [-d|--delay] [-p|--pad <len>] [-m|--mode'
    echo '           <mode>] [-o|--owner <owner>] [-g|--group <group>] [-M|--migrate'
138
139
140
141
142
    echo '           <cmd>] [-P|--prolog <cmd>] [-E|--epilog <cmd>] <file> [...]'
    echo '  tarball  [-t|--trace] [-v|--verbose] [-o|--output <tarball>]'
    echo '           [-c|--compress <prog>] [-d|--directory <dir>] [-u|--user'
    echo '           <user>] [-g|--group <group>] [-e|--exclude <pattern>]'
    echo '           <path> [<path> ...]'
Kurt Zeilenga's avatar
Kurt Zeilenga committed
143
144
145
146
    echo '  platform [-F|--format <format>] [-S|--sep <string>] [-C|--conc'
    echo '           <string>] [-L|--lower] [-U|--upper] [-v|--verbose]'
    echo '           [-c|--concise] [-n|--no-newline] [-t|--type <type>]'
    echo '           [-V|--version] [-h|--help]'
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
    echo '  arx      [-t|--trace] [-C|--command <cmd>] <op> <archive> [<file>'
    echo '           ...]'
    echo '  slo      [-p|--prefix <str>] -- -L<dir> -l<lib> [-L<dir> -l<lib>'
    echo '           ...]'
    echo '  scpp     [-v|--verbose] [-p|--preserve] [-f|--filter <filter>]'
    echo '           [-o|--output <ofile>] [-t|--template <tfile>] [-M|--mark'
    echo '           <mark>] [-D|--define <dname>] [-C|--class <cname>]'
    echo '           <file> [<file> ...]'
    echo '  version  [-l|--language <lang>] [-n|--name <name>] [-p|--prefix'
    echo '           <prefix>] [-s|--set <version>] [-e|--edit] [-i|--increase'
    echo '           <knob>] [-d|--display <type>] <file>'
    echo '  path     [-s|--suppress] [-r|--reverse] [-d|--dirname] [-b|--basename]'
    echo '           [-m|--magic] [-p|--path <path>] <str> [<str> ...]'
    echo ''
    exit 0
fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
163
if [ ".$1" = ".-v" ] || [ ".$1" = ".--version" ]; then
164
    echo "GNU shtool 2.0.8 (18-Jul-2008)"
165
166
    exit 0
fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
167
if [ ".$1" = ".-r" ] || [ ".$1" = ".--recreate" ]; then
168
    shtoolize -oshtool echo move install mkdir mkln subst
169
170
    exit 0
fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
171
if [ ".$1" = ".-d" ] || [ ".$1" = ".--debug" ]; then
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
    shift
    set -x
fi
name=`echo "$0" | sed -e 's;.*/\([^/]*\)$;\1;' -e 's;-sh$;;' -e 's;\.sh$;;'`
case "$name" in
    echo|move|install|mkdir|mkln|subst )
        #   implicit tool command selection
        tool="$name"
        ;;
    * )
        #   explicit tool command selection
        tool="$1"
        shift
        ;;
esac
arg_spec=""
opt_spec=""
gen_tmpfile=no

##
##  DISPATCH INTO SCRIPT PROLOG
##

case $tool in
    echo )
        str_tool="echo"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
198
        str_usage="[-n|--newline] [-e|--expand] [<string> ...]"
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
        arg_spec="0+"
        opt_spec="n.e."
        opt_alias="n:newline,e:expand"
        opt_n=no
        opt_e=no
        ;;
    move )
        str_tool="move"
        str_usage="[-v|--verbose] [-t|--trace] [-e|--expand] [-p|--preserve] <src-file> <dst-file>"
        arg_spec="2="
        opt_spec="v.t.e.p."
        opt_alias="v:verbose,t:trace,e:expand,p:preserve"
        opt_v=no
        opt_t=no
        opt_e=no
        opt_p=no
        ;;
    install )
        str_tool="install"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
218
219
220
221
        str_usage="[-v|--verbose] [-t|--trace] [-d|--mkdir] [-c|--copy] [-C|--compare-copy] [-s|--strip] [-m|--mode <mode>] [-o|--owner <owner>] [-g|--group <group>] [-e|--exec <sed-cmd>] <file> [<file> ...] <path>"
        arg_spec="1+"
        opt_spec="v.t.d.c.C.s.m:o:g:e+"
        opt_alias="v:verbose,t:trace,d:mkdir,c:copy,C:compare-copy,s:strip,m:mode,o:owner,g:group,e:exec"
222
223
        opt_v=no
        opt_t=no
Kurt Zeilenga's avatar
Kurt Zeilenga committed
224
        opt_d=no
225
226
227
228
229
230
231
232
233
234
        opt_c=no
        opt_C=no
        opt_s=no
        opt_m="0755"
        opt_o=""
        opt_g=""
        opt_e=""
        ;;
    mkdir )
        str_tool="mkdir"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
235
        str_usage="[-t|--trace] [-f|--force] [-p|--parents] [-m|--mode <mode>] [-o|--owner <owner>] [-g|--group <group>] <dir> [<dir> ...]"
236
        arg_spec="1+"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
237
238
        opt_spec="t.f.p.m:o:g:"
        opt_alias="t:trace,f:force,p:parents,m:mode,o:owner,g:group"
239
240
241
242
        opt_t=no
        opt_f=no
        opt_p=no
        opt_m=""
Kurt Zeilenga's avatar
Kurt Zeilenga committed
243
244
        opt_o=""
        opt_g=""
245
246
247
248
249
250
251
252
253
254
255
256
257
        ;;
    mkln )
        str_tool="mkln"
        str_usage="[-t|--trace] [-f|--force] [-s|--symbolic] <src-path> [<src-path> ...] <dst-path>"
        arg_spec="2+"
        opt_spec="t.f.s."
        opt_alias="t:trace,f:force,s:symbolic"
        opt_t=no
        opt_f=no
        opt_s=no
        ;;
    subst )
        str_tool="subst"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
258
        str_usage="[-v|--verbose] [-t|--trace] [-n|--nop] [-w|--warning] [-q|--quiet] [-s|--stealth] [-i|--interactive] [-b|--backup <ext>] [-e|--exec <cmd>] [-f|--file <cmd-file>] [<file>] [...]"
259
260
        gen_tmpfile=yes
        arg_spec="0+"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
261
262
        opt_spec="v.t.n.w.q.s.i.b:e+f:"
        opt_alias="v:verbose,t:trace,n:nop,w:warning,q:quiet,s:stealth,i:interactive,b:backup,e:exec,f:file"
263
264
265
        opt_v=no
        opt_t=no
        opt_n=no
Kurt Zeilenga's avatar
Kurt Zeilenga committed
266
267
        opt_w=no
        opt_q=no
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
        opt_s=no
        opt_i=no
        opt_b=""
        opt_e=""
        opt_f=""
        ;;
    -* )
        echo "$0:Error: unknown option \`$tool'" 2>&1
        echo "$0:Hint:  run \`$0 -h' for usage" 2>&1
        exit 1
        ;;
    * )
        echo "$0:Error: unknown command \`$tool'" 2>&1
        echo "$0:Hint:  run \`$0 -h' for usage" 2>&1
        exit 1
        ;;
esac

##
##  COMMON UTILITY CODE
##

#   commonly used ASCII values
ASC_TAB="	"
ASC_NL="
"

#   determine name of tool
if [ ".$tool" != . ]; then
    #   used inside shtool script
    toolcmd="$0 $tool"
    toolcmdhelp="shtool $tool"
    msgprefix="shtool:$tool"
else
    #   used as standalone script
    toolcmd="$0"
    toolcmdhelp="sh $0"
    msgprefix="$str_tool"
fi

#   parse argument specification string
eval `echo $arg_spec |\
      sed -e 's/^\([0-9]*\)\([+=]\)/arg_NUMS=\1; arg_MODE=\2/'`

#   parse option specification string
eval `echo h.$opt_spec |\
      sed -e 's/\([a-zA-Z0-9]\)\([.:+]\)/opt_MODE_\1=\2;/g'`

#   parse option alias string
eval `echo h:help,$opt_alias |\
Kurt Zeilenga's avatar
Kurt Zeilenga committed
318
      sed -e 's/-/_/g' -e 's/\([a-zA-Z0-9]\):\([^,]*\),*/opt_ALIAS_\2=\1;/g'`
319

320
#   interate over argument line
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
opt_PREV=''
while [ $# -gt 0 ]; do
    #   special option stops processing
    if [ ".$1" = ".--" ]; then
        shift
        break
    fi

    #   determine option and argument
    opt_ARG_OK=no
    if [ ".$opt_PREV" != . ]; then
        #   merge previous seen option with argument
        opt_OPT="$opt_PREV"
        opt_ARG="$1"
        opt_ARG_OK=yes
        opt_PREV=''
    else
        #   split argument into option and argument
        case "$1" in
            --[a-zA-Z0-9]*=*)
                eval `echo "x$1" |\
                      sed -e 's/^x--\([a-zA-Z0-9-]*\)=\(.*\)$/opt_OPT="\1";opt_ARG="\2"/'`
Kurt Zeilenga's avatar
Kurt Zeilenga committed
343
                opt_STR=`echo $opt_OPT | sed -e 's/-/_/g'`
344
345
346
347
                eval "opt_OPT=\${opt_ALIAS_${opt_STR}-${opt_OPT}}"
                ;;
            --[a-zA-Z0-9]*)
                opt_OPT=`echo "x$1" | cut -c4-`
Kurt Zeilenga's avatar
Kurt Zeilenga committed
348
                opt_STR=`echo $opt_OPT | sed -e 's/-/_/g'`
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
                eval "opt_OPT=\${opt_ALIAS_${opt_STR}-${opt_OPT}}"
                opt_ARG=''
                ;;
            -[a-zA-Z0-9]*)
                eval `echo "x$1" |\
                      sed -e 's/^x-\([a-zA-Z0-9]\)/opt_OPT="\1";/' \
                          -e 's/";\(.*\)$/"; opt_ARG="\1"/'`
                ;;
            -[a-zA-Z0-9])
                opt_OPT=`echo "x$1" | cut -c3-`
                opt_ARG=''
                ;;
            *)
                break
                ;;
        esac
    fi

    #   eat up option
    shift

    #   determine whether option needs an argument
    eval "opt_MODE=\$opt_MODE_${opt_OPT}"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
372
373
    if [ ".$opt_ARG" = . ] && [ ".$opt_ARG_OK" != .yes ]; then
        if [ ".$opt_MODE" = ".:" ] || [ ".$opt_MODE" = ".+" ]; then
374
375
376
377
378
379
380
381
382
383
384
385
            opt_PREV="$opt_OPT"
            continue
        fi
    fi

    #   process option
    case $opt_MODE in
        '.' )
            #   boolean option
            eval "opt_${opt_OPT}=yes"
            ;;
        ':' )
386
            #   option with argument (multiple occurances override)
387
388
389
            eval "opt_${opt_OPT}=\"\$opt_ARG\""
            ;;
        '+' )
390
            #   option with argument (multiple occurances append)
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
            eval "opt_${opt_OPT}=\"\$opt_${opt_OPT}\${ASC_NL}\$opt_ARG\""
            ;;
        * )
            echo "$msgprefix:Error: unknown option: \`$opt_OPT'" 1>&2
            echo "$msgprefix:Hint:  run \`$toolcmdhelp -h' or \`man shtool' for details" 1>&2
            exit 1
            ;;
    esac
done
if [ ".$opt_PREV" != . ]; then
    echo "$msgprefix:Error: missing argument to option \`$opt_PREV'" 1>&2
    echo "$msgprefix:Hint:  run \`$toolcmdhelp -h' or \`man shtool' for details" 1>&2
    exit 1
fi

#   process help option
if [ ".$opt_h" = .yes ]; then
    echo "Usage: $toolcmdhelp $str_usage"
    exit 0
fi

#   complain about incorrect number of arguments
case $arg_MODE in
    '=' )
        if [ $# -ne $arg_NUMS ]; then
            echo "$msgprefix:Error: invalid number of arguments (exactly $arg_NUMS expected)" 1>&2
            echo "$msgprefix:Hint:  run \`$toolcmd -h' or \`man shtool' for details" 1>&2
            exit 1
        fi
        ;;
    '+' )
        if [ $# -lt $arg_NUMS ]; then
            echo "$msgprefix:Error: invalid number of arguments (at least $arg_NUMS expected)" 1>&2
            echo "$msgprefix:Hint:  run \`$toolcmd -h' or \`man shtool' for details" 1>&2
            exit 1
        fi
        ;;
esac

#   establish a temporary file on request
if [ ".$gen_tmpfile" = .yes ]; then
432
    #   create (explicitly) secure temporary directory
433
434
435
436
437
438
439
    if [ ".$TMPDIR" != . ]; then
        tmpdir="$TMPDIR"
    elif [ ".$TEMPDIR" != . ]; then
        tmpdir="$TEMPDIR"
    else
        tmpdir="/tmp"
    fi
440
441
442
443
444
445
446
447
448
449
450
451
452
    tmpdir="$tmpdir/.shtool.$$"
    ( umask 077
      rm -rf "$tmpdir" >/dev/null 2>&1 || true
      mkdir  "$tmpdir" >/dev/null 2>&1
      if [ $? -ne 0 ]; then
          echo "$msgprefix:Error: failed to create temporary directory \`$tmpdir'" 1>&2
          exit 1
      fi
    )

    #   create (implicitly) secure temporary file
    tmpfile="$tmpdir/shtool.tmp"
    touch "$tmpfile"
453
454
fi

Kurt Zeilenga's avatar
Kurt Zeilenga committed
455
456
457
458
459
460
461
462
463
464
465
466
467
468
#   utility function: map string to lower case
util_lower () {
    echo "$1" | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'
}

#   utility function: map string to upper case
util_upper () {
    echo "$1" | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
}

#   cleanup procedure
shtool_exit () {
    rc="$1"
    if [ ".$gen_tmpfile" = .yes ]; then
469
        rm -rf "$tmpdir" >/dev/null 2>&1 || true
Kurt Zeilenga's avatar
Kurt Zeilenga committed
470
471
472
473
    fi
    exit $rc
}

474
475
476
477
478
479
480
481
482
##
##  DISPATCH INTO SCRIPT BODY
##

case $tool in

echo )
    ##
    ##  echo -- Print string with optional construct expansion
483
    ##  Copyright (c) 1998-2008 Ralf S. Engelschall <rse@engelschall.com>
484
    ##
Kurt Zeilenga's avatar
Kurt Zeilenga committed
485

486
    text="$*"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
487

488
489
490
491
492
493
494
495
496
    #   check for broken escape sequence expansion
    seo=''
    bytes=`echo '\1' | wc -c | awk '{ printf("%s", $1); }'`
    if [ ".$bytes" != .3 ]; then
        bytes=`echo -E '\1' | wc -c | awk '{ printf("%s", $1); }'`
        if [ ".$bytes" = .3 ]; then
            seo='-E'
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
497

498
499
500
501
502
503
    #   check for existing -n option (to suppress newline)
    minusn=''
    bytes=`echo -n 123 2>/dev/null | wc -c | awk '{ printf("%s", $1); }'`
    if [ ".$bytes" = .3 ]; then
        minusn='-n'
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
504

505
506
507
    #   determine terminal bold sequence
    term_bold=''
    term_norm=''
Kurt Zeilenga's avatar
Kurt Zeilenga committed
508
    if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%[Bb]'`" != . ]; then
509
510
511
512
513
514
        case $TERM in
            #   for the most important terminal types we directly know the sequences
            xterm|xterm*|vt220|vt220*)
                term_bold=`awk 'BEGIN { printf("%c%c%c%c", 27, 91, 49, 109); }' </dev/null 2>/dev/null`
                term_norm=`awk 'BEGIN { printf("%c%c%c", 27, 91, 109); }' </dev/null 2>/dev/null`
                ;;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
515
            vt100|vt100*|cygwin)
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
                term_bold=`awk 'BEGIN { printf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); }' </dev/null 2>/dev/null`
                term_norm=`awk 'BEGIN { printf("%c%c%c%c%c", 27, 91, 109, 0, 0); }' </dev/null 2>/dev/null`
                ;;
            #   for all others, we try to use a possibly existing `tput' or `tcout' utility
            * )
                paths=`echo $PATH | sed -e 's/:/ /g'`
                for tool in tput tcout; do
                    for dir in $paths; do
                        if [ -r "$dir/$tool" ]; then
                            for seq in bold md smso; do # 'smso' is last
                                bold="`$dir/$tool $seq 2>/dev/null`"
                                if [ ".$bold" != . ]; then
                                    term_bold="$bold"
                                    break
                                fi
                            done
                            if [ ".$term_bold" != . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
533
                                for seq in sgr0 me rmso init reset; do # 'reset' is last
534
535
536
537
538
539
540
541
542
543
                                    norm="`$dir/$tool $seq 2>/dev/null`"
                                    if [ ".$norm" != . ]; then
                                        term_norm="$norm"
                                        break
                                    fi
                                done
                            fi
                            break
                        fi
                    done
Kurt Zeilenga's avatar
Kurt Zeilenga committed
544
                    if [ ".$term_bold" != . ] && [ ".$term_norm" != . ]; then
545
546
547
548
549
                        break;
                    fi
                done
                ;;
        esac
Kurt Zeilenga's avatar
Kurt Zeilenga committed
550
        if [ ".$term_bold" = . ] || [ ".$term_norm" = . ]; then
551
            echo "$msgprefix:Warning: unable to determine terminal sequence for bold mode" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
552
553
            term_bold=''
            term_norm=''
554
555
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
556

557
558
    #   determine user name
    username=''
Kurt Zeilenga's avatar
Kurt Zeilenga committed
559
560
    if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%[uUgG]'`" != . ]; then
        username="`(id -un) 2>/dev/null`"
561
        if [ ".$username" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
562
563
564
565
            str="`(id) 2>/dev/null`"
            if [ ".`echo $str | grep '^uid[ 	]*=[ 	]*[0-9]*('`" != . ]; then
                username=`echo $str | sed -e 's/^uid[ 	]*=[ 	]*[0-9]*(//' -e 's/).*$//'`
            fi
566
            if [ ".$username" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
567
                username="$LOGNAME"
568
                if [ ".$username" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
569
                    username="$USER"
570
                    if [ ".$username" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
571
572
573
574
575
576
577
578
579
                        username="`(whoami) 2>/dev/null |\
                                   awk '{ printf("%s", $1); }'`"
                        if [ ".$username" = . ]; then
                            username="`(who am i) 2>/dev/null |\
                                       awk '{ printf("%s", $1); }'`"
                            if [ ".$username" = . ]; then
                                username='unknown'
                            fi
                        fi
580
581
582
583
584
                    fi
                fi
            fi
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
585

586
587
    #   determine user id
    userid=''
Kurt Zeilenga's avatar
Kurt Zeilenga committed
588
    if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%U'`" != . ]; then
589
590
        userid="`(id -u) 2>/dev/null`"
        if [ ".$userid" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
591
            userid="`(id -u ${username}) 2>/dev/null`"
592
            if [ ".$userid" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
593
594
595
596
                str="`(id) 2>/dev/null`"
                if [ ".`echo $str | grep '^uid[ 	]*=[ 	]*[0-9]*('`" != . ]; then
                    userid=`echo $str | sed -e 's/^uid[ 	]*=[ 	]*//' -e 's/(.*$//'`
                fi
597
                if [ ".$userid" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
598
                    userid=`(getent passwd ${username}) 2>/dev/null | \
599
600
                            sed -e 's/[^:]*:[^:]*://' -e 's/:.*$//'`
                    if [ ".$userid" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
601
602
603
                        userid=`grep "^${username}:" /etc/passwd 2>/dev/null | \
                                sed -e 's/[^:]*:[^:]*://' -e 's/:.*$//'`
                        if [ ".$userid" = . ]; then
604
605
                            userid=`(ypmatch "${username}" passwd; nismatch "${username}" passwd) 2>/dev/null | \
                                    sed -e 'q' | sed -e 's/[^:]*:[^:]*://' -e 's/:.*$//'`
Kurt Zeilenga's avatar
Kurt Zeilenga committed
606
                            if [ ".$userid" = . ]; then
607
608
609
610
611
                                userid=`(nidump passwd . | grep "^${username}:") 2>/dev/null | \
                                        sed -e 's/[^:]*:[^:]*://' -e 's/:.*$//'`
                                if [ ".$userid" = . ]; then
                                    userid='?'
                                fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
612
613
                            fi
                        fi
614
615
616
617
618
                    fi
                fi
            fi
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
619

620
621
    #   determine (primary) group id
    groupid=''
Kurt Zeilenga's avatar
Kurt Zeilenga committed
622
623
    if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%[gG]'`" != . ]; then
        groupid="`(id -g ${username}) 2>/dev/null`"
624
        if [ ".$groupid" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
625
626
627
628
            str="`(id) 2>/dev/null`"
            if [ ".`echo $str | grep 'gid[ 	]*=[ 	]*[0-9]*('`" != . ]; then
                groupid=`echo $str | sed -e 's/^.*gid[ 	]*=[ 	]*//' -e 's/(.*$//'`
            fi
629
            if [ ".$groupid" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
630
631
632
633
634
635
                groupid=`(getent passwd ${username}) 2>/dev/null | \
                         sed -e 's/[^:]*:[^:]*:[^:]*://' -e 's/:.*$//'`
                if [ ".$groupid" = . ]; then
                    groupid=`grep "^${username}:" /etc/passwd 2>/dev/null | \
                             sed -e 's/[^:]*:[^:]*:[^:]*://' -e 's/:.*$//'`
                    if [ ".$groupid" = . ]; then
636
637
                        groupid=`(ypmatch "${username}" passwd; nismatch "${username}" passwd) 2>/dev/null | \
                                 sed -e 'q' | sed -e 's/[^:]*:[^:]*:[^:]*://' -e 's/:.*$//'`
Kurt Zeilenga's avatar
Kurt Zeilenga committed
638
                        if [ ".$groupid" = . ]; then
639
640
641
642
643
                            groupid=`(nidump passwd . | grep "^${username}:") 2>/dev/null | \
                                     sed -e 's/[^:]*:[^:]*:[^:]*://' -e 's/:.*$//'`
                            if [ ".$groupid" = . ]; then
                                groupid='?'
                            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
644
645
646
                        fi
                    fi
                fi
647
648
649
            fi
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
650

651
652
    #   determine (primary) group name
    groupname=''
Kurt Zeilenga's avatar
Kurt Zeilenga committed
653
654
    if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%g'`" != . ]; then
        groupname="`(id -gn ${username}) 2>/dev/null`"
655
        if [ ".$groupname" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
656
657
658
659
            str="`(id) 2>/dev/null`"
            if [ ".`echo $str | grep 'gid[ 	]*=[ 	]*[0-9]*('`" != . ]; then
                groupname=`echo $str | sed -e 's/^.*gid[ 	]*=[ 	]*[0-9]*(//' -e 's/).*$//'`
            fi
660
            if [ ".$groupname" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
661
662
663
664
665
666
667
                groupname=`(getent group) 2>/dev/null | \
                           grep "^[^:]*:[^:]*:${groupid}:" | \
                           sed -e 's/:.*$//'`
                if [ ".$groupname" = . ]; then
                    groupname=`grep "^[^:]*:[^:]*:${groupid}:" /etc/group 2>/dev/null | \
                               sed -e 's/:.*$//'`
                    if [ ".$groupname" = . ]; then
668
669
                        groupname=`(ypcat group; niscat group) 2>/dev/null | \
                                   sed -e 'q' | grep "^[^:]*:[^:]*:${groupid}:" | \
Kurt Zeilenga's avatar
Kurt Zeilenga committed
670
671
                                   sed -e 's/:.*$//'`
                        if [ ".$groupname" = . ]; then
672
673
674
675
676
677
                            groupname=`(nidump group .) 2>/dev/null | \
                                       grep "^[^:]*:[^:]*:${groupid}:" | \
                                       sed -e 's/:.*$//'`
                            if [ ".$groupname" = . ]; then
                                groupname='?'
                            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
678
679
680
                        fi
                    fi
                fi
681
682
683
            fi
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
684

685
686
687
    #   determine host and domain name
    hostname=''
    domainname=''
Kurt Zeilenga's avatar
Kurt Zeilenga committed
688
    if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%h'`" != . ]; then
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
        hostname="`(uname -n) 2>/dev/null |\
                   awk '{ printf("%s", $1); }'`"
        if [ ".$hostname" = . ]; then
            hostname="`(hostname) 2>/dev/null |\
                       awk '{ printf("%s", $1); }'`"
            if [ ".$hostname" = . ]; then
                hostname='unknown'
            fi
        fi
        case $hostname in
            *.* )
                domainname=".`echo $hostname | cut -d. -f2-`"
                hostname="`echo $hostname | cut -d. -f1`"
                ;;
        esac
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
705
    if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%d'`" != . ]; then
706
707
        if [ ".$domainname" = . ]; then
            if [ -f /etc/resolv.conf ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
708
                domainname="`grep '^[ 	]*domain' /etc/resolv.conf | sed -e 'q' |\
709
710
711
712
713
                             sed -e 's/.*domain//' \
                                 -e 's/^[ 	]*//' -e 's/^ *//' -e 's/^	*//' \
                                 -e 's/^\.//' -e 's/^/./' |\
                             awk '{ printf("%s", $1); }'`"
                if [ ".$domainname" = . ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
714
                    domainname="`grep '^[ 	]*search' /etc/resolv.conf | sed -e 'q' |\
715
716
717
718
719
720
721
722
723
                                 sed -e 's/.*search//' \
                                     -e 's/^[ 	]*//' -e 's/^ *//' -e 's/^	*//' \
                                     -e 's/ .*//' -e 's/	.*//' \
                                     -e 's/^\.//' -e 's/^/./' |\
                                 awk '{ printf("%s", $1); }'`"
                fi
            fi
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
724

725
726
727
728
729
    #   determine current time
    time_day=''
    time_month=''
    time_year=''
    time_monthname=''
Kurt Zeilenga's avatar
Kurt Zeilenga committed
730
    if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%[DMYm]'`" != . ]; then
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
        time_day=`date '+%d'`
        time_month=`date '+%m'`
        time_year=`date '+%Y' 2>/dev/null`
        if [ ".$time_year" = . ]; then
            time_year=`date '+%y'`
            case $time_year in
                [5-9][0-9]) time_year="19$time_year" ;;
                [0-4][0-9]) time_year="20$time_year" ;;
            esac
        fi
        case $time_month in
            1|01) time_monthname='Jan' ;;
            2|02) time_monthname='Feb' ;;
            3|03) time_monthname='Mar' ;;
            4|04) time_monthname='Apr' ;;
            5|05) time_monthname='May' ;;
            6|06) time_monthname='Jun' ;;
            7|07) time_monthname='Jul' ;;
            8|08) time_monthname='Aug' ;;
            9|09) time_monthname='Sep' ;;
              10) time_monthname='Oct' ;;
              11) time_monthname='Nov' ;;
              12) time_monthname='Dec' ;;
        esac
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
756

757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
    #   expand special ``%x'' constructs
    if [ ".$opt_e" = .yes ]; then
        text=`echo $seo "$text" |\
              sed -e "s/%B/${term_bold}/g" \
                  -e "s/%b/${term_norm}/g" \
                  -e "s/%u/${username}/g" \
                  -e "s/%U/${userid}/g" \
                  -e "s/%g/${groupname}/g" \
                  -e "s/%G/${groupid}/g" \
                  -e "s/%h/${hostname}/g" \
                  -e "s/%d/${domainname}/g" \
                  -e "s/%D/${time_day}/g" \
                  -e "s/%M/${time_month}/g" \
                  -e "s/%Y/${time_year}/g" \
                  -e "s/%m/${time_monthname}/g" 2>/dev/null`
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
773

774
775
776
777
778
779
780
781
782
783
784
785
    #   create output
    if [ .$opt_n = .no ]; then
        echo $seo "$text"
    else
        #   the harder part: echo -n is best, because
        #   awk may complain about some \xx sequences.
        if [ ".$minusn" != . ]; then
            echo $seo $minusn "$text"
        else
            echo dummy | awk '{ printf("%s", TEXT); }' TEXT="$text"
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
786
787

    shtool_exit 0
788
789
790
791
792
    ;;

move )
    ##
    ##  move -- Move files with simultaneous substitution
793
    ##  Copyright (c) 1999-2008 Ralf S. Engelschall <rse@engelschall.com>
794
    ##
Kurt Zeilenga's avatar
Kurt Zeilenga committed
795

796
797
    src="$1"
    dst="$2"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
798

799
    #   consistency checks
Kurt Zeilenga's avatar
Kurt Zeilenga committed
800
    if [ ".$src" = . ] || [ ".$dst" = . ]; then
801
        echo "$msgprefix:Error: Invalid arguments" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
802
        shtool_exit 1
803
804
805
    fi
    if [ ".$src" = ".$dst" ]; then
        echo "$msgprefix:Error: Source and destination files are the same" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
806
        shtool_exit 1
807
808
809
810
811
812
813
814
    fi
    expsrc="$src"
    if [ ".$opt_e" = .yes ]; then
        expsrc="`echo $expsrc`"
    fi
    if [ ".$opt_e" = .yes ]; then
        if [ ".`echo "$src" | sed -e 's;^.*\\*.*$;;'`" = ".$src" ]; then
            echo "$msgprefix:Error: Source doesn't contain wildcard ('*'): $dst" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
815
            shtool_exit 1
816
817
818
        fi
        if [ ".`echo "$dst" | sed -e 's;^.*%[1-9].*$;;'`" = ".$dst" ]; then
            echo "$msgprefix:Error: Destination doesn't contain substitution ('%N'): $dst" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
819
            shtool_exit 1
820
821
822
        fi
        if [ ".$expsrc" = ".$src" ]; then
            echo "$msgprefix:Error: Sources not found or no asterisk : $src" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
823
            shtool_exit 1
824
825
826
827
        fi
    else
        if [ ! -r "$src" ]; then
            echo "$msgprefix:Error: Source not found: $src" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
828
            shtool_exit 1
829
830
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
831

832
833
834
835
836
    #   determine substitution patterns
    if [ ".$opt_e" = .yes ]; then
        srcpat=`echo "$src" | sed -e 's/\\./\\\\./g' -e 's/;/\\;/g' -e 's;\\*;\\\\(.*\\\\);g'`
        dstpat=`echo "$dst" | sed -e 's;%\([1-9]\);\\\\\1;g'`
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
837

838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
    #   iterate over source(s)
    for onesrc in $expsrc; do
        if [ .$opt_e = .yes ]; then
            onedst=`echo $onesrc | sed -e "s;$srcpat;$dstpat;"`
        else
            onedst="$dst"
        fi
        errorstatus=0
        if [ ".$opt_v" = .yes ]; then
            echo "$onesrc -> $onedst"
        fi
        if [ ".$opt_p" = .yes ]; then
            if [ -r $onedst ]; then
                if cmp -s $onesrc $onedst; then
                    if [ ".$opt_t" = .yes ]; then
                        echo "rm -f $onesrc" 1>&2
                    fi
                    rm -f $onesrc || errorstatus=$?
                else
                    if [ ".$opt_t" = .yes ]; then
                        echo "mv -f $onesrc $onedst" 1>&2
                    fi
                    mv -f $onesrc $onedst || errorstatus=$?
                fi
            else
                if [ ".$opt_t" = .yes ]; then
                    echo "mv -f $onesrc $onedst" 1>&2
                fi
                mv -f $onesrc $onedst || errorstatus=$?
            fi
        else
            if [ ".$opt_t" = .yes ]; then
                echo "mv -f $onesrc $onedst" 1>&2
            fi
            mv -f $onesrc $onedst || errorstatus=$?
        fi
        if [ $errorstatus -ne 0 ]; then
            break;
        fi
    done
Kurt Zeilenga's avatar
Kurt Zeilenga committed
878
879

    shtool_exit $errorstatus
880
881
882
883
884
    ;;

install )
    ##
    ##  install -- Install a program, script or datafile
885
    ##  Copyright (c) 1997-2008 Ralf S. Engelschall <rse@engelschall.com>
886
    ##
Kurt Zeilenga's avatar
Kurt Zeilenga committed
887

Kurt Zeilenga's avatar
Kurt Zeilenga committed
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
    #   special case: "shtool install -d <dir> [...]" internally
    #   maps to "shtool mkdir -f -p -m 755 <dir> [...]"
    if [ "$opt_d" = yes ]; then
        cmd="$0 mkdir -f -p -m 755"
        if [ ".$opt_o" != . ]; then
            cmd="$cmd -o '$opt_o'"
        fi
        if [ ".$opt_g" != . ]; then
            cmd="$cmd -g '$opt_g'"
        fi
        if [ ".$opt_v" = .yes ]; then
            cmd="$cmd -v"
        fi
        if [ ".$opt_t" = .yes ]; then
            cmd="$cmd -t"
        fi
        for dir in "$@"; do
Kurt Zeilenga's avatar
Kurt Zeilenga committed
905
            eval "$cmd $dir" || shtool_exit $?
Kurt Zeilenga's avatar
Kurt Zeilenga committed
906
        done
Kurt Zeilenga's avatar
Kurt Zeilenga committed
907
        shtool_exit 0
Kurt Zeilenga's avatar
Kurt Zeilenga committed
908
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
909

910
911
912
913
914
915
916
917
    #   determine source(s) and destination
    argc=$#
    srcs=""
    while [ $# -gt 1 ]; do
        srcs="$srcs $1"
        shift
    done
    dstpath="$1"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
918

919
920
921
922
923
924
    #   type check for destination
    dstisdir=0
    if [ -d $dstpath ]; then
        dstpath=`echo "$dstpath" | sed -e 's:/$::'`
        dstisdir=1
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
925

926
    #   consistency check for destination
Kurt Zeilenga's avatar
Kurt Zeilenga committed
927
    if [ $argc -gt 2 ] && [ $dstisdir = 0 ]; then
928
        echo "$msgprefix:Error: multiple sources require destination to be directory" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
929
        shtool_exit 1
930
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
931

932
933
934
    #   iterate over all source(s)
    for src in $srcs; do
        dst=$dstpath
Kurt Zeilenga's avatar
Kurt Zeilenga committed
935

936
937
938
939
940
        #   if destination is a directory, append the input filename
        if [ $dstisdir = 1 ]; then
            dstfile=`echo "$src" | sed -e 's;.*/\([^/]*\)$;\1;'`
            dst="$dst/$dstfile"
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
941

942
943
944
945
946
947
948
949
950
        #   check for correct arguments
        if [ ".$src" = ".$dst" ]; then
            echo "$msgprefix:Warning: source and destination are the same - skipped" 1>&2
            continue
        fi
        if [ -d "$src" ]; then
            echo "$msgprefix:Warning: source \`$src' is a directory - skipped" 1>&2
            continue
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
951

952
953
954
955
        #   make a temp file name in the destination directory
        dsttmp=`echo $dst |\
                sed -e 's;[^/]*$;;' -e 's;\(.\)/$;\1;' -e 's;^$;.;' \
                    -e "s;\$;/#INST@$$#;"`
Kurt Zeilenga's avatar
Kurt Zeilenga committed
956

957
958
959
960
        #   verbosity
        if [ ".$opt_v" = .yes ]; then
            echo "$src -> $dst" 1>&2
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
961

962
963
964
965
966
967
968
969
970
        #   copy or move the file name to the temp name
        #   (because we might be not allowed to change the source)
        if [ ".$opt_C" = .yes ]; then
            opt_c=yes
        fi
        if [ ".$opt_c" = .yes ]; then
            if [ ".$opt_t" = .yes ]; then
                echo "cp $src $dsttmp" 1>&2
            fi
971
            cp "$src" "$dsttmp" || shtool_exit $?
972
973
974
975
        else
            if [ ".$opt_t" = .yes ]; then
                echo "mv $src $dsttmp" 1>&2
            fi
976
            mv "$src" "$dsttmp" || shtool_exit $?
977
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
978

979
980
981
982
983
984
985
986
        #   adjust the target file
        if [ ".$opt_e" != . ]; then
            sed='sed'
            OIFS="$IFS"; IFS="$ASC_NL"; set -- $opt_e; IFS="$OIFS"
            for e
            do
                sed="$sed -e '$e'"
            done
987
            cp "$dsttmp" "$dsttmp.old"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
988
989
            chmod u+w $dsttmp
            eval "$sed <$dsttmp.old >$dsttmp" || shtool_exit $?
990
991
992
993
994
995
            rm -f $dsttmp.old
        fi
        if [ ".$opt_s" = .yes ]; then
            if [ ".$opt_t" = .yes ]; then
                echo "strip $dsttmp" 1>&2
            fi
996
            $STRIP $dsttmp || shtool_exit $?
997
998
999
1000
1001
        fi
        if [ ".$opt_o" != . ]; then
            if [ ".$opt_t" = .yes ]; then
                echo "chown $opt_o $dsttmp" 1>&2
            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1002
            chown $opt_o $dsttmp || shtool_exit $?
1003
1004
1005
1006
1007
        fi
        if [ ".$opt_g" != . ]; then
            if [ ".$opt_t" = .yes ]; then
                echo "chgrp $opt_g $dsttmp" 1>&2
            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1008
            chgrp $opt_g $dsttmp || shtool_exit $?
1009
1010
1011
1012
1013
        fi
        if [ ".$opt_m" != ".-" ]; then
            if [ ".$opt_t" = .yes ]; then
                echo "chmod $opt_m $dsttmp" 1>&2
            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1014
            chmod $opt_m $dsttmp || shtool_exit $?
1015
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1016

1017
1018
1019
1020
1021
        #   determine whether to do a quick install
        #   (has to be done _after_ the strip was already done)
        quick=no
        if [ ".$opt_C" = .yes ]; then
            if [ -r $dst ]; then
1022
                if cmp -s "$src" "$dst"; then
1023
1024
1025
1026
                    quick=yes
                fi
            fi
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1027

1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
        #   finally, install the file to the real destination
        if [ $quick = yes ]; then
            if [ ".$opt_t" = .yes ]; then
                echo "rm -f $dsttmp" 1>&2
            fi
            rm -f $dsttmp
        else
            if [ ".$opt_t" = .yes ]; then
                echo "rm -f $dst && mv $dsttmp $dst" 1>&2
            fi
            rm -f $dst && mv $dsttmp $dst
        fi
    done
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1041
1042

    shtool_exit 0
1043
1044
1045
1046
1047
    ;;

mkdir )
    ##
    ##  mkdir -- Make one or more directories
1048
    ##  Copyright (c) 1996-2008 Ralf S. Engelschall <rse@engelschall.com>
1049
    ##
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1050

1051
1052
1053
1054
    errstatus=0
    for p in ${1+"$@"}; do
        #   if the directory already exists...
        if [ -d "$p" ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1055
            if [ ".$opt_f" = .no ] && [ ".$opt_p" = .no ]; then
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
                echo "$msgprefix:Error: directory already exists: $p" 1>&2
                errstatus=1
                break
            else
                continue
            fi
        fi
        #   if the directory has to be created...
        if [ ".$opt_p" = .no ]; then
            if [ ".$opt_t" = .yes ]; then
                echo "mkdir $p" 1>&2
            fi
            mkdir $p || errstatus=$?
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
            if [ ".$opt_o" != . ]; then
                if [ ".$opt_t" = .yes ]; then
                    echo "chown $opt_o $p" 1>&2
                fi
                chown $opt_o $p || errstatus=$?
            fi
            if [ ".$opt_g" != . ]; then
                if [ ".$opt_t" = .yes ]; then
                    echo "chgrp $opt_g $p" 1>&2
                fi
                chgrp $opt_g $p || errstatus=$?
            fi
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
            if [ ".$opt_m" != . ]; then
                if [ ".$opt_t" = .yes ]; then
                    echo "chmod $opt_m $p" 1>&2
                fi
                chmod $opt_m $p || errstatus=$?
            fi
        else
            #   the smart situation
            set fnord `echo ":$p" |\
                       sed -e 's/^:\//%/' \
                           -e 's/^://' \
                           -e 's/\// /g' \
                           -e 's/^%/\//'`
            shift
            pathcomp=''
            for d in ${1+"$@"}; do
                pathcomp="$pathcomp$d"
                case "$pathcomp" in
                    -* ) pathcomp="./$pathcomp" ;;
                esac
                if [ ! -d "$pathcomp" ]; then
                    if [ ".$opt_t" = .yes ]; then
                        echo "mkdir $pathcomp" 1>&2
                    fi
                    mkdir $pathcomp || errstatus=$?
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
                    if [ ".$opt_o" != . ]; then
                        if [ ".$opt_t" = .yes ]; then
                            echo "chown $opt_o $pathcomp" 1>&2
                        fi
                        chown $opt_o $pathcomp || errstatus=$?
                    fi
                    if [ ".$opt_g" != . ]; then
                        if [ ".$opt_t" = .yes ]; then
                            echo "chgrp $opt_g $pathcomp" 1>&2
                        fi
                        chgrp $opt_g $pathcomp || errstatus=$?
                    fi
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
                    if [ ".$opt_m" != . ]; then
                        if [ ".$opt_t" = .yes ]; then
                            echo "chmod $opt_m $pathcomp" 1>&2
                        fi
                        chmod $opt_m $pathcomp || errstatus=$?
                    fi
                fi
                pathcomp="$pathcomp/"
            done
        fi
    done
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1129
1130

    shtool_exit $errstatus
1131
1132
1133
1134
1135
    ;;

mkln )
    ##
    ##  mkln -- Make link with calculation of relative paths
1136
    ##  Copyright (c) 1998-2008 Ralf S. Engelschall <rse@engelschall.com>
1137
    ##
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1138
1139

    #   determine source(s) and destination
1140
    args=$#
1141
1142
1143
1144
1145
1146
1147
1148
1149
    srcs=""
    while [ $# -gt 1 ]; do
        srcs="$srcs $1"
        shift
    done
    dst="$1"
    if [ ! -d $dst ]; then
        if [ $args -gt 2 ]; then
            echo "$msgprefix:Error: multiple sources not allowed when target isn't a directory" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1150
            shtool_exit 1
1151
1152
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1153

1154
1155
1156
1157
1158
1159
1160
1161
    #   determine link options
    lnopt=""
    if [ ".$opt_f" = .yes ]; then
        lnopt="$lnopt -f"
    fi
    if [ ".$opt_s" = .yes ]; then
        lnopt="$lnopt -s"
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1162

1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
    #   iterate over sources
    for src in $srcs; do
        #   determine if one of the paths is an absolute path,
        #   because then we _have_ to use an absolute symlink
        oneisabs=0
        srcisabs=0
        dstisabs=0
        case $src in
            /* ) oneisabs=1; srcisabs=1 ;;
        esac
        case $dst in
            /* ) oneisabs=1; dstisabs=1 ;;
        esac
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1176

1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
        #   split source and destination into dir and base name
        if [ -d $src ]; then
            srcdir=`echo $src | sed -e 's;/*$;;'`
            srcbase=""
        else
            srcdir=`echo  $src | sed -e 's;^[^/]*$;;' -e 's;^\(.*/\)[^/]*$;\1;' -e 's;\(.\)/$;\1;'`
            srcbase=`echo $src | sed -e 's;.*/\([^/]*\)$;\1;'`
        fi
        if [ -d $dst ]; then
            dstdir=`echo $dst | sed -e 's;/*$;;'`
            dstbase=""
        else
            dstdir=`echo  $dst | sed -e 's;^[^/]*$;;' -e 's;^\(.*/\)[^/]*$;\1;' -e 's;\(.\)/$;\1;'`
            dstbase=`echo $dst | sed -e 's;.*/\([^/]*\)$;\1;'`
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1192

1193
1194
1195
1196
        #   consistency check
        if [ ".$dstdir" != . ]; then
            if [ ! -d $dstdir ]; then
                echo "$msgprefix:Error: destination directory not found: $dstdir" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1197
                shtool_exit 1
1198
1199
            fi
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1200

1201
1202
1203
        #   make sure the source is reachable from the destination
        if [ $dstisabs = 1 ]; then
            if [ $srcisabs = 0 ]; then
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1204
1205
1206
1207
1208
                if [ ".$srcdir" = . ]; then
                    srcdir="`pwd | sed -e 's;/*$;;'`"
                    srcisabs=1
                    oneisabs=1
                elif [ -d $srcdir ]; then
1209
1210
1211
1212
1213
1214
                    srcdir="`cd $srcdir; pwd | sed -e 's;/*$;;'`"
                    srcisabs=1
                    oneisabs=1
                fi
            fi
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1215

1216
1217
        #   split away a common prefix
        prefix=""
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1218
        if [ ".$srcdir" = ".$dstdir" ] && [ ".$srcdir" != . ]; then
1219
1220
1221
1222
            prefix="$srcdir/"
            srcdir=""
            dstdir=""
        else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1223
            while [ ".$srcdir" != . ] && [ ".$dstdir" != . ]; do
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
                presrc=`echo $srcdir | sed -e 's;^\([^/]*\)/.*;\1;'`
                predst=`echo $dstdir | sed -e 's;^\([^/]*\)/.*;\1;'`
                if [ ".$presrc" != ".$predst" ]; then
                    break
                fi
                prefix="$prefix$presrc/"
                srcdir=`echo $srcdir | sed -e 's;^[^/]*/*;;'`
                dstdir=`echo $dstdir | sed -e 's;^[^/]*/*;;'`
            done
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1234

1235
1236
        #   destination prefix is just the common prefix
        dstpre="$prefix"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1237

1238
1239
1240
        #   determine source prefix which is the reverse directory
        #   step-up corresponding to the destination directory
        srcpre=""
1241
1242
1243
1244
1245
1246
1247
1248
1249
        allow_relative_srcpre=no
        if [ ".$prefix" != . ] && [ ".$prefix" != ./ ]; then
            allow_relative_srcpre=yes
        fi
        if [ $oneisabs = 0 ]; then
            allow_relative_srcpre=yes
        fi
        if [ ".$opt_s" != .yes ]; then
            allow_relative_srcpre=no
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1250
        fi
1251
        if [ ".$allow_relative_srcpre" = .yes ]; then
1252
1253
1254
            pl="$dstdir/"
            OIFS="$IFS"; IFS='/'
            for pe in $pl; do
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1255
1256
                [ ".$pe" = .  ] && continue
                [ ".$pe" = .. ] && continue
1257
1258
1259
1260
1261
1262
1263
1264
                srcpre="../$srcpre"
            done
            IFS="$OIFS"
        else
            if [ $srcisabs = 1 ]; then
                srcpre="$prefix"
            fi
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1265

1266
1267
1268
1269
1270
1271
1272
1273
        #   determine destination symlink name
        if [ ".$dstbase" = . ]; then
            if [ ".$srcbase" != . ]; then
                dstbase="$srcbase"
            else
                dstbase=`echo "$prefix$srcdir" | sed -e 's;/*$;;' -e 's;.*/\([^/]*\)$;\1;'`
            fi
        fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1274

1275
1276
1277
        #   now finalize source and destination directory paths
        srcdir=`echo $srcdir | sed -e 's;\([^/]\)$;\1/;'`
        dstdir=`echo $dstdir | sed -e 's;\([^/]\)$;\1/;'`
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1278

1279
1280
1281
1282
1283
1284
        #   run the final link command
        if [ ".$opt_t" = .yes ]; then
            echo "ln$lnopt $srcpre$srcdir$srcbase $dstpre$dstdir$dstbase"
        fi
        eval ln$lnopt $srcpre$srcdir$srcbase $dstpre$dstdir$dstbase
    done
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1285
1286

    shtool_exit 0
1287
1288
1289
1290
1291
    ;;

subst )
    ##
    ##  subst -- Apply sed(1) substitution operations
1292
    ##  Copyright (c) 2001-2008 Ralf S. Engelschall <rse@engelschall.com>
1293
    ##
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1294

1295
1296
    #   remember optional list of file(s)
    files="$*"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1297
1298
    files_num="$#"

1299
    #   parameter consistency check
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1300
    if [ $# -eq 0 ] && [ ".$opt_b" != . ]; then
1301
        echo "$msgprefix:Error: option -b cannot be applied to stdin" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1302
        shtool_exit 1
1303
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1304
    if [ $# -eq 0 ] && [ ".$opt_s" = .yes ]; then
1305
        echo "$msgprefix:Error: option -s cannot be applied to stdin" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1306
        shtool_exit 1
1307
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1308

1309
1310
1311
1312
1313
1314
    #   build underlying sed(1) command
    sedcmd='sed'
    if [ ".$opt_e" != . ]; then
        OIFS="$IFS"; IFS="$ASC_NL"; set -- $opt_e; IFS="$OIFS"
        for e
        do
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1315
            sedcmd="$sedcmd -e '$e'"
1316
1317
1318
1319
        done
    elif [ ".$opt_f" != . ]; then
        if [ ! -f $opt_f ]; then
            echo "$msgprefix:Error: command file \`$opt_f' not found or not a regular file" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1320
            shtool_exit 1
1321
1322
1323
1324
        fi
        sedcmd="$sedcmd -f '$opt_f'"
    else
        echo "$msgprefix:Error: either -e option(s) or -f option required" 1>&2
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1325
        shtool_exit 1
1326
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1327

1328
1329
1330
1331
1332
    #   determine extension for original file
    orig=".orig"
    if [ ".$opt_b" != . ]; then
        orig="$opt_b"
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1333

1334
1335
1336
    #   apply sed(1) operation(s)
    if [ ".$files" != . ]; then
        #   apply operation(s) to files
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1337
        substdone=no
1338
1339
1340
1341
1342
1343
        for file in $files; do
            test ".$file" = . && continue
            if [ ! -f $file ]; then
                echo "$msgprefix:Warning: file \`$file' not found or not a regular file" 1>&2
                continue
            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1344

1345
1346
1347
1348
1349
1350
1351
1352
            #   handle interactive mode
            if [ ".$opt_i" = .yes ]; then
                eval "$sedcmd <$file >$file.new"
                skip=no
                if cmp $file $file.new >/dev/null 2>&1; then
                    rm -f $file.new
                    skip=yes
                else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1353
                    (diff -U1 $file $file.new >$tmpfile) 2>/dev/null
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
                    if [ ".`cat $tmpfile`" = . ]; then
                        (diff -C1 $file $file.new >$tmpfile) 2>/dev/null
                        if [ ".`cat $tmpfile`" = . ]; then
                            echo "$msgprefix:Warning: unable to show difference for file \`$file'" 1>&2
                            cp /dev/null $tmpfile
                        fi
                    fi
                    rm -f $file.new
                    cat $tmpfile
                    echo dummy | awk '{ printf("%s", TEXT); }' TEXT=">>> Apply [Y/n]: "
                    read input
                    if [ ".$input" != .Y ] &&\
                       [ ".$input" != .y ] &&\
                       [ ".$input" != . ]; then
                       skip=yes
                    fi
                fi
                if [ ".$skip" = .yes ]; then
                    if [ ".$opt_v" = .yes ]; then
                        echo "file \`$file' -- skipped" 1>&2
                    fi
                    continue
                fi
            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1378

1379
1380
1381
1382
1383
1384
            #   apply sed(1) operation(s)
            if [ ".$opt_v" = .yes ]; then
                echo "patching \`$file'" 1>&2
            fi
            if [ ".$opt_t" = .yes ]; then
                echo "\$ cp -p $file $file$orig"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1385
                echo "\$ chmod u+w $file"
1386
1387
1388
1389
                echo "\$ $sedcmd <$file$orig >$file"
            fi
            if [ ".$opt_n" = .no ]; then
                cp -p $file $file$orig
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1390
                chmod u+w $file >/dev/null 2>&1 || true
1391
1392
                eval "$sedcmd <$file$orig >$file"
            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1393

1394
1395
1396
1397
1398
1399
1400
1401
1402
            #   optionally fix timestamp
            if [ ".$opt_s" = .yes ]; then
                if [ ".$opt_t" = .yes ]; then
                    echo "\$ touch -r $file$orig $file"
                fi
                if [ ".$opt_n" = .no ]; then
                    touch -r $file$orig $file
                fi
            fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1403

1404
            #   optionally check whether any content change actually occurred
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
            if [ ".$opt_q" = .no ]; then
                if cmp $file$orig $file >/dev/null 2>&1; then
                    if [ ".$opt_w" = .yes ]; then
                        echo "$msgprefix:Warning: substitution resulted in no content change on file \"$file\"" 1>&2
                    fi
                else
                    substdone=yes
                fi
            fi

1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
            #   optionally remove preserved original file
            if [ ".$opt_b" = . ]; then
                if [ ".$opt_t" = .yes ]; then
                    echo "\$ rm -f $file$orig"
                fi
                if [ ".$opt_n" = .no ]; then
                    rm -f $file$orig
                fi
            fi
        done
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1425
1426
1427
1428
1429
1430
1431
1432
1433
        if [ ".$opt_q" = .no ] && [ ".$opt_w" = .no ]; then
            if [ ".$substdone" = .no ]; then
                if [ ".$files_num" = .1 ]; then
                    echo "$msgprefix:Warning: substitution resulted in no content change on file \"$file\"" 1>&2
                else
                    echo "$msgprefix:Warning: substitution resulted in no content change on any file" 1>&2
                fi
            fi
        fi
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
    else
        #   apply operation(s) to stdin/stdout
        if [ ".$opt_v" = .yes ]; then
            echo "patching <stdin>" 1>&2
        fi
        if [ ".$opt_t" = .yes ]; then
            echo "\$ $sedcmd"
        fi
        if [ ".$opt_n" = .no ]; then
            eval "$sedcmd"
        fi
    fi
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1446
1447

    shtool_exit 0
1448
1449
1450
1451
    ;;

esac

Kurt Zeilenga's avatar
Kurt Zeilenga committed
1452
shtool_exit 0
1453