Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/shell


Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
Location:
branches/2.2.9/mindi-busybox/shell
Files:
444 added
16 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/shell/Config.in

    r1765 r2725  
     1# DO NOT EDIT. This file is generated from Config.src
    12#
    23# For a description of the syntax of this configuration file,
     
    67menu "Shells"
    78
    8 choice
    9     prompt "Choose your default shell"
    10     default FEATURE_SH_IS_NONE
    11     help
    12       Choose a shell. The ash shell is the most bash compatible
    13       and full featured one.
    14 
    15 config FEATURE_SH_IS_ASH
    16     select ASH
    17     bool "ash"
    18 
    19 config FEATURE_SH_IS_HUSH
    20     select HUSH
    21     bool "hush"
    22 
    23 config FEATURE_SH_IS_LASH
    24     select LASH
    25     bool "lash"
    26 
    27 config FEATURE_SH_IS_MSH
    28     select MSH
    29     bool "msh"
    30 
    31 config FEATURE_SH_IS_NONE
    32     bool "none"
    33 
    34 endchoice
    35 
    369config ASH
    3710    bool "ash"
    38     default n
    39     select TEST
     11    default y
     12    depends on !NOMMU
    4013    help
    4114      Tha 'ash' shell adds about 60k in the default configuration and is
    4215      the most complete and most pedantically correct shell included with
    43       busybox.  This shell is actually a derivative of the Debian 'dash'
     16      busybox. This shell is actually a derivative of the Debian 'dash'
    4417      shell (by Herbert Xu), which was created by porting the 'ash' shell
    4518      (written by Kenneth Almquist) from NetBSD.
    4619
    47 comment "Ash Shell Options"
    48     depends on ASH
     20config ASH_BASH_COMPAT
     21    bool "bash-compatible extensions"
     22    default y
     23    depends on ASH
     24    help
     25      Enable bash-compatible extensions.
    4926
    5027config ASH_JOB_CONTROL
     
    5532      Enable job control in the ash shell.
    5633
    57 config ASH_READ_NCHARS
    58     bool "'read -n N' and 'read -s' support"
    59     default n
    60     depends on ASH
    61     help
    62       'read -n N' will return a value after N characters have been read.
    63       'read -s' will read without echoing the user's input.
    64 
    65 config ASH_READ_TIMEOUT
    66     bool "'read -t S' support."
    67     default n
    68     depends on ASH
    69     help
    70       'read -t S' will return a value after S seconds have passed.
    71       This implementation will allow fractional seconds, expressed
    72       as a decimal fraction, e.g. 'read -t 2.5 foo'.
    73 
    7434config ASH_ALIAS
    7535    bool "alias support"
     
    7939      Enable alias support in the ash shell.
    8040
    81 config ASH_MATH_SUPPORT
    82     bool "Posix math support"
    83     default y
    84     depends on ASH
    85     help
    86       Enable math support in the ash shell.
    87 
    88 config ASH_MATH_SUPPORT_64
    89     bool "Extend Posix math support to 64 bit"
    90     default n
    91     depends on ASH_MATH_SUPPORT
    92     help
    93       Enable 64-bit math support in the ash shell.  This will make
    94       the shell slightly larger, but will allow computation with very
    95       large numbers.
    96 
    9741config ASH_GETOPTS
    9842    bool "Builtin getopt to parse positional parameters"
    99     default n
     43    default y
    10044    depends on ASH
    10145    help
     
    10549    bool "Builtin version of 'echo'"
    10650    default y
    107     select ECHO
    10851    depends on ASH
    10952    help
    11053      Enable support for echo, builtin to ash.
     54
     55config ASH_BUILTIN_PRINTF
     56    bool "Builtin version of 'printf'"
     57    default y
     58    depends on ASH
     59    help
     60      Enable support for printf, builtin to ash.
    11161
    11262config ASH_BUILTIN_TEST
    11363    bool "Builtin version of 'test'"
    11464    default y
    115     select TEST
    11665    depends on ASH
    11766    help
     
    12069config ASH_CMDCMD
    12170    bool "'command' command to override shell builtins"
    122     default n
     71    default y
    12372    depends on ASH
    12473    help
     
    12978config ASH_MAIL
    13079    bool "Check for new mail on interactive shells"
    131     default y
     80    default n
    13281    depends on ASH
    13382    help
     
    14291
    14392config ASH_RANDOM_SUPPORT
    144     bool "Pseudorandom generator and variable $RANDOM"
    145     default n
     93    bool "Pseudorandom generator and $RANDOM variable"
     94    default y
    14695    depends on ASH
    14796    help
     
    14998      Each read of "$RANDOM" will generate a new pseudorandom value.
    15099      You can reset the generator by using a specified start value.
    151       After "unset RANDOM" then generator will switch off and this
     100      After "unset RANDOM" the generator will switch off and this
    152101      variable will no longer have special treatment.
    153102
    154103config ASH_EXPAND_PRMT
    155104    bool "Expand prompt string"
    156     default n
    157     depends on ASH
    158     help
    159       "PS#" may be contain volatile content, such as backquote commands.
     105    default y
     106    depends on ASH
     107    help
     108      "PS#" may contain volatile content, such as backquote commands.
    160109      This option recreates the prompt string from the environment
    161110      variable each time it is displayed.
    162111
     112config CTTYHACK
     113    bool "cttyhack"
     114    default y
     115    help
     116      One common problem reported on the mailing list is "can't access tty;
     117      job control turned off" error message which typically appears when
     118      one tries to use shell with stdin/stdout opened to /dev/console.
     119      This device is special - it cannot be a controlling tty.
     120
     121      Proper solution is to use correct device instead of /dev/console.
     122
     123      cttyhack provides "quick and dirty" solution to this problem.
     124      It analyzes stdin with various ioctls, trying to determine whether
     125      it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line).
     126      If it detects one, it closes stdin/out/err and reopens that device.
     127      Then it executes given program. Opening the device will make
     128      that device a controlling tty. This may require cttyhack
     129      to be a session leader.
     130
     131      Example for /etc/inittab (for busybox init):
     132
     133      ::respawn:/bin/cttyhack /bin/sh
     134
     135      Starting an interactive shell from boot shell script:
     136
     137      setsid cttyhack sh
     138
     139      Giving controlling tty to shell running with PID 1:
     140
     141      # exec cttyhack sh
     142
     143      Without cttyhack, you need to know exact tty name,
     144      and do something like this:
     145
     146      # exec setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1'
     147
    163148config HUSH
    164149    bool "hush"
    165     default n
    166     select TRUE
    167     select FALSE
    168     select TEST
    169     help
    170       hush is a very small shell (just 18k) and it has fairly complete
    171       Bourne shell grammar.  It even handles all the normal flow control
    172       options such as if/then/elif/else/fi, for/in/do/done, while loops,
    173       etc.
    174 
    175       It does not handle case/esac, select, function, here documents ( <<
    176       word ), arithmetic expansion, aliases, brace expansion, tilde
    177       expansion, &> and >& redirection of stdout+stderr, etc.
     150    default y
     151    help
     152      hush is a small shell (25k). It handles the normal flow control
     153      constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
     154      case/esac. Redirections, here documents, $((arithmetic))
     155      and functions are supported.
     156
     157      It will compile and work on no-mmu systems.
     158
     159      It does not handle select, aliases, tilde expansion,
     160      &>file and >&file redirection of stdout+stderr.
     161
     162config HUSH_BASH_COMPAT
     163    bool "bash-compatible extensions"
     164    default y
     165    depends on HUSH
     166    help
     167      Enable bash-compatible extensions.
     168
     169config HUSH_BRACE_EXPANSION
     170    bool "Brace expansion"
     171    default y
     172    depends on HUSH_BASH_COMPAT
     173    help
     174      Enable {abc,def} extension.
    178175
    179176config HUSH_HELP
    180177    bool "help builtin"
    181     default n
     178    default y
    182179    depends on HUSH
    183180    help
     
    191188      Enable interactive mode (prompt and command editing).
    192189      Without this, hush simply reads and executes commands
    193       from stdin just like a shell script from the file.
     190      from stdin just like a shell script from a file.
    194191      No prompt, no PS1/PS2 magic shell variables.
     192
     193config HUSH_SAVEHISTORY
     194    bool "Save command history to .hush_history"
     195    default y
     196    depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
     197    help
     198      Enable history saving in hush.
    195199
    196200config HUSH_JOB
    197201    bool "Job control"
    198     default n
     202    default y
    199203    depends on HUSH_INTERACTIVE
    200204    help
     
    207211config HUSH_TICK
    208212    bool "Process substitution"
    209     default n
     213    default y
    210214    depends on HUSH
    211215    help
     
    214218config HUSH_IF
    215219    bool "Support if/then/elif/else/fi"
    216     default n
     220    default y
    217221    depends on HUSH
    218222    help
     
    221225config HUSH_LOOPS
    222226    bool "Support for, while and until loops"
     227    default y
     228    depends on HUSH
     229    help
     230      Enable for, while and until loops in hush.
     231
     232config HUSH_CASE
     233    bool "Support case ... esac statement"
     234    default y
     235    depends on HUSH
     236    help
     237      Enable case ... esac statement in hush. +400 bytes.
     238
     239config HUSH_FUNCTIONS
     240    bool "Support funcname() { commands; } syntax"
     241    default y
     242    depends on HUSH
     243    help
     244      Enable support for shell functions in hush. +800 bytes.
     245
     246config HUSH_LOCAL
     247    bool "Support local builtin"
     248    default y
     249    depends on HUSH_FUNCTIONS
     250    help
     251      Enable support for local variables in functions.
     252
     253config HUSH_RANDOM_SUPPORT
     254    bool "Pseudorandom generator and $RANDOM variable"
     255    default y
     256    depends on HUSH
     257    help
     258      Enable pseudorandom generator and dynamic variable "$RANDOM".
     259      Each read of "$RANDOM" will generate a new pseudorandom value.
     260
     261config HUSH_EXPORT_N
     262    bool "Support 'export -n' option"
     263    default y
     264    depends on HUSH
     265    help
     266      export -n unexports variables. It is a bash extension.
     267
     268config HUSH_MODE_X
     269    bool "Support 'hush -x' option and 'set -x' command"
     270    default y
     271    depends on HUSH
     272    help
     273      This instructs hush to print commands before execution.
     274      Adds ~300 bytes.
     275
     276config MSH
     277    bool "msh (deprecated: aliased to hush)"
    223278    default n
    224     depends on HUSH
    225     help
    226       Enable for, while and until loops in hush.
    227 
    228 config LASH
    229     bool "lash"
    230     default n
    231     select TRUE
    232     select FALSE
    233     select TEST
    234     help
    235       lash is the very smallest shell (adds just 10k) and it is quite
    236       usable as a command prompt, but it is not suitable for any but the
    237       most trivial scripting (such as an initrd that calls insmod a few
    238       times) since it does not understand any Bourne shell grammar.  It
    239       does handle pipes, redirects, and job control though.  Adding in
    240       command editing makes it a very nice lightweight command prompt.
    241 
    242 
    243 config MSH
    244     bool "msh"
    245     default n
    246     select TRUE
    247     select FALSE
    248     select TEST
    249     help
    250       The minix shell (adds just 30k) is quite complete and handles things
    251       like for/do/done, case/esac and all the things you expect a Bourne
    252       shell to do.  It is not always pedantically correct about Bourne
    253       shell grammar (try running the shell testscript "tests/sh.testcases"
    254       on it and compare vs bash) but for most things it works quite well.
    255       It also uses only vfork, so it can be used on uClinux systems.
    256 
    257 comment "Bourne Shell Options"
    258     depends on MSH || LASH || HUSH || ASH
     279    select HUSH
     280    help
     281      msh is deprecated and will be removed, please migrate to hush.
     282
     283
     284choice
     285    prompt "Choose which shell is aliased to 'sh' name"
     286    default FEATURE_SH_IS_ASH
     287    help
     288      Choose which shell you want to be executed by 'sh' alias.
     289      The ash shell is the most bash compatible and full featured one.
     290
     291# note: cannot use "select ASH" here, it breaks "make allnoconfig"
     292config FEATURE_SH_IS_ASH
     293    depends on ASH
     294    bool "ash"
     295    depends on !NOMMU
     296
     297config FEATURE_SH_IS_HUSH
     298    depends on HUSH
     299    bool "hush"
     300
     301config FEATURE_SH_IS_NONE
     302    bool "none"
     303
     304endchoice
     305
     306choice
     307    prompt "Choose which shell is aliased to 'bash' name"
     308    default FEATURE_BASH_IS_NONE
     309    help
     310      Choose which shell you want to be executed by 'bash' alias.
     311      The ash shell is the most bash compatible and full featured one.
     312
     313      Note that selecting this option does not switch on any bash
     314      compatibility code. It merely makes it possible to install
     315      /bin/bash (sym)link and run scripts which start with
     316      #!/bin/bash line.
     317
     318      Many systems use it in scripts which use bash-specific features,
     319      even simple ones like $RANDOM. Without this option, busybox
     320      can't be used for running them because it won't recongnize
     321      "bash" as a supported applet name.
     322
     323config FEATURE_BASH_IS_ASH
     324    depends on ASH
     325    bool "ash"
     326    depends on !NOMMU
     327
     328config FEATURE_BASH_IS_HUSH
     329    depends on HUSH
     330    bool "hush"
     331
     332config FEATURE_BASH_IS_NONE
     333    bool "none"
     334
     335endchoice
     336
     337
     338config SH_MATH_SUPPORT
     339    bool "POSIX math support"
     340    default y
     341    depends on ASH || HUSH
     342    help
     343      Enable math support in the shell via $((...)) syntax.
     344
     345config SH_MATH_SUPPORT_64
     346    bool "Extend POSIX math support to 64 bit"
     347    default y
     348    depends on SH_MATH_SUPPORT
     349    help
     350      Enable 64-bit math support in the shell. This will make the shell
     351      slightly larger, but will allow computation with very large numbers.
     352      This is not in POSIX, so do not rely on this in portable code.
    259353
    260354config FEATURE_SH_EXTRA_QUIET
    261355    bool "Hide message on interactive shell startup"
    262     default n
    263     depends on MSH || LASH || HUSH || ASH
     356    default y
     357    depends on HUSH || ASH
    264358    help
    265359      Remove the busybox introduction when starting a shell.
     
    268362    bool "Standalone shell"
    269363    default n
    270     depends on (MSH || LASH || HUSH || ASH) && FEATURE_PREFER_APPLETS
     364    depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS
    271365    help
    272366      This option causes busybox shells to use busybox applets
    273       in preference to executables in the PATH whenever possible.  For
     367      in preference to executables in the PATH whenever possible. For
    274368      example, entering the command 'ifconfig' into the shell would cause
    275       busybox to use the ifconfig busybox applet.  Specifying the fully
     369      busybox to use the ifconfig busybox applet. Specifying the fully
    276370      qualified executable name, such as '/sbin/ifconfig' will still
    277       execute the /sbin/ifconfig executable on the filesystem.  This option
     371      execute the /sbin/ifconfig executable on the filesystem. This option
    278372      is generally used when creating a statically linked version of busybox
    279373      for use as a rescue shell, in the event that you screw up your system.
     
    289383# untrue?
    290384#     Note that this will *also* cause applets to take precedence
    291 #     over shell builtins of the same name.  So turning this on will
     385#     over shell builtins of the same name. So turning this on will
    292386#     eliminate any performance gained by turning on the builtin "echo"
    293387#     and "test" commands in ash.
    294388# untrue?
    295389#     Note that when using this option, the shell will attempt to directly
    296 #     run '/bin/busybox'.  If you do not have the busybox binary sitting in
     390#     run '/bin/busybox'. If you do not have the busybox binary sitting in
    297391#     that exact location with that exact name, this option will not work at
    298392#     all.
    299393
    300 config CTTYHACK
    301     bool "cttyhack"
     394config FEATURE_SH_NOFORK
     395    bool "Run 'nofork' applets directly"
    302396    default n
    303     help
    304       One common problem reported on the mailing list is "can't access tty;
    305       job control turned off" error message which typically appears when
    306       one tries to use shell with stdin/stdout opened to /dev/console.
    307       This device is special - it cannot be a controlling tty.
    308 
    309       Proper solution is to use correct device instead of /dev/console.
    310 
    311       cttyhack provides "quick and dirty" solution to this problem.
    312       It analyzes stdin with various ioctls, trying to determine whether
    313       it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line).
    314       If it detects one, it closes stdin/out/err and reopens that device.
    315       Then it executes given program. Usage example for /etc/inittab
    316       (for busybox init):
    317 
    318       ::respawn:/bin/cttyhack /bin/sh
     397    depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS
     398    help
     399      This option causes busybox shells [currently only ash]
     400      to not execute typical fork/exec/wait sequence, but call <applet>_main
     401      directly, if possible. (Sometimes it is not possible: for example,
     402      this is not possible in pipes).
     403
     404      This will be done only for some applets (those which are marked
     405      NOFORK in include/applets.h).
     406
     407      This may significantly speed up some shell scripts.
     408
     409      This feature is relatively new. Use with care.
    319410
    320411endmenu
  • branches/2.2.9/mindi-busybox/shell/Kbuild

    r1765 r2725  
     1# DO NOT EDIT. This file is generated from Kbuild.src
    12# Makefile for busybox
    23#
    34# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
    45#
    5 # Licensed under the GPL v2, see the file LICENSE in this tarball.
     6# Licensed under GPLv2, see file LICENSE in this source tree.
    67
    78lib-y:=
    8 lib-$(CONFIG_ASH)  += ash.o
    9 lib-$(CONFIG_HUSH) += hush.o
    10 lib-$(CONFIG_LASH) += lash.o
    11 lib-$(CONFIG_MSH)  += msh.o
    129
     10lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
     11lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
    1312lib-$(CONFIG_CTTYHACK) += cttyhack.o
     13lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
     14lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
     15
     16lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
  • branches/2.2.9/mindi-busybox/shell/README

    r1765 r2725  
    1 Various bits of what is known about busybox shells, in no particular order.
     1http://www.opengroup.org/onlinepubs/9699919799/
     2Open Group Base Specifications Issue 7
    23
    3 2007-06-13
    4 hush: exec <"$1" doesn't do parameter subst
    54
    6 2007-05-24
    7 hush: environment-related memory leak plugged, with net code size
    8 decrease.
     5http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap01.html
     6Shell & Utilities
    97
    10 2007-05-24
    11 hush: '( echo ${name )' will show syntax error message, but prompt
    12 doesn't return (need to press <enter>). Pressing Ctrl-C, <enter>,
    13 '( echo ${name )' again, Ctrl-C segfaults.
     8It says that any of the standard utilities may be implemented
     9as a regular shell built-in. It gives a list of utilities which
     10are usually implemented that way (and some of them can only
     11be implemented as built-ins, like "alias"):
    1412
    15 2007-05-21
    16 hush: environment cannot be handled by libc routines as they are leaky
    17 (by API design and thus unfixable): hush will leak memory in this script,
    18 bash does not:
    19 pid=$$
    20 while true; do
    21     unset t;
    22     t=111111111111111111111111111111111111111111111111111111111111111111111111
    23     export t
    24     ps -o vsz,pid,comm | grep " $pid "
    25 done
    26 The fix is to not use setenv/putenv/unsetenv but manipulate env ourself. TODO.
    27 hush: meanwhile, first three command subst bugs mentioned below are fixed. :)
     13alias
     14bg
     15cd
     16command
     17false
     18fc
     19fg
     20getopts
     21jobs
     22kill
     23newgrp
     24pwd
     25read
     26true
     27umask
     28unalias
     29wait
    2830
    29 2007-05-06
    30 hush: more bugs spotted. Comparison with bash:
    31 bash-3.2# echo "TEST`date;echo;echo`BEST"
    32 TESTSun May  6 09:21:05 CEST 2007BEST         [we dont strip eols]
    33 bash-3.2# echo "TEST`echo '$(echo ZZ)'`BEST"
    34 TEST$(echo ZZ)BEST                            [we execute inner echo]
    35 bash-3.2# echo "TEST`echo "'"`BEST"
    36 TEST'BEST                                     [we totally mess up this one]
    37 bash-3.2# echo `sleep 5`
    38 [Ctrl-C should work, Ctrl-Z should do nothing][we totally mess up this one]
    39 bash-3.2# if true; then
    40 > [Ctrl-C]
    41 bash-3.2#                                     [we re-issue "> "]
    42 bash-3.2# if echo `sleep 5`; then
    43 > true; fi                                    [we execute sleep before "> "]
    4431
    45 2007-05-04
    46 hush: made ctrl-Z/C work correctly for "while true; do true; done"
    47 (namely, it backgrounds/interrupts entire "while")
     32http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
     33Shell Command Language
    4834
    49 2007-05-03
    50 hush: new bug spotted: Ctrl-C on "while true; do true; done" doesn't
    51 work right:
    52 # while true; do true; done
    53 [1] 0 true <-- pressing Ctrl-C several times...
    54 [2] 0 true
    55 [3] 0 true
    56 Segmentation fault
     35It says that shell must implement special built-ins. Special built-ins
     36differ from regular ones by the fact that variable assignments
     37done on special builtin are *PRESERVED*. That is,
    5738
    58 2007-05-03
    59 hush: update on "sleep 1 | exit 3; echo $?" bug.
    60 parse_stream_outer() repeatedly calls parse_stream().
    61 parse_stream() is now fixed to stop on ';' in this example,
    62 fixing it (parse_stream_outer() will call parse_stream() 1st time,
    63 execute the parse tree, call parse_stream() 2nd time and execute the tree).
    64 But it's not the end of story.
    65 In more complex situations we _must_ parse way farther before executing.
    66 Example #2: "{ sleep 1 | exit 3; echo $?; ...few_lines... } >file".
    67 Because of redirection, we cannot execute 1st pipe before we parse it all.
    68 We probably need to learn to store $var expressions in parse tree.
    69 Debug printing of parse tree would be nice too.
     39VAR=VAL special_builtin; echo $VAR
    7040
    71 2007-04-28
    72 hush: Ctrl-C and Ctrl-Z for single NOFORK commands are working.
    73 Memory and other resource leaks (opendir) are not addressed
    74 (testcase is "rm -i" interrupted by ctrl-c).
     41should print VAL.
    7542
    76 2007-04-21
    77 hush: "sleep 5 | sleep 6" + Ctrl-Z + fg seems to work.
    78 "rm -i" + Ctrl-C, "sleep 5" + Ctrl-Z still doesn't work
    79 for SH_STANDALONE case :(
     43(Another distinction is that an error in special built-in should
     44abort the shell, but this is not such a critical difference,
     45and moreover, at least bash's "set" does not follow this rule,
     46which is even codified in autoconf configure logic now...)
    8047
    81 2007-04-21
    82 hush: fixed non-backgrounding of "sleep 1 &" and totally broken
    83 "sleep 1 | sleep 2 &". Noticed a bug where successive jobs
    84 get numbers 1,2,3 even when job #1 has exited before job# 2 is started.
    85 (bash reuses #1 in this case)
     48List of special builtins:
    8649
    87 2007-04-21
    88 hush: "sleep 1 | exit 3; echo $?" prints 0 because $? is substituted
    89 _before_ pipe gets executed!! run_list_real() already has "pipe;echo"
    90 parsed and handed to it for execution, so it sees "pipe"; "echo 0".
     50. file
     51: [argument...]
     52break [n]
     53continue [n]
     54eval [argument...]
     55exec [command [argument...]]
     56exit [n]
     57export name[=word]...
     58export -p
     59readonly name[=word]...
     60readonly -p
     61return [n]
     62set [-abCefhmnuvx] [-o option] [argument...]
     63set [+abCefhmnuvx] [+o option] [argument...]
     64set -- [argument...]
     65set -o
     66set +o
     67shift [n]
     68times
     69trap n [condition...]
     70trap [action condition...]
     71unset [-fv] name...
    9172
    92 2007-04-21
    93 hush: removed setsid() and made job control sort-of-sometimes-work.
    94 Ctrl-C in "rm -i" works now except for SH_STANDALONE case.
    95 "sleep 1 | exit 3" + "echo $?" works, "sleep 1 | exit 3; echo $?"
    96 shows exitcode 0 (should be 3). "sleep 1 | sleep 2 &" fails horribly.
     73In practice, no one uses this obscure feature - none of these builtins
     74gives any special reasons to play such dirty tricks.
    9775
    98 2007-04-14
    99 lash, hush: both do setsid() and as a result don't have ctty!
    100 Ctrl-C doesn't work for any child (try rm -i), etc...
    101 lash: bare ">file" doesn't create a file (hush works)
     76However. This section also says that *function invocation* should act
     77similar to special built-in. That is, variable assignments
     78done on function invocation should be preserved after function invocation.
     79
     80This is significant: it is not unthinkable to want to run a function
     81with some variables set to special values. But because of the above,
     82it does not work: variable will "leak" out of the function.
  • branches/2.2.9/mindi-busybox/shell/ash.c

    r1772 r2725  
    22/*
    33 * ash shell port for busybox
     4 *
     5 * This code is derived from software contributed to Berkeley by
     6 * Kenneth Almquist.
     7 *
     8 * Original BSD copyright notice is retained at the end of this file.
    49 *
    510 * Copyright (c) 1989, 1991, 1993, 1994
     
    914 * was re-ported from NetBSD and debianized.
    1015 *
    11  *
    12  * This code is derived from software contributed to Berkeley by
    13  * Kenneth Almquist.
    14  *
    15  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
    16  *
    17  * Original BSD copyright notice is retained at the end of this file.
    18  */
    19 
    20 /*
    21  * rewrite arith.y to micro stack based cryptic algorithm by
    22  * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
    23  *
    24  * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
    25  * dynamic variables.
    26  *
    27  * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
    28  * used in busybox and size optimizations,
    29  * rewrote arith (see notes to this), added locale support,
    30  * rewrote dynamic variables.
    31  *
    32  */
    33 
    34 /*
    35  * The follow should be set to reflect the type of system you have:
     16 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
     17 */
     18
     19/*
     20 * The following should be set to reflect the type of system you have:
    3621 *      JOBS -> 1 if you have Berkeley job control, 0 otherwise.
    3722 *      define SYSV if you are running under System V.
     
    4328 */
    4429#define DEBUG 0
    45 #define IFS_BROKEN
     30/* Tweak debug output verbosity here */
     31#define DEBUG_TIME 0
     32#define DEBUG_PID 1
     33#define DEBUG_SIG 1
     34
    4635#define PROFILE 0
    47 #if ENABLE_ASH_JOB_CONTROL
    48 #define JOBS 1
    49 #else
    50 #define JOBS 0
    51 #endif
    52 
    53 #if DEBUG
    54 #define _GNU_SOURCE
    55 #endif
    56 #include "busybox.h" /* for struct bb_applet */
     36
     37#define JOBS ENABLE_ASH_JOB_CONTROL
     38
     39#include "busybox.h" /* for applet_names */
    5740#include <paths.h>
    5841#include <setjmp.h>
    5942#include <fnmatch.h>
    60 #if JOBS || ENABLE_ASH_READ_NCHARS
    61 #include <termios.h>
    62 #endif
    63 extern char **environ;
    64 
    65 #if defined(__uClinux__)
    66 #error "Do not even bother, ash will not run on uClinux"
    67 #endif
    68 
    69 
    70 /* ============ Misc helpers */
    71 
    72 #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
    73 
    74 /* C99 say: "char" declaration may be signed or unsigned default */
    75 #define signed_char2int(sc) ((int)((signed char)sc))
     43#include <sys/times.h>
     44
     45#include "shell_common.h"
     46#if ENABLE_SH_MATH_SUPPORT
     47# include "math.h"
     48#endif
     49#if ENABLE_ASH_RANDOM_SUPPORT
     50# include "random.h"
     51#else
     52# define CLEAR_RANDOM_T(rnd) ((void)0)
     53#endif
     54
     55#include "NUM_APPLETS.h"
     56#if NUM_APPLETS == 1
     57/* STANDALONE does not make sense, and won't compile */
     58# undef CONFIG_FEATURE_SH_STANDALONE
     59# undef ENABLE_FEATURE_SH_STANDALONE
     60# undef IF_FEATURE_SH_STANDALONE
     61# undef IF_NOT_FEATURE_SH_STANDALONE
     62# define ENABLE_FEATURE_SH_STANDALONE 0
     63# define IF_FEATURE_SH_STANDALONE(...)
     64# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
     65#endif
     66
     67#ifndef PIPE_BUF
     68# define PIPE_BUF 4096           /* amount of buffering in a pipe */
     69#endif
     70
     71#if !BB_MMU
     72# error "Do not even bother, ash will not run on NOMMU machine"
     73#endif
     74
     75//applet:IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP))
     76//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh))
     77//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash))
     78
     79//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
     80//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
     81
     82//config:config ASH
     83//config:   bool "ash"
     84//config:   default y
     85//config:   depends on !NOMMU
     86//config:   help
     87//config:     Tha 'ash' shell adds about 60k in the default configuration and is
     88//config:     the most complete and most pedantically correct shell included with
     89//config:     busybox. This shell is actually a derivative of the Debian 'dash'
     90//config:     shell (by Herbert Xu), which was created by porting the 'ash' shell
     91//config:     (written by Kenneth Almquist) from NetBSD.
     92//config:
     93//config:config ASH_BASH_COMPAT
     94//config:   bool "bash-compatible extensions"
     95//config:   default y
     96//config:   depends on ASH
     97//config:   help
     98//config:     Enable bash-compatible extensions.
     99//config:
     100//config:config ASH_JOB_CONTROL
     101//config:   bool "Job control"
     102//config:   default y
     103//config:   depends on ASH
     104//config:   help
     105//config:     Enable job control in the ash shell.
     106//config:
     107//config:config ASH_ALIAS
     108//config:   bool "alias support"
     109//config:   default y
     110//config:   depends on ASH
     111//config:   help
     112//config:     Enable alias support in the ash shell.
     113//config:
     114//config:config ASH_GETOPTS
     115//config:   bool "Builtin getopt to parse positional parameters"
     116//config:   default y
     117//config:   depends on ASH
     118//config:   help
     119//config:     Enable getopts builtin in the ash shell.
     120//config:
     121//config:config ASH_BUILTIN_ECHO
     122//config:   bool "Builtin version of 'echo'"
     123//config:   default y
     124//config:   depends on ASH
     125//config:   help
     126//config:     Enable support for echo, builtin to ash.
     127//config:
     128//config:config ASH_BUILTIN_PRINTF
     129//config:   bool "Builtin version of 'printf'"
     130//config:   default y
     131//config:   depends on ASH
     132//config:   help
     133//config:     Enable support for printf, builtin to ash.
     134//config:
     135//config:config ASH_BUILTIN_TEST
     136//config:   bool "Builtin version of 'test'"
     137//config:   default y
     138//config:   depends on ASH
     139//config:   help
     140//config:     Enable support for test, builtin to ash.
     141//config:
     142//config:config ASH_CMDCMD
     143//config:   bool "'command' command to override shell builtins"
     144//config:   default y
     145//config:   depends on ASH
     146//config:   help
     147//config:     Enable support for the ash 'command' builtin, which allows
     148//config:     you to run the specified command with the specified arguments,
     149//config:     even when there is an ash builtin command with the same name.
     150//config:
     151//config:config ASH_MAIL
     152//config:   bool "Check for new mail on interactive shells"
     153//config:   default n
     154//config:   depends on ASH
     155//config:   help
     156//config:     Enable "check for new mail" in the ash shell.
     157//config:
     158//config:config ASH_OPTIMIZE_FOR_SIZE
     159//config:   bool "Optimize for size instead of speed"
     160//config:   default y
     161//config:   depends on ASH
     162//config:   help
     163//config:     Compile ash for reduced size at the price of speed.
     164//config:
     165//config:config ASH_RANDOM_SUPPORT
     166//config:   bool "Pseudorandom generator and $RANDOM variable"
     167//config:   default y
     168//config:   depends on ASH
     169//config:   help
     170//config:     Enable pseudorandom generator and dynamic variable "$RANDOM".
     171//config:     Each read of "$RANDOM" will generate a new pseudorandom value.
     172//config:     You can reset the generator by using a specified start value.
     173//config:     After "unset RANDOM" the generator will switch off and this
     174//config:     variable will no longer have special treatment.
     175//config:
     176//config:config ASH_EXPAND_PRMT
     177//config:   bool "Expand prompt string"
     178//config:   default y
     179//config:   depends on ASH
     180//config:   help
     181//config:     "PS#" may contain volatile content, such as backquote commands.
     182//config:     This option recreates the prompt string from the environment
     183//config:     variable each time it is displayed.
     184//config:
     185
     186//usage:#define ash_trivial_usage NOUSAGE_STR
     187//usage:#define ash_full_usage ""
     188//usage:#define sh_trivial_usage NOUSAGE_STR
     189//usage:#define sh_full_usage ""
     190//usage:#define bash_trivial_usage NOUSAGE_STR
     191//usage:#define bash_full_usage ""
     192
     193
     194/* ============ Hash table sizes. Configurable. */
     195
     196#define VTABSIZE 39
     197#define ATABSIZE 39
     198#define CMDTABLESIZE 31         /* should be prime */
    76199
    77200
     
    93216    "u"   "nounset",
    94217    "\0"  "vi"
     218#if ENABLE_ASH_BASH_COMPAT
     219    ,"\0"  "pipefail"
     220#endif
    95221#if DEBUG
    96222    ,"\0"  "nolog"
     
    99225};
    100226
    101 #define optletters(n) optletters_optnames[(n)][0]
    102 #define optnames(n) (&optletters_optnames[(n)][1])
     227#define optletters(n)  optletters_optnames[n][0]
     228#define optnames(n)   (optletters_optnames[n] + 1)
    103229
    104230enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
    105231
    106 static char optlist[NOPTS] ALIGN1;
    107 
     232
     233/* ============ Misc data */
     234
     235#define msg_illnum "Illegal number: %s"
     236
     237/*
     238 * We enclose jmp_buf in a structure so that we can declare pointers to
     239 * jump locations.  The global variable handler contains the location to
     240 * jump to when an exception occurs, and the global variable exception_type
     241 * contains a code identifying the exception.  To implement nested
     242 * exception handlers, the user should save the value of handler on entry
     243 * to an inner scope, set handler to point to a jmploc structure for the
     244 * inner scope, and restore handler on exit from the scope.
     245 */
     246struct jmploc {
     247    jmp_buf loc;
     248};
     249
     250struct globals_misc {
     251    /* pid of main shell */
     252    int rootpid;
     253    /* shell level: 0 for the main shell, 1 for its children, and so on */
     254    int shlvl;
     255#define rootshell (!shlvl)
     256    char *minusc;  /* argument to -c option */
     257
     258    char *curdir; // = nullstr;     /* current working directory */
     259    char *physdir; // = nullstr;    /* physical working directory */
     260
     261    char *arg0; /* value of $0 */
     262
     263    struct jmploc *exception_handler;
     264
     265    volatile int suppress_int; /* counter */
     266    volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
     267    /* last pending signal */
     268    volatile /*sig_atomic_t*/ smallint pending_sig;
     269    smallint exception_type; /* kind of exception (0..5) */
     270    /* exceptions */
     271#define EXINT 0         /* SIGINT received */
     272#define EXERROR 1       /* a generic error */
     273#define EXSHELLPROC 2   /* execute a shell procedure */
     274#define EXEXEC 3        /* command execution failed */
     275#define EXEXIT 4        /* exit the shell */
     276#define EXSIG 5         /* trapped signal in wait(1) */
     277
     278    smallint isloginsh;
     279    char nullstr[1];        /* zero length string */
     280
     281    char optlist[NOPTS];
    108282#define eflag optlist[0]
    109283#define fflag optlist[1]
     
    120294#define uflag optlist[12]
    121295#define viflag optlist[13]
     296#if ENABLE_ASH_BASH_COMPAT
     297# define pipefail optlist[14]
     298#else
     299# define pipefail 0
     300#endif
    122301#if DEBUG
    123 #define nolog optlist[14]
    124 #define debug optlist[15]
    125 #endif
    126 
    127 
    128 /* ============ Misc data */
    129 
    130 static char nullstr[1] ALIGN1;  /* zero length string */
    131 static const char homestr[] ALIGN1 = "HOME";
    132 static const char snlfmt[] ALIGN1 = "%s\n";
    133 static const char illnum[] ALIGN1 = "Illegal number: %s";
    134 
    135 static char *minusc;  /* argument to -c option */
    136 
    137 /* pid of main shell */
    138 static int rootpid;
    139 /* shell level: 0 for the main shell, 1 for its children, and so on */
    140 static int shlvl;
    141 #define rootshell (!shlvl)
    142 /* trap handler commands */
    143 static char *trap[NSIG];
    144 static smallint isloginsh;
    145 /* current value of signal */
    146 static char sigmode[NSIG - 1];
    147 /* indicates specified signal received */
    148 static char gotsig[NSIG - 1];
    149 static char *arg0; /* value of $0 */
     302# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
     303# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
     304#endif
     305
     306    /* trap handler commands */
     307    /*
     308     * Sigmode records the current value of the signal handlers for the various
     309     * modes.  A value of zero means that the current handler is not known.
     310     * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
     311     */
     312    char sigmode[NSIG - 1];
     313#define S_DFL      1            /* default signal handling (SIG_DFL) */
     314#define S_CATCH    2            /* signal is caught */
     315#define S_IGN      3            /* signal is ignored (SIG_IGN) */
     316#define S_HARD_IGN 4            /* signal is ignored permenantly */
     317
     318    /* indicates specified signal received */
     319    uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
     320    uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
     321    char *trap[NSIG];
     322    char **trap_ptr;        /* used only by "trap hack" */
     323
     324    /* Rarely referenced stuff */
     325#if ENABLE_ASH_RANDOM_SUPPORT
     326    random_t random_gen;
     327#endif
     328    pid_t backgndpid;        /* pid of last background process */
     329    smallint job_warning;    /* user was warned about stopped jobs (can be 2, 1 or 0). */
     330};
     331extern struct globals_misc *const ash_ptr_to_globals_misc;
     332#define G_misc (*ash_ptr_to_globals_misc)
     333#define rootpid     (G_misc.rootpid    )
     334#define shlvl       (G_misc.shlvl      )
     335#define minusc      (G_misc.minusc     )
     336#define curdir      (G_misc.curdir     )
     337#define physdir     (G_misc.physdir    )
     338#define arg0        (G_misc.arg0       )
     339#define exception_handler (G_misc.exception_handler)
     340#define exception_type    (G_misc.exception_type   )
     341#define suppress_int      (G_misc.suppress_int     )
     342#define pending_int       (G_misc.pending_int      )
     343#define pending_sig       (G_misc.pending_sig      )
     344#define isloginsh   (G_misc.isloginsh  )
     345#define nullstr     (G_misc.nullstr    )
     346#define optlist     (G_misc.optlist    )
     347#define sigmode     (G_misc.sigmode    )
     348#define gotsig      (G_misc.gotsig     )
     349#define may_have_traps    (G_misc.may_have_traps   )
     350#define trap        (G_misc.trap       )
     351#define trap_ptr    (G_misc.trap_ptr   )
     352#define random_gen  (G_misc.random_gen )
     353#define backgndpid  (G_misc.backgndpid )
     354#define job_warning (G_misc.job_warning)
     355#define INIT_G_misc() do { \
     356    (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
     357    barrier(); \
     358    curdir = nullstr; \
     359    physdir = nullstr; \
     360    trap_ptr = trap; \
     361} while (0)
     362
     363
     364/* ============ DEBUG */
     365#if DEBUG
     366static void trace_printf(const char *fmt, ...);
     367static void trace_vprintf(const char *fmt, va_list va);
     368# define TRACE(param)    trace_printf param
     369# define TRACEV(param)   trace_vprintf param
     370# define close(fd) do { \
     371    int dfd = (fd); \
     372    if (close(dfd) < 0) \
     373        bb_error_msg("bug on %d: closing %d(0x%x)", \
     374            __LINE__, dfd, dfd); \
     375} while (0)
     376#else
     377# define TRACE(param)
     378# define TRACEV(param)
     379#endif
     380
     381
     382/* ============ Utility functions */
     383#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
     384
     385static int isdigit_str9(const char *str)
     386{
     387    int maxlen = 9 + 1; /* max 9 digits: 999999999 */
     388    while (--maxlen && isdigit(*str))
     389        str++;
     390    return (*str == '\0');
     391}
     392
     393static const char *var_end(const char *var)
     394{
     395    while (*var)
     396        if (*var++ == '=')
     397            break;
     398    return var;
     399}
    150400
    151401
    152402/* ============ Interrupts / exceptions */
    153 
    154 /*
    155  * We enclose jmp_buf in a structure so that we can declare pointers to
    156  * jump locations.  The global variable handler contains the location to
    157  * jump to when an exception occurs, and the global variable exception
    158  * contains a code identifying the exception.  To implement nested
    159  * exception handlers, the user should save the value of handler on entry
    160  * to an inner scope, set handler to point to a jmploc structure for the
    161  * inner scope, and restore handler on exit from the scope.
    162  */
    163 struct jmploc {
    164     jmp_buf loc;
    165 };
    166 static struct jmploc *exception_handler;
    167 static int exception;
    168 /* exceptions */
    169 #define EXINT 0         /* SIGINT received */
    170 #define EXERROR 1       /* a generic error */
    171 #define EXSHELLPROC 2   /* execute a shell procedure */
    172 #define EXEXEC 3        /* command execution failed */
    173 #define EXEXIT 4        /* exit the shell */
    174 #define EXSIG 5         /* trapped signal in wait(1) */
    175 static volatile int suppressint;
    176 static volatile sig_atomic_t intpending;
    177 /* do we generate EXSIG events */
    178 static int exsig;
    179 /* last pending signal */
    180 static volatile sig_atomic_t pendingsig;
    181 
    182 /*
    183  * Sigmode records the current value of the signal handlers for the various
    184  * modes.  A value of zero means that the current handler is not known.
    185  * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
    186  */
    187 
    188 #define S_DFL 1                 /* default signal handling (SIG_DFL) */
    189 #define S_CATCH 2               /* signal is caught */
    190 #define S_IGN 3                 /* signal is ignored (SIG_IGN) */
    191 #define S_HARD_IGN 4            /* signal is ignored permenantly */
    192 #define S_RESET 5               /* temporary - to reset a hard ignored sig */
    193 
    194403/*
    195404 * These macros allow the user to suspend the handling of interrupt signals
    196  * over a period of time.  This is similar to SIGHOLD to or sigblock, but
     405 * over a period of time.  This is similar to SIGHOLD or to sigblock, but
    197406 * much more efficient and portable.  (But hacking the kernel is so much
    198407 * more fun than worrying about efficiency and portability. :-))
    199408 */
    200 #define INT_OFF \
    201     do { \
    202         suppressint++; \
    203         xbarrier(); \
    204     } while (0)
     409#define INT_OFF do { \
     410    suppress_int++; \
     411    xbarrier(); \
     412} while (0)
    205413
    206414/*
    207415 * Called to raise an exception.  Since C doesn't include exceptions, we
    208416 * just do a longjmp to the exception handler.  The type of exception is
    209  * stored in the global variable "exception".
    210  */
    211 static void raise_exception(int) ATTRIBUTE_NORETURN;
     417 * stored in the global variable "exception_type".
     418 */
     419static void raise_exception(int) NORETURN;
    212420static void
    213421raise_exception(int e)
     
    218426#endif
    219427    INT_OFF;
    220     exception = e;
     428    exception_type = e;
    221429    longjmp(exception_handler->loc, 1);
    222430}
     431#if DEBUG
     432#define raise_exception(e) do { \
     433    TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
     434    raise_exception(e); \
     435} while (0)
     436#endif
    223437
    224438/*
     
    229443 * defensive programming.)
    230444 */
    231 static void raise_interrupt(void) ATTRIBUTE_NORETURN;
     445static void raise_interrupt(void) NORETURN;
    232446static void
    233447raise_interrupt(void)
    234448{
    235     int i;
    236     sigset_t mask;
    237 
    238     intpending = 0;
    239     /* Signal is not automatically re-enabled after it is raised,
    240      * do it ourself */
    241     sigemptyset(&mask);
    242     sigprocmask(SIG_SETMASK, &mask, 0);
    243     /* pendingsig = 0; - now done in onsig() */
    244 
    245     i = EXSIG;
     449    int ex_type;
     450
     451    pending_int = 0;
     452    /* Signal is not automatically unmasked after it is raised,
     453     * do it ourself - unmask all signals */
     454    sigprocmask_allsigs(SIG_UNBLOCK);
     455    /* pending_sig = 0; - now done in signal_handler() */
     456
     457    ex_type = EXSIG;
    246458    if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
    247459        if (!(rootshell && iflag)) {
     460            /* Kill ourself with SIGINT */
    248461            signal(SIGINT, SIG_DFL);
    249462            raise(SIGINT);
    250463        }
    251         i = EXINT;
    252     }
    253     raise_exception(i);
     464        ex_type = EXINT;
     465    }
     466    raise_exception(ex_type);
    254467    /* NOTREACHED */
    255468}
    256 
    257 #if ENABLE_ASH_OPTIMIZE_FOR_SIZE
    258 static void
     469#if DEBUG
     470#define raise_interrupt() do { \
     471    TRACE(("raising interrupt on line %d\n", __LINE__)); \
     472    raise_interrupt(); \
     473} while (0)
     474#endif
     475
     476static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
    259477int_on(void)
    260478{
    261     if (--suppressint == 0 && intpending) {
     479    xbarrier();
     480    if (--suppress_int == 0 && pending_int) {
    262481        raise_interrupt();
    263482    }
    264483}
    265484#define INT_ON int_on()
    266 static void
     485static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
    267486force_int_on(void)
    268487{
    269     suppressint = 0;
    270     if (intpending)
     488    xbarrier();
     489    suppress_int = 0;
     490    if (pending_int)
    271491        raise_interrupt();
    272492}
    273493#define FORCE_INT_ON force_int_on()
    274 #else
    275 #define INT_ON \
    276     do { \
    277         xbarrier(); \
    278         if (--suppressint == 0 && intpending) \
    279             raise_interrupt(); \
    280     } while (0)
    281 #define FORCE_INT_ON \
    282     do { \
    283         xbarrier(); \
    284         suppressint = 0; \
    285         if (intpending) \
    286             raise_interrupt(); \
    287     } while (0)
    288 #endif /* ASH_OPTIMIZE_FOR_SIZE */
    289 
    290 #define SAVE_INT(v) ((v) = suppressint)
    291 
    292 #define RESTORE_INT(v) \
    293     do { \
    294         xbarrier(); \
    295         suppressint = (v); \
    296         if (suppressint == 0 && intpending) \
    297             raise_interrupt(); \
    298     } while (0)
    299 
    300 #define EXSIGON \
    301     do { \
    302         exsig++; \
    303         xbarrier(); \
    304         if (pendingsig) \
    305             raise_exception(EXSIG); \
    306     } while (0)
    307 /* EXSIG is turned off by evalbltin(). */
    308 
    309 /*
    310  * Ignore a signal. Only one usage site - in forkchild()
    311  */
    312 static void
    313 ignoresig(int signo)
    314 {
    315     if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
    316         signal(signo, SIG_IGN);
    317     }
    318     sigmode[signo - 1] = S_HARD_IGN;
    319 }
    320 
    321 /*
    322  * Signal handler. Only one usage site - in setsignal()
    323  */
    324 static void
    325 onsig(int signo)
    326 {
    327     gotsig[signo - 1] = 1;
    328     pendingsig = signo;
    329 
    330     if (exsig || (signo == SIGINT && !trap[SIGINT])) {
    331         if (!suppressint) {
    332             pendingsig = 0;
    333             raise_interrupt();
    334         }
    335         intpending = 1;
    336     }
    337 }
     494
     495#define SAVE_INT(v) ((v) = suppress_int)
     496
     497#define RESTORE_INT(v) do { \
     498    xbarrier(); \
     499    suppress_int = (v); \
     500    if (suppress_int == 0 && pending_int) \
     501        raise_interrupt(); \
     502} while (0)
    338503
    339504
     
    352517{
    353518    INT_OFF;
    354     fflush(stdout);
    355     fflush(stderr);
    356     INT_ON;
    357 }
    358 
    359 static void
    360 flush_stderr(void)
    361 {
    362     INT_OFF;
    363     fflush(stderr);
     519    fflush_all();
    364520    INT_ON;
    365521}
     
    414570{
    415571    outstr(p, stderr);
    416     flush_stderr();
     572    flush_stdout_stderr();
    417573}
    418574
     
    421577
    422578/* control characters in argument strings */
    423 #define CTLESC '\201'           /* escape next character */
    424 #define CTLVAR '\202'           /* variable defn */
    425 #define CTLENDVAR '\203'
    426 #define CTLBACKQ '\204'
     579#define CTL_FIRST CTLESC
     580#define CTLESC       ((unsigned char)'\201')    /* escape next character */
     581#define CTLVAR       ((unsigned char)'\202')    /* variable defn */
     582#define CTLENDVAR    ((unsigned char)'\203')
     583#define CTLBACKQ     ((unsigned char)'\204')
    427584#define CTLQUOTE 01             /* ored with CTLBACKQ code if in quotes */
    428585/*      CTLBACKQ | CTLQUOTE == '\205' */
    429 #define CTLARI  '\206'          /* arithmetic expression */
    430 #define CTLENDARI '\207'
    431 #define CTLQUOTEMARK '\210'
     586#define CTLARI       ((unsigned char)'\206')    /* arithmetic expression */
     587#define CTLENDARI    ((unsigned char)'\207')
     588#define CTLQUOTEMARK ((unsigned char)'\210')
     589#define CTL_LAST CTLQUOTEMARK
    432590
    433591/* variable substitution byte (follows CTLVAR) */
     
    437595
    438596/* values of VSTYPE field */
    439 #define VSNORMAL        0x1             /* normal variable:  $var or ${var} */
    440 #define VSMINUS         0x2             /* ${var-text} */
    441 #define VSPLUS          0x3             /* ${var+text} */
    442 #define VSQUESTION      0x4             /* ${var?message} */
    443 #define VSASSIGN        0x5             /* ${var=text} */
    444 #define VSTRIMRIGHT     0x6             /* ${var%pattern} */
    445 #define VSTRIMRIGHTMAX  0x7             /* ${var%%pattern} */
    446 #define VSTRIMLEFT      0x8             /* ${var#pattern} */
    447 #define VSTRIMLEFTMAX   0x9             /* ${var##pattern} */
    448 #define VSLENGTH        0xa             /* ${#var} */
     597#define VSNORMAL        0x1     /* normal variable:  $var or ${var} */
     598#define VSMINUS         0x2     /* ${var-text} */
     599#define VSPLUS          0x3     /* ${var+text} */
     600#define VSQUESTION      0x4     /* ${var?message} */
     601#define VSASSIGN        0x5     /* ${var=text} */
     602#define VSTRIMRIGHT     0x6     /* ${var%pattern} */
     603#define VSTRIMRIGHTMAX  0x7     /* ${var%%pattern} */
     604#define VSTRIMLEFT      0x8     /* ${var#pattern} */
     605#define VSTRIMLEFTMAX   0x9     /* ${var##pattern} */
     606#define VSLENGTH        0xa     /* ${#var} */
     607#if ENABLE_ASH_BASH_COMPAT
     608#define VSSUBSTR        0xc     /* ${var:position:length} */
     609#define VSREPLACE       0xd     /* ${var/pattern/replacement} */
     610#define VSREPLACEALL    0xe     /* ${var//pattern/replacement} */
     611#endif
    449612
    450613static const char dolatstr[] ALIGN1 = {
     
    452615};
    453616
    454 #define NCMD 0
    455 #define NPIPE 1
    456 #define NREDIR 2
    457 #define NBACKGND 3
     617#define NCMD      0
     618#define NPIPE     1
     619#define NREDIR    2
     620#define NBACKGND  3
    458621#define NSUBSHELL 4
    459 #define NAND 5
    460 #define NOR 6
    461 #define NSEMI 7
    462 #define NIF 8
    463 #define NWHILE 9
    464 #define NUNTIL 10
    465 #define NFOR 11
    466 #define NCASE 12
    467 #define NCLIST 13
    468 #define NDEFUN 14
    469 #define NARG 15
    470 #define NTO 16
    471 #define NCLOBBER 17
    472 #define NFROM 18
    473 #define NFROMTO 19
    474 #define NAPPEND 20
    475 #define NTOFD 21
    476 #define NFROMFD 22
    477 #define NHERE 23
    478 #define NXHERE 24
    479 #define NNOT 25
     622#define NAND      5
     623#define NOR       6
     624#define NSEMI     7
     625#define NIF       8
     626#define NWHILE    9
     627#define NUNTIL   10
     628#define NFOR     11
     629#define NCASE    12
     630#define NCLIST   13
     631#define NDEFUN   14
     632#define NARG     15
     633#define NTO      16
     634#if ENABLE_ASH_BASH_COMPAT
     635#define NTO2     17
     636#endif
     637#define NCLOBBER 18
     638#define NFROM    19
     639#define NFROMTO  20
     640#define NAPPEND  21
     641#define NTOFD    22
     642#define NFROMFD  23
     643#define NHERE    24
     644#define NXHERE   25
     645#define NNOT     26
     646#define N_NUMBER 27
    480647
    481648union node;
    482649
    483650struct ncmd {
    484     int type;
     651    smallint type; /* Nxxxx */
    485652    union node *assign;
    486653    union node *args;
     
    489656
    490657struct npipe {
    491     int type;
    492     int backgnd;
     658    smallint type;
     659    smallint pipe_backgnd;
    493660    struct nodelist *cmdlist;
    494661};
    495662
    496663struct nredir {
    497     int type;
     664    smallint type;
    498665    union node *n;
    499666    union node *redirect;
     
    501668
    502669struct nbinary {
    503     int type;
     670    smallint type;
    504671    union node *ch1;
    505672    union node *ch2;
     
    507674
    508675struct nif {
    509     int type;
     676    smallint type;
    510677    union node *test;
    511678    union node *ifpart;
     
    514681
    515682struct nfor {
    516     int type;
     683    smallint type;
    517684    union node *args;
    518685    union node *body;
     
    521688
    522689struct ncase {
    523     int type;
     690    smallint type;
    524691    union node *expr;
    525692    union node *cases;
     
    527694
    528695struct nclist {
    529     int type;
     696    smallint type;
    530697    union node *next;
    531698    union node *pattern;
     
    534701
    535702struct narg {
    536     int type;
     703    smallint type;
    537704    union node *next;
    538705    char *text;
     
    540707};
    541708
     709/* nfile and ndup layout must match!
     710 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
     711 * that it is actually NTO2 (>&file), and change its type.
     712 */
    542713struct nfile {
    543     int type;
     714    smallint type;
    544715    union node *next;
    545716    int fd;
     717    int _unused_dupfd;
    546718    union node *fname;
    547719    char *expfname;
     
    549721
    550722struct ndup {
    551     int type;
     723    smallint type;
    552724    union node *next;
    553725    int fd;
    554726    int dupfd;
    555727    union node *vname;
     728    char *_unused_expfname;
    556729};
    557730
    558731struct nhere {
    559     int type;
     732    smallint type;
    560733    union node *next;
    561734    int fd;
     
    564737
    565738struct nnot {
    566     int type;
     739    smallint type;
    567740    union node *com;
    568741};
    569742
    570743union node {
    571     int type;
     744    smallint type;
    572745    struct ncmd ncmd;
    573746    struct npipe npipe;
     
    585758};
    586759
     760/*
     761 * NODE_EOF is returned by parsecmd when it encounters an end of file.
     762 * It must be distinct from NULL.
     763 */
     764#define NODE_EOF ((union node *) -1L)
     765
    587766struct nodelist {
    588767    struct nodelist *next;
     
    619798    if (debug != 1)
    620799        return;
     800    if (DEBUG_TIME)
     801        fprintf(tracefile, "%u ", (int) time(NULL));
     802    if (DEBUG_PID)
     803        fprintf(tracefile, "[%u] ", (int) getpid());
     804    if (DEBUG_SIG)
     805        fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
    621806    va_start(va, fmt);
    622807    vfprintf(tracefile, fmt, va);
     
    629814    if (debug != 1)
    630815        return;
     816    if (DEBUG_TIME)
     817        fprintf(tracefile, "%u ", (int) time(NULL));
     818    if (DEBUG_PID)
     819        fprintf(tracefile, "[%u] ", (int) getpid());
     820    if (DEBUG_SIG)
     821        fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
    631822    vfprintf(tracefile, fmt, va);
    632823}
     
    650841    putc('"', tracefile);
    651842    for (p = s; *p; p++) {
    652         switch (*p) {
    653         case '\n':  c = 'n'; goto backslash;
    654         case '\t':  c = 't'; goto backslash;
    655         case '\r':  c = 'r'; goto backslash;
    656         case '"':  c = '"'; goto backslash;
    657         case '\\':  c = '\\'; goto backslash;
    658         case CTLESC:  c = 'e'; goto backslash;
    659         case CTLVAR:  c = 'v'; goto backslash;
    660         case CTLVAR+CTLQUOTE:  c = 'V'; goto backslash;
    661         case CTLBACKQ:  c = 'q'; goto backslash;
    662         case CTLBACKQ+CTLQUOTE:  c = 'Q'; goto backslash;
     843        switch ((unsigned char)*p) {
     844        case '\n': c = 'n'; goto backslash;
     845        case '\t': c = 't'; goto backslash;
     846        case '\r': c = 'r'; goto backslash;
     847        case '\"': c = '\"'; goto backslash;
     848        case '\\': c = '\\'; goto backslash;
     849        case CTLESC: c = 'e'; goto backslash;
     850        case CTLVAR: c = 'v'; goto backslash;
     851        case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
     852        case CTLBACKQ: c = 'q'; goto backslash;
     853        case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
    663854 backslash:
    664855            putc('\\', tracefile);
     
    670861            else {
    671862                putc('\\', tracefile);
    672                 putc(*p >> 6 & 03, tracefile);
    673                 putc(*p >> 3 & 07, tracefile);
     863                putc((*p >> 6) & 03, tracefile);
     864                putc((*p >> 3) & 07, tracefile);
    674865                putc(*p & 07, tracefile);
    675866            }
     
    755946    char *p;
    756947    struct nodelist *bqlist;
    757     int subtype;
     948    unsigned char subtype;
    758949
    759950    if (arg->type != NARG) {
     
    763954    bqlist = arg->narg.backquote;
    764955    for (p = arg->narg.text; *p; p++) {
    765         switch (*p) {
     956        switch ((unsigned char)*p) {
    766957        case CTLESC:
    767             putc(*++p, fp);
     958            p++;
     959            putc(*p, fp);
    768960            break;
    769961        case CTLVAR:
     
    774966                putc('#', fp);
    775967
    776             while (*p != '=')
    777                 putc(*p++, fp);
     968            while (*p != '=') {
     969                putc(*p, fp);
     970                p++;
     971            }
    778972
    779973            if (subtype & VSNUL)
     
    8561050        case NCLOBBER: s = ">|"; dftfd = 1; break;
    8571051        case NAPPEND:  s = ">>"; dftfd = 1; break;
     1052#if ENABLE_ASH_BASH_COMPAT
     1053        case NTO2:
     1054#endif
    8581055        case NTOFD:    s = ">&"; dftfd = 1; break;
    859         case NFROM:    s = "<";  break;
     1056        case NFROM:    s = "<"; break;
    8601057        case NFROMFD:  s = "<&"; break;
    8611058        case NFROMTO:  s = "<>"; break;
     
    8841081
    8851082    indent(ind, pfx, fp);
     1083
     1084    if (n == NODE_EOF) {
     1085        fputs("<EOF>", fp);
     1086        return;
     1087    }
     1088
    8861089    switch (n->type) {
    8871090    case NSEMI:
     
    9061109    case NPIPE:
    9071110        for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
    908             shcmd(lp->n, fp);
     1111            shtree(lp->n, 0, NULL, fp);
    9091112            if (lp->next)
    9101113                fputs(" | ", fp);
    9111114        }
    912         if (n->npipe.backgnd)
     1115        if (n->npipe.pipe_backgnd)
    9131116            fputs(" &", fp);
    9141117        if (ind >= 0)
     
    9271130{
    9281131    trace_puts("showtree called\n");
    929     shtree(n, 1, NULL, stdout);
    930 }
    931 
    932 #define TRACE(param)    trace_printf param
    933 #define TRACEV(param)   trace_vprintf param
    934 
    935 #else
    936 
    937 #define TRACE(param)
    938 #define TRACEV(param)
     1132    shtree(n, 1, NULL, stderr);
     1133}
    9391134
    9401135#endif /* DEBUG */
     
    9511146};
    9521147
    953 #if ENABLE_ASH_ALIAS
    9541148struct alias;
    955 #endif
    9561149
    9571150struct strpush {
    9581151    struct strpush *prev;   /* preceding string on stack */
    959     char *prevstring;
    960     int prevnleft;
     1152    char *prev_string;
     1153    int prev_left_in_line;
    9611154#if ENABLE_ASH_ALIAS
    9621155    struct alias *ap;       /* if push was associated with an alias */
     
    9681161    struct parsefile *prev; /* preceding file on stack */
    9691162    int linno;              /* current line */
    970     int fd;                 /* file descriptor (or -1 if string) */
    971     int nleft;              /* number of chars left in this line */
    972     int lleft;              /* number of chars left in this buffer */
    973     char *nextc;            /* next char in buffer */
     1163    int pf_fd;              /* file descriptor (or -1 if string) */
     1164    int left_in_line;       /* number of chars left in this line */
     1165    int left_in_buffer;     /* number of chars left in this buffer past the line */
     1166    char *next_to_pgetc;    /* next char in buffer */
    9741167    char *buf;              /* input buffer */
    9751168    struct strpush *strpush; /* for pushing strings at this level */
     
    9771170};
    9781171
    979 static struct parsefile basepf;         /* top level input file */
    980 static struct parsefile *parsefile = &basepf;  /* current input file */
     1172static struct parsefile basepf;        /* top level input file */
     1173static struct parsefile *g_parsefile = &basepf;  /* current input file */
    9811174static int startlinno;                 /* line # where last token started */
    9821175static char *commandname;              /* currently executing command */
    9831176static struct strlist *cmdenviron;     /* environment for builtin command */
    984 static int exitstatus;                 /* exit status of last command */
     1177static uint8_t exitstatus;             /* exit status of last command */
    9851178
    9861179
     
    9941187        if (strcmp(arg0, commandname))
    9951188            fprintf(stderr, "%s: ", commandname);
    996         if (!iflag || parsefile->fd)
     1189        if (!iflag || g_parsefile->pf_fd > 0)
    9971190            fprintf(stderr, "line %d: ", startlinno);
    9981191    }
     
    10061199 * formatting.  It then raises the error exception.
    10071200 */
    1008 static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
     1201static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
    10091202static void
    10101203ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
     
    10261219}
    10271220
    1028 static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
     1221static void ash_msg_and_raise_error(const char *, ...) NORETURN;
    10291222static void
    10301223ash_msg_and_raise_error(const char *msg, ...)
     
    10381231}
    10391232
    1040 static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
     1233static void raise_error_syntax(const char *) NORETURN;
     1234static void
     1235raise_error_syntax(const char *msg)
     1236{
     1237    ash_msg_and_raise_error("syntax error: %s", msg);
     1238    /* NOTREACHED */
     1239}
     1240
     1241static void ash_msg_and_raise(int, const char *, ...) NORETURN;
    10411242static void
    10421243ash_msg_and_raise(int cond, const char *msg, ...)
     
    10791280
    10801281/* ============ Memory allocation */
     1282
     1283#if 0
     1284/* I consider these wrappers nearly useless:
     1285 * ok, they return you to nearest exception handler, but
     1286 * how much memory do you leak in the process, making
     1287 * memory starvation worse?
     1288 */
     1289static void *
     1290ckrealloc(void * p, size_t nbytes)
     1291{
     1292    p = realloc(p, nbytes);
     1293    if (!p)
     1294        ash_msg_and_raise_error(bb_msg_memory_exhausted);
     1295    return p;
     1296}
     1297
     1298static void *
     1299ckmalloc(size_t nbytes)
     1300{
     1301    return ckrealloc(NULL, nbytes);
     1302}
     1303
     1304static void *
     1305ckzalloc(size_t nbytes)
     1306{
     1307    return memset(ckmalloc(nbytes), 0, nbytes);
     1308}
     1309
     1310static char *
     1311ckstrdup(const char *s)
     1312{
     1313    char *p = strdup(s);
     1314    if (!p)
     1315        ash_msg_and_raise_error(bb_msg_memory_exhausted);
     1316    return p;
     1317}
     1318#else
     1319/* Using bbox equivalents. They exit if out of memory */
     1320# define ckrealloc xrealloc
     1321# define ckmalloc  xmalloc
     1322# define ckzalloc  xzalloc
     1323# define ckstrdup  xstrdup
     1324#endif
    10811325
    10821326/*
     
    10891333     * in some way.  The following macro will get this right
    10901334     * on many machines.  */
    1091     SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
     1335    SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
    10921336    /* Minimum size of a block */
    1093     MINSIZE  = SHELL_ALIGN(504),
     1337    MINSIZE = SHELL_ALIGN(504),
    10941338};
    10951339
     
    11061350};
    11071351
    1108 static struct stack_block stackbase;
    1109 static struct stack_block *stackp = &stackbase;
    1110 static struct stackmark *markp;
    1111 static char *stacknxt = stackbase.space;
    1112 static size_t stacknleft = MINSIZE;
    1113 static char *sstrend = stackbase.space + MINSIZE;
    1114 static int herefd = -1;
    1115 
    1116 #define stackblock() ((void *)stacknxt)
    1117 #define stackblocksize() stacknleft
    1118 
    1119 static void *
    1120 ckrealloc(void * p, size_t nbytes)
    1121 {
    1122     p = realloc(p, nbytes);
    1123     if (!p)
    1124         ash_msg_and_raise_error(bb_msg_memory_exhausted);
    1125     return p;
    1126 }
    1127 
    1128 static void *
    1129 ckmalloc(size_t nbytes)
    1130 {
    1131     return ckrealloc(NULL, nbytes);
    1132 }
    1133 
    1134 /*
    1135  * Make a copy of a string in safe storage.
    1136  */
    1137 static char *
    1138 ckstrdup(const char *s)
    1139 {
    1140     char *p = strdup(s);
    1141     if (!p)
    1142         ash_msg_and_raise_error(bb_msg_memory_exhausted);
    1143     return p;
    1144 }
     1352
     1353struct globals_memstack {
     1354    struct stack_block *g_stackp; // = &stackbase;
     1355    struct stackmark *markp;
     1356    char *g_stacknxt; // = stackbase.space;
     1357    char *sstrend; // = stackbase.space + MINSIZE;
     1358    size_t g_stacknleft; // = MINSIZE;
     1359    int    herefd; // = -1;
     1360    struct stack_block stackbase;
     1361};
     1362extern struct globals_memstack *const ash_ptr_to_globals_memstack;
     1363#define G_memstack (*ash_ptr_to_globals_memstack)
     1364#define g_stackp     (G_memstack.g_stackp    )
     1365#define markp        (G_memstack.markp       )
     1366#define g_stacknxt   (G_memstack.g_stacknxt  )
     1367#define sstrend      (G_memstack.sstrend     )
     1368#define g_stacknleft (G_memstack.g_stacknleft)
     1369#define herefd       (G_memstack.herefd      )
     1370#define stackbase    (G_memstack.stackbase   )
     1371#define INIT_G_memstack() do { \
     1372    (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
     1373    barrier(); \
     1374    g_stackp = &stackbase; \
     1375    g_stacknxt = stackbase.space; \
     1376    g_stacknleft = MINSIZE; \
     1377    sstrend = stackbase.space + MINSIZE; \
     1378    herefd = -1; \
     1379} while (0)
     1380
     1381
     1382#define stackblock()     ((void *)g_stacknxt)
     1383#define stackblocksize() g_stacknleft
    11451384
    11461385/*
     
    11591398
    11601399    aligned = SHELL_ALIGN(nbytes);
    1161     if (aligned > stacknleft) {
     1400    if (aligned > g_stacknleft) {
    11621401        size_t len;
    11631402        size_t blocksize;
     
    11721411        INT_OFF;
    11731412        sp = ckmalloc(len);
    1174         sp->prev = stackp;
    1175         stacknxt = sp->space;
    1176         stacknleft = blocksize;
    1177         sstrend = stacknxt + blocksize;
    1178         stackp = sp;
     1413        sp->prev = g_stackp;
     1414        g_stacknxt = sp->space;
     1415        g_stacknleft = blocksize;
     1416        sstrend = g_stacknxt + blocksize;
     1417        g_stackp = sp;
    11791418        INT_ON;
    11801419    }
    1181     p = stacknxt;
    1182     stacknxt += aligned;
    1183     stacknleft -= aligned;
     1420    p = g_stacknxt;
     1421    g_stacknxt += aligned;
     1422    g_stacknleft -= aligned;
    11841423    return p;
     1424}
     1425
     1426static void *
     1427stzalloc(size_t nbytes)
     1428{
     1429    return memset(stalloc(nbytes), 0, nbytes);
    11851430}
    11861431
     
    11891434{
    11901435#if DEBUG
    1191     if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
    1192         write(2, "stunalloc\n", 10);
     1436    if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
     1437        write(STDERR_FILENO, "stunalloc\n", 10);
    11931438        abort();
    11941439    }
    11951440#endif
    1196     stacknleft += stacknxt - (char *)p;
    1197     stacknxt = p;
     1441    g_stacknleft += g_stacknxt - (char *)p;
     1442    g_stacknxt = p;
    11981443}
    11991444
     
    12111456setstackmark(struct stackmark *mark)
    12121457{
    1213     mark->stackp = stackp;
    1214     mark->stacknxt = stacknxt;
    1215     mark->stacknleft = stacknleft;
     1458    mark->stackp = g_stackp;
     1459    mark->stacknxt = g_stacknxt;
     1460    mark->stacknleft = g_stacknleft;
    12161461    mark->marknext = markp;
    12171462    markp = mark;
     
    12281473    INT_OFF;
    12291474    markp = mark->marknext;
    1230     while (stackp != mark->stackp) {
    1231         sp = stackp;
    1232         stackp = sp->prev;
     1475    while (g_stackp != mark->stackp) {
     1476        sp = g_stackp;
     1477        g_stackp = sp->prev;
    12331478        free(sp);
    12341479    }
    1235     stacknxt = mark->stacknxt;
    1236     stacknleft = mark->stacknleft;
     1480    g_stacknxt = mark->stacknxt;
     1481    g_stacknleft = mark->stacknleft;
    12371482    sstrend = mark->stacknxt + mark->stacknleft;
    12381483    INT_ON;
     
    12531498    size_t newlen;
    12541499
    1255     newlen = stacknleft * 2;
    1256     if (newlen < stacknleft)
     1500    newlen = g_stacknleft * 2;
     1501    if (newlen < g_stacknleft)
    12571502        ash_msg_and_raise_error(bb_msg_memory_exhausted);
    12581503    if (newlen < 128)
    12591504        newlen += 128;
    12601505
    1261     if (stacknxt == stackp->space && stackp != &stackbase) {
     1506    if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
    12621507        struct stack_block *oldstackp;
    12631508        struct stackmark *xmark;
     
    12671512
    12681513        INT_OFF;
    1269         oldstackp = stackp;
    1270         sp = stackp;
     1514        oldstackp = g_stackp;
     1515        sp = g_stackp;
    12711516        prevstackp = sp->prev;
    12721517        grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
    12731518        sp = ckrealloc(sp, grosslen);
    12741519        sp->prev = prevstackp;
    1275         stackp = sp;
    1276         stacknxt = sp->space;
    1277         stacknleft = newlen;
     1520        g_stackp = sp;
     1521        g_stacknxt = sp->space;
     1522        g_stacknleft = newlen;
    12781523        sstrend = sp->space + newlen;
    12791524
     
    12841529        xmark = markp;
    12851530        while (xmark != NULL && xmark->stackp == oldstackp) {
    1286             xmark->stackp = stackp;
    1287             xmark->stacknxt = stacknxt;
    1288             xmark->stacknleft = stacknleft;
     1531            xmark->stackp = g_stackp;
     1532            xmark->stacknxt = g_stacknxt;
     1533            xmark->stacknleft = g_stacknleft;
    12891534            xmark = xmark->marknext;
    12901535        }
    12911536        INT_ON;
    12921537    } else {
    1293         char *oldspace = stacknxt;
    1294         int oldlen = stacknleft;
     1538        char *oldspace = g_stacknxt;
     1539        size_t oldlen = g_stacknleft;
    12951540        char *p = stalloc(newlen);
    12961541
    12971542        /* free the space we just allocated */
    1298         stacknxt = memcpy(p, oldspace, oldlen);
    1299         stacknleft += newlen;
     1543        g_stacknxt = memcpy(p, oldspace, oldlen);
     1544        g_stacknleft += newlen;
    13001545    }
    13011546}
     
    13051550{
    13061551    len = SHELL_ALIGN(len);
    1307     stacknxt += len;
    1308     stacknleft -= len;
     1552    g_stacknxt += len;
     1553    g_stacknleft -= len;
    13091554}
    13101555
     
    13351580    }
    13361581    growstackblock();
    1337     return stackblock() + len;
     1582    return (char *)stackblock() + len;
    13381583}
    13391584
     
    13441589makestrspace(size_t newlen, char *p)
    13451590{
    1346     size_t len = p - stacknxt;
     1591    size_t len = p - g_stacknxt;
    13471592    size_t size = stackblocksize();
    13481593
     
    13561601        growstackblock();
    13571602    }
    1358     return stackblock() + len;
     1603    return (char *)stackblock() + len;
    13591604}
    13601605
     
    13631608{
    13641609    p = makestrspace(n, p);
    1365     p = memcpy(p, s, n) + n;
     1610    p = (char *)memcpy(p, s, n) + n;
    13661611    return p;
    13671612}
     
    13841629#define STARTSTACKSTR(p)        ((p) = stackblock())
    13851630#define STPUTC(c, p)            ((p) = _STPUTC((c), (p)))
    1386 #define CHECKSTRSPACE(n, p) \
    1387     do { \
    1388         char *q = (p); \
    1389         size_t l = (n); \
    1390         size_t m = sstrend - q; \
    1391         if (l > m) \
    1392             (p) = makestrspace(l, q); \
    1393     } while (0)
    1394 #define USTPUTC(c, p)           (*p++ = (c))
    1395 #define STACKSTRNUL(p) \
    1396     do { \
    1397         if ((p) == sstrend) \
    1398             p = growstackstr(); \
    1399         *p = '\0'; \
    1400     } while (0)
    1401 #define STUNPUTC(p)             (--p)
    1402 #define STTOPC(p)               (p[-1])
    1403 #define STADJUST(amount, p)     (p += (amount))
     1631#define CHECKSTRSPACE(n, p) do { \
     1632    char *q = (p); \
     1633    size_t l = (n); \
     1634    size_t m = sstrend - q; \
     1635    if (l > m) \
     1636        (p) = makestrspace(l, q); \
     1637} while (0)
     1638#define USTPUTC(c, p)           (*(p)++ = (c))
     1639#define STACKSTRNUL(p) do { \
     1640    if ((p) == sstrend) \
     1641        (p) = growstackstr(); \
     1642    *(p) = '\0'; \
     1643} while (0)
     1644#define STUNPUTC(p)             (--(p))
     1645#define STTOPC(p)               ((p)[-1])
     1646#define STADJUST(amount, p)     ((p) += (amount))
    14041647
    14051648#define grabstackstr(p)         stalloc((char *)(p) - (char *)stackblock())
    1406 #define ungrabstackstr(s, p)    stunalloc((s))
     1649#define ungrabstackstr(s, p)    stunalloc(s)
    14071650#define stackstrend()           ((void *)sstrend)
    14081651
     
    14181661    while (*pfx) {
    14191662        if (*pfx++ != *string++)
    1420             return 0;
     1663            return NULL;
    14211664    }
    14221665    return (char *) string;
     
    14441687{
    14451688    if (!is_number(s))
    1446         ash_msg_and_raise_error(illnum, s);
     1689        ash_msg_and_raise_error(msg_illnum, s);
    14471690    return atoi(s);
    14481691}
     
    14681711
    14691712        *q++ = '\'';
    1470         q = memcpy(q, s, len) + len;
     1713        q = (char *)memcpy(q, s, len) + len;
    14711714        *q++ = '\'';
    14721715        s += len;
     
    14741717        STADJUST(q - p, p);
    14751718
    1476         len = strspn(s, "'");
    1477         if (!len)
     1719        if (*s != '\'')
    14781720            break;
     1721        len = 0;
     1722        do len++; while (*++s == '\'');
    14791723
    14801724        q = p = makestrspace(len + 3, p);
    14811725
    14821726        *q++ = '"';
    1483         q = memcpy(q, s, len) + len;
     1727        q = (char *)memcpy(q, s - len, len) + len;
    14841728        *q++ = '"';
    1485         s += len;
    14861729
    14871730        STADJUST(q - p, p);
    14881731    } while (*s);
    14891732
    1490     USTPUTC(0, p);
     1733    USTPUTC('\0', p);
    14911734
    14921735    return stackblock();
     
    15011744
    15021745/*
    1503  * XXX - should get rid of.  have all builtins use getopt(3).  the
    1504  * library getopt must have the BSD extension static variable "optreset"
    1505  * otherwise it can't be used within the shell safely.
     1746 * XXX - should get rid of. Have all builtins use getopt(3).
     1747 * The library getopt must have the BSD extension static variable
     1748 * "optreset", otherwise it can't be used within the shell safely.
    15061749 *
    1507  * Standard option processing (a la getopt) for builtin routines.  The
    1508  * only argument that is passed to nextopt is the option string; the
    1509  * other arguments are unnecessary.  It return the character, or '\0' on
    1510  * end of input.
     1750 * Standard option processing (a la getopt) for builtin routines.
     1751 * The only argument that is passed to nextopt is the option string;
     1752 * the other arguments are unnecessary. It returns the character,
     1753 * or '\0' on end of input.
    15111754 */
    15121755static int
     
    15191762    p = optptr;
    15201763    if (p == NULL || *p == '\0') {
     1764        /* We ate entire "-param", take next one */
    15211765        p = *argptr;
    1522         if (p == NULL || *p != '-' || *++p == '\0')
     1766        if (p == NULL)
     1767            return '\0';
     1768        if (*p != '-')
     1769            return '\0';
     1770        if (*++p == '\0') /* just "-" ? */
    15231771            return '\0';
    15241772        argptr++;
    1525         if (LONE_DASH(p))        /* check for "--" */
     1773        if (LONE_DASH(p)) /* "--" ? */
    15261774            return '\0';
    1527     }
     1775        /* p => next "-param" */
     1776    }
     1777    /* p => some option char in the middle of a "-param" */
    15281778    c = *p++;
    1529     for (q = optstring; *q != c; ) {
     1779    for (q = optstring; *q != c;) {
    15301780        if (*q == '\0')
    15311781            ash_msg_and_raise_error("illegal option -%c", c);
     
    15341784    }
    15351785    if (*++q == ':') {
    1536         if (*p == '\0' && (p = *argptr++) == NULL)
    1537             ash_msg_and_raise_error("no arg for -%c option", c);
     1786        if (*p == '\0') {
     1787            p = *argptr++;
     1788            if (p == NULL)
     1789                ash_msg_and_raise_error("no arg for -%c option", c);
     1790        }
    15381791        optionarg = p;
    15391792        p = NULL;
     
    15441797
    15451798
    1546 /* ============ Math support definitions */
    1547 
    1548 #if ENABLE_ASH_MATH_SUPPORT_64
    1549 typedef int64_t arith_t;
    1550 #define arith_t_type long long
    1551 #else
    1552 typedef long arith_t;
    1553 #define arith_t_type long
    1554 #endif
    1555 
    1556 #if ENABLE_ASH_MATH_SUPPORT
    1557 static arith_t dash_arith(const char *);
    1558 static arith_t arith(const char *expr, int *perrcode);
    1559 #endif
    1560 
    1561 #if ENABLE_ASH_RANDOM_SUPPORT
    1562 static unsigned long rseed;
    1563 #ifndef DYNAMIC_VAR
    1564 #define DYNAMIC_VAR
    1565 #endif
    1566 #endif
    1567 
    1568 
    15691799/* ============ Shell variables */
     1800
     1801/*
     1802 * The parsefile structure pointed to by the global variable parsefile
     1803 * contains information about the current file being read.
     1804 */
     1805struct shparam {
     1806    int nparam;             /* # of positional parameters (without $0) */
     1807#if ENABLE_ASH_GETOPTS
     1808    int optind;             /* next parameter to be processed by getopts */
     1809    int optoff;             /* used by getopts */
     1810#endif
     1811    unsigned char malloced; /* if parameter list dynamically allocated */
     1812    char **p;               /* parameter list */
     1813};
     1814
     1815/*
     1816 * Free the list of positional parameters.
     1817 */
     1818static void
     1819freeparam(volatile struct shparam *param)
     1820{
     1821    if (param->malloced) {
     1822        char **ap, **ap1;
     1823        ap = ap1 = param->p;
     1824        while (*ap)
     1825            free(*ap++);
     1826        free(ap1);
     1827    }
     1828}
     1829
     1830#if ENABLE_ASH_GETOPTS
     1831static void FAST_FUNC getoptsreset(const char *value);
     1832#endif
     1833
     1834struct var {
     1835    struct var *next;               /* next entry in hash list */
     1836    int flags;                      /* flags are defined above */
     1837    const char *var_text;           /* name=value */
     1838    void (*var_func)(const char *) FAST_FUNC; /* function to be called when  */
     1839                    /* the variable gets set/unset */
     1840};
     1841
     1842struct localvar {
     1843    struct localvar *next;          /* next local variable in list */
     1844    struct var *vp;                 /* the variable that was made local */
     1845    int flags;                      /* saved flags */
     1846    const char *text;               /* saved text */
     1847};
    15701848
    15711849/* flags */
     
    15791857#define VNOSET          0x80    /* do not set variable - just readonly test */
    15801858#define VNOSAVE         0x100   /* when text is on the heap before setvareq */
    1581 #ifdef DYNAMIC_VAR
     1859#if ENABLE_ASH_RANDOM_SUPPORT
    15821860# define VDYNAMIC       0x200   /* dynamic variable */
    15831861#else
     
    15851863#endif
    15861864
    1587 #ifdef IFS_BROKEN
    1588 static const char defifsvar[] ALIGN1 = "IFS= \t\n";
    1589 #define defifs (defifsvar + 4)
    1590 #else
    1591 static const char defifs[] ALIGN1 = " \t\n";
    1592 #endif
    1593 
    1594 struct shparam {
    1595     int nparam;             /* # of positional parameters (without $0) */
    1596     unsigned char malloc;   /* if parameter list dynamically allocated */
    1597     char **p;               /* parameter list */
    1598 #if ENABLE_ASH_GETOPTS
    1599     int optind;             /* next parameter to be processed by getopts */
    1600     int optoff;             /* used by getopts */
    1601 #endif
    1602 };
    1603 
    1604 static struct shparam shellparam;      /* $@ current positional parameters */
    1605 
    1606 /*
    1607  * Free the list of positional parameters.
    1608  */
    1609 static void
    1610 freeparam(volatile struct shparam *param)
    1611 {
    1612     char **ap;
    1613 
    1614     if (param->malloc) {
    1615         for (ap = param->p; *ap; ap++)
    1616             free(*ap);
    1617         free(param->p);
    1618     }
    1619 }
    1620 
    1621 #if ENABLE_ASH_GETOPTS
    1622 static void
    1623 getoptsreset(const char *value)
    1624 {
    1625     shellparam.optind = number(value);
    1626     shellparam.optoff = -1;
    1627 }
    1628 #endif
    1629 
    1630 struct var {
    1631     struct var *next;               /* next entry in hash list */
    1632     int flags;                      /* flags are defined above */
    1633     const char *text;               /* name=value */
    1634     void (*func)(const char *);     /* function to be called when  */
    1635                     /* the variable gets set/unset */
    1636 };
    1637 
    1638 struct localvar {
    1639     struct localvar *next;          /* next local variable in list */
    1640     struct var *vp;                 /* the variable that was made local */
    1641     int flags;                      /* saved flags */
    1642     const char *text;               /* saved text */
    1643 };
    1644 
    1645 /* Forward decls for varinit[] */
     1865
     1866/* Need to be before varinit_data[] */
    16461867#if ENABLE_LOCALE_SUPPORT
    1647 static void
     1868static void FAST_FUNC
    16481869change_lc_all(const char *value)
    16491870{
     
    16511872        setlocale(LC_ALL, value);
    16521873}
    1653 static void
     1874static void FAST_FUNC
    16541875change_lc_ctype(const char *value)
    16551876{
     
    16601881#if ENABLE_ASH_MAIL
    16611882static void chkmail(void);
    1662 static void changemail(const char *);
    1663 #endif
    1664 static void changepath(const char *);
     1883static void changemail(const char *) FAST_FUNC;
     1884#endif
     1885static void changepath(const char *) FAST_FUNC;
    16651886#if ENABLE_ASH_RANDOM_SUPPORT
    1666 static void change_random(const char *);
    1667 #endif
    1668 
    1669 static struct var varinit[] = {
    1670 #ifdef IFS_BROKEN
    1671     { NULL, VSTRFIXED|VTEXTFIXED,           defifsvar,      NULL },
     1887static void change_random(const char *) FAST_FUNC;
     1888#endif
     1889
     1890static const struct {
     1891    int flags;
     1892    const char *var_text;
     1893    void (*var_func)(const char *) FAST_FUNC;
     1894} varinit_data[] = {
     1895    { VSTRFIXED|VTEXTFIXED       , defifsvar   , NULL            },
     1896#if ENABLE_ASH_MAIL
     1897    { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL"      , changemail      },
     1898    { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH"  , changemail      },
     1899#endif
     1900    { VSTRFIXED|VTEXTFIXED       , bb_PATH_root_path, changepath },
     1901    { VSTRFIXED|VTEXTFIXED       , "PS1=$ "    , NULL            },
     1902    { VSTRFIXED|VTEXTFIXED       , "PS2=> "    , NULL            },
     1903    { VSTRFIXED|VTEXTFIXED       , "PS4=+ "    , NULL            },
     1904#if ENABLE_ASH_GETOPTS
     1905    { VSTRFIXED|VTEXTFIXED       , "OPTIND=1"  , getoptsreset    },
     1906#endif
     1907#if ENABLE_ASH_RANDOM_SUPPORT
     1908    { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
     1909#endif
     1910#if ENABLE_LOCALE_SUPPORT
     1911    { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL"    , change_lc_all   },
     1912    { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE"  , change_lc_ctype },
     1913#endif
     1914#if ENABLE_FEATURE_EDITING_SAVEHISTORY
     1915    { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE"  , NULL            },
     1916#endif
     1917};
     1918
     1919struct redirtab;
     1920
     1921struct globals_var {
     1922    struct shparam shellparam;      /* $@ current positional parameters */
     1923    struct redirtab *redirlist;
     1924    int g_nullredirs;
     1925    int preverrout_fd;   /* save fd2 before print debug if xflag is set. */
     1926    struct var *vartab[VTABSIZE];
     1927    struct var varinit[ARRAY_SIZE(varinit_data)];
     1928};
     1929extern struct globals_var *const ash_ptr_to_globals_var;
     1930#define G_var (*ash_ptr_to_globals_var)
     1931#define shellparam    (G_var.shellparam   )
     1932//#define redirlist     (G_var.redirlist    )
     1933#define g_nullredirs  (G_var.g_nullredirs )
     1934#define preverrout_fd (G_var.preverrout_fd)
     1935#define vartab        (G_var.vartab       )
     1936#define varinit       (G_var.varinit      )
     1937#define INIT_G_var() do { \
     1938    unsigned i; \
     1939    (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
     1940    barrier(); \
     1941    for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
     1942        varinit[i].flags    = varinit_data[i].flags; \
     1943        varinit[i].var_text = varinit_data[i].var_text; \
     1944        varinit[i].var_func = varinit_data[i].var_func; \
     1945    } \
     1946} while (0)
     1947
     1948#define vifs      varinit[0]
     1949#if ENABLE_ASH_MAIL
     1950# define vmail    (&vifs)[1]
     1951# define vmpath   (&vmail)[1]
     1952# define vpath    (&vmpath)[1]
    16721953#else
    1673     { NULL, VSTRFIXED|VTEXTFIXED|VUNSET,    "IFS\0",        NULL },
    1674 #endif
    1675 #if ENABLE_ASH_MAIL
    1676     { NULL, VSTRFIXED|VTEXTFIXED|VUNSET,    "MAIL\0",       changemail },
    1677     { NULL, VSTRFIXED|VTEXTFIXED|VUNSET,    "MAILPATH\0",   changemail },
    1678 #endif
    1679     { NULL, VSTRFIXED|VTEXTFIXED,           bb_PATH_root_path, changepath },
    1680     { NULL, VSTRFIXED|VTEXTFIXED,           "PS1=$ ",       NULL },
    1681     { NULL, VSTRFIXED|VTEXTFIXED,           "PS2=> ",       NULL },
    1682     { NULL, VSTRFIXED|VTEXTFIXED,           "PS4=+ ",       NULL },
     1954# define vpath    (&vifs)[1]
     1955#endif
     1956#define vps1      (&vpath)[1]
     1957#define vps2      (&vps1)[1]
     1958#define vps4      (&vps2)[1]
    16831959#if ENABLE_ASH_GETOPTS
    1684     { NULL, VSTRFIXED|VTEXTFIXED,           "OPTIND=1",     getoptsreset },
    1685 #endif
    1686 #if ENABLE_ASH_RANDOM_SUPPORT
    1687     { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
    1688 #endif
    1689 #if ENABLE_LOCALE_SUPPORT
    1690     { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0",    change_lc_all },
    1691     { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0",  change_lc_ctype },
    1692 #endif
    1693 #if ENABLE_FEATURE_EDITING_SAVEHISTORY
    1694     { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0",  NULL },
    1695 #endif
    1696 };
    1697 
    1698 #define vifs varinit[0]
    1699 #if ENABLE_ASH_MAIL
    1700 #define vmail (&vifs)[1]
    1701 #define vmpath (&vmail)[1]
     1960# define voptind  (&vps4)[1]
     1961# if ENABLE_ASH_RANDOM_SUPPORT
     1962#  define vrandom (&voptind)[1]
     1963# endif
    17021964#else
    1703 #define vmpath vifs
    1704 #endif
    1705 #define vpath (&vmpath)[1]
    1706 #define vps1 (&vpath)[1]
    1707 #define vps2 (&vps1)[1]
    1708 #define vps4 (&vps2)[1]
    1709 #define voptind (&vps4)[1]
    1710 #if ENABLE_ASH_GETOPTS
    1711 #define vrandom (&voptind)[1]
    1712 #else
    1713 #define vrandom (&vps4)[1]
     1965# if ENABLE_ASH_RANDOM_SUPPORT
     1966#  define vrandom (&vps4)[1]
     1967# endif
    17141968#endif
    17151969
     
    17191973 * for unset variables.
    17201974 */
    1721 #define ifsval()        (vifs.text + 4)
     1975#define ifsval()        (vifs.var_text + 4)
    17221976#define ifsset()        ((vifs.flags & VUNSET) == 0)
    1723 #define mailval()       (vmail.text + 5)
    1724 #define mpathval()      (vmpath.text + 9)
    1725 #define pathval()       (vpath.text + 5)
    1726 #define ps1val()        (vps1.text + 4)
    1727 #define ps2val()        (vps2.text + 4)
    1728 #define ps4val()        (vps4.text + 4)
    1729 #define optindval()     (voptind.text + 7)
    1730 
    1731 #define mpathset()      ((vmpath.flags & VUNSET) == 0)
    1732 
    1733 /*
    1734  * The parsefile structure pointed to by the global variable parsefile
    1735  * contains information about the current file being read.
    1736  */
    1737 struct redirtab {
    1738     struct redirtab *next;
    1739     int renamed[10];
    1740     int nullredirs;
    1741 };
    1742 
    1743 static struct redirtab *redirlist;
    1744 static int nullredirs;
    1745 static int preverrout_fd;   /* save fd2 before print debug if xflag is set. */
    1746 
    1747 #define VTABSIZE 39
    1748 
    1749 static struct var *vartab[VTABSIZE];
    1750 
     1977#if ENABLE_ASH_MAIL
     1978# define mailval()      (vmail.var_text + 5)
     1979# define mpathval()     (vmpath.var_text + 9)
     1980# define mpathset()     ((vmpath.flags & VUNSET) == 0)
     1981#endif
     1982#define pathval()       (vpath.var_text + 5)
     1983#define ps1val()        (vps1.var_text + 4)
     1984#define ps2val()        (vps2.var_text + 4)
     1985#define ps4val()        (vps4.var_text + 4)
     1986#if ENABLE_ASH_GETOPTS
     1987# define optindval()    (voptind.var_text + 7)
     1988#endif
     1989
     1990#if ENABLE_ASH_GETOPTS
     1991static void FAST_FUNC
     1992getoptsreset(const char *value)
     1993{
     1994    shellparam.optind = number(value);
     1995    shellparam.optoff = -1;
     1996}
     1997#endif
     1998
     1999/* math.h has these, otherwise define our private copies */
     2000#if !ENABLE_SH_MATH_SUPPORT
    17512001#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
    17522002#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
    1753 
    1754 /*
    1755  * Return of a legal variable name (a letter or underscore followed by zero or
    1756  * more letters, underscores, and digits).
    1757  */
    1758 static char *
     2003/*
     2004 * Return the pointer to the first char which is not part of a legal variable name
     2005 * (a letter or underscore followed by letters, underscores, and digits).
     2006 */
     2007static const char*
    17592008endofname(const char *name)
    17602009{
    1761     char *p;
    1762 
    1763     p = (char *) name;
    1764     if (!is_name(*p))
    1765         return p;
    1766     while (*++p) {
    1767         if (!is_in_name(*p))
     2010    if (!is_name(*name))
     2011        return name;
     2012    while (*++name) {
     2013        if (!is_in_name(*name))
    17682014            break;
    17692015    }
    1770     return p;
    1771 }
     2016    return name;
     2017}
     2018#endif
    17722019
    17732020/*
     
    17952042}
    17962043
    1797 static int
    1798 varequal(const char *a, const char *b)
    1799 {
    1800     return !varcmp(a, b);
    1801 }
    1802 
    18032044/*
    18042045 * Find the appropriate entry in the hash table from the name.
     
    18352076     */
    18362077#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
    1837     vps1.text = "PS1=\\w \\$ ";
     2078    vps1.var_text = "PS1=\\w \\$ ";
    18382079#else
    18392080    if (!geteuid())
    1840         vps1.text = "PS1=# ";
     2081        vps1.var_text = "PS1=# ";
    18412082#endif
    18422083    vp = varinit;
    18432084    end = vp + ARRAY_SIZE(varinit);
    18442085    do {
    1845         vpp = hashvar(vp->text);
     2086        vpp = hashvar(vp->var_text);
    18462087        vp->next = *vpp;
    18472088        *vpp = vp;
     
    18532094{
    18542095    for (; *vpp; vpp = &(*vpp)->next) {
    1855         if (varequal((*vpp)->text, name)) {
     2096        if (varcmp((*vpp)->var_text, name) == 0) {
    18562097            break;
    18572098        }
     
    18632104 * Find the value of a variable.  Returns NULL if not set.
    18642105 */
    1865 static char *
     2106static const char* FAST_FUNC
    18662107lookupvar(const char *name)
    18672108{
     
    18702111    v = *findvar(hashvar(name), name);
    18712112    if (v) {
    1872 #ifdef DYNAMIC_VAR
     2113#if ENABLE_ASH_RANDOM_SUPPORT
    18732114    /*
    18742115     * Dynamic variables are implemented roughly the same way they are
     
    18772118     * lookup will no longer happen at that point. -- PFM.
    18782119     */
    1879         if ((v->flags & VDYNAMIC))
    1880             (*v->func)(NULL);
     2120        if (v->flags & VDYNAMIC)
     2121            v->var_func(NULL);
    18812122#endif
    18822123        if (!(v->flags & VUNSET))
    1883             return strchrnul(v->text, '=') + 1;
     2124            return var_end(v->var_text);
    18842125    }
    18852126    return NULL;
     
    18892130 * Search the environment of a builtin command.
    18902131 */
    1891 static char *
     2132static const char *
    18922133bltinlookup(const char *name)
    18932134{
     
    18952136
    18962137    for (sp = cmdenviron; sp; sp = sp->next) {
    1897         if (varequal(sp->text, name))
    1898             return strchrnul(sp->text, '=') + 1;
     2138        if (varcmp(sp->text, name) == 0)
     2139            return var_end(sp->text);
    18992140    }
    19002141    return lookupvar(name);
     
    19222163            if (flags & VNOSAVE)
    19232164                free(s);
    1924             n = vp->text;
     2165            n = vp->var_text;
    19252166            ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
    19262167        }
     
    19292170            return;
    19302171
    1931         if (vp->func && (flags & VNOFUNC) == 0)
    1932             (*vp->func)(strchrnul(s, '=') + 1);
    1933 
    1934         if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
    1935             free((char*)vp->text);
     2172        if (vp->var_func && !(flags & VNOFUNC))
     2173            vp->var_func(var_end(s));
     2174
     2175        if (!(vp->flags & (VTEXTFIXED|VSTACK)))
     2176            free((char*)vp->var_text);
    19362177
    19372178        flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
    19382179    } else {
     2180        /* variable s is not found */
    19392181        if (flags & VNOSET)
    19402182            return;
    1941         /* not found */
    1942         vp = ckmalloc(sizeof(*vp));
     2183        vp = ckzalloc(sizeof(*vp));
    19432184        vp->next = *vpp;
    1944         vp->func = NULL;
     2185        /*vp->func = NULL; - ckzalloc did it */
    19452186        *vpp = vp;
    19462187    }
    19472188    if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
    19482189        s = ckstrdup(s);
    1949     vp->text = s;
     2190    vp->var_text = s;
    19502191    vp->flags = flags;
    19512192}
     
    19582199setvar(const char *name, const char *val, int flags)
    19592200{
    1960     char *p, *q;
     2201    const char *q;
     2202    char *p;
     2203    char *nameeq;
    19612204    size_t namelen;
    1962     char *nameeq;
    19632205    size_t vallen;
    19642206
     
    19742216        vallen = strlen(val);
    19752217    }
     2218
    19762219    INT_OFF;
    19772220    nameeq = ckmalloc(namelen + vallen + 2);
     
    19842227    setvareq(nameeq, flags | VNOSAVE);
    19852228    INT_ON;
     2229}
     2230
     2231static void FAST_FUNC
     2232setvar2(const char *name, const char *val)
     2233{
     2234    setvar(name, val, 0);
    19862235}
    19872236
     
    20312280        if (flags & VREADONLY)
    20322281            goto out;
    2033 #ifdef DYNAMIC_VAR
     2282#if ENABLE_ASH_RANDOM_SUPPORT
    20342283        vp->flags &= ~VDYNAMIC;
    20352284#endif
     
    20392288            INT_OFF;
    20402289            if ((flags & (VTEXTFIXED|VSTACK)) == 0)
    2041                 free((char*)vp->text);
     2290                free((char*)vp->var_text);
    20422291            *vpp = vp->next;
    20432292            free(vp);
     
    20912340                if (ep == stackstrend())
    20922341                    ep = growstackstr();
    2093                 *ep++ = (char *) vp->text;
     2342                *ep++ = (char*)vp->var_text;
    20942343            }
    20952344        }
     
    21072356 *
    21082357 * The variable path (passed by reference) should be set to the start
    2109  * of the path before the first call; padvance will update
    2110  * this value as it proceeds.  Successive calls to padvance will return
     2358 * of the path before the first call; path_advance will update
     2359 * this value as it proceeds.  Successive calls to path_advance will return
    21112360 * the possible path expansions in sequence.  If an option (indicated by
    21122361 * a percent sign) appears in the path entry then the global variable
     
    21142363 * NULL.
    21152364 */
    2116 static const char *pathopt;     /* set by padvance */
     2365static const char *pathopt;     /* set by path_advance */
    21172366
    21182367static char *
    2119 padvance(const char **path, const char *name)
     2368path_advance(const char **path, const char *name)
    21202369{
    21212370    const char *p;
     
    21272376        return NULL;
    21282377    start = *path;
    2129     for (p = start; *p && *p != ':' && *p != '%'; p++);
     2378    for (p = start; *p && *p != ':' && *p != '%'; p++)
     2379        continue;
    21302380    len = p - start + strlen(name) + 2;     /* "2" is for '/' and '\0' */
    21312381    while (stackblocksize() < len)
     
    21412391    if (*p == '%') {
    21422392        pathopt = ++p;
    2143         while (*p && *p != ':') p++;
     2393        while (*p && *p != ':')
     2394            p++;
    21442395    }
    21452396    if (*p == ':')
     
    21532404/* ============ Prompt */
    21542405
    2155 static int doprompt;                   /* if set, prompt the user */
    2156 static int needprompt;                 /* true if interactive and at start of line */
     2406static smallint doprompt;                   /* if set, prompt the user */
     2407static smallint needprompt;                 /* true if interactive and at start of line */
    21572408
    21582409#if ENABLE_FEATURE_EDITING
     
    21852436
    21862437static void
    2187 setprompt(int whichprompt)
     2438setprompt_if(smallint do_set, int whichprompt)
    21882439{
    21892440    const char *prompt;
    2190 #if ENABLE_ASH_EXPAND_PRMT
    2191     struct stackmark smark;
    2192 #endif
     2441    IF_ASH_EXPAND_PRMT(struct stackmark smark;)
     2442
     2443    if (!do_set)
     2444        return;
    21932445
    21942446    needprompt = 0;
     
    22202472#define CD_PRINT 2
    22212473
    2222 static int docd(const char *, int);
    2223 
    2224 static char *curdir = nullstr;          /* current working directory */
    2225 static char *physdir = nullstr;         /* physical working directory */
    2226 
    22272474static int
    22282475cdopt(void)
     
    22322479
    22332480    j = 'L';
    2234     while ((i = nextopt("LP"))) {
     2481    while ((i = nextopt("LP")) != '\0') {
    22352482        if (i != j) {
    22362483            flags ^= CD_PHYSICAL;
     
    22622509    }
    22632510    new = makestrspace(strlen(dir) + 2, new);
    2264     lim = stackblock() + 1;
     2511    lim = (char *)stackblock() + 1;
    22652512    if (*dir != '/') {
    22662513        if (new[-1] != '/')
     
    23112558getpwd(void)
    23122559{
    2313     char *dir = getcwd(0, 0);
     2560    char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
    23142561    return dir ? dir : nullstr;
    23152562}
     
    23552602docd(const char *dest, int flags)
    23562603{
    2357     const char *dir = 0;
     2604    const char *dir = NULL;
    23582605    int err;
    23592606
     
    23762623}
    23772624
    2378 static int
    2379 cdcmd(int argc, char **argv)
     2625static int FAST_FUNC
     2626cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    23802627{
    23812628    const char *dest;
     
    23892636    dest = *argptr;
    23902637    if (!dest)
    2391         dest = bltinlookup(homestr);
     2638        dest = bltinlookup("HOME");
    23922639    else if (LONE_DASH(dest)) {
    23932640        dest = bltinlookup("OLDPWD");
     
    24222669    do {
    24232670        c = *path;
    2424         p = padvance(&path, dest);
     2671        p = path_advance(&path, dest);
    24252672        if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
    24262673            if (c && c != ':')
     
    24362683 out:
    24372684    if (flags & CD_PRINT)
    2438         out1fmt(snlfmt, curdir);
     2685        out1fmt("%s\n", curdir);
    24392686    return 0;
    24402687}
    24412688
    2442 static int
    2443 pwdcmd(int argc, char **argv)
     2689static int FAST_FUNC
     2690pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    24442691{
    24452692    int flags;
     
    24522699        dir = physdir;
    24532700    }
    2454     out1fmt(snlfmt, dir);
     2701    out1fmt("%s\n", dir);
    24552702    return 0;
    24562703}
     
    24592706/* ============ ... */
    24602707
    2461 #define IBUFSIZ (BUFSIZ + 1)
    2462 #define basebuf bb_common_bufsiz1       /* buffer for top level input file */
     2708
     2709#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
    24632710
    24642711/* Syntax classes */
    2465 #define CWORD 0                 /* character is nothing special */
    2466 #define CNL 1                   /* newline character */
    2467 #define CBACK 2                 /* a backslash character */
    2468 #define CSQUOTE              /* single quote */
    2469 #define CDQUOTE              /* double quote */
     2712#define CWORD     0             /* character is nothing special */
     2713#define CNL       1             /* newline character */
     2714#define CBACK     2             /* a backslash character */
     2715#define CSQUOTE   3             /* single quote */
     2716#define CDQUOTE   4             /* double quote */
    24702717#define CENDQUOTE 5             /* a terminating quote */
    2471 #define CBQUOTE              /* backwards single quote */
    2472 #define CVAR 7                  /* a dollar sign */
    2473 #define CENDVAR              /* a '}' character */
    2474 #define CLP 9                   /* a left paren in arithmetic */
    2475 #define CRP 10                  /* a right paren in arithmetic */
     2718#define CBQUOTE   6             /* backwards single quote */
     2719#define CVAR      7             /* a dollar sign */
     2720#define CENDVAR   8             /* a '}' character */
     2721#define CLP       9             /* a left paren in arithmetic */
     2722#define CRP      10             /* a right paren in arithmetic */
    24762723#define CENDFILE 11             /* end of file */
    2477 #define CCTL 12                 /* like CWORD, except it must be escaped */
    2478 #define CSPCL 13                /* these terminate a word */
    2479 #define CIGN 14                 /* character should be ignored */
    2480 
     2724#define CCTL     12             /* like CWORD, except it must be escaped */
     2725#define CSPCL    13             /* these terminate a word */
     2726#define CIGN     14             /* character should be ignored */
     2727
     2728#define PEOF     256
    24812729#if ENABLE_ASH_ALIAS
    2482 #define SYNBASE 130
    2483 #define PEOF -130
    2484 #define PEOA -129
    2485 #define PEOA_OR_PEOF PEOA
     2730# define PEOA    257
     2731#endif
     2732
     2733#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
     2734
     2735#if ENABLE_SH_MATH_SUPPORT
     2736# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
    24862737#else
    2487 #define SYNBASE 129
    2488 #define PEOF -129
    2489 #define PEOA_OR_PEOF PEOF
    2490 #endif
    2491 
    2492 /* number syntax index */
     2738# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
     2739#endif
     2740static const uint16_t S_I_T[] = {
     2741#if ENABLE_ASH_ALIAS
     2742    SIT_ITEM(CSPCL   , CIGN     , CIGN , CIGN   ),    /* 0, PEOA */
     2743#endif
     2744    SIT_ITEM(CSPCL   , CWORD    , CWORD, CWORD  ),    /* 1, ' ' */
     2745    SIT_ITEM(CNL     , CNL      , CNL  , CNL    ),    /* 2, \n */
     2746    SIT_ITEM(CWORD   , CCTL     , CCTL , CWORD  ),    /* 3, !*-/:=?[]~ */
     2747    SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD  ),    /* 4, '"' */
     2748    SIT_ITEM(CVAR    , CVAR     , CWORD, CVAR   ),    /* 5, $ */
     2749    SIT_ITEM(CSQUOTE , CWORD    , CENDQUOTE, CWORD),  /* 6, "'" */
     2750    SIT_ITEM(CSPCL   , CWORD    , CWORD, CLP    ),    /* 7, ( */
     2751    SIT_ITEM(CSPCL   , CWORD    , CWORD, CRP    ),    /* 8, ) */
     2752    SIT_ITEM(CBACK   , CBACK    , CCTL , CBACK  ),    /* 9, \ */
     2753    SIT_ITEM(CBQUOTE , CBQUOTE  , CWORD, CBQUOTE),    /* 10, ` */
     2754    SIT_ITEM(CENDVAR , CENDVAR  , CWORD, CENDVAR),    /* 11, } */
     2755#if !USE_SIT_FUNCTION
     2756    SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
     2757    SIT_ITEM(CWORD   , CWORD    , CWORD, CWORD  ),    /* 13, 0-9A-Za-z */
     2758    SIT_ITEM(CCTL    , CCTL     , CCTL , CCTL   )     /* 14, CTLESC ... */
     2759#endif
     2760#undef SIT_ITEM
     2761};
     2762/* Constants below must match table above */
     2763enum {
     2764#if ENABLE_ASH_ALIAS
     2765    CSPCL_CIGN_CIGN_CIGN               , /*  0 */
     2766#endif
     2767    CSPCL_CWORD_CWORD_CWORD            , /*  1 */
     2768    CNL_CNL_CNL_CNL                    , /*  2 */
     2769    CWORD_CCTL_CCTL_CWORD              , /*  3 */
     2770    CDQUOTE_CENDQUOTE_CWORD_CWORD      , /*  4 */
     2771    CVAR_CVAR_CWORD_CVAR               , /*  5 */
     2772    CSQUOTE_CWORD_CENDQUOTE_CWORD      , /*  6 */
     2773    CSPCL_CWORD_CWORD_CLP              , /*  7 */
     2774    CSPCL_CWORD_CWORD_CRP              , /*  8 */
     2775    CBACK_CBACK_CCTL_CBACK             , /*  9 */
     2776    CBQUOTE_CBQUOTE_CWORD_CBQUOTE      , /* 10 */
     2777    CENDVAR_CENDVAR_CWORD_CENDVAR      , /* 11 */
     2778    CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
     2779    CWORD_CWORD_CWORD_CWORD            , /* 13 */
     2780    CCTL_CCTL_CCTL_CCTL                , /* 14 */
     2781};
     2782
     2783/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
     2784 * caller must ensure proper cast on it if c is *char_ptr!
     2785 */
     2786/* Values for syntax param */
    24932787#define BASESYNTAX 0    /* not in quotes */
    24942788#define DQSYNTAX   1    /* in double quotes */
    24952789#define SQSYNTAX   2    /* in single quotes */
    24962790#define ARISYNTAX  3    /* in arithmetic */
    2497 
    2498 #if ENABLE_ASH_OPTIMIZE_FOR_SIZE
    2499 #define USE_SIT_FUNCTION
    2500 #endif
    2501 
    2502 #if ENABLE_ASH_MATH_SUPPORT
    2503 static const char S_I_T[][4] = {
    2504 #if ENABLE_ASH_ALIAS
    2505     { CSPCL, CIGN, CIGN, CIGN },            /* 0, PEOA */
    2506 #endif
    2507     { CSPCL, CWORD, CWORD, CWORD },         /* 1, ' ' */
    2508     { CNL, CNL, CNL, CNL },                 /* 2, \n */
    2509     { CWORD, CCTL, CCTL, CWORD },           /* 3, !*-/:=?[]~ */
    2510     { CDQUOTE, CENDQUOTE, CWORD, CWORD },   /* 4, '"' */
    2511     { CVAR, CVAR, CWORD, CVAR },            /* 5, $ */
    2512     { CSQUOTE, CWORD, CENDQUOTE, CWORD },   /* 6, "'" */
    2513     { CSPCL, CWORD, CWORD, CLP },           /* 7, ( */
    2514     { CSPCL, CWORD, CWORD, CRP },           /* 8, ) */
    2515     { CBACK, CBACK, CCTL, CBACK },          /* 9, \ */
    2516     { CBQUOTE, CBQUOTE, CWORD, CBQUOTE },   /* 10, ` */
    2517     { CENDVAR, CENDVAR, CWORD, CENDVAR },   /* 11, } */
    2518 #ifndef USE_SIT_FUNCTION
    2519     { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
    2520     { CWORD, CWORD, CWORD, CWORD },         /* 13, 0-9A-Za-z */
    2521     { CCTL, CCTL, CCTL, CCTL }              /* 14, CTLESC ... */
    2522 #endif
    2523 };
    2524 #else
    2525 static const char S_I_T[][3] = {
    2526 #if ENABLE_ASH_ALIAS
    2527     { CSPCL, CIGN, CIGN },                  /* 0, PEOA */
    2528 #endif
    2529     { CSPCL, CWORD, CWORD },                /* 1, ' ' */
    2530     { CNL, CNL, CNL },                      /* 2, \n */
    2531     { CWORD, CCTL, CCTL },                  /* 3, !*-/:=?[]~ */
    2532     { CDQUOTE, CENDQUOTE, CWORD },          /* 4, '"' */
    2533     { CVAR, CVAR, CWORD },                  /* 5, $ */
    2534     { CSQUOTE, CWORD, CENDQUOTE },          /* 6, "'" */
    2535     { CSPCL, CWORD, CWORD },                /* 7, ( */
    2536     { CSPCL, CWORD, CWORD },                /* 8, ) */
    2537     { CBACK, CBACK, CCTL },                 /* 9, \ */
    2538     { CBQUOTE, CBQUOTE, CWORD },            /* 10, ` */
    2539     { CENDVAR, CENDVAR, CWORD },            /* 11, } */
    2540 #ifndef USE_SIT_FUNCTION
    2541     { CENDFILE, CENDFILE, CENDFILE },       /* 12, PEOF */
    2542     { CWORD, CWORD, CWORD },                /* 13, 0-9A-Za-z */
    2543     { CCTL, CCTL, CCTL }                    /* 14, CTLESC ... */
    2544 #endif
    2545 };
    2546 #endif /* ASH_MATH_SUPPORT */
    2547 
    2548 #ifdef USE_SIT_FUNCTION
     2791#define PSSYNTAX   4    /* prompt. never passed to SIT() */
     2792
     2793#if USE_SIT_FUNCTION
    25492794
    25502795static int
     
    25522797{
    25532798    static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
    2554 #if ENABLE_ASH_ALIAS
    2555     static const char syntax_index_table[] ALIGN1 = {
     2799# if ENABLE_ASH_ALIAS
     2800    static const uint8_t syntax_index_table[] ALIGN1 = {
    25562801        1, 2, 1, 3, 4, 5, 1, 6,         /* "\t\n !\"$&'" */
    25572802        7, 8, 3, 3, 3, 3, 1, 1,         /* "()*-/:;<" */
     
    25592804        11, 3                           /* "}~" */
    25602805    };
    2561 #else
    2562     static const char syntax_index_table[] ALIGN1 = {
     2806# else
     2807    static const uint8_t syntax_index_table[] ALIGN1 = {
    25632808        0, 1, 0, 2, 3, 4, 0, 5,         /* "\t\n !\"$&'" */
    25642809        6, 7, 2, 2, 2, 2, 0, 0,         /* "()*-/:;<" */
     
    25662811        10, 2                           /* "}~" */
    25672812    };
    2568 #endif
     2813# endif
    25692814    const char *s;
    25702815    int indx;
    25712816
    2572     if (c == PEOF)          /* 2^8+2 */
     2817    if (c == PEOF)
    25732818        return CENDFILE;
    2574 #if ENABLE_ASH_ALIAS
    2575     if (c == PEOA)          /* 2^8+1 */
     2819# if ENABLE_ASH_ALIAS
     2820    if (c == PEOA)
    25762821        indx = 0;
    25772822    else
    2578 #endif
    2579 #define U_C(c) ((unsigned char)(c))
    2580 
    2581     if ((unsigned char)c >= (unsigned char)(CTLESC)
    2582      && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
    2583     ) {
    2584         return CCTL;
    2585     } else {
    2586         s = strchr(spec_symbls, c);
    2587         if (s == NULL || *s == '\0')
     2823# endif
     2824    {
     2825        /* Cast is purely for paranoia here,
     2826         * just in case someone passed signed char to us */
     2827        if ((unsigned char)c >= CTL_FIRST
     2828         && (unsigned char)c <= CTL_LAST
     2829        ) {
     2830            return CCTL;
     2831        }
     2832        s = strchrnul(spec_symbls, c);
     2833        if (*s == '\0')
    25882834            return CWORD;
    2589         indx = syntax_index_table[(s - spec_symbls)];
    2590     }
    2591     return S_I_T[indx][syntax];
     2835        indx = syntax_index_table[s - spec_symbls];
     2836    }
     2837    return (S_I_T[indx] >> (syntax*4)) & 0xf;
    25922838}
    25932839
    25942840#else   /* !USE_SIT_FUNCTION */
    25952841
    2596 #if ENABLE_ASH_ALIAS
    2597 #define CSPCL_CIGN_CIGN_CIGN                     0
    2598 #define CSPCL_CWORD_CWORD_CWORD                  1
    2599 #define CNL_CNL_CNL_CNL                          2
    2600 #define CWORD_CCTL_CCTL_CWORD                    3
    2601 #define CDQUOTE_CENDQUOTE_CWORD_CWORD            4
    2602 #define CVAR_CVAR_CWORD_CVAR                     5
    2603 #define CSQUOTE_CWORD_CENDQUOTE_CWORD            6
    2604 #define CSPCL_CWORD_CWORD_CLP                    7
    2605 #define CSPCL_CWORD_CWORD_CRP                    8
    2606 #define CBACK_CBACK_CCTL_CBACK                   9
    2607 #define CBQUOTE_CBQUOTE_CWORD_CBQUOTE           10
    2608 #define CENDVAR_CENDVAR_CWORD_CENDVAR           11
    2609 #define CENDFILE_CENDFILE_CENDFILE_CENDFILE     12
    2610 #define CWORD_CWORD_CWORD_CWORD                 13
    2611 #define CCTL_CCTL_CCTL_CCTL                     14
    2612 #else
    2613 #define CSPCL_CWORD_CWORD_CWORD                  0
    2614 #define CNL_CNL_CNL_CNL                          1
    2615 #define CWORD_CCTL_CCTL_CWORD                    2
    2616 #define CDQUOTE_CENDQUOTE_CWORD_CWORD            3
    2617 #define CVAR_CVAR_CWORD_CVAR                     4
    2618 #define CSQUOTE_CWORD_CENDQUOTE_CWORD            5
    2619 #define CSPCL_CWORD_CWORD_CLP                    6
    2620 #define CSPCL_CWORD_CWORD_CRP                    7
    2621 #define CBACK_CBACK_CCTL_CBACK                   8
    2622 #define CBQUOTE_CBQUOTE_CWORD_CBQUOTE            9
    2623 #define CENDVAR_CENDVAR_CWORD_CENDVAR           10
    2624 #define CENDFILE_CENDFILE_CENDFILE_CENDFILE     11
    2625 #define CWORD_CWORD_CWORD_CWORD                 12
    2626 #define CCTL_CCTL_CCTL_CCTL                     13
    2627 #endif
    2628 
    2629 static const char syntax_index_table[258] = {
     2842static const uint8_t syntax_index_table[] = {
    26302843    /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
    2631     /*   0  PEOF */      CENDFILE_CENDFILE_CENDFILE_CENDFILE,
    2632 #if ENABLE_ASH_ALIAS
    2633     /*   1  PEOA */      CSPCL_CIGN_CIGN_CIGN,
    2634 #endif
    2635     /*   2  -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
    2636     /*   3  -127 CTLESC       */ CCTL_CCTL_CCTL_CCTL,
    2637     /*   4  -126 CTLVAR       */ CCTL_CCTL_CCTL_CCTL,
    2638     /*   5  -125 CTLENDVAR    */ CCTL_CCTL_CCTL_CCTL,
    2639     /*   6  -124 CTLBACKQ     */ CCTL_CCTL_CCTL_CCTL,
    2640     /*   7  -123 CTLQUOTE     */ CCTL_CCTL_CCTL_CCTL,
    2641     /*   8  -122 CTLARI       */ CCTL_CCTL_CCTL_CCTL,
    2642     /*   9  -121 CTLENDARI    */ CCTL_CCTL_CCTL_CCTL,
    2643     /*  10  -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
    2644     /*  11  -119      */ CWORD_CWORD_CWORD_CWORD,
    2645     /*  12  -118      */ CWORD_CWORD_CWORD_CWORD,
    2646     /*  13  -117      */ CWORD_CWORD_CWORD_CWORD,
    2647     /*  14  -116      */ CWORD_CWORD_CWORD_CWORD,
    2648     /*  15  -115      */ CWORD_CWORD_CWORD_CWORD,
    2649     /*  16  -114      */ CWORD_CWORD_CWORD_CWORD,
    2650     /*  17  -113      */ CWORD_CWORD_CWORD_CWORD,
    2651     /*  18  -112      */ CWORD_CWORD_CWORD_CWORD,
    2652     /*  19  -111      */ CWORD_CWORD_CWORD_CWORD,
    2653     /*  20  -110      */ CWORD_CWORD_CWORD_CWORD,
    2654     /*  21  -109      */ CWORD_CWORD_CWORD_CWORD,
    2655     /*  22  -108      */ CWORD_CWORD_CWORD_CWORD,
    2656     /*  23  -107      */ CWORD_CWORD_CWORD_CWORD,
    2657     /*  24  -106      */ CWORD_CWORD_CWORD_CWORD,
    2658     /*  25  -105      */ CWORD_CWORD_CWORD_CWORD,
    2659     /*  26  -104      */ CWORD_CWORD_CWORD_CWORD,
    2660     /*  27  -103      */ CWORD_CWORD_CWORD_CWORD,
    2661     /*  28  -102      */ CWORD_CWORD_CWORD_CWORD,
    2662     /*  29  -101      */ CWORD_CWORD_CWORD_CWORD,
    2663     /*  30  -100      */ CWORD_CWORD_CWORD_CWORD,
    2664     /*  31   -99      */ CWORD_CWORD_CWORD_CWORD,
    2665     /*  32   -98      */ CWORD_CWORD_CWORD_CWORD,
    2666     /*  33   -97      */ CWORD_CWORD_CWORD_CWORD,
    2667     /*  34   -96      */ CWORD_CWORD_CWORD_CWORD,
    2668     /*  35   -95      */ CWORD_CWORD_CWORD_CWORD,
    2669     /*  36   -94      */ CWORD_CWORD_CWORD_CWORD,
    2670     /*  37   -93      */ CWORD_CWORD_CWORD_CWORD,
    2671     /*  38   -92      */ CWORD_CWORD_CWORD_CWORD,
    2672     /*  39   -91      */ CWORD_CWORD_CWORD_CWORD,
    2673     /*  40   -90      */ CWORD_CWORD_CWORD_CWORD,
    2674     /*  41   -89      */ CWORD_CWORD_CWORD_CWORD,
    2675     /*  42   -88      */ CWORD_CWORD_CWORD_CWORD,
    2676     /*  43   -87      */ CWORD_CWORD_CWORD_CWORD,
    2677     /*  44   -86      */ CWORD_CWORD_CWORD_CWORD,
    2678     /*  45   -85      */ CWORD_CWORD_CWORD_CWORD,
    2679     /*  46   -84      */ CWORD_CWORD_CWORD_CWORD,
    2680     /*  47   -83      */ CWORD_CWORD_CWORD_CWORD,
    2681     /*  48   -82      */ CWORD_CWORD_CWORD_CWORD,
    2682     /*  49   -81      */ CWORD_CWORD_CWORD_CWORD,
    2683     /*  50   -80      */ CWORD_CWORD_CWORD_CWORD,
    2684     /*  51   -79      */ CWORD_CWORD_CWORD_CWORD,
    2685     /*  52   -78      */ CWORD_CWORD_CWORD_CWORD,
    2686     /*  53   -77      */ CWORD_CWORD_CWORD_CWORD,
    2687     /*  54   -76      */ CWORD_CWORD_CWORD_CWORD,
    2688     /*  55   -75      */ CWORD_CWORD_CWORD_CWORD,
    2689     /*  56   -74      */ CWORD_CWORD_CWORD_CWORD,
    2690     /*  57   -73      */ CWORD_CWORD_CWORD_CWORD,
    2691     /*  58   -72      */ CWORD_CWORD_CWORD_CWORD,
    2692     /*  59   -71      */ CWORD_CWORD_CWORD_CWORD,
    2693     /*  60   -70      */ CWORD_CWORD_CWORD_CWORD,
    2694     /*  61   -69      */ CWORD_CWORD_CWORD_CWORD,
    2695     /*  62   -68      */ CWORD_CWORD_CWORD_CWORD,
    2696     /*  63   -67      */ CWORD_CWORD_CWORD_CWORD,
    2697     /*  64   -66      */ CWORD_CWORD_CWORD_CWORD,
    2698     /*  65   -65      */ CWORD_CWORD_CWORD_CWORD,
    2699     /*  66   -64      */ CWORD_CWORD_CWORD_CWORD,
    2700     /*  67   -63      */ CWORD_CWORD_CWORD_CWORD,
    2701     /*  68   -62      */ CWORD_CWORD_CWORD_CWORD,
    2702     /*  69   -61      */ CWORD_CWORD_CWORD_CWORD,
    2703     /*  70   -60      */ CWORD_CWORD_CWORD_CWORD,
    2704     /*  71   -59      */ CWORD_CWORD_CWORD_CWORD,
    2705     /*  72   -58      */ CWORD_CWORD_CWORD_CWORD,
    2706     /*  73   -57      */ CWORD_CWORD_CWORD_CWORD,
    2707     /*  74   -56      */ CWORD_CWORD_CWORD_CWORD,
    2708     /*  75   -55      */ CWORD_CWORD_CWORD_CWORD,
    2709     /*  76   -54      */ CWORD_CWORD_CWORD_CWORD,
    2710     /*  77   -53      */ CWORD_CWORD_CWORD_CWORD,
    2711     /*  78   -52      */ CWORD_CWORD_CWORD_CWORD,
    2712     /*  79   -51      */ CWORD_CWORD_CWORD_CWORD,
    2713     /*  80   -50      */ CWORD_CWORD_CWORD_CWORD,
    2714     /*  81   -49      */ CWORD_CWORD_CWORD_CWORD,
    2715     /*  82   -48      */ CWORD_CWORD_CWORD_CWORD,
    2716     /*  83   -47      */ CWORD_CWORD_CWORD_CWORD,
    2717     /*  84   -46      */ CWORD_CWORD_CWORD_CWORD,
    2718     /*  85   -45      */ CWORD_CWORD_CWORD_CWORD,
    2719     /*  86   -44      */ CWORD_CWORD_CWORD_CWORD,
    2720     /*  87   -43      */ CWORD_CWORD_CWORD_CWORD,
    2721     /*  88   -42      */ CWORD_CWORD_CWORD_CWORD,
    2722     /*  89   -41      */ CWORD_CWORD_CWORD_CWORD,
    2723     /*  90   -40      */ CWORD_CWORD_CWORD_CWORD,
    2724     /*  91   -39      */ CWORD_CWORD_CWORD_CWORD,
    2725     /*  92   -38      */ CWORD_CWORD_CWORD_CWORD,
    2726     /*  93   -37      */ CWORD_CWORD_CWORD_CWORD,
    2727     /*  94   -36      */ CWORD_CWORD_CWORD_CWORD,
    2728     /*  95   -35      */ CWORD_CWORD_CWORD_CWORD,
    2729     /*  96   -34      */ CWORD_CWORD_CWORD_CWORD,
    2730     /*  97   -33      */ CWORD_CWORD_CWORD_CWORD,
    2731     /*  98   -32      */ CWORD_CWORD_CWORD_CWORD,
    2732     /*  99   -31      */ CWORD_CWORD_CWORD_CWORD,
    2733     /* 100   -30      */ CWORD_CWORD_CWORD_CWORD,
    2734     /* 101   -29      */ CWORD_CWORD_CWORD_CWORD,
    2735     /* 102   -28      */ CWORD_CWORD_CWORD_CWORD,
    2736     /* 103   -27      */ CWORD_CWORD_CWORD_CWORD,
    2737     /* 104   -26      */ CWORD_CWORD_CWORD_CWORD,
    2738     /* 105   -25      */ CWORD_CWORD_CWORD_CWORD,
    2739     /* 106   -24      */ CWORD_CWORD_CWORD_CWORD,
    2740     /* 107   -23      */ CWORD_CWORD_CWORD_CWORD,
    2741     /* 108   -22      */ CWORD_CWORD_CWORD_CWORD,
    2742     /* 109   -21      */ CWORD_CWORD_CWORD_CWORD,
    2743     /* 110   -20      */ CWORD_CWORD_CWORD_CWORD,
    2744     /* 111   -19      */ CWORD_CWORD_CWORD_CWORD,
    2745     /* 112   -18      */ CWORD_CWORD_CWORD_CWORD,
    2746     /* 113   -17      */ CWORD_CWORD_CWORD_CWORD,
    2747     /* 114   -16      */ CWORD_CWORD_CWORD_CWORD,
    2748     /* 115   -15      */ CWORD_CWORD_CWORD_CWORD,
    2749     /* 116   -14      */ CWORD_CWORD_CWORD_CWORD,
    2750     /* 117   -13      */ CWORD_CWORD_CWORD_CWORD,
    2751     /* 118   -12      */ CWORD_CWORD_CWORD_CWORD,
    2752     /* 119   -11      */ CWORD_CWORD_CWORD_CWORD,
    2753     /* 120   -10      */ CWORD_CWORD_CWORD_CWORD,
    2754     /* 121    -9      */ CWORD_CWORD_CWORD_CWORD,
    2755     /* 122    -8      */ CWORD_CWORD_CWORD_CWORD,
    2756     /* 123    -7      */ CWORD_CWORD_CWORD_CWORD,
    2757     /* 124    -6      */ CWORD_CWORD_CWORD_CWORD,
    2758     /* 125    -5      */ CWORD_CWORD_CWORD_CWORD,
    2759     /* 126    -4      */ CWORD_CWORD_CWORD_CWORD,
    2760     /* 127    -3      */ CWORD_CWORD_CWORD_CWORD,
    2761     /* 128    -2      */ CWORD_CWORD_CWORD_CWORD,
    2762     /* 129    -1      */ CWORD_CWORD_CWORD_CWORD,
    2763     /* 130     0      */ CWORD_CWORD_CWORD_CWORD,
    2764     /* 131     1      */ CWORD_CWORD_CWORD_CWORD,
    2765     /* 132     2      */ CWORD_CWORD_CWORD_CWORD,
    2766     /* 133     3      */ CWORD_CWORD_CWORD_CWORD,
    2767     /* 134     4      */ CWORD_CWORD_CWORD_CWORD,
    2768     /* 135     5      */ CWORD_CWORD_CWORD_CWORD,
    2769     /* 136     6      */ CWORD_CWORD_CWORD_CWORD,
    2770     /* 137     7      */ CWORD_CWORD_CWORD_CWORD,
    2771     /* 138     8      */ CWORD_CWORD_CWORD_CWORD,
    2772     /* 139     9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
    2773     /* 140    10 "\n" */ CNL_CNL_CNL_CNL,
    2774     /* 141    11      */ CWORD_CWORD_CWORD_CWORD,
    2775     /* 142    12      */ CWORD_CWORD_CWORD_CWORD,
    2776     /* 143    13      */ CWORD_CWORD_CWORD_CWORD,
    2777     /* 144    14      */ CWORD_CWORD_CWORD_CWORD,
    2778     /* 145    15      */ CWORD_CWORD_CWORD_CWORD,
    2779     /* 146    16      */ CWORD_CWORD_CWORD_CWORD,
    2780     /* 147    17      */ CWORD_CWORD_CWORD_CWORD,
    2781     /* 148    18      */ CWORD_CWORD_CWORD_CWORD,
    2782     /* 149    19      */ CWORD_CWORD_CWORD_CWORD,
    2783     /* 150    20      */ CWORD_CWORD_CWORD_CWORD,
    2784     /* 151    21      */ CWORD_CWORD_CWORD_CWORD,
    2785     /* 152    22      */ CWORD_CWORD_CWORD_CWORD,
    2786     /* 153    23      */ CWORD_CWORD_CWORD_CWORD,
    2787     /* 154    24      */ CWORD_CWORD_CWORD_CWORD,
    2788     /* 155    25      */ CWORD_CWORD_CWORD_CWORD,
    2789     /* 156    26      */ CWORD_CWORD_CWORD_CWORD,
    2790     /* 157    27      */ CWORD_CWORD_CWORD_CWORD,
    2791     /* 158    28      */ CWORD_CWORD_CWORD_CWORD,
    2792     /* 159    29      */ CWORD_CWORD_CWORD_CWORD,
    2793     /* 160    30      */ CWORD_CWORD_CWORD_CWORD,
    2794     /* 161    31      */ CWORD_CWORD_CWORD_CWORD,
    2795     /* 162    32  " " */ CSPCL_CWORD_CWORD_CWORD,
    2796     /* 163    33  "!" */ CWORD_CCTL_CCTL_CWORD,
    2797     /* 164    34  """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
    2798     /* 165    35  "#" */ CWORD_CWORD_CWORD_CWORD,
    2799     /* 166    36  "$" */ CVAR_CVAR_CWORD_CVAR,
    2800     /* 167    37  "%" */ CWORD_CWORD_CWORD_CWORD,
    2801     /* 168    38  "&" */ CSPCL_CWORD_CWORD_CWORD,
    2802     /* 169    39  "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
    2803     /* 170    40  "(" */ CSPCL_CWORD_CWORD_CLP,
    2804     /* 171    41  ")" */ CSPCL_CWORD_CWORD_CRP,
    2805     /* 172    42  "*" */ CWORD_CCTL_CCTL_CWORD,
    2806     /* 173    43  "+" */ CWORD_CWORD_CWORD_CWORD,
    2807     /* 174    44  "," */ CWORD_CWORD_CWORD_CWORD,
    2808     /* 175    45  "-" */ CWORD_CCTL_CCTL_CWORD,
    2809     /* 176    46  "." */ CWORD_CWORD_CWORD_CWORD,
    2810     /* 177    47  "/" */ CWORD_CCTL_CCTL_CWORD,
    2811     /* 178    48  "0" */ CWORD_CWORD_CWORD_CWORD,
    2812     /* 179    49  "1" */ CWORD_CWORD_CWORD_CWORD,
    2813     /* 180    50  "2" */ CWORD_CWORD_CWORD_CWORD,
    2814     /* 181    51  "3" */ CWORD_CWORD_CWORD_CWORD,
    2815     /* 182    52  "4" */ CWORD_CWORD_CWORD_CWORD,
    2816     /* 183    53  "5" */ CWORD_CWORD_CWORD_CWORD,
    2817     /* 184    54  "6" */ CWORD_CWORD_CWORD_CWORD,
    2818     /* 185    55  "7" */ CWORD_CWORD_CWORD_CWORD,
    2819     /* 186    56  "8" */ CWORD_CWORD_CWORD_CWORD,
    2820     /* 187    57  "9" */ CWORD_CWORD_CWORD_CWORD,
    2821     /* 188    58  ":" */ CWORD_CCTL_CCTL_CWORD,
    2822     /* 189    59  ";" */ CSPCL_CWORD_CWORD_CWORD,
    2823     /* 190    60  "<" */ CSPCL_CWORD_CWORD_CWORD,
    2824     /* 191    61  "=" */ CWORD_CCTL_CCTL_CWORD,
    2825     /* 192    62  ">" */ CSPCL_CWORD_CWORD_CWORD,
    2826     /* 193    63  "?" */ CWORD_CCTL_CCTL_CWORD,
    2827     /* 194    64  "@" */ CWORD_CWORD_CWORD_CWORD,
    2828     /* 195    65  "A" */ CWORD_CWORD_CWORD_CWORD,
    2829     /* 196    66  "B" */ CWORD_CWORD_CWORD_CWORD,
    2830     /* 197    67  "C" */ CWORD_CWORD_CWORD_CWORD,
    2831     /* 198    68  "D" */ CWORD_CWORD_CWORD_CWORD,
    2832     /* 199    69  "E" */ CWORD_CWORD_CWORD_CWORD,
    2833     /* 200    70  "F" */ CWORD_CWORD_CWORD_CWORD,
    2834     /* 201    71  "G" */ CWORD_CWORD_CWORD_CWORD,
    2835     /* 202    72  "H" */ CWORD_CWORD_CWORD_CWORD,
    2836     /* 203    73  "I" */ CWORD_CWORD_CWORD_CWORD,
    2837     /* 204    74  "J" */ CWORD_CWORD_CWORD_CWORD,
    2838     /* 205    75  "K" */ CWORD_CWORD_CWORD_CWORD,
    2839     /* 206    76  "L" */ CWORD_CWORD_CWORD_CWORD,
    2840     /* 207    77  "M" */ CWORD_CWORD_CWORD_CWORD,
    2841     /* 208    78  "N" */ CWORD_CWORD_CWORD_CWORD,
    2842     /* 209    79  "O" */ CWORD_CWORD_CWORD_CWORD,
    2843     /* 210    80  "P" */ CWORD_CWORD_CWORD_CWORD,
    2844     /* 211    81  "Q" */ CWORD_CWORD_CWORD_CWORD,
    2845     /* 212    82  "R" */ CWORD_CWORD_CWORD_CWORD,
    2846     /* 213    83  "S" */ CWORD_CWORD_CWORD_CWORD,
    2847     /* 214    84  "T" */ CWORD_CWORD_CWORD_CWORD,
    2848     /* 215    85  "U" */ CWORD_CWORD_CWORD_CWORD,
    2849     /* 216    86  "V" */ CWORD_CWORD_CWORD_CWORD,
    2850     /* 217    87  "W" */ CWORD_CWORD_CWORD_CWORD,
    2851     /* 218    88  "X" */ CWORD_CWORD_CWORD_CWORD,
    2852     /* 219    89  "Y" */ CWORD_CWORD_CWORD_CWORD,
    2853     /* 220    90  "Z" */ CWORD_CWORD_CWORD_CWORD,
    2854     /* 221    91  "[" */ CWORD_CCTL_CCTL_CWORD,
    2855     /* 222    92  "\" */ CBACK_CBACK_CCTL_CBACK,
    2856     /* 223    93  "]" */ CWORD_CCTL_CCTL_CWORD,
    2857     /* 224    94  "^" */ CWORD_CWORD_CWORD_CWORD,
    2858     /* 225    95  "_" */ CWORD_CWORD_CWORD_CWORD,
    2859     /* 226    96  "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
    2860     /* 227    97  "a" */ CWORD_CWORD_CWORD_CWORD,
    2861     /* 228    98  "b" */ CWORD_CWORD_CWORD_CWORD,
    2862     /* 229    99  "c" */ CWORD_CWORD_CWORD_CWORD,
    2863     /* 230   100  "d" */ CWORD_CWORD_CWORD_CWORD,
    2864     /* 231   101  "e" */ CWORD_CWORD_CWORD_CWORD,
    2865     /* 232   102  "f" */ CWORD_CWORD_CWORD_CWORD,
    2866     /* 233   103  "g" */ CWORD_CWORD_CWORD_CWORD,
    2867     /* 234   104  "h" */ CWORD_CWORD_CWORD_CWORD,
    2868     /* 235   105  "i" */ CWORD_CWORD_CWORD_CWORD,
    2869     /* 236   106  "j" */ CWORD_CWORD_CWORD_CWORD,
    2870     /* 237   107  "k" */ CWORD_CWORD_CWORD_CWORD,
    2871     /* 238   108  "l" */ CWORD_CWORD_CWORD_CWORD,
    2872     /* 239   109  "m" */ CWORD_CWORD_CWORD_CWORD,
    2873     /* 240   110  "n" */ CWORD_CWORD_CWORD_CWORD,
    2874     /* 241   111  "o" */ CWORD_CWORD_CWORD_CWORD,
    2875     /* 242   112  "p" */ CWORD_CWORD_CWORD_CWORD,
    2876     /* 243   113  "q" */ CWORD_CWORD_CWORD_CWORD,
    2877     /* 244   114  "r" */ CWORD_CWORD_CWORD_CWORD,
    2878     /* 245   115  "s" */ CWORD_CWORD_CWORD_CWORD,
    2879     /* 246   116  "t" */ CWORD_CWORD_CWORD_CWORD,
    2880     /* 247   117  "u" */ CWORD_CWORD_CWORD_CWORD,
    2881     /* 248   118  "v" */ CWORD_CWORD_CWORD_CWORD,
    2882     /* 249   119  "w" */ CWORD_CWORD_CWORD_CWORD,
    2883     /* 250   120  "x" */ CWORD_CWORD_CWORD_CWORD,
    2884     /* 251   121  "y" */ CWORD_CWORD_CWORD_CWORD,
    2885     /* 252   122  "z" */ CWORD_CWORD_CWORD_CWORD,
    2886     /* 253   123  "{" */ CWORD_CWORD_CWORD_CWORD,
    2887     /* 254   124  "|" */ CSPCL_CWORD_CWORD_CWORD,
    2888     /* 255   125  "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
    2889     /* 256   126  "~" */ CWORD_CCTL_CCTL_CWORD,
    2890     /* 257   127      */ CWORD_CWORD_CWORD_CWORD,
     2844    /*   0      */ CWORD_CWORD_CWORD_CWORD,
     2845    /*   1      */ CWORD_CWORD_CWORD_CWORD,
     2846    /*   2      */ CWORD_CWORD_CWORD_CWORD,
     2847    /*   3      */ CWORD_CWORD_CWORD_CWORD,
     2848    /*   4      */ CWORD_CWORD_CWORD_CWORD,
     2849    /*   5      */ CWORD_CWORD_CWORD_CWORD,
     2850    /*   6      */ CWORD_CWORD_CWORD_CWORD,
     2851    /*   7      */ CWORD_CWORD_CWORD_CWORD,
     2852    /*   8      */ CWORD_CWORD_CWORD_CWORD,
     2853    /*   9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
     2854    /*  10 "\n" */ CNL_CNL_CNL_CNL,
     2855    /*  11      */ CWORD_CWORD_CWORD_CWORD,
     2856    /*  12      */ CWORD_CWORD_CWORD_CWORD,
     2857    /*  13      */ CWORD_CWORD_CWORD_CWORD,
     2858    /*  14      */ CWORD_CWORD_CWORD_CWORD,
     2859    /*  15      */ CWORD_CWORD_CWORD_CWORD,
     2860    /*  16      */ CWORD_CWORD_CWORD_CWORD,
     2861    /*  17      */ CWORD_CWORD_CWORD_CWORD,
     2862    /*  18      */ CWORD_CWORD_CWORD_CWORD,
     2863    /*  19      */ CWORD_CWORD_CWORD_CWORD,
     2864    /*  20      */ CWORD_CWORD_CWORD_CWORD,
     2865    /*  21      */ CWORD_CWORD_CWORD_CWORD,
     2866    /*  22      */ CWORD_CWORD_CWORD_CWORD,
     2867    /*  23      */ CWORD_CWORD_CWORD_CWORD,
     2868    /*  24      */ CWORD_CWORD_CWORD_CWORD,
     2869    /*  25      */ CWORD_CWORD_CWORD_CWORD,
     2870    /*  26      */ CWORD_CWORD_CWORD_CWORD,
     2871    /*  27      */ CWORD_CWORD_CWORD_CWORD,
     2872    /*  28      */ CWORD_CWORD_CWORD_CWORD,
     2873    /*  29      */ CWORD_CWORD_CWORD_CWORD,
     2874    /*  30      */ CWORD_CWORD_CWORD_CWORD,
     2875    /*  31      */ CWORD_CWORD_CWORD_CWORD,
     2876    /*  32  " " */ CSPCL_CWORD_CWORD_CWORD,
     2877    /*  33  "!" */ CWORD_CCTL_CCTL_CWORD,
     2878    /*  34  """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
     2879    /*  35  "#" */ CWORD_CWORD_CWORD_CWORD,
     2880    /*  36  "$" */ CVAR_CVAR_CWORD_CVAR,
     2881    /*  37  "%" */ CWORD_CWORD_CWORD_CWORD,
     2882    /*  38  "&" */ CSPCL_CWORD_CWORD_CWORD,
     2883    /*  39  "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
     2884    /*  40  "(" */ CSPCL_CWORD_CWORD_CLP,
     2885    /*  41  ")" */ CSPCL_CWORD_CWORD_CRP,
     2886    /*  42  "*" */ CWORD_CCTL_CCTL_CWORD,
     2887    /*  43  "+" */ CWORD_CWORD_CWORD_CWORD,
     2888    /*  44  "," */ CWORD_CWORD_CWORD_CWORD,
     2889    /*  45  "-" */ CWORD_CCTL_CCTL_CWORD,
     2890    /*  46  "." */ CWORD_CWORD_CWORD_CWORD,
     2891    /*  47  "/" */ CWORD_CCTL_CCTL_CWORD,
     2892    /*  48  "0" */ CWORD_CWORD_CWORD_CWORD,
     2893    /*  49  "1" */ CWORD_CWORD_CWORD_CWORD,
     2894    /*  50  "2" */ CWORD_CWORD_CWORD_CWORD,
     2895    /*  51  "3" */ CWORD_CWORD_CWORD_CWORD,
     2896    /*  52  "4" */ CWORD_CWORD_CWORD_CWORD,
     2897    /*  53  "5" */ CWORD_CWORD_CWORD_CWORD,
     2898    /*  54  "6" */ CWORD_CWORD_CWORD_CWORD,
     2899    /*  55  "7" */ CWORD_CWORD_CWORD_CWORD,
     2900    /*  56  "8" */ CWORD_CWORD_CWORD_CWORD,
     2901    /*  57  "9" */ CWORD_CWORD_CWORD_CWORD,
     2902    /*  58  ":" */ CWORD_CCTL_CCTL_CWORD,
     2903    /*  59  ";" */ CSPCL_CWORD_CWORD_CWORD,
     2904    /*  60  "<" */ CSPCL_CWORD_CWORD_CWORD,
     2905    /*  61  "=" */ CWORD_CCTL_CCTL_CWORD,
     2906    /*  62  ">" */ CSPCL_CWORD_CWORD_CWORD,
     2907    /*  63  "?" */ CWORD_CCTL_CCTL_CWORD,
     2908    /*  64  "@" */ CWORD_CWORD_CWORD_CWORD,
     2909    /*  65  "A" */ CWORD_CWORD_CWORD_CWORD,
     2910    /*  66  "B" */ CWORD_CWORD_CWORD_CWORD,
     2911    /*  67  "C" */ CWORD_CWORD_CWORD_CWORD,
     2912    /*  68  "D" */ CWORD_CWORD_CWORD_CWORD,
     2913    /*  69  "E" */ CWORD_CWORD_CWORD_CWORD,
     2914    /*  70  "F" */ CWORD_CWORD_CWORD_CWORD,
     2915    /*  71  "G" */ CWORD_CWORD_CWORD_CWORD,
     2916    /*  72  "H" */ CWORD_CWORD_CWORD_CWORD,
     2917    /*  73  "I" */ CWORD_CWORD_CWORD_CWORD,
     2918    /*  74  "J" */ CWORD_CWORD_CWORD_CWORD,
     2919    /*  75  "K" */ CWORD_CWORD_CWORD_CWORD,
     2920    /*  76  "L" */ CWORD_CWORD_CWORD_CWORD,
     2921    /*  77  "M" */ CWORD_CWORD_CWORD_CWORD,
     2922    /*  78  "N" */ CWORD_CWORD_CWORD_CWORD,
     2923    /*  79  "O" */ CWORD_CWORD_CWORD_CWORD,
     2924    /*  80  "P" */ CWORD_CWORD_CWORD_CWORD,
     2925    /*  81  "Q" */ CWORD_CWORD_CWORD_CWORD,
     2926    /*  82  "R" */ CWORD_CWORD_CWORD_CWORD,
     2927    /*  83  "S" */ CWORD_CWORD_CWORD_CWORD,
     2928    /*  84  "T" */ CWORD_CWORD_CWORD_CWORD,
     2929    /*  85  "U" */ CWORD_CWORD_CWORD_CWORD,
     2930    /*  86  "V" */ CWORD_CWORD_CWORD_CWORD,
     2931    /*  87  "W" */ CWORD_CWORD_CWORD_CWORD,
     2932    /*  88  "X" */ CWORD_CWORD_CWORD_CWORD,
     2933    /*  89  "Y" */ CWORD_CWORD_CWORD_CWORD,
     2934    /*  90  "Z" */ CWORD_CWORD_CWORD_CWORD,
     2935    /*  91  "[" */ CWORD_CCTL_CCTL_CWORD,
     2936    /*  92  "\" */ CBACK_CBACK_CCTL_CBACK,
     2937    /*  93  "]" */ CWORD_CCTL_CCTL_CWORD,
     2938    /*  94  "^" */ CWORD_CWORD_CWORD_CWORD,
     2939    /*  95  "_" */ CWORD_CWORD_CWORD_CWORD,
     2940    /*  96  "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
     2941    /*  97  "a" */ CWORD_CWORD_CWORD_CWORD,
     2942    /*  98  "b" */ CWORD_CWORD_CWORD_CWORD,
     2943    /*  99  "c" */ CWORD_CWORD_CWORD_CWORD,
     2944    /* 100  "d" */ CWORD_CWORD_CWORD_CWORD,
     2945    /* 101  "e" */ CWORD_CWORD_CWORD_CWORD,
     2946    /* 102  "f" */ CWORD_CWORD_CWORD_CWORD,
     2947    /* 103  "g" */ CWORD_CWORD_CWORD_CWORD,
     2948    /* 104  "h" */ CWORD_CWORD_CWORD_CWORD,
     2949    /* 105  "i" */ CWORD_CWORD_CWORD_CWORD,
     2950    /* 106  "j" */ CWORD_CWORD_CWORD_CWORD,
     2951    /* 107  "k" */ CWORD_CWORD_CWORD_CWORD,
     2952    /* 108  "l" */ CWORD_CWORD_CWORD_CWORD,
     2953    /* 109  "m" */ CWORD_CWORD_CWORD_CWORD,
     2954    /* 110  "n" */ CWORD_CWORD_CWORD_CWORD,
     2955    /* 111  "o" */ CWORD_CWORD_CWORD_CWORD,
     2956    /* 112  "p" */ CWORD_CWORD_CWORD_CWORD,
     2957    /* 113  "q" */ CWORD_CWORD_CWORD_CWORD,
     2958    /* 114  "r" */ CWORD_CWORD_CWORD_CWORD,
     2959    /* 115  "s" */ CWORD_CWORD_CWORD_CWORD,
     2960    /* 116  "t" */ CWORD_CWORD_CWORD_CWORD,
     2961    /* 117  "u" */ CWORD_CWORD_CWORD_CWORD,
     2962    /* 118  "v" */ CWORD_CWORD_CWORD_CWORD,
     2963    /* 119  "w" */ CWORD_CWORD_CWORD_CWORD,
     2964    /* 120  "x" */ CWORD_CWORD_CWORD_CWORD,
     2965    /* 121  "y" */ CWORD_CWORD_CWORD_CWORD,
     2966    /* 122  "z" */ CWORD_CWORD_CWORD_CWORD,
     2967    /* 123  "{" */ CWORD_CWORD_CWORD_CWORD,
     2968    /* 124  "|" */ CSPCL_CWORD_CWORD_CWORD,
     2969    /* 125  "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
     2970    /* 126  "~" */ CWORD_CCTL_CCTL_CWORD,
     2971    /* 127  del */ CWORD_CWORD_CWORD_CWORD,
     2972    /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
     2973    /* 129 CTLESC       */ CCTL_CCTL_CCTL_CCTL,
     2974    /* 130 CTLVAR       */ CCTL_CCTL_CCTL_CCTL,
     2975    /* 131 CTLENDVAR    */ CCTL_CCTL_CCTL_CCTL,
     2976    /* 132 CTLBACKQ     */ CCTL_CCTL_CCTL_CCTL,
     2977    /* 133 CTLQUOTE     */ CCTL_CCTL_CCTL_CCTL,
     2978    /* 134 CTLARI       */ CCTL_CCTL_CCTL_CCTL,
     2979    /* 135 CTLENDARI    */ CCTL_CCTL_CCTL_CCTL,
     2980    /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
     2981    /* 137      */ CWORD_CWORD_CWORD_CWORD,
     2982    /* 138      */ CWORD_CWORD_CWORD_CWORD,
     2983    /* 139      */ CWORD_CWORD_CWORD_CWORD,
     2984    /* 140      */ CWORD_CWORD_CWORD_CWORD,
     2985    /* 141      */ CWORD_CWORD_CWORD_CWORD,
     2986    /* 142      */ CWORD_CWORD_CWORD_CWORD,
     2987    /* 143      */ CWORD_CWORD_CWORD_CWORD,
     2988    /* 144      */ CWORD_CWORD_CWORD_CWORD,
     2989    /* 145      */ CWORD_CWORD_CWORD_CWORD,
     2990    /* 146      */ CWORD_CWORD_CWORD_CWORD,
     2991    /* 147      */ CWORD_CWORD_CWORD_CWORD,
     2992    /* 148      */ CWORD_CWORD_CWORD_CWORD,
     2993    /* 149      */ CWORD_CWORD_CWORD_CWORD,
     2994    /* 150      */ CWORD_CWORD_CWORD_CWORD,
     2995    /* 151      */ CWORD_CWORD_CWORD_CWORD,
     2996    /* 152      */ CWORD_CWORD_CWORD_CWORD,
     2997    /* 153      */ CWORD_CWORD_CWORD_CWORD,
     2998    /* 154      */ CWORD_CWORD_CWORD_CWORD,
     2999    /* 155      */ CWORD_CWORD_CWORD_CWORD,
     3000    /* 156      */ CWORD_CWORD_CWORD_CWORD,
     3001    /* 157      */ CWORD_CWORD_CWORD_CWORD,
     3002    /* 158      */ CWORD_CWORD_CWORD_CWORD,
     3003    /* 159      */ CWORD_CWORD_CWORD_CWORD,
     3004    /* 160      */ CWORD_CWORD_CWORD_CWORD,
     3005    /* 161      */ CWORD_CWORD_CWORD_CWORD,
     3006    /* 162      */ CWORD_CWORD_CWORD_CWORD,
     3007    /* 163      */ CWORD_CWORD_CWORD_CWORD,
     3008    /* 164      */ CWORD_CWORD_CWORD_CWORD,
     3009    /* 165      */ CWORD_CWORD_CWORD_CWORD,
     3010    /* 166      */ CWORD_CWORD_CWORD_CWORD,
     3011    /* 167      */ CWORD_CWORD_CWORD_CWORD,
     3012    /* 168      */ CWORD_CWORD_CWORD_CWORD,
     3013    /* 169      */ CWORD_CWORD_CWORD_CWORD,
     3014    /* 170      */ CWORD_CWORD_CWORD_CWORD,
     3015    /* 171      */ CWORD_CWORD_CWORD_CWORD,
     3016    /* 172      */ CWORD_CWORD_CWORD_CWORD,
     3017    /* 173      */ CWORD_CWORD_CWORD_CWORD,
     3018    /* 174      */ CWORD_CWORD_CWORD_CWORD,
     3019    /* 175      */ CWORD_CWORD_CWORD_CWORD,
     3020    /* 176      */ CWORD_CWORD_CWORD_CWORD,
     3021    /* 177      */ CWORD_CWORD_CWORD_CWORD,
     3022    /* 178      */ CWORD_CWORD_CWORD_CWORD,
     3023    /* 179      */ CWORD_CWORD_CWORD_CWORD,
     3024    /* 180      */ CWORD_CWORD_CWORD_CWORD,
     3025    /* 181      */ CWORD_CWORD_CWORD_CWORD,
     3026    /* 182      */ CWORD_CWORD_CWORD_CWORD,
     3027    /* 183      */ CWORD_CWORD_CWORD_CWORD,
     3028    /* 184      */ CWORD_CWORD_CWORD_CWORD,
     3029    /* 185      */ CWORD_CWORD_CWORD_CWORD,
     3030    /* 186      */ CWORD_CWORD_CWORD_CWORD,
     3031    /* 187      */ CWORD_CWORD_CWORD_CWORD,
     3032    /* 188      */ CWORD_CWORD_CWORD_CWORD,
     3033    /* 189      */ CWORD_CWORD_CWORD_CWORD,
     3034    /* 190      */ CWORD_CWORD_CWORD_CWORD,
     3035    /* 191      */ CWORD_CWORD_CWORD_CWORD,
     3036    /* 192      */ CWORD_CWORD_CWORD_CWORD,
     3037    /* 193      */ CWORD_CWORD_CWORD_CWORD,
     3038    /* 194      */ CWORD_CWORD_CWORD_CWORD,
     3039    /* 195      */ CWORD_CWORD_CWORD_CWORD,
     3040    /* 196      */ CWORD_CWORD_CWORD_CWORD,
     3041    /* 197      */ CWORD_CWORD_CWORD_CWORD,
     3042    /* 198      */ CWORD_CWORD_CWORD_CWORD,
     3043    /* 199      */ CWORD_CWORD_CWORD_CWORD,
     3044    /* 200      */ CWORD_CWORD_CWORD_CWORD,
     3045    /* 201      */ CWORD_CWORD_CWORD_CWORD,
     3046    /* 202      */ CWORD_CWORD_CWORD_CWORD,
     3047    /* 203      */ CWORD_CWORD_CWORD_CWORD,
     3048    /* 204      */ CWORD_CWORD_CWORD_CWORD,
     3049    /* 205      */ CWORD_CWORD_CWORD_CWORD,
     3050    /* 206      */ CWORD_CWORD_CWORD_CWORD,
     3051    /* 207      */ CWORD_CWORD_CWORD_CWORD,
     3052    /* 208      */ CWORD_CWORD_CWORD_CWORD,
     3053    /* 209      */ CWORD_CWORD_CWORD_CWORD,
     3054    /* 210      */ CWORD_CWORD_CWORD_CWORD,
     3055    /* 211      */ CWORD_CWORD_CWORD_CWORD,
     3056    /* 212      */ CWORD_CWORD_CWORD_CWORD,
     3057    /* 213      */ CWORD_CWORD_CWORD_CWORD,
     3058    /* 214      */ CWORD_CWORD_CWORD_CWORD,
     3059    /* 215      */ CWORD_CWORD_CWORD_CWORD,
     3060    /* 216      */ CWORD_CWORD_CWORD_CWORD,
     3061    /* 217      */ CWORD_CWORD_CWORD_CWORD,
     3062    /* 218      */ CWORD_CWORD_CWORD_CWORD,
     3063    /* 219      */ CWORD_CWORD_CWORD_CWORD,
     3064    /* 220      */ CWORD_CWORD_CWORD_CWORD,
     3065    /* 221      */ CWORD_CWORD_CWORD_CWORD,
     3066    /* 222      */ CWORD_CWORD_CWORD_CWORD,
     3067    /* 223      */ CWORD_CWORD_CWORD_CWORD,
     3068    /* 224      */ CWORD_CWORD_CWORD_CWORD,
     3069    /* 225      */ CWORD_CWORD_CWORD_CWORD,
     3070    /* 226      */ CWORD_CWORD_CWORD_CWORD,
     3071    /* 227      */ CWORD_CWORD_CWORD_CWORD,
     3072    /* 228      */ CWORD_CWORD_CWORD_CWORD,
     3073    /* 229      */ CWORD_CWORD_CWORD_CWORD,
     3074    /* 230      */ CWORD_CWORD_CWORD_CWORD,
     3075    /* 231      */ CWORD_CWORD_CWORD_CWORD,
     3076    /* 232      */ CWORD_CWORD_CWORD_CWORD,
     3077    /* 233      */ CWORD_CWORD_CWORD_CWORD,
     3078    /* 234      */ CWORD_CWORD_CWORD_CWORD,
     3079    /* 235      */ CWORD_CWORD_CWORD_CWORD,
     3080    /* 236      */ CWORD_CWORD_CWORD_CWORD,
     3081    /* 237      */ CWORD_CWORD_CWORD_CWORD,
     3082    /* 238      */ CWORD_CWORD_CWORD_CWORD,
     3083    /* 239      */ CWORD_CWORD_CWORD_CWORD,
     3084    /* 230      */ CWORD_CWORD_CWORD_CWORD,
     3085    /* 241      */ CWORD_CWORD_CWORD_CWORD,
     3086    /* 242      */ CWORD_CWORD_CWORD_CWORD,
     3087    /* 243      */ CWORD_CWORD_CWORD_CWORD,
     3088    /* 244      */ CWORD_CWORD_CWORD_CWORD,
     3089    /* 245      */ CWORD_CWORD_CWORD_CWORD,
     3090    /* 246      */ CWORD_CWORD_CWORD_CWORD,
     3091    /* 247      */ CWORD_CWORD_CWORD_CWORD,
     3092    /* 248      */ CWORD_CWORD_CWORD_CWORD,
     3093    /* 249      */ CWORD_CWORD_CWORD_CWORD,
     3094    /* 250      */ CWORD_CWORD_CWORD_CWORD,
     3095    /* 251      */ CWORD_CWORD_CWORD_CWORD,
     3096    /* 252      */ CWORD_CWORD_CWORD_CWORD,
     3097    /* 253      */ CWORD_CWORD_CWORD_CWORD,
     3098    /* 254      */ CWORD_CWORD_CWORD_CWORD,
     3099    /* 255      */ CWORD_CWORD_CWORD_CWORD,
     3100    /* PEOF */     CENDFILE_CENDFILE_CENDFILE_CENDFILE,
     3101# if ENABLE_ASH_ALIAS
     3102    /* PEOA */     CSPCL_CIGN_CIGN_CIGN,
     3103# endif
    28913104};
    28923105
    2893 #define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
    2894 
    2895 #endif  /* USE_SIT_FUNCTION */
     3106# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
     3107
     3108#endif  /* !USE_SIT_FUNCTION */
    28963109
    28973110
     
    29023115#define ALIASINUSE 1
    29033116#define ALIASDEAD  2
    2904 
    2905 #define ATABSIZE 39
    29063117
    29073118struct alias {
     
    29123123};
    29133124
    2914 static struct alias *atab[ATABSIZE];
     3125
     3126static struct alias **atab; // [ATABSIZE];
     3127#define INIT_G_alias() do { \
     3128    atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
     3129} while (0)
     3130
    29153131
    29163132static struct alias **
     
    29833199    } else {
    29843200        /* not found */
    2985         ap = ckmalloc(sizeof(struct alias));
     3201        ap = ckzalloc(sizeof(struct alias));
    29863202        ap->name = ckstrdup(name);
    29873203        ap->val = ckstrdup(val);
    2988         ap->flag = 0;
    2989         ap->next = 0;
     3204        /*ap->flag = 0; - ckzalloc did it */
     3205        /*ap->next = NULL;*/
    29903206        *app = ap;
    29913207    }
     
    30383254 * TODO - sort output
    30393255 */
    3040 static int
    3041 aliascmd(int argc, char **argv)
     3256static int FAST_FUNC
     3257aliascmd(int argc UNUSED_PARAM, char **argv)
    30423258{
    30433259    char *n, *v;
     
    30453261    struct alias *ap;
    30463262
    3047     if (argc == 1) {
     3263    if (!argv[1]) {
    30483264        int i;
    30493265
    3050         for (i = 0; i < ATABSIZE; i++)
     3266        for (i = 0; i < ATABSIZE; i++) {
    30513267            for (ap = atab[i]; ap; ap = ap->next) {
    30523268                printalias(ap);
    30533269            }
     3270        }
    30543271        return 0;
    30553272    }
     
    30723289}
    30733290
    3074 static int
    3075 unaliascmd(int argc, char **argv)
     3291static int FAST_FUNC
     3292unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    30763293{
    30773294    int i;
     
    30993316
    31003317/* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
    3101 #define FORK_FG 0
    3102 #define FORK_BG 1
     3318#define FORK_FG    0
     3319#define FORK_BG    1
    31033320#define FORK_NOJOB 2
    31043321
    31053322/* mode flags for showjob(s) */
    3106 #define SHOW_PGID       0x01    /* only show pgid - for jobs -p */
    3107 #define SHOW_PID        0x04    /* include process pid */
    3108 #define SHOW_CHANGED    0x08    /* only jobs whose state has changed */
     3323#define SHOW_ONLY_PGID  0x01    /* show only pgid (jobs -p) */
     3324#define SHOW_PIDS       0x02    /* show individual pids, not just one line per job */
     3325#define SHOW_CHANGED    0x04    /* only jobs whose state has changed */
    31093326
    31103327/*
     
    31143331 * array of pids.
    31153332 */
    3116 
    31173333struct procstat {
    3118     pid_t   pid;            /* process id */
    3119     int     status;         /* last process status from wait() */
    3120     char    *cmd;           /* text of command being run */
     3334    pid_t   ps_pid;         /* process id */
     3335    int     ps_status;      /* last process status from wait() */
     3336    char    *ps_cmd;        /* text of command being run */
    31213337};
    31223338
     
    31433359};
    31443360
    3145 static pid_t backgndpid;        /* pid of last background process */
    3146 static smallint job_warning;    /* user was warned about stopped jobs (can be 2, 1 or 0). */
    3147 
    3148 static struct job *makejob(union node *, int);
     3361static struct job *makejob(/*union node *,*/ int);
    31493362static int forkshell(struct job *, union node *, int);
    31503363static int waitforjob(struct job *);
    31513364
    31523365#if !JOBS
    3153 enum { jobctl = 0 };
     3366enum { doing_jobctl = 0 };
    31543367#define setjobctl(on) do {} while (0)
    31553368#else
    3156 static smallint jobctl;              /* true if doing job control */
     3369static smallint doing_jobctl; //references:8
    31573370static void setjobctl(int);
    31583371#endif
     3372
     3373/*
     3374 * Ignore a signal.
     3375 */
     3376static void
     3377ignoresig(int signo)
     3378{
     3379    /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
     3380    if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
     3381        /* No, need to do it */
     3382        signal(signo, SIG_IGN);
     3383    }
     3384    sigmode[signo - 1] = S_HARD_IGN;
     3385}
     3386
     3387/*
     3388 * Only one usage site - in setsignal()
     3389 */
     3390static void
     3391signal_handler(int signo)
     3392{
     3393    gotsig[signo - 1] = 1;
     3394
     3395    if (signo == SIGINT && !trap[SIGINT]) {
     3396        if (!suppress_int) {
     3397            pending_sig = 0;
     3398            raise_interrupt(); /* does not return */
     3399        }
     3400        pending_int = 1;
     3401    } else {
     3402        pending_sig = signo;
     3403    }
     3404}
    31593405
    31603406/*
     
    31653411setsignal(int signo)
    31663412{
    3167     int action;
    3168     char *t, tsig;
     3413    char *t;
     3414    char cur_act, new_act;
    31693415    struct sigaction act;
    31703416
    31713417    t = trap[signo];
    3172     if (t == NULL)
    3173         action = S_DFL;
    3174     else if (*t != '\0')
    3175         action = S_CATCH;
    3176     else
    3177         action = S_IGN;
    3178     if (rootshell && action == S_DFL) {
     3418    new_act = S_DFL;
     3419    if (t != NULL) { /* trap for this sig is set */
     3420        new_act = S_CATCH;
     3421        if (t[0] == '\0') /* trap is "": ignore this sig */
     3422            new_act = S_IGN;
     3423    }
     3424
     3425    if (rootshell && new_act == S_DFL) {
    31793426        switch (signo) {
    31803427        case SIGINT:
    31813428            if (iflag || minusc || sflag == 0)
    3182                 action = S_CATCH;
     3429                new_act = S_CATCH;
    31833430            break;
    31843431        case SIGQUIT:
     
    31873434                break;
    31883435#endif
    3189             /* FALLTHROUGH */
     3436            /* man bash:
     3437             * "In all cases, bash ignores SIGQUIT. Non-builtin
     3438             * commands run by bash have signal handlers
     3439             * set to the values inherited by the shell
     3440             * from its parent". */
     3441            new_act = S_IGN;
     3442            break;
    31903443        case SIGTERM:
    31913444            if (iflag)
    3192                 action = S_IGN;
     3445                new_act = S_IGN;
    31933446            break;
    31943447#if JOBS
     
    31963449        case SIGTTOU:
    31973450            if (mflag)
    3198                 action = S_IGN;
     3451                new_act = S_IGN;
    31993452            break;
    32003453#endif
    32013454        }
    32023455    }
     3456//TODO: if !rootshell, we reset SIGQUIT to DFL,
     3457//whereas we have to restore it to what shell got on entry
     3458//from the parent. See comment above
    32033459
    32043460    t = &sigmode[signo - 1];
    3205     tsig = *t;
    3206     if (tsig == 0) {
    3207         /*
    3208          * current setting unknown
    3209          */
    3210         if (sigaction(signo, 0, &act) == -1) {
    3211             /*
    3212              * Pretend it worked; maybe we should give a warning
    3213              * here, but other shells don't. We don't alter
    3214              * sigmode, so that we retry every time.
    3215              */
     3461    cur_act = *t;
     3462    if (cur_act == 0) {
     3463        /* current setting is not yet known */
     3464        if (sigaction(signo, NULL, &act)) {
     3465            /* pretend it worked; maybe we should give a warning,
     3466             * but other shells don't. We don't alter sigmode,
     3467             * so we retry every time.
     3468             * btw, in Linux it never fails. --vda */
    32163469            return;
    32173470        }
    32183471        if (act.sa_handler == SIG_IGN) {
     3472            cur_act = S_HARD_IGN;
    32193473            if (mflag
    32203474             && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
    32213475            ) {
    3222                 tsig = S_IGN;   /* don't hard ignore these */
    3223             } else
    3224                 tsig = S_HARD_IGN;
    3225         } else {
    3226             tsig = S_RESET; /* force to be set */
    3227         }
    3228     }
    3229     if (tsig == S_HARD_IGN || tsig == action)
     3476                cur_act = S_IGN;   /* don't hard ignore these */
     3477            }
     3478        }
     3479    }
     3480    if (cur_act == S_HARD_IGN || cur_act == new_act)
    32303481        return;
    3231     switch (action) {
     3482
     3483    act.sa_handler = SIG_DFL;
     3484    switch (new_act) {
    32323485    case S_CATCH:
    3233         act.sa_handler = onsig;
     3486        act.sa_handler = signal_handler;
     3487        act.sa_flags = 0; /* matters only if !DFL and !IGN */
     3488        sigfillset(&act.sa_mask); /* ditto */
    32343489        break;
    32353490    case S_IGN:
    32363491        act.sa_handler = SIG_IGN;
    32373492        break;
    3238     default:
    3239         act.sa_handler = SIG_DFL;
    3240     }
    3241     *t = action;
    3242     act.sa_flags = 0;
    3243     sigfillset(&act.sa_mask);
    3244     sigaction(signo, &act, 0);
     3493    }
     3494    sigaction_set(signo, &act);
     3495
     3496    *t = new_act;
    32453497}
    32463498
     
    32513503
    32523504/* mode flags for dowait */
    3253 #define DOWAIT_NORMAL 0
    3254 #define DOWAIT_BLOCK 1
     3505#define DOWAIT_NONBLOCK WNOHANG
     3506#define DOWAIT_BLOCK    0
    32553507
    32563508#if JOBS
    32573509/* pgrp of shell on invocation */
    3258 static int initialpgrp;
    3259 static int ttyfd = -1;
     3510static int initialpgrp; //references:2
     3511static int ttyfd = -1; //5
    32603512#endif
    32613513/* array of jobs */
    3262 static struct job *jobtab;
     3514static struct job *jobtab; //5
    32633515/* size of array */
    3264 static unsigned njobs;
     3516static unsigned njobs; //4
    32653517/* current job */
    3266 static struct job *curjob;
     3518static struct job *curjob; //lots
    32673519/* number of presumed living untracked jobs */
    3268 static int jobless;
     3520static int jobless; //4
    32693521
    32703522static void
     
    33273579 * Convert a job name to a job structure.
    33283580 */
     3581#if !JOBS
     3582#define getjob(name, getctl) getjob(name)
     3583#endif
    33293584static struct job *
    33303585getjob(const char *name, int getctl)
     
    33323587    struct job *jp;
    33333588    struct job *found;
    3334     const char *err_msg = "No such job: %s";
     3589    const char *err_msg = "%s: no such job";
    33353590    unsigned num;
    33363591    int c;
     
    33833638    }
    33843639
    3385     found = 0;
    3386     while (1) {
    3387         if (!jp)
    3388             goto err;
    3389         if (match(jp->ps[0].cmd, p)) {
     3640    found = NULL;
     3641    while (jp) {
     3642        if (match(jp->ps[0].ps_cmd, p)) {
    33903643            if (found)
    33913644                goto err;
     
    33953648        jp = jp->prev_job;
    33963649    }
     3650    if (!found)
     3651        goto err;
     3652    jp = found;
    33973653
    33983654 gotit:
     
    34183674    INT_OFF;
    34193675    for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
    3420         if (ps->cmd != nullstr)
    3421             free(ps->cmd);
     3676        if (ps->ps_cmd != nullstr)
     3677            free(ps->ps_cmd);
    34223678    }
    34233679    if (jp->ps != &jp->ps0)
     
    34333689{
    34343690    if (tcsetpgrp(fd, pgrp))
    3435         ash_msg_and_raise_error("cannot set tty process group (%m)");
     3691        ash_msg_and_raise_error("can't set tty process group (%m)");
    34363692}
    34373693
     
    34513707    int pgrp;
    34523708
    3453     if (on == jobctl || rootshell == 0)
     3709    if (on == doing_jobctl || rootshell == 0)
    34543710        return;
    34553711    if (on) {
     
    34613717     * Obviously, a workaround for bugs when someone
    34623718     * failed to provide a controlling tty to bash! :) */
    3463             fd += 3;
    3464             while (!isatty(fd) && --fd >= 0)
    3465                 ;
     3719            fd = 2;
     3720            while (!isatty(fd))
     3721                if (--fd < 0)
     3722                    goto out;
    34663723        }
    34673724        fd = fcntl(fd, F_DUPFD, 10);
    3468         close(ofd);
     3725        if (ofd >= 0)
     3726            close(ofd);
    34693727        if (fd < 0)
    34703728            goto out;
    3471         fcntl(fd, F_SETFD, FD_CLOEXEC);
     3729        /* fd is a tty at this point */
     3730        close_on_exec_on(fd);
    34723731        do { /* while we are in the background */
    34733732            pgrp = tcgetpgrp(fd);
     
    34953754        pgrp = initialpgrp;
    34963755        /* was xtcsetpgrp, but this can make exiting ash
    3497          * with pty already deleted loop forever */
     3756         * loop forever if pty is already deleted */
    34983757        tcsetpgrp(fd, pgrp);
    34993758        setpgid(0, pgrp);
     
    35023761        setsignal(SIGTTIN);
    35033762 close:
    3504         close(fd);
     3763        if (fd >= 0)
     3764            close(fd);
    35053765        fd = -1;
    35063766    }
    35073767    ttyfd = fd;
    3508     jobctl = on;
    3509 }
    3510 
    3511 static int
     3768    doing_jobctl = on;
     3769}
     3770
     3771static int FAST_FUNC
    35123772killcmd(int argc, char **argv)
    35133773{
     3774    int i = 1;
    35143775    if (argv[1] && strcmp(argv[1], "-l") != 0) {
    3515         int i = 1;
    35163776        do {
    35173777            if (argv[i][0] == '%') {
    35183778                struct job *jp = getjob(argv[i], 0);
    3519                 unsigned pid = jp->ps[0].pid;
     3779                unsigned pid = jp->ps[0].ps_pid;
    35203780                /* Enough space for ' -NNN<nul>' */
    35213781                argv[i] = alloca(sizeof(int)*3 + 3);
     
    35313791
    35323792static void
    3533 showpipe(struct job *jp, FILE *out)
    3534 {
    3535     struct procstat *sp;
    3536     struct procstat *spend;
    3537 
    3538     spend = jp->ps + jp->nprocs;
    3539     for (sp = jp->ps + 1; sp < spend; sp++)
    3540         fprintf(out, " | %s", sp->cmd);
    3541     outcslow('\n', out);
     3793showpipe(struct job *jp /*, FILE *out*/)
     3794{
     3795    struct procstat *ps;
     3796    struct procstat *psend;
     3797
     3798    psend = jp->ps + jp->nprocs;
     3799    for (ps = jp->ps + 1; ps < psend; ps++)
     3800        printf(" | %s", ps->ps_cmd);
     3801    outcslow('\n', stdout);
    35423802    flush_stdout_stderr();
    35433803}
     
    35563816        goto out;
    35573817    jp->state = JOBRUNNING;
    3558     pgid = jp->ps->pid;
     3818    pgid = jp->ps[0].ps_pid;
    35593819    if (mode == FORK_FG)
    35603820        xtcsetpgrp(ttyfd, pgid);
     
    35633823    i = jp->nprocs;
    35643824    do {
    3565         if (WIFSTOPPED(ps->status)) {
    3566             ps->status = -1;
     3825        if (WIFSTOPPED(ps->ps_status)) {
     3826            ps->ps_status = -1;
    35673827        }
    35683828        ps++;
     
    35743834}
    35753835
    3576 static int
    3577 fg_bgcmd(int argc, char **argv)
     3836static int FAST_FUNC
     3837fg_bgcmd(int argc UNUSED_PARAM, char **argv)
    35783838{
    35793839    struct job *jp;
    3580     FILE *out;
    35813840    int mode;
    35823841    int retval;
     
    35853844    nextopt(nullstr);
    35863845    argv = argptr;
    3587     out = stdout;
    35883846    do {
    35893847        jp = getjob(*argv, 1);
    35903848        if (mode == FORK_BG) {
    35913849            set_curjob(jp, CUR_RUNNING);
    3592             fprintf(out, "[%d] ", jobno(jp));
    3593         }
    3594         outstr(jp->ps->cmd, out);
    3595         showpipe(jp, out);
     3850            printf("[%d] ", jobno(jp));
     3851        }
     3852        out1str(jp->ps[0].ps_cmd);
     3853        showpipe(jp /*, stdout*/);
    35963854        retval = restartjob(jp, mode);
    35973855    } while (*argv && *++argv);
     
    36383896}
    36393897
    3640 /*
    3641  * Do a wait system call.  If job control is compiled in, we accept
    3642  * stopped processes.  If block is zero, we return a value of zero
    3643  * rather than blocking.
    3644  *
    3645  * System V doesn't have a non-blocking wait system call.  It does
    3646  * have a SIGCLD signal that is sent to a process when one of it's
    3647  * children dies.  The obvious way to use SIGCLD would be to install
    3648  * a handler for SIGCLD which simply bumped a counter when a SIGCLD
    3649  * was received, and have waitproc bump another counter when it got
    3650  * the status of a process.  Waitproc would then know that a wait
    3651  * system call would not block if the two counters were different.
    3652  * This approach doesn't work because if a process has children that
    3653  * have not been waited for, System V will send it a SIGCLD when it
    3654  * installs a signal handler for SIGCLD.  What this means is that when
    3655  * a child exits, the shell will be sent SIGCLD signals continuously
    3656  * until is runs out of stack space, unless it does a wait call before
    3657  * restoring the signal handler.  The code below takes advantage of
    3658  * this (mis)feature by installing a signal handler for SIGCLD and
    3659  * then checking to see whether it was called.  If there are any
    3660  * children to be waited for, it will be.
    3661  *
    3662  * If neither SYSV nor BSD is defined, we don't implement nonblocking
    3663  * waits at all.  In this case, the user will not be informed when
    3664  * a background process until the next time she runs a real program
    3665  * (as opposed to running a builtin command or just typing return),
    3666  * and the jobs command may give out of date information.
    3667  */
    36683898static int
    3669 waitproc(int block, int *status)
    3670 {
    3671     int flags = 0;
    3672 
    3673 #if JOBS
    3674     if (jobctl)
    3675         flags |= WUNTRACED;
    3676 #endif
    3677     if (block == 0)
    3678         flags |= WNOHANG;
    3679     return wait3(status, flags, (struct rusage *)NULL);
    3680 }
    3681 
    3682 /*
    3683  * Wait for a process to terminate.
    3684  */
    3685 static int
    3686 dowait(int block, struct job *job)
     3899dowait(int wait_flags, struct job *job)
    36873900{
    36883901    int pid;
     
    36923905    int state;
    36933906
    3694     TRACE(("dowait(%d) called\n", block));
    3695     pid = waitproc(block, &status);
    3696     TRACE(("wait returns pid %d, status=%d\n", pid, status));
     3907    TRACE(("dowait(0x%x) called\n", wait_flags));
     3908
     3909    /* Do a wait system call. If job control is compiled in, we accept
     3910     * stopped processes. wait_flags may have WNOHANG, preventing blocking.
     3911     * NB: _not_ safe_waitpid, we need to detect EINTR */
     3912    if (doing_jobctl)
     3913        wait_flags |= WUNTRACED;
     3914    pid = waitpid(-1, &status, wait_flags);
     3915    TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
     3916                pid, status, errno, strerror(errno)));
    36973917    if (pid <= 0)
    36983918        return pid;
     3919
    36993920    INT_OFF;
    37003921    thisjob = NULL;
    37013922    for (jp = curjob; jp; jp = jp->prev_job) {
    3702         struct procstat *sp;
    3703         struct procstat *spend;
     3923        struct procstat *ps;
     3924        struct procstat *psend;
    37043925        if (jp->state == JOBDONE)
    37053926            continue;
    37063927        state = JOBDONE;
    3707         spend = jp->ps + jp->nprocs;
    3708         sp = jp->ps;
     3928        ps = jp->ps;
     3929        psend = ps + jp->nprocs;
    37093930        do {
    3710             if (sp->pid == pid) {
     3931            if (ps->ps_pid == pid) {
    37113932                TRACE(("Job %d: changing status of proc %d "
    37123933                    "from 0x%x to 0x%x\n",
    3713                     jobno(jp), pid, sp->status, status));
    3714                 sp->status = status;
     3934                    jobno(jp), pid, ps->ps_status, status));
     3935                ps->ps_status = status;
    37153936                thisjob = jp;
    37163937            }
    3717             if (sp->status == -1)
     3938            if (ps->ps_status == -1)
    37183939                state = JOBRUNNING;
    37193940#if JOBS
    37203941            if (state == JOBRUNNING)
    37213942                continue;
    3722             if (WIFSTOPPED(sp->status)) {
    3723                 jp->stopstatus = sp->status;
     3943            if (WIFSTOPPED(ps->ps_status)) {
     3944                jp->stopstatus = ps->ps_status;
    37243945                state = JOBSTOPPED;
    37253946            }
    37263947#endif
    3727         } while (++sp < spend);
     3948        } while (++ps < psend);
    37283949        if (thisjob)
    37293950            goto gotjob;
     
    37323953    if (!WIFSTOPPED(status))
    37333954#endif
    3734 
    37353955        jobless--;
    37363956    goto out;
     
    37623982        if (len) {
    37633983            s[len] = '\n';
    3764             s[len + 1] = 0;
     3984            s[len + 1] = '\0';
    37653985            out2str(s);
    37663986        }
    37673987    }
     3988    return pid;
     3989}
     3990
     3991static int
     3992blocking_wait_with_raise_on_sig(void)
     3993{
     3994    pid_t pid = dowait(DOWAIT_BLOCK, NULL);
     3995    if (pid <= 0 && pending_sig)
     3996        raise_exception(EXSIG);
    37683997    return pid;
    37693998}
     
    37814010    ps = jp->ps;
    37824011
    3783     if (mode & SHOW_PGID) {
     4012    if (mode & SHOW_ONLY_PGID) { /* jobs -p */
    37844013        /* just output process (group) id of pipeline */
    3785         fprintf(out, "%d\n", ps->pid);
     4014        fprintf(out, "%d\n", ps->ps_pid);
    37864015        return;
    37874016    }
     
    37914020
    37924021    if (jp == curjob)
    3793         s[col - 2] = '+';
     4022        s[col - 3] = '+';
    37944023    else if (curjob && jp == curjob->prev_job)
    3795         s[col - 2] = '-';
    3796 
    3797     if (mode & SHOW_PID)
    3798         col += fmtstr(s + col, 16, "%d ", ps->pid);
     4024        s[col - 3] = '-';
     4025
     4026    if (mode & SHOW_PIDS)
     4027        col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
    37994028
    38004029    psend = ps + jp->nprocs;
     
    38044033        col += sizeof("Running") - 1;
    38054034    } else {
    3806         int status = psend[-1].status;
     4035        int status = psend[-1].ps_status;
    38074036        if (jp->state == JOBSTOPPED)
    38084037            status = jp->stopstatus;
    38094038        col += sprint_status(s + col, status, 0);
    38104039    }
    3811 
     4040    /* By now, "[JOBID]*  [maybe PID] STATUS" is printed */
     4041
     4042    /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
     4043     * or prints several "PID             | <cmdN>" lines,
     4044     * depending on SHOW_PIDS bit.
     4045     * We do not print status of individual processes
     4046     * between PID and <cmdN>. bash does it, but not very well:
     4047     * first line shows overall job status, not process status,
     4048     * making it impossible to know 1st process status.
     4049     */
    38124050    goto start;
    3813 
    38144051    do {
    38154052        /* for each process */
    3816         col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
     4053        s[0] = '\0';
     4054        col = 33;
     4055        if (mode & SHOW_PIDS)
     4056            col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
    38174057 start:
    3818         fprintf(out, "%s%*c%s",
    3819             s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
     4058        fprintf(out, "%s%*c%s%s",
     4059                s,
     4060                33 - col >= 0 ? 33 - col : 0, ' ',
     4061                ps == jp->ps ? "" : "| ",
     4062                ps->ps_cmd
    38204063        );
    3821         if (!(mode & SHOW_PID)) {
    3822             showpipe(jp, out);
    3823             break;
    3824         }
    3825         if (++ps == psend) {
    3826             outcslow('\n', out);
    3827             break;
    3828         }
    3829     } while (1);
     4064    } while (++ps != psend);
     4065    outcslow('\n', out);
    38304066
    38314067    jp->changed = 0;
     
    38464082    struct job *jp;
    38474083
    3848     TRACE(("showjobs(%x) called\n", mode));
    3849 
    3850     /* If not even one one job changed, there is nothing to do */
    3851     while (dowait(DOWAIT_NORMAL, NULL) > 0)
     4084    TRACE(("showjobs(0x%x) called\n", mode));
     4085
     4086    /* Handle all finished jobs */
     4087    while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
    38524088        continue;
    38534089
     
    38594095}
    38604096
    3861 static int
    3862 jobscmd(int argc, char **argv)
     4097static int FAST_FUNC
     4098jobscmd(int argc UNUSED_PARAM, char **argv)
    38634099{
    38644100    int mode, m;
    38654101
    38664102    mode = 0;
    3867     while ((m = nextopt("lp"))) {
     4103    while ((m = nextopt("lp")) != '\0') {
    38684104        if (m == 'l')
    3869             mode = SHOW_PID;
     4105            mode |= SHOW_PIDS;
    38704106        else
    3871             mode = SHOW_PGID;
     4107            mode |= SHOW_ONLY_PGID;
    38724108    }
    38734109
     
    38754111    if (*argv) {
    38764112        do
    3877             showjob(stdout, getjob(*argv,0), mode);
     4113            showjob(stdout, getjob(*argv, 0), mode);
    38784114        while (*++argv);
    3879     } else
     4115    } else {
    38804116        showjobs(stdout, mode);
     4117    }
    38814118
    38824119    return 0;
     
    38844121#endif /* JOBS */
    38854122
     4123/* Called only on finished or stopped jobs (no members are running) */
    38864124static int
    38874125getstatus(struct job *job)
     
    38894127    int status;
    38904128    int retval;
    3891 
    3892     status = job->ps[job->nprocs - 1].status;
     4129    struct procstat *ps;
     4130
     4131    /* Fetch last member's status */
     4132    ps = job->ps + job->nprocs - 1;
     4133    status = ps->ps_status;
     4134    if (pipefail) {
     4135        /* "set -o pipefail" mode: use last _nonzero_ status */
     4136        while (status == 0 && --ps >= job->ps)
     4137            status = ps->ps_status;
     4138    }
     4139
    38934140    retval = WEXITSTATUS(status);
    38944141    if (!WIFEXITED(status)) {
     
    39074154        retval += 128;
    39084155    }
    3909     TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
     4156    TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
    39104157        jobno(job), job->nprocs, status, retval));
    39114158    return retval;
    39124159}
    39134160
    3914 static int
    3915 waitcmd(int argc, char **argv)
     4161static int FAST_FUNC
     4162waitcmd(int argc UNUSED_PARAM, char **argv)
    39164163{
    39174164    struct job *job;
     
    39194166    struct job *jp;
    39204167
    3921     EXSIGON;
     4168    if (pending_sig)
     4169        raise_exception(EXSIG);
    39224170
    39234171    nextopt(nullstr);
     
    39304178            jp = curjob;
    39314179            while (1) {
    3932                 if (!jp) {
    3933                     /* no running procs */
    3934                     goto out;
    3935                 }
     4180                if (!jp) /* no running procs */
     4181                    goto ret;
    39364182                if (jp->state == JOBRUNNING)
    39374183                    break;
     
    39394185                jp = jp->prev_job;
    39404186            }
    3941             dowait(DOWAIT_BLOCK, 0);
     4187            blocking_wait_with_raise_on_sig();
     4188    /* man bash:
     4189     * "When bash is waiting for an asynchronous command via
     4190     * the wait builtin, the reception of a signal for which a trap
     4191     * has been set will cause the wait builtin to return immediately
     4192     * with an exit status greater than 128, immediately after which
     4193     * the trap is executed."
     4194     *
     4195     * blocking_wait_with_raise_on_sig raises signal handlers
     4196     * if it gets no pid (pid < 0). However,
     4197     * if child sends us a signal *and immediately exits*,
     4198     * blocking_wait_with_raise_on_sig gets pid > 0
     4199     * and does not handle pending_sig. Check this case: */
     4200            if (pending_sig)
     4201                raise_exception(EXSIG);
    39424202        }
    39434203    }
     
    39484208            pid_t pid = number(*argv);
    39494209            job = curjob;
    3950             goto start;
    3951             do {
    3952                 if (job->ps[job->nprocs - 1].pid == pid)
     4210            while (1) {
     4211                if (!job)
     4212                    goto repeat;
     4213                if (job->ps[job->nprocs - 1].ps_pid == pid)
    39534214                    break;
    39544215                job = job->prev_job;
    3955  start:
    3956                 if (!job)
    3957                     goto repeat;
    3958             } while (1);
     4216            }
    39594217        } else
    39604218            job = getjob(*argv, 0);
    39614219        /* loop until process terminated or stopped */
    39624220        while (job->state == JOBRUNNING)
    3963             dowait(DOWAIT_BLOCK, 0);
     4221            blocking_wait_with_raise_on_sig();
    39644222        job->waited = 1;
    39654223        retval = getstatus(job);
    3966  repeat:
    3967         ;
     4224 repeat: ;
    39684225    } while (*++argv);
    39694226
    3970  out:
     4227 ret:
    39714228    return retval;
    39724229}
     
    40204277 */
    40214278static struct job *
    4022 makejob(union node *node, int nprocs)
     4279makejob(/*union node *node,*/ int nprocs)
    40234280{
    40244281    int i;
     
    40354292            continue;
    40364293#if JOBS
    4037         if (jobctl)
     4294        if (doing_jobctl)
    40384295            continue;
    40394296#endif
     
    40454302    /* jp->jobctl is a bitfield.
    40464303     * "jp->jobctl |= jobctl" likely to give awful code */
    4047     if (jobctl)
     4304    if (doing_jobctl)
    40484305        jp->jobctl = 1;
    40494306#endif
     
    40554312        jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
    40564313    }
    4057     TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
     4314    TRACE(("makejob(%d) returns %%%d\n", nprocs,
    40584315                jobno(jp)));
    40594316    return jp;
     
    40704327cmdputs(const char *s)
    40714328{
    4072     const char *p, *str;
    4073     char c, cc[2] = " ";
    4074     char *nextc;
    4075     int subtype = 0;
    4076     int quoted = 0;
    4077     static const char vstype[VSTYPE + 1][4] = {
     4329    static const char vstype[VSTYPE + 1][3] = {
    40784330        "", "}", "-", "+", "?", "=",
    40794331        "%", "%%", "#", "##"
     4332        IF_ASH_BASH_COMPAT(, ":", "/", "//")
    40804333    };
    40814334
     4335    const char *p, *str;
     4336    char cc[2];
     4337    char *nextc;
     4338    unsigned char c;
     4339    unsigned char subtype = 0;
     4340    int quoted = 0;
     4341
     4342    cc[1] = '\0';
    40824343    nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
    40834344    p = s;
    4084     while ((c = *p++) != 0) {
    4085         str = 0;
     4345    while ((c = *p++) != '\0') {
     4346        str = NULL;
    40864347        switch (c) {
    40874348        case CTLESC:
     
    41104371            str = "\"$(...)\"";
    41114372            goto dostr;
    4112 #if ENABLE_ASH_MATH_SUPPORT
     4373#if ENABLE_SH_MATH_SUPPORT
    41134374        case CTLARI:
    41144375            str = "$((";
     
    41504411            continue;
    41514412 dostr:
    4152         while ((c = *str++)) {
     4413        while ((c = *str++) != '\0') {
    41534414            USTPUTC(c, nextc);
    41544415        }
    4155     }
     4416    } /* while *p++ not NUL */
     4417
    41564418    if (quoted & 1) {
    41574419        USTPUTC('"', nextc);
     
    41824444    struct nodelist *lp;
    41834445    const char *p;
    4184     char s[2];
    41854446
    41864447    if (!n)
     
    42284489        cmdtxt(n->nif.test);
    42294490        cmdputs("; then ");
    4230         n = n->nif.ifpart;
    42314491        if (n->nif.elsepart) {
    4232             cmdtxt(n);
     4492            cmdtxt(n->nif.ifpart);
    42334493            cmdputs("; else ");
    42344494            n = n->nif.elsepart;
     4495        } else {
     4496            n = n->nif.ifpart;
    42354497        }
    42364498        p = "; fi";
     
    43024564        p = ">>";
    43034565        goto redir;
     4566#if ENABLE_ASH_BASH_COMPAT
     4567    case NTO2:
     4568#endif
    43044569    case NTOFD:
    43054570        p = ">&";
     
    43144579        p = "<>";
    43154580 redir:
    4316         s[0] = n->nfile.fd + '0';
    4317         s[1] = '\0';
    4318         cmdputs(s);
     4581        cmdputs(utoa(n->nfile.fd));
    43194582        cmdputs(p);
    43204583        if (n->type == NTOFD || n->type == NFROMFD) {
    4321             s[0] = n->ndup.dupfd + '0';
    4322             p = s;
    4323             goto dotail2;
     4584            cmdputs(utoa(n->ndup.dupfd));
     4585            break;
    43244586        }
    43254587        n = n->nfile.fname;
     
    43674629
    43684630    for (tp = trap; tp < &trap[NSIG]; tp++) {
    4369         if (*tp && **tp) {      /* trap not NULL or SIG_IGN */
     4631        if (*tp && **tp) {      /* trap not NULL or "" (SIG_IGN) */
    43704632            INT_OFF;
    4371             free(*tp);
     4633            if (trap_ptr == trap)
     4634                free(*tp);
     4635            /* else: it "belongs" to trap_ptr vector, don't free */
    43724636            *tp = NULL;
    4373             if (tp != &trap[0])
     4637            if ((tp - trap) != 0)
    43744638                setsignal(tp - trap);
    43754639            INT_ON;
    43764640        }
    43774641    }
     4642    may_have_traps = 0;
    43784643}
    43794644
     
    43824647
    43834648/* Called after fork(), in child */
    4384 static void
     4649static NOINLINE void
    43854650forkchild(struct job *jp, union node *n, int mode)
    43864651{
     
    43914656    shlvl++;
    43924657
     4658    /* man bash: "Non-builtin commands run by bash have signal handlers
     4659     * set to the values inherited by the shell from its parent".
     4660     * Do we do it correctly? */
     4661
    43934662    closescript();
     4663
     4664    if (mode == FORK_NOJOB          /* is it `xxx` ? */
     4665     && n && n->type == NCMD        /* is it single cmd? */
     4666    /* && n->ncmd.args->type == NARG - always true? */
     4667     && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
     4668     && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
     4669    /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
     4670    ) {
     4671        TRACE(("Trap hack\n"));
     4672        /* Awful hack for `trap` or $(trap).
     4673         *
     4674         * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
     4675         * contains an example where "trap" is executed in a subshell:
     4676         *
     4677         * save_traps=$(trap)
     4678         * ...
     4679         * eval "$save_traps"
     4680         *
     4681         * Standard does not say that "trap" in subshell shall print
     4682         * parent shell's traps. It only says that its output
     4683         * must have suitable form, but then, in the above example
     4684         * (which is not supposed to be normative), it implies that.
     4685         *
     4686         * bash (and probably other shell) does implement it
     4687         * (traps are reset to defaults, but "trap" still shows them),
     4688         * but as a result, "trap" logic is hopelessly messed up:
     4689         *
     4690         * # trap
     4691         * trap -- 'echo Ho' SIGWINCH  <--- we have a handler
     4692         * # (trap)        <--- trap is in subshell - no output (correct, traps are reset)
     4693         * # true | trap   <--- trap is in subshell - no output (ditto)
     4694         * # echo `true | trap`    <--- in subshell - output (but traps are reset!)
     4695         * trap -- 'echo Ho' SIGWINCH
     4696         * # echo `(trap)`         <--- in subshell in subshell - output
     4697         * trap -- 'echo Ho' SIGWINCH
     4698         * # echo `true | (trap)`  <--- in subshell in subshell in subshell - output!
     4699         * trap -- 'echo Ho' SIGWINCH
     4700         *
     4701         * The rules when to forget and when to not forget traps
     4702         * get really complex and nonsensical.
     4703         *
     4704         * Our solution: ONLY bare $(trap) or `trap` is special.
     4705         */
     4706        /* Save trap handler strings for trap builtin to print */
     4707        trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
     4708        /* Fall through into clearing traps */
     4709    }
    43944710    clear_traps();
    43954711#if JOBS
    43964712    /* do job control only in root shell */
    4397     jobctl = 0;
     4713    doing_jobctl = 0;
    43984714    if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
    43994715        pid_t pgrp;
     
    44024718            pgrp = getpid();
    44034719        else
    4404             pgrp = jp->ps[0].pid;
    4405         /* This can fail because we are doing it in the parent also */
    4406         (void)setpgid(0, pgrp);
     4720            pgrp = jp->ps[0].ps_pid;
     4721        /* this can fail because we are doing it in the parent also */
     4722        setpgid(0, pgrp);
    44074723        if (mode == FORK_FG)
    44084724            xtcsetpgrp(ttyfd, pgrp);
     
    44124728#endif
    44134729    if (mode == FORK_BG) {
     4730        /* man bash: "When job control is not in effect,
     4731         * asynchronous commands ignore SIGINT and SIGQUIT" */
    44144732        ignoresig(SIGINT);
    44154733        ignoresig(SIGQUIT);
     
    44174735            close(0);
    44184736            if (open(bb_dev_null, O_RDONLY) != 0)
    4419                 ash_msg_and_raise_error("can't open %s", bb_dev_null);
    4420         }
    4421     }
    4422     if (!oldlvl && iflag) {
    4423         setsignal(SIGINT);
     4737                ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
     4738        }
     4739    }
     4740    if (!oldlvl) {
     4741        if (iflag) { /* why if iflag only? */
     4742            setsignal(SIGINT);
     4743            setsignal(SIGTERM);
     4744        }
     4745        /* man bash:
     4746         * "In all cases, bash ignores SIGQUIT. Non-builtin
     4747         * commands run by bash have signal handlers
     4748         * set to the values inherited by the shell
     4749         * from its parent".
     4750         * Take care of the second rule: */
    44244751        setsignal(SIGQUIT);
    4425         setsignal(SIGTERM);
    4426     }
     4752    }
     4753#if JOBS
     4754    if (n && n->type == NCMD
     4755     && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
     4756    ) {
     4757        TRACE(("Job hack\n"));
     4758        /* "jobs": we do not want to clear job list for it,
     4759         * instead we remove only _its_ own_ job from job list.
     4760         * This makes "jobs .... | cat" more useful.
     4761         */
     4762        freejob(curjob);
     4763        return;
     4764    }
     4765#endif
    44274766    for (jp = curjob; jp; jp = jp->prev_job)
    44284767        freejob(jp);
     
    44314770
    44324771/* Called after fork(), in parent */
     4772#if !JOBS
     4773#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
     4774#endif
    44334775static void
    44344776forkparent(struct job *jp, union node *n, int mode, pid_t pid)
     
    44364778    TRACE(("In parent shell: child = %d\n", pid));
    44374779    if (!jp) {
    4438         while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
     4780        while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
     4781            continue;
    44394782        jobless++;
    44404783        return;
     
    44474790            pgrp = pid;
    44484791        else
    4449             pgrp = jp->ps[0].pid;
     4792            pgrp = jp->ps[0].ps_pid;
    44504793        /* This can fail because we are doing it in the child also */
    44514794        setpgid(pid, pgrp);
     
    44584801    if (jp) {
    44594802        struct procstat *ps = &jp->ps[jp->nprocs++];
    4460         ps->pid = pid;
    4461         ps->status = -1;
    4462         ps->cmd = nullstr;
     4803        ps->ps_pid = pid;
     4804        ps->ps_status = -1;
     4805        ps->ps_cmd = nullstr;
    44634806#if JOBS
    4464         if (jobctl && n)
    4465             ps->cmd = commandtext(n);
     4807        if (doing_jobctl && n)
     4808            ps->ps_cmd = commandtext(n);
    44664809#endif
    44674810    }
     
    44794822        if (jp)
    44804823            freejob(jp);
    4481         ash_msg_and_raise_error("cannot fork");
    4482     }
    4483     if (pid == 0)
     4824        ash_msg_and_raise_error("can't fork");
     4825    }
     4826    if (pid == 0) {
     4827        CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
    44844828        forkchild(jp, n, mode);
    4485     else
     4829    } else {
    44864830        forkparent(jp, n, mode, pid);
     4831    }
    44874832    return pid;
    44884833}
     
    44914836 * Wait for job to finish.
    44924837 *
    4493  * Under job control we have the problem that while a child process is
    4494  * running interrupts generated by the user are sent to the child but not
    4495  * to the shell.  This means that an infinite loop started by an inter-
    4496  * active user may be hard to kill.  With job control turned off, an
    4497  * interactive user may place an interactive program inside a loop.  If
    4498  * the interactive program catches interrupts, the user doesn't want
     4838 * Under job control we have the problem that while a child process
     4839 * is running interrupts generated by the user are sent to the child
     4840 * but not to the shell.  This means that an infinite loop started by
     4841 * an interactive user may be hard to kill.  With job control turned off,
     4842 * an interactive user may place an interactive program inside a loop.
     4843 * If the interactive program catches interrupts, the user doesn't want
    44994844 * these interrupts to also abort the loop.  The approach we take here
    45004845 * is to have the shell ignore interrupt signals while waiting for a
     
    45144859
    45154860    TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
     4861
     4862    INT_OFF;
    45164863    while (jp->state == JOBRUNNING) {
     4864        /* In non-interactive shells, we _can_ get
     4865         * a keyboard signal here and be EINTRed,
     4866         * but we just loop back, waiting for command to complete.
     4867         *
     4868         * man bash:
     4869         * "If bash is waiting for a command to complete and receives
     4870         * a signal for which a trap has been set, the trap
     4871         * will not be executed until the command completes."
     4872         *
     4873         * Reality is that even if trap is not set, bash
     4874         * will not act on the signal until command completes.
     4875         * Try this. sleep5intoff.c:
     4876         * #include <signal.h>
     4877         * #include <unistd.h>
     4878         * int main() {
     4879         *         sigset_t set;
     4880         *         sigemptyset(&set);
     4881         *         sigaddset(&set, SIGINT);
     4882         *         sigaddset(&set, SIGQUIT);
     4883         *         sigprocmask(SIG_BLOCK, &set, NULL);
     4884         *         sleep(5);
     4885         *         return 0;
     4886         * }
     4887         * $ bash -c './sleep5intoff; echo hi'
     4888         * ^C^C^C^C <--- pressing ^C once a second
     4889         * $ _
     4890         * $ bash -c './sleep5intoff; echo hi'
     4891         * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
     4892         * $ _
     4893         */
    45174894        dowait(DOWAIT_BLOCK, jp);
    45184895    }
     4896    INT_ON;
     4897
    45194898    st = getstatus(jp);
    45204899#if JOBS
     
    45294908         * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
    45304909         */
    4531         if (jp->sigint)
    4532             raise(SIGINT);
     4910        if (jp->sigint) /* TODO: do the same with all signals */
     4911            raise(SIGINT); /* ... by raise(jp->sig) instead? */
    45334912    }
    45344913    if (jp->state == JOBDONE)
     
    45674946
    45684947#define EMPTY -2                /* marks an unused slot in redirtab */
    4569 #ifndef PIPE_BUF
    4570 # define PIPESIZE 4096          /* amount of buffering in a pipe */
    4571 #else
    4572 # define PIPESIZE PIPE_BUF
    4573 #endif
     4948#define CLOSED -3               /* marks a slot of previously-closed fd */
    45744949
    45754950/*
     
    46214996     * replaced, return the file descriptor.
    46224997     */
    4623     if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
    4624      && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
     4998    if (fstat(fd, &finfo2) == 0
     4999     && !S_ISREG(finfo2.st_mode)
     5000     && finfo.st_dev == finfo2.st_dev
     5001     && finfo.st_ino == finfo2.st_ino
     5002    ) {
    46255003        return fd;
     5004    }
    46265005
    46275006    /* The file has been replaced.  badness. */
     
    46485027    if (redir->type == NHERE) {
    46495028        len = strlen(redir->nhere.doc->narg.text);
    4650         if (len <= PIPESIZE) {
     5029        if (len <= PIPE_BUF) {
    46515030            full_write(pip[1], redir->nhere.doc->narg.text, len);
    46525031            goto out;
     
    46545033    }
    46555034    if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
     5035        /* child */
    46565036        close(pip[0]);
    4657         signal(SIGINT, SIG_IGN);
    4658         signal(SIGQUIT, SIG_IGN);
    4659         signal(SIGHUP, SIG_IGN);
    4660 #ifdef SIGTSTP
    4661         signal(SIGTSTP, SIG_IGN);
    4662 #endif
     5037        ignoresig(SIGINT);  //signal(SIGINT, SIG_IGN);
     5038        ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
     5039        ignoresig(SIGHUP);  //signal(SIGHUP, SIG_IGN);
     5040        ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
    46635041        signal(SIGPIPE, SIG_DFL);
    46645042        if (redir->type == NHERE)
    46655043            full_write(pip[1], redir->nhere.doc->narg.text, len);
    4666         else
     5044        else /* NXHERE */
    46675045            expandhere(redir->nhere.doc, pip[1]);
    4668         _exit(0);
     5046        _exit(EXIT_SUCCESS);
    46695047    }
    46705048 out:
     
    46885066    case NFROMTO:
    46895067        fname = redir->nfile.expfname;
    4690         f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
     5068        f = open(fname, O_RDWR|O_CREAT, 0666);
    46915069        if (f < 0)
    46925070            goto ecreate;
    46935071        break;
    46945072    case NTO:
     5073#if ENABLE_ASH_BASH_COMPAT
     5074    case NTO2:
     5075#endif
    46955076        /* Take care of noclobber mode. */
    46965077        if (Cflag) {
     
    47195100#endif
    47205101        /* Fall through to eliminate warning. */
    4721     case NTOFD:
    4722     case NFROMFD:
    4723         f = -1;
    4724         break;
     5102/* Our single caller does this itself */
     5103//  case NTOFD:
     5104//  case NFROMFD:
     5105//      f = -1;
     5106//      break;
    47255107    case NHERE:
    47265108    case NXHERE:
     
    47315113    return f;
    47325114 ecreate:
    4733     ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
     5115    ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
    47345116 eopen:
    4735     ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
     5117    ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
    47365118}
    47375119
     
    47415123 * file descriptors left.
    47425124 */
     5125/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
     5126 * old code was doing close(to) prior to copyfd() to achieve the same */
     5127enum {
     5128    COPYFD_EXACT   = (int)~(INT_MAX),
     5129    COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
     5130};
    47435131static int
    47445132copyfd(int from, int to)
     
    47465134    int newfd;
    47475135
    4748     newfd = fcntl(from, F_DUPFD, to);
     5136    if (to & COPYFD_EXACT) {
     5137        to &= ~COPYFD_EXACT;
     5138        /*if (from != to)*/
     5139            newfd = dup2(from, to);
     5140    } else {
     5141        newfd = fcntl(from, F_DUPFD, to);
     5142    }
    47495143    if (newfd < 0) {
    47505144        if (errno == EMFILE)
    47515145            return EMPTY;
     5146        /* Happens when source fd is not open: try "echo >&99" */
    47525147        ash_msg_and_raise_error("%d: %m", from);
    47535148    }
     
    47555150}
    47565151
    4757 static void
    4758 dupredirect(union node *redir, int f)
    4759 {
    4760     int fd = redir->nfile.fd;
    4761 
    4762     if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
    4763         if (redir->ndup.dupfd >= 0) {   /* if not ">&-" */
    4764             copyfd(redir->ndup.dupfd, fd);
    4765         }
    4766         return;
    4767     }
    4768 
    4769     if (f != fd) {
    4770         copyfd(f, fd);
    4771         close(f);
    4772     }
     5152/* Struct def and variable are moved down to the first usage site */
     5153struct two_fd_t {
     5154    int orig, copy;
     5155};
     5156struct redirtab {
     5157    struct redirtab *next;
     5158    int nullredirs;
     5159    int pair_count;
     5160    struct two_fd_t two_fd[];
     5161};
     5162#define redirlist (G_var.redirlist)
     5163
     5164static int need_to_remember(struct redirtab *rp, int fd)
     5165{
     5166    int i;
     5167
     5168    if (!rp) /* remembering was not requested */
     5169        return 0;
     5170
     5171    for (i = 0; i < rp->pair_count; i++) {
     5172        if (rp->two_fd[i].orig == fd) {
     5173            /* already remembered */
     5174            return 0;
     5175        }
     5176    }
     5177    return 1;
     5178}
     5179
     5180/* "hidden" fd is a fd used to read scripts, or a copy of such */
     5181static int is_hidden_fd(struct redirtab *rp, int fd)
     5182{
     5183    int i;
     5184    struct parsefile *pf;
     5185
     5186    if (fd == -1)
     5187        return 0;
     5188    /* Check open scripts' fds */
     5189    pf = g_parsefile;
     5190    while (pf) {
     5191        /* We skip pf_fd == 0 case because of the following case:
     5192         * $ ash  # running ash interactively
     5193         * $ . ./script.sh
     5194         * and in script.sh: "exec 9>&0".
     5195         * Even though top-level pf_fd _is_ 0,
     5196         * it's still ok to use it: "read" builtin uses it,
     5197         * why should we cripple "exec" builtin?
     5198         */
     5199        if (pf->pf_fd > 0 && fd == pf->pf_fd) {
     5200            return 1;
     5201        }
     5202        pf = pf->prev;
     5203    }
     5204
     5205    if (!rp)
     5206        return 0;
     5207    /* Check saved fds of redirects */
     5208    fd |= COPYFD_RESTORE;
     5209    for (i = 0; i < rp->pair_count; i++) {
     5210        if (rp->two_fd[i].copy == fd) {
     5211            return 1;
     5212        }
     5213    }
     5214    return 0;
    47735215}
    47745216
     
    47765218 * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
    47775219 * old file descriptors are stashed away so that the redirection can be
    4778  * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
    4779  * standard output, and the standard error if it becomes a duplicate of
    4780  * stdout, is saved in memory.
     5220 * undone by calling popredir.
    47815221 */
    47825222/* flags passed to redirect */
     
    47865226redirect(union node *redir, int flags)
    47875227{
    4788     union node *n;
    47895228    struct redirtab *sv;
     5229    int sv_pos;
    47905230    int i;
    47915231    int fd;
    47925232    int newfd;
    4793     int *p;
    4794     nullredirs++;
     5233    int copied_fd2 = -1;
     5234
     5235    g_nullredirs++;
    47955236    if (!redir) {
    47965237        return;
    47975238    }
     5239
    47985240    sv = NULL;
     5241    sv_pos = 0;
    47995242    INT_OFF;
    48005243    if (flags & REDIR_PUSH) {
    4801         struct redirtab *q;
    4802         q = ckmalloc(sizeof(struct redirtab));
    4803         q->next = redirlist;
    4804         redirlist = q;
    4805         q->nullredirs = nullredirs - 1;
    4806         for (i = 0; i < 10; i++)
    4807             q->renamed[i] = EMPTY;
    4808         nullredirs = 0;
    4809         sv = q;
    4810     }
    4811     n = redir;
     5244        union node *tmp = redir;
     5245        do {
     5246            sv_pos++;
     5247#if ENABLE_ASH_BASH_COMPAT
     5248            if (tmp->nfile.type == NTO2)
     5249                sv_pos++;
     5250#endif
     5251            tmp = tmp->nfile.next;
     5252        } while (tmp);
     5253        sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
     5254        sv->next = redirlist;
     5255        sv->pair_count = sv_pos;
     5256        redirlist = sv;
     5257        sv->nullredirs = g_nullredirs - 1;
     5258        g_nullredirs = 0;
     5259        while (sv_pos > 0) {
     5260            sv_pos--;
     5261            sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
     5262        }
     5263    }
     5264
    48125265    do {
    4813         fd = n->nfile.fd;
    4814         if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
    4815          && n->ndup.dupfd == fd)
    4816             continue; /* redirect from/to same file descriptor */
    4817 
    4818         newfd = openredirect(n);
    4819         if (fd == newfd)
    4820             continue;
    4821         if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
    4822             i = fcntl(fd, F_DUPFD, 10);
    4823 
     5266        int right_fd = -1;
     5267        fd = redir->nfile.fd;
     5268        if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
     5269            right_fd = redir->ndup.dupfd;
     5270            //bb_error_msg("doing %d > %d", fd, right_fd);
     5271            /* redirect from/to same file descriptor? */
     5272            if (right_fd == fd)
     5273                continue;
     5274            /* "echo >&10" and 10 is a fd opened to a sh script? */
     5275            if (is_hidden_fd(sv, right_fd)) {
     5276                errno = EBADF; /* as if it is closed */
     5277                ash_msg_and_raise_error("%d: %m", right_fd);
     5278            }
     5279            newfd = -1;
     5280        } else {
     5281            newfd = openredirect(redir); /* always >= 0 */
     5282            if (fd == newfd) {
     5283                /* Descriptor wasn't open before redirect.
     5284                 * Mark it for close in the future */
     5285                if (need_to_remember(sv, fd)) {
     5286                    goto remember_to_close;
     5287                }
     5288                continue;
     5289            }
     5290        }
     5291#if ENABLE_ASH_BASH_COMPAT
     5292 redirect_more:
     5293#endif
     5294        if (need_to_remember(sv, fd)) {
     5295            /* Copy old descriptor */
     5296            /* Careful to not accidentally "save"
     5297             * to the same fd as right side fd in N>&M */
     5298            int minfd = right_fd < 10 ? 10 : right_fd + 1;
     5299            i = fcntl(fd, F_DUPFD, minfd);
     5300/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
     5301 * are closed in popredir() in the child, preventing them from leaking
     5302 * into child. (popredir() also cleans up the mess in case of failures)
     5303 */
    48245304            if (i == -1) {
    48255305                i = errno;
    48265306                if (i != EBADF) {
    4827                     close(newfd);
     5307                    /* Strange error (e.g. "too many files" EMFILE?) */
     5308                    if (newfd >= 0)
     5309                        close(newfd);
    48285310                    errno = i;
    48295311                    ash_msg_and_raise_error("%d: %m", fd);
    48305312                    /* NOTREACHED */
    48315313                }
     5314                /* EBADF: it is not open - good, remember to close it */
     5315 remember_to_close:
     5316                i = CLOSED;
     5317            } else { /* fd is open, save its copy */
     5318                /* "exec fd>&-" should not close fds
     5319                 * which point to script file(s).
     5320                 * Force them to be restored afterwards */
     5321                if (is_hidden_fd(sv, fd))
     5322                    i |= COPYFD_RESTORE;
     5323            }
     5324            if (fd == 2)
     5325                copied_fd2 = i;
     5326            sv->two_fd[sv_pos].orig = fd;
     5327            sv->two_fd[sv_pos].copy = i;
     5328            sv_pos++;
     5329        }
     5330        if (newfd < 0) {
     5331            /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
     5332            if (redir->ndup.dupfd < 0) { /* "fd>&-" */
     5333                /* Don't want to trigger debugging */
     5334                if (fd != -1)
     5335                    close(fd);
    48325336            } else {
    4833                 *p = i;
    4834                 close(fd);
     5337                copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
    48355338            }
    4836         } else {
    4837             close(fd);
    4838         }
    4839         dupredirect(n, newfd);
    4840     } while ((n = n->nfile.next));
     5339        } else if (fd != newfd) { /* move newfd to fd */
     5340            copyfd(newfd, fd | COPYFD_EXACT);
     5341#if ENABLE_ASH_BASH_COMPAT
     5342            if (!(redir->nfile.type == NTO2 && fd == 2))
     5343#endif
     5344                close(newfd);
     5345        }
     5346#if ENABLE_ASH_BASH_COMPAT
     5347        if (redir->nfile.type == NTO2 && fd == 1) {
     5348            /* We already redirected it to fd 1, now copy it to 2 */
     5349            newfd = 1;
     5350            fd = 2;
     5351            goto redirect_more;
     5352        }
     5353#endif
     5354    } while ((redir = redir->nfile.next) != NULL);
     5355
    48415356    INT_ON;
    4842     if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
    4843         preverrout_fd = sv->renamed[2];
     5357    if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
     5358        preverrout_fd = copied_fd2;
    48445359}
    48455360
     
    48485363 */
    48495364static void
    4850 popredir(int drop)
     5365popredir(int drop, int restore)
    48515366{
    48525367    struct redirtab *rp;
    48535368    int i;
    48545369
    4855     if (--nullredirs >= 0)
     5370    if (--g_nullredirs >= 0)
    48565371        return;
    48575372    INT_OFF;
    48585373    rp = redirlist;
    4859     for (i = 0; i < 10; i++) {
    4860         if (rp->renamed[i] != EMPTY) {
    4861             if (!drop) {
    4862                 close(i);
    4863                 copyfd(rp->renamed[i], i);
     5374    for (i = 0; i < rp->pair_count; i++) {
     5375        int fd = rp->two_fd[i].orig;
     5376        int copy = rp->two_fd[i].copy;
     5377        if (copy == CLOSED) {
     5378            if (!drop)
     5379                close(fd);
     5380            continue;
     5381        }
     5382        if (copy != EMPTY) {
     5383            if (!drop || (restore && (copy & COPYFD_RESTORE))) {
     5384                copy &= ~COPYFD_RESTORE;
     5385                /*close(fd);*/
     5386                copyfd(copy, fd | COPYFD_EXACT);
    48645387            }
    4865             close(rp->renamed[i]);
     5388            close(copy & ~COPYFD_RESTORE);
    48665389        }
    48675390    }
    48685391    redirlist = rp->next;
    4869     nullredirs = rp->nullredirs;
     5392    g_nullredirs = rp->nullredirs;
    48705393    free(rp);
    48715394    INT_ON;
     
    48835406{
    48845407    for (;;) {
    4885         nullredirs = 0;
     5408        g_nullredirs = 0;
    48865409        if (!redirlist)
    48875410            break;
    4888         popredir(drop);
     5411        popredir(drop, /*restore:*/ 0);
    48895412    }
    48905413}
     
    48995422
    49005423    SAVE_INT(saveint);
    4901     err = setjmp(jmploc.loc) * 2;
     5424    /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
     5425    err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
    49025426    if (!err) {
    49035427        exception_handler = &jmploc;
     
    49055429    }
    49065430    exception_handler = savehandler;
    4907     if (err && exception != EXERROR)
     5431    if (err && exception_type != EXERROR)
    49085432        longjmp(exception_handler->loc, 1);
    49095433    RESTORE_INT(saveint);
     
    49165440 * We have to deal with backquotes, shell variables, and file metacharacters.
    49175441 */
     5442
     5443#if ENABLE_SH_MATH_SUPPORT
     5444static arith_t
     5445ash_arith(const char *s)
     5446{
     5447    arith_state_t math_state;
     5448    arith_t result;
     5449
     5450    math_state.lookupvar = lookupvar;
     5451    math_state.setvar    = setvar2;
     5452    //math_state.endofname = endofname;
     5453
     5454    INT_OFF;
     5455    result = arith(&math_state, s);
     5456    if (math_state.errmsg)
     5457        ash_msg_and_raise_error(math_state.errmsg);
     5458    INT_ON;
     5459
     5460    return result;
     5461}
     5462#endif
    49185463
    49195464/*
     
    49305475#define EXP_QWORD       0x100   /* expand word in quoted parameter expansion */
    49315476/*
    4932  * _rmescape() flags
     5477 * rmescape() flags
    49335478 */
    49345479#define RMESCAPE_ALLOC  0x1     /* Allocate a new string */
     
    49685513 * Our own itoa().
    49695514 */
     5515#if !ENABLE_SH_MATH_SUPPORT
     5516/* cvtnum() is used even if math support is off (to prepare $? values and such) */
     5517typedef long arith_t;
     5518# define ARITH_FMT "%ld"
     5519#endif
    49705520static int
    49715521cvtnum(arith_t num)
     
    49745524
    49755525    expdest = makestrspace(32, expdest);
    4976 #if ENABLE_ASH_MATH_SUPPORT_64
    4977     len = fmtstr(expdest, 32, "%lld", (long long) num);
    4978 #else
    4979     len = fmtstr(expdest, 32, "%ld", num);
    4980 #endif
     5526    len = fmtstr(expdest, 32, ARITH_FMT, num);
    49815527    STADJUST(len, expdest);
    49825528    return len;
     
    49885534    size_t esc = 0;
    49895535
    4990     while (p > start && *--p == CTLESC) {
     5536    while (p > start && (unsigned char)*--p == CTLESC) {
    49915537        esc++;
    49925538    }
     
    49985544 */
    49995545static char *
    5000 _rmescapes(char *str, int flag)
     5546rmescapes(char *str, int flag)
    50015547{
    50025548    static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
     
    50045550    char *p, *q, *r;
    50055551    unsigned inquotes;
    5006     int notescaped;
    5007     int globbing;
     5552    unsigned protect_against_glob;
     5553    unsigned globbing;
    50085554
    50095555    p = strpbrk(str, qchars);
    5010     if (!p) {
     5556    if (!p)
    50115557        return str;
    5012     }
     5558
    50135559    q = p;
    50145560    r = str;
     
    50185564
    50195565        if (flag & RMESCAPE_GROW) {
     5566            int strloc = str - (char *)stackblock();
    50205567            r = makestrspace(fulllen, expdest);
     5568            /* p and str may be invalidated by makestrspace */
     5569            str = (char *)stackblock() + strloc;
     5570            p = str + len;
    50215571        } else if (flag & RMESCAPE_HEAP) {
    50225572            r = ckmalloc(fulllen);
     
    50265576        q = r;
    50275577        if (len > 0) {
    5028             q = memcpy(q, str, len) + len;
    5029         }
    5030     }
     5578            q = (char *)memcpy(q, str, len) + len;
     5579        }
     5580    }
     5581
    50315582    inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
    50325583    globbing = flag & RMESCAPE_GLOB;
    5033     notescaped = globbing;
     5584    protect_against_glob = globbing;
    50345585    while (*p) {
    5035         if (*p == CTLQUOTEMARK) {
     5586        if ((unsigned char)*p == CTLQUOTEMARK) {
     5587// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
     5588// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
     5589// Note: both inquotes and protect_against_glob only affect whether
     5590// CTLESC,<ch> gets converted to <ch> or to \<ch>
    50365591            inquotes = ~inquotes;
    50375592            p++;
    5038             notescaped = globbing;
     5593            protect_against_glob = globbing;
    50395594            continue;
    50405595        }
    50415596        if (*p == '\\') {
    50425597            /* naked back slash */
    5043             notescaped = 0;
     5598            protect_against_glob = 0;
    50445599            goto copy;
    50455600        }
    5046         if (*p == CTLESC) {
     5601        if ((unsigned char)*p == CTLESC) {
    50475602            p++;
    5048             if (notescaped && inquotes && *p != '/') {
     5603            if (protect_against_glob && inquotes && *p != '/') {
    50495604                *q++ = '\\';
    50505605            }
    50515606        }
    5052         notescaped = globbing;
     5607        protect_against_glob = globbing;
    50535608 copy:
    50545609        *q++ = *p++;
     
    50615616    return r;
    50625617}
    5063 #define rmescapes(p) _rmescapes((p), 0)
    5064 
    50655618#define pmatch(a, b) !fnmatch((a), (b), 0)
    50665619
     
    50775630        flag |= RMESCAPE_QUOTED;
    50785631    }
    5079     return _rmescapes((char *)pattern, flag);
     5632    return rmescapes((char *)pattern, flag);
    50805633}
    50815634
     
    50885641    char *q = expdest;
    50895642
    5090     q = makestrspace(len * 2, q);
     5643    q = makestrspace(quotes ? len * 2 : len, q);
    50915644
    50925645    while (len--) {
    5093         int c = signed_char2int(*p++);
    5094         if (!c)
     5646        unsigned char c = *p++;
     5647        if (c == '\0')
    50955648            continue;
    5096         if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
    5097             USTPUTC(CTLESC, q);
     5649        if (quotes) {
     5650            int n = SIT(c, syntax);
     5651            if (n == CCTL || n == CBACK)
     5652                USTPUTC(CTLESC, q);
     5653        }
    50985654        USTPUTC(c, q);
    50995655    }
     
    51215677    } else {
    51225678        INT_OFF;
    5123         ifsp = ckmalloc(sizeof(*ifsp));
    5124         ifsp->next = NULL;
     5679        ifsp = ckzalloc(sizeof(*ifsp));
     5680        /*ifsp->next = NULL; - ckzalloc did it */
    51255681        ifslastp->next = ifsp;
    51265682        INT_ON;
     
    51395695
    51405696    if (ifsfirst.endoff > endoff) {
    5141         while (ifsfirst.next != NULL) {
     5697        while (ifsfirst.next) {
    51425698            struct ifsregion *ifsp;
    51435699            INT_OFF;
     
    51475703            INT_ON;
    51485704        }
    5149         if (ifsfirst.begoff > endoff)
     5705        if (ifsfirst.begoff > endoff) {
    51505706            ifslastp = NULL;
    5151         else {
     5707        } else {
    51525708            ifslastp = &ifsfirst;
    51535709            ifsfirst.endoff = endoff;
     
    51585714    ifslastp = &ifsfirst;
    51595715    while (ifslastp->next && ifslastp->next->begoff < endoff)
    5160         ifslastp=ifslastp->next;
    5161     while (ifslastp->next != NULL) {
     5716        ifslastp = ifslastp->next;
     5717    while (ifslastp->next) {
    51625718        struct ifsregion *ifsp;
    51635719        INT_OFF;
     
    51725728
    51735729static char *
    5174 exptilde(char *startp, char *p, int flag)
    5175 {
    5176     char c;
     5730exptilde(char *startp, char *p, int flags)
     5731{
     5732    unsigned char c;
    51775733    char *name;
    51785734    struct passwd *pw;
    51795735    const char *home;
    5180     int quotes = flag & (EXP_FULL | EXP_CASE);
     5736    int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
    51815737    int startloc;
    51825738
     
    51905746            return startp;
    51915747        case ':':
    5192             if (flag & EXP_VARTILDE)
     5748            if (flags & EXP_VARTILDE)
    51935749                goto done;
    51945750            break;
     
    52015757    *p = '\0';
    52025758    if (*name == '\0') {
    5203         home = lookupvar(homestr);
     5759        home = lookupvar("HOME");
    52045760    } else {
    52055761        pw = getpwnam(name);
     
    52285784struct backcmd {                /* result of evalbackcmd */
    52295785    int fd;                 /* file descriptor to read from */
     5786    int nleft;              /* number of chars in buffer */
    52305787    char *buf;              /* buffer */
    5231     int nleft;              /* number of chars in buffer */
    52325788    struct job *jp;         /* job structure for command */
    52335789};
    52345790
    52355791/* These forward decls are needed to use "eval" code for backticks handling: */
    5236 static int back_exitstatus; /* exit status of backquoted command */
     5792static uint8_t back_exitstatus; /* exit status of backquoted command */
    52375793#define EV_EXIT 01              /* exit after evaluating tree */
    52385794static void evaltree(union node *, int);
    52395795
    5240 static void
     5796static void FAST_FUNC
    52415797evalbackcmd(union node *n, struct backcmd *result)
    52425798{
     
    52475803    result->nleft = 0;
    52485804    result->jp = NULL;
    5249     if (n == NULL) {
     5805    if (n == NULL)
    52505806        goto out;
    5251     }
    52525807
    52535808    saveherefd = herefd;
     
    52605815        if (pipe(pip) < 0)
    52615816            ash_msg_and_raise_error("pipe call failed");
    5262         jp = makejob(n, 1);
     5817        jp = makejob(/*n,*/ 1);
    52635818        if (forkshell(jp, n, FORK_NOJOB) == 0) {
    52645819            FORCE_INT_ON;
    52655820            close(pip[0]);
    52665821            if (pip[1] != 1) {
    5267                 close(1);
    5268                 copyfd(pip[1], 1);
     5822                /*close(1);*/
     5823                copyfd(pip[1], 1 | COPYFD_EXACT);
    52695824                close(pip[1]);
    52705825            }
     
    52955850    char *dest;
    52965851    int startloc;
    5297     int syntax = quoted? DQSYNTAX : BASESYNTAX;
     5852    int syntax = quoted ? DQSYNTAX : BASESYNTAX;
    52985853    struct stackmark smark;
    52995854
     
    53155870        if (in.fd < 0)
    53165871            break;
    5317         i = safe_read(in.fd, buf, sizeof(buf));
     5872        i = nonblock_safe_read(in.fd, buf, sizeof(buf));
    53185873        TRACE(("expbackq: read returns %d\n", i));
    53195874        if (i <= 0)
     
    53225877    }
    53235878
    5324     if (in.buf)
    5325         free(in.buf);
     5879    free(in.buf);
    53265880    if (in.fd >= 0) {
    53275881        close(in.fd);
     
    53385892    if (quoted == 0)
    53395893        recordregion(startloc, dest - (char *)stackblock(), 0);
    5340     TRACE(("evalbackq: size=%d: \"%.*s\"\n",
    5341         (dest - (char *)stackblock()) - startloc,
    5342         (dest - (char *)stackblock()) - startloc,
     5894    TRACE(("evalbackq: size:%d:'%.*s'\n",
     5895        (int)((dest - (char *)stackblock()) - startloc),
     5896        (int)((dest - (char *)stackblock()) - startloc),
    53435897        stackblock() + startloc));
    53445898}
    53455899
    5346 #if ENABLE_ASH_MATH_SUPPORT
     5900#if ENABLE_SH_MATH_SUPPORT
    53475901/*
    53485902 * Expand arithmetic expression.  Backup to start of expression,
     
    53575911    int len;
    53585912
    5359     /*      ifsfree(); */
     5913    /* ifsfree(); */
    53605914
    53615915    /*
     
    53715925        int esc;
    53725926
    5373         while (*p != CTLARI) {
     5927        while ((unsigned char)*p != CTLARI) {
    53745928            p--;
    53755929#if DEBUG
     
    53975951
    53985952    if (quotes)
    5399         rmescapes(p + 2);
    5400 
    5401     len = cvtnum(dash_arith(p + 2));
     5953        rmescapes(p + 2, 0);
     5954
     5955    len = cvtnum(ash_arith(p + 2));
    54025956
    54035957    if (flag != '"')
     
    54075961
    54085962/* argstr needs it */
    5409 static char *evalvar(char *p, int flag);
     5963static char *evalvar(char *p, int flags, struct strlist *var_str_list);
    54105964
    54115965/*
     
    54135967 * characters to allow for further processing.  Otherwise treat
    54145968 * $@ like $* since no splitting will be performed.
     5969 *
     5970 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
     5971 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
     5972 * for correct expansion of "B=$A" word.
    54155973 */
    54165974static void
    5417 argstr(char *p, int flag)
     5975argstr(char *p, int flags, struct strlist *var_str_list)
    54185976{
    54195977    static const char spclchars[] ALIGN1 = {
     
    54265984        CTLBACKQ,
    54275985        CTLBACKQ | CTLQUOTE,
    5428 #if ENABLE_ASH_MATH_SUPPORT
     5986#if ENABLE_SH_MATH_SUPPORT
    54295987        CTLENDARI,
    54305988#endif
    5431         0
     5989        '\0'
    54325990    };
    54335991    const char *reject = spclchars;
    5434     int c;
    5435     int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
    5436     int breakall = flag & EXP_WORD;
     5992    int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
     5993    int breakall = flags & EXP_WORD;
    54375994    int inquotes;
    54385995    size_t length;
    54395996    int startloc;
    54405997
    5441     if (!(flag & EXP_VARTILDE)) {
     5998    if (!(flags & EXP_VARTILDE)) {
    54425999        reject += 2;
    5443     } else if (flag & EXP_VARTILDE2) {
     6000    } else if (flags & EXP_VARTILDE2) {
    54446001        reject++;
    54456002    }
    54466003    inquotes = 0;
    54476004    length = 0;
    5448     if (flag & EXP_TILDE) {
     6005    if (flags & EXP_TILDE) {
    54496006        char *q;
    54506007
    5451         flag &= ~EXP_TILDE;
     6008        flags &= ~EXP_TILDE;
    54526009 tilde:
    54536010        q = p;
    5454         if (*q == CTLESC && (flag & EXP_QWORD))
     6011        if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
    54556012            q++;
    54566013        if (*q == '~')
    5457             p = exptilde(p, q, flag);
     6014            p = exptilde(p, q, flags);
    54586015    }
    54596016 start:
    54606017    startloc = expdest - (char *)stackblock();
    54616018    for (;;) {
     6019        unsigned char c;
     6020
    54626021        length += strcspn(p + length, reject);
    54636022        c = p[length];
    5464         if (c && (!(c & 0x80)
    5465 #if ENABLE_ASH_MATH_SUPPORT
    5466                     || c == CTLENDARI
    5467 #endif
    5468            )) {
    5469             /* c == '=' || c == ':' || c == CTLENDARI */
    5470             length++;
     6023        if (c) {
     6024            if (!(c & 0x80)
     6025            IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
     6026            ) {
     6027                /* c == '=' || c == ':' || c == CTLENDARI */
     6028                length++;
     6029            }
    54716030        }
    54726031        if (length > 0) {
     
    54866045            goto breakloop;
    54876046        case '=':
    5488             if (flag & EXP_VARTILDE2) {
     6047            if (flags & EXP_VARTILDE2) {
    54896048                p--;
    54906049                continue;
    54916050            }
    5492             flag |= EXP_VARTILDE2;
     6051            flags |= EXP_VARTILDE2;
    54936052            reject++;
    54946053            /* fall through */
     
    55096068        case CTLQUOTEMARK:
    55106069            /* "$@" syntax adherence hack */
    5511             if (
    5512                 !inquotes &&
    5513                 !memcmp(p, dolatstr, 4) &&
    5514                 (p[4] == CTLQUOTEMARK || (
    5515                     p[4] == CTLENDVAR &&
    5516                     p[5] == CTLQUOTEMARK
    5517                 ))
     6070            if (!inquotes
     6071             && memcmp(p, dolatstr, 4) == 0
     6072             && (  p[4] == (char)CTLQUOTEMARK
     6073                || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
     6074                )
    55186075            ) {
    5519                 p = evalvar(p + 1, flag) + 1;
     6076                p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
    55206077                goto start;
    55216078            }
     
    55336090            goto addquote;
    55346091        case CTLVAR:
    5535             p = evalvar(p, flag);
     6092            p = evalvar(p, flags, var_str_list);
    55366093            goto start;
    55376094        case CTLBACKQ:
    5538             c = 0;
     6095            c = '\0';
    55396096        case CTLBACKQ|CTLQUOTE:
    55406097            expbackq(argbackq->n, c, quotes);
    55416098            argbackq = argbackq->next;
    55426099            goto start;
    5543 #if ENABLE_ASH_MATH_SUPPORT
     6100#if ENABLE_SH_MATH_SUPPORT
    55446101        case CTLENDARI:
    55456102            p--;
     
    55496106        }
    55506107    }
    5551  breakloop:
    5552     ;
     6108 breakloop: ;
    55536109}
    55546110
    55556111static char *
    5556 scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
    5557     int zero)
    5558 {
    5559     char *loc;
    5560     char *loc2;
     6112scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
     6113        char *pattern, int quotes, int zero)
     6114{
     6115    char *loc, *loc2;
    55616116    char c;
    55626117
     
    55666121        int match;
    55676122        const char *s = loc2;
     6123
    55686124        c = *loc2;
    55696125        if (zero) {
     
    55716127            s = rmesc;
    55726128        }
    5573         match = pmatch(str, s);
     6129        match = pmatch(pattern, s);
     6130
    55746131        *loc2 = c;
    55756132        if (match)
    55766133            return loc;
    5577         if (quotes && *loc == CTLESC)
     6134        if (quotes && (unsigned char)*loc == CTLESC)
    55786135            loc++;
    55796136        loc++;
    55806137        loc2++;
    55816138    } while (c);
    5582     return 0;
     6139    return NULL;
    55836140}
    55846141
    55856142static char *
    5586 scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
    5587     int zero)
    5588 {
     6143scanright(char *startp, char *rmesc, char *rmescend,
     6144        char *pattern, int quotes, int match_at_start)
     6145{
     6146#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
     6147    int try2optimize = match_at_start;
     6148#endif
    55896149    int esc = 0;
    55906150    char *loc;
    55916151    char *loc2;
    55926152
    5593     for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
     6153    /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
     6154     * startp="escaped_value_of_v" rmesc="raw_value_of_v"
     6155     * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
     6156     * Logic:
     6157     * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
     6158     * and on each iteration they go back two/one char until they reach the beginning.
     6159     * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
     6160     */
     6161    /* TODO: document in what other circumstances we are called. */
     6162
     6163    for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
    55946164        int match;
    55956165        char c = *loc2;
    55966166        const char *s = loc2;
    5597         if (zero) {
     6167        if (match_at_start) {
    55986168            *loc2 = '\0';
    55996169            s = rmesc;
    56006170        }
    5601         match = pmatch(str, s);
     6171        match = pmatch(pattern, s);
     6172        //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
    56026173        *loc2 = c;
    56036174        if (match)
    56046175            return loc;
     6176#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
     6177        if (try2optimize) {
     6178            /* Maybe we can optimize this:
     6179             * if pattern ends with unescaped *, we can avoid checking
     6180             * shorter strings: if "foo*" doesnt match "raw_value_of_v",
     6181             * it wont match truncated "raw_value_of_" strings too.
     6182             */
     6183            unsigned plen = strlen(pattern);
     6184            /* Does it end with "*"? */
     6185            if (plen != 0 && pattern[--plen] == '*') {
     6186                /* "xxxx*" is not escaped */
     6187                /* "xxx\*" is escaped */
     6188                /* "xx\\*" is not escaped */
     6189                /* "x\\\*" is escaped */
     6190                int slashes = 0;
     6191                while (plen != 0 && pattern[--plen] == '\\')
     6192                    slashes++;
     6193                if (!(slashes & 1))
     6194                    break; /* ends with unescaped "*" */
     6195            }
     6196            try2optimize = 0;
     6197        }
     6198#endif
    56056199        loc--;
    56066200        if (quotes) {
     
    56146208        }
    56156209    }
    5616     return 0;
    5617 }
    5618 
    5619 static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
     6210    return NULL;
     6211}
     6212
     6213static void varunset(const char *, const char *, const char *, int) NORETURN;
    56206214static void
    56216215varunset(const char *end, const char *var, const char *umsg, int varflags)
     
    56276221    msg = "parameter not set";
    56286222    if (umsg) {
    5629         if (*end == CTLENDVAR) {
     6223        if ((unsigned char)*end == CTLENDVAR) {
    56306224            if (varflags & VSNUL)
    56316225                tail = " or null";
    5632         } else
     6226        } else {
    56336227            msg = umsg;
    5634     }
    5635     ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
    5636 }
     6228        }
     6229    }
     6230    ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
     6231}
     6232
     6233#if ENABLE_ASH_BASH_COMPAT
     6234static char *
     6235parse_sub_pattern(char *arg, int varflags)
     6236{
     6237    char *idx, *repl = NULL;
     6238    unsigned char c;
     6239
     6240    //char *org_arg = arg;
     6241    //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
     6242    idx = arg;
     6243    while (1) {
     6244        c = *arg;
     6245        if (!c)
     6246            break;
     6247        if (c == '/') {
     6248            /* Only the first '/' seen is our separator */
     6249            if (!repl) {
     6250                repl = idx + 1;
     6251                c = '\0';
     6252            }
     6253        }
     6254        *idx++ = c;
     6255        arg++;
     6256        /*
     6257         * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
     6258         * The result is a_\_z_c (not a\_\_z_c)!
     6259         *
     6260         * Enable debug prints in this function and you'll see:
     6261         * ash: arg:'\\b/_\\_z_' varflags:d
     6262         * ash: pattern:'\\b' repl:'_\_z_'
     6263         * That is, \\b is interpreted as \\b, but \\_ as \_!
     6264         * IOW: search pattern and replace string treat backslashes
     6265         * differently! That is the reason why we check repl below:
     6266         */
     6267        if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
     6268            arg++; /* skip both '\', not just first one */
     6269    }
     6270    *idx = c; /* NUL */
     6271    //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
     6272
     6273    return repl;
     6274}
     6275#endif /* ENABLE_ASH_BASH_COMPAT */
    56376276
    56386277static const char *
    5639 subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
    5640 {
     6278subevalvar(char *p, char *varname, int strloc, int subtype,
     6279        int startloc, int varflags, int quotes, struct strlist *var_str_list)
     6280{
     6281    struct nodelist *saveargbackq = argbackq;
    56416282    char *startp;
    56426283    char *loc;
     6284    char *rmesc, *rmescend;
     6285    char *str;
     6286    IF_ASH_BASH_COMPAT(const char *repl = NULL;)
     6287    IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
    56436288    int saveherefd = herefd;
    5644     struct nodelist *saveargbackq = argbackq;
    5645     int amount;
    5646     char *rmesc, *rmescend;
     6289    int amount, workloc, resetloc;
    56476290    int zero;
    5648     char *(*scan)(char *, char *, char *, char *, int , int);
     6291    char *(*scan)(char*, char*, char*, char*, int, int);
     6292
     6293    //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
     6294    //      p, varname, strloc, subtype, startloc, varflags, quotes);
    56496295
    56506296    herefd = -1;
    5651     argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
     6297    argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
     6298            var_str_list);
    56526299    STPUTC('\0', expdest);
    56536300    herefd = saveherefd;
    56546301    argbackq = saveargbackq;
    5655     startp = stackblock() + startloc;
     6302    startp = (char *)stackblock() + startloc;
    56566303
    56576304    switch (subtype) {
    56586305    case VSASSIGN:
    5659         setvar(str, startp, 0);
     6306        setvar(varname, startp, 0);
    56606307        amount = startp - expdest;
    56616308        STADJUST(amount, expdest);
     
    56636310
    56646311    case VSQUESTION:
    5665         varunset(p, str, startp, varflags);
     6312        varunset(p, varname, startp, varflags);
    56666313        /* NOTREACHED */
    5667     }
     6314
     6315#if ENABLE_ASH_BASH_COMPAT
     6316    case VSSUBSTR:
     6317        loc = str = stackblock() + strloc;
     6318        /* Read POS in ${var:POS:LEN} */
     6319        pos = atoi(loc); /* number(loc) errors out on "1:4" */
     6320        len = str - startp - 1;
     6321
     6322        /* *loc != '\0', guaranteed by parser */
     6323        if (quotes) {
     6324            char *ptr;
     6325
     6326            /* Adjust the length by the number of escapes */
     6327            for (ptr = startp; ptr < (str - 1); ptr++) {
     6328                if ((unsigned char)*ptr == CTLESC) {
     6329                    len--;
     6330                    ptr++;
     6331                }
     6332            }
     6333        }
     6334        orig_len = len;
     6335
     6336        if (*loc++ == ':') {
     6337            /* ${var::LEN} */
     6338            len = number(loc);
     6339        } else {
     6340            /* Skip POS in ${var:POS:LEN} */
     6341            len = orig_len;
     6342            while (*loc && *loc != ':') {
     6343                /* TODO?
     6344                 * bash complains on: var=qwe; echo ${var:1a:123}
     6345                if (!isdigit(*loc))
     6346                    ash_msg_and_raise_error(msg_illnum, str);
     6347                 */
     6348                loc++;
     6349            }
     6350            if (*loc++ == ':') {
     6351                len = number(loc);
     6352            }
     6353        }
     6354        if (pos >= orig_len) {
     6355            pos = 0;
     6356            len = 0;
     6357        }
     6358        if (len > (orig_len - pos))
     6359            len = orig_len - pos;
     6360
     6361        for (str = startp; pos; str++, pos--) {
     6362            if (quotes && (unsigned char)*str == CTLESC)
     6363                str++;
     6364        }
     6365        for (loc = startp; len; len--) {
     6366            if (quotes && (unsigned char)*str == CTLESC)
     6367                *loc++ = *str++;
     6368            *loc++ = *str++;
     6369        }
     6370        *loc = '\0';
     6371        amount = loc - expdest;
     6372        STADJUST(amount, expdest);
     6373        return loc;
     6374#endif
     6375    }
     6376
     6377    resetloc = expdest - (char *)stackblock();
     6378
     6379    /* We'll comeback here if we grow the stack while handling
     6380     * a VSREPLACE or VSREPLACEALL, since our pointers into the
     6381     * stack will need rebasing, and we'll need to remove our work
     6382     * areas each time
     6383     */
     6384 IF_ASH_BASH_COMPAT(restart:)
     6385
     6386    amount = expdest - ((char *)stackblock() + resetloc);
     6387    STADJUST(-amount, expdest);
     6388    startp = (char *)stackblock() + startloc;
     6389
     6390    rmesc = startp;
     6391    rmescend = (char *)stackblock() + strloc;
     6392    if (quotes) {
     6393        rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
     6394        if (rmesc != startp) {
     6395            rmescend = expdest;
     6396            startp = (char *)stackblock() + startloc;
     6397        }
     6398    }
     6399    rmescend--;
     6400    str = (char *)stackblock() + strloc;
     6401    preglob(str, varflags & VSQUOTE, 0);
     6402    workloc = expdest - (char *)stackblock();
     6403
     6404#if ENABLE_ASH_BASH_COMPAT
     6405    if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
     6406        char *idx, *end;
     6407
     6408        if (!repl) {
     6409            repl = parse_sub_pattern(str, varflags);
     6410            //bb_error_msg("repl:'%s'", repl);
     6411            if (!repl)
     6412                repl = nullstr;
     6413        }
     6414
     6415        /* If there's no pattern to match, return the expansion unmolested */
     6416        if (str[0] == '\0')
     6417            return NULL;
     6418
     6419        len = 0;
     6420        idx = startp;
     6421        end = str - 1;
     6422        while (idx < end) {
     6423 try_to_match:
     6424            loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
     6425            //bb_error_msg("scanright('%s'):'%s'", str, loc);
     6426            if (!loc) {
     6427                /* No match, advance */
     6428                char *restart_detect = stackblock();
     6429 skip_matching:
     6430                STPUTC(*idx, expdest);
     6431                if (quotes && (unsigned char)*idx == CTLESC) {
     6432                    idx++;
     6433                    len++;
     6434                    STPUTC(*idx, expdest);
     6435                }
     6436                if (stackblock() != restart_detect)
     6437                    goto restart;
     6438                idx++;
     6439                len++;
     6440                rmesc++;
     6441                /* continue; - prone to quadratic behavior, smarter code: */
     6442                if (idx >= end)
     6443                    break;
     6444                if (str[0] == '*') {
     6445                    /* Pattern is "*foo". If "*foo" does not match "long_string",
     6446                     * it would never match "ong_string" etc, no point in trying.
     6447                     */
     6448                    goto skip_matching;
     6449                }
     6450                goto try_to_match;
     6451            }
     6452
     6453            if (subtype == VSREPLACEALL) {
     6454                while (idx < loc) {
     6455                    if (quotes && (unsigned char)*idx == CTLESC)
     6456                        idx++;
     6457                    idx++;
     6458                    rmesc++;
     6459                }
     6460            } else {
     6461                idx = loc;
     6462            }
     6463
     6464            //bb_error_msg("repl:'%s'", repl);
     6465            for (loc = (char*)repl; *loc; loc++) {
     6466                char *restart_detect = stackblock();
     6467                if (quotes && *loc == '\\') {
     6468                    STPUTC(CTLESC, expdest);
     6469                    len++;
     6470                }
     6471                STPUTC(*loc, expdest);
     6472                if (stackblock() != restart_detect)
     6473                    goto restart;
     6474                len++;
     6475            }
     6476
     6477            if (subtype == VSREPLACE) {
     6478                //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
     6479                while (*idx) {
     6480                    char *restart_detect = stackblock();
     6481                    STPUTC(*idx, expdest);
     6482                    if (stackblock() != restart_detect)
     6483                        goto restart;
     6484                    len++;
     6485                    idx++;
     6486                }
     6487                break;
     6488            }
     6489        }
     6490
     6491        /* We've put the replaced text into a buffer at workloc, now
     6492         * move it to the right place and adjust the stack.
     6493         */
     6494        STPUTC('\0', expdest);
     6495        startp = (char *)stackblock() + startloc;
     6496        memmove(startp, (char *)stackblock() + workloc, len + 1);
     6497        //bb_error_msg("startp:'%s'", startp);
     6498        amount = expdest - (startp + len);
     6499        STADJUST(-amount, expdest);
     6500        return startp;
     6501    }
     6502#endif /* ENABLE_ASH_BASH_COMPAT */
    56686503
    56696504    subtype -= VSTRIMRIGHT;
    56706505#if DEBUG
    5671     if (subtype < 0 || subtype > 3)
     6506    if (subtype < 0 || subtype > 7)
    56726507        abort();
    56736508#endif
    5674 
    5675     rmesc = startp;
    5676     rmescend = stackblock() + strloc;
    5677     if (quotes) {
    5678         rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
    5679         if (rmesc != startp) {
    5680             rmescend = expdest;
    5681             startp = stackblock() + startloc;
    5682         }
    5683     }
    5684     rmescend--;
    5685     str = stackblock() + strloc;
    5686     preglob(str, varflags & VSQUOTE, 0);
    5687 
    5688     /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
     6509    /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
    56896510    zero = subtype >> 1;
    56906511    /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
     
    57066527/*
    57076528 * Add the value of a specialized variable to the stack string.
    5708  */
    5709 static ssize_t
    5710 varvalue(char *name, int varflags, int flags)
    5711 {
     6529 * name parameter (examples):
     6530 * ash -c 'echo $1'      name:'1='
     6531 * ash -c 'echo $qwe'    name:'qwe='
     6532 * ash -c 'echo $$'      name:'$='
     6533 * ash -c 'echo ${$}'    name:'$='
     6534 * ash -c 'echo ${$##q}' name:'$=q'
     6535 * ash -c 'echo ${#$}'   name:'$='
     6536 * note: examples with bad shell syntax:
     6537 * ash -c 'echo ${#$1}'  name:'$=1'
     6538 * ash -c 'echo ${#1#}'  name:'1=#'
     6539 */
     6540static NOINLINE ssize_t
     6541varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
     6542{
     6543    const char *p;
    57126544    int num;
    5713     char *p;
    57146545    int i;
    5715     int sep = 0;
    57166546    int sepq = 0;
    57176547    ssize_t len = 0;
    5718     char **ap;
    5719     int syntax;
     6548    int subtype = varflags & VSTYPE;
     6549    int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
    57206550    int quoted = varflags & VSQUOTE;
    5721     int subtype = varflags & VSTYPE;
    5722     int quotes = flags & (EXP_FULL | EXP_CASE);
    5723 
    5724     if (quoted && (flags & EXP_FULL))
    5725         sep = 1 << CHAR_BIT;
    5726 
    5727     syntax = quoted ? DQSYNTAX : BASESYNTAX;
     6551    int syntax = quoted ? DQSYNTAX : BASESYNTAX;
     6552
    57286553    switch (*name) {
    57296554    case '$':
     
    57426567 numvar:
    57436568        len = cvtnum(num);
    5744         break;
     6569        goto check_1char_name;
    57456570    case '-':
    5746         p = makestrspace(NOPTS, expdest);
     6571        expdest = makestrspace(NOPTS, expdest);
    57476572        for (i = NOPTS - 1; i >= 0; i--) {
    57486573            if (optlist[i]) {
    5749                 USTPUTC(optletters(i), p);
     6574                USTPUTC(optletters(i), expdest);
    57506575                len++;
    57516576            }
    57526577        }
    5753         expdest = p;
     6578 check_1char_name:
     6579#if 0
     6580        /* handles cases similar to ${#$1} */
     6581        if (name[2] != '\0')
     6582            raise_error_syntax("bad substitution");
     6583#endif
    57546584        break;
    5755     case '@':
    5756         if (sep)
     6585    case '@': {
     6586        char **ap;
     6587        int sep;
     6588
     6589        if (quoted && (flags & EXP_FULL)) {
     6590            /* note: this is not meant as PEOF value */
     6591            sep = 1 << CHAR_BIT;
    57576592            goto param;
     6593        }
    57586594        /* fall through */
    57596595    case '*':
    5760         sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
    5761         if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
     6596        sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
     6597        i = SIT(sep, syntax);
     6598        if (quotes && (i == CCTL || i == CBACK))
    57626599            sepq = 1;
    57636600 param:
     
    57656602        if (!ap)
    57666603            return -1;
    5767         while ((p = *ap++)) {
     6604        while ((p = *ap++) != NULL) {
    57686605            size_t partlen;
    57696606
     
    57846621                if (sepq)
    57856622                    STPUTC(CTLESC, q);
     6623                /* note: may put NUL despite sep != 0
     6624                 * (see sep = 1 << CHAR_BIT above) */
    57866625                STPUTC(sep, q);
    57876626                expdest = q;
     
    57896628        }
    57906629        return len;
     6630    } /* case '@' and '*' */
    57916631    case '0':
    57926632    case '1':
     
    57996639    case '8':
    58006640    case '9':
    5801         num = atoi(name);
     6641        num = atoi(name); /* number(name) fails on ${N#str} etc */
    58026642        if (num < 0 || num > shellparam.nparam)
    58036643            return -1;
     
    58056645        goto value;
    58066646    default:
     6647        /* NB: name has form "VAR=..." */
     6648
     6649        /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
     6650         * which should be considered before we check variables. */
     6651        if (var_str_list) {
     6652            unsigned name_len = (strchrnul(name, '=') - name) + 1;
     6653            p = NULL;
     6654            do {
     6655                char *str, *eq;
     6656                str = var_str_list->text;
     6657                eq = strchr(str, '=');
     6658                if (!eq) /* stop at first non-assignment */
     6659                    break;
     6660                eq++;
     6661                if (name_len == (unsigned)(eq - str)
     6662                 && strncmp(str, name, name_len) == 0
     6663                ) {
     6664                    p = eq;
     6665                    /* goto value; - WRONG! */
     6666                    /* think "A=1 A=2 B=$A" */
     6667                }
     6668                var_str_list = var_str_list->next;
     6669            } while (var_str_list);
     6670            if (p)
     6671                goto value;
     6672        }
    58076673        p = lookupvar(name);
    58086674 value:
     
    58266692 */
    58276693static char *
    5828 evalvar(char *p, int flag)
    5829 {
    5830     int subtype;
    5831     int varflags;
     6694evalvar(char *p, int flags, struct strlist *var_str_list)
     6695{
     6696    char varflags;
     6697    char subtype;
     6698    char quoted;
     6699    char easy;
    58326700    char *var;
    58336701    int patloc;
    5834     int c;
    58356702    int startloc;
    58366703    ssize_t varlen;
    5837     int easy;
    5838     int quotes;
    5839     int quoted;
    5840 
    5841     quotes = flag & (EXP_FULL | EXP_CASE);
    5842     varflags = *p++;
     6704
     6705    varflags = (unsigned char) *p++;
    58436706    subtype = varflags & VSTYPE;
    58446707    quoted = varflags & VSQUOTE;
     
    58466709    easy = (!quoted || (*var == '@' && shellparam.nparam));
    58476710    startloc = expdest - (char *)stackblock();
    5848     p = strchr(p, '=') + 1;
     6711    p = strchr(p, '=') + 1; //TODO: use var_end(p)?
    58496712
    58506713 again:
    5851     varlen = varvalue(var, varflags, flag);
     6714    varlen = varvalue(var, varflags, flags, var_str_list);
    58526715    if (varflags & VSNUL)
    58536716        varlen--;
     
    58626725        if (varlen < 0) {
    58636726            argstr(
    5864                 p, flag | EXP_TILDE |
    5865                     (quoted ?  EXP_QWORD : EXP_WORD)
     6727                p,
     6728                flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
     6729                var_str_list
    58666730            );
    58676731            goto end;
     
    58746738    if (subtype == VSASSIGN || subtype == VSQUESTION) {
    58756739        if (varlen < 0) {
    5876             if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
     6740            if (subevalvar(p, var, /* strloc: */ 0,
     6741                    subtype, startloc, varflags,
     6742                    /* quotes: */ 0,
     6743                    var_str_list)
     6744            ) {
    58776745                varflags &= ~VSNUL;
    58786746                /*
     
    58996767
    59006768    if (subtype == VSNORMAL) {
    5901         if (!easy)
    5902             goto end;
    5903  record:
    5904         recordregion(startloc, expdest - (char *)stackblock(), quoted);
     6769        if (easy)
     6770            goto record;
    59056771        goto end;
    59066772    }
     
    59126778    case VSTRIMRIGHT:
    59136779    case VSTRIMRIGHTMAX:
     6780#if ENABLE_ASH_BASH_COMPAT
     6781    case VSSUBSTR:
     6782    case VSREPLACE:
     6783    case VSREPLACEALL:
     6784#endif
    59146785        break;
    59156786    default:
     
    59256796        STPUTC('\0', expdest);
    59266797        patloc = expdest - (char *)stackblock();
    5927         if (subevalvar(p, NULL, patloc, subtype,
    5928                 startloc, varflags, quotes) == 0) {
     6798        if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
     6799                startloc, varflags,
     6800//TODO: | EXP_REDIR too? All other such places do it too
     6801                /* quotes: */ flags & (EXP_FULL | EXP_CASE),
     6802                var_str_list)
     6803        ) {
    59296804            int amount = expdest - (
    59306805                (char *)stackblock() + patloc - 1
     
    59346809        /* Remove any recorded regions beyond start of variable */
    59356810        removerecordregions(startloc);
    5936         goto record;
     6811 record:
     6812        recordregion(startloc, expdest - (char *)stackblock(), quoted);
    59376813    }
    59386814
     
    59416817        int nesting = 1;
    59426818        for (;;) {
    5943             c = *p++;
     6819            unsigned char c = *p++;
    59446820            if (c == CTLESC)
    59456821                p++;
     
    59896865            while (p < string + ifsp->endoff) {
    59906866                q = p;
    5991                 if (*p == CTLESC)
     6867                if ((unsigned char)*p == CTLESC)
    59926868                    p++;
    59936869                if (!strchr(ifs, *p)) {
     
    60046880                }
    60056881                *q = '\0';
    6006                 sp = stalloc(sizeof(*sp));
     6882                sp = stzalloc(sizeof(*sp));
    60076883                sp->text = start;
    60086884                *arglist->lastp = sp;
     
    60156891                        }
    60166892                        q = p;
    6017                         if (*p == CTLESC)
     6893                        if ((unsigned char)*p == CTLESC)
    60186894                            p++;
    6019                         if (strchr(ifs, *p) == NULL ) {
     6895                        if (strchr(ifs, *p) == NULL) {
    60206896                            p = q;
    60216897                            break;
    6022                         } else if (strchr(defifs, *p) == NULL) {
     6898                        }
     6899                        if (strchr(defifs, *p) == NULL) {
    60236900                            if (ifsspc) {
    60246901                                p++;
     
    60446921
    60456922 add:
    6046     sp = stalloc(sizeof(*sp));
     6923    sp = stzalloc(sizeof(*sp));
    60476924    sp->text = start;
    60486925    *arglist->lastp = sp;
     
    60766953    struct strlist *sp;
    60776954
    6078     sp = stalloc(sizeof(*sp));
     6955    sp = stzalloc(sizeof(*sp));
    60796956    sp->text = ststrdup(name);
    60806957    *exparg.lastp = sp;
     
    60826959}
    60836960
    6084 static char *expdir;
    6085 
    60866961/*
    60876962 * Do metacharacter (i.e. *, ?, [...]) expansion.
    60886963 */
    60896964static void
    6090 expmeta(char *enddir, char *name)
     6965expmeta(char *expdir, char *enddir, char *name)
    60916966{
    60926967    char *p;
     
    61767051    if (*p == '.')
    61777052        matchdot++;
    6178     while (! intpending && (dp = readdir(dirp)) != NULL) {
    6179         if (dp->d_name[0] == '.' && ! matchdot)
     7053    while (!pending_int && (dp = readdir(dirp)) != NULL) {
     7054        if (dp->d_name[0] == '.' && !matchdot)
    61807055            continue;
    61817056        if (pmatch(start, dp->d_name)) {
     
    61877062                    continue;
    61887063                p[-1] = '/';
    6189                 expmeta(p, endname);
     7064                expmeta(expdir, p, endname);
    61907065            }
    61917066        }
    61927067    }
    61937068    closedir(dirp);
    6194     if (! atend)
     7069    if (!atend)
    61957070        endname[-1] = '/';
    61967071}
     
    62087083    half = len >> 1;
    62097084    p = list;
    6210     for (n = half; --n >= 0; ) {
     7085    for (n = half; --n >= 0;) {
    62117086        q = p;
    62127087        p = p->next;
     
    62617136
    62627137static void
    6263 expandmeta(struct strlist *str, int flag)
     7138expandmeta(struct strlist *str /*, int flag*/)
    62647139{
    62657140    static const char metachars[] ALIGN1 = {
     
    62697144
    62707145    while (str) {
     7146        char *expdir;
    62717147        struct strlist **savelastp;
    62727148        struct strlist *sp;
     
    62857161            expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
    62867162        }
    6287 
    6288         expmeta(expdir, p);
     7163        expmeta(expdir, expdir, p);
    62897164        free(expdir);
    62907165        if (p != str->text)
     
    62977172 nometa:
    62987173            *exparg.lastp = str;
    6299             rmescapes(str->text);
     7174            rmescapes(str->text, 0);
    63007175            exparg.lastp = &str->next;
    63017176        } else {
     
    63267201    ifsfirst.next = NULL;
    63277202    ifslastp = NULL;
    6328     argstr(arg->narg.text, flag);
     7203    argstr(arg->narg.text, flag,
     7204            /* var_str_list: */ arglist ? arglist->list : NULL);
    63297205    p = _STPUTC('\0', expdest);
    63307206    expdest = p - 1;
     
    63417217        *exparg.lastp = NULL;
    63427218        exparg.lastp = &exparg.list;
    6343         expandmeta(exparg.list, flag);
     7219        expandmeta(exparg.list /*, flag*/);
    63447220    } else {
    63457221        if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
    6346             rmescapes(p);
    6347         sp = stalloc(sizeof(*sp));
     7222            rmescapes(p, 0);
     7223        sp = stzalloc(sizeof(*sp));
    63487224        sp->text = p;
    63497225        *exparg.lastp = sp;
     
    63927268    STARTSTACKSTR(expdest);
    63937269    ifslastp = NULL;
    6394     argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
     7270    argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
     7271            /* var_str_list: */ NULL);
    63957272    STACKSTRNUL(expdest);
    63967273    result = patmatch(stackblock(), val);
     
    64047281struct builtincmd {
    64057282    const char *name;
    6406     int (*builtin)(int, char **);
     7283    int (*builtin)(int, char **) FAST_FUNC;
    64077284    /* unsigned flags; */
    64087285};
    64097286#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
     7287/* "regular" builtins always take precedence over commands,
     7288 * regardless of PATH=....%builtin... position */
    64107289#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
    6411 #define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
     7290#define IS_BUILTIN_ASSIGN(b)  ((b)->name[0] & 4)
    64127291
    64137292struct cmdentry {
    6414     int cmdtype;
     7293    smallint cmdtype;       /* CMDxxx */
    64157294    union param {
    64167295        int index;
     7296        /* index >= 0 for commands without path (slashes) */
     7297        /* (TODO: what exactly does the value mean? PATH position?) */
     7298        /* index == -1 for commands with slashes */
     7299        /* index == (-2 - applet_no) for NOFORK applets */
    64177300        const struct builtincmd *cmd;
    64187301        struct funcnode *func;
     
    64467329 */
    64477330
    6448 #define CMDTABLESIZE 31         /* should be prime */
    6449 #define ARB 1                   /* actual size determined at run time */
    6450 
    64517331struct tblentry {
    64527332    struct tblentry *next;  /* next entry in hash chain */
    64537333    union param param;      /* definition of builtin function */
    6454     short cmdtype;          /* index identifying command */
     7334    smallint cmdtype;       /* CMDxxx */
    64557335    char rehash;            /* if set, cd done since entry created */
    6456     char cmdname[ARB];      /* name of command */
     7336    char cmdname[1];        /* name of command */
    64577337};
    64587338
    6459 static struct tblentry *cmdtable[CMDTABLESIZE];
    6460 static int builtinloc = -1;             /* index in path of %builtin, or -1 */
     7339static struct tblentry **cmdtable;
     7340#define INIT_G_cmdtable() do { \
     7341    cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
     7342} while (0)
     7343
     7344static int builtinloc = -1;     /* index in path of %builtin, or -1 */
     7345
    64617346
    64627347static void
    6463 tryexec(char *cmd, char **argv, char **envp)
     7348tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
    64647349{
    64657350    int repeated = 0;
    64667351
    64677352#if ENABLE_FEATURE_SH_STANDALONE
    6468     if (strchr(cmd, '/') == NULL) {
    6469         const struct bb_applet *a;
    6470 
    6471         a = find_applet_by_name(cmd);
    6472         if (a) {
    6473             if (a->noexec) {
    6474                 current_applet = a;
    6475                 run_current_applet_and_exit(argv);
    6476             }
    6477             /* re-exec ourselves with the new arguments */
    6478             execve(bb_busybox_exec_path, argv, envp);
    6479             /* If they called chroot or otherwise made the binary no longer
    6480              * executable, fall through */
    6481         }
     7353    if (applet_no >= 0) {
     7354        if (APPLET_IS_NOEXEC(applet_no)) {
     7355            clearenv();
     7356            while (*envp)
     7357                putenv(*envp++);
     7358            run_applet_no_and_exit(applet_no, argv);
     7359        }
     7360        /* re-exec ourselves with the new arguments */
     7361        execve(bb_busybox_exec_path, argv, envp);
     7362        /* If they called chroot or otherwise made the binary no longer
     7363         * executable, fall through */
    64827364    }
    64837365#endif
     
    64917373    execve(cmd, argv, envp);
    64927374#endif
    6493     if (repeated++) {
     7375    if (repeated) {
    64947376        free(argv);
    6495     } else if (errno == ENOEXEC) {
     7377        return;
     7378    }
     7379    if (errno == ENOEXEC) {
    64967380        char **ap;
    64977381        char **new;
    64987382
    64997383        for (ap = argv; *ap; ap++)
    6500             ;
    6501         ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
     7384            continue;
     7385        ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
    65027386        ap[1] = cmd;
    65037387        ap[0] = cmd = (char *)DEFAULT_SHELL;
    65047388        ap += 2;
    65057389        argv++;
    6506         while ((*ap++ = *argv++))
    6507             ;
     7390        while ((*ap++ = *argv++) != NULL)
     7391            continue;
    65087392        argv = new;
     7393        repeated++;
    65097394        goto repeat;
    65107395    }
     
    65157400 * have to change the find_command routine as well.
    65167401 */
    6517 #define environment() listvars(VEXPORT, VUNSET, 0)
    6518 static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
     7402static void shellexec(char **, const char *, int) NORETURN;
    65197403static void
    65207404shellexec(char **argv, const char *path, int idx)
     
    65247408    char **envp;
    65257409    int exerrno;
    6526 
    6527     clearredir(1);
    6528     envp = environment();
    6529     if (strchr(argv[0], '/')
    65307410#if ENABLE_FEATURE_SH_STANDALONE
    6531      || find_applet_by_name(argv[0])
     7411    int applet_no = -1;
     7412#endif
     7413
     7414    clearredir(/*drop:*/ 1);
     7415    envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
     7416    if (strchr(argv[0], '/') != NULL
     7417#if ENABLE_FEATURE_SH_STANDALONE
     7418     || (applet_no = find_applet_by_name(argv[0])) >= 0
    65327419#endif
    65337420    ) {
    6534         tryexec(argv[0], argv, envp);
     7421        tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
    65357422        e = errno;
    65367423    } else {
    65377424        e = ENOENT;
    6538         while ((cmdname = padvance(&path, argv[0])) != NULL) {
     7425        while ((cmdname = path_advance(&path, argv[0])) != NULL) {
    65397426            if (--idx < 0 && pathopt == NULL) {
    6540                 tryexec(cmdname, argv, envp);
     7427                tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
    65417428                if (errno != ENOENT && errno != ENOTDIR)
    65427429                    e = errno;
     
    65597446    }
    65607447    exitstatus = exerrno;
    6561     TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
    6562         argv[0], e, suppressint ));
     7448    TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
     7449        argv[0], e, suppress_int));
    65637450    ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
    65647451    /* NOTREACHED */
     
    65757462    path = pathval();
    65767463    do {
    6577         name = padvance(&path, cmdp->cmdname);
     7464        name = path_advance(&path, cmdp->cmdname);
    65787465        stunalloc(name);
    65797466    } while (--idx >= 0);
     
    66427529    }
    66437530    if (add && cmdp == NULL) {
    6644         cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
    6645                     + strlen(name) + 1);
    6646         cmdp->next = NULL;
     7531        cmdp = *pp = ckzalloc(sizeof(struct tblentry)
     7532                + strlen(name)
     7533                /* + 1 - already done because
     7534                 * tblentry::cmdname is char[1] */);
     7535        /*cmdp->next = NULL; - ckzalloc did it */
    66477536        cmdp->cmdtype = CMDUNKNOWN;
    66487537        strcpy(cmdp->cmdname, name);
     
    66877576}
    66887577
    6689 static int
    6690 hashcmd(int argc, char **argv)
     7578static int FAST_FUNC
     7579hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    66917580{
    66927581    struct tblentry **pp;
     
    66967585    char *name;
    66977586
    6698     while ((c = nextopt("r")) != '\0') {
     7587    if (nextopt("r") != '\0') {
    66997588        clearcmdentry(0);
    67007589        return 0;
    67017590    }
     7591
    67027592    if (*argptr == NULL) {
    67037593        for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
     
    67097599        return 0;
    67107600    }
     7601
    67117602    c = 0;
    67127603    while ((name = *argptr) != NULL) {
     
    67147605        if (cmdp != NULL
    67157606         && (cmdp->cmdtype == CMDNORMAL
    6716              || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
     7607             || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
     7608        ) {
    67177609            delete_cmd_entry();
     7610        }
    67187611        find_command(name, &entry, DO_ERR, pathval());
    67197612        if (entry.cmdtype == CMDUNKNOWN)
     
    67367629    for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
    67377630        for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
    6738             if (cmdp->cmdtype == CMDNORMAL || (
    6739                 cmdp->cmdtype == CMDBUILTIN &&
    6740                 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
    6741                 builtinloc > 0
    6742             ))
     7631            if (cmdp->cmdtype == CMDNORMAL
     7632             || (cmdp->cmdtype == CMDBUILTIN
     7633                 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
     7634                 && builtinloc > 0)
     7635            ) {
    67437636                cmdp->rehash = 1;
     7637            }
    67447638        }
    67457639    }
     
    67527646 * Called with interrupts off.
    67537647 */
    6754 static void
    6755 changepath(const char *newval)
    6756 {
    6757     const char *old, *new;
     7648static void FAST_FUNC
     7649changepath(const char *new)
     7650{
     7651    const char *old;
     7652    int firstchange;
    67587653    int idx;
    6759     int firstchange;
    67607654    int idx_bltin;
    67617655
    67627656    old = pathval();
    6763     new = newval;
    67647657    firstchange = 9999;     /* assume no change */
    67657658    idx = 0;
     
    67697662            firstchange = idx;
    67707663            if ((*old == '\0' && *new == ':')
    6771              || (*old == ':' && *new == '\0'))
     7664             || (*old == ':' && *new == '\0')
     7665            ) {
    67727666                firstchange++;
     7667            }
    67737668            old = new;      /* ignore subsequent differences */
    67747669        }
     
    67777672        if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
    67787673            idx_bltin = idx;
    6779         if (*new == ':') {
     7674        if (*new == ':')
    67807675            idx++;
    6781         }
    6782         new++, old++;
     7676        new++;
     7677        old++;
    67837678    }
    67847679    if (builtinloc < 0 && idx_bltin >= 0)
     
    68197714#define TBEGIN 27
    68207715#define TEND 28
     7716typedef smallint token_id_t;
    68217717
    68227718/* first char is indicating which tokens mark the end of a list */
     
    68557751};
    68567752
    6857 static const char *
    6858 tokname(int tok)
    6859 {
    6860     static char buf[16];
    6861 
    6862 //try this:
    6863 //if (tok < TSEMI) return tokname_array[tok] + 1;
    6864 //sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
    6865 //return buf;
    6866 
    6867     if (tok >= TSEMI)
    6868         buf[0] = '"';
    6869     sprintf(buf + (tok >= TSEMI), "%s%c",
    6870             tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
    6871     return buf;
    6872 }
    6873 
    68747753/* Wrapper around strcmp for qsort/bsearch/... */
    68757754static int
     
    69377816        int j = entry.u.index;
    69387817        char *p;
    6939         if (j == -1) {
     7818        if (j < 0) {
    69407819            p = command;
    69417820        } else {
    69427821            do {
    6943                 p = padvance(&path, command);
     7822                p = path_advance(&path, command);
    69447823                stunalloc(p);
    69457824            } while (--j >= 0);
     
    69817860    }
    69827861 out:
    6983     outstr("\n", stdout);
     7862    out1str("\n");
    69847863    return 0;
    69857864}
    69867865
    6987 static int
    6988 typecmd(int argc, char **argv)
     7866static int FAST_FUNC
     7867typecmd(int argc UNUSED_PARAM, char **argv)
    69897868{
    69907869    int i = 1;
     
    69977876        verbose = 0;
    69987877    }
    6999     while (i < argc) {
     7878    while (argv[i]) {
    70007879        err |= describe_command(argv[i++], verbose);
    70017880    }
     
    70047883
    70057884#if ENABLE_ASH_CMDCMD
    7006 static int
    7007 commandcmd(int argc, char **argv)
     7885static int FAST_FUNC
     7886commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    70087887{
    70097888    int c;
     
    70227901            abort();
    70237902#endif
    7024     if (verify)
     7903    /* Mimic bash: just "command -v" doesn't complain, it's a nop */
     7904    if (verify && (*argptr != NULL)) {
    70257905        return describe_command(*argptr, verify - VERIFY_BRIEF);
     7906    }
    70267907
    70277908    return 0;
     
    70327913/* ============ eval.c */
    70337914
    7034 static int funcblocksize;          /* size of structures in function */
    7035 static int funcstringsize;         /* size of strings in node */
    7036 static void *funcblock;            /* block to allocate function from */
    7037 static char *funcstring;           /* block to allocate strings from */
     7915static int funcblocksize;       /* size of structures in function */
     7916static int funcstringsize;      /* size of strings in node */
     7917static void *funcblock;         /* block to allocate function from */
     7918static char *funcstring;        /* block to allocate strings from */
    70387919
    70397920/* flags in argument to evaltree */
    7040 #define EV_EXIT 01              /* exit after evaluating tree */
    7041 #define EV_TESTED 02            /* exit status is checked; ignore -e flag */
     7921#define EV_EXIT    01           /* exit after evaluating tree */
     7922#define EV_TESTED  02           /* exit status is checked; ignore -e flag */
    70427923#define EV_BACKCMD 04           /* command executing within back quotes */
    70437924
    7044 static const short nodesize[26] = {
    7045     SHELL_ALIGN(sizeof(struct ncmd)),
    7046     SHELL_ALIGN(sizeof(struct npipe)),
    7047     SHELL_ALIGN(sizeof(struct nredir)),
    7048     SHELL_ALIGN(sizeof(struct nredir)),
    7049     SHELL_ALIGN(sizeof(struct nredir)),
    7050     SHELL_ALIGN(sizeof(struct nbinary)),
    7051     SHELL_ALIGN(sizeof(struct nbinary)),
    7052     SHELL_ALIGN(sizeof(struct nbinary)),
    7053     SHELL_ALIGN(sizeof(struct nif)),
    7054     SHELL_ALIGN(sizeof(struct nbinary)),
    7055     SHELL_ALIGN(sizeof(struct nbinary)),
    7056     SHELL_ALIGN(sizeof(struct nfor)),
    7057     SHELL_ALIGN(sizeof(struct ncase)),
    7058     SHELL_ALIGN(sizeof(struct nclist)),
    7059     SHELL_ALIGN(sizeof(struct narg)),
    7060     SHELL_ALIGN(sizeof(struct narg)),
    7061     SHELL_ALIGN(sizeof(struct nfile)),
    7062     SHELL_ALIGN(sizeof(struct nfile)),
    7063     SHELL_ALIGN(sizeof(struct nfile)),
    7064     SHELL_ALIGN(sizeof(struct nfile)),
    7065     SHELL_ALIGN(sizeof(struct nfile)),
    7066     SHELL_ALIGN(sizeof(struct ndup)),
    7067     SHELL_ALIGN(sizeof(struct ndup)),
    7068     SHELL_ALIGN(sizeof(struct nhere)),
    7069     SHELL_ALIGN(sizeof(struct nhere)),
    7070     SHELL_ALIGN(sizeof(struct nnot)),
     7925static const uint8_t nodesize[N_NUMBER] = {
     7926    [NCMD     ] = SHELL_ALIGN(sizeof(struct ncmd)),
     7927    [NPIPE    ] = SHELL_ALIGN(sizeof(struct npipe)),
     7928    [NREDIR   ] = SHELL_ALIGN(sizeof(struct nredir)),
     7929    [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
     7930    [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
     7931    [NAND     ] = SHELL_ALIGN(sizeof(struct nbinary)),
     7932    [NOR      ] = SHELL_ALIGN(sizeof(struct nbinary)),
     7933    [NSEMI    ] = SHELL_ALIGN(sizeof(struct nbinary)),
     7934    [NIF      ] = SHELL_ALIGN(sizeof(struct nif)),
     7935    [NWHILE   ] = SHELL_ALIGN(sizeof(struct nbinary)),
     7936    [NUNTIL   ] = SHELL_ALIGN(sizeof(struct nbinary)),
     7937    [NFOR     ] = SHELL_ALIGN(sizeof(struct nfor)),
     7938    [NCASE    ] = SHELL_ALIGN(sizeof(struct ncase)),
     7939    [NCLIST   ] = SHELL_ALIGN(sizeof(struct nclist)),
     7940    [NDEFUN   ] = SHELL_ALIGN(sizeof(struct narg)),
     7941    [NARG     ] = SHELL_ALIGN(sizeof(struct narg)),
     7942    [NTO      ] = SHELL_ALIGN(sizeof(struct nfile)),
     7943#if ENABLE_ASH_BASH_COMPAT
     7944    [NTO2     ] = SHELL_ALIGN(sizeof(struct nfile)),
     7945#endif
     7946    [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
     7947    [NFROM    ] = SHELL_ALIGN(sizeof(struct nfile)),
     7948    [NFROMTO  ] = SHELL_ALIGN(sizeof(struct nfile)),
     7949    [NAPPEND  ] = SHELL_ALIGN(sizeof(struct nfile)),
     7950    [NTOFD    ] = SHELL_ALIGN(sizeof(struct ndup)),
     7951    [NFROMFD  ] = SHELL_ALIGN(sizeof(struct ndup)),
     7952    [NHERE    ] = SHELL_ALIGN(sizeof(struct nhere)),
     7953    [NXHERE   ] = SHELL_ALIGN(sizeof(struct nhere)),
     7954    [NNOT     ] = SHELL_ALIGN(sizeof(struct nnot)),
    70717955};
    70727956
     
    71388022        break;
    71398023    case NTO:
     8024#if ENABLE_ASH_BASH_COMPAT
     8025    case NTO2:
     8026#endif
    71408027    case NCLOBBER:
    71418028    case NFROM:
     
    72098096    case NPIPE:
    72108097        new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
    7211         new->npipe.backgnd = n->npipe.backgnd;
     8098        new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
    72128099        break;
    72138100    case NREDIR:
     
    72518138        break;
    72528139    case NTO:
     8140#if ENABLE_ASH_BASH_COMPAT
     8141    case NTO2:
     8142#endif
    72538143    case NCLOBBER:
    72548144    case NFROM:
     
    73168206}
    73178207
    7318 static int evalskip;            /* set if we are skipping commands */
    7319 /* reasons for skipping commands (see comment on breakcmd routine) */
     8208/* Reasons for skipping commands (see comment on breakcmd routine) */
    73208209#define SKIPBREAK      (1 << 0)
    73218210#define SKIPCONT       (1 << 1)
     
    73238212#define SKIPFILE       (1 << 3)
    73248213#define SKIPEVAL       (1 << 4)
     8214static smallint evalskip;       /* set to SKIPxxx if we are skipping commands */
    73258215static int skipcount;           /* number of levels to skip */
    73268216static int funcnest;            /* depth of function calls */
    7327 
    7328 /* forward decl way out to parsing code - dotrap needs it */
     8217static int loopnest;            /* current loop nesting level */
     8218
     8219/* Forward decl way out to parsing code - dotrap needs it */
    73298220static int evalstring(char *s, int mask);
    73308221
    7331 /*
    7332  * Called to execute a trap.  Perhaps we should avoid entering new trap
    7333  * handlers while we are executing a trap handler.
     8222/* Called to execute a trap.
     8223 * Single callsite - at the end of evaltree().
     8224 * If we return non-zero, evaltree raises EXEXIT exception.
     8225 *
     8226 * Perhaps we should avoid entering new trap handlers
     8227 * while we are executing a trap handler. [is it a TODO?]
    73348228 */
    73358229static int
    73368230dotrap(void)
    73378231{
    7338     char *p;
    7339     char *q;
    7340     int i;
    7341     int savestatus;
    7342     int skip = 0;
     8232    uint8_t *g;
     8233    int sig;
     8234    uint8_t savestatus;
    73438235
    73448236    savestatus = exitstatus;
    7345     pendingsig = 0;
     8237    pending_sig = 0;
    73468238    xbarrier();
    73478239
    7348     for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
    7349         if (!*q)
     8240    TRACE(("dotrap entered\n"));
     8241    for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
     8242        int want_exexit;
     8243        char *t;
     8244
     8245        if (*g == 0)
    73508246            continue;
    7351         *q = '\0';
    7352 
    7353         p = trap[i + 1];
    7354         if (!p)
     8247        t = trap[sig];
     8248        /* non-trapped SIGINT is handled separately by raise_interrupt,
     8249         * don't upset it by resetting gotsig[SIGINT-1] */
     8250        if (sig == SIGINT && !t)
    73558251            continue;
    7356         skip = evalstring(p, SKIPEVAL);
     8252
     8253        TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
     8254        *g = 0;
     8255        if (!t)
     8256            continue;
     8257        want_exexit = evalstring(t, SKIPEVAL);
    73578258        exitstatus = savestatus;
    7358         if (skip)
    7359             break;
    7360     }
    7361 
    7362     return skip;
     8259        if (want_exexit) {
     8260            TRACE(("dotrap returns %d\n", want_exexit));
     8261            return want_exexit;
     8262        }
     8263    }
     8264
     8265    TRACE(("dotrap returns 0\n"));
     8266    return 0;
    73638267}
    73648268
     
    73818285evaltree(union node *n, int flags)
    73828286{
     8287    struct jmploc *volatile savehandler = exception_handler;
     8288    struct jmploc jmploc;
    73838289    int checkexit = 0;
    73848290    void (*evalfn)(union node *, int);
    7385     unsigned isor;
    73868291    int status;
     8292    int int_level;
     8293
     8294    SAVE_INT(int_level);
     8295
    73878296    if (n == NULL) {
    73888297        TRACE(("evaltree(NULL) called\n"));
    7389         goto out;
    7390     }
    7391     TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
    7392             getpid(), n, n->type, flags));
     8298        goto out1;
     8299    }
     8300    TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
     8301
     8302    exception_handler = &jmploc;
     8303    {
     8304        int err = setjmp(jmploc.loc);
     8305        if (err) {
     8306            /* if it was a signal, check for trap handlers */
     8307            if (exception_type == EXSIG) {
     8308                TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
     8309                        exception_type, err));
     8310                goto out;
     8311            }
     8312            /* continue on the way out */
     8313            TRACE(("exception %d in evaltree, propagating err=%d\n",
     8314                    exception_type, err));
     8315            exception_handler = savehandler;
     8316            longjmp(exception_handler->loc, err);
     8317        }
     8318    }
     8319
    73938320    switch (n->type) {
    73948321    default:
    73958322#if DEBUG
    73968323        out1fmt("Node type = %d\n", n->type);
    7397         fflush(stdout);
     8324        fflush_all();
    73988325        break;
    73998326#endif
     
    74098336            status = exitstatus;
    74108337        }
    7411         popredir(0);
     8338        popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
    74128339        goto setstatus;
    74138340    case NCMD:
     
    74368363    case NAND:
    74378364    case NOR:
    7438     case NSEMI:
     8365    case NSEMI: {
     8366
    74398367#if NAND + 1 != NOR
    74408368#error NAND + 1 != NOR
     
    74438371#error NOR + 1 != NSEMI
    74448372#endif
    7445         isor = n->type - NAND;
     8373        unsigned is_or = n->type - NAND;
    74468374        evaltree(
    74478375            n->nbinary.ch1,
    7448             (flags | ((isor >> 1) - 1)) & EV_TESTED
     8376            (flags | ((is_or >> 1) - 1)) & EV_TESTED
    74498377        );
    7450         if (!exitstatus == isor)
     8378        if (!exitstatus == is_or)
    74518379            break;
    74528380        if (!evalskip) {
     
    74598387        }
    74608388        break;
     8389    }
    74618390    case NIF:
    74628391        evaltree(n->nif.test, EV_TESTED);
     
    74668395            n = n->nif.ifpart;
    74678396            goto evaln;
    7468         } else if (n->nif.elsepart) {
     8397        }
     8398        if (n->nif.elsepart) {
    74698399            n = n->nif.elsepart;
    74708400            goto evaln;
     
    74798409        break;
    74808410    }
     8411
    74818412 out:
    7482     if ((checkexit & exitstatus))
     8413    exception_handler = savehandler;
     8414
     8415 out1:
     8416    /* Order of checks below is important:
     8417     * signal handlers trigger before exit caused by "set -e".
     8418     */
     8419    if (pending_sig && dotrap())
     8420        goto exexit;
     8421    if (checkexit & exitstatus)
    74838422        evalskip |= SKIPEVAL;
    7484     else if (pendingsig && dotrap())
    7485         goto exexit;
    74868423
    74878424    if (flags & EV_EXIT) {
     
    74898426        raise_exception(EXEXIT);
    74908427    }
     8428
     8429    RESTORE_INT(int_level);
     8430    TRACE(("leaving evaltree (no interrupts)\n"));
    74918431}
    74928432
     
    74958435#endif
    74968436void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
    7497 
    7498 static int loopnest;            /* current loop nesting level */
    74998437
    75008438static void
     
    75438481
    75448482    setstackmark(&smark);
     8483    arglist.list = NULL;
    75458484    arglist.lastp = &arglist.list;
    75468485    for (argp = n->nfor.args; argp; argp = argp->narg.next) {
     
    75828521
    75838522    setstackmark(&smark);
     8523    arglist.list = NULL;
    75848524    arglist.lastp = &arglist.list;
    75858525    expandarg(n->ncase.expr, &arglist, EXP_TILDE);
     
    76108550
    76118551    expredir(n->nredir.redirect);
    7612     if (!backgnd && flags & EV_EXIT && !trap[0])
     8552    if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
    76138553        goto nofork;
    76148554    INT_OFF;
    7615     jp = makejob(n, 1);
     8555    jp = makejob(/*n,*/ 1);
    76168556    if (forkshell(jp, n, backgnd) == 0) {
     8557        /* child */
    76178558        INT_ON;
    76188559        flags |= EV_EXIT;
    76198560        if (backgnd)
    7620             flags &=~ EV_TESTED;
     8561            flags &= ~EV_TESTED;
    76218562 nofork:
    76228563        redirect(n->nredir.redirect, 0);
     
    76258566    }
    76268567    status = 0;
    7627     if (! backgnd)
     8568    if (!backgnd)
    76288569        status = waitforjob(jp);
    76298570    exitstatus = status;
     
    76438584        struct arglist fn;
    76448585
    7645         memset(&fn, 0, sizeof(fn));
     8586        fn.list = NULL;
    76468587        fn.lastp = &fn.list;
    76478588        switch (redir->type) {
     
    76498590        case NFROM:
    76508591        case NTO:
     8592#if ENABLE_ASH_BASH_COMPAT
     8593        case NTO2:
     8594#endif
    76518595        case NCLOBBER:
    76528596        case NAPPEND:
    76538597            expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
     8598#if ENABLE_ASH_BASH_COMPAT
     8599 store_expfname:
     8600#endif
    76548601            redir->nfile.expfname = fn.list->text;
    76558602            break;
    76568603        case NFROMFD:
    7657         case NTOFD:
     8604        case NTOFD: /* >& */
    76588605            if (redir->ndup.vname) {
    76598606                expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
    76608607                if (fn.list == NULL)
    76618608                    ash_msg_and_raise_error("redir error");
     8609#if ENABLE_ASH_BASH_COMPAT
     8610//FIXME: we used expandarg with different args!
     8611                if (!isdigit_str9(fn.list->text)) {
     8612                    /* >&file, not >&fd */
     8613                    if (redir->nfile.fd != 1) /* 123>&file - BAD */
     8614                        ash_msg_and_raise_error("redir error");
     8615                    redir->type = NTO2;
     8616                    goto store_expfname;
     8617                }
     8618#endif
    76628619                fixredir(redir, fn.list->text, 1);
    76638620            }
     
    76888645    flags |= EV_EXIT;
    76898646    INT_OFF;
    7690     jp = makejob(n, pipelen);
     8647    jp = makejob(/*n,*/ pipelen);
    76918648    prevfd = -1;
    76928649    for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
     
    76998656            }
    77008657        }
    7701         if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
     8658        if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
    77028659            INT_ON;
    77038660            if (pip[1] >= 0) {
     
    77188675            close(prevfd);
    77198676        prevfd = pip[0];
    7720         close(pip[1]);
    7721     }
    7722     if (n->npipe.backgnd == 0) {
     8677        /* Don't want to trigger debugging */
     8678        if (pip[1] != -1)
     8679            close(pip[1]);
     8680    }
     8681    if (n->npipe.pipe_backgnd == 0) {
    77238682        exitstatus = waitforjob(jp);
    77248683        TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
     
    77338692setinteractive(int on)
    77348693{
    7735     static int is_interactive;
     8694    static smallint is_interactive;
    77368695
    77378696    if (++on == is_interactive)
     
    77478706
    77488707        if (!did_banner) {
    7749             out1fmt(
    7750                 "\n\n"
    7751                 "%s built-in shell (ash)\n"
     8708            /* note: ash and hush share this string */
     8709            out1fmt("\n\n%s %s\n"
    77528710                "Enter 'help' for a list of built-in commands."
    77538711                "\n\n",
    7754                 bb_banner);
     8712                bb_banner,
     8713                "built-in shell (ash)"
     8714            );
    77558715            did_banner = 1;
    77568716        }
     
    77588718#endif
    77598719}
    7760 
    7761 #if ENABLE_FEATURE_EDITING_VI
    7762 #define setvimode(on) do { \
    7763     if (on) line_input_state->flags |= VI_MODE; \
    7764     else line_input_state->flags &= ~VI_MODE; \
    7765 } while (0)
    7766 #else
    7767 #define setvimode(on) viflag = 0   /* forcibly keep the option off */
    7768 #endif
    77698720
    77708721static void
     
    77768727    setinteractive(iflag);
    77778728    setjobctl(mflag);
    7778     setvimode(viflag);
     8729#if ENABLE_FEATURE_EDITING_VI
     8730    if (viflag)
     8731        line_input_state->flags |= VI_MODE;
     8732    else
     8733        line_input_state->flags &= ~VI_MODE;
     8734#else
     8735    viflag = 0; /* forcibly keep the option off */
     8736#endif
    77798737}
    77808738
     
    77948752        localvars = lvp->next;
    77958753        vp = lvp->vp;
    7796         TRACE(("poplocalvar %s", vp ? vp->text : "-"));
     8754        TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
    77978755        if (vp == NULL) {       /* $- saved */
    77988756            memcpy(optlist, lvp->text, sizeof(optlist));
     
    78008758            optschanged();
    78018759        } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
    7802             unsetvar(vp->text);
     8760            unsetvar(vp->var_text);
    78038761        } else {
    7804             if (vp->func)
    7805                 (*vp->func)(strchrnul(lvp->text, '=') + 1);
     8762            if (vp->var_func)
     8763                vp->var_func(var_end(lvp->text));
    78068764            if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
    7807                 free((char*)vp->text);
     8765                free((char*)vp->var_text);
    78088766            vp->flags = lvp->flags;
    7809             vp->text = lvp->text;
     8767            vp->var_text = lvp->text;
    78108768        }
    78118769        free(lvp);
     
    78328790    exception_handler = &jmploc;
    78338791    localvars = NULL;
    7834     shellparam.malloc = 0;
     8792    shellparam.malloced = 0;
    78358793    func->count++;
    78368794    funcnest++;
     
    78438801#endif
    78448802    evaltree(&func->n, flags & EV_TESTED);
    7845 funcdone:
     8803 funcdone:
    78468804    INT_OFF;
    78478805    funcnest--;
     
    79068864
    79078865    INT_OFF;
    7908     lvp = ckmalloc(sizeof(struct localvar));
     8866    lvp = ckzalloc(sizeof(struct localvar));
    79098867    if (LONE_DASH(name)) {
    79108868        char *p;
     
    79268884            lvp->flags = VUNSET;
    79278885        } else {
    7928             lvp->text = vp->text;
     8886            lvp->text = vp->var_text;
    79298887            lvp->flags = vp->flags;
    79308888            vp->flags |= VSTRFIXED|VTEXTFIXED;
     
    79428900 * The "local" command.
    79438901 */
    7944 static int
    7945 localcmd(int argc, char **argv)
     8902static int FAST_FUNC
     8903localcmd(int argc UNUSED_PARAM, char **argv)
    79468904{
    79478905    char *name;
     
    79548912}
    79558913
    7956 static int
    7957 falsecmd(int argc, char **argv)
     8914static int FAST_FUNC
     8915falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    79588916{
    79598917    return 1;
    79608918}
    79618919
    7962 static int
    7963 truecmd(int argc, char **argv)
     8920static int FAST_FUNC
     8921truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    79648922{
    79658923    return 0;
    79668924}
    79678925
    7968 static int
    7969 execcmd(int argc, char **argv)
    7970 {
    7971     if (argc > 1) {
     8926static int FAST_FUNC
     8927execcmd(int argc UNUSED_PARAM, char **argv)
     8928{
     8929    if (argv[1]) {
    79728930        iflag = 0;              /* exit on error */
    79738931        mflag = 0;
     
    79818939 * The return command.
    79828940 */
    7983 static int
    7984 returncmd(int argc, char **argv)
     8941static int FAST_FUNC
     8942returncmd(int argc UNUSED_PARAM, char **argv)
    79858943{
    79868944    /*
     
    79938951
    79948952/* Forward declarations for builtintab[] */
    7995 static int breakcmd(int, char **);
    7996 static int dotcmd(int, char **);
    7997 static int evalcmd(int, char **);
    7998 #if ENABLE_ASH_BUILTIN_ECHO
    7999 static int echocmd(int, char **);
    8000 #endif
    8001 #if ENABLE_ASH_BUILTIN_TEST
    8002 static int testcmd(int, char **);
    8003 #endif
    8004 static int exitcmd(int, char **);
    8005 static int exportcmd(int, char **);
     8953static int breakcmd(int, char **) FAST_FUNC;
     8954static int dotcmd(int, char **) FAST_FUNC;
     8955static int evalcmd(int, char **) FAST_FUNC;
     8956static int exitcmd(int, char **) FAST_FUNC;
     8957static int exportcmd(int, char **) FAST_FUNC;
    80068958#if ENABLE_ASH_GETOPTS
    8007 static int getoptscmd(int, char **);
     8959static int getoptscmd(int, char **) FAST_FUNC;
    80088960#endif
    80098961#if !ENABLE_FEATURE_SH_EXTRA_QUIET
    8010 static int helpcmd(int argc, char **argv);
    8011 #endif
    8012 #if ENABLE_ASH_MATH_SUPPORT
    8013 static int letcmd(int, char **);
    8014 #endif
    8015 static int readcmd(int, char **);
    8016 static int setcmd(int, char **);
    8017 static int shiftcmd(int, char **);
    8018 static int timescmd(int, char **);
    8019 static int trapcmd(int, char **);
    8020 static int umaskcmd(int, char **);
    8021 static int unsetcmd(int, char **);
    8022 static int ulimitcmd(int, char **);
     8962static int helpcmd(int, char **) FAST_FUNC;
     8963#endif
     8964#if ENABLE_SH_MATH_SUPPORT
     8965static int letcmd(int, char **) FAST_FUNC;
     8966#endif
     8967static int readcmd(int, char **) FAST_FUNC;
     8968static int setcmd(int, char **) FAST_FUNC;
     8969static int shiftcmd(int, char **) FAST_FUNC;
     8970static int timescmd(int, char **) FAST_FUNC;
     8971static int trapcmd(int, char **) FAST_FUNC;
     8972static int umaskcmd(int, char **) FAST_FUNC;
     8973static int unsetcmd(int, char **) FAST_FUNC;
     8974static int ulimitcmd(int, char **) FAST_FUNC;
    80238975
    80248976#define BUILTIN_NOSPEC          "0"
     
    80318983#define BUILTIN_SPEC_REG_ASSG   "7"
    80328984
    8033 /* make sure to keep these in proper order since it is searched via bsearch() */
     8985/* Stubs for calling non-FAST_FUNC's */
     8986#if ENABLE_ASH_BUILTIN_ECHO
     8987static int FAST_FUNC echocmd(int argc, char **argv)   { return echo_main(argc, argv); }
     8988#endif
     8989#if ENABLE_ASH_BUILTIN_PRINTF
     8990static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
     8991#endif
     8992#if ENABLE_ASH_BUILTIN_TEST
     8993static int FAST_FUNC testcmd(int argc, char **argv)   { return test_main(argc, argv); }
     8994#endif
     8995
     8996/* Keep these in proper order since it is searched via bsearch() */
    80348997static const struct builtincmd builtintab[] = {
    8035     { BUILTIN_SPEC_REG      ".", dotcmd },
    8036     { BUILTIN_SPEC_REG      ":", truecmd },
     8998    { BUILTIN_SPEC_REG      "."       , dotcmd    },
     8999    { BUILTIN_SPEC_REG      ":"       , truecmd    },
    80379000#if ENABLE_ASH_BUILTIN_TEST
    8038     { BUILTIN_REGULAR   "[", testcmd },
    8039     { BUILTIN_REGULAR   "[[", testcmd },
     9001    { BUILTIN_REGULAR       "["       , testcmd    },
     9002#if ENABLE_ASH_BASH_COMPAT
     9003    { BUILTIN_REGULAR       "[["      , testcmd    },
     9004#endif
    80409005#endif
    80419006#if ENABLE_ASH_ALIAS
    8042     { BUILTIN_REG_ASSG      "alias", aliascmd },
     9007    { BUILTIN_REG_ASSG      "alias"   , aliascmd  },
    80439008#endif
    80449009#if JOBS
    8045     { BUILTIN_REGULAR       "bg", fg_bgcmd },
    8046 #endif
    8047     { BUILTIN_SPEC_REG      "break", breakcmd },
    8048     { BUILTIN_REGULAR       "cd", cdcmd },
    8049     { BUILTIN_NOSPEC        "chdir", cdcmd },
     9010    { BUILTIN_REGULAR       "bg"      , fg_bgcmd  },
     9011#endif
     9012    { BUILTIN_SPEC_REG      "break"   , breakcmd  },
     9013    { BUILTIN_REGULAR       "cd"      , cdcmd      },
     9014    { BUILTIN_NOSPEC        "chdir"   , cdcmd      },
    80509015#if ENABLE_ASH_CMDCMD
    8051     { BUILTIN_REGULAR       "command", commandcmd },
    8052 #endif
    8053     { BUILTIN_SPEC_REG      "continue", breakcmd },
     9016    { BUILTIN_REGULAR       "command" , commandcmd },
     9017#endif
     9018    { BUILTIN_SPEC_REG      "continue", breakcmd   },
    80549019#if ENABLE_ASH_BUILTIN_ECHO
    8055     { BUILTIN_REGULAR       "echo", echocmd },
    8056 #endif
    8057     { BUILTIN_SPEC_REG      "eval", evalcmd },
    8058     { BUILTIN_SPEC_REG      "exec", execcmd },
    8059     { BUILTIN_SPEC_REG      "exit", exitcmd },
    8060     { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
    8061     { BUILTIN_REGULAR       "false", falsecmd },
     9020    { BUILTIN_REGULAR       "echo"    , echocmd    },
     9021#endif
     9022    { BUILTIN_SPEC_REG      "eval"    , evalcmd    },
     9023    { BUILTIN_SPEC_REG      "exec"    , execcmd    },
     9024    { BUILTIN_SPEC_REG      "exit"    , exitcmd    },
     9025    { BUILTIN_SPEC_REG_ASSG "export"  , exportcmd },
     9026    { BUILTIN_REGULAR       "false"   , falsecmd  },
    80629027#if JOBS
    8063     { BUILTIN_REGULAR       "fg", fg_bgcmd },
     9028    { BUILTIN_REGULAR       "fg"      , fg_bgcmd  },
    80649029#endif
    80659030#if ENABLE_ASH_GETOPTS
    8066     { BUILTIN_REGULAR       "getopts", getoptscmd },
    8067 #endif
    8068     { BUILTIN_NOSPEC        "hash", hashcmd },
     9031    { BUILTIN_REGULAR       "getopts" , getoptscmd },
     9032#endif
     9033    { BUILTIN_NOSPEC        "hash"    , hashcmd    },
    80699034#if !ENABLE_FEATURE_SH_EXTRA_QUIET
    8070     { BUILTIN_NOSPEC        "help", helpcmd },
     9035    { BUILTIN_NOSPEC        "help"    , helpcmd    },
    80719036#endif
    80729037#if JOBS
    8073     { BUILTIN_REGULAR       "jobs", jobscmd },
    8074     { BUILTIN_REGULAR       "kill", killcmd },
    8075 #endif
    8076 #if ENABLE_ASH_MATH_SUPPORT
    8077     { BUILTIN_NOSPEC        "let", letcmd },
    8078 #endif
    8079     { BUILTIN_ASSIGN        "local", localcmd },
    8080     { BUILTIN_NOSPEC        "pwd", pwdcmd },
    8081     { BUILTIN_REGULAR       "read", readcmd },
    8082     { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
    8083     { BUILTIN_SPEC_REG      "return", returncmd },
    8084     { BUILTIN_SPEC_REG      "set", setcmd },
    8085     { BUILTIN_SPEC_REG      "shift", shiftcmd },
    8086     { BUILTIN_SPEC_REG      "source", dotcmd },
     9038    { BUILTIN_REGULAR       "jobs"    , jobscmd    },
     9039    { BUILTIN_REGULAR       "kill"    , killcmd    },
     9040#endif
     9041#if ENABLE_SH_MATH_SUPPORT
     9042    { BUILTIN_NOSPEC        "let"     , letcmd     },
     9043#endif
     9044    { BUILTIN_ASSIGN        "local"   , localcmd   },
     9045#if ENABLE_ASH_BUILTIN_PRINTF
     9046    { BUILTIN_REGULAR       "printf"  , printfcmd  },
     9047#endif
     9048    { BUILTIN_NOSPEC        "pwd"     , pwdcmd     },
     9049    { BUILTIN_REGULAR       "read"    , readcmd    },
     9050    { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd  },
     9051    { BUILTIN_SPEC_REG      "return"  , returncmd  },
     9052    { BUILTIN_SPEC_REG      "set"     , setcmd     },
     9053    { BUILTIN_SPEC_REG      "shift"   , shiftcmd   },
     9054#if ENABLE_ASH_BASH_COMPAT
     9055    { BUILTIN_SPEC_REG      "source"  , dotcmd     },
     9056#endif
    80879057#if ENABLE_ASH_BUILTIN_TEST
    8088     { BUILTIN_REGULAR   "test", testcmd },
    8089 #endif
    8090     { BUILTIN_SPEC_REG      "times", timescmd },
    8091     { BUILTIN_SPEC_REG      "trap", trapcmd },
    8092     { BUILTIN_REGULAR       "true", truecmd },
    8093     { BUILTIN_NOSPEC        "type", typecmd },
    8094     { BUILTIN_NOSPEC        "ulimit", ulimitcmd },
    8095     { BUILTIN_REGULAR       "umask", umaskcmd },
     9058    { BUILTIN_REGULAR       "test"    , testcmd    },
     9059#endif
     9060    { BUILTIN_SPEC_REG      "times"   , timescmd  },
     9061    { BUILTIN_SPEC_REG      "trap"    , trapcmd    },
     9062    { BUILTIN_REGULAR       "true"    , truecmd    },
     9063    { BUILTIN_NOSPEC        "type"    , typecmd    },
     9064    { BUILTIN_NOSPEC        "ulimit"  , ulimitcmd },
     9065    { BUILTIN_REGULAR       "umask"   , umaskcmd  },
    80969066#if ENABLE_ASH_ALIAS
    8097     { BUILTIN_REGULAR       "unalias", unaliascmd },
    8098 #endif
    8099     { BUILTIN_SPEC_REG      "unset", unsetcmd },
    8100     { BUILTIN_REGULAR       "wait", waitcmd },
     9067    { BUILTIN_REGULAR       "unalias" , unaliascmd },
     9068#endif
     9069    { BUILTIN_SPEC_REG      "unset"   , unsetcmd  },
     9070    { BUILTIN_REGULAR       "wait"    , waitcmd    },
    81019071};
    81029072
    8103 
    8104 #define COMMANDCMD (builtintab + 5 + \
    8105     2 * ENABLE_ASH_BUILTIN_TEST + \
    8106     ENABLE_ASH_ALIAS + \
    8107     ENABLE_ASH_JOB_CONTROL)
    8108 #define EXECCMD (builtintab + 7 + \
    8109     2 * ENABLE_ASH_BUILTIN_TEST + \
    8110     ENABLE_ASH_ALIAS + \
    8111     ENABLE_ASH_JOB_CONTROL + \
    8112     ENABLE_ASH_CMDCMD + \
    8113     ENABLE_ASH_BUILTIN_ECHO)
     9073/* Should match the above table! */
     9074#define COMMANDCMD (builtintab + \
     9075    2 + \
     9076    1 * ENABLE_ASH_BUILTIN_TEST + \
     9077    1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
     9078    1 * ENABLE_ASH_ALIAS + \
     9079    1 * ENABLE_ASH_JOB_CONTROL + \
     9080    3)
     9081#define EXECCMD (builtintab + \
     9082    2 + \
     9083    1 * ENABLE_ASH_BUILTIN_TEST + \
     9084    1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
     9085    1 * ENABLE_ASH_ALIAS + \
     9086    1 * ENABLE_ASH_JOB_CONTROL + \
     9087    3 + \
     9088    1 * ENABLE_ASH_CMDCMD + \
     9089    1 + \
     9090    ENABLE_ASH_BUILTIN_ECHO + \
     9091    1)
    81149092
    81159093/*
     
    81319109 * Execute a simple command.
    81329110 */
    8133 static int back_exitstatus; /* exit status of backquoted command */
    81349111static int
    81359112isassignment(const char *p)
     
    81409117    return *q == '=';
    81419118}
    8142 static int
    8143 bltincmd(int argc, char **argv)
     9119static int FAST_FUNC
     9120bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    81449121{
    81459122    /* Preserve exitstatus of a previous possible redirection
     
    81509127evalcommand(union node *cmd, int flags)
    81519128{
    8152     static const struct builtincmd bltin = {
    8153         "\0\0", bltincmd
     9129    static const struct builtincmd null_bltin = {
     9130        "\0\0", bltincmd /* why three NULs? */
    81549131    };
    81559132    struct stackmark smark;
     
    81659142    const char *path;
    81669143    int spclbltin;
    8167     int cmd_is_exec;
    81689144    int status;
    81699145    char **nargv;
    81709146    struct builtincmd *bcmd;
    8171     int pseudovarflag = 0;
     9147    smallint cmd_is_exec;
     9148    smallint pseudovarflag = 0;
    81729149
    81739150    /* First expand the arguments. */
     
    81779154
    81789155    cmdentry.cmdtype = CMDBUILTIN;
    8179     cmdentry.u.cmd = &bltin;
     9156    cmdentry.u.cmd = &null_bltin;
    81809157    varlist.lastp = &varlist.list;
    81819158    *varlist.lastp = NULL;
     
    82179194    status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
    82189195
    8219     path = vpath.text;
     9196    path = vpath.var_text;
    82209197    for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
    82219198        struct strlist **spp;
     
    82309207         */
    82319208        p = (*spp)->text;
    8232         if (varequal(p, path))
     9209        if (varcmp(p, path) == 0)
    82339210            path = p;
    82349211    }
     
    82379214    if (xflag) {
    82389215        int n;
    8239         const char *p = " %s";
    8240 
    8241         p++;
    8242         dprintf(preverrout_fd, p, expandstr(ps4val()));
    8243 
     9216        const char *p = " %s" + 1;
     9217
     9218        fdprintf(preverrout_fd, p, expandstr(ps4val()));
    82449219        sp = varlist.list;
    82459220        for (n = 0; n < 2; n++) {
    82469221            while (sp) {
    8247                 dprintf(preverrout_fd, p, sp->text);
     9222                fdprintf(preverrout_fd, p, sp->text);
    82489223                sp = sp->next;
    8249                 if (*p == '%') {
    8250                     p--;
    8251                 }
     9224                p = " %s";
    82529225            }
    82539226            sp = arglist.list;
    82549227        }
    8255         full_write(preverrout_fd, "\n", 1);
     9228        safe_write(preverrout_fd, "\n", 1);
    82569229    }
    82579230
     
    82699242            find_command(argv[0], &cmdentry, cmd_flag, path);
    82709243            if (cmdentry.cmdtype == CMDUNKNOWN) {
     9244                flush_stdout_stderr();
    82719245                status = 127;
    8272                 flush_stderr();
    82739246                goto bail;
    82749247            }
     
    82809253                spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
    82819254            if (cmdentry.u.cmd == EXECCMD)
    8282                 cmd_is_exec++;
     9255                cmd_is_exec = 1;
    82839256#if ENABLE_ASH_CMDCMD
    82849257            if (cmdentry.u.cmd == COMMANDCMD) {
     
    83079280    /* Execute the command. */
    83089281    switch (cmdentry.cmdtype) {
    8309     default:
    8310         /* Fork off a child process if necessary. */
    8311         if (!(flags & EV_EXIT) || trap[0]) {
     9282    default: {
     9283
     9284#if ENABLE_FEATURE_SH_NOFORK
     9285/* (1) BUG: if variables are set, we need to fork, or save/restore them
     9286 *     around run_nofork_applet() call.
     9287 * (2) Should this check also be done in forkshell()?
     9288 *     (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
     9289 */
     9290        /* find_command() encodes applet_no as (-2 - applet_no) */
     9291        int applet_no = (- cmdentry.u.index - 2);
     9292        if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
     9293            listsetvar(varlist.list, VEXPORT|VSTACK);
     9294            /* run <applet>_main() */
     9295            exitstatus = run_nofork_applet(applet_no, argv);
     9296            break;
     9297        }
     9298#endif
     9299        /* Can we avoid forking off? For example, very last command
     9300         * in a script or a subshell does not need forking,
     9301         * we can just exec it.
     9302         */
     9303        if (!(flags & EV_EXIT) || may_have_traps) {
     9304            /* No, forking off a child is necessary */
    83129305            INT_OFF;
    8313             jp = makejob(cmd, 1);
     9306            jp = makejob(/*cmd,*/ 1);
    83149307            if (forkshell(jp, cmd, FORK_FG) != 0) {
     9308                /* parent */
    83159309                exitstatus = waitforjob(jp);
    83169310                INT_ON;
     9311                TRACE(("forked child exited with %d\n", exitstatus));
    83179312                break;
    83189313            }
     9314            /* child */
    83199315            FORCE_INT_ON;
     9316            /* fall through to exec'ing external program */
    83209317        }
    83219318        listsetvar(varlist.list, VEXPORT|VSTACK);
    83229319        shellexec(argv, path, cmdentry.u.index);
    83239320        /* NOTREACHED */
    8324 
     9321    } /* default */
    83259322    case CMDBUILTIN:
    83269323        cmdenviron = varlist.list;
     
    83359332            listsetvar(list, i);
    83369333        }
     9334        /* Tight loop with builtins only:
     9335         * "while kill -0 $child; do true; done"
     9336         * will never exit even if $child died, unless we do this
     9337         * to reap the zombie and make kill detect that it's gone: */
     9338        dowait(DOWAIT_NONBLOCK, NULL);
     9339
    83379340        if (evalbltin(cmdentry.u.cmd, argc, argv)) {
    83389341            int exit_status;
    8339             int i, j;
    8340 
    8341             i = exception;
     9342            int i = exception_type;
    83429343            if (i == EXEXIT)
    83439344                goto raise;
    8344 
    83459345            exit_status = 2;
    8346             j = 0;
    83479346            if (i == EXINT)
    8348                 j = SIGINT;
     9347                exit_status = 128 + SIGINT;
    83499348            if (i == EXSIG)
    8350                 j = pendingsig;
    8351             if (j)
    8352                 exit_status = j + 128;
     9349                exit_status = 128 + pending_sig;
    83539350            exitstatus = exit_status;
    8354 
    83559351            if (i == EXINT || spclbltin > 0) {
    83569352 raise:
     
    83639359    case CMDFUNCTION:
    83649360        listsetvar(varlist.list, 0);
     9361        /* See above for the rationale */
     9362        dowait(DOWAIT_NONBLOCK, NULL);
    83659363        if (evalfun(cmdentry.u.func, argc, argv, flags))
    83669364            goto raise;
    83679365        break;
    8368     }
     9366
     9367    } /* switch */
    83699368
    83709369 out:
    8371     popredir(cmd_is_exec);
    8372     if (lastarg)
     9370    popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
     9371    if (lastarg) {
    83739372        /* dsl: I think this is intended to be used to support
    83749373         * '_' in 'vi' command mode during line editing...
     
    83769375         */
    83779376        setvar("_", lastarg, 0);
     9377    }
    83789378    popstackmark(&smark);
    83799379}
     
    84029402    clearerr(stdout);
    84039403    commandname = savecmdname;
    8404     exsig = 0;
    84059404    exception_handler = savehandler;
    84069405
     
    84119410goodname(const char *p)
    84129411{
    8413     return !*endofname(p);
     9412    return endofname(p)[0] == '\0';
    84149413}
    84159414
     
    84479446 * in the standard shell so we don't make it one here.
    84489447 */
    8449 static int
    8450 breakcmd(int argc, char **argv)
    8451 {
    8452     int n = argc > 1 ? number(argv[1]) : 1;
     9448static int FAST_FUNC
     9449breakcmd(int argc UNUSED_PARAM, char **argv)
     9450{
     9451    int n = argv[1] ? number(argv[1]) : 1;
    84539452
    84549453    if (n <= 0)
    8455         ash_msg_and_raise_error(illnum, argv[1]);
     9454        ash_msg_and_raise_error(msg_illnum, argv[1]);
    84569455    if (n > loopnest)
    84579456        n = loopnest;
     
    84699468 */
    84709469
    8471 #define EOF_NLEFT -99           /* value of parsenleft when EOF pushed back */
    8472 
    84739470enum {
    84749471    INPUT_PUSH_FILE = 1,
     
    84769473};
    84779474
    8478 /*
    8479  * NEOF is returned by parsecmd when it encounters an end of file.  It
    8480  * must be distinct from NULL, so we use the address of a variable that
    8481  * happens to be handy.
    8482  */
    8483 static int plinno = 1;                  /* input line number */
    8484 /* number of characters left in input buffer */
    8485 static int parsenleft;                  /* copy of parsefile->nleft */
    8486 static int parselleft;                  /* copy of parsefile->lleft */
    8487 /* next character in input buffer */
    8488 static char *parsenextc;                /* copy of parsefile->nextc */
    8489 
    8490 static int checkkwd;
     9475static smallint checkkwd;
    84919476/* values of checkkwd variable */
    84929477#define CHKALIAS        0x1
     
    84949479#define CHKNL           0x4
    84959480
     9481/*
     9482 * Push a string back onto the input at this current parsefile level.
     9483 * We handle aliases this way.
     9484 */
     9485#if !ENABLE_ASH_ALIAS
     9486#define pushstring(s, ap) pushstring(s)
     9487#endif
     9488static void
     9489pushstring(char *s, struct alias *ap)
     9490{
     9491    struct strpush *sp;
     9492    int len;
     9493
     9494    len = strlen(s);
     9495    INT_OFF;
     9496    if (g_parsefile->strpush) {
     9497        sp = ckzalloc(sizeof(*sp));
     9498        sp->prev = g_parsefile->strpush;
     9499    } else {
     9500        sp = &(g_parsefile->basestrpush);
     9501    }
     9502    g_parsefile->strpush = sp;
     9503    sp->prev_string = g_parsefile->next_to_pgetc;
     9504    sp->prev_left_in_line = g_parsefile->left_in_line;
     9505#if ENABLE_ASH_ALIAS
     9506    sp->ap = ap;
     9507    if (ap) {
     9508        ap->flag |= ALIASINUSE;
     9509        sp->string = s;
     9510    }
     9511#endif
     9512    g_parsefile->next_to_pgetc = s;
     9513    g_parsefile->left_in_line = len;
     9514    INT_ON;
     9515}
     9516
    84969517static void
    84979518popstring(void)
    84989519{
    8499     struct strpush *sp = parsefile->strpush;
     9520    struct strpush *sp = g_parsefile->strpush;
    85009521
    85019522    INT_OFF;
    85029523#if ENABLE_ASH_ALIAS
    85039524    if (sp->ap) {
    8504         if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
     9525        if (g_parsefile->next_to_pgetc[-1] == ' '
     9526         || g_parsefile->next_to_pgetc[-1] == '\t'
     9527        ) {
    85059528            checkkwd |= CHKALIAS;
    85069529        }
     
    85149537    }
    85159538#endif
    8516     parsenextc = sp->prevstring;
    8517     parsenleft = sp->prevnleft;
    8518 /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
    8519     parsefile->strpush = sp->prev;
    8520     if (sp != &(parsefile->basestrpush))
     9539    g_parsefile->next_to_pgetc = sp->prev_string;
     9540    g_parsefile->left_in_line = sp->prev_left_in_line;
     9541    g_parsefile->strpush = sp->prev;
     9542    if (sp != &(g_parsefile->basestrpush))
    85219543        free(sp);
    85229544    INT_ON;
    85239545}
    85249546
     9547//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
     9548//it peeks whether it is &>, and then pushes back both chars.
     9549//This function needs to save last *next_to_pgetc to buf[0]
     9550//to make two pungetc() reliable. Currently,
     9551// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
    85259552static int
    85269553preadfd(void)
    85279554{
    85289555    int nr;
    8529     char *buf =  parsefile->buf;
    8530     parsenextc = buf;
    8531 
     9556    char *buf = g_parsefile->buf;
     9557
     9558    g_parsefile->next_to_pgetc = buf;
     9559#if ENABLE_FEATURE_EDITING
    85329560 retry:
    8533 #if ENABLE_FEATURE_EDITING
    8534     if (!iflag || parsefile->fd)
    8535         nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
     9561    if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
     9562        nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
    85369563    else {
    85379564#if ENABLE_FEATURE_TAB_COMPLETION
    85389565        line_input_state->path_lookup = pathval();
    85399566#endif
    8540         nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
     9567        nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
    85419568        if (nr == 0) {
    85429569            /* Ctrl+C pressed */
     
    85509577        }
    85519578        if (nr < 0 && errno == 0) {
    8552             /* Ctrl+D presend */
     9579            /* Ctrl+D pressed */
    85539580            nr = 0;
    85549581        }
    85559582    }
    85569583#else
    8557     nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
    8558 #endif
    8559 
     9584    nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
     9585#endif
     9586
     9587#if 0
     9588/* nonblock_safe_read() handles this problem */
    85609589    if (nr < 0) {
    85619590        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
    85629591            int flags = fcntl(0, F_GETFL);
    8563             if (flags >= 0 && flags & O_NONBLOCK) {
    8564                 flags &=~ O_NONBLOCK;
     9592            if (flags >= 0 && (flags & O_NONBLOCK)) {
     9593                flags &= ~O_NONBLOCK;
    85659594                if (fcntl(0, F_SETFL, flags) >= 0) {
    85669595                    out2str("sh: turning off NDELAY mode\n");
     
    85709599        }
    85719600    }
     9601#endif
    85729602    return nr;
    85739603}
     
    85779607 *
    85789608 * 1) If a string was pushed back on the input, pop it;
    8579  * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
    8580  *    from a string so we can't refill the buffer, return EOF.
    8581  * 3) If the is more stuff in this buffer, use it else call read to fill it.
     9609 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
     9610 *    or we are reading from a string so we can't refill the buffer,
     9611 *    return EOF.
     9612 * 3) If there is more stuff in this buffer, use it else call read to fill it.
    85829613 * 4) Process input up to the next newline, deleting nul characters.
    85839614 */
     9615//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
     9616#define pgetc_debug(...) ((void)0)
    85849617static int
    85859618preadbuffer(void)
     
    85879620    char *q;
    85889621    int more;
    8589     char savec;
    8590 
    8591     while (parsefile->strpush) {
     9622
     9623    while (g_parsefile->strpush) {
    85929624#if ENABLE_ASH_ALIAS
    8593         if (parsenleft == -1 && parsefile->strpush->ap &&
    8594             parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
     9625        if (g_parsefile->left_in_line == -1
     9626         && g_parsefile->strpush->ap
     9627         && g_parsefile->next_to_pgetc[-1] != ' '
     9628         && g_parsefile->next_to_pgetc[-1] != '\t'
     9629        ) {
     9630            pgetc_debug("preadbuffer PEOA");
    85959631            return PEOA;
    85969632        }
    85979633#endif
    85989634        popstring();
    8599         if (--parsenleft >= 0)
    8600             return signed_char2int(*parsenextc++);
    8601     }
    8602     if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
     9635        /* try "pgetc" now: */
     9636        pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
     9637                g_parsefile->left_in_line,
     9638                g_parsefile->next_to_pgetc,
     9639                g_parsefile->next_to_pgetc);
     9640        if (--g_parsefile->left_in_line >= 0)
     9641            return (unsigned char)(*g_parsefile->next_to_pgetc++);
     9642    }
     9643    /* on both branches above g_parsefile->left_in_line < 0.
     9644     * "pgetc" needs refilling.
     9645     */
     9646
     9647    /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
     9648     * pungetc() may increment it a few times.
     9649     * Assuming it won't increment it to less than -90.
     9650     */
     9651    if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
     9652        pgetc_debug("preadbuffer PEOF1");
     9653        /* even in failure keep left_in_line and next_to_pgetc
     9654         * in lock step, for correct multi-layer pungetc.
     9655         * left_in_line was decremented before preadbuffer(),
     9656         * must inc next_to_pgetc: */
     9657        g_parsefile->next_to_pgetc++;
    86039658        return PEOF;
    8604     flush_stdout_stderr();
    8605 
    8606     more = parselleft;
     9659    }
     9660
     9661    more = g_parsefile->left_in_buffer;
    86079662    if (more <= 0) {
     9663        flush_stdout_stderr();
    86089664 again:
    86099665        more = preadfd();
    86109666        if (more <= 0) {
    8611             parselleft = parsenleft = EOF_NLEFT;
     9667            /* don't try reading again */
     9668            g_parsefile->left_in_line = -99;
     9669            pgetc_debug("preadbuffer PEOF2");
     9670            g_parsefile->next_to_pgetc++;
    86129671            return PEOF;
    86139672        }
    86149673    }
    86159674
    8616     q = parsenextc;
    8617 
    8618     /* delete nul characters */
     9675    /* Find out where's the end of line.
     9676     * Set g_parsefile->left_in_line
     9677     * and g_parsefile->left_in_buffer acordingly.
     9678     * NUL chars are deleted.
     9679     */
     9680    q = g_parsefile->next_to_pgetc;
    86199681    for (;;) {
    8620         int c;
     9682        char c;
    86219683
    86229684        more--;
     9685
    86239686        c = *q;
    8624 
    8625         if (!c)
     9687        if (c == '\0') {
    86269688            memmove(q, q + 1, more);
    8627         else {
     9689        } else {
    86289690            q++;
    86299691            if (c == '\n') {
    8630                 parsenleft = q - parsenextc - 1;
     9692                g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
    86319693                break;
    86329694            }
     
    86349696
    86359697        if (more <= 0) {
    8636             parsenleft = q - parsenextc - 1;
    8637             if (parsenleft < 0)
     9698            g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
     9699            if (g_parsefile->left_in_line < 0)
    86389700                goto again;
    86399701            break;
    86409702        }
    86419703    }
    8642     parselleft = more;
    8643 
    8644     savec = *q;
    8645     *q = '\0';
     9704    g_parsefile->left_in_buffer = more;
    86469705
    86479706    if (vflag) {
    8648         out2str(parsenextc);
    8649     }
    8650 
    8651     *q = savec;
    8652 
    8653     return signed_char2int(*parsenextc++);
    8654 }
    8655 
    8656 #define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
     9707        char save = *q;
     9708        *q = '\0';
     9709        out2str(g_parsefile->next_to_pgetc);
     9710        *q = save;
     9711    }
     9712
     9713    pgetc_debug("preadbuffer at %d:%p'%s'",
     9714            g_parsefile->left_in_line,
     9715            g_parsefile->next_to_pgetc,
     9716            g_parsefile->next_to_pgetc);
     9717    return (unsigned char)*g_parsefile->next_to_pgetc++;
     9718}
     9719
     9720#define pgetc_as_macro() \
     9721    (--g_parsefile->left_in_line >= 0 \
     9722    ? (unsigned char)*g_parsefile->next_to_pgetc++ \
     9723    : preadbuffer() \
     9724    )
     9725
    86579726static int
    86589727pgetc(void)
    86599728{
     9729    pgetc_debug("pgetc_fast at %d:%p'%s'",
     9730            g_parsefile->left_in_line,
     9731            g_parsefile->next_to_pgetc,
     9732            g_parsefile->next_to_pgetc);
    86609733    return pgetc_as_macro();
    86619734}
    86629735
    86639736#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
    8664 #define pgetc_macro() pgetc()
     9737# define pgetc_fast() pgetc()
    86659738#else
    8666 #define pgetc_macro() pgetc_as_macro()
    8667 #endif
    8668 
    8669 /*
    8670  * Same as pgetc(), but ignores PEOA.
    8671  */
     9739# define pgetc_fast() pgetc_as_macro()
     9740#endif
     9741
    86729742#if ENABLE_ASH_ALIAS
    86739743static int
    8674 pgetc2(void)
     9744pgetc_without_PEOA(void)
    86759745{
    86769746    int c;
    8677 
    86789747    do {
    8679         c = pgetc_macro();
     9748        pgetc_debug("pgetc_fast at %d:%p'%s'",
     9749                g_parsefile->left_in_line,
     9750                g_parsefile->next_to_pgetc,
     9751                g_parsefile->next_to_pgetc);
     9752        c = pgetc_fast();
    86809753    } while (c == PEOA);
    86819754    return c;
    86829755}
    86839756#else
    8684 static int
    8685 pgetc2(void)
    8686 {
    8687     return pgetc_macro();
    8688 }
     9757# define pgetc_without_PEOA() pgetc()
    86899758#endif
    86909759
     
    87009769
    87019770    while (--nleft > 0) {
    8702         c = pgetc2();
     9771        c = pgetc_without_PEOA();
    87039772        if (c == PEOF) {
    87049773            if (p == line)
     
    87219790pungetc(void)
    87229791{
    8723     parsenleft++;
    8724     parsenextc--;
    8725 }
    8726 
    8727 /*
    8728  * Push a string back onto the input at this current parsefile level.
    8729  * We handle aliases this way.
    8730  */
    8731 static void
    8732 pushstring(char *s, void *ap)
    8733 {
    8734     struct strpush *sp;
    8735     size_t len;
    8736 
    8737     len = strlen(s);
    8738     INT_OFF;
    8739 /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
    8740     if (parsefile->strpush) {
    8741         sp = ckmalloc(sizeof(struct strpush));
    8742         sp->prev = parsefile->strpush;
    8743         parsefile->strpush = sp;
    8744     } else
    8745         sp = parsefile->strpush = &(parsefile->basestrpush);
    8746     sp->prevstring = parsenextc;
    8747     sp->prevnleft = parsenleft;
    8748 #if ENABLE_ASH_ALIAS
    8749     sp->ap = (struct alias *)ap;
    8750     if (ap) {
    8751         ((struct alias *)ap)->flag |= ALIASINUSE;
    8752         sp->string = s;
    8753     }
    8754 #endif
    8755     parsenextc = s;
    8756     parsenleft = len;
    8757     INT_ON;
     9792    g_parsefile->left_in_line++;
     9793    g_parsefile->next_to_pgetc--;
     9794    pgetc_debug("pushed back to %d:%p'%s'",
     9795            g_parsefile->left_in_line,
     9796            g_parsefile->next_to_pgetc,
     9797            g_parsefile->next_to_pgetc);
    87589798}
    87599799
     
    87679807    struct parsefile *pf;
    87689808
    8769     parsefile->nleft = parsenleft;
    8770     parsefile->lleft = parselleft;
    8771     parsefile->nextc = parsenextc;
    8772     parsefile->linno = plinno;
    8773     pf = ckmalloc(sizeof(*pf));
    8774     pf->prev = parsefile;
    8775     pf->fd = -1;
    8776     pf->strpush = NULL;
    8777     pf->basestrpush.prev = NULL;
    8778     parsefile = pf;
     9809    pf = ckzalloc(sizeof(*pf));
     9810    pf->prev = g_parsefile;
     9811    pf->pf_fd = -1;
     9812    /*pf->strpush = NULL; - ckzalloc did it */
     9813    /*pf->basestrpush.prev = NULL;*/
     9814    g_parsefile = pf;
    87799815}
    87809816
     
    87829818popfile(void)
    87839819{
    8784     struct parsefile *pf = parsefile;
     9820    struct parsefile *pf = g_parsefile;
    87859821
    87869822    INT_OFF;
    8787     if (pf->fd >= 0)
    8788         close(pf->fd);
    8789     if (pf->buf)
    8790         free(pf->buf);
     9823    if (pf->pf_fd >= 0)
     9824        close(pf->pf_fd);
     9825    free(pf->buf);
    87919826    while (pf->strpush)
    87929827        popstring();
    8793     parsefile = pf->prev;
     9828    g_parsefile = pf->prev;
    87949829    free(pf);
    8795     parsenleft = parsefile->nleft;
    8796     parselleft = parsefile->lleft;
    8797     parsenextc = parsefile->nextc;
    8798     plinno = parsefile->linno;
    87999830    INT_ON;
    88009831}
     
    88069837popallfiles(void)
    88079838{
    8808     while (parsefile != &basepf)
     9839    while (g_parsefile != &basepf)
    88099840        popfile();
    88109841}
     
    88189849{
    88199850    popallfiles();
    8820     if (parsefile->fd > 0) {
    8821         close(parsefile->fd);
    8822         parsefile->fd = 0;
     9851    if (g_parsefile->pf_fd > 0) {
     9852        close(g_parsefile->pf_fd);
     9853        g_parsefile->pf_fd = 0;
    88239854    }
    88249855}
     
    88319862setinputfd(int fd, int push)
    88329863{
    8833     fcntl(fd, F_SETFD, FD_CLOEXEC);
     9864    close_on_exec_on(fd);
    88349865    if (push) {
    88359866        pushfile();
    8836         parsefile->buf = 0;
    8837     }
    8838     parsefile->fd = fd;
    8839     if (parsefile->buf == NULL)
    8840         parsefile->buf = ckmalloc(IBUFSIZ);
    8841     parselleft = parsenleft = 0;
    8842     plinno = 1;
     9867        g_parsefile->buf = NULL;
     9868    }
     9869    g_parsefile->pf_fd = fd;
     9870    if (g_parsefile->buf == NULL)
     9871        g_parsefile->buf = ckmalloc(IBUFSIZ);
     9872    g_parsefile->left_in_buffer = 0;
     9873    g_parsefile->left_in_line = 0;
     9874    g_parsefile->linno = 1;
    88439875}
    88449876
     
    88589890        if (flags & INPUT_NOFILE_OK)
    88599891            goto out;
    8860         ash_msg_and_raise_error("can't open %s", fname);
     9892        ash_msg_and_raise_error("can't open '%s'", fname);
    88619893    }
    88629894    if (fd < 10) {
     
    88819913    INT_OFF;
    88829914    pushfile();
    8883     parsenextc = string;
    8884     parsenleft = strlen(string);
    8885     parsefile->buf = NULL;
    8886     plinno = 1;
     9915    g_parsefile->next_to_pgetc = string;
     9916    g_parsefile->left_in_line = strlen(string);
     9917    g_parsefile->buf = NULL;
     9918    g_parsefile->linno = 1;
    88879919    INT_ON;
    88889920}
     
    89019933static time_t mailtime[MAXMBOXES];
    89029934/* Set if MAIL or MAILPATH is changed. */
    8903 static int mail_var_path_changed;
     9935static smallint mail_var_path_changed;
    89049936
    89059937/*
     
    89229954    mpath = mpathset() ? mpathval() : mailval();
    89239955    for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
    8924         p = padvance(&mpath, nullstr);
     9956        p = path_advance(&mpath, nullstr);
    89259957        if (p == NULL)
    89269958            break;
    89279959        if (*p == '\0')
    89289960            continue;
    8929         for (q = p; *q; q++);
     9961        for (q = p; *q; q++)
     9962            continue;
    89309963#if DEBUG
    89319964        if (q[-1] != '/')
     
    89399972        if (!mail_var_path_changed && statb.st_mtime != *mtp) {
    89409973            fprintf(
    8941                 stderr, snlfmt,
     9974                stderr, "%s\n",
    89429975                pathopt ? pathopt : "you have mail"
    89439976            );
     
    89499982}
    89509983
    8951 static void
    8952 changemail(const char *val)
    8953 {
    8954     mail_var_path_changed++;
     9984static void FAST_FUNC
     9985changemail(const char *val UNUSED_PARAM)
     9986{
     9987    mail_var_path_changed = 1;
    89559988}
    89569989
     
    897010003    int nparam;
    897110004
    8972     for (nparam = 0; argv[nparam]; nparam++);
     10005    for (nparam = 0; argv[nparam]; nparam++)
     10006        continue;
    897310007    ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
    897410008    while (*argv) {
     
    897710011    *ap = NULL;
    897810012    freeparam(&shellparam);
    8979     shellparam.malloc = 1;
     10013    shellparam.malloced = 1;
    898010014    shellparam.nparam = nparam;
    898110015    shellparam.p = newparam;
     
    898910023 * Process shell options.  The global variable argptr contains a pointer
    899010024 * to the argument list; we advance it past the options.
    8991  */
    8992 static void
    8993 minus_o(char *name, int val)
     10025 *
     10026 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
     10027 * For a non-interactive shell, an error condition encountered
     10028 * by a special built-in ... shall cause the shell to write a diagnostic message
     10029 * to standard error and exit as shown in the following table:
     10030 * Error                                           Special Built-In
     10031 * ...
     10032 * Utility syntax error (option or operand error)  Shall exit
     10033 * ...
     10034 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
     10035 * we see that bash does not do that (set "finishes" with error code 1 instead,
     10036 * and shell continues), and people rely on this behavior!
     10037 * Testcase:
     10038 * set -o barfoo 2>/dev/null
     10039 * echo $?
     10040 *
     10041 * Oh well. Let's mimic that.
     10042 */
     10043static int
     10044plus_minus_o(char *name, int val)
    899410045{
    899510046    int i;
     
    899910050            if (strcmp(name, optnames(i)) == 0) {
    900010051                optlist[i] = val;
    9001                 return;
     10052                return 0;
    900210053            }
    900310054        }
    9004         ash_msg_and_raise_error("illegal option -o %s", name);
    9005     }
    9006     out1str("Current option settings\n");
    9007     for (i = 0; i < NOPTS; i++)
    9008         out1fmt("%-16s%s\n", optnames(i),
    9009                 optlist[i] ? "on" : "off");
     10055        ash_msg("illegal option %co %s", val ? '-' : '+', name);
     10056        return 1;
     10057    }
     10058    for (i = 0; i < NOPTS; i++) {
     10059        if (val) {
     10060            out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
     10061        } else {
     10062            out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
     10063        }
     10064    }
     10065    return 0;
    901010066}
    901110067static void
     
    902010076        }
    902110077    }
    9022     ash_msg_and_raise_error("illegal option -%c", flag);
     10078    ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
    902310079    /* NOTREACHED */
    902410080}
    9025 static void
     10081static int
    902610082options(int cmdline)
    902710083{
     
    905810114                minusc = p;     /* command is after shell args */
    905910115            } else if (c == 'o') {
    9060                 minus_o(*argptr, val);
     10116                if (plus_minus_o(*argptr, val)) {
     10117                    /* it already printed err message */
     10118                    return 1; /* error */
     10119                }
    906110120                if (*argptr)
    906210121                    argptr++;
     
    907310132        }
    907410133    }
     10134    return 0;
    907510135}
    907610136
     
    907810138 * The shift builtin command.
    907910139 */
    9080 static int
    9081 shiftcmd(int argc, char **argv)
     10140static int FAST_FUNC
     10141shiftcmd(int argc UNUSED_PARAM, char **argv)
    908210142{
    908310143    int n;
     
    908510145
    908610146    n = 1;
    9087     if (argc > 1)
     10147    if (argv[1])
    908810148        n = number(argv[1]);
    908910149    if (n > shellparam.nparam)
    9090         ash_msg_and_raise_error("can't shift that many");
     10150        n = 0; /* bash compat, was = shellparam.nparam; */
    909110151    INT_OFF;
    909210152    shellparam.nparam -= n;
    909310153    for (ap1 = shellparam.p; --n >= 0; ap1++) {
    9094         if (shellparam.malloc)
     10154        if (shellparam.malloced)
    909510155            free(*ap1);
    909610156    }
    909710157    ap2 = shellparam.p;
    9098     while ((*ap2++ = *ap1++) != NULL);
     10158    while ((*ap2++ = *ap1++) != NULL)
     10159        continue;
    909910160#if ENABLE_ASH_GETOPTS
    910010161    shellparam.optind = 1;
     
    913910200 * The set command builtin.
    914010201 */
    9141 static int
    9142 setcmd(int argc, char **argv)
    9143 {
    9144     if (argc == 1)
     10202static int FAST_FUNC
     10203setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
     10204{
     10205    int retval;
     10206
     10207    if (!argv[1])
    914510208        return showvars(nullstr, 0, VUNSET);
    914610209    INT_OFF;
    9147     options(0);
    9148     optschanged();
    9149     if (*argptr != NULL) {
    9150         setparam(argptr);
     10210    retval = 1;
     10211    if (!options(0)) { /* if no parse error... */
     10212        retval = 0;
     10213        optschanged();
     10214        if (*argptr != NULL) {
     10215            setparam(argptr);
     10216        }
    915110217    }
    915210218    INT_ON;
    9153     return 0;
     10219    return retval;
    915410220}
    915510221
    915610222#if ENABLE_ASH_RANDOM_SUPPORT
    9157 /* Roughly copied from bash.. */
    9158 static void
     10223static void FAST_FUNC
    915910224change_random(const char *value)
    916010225{
     10226    uint32_t t;
     10227
    916110228    if (value == NULL) {
    916210229        /* "get", generate */
    9163         char buf[16];
    9164 
    9165         rseed = rseed * 1103515245 + 12345;
    9166         sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
     10230        t = next_random(&random_gen);
    916710231        /* set without recursion */
    9168         setvar(vrandom.text, buf, VNOFUNC);
     10232        setvar(vrandom.var_text, utoa(t), VNOFUNC);
    916910233        vrandom.flags &= ~VNOFUNC;
    917010234    } else {
    917110235        /* set/reset */
    9172         rseed = strtoul(value, (char **)NULL, 10);
     10236        t = strtoul(value, NULL, 10);
     10237        INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
    917310238    }
    917410239}
     
    919010255    optnext = optfirst + *param_optind - 1;
    919110256
    9192     if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
     10257    if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
    919310258        p = NULL;
    919410259    else
     
    920910274
    921010275    c = *p++;
    9211     for (q = optstr; *q != c; ) {
     10276    for (q = optstr; *q != c;) {
    921210277        if (*q == '\0') {
    921310278            if (optstr[0] == ':') {
     
    927010335 * then it's the first time getopts has been called.
    927110336 */
    9272 static int
     10337static int FAST_FUNC
    927310338getoptscmd(int argc, char **argv)
    927410339{
     
    929910364/* ============ Shell parser */
    930010365
    9301 static int tokpushback;                /* last token pushed back */
    9302 #define NEOF ((union node *)&tokpushback)
    9303 static int parsebackquote;             /* nonzero if we are inside backquotes */
    9304 static int lasttoken;                  /* last token read */
     10366struct heredoc {
     10367    struct heredoc *next;   /* next here document in list */
     10368    union node *here;       /* redirection node */
     10369    char *eofmark;          /* string indicating end of input */
     10370    smallint striptabs;     /* if set, strip leading tabs */
     10371};
     10372
     10373static smallint tokpushback;           /* last token pushed back */
     10374static smallint parsebackquote;        /* nonzero if we are inside backquotes */
     10375static smallint quoteflag;             /* set if (part of) last token was quoted */
     10376static token_id_t lasttoken;           /* last token read (integer id Txxx) */
     10377static struct heredoc *heredoclist;    /* list of here documents to read */
    930510378static char *wordtext;                 /* text of last word returned by readtoken */
    930610379static struct nodelist *backquotelist;
    930710380static union node *redirnode;
    930810381static struct heredoc *heredoc;
    9309 static int quoteflag;                  /* set if (part of) last token was quoted */
    9310 
    9311 static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
    9312 static void
    9313 raise_error_syntax(const char *msg)
    9314 {
    9315     ash_msg_and_raise_error("syntax error: %s", msg);
    9316     /* NOTREACHED */
    9317 }
    9318 
    9319 /*
     10382
     10383static const char *
     10384tokname(char *buf, int tok)
     10385{
     10386    if (tok < TSEMI)
     10387        return tokname_array[tok] + 1;
     10388    sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
     10389    return buf;
     10390}
     10391
     10392/* raise_error_unexpected_syntax:
    932010393 * Called when an unexpected token is read during the parse.  The argument
    932110394 * is the token that is expected, or -1 if more than one type of token can
    932210395 * occur at this point.
    932310396 */
    9324 static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
     10397static void raise_error_unexpected_syntax(int) NORETURN;
    932510398static void
    932610399raise_error_unexpected_syntax(int token)
    932710400{
    932810401    char msg[64];
     10402    char buf[16];
    932910403    int l;
    933010404
    9331     l = sprintf(msg, "%s unexpected", tokname(lasttoken));
     10405    l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
    933210406    if (token >= 0)
    9333         sprintf(msg + l, " (expecting %s)", tokname(token));
     10407        sprintf(msg + l, " (expecting %s)", tokname(buf, token));
    933410408    raise_error_syntax(msg);
    933510409    /* NOTREACHED */
     
    933710411
    933810412#define EOFMARKLEN 79
    9339 
    9340 struct heredoc {
    9341     struct heredoc *next;   /* next here document in list */
    9342     union node *here;               /* redirection node */
    9343     char *eofmark;          /* string indicating end of input */
    9344     int striptabs;          /* if set, strip leading tabs */
    9345 };
    9346 
    9347 static struct heredoc *heredoclist;    /* list of here documents to read */
    934810413
    934910414/* parsing is heavily cross-recursive, need these forward decls */
     
    937010435        if (tok == TBACKGND) {
    937110436            if (n2->type == NPIPE) {
    9372                 n2->npipe.backgnd = 1;
     10437                n2->npipe.pipe_backgnd = 1;
    937310438            } else {
    937410439                if (n2->type != NREDIR) {
    9375                     n3 = stalloc(sizeof(struct nredir));
     10440                    n3 = stzalloc(sizeof(struct nredir));
    937610441                    n3->nredir.n = n2;
    9377                     n3->nredir.redirect = NULL;
     10442                    /*n3->nredir.redirect = NULL; - stzalloc did it */
    937810443                    n2 = n3;
    937910444                }
     
    938410449            n1 = n2;
    938510450        } else {
    9386             n3 = stalloc(sizeof(struct nbinary));
     10451            n3 = stzalloc(sizeof(struct nbinary));
    938710452            n3->type = NSEMI;
    938810453            n3->nbinary.ch1 = n1;
     
    940110466                    return n1;
    940210467            } else {
    9403                 tokpushback++;
     10468                tokpushback = 1;
    940410469            }
    940510470            checkkwd = CHKNL | CHKKWD | CHKALIAS;
     
    941610481            if (nlflag == 1)
    941710482                raise_error_unexpected_syntax(-1);
    9418             tokpushback++;
     10483            tokpushback = 1;
    941910484            return n1;
    942010485        }
     
    943610501            t = NOR;
    943710502        } else {
    9438             tokpushback++;
     10503            tokpushback = 1;
    943910504            return n1;
    944010505        }
    944110506        checkkwd = CHKNL | CHKKWD | CHKALIAS;
    944210507        n2 = pipeline();
    9443         n3 = stalloc(sizeof(struct nbinary));
     10508        n3 = stzalloc(sizeof(struct nbinary));
    944410509        n3->type = t;
    944510510        n3->nbinary.ch1 = n1;
     
    946210527        checkkwd = CHKKWD | CHKALIAS;
    946310528    } else
    9464         tokpushback++;
     10529        tokpushback = 1;
    946510530    n1 = parse_command();
    946610531    if (readtoken() == TPIPE) {
    9467         pipenode = stalloc(sizeof(struct npipe));
     10532        pipenode = stzalloc(sizeof(struct npipe));
    946810533        pipenode->type = NPIPE;
    9469         pipenode->npipe.backgnd = 0;
    9470         lp = stalloc(sizeof(struct nodelist));
     10534        /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
     10535        lp = stzalloc(sizeof(struct nodelist));
    947110536        pipenode->npipe.cmdlist = lp;
    947210537        lp->n = n1;
    947310538        do {
    947410539            prev = lp;
    9475             lp = stalloc(sizeof(struct nodelist));
     10540            lp = stzalloc(sizeof(struct nodelist));
    947610541            checkkwd = CHKNL | CHKKWD | CHKALIAS;
    947710542            lp->n = parse_command();
     
    948110546        n1 = pipenode;
    948210547    }
    9483     tokpushback++;
     10548    tokpushback = 1;
    948410549    if (negate) {
    9485         n2 = stalloc(sizeof(struct nnot));
     10550        n2 = stzalloc(sizeof(struct nnot));
    948610551        n2->type = NNOT;
    948710552        n2->nnot.com = n1;
     
    949610561    union node *n;
    949710562
    9498     n = stalloc(sizeof(struct narg));
     10563    n = stzalloc(sizeof(struct narg));
    949910564    n->type = NARG;
    9500     n->narg.next = NULL;
     10565    /*n->narg.next = NULL; - stzalloc did it */
    950110566    n->narg.text = wordtext;
    950210567    n->narg.backquote = backquotelist;
     
    950710572fixredir(union node *n, const char *text, int err)
    950810573{
     10574    int fd;
     10575
    950910576    TRACE(("Fix redir %s %d\n", text, err));
    951010577    if (!err)
    951110578        n->ndup.vname = NULL;
    951210579
    9513     if (isdigit(text[0]) && text[1] == '\0')
    9514         n->ndup.dupfd = text[0] - '0';
     10580    fd = bb_strtou(text, NULL, 10);
     10581    if (!errno && fd >= 0)
     10582        n->ndup.dupfd = fd;
    951510583    else if (LONE_DASH(text))
    951610584        n->ndup.dupfd = -1;
    951710585    else {
    951810586        if (err)
    9519             raise_error_syntax("Bad fd number");
     10587            raise_error_syntax("bad fd number");
    952010588        n->ndup.vname = makename();
    952110589    }
     
    952710595 */
    952810596static int
    9529 noexpand(char *text)
    9530 {
    9531     char *p;
    9532     char c;
    9533 
    9534     p = text;
    9535     while ((c = *p++) != '\0') {
     10597noexpand(const char *text)
     10598{
     10599    unsigned char c;
     10600
     10601    while ((c = *text++) != '\0') {
    953610602        if (c == CTLQUOTEMARK)
    953710603            continue;
    953810604        if (c == CTLESC)
    9539             p++;
     10605            text++;
    954010606        else if (SIT(c, BASESYNTAX) == CCTL)
    954110607            return 0;
     
    956010626        TRACE(("Here document %d\n", n->type));
    956110627        if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
    9562             raise_error_syntax("Illegal eof marker for << redirection");
    9563         rmescapes(wordtext);
     10628            raise_error_syntax("illegal eof marker for << redirection");
     10629        rmescapes(wordtext, 0);
    956410630        here->eofmark = wordtext;
    956510631        here->next = NULL;
     
    956710633            heredoclist = here;
    956810634        else {
    9569             for (p = heredoclist; p->next; p = p->next);
     10635            for (p = heredoclist; p->next; p = p->next)
     10636                continue;
    957010637            p->next = here;
    957110638        }
     
    958510652    union node **rpp, *redir;
    958610653    int savecheckkwd;
     10654#if ENABLE_ASH_BASH_COMPAT
     10655    smallint double_brackets_flag = 0;
     10656#endif
    958710657
    958810658    args = NULL;
     
    959510665    savecheckkwd = CHKALIAS;
    959610666    for (;;) {
     10667        int t;
    959710668        checkkwd = savecheckkwd;
    9598         switch (readtoken()) {
     10669        t = readtoken();
     10670        switch (t) {
     10671#if ENABLE_ASH_BASH_COMPAT
     10672        case TAND: /* "&&" */
     10673        case TOR: /* "||" */
     10674            if (!double_brackets_flag) {
     10675                tokpushback = 1;
     10676                goto out;
     10677            }
     10678            wordtext = (char *) (t == TAND ? "-a" : "-o");
     10679#endif
    959910680        case TWORD:
    9600             n = stalloc(sizeof(struct narg));
     10681            n = stzalloc(sizeof(struct narg));
    960110682            n->type = NARG;
     10683            /*n->narg.next = NULL; - stzalloc did it */
    960210684            n->narg.text = wordtext;
     10685#if ENABLE_ASH_BASH_COMPAT
     10686            if (strcmp("[[", wordtext) == 0)
     10687                double_brackets_flag = 1;
     10688            else if (strcmp("]]", wordtext) == 0)
     10689                double_brackets_flag = 0;
     10690#endif
    960310691            n->narg.backquote = backquotelist;
    960410692            if (savecheckkwd && isassignment(wordtext)) {
     
    963010718                 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
    963110719                ) {
    9632                     raise_error_syntax("Bad function name");
     10720                    raise_error_syntax("bad function name");
    963310721                }
    963410722                n->type = NDEFUN;
     
    963910727            /* fall through */
    964010728        default:
    9641             tokpushback++;
     10729            tokpushback = 1;
    964210730            goto out;
    964310731        }
     
    964710735    *vpp = NULL;
    964810736    *rpp = NULL;
    9649     n = stalloc(sizeof(struct ncmd));
     10737    n = stzalloc(sizeof(struct ncmd));
    965010738    n->type = NCMD;
    965110739    n->ncmd.args = args;
     
    967310761        /* NOTREACHED */
    967410762    case TIF:
    9675         n1 = stalloc(sizeof(struct nif));
     10763        n1 = stzalloc(sizeof(struct nif));
    967610764        n1->type = NIF;
    967710765        n1->nif.test = list(0);
     
    968110769        n2 = n1;
    968210770        while (readtoken() == TELIF) {
    9683             n2->nif.elsepart = stalloc(sizeof(struct nif));
     10771            n2->nif.elsepart = stzalloc(sizeof(struct nif));
    968410772            n2 = n2->nif.elsepart;
    968510773            n2->type = NIF;
     
    969310781        else {
    969410782            n2->nif.elsepart = NULL;
    9695             tokpushback++;
     10783            tokpushback = 1;
    969610784        }
    969710785        t = TFI;
     
    970010788    case TUNTIL: {
    970110789        int got;
    9702         n1 = stalloc(sizeof(struct nbinary));
     10790        n1 = stzalloc(sizeof(struct nbinary));
    970310791        n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
    970410792        n1->nbinary.ch1 = list(0);
    970510793        got = readtoken();
    970610794        if (got != TDO) {
    9707             TRACE(("expecting DO got %s %s\n", tokname(got),
     10795            TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
    970810796                    got == TWORD ? wordtext : ""));
    970910797            raise_error_unexpected_syntax(TDO);
     
    971410802    }
    971510803    case TFOR:
    9716         if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
    9717             raise_error_syntax("Bad for loop variable");
    9718         n1 = stalloc(sizeof(struct nfor));
     10804        if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
     10805            raise_error_syntax("bad for loop variable");
     10806        n1 = stzalloc(sizeof(struct nfor));
    971910807        n1->type = NFOR;
    972010808        n1->nfor.var = wordtext;
     
    972310811            app = &ap;
    972410812            while (readtoken() == TWORD) {
    9725                 n2 = stalloc(sizeof(struct narg));
     10813                n2 = stzalloc(sizeof(struct narg));
    972610814                n2->type = NARG;
     10815                /*n2->narg.next = NULL; - stzalloc did it */
    972710816                n2->narg.text = wordtext;
    972810817                n2->narg.backquote = backquotelist;
     
    973510824                raise_error_unexpected_syntax(-1);
    973610825        } else {
    9737             n2 = stalloc(sizeof(struct narg));
     10826            n2 = stzalloc(sizeof(struct narg));
    973810827            n2->type = NARG;
     10828            /*n2->narg.next = NULL; - stzalloc did it */
    973910829            n2->narg.text = (char *)dolatstr;
    9740             n2->narg.backquote = NULL;
    9741             n2->narg.next = NULL;
     10830            /*n2->narg.backquote = NULL;*/
    974210831            n1->nfor.args = n2;
    974310832            /*
     
    974610835             */
    974710836            if (lasttoken != TNL && lasttoken != TSEMI)
    9748                 tokpushback++;
     10837                tokpushback = 1;
    974910838        }
    975010839        checkkwd = CHKNL | CHKKWD | CHKALIAS;
     
    975510844        break;
    975610845    case TCASE:
    9757         n1 = stalloc(sizeof(struct ncase));
     10846        n1 = stzalloc(sizeof(struct ncase));
    975810847        n1->type = NCASE;
    975910848        if (readtoken() != TWORD)
    976010849            raise_error_unexpected_syntax(TWORD);
    9761         n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
     10850        n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
    976210851        n2->type = NARG;
     10852        /*n2->narg.next = NULL; - stzalloc did it */
    976310853        n2->narg.text = wordtext;
    976410854        n2->narg.backquote = backquotelist;
    9765         n2->narg.next = NULL;
    976610855        do {
    976710856            checkkwd = CHKKWD | CHKALIAS;
     
    977610865            if (lasttoken == TLP)
    977710866                readtoken();
    9778             *cpp = cp = stalloc(sizeof(struct nclist));
     10867            *cpp = cp = stzalloc(sizeof(struct nclist));
    977910868            cp->type = NCLIST;
    978010869            app = &cp->nclist.pattern;
    978110870            for (;;) {
    9782                 *app = ap = stalloc(sizeof(struct narg));
     10871                *app = ap = stzalloc(sizeof(struct narg));
    978310872                ap->type = NARG;
     10873                /*ap->narg.next = NULL; - stzalloc did it */
    978410874                ap->narg.text = wordtext;
    978510875                ap->narg.backquote = backquotelist;
     
    978910879                readtoken();
    979010880            }
    9791             ap->narg.next = NULL;
     10881            //ap->narg.next = NULL;
    979210882            if (lasttoken != TRP)
    979310883                raise_error_unexpected_syntax(TRP);
     
    980710897        goto redir;
    980810898    case TLP:
    9809         n1 = stalloc(sizeof(struct nredir));
     10899        n1 = stzalloc(sizeof(struct nredir));
    981010900        n1->type = NSUBSHELL;
    981110901        n1->nredir.n = list(0);
    9812         n1->nredir.redirect = NULL;
     10902        /*n1->nredir.redirect = NULL; - stzalloc did it */
    981310903        t = TRP;
    981410904        break;
     
    981910909    case TWORD:
    982010910    case TREDIR:
    9821         tokpushback++;
     10911        tokpushback = 1;
    982210912        return simplecmd();
    982310913    }
     
    983510925        parsefname();
    983610926    }
    9837     tokpushback++;
     10927    tokpushback = 1;
    983810928    *rpp = NULL;
    983910929    if (redir) {
    984010930        if (n1->type != NSUBSHELL) {
    9841             n2 = stalloc(sizeof(struct nredir));
     10931            n2 = stzalloc(sizeof(struct nredir));
    984210932            n2->type = NREDIR;
    984310933            n2->nredir.n = n1;
     
    984910939}
    985010940
     10941#if ENABLE_ASH_BASH_COMPAT
     10942static int decode_dollar_squote(void)
     10943{
     10944    static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
     10945    int c, cnt;
     10946    char *p;
     10947    char buf[4];
     10948
     10949    c = pgetc();
     10950    p = strchr(C_escapes, c);
     10951    if (p) {
     10952        buf[0] = c;
     10953        p = buf;
     10954        cnt = 3;
     10955        if ((unsigned char)(c - '0') <= 7) { /* \ooo */
     10956            do {
     10957                c = pgetc();
     10958                *++p = c;
     10959            } while ((unsigned char)(c - '0') <= 7 && --cnt);
     10960            pungetc();
     10961        } else if (c == 'x') { /* \xHH */
     10962            do {
     10963                c = pgetc();
     10964                *++p = c;
     10965            } while (isxdigit(c) && --cnt);
     10966            pungetc();
     10967            if (cnt == 3) { /* \x but next char is "bad" */
     10968                c = 'x';
     10969                goto unrecognized;
     10970            }
     10971        } else { /* simple seq like \\ or \t */
     10972            p++;
     10973        }
     10974        *p = '\0';
     10975        p = buf;
     10976        c = bb_process_escape_sequence((void*)&p);
     10977    } else { /* unrecognized "\z": print both chars unless ' or " */
     10978        if (c != '\'' && c != '"') {
     10979 unrecognized:
     10980            c |= 0x100; /* "please encode \, then me" */
     10981        }
     10982    }
     10983    return c;
     10984}
     10985#endif
     10986
    985110987/*
    985210988 * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
    985310989 * is not NULL, read a here document.  In the latter case, eofmark is the
    985410990 * word which marks the end of the document and striptabs is true if
    9855  * leading tabs should be stripped from the document.  The argument firstc
     10991 * leading tabs should be stripped from the document.  The argument c
    985610992 * is the first character of the input token or document.
    985710993 *
     
    986010996 * will run code that appears at the end of readtoken1.
    986110997 */
    9862 
    9863 static int parsebackquote;             /* nonzero if we are inside backquotes */
    9864 
    986510998#define CHECKEND()      {goto checkend; checkend_return:;}
    986610999#define PARSEREDIR()    {goto parseredir; parseredir_return:;}
     
    986911002#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
    987011003#define PARSEARITH()    {goto parsearith; parsearith_return:;}
    9871 
    987211004static int
    9873 readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
    9874 {
    9875     int c = firstc;
     11005readtoken1(int c, int syntax, char *eofmark, int striptabs)
     11006{
     11007    /* NB: syntax parameter fits into smallint */
     11008    /* c parameter is an unsigned char or PEOF or PEOA */
    987611009    char *out;
    987711010    int len;
    987811011    char line[EOFMARKLEN + 1];
    9879     struct nodelist *bqlist = 0;
    9880     int quotef = 0;
    9881     int dblquote = 0;
    9882     int varnest = 0;    /* levels of variables expansion */
    9883     int arinest = 0;    /* levels of arithmetic expansion */
    9884     int parenlevel = 0; /* levels of parens in arithmetic */
    9885     int dqvarnest = 0;  /* levels of variables expansion within double quotes */
    9886     int oldstyle = 0;
    9887     int prevsyntax = 0; /* syntax before arithmetic */
     11012    struct nodelist *bqlist;
     11013    smallint quotef;
     11014    smallint dblquote;
     11015    smallint oldstyle;
     11016    smallint prevsyntax; /* syntax before arithmetic */
     11017#if ENABLE_ASH_EXPAND_PRMT
     11018    smallint pssyntax;   /* we are expanding a prompt string */
     11019#endif
     11020    int varnest;         /* levels of variables expansion */
     11021    int arinest;         /* levels of arithmetic expansion */
     11022    int parenlevel;      /* levels of parens in arithmetic */
     11023    int dqvarnest;       /* levels of variables expansion within double quotes */
     11024
     11025    IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
     11026
    988811027#if __GNUC__
    988911028    /* Avoid longjmp clobbering */
     
    989911038    (void) &syntax;
    990011039#endif
    9901 
    9902     startlinno = plinno;
    9903     dblquote = 0;
    9904     if (syntax == DQSYNTAX)
    9905         dblquote = 1;
     11040    startlinno = g_parsefile->linno;
     11041    bqlist = NULL;
    990611042    quotef = 0;
    9907     bqlist = NULL;
     11043    prevsyntax = 0;
     11044#if ENABLE_ASH_EXPAND_PRMT
     11045    pssyntax = (syntax == PSSYNTAX);
     11046    if (pssyntax)
     11047        syntax = DQSYNTAX;
     11048#endif
     11049    dblquote = (syntax == DQSYNTAX);
    990811050    varnest = 0;
    990911051    arinest = 0;
     
    991211054
    991311055    STARTSTACKSTR(out);
    9914     loop: { /* for each line, until end of word */
    9915         CHECKEND();     /* set c to PEOF if at end of here document */
    9916         for (;;) {      /* until end of line or end of word */
    9917             CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
    9918             switch (SIT(c, syntax)) {
    9919             case CNL:       /* '\n' */
    9920                 if (syntax == BASESYNTAX)
    9921                     goto endword;   /* exit outer loop */
    9922                 USTPUTC(c, out);
    9923                 plinno++;
    9924                 if (doprompt)
    9925                     setprompt(2);
    9926                 c = pgetc();
    9927                 goto loop;              /* continue outer loop */
    9928             case CWORD:
    9929                 USTPUTC(c, out);
    9930                 break;
    9931             case CCTL:
    9932                 if (eofmark == NULL || dblquote)
     11056 loop:
     11057    /* For each line, until end of word */
     11058    CHECKEND();     /* set c to PEOF if at end of here document */
     11059    for (;;) {      /* until end of line or end of word */
     11060        CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
     11061        switch (SIT(c, syntax)) {
     11062        case CNL:       /* '\n' */
     11063            if (syntax == BASESYNTAX)
     11064                goto endword;   /* exit outer loop */
     11065            USTPUTC(c, out);
     11066            g_parsefile->linno++;
     11067            setprompt_if(doprompt, 2);
     11068            c = pgetc();
     11069            goto loop;              /* continue outer loop */
     11070        case CWORD:
     11071            USTPUTC(c, out);
     11072            break;
     11073        case CCTL:
     11074            if (eofmark == NULL || dblquote)
     11075                USTPUTC(CTLESC, out);
     11076#if ENABLE_ASH_BASH_COMPAT
     11077            if (c == '\\' && bash_dollar_squote) {
     11078                c = decode_dollar_squote();
     11079                if (c & 0x100) {
     11080                    USTPUTC('\\', out);
     11081                    c = (unsigned char)c;
     11082                }
     11083            }
     11084#endif
     11085            USTPUTC(c, out);
     11086            break;
     11087        case CBACK:     /* backslash */
     11088            c = pgetc_without_PEOA();
     11089            if (c == PEOF) {
     11090                USTPUTC(CTLESC, out);
     11091                USTPUTC('\\', out);
     11092                pungetc();
     11093            } else if (c == '\n') {
     11094                setprompt_if(doprompt, 2);
     11095            } else {
     11096#if ENABLE_ASH_EXPAND_PRMT
     11097                if (c == '$' && pssyntax) {
     11098                    USTPUTC(CTLESC, out);
     11099                    USTPUTC('\\', out);
     11100                }
     11101#endif
     11102                /* Backslash is retained if we are in "str" and next char isn't special */
     11103                if (dblquote
     11104                 && c != '\\'
     11105                 && c != '`'
     11106                 && c != '$'
     11107                 && (c != '"' || eofmark != NULL)
     11108                ) {
     11109                    USTPUTC(CTLESC, out);
     11110                    USTPUTC('\\', out);
     11111                }
     11112                if (SIT(c, SQSYNTAX) == CCTL)
    993311113                    USTPUTC(CTLESC, out);
    993411114                USTPUTC(c, out);
    9935                 break;
    9936             case CBACK:     /* backslash */
    9937                 c = pgetc2();
    9938                 if (c == PEOF) {
    9939                     USTPUTC(CTLESC, out);
    9940                     USTPUTC('\\', out);
     11115                quotef = 1;
     11116            }
     11117            break;
     11118        case CSQUOTE:
     11119            syntax = SQSYNTAX;
     11120 quotemark:
     11121            if (eofmark == NULL) {
     11122                USTPUTC(CTLQUOTEMARK, out);
     11123            }
     11124            break;
     11125        case CDQUOTE:
     11126            syntax = DQSYNTAX;
     11127            dblquote = 1;
     11128            goto quotemark;
     11129        case CENDQUOTE:
     11130            IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
     11131            if (eofmark != NULL && arinest == 0
     11132             && varnest == 0
     11133            ) {
     11134                USTPUTC(c, out);
     11135            } else {
     11136                if (dqvarnest == 0) {
     11137                    syntax = BASESYNTAX;
     11138                    dblquote = 0;
     11139                }
     11140                quotef = 1;
     11141                goto quotemark;
     11142            }
     11143            break;
     11144        case CVAR:      /* '$' */
     11145            PARSESUB();             /* parse substitution */
     11146            break;
     11147        case CENDVAR:   /* '}' */
     11148            if (varnest > 0) {
     11149                varnest--;
     11150                if (dqvarnest > 0) {
     11151                    dqvarnest--;
     11152                }
     11153                c = CTLENDVAR;
     11154            }
     11155            USTPUTC(c, out);
     11156            break;
     11157#if ENABLE_SH_MATH_SUPPORT
     11158        case CLP:       /* '(' in arithmetic */
     11159            parenlevel++;
     11160            USTPUTC(c, out);
     11161            break;
     11162        case CRP:       /* ')' in arithmetic */
     11163            if (parenlevel > 0) {
     11164                parenlevel--;
     11165            } else {
     11166                if (pgetc() == ')') {
     11167                    if (--arinest == 0) {
     11168                        syntax = prevsyntax;
     11169                        dblquote = (syntax == DQSYNTAX);
     11170                        c = CTLENDARI;
     11171                    }
     11172                } else {
     11173                    /*
     11174                     * unbalanced parens
     11175                     * (don't 2nd guess - no error)
     11176                     */
    994111177                    pungetc();
    9942                 } else if (c == '\n') {
    9943                     if (doprompt)
    9944                         setprompt(2);
    9945                 } else {
    9946                     if (dblquote &&
    9947                         c != '\\' && c != '`' &&
    9948                         c != '$' && (
    9949                             c != '"' ||
    9950                             eofmark != NULL)
    9951                     ) {
    9952                         USTPUTC(CTLESC, out);
    9953                         USTPUTC('\\', out);
    9954                     }
    9955                     if (SIT(c, SQSYNTAX) == CCTL)
    9956                         USTPUTC(CTLESC, out);
    9957                     USTPUTC(c, out);
    9958                     quotef++;
    995911178                }
    9960                 break;
    9961             case CSQUOTE:
    9962                 syntax = SQSYNTAX;
    9963  quotemark:
    9964                 if (eofmark == NULL) {
    9965                     USTPUTC(CTLQUOTEMARK, out);
     11179            }
     11180            USTPUTC(c, out);
     11181            break;
     11182#endif
     11183        case CBQUOTE:   /* '`' */
     11184            PARSEBACKQOLD();
     11185            break;
     11186        case CENDFILE:
     11187            goto endword;           /* exit outer loop */
     11188        case CIGN:
     11189            break;
     11190        default:
     11191            if (varnest == 0) {
     11192#if ENABLE_ASH_BASH_COMPAT
     11193                if (c == '&') {
     11194                    if (pgetc() == '>')
     11195                        c = 0x100 + '>'; /* flag &> */
     11196                    pungetc();
    996611197                }
    9967                 break;
    9968             case CDQUOTE:
    9969                 syntax = DQSYNTAX;
    9970                 dblquote = 1;
    9971                 goto quotemark;
    9972             case CENDQUOTE:
    9973                 if (eofmark != NULL && arinest == 0
    9974                  && varnest == 0
    9975                 ) {
    9976                     USTPUTC(c, out);
    9977                 } else {
    9978                     if (dqvarnest == 0) {
    9979                         syntax = BASESYNTAX;
    9980                         dblquote = 0;
    9981                     }
    9982                     quotef++;
    9983                     goto quotemark;
    9984                 }
    9985                 break;
    9986             case CVAR:      /* '$' */
    9987                 PARSESUB();             /* parse substitution */
    9988                 break;
    9989             case CENDVAR:   /* '}' */
    9990                 if (varnest > 0) {
    9991                     varnest--;
    9992                     if (dqvarnest > 0) {
    9993                         dqvarnest--;
    9994                     }
    9995                     USTPUTC(CTLENDVAR, out);
    9996                 } else {
    9997                     USTPUTC(c, out);
    9998                 }
    9999                 break;
    10000 #if ENABLE_ASH_MATH_SUPPORT
    10001             case CLP:       /* '(' in arithmetic */
    10002                 parenlevel++;
     11198#endif
     11199                goto endword;   /* exit outer loop */
     11200            }
     11201            IF_ASH_ALIAS(if (c != PEOA))
    1000311202                USTPUTC(c, out);
    10004                 break;
    10005             case CRP:       /* ')' in arithmetic */
    10006                 if (parenlevel > 0) {
    10007                     USTPUTC(c, out);
    10008                     --parenlevel;
    10009                 } else {
    10010                     if (pgetc() == ')') {
    10011                         if (--arinest == 0) {
    10012                             USTPUTC(CTLENDARI, out);
    10013                             syntax = prevsyntax;
    10014                             if (syntax == DQSYNTAX)
    10015                                 dblquote = 1;
    10016                             else
    10017                                 dblquote = 0;
    10018                         } else
    10019                             USTPUTC(')', out);
    10020                     } else {
    10021                         /*
    10022                          * unbalanced parens
    10023                          *  (don't 2nd guess - no error)
    10024                          */
    10025                         pungetc();
    10026                         USTPUTC(')', out);
    10027                     }
    10028                 }
    10029                 break;
    10030 #endif
    10031             case CBQUOTE:   /* '`' */
    10032                 PARSEBACKQOLD();
    10033                 break;
    10034             case CENDFILE:
    10035                 goto endword;           /* exit outer loop */
    10036             case CIGN:
    10037                 break;
    10038             default:
    10039                 if (varnest == 0)
    10040                     goto endword;   /* exit outer loop */
    10041 #if ENABLE_ASH_ALIAS
    10042                 if (c != PEOA)
    10043 #endif
    10044                     USTPUTC(c, out);
    10045 
    10046             }
    10047             c = pgetc_macro();
    10048         }
    10049     }
     11203        }
     11204        c = pgetc_fast();
     11205    } /* for (;;) */
    1005011206 endword:
    10051 #if ENABLE_ASH_MATH_SUPPORT
     11207
     11208#if ENABLE_SH_MATH_SUPPORT
    1005211209    if (syntax == ARISYNTAX)
    10053         raise_error_syntax("Missing '))'");
     11210        raise_error_syntax("missing '))'");
    1005411211#endif
    1005511212    if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
    10056         raise_error_syntax("Unterminated quoted string");
     11213        raise_error_syntax("unterminated quoted string");
    1005711214    if (varnest != 0) {
    10058         startlinno = plinno;
     11215        startlinno = g_parsefile->linno;
    1005911216        /* { */
    10060         raise_error_syntax("Missing '}'");
     11217        raise_error_syntax("missing '}'");
    1006111218    }
    1006211219    USTPUTC('\0', out);
     
    1006411221    out = stackblock();
    1006511222    if (eofmark == NULL) {
    10066         if ((c == '>' || c == '<')
     11223        if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
    1006711224         && quotef == 0
    10068          && len <= 2
    10069          && (*out == '\0' || isdigit(*out))) {
    10070             PARSEREDIR();
    10071             return lasttoken = TREDIR;
    10072         } else {
    10073             pungetc();
    10074         }
     11225        ) {
     11226            if (isdigit_str9(out)) {
     11227                PARSEREDIR(); /* passed as params: out, c */
     11228                lasttoken = TREDIR;
     11229                return lasttoken;
     11230            }
     11231            /* else: non-number X seen, interpret it
     11232             * as "NNNX>file" = "NNNX >file" */
     11233        }
     11234        pungetc();
    1007511235    }
    1007611236    quoteflag = quotef;
     
    1009011250    if (eofmark) {
    1009111251#if ENABLE_ASH_ALIAS
    10092         if (c == PEOA) {
    10093             c = pgetc2();
    10094         }
     11252        if (c == PEOA)
     11253            c = pgetc_without_PEOA();
    1009511254#endif
    1009611255        if (striptabs) {
    1009711256            while (c == '\t') {
    10098                 c = pgetc2();
     11257                c = pgetc_without_PEOA();
    1009911258            }
    1010011259        }
     
    1010411263
    1010511264                p = line;
    10106                 for (q = eofmark + 1; *q && *p == *q; p++, q++);
     11265                for (q = eofmark + 1; *q && *p == *q; p++, q++)
     11266                    continue;
    1010711267                if (*p == '\n' && *q == '\0') {
    1010811268                    c = PEOF;
    10109                     plinno++;
     11269                    g_parsefile->linno++;
    1011011270                    needprompt = doprompt;
    1011111271                } else {
     
    1012411284 */
    1012511285parseredir: {
    10126     char fd = *out;
     11286    /* out is already checked to be a valid number or "" */
     11287    int fd = (*out == '\0' ? -1 : atoi(out));
    1012711288    union node *np;
    1012811289
    10129     np = stalloc(sizeof(struct nfile));
     11290    np = stzalloc(sizeof(struct nfile));
    1013011291    if (c == '>') {
    1013111292        np->nfile.fd = 1;
     
    1013711298        else if (c == '&')
    1013811299            np->type = NTOFD;
     11300            /* it also can be NTO2 (>&file), but we can't figure it out yet */
    1013911301        else {
    1014011302            np->type = NTO;
    1014111303            pungetc();
    1014211304        }
    10143     } else {        /* c == '<' */
    10144         np->nfile.fd = 0;
     11305    }
     11306#if ENABLE_ASH_BASH_COMPAT
     11307    else if (c == 0x100 + '>') { /* this flags &> redirection */
     11308        np->nfile.fd = 1;
     11309        pgetc(); /* this is '>', no need to check */
     11310        np->type = NTO2;
     11311    }
     11312#endif
     11313    else { /* c == '<' */
     11314        /*np->nfile.fd = 0; - stzalloc did it */
    1014511315        c = pgetc();
    1014611316        switch (c) {
    1014711317        case '<':
    1014811318            if (sizeof(struct nfile) != sizeof(struct nhere)) {
    10149                 np = stalloc(sizeof(struct nhere));
    10150                 np->nfile.fd = 0;
     11319                np = stzalloc(sizeof(struct nhere));
     11320                /*np->nfile.fd = 0; - stzalloc did it */
    1015111321            }
    1015211322            np->type = NHERE;
    10153             heredoc = stalloc(sizeof(struct heredoc));
     11323            heredoc = stzalloc(sizeof(struct heredoc));
    1015411324            heredoc->here = np;
    1015511325            c = pgetc();
     
    1015711327                heredoc->striptabs = 1;
    1015811328            } else {
    10159                 heredoc->striptabs = 0;
     11329                /*heredoc->striptabs = 0; - stzalloc did it */
    1016011330                pungetc();
    1016111331            }
     
    1017611346        }
    1017711347    }
    10178     if (fd != '\0')
    10179         np->nfile.fd = fd - '0';
     11348    if (fd >= 0)
     11349        np->nfile.fd = fd;
    1018011350    redirnode = np;
    1018111351    goto parseredir_return;
     
    1019011360 * (assuming ascii char codes, as the original implementation did) */
    1019111361#define is_special(c) \
    10192     ((((unsigned int)c) - 33 < 32) \
    10193             && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
     11362    (((unsigned)(c) - 33 < 32) \
     11363            && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
    1019411364parsesub: {
    10195     int subtype;
     11365    unsigned char subtype;
    1019611366    int typeloc;
    1019711367    int flags;
    10198     char *p;
    10199     static const char types[] ALIGN1 = "}-+?=";
    1020011368
    1020111369    c = pgetc();
    10202     if (
    10203         c <= PEOA_OR_PEOF  ||
    10204         (c != '(' && c != '{' && !is_name(c) && !is_special(c))
     11370    if (c > 255 /* PEOA or PEOF */
     11371     || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
    1020511372    ) {
    10206         USTPUTC('$', out);
     11373#if ENABLE_ASH_BASH_COMPAT
     11374        if (c == '\'')
     11375            bash_dollar_squote = 1;
     11376        else
     11377#endif
     11378            USTPUTC('$', out);
    1020711379        pungetc();
    10208     } else if (c == '(') {  /* $(command) or $((arith)) */
     11380    } else if (c == '(') {
     11381        /* $(command) or $((arith)) */
    1020911382        if (pgetc() == '(') {
    10210 #if ENABLE_ASH_MATH_SUPPORT
     11383#if ENABLE_SH_MATH_SUPPORT
    1021111384            PARSEARITH();
    1021211385#else
    10213             raise_error_syntax("We unsupport $((arith))");
     11386            raise_error_syntax("you disabled math support for $((arith)) syntax");
    1021411387#endif
    1021511388        } else {
     
    1021811391        }
    1021911392    } else {
     11393        /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
    1022011394        USTPUTC(CTLVAR, out);
    1022111395        typeloc = out - (char *)stackblock();
     
    1022711401                c = pgetc();
    1022811402                if (c == '}')
    10229                     c = '#';
     11403                    c = '#'; /* ${#} - same as $# */
    1023011404                else
    10231                     subtype = VSLENGTH;
    10232             } else
     11405                    subtype = VSLENGTH; /* ${#VAR} */
     11406            } else {
    1023311407                subtype = 0;
    10234         }
    10235         if (c > PEOA_OR_PEOF && is_name(c)) {
     11408            }
     11409        }
     11410        if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
     11411            /* $[{[#]]NAME[}] */
    1023611412            do {
    1023711413                STPUTC(c, out);
    1023811414                c = pgetc();
    10239             } while (c > PEOA_OR_PEOF && is_in_name(c));
     11415            } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
    1024011416        } else if (isdigit(c)) {
     11417            /* $[{[#]]NUM[}] */
    1024111418            do {
    1024211419                STPUTC(c, out);
     
    1024411421            } while (isdigit(c));
    1024511422        } else if (is_special(c)) {
     11423            /* $[{[#]]<specialchar>[}] */
    1024611424            USTPUTC(c, out);
    1024711425            c = pgetc();
    10248         } else
    10249  badsub:        raise_error_syntax("Bad substitution");
     11426        } else {
     11427 badsub:
     11428            raise_error_syntax("bad substitution");
     11429        }
     11430        if (c != '}' && subtype == VSLENGTH) {
     11431            /* ${#VAR didn't end with } */
     11432            goto badsub;
     11433        }
    1025011434
    1025111435        STPUTC('=', out);
    1025211436        flags = 0;
    1025311437        if (subtype == 0) {
     11438            /* ${VAR...} but not $VAR or ${#VAR} */
     11439            /* c == first char after VAR */
    1025411440            switch (c) {
    1025511441            case ':':
     11442                c = pgetc();
     11443#if ENABLE_ASH_BASH_COMPAT
     11444                if (c == ':' || c == '$' || isdigit(c)) {
     11445//TODO: support more general format ${v:EXPR:EXPR},
     11446// where EXPR follows $(()) rules
     11447                    subtype = VSSUBSTR;
     11448                    pungetc();
     11449                    break; /* "goto do_pungetc" is bigger (!) */
     11450                }
     11451#endif
    1025611452                flags = VSNUL;
    10257                 c = pgetc();
    1025811453                /*FALLTHROUGH*/
    10259             default:
    10260                 p = strchr(types, c);
     11454            default: {
     11455                static const char types[] ALIGN1 = "}-+?=";
     11456                const char *p = strchr(types, c);
    1026111457                if (p == NULL)
    1026211458                    goto badsub;
    1026311459                subtype = p - types + VSNORMAL;
    1026411460                break;
     11461            }
    1026511462            case '%':
    10266             case '#':
    10267                 {
    10268                     int cc = c;
    10269                     subtype = c == '#' ? VSTRIMLEFT :
    10270                                          VSTRIMRIGHT;
    10271                     c = pgetc();
    10272                     if (c == cc)
    10273                         subtype++;
    10274                     else
    10275                         pungetc();
    10276                     break;
    10277                 }
     11463            case '#': {
     11464                int cc = c;
     11465                subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
     11466                c = pgetc();
     11467                if (c != cc)
     11468                    goto do_pungetc;
     11469                subtype++;
     11470                break;
     11471            }
     11472#if ENABLE_ASH_BASH_COMPAT
     11473            case '/':
     11474                /* ${v/[/]pattern/repl} */
     11475//TODO: encode pattern and repl separately.
     11476// Currently ${v/$var_with_slash/repl} is horribly broken
     11477                subtype = VSREPLACE;
     11478                c = pgetc();
     11479                if (c != '/')
     11480                    goto do_pungetc;
     11481                subtype++; /* VSREPLACEALL */
     11482                break;
     11483#endif
    1027811484            }
    1027911485        } else {
     11486 do_pungetc:
    1028011487            pungetc();
    1028111488        }
    1028211489        if (dblquote || arinest)
    1028311490            flags |= VSQUOTE;
    10284         *((char *)stackblock() + typeloc) = subtype | flags;
     11491        ((unsigned char *)stackblock())[typeloc] = subtype | flags;
    1028511492        if (subtype != VSNORMAL) {
    1028611493            varnest++;
     
    1030111508parsebackq: {
    1030211509    struct nodelist **nlpp;
    10303     int savepbq;
     11510    smallint savepbq;
    1030411511    union node *n;
    1030511512    char *volatile str;
     
    1030711514    struct jmploc *volatile savehandler;
    1030811515    size_t savelen;
    10309     int saveprompt = 0;
     11516    smallint saveprompt = 0;
     11517
    1031011518#ifdef __GNUC__
    1031111519    (void) &saveprompt;
    1031211520#endif
    10313 
    1031411521    savepbq = parsebackquote;
    1031511522    if (setjmp(jmploc.loc)) {
    10316         if (str)
    10317             free(str);
     11523        free(str);
    1031811524        parsebackquote = 0;
    1031911525        exception_handler = savehandler;
     
    1033511541           reread it as input, interpreting it normally.  */
    1033611542        char *pout;
    10337         int pc;
    1033811543        size_t psavelen;
    1033911544        char *pstr;
    1034011545
    10341 
    1034211546        STARTSTACKSTR(pout);
    1034311547        for (;;) {
    10344             if (needprompt) {
    10345                 setprompt(2);
    10346             }
     11548            int pc;
     11549
     11550            setprompt_if(needprompt, 2);
    1034711551            pc = pgetc();
    1034811552            switch (pc) {
     
    1035311557                pc = pgetc();
    1035411558                if (pc == '\n') {
    10355                     plinno++;
    10356                     if (doprompt)
    10357                         setprompt(2);
     11559                    g_parsefile->linno++;
     11560                    setprompt_if(doprompt, 2);
    1035811561                    /*
    1035911562                     * If eating a newline, avoid putting
     
    1036511568                }
    1036611569                if (pc != '\\' && pc != '`' && pc != '$'
    10367                  && (!dblquote || pc != '"'))
     11570                 && (!dblquote || pc != '"')
     11571                ) {
    1036811572                    STPUTC('\\', pout);
    10369                 if (pc > PEOA_OR_PEOF) {
     11573                }
     11574                if (pc <= 255 /* not PEOA or PEOF */) {
    1037011575                    break;
    1037111576                }
     
    1037311578
    1037411579            case PEOF:
    10375 #if ENABLE_ASH_ALIAS
    10376             case PEOA:
    10377 #endif
    10378                 startlinno = plinno;
     11580            IF_ASH_ALIAS(case PEOA:)
     11581                startlinno = g_parsefile->linno;
    1037911582                raise_error_syntax("EOF in backquote substitution");
    1038011583
    1038111584            case '\n':
    10382                 plinno++;
     11585                g_parsefile->linno++;
    1038311586                needprompt = doprompt;
    1038411587                break;
     
    1040011603    while (*nlpp)
    1040111604        nlpp = &(*nlpp)->next;
    10402     *nlpp = stalloc(sizeof(**nlpp));
    10403     (*nlpp)->next = NULL;
     11605    *nlpp = stzalloc(sizeof(**nlpp));
     11606    /* (*nlpp)->next = NULL; - stzalloc did it */
    1040411607    parsebackquote = oldstyle;
    1040511608
     
    1044711650}
    1044811651
    10449 #if ENABLE_ASH_MATH_SUPPORT
     11652#if ENABLE_SH_MATH_SUPPORT
    1045011653/*
    1045111654 * Parse an arithmetic expansion (indicate start of one and set state)
     
    1049411697/* singles must be first! */
    1049511698static const char xxreadtoken_chars[7] ALIGN1 = {
    10496     '\n', '(', ')', '&', '|', ';', 0
     11699    '\n', '(', ')', /* singles */
     11700    '&', '|', ';',  /* doubles */
     11701    0
    1049711702};
     11703
     11704#define xxreadtoken_singles 3
     11705#define xxreadtoken_doubles 3
    1049811706
    1049911707static const char xxreadtoken_tokens[] ALIGN1 = {
     
    1050411712};
    1050511713
    10506 #define xxreadtoken_doubles \
    10507     (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
    10508 #define xxreadtoken_singles \
    10509     (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
    10510 
    1051111714static int
    1051211715xxreadtoken(void)
     
    1051811721        return lasttoken;
    1051911722    }
    10520     if (needprompt) {
    10521         setprompt(2);
    10522     }
    10523     startlinno = plinno;
     11723    setprompt_if(needprompt, 2);
     11724    startlinno = g_parsefile->linno;
    1052411725    for (;;) {                      /* until token or start of word found */
    10525         c = pgetc_macro();
    10526 
    10527         if ((c != ' ') && (c != '\t')
    10528 #if ENABLE_ASH_ALIAS
    10529          && (c != PEOA)
    10530 #endif
    10531         ) {
    10532             if (c == '#') {
    10533                 while ((c = pgetc()) != '\n' && c != PEOF);
     11726        c = pgetc_fast();
     11727        if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
     11728            continue;
     11729
     11730        if (c == '#') {
     11731            while ((c = pgetc()) != '\n' && c != PEOF)
     11732                continue;
     11733            pungetc();
     11734        } else if (c == '\\') {
     11735            if (pgetc() != '\n') {
    1053411736                pungetc();
    10535             } else if (c == '\\') {
    10536                 if (pgetc() != '\n') {
    10537                     pungetc();
    10538                     goto READTOKEN1;
     11737                break; /* return readtoken1(...) */
     11738            }
     11739            startlinno = ++g_parsefile->linno;
     11740            setprompt_if(doprompt, 2);
     11741        } else {
     11742            const char *p;
     11743
     11744            p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
     11745            if (c != PEOF) {
     11746                if (c == '\n') {
     11747                    g_parsefile->linno++;
     11748                    needprompt = doprompt;
    1053911749                }
    10540                 startlinno = ++plinno;
    10541                 if (doprompt)
    10542                     setprompt(2);
    10543             } else {
    10544                 const char *p
    10545                     = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
    10546 
    10547                 if (c != PEOF) {
    10548                     if (c == '\n') {
    10549                         plinno++;
    10550                         needprompt = doprompt;
    10551                     }
    10552 
    10553                     p = strchr(xxreadtoken_chars, c);
    10554                     if (p == NULL) {
    10555  READTOKEN1:
    10556                         return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
    10557                     }
    10558 
    10559                     if (p - xxreadtoken_chars >= xxreadtoken_singles) {
    10560                         if (pgetc() == *p) {    /* double occurrence? */
    10561                             p += xxreadtoken_doubles + 1;
    10562                         } else {
    10563                             pungetc();
    10564                         }
     11750
     11751                p = strchr(xxreadtoken_chars, c);
     11752                if (p == NULL)
     11753                    break; /* return readtoken1(...) */
     11754
     11755                if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
     11756                    int cc = pgetc();
     11757                    if (cc == c) {    /* double occurrence? */
     11758                        p += xxreadtoken_doubles + 1;
     11759                    } else {
     11760                        pungetc();
     11761#if ENABLE_ASH_BASH_COMPAT
     11762                        if (c == '&' && cc == '>') /* &> */
     11763                            break; /* return readtoken1(...) */
     11764#endif
    1056511765                    }
    1056611766                }
    10567                 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
    1056811767            }
    10569         }
    10570     } /* for */
    10571 }
    10572 #else
     11768            lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
     11769            return lasttoken;
     11770        }
     11771    } /* for (;;) */
     11772
     11773    return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
     11774}
     11775#else /* old xxreadtoken */
    1057311776#define RETURN(token)   return lasttoken = token
    1057411777static int
     
    1058111784        return lasttoken;
    1058211785    }
    10583     if (needprompt) {
    10584         setprompt(2);
    10585     }
    10586     startlinno = plinno;
     11786    setprompt_if(needprompt, 2);
     11787    startlinno = g_parsefile->linno;
    1058711788    for (;;) {      /* until token or start of word found */
    10588         c = pgetc_macro();
     11789        c = pgetc_fast();
    1058911790        switch (c) {
    1059011791        case ' ': case '\t':
    10591 #if ENABLE_ASH_ALIAS
    10592         case PEOA:
    10593 #endif
     11792        IF_ASH_ALIAS(case PEOA:)
    1059411793            continue;
    1059511794        case '#':
    10596             while ((c = pgetc()) != '\n' && c != PEOF);
     11795            while ((c = pgetc()) != '\n' && c != PEOF)
     11796                continue;
    1059711797            pungetc();
    1059811798            continue;
    1059911799        case '\\':
    1060011800            if (pgetc() == '\n') {
    10601                 startlinno = ++plinno;
    10602                 if (doprompt)
    10603                     setprompt(2);
     11801                startlinno = ++g_parsefile->linno;
     11802                setprompt_if(doprompt, 2);
    1060411803                continue;
    1060511804            }
     
    1060711806            goto breakloop;
    1060811807        case '\n':
    10609             plinno++;
     11808            g_parsefile->linno++;
    1061011809            needprompt = doprompt;
    1061111810            RETURN(TNL);
     
    1063911838#undef RETURN
    1064011839}
    10641 #endif /* NEW_xxreadtoken */
     11840#endif /* old xxreadtoken */
    1064211841
    1064311842static int
     
    1064611845    int t;
    1064711846#if DEBUG
    10648     int alreadyseen = tokpushback;
     11847    smallint alreadyseen = tokpushback;
    1064911848#endif
    1065011849
     
    1067811877        if (pp) {
    1067911878            lasttoken = t = pp - tokname_array;
    10680             TRACE(("keyword %s recognized\n", tokname(t)));
     11879            TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
    1068111880            goto out;
    1068211881        }
     
    1069911898#if DEBUG
    1070011899    if (!alreadyseen)
    10701         TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
     11900        TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
    1070211901    else
    10703         TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
     11902        TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
    1070411903#endif
    1070511904    return t;
     
    1071211911
    1071311912    t = readtoken();
    10714     tokpushback++;
     11913    tokpushback = 1;
    1071511914    return tokname_array[t][0];
    1071611915}
    1071711916
    1071811917/*
    10719  * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
    10720  * valid parse tree indicating a blank line.)
     11918 * Read and parse a command.  Returns NODE_EOF on end of file.
     11919 * (NULL is a valid parse tree indicating a blank line.)
    1072111920 */
    1072211921static union node *
     
    1072711926    tokpushback = 0;
    1072811927    doprompt = interact;
    10729     if (doprompt)
    10730         setprompt(doprompt);
     11928    setprompt_if(doprompt, doprompt);
    1073111929    needprompt = 0;
    1073211930    t = readtoken();
    1073311931    if (t == TEOF)
    10734         return NEOF;
     11932        return NODE_EOF;
    1073511933    if (t == TNL)
    1073611934        return NULL;
    10737     tokpushback++;
     11935    tokpushback = 1;
    1073811936    return list(1);
    1073911937}
     
    1074911947
    1075011948    here = heredoclist;
    10751     heredoclist = 0;
     11949    heredoclist = NULL;
    1075211950
    1075311951    while (here) {
    10754         if (needprompt) {
    10755             setprompt(2);
    10756         }
    10757         readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
     11952        setprompt_if(needprompt, 2);
     11953        readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
    1075811954                here->eofmark, here->striptabs);
    10759         n = stalloc(sizeof(struct narg));
     11955        n = stzalloc(sizeof(struct narg));
    1076011956        n->narg.type = NARG;
    10761         n->narg.next = NULL;
     11957        /*n->narg.next = NULL; - stzalloc did it */
    1076211958        n->narg.text = wordtext;
    1076311959        n->narg.backquote = backquotelist;
     
    1077711973    union node n;
    1077811974
    10779     /* XXX Fix (char *) cast. */
     11975    /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
     11976     * and token processing _can_ alter it (delete NULs etc). */
    1078011977    setinputstring((char *)ps);
    10781     readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
     11978    readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
    1078211979    popfile();
    1078311980
     
    1080612003
    1080712004    skip = 0;
    10808     while ((n = parsecmd(0)) != NEOF) {
     12005    while ((n = parsecmd(0)) != NODE_EOF) {
    1080912006        evaltree(n, 0);
    1081012007        popstackmark(&smark);
     
    1082312020 * The eval command.
    1082412021 */
    10825 static int
    10826 evalcmd(int argc, char **argv)
     12022static int FAST_FUNC
     12023evalcmd(int argc UNUSED_PARAM, char **argv)
    1082712024{
    1082812025    char *p;
    1082912026    char *concat;
    10830     char **ap;
    10831 
    10832     if (argc > 1) {
     12027
     12028    if (argv[1]) {
    1083312029        p = argv[1];
    10834         if (argc > 2) {
     12030        argv += 2;
     12031        if (argv[0]) {
    1083512032            STARTSTACKSTR(concat);
    10836             ap = argv + 2;
    1083712033            for (;;) {
    1083812034                concat = stack_putstr(p, concat);
    10839                 p = *ap++;
     12035                p = *argv++;
    1084012036                if (p == NULL)
    1084112037                    break;
     
    1084612042        }
    1084712043        evalstring(p, ~SKIPEVAL);
    10848 
    1084912044    }
    1085012045    return exitstatus;
     
    1085212047
    1085312048/*
    10854  * Read and execute commands.  "Top" is nonzero for the top level command
    10855  * loop; it turns on prompting if the shell is interactive.
     12049 * Read and execute commands.
     12050 * "Top" is nonzero for the top level command loop;
     12051 * it turns on prompting if the shell is interactive.
    1085612052 */
    1085712053static int
     
    1086912065        setstackmark(&smark);
    1087012066#if JOBS
    10871         if (jobctl)
     12067        if (doing_jobctl)
    1087212068            showjobs(stderr, SHOW_CHANGED);
    1087312069#endif
     
    1088012076        }
    1088112077        n = parsecmd(inter);
    10882         /* showtree(n); DEBUG */
    10883         if (n == NEOF) {
     12078#if DEBUG
     12079        if (DEBUG > 2 && debug && (n != NODE_EOF))
     12080            showtree(n);
     12081#endif
     12082        if (n == NODE_EOF) {
    1088412083            if (!top || numeof >= 50)
    1088512084                break;
     
    1092212121        return name;
    1092312122
    10924     while ((fullname = padvance(&path, name)) != NULL) {
     12123    /* IIRC standards do not say whether . is to be searched.
     12124     * And it is even smaller this way, making it unconditional for now:
     12125     */
     12126    if (1) { /* ENABLE_ASH_BASH_COMPAT */
     12127        fullname = name;
     12128        goto try_cur_dir;
     12129    }
     12130
     12131    while ((fullname = path_advance(&path, name)) != NULL) {
     12132 try_cur_dir:
    1092512133        if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
    1092612134            /*
     
    1093012138            return fullname;
    1093112139        }
    10932         stunalloc(fullname);
     12140        if (fullname != name)
     12141            stunalloc(fullname);
    1093312142    }
    1093412143
     
    1093812147}
    1093912148
    10940 static int
     12149static int FAST_FUNC
    1094112150dotcmd(int argc, char **argv)
    1094212151{
     12152    char *fullname;
    1094312153    struct strlist *sp;
    1094412154    volatile struct shparam saveparam;
    10945     int status = 0;
    1094612155
    1094712156    for (sp = cmdenviron; sp; sp = sp->next)
    1094812157        setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
    1094912158
    10950     if (argc >= 2) {        /* That's what SVR2 does */
    10951         char *fullname;
    10952 
    10953         fullname = find_dot_file(argv[1]);
    10954 
    10955         if (argc > 2) {
    10956             saveparam = shellparam;
    10957             shellparam.malloc = 0;
    10958             shellparam.nparam = argc - 2;
    10959             shellparam.p = argv + 2;
    10960         };
    10961 
    10962         setinputfile(fullname, INPUT_PUSH_FILE);
    10963         commandname = fullname;
    10964         cmdloop(0);
    10965         popfile();
    10966 
    10967         if (argc > 2) {
    10968             freeparam(&shellparam);
    10969             shellparam = saveparam;
    10970         };
    10971         status = exitstatus;
    10972     }
    10973     return status;
    10974 }
    10975 
    10976 static int
    10977 exitcmd(int argc, char **argv)
     12159    if (!argv[1]) {
     12160        /* bash says: "bash: .: filename argument required" */
     12161        return 2; /* bash compat */
     12162    }
     12163
     12164    /* "false; . empty_file; echo $?" should print 0, not 1: */
     12165    exitstatus = 0;
     12166
     12167    fullname = find_dot_file(argv[1]);
     12168
     12169    argv += 2;
     12170    argc -= 2;
     12171    if (argc) { /* argc > 0, argv[0] != NULL */
     12172        saveparam = shellparam;
     12173        shellparam.malloced = 0;
     12174        shellparam.nparam = argc;
     12175        shellparam.p = argv;
     12176    };
     12177
     12178    setinputfile(fullname, INPUT_PUSH_FILE);
     12179    commandname = fullname;
     12180    cmdloop(0);
     12181    popfile();
     12182
     12183    if (argc) {
     12184        freeparam(&shellparam);
     12185        shellparam = saveparam;
     12186    };
     12187
     12188    return exitstatus;
     12189}
     12190
     12191static int FAST_FUNC
     12192exitcmd(int argc UNUSED_PARAM, char **argv)
    1097812193{
    1097912194    if (stoppedjobs())
    1098012195        return 0;
    10981     if (argc > 1)
     12196    if (argv[1])
    1098212197        exitstatus = number(argv[1]);
    1098312198    raise_exception(EXEXIT);
    1098412199    /* NOTREACHED */
    1098512200}
    10986 
    10987 #if ENABLE_ASH_BUILTIN_ECHO
    10988 static int
    10989 echocmd(int argc, char **argv)
    10990 {
    10991     return bb_echo(argv);
    10992 }
    10993 #endif
    10994 
    10995 #if ENABLE_ASH_BUILTIN_TEST
    10996 static int
    10997 testcmd(int argc, char **argv)
    10998 {
    10999     return test_main(argc, argv);
    11000 }
    11001 #endif
    1100212201
    1100312202/*
     
    1109912298
    1110012299#if ENABLE_FEATURE_SH_STANDALONE
    11101     if (find_applet_by_name(name)) {
    11102         entry->cmdtype = CMDNORMAL;
    11103         entry->u.index = -1;
    11104         return;
     12300    {
     12301        int applet_no = find_applet_by_name(name);
     12302        if (applet_no >= 0) {
     12303            entry->cmdtype = CMDNORMAL;
     12304            entry->u.index = -2 - applet_no;
     12305            return;
     12306        }
    1110512307    }
    1110612308#endif
     
    1111812320    idx = -1;
    1111912321 loop:
    11120     while ((fullname = padvance(&path, name)) != NULL) {
     12322    while ((fullname = path_advance(&path, name)) != NULL) {
    1112112323        stunalloc(fullname);
    1112212324        /* NB: code below will still use fullname
     
    1112812330                    goto builtin_success;
    1112912331                continue;
    11130             } else if (!(act & DO_NOFUNC)
    11131              && prefix(pathopt, "func")) {
    11132                 /* handled below */
    11133             } else {
    11134                 /* ignore unimplemented options */
     12332            }
     12333            if ((act & DO_NOFUNC)
     12334             || !prefix(pathopt, "func")
     12335            ) {     /* ignore unimplemented options */
    1113512336                continue;
    1113612337            }
     
    1121212413 * The trap builtin.
    1121312414 */
    11214 static int
    11215 trapcmd(int argc, char **argv)
     12415static int FAST_FUNC
     12416trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    1121612417{
    1121712418    char *action;
    1121812419    char **ap;
    11219     int signo;
     12420    int signo, exitcode;
    1122012421
    1122112422    nextopt(nullstr);
     
    1122312424    if (!*ap) {
    1122412425        for (signo = 0; signo < NSIG; signo++) {
    11225             if (trap[signo] != NULL) {
    11226                 const char *sn;
    11227 
    11228                 sn = get_signame(signo);
     12426            char *tr = trap_ptr[signo];
     12427            if (tr) {
     12428                /* note: bash adds "SIG", but only if invoked
     12429                 * as "bash". If called as "sh", or if set -o posix,
     12430                 * then it prints short signal names.
     12431                 * We are printing short names: */
    1122912432                out1fmt("trap -- %s %s\n",
    11230                     single_quote(trap[signo]), sn);
     12433                        single_quote(tr),
     12434                        get_signame(signo));
     12435        /* trap_ptr != trap only if we are in special-cased `trap` code.
     12436         * In this case, we will exit very soon, no need to free(). */
     12437                /* if (trap_ptr != trap && tp[0]) */
     12438                /*  free(tr); */
    1123112439            }
    1123212440        }
     12441        /*
     12442        if (trap_ptr != trap) {
     12443            free(trap_ptr);
     12444            trap_ptr = trap;
     12445        }
     12446        */
    1123312447        return 0;
    1123412448    }
    11235     if (!ap[1])
    11236         action = NULL;
    11237     else
     12449
     12450    action = NULL;
     12451    if (ap[1])
    1123812452        action = *ap++;
     12453    exitcode = 0;
    1123912454    while (*ap) {
    1124012455        signo = get_signum(*ap);
    11241         if (signo < 0)
    11242             ash_msg_and_raise_error("%s: bad trap", *ap);
     12456        if (signo < 0) {
     12457            /* Mimic bash message exactly */
     12458            ash_msg("%s: invalid signal specification", *ap);
     12459            exitcode = 1;
     12460            goto next;
     12461        }
    1124312462        INT_OFF;
    1124412463        if (action) {
     
    1124812467                action = ckstrdup(action);
    1124912468        }
    11250         if (trap[signo])
    11251             free(trap[signo]);
     12469        free(trap[signo]);
     12470        if (action)
     12471            may_have_traps = 1;
    1125212472        trap[signo] = action;
    1125312473        if (signo != 0)
    1125412474            setsignal(signo);
    1125512475        INT_ON;
     12476 next:
    1125612477        ap++;
    1125712478    }
    11258     return 0;
     12479    return exitcode;
    1125912480}
    1126012481
     
    1126612487 * Lists available builtins
    1126712488 */
    11268 static int
    11269 helpcmd(int argc, char **argv)
    11270 {
    11271     int col, i;
    11272 
    11273     out1fmt("\nBuilt-in commands:\n-------------------\n");
     12489static int FAST_FUNC
     12490helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
     12491{
     12492    unsigned col;
     12493    unsigned i;
     12494
     12495    out1fmt(
     12496        "Built-in commands:\n"
     12497        "------------------\n");
    1127412498    for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
    1127512499        col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
     
    1128112505    }
    1128212506#if ENABLE_FEATURE_SH_STANDALONE
    11283     for (i = 0; i < NUM_APPLETS; i++) {
    11284         col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
    11285         if (col > 60) {
    11286             out1fmt("\n");
    11287             col = 0;
     12507    {
     12508        const char *a = applet_names;
     12509        while (*a) {
     12510            col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
     12511            if (col > 60) {
     12512                out1fmt("\n");
     12513                col = 0;
     12514            }
     12515            a += strlen(a) + 1;
    1128812516        }
    1128912517    }
     
    1129712525 * The export and readonly commands.
    1129812526 */
    11299 static int
    11300 exportcmd(int argc, char **argv)
     12527static int FAST_FUNC
     12528exportcmd(int argc UNUSED_PARAM, char **argv)
    1130112529{
    1130212530    struct var *vp;
     
    1130412532    const char *p;
    1130512533    char **aptr;
    11306     int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
     12534    int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
    1130712535
    1130812536    if (nextopt("p") != 'p') {
     
    1133912567
    1134012568    cmdp = cmdlookup(name, 0);
    11341     if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
     12569    if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
    1134212570        delete_cmd_entry();
    1134312571}
     
    1134812576 * with the same name.
    1134912577 */
    11350 static int
    11351 unsetcmd(int argc, char **argv)
     12578static int FAST_FUNC
     12579unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
    1135212580{
    1135312581    char **ap;
     
    1135612584    int ret = 0;
    1135712585
    11358     while ((i = nextopt("vf")) != '\0') {
     12586    while ((i = nextopt("vf")) != 0) {
    1135912587        flag = i;
    1136012588    }
     
    1137212600    return ret & 1;
    1137312601}
    11374 
    11375 
    11376 /*      setmode.c      */
    11377 
    11378 #include <sys/times.h>
    1137912602
    1138012603static const unsigned char timescmd_str[] ALIGN1 = {
     
    1138512608    0
    1138612609};
    11387 
    11388 static int
    11389 timescmd(int ac, char **av)
    11390 {
    11391     long clk_tck, s, t;
     12610static int FAST_FUNC
     12611timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
     12612{
     12613    unsigned long clk_tck, s, t;
    1139212614    const unsigned char *p;
    1139312615    struct tms buf;
     
    1140012622        t = *(clock_t *)(((char *) &buf) + p[1]);
    1140112623        s = t / clk_tck;
    11402         out1fmt("%ldm%ld.%.3lds%c",
    11403             s/60, s%60,
    11404             ((t - s * clk_tck) * 1000) / clk_tck,
     12624        t = t % clk_tck;
     12625        out1fmt("%lum%lu.%03lus%c",
     12626            s / 60, s % 60,
     12627            (t * 1000) / clk_tck,
    1140512628            p[0]);
    11406     } while (*(p += 2));
     12629        p += 2;
     12630    } while (*p);
    1140712631
    1140812632    return 0;
    1140912633}
    1141012634
    11411 #if ENABLE_ASH_MATH_SUPPORT
    11412 static arith_t
    11413 dash_arith(const char *s)
    11414 {
    11415     arith_t result;
    11416     int errcode = 0;
    11417 
    11418     INT_OFF;
    11419     result = arith(s, &errcode);
    11420     if (errcode < 0) {
    11421         if (errcode == -3)
    11422             ash_msg_and_raise_error("exponent less than 0");
    11423         if (errcode == -2)
    11424             ash_msg_and_raise_error("divide by zero");
    11425         if (errcode == -5)
    11426             ash_msg_and_raise_error("expression recursion loop detected");
    11427         raise_error_syntax(s);
    11428     }
    11429     INT_ON;
    11430 
    11431     return result;
    11432 }
    11433 
    11434 /*
    11435  *  The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
    11436  *  Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
     12635#if ENABLE_SH_MATH_SUPPORT
     12636/*
     12637 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
     12638 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
    1143712639 *
    11438  *  Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
    11439  */
    11440 static int
    11441 letcmd(int argc, char **argv)
    11442 {
    11443     char **ap;
    11444     arith_t i = 0;
    11445 
    11446     ap = argv + 1;
    11447     if (!*ap)
     12640 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
     12641 */
     12642static int FAST_FUNC
     12643letcmd(int argc UNUSED_PARAM, char **argv)
     12644{
     12645    arith_t i;
     12646
     12647    argv++;
     12648    if (!*argv)
    1144812649        ash_msg_and_raise_error("expression expected");
    11449     for (ap = argv + 1; *ap; ap++) {
    11450         i = dash_arith(*ap);
    11451     }
     12650    do {
     12651        i = ash_arith(*argv);
     12652    } while (*++argv);
    1145212653
    1145312654    return !i;
    1145412655}
    11455 #endif /* ASH_MATH_SUPPORT */
    11456 
    11457 
    11458 /* ============ miscbltin.c
    11459  *
    11460  * Miscellaneous builtins.
    11461  */
    11462 
    11463 #undef rflag
    11464 
    11465 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
    11466 typedef enum __rlimit_resource rlim_t;
    11467 #endif
    11468 
    11469 /*
    11470  * The read builtin.  The -e option causes backslashes to escape the
    11471  * following character.
    11472  *
     12656#endif
     12657
     12658/*
     12659 * The read builtin. Options:
     12660 *      -r              Do not interpret '\' specially
     12661 *      -s              Turn off echo (tty only)
     12662 *      -n NCHARS       Read NCHARS max
     12663 *      -p PROMPT       Display PROMPT on stderr (if input is from tty)
     12664 *      -t SECONDS      Timeout after SECONDS (tty or pipe only)
     12665 *      -u FD           Read from given FD instead of fd 0
    1147312666 * This uses unbuffered input, which may be avoidable in some cases.
    11474  */
    11475 static int
    11476 readcmd(int argc, char **argv)
    11477 {
    11478     char **ap;
    11479     int backslash;
    11480     char c;
    11481     int rflag;
    11482     char *prompt;
    11483     const char *ifs;
    11484     char *p;
    11485     int startword;
    11486     int status;
     12667 * TODO: bash also has:
     12668 *      -a ARRAY        Read into array[0],[1],etc
     12669 *      -d DELIM        End on DELIM char, not newline
     12670 *      -e              Use line editing (tty only)
     12671 */
     12672static int FAST_FUNC
     12673readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
     12674{
     12675    char *opt_n = NULL;
     12676    char *opt_p = NULL;
     12677    char *opt_t = NULL;
     12678    char *opt_u = NULL;
     12679    int read_flags = 0;
     12680    const char *r;
    1148712681    int i;
    11488 #if ENABLE_ASH_READ_NCHARS
    11489     int nch_flag = 0;
    11490     int nchars = 0;
    11491     int silent = 0;
    11492     struct termios tty, old_tty;
    11493 #endif
    11494 #if ENABLE_ASH_READ_TIMEOUT
    11495     fd_set set;
    11496     struct timeval ts;
    11497 
    11498     ts.tv_sec = ts.tv_usec = 0;
    11499 #endif
    11500 
    11501     rflag = 0;
    11502     prompt = NULL;
    11503 #if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
    11504     while ((i = nextopt("p:rt:n:s")) != '\0')
    11505 #elif ENABLE_ASH_READ_NCHARS
    11506     while ((i = nextopt("p:rn:s")) != '\0')
    11507 #elif ENABLE_ASH_READ_TIMEOUT
    11508     while ((i = nextopt("p:rt:")) != '\0')
    11509 #else
    11510     while ((i = nextopt("p:r")) != '\0')
    11511 #endif
    11512     {
     12682
     12683    while ((i = nextopt("p:u:rt:n:s")) != '\0') {
    1151312684        switch (i) {
    1151412685        case 'p':
    11515             prompt = optionarg;
     12686            opt_p = optionarg;
    1151612687            break;
    11517 #if ENABLE_ASH_READ_NCHARS
    1151812688        case 'n':
    11519             nchars = strtol(optionarg, &p, 10);
    11520             if (*p)
    11521                 ash_msg_and_raise_error("invalid count");
    11522             nch_flag = (nchars > 0);
     12689            opt_n = optionarg;
    1152312690            break;
    1152412691        case 's':
    11525             silent = 1;
     12692            read_flags |= BUILTIN_READ_SILENT;
    1152612693            break;
    11527 #endif
    11528 #if ENABLE_ASH_READ_TIMEOUT
    1152912694        case 't':
    11530             ts.tv_sec = strtol(optionarg, &p, 10);
    11531             ts.tv_usec = 0;
    11532             if (*p == '.') {
    11533                 char *p2;
    11534                 if (*++p) {
    11535                     int scale;
    11536                     ts.tv_usec = strtol(p, &p2, 10);
    11537                     if (*p2)
    11538                         ash_msg_and_raise_error("invalid timeout");
    11539                     scale = p2 - p;
    11540                     /* normalize to usec */
    11541                     if (scale > 6)
    11542                         ash_msg_and_raise_error("invalid timeout");
    11543                     while (scale++ < 6)
    11544                         ts.tv_usec *= 10;
    11545                 }
    11546             } else if (*p) {
    11547                 ash_msg_and_raise_error("invalid timeout");
    11548             }
    11549             if ( ! ts.tv_sec && ! ts.tv_usec)
    11550                 ash_msg_and_raise_error("invalid timeout");
     12695            opt_t = optionarg;
    1155112696            break;
    11552 #endif
    1155312697        case 'r':
    11554             rflag = 1;
     12698            read_flags |= BUILTIN_READ_RAW;
     12699            break;
     12700        case 'u':
     12701            opt_u = optionarg;
    1155512702            break;
    1155612703        default:
     
    1155812705        }
    1155912706    }
    11560     if (prompt && isatty(0)) {
    11561         out2str(prompt);
    11562     }
    11563     ap = argptr;
    11564     if (*ap == NULL)
    11565         ash_msg_and_raise_error("arg count");
    11566     ifs = bltinlookup("IFS");
    11567     if (ifs == NULL)
    11568         ifs = defifs;
    11569 #if ENABLE_ASH_READ_NCHARS
    11570     if (nch_flag || silent) {
    11571         tcgetattr(0, &tty);
    11572         old_tty = tty;
    11573         if (nch_flag) {
    11574             tty.c_lflag &= ~ICANON;
    11575             tty.c_cc[VMIN] = nchars;
    11576         }
    11577         if (silent) {
    11578             tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
    11579 
    11580         }
    11581         tcsetattr(0, TCSANOW, &tty);
    11582     }
    11583 #endif
    11584 #if ENABLE_ASH_READ_TIMEOUT
    11585     if (ts.tv_sec || ts.tv_usec) {
    11586         FD_ZERO(&set);
    11587         FD_SET(0, &set);
    11588 
    11589         i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
    11590         if (!i) {
    11591 #if ENABLE_ASH_READ_NCHARS
    11592             if (nch_flag)
    11593                 tcsetattr(0, TCSANOW, &old_tty);
    11594 #endif
    11595             return 1;
    11596         }
    11597     }
    11598 #endif
    11599     status = 0;
    11600     startword = 1;
    11601     backslash = 0;
    11602     STARTSTACKSTR(p);
    11603 #if ENABLE_ASH_READ_NCHARS
    11604     while (!nch_flag || nchars--)
    11605 #else
    11606     for (;;)
    11607 #endif
    11608     {
    11609         if (read(0, &c, 1) != 1) {
    11610             status = 1;
    11611             break;
    11612         }
    11613         if (c == '\0')
    11614             continue;
    11615         if (backslash) {
    11616             backslash = 0;
    11617             if (c != '\n')
    11618                 goto put;
    11619             continue;
    11620         }
    11621         if (!rflag && c == '\\') {
    11622             backslash++;
    11623             continue;
    11624         }
    11625         if (c == '\n')
    11626             break;
    11627         if (startword && *ifs == ' ' && strchr(ifs, c)) {
    11628             continue;
    11629         }
    11630         startword = 0;
    11631         if (ap[1] != NULL && strchr(ifs, c) != NULL) {
    11632             STACKSTRNUL(p);
    11633             setvar(*ap, stackblock(), 0);
    11634             ap++;
    11635             startword = 1;
    11636             STARTSTACKSTR(p);
    11637         } else {
    11638  put:
    11639             STPUTC(c, p);
    11640         }
    11641     }
    11642 #if ENABLE_ASH_READ_NCHARS
    11643     if (nch_flag || silent)
    11644         tcsetattr(0, TCSANOW, &old_tty);
    11645 #endif
    11646 
    11647     STACKSTRNUL(p);
    11648     /* Remove trailing blanks */
    11649     while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
    11650         *p = '\0';
    11651     setvar(*ap, stackblock(), 0);
    11652     while (*++ap != NULL)
    11653         setvar(*ap, nullstr, 0);
    11654     return status;
    11655 }
    11656 
    11657 static int
    11658 umaskcmd(int argc, char **argv)
     12707
     12708    r = shell_builtin_read(setvar2,
     12709        argptr,
     12710        bltinlookup("IFS"), /* can be NULL */
     12711        read_flags,
     12712        opt_n,
     12713        opt_p,
     12714        opt_t,
     12715        opt_u
     12716    );
     12717
     12718    if ((uintptr_t)r > 1)
     12719        ash_msg_and_raise_error(r);
     12720
     12721    return (uintptr_t)r;
     12722}
     12723
     12724static int FAST_FUNC
     12725umaskcmd(int argc UNUSED_PARAM, char **argv)
    1165912726{
    1166012727    static const char permuser[3] ALIGN1 = "ugo";
     
    1166512732        S_IROTH, S_IWOTH, S_IXOTH
    1166612733    };
     12734
     12735    /* TODO: use bb_parse_mode() instead */
    1166712736
    1166812737    char *ap;
     
    1170812777            do {
    1170912778                if (*ap >= '8' || *ap < '0')
    11710                     ash_msg_and_raise_error(illnum, argv[1]);
     12779                    ash_msg_and_raise_error(msg_illnum, argv[1]);
    1171112780                mask = (mask << 3) + (*ap - '0');
    1171212781            } while (*++ap != '\0');
     
    1172312792}
    1172412793
    11725 /*
    11726  * ulimit builtin
    11727  *
    11728  * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
    11729  * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
    11730  * ash by J.T. Conklin.
    11731  *
    11732  * Public domain.
    11733  */
    11734 
    11735 struct limits {
    11736     const char *name;
    11737     int     cmd;
    11738     int     factor; /* multiply by to get rlim_{cur,max} values */
    11739     char    option;
    11740 };
    11741 
    11742 static const struct limits limits[] = {
    11743 #ifdef RLIMIT_CPU
    11744     { "time(seconds)",              RLIMIT_CPU,        1, 't' },
    11745 #endif
    11746 #ifdef RLIMIT_FSIZE
    11747     { "file(blocks)",               RLIMIT_FSIZE,    512, 'f' },
    11748 #endif
    11749 #ifdef RLIMIT_DATA
    11750     { "data(kbytes)",               RLIMIT_DATA,    1024, 'd' },
    11751 #endif
    11752 #ifdef RLIMIT_STACK
    11753     { "stack(kbytes)",              RLIMIT_STACK,   1024, 's' },
    11754 #endif
    11755 #ifdef  RLIMIT_CORE
    11756     { "coredump(blocks)",           RLIMIT_CORE,     512, 'c' },
    11757 #endif
    11758 #ifdef RLIMIT_RSS
    11759     { "memory(kbytes)",             RLIMIT_RSS,     1024, 'm' },
    11760 #endif
    11761 #ifdef RLIMIT_MEMLOCK
    11762     { "locked memory(kbytes)",      RLIMIT_MEMLOCK, 1024, 'l' },
    11763 #endif
    11764 #ifdef RLIMIT_NPROC
    11765     { "process",                    RLIMIT_NPROC,      1, 'p' },
    11766 #endif
    11767 #ifdef RLIMIT_NOFILE
    11768     { "nofiles",                    RLIMIT_NOFILE,     1, 'n' },
    11769 #endif
    11770 #ifdef RLIMIT_AS
    11771     { "vmemory(kbytes)",            RLIMIT_AS,      1024, 'v' },
    11772 #endif
    11773 #ifdef RLIMIT_LOCKS
    11774     { "locks",                      RLIMIT_LOCKS,      1, 'w' },
    11775 #endif
    11776     { NULL,                         0,                 0,  '\0' }
    11777 };
    11778 
    11779 enum limtype { SOFT = 0x1, HARD = 0x2 };
    11780 
    11781 static void
    11782 printlim(enum limtype how, const struct rlimit *limit,
    11783             const struct limits *l)
    11784 {
    11785     rlim_t val;
    11786 
    11787     val = limit->rlim_max;
    11788     if (how & SOFT)
    11789         val = limit->rlim_cur;
    11790 
    11791     if (val == RLIM_INFINITY)
    11792         out1fmt("unlimited\n");
    11793     else {
    11794         val /= l->factor;
    11795         out1fmt("%lld\n", (long long) val);
    11796     }
    11797 }
    11798 
    11799 static int
    11800 ulimitcmd(int argc, char **argv)
    11801 {
    11802     int c;
    11803     rlim_t val = 0;
    11804     enum limtype how = SOFT | HARD;
    11805     const struct limits *l;
    11806     int set, all = 0;
    11807     int optc, what;
    11808     struct rlimit limit;
    11809 
    11810     what = 'f';
    11811     while ((optc = nextopt("HSa"
    11812 #ifdef RLIMIT_CPU
    11813                 "t"
    11814 #endif
    11815 #ifdef RLIMIT_FSIZE
    11816                 "f"
    11817 #endif
    11818 #ifdef RLIMIT_DATA
    11819                 "d"
    11820 #endif
    11821 #ifdef RLIMIT_STACK
    11822                 "s"
    11823 #endif
    11824 #ifdef RLIMIT_CORE
    11825                 "c"
    11826 #endif
    11827 #ifdef RLIMIT_RSS
    11828                 "m"
    11829 #endif
    11830 #ifdef RLIMIT_MEMLOCK
    11831                 "l"
    11832 #endif
    11833 #ifdef RLIMIT_NPROC
    11834                 "p"
    11835 #endif
    11836 #ifdef RLIMIT_NOFILE
    11837                 "n"
    11838 #endif
    11839 #ifdef RLIMIT_AS
    11840                 "v"
    11841 #endif
    11842 #ifdef RLIMIT_LOCKS
    11843                 "w"
    11844 #endif
    11845                     )) != '\0')
    11846         switch (optc) {
    11847         case 'H':
    11848             how = HARD;
    11849             break;
    11850         case 'S':
    11851             how = SOFT;
    11852             break;
    11853         case 'a':
    11854             all = 1;
    11855             break;
    11856         default:
    11857             what = optc;
    11858         }
    11859 
    11860     for (l = limits; l->option != what; l++)
    11861         ;
    11862 
    11863     set = *argptr ? 1 : 0;
    11864     if (set) {
    11865         char *p = *argptr;
    11866 
    11867         if (all || argptr[1])
    11868             ash_msg_and_raise_error("too many arguments");
    11869         if (strncmp(p, "unlimited\n", 9) == 0)
    11870             val = RLIM_INFINITY;
    11871         else {
    11872             val = (rlim_t) 0;
    11873 
    11874             while ((c = *p++) >= '0' && c <= '9') {
    11875                 val = (val * 10) + (long)(c - '0');
    11876                 if (val < (rlim_t) 0)
    11877                     break;
    11878             }
    11879             if (c)
    11880                 ash_msg_and_raise_error("bad number");
    11881             val *= l->factor;
    11882         }
    11883     }
    11884     if (all) {
    11885         for (l = limits; l->name; l++) {
    11886             getrlimit(l->cmd, &limit);
    11887             out1fmt("%-20s ", l->name);
    11888             printlim(how, &limit, l);
    11889         }
    11890         return 0;
    11891     }
    11892 
    11893     getrlimit(l->cmd, &limit);
    11894     if (set) {
    11895         if (how & HARD)
    11896             limit.rlim_max = val;
    11897         if (how & SOFT)
    11898             limit.rlim_cur = val;
    11899         if (setrlimit(l->cmd, &limit) < 0)
    11900             ash_msg_and_raise_error("error setting limit (%m)");
    11901     } else {
    11902         printlim(how, &limit, l);
    11903     }
    11904     return 0;
    11905 }
    11906 
    11907 
    11908 /* ============ Math support */
    11909 
    11910 #if ENABLE_ASH_MATH_SUPPORT
    11911 
    11912 /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
    11913 
    11914    Permission is hereby granted, free of charge, to any person obtaining
    11915    a copy of this software and associated documentation files (the
    11916    "Software"), to deal in the Software without restriction, including
    11917    without limitation the rights to use, copy, modify, merge, publish,
    11918    distribute, sublicense, and/or sell copies of the Software, and to
    11919    permit persons to whom the Software is furnished to do so, subject to
    11920    the following conditions:
    11921 
    11922    The above copyright notice and this permission notice shall be
    11923    included in all copies or substantial portions of the Software.
    11924 
    11925    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    11926    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    11927    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    11928    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    11929    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    11930    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    11931    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    11932 */
    11933 
    11934 /* This is my infix parser/evaluator. It is optimized for size, intended
    11935  * as a replacement for yacc-based parsers. However, it may well be faster
    11936  * than a comparable parser written in yacc. The supported operators are
    11937  * listed in #defines below. Parens, order of operations, and error handling
    11938  * are supported. This code is thread safe. The exact expression format should
    11939  * be that which POSIX specifies for shells. */
    11940 
    11941 /* The code uses a simple two-stack algorithm. See
    11942  * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
    11943  * for a detailed explanation of the infix-to-postfix algorithm on which
    11944  * this is based (this code differs in that it applies operators immediately
    11945  * to the stack instead of adding them to a queue to end up with an
    11946  * expression). */
    11947 
    11948 /* To use the routine, call it with an expression string and error return
    11949  * pointer */
    11950 
    11951 /*
    11952  * Aug 24, 2001              Manuel Novoa III
    11953  *
    11954  * Reduced the generated code size by about 30% (i386) and fixed several bugs.
    11955  *
    11956  * 1) In arith_apply():
    11957  *    a) Cached values of *numptr and &(numptr[-1]).
    11958  *    b) Removed redundant test for zero denominator.
    11959  *
    11960  * 2) In arith():
    11961  *    a) Eliminated redundant code for processing operator tokens by moving
    11962  *       to a table-based implementation.  Also folded handling of parens
    11963  *       into the table.
    11964  *    b) Combined all 3 loops which called arith_apply to reduce generated
    11965  *       code size at the cost of speed.
    11966  *
    11967  * 3) The following expressions were treated as valid by the original code:
    11968  *       1()  ,    0!  ,    1 ( *3 )   .
    11969  *    These bugs have been fixed by internally enclosing the expression in
    11970  *    parens and then checking that all binary ops and right parens are
    11971  *    preceded by a valid expression (NUM_TOKEN).
    11972  *
    11973  * Note: It may be desirable to replace Aaron's test for whitespace with
    11974  * ctype's isspace() if it is used by another busybox applet or if additional
    11975  * whitespace chars should be considered.  Look below the "#include"s for a
    11976  * precompiler test.
    11977  */
    11978 
    11979 /*
    11980  * Aug 26, 2001              Manuel Novoa III
    11981  *
    11982  * Return 0 for null expressions.  Pointed out by Vladimir Oleynik.
    11983  *
    11984  * Merge in Aaron's comments previously posted to the busybox list,
    11985  * modified slightly to take account of my changes to the code.
    11986  *
    11987  */
    11988 
    11989 /*
    11990  *  (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
    11991  *
    11992  * - allow access to variable,
    11993  *   used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
    11994  * - realize assign syntax (VAR=expr, +=, *= etc)
    11995  * - realize exponentiation (** operator)
    11996  * - realize comma separated - expr, expr
    11997  * - realise ++expr --expr expr++ expr--
    11998  * - realise expr ? expr : expr (but, second expr calculate always)
    11999  * - allow hexadecimal and octal numbers
    12000  * - was restored loses XOR operator
    12001  * - remove one goto label, added three ;-)
    12002  * - protect $((num num)) as true zero expr (Manuel`s error)
    12003  * - always use special isspace(), see comment from bash ;-)
    12004  */
    12005 
    12006 #define arith_isspace(arithval) \
    12007     (arithval == ' ' || arithval == '\n' || arithval == '\t')
    12008 
    12009 typedef unsigned char operator;
    12010 
    12011 /* An operator's token id is a bit of a bitfield. The lower 5 bits are the
    12012  * precedence, and 3 high bits are an ID unique across operators of that
    12013  * precedence. The ID portion is so that multiple operators can have the
    12014  * same precedence, ensuring that the leftmost one is evaluated first.
    12015  * Consider * and /. */
    12016 
    12017 #define tok_decl(prec,id) (((id)<<5)|(prec))
    12018 #define PREC(op) ((op) & 0x1F)
    12019 
    12020 #define TOK_LPAREN tok_decl(0,0)
    12021 
    12022 #define TOK_COMMA tok_decl(1,0)
    12023 
    12024 #define TOK_ASSIGN tok_decl(2,0)
    12025 #define TOK_AND_ASSIGN tok_decl(2,1)
    12026 #define TOK_OR_ASSIGN tok_decl(2,2)
    12027 #define TOK_XOR_ASSIGN tok_decl(2,3)
    12028 #define TOK_PLUS_ASSIGN tok_decl(2,4)
    12029 #define TOK_MINUS_ASSIGN tok_decl(2,5)
    12030 #define TOK_LSHIFT_ASSIGN tok_decl(2,6)
    12031 #define TOK_RSHIFT_ASSIGN tok_decl(2,7)
    12032 
    12033 #define TOK_MUL_ASSIGN tok_decl(3,0)
    12034 #define TOK_DIV_ASSIGN tok_decl(3,1)
    12035 #define TOK_REM_ASSIGN tok_decl(3,2)
    12036 
    12037 /* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
    12038 #define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
    12039 
    12040 /* conditional is right associativity too */
    12041 #define TOK_CONDITIONAL tok_decl(4,0)
    12042 #define TOK_CONDITIONAL_SEP tok_decl(4,1)
    12043 
    12044 #define TOK_OR tok_decl(5,0)
    12045 
    12046 #define TOK_AND tok_decl(6,0)
    12047 
    12048 #define TOK_BOR tok_decl(7,0)
    12049 
    12050 #define TOK_BXOR tok_decl(8,0)
    12051 
    12052 #define TOK_BAND tok_decl(9,0)
    12053 
    12054 #define TOK_EQ tok_decl(10,0)
    12055 #define TOK_NE tok_decl(10,1)
    12056 
    12057 #define TOK_LT tok_decl(11,0)
    12058 #define TOK_GT tok_decl(11,1)
    12059 #define TOK_GE tok_decl(11,2)
    12060 #define TOK_LE tok_decl(11,3)
    12061 
    12062 #define TOK_LSHIFT tok_decl(12,0)
    12063 #define TOK_RSHIFT tok_decl(12,1)
    12064 
    12065 #define TOK_ADD tok_decl(13,0)
    12066 #define TOK_SUB tok_decl(13,1)
    12067 
    12068 #define TOK_MUL tok_decl(14,0)
    12069 #define TOK_DIV tok_decl(14,1)
    12070 #define TOK_REM tok_decl(14,2)
    12071 
    12072 /* exponent is right associativity */
    12073 #define TOK_EXPONENT tok_decl(15,1)
    12074 
    12075 /* For now unary operators. */
    12076 #define UNARYPREC 16
    12077 #define TOK_BNOT tok_decl(UNARYPREC,0)
    12078 #define TOK_NOT tok_decl(UNARYPREC,1)
    12079 
    12080 #define TOK_UMINUS tok_decl(UNARYPREC+1,0)
    12081 #define TOK_UPLUS tok_decl(UNARYPREC+1,1)
    12082 
    12083 #define PREC_PRE (UNARYPREC+2)
    12084 
    12085 #define TOK_PRE_INC tok_decl(PREC_PRE, 0)
    12086 #define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
    12087 
    12088 #define PREC_POST (UNARYPREC+3)
    12089 
    12090 #define TOK_POST_INC tok_decl(PREC_POST, 0)
    12091 #define TOK_POST_DEC tok_decl(PREC_POST, 1)
    12092 
    12093 #define SPEC_PREC (UNARYPREC+4)
    12094 
    12095 #define TOK_NUM tok_decl(SPEC_PREC, 0)
    12096 #define TOK_RPAREN tok_decl(SPEC_PREC, 1)
    12097 
    12098 #define NUMPTR (*numstackptr)
    12099 
    12100 static int
    12101 tok_have_assign(operator op)
    12102 {
    12103     operator prec = PREC(op);
    12104 
    12105     convert_prec_is_assing(prec);
    12106     return (prec == PREC(TOK_ASSIGN) ||
    12107             prec == PREC_PRE || prec == PREC_POST);
    12108 }
    12109 
    12110 static int
    12111 is_right_associativity(operator prec)
    12112 {
    12113     return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
    12114             || prec == PREC(TOK_CONDITIONAL));
    12115 }
    12116 
    12117 typedef struct ARITCH_VAR_NUM {
    12118     arith_t val;
    12119     arith_t contidional_second_val;
    12120     char contidional_second_val_initialized;
    12121     char *var;      /* if NULL then is regular number,
    12122                else is variable name */
    12123 } v_n_t;
    12124 
    12125 typedef struct CHK_VAR_RECURSIVE_LOOPED {
    12126     const char *var;
    12127     struct CHK_VAR_RECURSIVE_LOOPED *next;
    12128 } chk_var_recursive_looped_t;
    12129 
    12130 static chk_var_recursive_looped_t *prev_chk_var_recursive;
    12131 
    12132 static int
    12133 arith_lookup_val(v_n_t *t)
    12134 {
    12135     if (t->var) {
    12136         const char * p = lookupvar(t->var);
    12137 
    12138         if (p) {
    12139             int errcode;
    12140 
    12141             /* recursive try as expression */
    12142             chk_var_recursive_looped_t *cur;
    12143             chk_var_recursive_looped_t cur_save;
    12144 
    12145             for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
    12146                 if (strcmp(cur->var, t->var) == 0) {
    12147                     /* expression recursion loop detected */
    12148                     return -5;
    12149                 }
    12150             }
    12151             /* save current lookuped var name */
    12152             cur = prev_chk_var_recursive;
    12153             cur_save.var = t->var;
    12154             cur_save.next = cur;
    12155             prev_chk_var_recursive = &cur_save;
    12156 
    12157             t->val = arith (p, &errcode);
    12158             /* restore previous ptr after recursiving */
    12159             prev_chk_var_recursive = cur;
    12160             return errcode;
    12161         }
    12162         /* allow undefined var as 0 */
    12163         t->val = 0;
    12164     }
    12165     return 0;
    12166 }
    12167 
    12168 /* "applying" a token means performing it on the top elements on the integer
    12169  * stack. For a unary operator it will only change the top element, but a
    12170  * binary operator will pop two arguments and push a result */
    12171 static int
    12172 arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
    12173 {
    12174     v_n_t *numptr_m1;
    12175     arith_t numptr_val, rez;
    12176     int ret_arith_lookup_val;
    12177 
    12178     /* There is no operator that can work without arguments */
    12179     if (NUMPTR == numstack) goto err;
    12180     numptr_m1 = NUMPTR - 1;
    12181 
    12182     /* check operand is var with noninteger value */
    12183     ret_arith_lookup_val = arith_lookup_val(numptr_m1);
    12184     if (ret_arith_lookup_val)
    12185         return ret_arith_lookup_val;
    12186 
    12187     rez = numptr_m1->val;
    12188     if (op == TOK_UMINUS)
    12189         rez *= -1;
    12190     else if (op == TOK_NOT)
    12191         rez = !rez;
    12192     else if (op == TOK_BNOT)
    12193         rez = ~rez;
    12194     else if (op == TOK_POST_INC || op == TOK_PRE_INC)
    12195         rez++;
    12196     else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
    12197         rez--;
    12198     else if (op != TOK_UPLUS) {
    12199         /* Binary operators */
    12200 
    12201         /* check and binary operators need two arguments */
    12202         if (numptr_m1 == numstack) goto err;
    12203 
    12204         /* ... and they pop one */
    12205         --NUMPTR;
    12206         numptr_val = rez;
    12207         if (op == TOK_CONDITIONAL) {
    12208             if (! numptr_m1->contidional_second_val_initialized) {
    12209                 /* protect $((expr1 ? expr2)) without ": expr" */
    12210                 goto err;
    12211             }
    12212             rez = numptr_m1->contidional_second_val;
    12213         } else if (numptr_m1->contidional_second_val_initialized) {
    12214             /* protect $((expr1 : expr2)) without "expr ? " */
    12215             goto err;
    12216         }
    12217         numptr_m1 = NUMPTR - 1;
    12218         if (op != TOK_ASSIGN) {
    12219             /* check operand is var with noninteger value for not '=' */
    12220             ret_arith_lookup_val = arith_lookup_val(numptr_m1);
    12221             if (ret_arith_lookup_val)
    12222                 return ret_arith_lookup_val;
    12223         }
    12224         if (op == TOK_CONDITIONAL) {
    12225             numptr_m1->contidional_second_val = rez;
    12226         }
    12227         rez = numptr_m1->val;
    12228         if (op == TOK_BOR || op == TOK_OR_ASSIGN)
    12229             rez |= numptr_val;
    12230         else if (op == TOK_OR)
    12231             rez = numptr_val || rez;
    12232         else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
    12233             rez &= numptr_val;
    12234         else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
    12235             rez ^= numptr_val;
    12236         else if (op == TOK_AND)
    12237             rez = rez && numptr_val;
    12238         else if (op == TOK_EQ)
    12239             rez = (rez == numptr_val);
    12240         else if (op == TOK_NE)
    12241             rez = (rez != numptr_val);
    12242         else if (op == TOK_GE)
    12243             rez = (rez >= numptr_val);
    12244         else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
    12245             rez >>= numptr_val;
    12246         else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
    12247             rez <<= numptr_val;
    12248         else if (op == TOK_GT)
    12249             rez = (rez > numptr_val);
    12250         else if (op == TOK_LT)
    12251             rez = (rez < numptr_val);
    12252         else if (op == TOK_LE)
    12253             rez = (rez <= numptr_val);
    12254         else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
    12255             rez *= numptr_val;
    12256         else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
    12257             rez += numptr_val;
    12258         else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
    12259             rez -= numptr_val;
    12260         else if (op == TOK_ASSIGN || op == TOK_COMMA)
    12261             rez = numptr_val;
    12262         else if (op == TOK_CONDITIONAL_SEP) {
    12263             if (numptr_m1 == numstack) {
    12264                 /* protect $((expr : expr)) without "expr ? " */
    12265                 goto err;
    12266             }
    12267             numptr_m1->contidional_second_val_initialized = op;
    12268             numptr_m1->contidional_second_val = numptr_val;
    12269         } else if (op == TOK_CONDITIONAL) {
    12270             rez = rez ?
    12271                 numptr_val : numptr_m1->contidional_second_val;
    12272         } else if (op == TOK_EXPONENT) {
    12273             if (numptr_val < 0)
    12274                 return -3;      /* exponent less than 0 */
    12275             else {
    12276                 arith_t c = 1;
    12277 
    12278                 if (numptr_val)
    12279                     while (numptr_val--)
    12280                         c *= rez;
    12281                 rez = c;
    12282             }
    12283         } else if (numptr_val==0)          /* zero divisor check */
    12284             return -2;
    12285         else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
    12286             rez /= numptr_val;
    12287         else if (op == TOK_REM || op == TOK_REM_ASSIGN)
    12288             rez %= numptr_val;
    12289     }
    12290     if (tok_have_assign(op)) {
    12291         char buf[sizeof(arith_t_type)*3 + 2];
    12292 
    12293         if (numptr_m1->var == NULL) {
    12294             /* Hmm, 1=2 ? */
    12295             goto err;
    12296         }
    12297         /* save to shell variable */
    12298 #if ENABLE_ASH_MATH_SUPPORT_64
    12299         snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
    12300 #else
    12301         snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
    12302 #endif
    12303         setvar(numptr_m1->var, buf, 0);
    12304         /* after saving, make previous value for v++ or v-- */
    12305         if (op == TOK_POST_INC)
    12306             rez--;
    12307         else if (op == TOK_POST_DEC)
    12308             rez++;
    12309     }
    12310     numptr_m1->val = rez;
    12311     /* protect geting var value, is number now */
    12312     numptr_m1->var = NULL;
    12313     return 0;
    12314  err:
    12315     return -1;
    12316 }
    12317 
    12318 /* longest must be first */
    12319 static const char op_tokens[] ALIGN1 = {
    12320     '<','<','=',0, TOK_LSHIFT_ASSIGN,
    12321     '>','>','=',0, TOK_RSHIFT_ASSIGN,
    12322     '<','<',    0, TOK_LSHIFT,
    12323     '>','>',    0, TOK_RSHIFT,
    12324     '|','|',    0, TOK_OR,
    12325     '&','&',    0, TOK_AND,
    12326     '!','=',    0, TOK_NE,
    12327     '<','=',    0, TOK_LE,
    12328     '>','=',    0, TOK_GE,
    12329     '=','=',    0, TOK_EQ,
    12330     '|','=',    0, TOK_OR_ASSIGN,
    12331     '&','=',    0, TOK_AND_ASSIGN,
    12332     '*','=',    0, TOK_MUL_ASSIGN,
    12333     '/','=',    0, TOK_DIV_ASSIGN,
    12334     '%','=',    0, TOK_REM_ASSIGN,
    12335     '+','=',    0, TOK_PLUS_ASSIGN,
    12336     '-','=',    0, TOK_MINUS_ASSIGN,
    12337     '-','-',    0, TOK_POST_DEC,
    12338     '^','=',    0, TOK_XOR_ASSIGN,
    12339     '+','+',    0, TOK_POST_INC,
    12340     '*','*',    0, TOK_EXPONENT,
    12341     '!',        0, TOK_NOT,
    12342     '<',        0, TOK_LT,
    12343     '>',        0, TOK_GT,
    12344     '=',        0, TOK_ASSIGN,
    12345     '|',        0, TOK_BOR,
    12346     '&',        0, TOK_BAND,
    12347     '*',        0, TOK_MUL,
    12348     '/',        0, TOK_DIV,
    12349     '%',        0, TOK_REM,
    12350     '+',        0, TOK_ADD,
    12351     '-',        0, TOK_SUB,
    12352     '^',        0, TOK_BXOR,
    12353     /* uniq */
    12354     '~',        0, TOK_BNOT,
    12355     ',',        0, TOK_COMMA,
    12356     '?',        0, TOK_CONDITIONAL,
    12357     ':',        0, TOK_CONDITIONAL_SEP,
    12358     ')',        0, TOK_RPAREN,
    12359     '(',        0, TOK_LPAREN,
    12360     0
    12361 };
    12362 /* ptr to ")" */
    12363 #define endexpression &op_tokens[sizeof(op_tokens)-7]
    12364 
    12365 static arith_t
    12366 arith(const char *expr, int *perrcode)
    12367 {
    12368     char arithval; /* Current character under analysis */
    12369     operator lasttok, op;
    12370     operator prec;
    12371 
    12372     const char *p = endexpression;
    12373     int errcode;
    12374 
    12375     size_t datasizes = strlen(expr) + 2;
    12376 
    12377     /* Stack of integers */
    12378     /* The proof that there can be no more than strlen(startbuf)/2+1 integers
    12379      * in any given correct or incorrect expression is left as an exercise to
    12380      * the reader. */
    12381     v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
    12382                 *numstackptr = numstack;
    12383     /* Stack of operator tokens */
    12384     operator *stack = alloca((datasizes) * sizeof(operator)),
    12385                 *stackptr = stack;
    12386 
    12387     *stackptr++ = lasttok = TOK_LPAREN;     /* start off with a left paren */
    12388     *perrcode = errcode = 0;
    12389 
    12390     while (1) {
    12391         arithval = *expr;
    12392         if (arithval == 0) {
    12393             if (p == endexpression) {
    12394                 /* Null expression. */
    12395                 return 0;
    12396             }
    12397 
    12398             /* This is only reached after all tokens have been extracted from the
    12399              * input stream. If there are still tokens on the operator stack, they
    12400              * are to be applied in order. At the end, there should be a final
    12401              * result on the integer stack */
    12402 
    12403             if (expr != endexpression + 1) {
    12404                 /* If we haven't done so already, */
    12405                 /* append a closing right paren */
    12406                 expr = endexpression;
    12407                 /* and let the loop process it. */
    12408                 continue;
    12409             }
    12410             /* At this point, we're done with the expression. */
    12411             if (numstackptr != numstack+1) {
    12412                 /* ... but if there isn't, it's bad */
    12413  err:
    12414                 return (*perrcode = -1);
    12415             }
    12416             if (numstack->var) {
    12417                 /* expression is $((var)) only, lookup now */
    12418                 errcode = arith_lookup_val(numstack);
    12419             }
    12420  ret:
    12421             *perrcode = errcode;
    12422             return numstack->val;
    12423         }
    12424 
    12425         /* Continue processing the expression. */
    12426         if (arith_isspace(arithval)) {
    12427             /* Skip whitespace */
    12428             goto prologue;
    12429         }
    12430         p = endofname(expr);
    12431         if (p != expr) {
    12432             size_t var_name_size = (p-expr) + 1;  /* trailing zero */
    12433 
    12434             numstackptr->var = alloca(var_name_size);
    12435             safe_strncpy(numstackptr->var, expr, var_name_size);
    12436             expr = p;
    12437  num:
    12438             numstackptr->contidional_second_val_initialized = 0;
    12439             numstackptr++;
    12440             lasttok = TOK_NUM;
    12441             continue;
    12442         }
    12443         if (isdigit(arithval)) {
    12444             numstackptr->var = NULL;
    12445 #if ENABLE_ASH_MATH_SUPPORT_64
    12446             numstackptr->val = strtoll(expr, (char **) &expr, 0);
    12447 #else
    12448             numstackptr->val = strtol(expr, (char **) &expr, 0);
    12449 #endif
    12450             goto num;
    12451         }
    12452         for (p = op_tokens; ; p++) {
    12453             const char *o;
    12454 
    12455             if (*p == 0) {
    12456                 /* strange operator not found */
    12457                 goto err;
    12458             }
    12459             for (o = expr; *p && *o == *p; p++)
    12460                 o++;
    12461             if (! *p) {
    12462                 /* found */
    12463                 expr = o - 1;
    12464                 break;
    12465             }
    12466             /* skip tail uncompared token */
    12467             while (*p)
    12468                 p++;
    12469             /* skip zero delim */
    12470             p++;
    12471         }
    12472         op = p[1];
    12473 
    12474         /* post grammar: a++ reduce to num */
    12475         if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
    12476             lasttok = TOK_NUM;
    12477 
    12478         /* Plus and minus are binary (not unary) _only_ if the last
    12479          * token was as number, or a right paren (which pretends to be
    12480          * a number, since it evaluates to one). Think about it.
    12481          * It makes sense. */
    12482         if (lasttok != TOK_NUM) {
    12483             switch (op) {
    12484             case TOK_ADD:
    12485                 op = TOK_UPLUS;
    12486                 break;
    12487             case TOK_SUB:
    12488                 op = TOK_UMINUS;
    12489                 break;
    12490             case TOK_POST_INC:
    12491                 op = TOK_PRE_INC;
    12492                 break;
    12493             case TOK_POST_DEC:
    12494                 op = TOK_PRE_DEC;
    12495                 break;
    12496             }
    12497         }
    12498         /* We don't want a unary operator to cause recursive descent on the
    12499          * stack, because there can be many in a row and it could cause an
    12500          * operator to be evaluated before its argument is pushed onto the
    12501          * integer stack. */
    12502         /* But for binary operators, "apply" everything on the operator
    12503          * stack until we find an operator with a lesser priority than the
    12504          * one we have just extracted. */
    12505         /* Left paren is given the lowest priority so it will never be
    12506          * "applied" in this way.
    12507          * if associativity is right and priority eq, applied also skip
    12508          */
    12509         prec = PREC(op);
    12510         if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
    12511             /* not left paren or unary */
    12512             if (lasttok != TOK_NUM) {
    12513                 /* binary op must be preceded by a num */
    12514                 goto err;
    12515             }
    12516             while (stackptr != stack) {
    12517                 if (op == TOK_RPAREN) {
    12518                     /* The algorithm employed here is simple: while we don't
    12519                      * hit an open paren nor the bottom of the stack, pop
    12520                      * tokens and apply them */
    12521                     if (stackptr[-1] == TOK_LPAREN) {
    12522                         --stackptr;
    12523                         /* Any operator directly after a */
    12524                         lasttok = TOK_NUM;
    12525                         /* close paren should consider itself binary */
    12526                         goto prologue;
    12527                     }
    12528                 } else {
    12529                     operator prev_prec = PREC(stackptr[-1]);
    12530 
    12531                     convert_prec_is_assing(prec);
    12532                     convert_prec_is_assing(prev_prec);
    12533                     if (prev_prec < prec)
    12534                         break;
    12535                     /* check right assoc */
    12536                     if (prev_prec == prec && is_right_associativity(prec))
    12537                         break;
    12538                 }
    12539                 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
    12540                 if (errcode) goto ret;
    12541             }
    12542             if (op == TOK_RPAREN) {
    12543                 goto err;
    12544             }
    12545         }
    12546 
    12547         /* Push this operator to the stack and remember it. */
    12548         *stackptr++ = lasttok = op;
    12549  prologue:
    12550         ++expr;
    12551     } /* while */
    12552 }
    12553 #endif /* ASH_MATH_SUPPORT */
    12554 
     12794static int FAST_FUNC
     12795ulimitcmd(int argc UNUSED_PARAM, char **argv)
     12796{
     12797    return shell_builtin_ulimit(argv);
     12798}
    1255512799
    1255612800/* ============ main() and helpers */
     
    1255912803 * Called to exit the shell.
    1256012804 */
    12561 static void exitshell(void) ATTRIBUTE_NORETURN;
     12805static void exitshell(void) NORETURN;
    1256212806static void
    1256312807exitshell(void)
     
    1257012814    TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
    1257112815    if (setjmp(loc.loc)) {
    12572         if (exception == EXEXIT)
     12816        if (exception_type == EXEXIT)
    1257312817/* dash bug: it just does _exit(exitstatus) here
    1257412818 * but we have to do setjobctl(0) first!
     
    1258312827        trap[0] = NULL;
    1258412828        evalstring(p, 0);
     12829        free(p);
    1258512830    }
    1258612831    flush_stdout_stderr();
     
    1259512840{
    1259612841    /* from input.c: */
    12597     basepf.nextc = basepf.buf = basebuf;
     12842    /* we will never free this */
     12843    basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
    1259812844
    1259912845    /* from trap.c: */
    1260012846    signal(SIGCHLD, SIG_DFL);
     12847    /* bash re-enables SIGHUP which is SIG_IGNed on entry.
     12848     * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
     12849     */
     12850    signal(SIGHUP, SIG_DFL);
    1260112851
    1260212852    /* from var.c: */
    1260312853    {
    1260412854        char **envp;
    12605         char ppid[sizeof(int)*3 + 1];
    1260612855        const char *p;
    1260712856        struct stat st1, st2;
     
    1261412863        }
    1261512864
    12616         snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
    12617         setvar("PPID", ppid, 0);
     12865        setvar("PPID", utoa(getppid()), 0);
    1261812866
    1261912867        p = lookupvar("PWD");
     
    1263012878 */
    1263112879static void
    12632 procargs(int argc, char **argv)
     12880procargs(char **argv)
    1263312881{
    1263412882    int i;
     
    1263812886    xargv = argv;
    1263912887    arg0 = xargv[0];
    12640     if (argc > 0)
     12888    /* if (xargv[0]) - mmm, this is always true! */
    1264112889        xargv++;
    1264212890    for (i = 0; i < NOPTS; i++)
    1264312891        optlist[i] = 2;
    1264412892    argptr = xargv;
    12645     options(1);
     12893    if (options(1)) {
     12894        /* it already printed err message */
     12895        raise_exception(EXERROR);
     12896    }
    1264612897    xargv = argptr;
    1264712898    xminusc = minusc;
     
    1267812929    shellparam.optoff = -1;
    1267912930#endif
    12680     /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
     12931    /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
    1268112932    while (*xargv) {
    1268212933        shellparam.nparam++;
     
    1271312964    loopnest = 0;
    1271412965    /* from input.c: */
    12715     parselleft = parsenleft = 0;      /* clear input buffer */
     12966    g_parsefile->left_in_buffer = 0;
     12967    g_parsefile->left_in_line = 0;      /* clear input buffer */
    1271612968    popallfiles();
    1271712969    /* from parser.c: */
     
    1271912971    checkkwd = 0;
    1272012972    /* from redir.c: */
    12721     clearredir(0);
     12973    clearredir(/*drop:*/ 0);
    1272212974}
    1272312975
     
    1273412986 * is used to figure out how far we had gotten.
    1273512987 */
    12736 int ash_main(int argc, char **argv);
    12737 int ash_main(int argc, char **argv)
    12738 {
    12739     char *shinit;
    12740     volatile int state;
     12988int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     12989int ash_main(int argc UNUSED_PARAM, char **argv)
     12990{
     12991    const char *shinit;
     12992    volatile smallint state;
    1274112993    struct jmploc jmploc;
    1274212994    struct stackmark smark;
    1274312995
     12996    /* Initialize global data */
     12997    INIT_G_misc();
     12998    INIT_G_memstack();
     12999    INIT_G_var();
     13000#if ENABLE_ASH_ALIAS
     13001    INIT_G_alias();
     13002#endif
     13003    INIT_G_cmdtable();
     13004
    1274413005#if PROFILE
    1274513006    monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
     
    1275113012    state = 0;
    1275213013    if (setjmp(jmploc.loc)) {
    12753         int e;
    12754         int s;
     13014        smallint e;
     13015        smallint s;
    1275513016
    1275613017        reset();
    1275713018
    12758         e = exception;
     13019        e = exception_type;
    1275913020        if (e == EXERROR)
    1276013021            exitstatus = 2;
    1276113022        s = state;
    12762         if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
     13023        if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
    1276313024            exitshell();
    12764 
     13025        }
    1276513026        if (e == EXINT) {
    1276613027            outcslow('\n', stderr);
    1276713028        }
     13029
    1276813030        popstackmark(&smark);
    1276913031        FORCE_INT_ON; /* enable interrupts */
     
    1277913041#if DEBUG
    1278013042    opentrace();
    12781     trace_puts("Shell args: ");
     13043    TRACE(("Shell args: "));
    1278213044    trace_puts_args(argv);
    1278313045#endif
    1278413046    rootpid = getpid();
    1278513047
    12786 #if ENABLE_ASH_RANDOM_SUPPORT
    12787     rseed = rootpid + time(NULL);
    12788 #endif
    1278913048    init();
    1279013049    setstackmark(&smark);
    12791     procargs(argc, argv);
     13050    procargs(argv);
     13051
    1279213052#if ENABLE_FEATURE_EDITING_SAVEHISTORY
    1279313053    if (iflag) {
     
    1280413064    }
    1280513065#endif
    12806     if (argv[0] && argv[0][0] == '-')
     13066    if (/* argv[0] && */ argv[0][0] == '-')
    1280713067        isloginsh = 1;
    1280813068    if (isloginsh) {
     
    1282813088 state3:
    1282913089    state = 4;
    12830     if (minusc)
     13090    if (minusc) {
     13091        /* evalstring pushes parsefile stack.
     13092         * Ensure we don't falsely claim that 0 (stdin)
     13093         * is one of stacked source fds.
     13094         * Testcase: ash -c 'exec 1>&0' must not complain. */
     13095        // if (!sflag) g_parsefile->pf_fd = -1;
     13096        // ^^ not necessary since now we special-case fd 0
     13097        // in is_hidden_fd() to not be considered "hidden fd"
    1283113098        evalstring(minusc, 0);
     13099    }
    1283213100
    1283313101    if (sflag || minusc == NULL) {
    12834 #if ENABLE_FEATURE_EDITING_SAVEHISTORY
    12835         if ( iflag ) {
     13102#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
     13103        if (iflag) {
    1283613104            const char *hp = lookupvar("HISTFILE");
    12837 
    12838             if (hp != NULL)
     13105            if (hp)
    1283913106                line_input_state->hist_file = hp;
    1284013107        }
     
    1285213119    }
    1285313120#endif
     13121    TRACE(("End of main reached\n"));
    1285413122    exitshell();
    1285513123    /* NOTREACHED */
    1285613124}
    12857 
    12858 #if DEBUG
    12859 const char *applet_name = "debug stuff usage";
    12860 int main(int argc, char **argv)
    12861 {
    12862     return ash_main(argc, argv);
    12863 }
    12864 #endif
    1286513125
    1286613126
  • branches/2.2.9/mindi-busybox/shell/ash_test/ash-arith/arith.right

    r1765 r2725  
    565620 20
    575730 30
    58 ./arith.tests: line 117: syntax error: 1 ? 20 : x+=2
     58./arith.tests: line 117: arithmetic syntax error
    59596 6
    60606,5,3 6,5,3
     
    6262255 255
    636340 40
    64 ./arith.tests: line 163: syntax error:  7 = 43
     64./arith.tests: line 163: arithmetic syntax error
    6565./arith.tests: line 165: divide by zero
    66 ./arith.tests: let: line 166: syntax error: jv += $iv
    67 ./arith.tests: line 167: syntax error:  jv += $iv
    68 ./arith.tests: let: line 168: syntax error: rv = 7 + (43 * 6
     66./arith.tests: let: line 166: arithmetic syntax error
     67./arith.tests: line 167: arithmetic syntax error
     68./arith.tests: let: line 168: arithmetic syntax error
    6969abc
    7070def
    7171ghi
    72 ./arith.tests: line 191: syntax error:  ( 4 + A ) + 4
     72./arith.tests: line 191: arithmetic syntax error
    737316 16
    74 ./arith.tests: line 196: syntax error:  4 ? : 3 + 5
    75 ./arith.tests: line 197: syntax error:  1 ? 20
    76 ./arith.tests: line 198: syntax error:  4 ? 20 :
     74./arith.tests: line 196: arithmetic syntax error
     75./arith.tests: line 197: malformed ?: operator
     76./arith.tests: line 198: arithmetic syntax error
    77779 9
    78 ./arith.tests: line 205: syntax error:  0 && B=42
    79 ./arith.tests: line 208: syntax error:  1 || B=88
     78./arith.tests: line 205: arithmetic syntax error
     79./arith.tests: line 208: arithmetic syntax error
    80809 9
    81819 9
     
    98984 4
    99994 4
    100 ./arith.tests: line 257: syntax error:  7--
    101 ./arith.tests: line 259: syntax error:  --x=7
    102 ./arith.tests: line 260: syntax error:  ++x=7
    103 ./arith.tests: line 262: syntax error:  x++=7
    104 ./arith.tests: line 263: syntax error:  x--=7
     100./arith.tests: line 257: arithmetic syntax error
     101./arith.tests: line 259: arithmetic syntax error
     102./arith.tests: line 260: arithmetic syntax error
     103./arith.tests: line 262: arithmetic syntax error
     104./arith.tests: line 263: arithmetic syntax error
    1051054 4
    1061067 7
    107107-7 -7
    108 ./arith1.sub: line 2: syntax error:  4--
    109 ./arith1.sub: line 3: syntax error:  4++
    110 ./arith1.sub: line 4: syntax error:  4 --
    111 ./arith1.sub: line 5: syntax error:  4 ++
     108./arith1.sub: line 2: arithmetic syntax error
     109./arith1.sub: line 3: arithmetic syntax error
     110./arith1.sub: line 4: arithmetic syntax error
     111./arith1.sub: line 5: arithmetic syntax error
    1121126 6
    1131133 3
     
    120120-2 -2
    1211211 1
    122 ./arith1.sub: line 37: syntax error:  +++7
    123 ./arith2.sub: line 2: syntax error:  --7
    124 ./arith2.sub: line 3: syntax error:  ++7
    125 ./arith2.sub: line 4: syntax error:  -- 7
    126 ./arith2.sub: line 5: syntax error:  ++ 7
     122./arith1.sub: line 37: arithmetic syntax error
     123./arith2.sub: line 2: arithmetic syntax error
     124./arith2.sub: line 3: arithmetic syntax error
     125./arith2.sub: line 4: arithmetic syntax error
     126./arith2.sub: line 5: arithmetic syntax error
    1271275 5
    1281281 1
    1291294 4
    1301300 0
    131 ./arith2.sub: line 42: syntax error:  -- - 7
    132 ./arith2.sub: line 47: syntax error:  ++ + 7
     131./arith2.sub: line 42: arithmetic syntax error
     132./arith2.sub: line 47: arithmetic syntax error
    1331338 12
    134 ./arith.tests: line 290: syntax error: a b
     134./arith.tests: line 290: arithmetic syntax error
    13513542
    13613642
  • branches/2.2.9/mindi-busybox/shell/ash_test/printenv.c

    r1765 r2725  
    4646      for (envp = environ; *envp; envp++)
    4747    puts (*envp);
    48       exit (0);
     48      exit(EXIT_SUCCESS);
    4949    }
    5050
     
    6060        {
    6161          puts (eval + 1);
    62           exit (0);
     62          exit(EXIT_SUCCESS);
    6363        }
    6464    }
    6565    }
    66   exit (1);
     66  exit(EXIT_FAILURE);
    6767}
  • branches/2.2.9/mindi-busybox/shell/ash_test/recho.c

    r1765 r2725  
    3030void strprint();
    3131
    32 int
    33 main(argc, argv)
    34 int argc;
    35 char    **argv;
     32int main(int argc, char **argv)
    3633{
    37     register int    i;
     34    int i;
    3835
    3936    for (i = 1; i < argc; i++) {
     
    4239        printf(">\n");
    4340    }
    44     exit(0);
     41    exit(EXIT_SUCCESS);
    4542}
    4643
    47 void
    48 strprint(str)
    49 char    *str;
     44void strprint(char *str)
    5045{
    51     register unsigned char *s;
     46    unsigned char *s;
    5247
    5348    for (s = (unsigned char *)str; s && *s; s++) {
  • branches/2.2.9/mindi-busybox/shell/ash_test/run-all

    r1765 r2725  
    11#!/bin/sh
    22
    3 test -x ash || { echo "No ./ash?!"; exit; }
     3TOPDIR=$PWD
     4
     5test -x ash || {
     6    echo "No ./ash - creating a link to ../../busybox"
     7    ln -s ../../busybox ash
     8}
    49test -x printenv || gcc -O2 -o printenv printenv.c || exit $?
    510test -x recho    || gcc -O2 -o recho    recho.c    || exit $?
     
    1520{
    1621    test -d "$1" || return 0
     22#   echo do_test "$1"
     23    # $1 but with / replaced by # so that it can be used as filename part
     24    noslash=`echo "$1" | sed 's:/:#:g'`
    1725    (
    1826    cd "$1" || { echo "cannot cd $1!"; exit 1; }
     
    2432        #*) echo $x ; sh $x ;;
    2533        *)
    26         sh "$x" >"../$1-$x.fail" 2>&1 && \
    27         { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
     34        sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \
     35        { echo "$1/$x: ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "$1/$x: fail";
    2836        ;;
    2937    esac
     
    3745    {
    3846        "$THIS_SH" "./$x" >"$name.xx" 2>&1
    39         diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
     47        diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \
     48        && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail"
    4049    } && echo "$1/$x: ok" || echo "$1/$x: fail"
    4150    done
     
    4958    # All sub directories
    5059    modules=`ls -d ash-*`
     60    # If you want to test ash against hush and msh testsuites
     61    # (have to copy hush_test and msh_test dirs to current dir first):
     62    #modules=`ls -d ash-* hush_test/hush-* msh_test/msh-*`
    5163
    5264    for module in $modules; do
  • branches/2.2.9/mindi-busybox/shell/ash_test/zecho.c

    r1765 r2725  
    2222#include <stdlib.h>
    2323
    24 int
    25 main(argc, argv)
    26 int argc;
    27 char    **argv;
     24int main(int argc, char **argv)
    2825{
    2926    argv++;
     
    3633
    3734    putchar('\n');
    38     exit(0);
     35    exit(EXIT_SUCCESS);
    3936}
  • branches/2.2.9/mindi-busybox/shell/cttyhack.c

    r1765 r2725  
    1 /* This code is adapted from busybox project
     1/* vi: set sw=4 ts=4: */
     2/*
     3 * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
    24 *
    3  * Licensed under GPLv2
     5 * Licensed under GPLv2, see file LICENSE in this source tree.
    46 */
    57#include "libbb.h"
    68
     9//applet:IF_CTTYHACK(APPLET(cttyhack, _BB_DIR_BIN, _BB_SUID_DROP))
     10
     11//kbuild:lib-$(CONFIG_CTTYHACK) += cttyhack.o
     12
     13//config:config CTTYHACK
     14//config:   bool "cttyhack"
     15//config:   default y
     16//config:   help
     17//config:     One common problem reported on the mailing list is "can't access tty;
     18//config:     job control turned off" error message which typically appears when
     19//config:     one tries to use shell with stdin/stdout opened to /dev/console.
     20//config:     This device is special - it cannot be a controlling tty.
     21//config:
     22//config:     Proper solution is to use correct device instead of /dev/console.
     23//config:
     24//config:     cttyhack provides "quick and dirty" solution to this problem.
     25//config:     It analyzes stdin with various ioctls, trying to determine whether
     26//config:     it is a /dev/ttyN or /dev/ttySN (virtual terminal or serial line).
     27//config:     If it detects one, it closes stdin/out/err and reopens that device.
     28//config:     Then it executes given program. Opening the device will make
     29//config:     that device a controlling tty. This may require cttyhack
     30//config:     to be a session leader.
     31//config:
     32//config:     Example for /etc/inittab (for busybox init):
     33//config:
     34//config:     ::respawn:/bin/cttyhack /bin/sh
     35//config:
     36//config:     Starting an interactive shell from boot shell script:
     37//config:
     38//config:     setsid cttyhack sh
     39//config:
     40//config:     Giving controlling tty to shell running with PID 1:
     41//config:
     42//config:     # exec cttyhack sh
     43//config:
     44//config:     Without cttyhack, you need to know exact tty name,
     45//config:     and do something like this:
     46//config:
     47//config:     # exec setsid sh -c 'exec sh </dev/tty1 >/dev/tty1 2>&1'
     48//config:
     49
     50//usage:#define cttyhack_trivial_usage
     51//usage:       "PROG ARGS"
     52//usage:#define cttyhack_full_usage "\n\n"
     53//usage:       "Give PROG a controlling tty if possible."
     54//usage:     "\nExample for /etc/inittab (for busybox init):"
     55//usage:     "\n    ::respawn:/bin/cttyhack /bin/sh"
     56//usage:     "\nGiving controlling tty to shell running with PID 1:"
     57//usage:     "\n    $ exec cttyhack sh"
     58//usage:     "\nStarting interactive shell from boot shell script:"
     59//usage:     "\n    setsid cttyhack sh"
     60
     61#if !defined(__linux__) && !defined(TIOCGSERIAL) && !ENABLE_WERROR
     62# warning cttyhack will not be able to detect a controlling tty on this system
     63#endif
     64
    765/* From <linux/vt.h> */
    866struct vt_stat {
    9     unsigned short v_active;    /* active vt */
    10     unsigned short v_signal;    /* signal to send */
    11     unsigned short v_state; /* vt bitmask */
     67    unsigned short v_active;        /* active vt */
     68    unsigned short v_signal;        /* signal to send */
     69    unsigned short v_state;         /* vt bitmask */
    1270};
    13 enum { VT_GETSTATE = 0x5603 };  /* get global vt state info */
     71enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
    1472
    1573/* From <linux/serial.h> */
     
    2785    char    reserved_char[1];
    2886    int hub6;
    29     unsigned short  closing_wait; /* time to wait before closing */
    30     unsigned short  closing_wait2; /* no longer used... */
     87    unsigned short  closing_wait;   /* time to wait before closing */
     88    unsigned short  closing_wait2;  /* no longer used... */
    3189    unsigned char   *iomem_base;
    3290    unsigned short  iomem_reg_shift;
     
    3694};
    3795
    38 int cttyhack_main(int argc, char **argv) ATTRIBUTE_NORETURN;
    39 int cttyhack_main(int argc, char **argv)
     96int cttyhack_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     97int cttyhack_main(int argc UNUSED_PARAM, char **argv)
    4098{
    4199    int fd;
     
    52110
    53111    strcpy(console, "/dev/tty");
    54     if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
    55         /* this is a serial console */
    56         sprintf(console + 8, "S%d", u.sr.line);
    57     } else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
    58         /* this is linux virtual tty */
    59         sprintf(console + 8, "S%d" + 1, u.vt.v_active);
     112    fd = open(console, O_RDWR);
     113    if (fd >= 0) {
     114        /* We already have ctty, nothing to do */
     115        close(fd);
     116    } else {
     117        /* We don't have ctty (or don't have "/dev/tty" node...) */
     118        if (0) {}
     119#ifdef TIOCGSERIAL
     120        else if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
     121            /* this is a serial console */
     122            sprintf(console + 8, "S%d", u.sr.line);
     123        }
     124#endif
     125#ifdef __linux__
     126        else if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
     127            /* this is linux virtual tty */
     128            sprintf(console + 8, "S%d" + 1, u.vt.v_active);
     129        }
     130#endif
     131        if (console[8]) {
     132            fd = xopen(console, O_RDWR);
     133            //bb_error_msg("switching to '%s'", console);
     134            dup2(fd, 0);
     135            dup2(fd, 1);
     136            dup2(fd, 2);
     137            while (fd > 2)
     138                close(fd--);
     139            /* Some other session may have it as ctty,
     140             * steal it from them:
     141             */
     142            ioctl(0, TIOCSCTTY, 1);
     143        }
    60144    }
    61145
    62     if (console[8]) {
    63         fd = xopen(console, O_RDWR);
    64         //bb_error_msg("switching to '%s'", console);
    65         dup2(fd, 0);
    66         dup2(fd, 1);
    67         dup2(fd, 2);
    68         while (fd > 2) close(fd--);
    69     }
    70 
    71     execvp(argv[0], argv);
    72     bb_perror_msg_and_die("cannot exec '%s'", argv[0]);
     146    BB_EXECVP_or_die(argv);
    73147}
  • branches/2.2.9/mindi-busybox/shell/hush.c

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  * sh.c -- a prototype Bourne shell grammar parser
    4  *      Intended to follow the original Thompson and Ritchie
    5  *      "small and simple is beautiful" philosophy, which
    6  *      incidentally is a good match to today's BusyBox.
     3 * A prototype Bourne shell grammar parser.
     4 * Intended to follow the original Thompson and Ritchie
     5 * "small and simple is beautiful" philosophy, which
     6 * incidentally is a good match to today's BusyBox.
    77 *
    8  * Copyright (C) 2000,2001  Larry Doolittle  <larry@doolittle.boa.org>
     8 * Copyright (C) 2000,2001  Larry Doolittle <larry@doolittle.boa.org>
     9 * Copyright (C) 2008,2009  Denys Vlasenko <vda.linux@googlemail.com>
     10 *
     11 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    912 *
    1013 * Credits:
     
    2124 *
    2225 * Other credits:
    23  *      b_addchr() derived from similar w_addchar function in glibc-2.2
    24  *      setup_redirect(), redirect_opt_num(), and big chunks of main()
    25  *      and many builtins derived from contributions by Erik Andersen
    26  *      miscellaneous bugfixes from Matt Kraai
     26 *      o_addchr derived from similar w_addchar function in glibc-2.2.
     27 *      parse_redirect, redirect_opt_num, and big chunks of main
     28 *      and many builtins derived from contributions by Erik Andersen.
     29 *      Miscellaneous bugfixes from Matt Kraai.
    2730 *
    2831 * There are two big (and related) architecture differences between
     
    3740 * across continuation lines.
    3841 *
    39  * Bash grammar not implemented: (how many of these were in original sh?)
    40  *      $_
    41  *      ! negation operator for pipes
    42  *      &> and >& redirection of stdout+stderr
    43  *      Brace Expansion
    44  *      Tilde Expansion
    45  *      fancy forms of Parameter Expansion
     42 * TODOs:
     43 *      grep for "TODO" and fix (some of them are easy)
     44 *      special variables (done: PWD, PPID, RANDOM)
     45 *      tilde expansion
    4646 *      aliases
    47  *      Arithmetic Expansion
    48  *      <(list) and >(list) Process Substitution
    49  *      reserved words: case, esac, select, function
    50  *      Here Documents ( << word )
    51  *      Functions
    52  * Major bugs:
    53  *      job handling woefully incomplete and buggy (improved --vda)
    54  *      reserved word execution woefully incomplete and buggy
    55  * to-do:
    56  *      port selected bugfixes from post-0.49 busybox lash - done?
    57  *      finish implementing reserved words: for, while, until, do, done
    58  *      change { and } from special chars to reserved words
    59  *      builtins: break, continue, eval, return, set, trap, ulimit
    60  *      test magic exec
    61  *      handle children going into background
    62  *      clean up recognition of null pipes
    63  *      check setting of global_argc and global_argv
    64  *      control-C handling, probably with longjmp
    6547 *      follow IFS rules more precisely, including update semantics
    66  *      figure out what to do with backslash-newline
    67  *      explain why we use signal instead of sigaction
    68  *      propagate syntax errors, die on resource errors?
    69  *      continuation lines, both explicit and implicit - done?
    70  *      memory leak finding and plugging - done?
    71  *      more testing, especially quoting rules and redirection
    72  *      document how quoting rules not precisely followed for variable assignments
    73  *      maybe change charmap[] to use 2-bit entries
    74  *      (eventually) remove all the printf's
     48 *      builtins mandated by standards we don't support:
     49 *          [un]alias, command, fc, getopts, newgrp, readonly, times
     50 *      make complex ${var%...} constructs support optional
     51 *      make here documents optional
    7552 *
    76  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     53 * Bash compat TODO:
     54 *      redirection of stdout+stderr: &> and >&
     55 *      reserved words: function select
     56 *      advanced test: [[ ]]
     57 *      process substitution: <(list) and >(list)
     58 *      =~: regex operator
     59 *      let EXPR [EXPR...]
     60 *          Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION)
     61 *          If the last arg evaluates to 0, let returns 1; 0 otherwise.
     62 *          NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used)
     63 *      ((EXPR))
     64 *          The EXPR is evaluated according to ARITHMETIC EVALUATION.
     65 *          This is exactly equivalent to let "EXPR".
     66 *      $[EXPR]: synonym for $((EXPR))
     67 *
     68 * Won't do:
     69 *      In bash, export builtin is special, its arguments are assignments
     70 *          and therefore expansion of them should be "one-word" expansion:
     71 *              $ export i=`echo 'a  b'` # export has one arg: "i=a  b"
     72 *          compare with:
     73 *              $ ls i=`echo 'a  b'`     # ls has two args: "i=a" and "b"
     74 *              ls: cannot access i=a: No such file or directory
     75 *              ls: cannot access b: No such file or directory
     76 *          Note1: same applies to local builtin.
     77 *          Note2: bash 3.2.33(1) does this only if export word itself
     78 *          is not quoted:
     79 *              $ export i=`echo 'aaa  bbb'`; echo "$i"
     80 *              aaa  bbb
     81 *              $ "export" i=`echo 'aaa  bbb'`; echo "$i"
     82 *              aaa
    7783 */
    78 
    79 
    80 #include <glob.h>      /* glob, of course */
    81 #include <getopt.h>    /* should be pretty obvious */
     84#include "busybox.h"  /* for APPLET_IS_NOFORK/NOEXEC */
     85#include <malloc.h>   /* for malloc_trim */
     86#include <glob.h>
    8287/* #include <dmalloc.h> */
    83 
    84 extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
    85 
    86 #include "busybox.h" /* for struct bb_applet */
    87 
     88#if ENABLE_HUSH_CASE
     89# include <fnmatch.h>
     90#endif
     91
     92#include "shell_common.h"
     93#include "math.h"
     94#include "match.h"
     95#if ENABLE_HUSH_RANDOM_SUPPORT
     96# include "random.h"
     97#else
     98# define CLEAR_RANDOM_T(rnd) ((void)0)
     99#endif
     100#ifndef PIPE_BUF
     101# define PIPE_BUF 4096  /* amount of buffering in a pipe */
     102#endif
     103
     104//applet:IF_HUSH(APPLET(hush, _BB_DIR_BIN, _BB_SUID_DROP))
     105//applet:IF_MSH(APPLET(msh, _BB_DIR_BIN, _BB_SUID_DROP))
     106//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, _BB_DIR_BIN, _BB_SUID_DROP, sh))
     107//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, _BB_DIR_BIN, _BB_SUID_DROP, bash))
     108
     109//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
     110//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
     111
     112//config:config HUSH
     113//config:   bool "hush"
     114//config:   default y
     115//config:   help
     116//config:     hush is a small shell (25k). It handles the normal flow control
     117//config:     constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
     118//config:     case/esac. Redirections, here documents, $((arithmetic))
     119//config:     and functions are supported.
     120//config:
     121//config:     It will compile and work on no-mmu systems.
     122//config:
     123//config:     It does not handle select, aliases, tilde expansion,
     124//config:     &>file and >&file redirection of stdout+stderr.
     125//config:
     126//config:config HUSH_BASH_COMPAT
     127//config:   bool "bash-compatible extensions"
     128//config:   default y
     129//config:   depends on HUSH
     130//config:   help
     131//config:     Enable bash-compatible extensions.
     132//config:
     133//config:config HUSH_BRACE_EXPANSION
     134//config:   bool "Brace expansion"
     135//config:   default y
     136//config:   depends on HUSH_BASH_COMPAT
     137//config:   help
     138//config:     Enable {abc,def} extension.
     139//config:
     140//config:config HUSH_HELP
     141//config:   bool "help builtin"
     142//config:   default y
     143//config:   depends on HUSH
     144//config:   help
     145//config:     Enable help builtin in hush. Code size + ~1 kbyte.
     146//config:
     147//config:config HUSH_INTERACTIVE
     148//config:   bool "Interactive mode"
     149//config:   default y
     150//config:   depends on HUSH
     151//config:   help
     152//config:     Enable interactive mode (prompt and command editing).
     153//config:     Without this, hush simply reads and executes commands
     154//config:     from stdin just like a shell script from a file.
     155//config:     No prompt, no PS1/PS2 magic shell variables.
     156//config:
     157//config:config HUSH_SAVEHISTORY
     158//config:   bool "Save command history to .hush_history"
     159//config:   default y
     160//config:   depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
     161//config:   help
     162//config:     Enable history saving in hush.
     163//config:
     164//config:config HUSH_JOB
     165//config:   bool "Job control"
     166//config:   default y
     167//config:   depends on HUSH_INTERACTIVE
     168//config:   help
     169//config:     Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
     170//config:     command (not entire shell), fg/bg builtins work. Without this option,
     171//config:     "cmd &" still works by simply spawning a process and immediately
     172//config:     prompting for next command (or executing next command in a script),
     173//config:     but no separate process group is formed.
     174//config:
     175//config:config HUSH_TICK
     176//config:   bool "Process substitution"
     177//config:   default y
     178//config:   depends on HUSH
     179//config:   help
     180//config:     Enable process substitution `command` and $(command) in hush.
     181//config:
     182//config:config HUSH_IF
     183//config:   bool "Support if/then/elif/else/fi"
     184//config:   default y
     185//config:   depends on HUSH
     186//config:   help
     187//config:     Enable if/then/elif/else/fi in hush.
     188//config:
     189//config:config HUSH_LOOPS
     190//config:   bool "Support for, while and until loops"
     191//config:   default y
     192//config:   depends on HUSH
     193//config:   help
     194//config:     Enable for, while and until loops in hush.
     195//config:
     196//config:config HUSH_CASE
     197//config:   bool "Support case ... esac statement"
     198//config:   default y
     199//config:   depends on HUSH
     200//config:   help
     201//config:     Enable case ... esac statement in hush. +400 bytes.
     202//config:
     203//config:config HUSH_FUNCTIONS
     204//config:   bool "Support funcname() { commands; } syntax"
     205//config:   default y
     206//config:   depends on HUSH
     207//config:   help
     208//config:     Enable support for shell functions in hush. +800 bytes.
     209//config:
     210//config:config HUSH_LOCAL
     211//config:   bool "Support local builtin"
     212//config:   default y
     213//config:   depends on HUSH_FUNCTIONS
     214//config:   help
     215//config:     Enable support for local variables in functions.
     216//config:
     217//config:config HUSH_RANDOM_SUPPORT
     218//config:   bool "Pseudorandom generator and $RANDOM variable"
     219//config:   default y
     220//config:   depends on HUSH
     221//config:   help
     222//config:     Enable pseudorandom generator and dynamic variable "$RANDOM".
     223//config:     Each read of "$RANDOM" will generate a new pseudorandom value.
     224//config:
     225//config:config HUSH_EXPORT_N
     226//config:   bool "Support 'export -n' option"
     227//config:   default y
     228//config:   depends on HUSH
     229//config:   help
     230//config:     export -n unexports variables. It is a bash extension.
     231//config:
     232//config:config HUSH_MODE_X
     233//config:   bool "Support 'hush -x' option and 'set -x' command"
     234//config:   default y
     235//config:   depends on HUSH
     236//config:   help
     237//config:     This instructs hush to print commands before execution.
     238//config:     Adds ~300 bytes.
     239//config:
     240//config:config MSH
     241//config:   bool "msh (deprecated: aliased to hush)"
     242//config:   default n
     243//config:   select HUSH
     244//config:   help
     245//config:     msh is deprecated and will be removed, please migrate to hush.
     246//config:
     247
     248//usage:#define hush_trivial_usage NOUSAGE_STR
     249//usage:#define hush_full_usage ""
     250//usage:#define msh_trivial_usage NOUSAGE_STR
     251//usage:#define msh_full_usage ""
     252//usage:#define sh_trivial_usage NOUSAGE_STR
     253//usage:#define sh_full_usage ""
     254//usage:#define bash_trivial_usage NOUSAGE_STR
     255//usage:#define bash_full_usage ""
     256
     257
     258/* Build knobs */
     259#define LEAK_HUNTING 0
     260#define BUILD_AS_NOMMU 0
     261/* Enable/disable sanity checks. Ok to enable in production,
     262 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
     263 * Keeping 1 for now even in released versions.
     264 */
     265#define HUSH_DEBUG 1
     266/* Slightly bigger (+200 bytes), but faster hush.
     267 * So far it only enables a trick with counting SIGCHLDs and forks,
     268 * which allows us to do fewer waitpid's.
     269 * (we can detect a case where neither forks were done nor SIGCHLDs happened
     270 * and therefore waitpid will return the same result as last time)
     271 */
     272#define ENABLE_HUSH_FAST 0
     273/* TODO: implement simplified code for users which do not need ${var%...} ops
     274 * So far ${var%...} ops are always enabled:
     275 */
     276#define ENABLE_HUSH_DOLLAR_OPS 1
     277
     278
     279#if BUILD_AS_NOMMU
     280# undef BB_MMU
     281# undef USE_FOR_NOMMU
     282# undef USE_FOR_MMU
     283# define BB_MMU 0
     284# define USE_FOR_NOMMU(...) __VA_ARGS__
     285# define USE_FOR_MMU(...)
     286#endif
     287
     288#include "NUM_APPLETS.h"
     289#if NUM_APPLETS == 1
     290/* STANDALONE does not make sense, and won't compile */
     291# undef CONFIG_FEATURE_SH_STANDALONE
     292# undef ENABLE_FEATURE_SH_STANDALONE
     293# undef IF_FEATURE_SH_STANDALONE
     294# undef IF_NOT_FEATURE_SH_STANDALONE
     295# define ENABLE_FEATURE_SH_STANDALONE 0
     296# define IF_FEATURE_SH_STANDALONE(...)
     297# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
     298#endif
     299
     300#if !ENABLE_HUSH_INTERACTIVE
     301# undef ENABLE_FEATURE_EDITING
     302# define ENABLE_FEATURE_EDITING 0
     303# undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
     304# define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
     305#endif
     306
     307/* Do we support ANY keywords? */
     308#if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
     309# define HAS_KEYWORDS 1
     310# define IF_HAS_KEYWORDS(...) __VA_ARGS__
     311# define IF_HAS_NO_KEYWORDS(...)
     312#else
     313# define HAS_KEYWORDS 0
     314# define IF_HAS_KEYWORDS(...)
     315# define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__
     316#endif
    88317
    89318/* If you comment out one of these below, it will be #defined later
     
    94323#define debug_print_tree(a, b)   do {} while (0)
    95324#define debug_printf_exec(...)   do {} while (0)
     325#define debug_printf_env(...)    do {} while (0)
    96326#define debug_printf_jobs(...)   do {} while (0)
    97327#define debug_printf_expand(...) do {} while (0)
     328#define debug_printf_varexp(...) do {} while (0)
     329#define debug_printf_glob(...)   do {} while (0)
     330#define debug_printf_list(...)   do {} while (0)
     331#define debug_printf_subst(...)  do {} while (0)
    98332#define debug_printf_clean(...)  do {} while (0)
    99333
    100 #ifndef debug_printf
    101 #define debug_printf(...) fprintf(stderr, __VA_ARGS__)
    102 #endif
    103 
    104 #ifndef debug_printf_parse
    105 #define debug_printf_parse(...) fprintf(stderr, __VA_ARGS__)
    106 #endif
    107 
    108 #ifndef debug_printf_exec
    109 #define debug_printf_exec(...) fprintf(stderr, __VA_ARGS__)
    110 #endif
    111 
    112 #ifndef debug_printf_jobs
    113 #define debug_printf_jobs(...) fprintf(stderr, __VA_ARGS__)
    114 #define DEBUG_SHELL_JOBS 1
    115 #endif
    116 
    117 #ifndef debug_printf_expand
    118 #define debug_printf_expand(...) fprintf(stderr, __VA_ARGS__)
    119 #define DEBUG_EXPAND 1
    120 #endif
    121 
    122 /* Keep unconditionally on for now */
    123 #define ENABLE_HUSH_DEBUG 1
    124 
    125 #ifndef debug_printf_clean
    126 /* broken, of course, but OK for testing */
    127 static const char *indenter(int i)
    128 {
    129     static const char blanks[] ALIGN1 =
    130         "                                    ";
    131     return &blanks[sizeof(blanks) - i - 1];
    132 }
    133 #define debug_printf_clean(...) fprintf(stderr, __VA_ARGS__)
    134 #define DEBUG_CLEAN 1
    135 #endif
    136 
    137 
    138 #if !ENABLE_HUSH_INTERACTIVE
    139 #undef ENABLE_FEATURE_EDITING
    140 #define ENABLE_FEATURE_EDITING 0
    141 #undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
    142 #define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
     334#define ERR_PTR ((void*)(long)1)
     335
     336#define JOB_STATUS_FORMAT    "[%d] %-22s %.40s\n"
     337
     338#define _SPECIAL_VARS_STR     "_*@$!?#"
     339#define SPECIAL_VARS_STR     ("_*@$!?#" + 1)
     340#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
     341#if ENABLE_HUSH_BASH_COMPAT
     342/* Support / and // replace ops */
     343/* Note that // is stored as \ in "encoded" string representation */
     344# define VAR_ENCODED_SUBST_OPS      "\\/%#:-=+?"
     345# define VAR_SUBST_OPS             ("\\/%#:-=+?" + 1)
     346# define MINUS_PLUS_EQUAL_QUESTION ("\\/%#:-=+?" + 5)
     347#else
     348# define VAR_ENCODED_SUBST_OPS      "%#:-=+?"
     349# define VAR_SUBST_OPS              "%#:-=+?"
     350# define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3)
    143351#endif
    144352
    145353#define SPECIAL_VAR_SYMBOL   3
    146354
    147 #define PARSEFLAG_EXIT_FROM_LOOP 1
    148 #define PARSEFLAG_SEMICOLON      (1 << 1)  /* symbol ';' is special for parser */
    149 #define PARSEFLAG_REPARSING      (1 << 2)  /* >= 2nd pass */
    150 
    151 typedef enum {
    152     REDIRECT_INPUT     = 1,
    153     REDIRECT_OVERWRITE = 2,
    154     REDIRECT_APPEND    = 3,
    155     REDIRECT_HEREIS    = 4,
    156     REDIRECT_IO        = 5
    157 } redir_type;
    158 
    159 /* The descrip member of this structure is only used to make debugging
    160  * output pretty */
     355struct variable;
     356
     357static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
     358
     359/* This supports saving pointers malloced in vfork child,
     360 * to be freed in the parent.
     361 */
     362#if !BB_MMU
     363typedef struct nommu_save_t {
     364    char **new_env;
     365    struct variable *old_vars;
     366    char **argv;
     367    char **argv_from_re_execing;
     368} nommu_save_t;
     369#endif
     370
     371enum {
     372    RES_NONE  = 0,
     373#if ENABLE_HUSH_IF
     374    RES_IF    ,
     375    RES_THEN  ,
     376    RES_ELIF  ,
     377    RES_ELSE  ,
     378    RES_FI    ,
     379#endif
     380#if ENABLE_HUSH_LOOPS
     381    RES_FOR   ,
     382    RES_WHILE ,
     383    RES_UNTIL ,
     384    RES_DO    ,
     385    RES_DONE  ,
     386#endif
     387#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
     388    RES_IN    ,
     389#endif
     390#if ENABLE_HUSH_CASE
     391    RES_CASE  ,
     392    /* three pseudo-keywords support contrived "case" syntax: */
     393    RES_CASE_IN,   /* "case ... IN", turns into RES_MATCH when IN is observed */
     394    RES_MATCH ,    /* "word)" */
     395    RES_CASE_BODY, /* "this command is inside CASE" */
     396    RES_ESAC  ,
     397#endif
     398    RES_XXXX  ,
     399    RES_SNTX
     400};
     401
     402typedef struct o_string {
     403    char *data;
     404    int length; /* position where data is appended */
     405    int maxlen;
     406    int o_expflags;
     407    /* At least some part of the string was inside '' or "",
     408     * possibly empty one: word"", wo''rd etc. */
     409    smallint has_quoted_part;
     410    smallint has_empty_slot;
     411    smallint o_assignment; /* 0:maybe, 1:yes, 2:no */
     412} o_string;
     413enum {
     414    EXP_FLAG_SINGLEWORD     = 0x80, /* must be 0x80 */
     415    EXP_FLAG_GLOB           = 0x2,
     416    /* Protect newly added chars against globbing
     417     * by prepending \ to *, ?, [, \ */
     418    EXP_FLAG_ESC_GLOB_CHARS = 0x1,
     419};
     420enum {
     421    MAYBE_ASSIGNMENT      = 0,
     422    DEFINITELY_ASSIGNMENT = 1,
     423    NOT_ASSIGNMENT        = 2,
     424    /* Not an assigment, but next word may be: "if v=xyz cmd;" */
     425    WORD_IS_KEYWORD       = 3,
     426};
     427/* Used for initialization: o_string foo = NULL_O_STRING; */
     428#define NULL_O_STRING { NULL }
     429
     430/* I can almost use ordinary FILE*.  Is open_memstream() universally
     431 * available?  Where is it documented? */
     432typedef struct in_str {
     433    const char *p;
     434    /* eof_flag=1: last char in ->p is really an EOF */
     435    char eof_flag; /* meaningless if ->p == NULL */
     436    char peek_buf[2];
     437#if ENABLE_HUSH_INTERACTIVE
     438    smallint promptme;
     439    smallint promptmode; /* 0: PS1, 1: PS2 */
     440#endif
     441    FILE *file;
     442    int (*get) (struct in_str *) FAST_FUNC;
     443    int (*peek) (struct in_str *) FAST_FUNC;
     444} in_str;
     445#define i_getch(input) ((input)->get(input))
     446#define i_peek(input) ((input)->peek(input))
     447
     448/* The descrip member of this structure is only used to make
     449 * debugging output pretty */
    161450static const struct {
    162451    int mode;
     
    164453    char descrip[3];
    165454} redir_table[] = {
    166     { 0,                         0, "()" },
    167455    { O_RDONLY,                  0, "<"  },
    168456    { O_CREAT|O_TRUNC|O_WRONLY,  1, ">"  },
    169457    { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
    170     { O_RDONLY,                 -1, "<<" },
    171     { O_RDWR,                    1, "<>" }
     458    { O_CREAT|O_RDWR,            1, "<>" },
     459    { O_RDONLY,                  0, "<<" },
     460/* Should not be needed. Bogus default_fd helps in debugging */
     461/*  { O_RDONLY,                 77, "<<" }, */
    172462};
    173463
    174 typedef enum {
     464struct redir_struct {
     465    struct redir_struct *next;
     466    char *rd_filename;          /* filename */
     467    int rd_fd;                  /* fd to redirect */
     468    /* fd to redirect to, or -3 if rd_fd is to be closed (n>&-) */
     469    int rd_dup;
     470    smallint rd_type;           /* (enum redir_type) */
     471    /* note: for heredocs, rd_filename contains heredoc delimiter,
     472     * and subsequently heredoc itself; and rd_dup is a bitmask:
     473     * bit 0: do we need to trim leading tabs?
     474     * bit 1: is heredoc quoted (<<'delim' syntax) ?
     475     */
     476};
     477typedef enum redir_type {
     478    REDIRECT_INPUT     = 0,
     479    REDIRECT_OVERWRITE = 1,
     480    REDIRECT_APPEND    = 2,
     481    REDIRECT_IO        = 3,
     482    REDIRECT_HEREDOC   = 4,
     483    REDIRECT_HEREDOC2  = 5, /* REDIRECT_HEREDOC after heredoc is loaded */
     484
     485    REDIRFD_CLOSE      = -3,
     486    REDIRFD_SYNTAX_ERR = -2,
     487    REDIRFD_TO_FILE    = -1,
     488    /* otherwise, rd_fd is redirected to rd_dup */
     489
     490    HEREDOC_SKIPTABS = 1,
     491    HEREDOC_QUOTED   = 2,
     492} redir_type;
     493
     494
     495struct command {
     496    pid_t pid;                  /* 0 if exited */
     497    int assignment_cnt;         /* how many argv[i] are assignments? */
     498    smallint is_stopped;        /* is the command currently running? */
     499    smallint cmd_type;          /* CMD_xxx */
     500#define CMD_NORMAL   0
     501#define CMD_SUBSHELL 1
     502#if ENABLE_HUSH_BASH_COMPAT
     503/* used for "[[ EXPR ]]" */
     504# define CMD_SINGLEWORD_NOGLOB 2
     505#endif
     506#if ENABLE_HUSH_FUNCTIONS
     507# define CMD_FUNCDEF 3
     508#endif
     509
     510    smalluint cmd_exitcode;
     511    /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
     512    struct pipe *group;
     513#if !BB_MMU
     514    char *group_as_string;
     515#endif
     516#if ENABLE_HUSH_FUNCTIONS
     517    struct function *child_func;
     518/* This field is used to prevent a bug here:
     519 * while...do f1() {a;}; f1; f1() {b;}; f1; done
     520 * When we execute "f1() {a;}" cmd, we create new function and clear
     521 * cmd->group, cmd->group_as_string, cmd->argv[0].
     522 * When we execute "f1() {b;}", we notice that f1 exists,
     523 * and that its "parent cmd" struct is still "alive",
     524 * we put those fields back into cmd->xxx
     525 * (struct function has ->parent_cmd ptr to facilitate that).
     526 * When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
     527 * Without this trick, loop would execute a;b;b;b;...
     528 * instead of correct sequence a;b;a;b;...
     529 * When command is freed, it severs the link
     530 * (sets ->child_func->parent_cmd to NULL).
     531 */
     532#endif
     533    char **argv;                /* command name and arguments */
     534/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
     535 * and on execution these are substituted with their values.
     536 * Substitution can make _several_ words out of one argv[n]!
     537 * Example: argv[0]=='.^C*^C.' here: echo .$*.
     538 * References of the form ^C`cmd arg^C are `cmd arg` substitutions.
     539 */
     540    struct redir_struct *redirects; /* I/O redirections */
     541};
     542/* Is there anything in this command at all? */
     543#define IS_NULL_CMD(cmd) \
     544    (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects)
     545
     546struct pipe {
     547    struct pipe *next;
     548    int num_cmds;               /* total number of commands in pipe */
     549    int alive_cmds;             /* number of commands running (not exited) */
     550    int stopped_cmds;           /* number of commands alive, but stopped */
     551#if ENABLE_HUSH_JOB
     552    int jobid;                  /* job number */
     553    pid_t pgrp;                 /* process group ID for the job */
     554    char *cmdtext;              /* name of job */
     555#endif
     556    struct command *cmds;       /* array of commands in pipe */
     557    smallint followup;          /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
     558    IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */
     559    IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */
     560};
     561typedef enum pipe_style {
    175562    PIPE_SEQ = 1,
    176563    PIPE_AND = 2,
     
    178565    PIPE_BG  = 4,
    179566} pipe_style;
    180 
    181 /* might eventually control execution */
    182 typedef enum {
    183     RES_NONE  = 0,
    184 #if ENABLE_HUSH_IF
    185     RES_IF    = 1,
    186     RES_THEN  = 2,
    187     RES_ELIF  = 3,
    188     RES_ELSE  = 4,
    189     RES_FI    = 5,
    190 #endif
    191 #if ENABLE_HUSH_LOOPS
    192     RES_FOR   = 6,
    193     RES_WHILE = 7,
    194     RES_UNTIL = 8,
    195     RES_DO    = 9,
    196     RES_DONE  = 10,
    197     RES_IN    = 11,
    198 #endif
    199     RES_XXXX  = 12,
    200     RES_SNTX  = 13
    201 } reserved_style;
    202 enum {
    203     FLAG_END   = (1 << RES_NONE ),
    204 #if ENABLE_HUSH_IF
    205     FLAG_IF    = (1 << RES_IF   ),
    206     FLAG_THEN  = (1 << RES_THEN ),
    207     FLAG_ELIF  = (1 << RES_ELIF ),
    208     FLAG_ELSE  = (1 << RES_ELSE ),
    209     FLAG_FI    = (1 << RES_FI   ),
    210 #endif
    211 #if ENABLE_HUSH_LOOPS
    212     FLAG_FOR   = (1 << RES_FOR  ),
    213     FLAG_WHILE = (1 << RES_WHILE),
    214     FLAG_UNTIL = (1 << RES_UNTIL),
    215     FLAG_DO    = (1 << RES_DO   ),
    216     FLAG_DONE  = (1 << RES_DONE ),
    217     FLAG_IN    = (1 << RES_IN   ),
    218 #endif
    219     FLAG_START = (1 << RES_XXXX ),
    220 };
     567/* Is there anything in this pipe at all? */
     568#define IS_NULL_PIPE(pi) \
     569    ((pi)->num_cmds == 0 IF_HAS_KEYWORDS( && (pi)->res_word == RES_NONE))
    221570
    222571/* This holds pointers to the various results of parsing */
    223 struct p_context {
    224     struct child_prog *child;
     572struct parse_context {
     573    /* linked list of pipes */
    225574    struct pipe *list_head;
     575    /* last pipe (being constructed right now) */
    226576    struct pipe *pipe;
     577    /* last command in pipe (being constructed right now) */
     578    struct command *command;
     579    /* last redirect in command->redirects list */
    227580    struct redir_struct *pending_redirect;
    228     smallint res_w;
    229     smallint parse_type;        /* bitmask of PARSEFLAG_xxx, defines type of parser : ";$" common or special symbol */
    230     int old_flag;               /* bitmask of FLAG_xxx, for figuring out valid reserved words */
    231     struct p_context *stack;
    232     /* How about quoting status? */
    233 };
    234 
    235 struct redir_struct {
    236     struct redir_struct *next;  /* pointer to the next redirect in the list */
    237     redir_type type;            /* type of redirection */
    238     int fd;                     /* file descriptor being redirected */
    239     int dup;                    /* -1, or file descriptor being duplicated */
    240     glob_t word;                /* *word.gl_pathv is the filename */
    241 };
    242 
    243 struct child_prog {
    244     pid_t pid;                  /* 0 if exited */
    245     char **argv;                /* program name and arguments */
    246     struct pipe *group;         /* if non-NULL, first in group or subshell */
    247     smallint subshell;          /* flag, non-zero if group must be forked */
    248     smallint is_stopped;        /* is the program currently running? */
    249     struct redir_struct *redirects; /* I/O redirections */
    250     glob_t glob_result;         /* result of parameter globbing */
    251     struct pipe *family;        /* pointer back to the child's parent pipe */
    252     //sp counting seems to be broken... so commented out, grep for '//sp:'
    253     //sp: int sp;               /* number of SPECIAL_VAR_SYMBOL */
    254     //seems to be unused, grep for '//pt:'
    255     //pt: int parse_type;
    256 };
    257 /* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
    258  * and on execution these are substituted with their values.
    259  * Substitution can make _several_ words out of one argv[n]!
    260  * Example: argv[0]=='.^C*^C.' here: echo .$*.
    261  */
    262 
    263 struct pipe {
    264     struct pipe *next;
    265     int num_progs;              /* total number of programs in job */
    266     int running_progs;          /* number of programs running (not exited) */
    267     int stopped_progs;          /* number of programs alive, but stopped */
    268 #if ENABLE_HUSH_JOB
    269     int jobid;                  /* job number */
    270     pid_t pgrp;                 /* process group ID for the job */
    271     char *cmdtext;              /* name of job */
    272 #endif
    273     char *cmdbuf;               /* buffer various argv's point into */
    274     struct child_prog *progs;   /* array of commands in pipe */
    275     int job_context;            /* bitmask defining current context */
    276     smallint followup;          /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
    277     smallint res_word;          /* needed for if, for, while, until... */
    278 };
    279 
    280 struct close_me {
    281     struct close_me *next;
    282     int fd;
     581#if !BB_MMU
     582    o_string as_string;
     583#endif
     584#if HAS_KEYWORDS
     585    smallint ctx_res_w;
     586    smallint ctx_inverted; /* "! cmd | cmd" */
     587#if ENABLE_HUSH_CASE
     588    smallint ctx_dsemicolon; /* ";;" seen */
     589#endif
     590    /* bitmask of FLAG_xxx, for figuring out valid reserved words */
     591    int old_flag;
     592    /* group we are enclosed in:
     593     * example: "if pipe1; pipe2; then pipe3; fi"
     594     * when we see "if" or "then", we malloc and copy current context,
     595     * and make ->stack point to it. then we parse pipeN.
     596     * when closing "then" / fi" / whatever is found,
     597     * we move list_head into ->stack->command->group,
     598     * copy ->stack into current context, and delete ->stack.
     599     * (parsing of { list } and ( list ) doesn't use this method)
     600     */
     601    struct parse_context *stack;
     602#endif
    283603};
    284604
     
    292612    struct variable *next;
    293613    char *varstr;        /* points to "name=" portion */
     614#if ENABLE_HUSH_LOCAL
     615    unsigned func_nest_level;
     616#endif
    294617    int max_len;         /* if > 0, name is part of initial env; else name is malloced */
    295618    smallint flg_export; /* putenv should be done on this var */
     
    297620};
    298621
    299 typedef struct {
    300     char *data;
    301     int length;
    302     int maxlen;
    303     int quote;
    304     int nonnull;
    305 } o_string;
    306 #define NULL_O_STRING {NULL,0,0,0,0}
    307 /* used for initialization: o_string foo = NULL_O_STRING; */
    308 
    309 /* I can almost use ordinary FILE *.  Is open_memstream() universally
    310  * available?  Where is it documented? */
    311 struct in_str {
    312     const char *p;
    313     /* eof_flag=1: last char in ->p is really an EOF */
    314     char eof_flag; /* meaningless if ->p == NULL */
    315     char peek_buf[2];
    316 #if ENABLE_HUSH_INTERACTIVE
    317     smallint promptme;
    318     smallint promptmode; /* 0: PS1, 1: PS2 */
    319 #endif
    320     FILE *file;
    321     int (*get) (struct in_str *);
    322     int (*peek) (struct in_str *);
     622enum {
     623    BC_BREAK = 1,
     624    BC_CONTINUE = 2,
    323625};
    324 #define b_getch(input) ((input)->get(input))
    325 #define b_peek(input) ((input)->peek(input))
    326 
     626
     627#if ENABLE_HUSH_FUNCTIONS
     628struct function {
     629    struct function *next;
     630    char *name;
     631    struct command *parent_cmd;
     632    struct pipe *body;
     633# if !BB_MMU
     634    char *body_as_string;
     635# endif
     636};
     637#endif
     638
     639
     640/* set -/+o OPT support. (TODO: make it optional)
     641 * bash supports the following opts:
     642 * allexport       off
     643 * braceexpand     on
     644 * emacs           on
     645 * errexit         off
     646 * errtrace        off
     647 * functrace       off
     648 * hashall         on
     649 * histexpand      off
     650 * history         on
     651 * ignoreeof       off
     652 * interactive-comments    on
     653 * keyword         off
     654 * monitor         on
     655 * noclobber       off
     656 * noexec          off
     657 * noglob          off
     658 * nolog           off
     659 * notify          off
     660 * nounset         off
     661 * onecmd          off
     662 * physical        off
     663 * pipefail        off
     664 * posix           off
     665 * privileged      off
     666 * verbose         off
     667 * vi              off
     668 * xtrace          off
     669 */
     670static const char o_opt_strings[] ALIGN1 = "pipefail\0";
    327671enum {
    328     CHAR_ORDINARY           = 0,
    329     CHAR_ORDINARY_IF_QUOTED = 1, /* example: *, # */
    330     CHAR_IFS                = 2, /* treated as ordinary if quoted */
    331     CHAR_SPECIAL            = 3, /* example: $ */
     672    OPT_O_PIPEFAIL,
     673    NUM_OPT_O
    332674};
    333675
    334 #define HUSH_VER_STR "0.02"
    335676
    336677/* "Globals" within this file */
    337 
    338678/* Sorted roughly by size (smaller offsets == smaller code) */
    339679struct globals {
     680    /* interactive_fd != 0 means we are an interactive shell.
     681     * If we are, then saved_tty_pgrp can also be != 0, meaning
     682     * that controlling tty is available. With saved_tty_pgrp == 0,
     683     * job control still works, but terminal signals
     684     * (^C, ^Z, ^Y, ^\) won't work at all, and background
     685     * process groups can only be created with "cmd &".
     686     * With saved_tty_pgrp != 0, hush will use tcsetpgrp()
     687     * to give tty to the foreground process group,
     688     * and will take it back when the group is stopped (^Z)
     689     * or killed (^C).
     690     */
    340691#if ENABLE_HUSH_INTERACTIVE
    341692    /* 'interactive_fd' is a fd# open to ctty, if we have one
     
    344695    const char *PS1;
    345696    const char *PS2;
     697# define G_interactive_fd (G.interactive_fd)
     698#else
     699# define G_interactive_fd 0
    346700#endif
    347701#if ENABLE_FEATURE_EDITING
    348702    line_input_t *line_input_state;
    349703#endif
     704    pid_t root_pid;
     705    pid_t root_ppid;
     706    pid_t last_bg_pid;
     707#if ENABLE_HUSH_RANDOM_SUPPORT
     708    random_t random_gen;
     709#endif
    350710#if ENABLE_HUSH_JOB
    351711    int run_list_level;
    352     pid_t saved_task_pgrp;
     712    int last_jobid;
    353713    pid_t saved_tty_pgrp;
    354     int last_jobid;
    355714    struct pipe *job_list;
    356     struct pipe *toplevel_list;
    357     smallint ctrl_z_flag;
    358 #endif
    359     smallint fake_mode;
    360     /* these three support $?, $#, and $1 */
     715# define G_saved_tty_pgrp (G.saved_tty_pgrp)
     716#else
     717# define G_saved_tty_pgrp 0
     718#endif
     719    char o_opt[NUM_OPT_O];
     720    smallint flag_SIGINT;
     721#if ENABLE_HUSH_LOOPS
     722    smallint flag_break_continue;
     723#endif
     724#if ENABLE_HUSH_FUNCTIONS
     725    /* 0: outside of a function (or sourced file)
     726     * -1: inside of a function, ok to use return builtin
     727     * 1: return is invoked, skip all till end of func
     728     */
     729    smallint flag_return_in_progress;
     730#endif
     731    smallint n_mode;
     732#if ENABLE_HUSH_MODE_X
     733    smallint x_mode;
     734# define G_x_mode (G.x_mode)
     735#else
     736# define G_x_mode 0
     737#endif
     738    smallint exiting; /* used to prevent EXIT trap recursion */
     739    /* These four support $?, $#, and $1 */
     740    smalluint last_exitcode;
     741    /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
     742    smalluint global_args_malloced;
     743    smalluint inherited_set_is_saved;
     744    /* how many non-NULL argv's we have. NB: $# + 1 */
     745    int global_argc;
    361746    char **global_argv;
    362     int global_argc;
    363     int last_return_code;
     747#if !BB_MMU
     748    char *argv0_for_re_execing;
     749#endif
     750#if ENABLE_HUSH_LOOPS
     751    unsigned depth_break_continue;
     752    unsigned depth_of_loop;
     753#endif
    364754    const char *ifs;
    365     struct close_me *close_me_head;
    366755    const char *cwd;
    367     unsigned last_bg_pid;
    368     struct variable *top_var; /* = &shell_ver (set in main()) */
    369     struct variable shell_ver;
    370 #if ENABLE_FEATURE_SH_STANDALONE
    371     struct nofork_save_area nofork_save;
    372 #endif
     756    struct variable *top_var;
     757    char **expanded_assignments;
     758#if ENABLE_HUSH_FUNCTIONS
     759    struct function *top_func;
     760# if ENABLE_HUSH_LOCAL
     761    struct variable **shadowed_vars_pp;
     762    unsigned func_nest_level;
     763# endif
     764#endif
     765    /* Signal and trap handling */
     766#if ENABLE_HUSH_FAST
     767    unsigned count_SIGCHLD;
     768    unsigned handled_SIGCHLD;
     769    smallint we_have_children;
     770#endif
     771    /* which signals have non-DFL handler (even with no traps set)? */
     772    unsigned non_DFL_mask;
     773    char **traps; /* char *traps[NSIG] */
     774    sigset_t blocked_set;
     775    sigset_t inherited_set;
     776#if HUSH_DEBUG
     777    unsigned long memleak_value;
     778    int debug_indent;
     779#endif
     780    char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2];
     781};
     782#define G (*ptr_to_globals)
     783/* Not #defining name to G.name - this quickly gets unwieldy
     784 * (too many defines). Also, I actually prefer to see when a variable
     785 * is global, thus "G." prefix is a useful hint */
     786#define INIT_G() do { \
     787    SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
     788} while (0)
     789
     790
     791/* Function prototypes for builtins */
     792static int builtin_cd(char **argv) FAST_FUNC;
     793static int builtin_echo(char **argv) FAST_FUNC;
     794static int builtin_eval(char **argv) FAST_FUNC;
     795static int builtin_exec(char **argv) FAST_FUNC;
     796static int builtin_exit(char **argv) FAST_FUNC;
     797static int builtin_export(char **argv) FAST_FUNC;
    373798#if ENABLE_HUSH_JOB
    374     sigjmp_buf toplevel_jb;
    375 #endif
    376     unsigned char charmap[256];
    377     char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2];
    378 };
    379 
    380 #define G (*ptr_to_globals)
    381 
    382 #if !ENABLE_HUSH_INTERACTIVE
    383 enum { interactive_fd = 0 };
    384 #endif
    385 #if !ENABLE_HUSH_JOB
    386 enum { run_list_level = 0 };
    387 #endif
    388 
    389 #if ENABLE_HUSH_INTERACTIVE
    390 #define interactive_fd   (G.interactive_fd  )
    391 #define PS1              (G.PS1             )
    392 #define PS2              (G.PS2             )
    393 #endif
    394 #if ENABLE_FEATURE_EDITING
    395 #define line_input_state (G.line_input_state)
    396 #endif
    397 #if ENABLE_HUSH_JOB
    398 #define run_list_level   (G.run_list_level  )
    399 #define saved_task_pgrp  (G.saved_task_pgrp )
    400 #define saved_tty_pgrp   (G.saved_tty_pgrp  )
    401 #define last_jobid       (G.last_jobid      )
    402 #define job_list         (G.job_list        )
    403 #define toplevel_list    (G.toplevel_list   )
    404 #define toplevel_jb      (G.toplevel_jb     )
    405 #define ctrl_z_flag      (G.ctrl_z_flag     )
    406 #endif /* JOB */
    407 #define global_argv      (G.global_argv     )
    408 #define global_argc      (G.global_argc     )
    409 #define last_return_code (G.last_return_code)
    410 #define ifs              (G.ifs             )
    411 #define fake_mode        (G.fake_mode       )
    412 #define close_me_head    (G.close_me_head   )
    413 #define cwd              (G.cwd             )
    414 #define last_bg_pid      (G.last_bg_pid     )
    415 #define top_var          (G.top_var         )
    416 #define shell_ver        (G.shell_ver       )
    417 #if ENABLE_FEATURE_SH_STANDALONE
    418 #define nofork_save      (G.nofork_save     )
    419 #endif
    420 #if ENABLE_HUSH_JOB
    421 #define toplevel_jb      (G.toplevel_jb     )
    422 #endif
    423 #define charmap          (G.charmap         )
    424 #define user_input_buf   (G.user_input_buf  )
    425 
    426 
    427 #define B_CHUNK  100
    428 #define B_NOSPAC 1
    429 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
    430 
    431 #if 1
    432 /* Normal */
    433 static void syntax(const char *msg)
    434 {
    435     /* Was using fancy stuff:
    436      * (interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...)
    437      * but it SEGVs. ?! Oh well... explicit temp ptr works around that */
    438     void (*fp)(const char *s, ...);
    439 
    440     fp = (interactive_fd ? bb_error_msg : bb_error_msg_and_die);
    441     fp(msg ? "%s: %s" : "syntax error", "syntax error", msg);
    442 }
    443 
    444 #else
    445 /* Debug */
    446 static void syntax_lineno(int line)
    447 {
    448     void (*fp)(const char *s, ...);
    449 
    450     fp = (interactive_fd ? bb_error_msg : bb_error_msg_and_die);
    451     fp("syntax error hush.c:%d", line);
    452 }
    453 #define syntax(str) syntax_lineno(__LINE__)
    454 #endif
    455 
    456 /* Index of subroutines: */
    457 /*   function prototypes for builtins */
    458 static int builtin_cd(char **argv);
    459 static int builtin_eval(char **argv);
    460 static int builtin_exec(char **argv);
    461 static int builtin_exit(char **argv);
    462 static int builtin_export(char **argv);
    463 #if ENABLE_HUSH_JOB
    464 static int builtin_fg_bg(char **argv);
    465 static int builtin_jobs(char **argv);
     799static int builtin_fg_bg(char **argv) FAST_FUNC;
     800static int builtin_jobs(char **argv) FAST_FUNC;
    466801#endif
    467802#if ENABLE_HUSH_HELP
    468 static int builtin_help(char **argv);
    469 #endif
    470 static int builtin_pwd(char **argv);
    471 static int builtin_read(char **argv);
    472 static int builtin_set(char **argv);
    473 static int builtin_shift(char **argv);
    474 static int builtin_source(char **argv);
    475 static int builtin_umask(char **argv);
    476 static int builtin_unset(char **argv);
    477 //static int builtin_not_written(char **argv);
    478 /*   o_string manipulation: */
    479 static int b_check_space(o_string *o, int len);
    480 static int b_addchr(o_string *o, int ch);
    481 static void b_reset(o_string *o);
    482 static int b_addqchr(o_string *o, int ch, int quote);
    483 /*  in_str manipulations: */
    484 static int static_get(struct in_str *i);
    485 static int static_peek(struct in_str *i);
    486 static int file_get(struct in_str *i);
    487 static int file_peek(struct in_str *i);
    488 static void setup_file_in_str(struct in_str *i, FILE *f);
    489 static void setup_string_in_str(struct in_str *i, const char *s);
    490 /*  close_me manipulations: */
    491 static void mark_open(int fd);
    492 static void mark_closed(int fd);
    493 static void close_all(void);
    494 /*  "run" the final data structures: */
    495 #if !defined(DEBUG_CLEAN)
    496 #define free_pipe_list(head, indent) free_pipe_list(head)
    497 #define free_pipe(pi, indent)        free_pipe(pi)
    498 #endif
    499 static int free_pipe_list(struct pipe *head, int indent);
    500 static int free_pipe(struct pipe *pi, int indent);
    501 /*  really run the final data structures: */
    502 static int setup_redirects(struct child_prog *prog, int squirrel[]);
    503 static int run_list_real(struct pipe *pi);
    504 static void pseudo_exec_argv(char **argv) ATTRIBUTE_NORETURN;
    505 static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN;
    506 static int run_pipe_real(struct pipe *pi);
    507 /*   extended glob support: */
    508 static int globhack(const char *src, int flags, glob_t *pglob);
    509 static int glob_needed(const char *s);
    510 static int xglob(o_string *dest, int flags, glob_t *pglob);
    511 /*   variable assignment: */
    512 static int is_assignment(const char *s);
    513 /*   data structure manipulation: */
    514 static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
    515 static void initialize_context(struct p_context *ctx);
    516 static int done_word(o_string *dest, struct p_context *ctx);
    517 static int done_command(struct p_context *ctx);
    518 static int done_pipe(struct p_context *ctx, pipe_style type);
    519 /*   primary string parsing: */
    520 static int redirect_dup_num(struct in_str *input);
    521 static int redirect_opt_num(o_string *o);
    522 #if ENABLE_HUSH_TICK
    523 static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, const char *subst_end);
    524 #endif
    525 static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
    526 static const char *lookup_param(const char *src);
    527 static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input);
    528 static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger);
    529 /*   setup: */
    530 static int parse_and_run_stream(struct in_str *inp, int parse_flag);
    531 static int parse_and_run_string(const char *s, int parse_flag);
    532 static int parse_and_run_file(FILE *f);
    533 /*   job management: */
    534 static int checkjobs(struct pipe* fg_pipe);
    535 #if ENABLE_HUSH_JOB
    536 static int checkjobs_and_fg_shell(struct pipe* fg_pipe);
    537 static void insert_bg_job(struct pipe *pi);
    538 static void remove_bg_job(struct pipe *pi);
    539 static void delete_finished_bg_job(struct pipe *pi);
    540 #else
    541 int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */
    542 #endif
    543 /*     local variable support */
    544 static char **expand_strvec_to_strvec(char **argv);
    545 /* used for eval */
    546 static char *expand_strvec_to_string(char **argv);
    547 /* used for expansion of right hand of assignments */
    548 static char *expand_string_to_string(const char *str);
    549 static struct variable *get_local_var(const char *name);
    550 static int set_local_var(char *str, int flg_export);
    551 static void unset_local_var(const char *name);
     803static int builtin_help(char **argv) FAST_FUNC;
     804#endif
     805#if ENABLE_HUSH_LOCAL
     806static int builtin_local(char **argv) FAST_FUNC;
     807#endif
     808#if HUSH_DEBUG
     809static int builtin_memleak(char **argv) FAST_FUNC;
     810#endif
     811#if ENABLE_PRINTF
     812static int builtin_printf(char **argv) FAST_FUNC;
     813#endif
     814static int builtin_pwd(char **argv) FAST_FUNC;
     815static int builtin_read(char **argv) FAST_FUNC;
     816static int builtin_set(char **argv) FAST_FUNC;
     817static int builtin_shift(char **argv) FAST_FUNC;
     818static int builtin_source(char **argv) FAST_FUNC;
     819static int builtin_test(char **argv) FAST_FUNC;
     820static int builtin_trap(char **argv) FAST_FUNC;
     821static int builtin_type(char **argv) FAST_FUNC;
     822static int builtin_true(char **argv) FAST_FUNC;
     823static int builtin_umask(char **argv) FAST_FUNC;
     824static int builtin_unset(char **argv) FAST_FUNC;
     825static int builtin_wait(char **argv) FAST_FUNC;
     826#if ENABLE_HUSH_LOOPS
     827static int builtin_break(char **argv) FAST_FUNC;
     828static int builtin_continue(char **argv) FAST_FUNC;
     829#endif
     830#if ENABLE_HUSH_FUNCTIONS
     831static int builtin_return(char **argv) FAST_FUNC;
     832#endif
    552833
    553834/* Table of built-in functions.  They can be forked or not, depending on
     
    558839 * still be set at the end. */
    559840struct built_in_command {
    560     const char *cmd;                /* name */
    561     int (*function) (char **argv);  /* function ptr */
     841    const char *b_cmd;
     842    int (*b_function)(char **argv) FAST_FUNC;
    562843#if ENABLE_HUSH_HELP
    563     const char *descr;              /* description */
    564 #define BLTIN(cmd, func, help) { cmd, func, help }
     844    const char *b_descr;
     845# define BLTIN(cmd, func, help) { cmd, func, help }
    565846#else
    566 #define BLTIN(cmd, func, help) { cmd, func }
     847# define BLTIN(cmd, func, help) { cmd, func }
    567848#endif
    568849};
    569850
    570 static const struct built_in_command bltins[] = {
     851static const struct built_in_command bltins1[] = {
     852    BLTIN("."        , builtin_source  , "Run commands in a file"),
     853    BLTIN(":"        , builtin_true    , NULL),
    571854#if ENABLE_HUSH_JOB
    572     BLTIN("bg"    , builtin_fg_bg, "Resume a job in the background"),
    573 #endif
    574 //  BLTIN("break" , builtin_not_written, "Exit for, while or until loop"),
    575     BLTIN("cd"    , builtin_cd, "Change working directory"),
    576 //  BLTIN("continue", builtin_not_written, "Continue for, while or until loop"),
    577     BLTIN("eval"  , builtin_eval, "Construct and run shell command"),
    578     BLTIN("exec"  , builtin_exec, "Exec command, replacing this shell with the exec'd process"),
    579     BLTIN("exit"  , builtin_exit, "Exit from shell"),
    580     BLTIN("export", builtin_export, "Set environment variable"),
     855    BLTIN("bg"       , builtin_fg_bg   , "Resume a job in the background"),
     856#endif
     857#if ENABLE_HUSH_LOOPS
     858    BLTIN("break"    , builtin_break   , "Exit from a loop"),
     859#endif
     860    BLTIN("cd"       , builtin_cd      , "Change directory"),
     861#if ENABLE_HUSH_LOOPS
     862    BLTIN("continue" , builtin_continue, "Start new loop iteration"),
     863#endif
     864    BLTIN("eval"     , builtin_eval    , "Construct and run shell command"),
     865    BLTIN("exec"     , builtin_exec    , "Execute command, don't return to shell"),
     866    BLTIN("exit"     , builtin_exit    , "Exit"),
     867    BLTIN("export"   , builtin_export  , "Set environment variables"),
    581868#if ENABLE_HUSH_JOB
    582     BLTIN("fg"    , builtin_fg_bg, "Bring job into the foreground"),
    583     BLTIN("jobs"  , builtin_jobs, "Lists the active jobs"),
    584 #endif
    585 // TODO: remove pwd? we have it as an applet...
    586     BLTIN("pwd"   , builtin_pwd, "Print current directory"),
    587     BLTIN("read"  , builtin_read, "Input environment variable"),
    588 //  BLTIN("return", builtin_not_written, "Return from a function"),
    589     BLTIN("set"   , builtin_set, "Set/unset shell local variables"),
    590     BLTIN("shift" , builtin_shift, "Shift positional parameters"),
    591 //  BLTIN("trap"  , builtin_not_written, "Trap signals"),
    592 //  BLTIN("ulimit", builtin_not_written, "Controls resource limits"),
    593     BLTIN("umask" , builtin_umask, "Sets file creation mask"),
    594     BLTIN("unset" , builtin_unset, "Unset environment variable"),
    595     BLTIN("."     , builtin_source, "Source-in and run commands in a file"),
     869    BLTIN("fg"       , builtin_fg_bg   , "Bring job into the foreground"),
     870#endif
    596871#if ENABLE_HUSH_HELP
    597     BLTIN("help"  , builtin_help, "List shell built-in commands"),
    598 #endif
    599     BLTIN(NULL, NULL, NULL)
     872    BLTIN("help"     , builtin_help    , NULL),
     873#endif
     874#if ENABLE_HUSH_JOB
     875    BLTIN("jobs"     , builtin_jobs    , "List jobs"),
     876#endif
     877#if ENABLE_HUSH_LOCAL
     878    BLTIN("local"    , builtin_local   , "Set local variables"),
     879#endif
     880#if HUSH_DEBUG
     881    BLTIN("memleak"  , builtin_memleak , NULL),
     882#endif
     883    BLTIN("read"     , builtin_read    , "Input into variable"),
     884#if ENABLE_HUSH_FUNCTIONS
     885    BLTIN("return"   , builtin_return  , "Return from a function"),
     886#endif
     887    BLTIN("set"      , builtin_set     , "Set/unset positional parameters"),
     888    BLTIN("shift"    , builtin_shift   , "Shift positional parameters"),
     889#if ENABLE_HUSH_BASH_COMPAT
     890    BLTIN("source"   , builtin_source  , "Run commands in a file"),
     891#endif
     892    BLTIN("trap"     , builtin_trap    , "Trap signals"),
     893    BLTIN("type"     , builtin_type    , "Show command type"),
     894    BLTIN("ulimit"   , shell_builtin_ulimit  , "Control resource limits"),
     895    BLTIN("umask"    , builtin_umask   , "Set file creation mask"),
     896    BLTIN("unset"    , builtin_unset   , "Unset variables"),
     897    BLTIN("wait"     , builtin_wait    , "Wait for process"),
    600898};
    601 
     899/* For now, echo and test are unconditionally enabled.
     900 * Maybe make it configurable? */
     901static const struct built_in_command bltins2[] = {
     902    BLTIN("["        , builtin_test    , NULL),
     903    BLTIN("echo"     , builtin_echo    , NULL),
     904#if ENABLE_PRINTF
     905    BLTIN("printf"   , builtin_printf  , NULL),
     906#endif
     907    BLTIN("pwd"      , builtin_pwd     , NULL),
     908    BLTIN("test"     , builtin_test    , NULL),
     909};
     910
     911
     912/* Debug printouts.
     913 */
     914#if HUSH_DEBUG
     915/* prevent disasters with G.debug_indent < 0 */
     916# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
     917# define debug_enter() (G.debug_indent++)
     918# define debug_leave() (G.debug_indent--)
     919#else
     920# define indent()      ((void)0)
     921# define debug_enter() ((void)0)
     922# define debug_leave() ((void)0)
     923#endif
     924
     925#ifndef debug_printf
     926# define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__))
     927#endif
     928
     929#ifndef debug_printf_parse
     930# define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__))
     931#endif
     932
     933#ifndef debug_printf_exec
     934#define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__))
     935#endif
     936
     937#ifndef debug_printf_env
     938# define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__))
     939#endif
     940
     941#ifndef debug_printf_jobs
     942# define debug_printf_jobs(...) (indent(), fdprintf(2, __VA_ARGS__))
     943# define DEBUG_JOBS 1
     944#else
     945# define DEBUG_JOBS 0
     946#endif
     947
     948#ifndef debug_printf_expand
     949# define debug_printf_expand(...) (indent(), fdprintf(2, __VA_ARGS__))
     950# define DEBUG_EXPAND 1
     951#else
     952# define DEBUG_EXPAND 0
     953#endif
     954
     955#ifndef debug_printf_varexp
     956# define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__))
     957#endif
     958
     959#ifndef debug_printf_glob
     960# define debug_printf_glob(...) (indent(), fdprintf(2, __VA_ARGS__))
     961# define DEBUG_GLOB 1
     962#else
     963# define DEBUG_GLOB 0
     964#endif
     965
     966#ifndef debug_printf_list
     967# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__))
     968#endif
     969
     970#ifndef debug_printf_subst
     971# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
     972#endif
     973
     974#ifndef debug_printf_clean
     975# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
     976# define DEBUG_CLEAN 1
     977#else
     978# define DEBUG_CLEAN 0
     979#endif
     980
     981#if DEBUG_EXPAND
     982static void debug_print_strings(const char *prefix, char **vv)
     983{
     984    indent();
     985    fdprintf(2, "%s:\n", prefix);
     986    while (*vv)
     987        fdprintf(2, " '%s'\n", *vv++);
     988}
     989#else
     990# define debug_print_strings(prefix, vv) ((void)0)
     991#endif
     992
     993
     994/* Leak hunting. Use hush_leaktool.sh for post-processing.
     995 */
     996#if LEAK_HUNTING
     997static void *xxmalloc(int lineno, size_t size)
     998{
     999    void *ptr = xmalloc((size + 0xff) & ~0xff);
     1000    fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
     1001    return ptr;
     1002}
     1003static void *xxrealloc(int lineno, void *ptr, size_t size)
     1004{
     1005    ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
     1006    fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
     1007    return ptr;
     1008}
     1009static char *xxstrdup(int lineno, const char *str)
     1010{
     1011    char *ptr = xstrdup(str);
     1012    fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
     1013    return ptr;
     1014}
     1015static void xxfree(void *ptr)
     1016{
     1017    fdprintf(2, "free %p\n", ptr);
     1018    free(ptr);
     1019}
     1020# define xmalloc(s)     xxmalloc(__LINE__, s)
     1021# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
     1022# define xstrdup(s)     xxstrdup(__LINE__, s)
     1023# define free(p)        xxfree(p)
     1024#endif
     1025
     1026
     1027/* Syntax and runtime errors. They always abort scripts.
     1028 * In interactive use they usually discard unparsed and/or unexecuted commands
     1029 * and return to the prompt.
     1030 * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
     1031 */
     1032#if HUSH_DEBUG < 2
     1033# define die_if_script(lineno, ...)             die_if_script(__VA_ARGS__)
     1034# define syntax_error(lineno, msg)              syntax_error(msg)
     1035# define syntax_error_at(lineno, msg)           syntax_error_at(msg)
     1036# define syntax_error_unterm_ch(lineno, ch)     syntax_error_unterm_ch(ch)
     1037# define syntax_error_unterm_str(lineno, s)     syntax_error_unterm_str(s)
     1038# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
     1039#endif
     1040
     1041static void die_if_script(unsigned lineno, const char *fmt, ...)
     1042{
     1043    va_list p;
     1044
     1045#if HUSH_DEBUG >= 2
     1046    bb_error_msg("hush.c:%u", lineno);
     1047#endif
     1048    va_start(p, fmt);
     1049    bb_verror_msg(fmt, p, NULL);
     1050    va_end(p);
     1051    if (!G_interactive_fd)
     1052        xfunc_die();
     1053}
     1054
     1055static void syntax_error(unsigned lineno, const char *msg)
     1056{
     1057    if (msg)
     1058        die_if_script(lineno, "syntax error: %s", msg);
     1059    else
     1060        die_if_script(lineno, "syntax error", NULL);
     1061}
     1062
     1063static void syntax_error_at(unsigned lineno, const char *msg)
     1064{
     1065    die_if_script(lineno, "syntax error at '%s'", msg);
     1066}
     1067
     1068static void syntax_error_unterm_str(unsigned lineno, const char *s)
     1069{
     1070    die_if_script(lineno, "syntax error: unterminated %s", s);
     1071}
     1072
     1073/* It so happens that all such cases are totally fatal
     1074 * even if shell is interactive: EOF while looking for closing
     1075 * delimiter. There is nowhere to read stuff from after that,
     1076 * it's EOF! The only choice is to terminate.
     1077 */
     1078static void syntax_error_unterm_ch(unsigned lineno, char ch) NORETURN;
     1079static void syntax_error_unterm_ch(unsigned lineno, char ch)
     1080{
     1081    char msg[2] = { ch, '\0' };
     1082    syntax_error_unterm_str(lineno, msg);
     1083    xfunc_die();
     1084}
     1085
     1086static void syntax_error_unexpected_ch(unsigned lineno, int ch)
     1087{
     1088    char msg[2];
     1089    msg[0] = ch;
     1090    msg[1] = '\0';
     1091    die_if_script(lineno, "syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
     1092}
     1093
     1094#if HUSH_DEBUG < 2
     1095# undef die_if_script
     1096# undef syntax_error
     1097# undef syntax_error_at
     1098# undef syntax_error_unterm_ch
     1099# undef syntax_error_unterm_str
     1100# undef syntax_error_unexpected_ch
     1101#else
     1102# define die_if_script(...)             die_if_script(__LINE__, __VA_ARGS__)
     1103# define syntax_error(msg)              syntax_error(__LINE__, msg)
     1104# define syntax_error_at(msg)           syntax_error_at(__LINE__, msg)
     1105# define syntax_error_unterm_ch(ch)     syntax_error_unterm_ch(__LINE__, ch)
     1106# define syntax_error_unterm_str(s)     syntax_error_unterm_str(__LINE__, s)
     1107# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
     1108#endif
     1109
     1110
     1111#if ENABLE_HUSH_INTERACTIVE
     1112static void cmdedit_update_prompt(void);
     1113#else
     1114# define cmdedit_update_prompt() ((void)0)
     1115#endif
     1116
     1117
     1118/* Utility functions
     1119 */
     1120/* Replace each \x with x in place, return ptr past NUL. */
     1121static char *unbackslash(char *src)
     1122{
     1123    char *dst = src = strchrnul(src, '\\');
     1124    while (1) {
     1125        if (*src == '\\')
     1126            src++;
     1127        if ((*dst++ = *src++) == '\0')
     1128            break;
     1129    }
     1130    return dst;
     1131}
     1132
     1133static char **add_strings_to_strings(char **strings, char **add, int need_to_dup)
     1134{
     1135    int i;
     1136    unsigned count1;
     1137    unsigned count2;
     1138    char **v;
     1139
     1140    v = strings;
     1141    count1 = 0;
     1142    if (v) {
     1143        while (*v) {
     1144            count1++;
     1145            v++;
     1146        }
     1147    }
     1148    count2 = 0;
     1149    v = add;
     1150    while (*v) {
     1151        count2++;
     1152        v++;
     1153    }
     1154    v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*));
     1155    v[count1 + count2] = NULL;
     1156    i = count2;
     1157    while (--i >= 0)
     1158        v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
     1159    return v;
     1160}
     1161#if LEAK_HUNTING
     1162static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
     1163{
     1164    char **ptr = add_strings_to_strings(strings, add, need_to_dup);
     1165    fdprintf(2, "line %d: add_strings_to_strings %p\n", lineno, ptr);
     1166    return ptr;
     1167}
     1168#define add_strings_to_strings(strings, add, need_to_dup) \
     1169    xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup)
     1170#endif
     1171
     1172/* Note: takes ownership of "add" ptr (it is not strdup'ed) */
     1173static char **add_string_to_strings(char **strings, char *add)
     1174{
     1175    char *v[2];
     1176    v[0] = add;
     1177    v[1] = NULL;
     1178    return add_strings_to_strings(strings, v, /*dup:*/ 0);
     1179}
     1180#if LEAK_HUNTING
     1181static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
     1182{
     1183    char **ptr = add_string_to_strings(strings, add);
     1184    fdprintf(2, "line %d: add_string_to_strings %p\n", lineno, ptr);
     1185    return ptr;
     1186}
     1187#define add_string_to_strings(strings, add) \
     1188    xx_add_string_to_strings(__LINE__, strings, add)
     1189#endif
     1190
     1191static void free_strings(char **strings)
     1192{
     1193    char **v;
     1194
     1195    if (!strings)
     1196        return;
     1197    v = strings;
     1198    while (*v) {
     1199        free(*v);
     1200        v++;
     1201    }
     1202    free(strings);
     1203}
     1204
     1205
     1206/* Helpers for setting new $n and restoring them back
     1207 */
     1208typedef struct save_arg_t {
     1209    char *sv_argv0;
     1210    char **sv_g_argv;
     1211    int sv_g_argc;
     1212    smallint sv_g_malloced;
     1213} save_arg_t;
     1214
     1215static void save_and_replace_G_args(save_arg_t *sv, char **argv)
     1216{
     1217    int n;
     1218
     1219    sv->sv_argv0 = argv[0];
     1220    sv->sv_g_argv = G.global_argv;
     1221    sv->sv_g_argc = G.global_argc;
     1222    sv->sv_g_malloced = G.global_args_malloced;
     1223
     1224    argv[0] = G.global_argv[0]; /* retain $0 */
     1225    G.global_argv = argv;
     1226    G.global_args_malloced = 0;
     1227
     1228    n = 1;
     1229    while (*++argv)
     1230        n++;
     1231    G.global_argc = n;
     1232}
     1233
     1234static void restore_G_args(save_arg_t *sv, char **argv)
     1235{
     1236    char **pp;
     1237
     1238    if (G.global_args_malloced) {
     1239        /* someone ran "set -- arg1 arg2 ...", undo */
     1240        pp = G.global_argv;
     1241        while (*++pp) /* note: does not free $0 */
     1242            free(*pp);
     1243        free(G.global_argv);
     1244    }
     1245    argv[0] = sv->sv_argv0;
     1246    G.global_argv = sv->sv_g_argv;
     1247    G.global_argc = sv->sv_g_argc;
     1248    G.global_args_malloced = sv->sv_g_malloced;
     1249}
     1250
     1251
     1252/* Basic theory of signal handling in shell
     1253 * ========================================
     1254 * This does not describe what hush does, rather, it is current understanding
     1255 * what it _should_ do. If it doesn't, it's a bug.
     1256 * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap
     1257 *
     1258 * Signals are handled only after each pipe ("cmd | cmd | cmd" thing)
     1259 * is finished or backgrounded. It is the same in interactive and
     1260 * non-interactive shells, and is the same regardless of whether
     1261 * a user trap handler is installed or a shell special one is in effect.
     1262 * ^C or ^Z from keyboard seems to execute "at once" because it usually
     1263 * backgrounds (i.e. stops) or kills all members of currently running
     1264 * pipe.
     1265 *
     1266 * Wait builtin in interruptible by signals for which user trap is set
     1267 * or by SIGINT in interactive shell.
     1268 *
     1269 * Trap handlers will execute even within trap handlers. (right?)
     1270 *
     1271 * User trap handlers are forgotten when subshell ("(cmd)") is entered,
     1272 * except for handlers set to '' (empty string).
     1273 *
     1274 * If job control is off, backgrounded commands ("cmd &")
     1275 * have SIGINT, SIGQUIT set to SIG_IGN.
     1276 *
     1277 * Commands which are run in command substitution ("`cmd`")
     1278 * have SIGTTIN, SIGTTOU, SIGTSTP set to SIG_IGN.
     1279 *
     1280 * Ordinary commands have signals set to SIG_IGN/DFL as inherited
     1281 * by the shell from its parent.
     1282 *
     1283 * Signals which differ from SIG_DFL action
     1284 * (note: child (i.e., [v]forked) shell is not an interactive shell):
     1285 *
     1286 * SIGQUIT: ignore
     1287 * SIGTERM (interactive): ignore
     1288 * SIGHUP (interactive):
     1289 *    send SIGCONT to stopped jobs, send SIGHUP to all jobs and exit
     1290 * SIGTTIN, SIGTTOU, SIGTSTP (if job control is on): ignore
     1291 *    Note that ^Z is handled not by trapping SIGTSTP, but by seeing
     1292 *    that all pipe members are stopped. Try this in bash:
     1293 *    while :; do :; done - ^Z does not background it
     1294 *    (while :; do :; done) - ^Z backgrounds it
     1295 * SIGINT (interactive): wait for last pipe, ignore the rest
     1296 *    of the command line, show prompt. NB: ^C does not send SIGINT
     1297 *    to interactive shell while shell is waiting for a pipe,
     1298 *    since shell is bg'ed (is not in foreground process group).
     1299 *    Example 1: this waits 5 sec, but does not execute ls:
     1300 *    "echo $$; sleep 5; ls -l" + "kill -INT <pid>"
     1301 *    Example 2: this does not wait and does not execute ls:
     1302 *    "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>"
     1303 *    Example 3: this does not wait 5 sec, but executes ls:
     1304 *    "sleep 5; ls -l" + press ^C
     1305 *
     1306 * (What happens to signals which are IGN on shell start?)
     1307 * (What happens with signal mask on shell start?)
     1308 *
     1309 * Implementation in hush
     1310 * ======================
     1311 * We use in-kernel pending signal mask to determine which signals were sent.
     1312 * We block all signals which we don't want to take action immediately,
     1313 * i.e. we block all signals which need to have special handling as described
     1314 * above, and all signals which have traps set.
     1315 * After each pipe execution, we extract any pending signals via sigtimedwait()
     1316 * and act on them.
     1317 *
     1318 * unsigned non_DFL_mask: a mask of such "special" signals
     1319 * sigset_t blocked_set:  current blocked signal set
     1320 *
     1321 * "trap - SIGxxx":
     1322 *    clear bit in blocked_set unless it is also in non_DFL_mask
     1323 * "trap 'cmd' SIGxxx":
     1324 *    set bit in blocked_set (even if 'cmd' is '')
     1325 * after [v]fork, if we plan to be a shell:
     1326 *    unblock signals with special interactive handling
     1327 *    (child shell is not interactive),
     1328 *    unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
     1329 * after [v]fork, if we plan to exec:
     1330 *    POSIX says fork clears pending signal mask in child - no need to clear it.
     1331 *    Restore blocked signal set to one inherited by shell just prior to exec.
     1332 *
     1333 * Note: as a result, we do not use signal handlers much. The only uses
     1334 * are to count SIGCHLDs
     1335 * and to restore tty pgrp on signal-induced exit.
     1336 *
     1337 * Note 2 (compat):
     1338 * Standard says "When a subshell is entered, traps that are not being ignored
     1339 * are set to the default actions". bash interprets it so that traps which
     1340 * are set to '' (ignore) are NOT reset to defaults. We do the same.
     1341 */
     1342enum {
     1343    SPECIAL_INTERACTIVE_SIGS = 0
     1344        | (1 << SIGTERM)
     1345        | (1 << SIGINT)
     1346        | (1 << SIGHUP)
     1347        ,
     1348    SPECIAL_JOB_SIGS = 0
    6021349#if ENABLE_HUSH_JOB
    603 
    604 /* move to libbb? */
    605 static void signal_SA_RESTART(int sig, void (*handler)(int))
    606 {
    607     struct sigaction sa;
    608     sa.sa_handler = handler;
    609     sa.sa_flags = SA_RESTART;
    610     sigemptyset(&sa.sa_mask);
    611     sigaction(sig, &sa, NULL);
    612 }
    613 
    614 /* Signals are grouped, we handle them in batches */
    615 static void set_fatal_sighandler(void (*handler)(int))
    616 {
    617     signal(SIGILL , handler);
    618     signal(SIGTRAP, handler);
    619     signal(SIGABRT, handler);
    620     signal(SIGFPE , handler);
    621     signal(SIGBUS , handler);
    622     signal(SIGSEGV, handler);
    623     /* bash 3.2 seems to handle these just like 'fatal' ones */
    624     signal(SIGHUP , handler);
    625     signal(SIGPIPE, handler);
    626     signal(SIGALRM, handler);
    627 }
    628 static void set_jobctrl_sighandler(void (*handler)(int))
    629 {
    630     signal(SIGTSTP, handler);
    631     signal(SIGTTIN, handler);
    632     signal(SIGTTOU, handler);
    633 }
    634 static void set_misc_sighandler(void (*handler)(int))
    635 {
    636     signal(SIGINT , handler);
    637     signal(SIGQUIT, handler);
    638     signal(SIGTERM, handler);
    639 }
    640 /* SIGCHLD is special and handled separately */
    641 
    642 static void set_every_sighandler(void (*handler)(int))
    643 {
    644     set_fatal_sighandler(handler);
    645     set_jobctrl_sighandler(handler);
    646     set_misc_sighandler(handler);
    647     signal(SIGCHLD, handler);
    648 }
    649 
    650 static void handler_ctrl_c(int sig)
    651 {
    652     debug_printf_jobs("got sig %d\n", sig);
    653 // as usual we can have all kinds of nasty problems with leaked malloc data here
    654     siglongjmp(toplevel_jb, 1);
    655 }
    656 
    657 static void handler_ctrl_z(int sig)
    658 {
    659     pid_t pid;
    660 
    661     debug_printf_jobs("got tty sig %d in pid %d\n", sig, getpid());
    662     pid = fork();
    663     if (pid < 0) /* can't fork. Pretend there was no ctrl-Z */
    664         return;
    665     ctrl_z_flag = 1;
    666     if (!pid) { /* child */
    667         setpgrp();
    668         debug_printf_jobs("set pgrp for child %d ok\n", getpid());
    669         set_every_sighandler(SIG_DFL);
    670         raise(SIGTSTP); /* resend TSTP so that child will be stopped */
    671         debug_printf_jobs("returning in child\n");
    672         /* return to nofork, it will eventually exit now,
    673          * not return back to shell */
    674         return;
    675     }
    676     /* parent */
    677     /* finish filling up pipe info */
    678     toplevel_list->pgrp = pid; /* child is in its own pgrp */
    679     toplevel_list->progs[0].pid = pid;
    680     /* parent needs to longjmp out of running nofork.
    681      * we will "return" exitcode 0, with child put in background */
    682 // as usual we can have all kinds of nasty problems with leaked malloc data here
    683     debug_printf_jobs("siglongjmp in parent\n");
    684     siglongjmp(toplevel_jb, 1);
    685 }
     1350        | (1 << SIGTTIN)
     1351        | (1 << SIGTTOU)
     1352        | (1 << SIGTSTP)
     1353#endif
     1354};
     1355
     1356#if ENABLE_HUSH_FAST
     1357static void SIGCHLD_handler(int sig UNUSED_PARAM)
     1358{
     1359    G.count_SIGCHLD++;
     1360//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
     1361}
     1362#endif
     1363
     1364#if ENABLE_HUSH_JOB
     1365
     1366/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
     1367# define disable_restore_tty_pgrp_on_exit() (die_sleep = 0)
     1368/* After [v]fork, in parent: restore tty pgrp on xfunc death */
     1369# define enable_restore_tty_pgrp_on_exit()  (die_sleep = -1)
    6861370
    6871371/* Restores tty foreground process group, and exits.
    6881372 * May be called as signal handler for fatal signal
    689  * (will faithfully resend signal to itself, producing correct exit state)
     1373 * (will resend signal to itself, producing correct exit state)
    6901374 * or called directly with -EXITCODE.
    6911375 * We also call it if xfunc is exiting. */
    692 static void sigexit(int sig) ATTRIBUTE_NORETURN;
     1376static void sigexit(int sig) NORETURN;
    6931377static void sigexit(int sig)
    6941378{
    695     sigset_t block_all;
    696 
    6971379    /* Disable all signals: job control, SIGPIPE, etc. */
    698     sigfillset(&block_all);
    699     sigprocmask(SIG_SETMASK, &block_all, NULL);
    700 
    701     if (interactive_fd)
    702         tcsetpgrp(interactive_fd, saved_tty_pgrp);
     1380    sigprocmask_allsigs(SIG_BLOCK);
     1381
     1382    /* Careful: we can end up here after [v]fork. Do not restore
     1383     * tty pgrp then, only top-level shell process does that */
     1384    if (G_saved_tty_pgrp && getpid() == G.root_pid)
     1385        tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
    7031386
    7041387    /* Not a signal, just exit */
     
    7061389        _exit(- sig);
    7071390
    708     /* Enable only this sig and kill ourself with it */
    709     signal(sig, SIG_DFL);
    710     sigdelset(&block_all, sig);
    711     sigprocmask(SIG_SETMASK, &block_all, NULL);
    712     raise(sig);
    713     _exit(1); /* Should not reach it */
    714 }
     1391    kill_myself_with_sig(sig); /* does not return */
     1392}
     1393#else
     1394
     1395# define disable_restore_tty_pgrp_on_exit() ((void)0)
     1396# define enable_restore_tty_pgrp_on_exit()  ((void)0)
     1397
     1398#endif
    7151399
    7161400/* Restores tty foreground process group, and exits. */
    717 static void hush_exit(int exitcode) ATTRIBUTE_NORETURN;
     1401static void hush_exit(int exitcode) NORETURN;
    7181402static void hush_exit(int exitcode)
    7191403{
    720     fflush(NULL); /* flush all streams */
     1404    if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) {
     1405        /* Prevent recursion:
     1406         * trap "echo Hi; exit" EXIT; exit
     1407         */
     1408        char *argv[3];
     1409        /* argv[0] is unused */
     1410        argv[1] = G.traps[0];
     1411        argv[2] = NULL;
     1412        G.exiting = 1; /* prevent EXIT trap recursion */
     1413        /* Note: G.traps[0] is not cleared!
     1414         * "trap" will still show it, if executed
     1415         * in the handler */
     1416        builtin_eval(argv);
     1417    }
     1418
     1419#if ENABLE_FEATURE_CLEAN_UP
     1420    {
     1421        struct variable *cur_var;
     1422        if (G.cwd != bb_msg_unknown)
     1423            free((char*)G.cwd);
     1424        cur_var = G.top_var;
     1425        while (cur_var) {
     1426            struct variable *tmp = cur_var;
     1427            if (!cur_var->max_len)
     1428                free(cur_var->varstr);
     1429            cur_var = cur_var->next;
     1430            free(tmp);
     1431        }
     1432    }
     1433#endif
     1434
     1435#if ENABLE_HUSH_JOB
     1436    fflush_all();
    7211437    sigexit(- (exitcode & 0xff));
    722 }
    723 
    724 #else /* !JOB */
    725 
    726 #define set_fatal_sighandler(handler)   ((void)0)
    727 #define set_jobctrl_sighandler(handler) ((void)0)
    728 #define set_misc_sighandler(handler)    ((void)0)
    729 #define hush_exit(e)                    exit(e)
    730 
    731 #endif /* JOB */
    732 
    733 
    734 static const char *set_cwd(void)
    735 {
    736     if (cwd == bb_msg_unknown)
    737         cwd = NULL;     /* xrealloc_getcwd_or_warn(arg) calls free(arg)! */
    738     cwd = xrealloc_getcwd_or_warn((char *)cwd);
    739     if (!cwd)
    740         cwd = bb_msg_unknown;
    741     return cwd;
    742 }
    743 
    744 /* built-in 'eval' handler */
    745 static int builtin_eval(char **argv)
    746 {
    747     int rcode = EXIT_SUCCESS;
    748 
    749     if (argv[1]) {
    750         char *str = expand_strvec_to_string(argv + 1);
    751         parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP |
    752                     PARSEFLAG_SEMICOLON);
     1438#else
     1439    exit(exitcode);
     1440#endif
     1441}
     1442
     1443
     1444static int check_and_run_traps(int sig)
     1445{
     1446    /* I want it in rodata, not in bss.
     1447     * gcc 4.2.1 puts it in rodata only if it has { 0, 0 }
     1448     * initializer. But other compilers may still use bss.
     1449     * TODO: find more portable solution.
     1450     */
     1451    static const struct timespec zero_timespec = { 0, 0 };
     1452    smalluint save_rcode;
     1453    int last_sig = 0;
     1454
     1455    if (sig)
     1456        goto jump_in;
     1457    while (1) {
     1458        sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec);
     1459        if (sig <= 0)
     1460            break;
     1461 jump_in:
     1462        last_sig = sig;
     1463        if (G.traps && G.traps[sig]) {
     1464            if (G.traps[sig][0]) {
     1465                /* We have user-defined handler */
     1466                char *argv[3];
     1467                /* argv[0] is unused */
     1468                argv[1] = G.traps[sig];
     1469                argv[2] = NULL;
     1470                save_rcode = G.last_exitcode;
     1471                builtin_eval(argv);
     1472                G.last_exitcode = save_rcode;
     1473            } /* else: "" trap, ignoring signal */
     1474            continue;
     1475        }
     1476        /* not a trap: special action */
     1477        switch (sig) {
     1478#if ENABLE_HUSH_FAST
     1479        case SIGCHLD:
     1480            G.count_SIGCHLD++;
     1481//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
     1482            break;
     1483#endif
     1484        case SIGINT:
     1485            /* Builtin was ^C'ed, make it look prettier: */
     1486            bb_putchar('\n');
     1487            G.flag_SIGINT = 1;
     1488            break;
     1489#if ENABLE_HUSH_JOB
     1490        case SIGHUP: {
     1491            struct pipe *job;
     1492            /* bash is observed to signal whole process groups,
     1493             * not individual processes */
     1494            for (job = G.job_list; job; job = job->next) {
     1495                if (job->pgrp <= 0)
     1496                    continue;
     1497                debug_printf_exec("HUPing pgrp %d\n", job->pgrp);
     1498                if (kill(- job->pgrp, SIGHUP) == 0)
     1499                    kill(- job->pgrp, SIGCONT);
     1500            }
     1501            sigexit(SIGHUP);
     1502        }
     1503#endif
     1504        default: /* ignored: */
     1505            /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
     1506            break;
     1507        }
     1508    }
     1509    return last_sig;
     1510}
     1511
     1512
     1513static const char *get_cwd(int force)
     1514{
     1515    if (force || G.cwd == NULL) {
     1516        /* xrealloc_getcwd_or_warn(arg) calls free(arg),
     1517         * we must not try to free(bb_msg_unknown) */
     1518        if (G.cwd == bb_msg_unknown)
     1519            G.cwd = NULL;
     1520        G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd);
     1521        if (!G.cwd)
     1522            G.cwd = bb_msg_unknown;
     1523    }
     1524    return G.cwd;
     1525}
     1526
     1527
     1528/*
     1529 * Shell and environment variable support
     1530 */
     1531static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
     1532{
     1533    struct variable **pp;
     1534    struct variable *cur;
     1535
     1536    pp = &G.top_var;
     1537    while ((cur = *pp) != NULL) {
     1538        if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
     1539            return pp;
     1540        pp = &cur->next;
     1541    }
     1542    return NULL;
     1543}
     1544
     1545static const char* FAST_FUNC get_local_var_value(const char *name)
     1546{
     1547    struct variable **vpp;
     1548    unsigned len = strlen(name);
     1549
     1550    if (G.expanded_assignments) {
     1551        char **cpp = G.expanded_assignments;
     1552        while (*cpp) {
     1553            char *cp = *cpp;
     1554            if (strncmp(cp, name, len) == 0 && cp[len] == '=')
     1555                return cp + len + 1;
     1556            cpp++;
     1557        }
     1558    }
     1559
     1560    vpp = get_ptr_to_local_var(name, len);
     1561    if (vpp)
     1562        return (*vpp)->varstr + len + 1;
     1563
     1564    if (strcmp(name, "PPID") == 0)
     1565        return utoa(G.root_ppid);
     1566    // bash compat: UID? EUID?
     1567#if ENABLE_HUSH_RANDOM_SUPPORT
     1568    if (strcmp(name, "RANDOM") == 0)
     1569        return utoa(next_random(&G.random_gen));
     1570#endif
     1571    return NULL;
     1572}
     1573
     1574/* str holds "NAME=VAL" and is expected to be malloced.
     1575 * We take ownership of it.
     1576 * flg_export:
     1577 *  0: do not change export flag
     1578 *     (if creating new variable, flag will be 0)
     1579 *  1: set export flag and putenv the variable
     1580 * -1: clear export flag and unsetenv the variable
     1581 * flg_read_only is set only when we handle -R var=val
     1582 */
     1583#if !BB_MMU && ENABLE_HUSH_LOCAL
     1584/* all params are used */
     1585#elif BB_MMU && ENABLE_HUSH_LOCAL
     1586#define set_local_var(str, flg_export, local_lvl, flg_read_only) \
     1587    set_local_var(str, flg_export, local_lvl)
     1588#elif BB_MMU && !ENABLE_HUSH_LOCAL
     1589#define set_local_var(str, flg_export, local_lvl, flg_read_only) \
     1590    set_local_var(str, flg_export)
     1591#elif !BB_MMU && !ENABLE_HUSH_LOCAL
     1592#define set_local_var(str, flg_export, local_lvl, flg_read_only) \
     1593    set_local_var(str, flg_export, flg_read_only)
     1594#endif
     1595static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_only)
     1596{
     1597    struct variable **var_pp;
     1598    struct variable *cur;
     1599    char *eq_sign;
     1600    int name_len;
     1601
     1602    eq_sign = strchr(str, '=');
     1603    if (!eq_sign) { /* not expected to ever happen? */
    7531604        free(str);
    754         rcode = last_return_code;
    755     }
    756     return rcode;
    757 }
    758 
    759 /* built-in 'cd <path>' handler */
    760 static int builtin_cd(char **argv)
    761 {
    762     const char *newdir;
    763     if (argv[1] == NULL)
    764         newdir = getenv("HOME") ? : "/";
    765     else
    766         newdir = argv[1];
    767     if (chdir(newdir)) {
    768         printf("cd: %s: %s\n", newdir, strerror(errno));
    769         return EXIT_FAILURE;
    770     }
    771     set_cwd();
     1605        return -1;
     1606    }
     1607
     1608    name_len = eq_sign - str + 1; /* including '=' */
     1609    var_pp = &G.top_var;
     1610    while ((cur = *var_pp) != NULL) {
     1611        if (strncmp(cur->varstr, str, name_len) != 0) {
     1612            var_pp = &cur->next;
     1613            continue;
     1614        }
     1615        /* We found an existing var with this name */
     1616        if (cur->flg_read_only) {
     1617#if !BB_MMU
     1618            if (!flg_read_only)
     1619#endif
     1620                bb_error_msg("%s: readonly variable", str);
     1621            free(str);
     1622            return -1;
     1623        }
     1624        if (flg_export == -1) { // "&& cur->flg_export" ?
     1625            debug_printf_env("%s: unsetenv '%s'\n", __func__, str);
     1626            *eq_sign = '\0';
     1627            unsetenv(str);
     1628            *eq_sign = '=';
     1629        }
     1630#if ENABLE_HUSH_LOCAL
     1631        if (cur->func_nest_level < local_lvl) {
     1632            /* New variable is declared as local,
     1633             * and existing one is global, or local
     1634             * from enclosing function.
     1635             * Remove and save old one: */
     1636            *var_pp = cur->next;
     1637            cur->next = *G.shadowed_vars_pp;
     1638            *G.shadowed_vars_pp = cur;
     1639            /* bash 3.2.33(1) and exported vars:
     1640             * # export z=z
     1641             * # f() { local z=a; env | grep ^z; }
     1642             * # f
     1643             * z=a
     1644             * # env | grep ^z
     1645             * z=z
     1646             */
     1647            if (cur->flg_export)
     1648                flg_export = 1;
     1649            break;
     1650        }
     1651#endif
     1652        if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) {
     1653 free_and_exp:
     1654            free(str);
     1655            goto exp;
     1656        }
     1657        if (cur->max_len != 0) {
     1658            if (cur->max_len >= strlen(str)) {
     1659                /* This one is from startup env, reuse space */
     1660                strcpy(cur->varstr, str);
     1661                goto free_and_exp;
     1662            }
     1663        } else {
     1664            /* max_len == 0 signifies "malloced" var, which we can
     1665             * (and has to) free */
     1666            free(cur->varstr);
     1667        }
     1668        cur->max_len = 0;
     1669        goto set_str_and_exp;
     1670    }
     1671
     1672    /* Not found - create new variable struct */
     1673    cur = xzalloc(sizeof(*cur));
     1674#if ENABLE_HUSH_LOCAL
     1675    cur->func_nest_level = local_lvl;
     1676#endif
     1677    cur->next = *var_pp;
     1678    *var_pp = cur;
     1679
     1680 set_str_and_exp:
     1681    cur->varstr = str;
     1682#if !BB_MMU
     1683    cur->flg_read_only = flg_read_only;
     1684#endif
     1685 exp:
     1686    if (flg_export == 1)
     1687        cur->flg_export = 1;
     1688    if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
     1689        cmdedit_update_prompt();
     1690    if (cur->flg_export) {
     1691        if (flg_export == -1) {
     1692            cur->flg_export = 0;
     1693            /* unsetenv was already done */
     1694        } else {
     1695            debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr);
     1696            return putenv(cur->varstr);
     1697        }
     1698    }
     1699    return 0;
     1700}
     1701
     1702/* Used at startup and after each cd */
     1703static void set_pwd_var(int exp)
     1704{
     1705    set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)),
     1706        /*exp:*/ exp, /*lvl:*/ 0, /*ro:*/ 0);
     1707}
     1708
     1709static int unset_local_var_len(const char *name, int name_len)
     1710{
     1711    struct variable *cur;
     1712    struct variable **var_pp;
     1713
     1714    if (!name)
     1715        return EXIT_SUCCESS;
     1716    var_pp = &G.top_var;
     1717    while ((cur = *var_pp) != NULL) {
     1718        if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
     1719            if (cur->flg_read_only) {
     1720                bb_error_msg("%s: readonly variable", name);
     1721                return EXIT_FAILURE;
     1722            }
     1723            *var_pp = cur->next;
     1724            debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
     1725            bb_unsetenv(cur->varstr);
     1726            if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
     1727                cmdedit_update_prompt();
     1728            if (!cur->max_len)
     1729                free(cur->varstr);
     1730            free(cur);
     1731            return EXIT_SUCCESS;
     1732        }
     1733        var_pp = &cur->next;
     1734    }
    7721735    return EXIT_SUCCESS;
    7731736}
    7741737
    775 /* built-in 'exec' handler */
    776 static int builtin_exec(char **argv)
    777 {
    778     if (argv[1] == NULL)
    779         return EXIT_SUCCESS;   /* Really? */
    780     pseudo_exec_argv(argv + 1);
    781     /* never returns */
    782 }
    783 
    784 /* built-in 'exit' handler */
    785 static int builtin_exit(char **argv)
    786 {
    787 // TODO: bash does it ONLY on top-level sh exit (+interacive only?)
    788     //puts("exit"); /* bash does it */
    789 // TODO: warn if we have background jobs: "There are stopped jobs"
    790 // On second consecutive 'exit', exit anyway.
    791 
    792     if (argv[1] == NULL)
    793         hush_exit(last_return_code);
    794     /* mimic bash: exit 123abc == exit 255 + error msg */
    795     xfunc_error_retval = 255;
    796     /* bash: exit -2 == exit 254, no error msg */
    797     hush_exit(xatoi(argv[1]) & 0xff);
    798 }
    799 
    800 /* built-in 'export VAR=value' handler */
    801 static int builtin_export(char **argv)
    802 {
    803     const char *value;
    804     char *name = argv[1];
    805 
    806     if (name == NULL) {
    807         // TODO:
    808         // ash emits: export VAR='VAL'
    809         // bash: declare -x VAR="VAL"
    810         // (both also escape as needed (quotes, $, etc))
    811         char **e = environ;
    812         if (e)
    813             while (*e)
    814                 puts(*e++);
    815         return EXIT_SUCCESS;
    816     }
    817 
    818     value = strchr(name, '=');
    819     if (!value) {
    820         /* They are exporting something without a =VALUE */
    821         struct variable *var;
    822 
    823         var = get_local_var(name);
    824         if (var) {
    825             var->flg_export = 1;
     1738static int unset_local_var(const char *name)
     1739{
     1740    return unset_local_var_len(name, strlen(name));
     1741}
     1742
     1743static void unset_vars(char **strings)
     1744{
     1745    char **v;
     1746
     1747    if (!strings)
     1748        return;
     1749    v = strings;
     1750    while (*v) {
     1751        const char *eq = strchrnul(*v, '=');
     1752        unset_local_var_len(*v, (int)(eq - *v));
     1753        v++;
     1754    }
     1755    free(strings);
     1756}
     1757
     1758static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
     1759{
     1760    char *var = xasprintf("%s=%s", name, val);
     1761    set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
     1762}
     1763
     1764
     1765/*
     1766 * Helpers for "var1=val1 var2=val2 cmd" feature
     1767 */
     1768static void add_vars(struct variable *var)
     1769{
     1770    struct variable *next;
     1771
     1772    while (var) {
     1773        next = var->next;
     1774        var->next = G.top_var;
     1775        G.top_var = var;
     1776        if (var->flg_export) {
     1777            debug_printf_env("%s: restoring exported '%s'\n", __func__, var->varstr);
    8261778            putenv(var->varstr);
    827         }
    828         /* bash does not return an error when trying to export
    829          * an undefined variable.  Do likewise. */
    830         return EXIT_SUCCESS;
    831     }
    832 
    833     set_local_var(xstrdup(name), 1);
    834     return EXIT_SUCCESS;
    835 }
    836 
    837 #if ENABLE_HUSH_JOB
    838 /* built-in 'fg' and 'bg' handler */
    839 static int builtin_fg_bg(char **argv)
    840 {
    841     int i, jobnum;
    842     struct pipe *pi;
    843 
    844     if (!interactive_fd)
    845         return EXIT_FAILURE;
    846     /* If they gave us no args, assume they want the last backgrounded task */
    847     if (!argv[1]) {
    848         for (pi = job_list; pi; pi = pi->next) {
    849             if (pi->jobid == last_jobid) {
    850                 goto found;
    851             }
    852         }
    853         bb_error_msg("%s: no current job", argv[0]);
    854         return EXIT_FAILURE;
    855     }
    856     if (sscanf(argv[1], "%%%d", &jobnum) != 1) {
    857         bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]);
    858         return EXIT_FAILURE;
    859     }
    860     for (pi = job_list; pi; pi = pi->next) {
    861         if (pi->jobid == jobnum) {
    862             goto found;
    863         }
    864     }
    865     bb_error_msg("%s: %d: no such job", argv[0], jobnum);
    866     return EXIT_FAILURE;
    867  found:
    868     // TODO: bash prints a string representation
    869     // of job being foregrounded (like "sleep 1 | cat")
    870     if (*argv[0] == 'f') {
    871         /* Put the job into the foreground.  */
    872         tcsetpgrp(interactive_fd, pi->pgrp);
    873     }
    874 
    875     /* Restart the processes in the job */
    876     debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_progs, pi->pgrp);
    877     for (i = 0; i < pi->num_progs; i++) {
    878         debug_printf_jobs("reviving pid %d\n", pi->progs[i].pid);
    879         pi->progs[i].is_stopped = 0;
    880     }
    881     pi->stopped_progs = 0;
    882 
    883     i = kill(- pi->pgrp, SIGCONT);
    884     if (i < 0) {
    885         if (errno == ESRCH) {
    886             delete_finished_bg_job(pi);
    887             return EXIT_SUCCESS;
    8881779        } else {
    889             bb_perror_msg("kill (SIGCONT)");
    890         }
    891     }
    892 
    893     if (*argv[0] == 'f') {
    894         remove_bg_job(pi);
    895         return checkjobs_and_fg_shell(pi);
    896     }
    897     return EXIT_SUCCESS;
    898 }
    899 #endif
    900 
    901 /* built-in 'help' handler */
    902 #if ENABLE_HUSH_HELP
    903 static int builtin_help(char **argv ATTRIBUTE_UNUSED)
    904 {
    905     const struct built_in_command *x;
    906 
    907     printf("\nBuilt-in commands:\n");
    908     printf("-------------------\n");
    909     for (x = bltins; x->cmd; x++) {
    910         printf("%s\t%s\n", x->cmd, x->descr);
    911     }
    912     printf("\n\n");
    913     return EXIT_SUCCESS;
    914 }
    915 #endif
    916 
    917 #if ENABLE_HUSH_JOB
    918 /* built-in 'jobs' handler */
    919 static int builtin_jobs(char **argv ATTRIBUTE_UNUSED)
    920 {
    921     struct pipe *job;
    922     const char *status_string;
    923 
    924     for (job = job_list; job; job = job->next) {
    925         if (job->running_progs == job->stopped_progs)
    926             status_string = "Stopped";
    927         else
    928             status_string = "Running";
    929 
    930         printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
    931     }
    932     return EXIT_SUCCESS;
    933 }
    934 #endif
    935 
    936 /* built-in 'pwd' handler */
    937 static int builtin_pwd(char **argv ATTRIBUTE_UNUSED)
    938 {
    939     puts(set_cwd());
    940     return EXIT_SUCCESS;
    941 }
    942 
    943 /* built-in 'read VAR' handler */
    944 static int builtin_read(char **argv)
    945 {
    946     char *string;
    947     const char *name = argv[1] ? argv[1] : "REPLY";
    948 
    949     string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name));
    950     return set_local_var(string, 0);
    951 }
    952 
    953 /* built-in 'set [VAR=value]' handler */
    954 static int builtin_set(char **argv)
    955 {
    956     char *temp = argv[1];
    957     struct variable *e;
    958 
    959     if (temp == NULL)
    960         for (e = top_var; e; e = e->next)
    961             puts(e->varstr);
    962     else
    963         set_local_var(xstrdup(temp), 0);
    964 
    965     return EXIT_SUCCESS;
    966 }
    967 
    968 
    969 /* Built-in 'shift' handler */
    970 static int builtin_shift(char **argv)
    971 {
    972     int n = 1;
    973     if (argv[1]) {
    974         n = atoi(argv[1]);
    975     }
    976     if (n >= 0 && n < global_argc) {
    977         global_argv[n] = global_argv[0];
    978         global_argc -= n;
    979         global_argv += n;
    980         return EXIT_SUCCESS;
    981     }
    982     return EXIT_FAILURE;
    983 }
    984 
    985 /* Built-in '.' handler (read-in and execute commands from file) */
    986 static int builtin_source(char **argv)
    987 {
    988     FILE *input;
    989     int status;
    990 
    991     if (argv[1] == NULL)
    992         return EXIT_FAILURE;
    993 
    994     /* XXX search through $PATH is missing */
    995     input = fopen(argv[1], "r");
    996     if (!input) {
    997         bb_error_msg("cannot open '%s'", argv[1]);
    998         return EXIT_FAILURE;
    999     }
    1000 
    1001     /* Now run the file */
    1002     /* XXX argv and argc are broken; need to save old global_argv
    1003      * (pointer only is OK!) on this stack frame,
    1004      * set global_argv=argv+1, recurse, and restore. */
    1005     mark_open(fileno(input));
    1006     status = parse_and_run_file(input);
    1007     mark_closed(fileno(input));
    1008     fclose(input);
    1009     return status;
    1010 }
    1011 
    1012 static int builtin_umask(char **argv)
    1013 {
    1014     mode_t new_umask;
    1015     const char *arg = argv[1];
    1016     char *end;
    1017     if (arg) {
    1018         new_umask = strtoul(arg, &end, 8);
    1019         if (*end != '\0' || end == arg) {
    1020             return EXIT_FAILURE;
    1021         }
     1780            debug_printf_env("%s: restoring variable '%s'\n", __func__, var->varstr);
     1781        }
     1782        var = next;
     1783    }
     1784}
     1785
     1786static struct variable *set_vars_and_save_old(char **strings)
     1787{
     1788    char **s;
     1789    struct variable *old = NULL;
     1790
     1791    if (!strings)
     1792        return old;
     1793    s = strings;
     1794    while (*s) {
     1795        struct variable *var_p;
     1796        struct variable **var_pp;
     1797        char *eq;
     1798
     1799        eq = strchr(*s, '=');
     1800        if (eq) {
     1801            var_pp = get_ptr_to_local_var(*s, eq - *s);
     1802            if (var_pp) {
     1803                /* Remove variable from global linked list */
     1804                var_p = *var_pp;
     1805                debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr);
     1806                *var_pp = var_p->next;
     1807                /* Add it to returned list */
     1808                var_p->next = old;
     1809                old = var_p;
     1810            }
     1811            set_local_var(*s, /*exp:*/ 1, /*lvl:*/ 0, /*ro:*/ 0);
     1812        }
     1813        s++;
     1814    }
     1815    return old;
     1816}
     1817
     1818
     1819/*
     1820 * in_str support
     1821 */
     1822static int FAST_FUNC static_get(struct in_str *i)
     1823{
     1824    int ch = *i->p;
     1825    if (ch != '\0') {
     1826        i->p++;
     1827        return ch;
     1828    }
     1829    return EOF;
     1830}
     1831
     1832static int FAST_FUNC static_peek(struct in_str *i)
     1833{
     1834    return *i->p;
     1835}
     1836
     1837#if ENABLE_HUSH_INTERACTIVE
     1838
     1839static void cmdedit_update_prompt(void)
     1840{
     1841    if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) {
     1842        G.PS1 = get_local_var_value("PS1");
     1843        if (G.PS1 == NULL)
     1844            G.PS1 = "\\w \\$ ";
     1845        G.PS2 = get_local_var_value("PS2");
    10221846    } else {
    1023         new_umask = umask(0);
    1024         printf("%.3o\n", (unsigned) new_umask);
    1025     }
    1026     umask(new_umask);
    1027     return EXIT_SUCCESS;
    1028 }
    1029 
    1030 /* built-in 'unset VAR' handler */
    1031 static int builtin_unset(char **argv)
    1032 {
    1033     /* bash always returns true */
    1034     unset_local_var(argv[1]);
    1035     return EXIT_SUCCESS;
    1036 }
    1037 
    1038 //static int builtin_not_written(char **argv)
    1039 //{
    1040 //  printf("builtin_%s not written\n", argv[0]);
    1041 //  return EXIT_FAILURE;
    1042 //}
    1043 
    1044 static int b_check_space(o_string *o, int len)
    1045 {
    1046     /* It would be easy to drop a more restrictive policy
    1047      * in here, such as setting a maximum string length */
    1048     if (o->length + len > o->maxlen) {
    1049         /* assert(data == NULL || o->maxlen != 0); */
    1050         o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK);
    1051         o->data = xrealloc(o->data, 1 + o->maxlen);
    1052     }
    1053     return o->data == NULL;
    1054 }
    1055 
    1056 static int b_addchr(o_string *o, int ch)
    1057 {
    1058     debug_printf("b_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
    1059     if (b_check_space(o, 1))
    1060         return B_NOSPAC;
    1061     o->data[o->length] = ch;
    1062     o->length++;
    1063     o->data[o->length] = '\0';
    1064     return 0;
    1065 }
    1066 
    1067 static void b_reset(o_string *o)
    1068 {
    1069     o->length = 0;
    1070     o->nonnull = 0;
    1071     if (o->data != NULL)
    1072         *o->data = '\0';
    1073 }
    1074 
    1075 static void b_free(o_string *o)
    1076 {
    1077     b_reset(o);
    1078     free(o->data);
    1079     o->data = NULL;
    1080     o->maxlen = 0;
    1081 }
    1082 
    1083 /* My analysis of quoting semantics tells me that state information
    1084  * is associated with a destination, not a source.
    1085  */
    1086 static int b_addqchr(o_string *o, int ch, int quote)
    1087 {
    1088     if (quote && strchr("*?[\\", ch)) {
    1089         int rc;
    1090         rc = b_addchr(o, '\\');
    1091         if (rc)
    1092             return rc;
    1093     }
    1094     return b_addchr(o, ch);
    1095 }
    1096 
    1097 static int static_get(struct in_str *i)
    1098 {
    1099     int ch = *i->p++;
    1100     if (ch == '\0') return EOF;
    1101     return ch;
    1102 }
    1103 
    1104 static int static_peek(struct in_str *i)
    1105 {
    1106     return *i->p;
    1107 }
    1108 
    1109 #if ENABLE_HUSH_INTERACTIVE
    1110 #if ENABLE_FEATURE_EDITING
    1111 static void cmdedit_set_initial_prompt(void)
    1112 {
    1113 #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
    1114     PS1 = NULL;
    1115 #else
    1116     PS1 = getenv("PS1");
    1117     if (PS1 == NULL)
    1118         PS1 = "\\w \\$ ";
    1119 #endif
    1120 }
    1121 #endif /* EDITING */
    1122 
    1123 static const char* setup_prompt_string(int promptmode)
     1847        G.PS1 = NULL;
     1848    }
     1849    if (G.PS2 == NULL)
     1850        G.PS2 = "> ";
     1851}
     1852
     1853static const char *setup_prompt_string(int promptmode)
    11241854{
    11251855    const char *prompt_str;
    11261856    debug_printf("setup_prompt_string %d ", promptmode);
    1127 #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
    1128     /* Set up the prompt */
    1129     if (promptmode == 0) { /* PS1 */
    1130         free((char*)PS1);
    1131         PS1 = xasprintf("%s %c ", cwd, (geteuid() != 0) ? '$' : '#');
    1132         prompt_str = PS1;
    1133     } else {
    1134         prompt_str = PS2;
    1135     }
    1136 #else
    1137     prompt_str = (promptmode == 0) ? PS1 : PS2;
    1138 #endif
     1857    if (!ENABLE_FEATURE_EDITING_FANCY_PROMPT) {
     1858        /* Set up the prompt */
     1859        if (promptmode == 0) { /* PS1 */
     1860            free((char*)G.PS1);
     1861            /* bash uses $PWD value, even if it is set by user.
     1862             * It uses current dir only if PWD is unset.
     1863             * We always use current dir. */
     1864            G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
     1865            prompt_str = G.PS1;
     1866        } else
     1867            prompt_str = G.PS2;
     1868    } else
     1869        prompt_str = (promptmode == 0) ? G.PS1 : G.PS2;
    11391870    debug_printf("result '%s'\n", prompt_str);
    11401871    return prompt_str;
     
    11471878
    11481879    prompt_str = setup_prompt_string(i->promptmode);
    1149 #if ENABLE_FEATURE_EDITING
     1880# if ENABLE_FEATURE_EDITING
    11501881    /* Enable command line editing only while a command line
    1151      * is actually being read; otherwise, we'll end up bequeathing
    1152      * atexit() handlers and other unwanted stuff to our
    1153      * child processes (rob@sysgo.de) */
    1154     r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state);
     1882     * is actually being read */
     1883    do {
     1884        G.flag_SIGINT = 0;
     1885        /* buglet: SIGINT will not make new prompt to appear _at once_,
     1886         * only after <Enter>. (^C will work) */
     1887        r = read_line_input(prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, G.line_input_state);
     1888        /* catch *SIGINT* etc (^C is handled by read_line_input) */
     1889        check_and_run_traps(0);
     1890    } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
    11551891    i->eof_flag = (r < 0);
    11561892    if (i->eof_flag) { /* EOF/error detected */
    1157         user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */
    1158         user_input_buf[1] = '\0';
    1159     }
    1160 #else
    1161     fputs(prompt_str, stdout);
    1162     fflush(stdout);
    1163     user_input_buf[0] = r = fgetc(i->file);
    1164     /*user_input_buf[1] = '\0'; - already is and never changed */
     1893        G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */
     1894        G.user_input_buf[1] = '\0';
     1895    }
     1896# else
     1897    do {
     1898        G.flag_SIGINT = 0;
     1899        fputs(prompt_str, stdout);
     1900        fflush_all();
     1901        G.user_input_buf[0] = r = fgetc(i->file);
     1902        /*G.user_input_buf[1] = '\0'; - already is and never changed */
     1903//do we need check_and_run_traps(0)? (maybe only if stdin)
     1904    } while (G.flag_SIGINT);
    11651905    i->eof_flag = (r == EOF);
    1166 #endif
    1167     i->p = user_input_buf;
    1168 }
     1906# endif
     1907    i->p = G.user_input_buf;
     1908}
     1909
    11691910#endif  /* INTERACTIVE */
    11701911
    11711912/* This is the magic location that prints prompts
    11721913 * and gets data back from the user */
    1173 static int file_get(struct in_str *i)
     1914static int FAST_FUNC file_get(struct in_str *i)
    11741915{
    11751916    int ch;
     
    11831924        if (i->eof_flag && !*i->p)
    11841925            ch = EOF;
     1926        /* note: ch is never NUL */
    11851927    } else {
    11861928        /* need to double check i->file because we might be doing something
    11871929         * more complicated by now, like sourcing or substituting. */
    11881930#if ENABLE_HUSH_INTERACTIVE
    1189         if (interactive_fd && i->promptme && i->file == stdin) {
     1931        if (G_interactive_fd && i->promptme && i->file == stdin) {
    11901932            do {
    11911933                get_user_input(i);
     
    11961938        }
    11971939#endif
    1198         ch = fgetc(i->file);
    1199     }
    1200     debug_printf("file_get: got a '%c' %d\n", ch, ch);
     1940        do ch = fgetc(i->file); while (ch == '\0');
     1941    }
     1942    debug_printf("file_get: got '%c' %d\n", ch, ch);
    12011943#if ENABLE_HUSH_INTERACTIVE
    12021944    if (ch == '\n')
     
    12061948}
    12071949
    1208 /* All the callers guarantee this routine will never be
    1209  * used right after a newline, so prompting is not needed.
     1950/* All callers guarantee this routine will never
     1951 * be used right after a newline, so prompting is not needed.
    12101952 */
    1211 static int file_peek(struct in_str *i)
     1953static int FAST_FUNC file_peek(struct in_str *i)
    12121954{
    12131955    int ch;
     
    12161958            return EOF;
    12171959        return *i->p;
    1218     }
    1219     ch = fgetc(i->file);
     1960        /* note: ch is never NUL */
     1961    }
     1962    do ch = fgetc(i->file); while (ch == '\0');
    12201963    i->eof_flag = (ch == EOF);
    12211964    i->peek_buf[0] = ch;
    12221965    i->peek_buf[1] = '\0';
    12231966    i->p = i->peek_buf;
    1224     debug_printf("file_peek: got a '%c' %d\n", *i->p, *i->p);
     1967    debug_printf("file_peek: got '%c' %d\n", ch, ch);
    12251968    return ch;
    12261969}
     
    12501993}
    12511994
    1252 static void mark_open(int fd)
    1253 {
    1254     struct close_me *new = xmalloc(sizeof(struct close_me));
    1255     new->fd = fd;
    1256     new->next = close_me_head;
    1257     close_me_head = new;
    1258 }
    1259 
    1260 static void mark_closed(int fd)
    1261 {
    1262     struct close_me *tmp;
    1263     if (close_me_head == NULL || close_me_head->fd != fd)
    1264         bb_error_msg_and_die("corrupt close_me");
    1265     tmp = close_me_head;
    1266     close_me_head = close_me_head->next;
    1267     free(tmp);
    1268 }
    1269 
    1270 static void close_all(void)
    1271 {
    1272     struct close_me *c;
    1273     for (c = close_me_head; c; c = c->next) {
    1274         close(c->fd);
    1275     }
    1276     close_me_head = NULL;
     1995
     1996/*
     1997 * o_string support
     1998 */
     1999#define B_CHUNK  (32 * sizeof(char*))
     2000
     2001static void o_reset_to_empty_unquoted(o_string *o)
     2002{
     2003    o->length = 0;
     2004    o->has_quoted_part = 0;
     2005    if (o->data)
     2006        o->data[0] = '\0';
     2007}
     2008
     2009static void o_free(o_string *o)
     2010{
     2011    free(o->data);
     2012    memset(o, 0, sizeof(*o));
     2013}
     2014
     2015static ALWAYS_INLINE void o_free_unsafe(o_string *o)
     2016{
     2017    free(o->data);
     2018}
     2019
     2020static void o_grow_by(o_string *o, int len)
     2021{
     2022    if (o->length + len > o->maxlen) {
     2023        o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK);
     2024        o->data = xrealloc(o->data, 1 + o->maxlen);
     2025    }
     2026}
     2027
     2028static void o_addchr(o_string *o, int ch)
     2029{
     2030    debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
     2031    o_grow_by(o, 1);
     2032    o->data[o->length] = ch;
     2033    o->length++;
     2034    o->data[o->length] = '\0';
     2035}
     2036
     2037static void o_addblock(o_string *o, const char *str, int len)
     2038{
     2039    o_grow_by(o, len);
     2040    memcpy(&o->data[o->length], str, len);
     2041    o->length += len;
     2042    o->data[o->length] = '\0';
     2043}
     2044
     2045static void o_addstr(o_string *o, const char *str)
     2046{
     2047    o_addblock(o, str, strlen(str));
     2048}
     2049
     2050#if !BB_MMU
     2051static void nommu_addchr(o_string *o, int ch)
     2052{
     2053    if (o)
     2054        o_addchr(o, ch);
     2055}
     2056#else
     2057# define nommu_addchr(o, str) ((void)0)
     2058#endif
     2059
     2060static void o_addstr_with_NUL(o_string *o, const char *str)
     2061{
     2062    o_addblock(o, str, strlen(str) + 1);
     2063}
     2064
     2065/*
     2066 * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side.
     2067 * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v.
     2068 * Apparently, on unquoted $v bash still does globbing
     2069 * ("v='*.txt'; echo $v" prints all .txt files),
     2070 * but NOT brace expansion! Thus, there should be TWO independent
     2071 * quoting mechanisms on $v expansion side: one protects
     2072 * $v from brace expansion, and other additionally protects "$v" against globbing.
     2073 * We have only second one.
     2074 */
     2075
     2076#if ENABLE_HUSH_BRACE_EXPANSION
     2077# define MAYBE_BRACES "{}"
     2078#else
     2079# define MAYBE_BRACES ""
     2080#endif
     2081
     2082/* My analysis of quoting semantics tells me that state information
     2083 * is associated with a destination, not a source.
     2084 */
     2085static void o_addqchr(o_string *o, int ch)
     2086{
     2087    int sz = 1;
     2088    char *found = strchr("*?[\\" MAYBE_BRACES, ch);
     2089    if (found)
     2090        sz++;
     2091    o_grow_by(o, sz);
     2092    if (found) {
     2093        o->data[o->length] = '\\';
     2094        o->length++;
     2095    }
     2096    o->data[o->length] = ch;
     2097    o->length++;
     2098    o->data[o->length] = '\0';
     2099}
     2100
     2101static void o_addQchr(o_string *o, int ch)
     2102{
     2103    int sz = 1;
     2104    if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
     2105     && strchr("*?[\\" MAYBE_BRACES, ch)
     2106    ) {
     2107        sz++;
     2108        o->data[o->length] = '\\';
     2109        o->length++;
     2110    }
     2111    o_grow_by(o, sz);
     2112    o->data[o->length] = ch;
     2113    o->length++;
     2114    o->data[o->length] = '\0';
     2115}
     2116
     2117static void o_addqblock(o_string *o, const char *str, int len)
     2118{
     2119    while (len) {
     2120        char ch;
     2121        int sz;
     2122        int ordinary_cnt = strcspn(str, "*?[\\" MAYBE_BRACES);
     2123        if (ordinary_cnt > len) /* paranoia */
     2124            ordinary_cnt = len;
     2125        o_addblock(o, str, ordinary_cnt);
     2126        if (ordinary_cnt == len)
     2127            return;
     2128        str += ordinary_cnt;
     2129        len -= ordinary_cnt + 1; /* we are processing + 1 char below */
     2130
     2131        ch = *str++;
     2132        sz = 1;
     2133        if (ch) { /* it is necessarily one of "*?[\\" MAYBE_BRACES */
     2134            sz++;
     2135            o->data[o->length] = '\\';
     2136            o->length++;
     2137        }
     2138        o_grow_by(o, sz);
     2139        o->data[o->length] = ch;
     2140        o->length++;
     2141        o->data[o->length] = '\0';
     2142    }
     2143}
     2144
     2145static void o_addQblock(o_string *o, const char *str, int len)
     2146{
     2147    if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) {
     2148        o_addblock(o, str, len);
     2149        return;
     2150    }
     2151    o_addqblock(o, str, len);
     2152}
     2153
     2154static void o_addQstr(o_string *o, const char *str)
     2155{
     2156    o_addQblock(o, str, strlen(str));
     2157}
     2158
     2159/* A special kind of o_string for $VAR and `cmd` expansion.
     2160 * It contains char* list[] at the beginning, which is grown in 16 element
     2161 * increments. Actual string data starts at the next multiple of 16 * (char*).
     2162 * list[i] contains an INDEX (int!) into this string data.
     2163 * It means that if list[] needs to grow, data needs to be moved higher up
     2164 * but list[i]'s need not be modified.
     2165 * NB: remembering how many list[i]'s you have there is crucial.
     2166 * o_finalize_list() operation post-processes this structure - calculates
     2167 * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well.
     2168 */
     2169#if DEBUG_EXPAND || DEBUG_GLOB
     2170static void debug_print_list(const char *prefix, o_string *o, int n)
     2171{
     2172    char **list = (char**)o->data;
     2173    int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
     2174    int i = 0;
     2175
     2176    indent();
     2177    fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
     2178            prefix, list, n, string_start, o->length, o->maxlen,
     2179            !!(o->o_expflags & EXP_FLAG_GLOB),
     2180            o->has_quoted_part,
     2181            !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
     2182    while (i < n) {
     2183        indent();
     2184        fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i],
     2185                o->data + (int)(uintptr_t)list[i] + string_start,
     2186                o->data + (int)(uintptr_t)list[i] + string_start);
     2187        i++;
     2188    }
     2189    if (n) {
     2190        const char *p = o->data + (int)(uintptr_t)list[n - 1] + string_start;
     2191        indent();
     2192        fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
     2193    }
     2194}
     2195#else
     2196# define debug_print_list(prefix, o, n) ((void)0)
     2197#endif
     2198
     2199/* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value
     2200 * in list[n] so that it points past last stored byte so far.
     2201 * It returns n+1. */
     2202static int o_save_ptr_helper(o_string *o, int n)
     2203{
     2204    char **list = (char**)o->data;
     2205    int string_start;
     2206    int string_len;
     2207
     2208    if (!o->has_empty_slot) {
     2209        string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
     2210        string_len = o->length - string_start;
     2211        if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */
     2212            debug_printf_list("list[%d]=%d string_start=%d (growing)\n", n, string_len, string_start);
     2213            /* list[n] points to string_start, make space for 16 more pointers */
     2214            o->maxlen += 0x10 * sizeof(list[0]);
     2215            o->data = xrealloc(o->data, o->maxlen + 1);
     2216            list = (char**)o->data;
     2217            memmove(list + n + 0x10, list + n, string_len);
     2218            o->length += 0x10 * sizeof(list[0]);
     2219        } else {
     2220            debug_printf_list("list[%d]=%d string_start=%d\n",
     2221                    n, string_len, string_start);
     2222        }
     2223    } else {
     2224        /* We have empty slot at list[n], reuse without growth */
     2225        string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */
     2226        string_len = o->length - string_start;
     2227        debug_printf_list("list[%d]=%d string_start=%d (empty slot)\n",
     2228                n, string_len, string_start);
     2229        o->has_empty_slot = 0;
     2230    }
     2231    list[n] = (char*)(uintptr_t)string_len;
     2232    return n + 1;
     2233}
     2234
     2235/* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */
     2236static int o_get_last_ptr(o_string *o, int n)
     2237{
     2238    char **list = (char**)o->data;
     2239    int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
     2240
     2241    return ((int)(uintptr_t)list[n-1]) + string_start;
     2242}
     2243
     2244#if ENABLE_HUSH_BRACE_EXPANSION
     2245/* There in a GNU extension, GLOB_BRACE, but it is not usable:
     2246 * first, it processes even {a} (no commas), second,
     2247 * I didn't manage to make it return strings when they don't match
     2248 * existing files. Need to re-implement it.
     2249 */
     2250
     2251/* Helper */
     2252static int glob_needed(const char *s)
     2253{
     2254    while (*s) {
     2255        if (*s == '\\') {
     2256            if (!s[1])
     2257                return 0;
     2258            s += 2;
     2259            continue;
     2260        }
     2261        if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
     2262            return 1;
     2263        s++;
     2264    }
     2265    return 0;
     2266}
     2267/* Return pointer to next closing brace or to comma */
     2268static const char *next_brace_sub(const char *cp)
     2269{
     2270    unsigned depth = 0;
     2271    cp++;
     2272    while (*cp != '\0') {
     2273        if (*cp == '\\') {
     2274            if (*++cp == '\0')
     2275                break;
     2276            cp++;
     2277            continue;
     2278        }
     2279        if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
     2280            break;
     2281        if (*cp++ == '{')
     2282            depth++;
     2283    }
     2284
     2285    return *cp != '\0' ? cp : NULL;
     2286}
     2287/* Recursive brace globber. Note: may garble pattern[]. */
     2288static int glob_brace(char *pattern, o_string *o, int n)
     2289{
     2290    char *new_pattern_buf;
     2291    const char *begin;
     2292    const char *next;
     2293    const char *rest;
     2294    const char *p;
     2295    size_t rest_len;
     2296
     2297    debug_printf_glob("glob_brace('%s')\n", pattern);
     2298
     2299    begin = pattern;
     2300    while (1) {
     2301        if (*begin == '\0')
     2302            goto simple_glob;
     2303        if (*begin == '{') {
     2304            /* Find the first sub-pattern and at the same time
     2305             * find the rest after the closing brace */
     2306            next = next_brace_sub(begin);
     2307            if (next == NULL) {
     2308                /* An illegal expression */
     2309                goto simple_glob;
     2310            }
     2311            if (*next == '}') {
     2312                /* "{abc}" with no commas - illegal
     2313                 * brace expr, disregard and skip it */
     2314                begin = next + 1;
     2315                continue;
     2316            }
     2317            break;
     2318        }
     2319        if (*begin == '\\' && begin[1] != '\0')
     2320            begin++;
     2321        begin++;
     2322    }
     2323    debug_printf_glob("begin:%s\n", begin);
     2324    debug_printf_glob("next:%s\n", next);
     2325
     2326    /* Now find the end of the whole brace expression */
     2327    rest = next;
     2328    while (*rest != '}') {
     2329        rest = next_brace_sub(rest);
     2330        if (rest == NULL) {
     2331            /* An illegal expression */
     2332            goto simple_glob;
     2333        }
     2334        debug_printf_glob("rest:%s\n", rest);
     2335    }
     2336    rest_len = strlen(++rest) + 1;
     2337
     2338    /* We are sure the brace expression is well-formed */
     2339
     2340    /* Allocate working buffer large enough for our work */
     2341    new_pattern_buf = xmalloc(strlen(pattern));
     2342
     2343    /* We have a brace expression.  BEGIN points to the opening {,
     2344     * NEXT points past the terminator of the first element, and REST
     2345     * points past the final }.  We will accumulate result names from
     2346     * recursive runs for each brace alternative in the buffer using
     2347     * GLOB_APPEND.  */
     2348
     2349    p = begin + 1;
     2350    while (1) {
     2351        /* Construct the new glob expression */
     2352        memcpy(
     2353            mempcpy(
     2354                mempcpy(new_pattern_buf,
     2355                    /* We know the prefix for all sub-patterns */
     2356                    pattern, begin - pattern),
     2357                p, next - p),
     2358            rest, rest_len);
     2359
     2360        /* Note: glob_brace() may garble new_pattern_buf[].
     2361         * That's why we re-copy prefix every time (1st memcpy above).
     2362         */
     2363        n = glob_brace(new_pattern_buf, o, n);
     2364        if (*next == '}') {
     2365            /* We saw the last entry */
     2366            break;
     2367        }
     2368        p = next + 1;
     2369        next = next_brace_sub(next);
     2370    }
     2371    free(new_pattern_buf);
     2372    return n;
     2373
     2374 simple_glob:
     2375    {
     2376        int gr;
     2377        glob_t globdata;
     2378
     2379        memset(&globdata, 0, sizeof(globdata));
     2380        gr = glob(pattern, 0, NULL, &globdata);
     2381        debug_printf_glob("glob('%s'):%d\n", pattern, gr);
     2382        if (gr != 0) {
     2383            if (gr == GLOB_NOMATCH) {
     2384                globfree(&globdata);
     2385                /* NB: garbles parameter */
     2386                unbackslash(pattern);
     2387                o_addstr_with_NUL(o, pattern);
     2388                debug_printf_glob("glob pattern '%s' is literal\n", pattern);
     2389                return o_save_ptr_helper(o, n);
     2390            }
     2391            if (gr == GLOB_NOSPACE)
     2392                bb_error_msg_and_die(bb_msg_memory_exhausted);
     2393            /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
     2394             * but we didn't specify it. Paranoia again. */
     2395            bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
     2396        }
     2397        if (globdata.gl_pathv && globdata.gl_pathv[0]) {
     2398            char **argv = globdata.gl_pathv;
     2399            while (1) {
     2400                o_addstr_with_NUL(o, *argv);
     2401                n = o_save_ptr_helper(o, n);
     2402                argv++;
     2403                if (!*argv)
     2404                    break;
     2405            }
     2406        }
     2407        globfree(&globdata);
     2408    }
     2409    return n;
     2410}
     2411/* Performs globbing on last list[],
     2412 * saving each result as a new list[].
     2413 */
     2414static int perform_glob(o_string *o, int n)
     2415{
     2416    char *pattern, *copy;
     2417
     2418    debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
     2419    if (!o->data)
     2420        return o_save_ptr_helper(o, n);
     2421    pattern = o->data + o_get_last_ptr(o, n);
     2422    debug_printf_glob("glob pattern '%s'\n", pattern);
     2423    if (!glob_needed(pattern)) {
     2424        /* unbackslash last string in o in place, fix length */
     2425        o->length = unbackslash(pattern) - o->data;
     2426        debug_printf_glob("glob pattern '%s' is literal\n", pattern);
     2427        return o_save_ptr_helper(o, n);
     2428    }
     2429
     2430    copy = xstrdup(pattern);
     2431    /* "forget" pattern in o */
     2432    o->length = pattern - o->data;
     2433    n = glob_brace(copy, o, n);
     2434    free(copy);
     2435    if (DEBUG_GLOB)
     2436        debug_print_list("perform_glob returning", o, n);
     2437    return n;
     2438}
     2439
     2440#else /* !HUSH_BRACE_EXPANSION */
     2441
     2442/* Helper */
     2443static int glob_needed(const char *s)
     2444{
     2445    while (*s) {
     2446        if (*s == '\\') {
     2447            if (!s[1])
     2448                return 0;
     2449            s += 2;
     2450            continue;
     2451        }
     2452        if (*s == '*' || *s == '[' || *s == '?')
     2453            return 1;
     2454        s++;
     2455    }
     2456    return 0;
     2457}
     2458/* Performs globbing on last list[],
     2459 * saving each result as a new list[].
     2460 */
     2461static int perform_glob(o_string *o, int n)
     2462{
     2463    glob_t globdata;
     2464    int gr;
     2465    char *pattern;
     2466
     2467    debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
     2468    if (!o->data)
     2469        return o_save_ptr_helper(o, n);
     2470    pattern = o->data + o_get_last_ptr(o, n);
     2471    debug_printf_glob("glob pattern '%s'\n", pattern);
     2472    if (!glob_needed(pattern)) {
     2473 literal:
     2474        /* unbackslash last string in o in place, fix length */
     2475        o->length = unbackslash(pattern) - o->data;
     2476        debug_printf_glob("glob pattern '%s' is literal\n", pattern);
     2477        return o_save_ptr_helper(o, n);
     2478    }
     2479
     2480    memset(&globdata, 0, sizeof(globdata));
     2481    /* Can't use GLOB_NOCHECK: it does not unescape the string.
     2482     * If we glob "*.\*" and don't find anything, we need
     2483     * to fall back to using literal "*.*", but GLOB_NOCHECK
     2484     * will return "*.\*"!
     2485     */
     2486    gr = glob(pattern, 0, NULL, &globdata);
     2487    debug_printf_glob("glob('%s'):%d\n", pattern, gr);
     2488    if (gr != 0) {
     2489        if (gr == GLOB_NOMATCH) {
     2490            globfree(&globdata);
     2491            goto literal;
     2492        }
     2493        if (gr == GLOB_NOSPACE)
     2494            bb_error_msg_and_die(bb_msg_memory_exhausted);
     2495        /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
     2496         * but we didn't specify it. Paranoia again. */
     2497        bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
     2498    }
     2499    if (globdata.gl_pathv && globdata.gl_pathv[0]) {
     2500        char **argv = globdata.gl_pathv;
     2501        /* "forget" pattern in o */
     2502        o->length = pattern - o->data;
     2503        while (1) {
     2504            o_addstr_with_NUL(o, *argv);
     2505            n = o_save_ptr_helper(o, n);
     2506            argv++;
     2507            if (!*argv)
     2508                break;
     2509        }
     2510    }
     2511    globfree(&globdata);
     2512    if (DEBUG_GLOB)
     2513        debug_print_list("perform_glob returning", o, n);
     2514    return n;
     2515}
     2516
     2517#endif /* !HUSH_BRACE_EXPANSION */
     2518
     2519/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
     2520 * Otherwise, just finish current list[] and start new */
     2521static int o_save_ptr(o_string *o, int n)
     2522{
     2523    if (o->o_expflags & EXP_FLAG_GLOB) {
     2524        /* If o->has_empty_slot, list[n] was already globbed
     2525         * (if it was requested back then when it was filled)
     2526         * so don't do that again! */
     2527        if (!o->has_empty_slot)
     2528            return perform_glob(o, n); /* o_save_ptr_helper is inside */
     2529    }
     2530    return o_save_ptr_helper(o, n);
     2531}
     2532
     2533/* "Please convert list[n] to real char* ptrs, and NULL terminate it." */
     2534static char **o_finalize_list(o_string *o, int n)
     2535{
     2536    char **list;
     2537    int string_start;
     2538
     2539    n = o_save_ptr(o, n); /* force growth for list[n] if necessary */
     2540    if (DEBUG_EXPAND)
     2541        debug_print_list("finalized", o, n);
     2542    debug_printf_expand("finalized n:%d\n", n);
     2543    list = (char**)o->data;
     2544    string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
     2545    list[--n] = NULL;
     2546    while (n) {
     2547        n--;
     2548        list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
     2549    }
     2550    return list;
     2551}
     2552
     2553static void free_pipe_list(struct pipe *pi);
     2554
     2555/* Returns pi->next - next pipe in the list */
     2556static struct pipe *free_pipe(struct pipe *pi)
     2557{
     2558    struct pipe *next;
     2559    int i;
     2560
     2561    debug_printf_clean("free_pipe (pid %d)\n", getpid());
     2562    for (i = 0; i < pi->num_cmds; i++) {
     2563        struct command *command;
     2564        struct redir_struct *r, *rnext;
     2565
     2566        command = &pi->cmds[i];
     2567        debug_printf_clean("  command %d:\n", i);
     2568        if (command->argv) {
     2569            if (DEBUG_CLEAN) {
     2570                int a;
     2571                char **p;
     2572                for (a = 0, p = command->argv; *p; a++, p++) {
     2573                    debug_printf_clean("   argv[%d] = %s\n", a, *p);
     2574                }
     2575            }
     2576            free_strings(command->argv);
     2577            //command->argv = NULL;
     2578        }
     2579        /* not "else if": on syntax error, we may have both! */
     2580        if (command->group) {
     2581            debug_printf_clean("   begin group (cmd_type:%d)\n",
     2582                    command->cmd_type);
     2583            free_pipe_list(command->group);
     2584            debug_printf_clean("   end group\n");
     2585            //command->group = NULL;
     2586        }
     2587        /* else is crucial here.
     2588         * If group != NULL, child_func is meaningless */
     2589#if ENABLE_HUSH_FUNCTIONS
     2590        else if (command->child_func) {
     2591            debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
     2592            command->child_func->parent_cmd = NULL;
     2593        }
     2594#endif
     2595#if !BB_MMU
     2596        free(command->group_as_string);
     2597        //command->group_as_string = NULL;
     2598#endif
     2599        for (r = command->redirects; r; r = rnext) {
     2600            debug_printf_clean("   redirect %d%s",
     2601                    r->rd_fd, redir_table[r->rd_type].descrip);
     2602            /* guard against the case >$FOO, where foo is unset or blank */
     2603            if (r->rd_filename) {
     2604                debug_printf_clean(" fname:'%s'\n", r->rd_filename);
     2605                free(r->rd_filename);
     2606                //r->rd_filename = NULL;
     2607            }
     2608            debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
     2609            rnext = r->next;
     2610            free(r);
     2611        }
     2612        //command->redirects = NULL;
     2613    }
     2614    free(pi->cmds);   /* children are an array, they get freed all at once */
     2615    //pi->cmds = NULL;
     2616#if ENABLE_HUSH_JOB
     2617    free(pi->cmdtext);
     2618    //pi->cmdtext = NULL;
     2619#endif
     2620
     2621    next = pi->next;
     2622    free(pi);
     2623    return next;
     2624}
     2625
     2626static void free_pipe_list(struct pipe *pi)
     2627{
     2628    while (pi) {
     2629#if HAS_KEYWORDS
     2630        debug_printf_clean("pipe reserved word %d\n", pi->res_word);
     2631#endif
     2632        debug_printf_clean("pipe followup code %d\n", pi->followup);
     2633        pi = free_pipe(pi);
     2634    }
     2635}
     2636
     2637
     2638/*** Parsing routines ***/
     2639
     2640#ifndef debug_print_tree
     2641static void debug_print_tree(struct pipe *pi, int lvl)
     2642{
     2643    static const char *const PIPE[] = {
     2644        [PIPE_SEQ] = "SEQ",
     2645        [PIPE_AND] = "AND",
     2646        [PIPE_OR ] = "OR" ,
     2647        [PIPE_BG ] = "BG" ,
     2648    };
     2649    static const char *RES[] = {
     2650        [RES_NONE ] = "NONE" ,
     2651# if ENABLE_HUSH_IF
     2652        [RES_IF   ] = "IF"   ,
     2653        [RES_THEN ] = "THEN" ,
     2654        [RES_ELIF ] = "ELIF" ,
     2655        [RES_ELSE ] = "ELSE" ,
     2656        [RES_FI   ] = "FI"   ,
     2657# endif
     2658# if ENABLE_HUSH_LOOPS
     2659        [RES_FOR  ] = "FOR"  ,
     2660        [RES_WHILE] = "WHILE",
     2661        [RES_UNTIL] = "UNTIL",
     2662        [RES_DO   ] = "DO"   ,
     2663        [RES_DONE ] = "DONE" ,
     2664# endif
     2665# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
     2666        [RES_IN   ] = "IN"   ,
     2667# endif
     2668# if ENABLE_HUSH_CASE
     2669        [RES_CASE ] = "CASE" ,
     2670        [RES_CASE_IN ] = "CASE_IN" ,
     2671        [RES_MATCH] = "MATCH",
     2672        [RES_CASE_BODY] = "CASE_BODY",
     2673        [RES_ESAC ] = "ESAC" ,
     2674# endif
     2675        [RES_XXXX ] = "XXXX" ,
     2676        [RES_SNTX ] = "SNTX" ,
     2677    };
     2678    static const char *const CMDTYPE[] = {
     2679        "{}",
     2680        "()",
     2681        "[noglob]",
     2682# if ENABLE_HUSH_FUNCTIONS
     2683        "func()",
     2684# endif
     2685    };
     2686
     2687    int pin, prn;
     2688
     2689    pin = 0;
     2690    while (pi) {
     2691        fdprintf(2, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "",
     2692                pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
     2693        prn = 0;
     2694        while (prn < pi->num_cmds) {
     2695            struct command *command = &pi->cmds[prn];
     2696            char **argv = command->argv;
     2697
     2698            fdprintf(2, "%*s cmd %d assignment_cnt:%d",
     2699                    lvl*2, "", prn,
     2700                    command->assignment_cnt);
     2701            if (command->group) {
     2702                fdprintf(2, " group %s: (argv=%p)%s%s\n",
     2703                        CMDTYPE[command->cmd_type],
     2704                        argv
     2705# if !BB_MMU
     2706                        , " group_as_string:", command->group_as_string
     2707# else
     2708                        , "", ""
     2709# endif
     2710                );
     2711                debug_print_tree(command->group, lvl+1);
     2712                prn++;
     2713                continue;
     2714            }
     2715            if (argv) while (*argv) {
     2716                fdprintf(2, " '%s'", *argv);
     2717                argv++;
     2718            }
     2719            fdprintf(2, "\n");
     2720            prn++;
     2721        }
     2722        pi = pi->next;
     2723        pin++;
     2724    }
     2725}
     2726#endif /* debug_print_tree */
     2727
     2728static struct pipe *new_pipe(void)
     2729{
     2730    struct pipe *pi;
     2731    pi = xzalloc(sizeof(struct pipe));
     2732    /*pi->followup = 0; - deliberately invalid value */
     2733    /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
     2734    return pi;
     2735}
     2736
     2737/* Command (member of a pipe) is complete, or we start a new pipe
     2738 * if ctx->command is NULL.
     2739 * No errors possible here.
     2740 */
     2741static int done_command(struct parse_context *ctx)
     2742{
     2743    /* The command is really already in the pipe structure, so
     2744     * advance the pipe counter and make a new, null command. */
     2745    struct pipe *pi = ctx->pipe;
     2746    struct command *command = ctx->command;
     2747
     2748    if (command) {
     2749        if (IS_NULL_CMD(command)) {
     2750            debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
     2751            goto clear_and_ret;
     2752        }
     2753        pi->num_cmds++;
     2754        debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
     2755        //debug_print_tree(ctx->list_head, 20);
     2756    } else {
     2757        debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
     2758    }
     2759
     2760    /* Only real trickiness here is that the uncommitted
     2761     * command structure is not counted in pi->num_cmds. */
     2762    pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
     2763    ctx->command = command = &pi->cmds[pi->num_cmds];
     2764 clear_and_ret:
     2765    memset(command, 0, sizeof(*command));
     2766    return pi->num_cmds; /* used only for 0/nonzero check */
     2767}
     2768
     2769static void done_pipe(struct parse_context *ctx, pipe_style type)
     2770{
     2771    int not_null;
     2772
     2773    debug_printf_parse("done_pipe entered, followup %d\n", type);
     2774    /* Close previous command */
     2775    not_null = done_command(ctx);
     2776    ctx->pipe->followup = type;
     2777#if HAS_KEYWORDS
     2778    ctx->pipe->pi_inverted = ctx->ctx_inverted;
     2779    ctx->ctx_inverted = 0;
     2780    ctx->pipe->res_word = ctx->ctx_res_w;
     2781#endif
     2782
     2783    /* Without this check, even just <enter> on command line generates
     2784     * tree of three NOPs (!). Which is harmless but annoying.
     2785     * IOW: it is safe to do it unconditionally. */
     2786    if (not_null
     2787#if ENABLE_HUSH_IF
     2788     || ctx->ctx_res_w == RES_FI
     2789#endif
     2790#if ENABLE_HUSH_LOOPS
     2791     || ctx->ctx_res_w == RES_DONE
     2792     || ctx->ctx_res_w == RES_FOR
     2793     || ctx->ctx_res_w == RES_IN
     2794#endif
     2795#if ENABLE_HUSH_CASE
     2796     || ctx->ctx_res_w == RES_ESAC
     2797#endif
     2798    ) {
     2799        struct pipe *new_p;
     2800        debug_printf_parse("done_pipe: adding new pipe: "
     2801                "not_null:%d ctx->ctx_res_w:%d\n",
     2802                not_null, ctx->ctx_res_w);
     2803        new_p = new_pipe();
     2804        ctx->pipe->next = new_p;
     2805        ctx->pipe = new_p;
     2806        /* RES_THEN, RES_DO etc are "sticky" -
     2807         * they remain set for pipes inside if/while.
     2808         * This is used to control execution.
     2809         * RES_FOR and RES_IN are NOT sticky (needed to support
     2810         * cases where variable or value happens to match a keyword):
     2811         */
     2812#if ENABLE_HUSH_LOOPS
     2813        if (ctx->ctx_res_w == RES_FOR
     2814         || ctx->ctx_res_w == RES_IN)
     2815            ctx->ctx_res_w = RES_NONE;
     2816#endif
     2817#if ENABLE_HUSH_CASE
     2818        if (ctx->ctx_res_w == RES_MATCH)
     2819            ctx->ctx_res_w = RES_CASE_BODY;
     2820        if (ctx->ctx_res_w == RES_CASE)
     2821            ctx->ctx_res_w = RES_CASE_IN;
     2822#endif
     2823        ctx->command = NULL; /* trick done_command below */
     2824        /* Create the memory for command, roughly:
     2825         * ctx->pipe->cmds = new struct command;
     2826         * ctx->command = &ctx->pipe->cmds[0];
     2827         */
     2828        done_command(ctx);
     2829        //debug_print_tree(ctx->list_head, 10);
     2830    }
     2831    debug_printf_parse("done_pipe return\n");
     2832}
     2833
     2834static void initialize_context(struct parse_context *ctx)
     2835{
     2836    memset(ctx, 0, sizeof(*ctx));
     2837    ctx->pipe = ctx->list_head = new_pipe();
     2838    /* Create the memory for command, roughly:
     2839     * ctx->pipe->cmds = new struct command;
     2840     * ctx->command = &ctx->pipe->cmds[0];
     2841     */
     2842    done_command(ctx);
     2843}
     2844
     2845/* If a reserved word is found and processed, parse context is modified
     2846 * and 1 is returned.
     2847 */
     2848#if HAS_KEYWORDS
     2849struct reserved_combo {
     2850    char literal[6];
     2851    unsigned char res;
     2852    unsigned char assignment_flag;
     2853    int flag;
     2854};
     2855enum {
     2856    FLAG_END   = (1 << RES_NONE ),
     2857# if ENABLE_HUSH_IF
     2858    FLAG_IF    = (1 << RES_IF   ),
     2859    FLAG_THEN  = (1 << RES_THEN ),
     2860    FLAG_ELIF  = (1 << RES_ELIF ),
     2861    FLAG_ELSE  = (1 << RES_ELSE ),
     2862    FLAG_FI    = (1 << RES_FI   ),
     2863# endif
     2864# if ENABLE_HUSH_LOOPS
     2865    FLAG_FOR   = (1 << RES_FOR  ),
     2866    FLAG_WHILE = (1 << RES_WHILE),
     2867    FLAG_UNTIL = (1 << RES_UNTIL),
     2868    FLAG_DO    = (1 << RES_DO   ),
     2869    FLAG_DONE  = (1 << RES_DONE ),
     2870    FLAG_IN    = (1 << RES_IN   ),
     2871# endif
     2872# if ENABLE_HUSH_CASE
     2873    FLAG_MATCH = (1 << RES_MATCH),
     2874    FLAG_ESAC  = (1 << RES_ESAC ),
     2875# endif
     2876    FLAG_START = (1 << RES_XXXX ),
     2877};
     2878
     2879static const struct reserved_combo* match_reserved_word(o_string *word)
     2880{
     2881    /* Mostly a list of accepted follow-up reserved words.
     2882     * FLAG_END means we are done with the sequence, and are ready
     2883     * to turn the compound list into a command.
     2884     * FLAG_START means the word must start a new compound list.
     2885     */
     2886    static const struct reserved_combo reserved_list[] = {
     2887# if ENABLE_HUSH_IF
     2888        { "!",     RES_NONE,  NOT_ASSIGNMENT , 0 },
     2889        { "if",    RES_IF,    WORD_IS_KEYWORD, FLAG_THEN | FLAG_START },
     2890        { "then",  RES_THEN,  WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
     2891        { "elif",  RES_ELIF,  WORD_IS_KEYWORD, FLAG_THEN },
     2892        { "else",  RES_ELSE,  WORD_IS_KEYWORD, FLAG_FI   },
     2893        { "fi",    RES_FI,    NOT_ASSIGNMENT , FLAG_END  },
     2894# endif
     2895# if ENABLE_HUSH_LOOPS
     2896        { "for",   RES_FOR,   NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
     2897        { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
     2898        { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START },
     2899        { "in",    RES_IN,    NOT_ASSIGNMENT , FLAG_DO   },
     2900        { "do",    RES_DO,    WORD_IS_KEYWORD, FLAG_DONE },
     2901        { "done",  RES_DONE,  NOT_ASSIGNMENT , FLAG_END  },
     2902# endif
     2903# if ENABLE_HUSH_CASE
     2904        { "case",  RES_CASE,  NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
     2905        { "esac",  RES_ESAC,  NOT_ASSIGNMENT , FLAG_END  },
     2906# endif
     2907    };
     2908    const struct reserved_combo *r;
     2909
     2910    for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
     2911        if (strcmp(word->data, r->literal) == 0)
     2912            return r;
     2913    }
     2914    return NULL;
     2915}
     2916/* Return 0: not a keyword, 1: keyword
     2917 */
     2918static int reserved_word(o_string *word, struct parse_context *ctx)
     2919{
     2920# if ENABLE_HUSH_CASE
     2921    static const struct reserved_combo reserved_match = {
     2922        "",        RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
     2923    };
     2924# endif
     2925    const struct reserved_combo *r;
     2926
     2927    if (word->has_quoted_part)
     2928        return 0;
     2929    r = match_reserved_word(word);
     2930    if (!r)
     2931        return 0;
     2932
     2933    debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
     2934# if ENABLE_HUSH_CASE
     2935    if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
     2936        /* "case word IN ..." - IN part starts first MATCH part */
     2937        r = &reserved_match;
     2938    } else
     2939# endif
     2940    if (r->flag == 0) { /* '!' */
     2941        if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
     2942            syntax_error("! ! command");
     2943            ctx->ctx_res_w = RES_SNTX;
     2944        }
     2945        ctx->ctx_inverted = 1;
     2946        return 1;
     2947    }
     2948    if (r->flag & FLAG_START) {
     2949        struct parse_context *old;
     2950
     2951        old = xmalloc(sizeof(*old));
     2952        debug_printf_parse("push stack %p\n", old);
     2953        *old = *ctx;   /* physical copy */
     2954        initialize_context(ctx);
     2955        ctx->stack = old;
     2956    } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
     2957        syntax_error_at(word->data);
     2958        ctx->ctx_res_w = RES_SNTX;
     2959        return 1;
     2960    } else {
     2961        /* "{...} fi" is ok. "{...} if" is not
     2962         * Example:
     2963         * if { echo foo; } then { echo bar; } fi */
     2964        if (ctx->command->group)
     2965            done_pipe(ctx, PIPE_SEQ);
     2966    }
     2967
     2968    ctx->ctx_res_w = r->res;
     2969    ctx->old_flag = r->flag;
     2970    word->o_assignment = r->assignment_flag;
     2971
     2972    if (ctx->old_flag & FLAG_END) {
     2973        struct parse_context *old;
     2974
     2975        done_pipe(ctx, PIPE_SEQ);
     2976        debug_printf_parse("pop stack %p\n", ctx->stack);
     2977        old = ctx->stack;
     2978        old->command->group = ctx->list_head;
     2979        old->command->cmd_type = CMD_NORMAL;
     2980# if !BB_MMU
     2981        o_addstr(&old->as_string, ctx->as_string.data);
     2982        o_free_unsafe(&ctx->as_string);
     2983        old->command->group_as_string = xstrdup(old->as_string.data);
     2984        debug_printf_parse("pop, remembering as:'%s'\n",
     2985                old->command->group_as_string);
     2986# endif
     2987        *ctx = *old;   /* physical copy */
     2988        free(old);
     2989    }
     2990    return 1;
     2991}
     2992#endif /* HAS_KEYWORDS */
     2993
     2994/* Word is complete, look at it and update parsing context.
     2995 * Normal return is 0. Syntax errors return 1.
     2996 * Note: on return, word is reset, but not o_free'd!
     2997 */
     2998static int done_word(o_string *word, struct parse_context *ctx)
     2999{
     3000    struct command *command = ctx->command;
     3001
     3002    debug_printf_parse("done_word entered: '%s' %p\n", word->data, command);
     3003    if (word->length == 0 && !word->has_quoted_part) {
     3004        debug_printf_parse("done_word return 0: true null, ignored\n");
     3005        return 0;
     3006    }
     3007
     3008    if (ctx->pending_redirect) {
     3009        /* We do not glob in e.g. >*.tmp case. bash seems to glob here
     3010         * only if run as "bash", not "sh" */
     3011        /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
     3012         * "2.7 Redirection
     3013         * ...the word that follows the redirection operator
     3014         * shall be subjected to tilde expansion, parameter expansion,
     3015         * command substitution, arithmetic expansion, and quote
     3016         * removal. Pathname expansion shall not be performed
     3017         * on the word by a non-interactive shell; an interactive
     3018         * shell may perform it, but shall do so only when
     3019         * the expansion would result in one word."
     3020         */
     3021        ctx->pending_redirect->rd_filename = xstrdup(word->data);
     3022        /* Cater for >\file case:
     3023         * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
     3024         * Same with heredocs:
     3025         * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
     3026         */
     3027        if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
     3028            unbackslash(ctx->pending_redirect->rd_filename);
     3029            /* Is it <<"HEREDOC"? */
     3030            if (word->has_quoted_part) {
     3031                ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
     3032            }
     3033        }
     3034        debug_printf_parse("word stored in rd_filename: '%s'\n", word->data);
     3035        ctx->pending_redirect = NULL;
     3036    } else {
     3037        /* If this word wasn't an assignment, next ones definitely
     3038         * can't be assignments. Even if they look like ones. */
     3039        if (word->o_assignment != DEFINITELY_ASSIGNMENT
     3040         && word->o_assignment != WORD_IS_KEYWORD
     3041        ) {
     3042            word->o_assignment = NOT_ASSIGNMENT;
     3043        } else {
     3044            if (word->o_assignment == DEFINITELY_ASSIGNMENT)
     3045                command->assignment_cnt++;
     3046            word->o_assignment = MAYBE_ASSIGNMENT;
     3047        }
     3048
     3049#if HAS_KEYWORDS
     3050# if ENABLE_HUSH_CASE
     3051        if (ctx->ctx_dsemicolon
     3052         && strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */
     3053        ) {
     3054            /* already done when ctx_dsemicolon was set to 1: */
     3055            /* ctx->ctx_res_w = RES_MATCH; */
     3056            ctx->ctx_dsemicolon = 0;
     3057        } else
     3058# endif
     3059        if (!command->argv /* if it's the first word... */
     3060# if ENABLE_HUSH_LOOPS
     3061         && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
     3062         && ctx->ctx_res_w != RES_IN
     3063# endif
     3064# if ENABLE_HUSH_CASE
     3065         && ctx->ctx_res_w != RES_CASE
     3066# endif
     3067        ) {
     3068            debug_printf_parse("checking '%s' for reserved-ness\n", word->data);
     3069            if (reserved_word(word, ctx)) {
     3070                o_reset_to_empty_unquoted(word);
     3071                debug_printf_parse("done_word return %d\n",
     3072                        (ctx->ctx_res_w == RES_SNTX));
     3073                return (ctx->ctx_res_w == RES_SNTX);
     3074            }
     3075# if ENABLE_HUSH_BASH_COMPAT
     3076            if (strcmp(word->data, "[[") == 0) {
     3077                command->cmd_type = CMD_SINGLEWORD_NOGLOB;
     3078            }
     3079            /* fall through */
     3080# endif
     3081        }
     3082#endif
     3083        if (command->group) {
     3084            /* "{ echo foo; } echo bar" - bad */
     3085            syntax_error_at(word->data);
     3086            debug_printf_parse("done_word return 1: syntax error, "
     3087                    "groups and arglists don't mix\n");
     3088            return 1;
     3089        }
     3090        if (word->has_quoted_part
     3091         /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
     3092         && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
     3093         /* (otherwise it's known to be not empty and is already safe) */
     3094        ) {
     3095            /* exclude "$@" - it can expand to no word despite "" */
     3096            char *p = word->data;
     3097            while (p[0] == SPECIAL_VAR_SYMBOL
     3098                && (p[1] & 0x7f) == '@'
     3099                && p[2] == SPECIAL_VAR_SYMBOL
     3100            ) {
     3101                p += 3;
     3102            }
     3103            if (p == word->data || p[0] != '\0') {
     3104                /* saw no "$@", or not only "$@" but some
     3105                 * real text is there too */
     3106                /* insert "empty variable" reference, this makes
     3107                 * e.g. "", $empty"" etc to not disappear */
     3108                o_addchr(word, SPECIAL_VAR_SYMBOL);
     3109                o_addchr(word, SPECIAL_VAR_SYMBOL);
     3110            }
     3111        }
     3112        command->argv = add_string_to_strings(command->argv, xstrdup(word->data));
     3113        debug_print_strings("word appended to argv", command->argv);
     3114    }
     3115
     3116#if ENABLE_HUSH_LOOPS
     3117    if (ctx->ctx_res_w == RES_FOR) {
     3118        if (word->has_quoted_part
     3119         || !is_well_formed_var_name(command->argv[0], '\0')
     3120        ) {
     3121            /* bash says just "not a valid identifier" */
     3122            syntax_error("not a valid identifier in for");
     3123            return 1;
     3124        }
     3125        /* Force FOR to have just one word (variable name) */
     3126        /* NB: basically, this makes hush see "for v in ..."
     3127         * syntax as if it is "for v; in ...". FOR and IN become
     3128         * two pipe structs in parse tree. */
     3129        done_pipe(ctx, PIPE_SEQ);
     3130    }
     3131#endif
     3132#if ENABLE_HUSH_CASE
     3133    /* Force CASE to have just one word */
     3134    if (ctx->ctx_res_w == RES_CASE) {
     3135        done_pipe(ctx, PIPE_SEQ);
     3136    }
     3137#endif
     3138
     3139    o_reset_to_empty_unquoted(word);
     3140
     3141    debug_printf_parse("done_word return 0\n");
     3142    return 0;
     3143}
     3144
     3145
     3146/* Peek ahead in the input to find out if we have a "&n" construct,
     3147 * as in "2>&1", that represents duplicating a file descriptor.
     3148 * Return:
     3149 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
     3150 * REDIRFD_SYNTAX_ERR if syntax error,
     3151 * REDIRFD_TO_FILE if no & was seen,
     3152 * or the number found.
     3153 */
     3154#if BB_MMU
     3155#define parse_redir_right_fd(as_string, input) \
     3156    parse_redir_right_fd(input)
     3157#endif
     3158static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
     3159{
     3160    int ch, d, ok;
     3161
     3162    ch = i_peek(input);
     3163    if (ch != '&')
     3164        return REDIRFD_TO_FILE;
     3165
     3166    ch = i_getch(input);  /* get the & */
     3167    nommu_addchr(as_string, ch);
     3168    ch = i_peek(input);
     3169    if (ch == '-') {
     3170        ch = i_getch(input);
     3171        nommu_addchr(as_string, ch);
     3172        return REDIRFD_CLOSE;
     3173    }
     3174    d = 0;
     3175    ok = 0;
     3176    while (ch != EOF && isdigit(ch)) {
     3177        d = d*10 + (ch-'0');
     3178        ok = 1;
     3179        ch = i_getch(input);
     3180        nommu_addchr(as_string, ch);
     3181        ch = i_peek(input);
     3182    }
     3183    if (ok) return d;
     3184
     3185//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
     3186
     3187    bb_error_msg("ambiguous redirect");
     3188    return REDIRFD_SYNTAX_ERR;
     3189}
     3190
     3191/* Return code is 0 normal, 1 if a syntax error is detected
     3192 */
     3193static int parse_redirect(struct parse_context *ctx,
     3194        int fd,
     3195        redir_type style,
     3196        struct in_str *input)
     3197{
     3198    struct command *command = ctx->command;
     3199    struct redir_struct *redir;
     3200    struct redir_struct **redirp;
     3201    int dup_num;
     3202
     3203    dup_num = REDIRFD_TO_FILE;
     3204    if (style != REDIRECT_HEREDOC) {
     3205        /* Check for a '>&1' type redirect */
     3206        dup_num = parse_redir_right_fd(&ctx->as_string, input);
     3207        if (dup_num == REDIRFD_SYNTAX_ERR)
     3208            return 1;
     3209    } else {
     3210        int ch = i_peek(input);
     3211        dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
     3212        if (dup_num) { /* <<-... */
     3213            ch = i_getch(input);
     3214            nommu_addchr(&ctx->as_string, ch);
     3215            ch = i_peek(input);
     3216        }
     3217    }
     3218
     3219    if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
     3220        int ch = i_peek(input);
     3221        if (ch == '|') {
     3222            /* >|FILE redirect ("clobbering" >).
     3223             * Since we do not support "set -o noclobber" yet,
     3224             * >| and > are the same for now. Just eat |.
     3225             */
     3226            ch = i_getch(input);
     3227            nommu_addchr(&ctx->as_string, ch);
     3228        }
     3229    }
     3230
     3231    /* Create a new redir_struct and append it to the linked list */
     3232    redirp = &command->redirects;
     3233    while ((redir = *redirp) != NULL) {
     3234        redirp = &(redir->next);
     3235    }
     3236    *redirp = redir = xzalloc(sizeof(*redir));
     3237    /* redir->next = NULL; */
     3238    /* redir->rd_filename = NULL; */
     3239    redir->rd_type = style;
     3240    redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
     3241
     3242    debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
     3243                redir_table[style].descrip);
     3244
     3245    redir->rd_dup = dup_num;
     3246    if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
     3247        /* Erik had a check here that the file descriptor in question
     3248         * is legit; I postpone that to "run time"
     3249         * A "-" representation of "close me" shows up as a -3 here */
     3250        debug_printf_parse("duplicating redirect '%d>&%d'\n",
     3251                redir->rd_fd, redir->rd_dup);
     3252    } else {
     3253        /* Set ctx->pending_redirect, so we know what to do at the
     3254         * end of the next parsed word. */
     3255        ctx->pending_redirect = redir;
     3256    }
     3257    return 0;
     3258}
     3259
     3260/* If a redirect is immediately preceded by a number, that number is
     3261 * supposed to tell which file descriptor to redirect.  This routine
     3262 * looks for such preceding numbers.  In an ideal world this routine
     3263 * needs to handle all the following classes of redirects...
     3264 *     echo 2>foo     # redirects fd  2 to file "foo", nothing passed to echo
     3265 *     echo 49>foo    # redirects fd 49 to file "foo", nothing passed to echo
     3266 *     echo -2>foo    # redirects fd  1 to file "foo",    "-2" passed to echo
     3267 *     echo 49x>foo   # redirects fd  1 to file "foo",   "49x" passed to echo
     3268 *
     3269 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
     3270 * "2.7 Redirection
     3271 * ... If n is quoted, the number shall not be recognized as part of
     3272 * the redirection expression. For example:
     3273 * echo \2>a
     3274 * writes the character 2 into file a"
     3275 * We are getting it right by setting ->has_quoted_part on any \<char>
     3276 *
     3277 * A -1 return means no valid number was found,
     3278 * the caller should use the appropriate default for this redirection.
     3279 */
     3280static int redirect_opt_num(o_string *o)
     3281{
     3282    int num;
     3283
     3284    if (o->data == NULL)
     3285        return -1;
     3286    num = bb_strtou(o->data, NULL, 10);
     3287    if (errno || num < 0)
     3288        return -1;
     3289    o_reset_to_empty_unquoted(o);
     3290    return num;
     3291}
     3292
     3293#if BB_MMU
     3294#define fetch_till_str(as_string, input, word, skip_tabs) \
     3295    fetch_till_str(input, word, skip_tabs)
     3296#endif
     3297static char *fetch_till_str(o_string *as_string,
     3298        struct in_str *input,
     3299        const char *word,
     3300        int heredoc_flags)
     3301{
     3302    o_string heredoc = NULL_O_STRING;
     3303    unsigned past_EOL;
     3304    int prev = 0; /* not \ */
     3305    int ch;
     3306
     3307    goto jump_in;
     3308    while (1) {
     3309        ch = i_getch(input);
     3310        if (ch != EOF)
     3311            nommu_addchr(as_string, ch);
     3312        if ((ch == '\n' || ch == EOF)
     3313         && ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\')
     3314        ) {
     3315            if (strcmp(heredoc.data + past_EOL, word) == 0) {
     3316                heredoc.data[past_EOL] = '\0';
     3317                debug_printf_parse("parsed heredoc '%s'\n", heredoc.data);
     3318                return heredoc.data;
     3319            }
     3320            while (ch == '\n') {
     3321                o_addchr(&heredoc, ch);
     3322                prev = ch;
     3323 jump_in:
     3324                past_EOL = heredoc.length;
     3325                do {
     3326                    ch = i_getch(input);
     3327                    if (ch != EOF)
     3328                        nommu_addchr(as_string, ch);
     3329                } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
     3330            }
     3331        }
     3332        if (ch == EOF) {
     3333            o_free_unsafe(&heredoc);
     3334            return NULL;
     3335        }
     3336        o_addchr(&heredoc, ch);
     3337        nommu_addchr(as_string, ch);
     3338        if (prev == '\\' && ch == '\\')
     3339            /* Correctly handle foo\\<eol> (not a line cont.) */
     3340            prev = 0; /* not \ */
     3341        else
     3342            prev = ch;
     3343    }
     3344}
     3345
     3346/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
     3347 * and load them all. There should be exactly heredoc_cnt of them.
     3348 */
     3349static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input)
     3350{
     3351    struct pipe *pi = ctx->list_head;
     3352
     3353    while (pi && heredoc_cnt) {
     3354        int i;
     3355        struct command *cmd = pi->cmds;
     3356
     3357        debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
     3358                pi->num_cmds,
     3359                cmd->argv ? cmd->argv[0] : "NONE");
     3360        for (i = 0; i < pi->num_cmds; i++) {
     3361            struct redir_struct *redir = cmd->redirects;
     3362
     3363            debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n",
     3364                    i, cmd->argv ? cmd->argv[0] : "NONE");
     3365            while (redir) {
     3366                if (redir->rd_type == REDIRECT_HEREDOC) {
     3367                    char *p;
     3368
     3369                    redir->rd_type = REDIRECT_HEREDOC2;
     3370                    /* redir->rd_dup is (ab)used to indicate <<- */
     3371                    p = fetch_till_str(&ctx->as_string, input,
     3372                            redir->rd_filename, redir->rd_dup);
     3373                    if (!p) {
     3374                        syntax_error("unexpected EOF in here document");
     3375                        return 1;
     3376                    }
     3377                    free(redir->rd_filename);
     3378                    redir->rd_filename = p;
     3379                    heredoc_cnt--;
     3380                }
     3381                redir = redir->next;
     3382            }
     3383            cmd++;
     3384        }
     3385        pi = pi->next;
     3386    }
     3387#if 0
     3388    /* Should be 0. If it isn't, it's a parse error */
     3389    if (heredoc_cnt)
     3390        bb_error_msg_and_die("heredoc BUG 2");
     3391#endif
     3392    return 0;
     3393}
     3394
     3395
     3396static int run_list(struct pipe *pi);
     3397#if BB_MMU
     3398#define parse_stream(pstring, input, end_trigger) \
     3399    parse_stream(input, end_trigger)
     3400#endif
     3401static struct pipe *parse_stream(char **pstring,
     3402        struct in_str *input,
     3403        int end_trigger);
     3404
     3405
     3406#if !ENABLE_HUSH_FUNCTIONS
     3407#define parse_group(dest, ctx, input, ch) \
     3408    parse_group(ctx, input, ch)
     3409#endif
     3410static int parse_group(o_string *dest, struct parse_context *ctx,
     3411    struct in_str *input, int ch)
     3412{
     3413    /* dest contains characters seen prior to ( or {.
     3414     * Typically it's empty, but for function defs,
     3415     * it contains function name (without '()'). */
     3416    struct pipe *pipe_list;
     3417    int endch;
     3418    struct command *command = ctx->command;
     3419
     3420    debug_printf_parse("parse_group entered\n");
     3421#if ENABLE_HUSH_FUNCTIONS
     3422    if (ch == '(' && !dest->has_quoted_part) {
     3423        if (dest->length)
     3424            if (done_word(dest, ctx))
     3425                return 1;
     3426        if (!command->argv)
     3427            goto skip; /* (... */
     3428        if (command->argv[1]) { /* word word ... (... */
     3429            syntax_error_unexpected_ch('(');
     3430            return 1;
     3431        }
     3432        /* it is "word(..." or "word (..." */
     3433        do
     3434            ch = i_getch(input);
     3435        while (ch == ' ' || ch == '\t');
     3436        if (ch != ')') {
     3437            syntax_error_unexpected_ch(ch);
     3438            return 1;
     3439        }
     3440        nommu_addchr(&ctx->as_string, ch);
     3441        do
     3442            ch = i_getch(input);
     3443        while (ch == ' ' || ch == '\t' || ch == '\n');
     3444        if (ch != '{') {
     3445            syntax_error_unexpected_ch(ch);
     3446            return 1;
     3447        }
     3448        nommu_addchr(&ctx->as_string, ch);
     3449        command->cmd_type = CMD_FUNCDEF;
     3450        goto skip;
     3451    }
     3452#endif
     3453
     3454#if 0 /* Prevented by caller */
     3455    if (command->argv /* word [word]{... */
     3456     || dest->length /* word{... */
     3457     || dest->has_quoted_part /* ""{... */
     3458    ) {
     3459        syntax_error(NULL);
     3460        debug_printf_parse("parse_group return 1: "
     3461            "syntax error, groups and arglists don't mix\n");
     3462        return 1;
     3463    }
     3464#endif
     3465
     3466#if ENABLE_HUSH_FUNCTIONS
     3467 skip:
     3468#endif
     3469    endch = '}';
     3470    if (ch == '(') {
     3471        endch = ')';
     3472        command->cmd_type = CMD_SUBSHELL;
     3473    } else {
     3474        /* bash does not allow "{echo...", requires whitespace */
     3475        ch = i_getch(input);
     3476        if (ch != ' ' && ch != '\t' && ch != '\n') {
     3477            syntax_error_unexpected_ch(ch);
     3478            return 1;
     3479        }
     3480        nommu_addchr(&ctx->as_string, ch);
     3481    }
     3482
     3483    {
     3484#if BB_MMU
     3485# define as_string NULL
     3486#else
     3487        char *as_string = NULL;
     3488#endif
     3489        pipe_list = parse_stream(&as_string, input, endch);
     3490#if !BB_MMU
     3491        if (as_string)
     3492            o_addstr(&ctx->as_string, as_string);
     3493#endif
     3494        /* empty ()/{} or parse error? */
     3495        if (!pipe_list || pipe_list == ERR_PTR) {
     3496            /* parse_stream already emitted error msg */
     3497            if (!BB_MMU)
     3498                free(as_string);
     3499            debug_printf_parse("parse_group return 1: "
     3500                "parse_stream returned %p\n", pipe_list);
     3501            return 1;
     3502        }
     3503        command->group = pipe_list;
     3504#if !BB_MMU
     3505        as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
     3506        command->group_as_string = as_string;
     3507        debug_printf_parse("end of group, remembering as:'%s'\n",
     3508                command->group_as_string);
     3509#endif
     3510#undef as_string
     3511    }
     3512    debug_printf_parse("parse_group return 0\n");
     3513    return 0;
     3514    /* command remains "open", available for possible redirects */
     3515}
     3516
     3517#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
     3518/* Subroutines for copying $(...) and `...` things */
     3519static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
     3520/* '...' */
     3521static void add_till_single_quote(o_string *dest, struct in_str *input)
     3522{
     3523    while (1) {
     3524        int ch = i_getch(input);
     3525        if (ch == EOF) {
     3526            syntax_error_unterm_ch('\'');
     3527            /*xfunc_die(); - redundant */
     3528        }
     3529        if (ch == '\'')
     3530            return;
     3531        o_addchr(dest, ch);
     3532    }
     3533}
     3534/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
     3535static void add_till_double_quote(o_string *dest, struct in_str *input)
     3536{
     3537    while (1) {
     3538        int ch = i_getch(input);
     3539        if (ch == EOF) {
     3540            syntax_error_unterm_ch('"');
     3541            /*xfunc_die(); - redundant */
     3542        }
     3543        if (ch == '"')
     3544            return;
     3545        if (ch == '\\') {  /* \x. Copy both chars. */
     3546            o_addchr(dest, ch);
     3547            ch = i_getch(input);
     3548        }
     3549        o_addchr(dest, ch);
     3550        if (ch == '`') {
     3551            add_till_backquote(dest, input, /*in_dquote:*/ 1);
     3552            o_addchr(dest, ch);
     3553            continue;
     3554        }
     3555        //if (ch == '$') ...
     3556    }
     3557}
     3558/* Process `cmd` - copy contents until "`" is seen. Complicated by
     3559 * \` quoting.
     3560 * "Within the backquoted style of command substitution, backslash
     3561 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
     3562 * The search for the matching backquote shall be satisfied by the first
     3563 * backquote found without a preceding backslash; during this search,
     3564 * if a non-escaped backquote is encountered within a shell comment,
     3565 * a here-document, an embedded command substitution of the $(command)
     3566 * form, or a quoted string, undefined results occur. A single-quoted
     3567 * or double-quoted string that begins, but does not end, within the
     3568 * "`...`" sequence produces undefined results."
     3569 * Example                               Output
     3570 * echo `echo '\'TEST\`echo ZZ\`BEST`    \TESTZZBEST
     3571 */
     3572static void add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
     3573{
     3574    while (1) {
     3575        int ch = i_getch(input);
     3576        if (ch == '`')
     3577            return;
     3578        if (ch == '\\') {
     3579            /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
     3580            ch = i_getch(input);
     3581            if (ch != '`'
     3582             && ch != '$'
     3583             && ch != '\\'
     3584             && (!in_dquote || ch != '"')
     3585            ) {
     3586                o_addchr(dest, '\\');
     3587            }
     3588        }
     3589        if (ch == EOF) {
     3590            syntax_error_unterm_ch('`');
     3591            /*xfunc_die(); - redundant */
     3592        }
     3593        o_addchr(dest, ch);
     3594    }
     3595}
     3596/* Process $(cmd) - copy contents until ")" is seen. Complicated by
     3597 * quoting and nested ()s.
     3598 * "With the $(command) style of command substitution, all characters
     3599 * following the open parenthesis to the matching closing parenthesis
     3600 * constitute the command. Any valid shell script can be used for command,
     3601 * except a script consisting solely of redirections which produces
     3602 * unspecified results."
     3603 * Example                              Output
     3604 * echo $(echo '(TEST)' BEST)           (TEST) BEST
     3605 * echo $(echo 'TEST)' BEST)            TEST) BEST
     3606 * echo $(echo \(\(TEST\) BEST)         ((TEST) BEST
     3607 *
     3608 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
     3609 * can contain arbitrary constructs, just like $(cmd).
     3610 * In bash compat mode, it needs to also be able to stop on ':' or '/'
     3611 * for ${var:N[:M]} and ${var/P[/R]} parsing.
     3612 */
     3613#define DOUBLE_CLOSE_CHAR_FLAG 0x80
     3614static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
     3615{
     3616    int ch;
     3617    char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
     3618# if ENABLE_HUSH_BASH_COMPAT
     3619    char end_char2 = end_ch >> 8;
     3620# endif
     3621    end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
     3622
     3623    while (1) {
     3624        ch = i_getch(input);
     3625        if (ch == EOF) {
     3626            syntax_error_unterm_ch(end_ch);
     3627            /*xfunc_die(); - redundant */
     3628        }
     3629        if (ch == end_ch  IF_HUSH_BASH_COMPAT( || ch == end_char2)) {
     3630            if (!dbl)
     3631                break;
     3632            /* we look for closing )) of $((EXPR)) */
     3633            if (i_peek(input) == end_ch) {
     3634                i_getch(input); /* eat second ')' */
     3635                break;
     3636            }
     3637        }
     3638        o_addchr(dest, ch);
     3639        if (ch == '(' || ch == '{') {
     3640            ch = (ch == '(' ? ')' : '}');
     3641            add_till_closing_bracket(dest, input, ch);
     3642            o_addchr(dest, ch);
     3643            continue;
     3644        }
     3645        if (ch == '\'') {
     3646            add_till_single_quote(dest, input);
     3647            o_addchr(dest, ch);
     3648            continue;
     3649        }
     3650        if (ch == '"') {
     3651            add_till_double_quote(dest, input);
     3652            o_addchr(dest, ch);
     3653            continue;
     3654        }
     3655        if (ch == '`') {
     3656            add_till_backquote(dest, input, /*in_dquote:*/ 0);
     3657            o_addchr(dest, ch);
     3658            continue;
     3659        }
     3660        if (ch == '\\') {
     3661            /* \x. Copy verbatim. Important for  \(, \) */
     3662            ch = i_getch(input);
     3663            if (ch == EOF) {
     3664                syntax_error_unterm_ch(')');
     3665                /*xfunc_die(); - redundant */
     3666            }
     3667            o_addchr(dest, ch);
     3668            continue;
     3669        }
     3670    }
     3671    return ch;
     3672}
     3673#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
     3674
     3675/* Return code: 0 for OK, 1 for syntax error */
     3676#if BB_MMU
     3677#define parse_dollar(as_string, dest, input, quote_mask) \
     3678    parse_dollar(dest, input, quote_mask)
     3679#define as_string NULL
     3680#endif
     3681static int parse_dollar(o_string *as_string,
     3682        o_string *dest,
     3683        struct in_str *input, unsigned char quote_mask)
     3684{
     3685    int ch = i_peek(input);  /* first character after the $ */
     3686
     3687    debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
     3688    if (isalpha(ch)) {
     3689        ch = i_getch(input);
     3690        nommu_addchr(as_string, ch);
     3691 make_var:
     3692        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3693        while (1) {
     3694            debug_printf_parse(": '%c'\n", ch);
     3695            o_addchr(dest, ch | quote_mask);
     3696            quote_mask = 0;
     3697            ch = i_peek(input);
     3698            if (!isalnum(ch) && ch != '_')
     3699                break;
     3700            ch = i_getch(input);
     3701            nommu_addchr(as_string, ch);
     3702        }
     3703        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3704    } else if (isdigit(ch)) {
     3705 make_one_char_var:
     3706        ch = i_getch(input);
     3707        nommu_addchr(as_string, ch);
     3708        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3709        debug_printf_parse(": '%c'\n", ch);
     3710        o_addchr(dest, ch | quote_mask);
     3711        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3712    } else switch (ch) {
     3713    case '$': /* pid */
     3714    case '!': /* last bg pid */
     3715    case '?': /* last exit code */
     3716    case '#': /* number of args */
     3717    case '*': /* args */
     3718    case '@': /* args */
     3719        goto make_one_char_var;
     3720    case '{': {
     3721        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3722
     3723        ch = i_getch(input); /* eat '{' */
     3724        nommu_addchr(as_string, ch);
     3725
     3726        ch = i_getch(input); /* first char after '{' */
     3727        /* It should be ${?}, or ${#var},
     3728         * or even ${?+subst} - operator acting on a special variable,
     3729         * or the beginning of variable name.
     3730         */
     3731        if (ch == EOF
     3732         || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
     3733        ) {
     3734 bad_dollar_syntax:
     3735            syntax_error_unterm_str("${name}");
     3736            debug_printf_parse("parse_dollar return 1: unterminated ${name}\n");
     3737            return 1;
     3738        }
     3739        nommu_addchr(as_string, ch);
     3740        ch |= quote_mask;
     3741
     3742        /* It's possible to just call add_till_closing_bracket() at this point.
     3743         * However, this regresses some of our testsuite cases
     3744         * which check invalid constructs like ${%}.
     3745         * Oh well... let's check that the var name part is fine... */
     3746
     3747        while (1) {
     3748            unsigned pos;
     3749
     3750            o_addchr(dest, ch);
     3751            debug_printf_parse(": '%c'\n", ch);
     3752
     3753            ch = i_getch(input);
     3754            nommu_addchr(as_string, ch);
     3755            if (ch == '}')
     3756                break;
     3757
     3758            if (!isalnum(ch) && ch != '_') {
     3759                unsigned end_ch;
     3760                unsigned char last_ch;
     3761                /* handle parameter expansions
     3762                 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
     3763                 */
     3764                if (!strchr(VAR_SUBST_OPS, ch)) /* ${var<bad_char>... */
     3765                    goto bad_dollar_syntax;
     3766
     3767                /* Eat everything until closing '}' (or ':') */
     3768                end_ch = '}';
     3769                if (ENABLE_HUSH_BASH_COMPAT
     3770                 && ch == ':'
     3771                 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
     3772                ) {
     3773                    /* It's ${var:N[:M]} thing */
     3774                    end_ch = '}' * 0x100 + ':';
     3775                }
     3776                if (ENABLE_HUSH_BASH_COMPAT
     3777                 && ch == '/'
     3778                ) {
     3779                    /* It's ${var/[/]pattern[/repl]} thing */
     3780                    if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
     3781                        i_getch(input);
     3782                        nommu_addchr(as_string, '/');
     3783                        ch = '\\';
     3784                    }
     3785                    end_ch = '}' * 0x100 + '/';
     3786                }
     3787                o_addchr(dest, ch);
     3788 again:
     3789                if (!BB_MMU)
     3790                    pos = dest->length;
     3791#if ENABLE_HUSH_DOLLAR_OPS
     3792                last_ch = add_till_closing_bracket(dest, input, end_ch);
     3793#else
     3794#error Simple code to only allow ${var} is not implemented
     3795#endif
     3796                if (as_string) {
     3797                    o_addstr(as_string, dest->data + pos);
     3798                    o_addchr(as_string, last_ch);
     3799                }
     3800
     3801                if (ENABLE_HUSH_BASH_COMPAT && (end_ch & 0xff00)) {
     3802                    /* close the first block: */
     3803                    o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3804                    /* while parsing N from ${var:N[:M]}
     3805                     * or pattern from ${var/[/]pattern[/repl]} */
     3806                    if ((end_ch & 0xff) == last_ch) {
     3807                        /* got ':' or '/'- parse the rest */
     3808                        end_ch = '}';
     3809                        goto again;
     3810                    }
     3811                    /* got '}' */
     3812                    if (end_ch == '}' * 0x100 + ':') {
     3813                        /* it's ${var:N} - emulate :999999999 */
     3814                        o_addstr(dest, "999999999");
     3815                    } /* else: it's ${var/[/]pattern} */
     3816                }
     3817                break;
     3818            }
     3819        }
     3820        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3821        break;
     3822    }
     3823#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
     3824    case '(': {
     3825        unsigned pos;
     3826
     3827        ch = i_getch(input);
     3828        nommu_addchr(as_string, ch);
     3829# if ENABLE_SH_MATH_SUPPORT
     3830        if (i_peek(input) == '(') {
     3831            ch = i_getch(input);
     3832            nommu_addchr(as_string, ch);
     3833            o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3834            o_addchr(dest, /*quote_mask |*/ '+');
     3835            if (!BB_MMU)
     3836                pos = dest->length;
     3837            add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG);
     3838            if (as_string) {
     3839                o_addstr(as_string, dest->data + pos);
     3840                o_addchr(as_string, ')');
     3841                o_addchr(as_string, ')');
     3842            }
     3843            o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3844            break;
     3845        }
     3846# endif
     3847# if ENABLE_HUSH_TICK
     3848        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3849        o_addchr(dest, quote_mask | '`');
     3850        if (!BB_MMU)
     3851            pos = dest->length;
     3852        add_till_closing_bracket(dest, input, ')');
     3853        if (as_string) {
     3854            o_addstr(as_string, dest->data + pos);
     3855            o_addchr(as_string, ')');
     3856        }
     3857        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3858# endif
     3859        break;
     3860    }
     3861#endif
     3862    case '_':
     3863        ch = i_getch(input);
     3864        nommu_addchr(as_string, ch);
     3865        ch = i_peek(input);
     3866        if (isalnum(ch)) { /* it's $_name or $_123 */
     3867            ch = '_';
     3868            goto make_var;
     3869        }
     3870        /* else: it's $_ */
     3871    /* TODO: $_ and $-: */
     3872    /* $_ Shell or shell script name; or last argument of last command
     3873     * (if last command wasn't a pipe; if it was, bash sets $_ to "");
     3874     * but in command's env, set to full pathname used to invoke it */
     3875    /* $- Option flags set by set builtin or shell options (-i etc) */
     3876    default:
     3877        o_addQchr(dest, '$');
     3878    }
     3879    debug_printf_parse("parse_dollar return 0\n");
     3880    return 0;
     3881#undef as_string
     3882}
     3883
     3884#if BB_MMU
     3885# if ENABLE_HUSH_BASH_COMPAT
     3886#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
     3887    encode_string(dest, input, dquote_end, process_bkslash)
     3888# else
     3889/* only ${var/pattern/repl} (its pattern part) needs additional mode */
     3890#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
     3891    encode_string(dest, input, dquote_end)
     3892# endif
     3893#define as_string NULL
     3894
     3895#else /* !MMU */
     3896
     3897# if ENABLE_HUSH_BASH_COMPAT
     3898/* all parameters are needed, no macro tricks */
     3899# else
     3900#define encode_string(as_string, dest, input, dquote_end, process_bkslash) \
     3901    encode_string(as_string, dest, input, dquote_end)
     3902# endif
     3903#endif
     3904static int encode_string(o_string *as_string,
     3905        o_string *dest,
     3906        struct in_str *input,
     3907        int dquote_end,
     3908        int process_bkslash)
     3909{
     3910#if !ENABLE_HUSH_BASH_COMPAT
     3911    const int process_bkslash = 1;
     3912#endif
     3913    int ch;
     3914    int next;
     3915
     3916 again:
     3917    ch = i_getch(input);
     3918    if (ch != EOF)
     3919        nommu_addchr(as_string, ch);
     3920    if (ch == dquote_end) { /* may be only '"' or EOF */
     3921        debug_printf_parse("encode_string return 0\n");
     3922        return 0;
     3923    }
     3924    /* note: can't move it above ch == dquote_end check! */
     3925    if (ch == EOF) {
     3926        syntax_error_unterm_ch('"');
     3927        /*xfunc_die(); - redundant */
     3928    }
     3929    next = '\0';
     3930    if (ch != '\n') {
     3931        next = i_peek(input);
     3932    }
     3933    debug_printf_parse("\" ch=%c (%d) escape=%d\n",
     3934            ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
     3935    if (process_bkslash && ch == '\\') {
     3936        if (next == EOF) {
     3937            syntax_error("\\<eof>");
     3938            xfunc_die();
     3939        }
     3940        /* bash:
     3941         * "The backslash retains its special meaning [in "..."]
     3942         * only when followed by one of the following characters:
     3943         * $, `, ", \, or <newline>.  A double quote may be quoted
     3944         * within double quotes by preceding it with a backslash."
     3945         * NB: in (unquoted) heredoc, above does not apply to ",
     3946         * therefore we check for it by "next == dquote_end" cond.
     3947         */
     3948        if (next == dquote_end || strchr("$`\\\n", next)) {
     3949            ch = i_getch(input); /* eat next */
     3950            if (ch == '\n')
     3951                goto again; /* skip \<newline> */
     3952        } /* else: ch remains == '\\', and we double it below: */
     3953        o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
     3954        nommu_addchr(as_string, ch);
     3955        goto again;
     3956    }
     3957    if (ch == '$') {
     3958        if (parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80) != 0) {
     3959            debug_printf_parse("encode_string return 1: "
     3960                    "parse_dollar returned non-0\n");
     3961            return 1;
     3962        }
     3963        goto again;
     3964    }
     3965#if ENABLE_HUSH_TICK
     3966    if (ch == '`') {
     3967        //unsigned pos = dest->length;
     3968        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3969        o_addchr(dest, 0x80 | '`');
     3970        add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"');
     3971        o_addchr(dest, SPECIAL_VAR_SYMBOL);
     3972        //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
     3973        goto again;
     3974    }
     3975#endif
     3976    o_addQchr(dest, ch);
     3977    goto again;
     3978#undef as_string
     3979}
     3980
     3981/*
     3982 * Scan input until EOF or end_trigger char.
     3983 * Return a list of pipes to execute, or NULL on EOF
     3984 * or if end_trigger character is met.
     3985 * On syntax error, exit is shell is not interactive,
     3986 * reset parsing machinery and start parsing anew,
     3987 * or return ERR_PTR.
     3988 */
     3989static struct pipe *parse_stream(char **pstring,
     3990        struct in_str *input,
     3991        int end_trigger)
     3992{
     3993    struct parse_context ctx;
     3994    o_string dest = NULL_O_STRING;
     3995    int heredoc_cnt;
     3996
     3997    /* Single-quote triggers a bypass of the main loop until its mate is
     3998     * found.  When recursing, quote state is passed in via dest->o_expflags.
     3999     */
     4000    debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
     4001            end_trigger ? end_trigger : 'X');
     4002    debug_enter();
     4003
     4004    /* If very first arg is "" or '', dest.data may end up NULL.
     4005     * Preventing this: */
     4006    o_addchr(&dest, '\0');
     4007    dest.length = 0;
     4008
     4009    /* We used to separate words on $IFS here. This was wrong.
     4010     * $IFS is used only for word splitting when $var is expanded,
     4011     * here we should use blank chars as separators, not $IFS
     4012     */
     4013
     4014 reset: /* we come back here only on syntax errors in interactive shell */
     4015
     4016#if ENABLE_HUSH_INTERACTIVE
     4017    input->promptmode = 0; /* PS1 */
     4018#endif
     4019    if (MAYBE_ASSIGNMENT != 0)
     4020        dest.o_assignment = MAYBE_ASSIGNMENT;
     4021    initialize_context(&ctx);
     4022    heredoc_cnt = 0;
     4023    while (1) {
     4024        const char *is_blank;
     4025        const char *is_special;
     4026        int ch;
     4027        int next;
     4028        int redir_fd;
     4029        redir_type redir_style;
     4030
     4031        ch = i_getch(input);
     4032        debug_printf_parse(": ch=%c (%d) escape=%d\n",
     4033                ch, ch, !!(dest.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
     4034        if (ch == EOF) {
     4035            struct pipe *pi;
     4036
     4037            if (heredoc_cnt) {
     4038                syntax_error_unterm_str("here document");
     4039                goto parse_error;
     4040            }
     4041            /* end_trigger == '}' case errors out earlier,
     4042             * checking only ')' */
     4043            if (end_trigger == ')') {
     4044                syntax_error_unterm_ch('('); /* exits */
     4045                /* goto parse_error; */
     4046            }
     4047
     4048            if (done_word(&dest, &ctx)) {
     4049                goto parse_error;
     4050            }
     4051            o_free(&dest);
     4052            done_pipe(&ctx, PIPE_SEQ);
     4053            pi = ctx.list_head;
     4054            /* If we got nothing... */
     4055            /* (this makes bare "&" cmd a no-op.
     4056             * bash says: "syntax error near unexpected token '&'") */
     4057            if (pi->num_cmds == 0
     4058                IF_HAS_KEYWORDS( && pi->res_word == RES_NONE)
     4059            ) {
     4060                free_pipe_list(pi);
     4061                pi = NULL;
     4062            }
     4063#if !BB_MMU
     4064            debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
     4065            if (pstring)
     4066                *pstring = ctx.as_string.data;
     4067            else
     4068                o_free_unsafe(&ctx.as_string);
     4069#endif
     4070            debug_leave();
     4071            debug_printf_parse("parse_stream return %p\n", pi);
     4072            return pi;
     4073        }
     4074        nommu_addchr(&ctx.as_string, ch);
     4075
     4076        next = '\0';
     4077        if (ch != '\n')
     4078            next = i_peek(input);
     4079
     4080        is_special = "{}<>;&|()#'" /* special outside of "str" */
     4081                "\\$\"" IF_HUSH_TICK("`"); /* always special */
     4082        /* Are { and } special here? */
     4083        if (ctx.command->argv /* word [word]{... - non-special */
     4084         || dest.length       /* word{... - non-special */
     4085         || dest.has_quoted_part     /* ""{... - non-special */
     4086         || (next != ';'             /* }; - special */
     4087            && next != ')'           /* }) - special */
     4088            && next != '&'           /* }& and }&& ... - special */
     4089            && next != '|'           /* }|| ... - special */
     4090            && !strchr(defifs, next) /* {word - non-special */
     4091            )
     4092        ) {
     4093            /* They are not special, skip "{}" */
     4094            is_special += 2;
     4095        }
     4096        is_special = strchr(is_special, ch);
     4097        is_blank = strchr(defifs, ch);
     4098
     4099        if (!is_special && !is_blank) { /* ordinary char */
     4100 ordinary_char:
     4101            o_addQchr(&dest, ch);
     4102            if ((dest.o_assignment == MAYBE_ASSIGNMENT
     4103                || dest.o_assignment == WORD_IS_KEYWORD)
     4104             && ch == '='
     4105             && is_well_formed_var_name(dest.data, '=')
     4106            ) {
     4107                dest.o_assignment = DEFINITELY_ASSIGNMENT;
     4108            }
     4109            continue;
     4110        }
     4111
     4112        if (is_blank) {
     4113            if (done_word(&dest, &ctx)) {
     4114                goto parse_error;
     4115            }
     4116            if (ch == '\n') {
     4117                /* Is this a case when newline is simply ignored?
     4118                 * Some examples:
     4119                 * "cmd | <newline> cmd ..."
     4120                 * "case ... in <newline> word) ..."
     4121                 */
     4122                if (IS_NULL_CMD(ctx.command)
     4123                 && dest.length == 0 && !dest.has_quoted_part
     4124                ) {
     4125                    /* This newline can be ignored. But...
     4126                     * Without check #1, interactive shell
     4127                     * ignores even bare <newline>,
     4128                     * and shows the continuation prompt:
     4129                     * ps1_prompt$ <enter>
     4130                     * ps2> _   <=== wrong, should be ps1
     4131                     * Without check #2, "cmd & <newline>"
     4132                     * is similarly mistreated.
     4133                     * (BTW, this makes "cmd & cmd"
     4134                     * and "cmd && cmd" non-orthogonal.
     4135                     * Really, ask yourself, why
     4136                     * "cmd && <newline>" doesn't start
     4137                     * cmd but waits for more input?
     4138                     * No reason...)
     4139                     */
     4140                    struct pipe *pi = ctx.list_head;
     4141                    if (pi->num_cmds != 0       /* check #1 */
     4142                     && pi->followup != PIPE_BG /* check #2 */
     4143                    ) {
     4144                        continue;
     4145                    }
     4146                }
     4147                /* Treat newline as a command separator. */
     4148                done_pipe(&ctx, PIPE_SEQ);
     4149                debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt);
     4150                if (heredoc_cnt) {
     4151                    if (fetch_heredocs(heredoc_cnt, &ctx, input)) {
     4152                        goto parse_error;
     4153                    }
     4154                    heredoc_cnt = 0;
     4155                }
     4156                dest.o_assignment = MAYBE_ASSIGNMENT;
     4157                ch = ';';
     4158                /* note: if (is_blank) continue;
     4159                 * will still trigger for us */
     4160            }
     4161        }
     4162
     4163        /* "cmd}" or "cmd }..." without semicolon or &:
     4164         * } is an ordinary char in this case, even inside { cmd; }
     4165         * Pathological example: { ""}; } should exec "}" cmd
     4166         */
     4167        if (ch == '}') {
     4168            if (!IS_NULL_CMD(ctx.command) /* cmd } */
     4169             || dest.length != 0 /* word} */
     4170             || dest.has_quoted_part    /* ""} */
     4171            ) {
     4172                goto ordinary_char;
     4173            }
     4174            if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
     4175                goto skip_end_trigger;
     4176            /* else: } does terminate a group */
     4177        }
     4178
     4179        if (end_trigger && end_trigger == ch
     4180         && (ch != ';' || heredoc_cnt == 0)
     4181#if ENABLE_HUSH_CASE
     4182         && (ch != ')'
     4183            || ctx.ctx_res_w != RES_MATCH
     4184            || (!dest.has_quoted_part && strcmp(dest.data, "esac") == 0)
     4185            )
     4186#endif
     4187        ) {
     4188            if (heredoc_cnt) {
     4189                /* This is technically valid:
     4190                 * { cat <<HERE; }; echo Ok
     4191                 * heredoc
     4192                 * heredoc
     4193                 * HERE
     4194                 * but we don't support this.
     4195                 * We require heredoc to be in enclosing {}/(),
     4196                 * if any.
     4197                 */
     4198                syntax_error_unterm_str("here document");
     4199                goto parse_error;
     4200            }
     4201            if (done_word(&dest, &ctx)) {
     4202                goto parse_error;
     4203            }
     4204            done_pipe(&ctx, PIPE_SEQ);
     4205            dest.o_assignment = MAYBE_ASSIGNMENT;
     4206            /* Do we sit outside of any if's, loops or case's? */
     4207            if (!HAS_KEYWORDS
     4208             IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
     4209            ) {
     4210                o_free(&dest);
     4211#if !BB_MMU
     4212                debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
     4213                if (pstring)
     4214                    *pstring = ctx.as_string.data;
     4215                else
     4216                    o_free_unsafe(&ctx.as_string);
     4217#endif
     4218                debug_leave();
     4219                debug_printf_parse("parse_stream return %p: "
     4220                        "end_trigger char found\n",
     4221                        ctx.list_head);
     4222                return ctx.list_head;
     4223            }
     4224        }
     4225 skip_end_trigger:
     4226        if (is_blank)
     4227            continue;
     4228
     4229        /* Catch <, > before deciding whether this word is
     4230         * an assignment. a=1 2>z b=2: b=2 is still assignment */
     4231        switch (ch) {
     4232        case '>':
     4233            redir_fd = redirect_opt_num(&dest);
     4234            if (done_word(&dest, &ctx)) {
     4235                goto parse_error;
     4236            }
     4237            redir_style = REDIRECT_OVERWRITE;
     4238            if (next == '>') {
     4239                redir_style = REDIRECT_APPEND;
     4240                ch = i_getch(input);
     4241                nommu_addchr(&ctx.as_string, ch);
     4242            }
     4243#if 0
     4244            else if (next == '(') {
     4245                syntax_error(">(process) not supported");
     4246                goto parse_error;
     4247            }
     4248#endif
     4249            if (parse_redirect(&ctx, redir_fd, redir_style, input))
     4250                goto parse_error;
     4251            continue; /* back to top of while (1) */
     4252        case '<':
     4253            redir_fd = redirect_opt_num(&dest);
     4254            if (done_word(&dest, &ctx)) {
     4255                goto parse_error;
     4256            }
     4257            redir_style = REDIRECT_INPUT;
     4258            if (next == '<') {
     4259                redir_style = REDIRECT_HEREDOC;
     4260                heredoc_cnt++;
     4261                debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt);
     4262                ch = i_getch(input);
     4263                nommu_addchr(&ctx.as_string, ch);
     4264            } else if (next == '>') {
     4265                redir_style = REDIRECT_IO;
     4266                ch = i_getch(input);
     4267                nommu_addchr(&ctx.as_string, ch);
     4268            }
     4269#if 0
     4270            else if (next == '(') {
     4271                syntax_error("<(process) not supported");
     4272                goto parse_error;
     4273            }
     4274#endif
     4275            if (parse_redirect(&ctx, redir_fd, redir_style, input))
     4276                goto parse_error;
     4277            continue; /* back to top of while (1) */
     4278        case '#':
     4279            if (dest.length == 0 && !dest.has_quoted_part) {
     4280                /* skip "#comment" */
     4281                while (1) {
     4282                    ch = i_peek(input);
     4283                    if (ch == EOF || ch == '\n')
     4284                        break;
     4285                    i_getch(input);
     4286                    /* note: we do not add it to &ctx.as_string */
     4287                }
     4288                nommu_addchr(&ctx.as_string, '\n');
     4289                continue; /* back to top of while (1) */
     4290            }
     4291            break;
     4292        case '\\':
     4293            if (next == '\n') {
     4294                /* It's "\<newline>" */
     4295#if !BB_MMU
     4296                /* Remove trailing '\' from ctx.as_string */
     4297                ctx.as_string.data[--ctx.as_string.length] = '\0';
     4298#endif
     4299                ch = i_getch(input); /* eat it */
     4300                continue; /* back to top of while (1) */
     4301            }
     4302            break;
     4303        }
     4304
     4305        if (dest.o_assignment == MAYBE_ASSIGNMENT
     4306         /* check that we are not in word in "a=1 2>word b=1": */
     4307         && !ctx.pending_redirect
     4308        ) {
     4309            /* ch is a special char and thus this word
     4310             * cannot be an assignment */
     4311            dest.o_assignment = NOT_ASSIGNMENT;
     4312        }
     4313
     4314        /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
     4315
     4316        switch (ch) {
     4317        case '#': /* non-comment #: "echo a#b" etc */
     4318            o_addQchr(&dest, ch);
     4319            break;
     4320        case '\\':
     4321            if (next == EOF) {
     4322                syntax_error("\\<eof>");
     4323                xfunc_die();
     4324            }
     4325            ch = i_getch(input);
     4326            /* note: ch != '\n' (that case does not reach this place) */
     4327            o_addchr(&dest, '\\');
     4328            /*nommu_addchr(&ctx.as_string, '\\'); - already done */
     4329            o_addchr(&dest, ch);
     4330            nommu_addchr(&ctx.as_string, ch);
     4331            /* Example: echo Hello \2>file
     4332             * we need to know that word 2 is quoted */
     4333            dest.has_quoted_part = 1;
     4334            break;
     4335        case '$':
     4336            if (parse_dollar(&ctx.as_string, &dest, input, /*quote_mask:*/ 0) != 0) {
     4337                debug_printf_parse("parse_stream parse error: "
     4338                    "parse_dollar returned non-0\n");
     4339                goto parse_error;
     4340            }
     4341            break;
     4342        case '\'':
     4343            dest.has_quoted_part = 1;
     4344            while (1) {
     4345                ch = i_getch(input);
     4346                if (ch == EOF) {
     4347                    syntax_error_unterm_ch('\'');
     4348                    /*xfunc_die(); - redundant */
     4349                }
     4350                nommu_addchr(&ctx.as_string, ch);
     4351                if (ch == '\'')
     4352                    break;
     4353                o_addqchr(&dest, ch);
     4354            }
     4355            break;
     4356        case '"':
     4357            dest.has_quoted_part = 1;
     4358            if (dest.o_assignment == NOT_ASSIGNMENT)
     4359                dest.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
     4360            if (encode_string(&ctx.as_string, &dest, input, '"', /*process_bkslash:*/ 1))
     4361                goto parse_error;
     4362            dest.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
     4363            break;
     4364#if ENABLE_HUSH_TICK
     4365        case '`': {
     4366            unsigned pos;
     4367
     4368            o_addchr(&dest, SPECIAL_VAR_SYMBOL);
     4369            o_addchr(&dest, '`');
     4370            pos = dest.length;
     4371            add_till_backquote(&dest, input, /*in_dquote:*/ 0);
     4372# if !BB_MMU
     4373            o_addstr(&ctx.as_string, dest.data + pos);
     4374            o_addchr(&ctx.as_string, '`');
     4375# endif
     4376            o_addchr(&dest, SPECIAL_VAR_SYMBOL);
     4377            //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
     4378            break;
     4379        }
     4380#endif
     4381        case ';':
     4382#if ENABLE_HUSH_CASE
     4383 case_semi:
     4384#endif
     4385            if (done_word(&dest, &ctx)) {
     4386                goto parse_error;
     4387            }
     4388            done_pipe(&ctx, PIPE_SEQ);
     4389#if ENABLE_HUSH_CASE
     4390            /* Eat multiple semicolons, detect
     4391             * whether it means something special */
     4392            while (1) {
     4393                ch = i_peek(input);
     4394                if (ch != ';')
     4395                    break;
     4396                ch = i_getch(input);
     4397                nommu_addchr(&ctx.as_string, ch);
     4398                if (ctx.ctx_res_w == RES_CASE_BODY) {
     4399                    ctx.ctx_dsemicolon = 1;
     4400                    ctx.ctx_res_w = RES_MATCH;
     4401                    break;
     4402                }
     4403            }
     4404#endif
     4405 new_cmd:
     4406            /* We just finished a cmd. New one may start
     4407             * with an assignment */
     4408            dest.o_assignment = MAYBE_ASSIGNMENT;
     4409            break;
     4410        case '&':
     4411            if (done_word(&dest, &ctx)) {
     4412                goto parse_error;
     4413            }
     4414            if (next == '&') {
     4415                ch = i_getch(input);
     4416                nommu_addchr(&ctx.as_string, ch);
     4417                done_pipe(&ctx, PIPE_AND);
     4418            } else {
     4419                done_pipe(&ctx, PIPE_BG);
     4420            }
     4421            goto new_cmd;
     4422        case '|':
     4423            if (done_word(&dest, &ctx)) {
     4424                goto parse_error;
     4425            }
     4426#if ENABLE_HUSH_CASE
     4427            if (ctx.ctx_res_w == RES_MATCH)
     4428                break; /* we are in case's "word | word)" */
     4429#endif
     4430            if (next == '|') { /* || */
     4431                ch = i_getch(input);
     4432                nommu_addchr(&ctx.as_string, ch);
     4433                done_pipe(&ctx, PIPE_OR);
     4434            } else {
     4435                /* we could pick up a file descriptor choice here
     4436                 * with redirect_opt_num(), but bash doesn't do it.
     4437                 * "echo foo 2| cat" yields "foo 2". */
     4438                done_command(&ctx);
     4439#if !BB_MMU
     4440                o_reset_to_empty_unquoted(&ctx.as_string);
     4441#endif
     4442            }
     4443            goto new_cmd;
     4444        case '(':
     4445#if ENABLE_HUSH_CASE
     4446            /* "case... in [(]word)..." - skip '(' */
     4447            if (ctx.ctx_res_w == RES_MATCH
     4448             && ctx.command->argv == NULL /* not (word|(... */
     4449             && dest.length == 0 /* not word(... */
     4450             && dest.has_quoted_part == 0 /* not ""(... */
     4451            ) {
     4452                continue;
     4453            }
     4454#endif
     4455        case '{':
     4456            if (parse_group(&dest, &ctx, input, ch) != 0) {
     4457                goto parse_error;
     4458            }
     4459            goto new_cmd;
     4460        case ')':
     4461#if ENABLE_HUSH_CASE
     4462            if (ctx.ctx_res_w == RES_MATCH)
     4463                goto case_semi;
     4464#endif
     4465        case '}':
     4466            /* proper use of this character is caught by end_trigger:
     4467             * if we see {, we call parse_group(..., end_trigger='}')
     4468             * and it will match } earlier (not here). */
     4469            syntax_error_unexpected_ch(ch);
     4470            goto parse_error;
     4471        default:
     4472            if (HUSH_DEBUG)
     4473                bb_error_msg_and_die("BUG: unexpected %c\n", ch);
     4474        }
     4475    } /* while (1) */
     4476
     4477 parse_error:
     4478    {
     4479        struct parse_context *pctx;
     4480        IF_HAS_KEYWORDS(struct parse_context *p2;)
     4481
     4482        /* Clean up allocated tree.
     4483         * Sample for finding leaks on syntax error recovery path.
     4484         * Run it from interactive shell, watch pmap `pidof hush`.
     4485         * while if false; then false; fi; do break; fi
     4486         * Samples to catch leaks at execution:
     4487         * while if (true | {true;}); then echo ok; fi; do break; done
     4488         * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
     4489         */
     4490        pctx = &ctx;
     4491        do {
     4492            /* Update pipe/command counts,
     4493             * otherwise freeing may miss some */
     4494            done_pipe(pctx, PIPE_SEQ);
     4495            debug_printf_clean("freeing list %p from ctx %p\n",
     4496                    pctx->list_head, pctx);
     4497            debug_print_tree(pctx->list_head, 0);
     4498            free_pipe_list(pctx->list_head);
     4499            debug_printf_clean("freed list %p\n", pctx->list_head);
     4500#if !BB_MMU
     4501            o_free_unsafe(&pctx->as_string);
     4502#endif
     4503            IF_HAS_KEYWORDS(p2 = pctx->stack;)
     4504            if (pctx != &ctx) {
     4505                free(pctx);
     4506            }
     4507            IF_HAS_KEYWORDS(pctx = p2;)
     4508        } while (HAS_KEYWORDS && pctx);
     4509        /* Free text, clear all dest fields */
     4510        o_free(&dest);
     4511        /* If we are not in top-level parse, we return,
     4512         * our caller will propagate error.
     4513         */
     4514        if (end_trigger != ';') {
     4515#if !BB_MMU
     4516            if (pstring)
     4517                *pstring = NULL;
     4518#endif
     4519            debug_leave();
     4520            return ERR_PTR;
     4521        }
     4522        /* Discard cached input, force prompt */
     4523        input->p = NULL;
     4524        IF_HUSH_INTERACTIVE(input->promptme = 1;)
     4525        goto reset;
     4526    }
     4527}
     4528
     4529
     4530/*** Execution routines ***/
     4531
     4532/* Expansion can recurse, need forward decls: */
     4533#if !ENABLE_HUSH_BASH_COMPAT
     4534/* only ${var/pattern/repl} (its pattern part) needs additional mode */
     4535#define expand_string_to_string(str, do_unbackslash) \
     4536    expand_string_to_string(str)
     4537#endif
     4538static char *expand_string_to_string(const char *str, int do_unbackslash);
     4539#if ENABLE_HUSH_TICK
     4540static int process_command_subs(o_string *dest, const char *s);
     4541#endif
     4542
     4543/* expand_strvec_to_strvec() takes a list of strings, expands
     4544 * all variable references within and returns a pointer to
     4545 * a list of expanded strings, possibly with larger number
     4546 * of strings. (Think VAR="a b"; echo $VAR).
     4547 * This new list is allocated as a single malloc block.
     4548 * NULL-terminated list of char* pointers is at the beginning of it,
     4549 * followed by strings themselves.
     4550 * Caller can deallocate entire list by single free(list). */
     4551
     4552/* A horde of its helpers come first: */
     4553
     4554static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
     4555{
     4556    while (--len >= 0) {
     4557        char c = *str++;
     4558
     4559#if ENABLE_HUSH_BRACE_EXPANSION
     4560        if (c == '{' || c == '}') {
     4561            /* { -> \{, } -> \} */
     4562            o_addchr(o, '\\');
     4563            /* And now we want to add { or } and continue:
     4564             *  o_addchr(o, c);
     4565             *  continue;
     4566             * luckily, just falling throught achieves this.
     4567             */
     4568        }
     4569#endif
     4570        o_addchr(o, c);
     4571        if (c == '\\') {
     4572            /* \z -> \\\z; \<eol> -> \\<eol> */
     4573            o_addchr(o, '\\');
     4574            if (len) {
     4575                len--;
     4576                o_addchr(o, '\\');
     4577                o_addchr(o, *str++);
     4578            }
     4579        }
     4580    }
     4581}
     4582
     4583/* Store given string, finalizing the word and starting new one whenever
     4584 * we encounter IFS char(s). This is used for expanding variable values.
     4585 * End-of-string does NOT finalize word: think about 'echo -$VAR-' */
     4586static int expand_on_ifs(o_string *output, int n, const char *str)
     4587{
     4588    while (1) {
     4589        int word_len = strcspn(str, G.ifs);
     4590        if (word_len) {
     4591            if (!(output->o_expflags & EXP_FLAG_GLOB)) {
     4592                o_addblock(output, str, word_len);
     4593            } else {
     4594                /* Protect backslashes against globbing up :)
     4595                 * Example: "v='\*'; echo b$v" prints "b\*"
     4596                 * (and does not try to glob on "*")
     4597                 */
     4598                o_addblock_duplicate_backslash(output, str, word_len);
     4599                /*/ Why can't we do it easier? */
     4600                /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
     4601                /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
     4602            }
     4603            str += word_len;
     4604        }
     4605        if (!*str)  /* EOL - do not finalize word */
     4606            break;
     4607        o_addchr(output, '\0');
     4608        debug_print_list("expand_on_ifs", output, n);
     4609        n = o_save_ptr(output, n);
     4610        str += strspn(str, G.ifs); /* skip ifs chars */
     4611    }
     4612    debug_print_list("expand_on_ifs[1]", output, n);
     4613    return n;
     4614}
     4615
     4616/* Helper to expand $((...)) and heredoc body. These act as if
     4617 * they are in double quotes, with the exception that they are not :).
     4618 * Just the rules are similar: "expand only $var and `cmd`"
     4619 *
     4620 * Returns malloced string.
     4621 * As an optimization, we return NULL if expansion is not needed.
     4622 */
     4623#if !ENABLE_HUSH_BASH_COMPAT
     4624/* only ${var/pattern/repl} (its pattern part) needs additional mode */
     4625#define encode_then_expand_string(str, process_bkslash, do_unbackslash) \
     4626    encode_then_expand_string(str)
     4627#endif
     4628static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash)
     4629{
     4630    char *exp_str;
     4631    struct in_str input;
     4632    o_string dest = NULL_O_STRING;
     4633
     4634    if (!strchr(str, '$')
     4635     && !strchr(str, '\\')
     4636#if ENABLE_HUSH_TICK
     4637     && !strchr(str, '`')
     4638#endif
     4639    ) {
     4640        return NULL;
     4641    }
     4642
     4643    /* We need to expand. Example:
     4644     * echo $(($a + `echo 1`)) $((1 + $((2)) ))
     4645     */
     4646    setup_string_in_str(&input, str);
     4647    encode_string(NULL, &dest, &input, EOF, process_bkslash);
     4648    //bb_error_msg("'%s' -> '%s'", str, dest.data);
     4649    exp_str = expand_string_to_string(dest.data, /*unbackslash:*/ do_unbackslash);
     4650    //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
     4651    o_free_unsafe(&dest);
     4652    return exp_str;
     4653}
     4654
     4655#if ENABLE_SH_MATH_SUPPORT
     4656static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
     4657{
     4658    arith_state_t math_state;
     4659    arith_t res;
     4660    char *exp_str;
     4661
     4662    math_state.lookupvar = get_local_var_value;
     4663    math_state.setvar = set_local_var_from_halves;
     4664    //math_state.endofname = endofname;
     4665    exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
     4666    res = arith(&math_state, exp_str ? exp_str : arg);
     4667    free(exp_str);
     4668    if (errmsg_p)
     4669        *errmsg_p = math_state.errmsg;
     4670    if (math_state.errmsg)
     4671        die_if_script(math_state.errmsg);
     4672    return res;
     4673}
     4674#endif
     4675
     4676#if ENABLE_HUSH_BASH_COMPAT
     4677/* ${var/[/]pattern[/repl]} helpers */
     4678static char *strstr_pattern(char *val, const char *pattern, int *size)
     4679{
     4680    while (1) {
     4681        char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
     4682        debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
     4683        if (end) {
     4684            *size = end - val;
     4685            return val;
     4686        }
     4687        if (*val == '\0')
     4688            return NULL;
     4689        /* Optimization: if "*pat" did not match the start of "string",
     4690         * we know that "tring", "ring" etc will not match too:
     4691         */
     4692        if (pattern[0] == '*')
     4693            return NULL;
     4694        val++;
     4695    }
     4696}
     4697static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
     4698{
     4699    char *result = NULL;
     4700    unsigned res_len = 0;
     4701    unsigned repl_len = strlen(repl);
     4702
     4703    while (1) {
     4704        int size;
     4705        char *s = strstr_pattern(val, pattern, &size);
     4706        if (!s)
     4707            break;
     4708
     4709        result = xrealloc(result, res_len + (s - val) + repl_len + 1);
     4710        memcpy(result + res_len, val, s - val);
     4711        res_len += s - val;
     4712        strcpy(result + res_len, repl);
     4713        res_len += repl_len;
     4714        debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
     4715
     4716        val = s + size;
     4717        if (exp_op == '/')
     4718            break;
     4719    }
     4720    if (val[0] && result) {
     4721        result = xrealloc(result, res_len + strlen(val) + 1);
     4722        strcpy(result + res_len, val);
     4723        debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
     4724    }
     4725    debug_printf_varexp("result:'%s'\n", result);
     4726    return result;
     4727}
     4728#endif
     4729
     4730/* Helper:
     4731 * Handles <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
     4732 */
     4733static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, char **pp)
     4734{
     4735    const char *val = NULL;
     4736    char *to_be_freed = NULL;
     4737    char *p = *pp;
     4738    char *var;
     4739    char first_char;
     4740    char exp_op;
     4741    char exp_save = exp_save; /* for compiler */
     4742    char *exp_saveptr; /* points to expansion operator */
     4743    char *exp_word = exp_word; /* for compiler */
     4744    char arg0;
     4745
     4746    *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
     4747    var = arg;
     4748    exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
     4749    arg0 = arg[0];
     4750    first_char = arg[0] = arg0 & 0x7f;
     4751    exp_op = 0;
     4752
     4753    if (first_char == '#'      /* ${#... */
     4754     && arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */
     4755    ) {
     4756        /* It must be length operator: ${#var} */
     4757        var++;
     4758        exp_op = 'L';
     4759    } else {
     4760        /* Maybe handle parameter expansion */
     4761        if (exp_saveptr /* if 2nd char is one of expansion operators */
     4762         && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
     4763        ) {
     4764            /* ${?:0}, ${#[:]%0} etc */
     4765            exp_saveptr = var + 1;
     4766        } else {
     4767            /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
     4768            exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
     4769        }
     4770        exp_op = exp_save = *exp_saveptr;
     4771        if (exp_op) {
     4772            exp_word = exp_saveptr + 1;
     4773            if (exp_op == ':') {
     4774                exp_op = *exp_word++;
     4775//TODO: try ${var:} and ${var:bogus} in non-bash config
     4776                if (ENABLE_HUSH_BASH_COMPAT
     4777                 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
     4778                ) {
     4779                    /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
     4780                    exp_op = ':';
     4781                    exp_word--;
     4782                }
     4783            }
     4784            *exp_saveptr = '\0';
     4785        } /* else: it's not an expansion op, but bare ${var} */
     4786    }
     4787
     4788    /* Look up the variable in question */
     4789    if (isdigit(var[0])) {
     4790        /* parse_dollar should have vetted var for us */
     4791        int n = xatoi_positive(var);
     4792        if (n < G.global_argc)
     4793            val = G.global_argv[n];
     4794        /* else val remains NULL: $N with too big N */
     4795    } else {
     4796        switch (var[0]) {
     4797        case '$': /* pid */
     4798            val = utoa(G.root_pid);
     4799            break;
     4800        case '!': /* bg pid */
     4801            val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
     4802            break;
     4803        case '?': /* exitcode */
     4804            val = utoa(G.last_exitcode);
     4805            break;
     4806        case '#': /* argc */
     4807            val = utoa(G.global_argc ? G.global_argc-1 : 0);
     4808            break;
     4809        default:
     4810            val = get_local_var_value(var);
     4811        }
     4812    }
     4813
     4814    /* Handle any expansions */
     4815    if (exp_op == 'L') {
     4816        debug_printf_expand("expand: length(%s)=", val);
     4817        val = utoa(val ? strlen(val) : 0);
     4818        debug_printf_expand("%s\n", val);
     4819    } else if (exp_op) {
     4820        if (exp_op == '%' || exp_op == '#') {
     4821            /* Standard-mandated substring removal ops:
     4822             * ${parameter%word} - remove smallest suffix pattern
     4823             * ${parameter%%word} - remove largest suffix pattern
     4824             * ${parameter#word} - remove smallest prefix pattern
     4825             * ${parameter##word} - remove largest prefix pattern
     4826             *
     4827             * Word is expanded to produce a glob pattern.
     4828             * Then var's value is matched to it and matching part removed.
     4829             */
     4830            if (val && val[0]) {
     4831                char *t;
     4832                char *exp_exp_word;
     4833                char *loc;
     4834                unsigned scan_flags = pick_scan(exp_op, *exp_word);
     4835                if (exp_op == *exp_word)  /* ## or %% */
     4836                    exp_word++;
     4837                exp_exp_word = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
     4838                if (exp_exp_word)
     4839                    exp_word = exp_exp_word;
     4840                /* HACK ALERT. We depend here on the fact that
     4841                 * G.global_argv and results of utoa and get_local_var_value
     4842                 * are actually in writable memory:
     4843                 * scan_and_match momentarily stores NULs there. */
     4844                t = (char*)val;
     4845                loc = scan_and_match(t, exp_word, scan_flags);
     4846                //bb_error_msg("op:%c str:'%s' pat:'%s' res:'%s'",
     4847                //      exp_op, t, exp_word, loc);
     4848                free(exp_exp_word);
     4849                if (loc) { /* match was found */
     4850                    if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
     4851                        val = loc; /* take right part */
     4852                    else /* %[%] */
     4853                        val = to_be_freed = xstrndup(val, loc - val); /* left */
     4854                }
     4855            }
     4856        }
     4857#if ENABLE_HUSH_BASH_COMPAT
     4858        else if (exp_op == '/' || exp_op == '\\') {
     4859            /* It's ${var/[/]pattern[/repl]} thing.
     4860             * Note that in encoded form it has TWO parts:
     4861             * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
     4862             * and if // is used, it is encoded as \:
     4863             * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
     4864             */
     4865            /* Empty variable always gives nothing: */
     4866            // "v=''; echo ${v/*/w}" prints "", not "w"
     4867            if (val && val[0]) {
     4868                /* pattern uses non-standard expansion.
     4869                 * repl should be unbackslashed and globbed
     4870                 * by the usual expansion rules:
     4871                 * >az; >bz;
     4872                 * v='a bz'; echo "${v/a*z/a*z}" prints "a*z"
     4873                 * v='a bz'; echo "${v/a*z/\z}"  prints "\z"
     4874                 * v='a bz'; echo ${v/a*z/a*z}   prints "az"
     4875                 * v='a bz'; echo ${v/a*z/\z}    prints "z"
     4876                 * (note that a*z _pattern_ is never globbed!)
     4877                 */
     4878                char *pattern, *repl, *t;
     4879                pattern = encode_then_expand_string(exp_word, /*process_bkslash:*/ 0, /*unbackslash:*/ 0);
     4880                if (!pattern)
     4881                    pattern = xstrdup(exp_word);
     4882                debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
     4883                *p++ = SPECIAL_VAR_SYMBOL;
     4884                exp_word = p;
     4885                p = strchr(p, SPECIAL_VAR_SYMBOL);
     4886                *p = '\0';
     4887                repl = encode_then_expand_string(exp_word, /*process_bkslash:*/ arg0 & 0x80, /*unbackslash:*/ 1);
     4888                debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
     4889                /* HACK ALERT. We depend here on the fact that
     4890                 * G.global_argv and results of utoa and get_local_var_value
     4891                 * are actually in writable memory:
     4892                 * replace_pattern momentarily stores NULs there. */
     4893                t = (char*)val;
     4894                to_be_freed = replace_pattern(t,
     4895                        pattern,
     4896                        (repl ? repl : exp_word),
     4897                        exp_op);
     4898                if (to_be_freed) /* at least one replace happened */
     4899                    val = to_be_freed;
     4900                free(pattern);
     4901                free(repl);
     4902            }
     4903        }
     4904#endif
     4905        else if (exp_op == ':') {
     4906#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
     4907            /* It's ${var:N[:M]} bashism.
     4908             * Note that in encoded form it has TWO parts:
     4909             * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
     4910             */
     4911            arith_t beg, len;
     4912            const char *errmsg;
     4913
     4914            beg = expand_and_evaluate_arith(exp_word, &errmsg);
     4915            if (errmsg)
     4916                goto arith_err;
     4917            debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
     4918            *p++ = SPECIAL_VAR_SYMBOL;
     4919            exp_word = p;
     4920            p = strchr(p, SPECIAL_VAR_SYMBOL);
     4921            *p = '\0';
     4922            len = expand_and_evaluate_arith(exp_word, &errmsg);
     4923            if (errmsg)
     4924                goto arith_err;
     4925            debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
     4926            if (len >= 0) { /* bash compat: len < 0 is illegal */
     4927                if (beg < 0) /* bash compat */
     4928                    beg = 0;
     4929                debug_printf_varexp("from val:'%s'\n", val);
     4930                if (len == 0 || !val || beg >= strlen(val)) {
     4931 arith_err:
     4932                    val = NULL;
     4933                } else {
     4934                    /* Paranoia. What if user entered 9999999999999
     4935                     * which fits in arith_t but not int? */
     4936                    if (len >= INT_MAX)
     4937                        len = INT_MAX;
     4938                    val = to_be_freed = xstrndup(val + beg, len);
     4939                }
     4940                debug_printf_varexp("val:'%s'\n", val);
     4941            } else
     4942#endif
     4943            {
     4944                die_if_script("malformed ${%s:...}", var);
     4945                val = NULL;
     4946            }
     4947        } else { /* one of "-=+?" */
     4948            /* Standard-mandated substitution ops:
     4949             * ${var?word} - indicate error if unset
     4950             *      If var is unset, word (or a message indicating it is unset
     4951             *      if word is null) is written to standard error
     4952             *      and the shell exits with a non-zero exit status.
     4953             *      Otherwise, the value of var is substituted.
     4954             * ${var-word} - use default value
     4955             *      If var is unset, word is substituted.
     4956             * ${var=word} - assign and use default value
     4957             *      If var is unset, word is assigned to var.
     4958             *      In all cases, final value of var is substituted.
     4959             * ${var+word} - use alternative value
     4960             *      If var is unset, null is substituted.
     4961             *      Otherwise, word is substituted.
     4962             *
     4963             * Word is subjected to tilde expansion, parameter expansion,
     4964             * command substitution, and arithmetic expansion.
     4965             * If word is not needed, it is not expanded.
     4966             *
     4967             * Colon forms (${var:-word}, ${var:=word} etc) do the same,
     4968             * but also treat null var as if it is unset.
     4969             */
     4970            int use_word = (!val || ((exp_save == ':') && !val[0]));
     4971            if (exp_op == '+')
     4972                use_word = !use_word;
     4973            debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
     4974                    (exp_save == ':') ? "true" : "false", use_word);
     4975            if (use_word) {
     4976                to_be_freed = encode_then_expand_string(exp_word, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
     4977                if (to_be_freed)
     4978                    exp_word = to_be_freed;
     4979                if (exp_op == '?') {
     4980                    /* mimic bash message */
     4981                    die_if_script("%s: %s",
     4982                        var,
     4983                        exp_word[0] ? exp_word : "parameter null or not set"
     4984                    );
     4985//TODO: how interactive bash aborts expansion mid-command?
     4986                } else {
     4987                    val = exp_word;
     4988                }
     4989
     4990                if (exp_op == '=') {
     4991                    /* ${var=[word]} or ${var:=[word]} */
     4992                    if (isdigit(var[0]) || var[0] == '#') {
     4993                        /* mimic bash message */
     4994                        die_if_script("$%s: cannot assign in this way", var);
     4995                        val = NULL;
     4996                    } else {
     4997                        char *new_var = xasprintf("%s=%s", var, val);
     4998                        set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
     4999                    }
     5000                }
     5001            }
     5002        } /* one of "-=+?" */
     5003
     5004        *exp_saveptr = exp_save;
     5005    } /* if (exp_op) */
     5006
     5007    arg[0] = arg0;
     5008
     5009    *pp = p;
     5010    *to_be_freed_pp = to_be_freed;
     5011    return val;
     5012}
     5013
     5014/* Expand all variable references in given string, adding words to list[]
     5015 * at n, n+1,... positions. Return updated n (so that list[n] is next one
     5016 * to be filled). This routine is extremely tricky: has to deal with
     5017 * variables/parameters with whitespace, $* and $@, and constructs like
     5018 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
     5019static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
     5020{
     5021    /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
     5022     * expansion of right-hand side of assignment == 1-element expand.
     5023     */
     5024    char cant_be_null = 0; /* only bit 0x80 matters */
     5025    char *p;
     5026
     5027    debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
     5028            !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
     5029    debug_print_list("expand_vars_to_list", output, n);
     5030    n = o_save_ptr(output, n);
     5031    debug_print_list("expand_vars_to_list[0]", output, n);
     5032
     5033    while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
     5034        char first_ch;
     5035        char *to_be_freed = NULL;
     5036        const char *val = NULL;
     5037#if ENABLE_HUSH_TICK
     5038        o_string subst_result = NULL_O_STRING;
     5039#endif
     5040#if ENABLE_SH_MATH_SUPPORT
     5041        char arith_buf[sizeof(arith_t)*3 + 2];
     5042#endif
     5043        o_addblock(output, arg, p - arg);
     5044        debug_print_list("expand_vars_to_list[1]", output, n);
     5045        arg = ++p;
     5046        p = strchr(p, SPECIAL_VAR_SYMBOL);
     5047
     5048        /* Fetch special var name (if it is indeed one of them)
     5049         * and quote bit, force the bit on if singleword expansion -
     5050         * important for not getting v=$@ expand to many words. */
     5051        first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
     5052
     5053        /* Is this variable quoted and thus expansion can't be null?
     5054         * "$@" is special. Even if quoted, it can still
     5055         * expand to nothing (not even an empty string),
     5056         * thus it is excluded. */
     5057        if ((first_ch & 0x7f) != '@')
     5058            cant_be_null |= first_ch;
     5059
     5060        switch (first_ch & 0x7f) {
     5061        /* Highest bit in first_ch indicates that var is double-quoted */
     5062        case '*':
     5063        case '@': {
     5064            int i;
     5065            if (!G.global_argv[1])
     5066                break;
     5067            i = 1;
     5068            cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
     5069            if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
     5070                while (G.global_argv[i]) {
     5071                    n = expand_on_ifs(output, n, G.global_argv[i]);
     5072                    debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
     5073                    if (G.global_argv[i++][0] && G.global_argv[i]) {
     5074                        /* this argv[] is not empty and not last:
     5075                         * put terminating NUL, start new word */
     5076                        o_addchr(output, '\0');
     5077                        debug_print_list("expand_vars_to_list[2]", output, n);
     5078                        n = o_save_ptr(output, n);
     5079                        debug_print_list("expand_vars_to_list[3]", output, n);
     5080                    }
     5081                }
     5082            } else
     5083            /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
     5084             * and in this case should treat it like '$*' - see 'else...' below */
     5085            if (first_ch == ('@'|0x80)  /* quoted $@ */
     5086             && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
     5087            ) {
     5088                while (1) {
     5089                    o_addQstr(output, G.global_argv[i]);
     5090                    if (++i >= G.global_argc)
     5091                        break;
     5092                    o_addchr(output, '\0');
     5093                    debug_print_list("expand_vars_to_list[4]", output, n);
     5094                    n = o_save_ptr(output, n);
     5095                }
     5096            } else { /* quoted $* (or v="$@" case): add as one word */
     5097                while (1) {
     5098                    o_addQstr(output, G.global_argv[i]);
     5099                    if (!G.global_argv[++i])
     5100                        break;
     5101                    if (G.ifs[0])
     5102                        o_addchr(output, G.ifs[0]);
     5103                }
     5104            }
     5105            break;
     5106        }
     5107        case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
     5108            /* "Empty variable", used to make "" etc to not disappear */
     5109            arg++;
     5110            cant_be_null = 0x80;
     5111            break;
     5112#if ENABLE_HUSH_TICK
     5113        case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
     5114            *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
     5115            arg++;
     5116            /* Can't just stuff it into output o_string,
     5117             * expanded result may need to be globbed
     5118             * and $IFS-splitted */
     5119            debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
     5120            G.last_exitcode = process_command_subs(&subst_result, arg);
     5121            debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
     5122            val = subst_result.data;
     5123            goto store_val;
     5124#endif
     5125#if ENABLE_SH_MATH_SUPPORT
     5126        case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
     5127            arith_t res;
     5128
     5129            arg++; /* skip '+' */
     5130            *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
     5131            debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
     5132            res = expand_and_evaluate_arith(arg, NULL);
     5133            debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res);
     5134            sprintf(arith_buf, ARITH_FMT, res);
     5135            val = arith_buf;
     5136            break;
     5137        }
     5138#endif
     5139        default:
     5140            val = expand_one_var(&to_be_freed, arg, &p);
     5141 IF_HUSH_TICK(store_val:)
     5142            if (!(first_ch & 0x80)) { /* unquoted $VAR */
     5143                debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
     5144                        !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
     5145                if (val && val[0]) {
     5146                    n = expand_on_ifs(output, n, val);
     5147                    val = NULL;
     5148                }
     5149            } else { /* quoted $VAR, val will be appended below */
     5150                debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
     5151                        !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
     5152            }
     5153            break;
     5154
     5155        } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
     5156
     5157        if (val && val[0]) {
     5158            o_addQstr(output, val);
     5159        }
     5160        free(to_be_freed);
     5161
     5162        /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
     5163         * Do the check to avoid writing to a const string. */
     5164        if (*p != SPECIAL_VAR_SYMBOL)
     5165            *p = SPECIAL_VAR_SYMBOL;
     5166
     5167#if ENABLE_HUSH_TICK
     5168        o_free(&subst_result);
     5169#endif
     5170        arg = ++p;
     5171    } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
     5172
     5173    if (arg[0]) {
     5174        debug_print_list("expand_vars_to_list[a]", output, n);
     5175        /* this part is literal, and it was already pre-quoted
     5176         * if needed (much earlier), do not use o_addQstr here! */
     5177        o_addstr_with_NUL(output, arg);
     5178        debug_print_list("expand_vars_to_list[b]", output, n);
     5179    } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
     5180     && !(cant_be_null & 0x80) /* and all vars were not quoted. */
     5181    ) {
     5182        n--;
     5183        /* allow to reuse list[n] later without re-growth */
     5184        output->has_empty_slot = 1;
     5185    } else {
     5186        o_addchr(output, '\0');
     5187    }
     5188
     5189    return n;
     5190}
     5191
     5192static char **expand_variables(char **argv, unsigned expflags)
     5193{
     5194    int n;
     5195    char **list;
     5196    o_string output = NULL_O_STRING;
     5197
     5198    output.o_expflags = expflags;
     5199
     5200    n = 0;
     5201    while (*argv) {
     5202        n = expand_vars_to_list(&output, n, *argv);
     5203        argv++;
     5204    }
     5205    debug_print_list("expand_variables", &output, n);
     5206
     5207    /* output.data (malloced in one block) gets returned in "list" */
     5208    list = o_finalize_list(&output, n);
     5209    debug_print_strings("expand_variables[1]", list);
     5210    return list;
     5211}
     5212
     5213static char **expand_strvec_to_strvec(char **argv)
     5214{
     5215    return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
     5216}
     5217
     5218#if ENABLE_HUSH_BASH_COMPAT
     5219static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
     5220{
     5221    return expand_variables(argv, EXP_FLAG_SINGLEWORD);
     5222}
     5223#endif
     5224
     5225/* Used for expansion of right hand of assignments,
     5226 * $((...)), heredocs, variable espansion parts.
     5227 *
     5228 * NB: should NOT do globbing!
     5229 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
     5230 */
     5231static char *expand_string_to_string(const char *str, int do_unbackslash)
     5232{
     5233#if !ENABLE_HUSH_BASH_COMPAT
     5234    const int do_unbackslash = 1;
     5235#endif
     5236    char *argv[2], **list;
     5237
     5238    debug_printf_expand("string_to_string<='%s'\n", str);
     5239    /* This is generally an optimization, but it also
     5240     * handles "", which otherwise trips over !list[0] check below.
     5241     * (is this ever happens that we actually get str="" here?)
     5242     */
     5243    if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
     5244        //TODO: Can use on strings with \ too, just unbackslash() them?
     5245        debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
     5246        return xstrdup(str);
     5247    }
     5248
     5249    argv[0] = (char*)str;
     5250    argv[1] = NULL;
     5251    list = expand_variables(argv, do_unbackslash
     5252            ? EXP_FLAG_ESC_GLOB_CHARS | EXP_FLAG_SINGLEWORD
     5253            : EXP_FLAG_SINGLEWORD
     5254    );
     5255    if (HUSH_DEBUG)
     5256        if (!list[0] || list[1])
     5257            bb_error_msg_and_die("BUG in varexp2");
     5258    /* actually, just move string 2*sizeof(char*) bytes back */
     5259    overlapping_strcpy((char*)list, list[0]);
     5260    if (do_unbackslash)
     5261        unbackslash((char*)list);
     5262    debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
     5263    return (char*)list;
     5264}
     5265
     5266/* Used for "eval" builtin */
     5267static char* expand_strvec_to_string(char **argv)
     5268{
     5269    char **list;
     5270
     5271    list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
     5272    /* Convert all NULs to spaces */
     5273    if (list[0]) {
     5274        int n = 1;
     5275        while (list[n]) {
     5276            if (HUSH_DEBUG)
     5277                if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
     5278                    bb_error_msg_and_die("BUG in varexp3");
     5279            /* bash uses ' ' regardless of $IFS contents */
     5280            list[n][-1] = ' ';
     5281            n++;
     5282        }
     5283    }
     5284    overlapping_strcpy((char*)list, list[0]);
     5285    debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
     5286    return (char*)list;
     5287}
     5288
     5289static char **expand_assignments(char **argv, int count)
     5290{
     5291    int i;
     5292    char **p;
     5293
     5294    G.expanded_assignments = p = NULL;
     5295    /* Expand assignments into one string each */
     5296    for (i = 0; i < count; i++) {
     5297        G.expanded_assignments = p = add_string_to_strings(p, expand_string_to_string(argv[i], /*unbackslash:*/ 1));
     5298    }
     5299    G.expanded_assignments = NULL;
     5300    return p;
     5301}
     5302
     5303
     5304#if BB_MMU
     5305/* never called */
     5306void re_execute_shell(char ***to_free, const char *s,
     5307        char *g_argv0, char **g_argv,
     5308        char **builtin_argv) NORETURN;
     5309
     5310static void reset_traps_to_defaults(void)
     5311{
     5312    /* This function is always called in a child shell
     5313     * after fork (not vfork, NOMMU doesn't use this function).
     5314     */
     5315    unsigned sig;
     5316    unsigned mask;
     5317
     5318    /* Child shells are not interactive.
     5319     * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
     5320     * Testcase: (while :; do :; done) + ^Z should background.
     5321     * Same goes for SIGTERM, SIGHUP, SIGINT.
     5322     */
     5323    if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS))
     5324        return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */
     5325
     5326    /* Switching off SPECIAL_INTERACTIVE_SIGS.
     5327     * Stupid. It can be done with *single* &= op, but we can't use
     5328     * the fact that G.blocked_set is implemented as a bitmask
     5329     * in libc... */
     5330    mask = (SPECIAL_INTERACTIVE_SIGS >> 1);
     5331    sig = 1;
     5332    while (1) {
     5333        if (mask & 1) {
     5334            /* Careful. Only if no trap or trap is not "" */
     5335            if (!G.traps || !G.traps[sig] || G.traps[sig][0])
     5336                sigdelset(&G.blocked_set, sig);
     5337        }
     5338        mask >>= 1;
     5339        if (!mask)
     5340            break;
     5341        sig++;
     5342    }
     5343    /* Our homegrown sig mask is saner to work with :) */
     5344    G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS;
     5345
     5346    /* Resetting all traps to default except empty ones */
     5347    mask = G.non_DFL_mask;
     5348    if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) {
     5349        if (!G.traps[sig] || !G.traps[sig][0])
     5350            continue;
     5351        free(G.traps[sig]);
     5352        G.traps[sig] = NULL;
     5353        /* There is no signal for 0 (EXIT) */
     5354        if (sig == 0)
     5355            continue;
     5356        /* There was a trap handler, we just removed it.
     5357         * But if sig still has non-DFL handling,
     5358         * we should not unblock the sig. */
     5359        if (mask & 1)
     5360            continue;
     5361        sigdelset(&G.blocked_set, sig);
     5362    }
     5363    sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
     5364}
     5365
     5366#else /* !BB_MMU */
     5367
     5368static void re_execute_shell(char ***to_free, const char *s,
     5369        char *g_argv0, char **g_argv,
     5370        char **builtin_argv) NORETURN;
     5371static void re_execute_shell(char ***to_free, const char *s,
     5372        char *g_argv0, char **g_argv,
     5373        char **builtin_argv)
     5374{
     5375# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
     5376    /* delims + 2 * (number of bytes in printed hex numbers) */
     5377    char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
     5378    char *heredoc_argv[4];
     5379    struct variable *cur;
     5380# if ENABLE_HUSH_FUNCTIONS
     5381    struct function *funcp;
     5382# endif
     5383    char **argv, **pp;
     5384    unsigned cnt;
     5385    unsigned long long empty_trap_mask;
     5386
     5387    if (!g_argv0) { /* heredoc */
     5388        argv = heredoc_argv;
     5389        argv[0] = (char *) G.argv0_for_re_execing;
     5390        argv[1] = (char *) "-<";
     5391        argv[2] = (char *) s;
     5392        argv[3] = NULL;
     5393        pp = &argv[3]; /* used as pointer to empty environment */
     5394        goto do_exec;
     5395    }
     5396
     5397    cnt = 0;
     5398    pp = builtin_argv;
     5399    if (pp) while (*pp++)
     5400        cnt++;
     5401
     5402    empty_trap_mask = 0;
     5403    if (G.traps) {
     5404        int sig;
     5405        for (sig = 1; sig < NSIG; sig++) {
     5406            if (G.traps[sig] && !G.traps[sig][0])
     5407                empty_trap_mask |= 1LL << sig;
     5408        }
     5409    }
     5410
     5411    sprintf(param_buf, NOMMU_HACK_FMT
     5412            , (unsigned) G.root_pid
     5413            , (unsigned) G.root_ppid
     5414            , (unsigned) G.last_bg_pid
     5415            , (unsigned) G.last_exitcode
     5416            , cnt
     5417            , empty_trap_mask
     5418            IF_HUSH_LOOPS(, G.depth_of_loop)
     5419            );
     5420# undef NOMMU_HACK_FMT
     5421    /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
     5422     * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
     5423     */
     5424    cnt += 6;
     5425    for (cur = G.top_var; cur; cur = cur->next) {
     5426        if (!cur->flg_export || cur->flg_read_only)
     5427            cnt += 2;
     5428    }
     5429# if ENABLE_HUSH_FUNCTIONS
     5430    for (funcp = G.top_func; funcp; funcp = funcp->next)
     5431        cnt += 3;
     5432# endif
     5433    pp = g_argv;
     5434    while (*pp++)
     5435        cnt++;
     5436    *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
     5437    *pp++ = (char *) G.argv0_for_re_execing;
     5438    *pp++ = param_buf;
     5439    for (cur = G.top_var; cur; cur = cur->next) {
     5440        if (strcmp(cur->varstr, hush_version_str) == 0)
     5441            continue;
     5442        if (cur->flg_read_only) {
     5443            *pp++ = (char *) "-R";
     5444            *pp++ = cur->varstr;
     5445        } else if (!cur->flg_export) {
     5446            *pp++ = (char *) "-V";
     5447            *pp++ = cur->varstr;
     5448        }
     5449    }
     5450# if ENABLE_HUSH_FUNCTIONS
     5451    for (funcp = G.top_func; funcp; funcp = funcp->next) {
     5452        *pp++ = (char *) "-F";
     5453        *pp++ = funcp->name;
     5454        *pp++ = funcp->body_as_string;
     5455    }
     5456# endif
     5457    /* We can pass activated traps here. Say, -Tnn:trap_string
     5458     *
     5459     * However, POSIX says that subshells reset signals with traps
     5460     * to SIG_DFL.
     5461     * I tested bash-3.2 and it not only does that with true subshells
     5462     * of the form ( list ), but with any forked children shells.
     5463     * I set trap "echo W" WINCH; and then tried:
     5464     *
     5465     * { echo 1; sleep 20; echo 2; } &
     5466     * while true; do echo 1; sleep 20; echo 2; break; done &
     5467     * true | { echo 1; sleep 20; echo 2; } | cat
     5468     *
     5469     * In all these cases sending SIGWINCH to the child shell
     5470     * did not run the trap. If I add trap "echo V" WINCH;
     5471     * _inside_ group (just before echo 1), it works.
     5472     *
     5473     * I conclude it means we don't need to pass active traps here.
     5474     * Even if we would use signal handlers instead of signal masking
     5475     * in order to implement trap handling,
     5476     * exec syscall below resets signals to SIG_DFL for us.
     5477     */
     5478    *pp++ = (char *) "-c";
     5479    *pp++ = (char *) s;
     5480    if (builtin_argv) {
     5481        while (*++builtin_argv)
     5482            *pp++ = *builtin_argv;
     5483        *pp++ = (char *) "";
     5484    }
     5485    *pp++ = g_argv0;
     5486    while (*g_argv)
     5487        *pp++ = *g_argv++;
     5488    /* *pp = NULL; - is already there */
     5489    pp = environ;
     5490
     5491 do_exec:
     5492    debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
     5493    sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
     5494    execve(bb_busybox_exec_path, argv, pp);
     5495    /* Fallback. Useful for init=/bin/hush usage etc */
     5496    if (argv[0][0] == '/')
     5497        execve(argv[0], argv, pp);
     5498    xfunc_error_retval = 127;
     5499    bb_error_msg_and_die("can't re-execute the shell");
     5500}
     5501#endif  /* !BB_MMU */
     5502
     5503
     5504static int run_and_free_list(struct pipe *pi);
     5505
     5506/* Executing from string: eval, sh -c '...'
     5507 *          or from file: /etc/profile, . file, sh <script>, sh (intereactive)
     5508 * end_trigger controls how often we stop parsing
     5509 * NUL: parse all, execute, return
     5510 * ';': parse till ';' or newline, execute, repeat till EOF
     5511 */
     5512static void parse_and_run_stream(struct in_str *inp, int end_trigger)
     5513{
     5514    /* Why we need empty flag?
     5515     * An obscure corner case "false; ``; echo $?":
     5516     * empty command in `` should still set $? to 0.
     5517     * But we can't just set $? to 0 at the start,
     5518     * this breaks "false; echo `echo $?`" case.
     5519     */
     5520    bool empty = 1;
     5521    while (1) {
     5522        struct pipe *pipe_list;
     5523
     5524        pipe_list = parse_stream(NULL, inp, end_trigger);
     5525        if (!pipe_list) { /* EOF */
     5526            if (empty)
     5527                G.last_exitcode = 0;
     5528            break;
     5529        }
     5530        debug_print_tree(pipe_list, 0);
     5531        debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
     5532        run_and_free_list(pipe_list);
     5533        empty = 0;
     5534    }
     5535}
     5536
     5537static void parse_and_run_string(const char *s)
     5538{
     5539    struct in_str input;
     5540    setup_string_in_str(&input, s);
     5541    parse_and_run_stream(&input, '\0');
     5542}
     5543
     5544static void parse_and_run_file(FILE *f)
     5545{
     5546    struct in_str input;
     5547    setup_file_in_str(&input, f);
     5548    parse_and_run_stream(&input, ';');
     5549}
     5550
     5551#if ENABLE_HUSH_TICK
     5552static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
     5553{
     5554    pid_t pid;
     5555    int channel[2];
     5556# if !BB_MMU
     5557    char **to_free = NULL;
     5558# endif
     5559
     5560    xpipe(channel);
     5561    pid = BB_MMU ? xfork() : xvfork();
     5562    if (pid == 0) { /* child */
     5563        disable_restore_tty_pgrp_on_exit();
     5564        /* Process substitution is not considered to be usual
     5565         * 'command execution'.
     5566         * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
     5567         */
     5568        bb_signals(0
     5569            + (1 << SIGTSTP)
     5570            + (1 << SIGTTIN)
     5571            + (1 << SIGTTOU)
     5572            , SIG_IGN);
     5573        CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
     5574        close(channel[0]); /* NB: close _first_, then move fd! */
     5575        xmove_fd(channel[1], 1);
     5576        /* Prevent it from trying to handle ctrl-z etc */
     5577        IF_HUSH_JOB(G.run_list_level = 1;)
     5578        /* Awful hack for `trap` or $(trap).
     5579         *
     5580         * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
     5581         * contains an example where "trap" is executed in a subshell:
     5582         *
     5583         * save_traps=$(trap)
     5584         * ...
     5585         * eval "$save_traps"
     5586         *
     5587         * Standard does not say that "trap" in subshell shall print
     5588         * parent shell's traps. It only says that its output
     5589         * must have suitable form, but then, in the above example
     5590         * (which is not supposed to be normative), it implies that.
     5591         *
     5592         * bash (and probably other shell) does implement it
     5593         * (traps are reset to defaults, but "trap" still shows them),
     5594         * but as a result, "trap" logic is hopelessly messed up:
     5595         *
     5596         * # trap
     5597         * trap -- 'echo Ho' SIGWINCH  <--- we have a handler
     5598         * # (trap)        <--- trap is in subshell - no output (correct, traps are reset)
     5599         * # true | trap   <--- trap is in subshell - no output (ditto)
     5600         * # echo `true | trap`    <--- in subshell - output (but traps are reset!)
     5601         * trap -- 'echo Ho' SIGWINCH
     5602         * # echo `(trap)`         <--- in subshell in subshell - output
     5603         * trap -- 'echo Ho' SIGWINCH
     5604         * # echo `true | (trap)`  <--- in subshell in subshell in subshell - output!
     5605         * trap -- 'echo Ho' SIGWINCH
     5606         *
     5607         * The rules when to forget and when to not forget traps
     5608         * get really complex and nonsensical.
     5609         *
     5610         * Our solution: ONLY bare $(trap) or `trap` is special.
     5611         */
     5612        s = skip_whitespace(s);
     5613        if (strncmp(s, "trap", 4) == 0
     5614         && skip_whitespace(s + 4)[0] == '\0'
     5615        ) {
     5616            static const char *const argv[] = { NULL, NULL };
     5617            builtin_trap((char**)argv);
     5618            exit(0); /* not _exit() - we need to fflush */
     5619        }
     5620# if BB_MMU
     5621        reset_traps_to_defaults();
     5622        parse_and_run_string(s);
     5623        _exit(G.last_exitcode);
     5624# else
     5625    /* We re-execute after vfork on NOMMU. This makes this script safe:
     5626     * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
     5627     * huge=`cat BIG` # was blocking here forever
     5628     * echo OK
     5629     */
     5630        re_execute_shell(&to_free,
     5631                s,
     5632                G.global_argv[0],
     5633                G.global_argv + 1,
     5634                NULL);
     5635# endif
     5636    }
     5637
     5638    /* parent */
     5639    *pid_p = pid;
     5640# if ENABLE_HUSH_FAST
     5641    G.count_SIGCHLD++;
     5642//bb_error_msg("[%d] fork in generate_stream_from_string:"
     5643//      " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
     5644//      getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
     5645# endif
     5646    enable_restore_tty_pgrp_on_exit();
     5647# if !BB_MMU
     5648    free(to_free);
     5649# endif
     5650    close(channel[1]);
     5651    close_on_exec_on(channel[0]);
     5652    return xfdopen_for_read(channel[0]);
     5653}
     5654
     5655/* Return code is exit status of the process that is run. */
     5656static int process_command_subs(o_string *dest, const char *s)
     5657{
     5658    FILE *fp;
     5659    struct in_str pipe_str;
     5660    pid_t pid;
     5661    int status, ch, eol_cnt;
     5662
     5663    fp = generate_stream_from_string(s, &pid);
     5664
     5665    /* Now send results of command back into original context */
     5666    setup_file_in_str(&pipe_str, fp);
     5667    eol_cnt = 0;
     5668    while ((ch = i_getch(&pipe_str)) != EOF) {
     5669        if (ch == '\n') {
     5670            eol_cnt++;
     5671            continue;
     5672        }
     5673        while (eol_cnt) {
     5674            o_addchr(dest, '\n');
     5675            eol_cnt--;
     5676        }
     5677        o_addQchr(dest, ch);
     5678    }
     5679
     5680    debug_printf("done reading from `cmd` pipe, closing it\n");
     5681    fclose(fp);
     5682    /* We need to extract exitcode. Test case
     5683     * "true; echo `sleep 1; false` $?"
     5684     * should print 1 */
     5685    safe_waitpid(pid, &status, 0);
     5686    debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
     5687    return WEXITSTATUS(status);
     5688}
     5689#endif /* ENABLE_HUSH_TICK */
     5690
     5691
     5692static void setup_heredoc(struct redir_struct *redir)
     5693{
     5694    struct fd_pair pair;
     5695    pid_t pid;
     5696    int len, written;
     5697    /* the _body_ of heredoc (misleading field name) */
     5698    const char *heredoc = redir->rd_filename;
     5699    char *expanded;
     5700#if !BB_MMU
     5701    char **to_free;
     5702#endif
     5703
     5704    expanded = NULL;
     5705    if (!(redir->rd_dup & HEREDOC_QUOTED)) {
     5706        expanded = encode_then_expand_string(heredoc, /*process_bkslash:*/ 1, /*unbackslash:*/ 1);
     5707        if (expanded)
     5708            heredoc = expanded;
     5709    }
     5710    len = strlen(heredoc);
     5711
     5712    close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
     5713    xpiped_pair(pair);
     5714    xmove_fd(pair.rd, redir->rd_fd);
     5715
     5716    /* Try writing without forking. Newer kernels have
     5717     * dynamically growing pipes. Must use non-blocking write! */
     5718    ndelay_on(pair.wr);
     5719    while (1) {
     5720        written = write(pair.wr, heredoc, len);
     5721        if (written <= 0)
     5722            break;
     5723        len -= written;
     5724        if (len == 0) {
     5725            close(pair.wr);
     5726            free(expanded);
     5727            return;
     5728        }
     5729        heredoc += written;
     5730    }
     5731    ndelay_off(pair.wr);
     5732
     5733    /* Okay, pipe buffer was not big enough */
     5734    /* Note: we must not create a stray child (bastard? :)
     5735     * for the unsuspecting parent process. Child creates a grandchild
     5736     * and exits before parent execs the process which consumes heredoc
     5737     * (that exec happens after we return from this function) */
     5738#if !BB_MMU
     5739    to_free = NULL;
     5740#endif
     5741    pid = xvfork();
     5742    if (pid == 0) {
     5743        /* child */
     5744        disable_restore_tty_pgrp_on_exit();
     5745        pid = BB_MMU ? xfork() : xvfork();
     5746        if (pid != 0)
     5747            _exit(0);
     5748        /* grandchild */
     5749        close(redir->rd_fd); /* read side of the pipe */
     5750#if BB_MMU
     5751        full_write(pair.wr, heredoc, len); /* may loop or block */
     5752        _exit(0);
     5753#else
     5754        /* Delegate blocking writes to another process */
     5755        xmove_fd(pair.wr, STDOUT_FILENO);
     5756        re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
     5757#endif
     5758    }
     5759    /* parent */
     5760#if ENABLE_HUSH_FAST
     5761    G.count_SIGCHLD++;
     5762//bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
     5763#endif
     5764    enable_restore_tty_pgrp_on_exit();
     5765#if !BB_MMU
     5766    free(to_free);
     5767#endif
     5768    close(pair.wr);
     5769    free(expanded);
     5770    wait(NULL); /* wait till child has died */
    12775771}
    12785772
    12795773/* squirrel != NULL means we squirrel away copies of stdin, stdout,
    12805774 * and stderr if they are redirected. */
    1281 static int setup_redirects(struct child_prog *prog, int squirrel[])
     5775static int setup_redirects(struct command *prog, int squirrel[])
    12825776{
    12835777    int openfd, mode;
     
    12855779
    12865780    for (redir = prog->redirects; redir; redir = redir->next) {
    1287         if (redir->dup == -1 && redir->word.gl_pathv == NULL) {
    1288             /* something went wrong in the parse.  Pretend it didn't happen */
     5781        if (redir->rd_type == REDIRECT_HEREDOC2) {
     5782            /* rd_fd<<HERE case */
     5783            if (squirrel && redir->rd_fd < 3
     5784             && squirrel[redir->rd_fd] < 0
     5785            ) {
     5786                squirrel[redir->rd_fd] = dup(redir->rd_fd);
     5787            }
     5788            /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
     5789             * of the heredoc */
     5790            debug_printf_parse("set heredoc '%s'\n",
     5791                    redir->rd_filename);
     5792            setup_heredoc(redir);
    12895793            continue;
    12905794        }
    1291         if (redir->dup == -1) {
    1292             mode = redir_table[redir->type].mode;
    1293             openfd = open_or_warn(redir->word.gl_pathv[0], mode);
     5795
     5796        if (redir->rd_dup == REDIRFD_TO_FILE) {
     5797            /* rd_fd<*>file case (<*> is <,>,>>,<>) */
     5798            char *p;
     5799            if (redir->rd_filename == NULL) {
     5800                /* Something went wrong in the parse.
     5801                 * Pretend it didn't happen */
     5802                bb_error_msg("bug in redirect parse");
     5803                continue;
     5804            }
     5805            mode = redir_table[redir->rd_type].mode;
     5806            p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1);
     5807            openfd = open_or_warn(p, mode);
     5808            free(p);
    12945809            if (openfd < 0) {
    12955810            /* this could get lost if stderr has been redirected, but
    1296                bash and ash both lose it as well (though zsh doesn't!) */
     5811             * bash and ash both lose it as well (though zsh doesn't!) */
     5812//what the above comment tries to say?
    12975813                return 1;
    12985814            }
    12995815        } else {
    1300             openfd = redir->dup;
    1301         }
    1302 
    1303         if (openfd != redir->fd) {
    1304             if (squirrel && redir->fd < 3) {
    1305                 squirrel[redir->fd] = dup(redir->fd);
    1306             }
    1307             if (openfd == -3) {
    1308                 close(openfd);
     5816            /* rd_fd<*>rd_dup or rd_fd<*>- cases */
     5817            openfd = redir->rd_dup;
     5818        }
     5819
     5820        if (openfd != redir->rd_fd) {
     5821            if (squirrel && redir->rd_fd < 3
     5822             && squirrel[redir->rd_fd] < 0
     5823            ) {
     5824                squirrel[redir->rd_fd] = dup(redir->rd_fd);
     5825            }
     5826            if (openfd == REDIRFD_CLOSE) {
     5827                /* "n>-" means "close me" */
     5828                close(redir->rd_fd);
    13095829            } else {
    1310                 dup2(openfd, redir->fd);
    1311                 if (redir->dup == -1)
     5830                xdup2(openfd, redir->rd_fd);
     5831                if (redir->rd_dup == REDIRFD_TO_FILE)
    13125832                    close(openfd);
    13135833            }
     
    13295849}
    13305850
    1331 /* never returns */
    1332 /* XXX no exit() here.  If you don't exec, use _exit instead.
     5851static char *find_in_path(const char *arg)
     5852{
     5853    char *ret = NULL;
     5854    const char *PATH = get_local_var_value("PATH");
     5855
     5856    if (!PATH)
     5857        return NULL;
     5858
     5859    while (1) {
     5860        const char *end = strchrnul(PATH, ':');
     5861        int sz = end - PATH; /* must be int! */
     5862
     5863        free(ret);
     5864        if (sz != 0) {
     5865            ret = xasprintf("%.*s/%s", sz, PATH, arg);
     5866        } else {
     5867            /* We have xxx::yyyy in $PATH,
     5868             * it means "use current dir" */
     5869            ret = xstrdup(arg);
     5870        }
     5871        if (access(ret, F_OK) == 0)
     5872            break;
     5873
     5874        if (*end == '\0') {
     5875            free(ret);
     5876            return NULL;
     5877        }
     5878        PATH = end + 1;
     5879    }
     5880
     5881    return ret;
     5882}
     5883
     5884static const struct built_in_command *find_builtin_helper(const char *name,
     5885        const struct built_in_command *x,
     5886        const struct built_in_command *end)
     5887{
     5888    while (x != end) {
     5889        if (strcmp(name, x->b_cmd) != 0) {
     5890            x++;
     5891            continue;
     5892        }
     5893        debug_printf_exec("found builtin '%s'\n", name);
     5894        return x;
     5895    }
     5896    return NULL;
     5897}
     5898static const struct built_in_command *find_builtin1(const char *name)
     5899{
     5900    return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
     5901}
     5902static const struct built_in_command *find_builtin(const char *name)
     5903{
     5904    const struct built_in_command *x = find_builtin1(name);
     5905    if (x)
     5906        return x;
     5907    return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
     5908}
     5909
     5910#if ENABLE_HUSH_FUNCTIONS
     5911static struct function **find_function_slot(const char *name)
     5912{
     5913    struct function **funcpp = &G.top_func;
     5914    while (*funcpp) {
     5915        if (strcmp(name, (*funcpp)->name) == 0) {
     5916            break;
     5917        }
     5918        funcpp = &(*funcpp)->next;
     5919    }
     5920    return funcpp;
     5921}
     5922
     5923static const struct function *find_function(const char *name)
     5924{
     5925    const struct function *funcp = *find_function_slot(name);
     5926    if (funcp)
     5927        debug_printf_exec("found function '%s'\n", name);
     5928    return funcp;
     5929}
     5930
     5931/* Note: takes ownership on name ptr */
     5932static struct function *new_function(char *name)
     5933{
     5934    struct function **funcpp = find_function_slot(name);
     5935    struct function *funcp = *funcpp;
     5936
     5937    if (funcp != NULL) {
     5938        struct command *cmd = funcp->parent_cmd;
     5939        debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
     5940        if (!cmd) {
     5941            debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
     5942            free(funcp->name);
     5943            /* Note: if !funcp->body, do not free body_as_string!
     5944             * This is a special case of "-F name body" function:
     5945             * body_as_string was not malloced! */
     5946            if (funcp->body) {
     5947                free_pipe_list(funcp->body);
     5948# if !BB_MMU
     5949                free(funcp->body_as_string);
     5950# endif
     5951            }
     5952        } else {
     5953            debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
     5954            cmd->argv[0] = funcp->name;
     5955            cmd->group = funcp->body;
     5956# if !BB_MMU
     5957            cmd->group_as_string = funcp->body_as_string;
     5958# endif
     5959        }
     5960    } else {
     5961        debug_printf_exec("remembering new function '%s'\n", name);
     5962        funcp = *funcpp = xzalloc(sizeof(*funcp));
     5963        /*funcp->next = NULL;*/
     5964    }
     5965
     5966    funcp->name = name;
     5967    return funcp;
     5968}
     5969
     5970static void unset_func(const char *name)
     5971{
     5972    struct function **funcpp = find_function_slot(name);
     5973    struct function *funcp = *funcpp;
     5974
     5975    if (funcp != NULL) {
     5976        debug_printf_exec("freeing function '%s'\n", funcp->name);
     5977        *funcpp = funcp->next;
     5978        /* funcp is unlinked now, deleting it.
     5979         * Note: if !funcp->body, the function was created by
     5980         * "-F name body", do not free ->body_as_string
     5981         * and ->name as they were not malloced. */
     5982        if (funcp->body) {
     5983            free_pipe_list(funcp->body);
     5984            free(funcp->name);
     5985# if !BB_MMU
     5986            free(funcp->body_as_string);
     5987# endif
     5988        }
     5989        free(funcp);
     5990    }
     5991}
     5992
     5993# if BB_MMU
     5994#define exec_function(to_free, funcp, argv) \
     5995    exec_function(funcp, argv)
     5996# endif
     5997static void exec_function(char ***to_free,
     5998        const struct function *funcp,
     5999        char **argv) NORETURN;
     6000static void exec_function(char ***to_free,
     6001        const struct function *funcp,
     6002        char **argv)
     6003{
     6004# if BB_MMU
     6005    int n = 1;
     6006
     6007    argv[0] = G.global_argv[0];
     6008    G.global_argv = argv;
     6009    while (*++argv)
     6010        n++;
     6011    G.global_argc = n;
     6012    /* On MMU, funcp->body is always non-NULL */
     6013    n = run_list(funcp->body);
     6014    fflush_all();
     6015    _exit(n);
     6016# else
     6017    re_execute_shell(to_free,
     6018            funcp->body_as_string,
     6019            G.global_argv[0],
     6020            argv + 1,
     6021            NULL);
     6022# endif
     6023}
     6024
     6025static int run_function(const struct function *funcp, char **argv)
     6026{
     6027    int rc;
     6028    save_arg_t sv;
     6029    smallint sv_flg;
     6030
     6031    save_and_replace_G_args(&sv, argv);
     6032
     6033    /* "we are in function, ok to use return" */
     6034    sv_flg = G.flag_return_in_progress;
     6035    G.flag_return_in_progress = -1;
     6036# if ENABLE_HUSH_LOCAL
     6037    G.func_nest_level++;
     6038# endif
     6039
     6040    /* On MMU, funcp->body is always non-NULL */
     6041# if !BB_MMU
     6042    if (!funcp->body) {
     6043        /* Function defined by -F */
     6044        parse_and_run_string(funcp->body_as_string);
     6045        rc = G.last_exitcode;
     6046    } else
     6047# endif
     6048    {
     6049        rc = run_list(funcp->body);
     6050    }
     6051
     6052# if ENABLE_HUSH_LOCAL
     6053    {
     6054        struct variable *var;
     6055        struct variable **var_pp;
     6056
     6057        var_pp = &G.top_var;
     6058        while ((var = *var_pp) != NULL) {
     6059            if (var->func_nest_level < G.func_nest_level) {
     6060                var_pp = &var->next;
     6061                continue;
     6062            }
     6063            /* Unexport */
     6064            if (var->flg_export)
     6065                bb_unsetenv(var->varstr);
     6066            /* Remove from global list */
     6067            *var_pp = var->next;
     6068            /* Free */
     6069            if (!var->max_len)
     6070                free(var->varstr);
     6071            free(var);
     6072        }
     6073        G.func_nest_level--;
     6074    }
     6075# endif
     6076    G.flag_return_in_progress = sv_flg;
     6077
     6078    restore_G_args(&sv, argv);
     6079
     6080    return rc;
     6081}
     6082#endif /* ENABLE_HUSH_FUNCTIONS */
     6083
     6084
     6085#if BB_MMU
     6086#define exec_builtin(to_free, x, argv) \
     6087    exec_builtin(x, argv)
     6088#else
     6089#define exec_builtin(to_free, x, argv) \
     6090    exec_builtin(to_free, argv)
     6091#endif
     6092static void exec_builtin(char ***to_free,
     6093        const struct built_in_command *x,
     6094        char **argv) NORETURN;
     6095static void exec_builtin(char ***to_free,
     6096        const struct built_in_command *x,
     6097        char **argv)
     6098{
     6099#if BB_MMU
     6100    int rcode = x->b_function(argv);
     6101    fflush_all();
     6102    _exit(rcode);
     6103#else
     6104    /* On NOMMU, we must never block!
     6105     * Example: { sleep 99 | read line; } & echo Ok
     6106     */
     6107    re_execute_shell(to_free,
     6108            argv[0],
     6109            G.global_argv[0],
     6110            G.global_argv + 1,
     6111            argv);
     6112#endif
     6113}
     6114
     6115
     6116static void execvp_or_die(char **argv) NORETURN;
     6117static void execvp_or_die(char **argv)
     6118{
     6119    debug_printf_exec("execing '%s'\n", argv[0]);
     6120    sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
     6121    execvp(argv[0], argv);
     6122    bb_perror_msg("can't execute '%s'", argv[0]);
     6123    _exit(127); /* bash compat */
     6124}
     6125
     6126#if ENABLE_HUSH_MODE_X
     6127static void dump_cmd_in_x_mode(char **argv)
     6128{
     6129    if (G_x_mode && argv) {
     6130        /* We want to output the line in one write op */
     6131        char *buf, *p;
     6132        int len;
     6133        int n;
     6134
     6135        len = 3;
     6136        n = 0;
     6137        while (argv[n])
     6138            len += strlen(argv[n++]) + 1;
     6139        buf = xmalloc(len);
     6140        buf[0] = '+';
     6141        p = buf + 1;
     6142        n = 0;
     6143        while (argv[n])
     6144            p += sprintf(p, " %s", argv[n++]);
     6145        *p++ = '\n';
     6146        *p = '\0';
     6147        fputs(buf, stderr);
     6148        free(buf);
     6149    }
     6150}
     6151#else
     6152# define dump_cmd_in_x_mode(argv) ((void)0)
     6153#endif
     6154
     6155#if BB_MMU
     6156#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
     6157    pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
     6158#define pseudo_exec(nommu_save, command, argv_expanded) \
     6159    pseudo_exec(command, argv_expanded)
     6160#endif
     6161
     6162/* Called after [v]fork() in run_pipe, or from builtin_exec.
     6163 * Never returns.
     6164 * Don't exit() here.  If you don't exec, use _exit instead.
    13336165 * The at_exit handlers apparently confuse the calling process,
    13346166 * in particular stdin handling.  Not sure why? -- because of vfork! (vda) */
    1335 static void pseudo_exec_argv(char **argv)
    1336 {
    1337     int i, rcode;
    1338     char *p;
    1339     const struct built_in_command *x;
    1340 
    1341     for (i = 0; is_assignment(argv[i]); i++) {
    1342         debug_printf_exec("pid %d environment modification: %s\n",
    1343                 getpid(), argv[i]);
    1344 // FIXME: vfork case??
    1345         p = expand_string_to_string(argv[i]);
    1346         putenv(p);
    1347     }
    1348     argv += i;
    1349     /* If a variable is assigned in a forest, and nobody listens,
    1350      * was it ever really set?
    1351     */
    1352     if (argv[0] == NULL) {
     6167static void pseudo_exec_argv(nommu_save_t *nommu_save,
     6168        char **argv, int assignment_cnt,
     6169        char **argv_expanded) NORETURN;
     6170static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
     6171        char **argv, int assignment_cnt,
     6172        char **argv_expanded)
     6173{
     6174    char **new_env;
     6175
     6176    new_env = expand_assignments(argv, assignment_cnt);
     6177    dump_cmd_in_x_mode(new_env);
     6178
     6179    if (!argv[assignment_cnt]) {
     6180        /* Case when we are here: ... | var=val | ...
     6181         * (note that we do not exit early, i.e., do not optimize out
     6182         * expand_assignments(): think about ... | var=`sleep 1` | ...
     6183        */
     6184        free_strings(new_env);
    13536185        _exit(EXIT_SUCCESS);
    13546186    }
    13556187
    1356     argv = expand_strvec_to_strvec(argv);
    1357 
    1358     /*
    1359      * Check if the command matches any of the builtins.
     6188#if BB_MMU
     6189    set_vars_and_save_old(new_env);
     6190    free(new_env); /* optional */
     6191    /* we can also destroy set_vars_and_save_old's return value,
     6192     * to save memory */
     6193#else
     6194    nommu_save->new_env = new_env;
     6195    nommu_save->old_vars = set_vars_and_save_old(new_env);
     6196#endif
     6197
     6198    if (argv_expanded) {
     6199        argv = argv_expanded;
     6200    } else {
     6201        argv = expand_strvec_to_strvec(argv + assignment_cnt);
     6202#if !BB_MMU
     6203        nommu_save->argv = argv;
     6204#endif
     6205    }
     6206    dump_cmd_in_x_mode(argv);
     6207
     6208#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
     6209    if (strchr(argv[0], '/') != NULL)
     6210        goto skip;
     6211#endif
     6212
     6213    /* Check if the command matches any of the builtins.
    13606214     * Depending on context, this might be redundant.  But it's
    13616215     * easier to waste a few CPU cycles than it is to figure out
    13626216     * if this is one of those cases.
    13636217     */
    1364     for (x = bltins; x->cmd; x++) {
    1365         if (strcmp(argv[0], x->cmd) == 0) {
    1366             debug_printf_exec("running builtin '%s'\n", argv[0]);
    1367             rcode = x->function(argv);
    1368             fflush(stdout);
    1369             _exit(rcode);
    1370         }
    1371     }
    1372 
     6218    {
     6219        /* On NOMMU, it is more expensive to re-execute shell
     6220         * just in order to run echo or test builtin.
     6221         * It's better to skip it here and run corresponding
     6222         * non-builtin later. */
     6223        const struct built_in_command *x;
     6224        x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
     6225        if (x) {
     6226            exec_builtin(&nommu_save->argv_from_re_execing, x, argv);
     6227        }
     6228    }
     6229#if ENABLE_HUSH_FUNCTIONS
     6230    /* Check if the command matches any functions */
     6231    {
     6232        const struct function *funcp = find_function(argv[0]);
     6233        if (funcp) {
     6234            exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
     6235        }
     6236    }
     6237#endif
     6238
     6239#if ENABLE_FEATURE_SH_STANDALONE
    13736240    /* Check if the command matches any busybox applets */
    1374 #if ENABLE_FEATURE_SH_STANDALONE
    1375     if (strchr(argv[0], '/') == NULL) {
    1376         const struct bb_applet *a = find_applet_by_name(argv[0]);
    1377         if (a) {
    1378             if (a->noexec) {
    1379                 current_applet = a;
     6241    {
     6242        int a = find_applet_by_name(argv[0]);
     6243        if (a >= 0) {
     6244# if BB_MMU /* see above why on NOMMU it is not allowed */
     6245            if (APPLET_IS_NOEXEC(a)) {
    13806246                debug_printf_exec("running applet '%s'\n", argv[0]);
    1381 // is it ok that run_current_applet_and_exit() does exit(), not _exit()?
    1382                 run_current_applet_and_exit(argv);
    1383             }
    1384             /* re-exec ourselves with the new arguments */
     6247                run_applet_no_and_exit(a, argv);
     6248            }
     6249# endif
     6250            /* Re-exec ourselves */
    13856251            debug_printf_exec("re-execing applet '%s'\n", argv[0]);
    1386             execvp(bb_busybox_exec_path, argv);
     6252            sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
     6253            execv(bb_busybox_exec_path, argv);
    13876254            /* If they called chroot or otherwise made the binary no longer
    13886255             * executable, fall through */
     
    13916258#endif
    13926259
    1393     debug_printf_exec("execing '%s'\n", argv[0]);
    1394     execvp(argv[0], argv);
    1395     bb_perror_msg("cannot exec '%s'", argv[0]);
    1396     _exit(1);
    1397 }
    1398 
    1399 static void pseudo_exec(struct child_prog *child)
    1400 {
    1401 // FIXME: buggy wrt NOMMU! Must not modify any global data
    1402 // until it does exec/_exit, but currently it does.
    1403     int rcode;
    1404 
    1405     if (child->argv) {
    1406         pseudo_exec_argv(child->argv);
    1407     }
    1408 
    1409     if (child->group) {
    1410     // FIXME: do not modify globals! Think vfork!
    1411 #if ENABLE_HUSH_INTERACTIVE
    1412         debug_printf_exec("pseudo_exec: setting interactive_fd=0\n");
    1413         interactive_fd = 0;    /* crucial!!!! */
    1414 #endif
    1415         debug_printf_exec("pseudo_exec: run_list_real\n");
    1416         rcode = run_list_real(child->group);
     6260#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
     6261 skip:
     6262#endif
     6263    execvp_or_die(argv);
     6264}
     6265
     6266/* Called after [v]fork() in run_pipe
     6267 */
     6268static void pseudo_exec(nommu_save_t *nommu_save,
     6269        struct command *command,
     6270        char **argv_expanded) NORETURN;
     6271static void pseudo_exec(nommu_save_t *nommu_save,
     6272        struct command *command,
     6273        char **argv_expanded)
     6274{
     6275    if (command->argv) {
     6276        pseudo_exec_argv(nommu_save, command->argv,
     6277                command->assignment_cnt, argv_expanded);
     6278    }
     6279
     6280    if (command->group) {
     6281        /* Cases when we are here:
     6282         * ( list )
     6283         * { list } &
     6284         * ... | ( list ) | ...
     6285         * ... | { list } | ...
     6286         */
     6287#if BB_MMU
     6288        int rcode;
     6289        debug_printf_exec("pseudo_exec: run_list\n");
     6290        reset_traps_to_defaults();
     6291        rcode = run_list(command->group);
    14176292        /* OK to leak memory by not calling free_pipe_list,
    14186293         * since this process is about to exit */
    14196294        _exit(rcode);
    1420     }
    1421 
    1422     /* Can happen.  See what bash does with ">foo" by itself. */
    1423     debug_printf("trying to pseudo_exec null command\n");
     6295#else
     6296        re_execute_shell(&nommu_save->argv_from_re_execing,
     6297                command->group_as_string,
     6298                G.global_argv[0],
     6299                G.global_argv + 1,
     6300                NULL);
     6301#endif
     6302    }
     6303
     6304    /* Case when we are here: ... | >file */
     6305    debug_printf_exec("pseudo_exec'ed null command\n");
    14246306    _exit(EXIT_SUCCESS);
    14256307}
     
    14376319    if (pi->cmdtext)
    14386320        return pi->cmdtext;
    1439     argv = pi->progs[0].argv;
    1440     if (!argv || !argv[0])
    1441         return (pi->cmdtext = xzalloc(1));
     6321    argv = pi->cmds[0].argv;
     6322    if (!argv || !argv[0]) {
     6323        pi->cmdtext = xzalloc(1);
     6324        return pi->cmdtext;
     6325    }
    14426326
    14436327    len = 0;
    1444     do len += strlen(*argv) + 1; while (*++argv);
    1445     pi->cmdtext = p = xmalloc(len);
    1446     argv = pi->progs[0].argv;
     6328    do {
     6329        len += strlen(*argv) + 1;
     6330    } while (*++argv);
     6331    p = xmalloc(len);
     6332    pi->cmdtext = p;
     6333    argv = pi->cmds[0].argv;
    14476334    do {
    14486335        len = strlen(*argv);
     
    14576344static void insert_bg_job(struct pipe *pi)
    14586345{
    1459     struct pipe *thejob;
     6346    struct pipe *job, **jobp;
    14606347    int i;
    14616348
    14626349    /* Linear search for the ID of the job to use */
    14636350    pi->jobid = 1;
    1464     for (thejob = job_list; thejob; thejob = thejob->next)
    1465         if (thejob->jobid >= pi->jobid)
    1466             pi->jobid = thejob->jobid + 1;
    1467 
    1468     /* Add thejob to the list of running jobs */
    1469     if (!job_list) {
    1470         thejob = job_list = xmalloc(sizeof(*thejob));
     6351    for (job = G.job_list; job; job = job->next)
     6352        if (job->jobid >= pi->jobid)
     6353            pi->jobid = job->jobid + 1;
     6354
     6355    /* Add job to the list of running jobs */
     6356    jobp = &G.job_list;
     6357    while ((job = *jobp) != NULL)
     6358        jobp = &job->next;
     6359    job = *jobp = xmalloc(sizeof(*job));
     6360
     6361    *job = *pi; /* physical copy */
     6362    job->next = NULL;
     6363    job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
     6364    /* Cannot copy entire pi->cmds[] vector! This causes double frees */
     6365    for (i = 0; i < pi->num_cmds; i++) {
     6366        job->cmds[i].pid = pi->cmds[i].pid;
     6367        /* all other fields are not used and stay zero */
     6368    }
     6369    job->cmdtext = xstrdup(get_cmdtext(pi));
     6370
     6371    if (G_interactive_fd)
     6372        printf("[%d] %d %s\n", job->jobid, job->cmds[0].pid, job->cmdtext);
     6373    G.last_jobid = job->jobid;
     6374}
     6375
     6376static void remove_bg_job(struct pipe *pi)
     6377{
     6378    struct pipe *prev_pipe;
     6379
     6380    if (pi == G.job_list) {
     6381        G.job_list = pi->next;
    14716382    } else {
    1472         for (thejob = job_list; thejob->next; thejob = thejob->next)
    1473             continue;
    1474         thejob->next = xmalloc(sizeof(*thejob));
    1475         thejob = thejob->next;
    1476     }
    1477 
    1478     /* Physically copy the struct job */
    1479     memcpy(thejob, pi, sizeof(struct pipe));
    1480     thejob->progs = xzalloc(sizeof(pi->progs[0]) * pi->num_progs);
    1481     /* We cannot copy entire pi->progs[] vector! Double free()s will happen */
    1482     for (i = 0; i < pi->num_progs; i++) {
    1483 // TODO: do we really need to have so many fields which are just dead weight
    1484 // at execution stage?
    1485         thejob->progs[i].pid = pi->progs[i].pid;
    1486         /* all other fields are not used and stay zero */
    1487     }
    1488     thejob->next = NULL;
    1489     thejob->cmdtext = xstrdup(get_cmdtext(pi));
    1490 
    1491     /* We don't wait for background thejobs to return -- append it
    1492        to the list of backgrounded thejobs and leave it alone */
    1493     printf("[%d] %d %s\n", thejob->jobid, thejob->progs[0].pid, thejob->cmdtext);
    1494     last_bg_pid = thejob->progs[0].pid;
    1495     last_jobid = thejob->jobid;
    1496 }
    1497 
    1498 static void remove_bg_job(struct pipe *pi)
    1499 {
    1500     struct pipe *prev_pipe;
    1501 
    1502     if (pi == job_list) {
    1503         job_list = pi->next;
    1504     } else {
    1505         prev_pipe = job_list;
     6383        prev_pipe = G.job_list;
    15066384        while (prev_pipe->next != pi)
    15076385            prev_pipe = prev_pipe->next;
    15086386        prev_pipe->next = pi->next;
    15096387    }
    1510     if (job_list)
    1511         last_jobid = job_list->jobid;
     6388    if (G.job_list)
     6389        G.last_jobid = G.job_list->jobid;
    15126390    else
    1513         last_jobid = 0;
    1514 }
    1515 
    1516 /* remove a backgrounded job */
     6391        G.last_jobid = 0;
     6392}
     6393
     6394/* Remove a backgrounded job */
    15176395static void delete_finished_bg_job(struct pipe *pi)
    15186396{
    15196397    remove_bg_job(pi);
    1520     pi->stopped_progs = 0;
    1521     free_pipe(pi, 0);
    1522     free(pi);
     6398    free_pipe(pi);
    15236399}
    15246400#endif /* JOB */
    15256401
    1526 /* Checks to see if any processes have exited -- if they
    1527    have, figure out why and see if a job has completed */
    1528 static int checkjobs(struct pipe* fg_pipe)
     6402/* Check to see if any processes have exited -- if they
     6403 * have, figure out why and see if a job has completed */
     6404static int checkjobs(struct pipe *fg_pipe)
    15296405{
    15306406    int attributes;
    15316407    int status;
    15326408#if ENABLE_HUSH_JOB
    1533     int prognum = 0;
    15346409    struct pipe *pi;
    15356410#endif
     
    15376412    int rcode = 0;
    15386413
     6414    debug_printf_jobs("checkjobs %p\n", fg_pipe);
     6415
    15396416    attributes = WUNTRACED;
    1540     if (fg_pipe == NULL) {
     6417    if (fg_pipe == NULL)
    15416418        attributes |= WNOHANG;
    1542     }
     6419
     6420    errno = 0;
     6421#if ENABLE_HUSH_FAST
     6422    if (G.handled_SIGCHLD == G.count_SIGCHLD) {
     6423//bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p",
     6424//getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe);
     6425        /* There was neither fork nor SIGCHLD since last waitpid */
     6426        /* Avoid doing waitpid syscall if possible */
     6427        if (!G.we_have_children) {
     6428            errno = ECHILD;
     6429            return -1;
     6430        }
     6431        if (fg_pipe == NULL) { /* is WNOHANG set? */
     6432            /* We have children, but they did not exit
     6433             * or stop yet (we saw no SIGCHLD) */
     6434            return 0;
     6435        }
     6436        /* else: !WNOHANG, waitpid will block, can't short-circuit */
     6437    }
     6438#endif
    15436439
    15446440/* Do we do this right?
     
    15486444 * bash-3.00# echo $?
    15496445 * 1   <========== bg pipe is not fully done, but exitcode is already known!
     6446 * [hush 1.14.0: yes we do it right]
    15506447 */
    1551 
    1552 //FIXME: non-interactive bash does not continue even if all processes in fg pipe
    1553 //are stopped. Testcase: "cat | cat" in a script (not on command line)
    1554 // + killall -STOP cat
    1555 
    15566448 wait_more:
    1557     while ((childpid = waitpid(-1, &status, attributes)) > 0) {
    1558         const int dead = WIFEXITED(status) || WIFSIGNALED(status);
    1559 
    1560 #ifdef DEBUG_SHELL_JOBS
     6449    while (1) {
     6450        int i;
     6451        int dead;
     6452
     6453#if ENABLE_HUSH_FAST
     6454        i = G.count_SIGCHLD;
     6455#endif
     6456        childpid = waitpid(-1, &status, attributes);
     6457        if (childpid <= 0) {
     6458            if (childpid && errno != ECHILD)
     6459                bb_perror_msg("waitpid");
     6460#if ENABLE_HUSH_FAST
     6461            else { /* Until next SIGCHLD, waitpid's are useless */
     6462                G.we_have_children = (childpid == 0);
     6463                G.handled_SIGCHLD = i;
     6464//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
     6465            }
     6466#endif
     6467            break;
     6468        }
     6469        dead = WIFEXITED(status) || WIFSIGNALED(status);
     6470
     6471#if DEBUG_JOBS
    15616472        if (WIFSTOPPED(status))
    15626473            debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
     
    15716482        /* Were we asked to wait for fg pipe? */
    15726483        if (fg_pipe) {
    1573             int i;
    1574             for (i = 0; i < fg_pipe->num_progs; i++) {
    1575                 debug_printf_jobs("check pid %d\n", fg_pipe->progs[i].pid);
    1576                 if (fg_pipe->progs[i].pid == childpid) {
    1577                     /* printf("process %d exit %d\n", i, WEXITSTATUS(status)); */
    1578                     if (dead) {
    1579                         fg_pipe->progs[i].pid = 0;
    1580                         fg_pipe->running_progs--;
    1581                         if (i == fg_pipe->num_progs-1)
    1582                             /* last process gives overall exitstatus */
    1583                             rcode = WEXITSTATUS(status);
    1584                     } else {
    1585                         fg_pipe->progs[i].is_stopped = 1;
    1586                         fg_pipe->stopped_progs++;
     6484            i = fg_pipe->num_cmds;
     6485            while (--i >= 0) {
     6486                debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
     6487                if (fg_pipe->cmds[i].pid != childpid)
     6488                    continue;
     6489                if (dead) {
     6490                    int ex;
     6491                    fg_pipe->cmds[i].pid = 0;
     6492                    fg_pipe->alive_cmds--;
     6493                    ex = WEXITSTATUS(status);
     6494                    /* bash prints killer signal's name for *last*
     6495                     * process in pipe (prints just newline for SIGINT).
     6496                     * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
     6497                     */
     6498                    if (WIFSIGNALED(status)) {
     6499                        int sig = WTERMSIG(status);
     6500                        if (i == fg_pipe->num_cmds-1)
     6501                            printf("%s\n", sig == SIGINT ? "" : get_signame(sig));
     6502                        /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
     6503                         * Maybe we need to use sig | 128? */
     6504                        ex = sig + 128;
    15876505                    }
    1588                     debug_printf_jobs("fg_pipe: running_progs %d stopped_progs %d\n",
    1589                             fg_pipe->running_progs, fg_pipe->stopped_progs);
    1590                     if (fg_pipe->running_progs - fg_pipe->stopped_progs <= 0) {
    1591                         /* All processes in fg pipe have exited/stopped */
     6506                    fg_pipe->cmds[i].cmd_exitcode = ex;
     6507                } else {
     6508                    fg_pipe->cmds[i].is_stopped = 1;
     6509                    fg_pipe->stopped_cmds++;
     6510                }
     6511                debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
     6512                        fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
     6513                if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
     6514                    /* All processes in fg pipe have exited or stopped */
     6515                    i = fg_pipe->num_cmds;
     6516                    while (--i >= 0) {
     6517                        rcode = fg_pipe->cmds[i].cmd_exitcode;
     6518                        /* usually last process gives overall exitstatus,
     6519                         * but with "set -o pipefail", last *failed* process does */
     6520                        if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
     6521                            break;
     6522                    }
     6523                    IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
     6524/* Note: *non-interactive* bash does not continue if all processes in fg pipe
     6525 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
     6526 * and "killall -STOP cat" */
     6527                    if (G_interactive_fd) {
    15926528#if ENABLE_HUSH_JOB
    1593                         if (fg_pipe->running_progs)
     6529                        if (fg_pipe->alive_cmds != 0)
    15946530                            insert_bg_job(fg_pipe);
    15956531#endif
    15966532                        return rcode;
    15976533                    }
    1598                     /* There are still running processes in the fg pipe */
    1599                     goto wait_more;
     6534                    if (fg_pipe->alive_cmds == 0)
     6535                        return rcode;
    16006536                }
    1601             }
    1602             /* fall through to searching process in bg pipes */
     6537                /* There are still running processes in the fg pipe */
     6538                goto wait_more; /* do waitpid again */
     6539            }
     6540            /* it wasnt fg_pipe, look for process in bg pipes */
    16036541        }
    16046542
     
    16066544        /* We asked to wait for bg or orphaned children */
    16076545        /* No need to remember exitcode in this case */
    1608         for (pi = job_list; pi; pi = pi->next) {
    1609             prognum = 0;
    1610             while (prognum < pi->num_progs) {
    1611                 if (pi->progs[prognum].pid == childpid)
     6546        for (pi = G.job_list; pi; pi = pi->next) {
     6547            for (i = 0; i < pi->num_cmds; i++) {
     6548                if (pi->cmds[i].pid == childpid)
    16126549                    goto found_pi_and_prognum;
    1613                 prognum++;
    1614             }
    1615         }
    1616 #endif
    1617 
     6550            }
     6551        }
    16186552        /* Happens when shell is used as init process (init=/bin/sh) */
    16196553        debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
    1620         goto wait_more;
    1621 
    1622 #if ENABLE_HUSH_JOB
     6554        continue; /* do waitpid again */
     6555
    16236556 found_pi_and_prognum:
    16246557        if (dead) {
    16256558            /* child exited */
    1626             pi->progs[prognum].pid = 0;
    1627             pi->running_progs--;
    1628             if (!pi->running_progs) {
    1629                 printf(JOB_STATUS_FORMAT, pi->jobid,
     6559            pi->cmds[i].pid = 0;
     6560            pi->alive_cmds--;
     6561            if (!pi->alive_cmds) {
     6562                if (G_interactive_fd)
     6563                    printf(JOB_STATUS_FORMAT, pi->jobid,
    16306564                            "Done", pi->cmdtext);
    16316565                delete_finished_bg_job(pi);
     
    16336567        } else {
    16346568            /* child stopped */
    1635             pi->stopped_progs++;
    1636             pi->progs[prognum].is_stopped = 1;
    1637         }
    1638 #endif
    1639     }
    1640 
    1641     /* wait found no children or failed */
    1642 
    1643     if (childpid && errno != ECHILD)
    1644         bb_perror_msg("waitpid");
     6569            pi->cmds[i].is_stopped = 1;
     6570            pi->stopped_cmds++;
     6571        }
     6572#endif
     6573    } /* while (waitpid succeeds)... */
     6574
    16456575    return rcode;
    16466576}
    16476577
    16486578#if ENABLE_HUSH_JOB
    1649 static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
     6579static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
    16506580{
    16516581    pid_t p;
    16526582    int rcode = checkjobs(fg_pipe);
    1653     /* Job finished, move the shell to the foreground */
    1654     p = getpgid(0); /* pgid of our process */
    1655     debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p);
    1656     if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY)
    1657         bb_perror_msg("tcsetpgrp-4a");
     6583    if (G_saved_tty_pgrp) {
     6584        /* Job finished, move the shell to the foreground */
     6585        p = getpgrp(); /* our process group id */
     6586        debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p);
     6587        tcsetpgrp(G_interactive_fd, p);
     6588    }
    16586589    return rcode;
    16596590}
    16606591#endif
    16616592
    1662 /* run_pipe_real() starts all the jobs, but doesn't wait for anything
    1663  * to finish.  See checkjobs().
     6593/* Start all the jobs, but don't wait for anything to finish.
     6594 * See checkjobs().
    16646595 *
    1665  * return code is normally -1, when the caller has to wait for children
     6596 * Return code is normally -1, when the caller has to wait for children
    16666597 * to finish to determine the exit status of the pipe.  If the pipe
    16676598 * is a simple builtin command, however, the action is done by the
    1668  * time run_pipe_real returns, and the exit code is provided as the
     6599 * time run_pipe returns, and the exit code is provided as the
    16696600 * return value.
    1670  *
    1671  * The input of the pipe is always stdin, the output is always
    1672  * stdout.  The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
    1673  * because it tries to avoid running the command substitution in
    1674  * subshell, when that is in fact necessary.  The subshell process
    1675  * now has its stdout directed to the input of the appropriate pipe,
    1676  * so this routine is noticeably simpler.
    16776601 *
    16786602 * Returns -1 only if started some children. IOW: we have to
    16796603 * mask out retvals of builtins etc with 0xff!
     6604 *
     6605 * The only case when we do not need to [v]fork is when the pipe
     6606 * is single, non-backgrounded, non-subshell command. Examples:
     6607 * cmd ; ...   { list } ; ...
     6608 * cmd && ...  { list } && ...
     6609 * cmd || ...  { list } || ...
     6610 * If it is, then we can run cmd as a builtin, NOFORK [do we do this?],
     6611 * or (if SH_STANDALONE) an applet, and we can run the { list }
     6612 * with run_list. If it isn't one of these, we fork and exec cmd.
     6613 *
     6614 * Cases when we must fork:
     6615 * non-single:   cmd | cmd
     6616 * backgrounded: cmd &     { list } &
     6617 * subshell:     ( list ) [&]
    16806618 */
    1681 static int run_pipe_real(struct pipe *pi)
    1682 {
    1683     int i;
    1684     int nextin, nextout;
    1685     int pipefds[2];             /* pipefds[0] is for reading */
    1686     struct child_prog *child;
    1687     const struct built_in_command *x;
    1688     char *p;
     6619#if !ENABLE_HUSH_MODE_X
     6620#define redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel, argv_expanded) \
     6621    redirect_and_varexp_helper(new_env_p, old_vars_p, command, squirrel)
     6622#endif
     6623static int redirect_and_varexp_helper(char ***new_env_p,
     6624        struct variable **old_vars_p,
     6625        struct command *command,
     6626        int squirrel[3],
     6627        char **argv_expanded)
     6628{
     6629    /* setup_redirects acts on file descriptors, not FILEs.
     6630     * This is perfect for work that comes after exec().
     6631     * Is it really safe for inline use?  Experimentally,
     6632     * things seem to work. */
     6633    int rcode = setup_redirects(command, squirrel);
     6634    if (rcode == 0) {
     6635        char **new_env = expand_assignments(command->argv, command->assignment_cnt);
     6636        *new_env_p = new_env;
     6637        dump_cmd_in_x_mode(new_env);
     6638        dump_cmd_in_x_mode(argv_expanded);
     6639        if (old_vars_p)
     6640            *old_vars_p = set_vars_and_save_old(new_env);
     6641    }
     6642    return rcode;
     6643}
     6644static NOINLINE int run_pipe(struct pipe *pi)
     6645{
     6646    static const char *const null_ptr = NULL;
     6647
     6648    int cmd_no;
     6649    int next_infd;
     6650    struct command *command;
     6651    char **argv_expanded;
     6652    char **argv;
    16896653    /* it is not always needed, but we aim to smaller code */
    16906654    int squirrel[] = { -1, -1, -1 };
    16916655    int rcode;
    1692     const int single_fg = (pi->num_progs == 1 && pi->followup != PIPE_BG);
    1693 
    1694     debug_printf_exec("run_pipe_real start: single_fg=%d\n", single_fg);
    1695 
    1696     nextin = 0;
     6656
     6657    debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
     6658    debug_enter();
     6659
     6660    /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
     6661     * Result should be 3 lines: q w e, qwe, q w e
     6662     */
     6663    G.ifs = get_local_var_value("IFS");
     6664    if (!G.ifs)
     6665        G.ifs = defifs;
     6666
     6667    IF_HUSH_JOB(pi->pgrp = -1;)
     6668    pi->stopped_cmds = 0;
     6669    command = &pi->cmds[0];
     6670    argv_expanded = NULL;
     6671
     6672    if (pi->num_cmds != 1
     6673     || pi->followup == PIPE_BG
     6674     || command->cmd_type == CMD_SUBSHELL
     6675    ) {
     6676        goto must_fork;
     6677    }
     6678
     6679    pi->alive_cmds = 1;
     6680
     6681    debug_printf_exec(": group:%p argv:'%s'\n",
     6682        command->group, command->argv ? command->argv[0] : "NONE");
     6683
     6684    if (command->group) {
     6685#if ENABLE_HUSH_FUNCTIONS
     6686        if (command->cmd_type == CMD_FUNCDEF) {
     6687            /* "executing" func () { list } */
     6688            struct function *funcp;
     6689
     6690            funcp = new_function(command->argv[0]);
     6691            /* funcp->name is already set to argv[0] */
     6692            funcp->body = command->group;
     6693# if !BB_MMU
     6694            funcp->body_as_string = command->group_as_string;
     6695            command->group_as_string = NULL;
     6696# endif
     6697            command->group = NULL;
     6698            command->argv[0] = NULL;
     6699            debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
     6700            funcp->parent_cmd = command;
     6701            command->child_func = funcp;
     6702
     6703            debug_printf_exec("run_pipe: return EXIT_SUCCESS\n");
     6704            debug_leave();
     6705            return EXIT_SUCCESS;
     6706        }
     6707#endif
     6708        /* { list } */
     6709        debug_printf("non-subshell group\n");
     6710        rcode = 1; /* exitcode if redir failed */
     6711        if (setup_redirects(command, squirrel) == 0) {
     6712            debug_printf_exec(": run_list\n");
     6713            rcode = run_list(command->group) & 0xff;
     6714        }
     6715        restore_redirects(squirrel);
     6716        IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
     6717        debug_leave();
     6718        debug_printf_exec("run_pipe: return %d\n", rcode);
     6719        return rcode;
     6720    }
     6721
     6722    argv = command->argv ? command->argv : (char **) &null_ptr;
     6723    {
     6724        const struct built_in_command *x;
     6725#if ENABLE_HUSH_FUNCTIONS
     6726        const struct function *funcp;
     6727#else
     6728        enum { funcp = 0 };
     6729#endif
     6730        char **new_env = NULL;
     6731        struct variable *old_vars = NULL;
     6732
     6733        if (argv[command->assignment_cnt] == NULL) {
     6734            /* Assignments, but no command */
     6735            /* Ensure redirects take effect (that is, create files).
     6736             * Try "a=t >file" */
     6737#if 0 /* A few cases in testsuite fail with this code. FIXME */
     6738            rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL);
     6739            /* Set shell variables */
     6740            if (new_env) {
     6741                argv = new_env;
     6742                while (*argv) {
     6743                    set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
     6744                    /* Do we need to flag set_local_var() errors?
     6745                     * "assignment to readonly var" and "putenv error"
     6746                     */
     6747                    argv++;
     6748                }
     6749            }
     6750            /* Redirect error sets $? to 1. Otherwise,
     6751             * if evaluating assignment value set $?, retain it.
     6752             * Try "false; q=`exit 2`; echo $?" - should print 2: */
     6753            if (rcode == 0)
     6754                rcode = G.last_exitcode;
     6755            /* Exit, _skipping_ variable restoring code: */
     6756            goto clean_up_and_ret0;
     6757
     6758#else /* Older, bigger, but more correct code */
     6759
     6760            rcode = setup_redirects(command, squirrel);
     6761            restore_redirects(squirrel);
     6762            /* Set shell variables */
     6763            if (G_x_mode)
     6764                bb_putchar_stderr('+');
     6765            while (*argv) {
     6766                char *p = expand_string_to_string(*argv, /*unbackslash:*/ 1);
     6767                if (G_x_mode)
     6768                    fprintf(stderr, " %s", p);
     6769                debug_printf_exec("set shell var:'%s'->'%s'\n",
     6770                        *argv, p);
     6771                set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
     6772                /* Do we need to flag set_local_var() errors?
     6773                 * "assignment to readonly var" and "putenv error"
     6774                 */
     6775                argv++;
     6776            }
     6777            if (G_x_mode)
     6778                bb_putchar_stderr('\n');
     6779            /* Redirect error sets $? to 1. Otherwise,
     6780             * if evaluating assignment value set $?, retain it.
     6781             * Try "false; q=`exit 2`; echo $?" - should print 2: */
     6782            if (rcode == 0)
     6783                rcode = G.last_exitcode;
     6784            IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
     6785            debug_leave();
     6786            debug_printf_exec("run_pipe: return %d\n", rcode);
     6787            return rcode;
     6788#endif
     6789        }
     6790
     6791        /* Expand the rest into (possibly) many strings each */
     6792        if (0) {}
     6793#if ENABLE_HUSH_BASH_COMPAT
     6794        else if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) {
     6795            argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
     6796        }
     6797#endif
     6798        else {
     6799            argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
     6800        }
     6801
     6802        /* if someone gives us an empty string: `cmd with empty output` */
     6803        if (!argv_expanded[0]) {
     6804            free(argv_expanded);
     6805            debug_leave();
     6806            return G.last_exitcode;
     6807        }
     6808
     6809        x = find_builtin(argv_expanded[0]);
     6810#if ENABLE_HUSH_FUNCTIONS
     6811        funcp = NULL;
     6812        if (!x)
     6813            funcp = find_function(argv_expanded[0]);
     6814#endif
     6815        if (x || funcp) {
     6816            if (!funcp) {
     6817                if (x->b_function == builtin_exec && argv_expanded[1] == NULL) {
     6818                    debug_printf("exec with redirects only\n");
     6819                    rcode = setup_redirects(command, NULL);
     6820                    goto clean_up_and_ret1;
     6821                }
     6822            }
     6823            rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
     6824            if (rcode == 0) {
     6825                if (!funcp) {
     6826                    debug_printf_exec(": builtin '%s' '%s'...\n",
     6827                        x->b_cmd, argv_expanded[1]);
     6828                    rcode = x->b_function(argv_expanded) & 0xff;
     6829                    fflush_all();
     6830                }
     6831#if ENABLE_HUSH_FUNCTIONS
     6832                else {
     6833# if ENABLE_HUSH_LOCAL
     6834                    struct variable **sv;
     6835                    sv = G.shadowed_vars_pp;
     6836                    G.shadowed_vars_pp = &old_vars;
     6837# endif
     6838                    debug_printf_exec(": function '%s' '%s'...\n",
     6839                        funcp->name, argv_expanded[1]);
     6840                    rcode = run_function(funcp, argv_expanded) & 0xff;
     6841# if ENABLE_HUSH_LOCAL
     6842                    G.shadowed_vars_pp = sv;
     6843# endif
     6844                }
     6845#endif
     6846            }
     6847 clean_up_and_ret:
     6848            unset_vars(new_env);
     6849            add_vars(old_vars);
     6850/* clean_up_and_ret0: */
     6851            restore_redirects(squirrel);
     6852 clean_up_and_ret1:
     6853            free(argv_expanded);
     6854            IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
     6855            debug_leave();
     6856            debug_printf_exec("run_pipe return %d\n", rcode);
     6857            return rcode;
     6858        }
     6859
     6860        if (ENABLE_FEATURE_SH_STANDALONE) {
     6861            int n = find_applet_by_name(argv_expanded[0]);
     6862            if (n >= 0 && APPLET_IS_NOFORK(n)) {
     6863                rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded);
     6864                if (rcode == 0) {
     6865                    debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
     6866                        argv_expanded[0], argv_expanded[1]);
     6867                    rcode = run_nofork_applet(n, argv_expanded);
     6868                }
     6869                goto clean_up_and_ret;
     6870            }
     6871        }
     6872        /* It is neither builtin nor applet. We must fork. */
     6873    }
     6874
     6875 must_fork:
     6876    /* NB: argv_expanded may already be created, and that
     6877     * might include `cmd` runs! Do not rerun it! We *must*
     6878     * use argv_expanded if it's non-NULL */
     6879
     6880    /* Going to fork a child per each pipe member */
     6881    pi->alive_cmds = 0;
     6882    next_infd = 0;
     6883
     6884    cmd_no = 0;
     6885    while (cmd_no < pi->num_cmds) {
     6886        struct fd_pair pipefds;
     6887#if !BB_MMU
     6888        volatile nommu_save_t nommu_save;
     6889        nommu_save.new_env = NULL;
     6890        nommu_save.old_vars = NULL;
     6891        nommu_save.argv = NULL;
     6892        nommu_save.argv_from_re_execing = NULL;
     6893#endif
     6894        command = &pi->cmds[cmd_no];
     6895        cmd_no++;
     6896        if (command->argv) {
     6897            debug_printf_exec(": pipe member '%s' '%s'...\n",
     6898                    command->argv[0], command->argv[1]);
     6899        } else {
     6900            debug_printf_exec(": pipe member with no argv\n");
     6901        }
     6902
     6903        /* pipes are inserted between pairs of commands */
     6904        pipefds.rd = 0;
     6905        pipefds.wr = 1;
     6906        if (cmd_no < pi->num_cmds)
     6907            xpiped_pair(pipefds);
     6908
     6909        command->pid = BB_MMU ? fork() : vfork();
     6910        if (!command->pid) { /* child */
    16976911#if ENABLE_HUSH_JOB
    1698     pi->pgrp = -1;
    1699 #endif
    1700     pi->running_progs = 1;
    1701     pi->stopped_progs = 0;
    1702 
    1703     /* Check if this is a simple builtin (not part of a pipe).
    1704      * Builtins within pipes have to fork anyway, and are handled in
    1705      * pseudo_exec.  "echo foo | read bar" doesn't work on bash, either.
    1706      */
    1707     child = &(pi->progs[0]);
    1708     if (single_fg && child->group && child->subshell == 0) {
    1709         debug_printf("non-subshell grouping\n");
    1710         setup_redirects(child, squirrel);
    1711         debug_printf_exec(": run_list_real\n");
    1712         rcode = run_list_real(child->group);
    1713         restore_redirects(squirrel);
    1714         debug_printf_exec("run_pipe_real return %d\n", rcode);
    1715         return rcode; // do we need to add '... & 0xff' ?
    1716     }
    1717 
    1718     if (single_fg && child->argv != NULL) {
    1719         char **argv_expanded;
    1720         char **argv = child->argv;
    1721 
    1722         for (i = 0; is_assignment(argv[i]); i++)
    1723             continue;
    1724         if (i != 0 && argv[i] == NULL) {
    1725             /* assignments, but no command: set the local environment */
    1726             for (i = 0; argv[i] != NULL; i++) {
    1727                 debug_printf("local environment set: %s\n", argv[i]);
    1728                 p = expand_string_to_string(argv[i]);
    1729                 set_local_var(p, 0);
    1730             }
    1731             return EXIT_SUCCESS;   /* don't worry about errors in set_local_var() yet */
    1732         }
    1733         for (i = 0; is_assignment(argv[i]); i++) {
    1734             p = expand_string_to_string(argv[i]);
    1735             //sp: child->sp--;
    1736             putenv(p);
    1737         }
    1738         for (x = bltins; x->cmd; x++) {
    1739             if (strcmp(argv[i], x->cmd) == 0) {
    1740                 if (x->function == builtin_exec && argv[i+1] == NULL) {
    1741                     debug_printf("magic exec\n");
    1742                     setup_redirects(child, NULL);
    1743                     return EXIT_SUCCESS;
    1744                 }
    1745                 debug_printf("builtin inline %s\n", argv[0]);
    1746                 /* XXX setup_redirects acts on file descriptors, not FILEs.
    1747                  * This is perfect for work that comes after exec().
    1748                  * Is it really safe for inline use?  Experimentally,
    1749                  * things seem to work with glibc. */
    1750                 setup_redirects(child, squirrel);
    1751                 debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv[i+1]);
    1752                 //sp: if (child->sp) /* btw we can do it unconditionally... */
    1753                 argv_expanded = expand_strvec_to_strvec(argv + i);
    1754                 rcode = x->function(argv_expanded) & 0xff;
    1755                 free(argv_expanded);
    1756                 restore_redirects(squirrel);
    1757                 debug_printf_exec("run_pipe_real return %d\n", rcode);
    1758                 return rcode;
    1759             }
    1760         }
    1761 #if ENABLE_FEATURE_SH_STANDALONE
    1762         {
    1763             const struct bb_applet *a = find_applet_by_name(argv[i]);
    1764             if (a && a->nofork) {
    1765                 setup_redirects(child, squirrel);
    1766                 save_nofork_data(&nofork_save);
    1767                 argv_expanded = argv + i;
    1768                 //sp: if (child->sp)
    1769                 argv_expanded = expand_strvec_to_strvec(argv + i);
    1770                 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]);
    1771                 rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded) & 0xff;
    1772                 free(argv_expanded);
    1773                 restore_redirects(squirrel);
    1774                 debug_printf_exec("run_pipe_real return %d\n", rcode);
    1775                 return rcode;
    1776             }
    1777         }
    1778 #endif
    1779     }
    1780 
    1781     /* Going to fork a child per each pipe member */
    1782     pi->running_progs = 0;
    1783 
    1784     /* Disable job control signals for shell (parent) and
    1785      * for initial child code after fork */
    1786     set_jobctrl_sighandler(SIG_IGN);
    1787 
    1788     for (i = 0; i < pi->num_progs; i++) {
    1789         child = &(pi->progs[i]);
    1790         if (child->argv)
    1791             debug_printf_exec(": pipe member '%s' '%s'...\n", child->argv[0], child->argv[1]);
    1792         else
    1793             debug_printf_exec(": pipe member with no argv\n");
    1794 
    1795         /* pipes are inserted between pairs of commands */
    1796         if ((i + 1) < pi->num_progs) {
    1797             pipe(pipefds);
    1798             nextout = pipefds[1];
    1799         } else {
    1800             nextout = 1;
    1801             pipefds[0] = -1;
    1802         }
    1803 
    1804         /* XXX test for failed fork()? */
    1805 #if BB_MMU
    1806         child->pid = fork();
    1807 #else
    1808         child->pid = vfork();
    1809 #endif
    1810         if (!child->pid) { /* child */
     6912            disable_restore_tty_pgrp_on_exit();
     6913            CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
     6914
    18116915            /* Every child adds itself to new process group
    1812              * with pgid == pid of first child in pipe */
    1813 #if ENABLE_HUSH_JOB
    1814             if (run_list_level == 1 && interactive_fd) {
    1815                 /* Don't do pgrp restore anymore on fatal signals */
    1816                 set_fatal_sighandler(SIG_DFL);
    1817                 if (pi->pgrp < 0) /* true for 1st process only */
    1818                     pi->pgrp = getpid();
    1819                 if (setpgid(0, pi->pgrp) == 0 && pi->followup != PIPE_BG) {
     6916             * with pgid == pid_of_first_child_in_pipe */
     6917            if (G.run_list_level == 1 && G_interactive_fd) {
     6918                pid_t pgrp;
     6919                pgrp = pi->pgrp;
     6920                if (pgrp < 0) /* true for 1st process only */
     6921                    pgrp = getpid();
     6922                if (setpgid(0, pgrp) == 0
     6923                 && pi->followup != PIPE_BG
     6924                 && G_saved_tty_pgrp /* we have ctty */
     6925                ) {
    18206926                    /* We do it in *every* child, not just first,
    18216927                     * to avoid races */
    1822                     tcsetpgrp(interactive_fd, pi->pgrp);
     6928                    tcsetpgrp(G_interactive_fd, pgrp);
    18236929                }
    18246930            }
    18256931#endif
    1826             /* in non-interactive case fatal sigs are already SIG_DFL */
    1827             close_all();
    1828             if (nextin != 0) {
    1829                 dup2(nextin, 0);
    1830                 close(nextin);
    1831             }
    1832             if (nextout != 1) {
    1833                 dup2(nextout, 1);
    1834                 close(nextout);
    1835             }
    1836             if (pipefds[0] != -1) {
    1837                 close(pipefds[0]);  /* opposite end of our output pipe */
    1838             }
     6932            if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) {
     6933                /* 1st cmd in backgrounded pipe
     6934                 * should have its stdin /dev/null'ed */
     6935                close(0);
     6936                if (open(bb_dev_null, O_RDONLY))
     6937                    xopen("/", O_RDONLY);
     6938            } else {
     6939                xmove_fd(next_infd, 0);
     6940            }
     6941            xmove_fd(pipefds.wr, 1);
     6942            if (pipefds.rd > 1)
     6943                close(pipefds.rd);
    18396944            /* Like bash, explicit redirects override pipes,
    18406945             * and the pipe fd is available for dup'ing. */
    1841             setup_redirects(child, NULL);
     6946            if (setup_redirects(command, NULL))
     6947                _exit(1);
    18426948
    18436949            /* Restore default handlers just prior to exec */
    1844             set_jobctrl_sighandler(SIG_DFL);
    1845             set_misc_sighandler(SIG_DFL);
    1846             signal(SIGCHLD, SIG_DFL);
    1847             pseudo_exec(child);
    1848         }
    1849 
    1850         pi->running_progs++;
    1851 
     6950            /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */
     6951
     6952            /* Stores to nommu_save list of env vars putenv'ed
     6953             * (NOMMU, on MMU we don't need that) */
     6954            /* cast away volatility... */
     6955            pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
     6956            /* pseudo_exec() does not return */
     6957        }
     6958
     6959        /* parent or error */
     6960#if ENABLE_HUSH_FAST
     6961        G.count_SIGCHLD++;
     6962//bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
     6963#endif
     6964        enable_restore_tty_pgrp_on_exit();
     6965#if !BB_MMU
     6966        /* Clean up after vforked child */
     6967        free(nommu_save.argv);
     6968        free(nommu_save.argv_from_re_execing);
     6969        unset_vars(nommu_save.new_env);
     6970        add_vars(nommu_save.old_vars);
     6971#endif
     6972        free(argv_expanded);
     6973        argv_expanded = NULL;
     6974        if (command->pid < 0) { /* [v]fork failed */
     6975            /* Clearly indicate, was it fork or vfork */
     6976            bb_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
     6977        } else {
     6978            pi->alive_cmds++;
    18526979#if ENABLE_HUSH_JOB
    1853         /* Second and next children need to know pid of first one */
    1854         if (pi->pgrp < 0)
    1855             pi->pgrp = child->pid;
    1856 #endif
    1857         if (nextin != 0)
    1858             close(nextin);
    1859         if (nextout != 1)
    1860             close(nextout);
    1861 
    1862         /* If there isn't another process, nextin is garbage
    1863            but it doesn't matter */
    1864         nextin = pipefds[0];
    1865     }
    1866     debug_printf_exec("run_pipe_real return -1\n");
     6980            /* Second and next children need to know pid of first one */
     6981            if (pi->pgrp < 0)
     6982                pi->pgrp = command->pid;
     6983#endif
     6984        }
     6985
     6986        if (cmd_no > 1)
     6987            close(next_infd);
     6988        if (cmd_no < pi->num_cmds)
     6989            close(pipefds.wr);
     6990        /* Pass read (output) pipe end to next iteration */
     6991        next_infd = pipefds.rd;
     6992    }
     6993
     6994    if (!pi->alive_cmds) {
     6995        debug_leave();
     6996        debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
     6997        return 1;
     6998    }
     6999
     7000    debug_leave();
     7001    debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
    18677002    return -1;
    18687003}
    1869 
    1870 #ifndef debug_print_tree
    1871 static void debug_print_tree(struct pipe *pi, int lvl)
    1872 {
    1873     static const char *PIPE[] = {
    1874         [PIPE_SEQ] = "SEQ",
    1875         [PIPE_AND] = "AND",
    1876         [PIPE_OR ] = "OR" ,
    1877         [PIPE_BG ] = "BG" ,
    1878     };
    1879     static const char *RES[] = {
    1880         [RES_NONE ] = "NONE" ,
    1881 #if ENABLE_HUSH_IF
    1882         [RES_IF   ] = "IF"   ,
    1883         [RES_THEN ] = "THEN" ,
    1884         [RES_ELIF ] = "ELIF" ,
    1885         [RES_ELSE ] = "ELSE" ,
    1886         [RES_FI   ] = "FI"   ,
    1887 #endif
    1888 #if ENABLE_HUSH_LOOPS
    1889         [RES_FOR  ] = "FOR"  ,
    1890         [RES_WHILE] = "WHILE",
    1891         [RES_UNTIL] = "UNTIL",
    1892         [RES_DO   ] = "DO"   ,
    1893         [RES_DONE ] = "DONE" ,
    1894         [RES_IN   ] = "IN"   ,
    1895 #endif
    1896         [RES_XXXX ] = "XXXX" ,
    1897         [RES_SNTX ] = "SNTX" ,
    1898     };
    1899 
    1900     int pin, prn;
    1901 
    1902     pin = 0;
    1903     while (pi) {
    1904         fprintf(stderr, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "",
    1905                 pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]);
    1906         prn = 0;
    1907         while (prn < pi->num_progs) {
    1908             struct child_prog *child = &pi->progs[prn];
    1909             char **argv = child->argv;
    1910 
    1911             fprintf(stderr, "%*s prog %d", lvl*2, "", prn);
    1912             if (child->group) {
    1913                 fprintf(stderr, " group %s: (argv=%p)\n",
    1914                         (child->subshell ? "()" : "{}"),
    1915                         argv);
    1916                 debug_print_tree(child->group, lvl+1);
    1917                 prn++;
    1918                 continue;
    1919             }
    1920             if (argv) while (*argv) {
    1921                 fprintf(stderr, " '%s'", *argv);
    1922                 argv++;
    1923             }
    1924             fprintf(stderr, "\n");
    1925             prn++;
    1926         }
    1927         pi = pi->next;
    1928         pin++;
    1929     }
    1930 }
    1931 #endif
    19327004
    19337005/* NB: called by pseudo_exec, and therefore must not modify any
    19347006 * global data until exec/_exit (we can be a child after vfork!) */
    1935 static int run_list_real(struct pipe *pi)
    1936 {
    1937     struct pipe *rpipe;
     7007static int run_list(struct pipe *pi)
     7008{
     7009#if ENABLE_HUSH_CASE
     7010    char *case_word = NULL;
     7011#endif
    19387012#if ENABLE_HUSH_LOOPS
    1939     char *for_varname = NULL;
     7013    struct pipe *loop_top = NULL;
    19407014    char **for_lcur = NULL;
    19417015    char **for_list = NULL;
    1942     int flag_rep = 0;
    1943 #endif
    1944     int save_num_progs;
    1945     int flag_skip = 1;
    1946     int rcode = 0; /* probably for gcc only */
    1947     int flag_restore = 0;
     7016#endif
     7017    smallint last_followup;
     7018    smalluint rcode;
     7019#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE
     7020    smalluint cond_code = 0;
     7021#else
     7022    enum { cond_code = 0 };
     7023#endif
     7024#if HAS_KEYWORDS
     7025    smallint rword;      /* RES_foo */
     7026    smallint last_rword; /* ditto */
     7027#endif
     7028
     7029    debug_printf_exec("run_list start lvl %d\n", G.run_list_level);
     7030    debug_enter();
     7031
     7032#if ENABLE_HUSH_LOOPS
     7033    /* Check syntax for "for" */
     7034    {
     7035        struct pipe *cpipe;
     7036        for (cpipe = pi; cpipe; cpipe = cpipe->next) {
     7037            if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
     7038                continue;
     7039            /* current word is FOR or IN (BOLD in comments below) */
     7040            if (cpipe->next == NULL) {
     7041                syntax_error("malformed for");
     7042                debug_leave();
     7043                debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
     7044                return 1;
     7045            }
     7046            /* "FOR v; do ..." and "for v IN a b; do..." are ok */
     7047            if (cpipe->next->res_word == RES_DO)
     7048                continue;
     7049            /* next word is not "do". It must be "in" then ("FOR v in ...") */
     7050            if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
     7051             || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
     7052            ) {
     7053                syntax_error("malformed for");
     7054                debug_leave();
     7055                debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
     7056                return 1;
     7057            }
     7058        }
     7059    }
     7060#endif
     7061
     7062    /* Past this point, all code paths should jump to ret: label
     7063     * in order to return, no direct "return" statements please.
     7064     * This helps to ensure that no memory is leaked. */
     7065
     7066#if ENABLE_HUSH_JOB
     7067    G.run_list_level++;
     7068#endif
     7069
     7070#if HAS_KEYWORDS
     7071    rword = RES_NONE;
     7072    last_rword = RES_XXXX;
     7073#endif
     7074    last_followup = PIPE_SEQ;
     7075    rcode = G.last_exitcode;
     7076
     7077    /* Go through list of pipes, (maybe) executing them. */
     7078    for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
     7079        if (G.flag_SIGINT)
     7080            break;
     7081
     7082        IF_HAS_KEYWORDS(rword = pi->res_word;)
     7083        debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
     7084                rword, cond_code, last_rword);
     7085#if ENABLE_HUSH_LOOPS
     7086        if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
     7087         && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
     7088        ) {
     7089            /* start of a loop: remember where loop starts */
     7090            loop_top = pi;
     7091            G.depth_of_loop++;
     7092        }
     7093#endif
     7094        /* Still in the same "if...", "then..." or "do..." branch? */
     7095        if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) {
     7096            if ((rcode == 0 && last_followup == PIPE_OR)
     7097             || (rcode != 0 && last_followup == PIPE_AND)
     7098            ) {
     7099                /* It is "<true> || CMD" or "<false> && CMD"
     7100                 * and we should not execute CMD */
     7101                debug_printf_exec("skipped cmd because of || or &&\n");
     7102                last_followup = pi->followup;
     7103                continue;
     7104            }
     7105        }
     7106        last_followup = pi->followup;
     7107        IF_HAS_KEYWORDS(last_rword = rword;)
    19487108#if ENABLE_HUSH_IF
    1949     int if_code = 0, next_if_code = 0;  /* need double-buffer to handle elif */
    1950 #else
    1951     enum { if_code = 0, next_if_code = 0 };
    1952 #endif
    1953     reserved_style rword;
    1954     reserved_style skip_more_for_this_rword = RES_XXXX;
    1955 
    1956     debug_printf_exec("run_list_real start lvl %d\n", run_list_level + 1);
    1957 
     7109        if (cond_code) {
     7110            if (rword == RES_THEN) {
     7111                /* if false; then ... fi has exitcode 0! */
     7112                G.last_exitcode = rcode = EXIT_SUCCESS;
     7113                /* "if <false> THEN cmd": skip cmd */
     7114                continue;
     7115            }
     7116        } else {
     7117            if (rword == RES_ELSE || rword == RES_ELIF) {
     7118                /* "if <true> then ... ELSE/ELIF cmd":
     7119                 * skip cmd and all following ones */
     7120                break;
     7121            }
     7122        }
     7123#endif
    19587124#if ENABLE_HUSH_LOOPS
    1959     /* check syntax for "for" */
    1960     for (rpipe = pi; rpipe; rpipe = rpipe->next) {
    1961         if ((rpipe->res_word == RES_IN || rpipe->res_word == RES_FOR)
    1962          && (rpipe->next == NULL)
    1963         ) {
    1964             syntax("malformed for"); /* no IN or no commands after IN */
    1965             debug_printf_exec("run_list_real lvl %d return 1\n", run_list_level);
    1966             return 1;
    1967         }
    1968         if ((rpipe->res_word == RES_IN && rpipe->next->res_word == RES_IN && rpipe->next->progs[0].argv != NULL)
    1969          || (rpipe->res_word == RES_FOR && rpipe->next->res_word != RES_IN)
    1970         ) {
    1971             /* TODO: what is tested in the first condition? */
    1972             syntax("malformed for"); /* 2nd condition: not followed by IN */
    1973             debug_printf_exec("run_list_real lvl %d return 1\n", run_list_level);
    1974             return 1;
    1975         }
    1976     }
    1977 #else
    1978     rpipe = NULL;
    1979 #endif
    1980 
     7125        if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
     7126            if (!for_lcur) {
     7127                /* first loop through for */
     7128
     7129                static const char encoded_dollar_at[] ALIGN1 = {
     7130                    SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0'
     7131                }; /* encoded representation of "$@" */
     7132                static const char *const encoded_dollar_at_argv[] = {
     7133                    encoded_dollar_at, NULL
     7134                }; /* argv list with one element: "$@" */
     7135                char **vals;
     7136
     7137                vals = (char**)encoded_dollar_at_argv;
     7138                if (pi->next->res_word == RES_IN) {
     7139                    /* if no variable values after "in" we skip "for" */
     7140                    if (!pi->next->cmds[0].argv) {
     7141                        G.last_exitcode = rcode = EXIT_SUCCESS;
     7142                        debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
     7143                        break;
     7144                    }
     7145                    vals = pi->next->cmds[0].argv;
     7146                } /* else: "for var; do..." -> assume "$@" list */
     7147                /* create list of variable values */
     7148                debug_print_strings("for_list made from", vals);
     7149                for_list = expand_strvec_to_strvec(vals);
     7150                for_lcur = for_list;
     7151                debug_print_strings("for_list", for_list);
     7152            }
     7153            if (!*for_lcur) {
     7154                /* "for" loop is over, clean up */
     7155                free(for_list);
     7156                for_list = NULL;
     7157                for_lcur = NULL;
     7158                break;
     7159            }
     7160            /* Insert next value from for_lcur */
     7161            /* note: *for_lcur already has quotes removed, $var expanded, etc */
     7162            set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
     7163            continue;
     7164        }
     7165        if (rword == RES_IN) {
     7166            continue; /* "for v IN list;..." - "in" has no cmds anyway */
     7167        }
     7168        if (rword == RES_DONE) {
     7169            continue; /* "done" has no cmds too */
     7170        }
     7171#endif
     7172#if ENABLE_HUSH_CASE
     7173        if (rword == RES_CASE) {
     7174            case_word = expand_strvec_to_string(pi->cmds->argv);
     7175            continue;
     7176        }
     7177        if (rword == RES_MATCH) {
     7178            char **argv;
     7179
     7180            if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
     7181                break;
     7182            /* all prev words didn't match, does this one match? */
     7183            argv = pi->cmds->argv;
     7184            while (*argv) {
     7185                char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 1);
     7186                /* TODO: which FNM_xxx flags to use? */
     7187                cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
     7188                free(pattern);
     7189                if (cond_code == 0) { /* match! we will execute this branch */
     7190                    free(case_word); /* make future "word)" stop */
     7191                    case_word = NULL;
     7192                    break;
     7193                }
     7194                argv++;
     7195            }
     7196            continue;
     7197        }
     7198        if (rword == RES_CASE_BODY) { /* inside of a case branch */
     7199            if (cond_code != 0)
     7200                continue; /* not matched yet, skip this pipe */
     7201        }
     7202#endif
     7203        /* Just pressing <enter> in shell should check for jobs.
     7204         * OTOH, in non-interactive shell this is useless
     7205         * and only leads to extra job checks */
     7206        if (pi->num_cmds == 0) {
     7207            if (G_interactive_fd)
     7208                goto check_jobs_and_continue;
     7209            continue;
     7210        }
     7211
     7212        /* After analyzing all keywords and conditions, we decided
     7213         * to execute this pipe. NB: have to do checkjobs(NULL)
     7214         * after run_pipe to collect any background children,
     7215         * even if list execution is to be stopped. */
     7216        debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
     7217        {
     7218            int r;
     7219#if ENABLE_HUSH_LOOPS
     7220            G.flag_break_continue = 0;
     7221#endif
     7222            rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
     7223            if (r != -1) {
     7224                /* We ran a builtin, function, or group.
     7225                 * rcode is already known
     7226                 * and we don't need to wait for anything. */
     7227                G.last_exitcode = rcode;
     7228                debug_printf_exec(": builtin/func exitcode %d\n", rcode);
     7229                check_and_run_traps(0);
     7230#if ENABLE_HUSH_LOOPS
     7231                /* Was it "break" or "continue"? */
     7232                if (G.flag_break_continue) {
     7233                    smallint fbc = G.flag_break_continue;
     7234                    /* We might fall into outer *loop*,
     7235                     * don't want to break it too */
     7236                    if (loop_top) {
     7237                        G.depth_break_continue--;
     7238                        if (G.depth_break_continue == 0)
     7239                            G.flag_break_continue = 0;
     7240                        /* else: e.g. "continue 2" should *break* once, *then* continue */
     7241                    } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
     7242                    if (G.depth_break_continue != 0 || fbc == BC_BREAK)
     7243                        goto check_jobs_and_break;
     7244                    /* "continue": simulate end of loop */
     7245                    rword = RES_DONE;
     7246                    continue;
     7247                }
     7248#endif
     7249#if ENABLE_HUSH_FUNCTIONS
     7250                if (G.flag_return_in_progress == 1) {
     7251                    /* same as "goto check_jobs_and_break" */
     7252                    checkjobs(NULL);
     7253                    break;
     7254                }
     7255#endif
     7256            } else if (pi->followup == PIPE_BG) {
     7257                /* What does bash do with attempts to background builtins? */
     7258                /* even bash 3.2 doesn't do that well with nested bg:
     7259                 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
     7260                 * I'm NOT treating inner &'s as jobs */
     7261                check_and_run_traps(0);
    19817262#if ENABLE_HUSH_JOB
    1982     /* Example of nested list: "while true; do { sleep 1 | exit 2; } done".
    1983      * We are saving state before entering outermost list ("while...done")
    1984      * so that ctrl-Z will correctly background _entire_ outermost list,
    1985      * not just a part of it (like "sleep 1 | exit 2") */
    1986     if (++run_list_level == 1 && interactive_fd) {
    1987         if (sigsetjmp(toplevel_jb, 1)) {
    1988             /* ctrl-Z forked and we are parent; or ctrl-C.
    1989              * Sighandler has longjmped us here */
    1990             signal(SIGINT, SIG_IGN);
    1991             signal(SIGTSTP, SIG_IGN);
    1992             /* Restore level (we can be coming from deep inside
    1993              * nested levels) */
    1994             run_list_level = 1;
    1995 #if ENABLE_FEATURE_SH_STANDALONE
    1996             if (nofork_save.saved) { /* if save area is valid */
    1997                 debug_printf_jobs("exiting nofork early\n");
    1998                 restore_nofork_data(&nofork_save);
    1999             }
    2000 #endif
    2001             if (ctrl_z_flag) {
    2002                 /* ctrl-Z has forked and stored pid of the child in pi->pid.
    2003                  * Remember this child as background job */
    2004                 insert_bg_job(pi);
     7263                if (G.run_list_level == 1)
     7264                    insert_bg_job(pi);
     7265#endif
     7266                /* Last command's pid goes to $! */
     7267                G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
     7268                G.last_exitcode = rcode = EXIT_SUCCESS;
     7269                debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
    20057270            } else {
    2006                 /* ctrl-C. We just stop doing whatever we were doing */
    2007                 putchar('\n');
    2008             }
    2009             rcode = 0;
    2010             goto ret;
    2011         }
    2012         /* ctrl-Z handler will store pid etc in pi */
    2013         toplevel_list = pi;
    2014         ctrl_z_flag = 0;
    2015 #if ENABLE_FEATURE_SH_STANDALONE
    2016         nofork_save.saved = 0; /* in case we will run a nofork later */
    2017 #endif
    2018         signal_SA_RESTART(SIGTSTP, handler_ctrl_z);
    2019         signal(SIGINT, handler_ctrl_c);
    2020     }
    2021 #endif
    2022 
    2023     for (; pi; pi = flag_restore ? rpipe : pi->next) {
    2024         rword = pi->res_word;
    2025 #if ENABLE_HUSH_LOOPS
    2026         if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) {
    2027             flag_restore = 0;
    2028             if (!rpipe) {
    2029                 flag_rep = 0;
    2030                 rpipe = pi;
    2031             }
    2032         }
    2033 #endif
    2034         debug_printf_exec(": rword=%d if_code=%d next_if_code=%d skip_more=%d\n",
    2035                 rword, if_code, next_if_code, skip_more_for_this_rword);
    2036         if (rword == skip_more_for_this_rword && flag_skip) {
    2037             if (pi->followup == PIPE_SEQ)
    2038                 flag_skip = 0;
    2039             continue;
    2040         }
    2041         flag_skip = 1;
    2042         skip_more_for_this_rword = RES_XXXX;
    2043 #if ENABLE_HUSH_IF
    2044         if (rword == RES_THEN || rword == RES_ELSE)
    2045             if_code = next_if_code;
    2046         if (rword == RES_THEN && if_code)
    2047             continue;
    2048         if (rword == RES_ELSE && !if_code)
    2049             continue;
    2050         if (rword == RES_ELIF && !if_code)
    2051             break;
    2052 #endif
    2053 #if ENABLE_HUSH_LOOPS
    2054         if (rword == RES_FOR && pi->num_progs) {
    2055             if (!for_lcur) {
    2056                 /* if no variable values after "in" we skip "for" */
    2057                 if (!pi->next->progs->argv)
    2058                     continue;
    2059                 /* create list of variable values */
    2060                 for_list = expand_strvec_to_strvec(pi->next->progs->argv);
    2061                 for_lcur = for_list;
    2062                 for_varname = pi->progs->argv[0];
    2063                 pi->progs->argv[0] = NULL;
    2064                 flag_rep = 1;
    2065             }
    2066             free(pi->progs->argv[0]);
    2067             if (!*for_lcur) {
    2068                 free(for_list);
    2069                 for_lcur = NULL;
    2070                 flag_rep = 0;
    2071                 pi->progs->argv[0] = for_varname;
    2072                 pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0];
    2073                 continue;
    2074             }
    2075             /* insert next value from for_lcur */
    2076             /* vda: does it need escaping? */
    2077             pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
    2078             pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0];
    2079         }
    2080         if (rword == RES_IN)
    2081             continue;
    2082         if (rword == RES_DO) {
    2083             if (!flag_rep)
    2084                 continue;
    2085         }
    2086         if (rword == RES_DONE) {
    2087             if (flag_rep) {
    2088                 flag_restore = 1;
    2089             } else {
    2090                 rpipe = NULL;
    2091             }
    2092         }
    2093 #endif
    2094         if (pi->num_progs == 0)
    2095             continue;
    2096         save_num_progs = pi->num_progs; /* save number of programs */
    2097         debug_printf_exec(": run_pipe_real with %d members\n", pi->num_progs);
    2098         rcode = run_pipe_real(pi);
    2099         if (rcode != -1) {
    2100             /* We only ran a builtin: rcode was set by the return value
    2101              * of run_pipe_real(), and we don't need to wait for anything. */
    2102         } else if (pi->followup == PIPE_BG) {
    2103             /* What does bash do with attempts to background builtins? */
    2104             /* Even bash 3.2 doesn't do that well with nested bg:
    2105              * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
    2106              * I'm NOT treating inner &'s as jobs */
    21077271#if ENABLE_HUSH_JOB
    2108             if (run_list_level == 1)
    2109                 insert_bg_job(pi);
    2110 #endif
    2111             rcode = EXIT_SUCCESS;
    2112         } else {
    2113 #if ENABLE_HUSH_JOB
    2114             /* Paranoia, just "interactive_fd" should be enough? */
    2115             if (run_list_level == 1 && interactive_fd) {
    2116                 /* waits for completion, then fg's main shell */
    2117                 rcode = checkjobs_and_fg_shell(pi);
    2118             } else
    2119 #endif
    2120             {
    2121                 /* this one just waits for completion */
    2122                 rcode = checkjobs(pi);
    2123             }
    2124             debug_printf_exec(": checkjobs returned %d\n", rcode);
    2125         }
    2126         debug_printf_exec(": setting last_return_code=%d\n", rcode);
    2127         last_return_code = rcode;
    2128         pi->num_progs = save_num_progs; /* restore number of programs */
     7272                if (G.run_list_level == 1 && G_interactive_fd) {
     7273                    /* Waits for completion, then fg's main shell */
     7274                    rcode = checkjobs_and_fg_shell(pi);
     7275                    debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
     7276                    check_and_run_traps(0);
     7277                } else
     7278#endif
     7279                { /* This one just waits for completion */
     7280                    rcode = checkjobs(pi);
     7281                    debug_printf_exec(": checkjobs exitcode %d\n", rcode);
     7282                    check_and_run_traps(0);
     7283                }
     7284                G.last_exitcode = rcode;
     7285            }
     7286        }
     7287
     7288        /* Analyze how result affects subsequent commands */
    21297289#if ENABLE_HUSH_IF
    21307290        if (rword == RES_IF || rword == RES_ELIF)
    2131             next_if_code = rcode;  /* can be overwritten a number of times */
     7291            cond_code = rcode;
    21327292#endif
    21337293#if ENABLE_HUSH_LOOPS
    2134         if (rword == RES_WHILE)
    2135             flag_rep = !last_return_code;
    2136         if (rword == RES_UNTIL)
    2137             flag_rep = last_return_code;
    2138 #endif
    2139         if ((rcode == EXIT_SUCCESS && pi->followup == PIPE_OR)
    2140          || (rcode != EXIT_SUCCESS && pi->followup == PIPE_AND)
    2141         ) {
    2142             skip_more_for_this_rword = rword;
    2143         }
     7294        /* Beware of "while false; true; do ..."! */
     7295        if (pi->next && pi->next->res_word == RES_DO) {
     7296            if (rword == RES_WHILE) {
     7297                if (rcode) {
     7298                    /* "while false; do...done" - exitcode 0 */
     7299                    G.last_exitcode = rcode = EXIT_SUCCESS;
     7300                    debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
     7301                    goto check_jobs_and_break;
     7302                }
     7303            }
     7304            if (rword == RES_UNTIL) {
     7305                if (!rcode) {
     7306                    debug_printf_exec(": until expr is true: breaking\n");
     7307 check_jobs_and_break:
     7308                    checkjobs(NULL);
     7309                    break;
     7310                }
     7311            }
     7312        }
     7313#endif
     7314
     7315 check_jobs_and_continue:
    21447316        checkjobs(NULL);
    2145     }
     7317    } /* for (pi) */
    21467318
    21477319#if ENABLE_HUSH_JOB
    2148     if (ctrl_z_flag) {
    2149         /* ctrl-Z forked somewhere in the past, we are the child,
    2150          * and now we completed running the list. Exit. */
    2151         exit(rcode);
    2152     }
    2153  ret:
    2154     if (!--run_list_level && interactive_fd) {
    2155         signal(SIGTSTP, SIG_IGN);
    2156         signal(SIGINT, SIG_IGN);
    2157     }
    2158 #endif
    2159     debug_printf_exec("run_list_real lvl %d return %d\n", run_list_level + 1, rcode);
     7320    G.run_list_level--;
     7321#endif
     7322#if ENABLE_HUSH_LOOPS
     7323    if (loop_top)
     7324        G.depth_of_loop--;
     7325    free(for_list);
     7326#endif
     7327#if ENABLE_HUSH_CASE
     7328    free(case_word);
     7329#endif
     7330    debug_leave();
     7331    debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode);
    21607332    return rcode;
    21617333}
    21627334
    2163 /* return code is the exit status of the pipe */
    2164 static int free_pipe(struct pipe *pi, int indent)
    2165 {
    2166     char **p;
    2167     struct child_prog *child;
    2168     struct redir_struct *r, *rnext;
    2169     int a, i, ret_code = 0;
    2170 
    2171     if (pi->stopped_progs > 0)
    2172         return ret_code;
    2173     debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid());
    2174     for (i = 0; i < pi->num_progs; i++) {
    2175         child = &pi->progs[i];
    2176         debug_printf_clean("%s  command %d:\n", indenter(indent), i);
    2177         if (child->argv) {
    2178             for (a = 0, p = child->argv; *p; a++, p++) {
    2179                 debug_printf_clean("%s   argv[%d] = %s\n", indenter(indent), a, *p);
    2180             }
    2181             globfree(&child->glob_result);
    2182             child->argv = NULL;
    2183         } else if (child->group) {
    2184             debug_printf_clean("%s   begin group (subshell:%d)\n", indenter(indent), child->subshell);
    2185             ret_code = free_pipe_list(child->group, indent+3);
    2186             debug_printf_clean("%s   end group\n", indenter(indent));
    2187         } else {
    2188             debug_printf_clean("%s   (nil)\n", indenter(indent));
    2189         }
    2190         for (r = child->redirects; r; r = rnext) {
    2191             debug_printf_clean("%s   redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip);
    2192             if (r->dup == -1) {
    2193                 /* guard against the case >$FOO, where foo is unset or blank */
    2194                 if (r->word.gl_pathv) {
    2195                     debug_printf_clean(" %s\n", *r->word.gl_pathv);
    2196                     globfree(&r->word);
    2197                 }
    2198             } else {
    2199                 debug_printf_clean("&%d\n", r->dup);
    2200             }
    2201             rnext = r->next;
    2202             free(r);
    2203         }
    2204         child->redirects = NULL;
    2205     }
    2206     free(pi->progs);   /* children are an array, they get freed all at once */
    2207     pi->progs = NULL;
     7335/* Select which version we will use */
     7336static int run_and_free_list(struct pipe *pi)
     7337{
     7338    int rcode = 0;
     7339    debug_printf_exec("run_and_free_list entered\n");
     7340    if (!G.n_mode) {
     7341        debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
     7342        rcode = run_list(pi);
     7343    }
     7344    /* free_pipe_list has the side effect of clearing memory.
     7345     * In the long run that function can be merged with run_list,
     7346     * but doing that now would hobble the debugging effort. */
     7347    free_pipe_list(pi);
     7348    debug_printf_exec("run_and_free_list return %d\n", rcode);
     7349    return rcode;
     7350}
     7351
     7352
     7353/* Called a few times only (or even once if "sh -c") */
     7354static void init_sigmasks(void)
     7355{
     7356    unsigned sig;
     7357    unsigned mask;
     7358    sigset_t old_blocked_set;
     7359
     7360    if (!G.inherited_set_is_saved) {
     7361        sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
     7362        G.inherited_set = G.blocked_set;
     7363    }
     7364    old_blocked_set = G.blocked_set;
     7365
     7366    mask = (1 << SIGQUIT);
     7367    if (G_interactive_fd) {
     7368        mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS;
     7369        if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
     7370            mask |= SPECIAL_JOB_SIGS;
     7371    }
     7372    G.non_DFL_mask = mask;
     7373
     7374    sig = 0;
     7375    while (mask) {
     7376        if (mask & 1)
     7377            sigaddset(&G.blocked_set, sig);
     7378        mask >>= 1;
     7379        sig++;
     7380    }
     7381    sigdelset(&G.blocked_set, SIGCHLD);
     7382
     7383    if (memcmp(&old_blocked_set, &G.blocked_set, sizeof(old_blocked_set)) != 0)
     7384        sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
     7385
     7386    /* POSIX allows shell to re-enable SIGCHLD
     7387     * even if it was SIG_IGN on entry */
     7388#if ENABLE_HUSH_FAST
     7389    G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
     7390    if (!G.inherited_set_is_saved)
     7391        signal(SIGCHLD, SIGCHLD_handler);
     7392#else
     7393    if (!G.inherited_set_is_saved)
     7394        signal(SIGCHLD, SIG_DFL);
     7395#endif
     7396
     7397    G.inherited_set_is_saved = 1;
     7398}
     7399
    22087400#if ENABLE_HUSH_JOB
    2209     free(pi->cmdtext);
    2210     pi->cmdtext = NULL;
    2211 #endif
    2212     return ret_code;
    2213 }
    2214 
    2215 static int free_pipe_list(struct pipe *head, int indent)
    2216 {
    2217     int rcode = 0;   /* if list has no members */
    2218     struct pipe *pi, *next;
    2219 
    2220     for (pi = head; pi; pi = next) {
    2221         debug_printf_clean("%s pipe reserved mode %d\n", indenter(indent), pi->res_word);
    2222         rcode = free_pipe(pi, indent);
    2223         debug_printf_clean("%s pipe followup code %d\n", indenter(indent), pi->followup);
    2224         next = pi->next;
    2225         /*pi->next = NULL;*/
    2226         free(pi);
    2227     }
    2228     return rcode;
    2229 }
    2230 
    2231 /* Select which version we will use */
    2232 static int run_list(struct pipe *pi)
    2233 {
    2234     int rcode = 0;
    2235     debug_printf_exec("run_list entered\n");
    2236     if (fake_mode == 0) {
    2237         debug_printf_exec(": run_list_real with %d members\n", pi->num_progs);
    2238         rcode = run_list_real(pi);
    2239     }
    2240     /* free_pipe_list has the side effect of clearing memory.
    2241      * In the long run that function can be merged with run_list_real,
    2242      * but doing that now would hobble the debugging effort. */
    2243     free_pipe_list(pi, 0);
    2244     debug_printf_exec("run_list return %d\n", rcode);
    2245     return rcode;
    2246 }
    2247 
    2248 /* The API for glob is arguably broken.  This routine pushes a non-matching
    2249  * string into the output structure, removing non-backslashed backslashes.
    2250  * If someone can prove me wrong, by performing this function within the
    2251  * original glob(3) api, feel free to rewrite this routine into oblivion.
    2252  * Return code (0 vs. GLOB_NOSPACE) matches glob(3).
    2253  * XXX broken if the last character is '\\', check that before calling.
    2254  */
    2255 static int globhack(const char *src, int flags, glob_t *pglob)
    2256 {
    2257     int cnt = 0, pathc;
    2258     const char *s;
    2259     char *dest;
    2260     for (cnt = 1, s = src; s && *s; s++) {
    2261         if (*s == '\\') s++;
    2262         cnt++;
    2263     }
    2264     dest = xmalloc(cnt);
    2265     if (!(flags & GLOB_APPEND)) {
    2266         pglob->gl_pathv = NULL;
    2267         pglob->gl_pathc = 0;
    2268         pglob->gl_offs = 0;
    2269         pglob->gl_offs = 0;
    2270     }
    2271     pathc = ++pglob->gl_pathc;
    2272     pglob->gl_pathv = xrealloc(pglob->gl_pathv, (pathc+1) * sizeof(*pglob->gl_pathv));
    2273     pglob->gl_pathv[pathc-1] = dest;
    2274     pglob->gl_pathv[pathc] = NULL;
    2275     for (s = src; s && *s; s++, dest++) {
    2276         if (*s == '\\') s++;
    2277         *dest = *s;
    2278     }
    2279     *dest = '\0';
    2280     return 0;
    2281 }
    2282 
    2283 /* XXX broken if the last character is '\\', check that before calling */
    2284 static int glob_needed(const char *s)
    2285 {
    2286     for (; *s; s++) {
    2287         if (*s == '\\') s++;
    2288         if (strchr("*[?", *s)) return 1;
    2289     }
    2290     return 0;
    2291 }
    2292 
    2293 static int xglob(o_string *dest, int flags, glob_t *pglob)
    2294 {
    2295     int gr;
    2296 
    2297     /* short-circuit for null word */
    2298     /* we can code this better when the debug_printf's are gone */
    2299     if (dest->length == 0) {
    2300         if (dest->nonnull) {
    2301             /* bash man page calls this an "explicit" null */
    2302             gr = globhack(dest->data, flags, pglob);
    2303             debug_printf("globhack returned %d\n", gr);
    2304         } else {
    2305             return 0;
    2306         }
    2307     } else if (glob_needed(dest->data)) {
    2308         gr = glob(dest->data, flags, NULL, pglob);
    2309         debug_printf("glob returned %d\n", gr);
    2310         if (gr == GLOB_NOMATCH) {
    2311             /* quote removal, or more accurately, backslash removal */
    2312             gr = globhack(dest->data, flags, pglob);
    2313             debug_printf("globhack returned %d\n", gr);
    2314         }
    2315     } else {
    2316         gr = globhack(dest->data, flags, pglob);
    2317         debug_printf("globhack returned %d\n", gr);
    2318     }
    2319     if (gr == GLOB_NOSPACE)
    2320         bb_error_msg_and_die("out of memory during glob");
    2321     if (gr != 0) { /* GLOB_ABORTED ? */
    2322         bb_error_msg("glob(3) error %d", gr);
    2323     }
    2324     /* globprint(glob_target); */
    2325     return gr;
    2326 }
    2327 
    2328 /* expand_strvec_to_strvec() takes a list of strings, expands
    2329  * all variable references within and returns a pointer to
    2330  * a list of expanded strings, possibly with larger number
    2331  * of strings. (Think VAR="a b"; echo $VAR).
    2332  * This new list is allocated as a single malloc block.
    2333  * NULL-terminated list of char* pointers is at the beginning of it,
    2334  * followed by strings themself.
    2335  * Caller can deallocate entire list by single free(list). */
    2336 
    2337 /* Helpers first:
    2338  * count_XXX estimates size of the block we need. It's okay
    2339  * to over-estimate sizes a bit, if it makes code simpler */
    2340 static int count_ifs(const char *str)
    2341 {
    2342     int cnt = 0;
    2343     debug_printf_expand("count_ifs('%s') ifs='%s'", str, ifs);
    2344     while (1) {
    2345         str += strcspn(str, ifs);
    2346         if (!*str) break;
    2347         str++; /* str += strspn(str, ifs); */
    2348         cnt++; /* cnt += strspn(str, ifs); - but this code is larger */
    2349     }
    2350     debug_printf_expand(" return %d\n", cnt);
    2351     return cnt;
    2352 }
    2353 
    2354 static void count_var_expansion_space(int *countp, int *lenp, char *arg)
    2355 {
    2356     char first_ch;
    2357     int i;
    2358     int len = *lenp;
    2359     int count = *countp;
    2360     const char *val;
    2361     char *p;
    2362 
    2363     while ((p = strchr(arg, SPECIAL_VAR_SYMBOL))) {
    2364         len += p - arg;
    2365         arg = ++p;
    2366         p = strchr(p, SPECIAL_VAR_SYMBOL);
    2367         first_ch = arg[0];
    2368 
    2369         switch (first_ch & 0x7f) {
    2370         /* high bit in 1st_ch indicates that var is double-quoted */
    2371         case '$': /* pid */
    2372         case '!': /* bg pid */
    2373         case '?': /* exitcode */
    2374         case '#': /* argc */
    2375             len += sizeof(int)*3 + 1; /* enough for int */
     7401/* helper */
     7402static void maybe_set_to_sigexit(int sig)
     7403{
     7404    void (*handler)(int);
     7405    /* non_DFL_mask'ed signals are, well, masked,
     7406     * no need to set handler for them.
     7407     */
     7408    if (!((G.non_DFL_mask >> sig) & 1)) {
     7409        handler = signal(sig, sigexit);
     7410        if (handler == SIG_IGN) /* oops... restore back to IGN! */
     7411            signal(sig, handler);
     7412    }
     7413}
     7414/* Set handlers to restore tty pgrp and exit */
     7415static void set_fatal_handlers(void)
     7416{
     7417    /* We _must_ restore tty pgrp on fatal signals */
     7418    if (HUSH_DEBUG) {
     7419        maybe_set_to_sigexit(SIGILL );
     7420        maybe_set_to_sigexit(SIGFPE );
     7421        maybe_set_to_sigexit(SIGBUS );
     7422        maybe_set_to_sigexit(SIGSEGV);
     7423        maybe_set_to_sigexit(SIGTRAP);
     7424    } /* else: hush is perfect. what SEGV? */
     7425    maybe_set_to_sigexit(SIGABRT);
     7426    /* bash 3.2 seems to handle these just like 'fatal' ones */
     7427    maybe_set_to_sigexit(SIGPIPE);
     7428    maybe_set_to_sigexit(SIGALRM);
     7429    /* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked.
     7430     * if we aren't interactive... but in this case
     7431     * we never want to restore pgrp on exit, and this fn is not called */
     7432    /*maybe_set_to_sigexit(SIGHUP );*/
     7433    /*maybe_set_to_sigexit(SIGTERM);*/
     7434    /*maybe_set_to_sigexit(SIGINT );*/
     7435}
     7436#endif
     7437
     7438static int set_mode(int state, char mode, const char *o_opt)
     7439{
     7440    int idx;
     7441    switch (mode) {
     7442    case 'n':
     7443        G.n_mode = state;
     7444        break;
     7445    case 'x':
     7446        IF_HUSH_MODE_X(G_x_mode = state;)
     7447        break;
     7448    case 'o':
     7449        if (!o_opt) {
     7450            /* "set -+o" without parameter.
     7451             * in bash, set -o produces this output:
     7452             *  pipefail        off
     7453             * and set +o:
     7454             *  set +o pipefail
     7455             * We always use the second form.
     7456             */
     7457            const char *p = o_opt_strings;
     7458            idx = 0;
     7459            while (*p) {
     7460                printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
     7461                idx++;
     7462                p += strlen(p) + 1;
     7463            }
    23767464            break;
    2377         case '*':
    2378         case '@':
    2379             for (i = 1; i < global_argc; i++) {
    2380                 len += strlen(global_argv[i]) + 1;
    2381                 count++;
    2382                 if (!(first_ch & 0x80))
    2383                     count += count_ifs(global_argv[i]);
    2384             }
     7465        }
     7466        idx = index_in_strings(o_opt_strings, o_opt);
     7467        if (idx >= 0) {
     7468            G.o_opt[idx] = state;
    23857469            break;
    2386         default:
    2387             *p = '\0';
    2388             arg[0] = first_ch & 0x7f;
    2389             if (isdigit(arg[0])) {
    2390                 i = xatoi_u(arg);
    2391                 val = NULL;
    2392                 if (i < global_argc)
    2393                     val = global_argv[i];
    2394             } else
    2395                 val = lookup_param(arg);
    2396             arg[0] = first_ch;
    2397             *p = SPECIAL_VAR_SYMBOL;
    2398 
    2399             if (val) {
    2400                 len += strlen(val) + 1;
    2401                 if (!(first_ch & 0x80))
    2402                     count += count_ifs(val);
    2403             }
    2404         }
    2405         arg = ++p;
    2406     }
    2407 
    2408     len += strlen(arg) + 1;
    2409     count++;
    2410     *lenp = len;
    2411     *countp = count;
    2412 }
    2413 
    2414 /* Store given string, finalizing the word and starting new one whenever
    2415  * we encounter ifs char(s). This is used for expanding variable values.
    2416  * End-of-string does NOT finalize word: think about 'echo -$VAR-' */
    2417 static int expand_on_ifs(char **list, int n, char **posp, const char *str)
    2418 {
    2419     char *pos = *posp;
    2420     while (1) {
    2421         int word_len = strcspn(str, ifs);
    2422         if (word_len) {
    2423             memcpy(pos, str, word_len); /* store non-ifs chars */
    2424             pos += word_len;
    2425             str += word_len;
    2426         }
    2427         if (!*str)  /* EOL - do not finalize word */
    2428             break;
    2429         *pos++ = '\0';
    2430         if (n) debug_printf_expand("expand_on_ifs finalized list[%d]=%p '%s' "
    2431             "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
    2432             strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
    2433         list[n++] = pos;
    2434         str += strspn(str, ifs); /* skip ifs chars */
    2435     }
    2436     *posp = pos;
    2437     return n;
    2438 }
    2439 
    2440 /* Expand all variable references in given string, adding words to list[]
    2441  * at n, n+1,... positions. Return updated n (so that list[n] is next one
    2442  * to be filled). This routine is extremely tricky: has to deal with
    2443  * variables/parameters with whitespace, $* and $@, and constructs like
    2444  * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
    2445 /* NB: another bug is that we cannot detect empty strings yet:
    2446  * "" or $empty"" expands to zero words, has to expand to empty word */
    2447 static int expand_vars_to_list(char **list, int n, char **posp, char *arg, char or_mask)
    2448 {
    2449     /* or_mask is either 0 (normal case) or 0x80
    2450      * (expansion of right-hand side of assignment == 1-element expand) */
    2451 
    2452     char first_ch, ored_ch;
    2453     int i;
    2454     const char *val;
    2455     char *p;
    2456     char *pos = *posp;
    2457 
    2458     ored_ch = 0;
    2459 
    2460     if (n) debug_printf_expand("expand_vars_to_list finalized list[%d]=%p '%s' "
    2461         "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
    2462         strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
    2463     list[n++] = pos;
    2464 
    2465     while ((p = strchr(arg, SPECIAL_VAR_SYMBOL))) {
    2466         memcpy(pos, arg, p - arg);
    2467         pos += (p - arg);
    2468         arg = ++p;
    2469         p = strchr(p, SPECIAL_VAR_SYMBOL);
    2470 
    2471         first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */
    2472         ored_ch |= first_ch;
    2473         val = NULL;
    2474         switch (first_ch & 0x7f) {
    2475         /* Highest bit in first_ch indicates that var is double-quoted */
    2476         case '$': /* pid */
    2477             /* FIXME: (echo $$) should still print pid of main shell */
    2478             val = utoa(getpid());
    2479             break;
    2480         case '!': /* bg pid */
    2481             val = last_bg_pid ? utoa(last_bg_pid) : (char*)"";
    2482             break;
    2483         case '?': /* exitcode */
    2484             val = utoa(last_return_code);
    2485             break;
    2486         case '#': /* argc */
    2487             val = utoa(global_argc ? global_argc-1 : 0);
    2488             break;
    2489         case '*':
    2490         case '@':
    2491             i = 1;
    2492             if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
    2493                 while (i < global_argc) {
    2494                     n = expand_on_ifs(list, n, &pos, global_argv[i]);
    2495                     debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, global_argc-1);
    2496                     if (global_argv[i++][0] && i < global_argc) {
    2497                         /* this argv[] is not empty and not last:
    2498                          * put terminating NUL, start new word */
    2499                         *pos++ = '\0';
    2500                         if (n) debug_printf_expand("expand_vars_to_list 2 finalized list[%d]=%p '%s' "
    2501                             "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
    2502                             strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
    2503                         list[n++] = pos;
    2504                     }
    2505                 }
    2506             } else
    2507             /* If or_mask is nonzero, we handle assignment 'a=....$@.....'
    2508              * and in this case should theat it like '$*' */
    2509             if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */
    2510                 while (1) {
    2511                     strcpy(pos, global_argv[i]);
    2512                     pos += strlen(global_argv[i]);
    2513                     if (++i >= global_argc)
    2514                         break;
    2515                     *pos++ = '\0';
    2516                     if (n) debug_printf_expand("expand_vars_to_list 3 finalized list[%d]=%p '%s' "
    2517                         "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
    2518                             strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
    2519                     list[n++] = pos;
    2520                 }
    2521             } else { /* quoted $*: add as one word */
    2522                 while (1) {
    2523                     strcpy(pos, global_argv[i]);
    2524                     pos += strlen(global_argv[i]);
    2525                     if (++i >= global_argc)
    2526                         break;
    2527                     if (ifs[0])
    2528                         *pos++ = ifs[0];
    2529                 }
    2530             }
    2531             break;
    2532         default:
    2533             *p = '\0';
    2534             arg[0] = first_ch & 0x7f;
    2535             if (isdigit(arg[0])) {
    2536                 i = xatoi_u(arg);
    2537                 val = NULL;
    2538                 if (i < global_argc)
    2539                     val = global_argv[i];
    2540             } else
    2541                 val = lookup_param(arg);
    2542             arg[0] = first_ch;
    2543             *p = SPECIAL_VAR_SYMBOL;
    2544             if (!(first_ch & 0x80)) { /* unquoted $VAR */
    2545                 if (val) {
    2546                     n = expand_on_ifs(list, n, &pos, val);
    2547                     val = NULL;
    2548                 }
    2549             } /* else: quoted $VAR, val will be appended at pos */
    2550         }
    2551         if (val) {
    2552             strcpy(pos, val);
    2553             pos += strlen(val);
    2554         }
    2555         arg = ++p;
    2556     }
    2557     debug_printf_expand("expand_vars_to_list adding tail '%s' at %p\n", arg, pos);
    2558     strcpy(pos, arg);
    2559     pos += strlen(arg) + 1;
    2560     if (pos == list[n-1] + 1) { /* expansion is empty */
    2561         if (!(ored_ch & 0x80)) { /* all vars were not quoted... */
    2562             debug_printf_expand("expand_vars_to_list list[%d] empty, going back\n", n);
    2563             pos--;
    2564             n--;
    2565         }
    2566     }
    2567 
    2568     *posp = pos;
    2569     return n;
    2570 }
    2571 
    2572 static char **expand_variables(char **argv, char or_mask)
    2573 {
    2574     int n;
    2575     int count = 1;
    2576     int len = 0;
    2577     char *pos, **v, **list;
    2578 
    2579     v = argv;
    2580     if (!*v) debug_printf_expand("count_var_expansion_space: "
    2581             "argv[0]=NULL count=%d len=%d alloc_space=%d\n",
    2582             count, len, sizeof(char*) * count + len);
    2583     while (*v) {
    2584         count_var_expansion_space(&count, &len, *v);
    2585         debug_printf_expand("count_var_expansion_space: "
    2586             "'%s' count=%d len=%d alloc_space=%d\n",
    2587             *v, count, len, sizeof(char*) * count + len);
    2588         v++;
    2589     }
    2590     len += sizeof(char*) * count; /* total to alloc */
    2591     list = xmalloc(len);
    2592     pos = (char*)(list + count);
    2593     debug_printf_expand("list=%p, list[0] should be %p\n", list, pos);
    2594     n = 0;
    2595     v = argv;
    2596     while (*v)
    2597         n = expand_vars_to_list(list, n, &pos, *v++, or_mask);
    2598 
    2599     if (n) debug_printf_expand("finalized list[%d]=%p '%s' "
    2600         "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1],
    2601         strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos);
    2602     list[n] = NULL;
    2603 
    2604 #ifdef DEBUG_EXPAND
    2605     {
    2606         int m = 0;
    2607         while (m <= n) {
    2608             debug_printf_expand("list[%d]=%p '%s'\n", m, list[m], list[m]);
    2609             m++;
    2610         }
    2611         debug_printf_expand("used_space=%d\n", pos - (char*)list);
    2612     }
    2613 #endif
    2614     if (ENABLE_HUSH_DEBUG)
    2615         if (pos - (char*)list > len)
    2616             bb_error_msg_and_die("BUG in varexp");
    2617     return list;
    2618 }
    2619 
    2620 static char **expand_strvec_to_strvec(char **argv)
    2621 {
    2622     return expand_variables(argv, 0);
    2623 }
    2624 
    2625 static char *expand_string_to_string(const char *str)
    2626 {
    2627     char *argv[2], **list;
    2628 
    2629     argv[0] = (char*)str;
    2630     argv[1] = NULL;
    2631     list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */
    2632     if (ENABLE_HUSH_DEBUG)
    2633         if (!list[0] || list[1])
    2634             bb_error_msg_and_die("BUG in varexp2");
    2635     /* actually, just move string 2*sizeof(char*) bytes back */
    2636     strcpy((char*)list, list[0]);
    2637     debug_printf_expand("string_to_string='%s'\n", (char*)list);
    2638     return (char*)list;
    2639 }
    2640 
    2641 static char* expand_strvec_to_string(char **argv)
    2642 {
    2643     char **list;
    2644 
    2645     list = expand_variables(argv, 0x80);
    2646     /* Convert all NULs to spaces */
    2647     if (list[0]) {
    2648         int n = 1;
    2649         while (list[n]) {
    2650             if (ENABLE_HUSH_DEBUG)
    2651                 if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
    2652                     bb_error_msg_and_die("BUG in varexp3");
    2653             list[n][-1] = ' '; /* TODO: or to ifs[0]? */
    2654             n++;
    2655         }
    2656     }
    2657     strcpy((char*)list, list[0]);
    2658     debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
    2659     return (char*)list;
    2660 }
    2661 
    2662 /* This is used to get/check local shell variables */
    2663 static struct variable *get_local_var(const char *name)
    2664 {
    2665     struct variable *cur;
    2666     int len;
    2667 
    2668     if (!name)
    2669         return NULL;
    2670     len = strlen(name);
    2671     for (cur = top_var; cur; cur = cur->next) {
    2672         if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
    2673             return cur;
    2674     }
    2675     return NULL;
    2676 }
    2677 
    2678 /* str holds "NAME=VAL" and is expected to be malloced.
    2679  * We take ownership of it. */
    2680 static int set_local_var(char *str, int flg_export)
    2681 {
    2682     struct variable *cur;
    2683     char *value;
    2684     int name_len;
    2685 
    2686     value = strchr(str, '=');
    2687     if (!value) { /* not expected to ever happen? */
    2688         free(str);
    2689         return -1;
    2690     }
    2691 
    2692     name_len = value - str + 1; /* including '=' */
    2693     cur = top_var; /* cannot be NULL (we have HUSH_VERSION and it's RO) */
    2694     while (1) {
    2695         if (strncmp(cur->varstr, str, name_len) != 0) {
    2696             if (!cur->next) {
    2697                 /* Bail out. Note that now cur points
    2698                  * to last var in linked list */
    2699                 break;
    2700             }
    2701             cur = cur->next;
    2702             continue;
    2703         }
    2704         /* We found an existing var with this name */
    2705         *value = '\0';
    2706         if (cur->flg_read_only) {
    2707             bb_error_msg("%s: readonly variable", str);
    2708             free(str);
    2709             return -1;
    2710         }
    2711         unsetenv(str); /* just in case */
    2712         *value = '=';
    2713         if (strcmp(cur->varstr, str) == 0) {
    2714  free_and_exp:
    2715             free(str);
    2716             goto exp;
    2717         }
    2718         if (cur->max_len >= strlen(str)) {
    2719             /* This one is from startup env, reuse space */
    2720             strcpy(cur->varstr, str);
    2721             goto free_and_exp;
    2722         }
    2723         /* max_len == 0 signifies "malloced" var, which we can
    2724          * (and has to) free */
    2725         if (!cur->max_len)
    2726             free(cur->varstr);
    2727         cur->max_len = 0;
    2728         goto set_str_and_exp;
    2729     }
    2730 
    2731     /* Not found - create next variable struct */
    2732     cur->next = xzalloc(sizeof(*cur));
    2733     cur = cur->next;
    2734 
    2735  set_str_and_exp:
    2736     cur->varstr = str;
    2737  exp:
    2738     if (flg_export)
    2739         cur->flg_export = 1;
    2740     if (cur->flg_export)
    2741         return putenv(cur->varstr);
    2742     return 0;
    2743 }
    2744 
    2745 static void unset_local_var(const char *name)
    2746 {
    2747     struct variable *cur;
    2748     struct variable *prev = prev; /* for gcc */
    2749     int name_len;
    2750 
    2751     if (!name)
    2752         return;
    2753     name_len = strlen(name);
    2754     cur = top_var;
    2755     while (cur) {
    2756         if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
    2757             if (cur->flg_read_only) {
    2758                 bb_error_msg("%s: readonly variable", name);
    2759                 return;
    2760             }
    2761         /* prev is ok to use here because 1st variable, HUSH_VERSION,
    2762          * is ro, and we cannot reach this code on the 1st pass */
    2763             prev->next = cur->next;
    2764             unsetenv(cur->varstr);
    2765             if (!cur->max_len)
    2766                 free(cur->varstr);
    2767             free(cur);
    2768             return;
    2769         }
    2770         prev = cur;
    2771         cur = cur->next;
    2772     }
    2773 }
    2774 
    2775 static int is_assignment(const char *s)
    2776 {
    2777     if (!s || !isalpha(*s))
    2778         return 0;
    2779     s++;
    2780     while (isalnum(*s) || *s == '_')
    2781         s++;
    2782     return *s == '=';
    2783 }
    2784 
    2785 /* the src parameter allows us to peek forward to a possible &n syntax
    2786  * for file descriptor duplication, e.g., "2>&1".
    2787  * Return code is 0 normally, 1 if a syntax error is detected in src.
    2788  * Resource errors (in xmalloc) cause the process to exit */
    2789 static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
    2790     struct in_str *input)
    2791 {
    2792     struct child_prog *child = ctx->child;
    2793     struct redir_struct *redir = child->redirects;
    2794     struct redir_struct *last_redir = NULL;
    2795 
    2796     /* Create a new redir_struct and drop it onto the end of the linked list */
    2797     while (redir) {
    2798         last_redir = redir;
    2799         redir = redir->next;
    2800     }
    2801     redir = xmalloc(sizeof(struct redir_struct));
    2802     redir->next = NULL;
    2803     redir->word.gl_pathv = NULL;
    2804     if (last_redir) {
    2805         last_redir->next = redir;
    2806     } else {
    2807         child->redirects = redir;
    2808     }
    2809 
    2810     redir->type = style;
    2811     redir->fd = (fd == -1) ? redir_table[style].default_fd : fd;
    2812 
    2813     debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip);
    2814 
    2815     /* Check for a '2>&1' type redirect */
    2816     redir->dup = redirect_dup_num(input);
    2817     if (redir->dup == -2) return 1;  /* syntax error */
    2818     if (redir->dup != -1) {
    2819         /* Erik had a check here that the file descriptor in question
    2820          * is legit; I postpone that to "run time"
    2821          * A "-" representation of "close me" shows up as a -3 here */
    2822         debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
    2823     } else {
    2824         /* We do _not_ try to open the file that src points to,
    2825          * since we need to return and let src be expanded first.
    2826          * Set ctx->pending_redirect, so we know what to do at the
    2827          * end of the next parsed word. */
    2828         ctx->pending_redirect = redir;
    2829     }
    2830     return 0;
    2831 }
    2832 
    2833 static struct pipe *new_pipe(void)
    2834 {
    2835     struct pipe *pi;
    2836     pi = xzalloc(sizeof(struct pipe));
    2837     /*pi->num_progs = 0;*/
    2838     /*pi->progs = NULL;*/
    2839     /*pi->next = NULL;*/
    2840     /*pi->followup = 0;  invalid */
    2841     if (RES_NONE)
    2842         pi->res_word = RES_NONE;
    2843     return pi;
    2844 }
    2845 
    2846 static void initialize_context(struct p_context *ctx)
    2847 {
    2848     ctx->child = NULL;
    2849     ctx->pipe = ctx->list_head = new_pipe();
    2850     ctx->pending_redirect = NULL;
    2851     ctx->res_w = RES_NONE;
    2852     //only ctx->parse_type is not touched... is this intentional?
    2853     ctx->old_flag = 0;
    2854     ctx->stack = NULL;
    2855     done_command(ctx);   /* creates the memory for working child */
    2856 }
    2857 
    2858 /* normal return is 0
    2859  * if a reserved word is found, and processed, return 1
    2860  * should handle if, then, elif, else, fi, for, while, until, do, done.
    2861  * case, function, and select are obnoxious, save those for later.
    2862  */
    2863 #if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS
    2864 static int reserved_word(o_string *dest, struct p_context *ctx)
    2865 {
    2866     struct reserved_combo {
    2867         char literal[7];
    2868         unsigned char code;
    2869         int flag;
    2870     };
    2871     /* Mostly a list of accepted follow-up reserved words.
    2872      * FLAG_END means we are done with the sequence, and are ready
    2873      * to turn the compound list into a command.
    2874      * FLAG_START means the word must start a new compound list.
    2875      */
    2876     static const struct reserved_combo reserved_list[] = {
    2877 #if ENABLE_HUSH_IF
    2878         { "if",    RES_IF,    FLAG_THEN | FLAG_START },
    2879         { "then",  RES_THEN,  FLAG_ELIF | FLAG_ELSE | FLAG_FI },
    2880         { "elif",  RES_ELIF,  FLAG_THEN },
    2881         { "else",  RES_ELSE,  FLAG_FI   },
    2882         { "fi",    RES_FI,    FLAG_END  },
    2883 #endif
    2884 #if ENABLE_HUSH_LOOPS
    2885         { "for",   RES_FOR,   FLAG_IN   | FLAG_START },
    2886         { "while", RES_WHILE, FLAG_DO   | FLAG_START },
    2887         { "until", RES_UNTIL, FLAG_DO   | FLAG_START },
    2888         { "in",    RES_IN,    FLAG_DO   },
    2889         { "do",    RES_DO,    FLAG_DONE },
    2890         { "done",  RES_DONE,  FLAG_END  }
    2891 #endif
    2892     };
    2893 
    2894     const struct reserved_combo *r;
    2895 
    2896     for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
    2897         if (strcmp(dest->data, r->literal) != 0)
    2898             continue;
    2899         debug_printf("found reserved word %s, code %d\n", r->literal, r->code);
    2900         if (r->flag & FLAG_START) {
    2901             struct p_context *new;
    2902             debug_printf("push stack\n");
    2903 #if ENABLE_HUSH_LOOPS
    2904             if (ctx->res_w == RES_IN || ctx->res_w == RES_FOR) {
    2905                 syntax("malformed for"); /* example: 'for if' */
    2906                 ctx->res_w = RES_SNTX;
    2907                 b_reset(dest);
    2908                 return 1;
    2909             }
    2910 #endif
    2911             new = xmalloc(sizeof(*new));
    2912             *new = *ctx;   /* physical copy */
    2913             initialize_context(ctx);
    2914             ctx->stack = new;
    2915         } else if (ctx->res_w == RES_NONE || !(ctx->old_flag & (1 << r->code))) {
    2916             syntax(NULL);
    2917             ctx->res_w = RES_SNTX;
    2918             b_reset(dest);
    2919             return 1;
    2920         }
    2921         ctx->res_w = r->code;
    2922         ctx->old_flag = r->flag;
    2923         if (ctx->old_flag & FLAG_END) {
    2924             struct p_context *old;
    2925             debug_printf("pop stack\n");
    2926             done_pipe(ctx, PIPE_SEQ);
    2927             old = ctx->stack;
    2928             old->child->group = ctx->list_head;
    2929             old->child->subshell = 0;
    2930             *ctx = *old;   /* physical copy */
    2931             free(old);
    2932         }
    2933         b_reset(dest);
    2934         return 1;
    2935     }
    2936     return 0;
    2937 }
    2938 #else
    2939 #define reserved_word(dest, ctx) ((int)0)
    2940 #endif
    2941 
    2942 /* Normal return is 0.
    2943  * Syntax or xglob errors return 1. */
    2944 static int done_word(o_string *dest, struct p_context *ctx)
    2945 {
    2946     struct child_prog *child = ctx->child;
    2947     glob_t *glob_target;
    2948     int gr, flags = 0;
    2949 
    2950     debug_printf_parse("done_word entered: '%s' %p\n", dest->data, child);
    2951     if (dest->length == 0 && !dest->nonnull) {
    2952         debug_printf_parse("done_word return 0: true null, ignored\n");
    2953         return 0;
    2954     }
    2955     if (ctx->pending_redirect) {
    2956         glob_target = &ctx->pending_redirect->word;
    2957     } else {
    2958         if (child->group) {
    2959             syntax(NULL);
    2960             debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n");
    2961             return 1;
    2962         }
    2963         if (!child->argv && (ctx->parse_type & PARSEFLAG_SEMICOLON)) {
    2964             debug_printf_parse(": checking '%s' for reserved-ness\n", dest->data);
    2965             if (reserved_word(dest, ctx)) {
    2966                 debug_printf_parse("done_word return %d\n", (ctx->res_w == RES_SNTX));
    2967                 return (ctx->res_w == RES_SNTX);
    2968             }
    2969         }
    2970         glob_target = &child->glob_result;
    2971         if (child->argv)
    2972             flags |= GLOB_APPEND;
    2973     }
    2974     gr = xglob(dest, flags, glob_target);
    2975     if (gr != 0) {
    2976         debug_printf_parse("done_word return 1: xglob returned %d\n", gr);
    2977         return 1;
    2978     }
    2979 
    2980     b_reset(dest);
    2981     if (ctx->pending_redirect) {
    2982         ctx->pending_redirect = NULL;
    2983         if (glob_target->gl_pathc != 1) {
    2984             bb_error_msg("ambiguous redirect");
    2985             debug_printf_parse("done_word return 1: ambiguous redirect\n");
    2986             return 1;
    2987         }
    2988     } else {
    2989         child->argv = glob_target->gl_pathv;
    2990     }
    2991 #if ENABLE_HUSH_LOOPS
    2992     if (ctx->res_w == RES_FOR) {
    2993         done_word(dest, ctx);
    2994         done_pipe(ctx, PIPE_SEQ);
    2995     }
    2996 #endif
    2997     debug_printf_parse("done_word return 0\n");
    2998     return 0;
    2999 }
    3000 
    3001 /* The only possible error here is out of memory, in which case
    3002  * xmalloc exits. */
    3003 static int done_command(struct p_context *ctx)
    3004 {
    3005     /* The child is really already in the pipe structure, so
    3006      * advance the pipe counter and make a new, null child. */
    3007     struct pipe *pi = ctx->pipe;
    3008     struct child_prog *child = ctx->child;
    3009 
    3010     if (child) {
    3011         if (child->group == NULL
    3012          && child->argv == NULL
    3013          && child->redirects == NULL
    3014         ) {
    3015             debug_printf_parse("done_command: skipping null cmd, num_progs=%d\n", pi->num_progs);
    3016             return pi->num_progs;
    3017         }
    3018         pi->num_progs++;
    3019         debug_printf_parse("done_command: ++num_progs=%d\n", pi->num_progs);
    3020     } else {
    3021         debug_printf_parse("done_command: initializing, num_progs=%d\n", pi->num_progs);
    3022     }
    3023 
    3024     /* Only real trickiness here is that the uncommitted
    3025      * child structure is not counted in pi->num_progs. */
    3026     pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
    3027     child = &pi->progs[pi->num_progs];
    3028 
    3029     memset(child, 0, sizeof(*child));
    3030     /*child->redirects = NULL;*/
    3031     /*child->argv = NULL;*/
    3032     /*child->is_stopped = 0;*/
    3033     /*child->group = NULL;*/
    3034     /*child->glob_result.gl_pathv = NULL;*/
    3035     child->family = pi;
    3036     //sp: /*child->sp = 0;*/
    3037     //pt: child->parse_type = ctx->parse_type;
    3038 
    3039     ctx->child = child;
    3040     /* but ctx->pipe and ctx->list_head remain unchanged */
    3041 
    3042     return pi->num_progs; /* used only for 0/nonzero check */
    3043 }
    3044 
    3045 static int done_pipe(struct p_context *ctx, pipe_style type)
    3046 {
    3047     struct pipe *new_p;
    3048     int not_null;
    3049 
    3050     debug_printf_parse("done_pipe entered, followup %d\n", type);
    3051     not_null = done_command(ctx);  /* implicit closure of previous command */
    3052     ctx->pipe->followup = type;
    3053     ctx->pipe->res_word = ctx->res_w;
    3054     /* Without this check, even just <enter> on command line generates
    3055      * tree of three NOPs (!). Which is harmless but annoying.
    3056      * IOW: it is safe to do it unconditionally. */
    3057     if (not_null) {
    3058         new_p = new_pipe();
    3059         ctx->pipe->next = new_p;
    3060         ctx->pipe = new_p;
    3061         ctx->child = NULL;
    3062         done_command(ctx);  /* set up new pipe to accept commands */
    3063     }
    3064     debug_printf_parse("done_pipe return 0\n");
    3065     return 0;
    3066 }
    3067 
    3068 /* peek ahead in the in_str to find out if we have a "&n" construct,
    3069  * as in "2>&1", that represents duplicating a file descriptor.
    3070  * returns either -2 (syntax error), -1 (no &), or the number found.
    3071  */
    3072 static int redirect_dup_num(struct in_str *input)
    3073 {
    3074     int ch, d = 0, ok = 0;
    3075     ch = b_peek(input);
    3076     if (ch != '&') return -1;
    3077 
    3078     b_getch(input);  /* get the & */
    3079     ch = b_peek(input);
    3080     if (ch == '-') {
    3081         b_getch(input);
    3082         return -3;  /* "-" represents "close me" */
    3083     }
    3084     while (isdigit(ch)) {
    3085         d = d*10 + (ch-'0');
    3086         ok = 1;
    3087         b_getch(input);
    3088         ch = b_peek(input);
    3089     }
    3090     if (ok) return d;
    3091 
    3092     bb_error_msg("ambiguous redirect");
    3093     return -2;
    3094 }
    3095 
    3096 /* If a redirect is immediately preceded by a number, that number is
    3097  * supposed to tell which file descriptor to redirect.  This routine
    3098  * looks for such preceding numbers.  In an ideal world this routine
    3099  * needs to handle all the following classes of redirects...
    3100  *     echo 2>foo     # redirects fd  2 to file "foo", nothing passed to echo
    3101  *     echo 49>foo    # redirects fd 49 to file "foo", nothing passed to echo
    3102  *     echo -2>foo    # redirects fd  1 to file "foo",    "-2" passed to echo
    3103  *     echo 49x>foo   # redirects fd  1 to file "foo",   "49x" passed to echo
    3104  * A -1 output from this program means no valid number was found, so the
    3105  * caller should use the appropriate default for this redirection.
    3106  */
    3107 static int redirect_opt_num(o_string *o)
    3108 {
    3109     int num;
    3110 
    3111     if (o->length == 0)
    3112         return -1;
    3113     for (num = 0; num < o->length; num++) {
    3114         if (!isdigit(*(o->data + num))) {
    3115             return -1;
    3116         }
    3117     }
    3118     /* reuse num (and save an int) */
    3119     num = atoi(o->data);
    3120     b_reset(o);
    3121     return num;
    3122 }
    3123 
    3124 #if ENABLE_HUSH_TICK
    3125 static FILE *generate_stream_from_list(struct pipe *head)
    3126 {
    3127     FILE *pf;
    3128     int pid, channel[2];
    3129 
    3130     xpipe(channel);
    3131 #if BB_MMU
    3132     pid = fork();
    3133 #else
    3134     pid = vfork();
    3135 #endif
    3136     if (pid < 0) {
    3137         bb_perror_msg_and_die("fork");
    3138     } else if (pid == 0) {
    3139         close(channel[0]);
    3140         if (channel[1] != 1) {
    3141             dup2(channel[1], 1);
    3142             close(channel[1]);
    3143         }
    3144         /* Prevent it from trying to handle ctrl-z etc */
    3145 #if ENABLE_HUSH_JOB
    3146         run_list_level = 1;
    3147 #endif
    3148         /* Process substitution is not considered to be usual
    3149          * 'command execution'.
    3150          * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. */
    3151         /* Not needed, we are relying on it being disabled
    3152          * everywhere outside actual command execution. */
    3153         /*set_jobctrl_sighandler(SIG_IGN);*/
    3154         set_misc_sighandler(SIG_DFL);
    3155         _exit(run_list_real(head));   /* leaks memory */
    3156     }
    3157     close(channel[1]);
    3158     pf = fdopen(channel[0], "r");
    3159     return pf;
    3160 }
    3161 
    3162 /* Return code is exit status of the process that is run. */
    3163 static int process_command_subs(o_string *dest, struct p_context *ctx,
    3164     struct in_str *input, const char *subst_end)
    3165 {
    3166     int retcode, ch, eol_cnt;
    3167     o_string result = NULL_O_STRING;
    3168     struct p_context inner;
    3169     FILE *p;
    3170     struct in_str pipe_str;
    3171 
    3172     initialize_context(&inner);
    3173 
    3174     /* recursion to generate command */
    3175     retcode = parse_stream(&result, &inner, input, subst_end);
    3176     if (retcode != 0)
    3177         return retcode;  /* syntax error or EOF */
    3178     done_word(&result, &inner);
    3179     done_pipe(&inner, PIPE_SEQ);
    3180     b_free(&result);
    3181 
    3182     p = generate_stream_from_list(inner.list_head);
    3183     if (p == NULL) return 1;
    3184     mark_open(fileno(p));
    3185     setup_file_in_str(&pipe_str, p);
    3186 
    3187     /* now send results of command back into original context */
    3188     eol_cnt = 0;
    3189     while ((ch = b_getch(&pipe_str)) != EOF) {
    3190         if (ch == '\n') {
    3191             eol_cnt++;
    3192             continue;
    3193         }
    3194         while (eol_cnt) {
    3195             b_addqchr(dest, '\n', dest->quote);
    3196             eol_cnt--;
    3197         }
    3198         b_addqchr(dest, ch, dest->quote);
    3199     }
    3200 
    3201     debug_printf("done reading from pipe, pclose()ing\n");
    3202     /* This is the step that wait()s for the child.  Should be pretty
    3203      * safe, since we just read an EOF from its stdout.  We could try
    3204      * to do better, by using wait(), and keeping track of background jobs
    3205      * at the same time.  That would be a lot of work, and contrary
    3206      * to the KISS philosophy of this program. */
    3207     mark_closed(fileno(p));
    3208     retcode = fclose(p);
    3209     free_pipe_list(inner.list_head, 0);
    3210     debug_printf("closed FILE from child, retcode=%d\n", retcode);
    3211     return retcode;
    3212 }
    3213 #endif
    3214 
    3215 static int parse_group(o_string *dest, struct p_context *ctx,
    3216     struct in_str *input, int ch)
    3217 {
    3218     int rcode;
    3219     const char *endch = NULL;
    3220     struct p_context sub;
    3221     struct child_prog *child = ctx->child;
    3222 
    3223     debug_printf_parse("parse_group entered\n");
    3224     if (child->argv) {
    3225         syntax(NULL);
    3226         debug_printf_parse("parse_group return 1: syntax error, groups and arglists don't mix\n");
    3227         return 1;
    3228     }
    3229     initialize_context(&sub);
    3230     endch = "}";
    3231     if (ch == '(') {
    3232         endch = ")";
    3233         child->subshell = 1;
    3234     }
    3235     rcode = parse_stream(dest, &sub, input, endch);
    3236 //vda: err chk?
    3237     done_word(dest, &sub); /* finish off the final word in the subcontext */
    3238     done_pipe(&sub, PIPE_SEQ);  /* and the final command there, too */
    3239     child->group = sub.list_head;
    3240 
    3241     debug_printf_parse("parse_group return %d\n", rcode);
    3242     return rcode;
    3243     /* child remains "open", available for possible redirects */
    3244 }
    3245 
    3246 /* Basically useful version until someone wants to get fancier,
    3247  * see the bash man page under "Parameter Expansion" */
    3248 static const char *lookup_param(const char *src)
    3249 {
    3250     struct variable *var = get_local_var(src);
    3251     if (var)
    3252         return strchr(var->varstr, '=') + 1;
    3253     return NULL;
    3254 }
    3255 
    3256 /* return code: 0 for OK, 1 for syntax error */
    3257 static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
    3258 {
    3259     int ch = b_peek(input);  /* first character after the $ */
    3260     unsigned char quote_mask = dest->quote ? 0x80 : 0;
    3261 
    3262     debug_printf_parse("handle_dollar entered: ch='%c'\n", ch);
    3263     if (isalpha(ch)) {
    3264         b_addchr(dest, SPECIAL_VAR_SYMBOL);
    3265         //sp: ctx->child->sp++;
    3266         while (1) {
    3267             debug_printf_parse(": '%c'\n", ch);
    3268             b_getch(input);
    3269             b_addchr(dest, ch | quote_mask);
    3270             quote_mask = 0;
    3271             ch = b_peek(input);
    3272             if (!isalnum(ch) && ch != '_')
    3273                 break;
    3274         }
    3275         b_addchr(dest, SPECIAL_VAR_SYMBOL);
    3276     } else if (isdigit(ch)) {
    3277  make_one_char_var:
    3278         b_addchr(dest, SPECIAL_VAR_SYMBOL);
    3279         //sp: ctx->child->sp++;
    3280         debug_printf_parse(": '%c'\n", ch);
    3281         b_getch(input);
    3282         b_addchr(dest, ch | quote_mask);
    3283         b_addchr(dest, SPECIAL_VAR_SYMBOL);
    3284     } else switch (ch) {
    3285         case '$': /* pid */
    3286         case '!': /* last bg pid */
    3287         case '?': /* last exit code */
    3288         case '#': /* number of args */
    3289         case '*': /* args */
    3290         case '@': /* args */
    3291             goto make_one_char_var;
    3292         case '{':
    3293             b_addchr(dest, SPECIAL_VAR_SYMBOL);
    3294             //sp: ctx->child->sp++;
    3295             b_getch(input);
    3296             /* XXX maybe someone will try to escape the '}' */
    3297             while (1) {
    3298                 ch = b_getch(input);
    3299                 if (ch == '}')
    3300                     break;
    3301                 if (!isalnum(ch) && ch != '_') {
    3302                     syntax("unterminated ${name}");
    3303                     debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
    3304                     return 1;
    3305                 }
    3306                 debug_printf_parse(": '%c'\n", ch);
    3307                 b_addchr(dest, ch | quote_mask);
    3308                 quote_mask = 0;
    3309             }
    3310             b_addchr(dest, SPECIAL_VAR_SYMBOL);
    3311             break;
    3312 #if ENABLE_HUSH_TICK
    3313         case '(':
    3314             b_getch(input);
    3315             process_command_subs(dest, ctx, input, ")");
    3316             break;
    3317 #endif
    3318         case '-':
    3319         case '_':
    3320             /* still unhandled, but should be eventually */
    3321             bb_error_msg("unhandled syntax: $%c", ch);
    3322             return 1;
    3323             break;
    3324         default:
    3325             b_addqchr(dest, '$', dest->quote);
    3326     }
    3327     debug_printf_parse("handle_dollar return 0\n");
    3328     return 0;
    3329 }
    3330 
    3331 /* return code is 0 for normal exit, 1 for syntax error */
    3332 static int parse_stream(o_string *dest, struct p_context *ctx,
    3333     struct in_str *input, const char *end_trigger)
    3334 {
    3335     int ch, m;
    3336     int redir_fd;
    3337     redir_type redir_style;
    3338     int next;
    3339 
    3340     /* Only double-quote state is handled in the state variable dest->quote.
    3341      * A single-quote triggers a bypass of the main loop until its mate is
    3342      * found.  When recursing, quote state is passed in via dest->quote. */
    3343 
    3344     debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger);
    3345 
    3346     while (1) {
    3347         m = CHAR_IFS;
    3348         next = '\0';
    3349         ch = b_getch(input);
    3350         if (ch != EOF) {
    3351             m = charmap[ch];
    3352             if (ch != '\n')
    3353                 next = b_peek(input);
    3354         }
    3355         debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n",
    3356                         ch, ch, m, dest->quote);
    3357         if (m == CHAR_ORDINARY
    3358          || (m != CHAR_SPECIAL && dest->quote)
    3359         ) {
    3360             if (ch == EOF) {
    3361                 syntax("unterminated \"");
    3362                 debug_printf_parse("parse_stream return 1: unterminated \"\n");
    3363                 return 1;
    3364             }
    3365             b_addqchr(dest, ch, dest->quote);
    3366             continue;
    3367         }
    3368         if (m == CHAR_IFS) {
    3369             if (done_word(dest, ctx)) {
    3370                 debug_printf_parse("parse_stream return 1: done_word!=0\n");
    3371                 return 1;
    3372             }
    3373             if (ch == EOF)
    3374                 break;
    3375             /* If we aren't performing a substitution, treat
    3376              * a newline as a command separator.
    3377              * [why we don't handle it exactly like ';'? --vda] */
    3378             if (end_trigger && ch == '\n') {
    3379                 done_pipe(ctx, PIPE_SEQ);
    3380             }
    3381         }
    3382         if ((end_trigger && strchr(end_trigger, ch))
    3383          && !dest->quote && ctx->res_w == RES_NONE
    3384         ) {
    3385             debug_printf_parse("parse_stream return 0: end_trigger char found\n");
    3386             return 0;
    3387         }
    3388         if (m == CHAR_IFS)
    3389             continue;
    3390         switch (ch) {
    3391         case '#':
    3392             if (dest->length == 0 && !dest->quote) {
    3393                 while (1) {
    3394                     ch = b_peek(input);
    3395                     if (ch == EOF || ch == '\n')
    3396                         break;
    3397                     b_getch(input);
    3398                 }
    3399             } else {
    3400                 b_addqchr(dest, ch, dest->quote);
    3401             }
    3402             break;
    3403         case '\\':
    3404             if (next == EOF) {
    3405                 syntax("\\<eof>");
    3406                 debug_printf_parse("parse_stream return 1: \\<eof>\n");
    3407                 return 1;
    3408             }
    3409             b_addqchr(dest, '\\', dest->quote);
    3410             b_addqchr(dest, b_getch(input), dest->quote);
    3411             break;
    3412         case '$':
    3413             if (handle_dollar(dest, ctx, input) != 0) {
    3414                 debug_printf_parse("parse_stream return 1: handle_dollar returned non-0\n");
    3415                 return 1;
    3416             }
    3417             break;
    3418         case '\'':
    3419             dest->nonnull = 1;
    3420             while (1) {
    3421                 ch = b_getch(input);
    3422                 if (ch == EOF || ch == '\'')
    3423                     break;
    3424                 b_addchr(dest, ch);
    3425             }
    3426             if (ch == EOF) {
    3427                 syntax("unterminated '");
    3428                 debug_printf_parse("parse_stream return 1: unterminated '\n");
    3429                 return 1;
    3430             }
    3431             break;
    3432         case '"':
    3433             dest->nonnull = 1;
    3434             dest->quote = !dest->quote;
    3435             break;
    3436 #if ENABLE_HUSH_TICK
    3437         case '`':
    3438             process_command_subs(dest, ctx, input, "`");
    3439             break;
    3440 #endif
    3441         case '>':
    3442             redir_fd = redirect_opt_num(dest);
    3443             done_word(dest, ctx);
    3444             redir_style = REDIRECT_OVERWRITE;
    3445             if (next == '>') {
    3446                 redir_style = REDIRECT_APPEND;
    3447                 b_getch(input);
    3448             }
    3449 #if 0
    3450             else if (next == '(') {
    3451                 syntax(">(process) not supported");
    3452                 debug_printf_parse("parse_stream return 1: >(process) not supported\n");
    3453                 return 1;
    3454             }
    3455 #endif
    3456             setup_redirect(ctx, redir_fd, redir_style, input);
    3457             break;
    3458         case '<':
    3459             redir_fd = redirect_opt_num(dest);
    3460             done_word(dest, ctx);
    3461             redir_style = REDIRECT_INPUT;
    3462             if (next == '<') {
    3463                 redir_style = REDIRECT_HEREIS;
    3464                 b_getch(input);
    3465             } else if (next == '>') {
    3466                 redir_style = REDIRECT_IO;
    3467                 b_getch(input);
    3468             }
    3469 #if 0
    3470             else if (next == '(') {
    3471                 syntax("<(process) not supported");
    3472                 debug_printf_parse("parse_stream return 1: <(process) not supported\n");
    3473                 return 1;
    3474             }
    3475 #endif
    3476             setup_redirect(ctx, redir_fd, redir_style, input);
    3477             break;
    3478         case ';':
    3479             done_word(dest, ctx);
    3480             done_pipe(ctx, PIPE_SEQ);
    3481             break;
    3482         case '&':
    3483             done_word(dest, ctx);
    3484             if (next == '&') {
    3485                 b_getch(input);
    3486                 done_pipe(ctx, PIPE_AND);
    3487             } else {
    3488                 done_pipe(ctx, PIPE_BG);
    3489             }
    3490             break;
    3491         case '|':
    3492             done_word(dest, ctx);
    3493             if (next == '|') {
    3494                 b_getch(input);
    3495                 done_pipe(ctx, PIPE_OR);
    3496             } else {
    3497                 /* we could pick up a file descriptor choice here
    3498                  * with redirect_opt_num(), but bash doesn't do it.
    3499                  * "echo foo 2| cat" yields "foo 2". */
    3500                 done_command(ctx);
    3501             }
    3502             break;
    3503         case '(':
    3504         case '{':
    3505             if (parse_group(dest, ctx, input, ch) != 0) {
    3506                 debug_printf_parse("parse_stream return 1: parse_group returned non-0\n");
    3507                 return 1;
    3508             }
    3509             break;
    3510         case ')':
    3511         case '}':
    3512             syntax("unexpected }");   /* Proper use of this character is caught by end_trigger */
    3513             debug_printf_parse("parse_stream return 1: unexpected '}'\n");
    3514             return 1;
    3515         default:
    3516             if (ENABLE_HUSH_DEBUG)
    3517                 bb_error_msg_and_die("BUG: unexpected %c\n", ch);
    3518         }
    3519     }
    3520     /* Complain if quote?  No, maybe we just finished a command substitution
    3521      * that was quoted.  Example:
    3522      * $ echo "`cat foo` plus more"
    3523      * and we just got the EOF generated by the subshell that ran "cat foo"
    3524      * The only real complaint is if we got an EOF when end_trigger != NULL,
    3525      * that is, we were really supposed to get end_trigger, and never got
    3526      * one before the EOF.  Can't use the standard "syntax error" return code,
    3527      * so that parse_stream_outer can distinguish the EOF and exit smoothly. */
    3528     debug_printf_parse("parse_stream return %d\n", -(end_trigger != NULL));
    3529     if (end_trigger)
    3530         return -1;
    3531     return 0;
    3532 }
    3533 
    3534 static void set_in_charmap(const char *set, int code)
    3535 {
    3536     while (*set)
    3537         charmap[(unsigned char)*set++] = code;
    3538 }
    3539 
    3540 static void update_charmap(void)
    3541 {
    3542     /* char *ifs and char charmap[256] are both globals. */
    3543     ifs = getenv("IFS");
    3544     if (ifs == NULL)
    3545         ifs = " \t\n";
    3546     /* Precompute a list of 'flow through' behavior so it can be treated
    3547      * quickly up front.  Computation is necessary because of IFS.
    3548      * Special case handling of IFS == " \t\n" is not implemented.
    3549      * The charmap[] array only really needs two bits each,
    3550      * and on most machines that would be faster (reduced L1 cache use).
    3551      */
    3552     memset(charmap, CHAR_ORDINARY, sizeof(charmap));
    3553 #if ENABLE_HUSH_TICK
    3554     set_in_charmap("\\$\"`", CHAR_SPECIAL);
    3555 #else
    3556     set_in_charmap("\\$\"", CHAR_SPECIAL);
    3557 #endif
    3558     set_in_charmap("<>;&|(){}#'", CHAR_ORDINARY_IF_QUOTED);
    3559     set_in_charmap(ifs, CHAR_IFS);  /* are ordinary if quoted */
    3560 }
    3561 
    3562 /* most recursion does not come through here, the exception is
    3563  * from builtin_source() and builtin_eval() */
    3564 static int parse_and_run_stream(struct in_str *inp, int parse_flag)
    3565 {
    3566     struct p_context ctx;
    3567     o_string temp = NULL_O_STRING;
    3568     int rcode;
    3569     do {
    3570         ctx.parse_type = parse_flag;
    3571         initialize_context(&ctx);
    3572         update_charmap();
    3573         if (!(parse_flag & PARSEFLAG_SEMICOLON) || (parse_flag & PARSEFLAG_REPARSING))
    3574             set_in_charmap(";$&|", CHAR_ORDINARY);
    3575 #if ENABLE_HUSH_INTERACTIVE
    3576         inp->promptmode = 0; /* PS1 */
    3577 #endif
    3578         /* We will stop & execute after each ';' or '\n'.
    3579          * Example: "sleep 9999; echo TEST" + ctrl-C:
    3580          * TEST should be printed */
    3581         rcode = parse_stream(&temp, &ctx, inp, ";\n");
    3582         if (rcode != 1 && ctx.old_flag != 0) {
    3583             syntax(NULL);
    3584         }
    3585         if (rcode != 1 && ctx.old_flag == 0) {
    3586             done_word(&temp, &ctx);
    3587             done_pipe(&ctx, PIPE_SEQ);
    3588             debug_print_tree(ctx.list_head, 0);
    3589             debug_printf_exec("parse_stream_outer: run_list\n");
    3590             run_list(ctx.list_head);
    3591         } else {
    3592             if (ctx.old_flag != 0) {
    3593                 free(ctx.stack);
    3594                 b_reset(&temp);
    3595             }
    3596             temp.nonnull = 0;
    3597             temp.quote = 0;
    3598             inp->p = NULL;
    3599             free_pipe_list(ctx.list_head, 0);
    3600         }
    3601         b_free(&temp);
    3602     } while (rcode != -1 && !(parse_flag & PARSEFLAG_EXIT_FROM_LOOP));   /* loop on syntax errors, return on EOF */
    3603     return 0;
    3604 }
    3605 
    3606 static int parse_and_run_string(const char *s, int parse_flag)
    3607 {
    3608     struct in_str input;
    3609     setup_string_in_str(&input, s);
    3610     return parse_and_run_stream(&input, parse_flag);
    3611 }
    3612 
    3613 static int parse_and_run_file(FILE *f)
    3614 {
    3615     int rcode;
    3616     struct in_str input;
    3617     setup_file_in_str(&input, f);
    3618     rcode = parse_and_run_stream(&input, PARSEFLAG_SEMICOLON);
    3619     return rcode;
    3620 }
    3621 
    3622 #if ENABLE_HUSH_JOB
    3623 /* Make sure we have a controlling tty.  If we get started under a job
    3624  * aware app (like bash for example), make sure we are now in charge so
    3625  * we don't fight over who gets the foreground */
    3626 static void setup_job_control(void)
    3627 {
    3628     pid_t shell_pgrp;
    3629 
    3630     saved_task_pgrp = shell_pgrp = getpgrp();
    3631     debug_printf_jobs("saved_task_pgrp=%d\n", saved_task_pgrp);
    3632     fcntl(interactive_fd, F_SETFD, FD_CLOEXEC);
    3633 
    3634     /* If we were ran as 'hush &',
    3635      * sleep until we are in the foreground.  */
    3636     while (tcgetpgrp(interactive_fd) != shell_pgrp) {
    3637         /* Send TTIN to ourself (should stop us) */
    3638         kill(- shell_pgrp, SIGTTIN);
    3639         shell_pgrp = getpgrp();
    3640     }
    3641 
    3642     /* Ignore job-control and misc signals.  */
    3643     set_jobctrl_sighandler(SIG_IGN);
    3644     set_misc_sighandler(SIG_IGN);
    3645 //huh?  signal(SIGCHLD, SIG_IGN);
    3646 
    3647     /* We _must_ restore tty pgrp on fatal signals */
    3648     set_fatal_sighandler(sigexit);
    3649 
    3650     /* Put ourselves in our own process group.  */
    3651     setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
    3652     /* Grab control of the terminal.  */
    3653     tcsetpgrp(interactive_fd, getpid());
    3654 }
    3655 #endif
    3656 
    3657 int hush_main(int argc, char **argv);
     7470        }
     7471    default:
     7472        return EXIT_FAILURE;
     7473    }
     7474    return EXIT_SUCCESS;
     7475}
     7476
     7477int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
    36587478int hush_main(int argc, char **argv)
    36597479{
    3660     static const char version_str[] ALIGN1 = "HUSH_VERSION="HUSH_VER_STR;
    3661     static const struct variable const_shell_ver = {
    3662         .next = NULL,
    3663         .varstr = (char*)version_str,
    3664         .max_len = 1, /* 0 can provoke free(name) */
    3665         .flg_export = 1,
    3666         .flg_read_only = 1,
    3667     };
    3668 
    36697480    int opt;
    3670     FILE *input;
     7481    unsigned builtin_argc;
    36717482    char **e;
    36727483    struct variable *cur_var;
    3673 
    3674     PTR_TO_GLOBALS = xzalloc(sizeof(G));
    3675 
     7484    struct variable *shell_ver;
     7485
     7486    INIT_G();
     7487    if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */
     7488        G.last_exitcode = EXIT_SUCCESS;
     7489#if !BB_MMU
     7490    G.argv0_for_re_execing = argv[0];
     7491#endif
    36767492    /* Deal with HUSH_VERSION */
    3677     shell_ver = const_shell_ver; /* copying struct here */
    3678     top_var = &shell_ver;
     7493    shell_ver = xzalloc(sizeof(*shell_ver));
     7494    shell_ver->flg_export = 1;
     7495    shell_ver->flg_read_only = 1;
     7496    /* Code which handles ${var<op>...} needs writable values for all variables,
     7497     * therefore we xstrdup: */
     7498    shell_ver->varstr = xstrdup(hush_version_str);
     7499    /* Create shell local variables from the values
     7500     * currently living in the environment */
     7501    debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
    36797502    unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
    3680     /* Initialize our shell local variables with the values
    3681      * currently living in the environment */
    3682     cur_var = top_var;
     7503    G.top_var = shell_ver;
     7504    cur_var = G.top_var;
    36837505    e = environ;
    36847506    if (e) while (*e) {
     
    36937515        e++;
    36947516    }
    3695     putenv((char *)version_str); /* reinstate HUSH_VERSION */
     7517    /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
     7518    debug_printf_env("putenv '%s'\n", shell_ver->varstr);
     7519    putenv(shell_ver->varstr);
     7520
     7521    /* Export PWD */
     7522    set_pwd_var(/*exp:*/ 1);
     7523    /* bash also exports SHLVL and _,
     7524     * and sets (but doesn't export) the following variables:
     7525     * BASH=/bin/bash
     7526     * BASH_VERSINFO=([0]="3" [1]="2" [2]="0" [3]="1" [4]="release" [5]="i386-pc-linux-gnu")
     7527     * BASH_VERSION='3.2.0(1)-release'
     7528     * HOSTTYPE=i386
     7529     * MACHTYPE=i386-pc-linux-gnu
     7530     * OSTYPE=linux-gnu
     7531     * HOSTNAME=<xxxxxxxxxx>
     7532     * PPID=<NNNNN> - we also do it elsewhere
     7533     * EUID=<NNNNN>
     7534     * UID=<NNNNN>
     7535     * GROUPS=()
     7536     * LINES=<NNN>
     7537     * COLUMNS=<NNN>
     7538     * BASH_ARGC=()
     7539     * BASH_ARGV=()
     7540     * BASH_LINENO=()
     7541     * BASH_SOURCE=()
     7542     * DIRSTACK=()
     7543     * PIPESTATUS=([0]="0")
     7544     * HISTFILE=/<xxx>/.bash_history
     7545     * HISTFILESIZE=500
     7546     * HISTSIZE=500
     7547     * MAILCHECK=60
     7548     * PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
     7549     * SHELL=/bin/bash
     7550     * SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
     7551     * TERM=dumb
     7552     * OPTERR=1
     7553     * OPTIND=1
     7554     * IFS=$' \t\n'
     7555     * PS1='\s-\v\$ '
     7556     * PS2='> '
     7557     * PS4='+ '
     7558     */
    36967559
    36977560#if ENABLE_FEATURE_EDITING
    3698     line_input_state = new_line_input_t(FOR_SHELL);
    3699 #endif
    3700     /* XXX what should these be while sourcing /etc/profile? */
    3701     global_argc = argc;
    3702     global_argv = argv;
     7561    G.line_input_state = new_line_input_t(FOR_SHELL);
     7562# if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_HUSH_SAVEHISTORY
     7563    {
     7564        const char *hp = get_local_var_value("HISTFILE");
     7565        if (!hp) {
     7566            hp = get_local_var_value("HOME");
     7567            if (hp) {
     7568                G.line_input_state->hist_file = concat_path_file(hp, ".hush_history");
     7569                //set_local_var(xasprintf("HISTFILE=%s", ...));
     7570            }
     7571        }
     7572    }
     7573# endif
     7574#endif
     7575
     7576    G.global_argc = argc;
     7577    G.global_argv = argv;
    37037578    /* Initialize some more globals to non-zero values */
    3704     set_cwd();
    3705 #if ENABLE_HUSH_INTERACTIVE
    3706 #if ENABLE_FEATURE_EDITING
    3707     cmdedit_set_initial_prompt();
    3708 #endif
    3709     PS2 = "> ";
    3710 #endif
    3711 
    3712     if (EXIT_SUCCESS) /* otherwise is already done */
    3713         last_return_code = EXIT_SUCCESS;
    3714 
    3715     if (argv[0] && argv[0][0] == '-') {
    3716         debug_printf("sourcing /etc/profile\n");
    3717         input = fopen("/etc/profile", "r");
    3718         if (input != NULL) {
    3719             mark_open(fileno(input));
    3720             parse_and_run_file(input);
    3721             mark_closed(fileno(input));
    3722             fclose(input);
    3723         }
    3724     }
    3725     input = stdin;
    3726 
    3727     while ((opt = getopt(argc, argv, "c:xif")) > 0) {
     7579    cmdedit_update_prompt();
     7580
     7581    if (setjmp(die_jmp)) {
     7582        /* xfunc has failed! die die die */
     7583        /* no EXIT traps, this is an escape hatch! */
     7584        G.exiting = 1;
     7585        hush_exit(xfunc_error_retval);
     7586    }
     7587
     7588    /* Shell is non-interactive at first. We need to call
     7589     * init_sigmasks() if we are going to execute "sh <script>",
     7590     * "sh -c <cmds>" or login shell's /etc/profile and friends.
     7591     * If we later decide that we are interactive, we run init_sigmasks()
     7592     * in order to intercept (more) signals.
     7593     */
     7594
     7595    /* Parse options */
     7596    /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
     7597    builtin_argc = 0;
     7598    while (1) {
     7599        opt = getopt(argc, argv, "+c:xins"
     7600#if !BB_MMU
     7601                "<:$:R:V:"
     7602# if ENABLE_HUSH_FUNCTIONS
     7603                "F:"
     7604# endif
     7605#endif
     7606        );
     7607        if (opt <= 0)
     7608            break;
    37287609        switch (opt) {
    37297610        case 'c':
    3730             global_argv = argv + optind;
    3731             global_argc = argc - optind;
    3732             opt = parse_and_run_string(optarg, PARSEFLAG_SEMICOLON);
     7611            /* Possibilities:
     7612             * sh ... -c 'script'
     7613             * sh ... -c 'script' ARG0 [ARG1...]
     7614             * On NOMMU, if builtin_argc != 0,
     7615             * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
     7616             * "" needs to be replaced with NULL
     7617             * and BARGV vector fed to builtin function.
     7618             * Note: the form without ARG0 never happens:
     7619             * sh ... -c 'builtin' BARGV... ""
     7620             */
     7621            if (!G.root_pid) {
     7622                G.root_pid = getpid();
     7623                G.root_ppid = getppid();
     7624            }
     7625            G.global_argv = argv + optind;
     7626            G.global_argc = argc - optind;
     7627            if (builtin_argc) {
     7628                /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
     7629                const struct built_in_command *x;
     7630
     7631                init_sigmasks();
     7632                x = find_builtin(optarg);
     7633                if (x) { /* paranoia */
     7634                    G.global_argc -= builtin_argc; /* skip [BARGV...] "" */
     7635                    G.global_argv += builtin_argc;
     7636                    G.global_argv[-1] = NULL; /* replace "" */
     7637                    G.last_exitcode = x->b_function(argv + optind - 1);
     7638                }
     7639                goto final_return;
     7640            }
     7641            if (!G.global_argv[0]) {
     7642                /* -c 'script' (no params): prevent empty $0 */
     7643                G.global_argv--; /* points to argv[i] of 'script' */
     7644                G.global_argv[0] = argv[0];
     7645                G.global_argc++;
     7646            } /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
     7647            init_sigmasks();
     7648            parse_and_run_string(optarg);
    37337649            goto final_return;
    37347650        case 'i':
    37357651            /* Well, we cannot just declare interactiveness,
    37367652             * we have to have some stuff (ctty, etc) */
    3737             /* interactive_fd++; */
     7653            /* G_interactive_fd++; */
    37387654            break;
    3739         case 'f':
    3740             fake_mode = 1;
     7655        case 's':
     7656            /* "-s" means "read from stdin", but this is how we always
     7657             * operate, so simply do nothing here. */
    37417658            break;
     7659#if !BB_MMU
     7660        case '<': /* "big heredoc" support */
     7661            full_write1_str(optarg);
     7662            _exit(0);
     7663        case '$': {
     7664            unsigned long long empty_trap_mask;
     7665
     7666            G.root_pid = bb_strtou(optarg, &optarg, 16);
     7667            optarg++;
     7668            G.root_ppid = bb_strtou(optarg, &optarg, 16);
     7669            optarg++;
     7670            G.last_bg_pid = bb_strtou(optarg, &optarg, 16);
     7671            optarg++;
     7672            G.last_exitcode = bb_strtou(optarg, &optarg, 16);
     7673            optarg++;
     7674            builtin_argc = bb_strtou(optarg, &optarg, 16);
     7675            optarg++;
     7676            empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
     7677            if (empty_trap_mask != 0) {
     7678                int sig;
     7679                init_sigmasks();
     7680                G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
     7681                for (sig = 1; sig < NSIG; sig++) {
     7682                    if (empty_trap_mask & (1LL << sig)) {
     7683                        G.traps[sig] = xzalloc(1); /* == xstrdup(""); */
     7684                        sigaddset(&G.blocked_set, sig);
     7685                    }
     7686                }
     7687                sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
     7688            }
     7689# if ENABLE_HUSH_LOOPS
     7690            optarg++;
     7691            G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
     7692# endif
     7693            break;
     7694        }
     7695        case 'R':
     7696        case 'V':
     7697            set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R');
     7698            break;
     7699# if ENABLE_HUSH_FUNCTIONS
     7700        case 'F': {
     7701            struct function *funcp = new_function(optarg);
     7702            /* funcp->name is already set to optarg */
     7703            /* funcp->body is set to NULL. It's a special case. */
     7704            funcp->body_as_string = argv[optind];
     7705            optind++;
     7706            break;
     7707        }
     7708# endif
     7709#endif
     7710        case 'n':
     7711        case 'x':
     7712            if (set_mode(1, opt, NULL) == 0) /* no error */
     7713                break;
    37427714        default:
    37437715#ifndef BB_VER
     
    37497721#endif
    37507722        }
    3751     }
    3752 #if ENABLE_HUSH_JOB
    3753     /* A shell is interactive if the '-i' flag was given, or if all of
    3754      * the following conditions are met:
     7723    } /* option parsing loop */
     7724
     7725    if (!G.root_pid) {
     7726        G.root_pid = getpid();
     7727        G.root_ppid = getppid();
     7728    }
     7729
     7730    /* If we are login shell... */
     7731    if (argv[0] && argv[0][0] == '-') {
     7732        FILE *input;
     7733        debug_printf("sourcing /etc/profile\n");
     7734        input = fopen_for_read("/etc/profile");
     7735        if (input != NULL) {
     7736            close_on_exec_on(fileno(input));
     7737            init_sigmasks();
     7738            parse_and_run_file(input);
     7739            fclose(input);
     7740        }
     7741        /* bash: after sourcing /etc/profile,
     7742         * tries to source (in the given order):
     7743         * ~/.bash_profile, ~/.bash_login, ~/.profile,
     7744         * stopping on first found. --noprofile turns this off.
     7745         * bash also sources ~/.bash_logout on exit.
     7746         * If called as sh, skips .bash_XXX files.
     7747         */
     7748    }
     7749
     7750    if (argv[optind]) {
     7751        FILE *input;
     7752        /*
     7753         * "bash <script>" (which is never interactive (unless -i?))
     7754         * sources $BASH_ENV here (without scanning $PATH).
     7755         * If called as sh, does the same but with $ENV.
     7756         */
     7757        debug_printf("running script '%s'\n", argv[optind]);
     7758        G.global_argv = argv + optind;
     7759        G.global_argc = argc - optind;
     7760        input = xfopen_for_read(argv[optind]);
     7761        close_on_exec_on(fileno(input));
     7762        init_sigmasks();
     7763        parse_and_run_file(input);
     7764#if ENABLE_FEATURE_CLEAN_UP
     7765        fclose(input);
     7766#endif
     7767        goto final_return;
     7768    }
     7769
     7770    /* Up to here, shell was non-interactive. Now it may become one.
     7771     * NB: don't forget to (re)run init_sigmasks() as needed.
     7772     */
     7773
     7774    /* A shell is interactive if the '-i' flag was given,
     7775     * or if all of the following conditions are met:
    37557776     *    no -c command
    37567777     *    no arguments remaining or the -s flag given
    37577778     *    standard input is a terminal
    37587779     *    standard output is a terminal
    3759      *    Refer to Posix.2, the description of the 'sh' utility. */
    3760     if (argv[optind] == NULL && input == stdin
    3761      && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
    3762     ) {
    3763         saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
    3764         debug_printf("saved_tty_pgrp=%d\n", saved_tty_pgrp);
    3765         if (saved_tty_pgrp >= 0) {
    3766             /* try to dup to high fd#, >= 255 */
    3767             interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
    3768             if (interactive_fd < 0) {
    3769                 /* try to dup to any fd */
    3770                 interactive_fd = dup(STDIN_FILENO);
    3771                 if (interactive_fd < 0)
    3772                     /* give up */
    3773                     interactive_fd = 0;
    3774             }
    3775             // TODO: track & disallow any attempts of user
    3776             // to (inadvertently) close/redirect it
    3777         }
    3778     }
    3779     debug_printf("interactive_fd=%d\n", interactive_fd);
    3780     if (interactive_fd) {
    3781         /* Looks like they want an interactive shell */
    3782         setup_job_control();
    3783         /* Make xfuncs do cleanup on exit */
    3784         die_sleep = -1; /* flag */
    3785 // FIXME: should we reset die_sleep = 0 whereever we fork?
    3786         if (setjmp(die_jmp)) {
    3787             /* xfunc has failed! die die die */
    3788             hush_exit(xfunc_error_retval);
    3789         }
    3790 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
    3791         printf("\n\n%s hush - the humble shell v"HUSH_VER_STR"\n", bb_banner);
    3792         printf("Enter 'help' for a list of built-in commands.\n\n");
    3793 #endif
     7780     * Refer to Posix.2, the description of the 'sh' utility.
     7781     */
     7782#if ENABLE_HUSH_JOB
     7783    if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
     7784        G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
     7785        debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
     7786        if (G_saved_tty_pgrp < 0)
     7787            G_saved_tty_pgrp = 0;
     7788
     7789        /* try to dup stdin to high fd#, >= 255 */
     7790        G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
     7791        if (G_interactive_fd < 0) {
     7792            /* try to dup to any fd */
     7793            G_interactive_fd = dup(STDIN_FILENO);
     7794            if (G_interactive_fd < 0) {
     7795                /* give up */
     7796                G_interactive_fd = 0;
     7797                G_saved_tty_pgrp = 0;
     7798            }
     7799        }
     7800// TODO: track & disallow any attempts of user
     7801// to (inadvertently) close/redirect G_interactive_fd
     7802    }
     7803    debug_printf("interactive_fd:%d\n", G_interactive_fd);
     7804    if (G_interactive_fd) {
     7805        close_on_exec_on(G_interactive_fd);
     7806
     7807        if (G_saved_tty_pgrp) {
     7808            /* If we were run as 'hush &', sleep until we are
     7809             * in the foreground (tty pgrp == our pgrp).
     7810             * If we get started under a job aware app (like bash),
     7811             * make sure we are now in charge so we don't fight over
     7812             * who gets the foreground */
     7813            while (1) {
     7814                pid_t shell_pgrp = getpgrp();
     7815                G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
     7816                if (G_saved_tty_pgrp == shell_pgrp)
     7817                    break;
     7818                /* send TTIN to ourself (should stop us) */
     7819                kill(- shell_pgrp, SIGTTIN);
     7820            }
     7821        }
     7822
     7823        /* Block some signals */
     7824        init_sigmasks();
     7825
     7826        if (G_saved_tty_pgrp) {
     7827            /* Set other signals to restore saved_tty_pgrp */
     7828            set_fatal_handlers();
     7829            /* Put ourselves in our own process group
     7830             * (bash, too, does this only if ctty is available) */
     7831            bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
     7832            /* Grab control of the terminal */
     7833            tcsetpgrp(G_interactive_fd, getpid());
     7834        }
     7835        /* -1 is special - makes xfuncs longjmp, not exit
     7836         * (we reset die_sleep = 0 whereever we [v]fork) */
     7837        enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
     7838    } else {
     7839        init_sigmasks();
    37947840    }
    37957841#elif ENABLE_HUSH_INTERACTIVE
    3796 /* no job control compiled, only prompt/line editing */
    3797     if (argv[optind] == NULL && input == stdin
    3798      && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
    3799     ) {
    3800         interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
    3801         if (interactive_fd < 0) {
     7842    /* No job control compiled in, only prompt/line editing */
     7843    if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
     7844        G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
     7845        if (G_interactive_fd < 0) {
    38027846            /* try to dup to any fd */
    3803             interactive_fd = dup(STDIN_FILENO);
    3804             if (interactive_fd < 0)
     7847            G_interactive_fd = dup(STDIN_FILENO);
     7848            if (G_interactive_fd < 0)
    38057849                /* give up */
    3806                 interactive_fd = 0;
    3807         }
    3808     }
    3809 
    3810 #endif
    3811 
    3812     if (argv[optind] == NULL) {
    3813         opt = parse_and_run_file(stdin);
    3814         goto final_return;
    3815     }
    3816 
    3817     debug_printf("\nrunning script '%s'\n", argv[optind]);
    3818     global_argv = argv + optind;
    3819     global_argc = argc - optind;
    3820     input = xfopen(argv[optind], "r");
    3821     opt = parse_and_run_file(input);
     7850                G_interactive_fd = 0;
     7851        }
     7852    }
     7853    if (G_interactive_fd) {
     7854        close_on_exec_on(G_interactive_fd);
     7855    }
     7856    init_sigmasks();
     7857#else
     7858    /* We have interactiveness code disabled */
     7859    init_sigmasks();
     7860#endif
     7861    /* bash:
     7862     * if interactive but not a login shell, sources ~/.bashrc
     7863     * (--norc turns this off, --rcfile <file> overrides)
     7864     */
     7865
     7866    if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) {
     7867        /* note: ash and hush share this string */
     7868        printf("\n\n%s %s\n"
     7869            IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
     7870            "\n",
     7871            bb_banner,
     7872            "hush - the humble shell"
     7873        );
     7874    }
     7875
     7876    parse_and_run_file(stdin);
    38227877
    38237878 final_return:
    3824 
    3825 #if ENABLE_FEATURE_CLEAN_UP
     7879    hush_exit(G.last_exitcode);
     7880}
     7881
     7882
     7883#if ENABLE_MSH
     7884int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     7885int msh_main(int argc, char **argv)
     7886{
     7887    //bb_error_msg("msh is deprecated, please use hush instead");
     7888    return hush_main(argc, argv);
     7889}
     7890#endif
     7891
     7892
     7893/*
     7894 * Built-ins
     7895 */
     7896static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
     7897{
     7898    return 0;
     7899}
     7900
     7901static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
     7902{
     7903    int argc = 0;
     7904    while (*argv) {
     7905        argc++;
     7906        argv++;
     7907    }
     7908    return applet_main_func(argc, argv - argc);
     7909}
     7910
     7911static int FAST_FUNC builtin_test(char **argv)
     7912{
     7913    return run_applet_main(argv, test_main);
     7914}
     7915
     7916static int FAST_FUNC builtin_echo(char **argv)
     7917{
     7918    return run_applet_main(argv, echo_main);
     7919}
     7920
     7921#if ENABLE_PRINTF
     7922static int FAST_FUNC builtin_printf(char **argv)
     7923{
     7924    return run_applet_main(argv, printf_main);
     7925}
     7926#endif
     7927
     7928static char **skip_dash_dash(char **argv)
     7929{
     7930    argv++;
     7931    if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0')
     7932        argv++;
     7933    return argv;
     7934}
     7935
     7936static int FAST_FUNC builtin_eval(char **argv)
     7937{
     7938    int rcode = EXIT_SUCCESS;
     7939
     7940    argv = skip_dash_dash(argv);
     7941    if (*argv) {
     7942        char *str = expand_strvec_to_string(argv);
     7943        /* bash:
     7944         * eval "echo Hi; done" ("done" is syntax error):
     7945         * "echo Hi" will not execute too.
     7946         */
     7947        parse_and_run_string(str);
     7948        free(str);
     7949        rcode = G.last_exitcode;
     7950    }
     7951    return rcode;
     7952}
     7953
     7954static int FAST_FUNC builtin_cd(char **argv)
     7955{
     7956    const char *newdir;
     7957
     7958    argv = skip_dash_dash(argv);
     7959    newdir = argv[0];
     7960    if (newdir == NULL) {
     7961        /* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
     7962         * bash says "bash: cd: HOME not set" and does nothing
     7963         * (exitcode 1)
     7964         */
     7965        const char *home = get_local_var_value("HOME");
     7966        newdir = home ? home : "/";
     7967    }
     7968    if (chdir(newdir)) {
     7969        /* Mimic bash message exactly */
     7970        bb_perror_msg("cd: %s", newdir);
     7971        return EXIT_FAILURE;
     7972    }
     7973    /* Read current dir (get_cwd(1) is inside) and set PWD.
     7974     * Note: do not enforce exporting. If PWD was unset or unexported,
     7975     * set it again, but do not export. bash does the same.
     7976     */
     7977    set_pwd_var(/*exp:*/ 0);
     7978    return EXIT_SUCCESS;
     7979}
     7980
     7981static int FAST_FUNC builtin_exec(char **argv)
     7982{
     7983    argv = skip_dash_dash(argv);
     7984    if (argv[0] == NULL)
     7985        return EXIT_SUCCESS; /* bash does this */
     7986
     7987    /* Careful: we can end up here after [v]fork. Do not restore
     7988     * tty pgrp then, only top-level shell process does that */
     7989    if (G_saved_tty_pgrp && getpid() == G.root_pid)
     7990        tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
     7991
     7992    /* TODO: if exec fails, bash does NOT exit! We do.
     7993     * We'll need to undo sigprocmask (it's inside execvp_or_die)
     7994     * and tcsetpgrp, and this is inherently racy.
     7995     */
     7996    execvp_or_die(argv);
     7997}
     7998
     7999static int FAST_FUNC builtin_exit(char **argv)
     8000{
     8001    debug_printf_exec("%s()\n", __func__);
     8002
     8003    /* interactive bash:
     8004     * # trap "echo EEE" EXIT
     8005     * # exit
     8006     * exit
     8007     * There are stopped jobs.
     8008     * (if there are _stopped_ jobs, running ones don't count)
     8009     * # exit
     8010     * exit
     8011     # EEE (then bash exits)
     8012     *
     8013     * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
     8014     */
     8015
     8016    /* note: EXIT trap is run by hush_exit */
     8017    argv = skip_dash_dash(argv);
     8018    if (argv[0] == NULL)
     8019        hush_exit(G.last_exitcode);
     8020    /* mimic bash: exit 123abc == exit 255 + error msg */
     8021    xfunc_error_retval = 255;
     8022    /* bash: exit -2 == exit 254, no error msg */
     8023    hush_exit(xatoi(argv[0]) & 0xff);
     8024}
     8025
     8026static void print_escaped(const char *s)
     8027{
     8028    if (*s == '\'')
     8029        goto squote;
     8030    do {
     8031        const char *p = strchrnul(s, '\'');
     8032        /* print 'xxxx', possibly just '' */
     8033        printf("'%.*s'", (int)(p - s), s);
     8034        if (*p == '\0')
     8035            break;
     8036        s = p;
     8037 squote:
     8038        /* s points to '; print "'''...'''" */
     8039        putchar('"');
     8040        do putchar('\''); while (*++s == '\'');
     8041        putchar('"');
     8042    } while (*s);
     8043}
     8044
     8045#if !ENABLE_HUSH_LOCAL
     8046#define helper_export_local(argv, exp, lvl) \
     8047    helper_export_local(argv, exp)
     8048#endif
     8049static void helper_export_local(char **argv, int exp, int lvl)
     8050{
     8051    do {
     8052        char *name = *argv;
     8053        char *name_end = strchrnul(name, '=');
     8054
     8055        /* So far we do not check that name is valid (TODO?) */
     8056
     8057        if (*name_end == '\0') {
     8058            struct variable *var, **vpp;
     8059
     8060            vpp = get_ptr_to_local_var(name, name_end - name);
     8061            var = vpp ? *vpp : NULL;
     8062
     8063            if (exp == -1) { /* unexporting? */
     8064                /* export -n NAME (without =VALUE) */
     8065                if (var) {
     8066                    var->flg_export = 0;
     8067                    debug_printf_env("%s: unsetenv '%s'\n", __func__, name);
     8068                    unsetenv(name);
     8069                } /* else: export -n NOT_EXISTING_VAR: no-op */
     8070                continue;
     8071            }
     8072            if (exp == 1) { /* exporting? */
     8073                /* export NAME (without =VALUE) */
     8074                if (var) {
     8075                    var->flg_export = 1;
     8076                    debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr);
     8077                    putenv(var->varstr);
     8078                    continue;
     8079                }
     8080            }
     8081            /* Exporting non-existing variable.
     8082             * bash does not put it in environment,
     8083             * but remembers that it is exported,
     8084             * and does put it in env when it is set later.
     8085             * We just set it to "" and export. */
     8086            /* Or, it's "local NAME" (without =VALUE).
     8087             * bash sets the value to "". */
     8088            name = xasprintf("%s=", name);
     8089        } else {
     8090            /* (Un)exporting/making local NAME=VALUE */
     8091            name = xstrdup(name);
     8092        }
     8093        set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0);
     8094    } while (*++argv);
     8095}
     8096
     8097static int FAST_FUNC builtin_export(char **argv)
     8098{
     8099    unsigned opt_unexport;
     8100
     8101#if ENABLE_HUSH_EXPORT_N
     8102    /* "!": do not abort on errors */
     8103    opt_unexport = getopt32(argv, "!n");
     8104    if (opt_unexport == (uint32_t)-1)
     8105        return EXIT_FAILURE;
     8106    argv += optind;
     8107#else
     8108    opt_unexport = 0;
     8109    argv++;
     8110#endif
     8111
     8112    if (argv[0] == NULL) {
     8113        char **e = environ;
     8114        if (e) {
     8115            while (*e) {
     8116#if 0
     8117                puts(*e++);
     8118#else
     8119                /* ash emits: export VAR='VAL'
     8120                 * bash: declare -x VAR="VAL"
     8121                 * we follow ash example */
     8122                const char *s = *e++;
     8123                const char *p = strchr(s, '=');
     8124
     8125                if (!p) /* wtf? take next variable */
     8126                    continue;
     8127                /* export var= */
     8128                printf("export %.*s", (int)(p - s) + 1, s);
     8129                print_escaped(p + 1);
     8130                putchar('\n');
     8131#endif
     8132            }
     8133            /*fflush_all(); - done after each builtin anyway */
     8134        }
     8135        return EXIT_SUCCESS;
     8136    }
     8137
     8138    helper_export_local(argv, (opt_unexport ? -1 : 1), 0);
     8139
     8140    return EXIT_SUCCESS;
     8141}
     8142
     8143#if ENABLE_HUSH_LOCAL
     8144static int FAST_FUNC builtin_local(char **argv)
     8145{
     8146    if (G.func_nest_level == 0) {
     8147        bb_error_msg("%s: not in a function", argv[0]);
     8148        return EXIT_FAILURE; /* bash compat */
     8149    }
     8150    helper_export_local(argv, 0, G.func_nest_level);
     8151    return EXIT_SUCCESS;
     8152}
     8153#endif
     8154
     8155static int FAST_FUNC builtin_trap(char **argv)
     8156{
     8157    int sig;
     8158    char *new_cmd;
     8159
     8160    if (!G.traps)
     8161        G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
     8162
     8163    argv++;
     8164    if (!*argv) {
     8165        int i;
     8166        /* No args: print all trapped */
     8167        for (i = 0; i < NSIG; ++i) {
     8168            if (G.traps[i]) {
     8169                printf("trap -- ");
     8170                print_escaped(G.traps[i]);
     8171                /* note: bash adds "SIG", but only if invoked
     8172                 * as "bash". If called as "sh", or if set -o posix,
     8173                 * then it prints short signal names.
     8174                 * We are printing short names: */
     8175                printf(" %s\n", get_signame(i));
     8176            }
     8177        }
     8178        /*fflush_all(); - done after each builtin anyway */
     8179        return EXIT_SUCCESS;
     8180    }
     8181
     8182    new_cmd = NULL;
     8183    /* If first arg is a number: reset all specified signals */
     8184    sig = bb_strtou(*argv, NULL, 10);
     8185    if (errno == 0) {
     8186        int ret;
     8187 process_sig_list:
     8188        ret = EXIT_SUCCESS;
     8189        while (*argv) {
     8190            sig = get_signum(*argv++);
     8191            if (sig < 0 || sig >= NSIG) {
     8192                ret = EXIT_FAILURE;
     8193                /* Mimic bash message exactly */
     8194                bb_perror_msg("trap: %s: invalid signal specification", argv[-1]);
     8195                continue;
     8196            }
     8197
     8198            free(G.traps[sig]);
     8199            G.traps[sig] = xstrdup(new_cmd);
     8200
     8201            debug_printf("trap: setting SIG%s (%i) to '%s'\n",
     8202                get_signame(sig), sig, G.traps[sig]);
     8203
     8204            /* There is no signal for 0 (EXIT) */
     8205            if (sig == 0)
     8206                continue;
     8207
     8208            if (new_cmd) {
     8209                sigaddset(&G.blocked_set, sig);
     8210            } else {
     8211                /* There was a trap handler, we are removing it
     8212                 * (if sig has non-DFL handling,
     8213                 * we don't need to do anything) */
     8214                if (sig < 32 && (G.non_DFL_mask & (1 << sig)))
     8215                    continue;
     8216                sigdelset(&G.blocked_set, sig);
     8217            }
     8218        }
     8219        sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
     8220        return ret;
     8221    }
     8222
     8223    if (!argv[1]) { /* no second arg */
     8224        bb_error_msg("trap: invalid arguments");
     8225        return EXIT_FAILURE;
     8226    }
     8227
     8228    /* First arg is "-": reset all specified to default */
     8229    /* First arg is "--": skip it, the rest is "handler SIGs..." */
     8230    /* Everything else: set arg as signal handler
     8231     * (includes "" case, which ignores signal) */
     8232    if (argv[0][0] == '-') {
     8233        if (argv[0][1] == '\0') { /* "-" */
     8234            /* new_cmd remains NULL: "reset these sigs" */
     8235            goto reset_traps;
     8236        }
     8237        if (argv[0][1] == '-' && argv[0][2] == '\0') { /* "--" */
     8238            argv++;
     8239        }
     8240        /* else: "-something", no special meaning */
     8241    }
     8242    new_cmd = *argv;
     8243 reset_traps:
     8244    argv++;
     8245    goto process_sig_list;
     8246}
     8247
     8248/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
     8249static int FAST_FUNC builtin_type(char **argv)
     8250{
     8251    int ret = EXIT_SUCCESS;
     8252
     8253    while (*++argv) {
     8254        const char *type;
     8255        char *path = NULL;
     8256
     8257        if (0) {} /* make conditional compile easier below */
     8258        /*else if (find_alias(*argv))
     8259            type = "an alias";*/
     8260#if ENABLE_HUSH_FUNCTIONS
     8261        else if (find_function(*argv))
     8262            type = "a function";
     8263#endif
     8264        else if (find_builtin(*argv))
     8265            type = "a shell builtin";
     8266        else if ((path = find_in_path(*argv)) != NULL)
     8267            type = path;
     8268        else {
     8269            bb_error_msg("type: %s: not found", *argv);
     8270            ret = EXIT_FAILURE;
     8271            continue;
     8272        }
     8273
     8274        printf("%s is %s\n", *argv, type);
     8275        free(path);
     8276    }
     8277
     8278    return ret;
     8279}
     8280
     8281#if ENABLE_HUSH_JOB
     8282/* built-in 'fg' and 'bg' handler */
     8283static int FAST_FUNC builtin_fg_bg(char **argv)
     8284{
     8285    int i, jobnum;
     8286    struct pipe *pi;
     8287
     8288    if (!G_interactive_fd)
     8289        return EXIT_FAILURE;
     8290
     8291    /* If they gave us no args, assume they want the last backgrounded task */
     8292    if (!argv[1]) {
     8293        for (pi = G.job_list; pi; pi = pi->next) {
     8294            if (pi->jobid == G.last_jobid) {
     8295                goto found;
     8296            }
     8297        }
     8298        bb_error_msg("%s: no current job", argv[0]);
     8299        return EXIT_FAILURE;
     8300    }
     8301    if (sscanf(argv[1], "%%%d", &jobnum) != 1) {
     8302        bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]);
     8303        return EXIT_FAILURE;
     8304    }
     8305    for (pi = G.job_list; pi; pi = pi->next) {
     8306        if (pi->jobid == jobnum) {
     8307            goto found;
     8308        }
     8309    }
     8310    bb_error_msg("%s: %d: no such job", argv[0], jobnum);
     8311    return EXIT_FAILURE;
     8312 found:
     8313    /* TODO: bash prints a string representation
     8314     * of job being foregrounded (like "sleep 1 | cat") */
     8315    if (argv[0][0] == 'f' && G_saved_tty_pgrp) {
     8316        /* Put the job into the foreground.  */
     8317        tcsetpgrp(G_interactive_fd, pi->pgrp);
     8318    }
     8319
     8320    /* Restart the processes in the job */
     8321    debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
     8322    for (i = 0; i < pi->num_cmds; i++) {
     8323        debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
     8324        pi->cmds[i].is_stopped = 0;
     8325    }
     8326    pi->stopped_cmds = 0;
     8327
     8328    i = kill(- pi->pgrp, SIGCONT);
     8329    if (i < 0) {
     8330        if (errno == ESRCH) {
     8331            delete_finished_bg_job(pi);
     8332            return EXIT_SUCCESS;
     8333        }
     8334        bb_perror_msg("kill (SIGCONT)");
     8335    }
     8336
     8337    if (argv[0][0] == 'f') {
     8338        remove_bg_job(pi);
     8339        return checkjobs_and_fg_shell(pi);
     8340    }
     8341    return EXIT_SUCCESS;
     8342}
     8343#endif
     8344
     8345#if ENABLE_HUSH_HELP
     8346static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
     8347{
     8348    const struct built_in_command *x;
     8349
     8350    printf(
     8351        "Built-in commands:\n"
     8352        "------------------\n");
     8353    for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
     8354        if (x->b_descr)
     8355            printf("%-10s%s\n", x->b_cmd, x->b_descr);
     8356    }
     8357    bb_putchar('\n');
     8358    return EXIT_SUCCESS;
     8359}
     8360#endif
     8361
     8362#if ENABLE_HUSH_JOB
     8363static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
     8364{
     8365    struct pipe *job;
     8366    const char *status_string;
     8367
     8368    for (job = G.job_list; job; job = job->next) {
     8369        if (job->alive_cmds == job->stopped_cmds)
     8370            status_string = "Stopped";
     8371        else
     8372            status_string = "Running";
     8373
     8374        printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
     8375    }
     8376    return EXIT_SUCCESS;
     8377}
     8378#endif
     8379
     8380#if HUSH_DEBUG
     8381static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
     8382{
     8383    void *p;
     8384    unsigned long l;
     8385
     8386# ifdef M_TRIM_THRESHOLD
     8387    /* Optional. Reduces probability of false positives */
     8388    malloc_trim(0);
     8389# endif
     8390    /* Crude attempt to find where "free memory" starts,
     8391     * sans fragmentation. */
     8392    p = malloc(240);
     8393    l = (unsigned long)p;
     8394    free(p);
     8395    p = malloc(3400);
     8396    if (l < (unsigned long)p) l = (unsigned long)p;
     8397    free(p);
     8398
     8399    if (!G.memleak_value)
     8400        G.memleak_value = l;
     8401
     8402    l -= G.memleak_value;
     8403    if ((long)l < 0)
     8404        l = 0;
     8405    l /= 1024;
     8406    if (l > 127)
     8407        l = 127;
     8408
     8409    /* Exitcode is "how many kilobytes we leaked since 1st call" */
     8410    return l;
     8411}
     8412#endif
     8413
     8414static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
     8415{
     8416    puts(get_cwd(0));
     8417    return EXIT_SUCCESS;
     8418}
     8419
     8420static int FAST_FUNC builtin_read(char **argv)
     8421{
     8422    const char *r;
     8423    char *opt_n = NULL;
     8424    char *opt_p = NULL;
     8425    char *opt_t = NULL;
     8426    char *opt_u = NULL;
     8427    int read_flags;
     8428
     8429    /* "!": do not abort on errors.
     8430     * Option string must start with "sr" to match BUILTIN_READ_xxx
     8431     */
     8432    read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
     8433    if (read_flags == (uint32_t)-1)
     8434        return EXIT_FAILURE;
     8435    argv += optind;
     8436
     8437    r = shell_builtin_read(set_local_var_from_halves,
     8438        argv,
     8439        get_local_var_value("IFS"), /* can be NULL */
     8440        read_flags,
     8441        opt_n,
     8442        opt_p,
     8443        opt_t,
     8444        opt_u
     8445    );
     8446
     8447    if ((uintptr_t)r > 1) {
     8448        bb_error_msg("%s", r);
     8449        r = (char*)(uintptr_t)1;
     8450    }
     8451
     8452    return (uintptr_t)r;
     8453}
     8454
     8455/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
     8456 * built-in 'set' handler
     8457 * SUSv3 says:
     8458 * set [-abCefhmnuvx] [-o option] [argument...]
     8459 * set [+abCefhmnuvx] [+o option] [argument...]
     8460 * set -- [argument...]
     8461 * set -o
     8462 * set +o
     8463 * Implementations shall support the options in both their hyphen and
     8464 * plus-sign forms. These options can also be specified as options to sh.
     8465 * Examples:
     8466 * Write out all variables and their values: set
     8467 * Set $1, $2, and $3 and set "$#" to 3: set c a b
     8468 * Turn on the -x and -v options: set -xv
     8469 * Unset all positional parameters: set --
     8470 * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
     8471 * Set the positional parameters to the expansion of x, even if x expands
     8472 * with a leading '-' or '+': set -- $x
     8473 *
     8474 * So far, we only support "set -- [argument...]" and some of the short names.
     8475 */
     8476static int FAST_FUNC builtin_set(char **argv)
     8477{
     8478    int n;
     8479    char **pp, **g_argv;
     8480    char *arg = *++argv;
     8481
     8482    if (arg == NULL) {
     8483        struct variable *e;
     8484        for (e = G.top_var; e; e = e->next)
     8485            puts(e->varstr);
     8486        return EXIT_SUCCESS;
     8487    }
     8488
     8489    do {
     8490        if (strcmp(arg, "--") == 0) {
     8491            ++argv;
     8492            goto set_argv;
     8493        }
     8494        if (arg[0] != '+' && arg[0] != '-')
     8495            break;
     8496        for (n = 1; arg[n]; ++n) {
     8497            if (set_mode((arg[0] == '-'), arg[n], argv[1]))
     8498                goto error;
     8499            if (arg[n] == 'o' && argv[1])
     8500                argv++;
     8501        }
     8502    } while ((arg = *++argv) != NULL);
     8503    /* Now argv[0] is 1st argument */
     8504
     8505    if (arg == NULL)
     8506        return EXIT_SUCCESS;
     8507 set_argv:
     8508
     8509    /* NB: G.global_argv[0] ($0) is never freed/changed */
     8510    g_argv = G.global_argv;
     8511    if (G.global_args_malloced) {
     8512        pp = g_argv;
     8513        while (*++pp)
     8514            free(*pp);
     8515        g_argv[1] = NULL;
     8516    } else {
     8517        G.global_args_malloced = 1;
     8518        pp = xzalloc(sizeof(pp[0]) * 2);
     8519        pp[0] = g_argv[0]; /* retain $0 */
     8520        g_argv = pp;
     8521    }
     8522    /* This realloc's G.global_argv */
     8523    G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
     8524
     8525    n = 1;
     8526    while (*++pp)
     8527        n++;
     8528    G.global_argc = n;
     8529
     8530    return EXIT_SUCCESS;
     8531
     8532    /* Nothing known, so abort */
     8533 error:
     8534    bb_error_msg("set: %s: invalid option", arg);
     8535    return EXIT_FAILURE;
     8536}
     8537
     8538static int FAST_FUNC builtin_shift(char **argv)
     8539{
     8540    int n = 1;
     8541    argv = skip_dash_dash(argv);
     8542    if (argv[0]) {
     8543        n = atoi(argv[0]);
     8544    }
     8545    if (n >= 0 && n < G.global_argc) {
     8546        if (G.global_args_malloced) {
     8547            int m = 1;
     8548            while (m <= n)
     8549                free(G.global_argv[m++]);
     8550        }
     8551        G.global_argc -= n;
     8552        memmove(&G.global_argv[1], &G.global_argv[n+1],
     8553                G.global_argc * sizeof(G.global_argv[0]));
     8554        return EXIT_SUCCESS;
     8555    }
     8556    return EXIT_FAILURE;
     8557}
     8558
     8559static int FAST_FUNC builtin_source(char **argv)
     8560{
     8561    char *arg_path, *filename;
     8562    FILE *input;
     8563    save_arg_t sv;
     8564#if ENABLE_HUSH_FUNCTIONS
     8565    smallint sv_flg;
     8566#endif
     8567
     8568    argv = skip_dash_dash(argv);
     8569    filename = argv[0];
     8570    if (!filename) {
     8571        /* bash says: "bash: .: filename argument required" */
     8572        return 2; /* bash compat */
     8573    }
     8574    arg_path = NULL;
     8575    if (!strchr(filename, '/')) {
     8576        arg_path = find_in_path(filename);
     8577        if (arg_path)
     8578            filename = arg_path;
     8579    }
     8580    input = fopen_or_warn(filename, "r");
     8581    free(arg_path);
     8582    if (!input) {
     8583        /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
     8584        return EXIT_FAILURE;
     8585    }
     8586    close_on_exec_on(fileno(input));
     8587
     8588#if ENABLE_HUSH_FUNCTIONS
     8589    sv_flg = G.flag_return_in_progress;
     8590    /* "we are inside sourced file, ok to use return" */
     8591    G.flag_return_in_progress = -1;
     8592#endif
     8593    save_and_replace_G_args(&sv, argv);
     8594
     8595    parse_and_run_file(input);
    38268596    fclose(input);
    3827     if (cwd != bb_msg_unknown)
    3828         free((char*)cwd);
    3829     cur_var = top_var->next;
    3830     while (cur_var) {
    3831         struct variable *tmp = cur_var;
    3832         if (!cur_var->max_len)
    3833             free(cur_var->varstr);
    3834         cur_var = cur_var->next;
    3835         free(tmp);
    3836     }
    3837 #endif
    3838     hush_exit(opt ? opt : last_return_code);
    3839 }
     8597
     8598    restore_G_args(&sv, argv);
     8599#if ENABLE_HUSH_FUNCTIONS
     8600    G.flag_return_in_progress = sv_flg;
     8601#endif
     8602
     8603    return G.last_exitcode;
     8604}
     8605
     8606static int FAST_FUNC builtin_umask(char **argv)
     8607{
     8608    int rc;
     8609    mode_t mask;
     8610
     8611    mask = umask(0);
     8612    argv = skip_dash_dash(argv);
     8613    if (argv[0]) {
     8614        mode_t old_mask = mask;
     8615
     8616        mask ^= 0777;
     8617        rc = bb_parse_mode(argv[0], &mask);
     8618        mask ^= 0777;
     8619        if (rc == 0) {
     8620            mask = old_mask;
     8621            /* bash messages:
     8622             * bash: umask: 'q': invalid symbolic mode operator
     8623             * bash: umask: 999: octal number out of range
     8624             */
     8625            bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
     8626        }
     8627    } else {
     8628        rc = 1;
     8629        /* Mimic bash */
     8630        printf("%04o\n", (unsigned) mask);
     8631        /* fall through and restore mask which we set to 0 */
     8632    }
     8633    umask(mask);
     8634
     8635    return !rc; /* rc != 0 - success */
     8636}
     8637
     8638/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
     8639static int FAST_FUNC builtin_unset(char **argv)
     8640{
     8641    int ret;
     8642    unsigned opts;
     8643
     8644    /* "!": do not abort on errors */
     8645    /* "+": stop at 1st non-option */
     8646    opts = getopt32(argv, "!+vf");
     8647    if (opts == (unsigned)-1)
     8648        return EXIT_FAILURE;
     8649    if (opts == 3) {
     8650        bb_error_msg("unset: -v and -f are exclusive");
     8651        return EXIT_FAILURE;
     8652    }
     8653    argv += optind;
     8654
     8655    ret = EXIT_SUCCESS;
     8656    while (*argv) {
     8657        if (!(opts & 2)) { /* not -f */
     8658            if (unset_local_var(*argv)) {
     8659                /* unset <nonexistent_var> doesn't fail.
     8660                 * Error is when one tries to unset RO var.
     8661                 * Message was printed by unset_local_var. */
     8662                ret = EXIT_FAILURE;
     8663            }
     8664        }
     8665#if ENABLE_HUSH_FUNCTIONS
     8666        else {
     8667            unset_func(*argv);
     8668        }
     8669#endif
     8670        argv++;
     8671    }
     8672    return ret;
     8673}
     8674
     8675/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
     8676static int FAST_FUNC builtin_wait(char **argv)
     8677{
     8678    int ret = EXIT_SUCCESS;
     8679    int status, sig;
     8680
     8681    argv = skip_dash_dash(argv);
     8682    if (argv[0] == NULL) {
     8683        /* Don't care about wait results */
     8684        /* Note 1: must wait until there are no more children */
     8685        /* Note 2: must be interruptible */
     8686        /* Examples:
     8687         * $ sleep 3 & sleep 6 & wait
     8688         * [1] 30934 sleep 3
     8689         * [2] 30935 sleep 6
     8690         * [1] Done                   sleep 3
     8691         * [2] Done                   sleep 6
     8692         * $ sleep 3 & sleep 6 & wait
     8693         * [1] 30936 sleep 3
     8694         * [2] 30937 sleep 6
     8695         * [1] Done                   sleep 3
     8696         * ^C <-- after ~4 sec from keyboard
     8697         * $
     8698         */
     8699        sigaddset(&G.blocked_set, SIGCHLD);
     8700        sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
     8701        while (1) {
     8702            checkjobs(NULL);
     8703            if (errno == ECHILD)
     8704                break;
     8705            /* Wait for SIGCHLD or any other signal of interest */
     8706            /* sigtimedwait with infinite timeout: */
     8707            sig = sigwaitinfo(&G.blocked_set, NULL);
     8708            if (sig > 0) {
     8709                sig = check_and_run_traps(sig);
     8710                if (sig && sig != SIGCHLD) { /* see note 2 */
     8711                    ret = 128 + sig;
     8712                    break;
     8713                }
     8714            }
     8715        }
     8716        sigdelset(&G.blocked_set, SIGCHLD);
     8717        sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
     8718        return ret;
     8719    }
     8720
     8721    /* This is probably buggy wrt interruptible-ness */
     8722    while (*argv) {
     8723        pid_t pid = bb_strtou(*argv, NULL, 10);
     8724        if (errno) {
     8725            /* mimic bash message */
     8726            bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
     8727            return EXIT_FAILURE;
     8728        }
     8729        if (waitpid(pid, &status, 0) == pid) {
     8730            if (WIFSIGNALED(status))
     8731                ret = 128 + WTERMSIG(status);
     8732            else if (WIFEXITED(status))
     8733                ret = WEXITSTATUS(status);
     8734            else /* wtf? */
     8735                ret = EXIT_FAILURE;
     8736        } else {
     8737            bb_perror_msg("wait %s", *argv);
     8738            ret = 127;
     8739        }
     8740        argv++;
     8741    }
     8742
     8743    return ret;
     8744}
     8745
     8746#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
     8747static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
     8748{
     8749    if (argv[1]) {
     8750        def = bb_strtou(argv[1], NULL, 10);
     8751        if (errno || def < def_min || argv[2]) {
     8752            bb_error_msg("%s: bad arguments", argv[0]);
     8753            def = UINT_MAX;
     8754        }
     8755    }
     8756    return def;
     8757}
     8758#endif
     8759
     8760#if ENABLE_HUSH_LOOPS
     8761static int FAST_FUNC builtin_break(char **argv)
     8762{
     8763    unsigned depth;
     8764    if (G.depth_of_loop == 0) {
     8765        bb_error_msg("%s: only meaningful in a loop", argv[0]);
     8766        return EXIT_SUCCESS; /* bash compat */
     8767    }
     8768    G.flag_break_continue++; /* BC_BREAK = 1 */
     8769
     8770    G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
     8771    if (depth == UINT_MAX)
     8772        G.flag_break_continue = BC_BREAK;
     8773    if (G.depth_of_loop < depth)
     8774        G.depth_break_continue = G.depth_of_loop;
     8775
     8776    return EXIT_SUCCESS;
     8777}
     8778
     8779static int FAST_FUNC builtin_continue(char **argv)
     8780{
     8781    G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */
     8782    return builtin_break(argv);
     8783}
     8784#endif
     8785
     8786#if ENABLE_HUSH_FUNCTIONS
     8787static int FAST_FUNC builtin_return(char **argv)
     8788{
     8789    int rc;
     8790
     8791    if (G.flag_return_in_progress != -1) {
     8792        bb_error_msg("%s: not in a function or sourced script", argv[0]);
     8793        return EXIT_FAILURE; /* bash compat */
     8794    }
     8795
     8796    G.flag_return_in_progress = 1;
     8797
     8798    /* bash:
     8799     * out of range: wraps around at 256, does not error out
     8800     * non-numeric param:
     8801     * f() { false; return qwe; }; f; echo $?
     8802     * bash: return: qwe: numeric argument required  <== we do this
     8803     * 255  <== we also do this
     8804     */
     8805    rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
     8806    return rc;
     8807}
     8808#endif
  • branches/2.2.9/mindi-busybox/shell/hush_test/hush-misc/syntax_err.right

    r1765 r2725  
    11shown
    22hush: syntax error: unterminated '
     3test
     4not shown
  • branches/2.2.9/mindi-busybox/shell/hush_test/hush-vars/var_subst_in_for.tests

    r1765 r2725  
    11if test $# = 0; then
    2     exec "$THIS_SH" var_subst_in_for.tests abc "d e"
     2    exec "$THIS_SH" "$0" abc "d e"
    33fi
    44
  • branches/2.2.9/mindi-busybox/shell/hush_test/hush-z_slow/leak_var.right

    r1765 r2725  
    11Measuring memory leak...
    2 vsz does not grow
     2Ok
  • branches/2.2.9/mindi-busybox/shell/hush_test/hush-z_slow/leak_var.tests

    r1765 r2725  
    1 pid=$$
    2 
    3 # Warm up
    4 unset t
    5 t=111111111111111111111111111111111111111111111111111111111111111111111111
    6 export t
    7 unset t
    8 t=111111111111111111111111111111111111111111111111111111111111111111111111
    9 export t
    10 unset t
    11 t=111111111111111111111111111111111111111111111111111111111111111111111111
    12 export t
    13 unset t
    14 t=111111111111111111111111111111111111111111111111111111111111111111111111
    15 export t
    16 unset t
    17 t=111111111111111111111111111111111111111111111111111111111111111111111111
    18 export t
    19 i=1
    20 if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi
    21 beg=`ps -o pid,vsz | grep "^ *$pid "`
    22 
    231echo "Measuring memory leak..."
    24 beg=`ps -o pid,vsz | grep "^ *$pid "`
    252i=1
    263while test $i != X; do
     
    6037    if test $i = 111111111111111111111111111111111111111111111i; then i=j; fi
    6138    if test $i = 111111111111111111111111111111111111111111111j; then i=X; fi
     39    memleak
    6240done
    63 end=`ps -o pid,vsz | grep "^ *$pid "`
    64 
    65 if test "$beg" != "$end"; then
    66     echo "vsz grows: $beg -> $end"
     41memleak
     42kb=$?
     43if test $kb -le 4; then
     44    echo Ok
    6745else
    68     echo "vsz does not grow"
     46    echo "Bad: $kb kb (or more) leaked"
    6947fi
  • branches/2.2.9/mindi-busybox/shell/hush_test/run-all

    r1765 r2725  
    11#!/bin/sh
    22
    3 test -x hush || { echo "No ./hush?!"; exit; }
     3unset LANG LANGUAGE
     4unset LC_COLLATE
     5unset LC_CTYPE
     6unset LC_MONETARY
     7unset LC_MESSAGES
     8unset LC_NUMERIC
     9unset LC_TIME
     10unset LC_ALL
    411
    5 PATH="$PWD:$PATH" # for hush and recho/zecho/printenv
     12if test ! -x hush; then
     13    if test ! -x ../../busybox; then
     14        echo "Can't run tests. Put hush binary into this directory (`pwd`)"
     15        exit 1
     16    fi
     17    echo "No ./hush - creating a link to ../../busybox"
     18    ln -s ../../busybox hush
     19fi
     20if test ! -f .config; then
     21    if test ! -f ../../.config; then
     22        echo "Missing .config file"
     23        exit 1
     24    fi
     25    cp ../../.config . || exit 1
     26fi
     27
     28eval $(sed -e '/^#/d' -e '/^$/d' -e 's:^:export :' .config)
     29
     30PATH="`pwd`:$PATH" # for hush and recho/zecho/printenv
    631export PATH
    732
    8 THIS_SH="$PWD/hush"
     33THIS_SH="`pwd`/hush"
    934export THIS_SH
    1035
    1136do_test()
    1237{
    13     test -d "$1" || return 0
    14     (
    15     cd "$1" || { echo "cannot cd $1!"; exit 1; }
    16     for x in run-*; do
    17     test -f "$x" || continue
    18     case "$x" in
    19         "$0"|run-minimal|run-gprof) ;;
    20         *.orig|*~) ;;
    21         #*) echo $x ; sh $x ;;
    22         *)
    23         sh "$x" >"../$1-$x.fail" 2>&1 && \
    24         { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
    25         ;;
    26     esac
    27     done
    28     # Many bash run-XXX scripts just do this,
    29     # no point in duplication it all over the place
    30     for x in *.tests; do
     38    test -d "$1" || return 0
     39    d=${d%/}
     40#   echo Running tests in directory "$1"
     41    (
     42    tret=0
     43    cd "$1" || { echo "cannot cd $1!"; exit 1; }
     44    for x in run-*; do
     45        test -f "$x" || continue
     46        case "$x" in
     47            "$0"|run-minimal|run-gprof) ;;
     48            *.orig|*~) ;;
     49            #*) echo $x ; sh $x ;;
     50            *)
     51            sh "$x" >"../$1-$x.fail" 2>&1 && \
     52            { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
     53            ;;
     54        esac
     55    done
     56    # Many bash run-XXX scripts just do this,
     57    # no point in duplication it all over the place
     58    for x in *.tests; do
    3159    test -x "$x" || continue
    3260    name="${x%%.tests}"
    3361    test -f "$name.right" || continue
    34     {
    35         "$THIS_SH" "./$x" >"$name.xx" 2>&1
    36         diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
    37     } && echo "$1/$x: ok" || echo "$1/$x: fail"
    38     done
    39     )
     62#   echo Running test: "$x"
     63    (
     64        "$THIS_SH" "./$x" >"$name.xx" 2>&1
     65        # filter C library differences
     66        sed -i \
     67            -e "/: invalid option /s:'::g" \
     68            "$name.xx"
     69        test $? -eq 77 && rm -f "../$1-$x.fail" && exit 77
     70        diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
     71    )
     72    case $? in
     73        0)  echo "$1/$x: ok";;
     74        77) echo "$1/$x: skip (feature disabled)";;
     75        *)  echo "$1/$x: fail"; tret=1;;
     76    esac
     77    done
     78    exit ${tret}
     79    )
    4080}
    4181
     
    4383# Usage: run-all [directories]
    4484
     85ret=0
     86
    4587if [ $# -lt 1 ]; then
    46     # All sub directories
    47     modules=`ls -d hush-*`
     88    # All sub directories
     89    modules=`ls -d hush-*`
    4890
    49     for module in $modules; do
    50     do_test $module
    51     done
     91    for module in $modules; do
     92    do_test $module || ret=1
     93    done
    5294else
    53     while [ $# -ge 1 ]; do
     95    while [ $# -ge 1 ]; do
    5496    if [ -d $1 ]; then
    55         do_test $1
     97        do_test $1 || ret=1
    5698    fi
    5799    shift
    58     done
     100    done
    59101fi
     102
     103exit ${ret}
Note: See TracChangeset for help on using the changeset viewer.