1#!/bin/sh
2#
3# Copyright 2010-2018 Brad Lanam Walnut Creek, CA, USA
4# Copyright 2020 Brad Lanam Pleasant Hill CA
5#
6
7if [ ! -f "${_MKCONFIG_DIR}/VERSION" ]; then
8  echo "Unable to locate ${_MKCONFIG_DIR}/VERSION."
9  echo "Not a valid mkconfig installation."
10  exit 1
11fi
12
13# dash on Mandriva 2011 segfaults intermittently when:
14#   read _MKCONFIG_VERSION < ${_MKCONFIG_DIR}/VERSION
15#   export _MKCONFIG_VERSION
16# was used.
17_MKCONFIG_VERSION=`cat ${_MKCONFIG_DIR}/VERSION`
18export _MKCONFIG_VERSION
19
20# posh set command doesn't output correct data; don't bother with it.
21# older yash has quoting/backquote issues (what version did this get fixed?)
22# bosh is a disaster.
23# zsh is not bourne shell compatible
24#   set | grep can be fixed (nulls are output) with: set | strings | grep
25#   and 'emulate ksh' can be set in mkconfig.sh, but there
26#   are more issues, and I'm not interested in tracking them down.
27tryshell="bash bash5 bash4 bash3 ksh ksh88 ksh93 mksh pdksh yash ash dash bash2 sh sh5 osh"
28
29mkconfigversion () {
30  echo "mkconfig version ${_MKCONFIG_VERSION}"
31}
32
33dosubst () {
34  subvar=$1
35  shift
36  sa=""
37  while test $# -gt 0; do
38    pattern=$1
39    sub=$2
40    shift
41    shift
42    sa="${sa} -e \"s~${pattern}~${sub}~g\""
43  done
44  cmd="${subvar}=\`echo \${${subvar}} | sed ${sa}\`"
45  eval $cmd;
46}
47
48test_echo () {
49  shhasprintf=0
50  shhasprintferr=""
51  (eval 'printf %s hello >/dev/null') 2>/dev/null
52  rc=$?
53  # Virtually all shells have printf.
54  # I had not known it was supported so far back -- most
55  # everything used echo, and printf was not mentioned much.
56  # 'printf' is POSIX compliant.
57  if [ $rc -eq 0 ]; then
58    shhasprintf=1
59    eval 'putsnonl () { printf '%s' "$*"; }'
60    eval 'puts () { printf "%s\n" "$*"; }'
61  else
62    shhasprintferr=none
63  fi
64
65  #
66  # Backslash interpolation with echo and single quotes has issues.
67  # Apparently I didn't code anything that ran into that problem.
68  #
69  # echo with single quotes should be avoided as too many shells
70  # interpolate backslashes when they should not.
71  #
72  # The read/printf combo fails due to differences from read/echo.
73  #
74  # 2019-6-26: Apparently some older ksh93 also have this issue.
75  # 2020-6-10: Apparently there are differences in quite a few shells here.
76  #            I am surprised I did not run into portability issues with this.
77  #
78  # $ echo "a \\ b"
79  # a \ b
80  # $ printf "%s\n" "a \\ b"
81  # a \ b
82  # $ cat tt
83  # a \\ b
84  # $ read tdat < tt
85  # $ echo "$tdat"
86  # a \ b               # should be: a \ b
87  # $ printf "%s\n" "$tdat"
88  # a \\ b              # should be: a \ b
89  # $ echo 'a \\ b'
90  # a \ b               # should be: a \\ b
91  # $ printf "%s\n" 'a \\ b'
92  # a \\ b              # should be: a \\ b
93  # $
94  #
95
96  # Test for interpolation bugs.
97  # If bugs are present, use 'echo'.
98  tz="a \\\\ b"            # should always interpolate as: a \\ b
99  t1=`echo "$tz"`          # and both of these should return: a \\ b
100  t2=`printf '%s\n' "$tz"`
101  # have to test against the original in order to catch the change
102  # if echo and printf do not match each other, it is very likely
103  # that the read/echo/printf mismatches also occur.
104  # osh does some odd stuff internally according to the output here,
105  # but echo and printf still seem to work.
106  if [ \( "$tz" != "$t1" \) -o \( "$tz" != "$t2" \) ]; then
107    shhasprintf=0
108    shhasprintferr=mismatch
109  fi
110
111  # 'echo' works everywhere, but is not a POSIX compliant command.
112  # Have not found any bourne compatible shell that does not support
113  # either -n or \c.
114  if [ $shhasprintf -eq 0 ]; then
115    _tEN='-n'
116    _tEC=''
117    if [ "`echo -n test`" = "-n test" ]; then
118      _tEN=''
119      _tEC='\c'
120    fi
121    eval 'putsnonl () { echo ${_tEN} "$*${_tEC}"; }'
122    eval 'puts () { echo "$*"; }'
123  fi
124}
125
126# The += form is much, much faster and less prone to errors.
127test_append () {
128  shhasappend=0
129  (eval 'x=a;x+=b; test z$x = zab') 2>/dev/null
130  if [ $? -eq 0 ]; then
131    shhasappend=1
132    eval 'doappend () { eval $1+=\$2; }'
133  else
134    eval 'doappend () { eval $1=\$${1}\$2; }'
135  fi
136}
137
138test_readraw () {
139  # $ cat tt
140  # a \\ b
141  # $ read tdat < tt
142  # $ echo "$tdat"
143  # a \ b               # should be: a \ b
144  # $ printf "%s\n" "$tdat"
145  # a \\ b              # should be: a \ b
146
147  shreqreadraw=0
148  # unixware 7.14 compiles and runs this code ok, but it's shell gets
149  # completely wacked out later.  So run it in a subshell.
150  # (similar to mandriva 2011 problem with read < file)
151  (
152    rrv='aa\\\\bb'
153    read rrx << _HERE_
154$rrv
155_HERE_
156    (
157      eval "read -r rry <<_HERE_
158$rrv
159_HERE_"
160    ) 2>/dev/null
161    yrc=$?
162    if [ $yrc -eq 0 ]; then
163      read -r rry << _HERE_
164$rrv
165_HERE_
166      if [ "${rrx}" != 'aa\\bb' -a "${rry}" = 'aa\\\\bb' ]; then
167        shreqreadraw=1
168      fi
169    fi
170    exit $shreqreadraw
171  )
172  shreqreadraw=$?
173}
174
175# use the faster method $((expr)) if possible.
176test_math () {
177  shhasmath=0
178  shhasmatherr=none
179  (eval 'x=1;y=$(($x+1)); test z$y = z2') 2>/dev/null
180  rc=$?
181  if [ $rc -eq 0 ]; then
182    shhasmath=1
183    shhasmatherr=""
184    eval 'domath () { mthvar=$1; mthval=\$\(\($2\)\); eval $mthvar=$mthval; }'
185  fi
186  if [ $shhasmath -eq 0 ]; then
187    eval 'domath () { mthvar=$1; mthval=`expr $2`; eval $mthvar=$mthval; }'
188  fi
189}
190
191# use the faster shell built-in if possible.
192test_upper () {
193  shhasupper=0
194  (eval 'typeset -u xuvar;xuvar=x;test z$xuvar = zX') 2>/dev/null
195  if [ $? -eq 0 ]; then
196    shhasupper=1
197    eval 'toupper () { ucvar=$1; typeset -u ucval; eval "ucval=\${$ucvar};$ucvar=\$ucval"; }'
198  else
199    eval 'toupper () { ucvar=$1; cmd="$ucvar=\`echo \${$ucvar} | tr \"[a-z]\" \"[A-Z]\"\`"; eval "$cmd"; }'
200  fi
201}
202
203# use the faster shell built-in if possible.
204test_lower () {
205  shhaslower=0
206  (eval 'typeset -l xuvar;xuvar=X;test z$xuvar = zx') 2>/dev/null
207  if [ $? -eq 0 ]; then
208    shhaslower=1
209    eval 'tolower () { lcvar=$1; typeset -l lcval; eval "lcval=\${$lcvar};$lcvar=\$lcval"; }'
210  else
211    eval 'tolower () { lcvar=$1; cmd="$lcvar=\`echo \${$lcvar} | tr \"[A-Z]\" \"[a-z]\"\`"; eval "$cmd"; }'
212  fi
213}
214
215testshcapability () {
216  test_echo
217  test_append
218  test_readraw
219  test_math
220  test_upper
221  test_lower
222}
223
224getshelltype () {
225  if [ "$1" != "" ]; then
226    trs=$1
227    shift
228  fi
229  gstecho=F
230  if [ "$1" = echo ]; then
231    gstecho=T
232  fi
233
234  baseshell=${_shell:-sh} # unknown or old
235  shell=${_shell:-sh}     # unknown or old
236  if [ "$trs" != "" ]; then
237    dispshell=`echo $trs | sed -e 's,.*/,,'`
238  else
239    dispshell=$shell
240  fi
241  ( eval 'echo ${.sh.version}' ) >/dev/null 2>&1
242  if [ $? -eq 0 ]; then
243    eval 'KSH_VERSION=${.sh.version}'
244  fi
245  if [ "$KSH_VERSION" != "" ]; then
246    shell=ksh
247    baseshell=ksh
248    shvers=$KSH_VERSION
249    case $KSH_VERSION in
250      *PD*)
251        shell=pdksh
252        ;;
253      *93*)
254        shell=ksh93
255        ;;
256      *88*)
257        shell=ksh88
258        ;;
259      *MIRBSD*)
260        shell=mksh
261        ;;
262    esac
263  elif [ "$BASH_VERSION" != "" ]; then
264    shvers=$BASH_VERSION
265    ver=`echo $BASH_VERSION | sed 's/\..*//'`
266    shell=bash${ver}
267    baseshell=bash
268  elif [ "$ZSH_VERSION" != "" ]; then
269    shvers=$ZSH_VERSION
270    shell=zsh
271    baseshell=zsh
272  elif [ "$POSH_VERSION" != "" ]; then
273    shvers=$POSH_VERSION
274    shell=posh
275    baseshell=posh
276  elif [ "$YASH_VERSION" != "" ]; then
277    shvers=$YASH_VERSION
278    shell=yash
279    baseshell=yash
280  elif [ "$OIL_VERSION" != "" ]; then
281    shvers=$OIL_VERSION
282    shell=osh
283    baseshell=osh
284  fi
285
286  if [ $dispshell = sh -a $dispshell != $shell ]; then
287    dispshell="$dispshell-$shell"
288  elif [ $dispshell = $baseshell ]; then
289    dispshell=$shell
290  fi
291  # can try --version, but don't really know the path
292  # of the shell running us; can't depend on $SHELL.
293  # and it only works for bash and some versions of ksh.
294  if [ $gstecho = T ]; then
295    echo $dispshell $shvers
296  fi
297}
298
299doshelltest () {
300  # force shell type.
301  if [ "$_MKCONFIG_SHELL" != "" ]; then
302    if [ "$SHELL" != "$_MKCONFIG_SHELL" ]; then
303      SHELL="$_MKCONFIG_SHELL"
304      export SHELL
305      loc=`pwd`
306      s=$1
307      shift
308      exec $SHELL $dstscript $s -d $loc $@
309    fi
310  fi
311
312  getshelltype  # for display of error below
313  chkshell "" $shell
314  if [ $? -ne 0 ]; then
315    echo "The shell in use ($dispshell) does not have the correct functionality:" >&2
316    echo $chkmsg >&2
317    echo "Please try another shell.
318_MKCONFIG_SHELL can be set to the path of another shell
319to override /bin/sh." >&2
320    exit 1
321  fi
322  testshcapability
323}
324
325locatecmd () {
326  lvar=$1
327  ltcmd=$2
328
329  getpaths
330
331  lcmd=""
332  for p in $_pthlist; do
333    if [ -x "$p/$ltcmd" ]; then
334      lcmd="$p/$ltcmd"
335      break
336    fi
337  done
338  eval $lvar=$lcmd
339}
340
341mkverscomp () {
342  v=$1
343
344  # put a 1 in front to avoid any octal constant issue.
345  echo $v | $awkcmd -F. '{ printf("1%03d%03d%03d\n", $1,$2,$3); }'
346}
347
348# rc = 0 : same version
349# rc = 1 : smaller version
350# rc = 2 : greater version
351versioncompare () {
352  v1=`mkverscomp $1`
353  v2=`mkverscomp $2`
354
355  rc=0
356  if [ $v1 -lt $v2 ]; then
357    rc=1
358  fi
359  if [ $v1 -gt $v2 ]; then
360    rc=2
361  fi
362  return $rc
363}
364
365# function to make sure the shell has
366# some basic capabilities w/o weirdness.
367chkshell () {
368  doecho=F
369  if [ "$1" = "echo" ]; then
370    doecho=T
371  fi
372  shellpath=$2
373
374  grc=0
375
376  case $shellpath in
377    *yash)
378      # reject older versions of yash
379      # I do not know in what version the quoting/backquoting issues
380      # got fixed.
381      locateawkcmd
382      versioncompare $YASH_VERSION 2.48
383      rc=$?
384      if [ $rc -eq 1 ]; then
385        chkmsg="${chkmsg}
386  older versions of yash are not supported"
387        grc=1
388      fi
389      ;;
390  esac
391
392  chkmsg=""
393  # test to make sure the set command works properly
394  # some shells output xyzzy=abc def
395  # some shells output xyzzy='abc def'
396  # some shells output xyzzy=$'abc def' (ok; handled in mkconfig.sh)
397  # yash does this correctly, but had quoting/backquote issues in older versions.
398  (
399    cmd='xyzzy="abc def"; val=`set | grep "^xyzzy"`; test "$val" = "xyzzy=abc def"'
400    eval $cmd 2>/dev/null
401    if [ $? -eq 0 ]; then
402      exit 0
403    fi
404    cmd="xyzzy=\"abc def\"; val=\`set | grep \"^xyzzy\"\`; test \"\$val\" = \"xyzzy='abc def'\" -o \"\$val\" = \"xyzzy=\\$'abc def'\""
405    eval $cmd 2>/dev/null
406    rc=$?
407    exit $rc
408  )
409  rc=$?
410  if [ $rc -ne 0 ]; then
411    grc=$rc
412    chkmsg="${chkmsg}
413  'set' output not x=a b or x='a b' or x=\$'a b'."
414  fi
415
416  # test to make sure the 'set -f' command is supported.
417  (
418    cmd='set -f'
419    eval $cmd 2>/dev/null
420    rc=$?
421    if [ $rc -eq 0 ]; then
422      exit 0
423    fi
424    exit $rc
425  )
426  rc=$?
427  if [ $rc -ne 0 ]; then
428    grc=$rc
429    chkmsg="${chkmsg}
430  'set -f' not supported"
431  fi
432
433  if [ $doecho = "T" ]; then
434    echo $chkmsg
435  fi
436
437  return $grc
438}
439
440getpaths () {
441  if [ "$_pthlist" != "" ]; then
442    return
443  fi
444
445  systype=`uname -s`
446  tpthlist=`echo $PATH | sed 's/:/ /g'`
447
448  # cygwin's /bin and /usr/bin are both mounted on same spot
449  case ${systype} in
450    CYGWIN*)
451      d=/bin
452      tpthlist=`echo $tpthlist | sed -e "s,^$d ,," -e "s, $d,,"`
453      ;;
454  esac
455
456  # remove symlinks
457  for d in $tpthlist; do
458    if [ ! -d $d ]; then
459      tpthlist=`echo $tpthlist | sed -e "s,^$d ,," -e "s, $d,,"`
460    else
461      if [ -h $d ]; then
462        tpthlist=`echo $tpthlist | sed -e "s,^$d ,," -e "s, $d,,"`
463        # make sure path symlink is pointing to is in the list
464        npath=`ls -ld $d | sed 's/.*-> //'`
465        tpthlist="$tpthlist $npath"
466      fi
467    fi
468  done
469
470  # remove dups
471  _pthlist=""
472  for d in $tpthlist; do
473    _pthlist="$_pthlist
474$d"
475  done
476  _pthlist=`echo $_pthlist | sort -u`
477}
478
479initifs () {
480  hasifs=0
481  if [ "$IFS" != "" ]; then
482    OIFS="$IFS"
483    hasifs=1
484  fi
485}
486
487setifs () {
488  # just newline for parsing include section
489  IFS="
490"
491}
492
493resetifs () {
494  if [ $hasifs -eq 1 ]; then
495    IFS="$OIFS"
496  else
497    unset IFS
498  fi
499}
500
501boolclean () {
502  nm=$1
503
504  dosubst $nm '(' ' ( ' ')' ' ) '
505  dosubst $nm ' not ' ' ! ' ' and ' ' -a ' ' or ' ' -o '
506  dosubst $nm '!' ' ! ' '&&' ' -a ' '||' ' -o '
507  dosubst $nm ' \+' ' ' '^ *' '' ' *$' ''
508}
509
510locateawkcmd () {
511  locatecmd awkcmd awk
512  locatecmd nawkcmd nawk
513  locatecmd gawkcmd gawk
514  locatecmd mawkcmd mawk
515  if [ "$nawkcmd" != "" ]; then
516    awkcmd=$nawkcmd
517  fi
518  if [ "$mawkcmd" != "" ]; then
519    awkcmd=$mawkcmd
520  fi
521  if [ "$gawkcmd" != "" ]; then
522    awkcmd=$gawkcmd
523  fi
524}
525
526locatepkgconfigcmd () {
527  locatecmd pkgconfigcmd pkg-config
528}
529
530