This is a maintenance fork
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

10851 lines
290 KiB

/*bin/echo ' #-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;coding:utf-8 -*-┤
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
├──────────────────────────────────────────────────────────────────────────────┤
│███▒ ▓░░░▒ █▓█▓ ▒▒███████▓█████ ██▓▓▓███▒██▒▓█▓████████ ▓██▓█████▓██ ░ ░▒ ░ │
│█░ ░ █░▒▒▒ █▓▓▓ ▒▓████▓░███▓█▓▓▓▓█▓▒▓▓███▓▒▒██▓▓█▓█████ ▒██▓█████▓██ ░ ▒░░░ ░│
│███▓ ▒ ▓██▓ █████▓░█▓▓█▓██░░█░████▓█▓██▒██▒▓▓▓█▓▓█▓█▒██▓█████▓██░▒ ▓░ │
│████ █▓ ▒███▓██████████████████████████████▓▓█▓█████▓████████▓██ ▒▓▓▓▒░▒ ░│
│███▓ ▓▓▓▓ ▒███████████▓████████████▓▓██▓▓▓█▓████▓███▓█████▓██ ▒░▓▒░░ ░│
│███▓ █▒▓▒▒░▓ ▒██▓████████████████████████████▓██▓▓████████▓██ ░░▒▒▓▓▒ ░│
│███▓ ▓▒▓▓▓ █▓▓ ▒███████████████████████████▓██▓▓░███████▓██ ░ ▒▒░░▓▓░│
│█▓█▓ █▒░░░ █▓██▓ ██████████████████████████▓█████████▓▓░░░ ▒░ ░░ ▒│
│█▓█▓░█▓▒▓▒ ██▓█▓ ▓ ▓██████████████████▒██▓████████ ▒░▒▒▒▒▒▒▒ ░│
│█▓▓▒░▓▓▒▓▓ █▓▓██ ██ ░ ░ ░░░░ ░░░ ░ ███████████████▓██▓██████ ░▒▓▓▒▓▓░▒▓░▒░ │
│██▓▓░▓▒▒▓▒░▓▓▓▓▓ █▓▓░ ░ ░░ ░ ░ ▓█████████████▒██▓████▒▒▒░░▓▓▒▓▒░▒░░▒░ ░│
│█▓▓▓░█▒▒▒▒ █▓▓▓▓ ██▓▓ ░ ░ ▒▒ ██████████▒██▓███░░░▒▒▓▓▓▓▒░░ ░ │
│██▓▓░█░▒▒▒░█▓▓▓▓ █▓▓█ ░ ░ ░░░░▓░ ████████▒██ ░░░▓▒▒▓▓▓▓ ▓▒░▒▒▒▒░░ │
│▒██▓▒▓░▒▓░░▓▓▓▓▓ ▓▓▓▓ ░░ ▒░░▒░░ ▓███████▒██░ ▒ ▒░░▒▓▒▒░▒▒▓▒ ░ │
│▒▓▓▓▒█▒▒▒▒░█▒▒▓▓ ▓▓█▓▓▓▓▓ ░▒ ░ ▒ ░▓▓▓░░░ █▒ ▓ ██▓███▒▓░▓▓▒░▓░░░░▒▒░░░ ░│
│▒█▒▓░▓▓░▓▓░▓▒▓▒▒ █▓████▓███ ░ ░▒░ ▓▓▒▒▒▒░░▓ ░ ░█ ▓ ▒█████░░ ▓▓▒▒░░▒▒▓▒░ ░░│
│░▓█▓▒▓▓▒▓▒░█▓▒▓▓ █▓████▓███▓▓ ░▒▒▒▓▓▒▒▓▓▒░▒▓░ ▒ ░██ ░ ███▒░▓░ ▓▒▓▒▒░ ░ │
│░█▓▓▓▓▓▓▒▒▓█▓▒▓▓ █▓▓███▓█████▓ ░▒▒▒▒▒▓▒▒░░▒░▓ ░░▓▒ ▒░▒░ ░▒ ▓░▓▓░░ ▓▓░ ░░░░ ░░ │
│ ██░ ░ ▒█▓▓▓▓ ██████▓██████▓░▒▒▒▓▓▓▓▓▓▒▒░▓▒░▓▒▒▒▒▓▓▓░▓▒▒░▒▒▓▓▒▒░▒ ░ ░ ░░│
│ ░░░▒ ▓▓▓▓ █▓▓█▓█▓███████▓░▒▓▒▓▓█▒░▒▓▓▓▒▒▓▓▒▓▓▒█░▒▓▒▒ ░▓▒▓ ▒▒░▒░ ▒ ░░░ │
│ ▒░░ ░ ▓ ░▓▓▓▓ ████▓█▓████████▒▓▒█░░▓▒▓ ▒▓▓░▒▒▓▒▒▓▒▓▒▒▓▒▓▓█▓▓ ▓░▒ ░░░░░ ░ │
│ ░▓ ▒░░░░ ▓█▓▓▓ █▓▒███▓█████████▓░░▒▒▓▒▓▒▒▓▓▒▓▓ ▓▒▓▓▒▓▒▓▒▓▓▒▓▓ ▓▒▒░▒ ▓▒ ░ │
│ ▒░ ░ ░░ ▓▒░░░ █▓▓██▓▓████▓▓▓█▓░░▒▓▓▓▒▒▓░▓▓▓▒▓▒▒▒▓▒▓▓▓▓▒▒▒▒▒░▒▒▒░██ │
│▒ ▒▒ ▒░░░ ▓▓▓▓▒ ▓▓▓███▒███▓███▓▓▒▒░░▒▒▒▒░▒▒▒▒▒▓▓▒▒░▓▓▓▒▓░▓▓▓▒▒▒▓░▓▓▓ ░ ░ │
│▒ ░ ▓ ▓▓▓▓▒ ▓▒▓▓▓█▒███▓▓▓█▒▒▓▓███████▓▒▓░▒▓░▓▒▓▒▓▓▒▒▒▓░▒▒░ ░▒▓▒░ │
│▓ ░░ ░ █ ▓▓▓▓▓ ▓▓▓█▓▓▓███▓█▓█▓████████████░▓▓▓▓▒▓▓▓▓▒▒▓▒▒▒ ▓▓ ▓░ ░▓ ░ │
│▓ ░ ░ ▒▓▓▓▒▒ ▓▓▓█▓█▒██▓▓▓█▓█▓█▓██████████░▓▒▒▓▓▓▒▓▓▓▒ ▒▓ ▒ ░▒▓▓▓█ ░ │
│▓▒░░▒▒░▒ ░░▓▓▓▓▓ ▓▓▓███▓██████████████████████░░▓▒░▓▒▒▒▒▒▒▓▓░ ░▒▒░░ │
│▓ ░▓▒▒ ░░▒▓▒ ░▓▓█▓█▓█████████████████████▒░░ ░ ░ ░▒░ ░ ░ ░▓▒ │
└──────────────────────────────────────────────────────────────────────────────┘
unbourne is a gnu/systemd init process »cosmopolitan»
╔────────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § the unbourne shell ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│─╝
The UNBOURNE SHELL is BASED off the Almquist Shell──colloquially
known as the Debian Almquist Shell, a.k.a. dash──which perfected
the work of the late Stephen Bourne whose shell set the standard
for decades thanks to a spark of brilliance from Ken Thompson.
git://git.kernel.org/pub/scm/utils/dash/dash.git
fba95e9e4a5d0f1f1ac9f7d86557e47bc0e2656c
Tue Jun 19 11:27:37 2018 -0400
The UNBOURNE SHELL pays HOMAGE to the Stewards of House California:
Almquist Shell
Derived from software contributed to Berkeley by Kenneth Almquist.
Copyright 1991,1993 The Regents of the University of California
Copyright 1997-2018 Herbert Xu
Copyright 1997 Christos Zoulas
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. Neither the name of the University nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
╔──────────────────────────────────────────────────────────────────────┬───┬───╗
│ cosmopolitan § the unbourne shell » build / / │
╚────────────────────────────────────────────────────────────────────'>/dev/null
cc -Os -o unbourne unbourne.c
exit
╔────────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § the unbourne shell » macros ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
#include "libc/alg/alg.h"
#include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/tms.h"
#include "libc/calls/termios.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/mem/alloca.h"
#include "libc/mem/mem.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/rlim.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/w.h"
#include "third_party/gdtoa/gdtoa.h"
#include "third_party/musl/passwd.h"
#define likely(expr) __builtin_expect(!!(expr), 1)
#define unlikely(expr) __builtin_expect(!!(expr), 0)
#undef CEOF
#undef rflag
/*
* The follow should be set to reflect the type of system you have:
* JOBS -> 1 if you have Berkeley job control, 0 otherwise.
* SHORTNAMES -> 1 if your linker cannot handle long names.
* define BSD if you are running 4.2 BSD or later.
* define SYSV if you are running under System V.
* define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
* define DEBUG=2 to compile in and turn on debugging.
* define DO_SHAREDVFORK to indicate that vfork(2) shares its address
* with its parent.
*
* When debugging is on, debugging info will be written to ./trace and
* a quit signal will generate a core dump.
*/
#define ALIASDEAD 2
#define ALIASINUSE 1
#define ARITH_MAX_PREC 8
#define ATABSIZE 39
#define CMDTABLESIZE 31
#define JOBS 1
#define NOPTS 17
#define OUTPUT_ERR 01
#define VTABSIZE 39
/* exceptions */
#define EXINT 0
#define EXERROR 1
#define EXEXIT 4
/*
* The input line number. Input.c just defines this variable, and saves
* and restores it when files are pushed and popped. The user of this
* package must set its value.
*/
#define plinno (parsefile->linno)
/* Syntax classes */
#define CWORD 0
#define CNL 1
#define CBACK 2
#define CSQUOTE 3
#define CDQUOTE 4
#define CENDQUOTE 5
#define CBQUOTE 6
#define CVAR 7
#define CENDVAR 8
#define CLP 9
#define CRP 10
#define CEOF 11
#define CCTL 12
#define CSPCL 13
#define CIGN 14
/* Syntax classes for is_ functions */
#define ISDIGIT 01
#define ISUPPER 02
#define ISLOWER 04
#define ISUNDER 010
#define ISSPECL 020
#define SYNBASE 130
#define PEOF -130
#define EOF_NLEFT -99
#define PEOA -129
#define BASESYNTAX (basesyntax + SYNBASE)
#define DQSYNTAX (dqsyntax + SYNBASE)
#define SQSYNTAX (sqsyntax + SYNBASE)
#define ARISYNTAX (arisyntax + SYNBASE)
#define ARITH_ASS 1
#define ARITH_OR 2
#define ARITH_AND 3
#define ARITH_BAD 4
#define ARITH_NUM 5
#define ARITH_VAR 6
#define ARITH_NOT 7
#define ARITH_BINOP_MIN 8
#define ARITH_LE 8
#define ARITH_GE 9
#define ARITH_LT 10
#define ARITH_GT 11
#define ARITH_EQ 12
#define ARITH_REM 13
#define ARITH_BAND 14
#define ARITH_LSHIFT 15
#define ARITH_RSHIFT 16
#define ARITH_MUL 17
#define ARITH_ADD 18
#define ARITH_BOR 19
#define ARITH_SUB 20
#define ARITH_BXOR 21
#define ARITH_DIV 22
#define ARITH_NE 23
#define ARITH_BINOP_MAX 24
#define ARITH_ASS_MIN 24
#define ARITH_REMASS 24
#define ARITH_BANDASS 25
#define ARITH_LSHIFTASS 26
#define ARITH_RSHIFTASS 27
#define ARITH_MULASS 28
#define ARITH_ADDASS 29
#define ARITH_BORASS 30
#define ARITH_SUBASS 31
#define ARITH_BXORASS 32
#define ARITH_DIVASS 33
#define ARITH_ASS_MAX 34
#define ARITH_LPAREN 34
#define ARITH_RPAREN 35
#define ARITH_BNOT 36
#define ARITH_QMARK 37
#define ARITH_COLON 38
/* expandarg() flags */
#define EXP_FULL 0x1
#define EXP_TILDE 0x2
#define EXP_VARTILDE 0x4
#define EXP_REDIR 0x8
#define EXP_CASE 0x10
#define EXP_VARTILDE2 0x40
#define EXP_WORD 0x80
#define EXP_QUOTED 0x100
#define EXP_KEEPNUL 0x200
#define EXP_DISCARD 0x400
/* reasons for skipping commands (see comment on breakcmd routine) */
#define SKIPBREAK (1 << 0)
#define SKIPCONT (1 << 1)
#define SKIPFUNC (1 << 2)
#define SKIPFUNCDEF (1 << 3)
#define TEOF 0
#define TNL 1
#define TSEMI 2
#define TBACKGND 3
#define TAND 4
#define TOR 5
#define TPIPE 6
#define TLP 7
#define TRP 8
#define TENDCASE 9
#define TENDBQUOTE 10
#define TREDIR 11
#define TWORD 12
#define TNOT 13
#define TCASE 14
#define TDO 15
#define TDONE 16
#define TELIF 17
#define TELSE 18
#define TESAC 19
#define TFI 20
#define TFOR 21
#define TIF 22
#define TIN 23
#define TTHEN 24
#define TUNTIL 25
#define TWHILE 26
#define TBEGIN 27
#define TEND 28
/* control characters in argument strings */
#define CTL_FIRST -127
#define CTLESC -127
#define CTLVAR -126
#define CTLENDVAR -125
#define CTLBACKQ -124
#define CTLARI -122
#define CTLENDARI -121
#define CTLQUOTEMARK -120
#define CTL_LAST -120
/* variable substitution byte (follows CTLVAR) */
#define VSTYPE 0x0f
#define VSNUL 0x10
/* values of VSTYPE field */
#define VSNORMAL 0x1
#define VSMINUS 0x2
#define VSPLUS 0x3
#define VSQUESTION 0x4
#define VSASSIGN 0x5
#define VSTRIMRIGHT 0x6
#define VSTRIMRIGHTMAX 0x7
#define VSTRIMLEFT 0x8
#define VSTRIMLEFTMAX 0x9
#define VSLENGTH 0xa
/* values of checkkwd variable */
#define CHKALIAS 0x1
#define CHKKWD 0x2
#define CHKNL 0x4
#define CHKEOFMARK 0x8
/* flags in argument to evaltree */
#define EV_EXIT 01
#define EV_TESTED 02
#define INT_CHARS (sizeof(int) * CHAR_BIT / 3)
/*
* These macros allow the user to suspend the handling of interrupt
* signals over a period of time. This is similar to SIGHOLD to or
* sigblock, but much more efficient and portable. (But hacking the
* kernel is so much more fun than worrying about efficiency and
* portability. :-))
*/
#define barrier() ({ asm volatile("" : : : "memory"); })
#define INTOFF \
({ \
suppressint++; \
barrier(); \
0; \
})
#define INTON \
({ \
barrier(); \
if (--suppressint == 0 && intpending) onint(); \
0; \
})
#define FORCEINTON \
({ \
barrier(); \
suppressint = 0; \
if (intpending) onint(); \
0; \
})
#define SAVEINT(v) ((v) = suppressint)
#define RESTOREINT(v) \
({ \
barrier(); \
if ((suppressint = (v)) == 0 && intpending) onint(); \
0; \
})
#define CLEAR_PENDING_INT intpending = 0
#define int_pending() intpending
/*
* Most machines require the value returned from malloc to be aligned
* in some way. The following macro will get this right on many machines.
*/
#define SHELL_SIZE \
(sizeof(union { \
int i; \
char *cp; \
double d; \
}) - \
1)
/*
* It appears that grabstackstr() will barf with such alignments
* because stalloc() will return a string allocated in a new stackblock.
*/
#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
/*
* Minimum size of a block
*
* Parse trees for commands are allocated in lifo order, so we use a stack
* to make this more efficient, and also to avoid all sorts of exception
* handling code to handle interrupts in the middle of a parse.
*
* The size 504 was chosen because the Ultrix malloc handles that size
* well.
*/
#define MINSIZE SHELL_ALIGN(504)
/* flags */
#define VEXPORT 0x001
#define VREADONLY 0x002
#define VSTRFIXED 0x004
#define VTEXTFIXED 0x008
#define VSTACK 0x010
#define VUNSET 0x020
#define VNOFUNC 0x040
#define VNOSET 0x080
#define VNOSAVE 0x100
/*
* Evaluate a command.
*/
#define ALIASCMD (kBuiltinCmds + 3)
#define BGCMD (kBuiltinCmds + 4)
#define BREAKCMD (kBuiltinCmds + 5)
#define CDCMD (kBuiltinCmds + 6)
#define COMMANDCMD (kBuiltinCmds + 8)
#define DOTCMD (kBuiltinCmds + 0)
#define ECHOCMD (kBuiltinCmds + 10)
#define EVALCMD (kBuiltinCmds + 11)
#define EXECCMD (kBuiltinCmds + 12)
#define EXITCMD (kBuiltinCmds + 13)
#define EXPORTCMD (kBuiltinCmds + 14)
#define FALSECMD (kBuiltinCmds + 15)
#define FGCMD (kBuiltinCmds + 16)
#define GETOPTSCMD (kBuiltinCmds + 17)
#define HASHCMD (kBuiltinCmds + 18)
#define JOBSCMD (kBuiltinCmds + 19)
#define KILLCMD (kBuiltinCmds + 20)
#define LOCALCMD (kBuiltinCmds + 21)
#define PRINTFCMD (kBuiltinCmds + 22)
#define PWDCMD (kBuiltinCmds + 23)
#define READCMD (kBuiltinCmds + 24)
#define RETURNCMD (kBuiltinCmds + 26)
#define SETCMD (kBuiltinCmds + 27)
#define SHIFTCMD (kBuiltinCmds + 28)
#define TESTCMD (kBuiltinCmds + 2)
#define TIMESCMD (kBuiltinCmds + 30)
#define TRAPCMD (kBuiltinCmds + 31)
#define TRUECMD (kBuiltinCmds + 1)
#define TYPECMD (kBuiltinCmds + 33)
#define ULIMITCMD (kBuiltinCmds + 34)
#define UMASKCMD (kBuiltinCmds + 35)
#define UNALIASCMD (kBuiltinCmds + 36)
#define UNSETCMD (kBuiltinCmds + 37)
#define WAITCMD (kBuiltinCmds + 38)
#define BUILTIN_SPECIAL 0x1
#define BUILTIN_REGULAR 0x2
#define BUILTIN_ASSIGN 0x4
/* mode flags for set_curjob */
#define CUR_DELETE 2
#define CUR_RUNNING 1
#define CUR_STOPPED 0
/* mode flags for dowait */
#define DOWAIT_NORMAL 0
#define DOWAIT_BLOCK 1
#define DOWAIT_WAITCMD 2
/* _rmescape() flags */
#define RMESCAPE_ALLOC 0x01
#define RMESCAPE_GLOB 0x02
#define RMESCAPE_GROW 0x08
#define RMESCAPE_HEAP 0x10
/* Add CTLESC when necessary. */
#define QUOTES_ESC (EXP_FULL | EXP_CASE)
#define IBUFSIZ (BUFSIZ + 1)
#define OUTBUFSIZ BUFSIZ
#define MEM_OUT -3
/*
* Sigmode records the current value of the signal handlers for the
* various modes. A value of zero means that the current handler is not
* known. S_HARD_IGN indicates that the signal was ignored on entry to
* the shell,
*/
#define S_DFL 1
#define S_CATCH 2
#define S_IGN 3
#define S_HARD_IGN 4
#define S_RESET 5
#define NCMD 0
#define NPIPE 1
#define NREDIR 2
#define NBACKGND 3
#define NSUBSHELL 4
#define NAND 5
#define NOR 6
#define NSEMI 7
#define NIF 8
#define NWHILE 9
#define NUNTIL 10
#define NFOR 11
#define NCASE 12
#define NCLIST 13
#define NDEFUN 14
#define NARG 15
#define NTO 16
#define NCLOBBER 17
#define NFROM 18
#define NFROMTO 19
#define NAPPEND 20
#define NTOFD 21
#define NFROMFD 22
#define NHERE 23
#define NXHERE 24
#define NNOT 25
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
#define FORK_FG 0
#define FORK_BG 1
#define FORK_NOJOB 2
/* mode flags for showjob(s) */
#define SHOW_PGID 0x01
#define SHOW_PID 0x04
#define SHOW_CHANGED 0x08
/* values of cmdtype */
#define CMDUNKNOWN -1
#define CMDNORMAL 0
#define CMDFUNCTION 1
#define CMDBUILTIN 2
/* action to find_command() */
#define DO_ERR 0x01
#define DO_ABS 0x02
#define DO_NOFUNC 0x04
#define DO_ALTPATH 0x08
#define DO_REGBLTIN 0x10
/* flags passed to redirect */
#define REDIR_PUSH 01
#define REDIR_SAVEFD2 03
#define CD_PHYSICAL 1
#define CD_PRINT 2
#define REALLY_CLOSED -3
#define EMPTY -2
#define CLOSED -1
#define PIPESIZE 4096
#define rootshell (!shlvl)
#define eflag optlist[0]
#define fflag optlist[1]
#define Iflag optlist[2]
#define iflag optlist[3]
#define mflag optlist[4]
#define nflag optlist[5]
#define sflag optlist[6]
#define xflag optlist[7]
#define vflag optlist[8]
#define Vflag optlist[9]
#define Eflag optlist[10]
#define Cflag optlist[11]
#define aflag optlist[12]
#define bflag optlist[13]
#define uflag optlist[14]
#define nolog optlist[15]
#define debug optlist[16]
/* Used by expandstr to get here-doc like behaviour. */
#define FAKEEOFMARK (char *)1
/*
* This file is included by programs which are optionally built into the
* shell. If SHELL is defined, we try to map the standard UNIX library
* routines to ash routines using defines.
*/
/* #undef stdout /\* TODO(jart): XXX *\/ */
/* #undef stderr */
/* #undef putc */
/* #undef putchar */
#undef fileno
/* #define stdout out1 */
/* #define stderr out2 */
#undef printf
#define printf out1fmt
/* #define putc(c, file) outc(c, file) */
/* #define putchar(c) out1c(c) */
#define FILE struct output
#define fileno(f) ((f)->fd)
/* #define ferror outerr */
#define INITARGS(argv)
#define setprogname(s)
#define getprogname() commandname
#define setlocate(l, s) 0
#define equal(s1, s2) (strcmp(s1, s2) == 0)
#define getenv(p) bltinlookup((p), 0)
#define isodigit(c) ((c) >= '0' && (c) <= '7')
#define octtobin(c) ((c) - '0')
#define scopy(s1, s2) ((void)strcpy(s2, s1))
#define TRACE(param)
/* #define TRACE(param) \ */
/* do { \ */
/* printf("TRACE: "); \ */
/* printf param; \ */
/* } while (0) */
#define TRACEV(param)
#define digit_val(c) ((c) - '0')
#define is_alpha(c) isalpha((unsigned char)(c))
#define is_digit(c) ((unsigned)((c) - '0') <= 9)
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
#define is_special(c) ((is_type + SYNBASE)[(signed char)(c)] & (ISSPECL | ISDIGIT))
#define uninitialized_var(x) x = x /* suppress uninitialized warning w/o code */
/*
* Shell variables.
*/
#define vifs varinit[0]
#define vpath (&vifs)[1]
#define vps1 (&vpath)[1]
#define vps2 (&vps1)[1]
#define vps4 (&vps2)[1]
#define voptind (&vps4)[1]
#define vlineno (&voptind)[1]
#define defifs (defifsvar + 4)
#define defpath (defpathvar + 36)
/*
* The following macros access the values of the above variables. They
* have to skip over the name. They return the null string for unset
* variables.
*/
#define ifsval() (vifs.text + 4)
#define ifsset() ((vifs.flags & VUNSET) == 0)
#define mailval() (vmail.text + 5)
#define mpathval() (vmpath.text + 9)
#define pathval() (vpath.text + 5)
#define ps1val() (vps1.text + 4)
#define ps2val() (vps2.text + 4)
#define ps4val() (vps4.text + 4)
#define optindval() (voptind.text + 7)
#define linenoval() (vlineno.text + 7)
#define mpathset() ((vmpath.flags & VUNSET) == 0)
#define environment() listvars(VEXPORT, VUNSET, 0)
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § the unbourne shell » data structures ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
typedef void *pointer;
struct redirtab {
struct redirtab *next;
int renamed[10];
};
/*
* We enclose jmp_buf in a structure so that we can declare pointers to
* jump locations. The global variable handler contains the location to
* jump to when an exception occurs, and the global variable exception
* contains a code identifying the exeception. To implement nested
* exception handlers, the user should save the value of handler on
* entry to an inner scope, set handler to point to a jmploc structure
* for the inner scope, and restore handler on exit from the scope.
*/
struct jmploc {
jmp_buf loc;
};
/* PEOF (the end of file marker) is defined in syntax.h */
enum {
INPUT_PUSH_FILE = 1,
INPUT_NOFILE_OK = 2,
};
struct alias {
struct alias *next;
char *name;
char *val;
int flag;
};
struct shparam {
int nparam; /* # of positional parameters (without $0) */
unsigned char malloc; /* if parameter list dynamically allocated */
char **p; /* parameter list */
int optind; /* next parameter to be processed by getopts */
int optoff; /* used by getopts */
};
struct strpush {
struct strpush *prev; /* preceding string on stack */
char *prevstring;
int prevnleft;
struct alias *ap; /* if push was associated with an alias */
char *string; /* remember the string since it may change */
int lastc[2]; /* Remember last two characters for pungetc. */
int unget; /* Number of outstanding calls to pungetc. */
};
/*
* The parsefile structure pointed to by the global variable parsefile
* contains information about the current file being read.
*/
struct parsefile {
struct parsefile *prev; /* preceding file on stack */
int linno; /* current line */
int fd; /* file descriptor (or -1 if string) */
int nleft; /* number of chars left in this line */
int lleft; /* number of chars left in this buffer */
char *nextc; /* next char in buffer */
char *buf; /* input buffer */
struct strpush *strpush; /* for pushing strings at this level */
struct strpush basestrpush; /* so pushing one is fast */
int lastc[2]; /* Remember last two characters for pungetc. */
int unget; /* Number of outstanding calls to pungetc. */
};
struct output {
char *nextc;
char *end;
char *buf;
unsigned bufsize;
int fd;
int flags;
};
struct heredoc {
struct heredoc *next; /* next here document in list */
union node *here; /* redirection node */
char *eofmark; /* string indicating end of input */
int striptabs; /* if set, strip leading tabs */
};
struct synstack {
const char *syntax;
struct synstack *prev;
struct synstack *next;
int innerdq;
int varpushed;
int dblquote;
int varnest; /* levels of variables expansion */
int parenlevel; /* levels of parens in arithmetic */
int dqvarnest; /* levels of variables expansion within double quotes */
};
struct procstat {
int pid; /* process id */
int status; /* last process status from wait() */
char *cmd; /* text of command being run */
};
/*
* A job structure contains information about a job. A job is either a
* single process or a set of processes contained in a pipeline. In the
* latter case, pidlist will be non-NULL, and will point to a -1 terminated
* array of pids.
*/
struct job {
struct procstat ps0; /* status of process */
struct procstat *ps; /* status or processes when more than one */
int stopstatus; /* status of a stopped job */
unsigned nprocs : 16, /* number of processes */
state : 8,
#define JOBRUNNING 0
#define JOBSTOPPED 1
#define JOBDONE 2
sigint : 1, /* job was killed by SIGINT */
jobctl : 1, /* job running under job control */
waited : 1, /* true if this entry has been waited for */
used : 1, /* true if this entry is in used */
changed : 1; /* true if status has changed */
struct job *prev_job; /* previous job */
};
struct ncmd {
int type;
int linno;
union node *assign;
union node *args;
union node *redirect;
};
struct npipe {
int type;
int backgnd;
struct nodelist *cmdlist;
};
struct nredir {
int type;
int linno;
union node *n;
union node *redirect;
};
struct nbinary {
int type;
union node *ch1;
union node *ch2;
};
struct nif {
int type;
union node *test;
union node *ifpart;
union node *elsepart;
};
struct nfor {
int type;
int linno;
union node *args;
union node *body;
char *var_;
};
struct ncase {
int type;
int linno;
union node *expr;
union node *cases;
};
struct nclist {
int type;
union node *next;
union node *pattern;
union node *body;
};
struct ndefun {
int type;
int linno;
char *text;
union node *body;
};
struct narg {
int type;
union node *next;
char *text;
struct nodelist *backquote;
};
struct nfile {
int type;
union node *next;
int fd;
union node *fname;
char *expfname;
};
struct ndup {
int type;
union node *next;
int fd;
int dupfd;
union node *vname;
};
struct nhere {
int type;
union node *next;
int fd;
union node *doc;
};
struct nnot {
int type;
union node *com;
};
union node {
int type;
struct ncmd ncmd;
struct npipe npipe;
struct nredir nredir;
struct nbinary nbinary;
struct nif nif;
struct nfor nfor;
struct ncase ncase;
struct nclist nclist;
struct ndefun ndefun;
struct narg narg;
struct nfile nfile;
struct ndup ndup;
struct nhere nhere;
struct nnot nnot;
};
struct nodelist {
struct nodelist *next;
union node *n;
};
struct funcnode {
int count;
union node n;
};
struct localvar_list {
struct localvar_list *next;
struct localvar *lv;
};
struct Var {
struct Var *next; /* next entry in hash list */
int flags; /* flags are defined above */
const char *text; /* name=value */
void (*func)(const char *);
/* function to be called when */
/* the variable gets set/unset */
};
struct localvar {
struct localvar *next; /* next local variable in list */
struct Var *vp; /* the variable that was made local */
int flags; /* saved flags */
const char *text; /* saved text */
};
union yystype {
int64_t val;
char *name;
};
struct strlist {
struct strlist *next;
char *text;
};
struct arglist {
struct strlist *list;
struct strlist **lastp;
};
/*
* Structure specifying which parts of the string should be searched
* for IFS characters.
*/
struct ifsregion {
struct ifsregion *next; /* next region in list */
int begoff; /* offset of start of region */
int endoff; /* offset of end of region */
int nulonly; /* search for nul bytes only */
};
struct builtincmd {
const char *name;
int (*builtin)(int, char **);
unsigned flags;
};
struct cmdentry {
int cmdtype;
union param {
int index;
const struct builtincmd *cmd;
struct funcnode *func;
} u;
};
struct tblentry {
struct tblentry *next; /* next entry in hash chain */
union param param; /* definition of builtin function */
short cmdtype; /* index identifying command */
char rehash; /* if set, cd done since entry created */
char cmdname[]; /* name of command */
};
struct backcmd { /* result of evalbackcmd */
int fd; /* file descriptor to read from */
char *buf; /* buffer */
int nleft; /* number of chars in buffer */
struct job *jp; /* job structure for command */
};
struct stack_block {
struct stack_block *prev;
char space[MINSIZE];
};
struct stackmark {
struct stack_block *stackp;
char *stacknxt;
unsigned stacknleft;
};
struct limits {
const char *name;
int cmd;
int factor; /* multiply by to get rlim_{cur,max} values */
char option;
};
struct t_op {
const char *op_text;
short op_num, op_type;
};
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § the unbourne shell » bss ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
static char **argptr; /* argument list for builtin commands */
static char **gargv;
static char **t_wp;
static char *arg0; /* value of $0 */
static char *cmdnextc;
static char *commandname;
static char *expdest; /* output of current string */
static char *expdir;
static char *funcstring; /* block to allocate strings from */
static char *minusc; /* argument to -c option */
static char *optionarg; /* set by nextopt (like getopt) */
static char *optptr; /* used by nextopt */
static char *trap[NSIG]; /* trap handler commands */
static char *wordtext; /* text of last word returned by readtoken */
static char basebuf[IBUFSIZ]; /* buffer for top level input file */
static char gotsig[NSIG - 1]; /* indicates specified signal received */
static char nullstr[1]; /* zero length string */
static char optlist[NOPTS];
static char sigmode[NSIG - 1]; /* current value of signal */
static const char *arith_buf;
static const char *arith_startbuf;
static const char *pathopt;
static int back_exitstatus; /* exit status of backquoted command */
static int checkkwd;
static int doprompt; /* if set, prompt the user */
static int errlinno;
static int evalskip; /* set if we are skipping commands */
static int exception;
static int exitstatus; /* exit status of last command */
static int funcblocksize; /* size of structures in function */
static int funcline; /* start line of function, or 0 if not in one */
static int funcstringsize; /* size of strings in node */
static int gotsigchld; /* received SIGCHLD */
static int initialpgrp; /* pgrp of shell on invocation */
static int job_warning;
static int jobctl;
static int last_token;
static int lasttoken; /* last token read */
static int lineno;
static int loopnest; /* current loop nesting level */
static int needprompt; /* true if interactive and at start of line */
static int quoteflag; /* set if (part of) last token was quoted */
static int rootpid;
static int rval;
static int shlvl;
static int skipcount; /* number of levels to skip */
static int suppressint;
static int tokpushback; /* last token pushed back */
static int trapcnt; /* number of non-null traps */
static int ttyfd = -1; /* control terminal */
static int vforked; /* Set if we are in the vforked child */
static int whichprompt; /* 1 == PS1, 2 == PS2 */
static int backgndpid; /* pid of last background process */
static pointer funcblock; /* block to allocate function from */
static struct arglist exparg; /* holds expanded arg list */
static struct heredoc *heredoc;
static struct heredoc *heredoclist; /* list of here documents to read */
static struct ifsregion *ifslastp; /* last struct in list */
static struct ifsregion ifsfirst; /* first struct in list of ifs regions */
static struct jmploc *handler;
static struct job *curjob; /* current job */
static struct job *jobtab; /* array of jobs */
static struct localvar_list *localvar_stack;
static struct nodelist *argbackq; /* list of back quote expressions */
static struct nodelist *backquotelist;
static struct output preverrout;
static struct parsefile basepf; /* top level input file */
static struct redirtab *redirlist;
static struct shparam shellparam; /* current positional parameters */
static struct stack_block stackbase;
static struct t_op const *t_wp_op;
static struct tblentry **lastcmdentry;
static struct tblentry *cmdtable[CMDTABLESIZE];
static struct Var *vartab[VTABSIZE];
static union node *redirnode;
static union yystype yylval;
static unsigned expdir_max;
static unsigned njobs; /* size of array */
static volatile sig_atomic_t intpending;
static volatile sig_atomic_t pending_sig; /* last pending signal */
static struct alias *atab[ATABSIZE];
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § the unbourne shell » data ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
static char *curdir = nullstr; /* current working directory */
static char *physdir = nullstr; /* physical working directory */
static char *sstrend = stackbase.space + MINSIZE;
static char *stacknxt = stackbase.space;
static char defifsvar[] = "IFS= \t\n";
static char defoptindvar[] = "OPTIND=1";
static char linenovar[sizeof("LINENO=") + INT_CHARS + 1] = "LINENO=";
static int builtinloc = -1; /* index in path of %builtin, or -1 */
static int savestatus = -1; /* exit status of last command outside traps */
static struct output errout = {0, 0, 0, 0, 2, 0};
static struct output output = {0, 0, 0, OUTBUFSIZ, 1, 0};
static struct parsefile *parsefile = &basepf; /* current input file */
static struct stack_block *stackp = &stackbase;
static unsigned stacknleft = MINSIZE;
static struct output *out1 = &output;
static struct output *out2 = &errout;
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § the unbourne shell » rodata ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
/* Array indicating which tokens mark the end of a list */
static const char tokendlist[] = {
1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1,
};
static const char *const tokname[] = {
"end of file", "newline", "\";\"", "\"&\"", "\"&&\"", "\"||\"",
"\"|\"", "\"(\"", "\")\"", "\";;\"", "\"`\"", "redirection",
"word", "\"!\"", "\"case\"", "\"do\"", "\"done\"", "\"elif\"",
"\"else\"", "\"esac\"", "\"fi\"", "\"for\"", "\"if\"", "\"in\"",
"\"then\"", "\"until\"", "\"while\"", "\"{\"", "\"}\"",
};
static const char *const parsekwd[] = {"!", "case", "do", "done", "elif", "else", "esac", "fi",
"for", "if", "in", "then", "until", "while", "{", "}"};
static const char defpathvar[] =
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
static const char *const optnames[NOPTS] = {
"errexit", "noglob", "ignoreeof", "interactive", "monitor", "noexec",
"stdin", "xtrace", "verbose", "vi", "emacs", "noclobber",
"allexport", "notify", "nounset", "nolog", "debug",
};
static const char optletters[NOPTS] = {
'e', 'f', 'I', 'i', 'm', 'n', 's', 'x', 'v', 'V', 'E', 'C', 'a', 'b', 'u', 0, 0,
};
static const char spcstr[] = " ";
static const char snlfmt[] = "%s\n";
static const char qchars[] = {CTLESC, CTLQUOTEMARK, 0};
static const char illnum[] = "Illegal number: %s";
static const char homestr[] = "HOME";
static const char dolatstr[] = {CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'};
/* TODO(jart): What's wrong with varinit? */
#if defined(__GNUC__) || defined(__llvm__)
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
/* Some macros depend on the order, add new variables to the end. */
static void changepath(const char *);
static void getoptsreset(const char *);
static struct Var varinit[] = {
{0, VSTRFIXED | VTEXTFIXED, defifsvar, 0},
{0, VSTRFIXED | VTEXTFIXED, defpathvar, changepath},
{0, VSTRFIXED | VTEXTFIXED, "PS1=$ ", 0},
{0, VSTRFIXED | VTEXTFIXED, "PS2=> ", 0},
{0, VSTRFIXED | VTEXTFIXED, "PS4=+ ", 0},
{0, VSTRFIXED | VTEXTFIXED, defoptindvar, getoptsreset},
{0, VSTRFIXED | VTEXTFIXED, linenovar, 0},
};
static const char kPrec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
#define ARITH_PRECEDENCE(OP, PREC) [OP - ARITH_BINOP_MIN] = PREC
ARITH_PRECEDENCE(ARITH_MUL, 0), ARITH_PRECEDENCE(ARITH_DIV, 0),
ARITH_PRECEDENCE(ARITH_REM, 0), ARITH_PRECEDENCE(ARITH_ADD, 1),
ARITH_PRECEDENCE(ARITH_SUB, 1), ARITH_PRECEDENCE(ARITH_LSHIFT, 2),
ARITH_PRECEDENCE(ARITH_RSHIFT, 2), ARITH_PRECEDENCE(ARITH_LT, 3),
ARITH_PRECEDENCE(ARITH_LE, 3), ARITH_PRECEDENCE(ARITH_GT, 3),
ARITH_PRECEDENCE(ARITH_GE, 3), ARITH_PRECEDENCE(ARITH_EQ, 4),
ARITH_PRECEDENCE(ARITH_NE, 4), ARITH_PRECEDENCE(ARITH_BAND, 5),
ARITH_PRECEDENCE(ARITH_BXOR, 6), ARITH_PRECEDENCE(ARITH_BOR, 7),
#undef ARITH_PRECEDENCE
};
static const short nodesize[26] /* clang-format off */ = {
SHELL_ALIGN(sizeof(struct ncmd)), SHELL_ALIGN(sizeof(struct npipe)),
SHELL_ALIGN(sizeof(struct nredir)), SHELL_ALIGN(sizeof(struct nredir)),
SHELL_ALIGN(sizeof(struct nredir)), SHELL_ALIGN(sizeof(struct nbinary)),
SHELL_ALIGN(sizeof(struct nbinary)), SHELL_ALIGN(sizeof(struct nbinary)),
SHELL_ALIGN(sizeof(struct nif)), SHELL_ALIGN(sizeof(struct nbinary)),
SHELL_ALIGN(sizeof(struct nbinary)), SHELL_ALIGN(sizeof(struct nfor)),
SHELL_ALIGN(sizeof(struct ncase)), SHELL_ALIGN(sizeof(struct nclist)),
SHELL_ALIGN(sizeof(struct ndefun)), SHELL_ALIGN(sizeof(struct narg)),
SHELL_ALIGN(sizeof(struct nfile)), SHELL_ALIGN(sizeof(struct nfile)),
SHELL_ALIGN(sizeof(struct nfile)), SHELL_ALIGN(sizeof(struct nfile)),
SHELL_ALIGN(sizeof(struct nfile)), SHELL_ALIGN(sizeof(struct ndup)),
SHELL_ALIGN(sizeof(struct ndup)), SHELL_ALIGN(sizeof(struct nhere)),
SHELL_ALIGN(sizeof(struct nhere)), SHELL_ALIGN(sizeof(struct nnot)),
} /* clang-format on */;
/* syntax table used when not in quotes */
static const char basesyntax[] /* clang-format off */ = {
CEOF, CSPCL, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL,
CCTL, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CSPCL, CNL, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CSPCL, CWORD, CDQUOTE, CWORD, CVAR, CWORD, CSPCL, CSQUOTE, CSPCL,
CSPCL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CSPCL, CSPCL, CWORD, CSPCL, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CBACK, CWORD, CWORD,
CWORD, CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CSPCL, CENDVAR, CWORD, CWORD
} /* clang-format on */;
/* syntax table used when in double quotes */
static const char dqsyntax[] /* clang-format off */ = {
CEOF, CIGN, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL,
CCTL, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CCTL, CENDQUOTE, CWORD, CVAR, CWORD, CWORD, CWORD, CWORD,
CWORD, CCTL, CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL,
CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CBACK, CCTL, CWORD,
CWORD, CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CENDVAR, CCTL, CWORD
} /* clang-format on */;
/* syntax table used when in single quotes */
static const char sqsyntax[] = {
CEOF, CIGN, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD,
CENDQUOTE, CWORD, CWORD, CCTL, CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD, CWORD, CCTL, CWORD, CCTL, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CCTL, CCTL, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD,
};
/* syntax table used when in arithmetic */
static const char arisyntax[] = {
CEOF, CIGN, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CVAR, CWORD, CWORD,
CWORD, CLP, CRP, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CBACK, CWORD, CWORD, CWORD, CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CENDVAR, CWORD, CWORD,
};
/* character classification table */
static const char is_type[] /* clang-format off */ = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, ISSPECL, 0, ISSPECL, ISSPECL, 0,
0, 0, 0, 0, ISSPECL, 0, 0, ISSPECL,
0, 0, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, 0, 0, 0, 0,
0, ISSPECL, ISSPECL, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, 0, 0, 0,
0, ISUNDER, 0, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, 0, 0, 0,
0, 0
} /* clang-format on */;
static int aliascmd();
static int bgcmd();
static int breakcmd();
static int cdcmd();
static int commandcmd();
static int dotcmd();
static int echocmd();
static int evalcmd();
static int execcmd();
static int exitcmd();
static int exportcmd();
static int falsecmd();
static int fgcmd();
static int getoptscmd();
static int hashcmd();
static int jobscmd();
static int killcmd();
static int localcmd();
static int printfcmd();
static int pwdcmd();
static int readcmd();
static int returncmd();
static int setcmd();
static int shiftcmd();
static int testcmd();
static int timescmd();
static int trapcmd();
static int truecmd();
static int typecmd();
static int ulimitcmd();
static int umaskcmd();
static int unaliascmd();
static int unsetcmd();
static int waitcmd();
static const struct builtincmd kBuiltinCmds[] = {
{".", dotcmd, 3}, //
{":", truecmd, 3}, //
{"[", testcmd, 0}, //
{"alias", aliascmd, 6}, //
{"bg", bgcmd, 2}, //
{"break", breakcmd, 3}, //
{"cd", cdcmd, 2}, //
{"chdir", cdcmd, 0}, //
{"command", commandcmd, 2}, //
{"continue", breakcmd, 3}, //
{"echo", echocmd, 0}, //
{"eval", NULL, 3}, //
{"exec", execcmd, 3}, //
{"exit", exitcmd, 3}, //
{"export", exportcmd, 7}, //
{"false", falsecmd, 2}, //
{"fg", fgcmd, 2}, //
{"getopts", getoptscmd, 2}, //
{"hash", hashcmd, 2}, //
{"jobs", jobscmd, 2}, //
{"kill", killcmd, 2}, //
{"local", localcmd, 7}, //
{"printf", printfcmd, 0}, //
{"pwd", pwdcmd, 2}, //
{"read", readcmd, 2}, //
{"readonly", exportcmd, 7}, //
{"return", returncmd, 3}, //
{"set", setcmd, 3}, //
{"shift", shiftcmd, 3}, //
{"test", testcmd, 0}, //
{"times", timescmd, 3}, //
{"trap", trapcmd, 3}, //
{"true", truecmd, 2}, //
{"type", typecmd, 2}, //
{"ulimit", ulimitcmd, 2}, //
{"umask", umaskcmd, 2}, //
{"unalias", unaliascmd, 2}, //
{"unset", unsetcmd, 3}, //
{"wait", waitcmd, 2}, //
};
enum token {
EOI,
FILRD,
FILWR,
FILEX,
FILEXIST,
FILREG,
FILDIR,
FILCDEV,
FILBDEV,
FILFIFO,
FILSOCK,
FILSYM,
FILGZ,
FILTT,
FILSUID,
FILSGID,
FILSTCK,
FILNT,
FILOT,
FILEQ,
FILUID,
FILGID,
STREZ,
STRNZ,
STREQ,
STRNE,
STRLT,
STRGT,
INTEQ,
INTNE,
INTGE,
INTGT,
INTLE,
INTLT,
UNOT,
BAND,
BOR,
LPAREN,
RPAREN,
OPERAND
};
enum token_types { UNOP, BINOP, BUNOP, BBINOP, PAREN };
static struct t_op const ops[] = {
{"-r", FILRD, UNOP},
{"-w", FILWR, UNOP},
{"-x", FILEX, UNOP},
{"-e", FILEXIST, UNOP},
{"-f", FILREG, UNOP},
{"-d", FILDIR, UNOP},
{"-c", FILCDEV, UNOP},
{"-b", FILBDEV, UNOP},
{"-p", FILFIFO, UNOP},
{"-u", FILSUID, UNOP},
{"-g", FILSGID, UNOP},
{"-k", FILSTCK, UNOP},
{"-s", FILGZ, UNOP},
{"-t", FILTT, UNOP},
{"-z", STREZ, UNOP},
{"-n", STRNZ, UNOP},
{"-h", FILSYM, UNOP}, /* for backwards compat */
{"-O", FILUID, UNOP},
{"-G", FILGID, UNOP},
{"-L", FILSYM, UNOP},
{"-S", FILSOCK, UNOP},
{"=", STREQ, BINOP},
{"!=", STRNE, BINOP},
{"<", STRLT, BINOP},
{">", STRGT, BINOP},
{"-eq", INTEQ, BINOP},
{"-ne", INTNE, BINOP},
{"-ge", INTGE, BINOP},
{"-gt", INTGT, BINOP},
{"-le", INTLE, BINOP},
{"-lt", INTLT, BINOP},
{"-nt", FILNT, BINOP},
{"-ot", FILOT, BINOP},
{"-ef", FILEQ, BINOP},
{"!", UNOT, BUNOP},
{"-a", BAND, BBINOP},
{"-o", BOR, BBINOP},
{"(", LPAREN, PAREN},
{")", RPAREN, PAREN},
{0, 0, 0},
};
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § the unbourne shell » text ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
/*
* Hack to calculate maximum length.
* (length * 8 - 1) * log10(2) + 1 + 1 + 12
* The second 1 is for the minus sign and the 12 is a safety margin.
*/
static inline int max_int_length(int bytes) {
return (bytes * 8 - 1) * 0.30102999566398119521 + 14;
}
/* prefix -- see if pfx is a prefix of string. */
static char *prefix(const char *string, const char *pfx) {
while (*pfx) {
if (*pfx++ != *string++) return 0;
}
return (char *)string;
}
/*
* Wrapper around strcmp for qsort/bsearch/...
*/
static int pstrcmp(const void *a, const void *b) {
return strcmp(*(const char *const *)a, *(const char *const *)b);
}
/*
* Find a string is in a sorted array.
*/
static const char *const *findstring(const char *s, const char *const *array, unsigned nmemb) {
return bsearch(&s, array, nmemb, sizeof(const char *), pstrcmp);
}
/* Types of operations (passed to the errmsg routine). */
enum ShErrorAction { E_OPEN, E_CREAT, E_EXEC };
/*
* Return a string describing an error. The returned string may be a
* pointer to a static buffer that will be overwritten on the next call.
* Action describes the operation that got the error.
*/
static const char *errmsg(int e, enum ShErrorAction action) {
if (e != ENOENT && e != ENOTDIR) return strerror(e);
switch (action) {
case E_OPEN:
return "No such file";
case E_CREAT:
return "Directory nonexistent";
default:
return "not found";
}
}
static inline void sigclearmask(void) {
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, 0);
}
/*
* Called to raise an exception. Since C doesn't include exceptions, we
* just do a longjmp to the exception handler. The type of exception is
* stored in the global variable "exception".
*/
wontreturn static void exraise(int e) {
if (vforked) _exit(exitstatus);
INTOFF;
exception = e;
longjmp(handler->loc, 1);
panic();
}
/*
* Called from trap.c when a SIGINT is received. (If the user specifies
* that SIGINT is to be trapped or ignored using the trap builtin, then
* this routine is not called.) Suppressint is nonzero when interrupts
* are held using the INTOFF macro. (The test for iflag is just
* defensive programming.)
*/
wontreturn static void onint(void) {
intpending = 0;
sigclearmask();
if (!(rootshell && iflag)) {
signal(SIGINT, SIG_DFL);
raise(SIGINT);
}
exitstatus = SIGINT + 128;
exraise(EXINT);
}
static pointer ckmalloc(unsigned nbytes) {
pointer p;
if (!(p = malloc(nbytes))) abort();
return p;
}
static pointer ckrealloc(pointer p, unsigned nbytes) {
if (!(p = realloc(p, nbytes))) abort();
return p;
}
#define stackblock() ((void *)stacknxt)
#define stackblocksize() stacknleft
#define STARTSTACKSTR(p) ((p) = stackblock())
#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
#define CHECKSTRSPACE(n, p) \
({ \
char *q = (p); \
unsigned l = (n); \
unsigned m = sstrend - q; \
if (l > m) (p) = makestrspace(l, q); \
0; \
})
#define USTPUTC(c, p) (*p++ = (c))
#define STACKSTRNUL(p) ((p) == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0'))
#define STUNPUTC(p) (--p)
#define STTOPC(p) p[-1]
#define STADJUST(amount, p) (p += (amount))
#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
#define ungrabstackstr(s, p) stunalloc((s))
#define stackstrend() ((void *)sstrend)
#define ckfree(p) free((pointer)(p))
static pointer stalloc(unsigned nbytes) {
char *p;
unsigned aligned;
aligned = SHELL_ALIGN(nbytes);
if (aligned > stacknleft) {
unsigned len;
unsigned blocksize;
struct stack_block *sp;
blocksize = aligned;
if (blocksize < MINSIZE) blocksize = MINSIZE;
len = sizeof(struct stack_block) - MINSIZE + blocksize;
if (len < blocksize) abort();
INTOFF;
sp = ckmalloc(len);
sp->prev = stackp;
stacknxt = sp->space;
stacknleft = blocksize;
sstrend = stacknxt + blocksize;
stackp = sp;
INTON;
}
p = stacknxt;
stacknxt += aligned;
stacknleft -= aligned;
return p;
}
static inline void grabstackblock(unsigned len) {
stalloc(len);
}
static void pushstackmark(struct stackmark *mark, unsigned len) {
mark->stackp = stackp;
mark->stacknxt = stacknxt;
mark->stacknleft = stacknleft;
grabstackblock(len);
}
static void popstackmark(struct stackmark *mark) {
struct stack_block *sp;
INTOFF;
while (stackp != mark->stackp) {
sp = stackp;
stackp = sp->prev;
ckfree(sp);
}
stacknxt = mark->stacknxt;
stacknleft = mark->stacknleft;
sstrend = mark->stacknxt + mark->stacknleft;
INTON;
}
static void setstackmark(struct stackmark *mark) {
pushstackmark(mark, stacknxt == stackp->space && stackp != &stackbase);
}
static void stunalloc(pointer p) {
stacknleft += stacknxt - (char *)p;
stacknxt = p;
}
/* Like strdup but works with the ash stack. */
static char *sstrdup(const char *p) {
unsigned len = strlen(p) + 1;
return memcpy(stalloc(len), p, len);
}
int xwrite(int, const void *, uint64_t);
static void flushout(struct output *dest) {
unsigned len;
len = dest->nextc - dest->buf;
if (!len || dest->fd < 0) return;
dest->nextc = dest->buf;
if ((xwrite(dest->fd, dest->buf, len))) dest->flags |= OUTPUT_ERR;
}
static void flushall(void) {
flushout(&output);
}
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § the unbourne shell » output routines ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│─╝
When a builtin command is interrupted we have to discard
any pending output.
When a builtin command appears in back quotes, we want to
save the output of the command in a region obtained
via malloc, rather than doing a fork and reading the
output of the command via a pipe. */
static int xvsnprintf(char *outbuf, unsigned length, const char *fmt, va_list ap) {
int ret;
INTOFF;
ret = vsnprintf(outbuf, length, fmt, ap);
INTON;
return ret;
}
static int xvasprintf(char **sp, unsigned size, const char *f, va_list ap) {
char *s;
int len;
va_list ap2;
va_copy(ap2, ap);
len = xvsnprintf(*sp, size, f, ap2);
va_end(ap2);
if (len < 0) abort();
if (len < size) return len;
s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1);
*sp = s;
len = xvsnprintf(s, len + 1, f, ap);
return len;
}
static void outmem(const char *p, unsigned len, struct output *dest) {
unsigned bufsize;
unsigned offset;
unsigned nleft;
nleft = dest->end - dest->nextc;
if (likely(nleft >= len)) {
buffered:
dest->nextc = mempcpy(dest->nextc, p, len);
return;
}
bufsize = dest->bufsize;
if (!bufsize) {
(void)0;
} else if (dest->buf == NULL) {
offset = 0;
INTOFF;
dest->buf = ckrealloc(dest->buf, bufsize);
dest->bufsize = bufsize;
dest->end = dest->buf + bufsize;
dest->nextc = dest->buf + offset;
INTON;
} else {
flushout(dest);
}
nleft = dest->end - dest->nextc;
if (nleft > len) goto buffered;
if ((xwrite(dest->fd, p, len))) {
dest->flags |= OUTPUT_ERR;
}
}
static void outstr(const char *p, struct output *file) {
unsigned len;
len = strlen(p);
outmem(p, len, file);
}
static void outcslow(int c, struct output *dest) {
char buf = c;
outmem(&buf, 1, dest);
}
printfesque(3) static int fmtstr(char *outbuf, unsigned length, const char *fmt, ...) {
va_list ap;
int ret;
va_start(ap, fmt);
ret = xvsnprintf(outbuf, length, fmt, ap);
va_end(ap);
return ret > (int)length ? length : ret;
}
printfesque(2) static int xasprintf(char **sp, const char *fmt, ...) {
va_list ap;
int ret;
va_start(ap, fmt);
ret = xvasprintf(sp, 0, fmt, ap);
va_end(ap);
return ret;
}
static void doformat(struct output *dest, const char *f, va_list ap) {
struct stackmark smark;
char *s;
int len;
int olen;
setstackmark(&smark);
s = dest->nextc;
olen = dest->end - dest->nextc;
len = xvasprintf(&s, olen, f, ap);
if (likely(olen > len)) {
dest->nextc += len;
goto out;
}
outmem(s, len, dest);
out:
popstackmark(&smark);
}
printfesque(1) static void out1fmt(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
doformat(out1, fmt, ap);
va_end(ap);
}
printfesque(2) static void outfmt(struct output *file, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
doformat(file, fmt, ap);
va_end(ap);
}
static void exvwarning(const char *msg, va_list ap) {
struct output *errs;
const char *name;
const char *fmt;
errs = out2;
name = arg0 ? arg0 : "sh";
if (!commandname) {
fmt = "%s: %d: ";
} else {
fmt = "%s: %d: %s: ";
}
outfmt(errs, fmt, name, errlinno, commandname);
doformat(errs, msg, ap);
outcslow('\n', errs);
}
/* error/warning routines for external builtins */
printfesque(1) static void sh_warnx(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
exvwarning(fmt, ap);
va_end(ap);
}
/*
* Exverror is called to raise the error exception. If the second argument
* is not NULL then error prints an error message using printf style
* formatting. It then raises the error exception.
*/
wontreturn static void exverror(int cond, const char *msg, va_list ap) {
exvwarning(msg, ap);
flushall();
exraise(cond);
}
wontreturn static void exerror(int cond, const char *msg, ...) {
va_list ap;
va_start(ap, msg);
exverror(cond, msg, ap);
va_end(ap);
}
wontreturn static void sh_error(const char *msg, ...) {
va_list ap;
exitstatus = 2;
va_start(ap, msg);
exverror(EXERROR, msg, ap);
va_end(ap);
}
wontreturn static void badnum(const char *s) {
sh_error(illnum, s);
}
wontreturn static void synerror(const char *msg) {
errlinno = plinno;
sh_error("Syntax error: %s", msg);
}
wontreturn static void yyerror(const char *s) {
sh_error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
}
/*
* Called when an unexpected token is read during the parse. The
* argument is the token that is expected, or -1 if more than one type
* of token can occur at this point.
*/
wontreturn static void synexpect(int token) {
char msg[64];
if (token >= 0) {
fmtstr(msg, 64, "%s unexpected (expecting %s)", tokname[lasttoken], tokname[token]);
} else {
fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
}
synerror(msg);
}
wontreturn static void varunset(const char *end, const char *var_, const char *umsg, int varflags) {
const char *msg;
const char *tail;
tail = nullstr;
msg = "parameter not set";
if (umsg) {
if (*end == (char)CTLENDVAR) {
if (varflags & VSNUL) tail = " or null";
} else
msg = umsg;
}
sh_error("%.*s: %s%s", end - var_ - 1, var_, msg, tail);
}
/*
* Convert a string into an integer of type int64. Alow trailing spaces.
*/
static int64_t atomax(const char *s, int base) {
char *p;
int64_t r;
errno = 0;
r = strtoimax(s, &p, base);
if (errno == ERANGE) badnum(s);
/*
* Disallow completely blank strings in non-arithmetic (base != 0)
* contexts.
*/
if (p == s && base) badnum(s);
while (isspace((unsigned char)*p)) p++;
if (*p) badnum(s);
return r;
}
static int64_t atomax10(const char *s) {
return atomax(s, 10);
}
/*
* Convert a string of digits to an integer, printing an error message
* on failure.
*/
static int number(const char *s) {
int64_t n = atomax10(s);
if (n < 0 || n > INT_MAX) badnum(s);
return n;
}
static inline int64_t getn(const char *s) {
return atomax10(s);
}
/*
* When the parser reads in a string, it wants to stick the string on
* the stack and only adjust the stack pointer when it knows how big the
* string is. Stackblock (defined in stack.h) returns a pointer to a
* block of space on top of the stack and stackblocklen returns the
* length of this block. Growstackblock will grow this space by at least
* one byte, possibly moving it (like realloc). Grabstackblock actually
* allocates the part of the block that has been used.
*/
static void growstackblock(unsigned min) {
unsigned newlen;
newlen = stacknleft * 2;
if (newlen < stacknleft) sh_error("Out of space");
min = SHELL_ALIGN(min | 128);
if (newlen < min) newlen += min;
if (stacknxt == stackp->space && stackp != &stackbase) {
struct stack_block *sp;
struct stack_block *prevstackp;
unsigned grosslen;
INTOFF;
sp = stackp;
prevstackp = sp->prev;
grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
sp = ckrealloc((pointer)sp, grosslen);
sp->prev = prevstackp;
stackp = sp;
stacknxt = sp->space;
stacknleft = newlen;
sstrend = sp->space + newlen;
INTON;
} else {
char *oldspace = stacknxt;
int oldlen = stacknleft;
char *p = stalloc(newlen);
/* free the space we just allocated */
stacknxt = memcpy(p, oldspace, oldlen);
stacknleft += newlen;
}
}
/*
* The following routines are somewhat easier to use than the above. The
* user declares a variable of type STACKSTR, which may be declared to
* be a register. The macro STARTSTACKSTR initializes things. Then the
* user uses the macro STPUTC to add characters to the string. In
* effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
* grown as necessary. When the user is done, she can just leave the
* string there and refer to it using stackblock(). Or she can allocate
* the space for it using grabstackstr(). If it is necessary to allow
* someone else to use the stack temporarily and then continue to grow
* the string, the user should use grabstack to allocate the space, and
* then call ungrabstr(p) to return to the previous mode of operation.
*
* USTPUTC is like STPUTC except that it doesn't check for overflow.
* CHECKSTACKSPACE can be called before USTPUTC to ensure that there
* is space for at least one character.
*/
static void *growstackstr(void) {
unsigned len = stackblocksize();
growstackblock(0);
return (char *)stackblock() + len;
}
static char *growstackto(unsigned len) {
if (stackblocksize() < len) growstackblock(len);
return stackblock();
}
/*
* Make a copy of a string in safe storage.
*/
static char *savestr(const char *s) {
char *p = strdup(s);
if (!p) sh_error("Out of space");
return p;
}
/* Called from CHECKSTRSPACE. */
static char *makestrspace(unsigned newlen, char *p) {
unsigned len = p - stacknxt;
return growstackto(len + newlen) + len;
}
static char *stnputs(const char *s, unsigned n, char *p) {
p = makestrspace(n, p);
p = mempcpy(p, s, n);
return p;
}
static char *stputs(const char *s, char *p) {
return stnputs(s, strlen(s), p);
}
static char *nodesavestr(s) char *s;
{
char *rtn = funcstring;
funcstring = stpcpy(funcstring, s) + 1;
return rtn;
}
wontreturn static void shellexec(char **, const char *, int);
static char **listvars(int, int, char ***);
static char *argstr(char *p, int flag);
static char *conv_escape(char *, int *);
static char *evalvar(char *, int);
static char *expari(char *start, int flag);
static char *exptilde(char *startp, int flag);
static int shlex(void);
static char *lookupvar(const char *);
static char *mklong(const char *, const char *);
static char *rmescapes(char *, int);
static char *scanleft(char *, char *, char *, char *, int, int);
static char *scanright(char *, char *, char *, char *, int, int);
static char *single_quote(const char *);
static const char *const *findkwd(const char *);
static const char *expandstr(const char *);
static const char *getprompt(void *);
static double getdouble(void);
static enum token t_lex(char **);
static int aexpr(enum token);
static int binop0(void);
static int bltincmd(int, char **);
static int conv_escape_str(char *, char **);
static int decode_signal(const char *, int);
static int decode_signum(const char *);
static int describe_command(struct output *, char *, const char *, int);
static int eprintlist(struct output *, struct strlist *, int);
static int equalf(const char *, const char *);
static int evalbltin(const struct builtincmd *, int, char **, int);
static int evalcase(union node *, int);
static int evalcommand(union node *, int);
static int evalfor(union node *, int);
static int evalfun(struct funcnode *, int, char **, int);
static int evalloop(union node *, int);
static int evalpipe(union node *, int);
static int evalsubshell(union node *, int);
static int filstat(char *, enum token);
static int forkshell(struct job *, union node *, int);
static int getopts(char *, char *, char **);
static int isassignment(const char *p);
static int isoperand(char **);
static int newerf(const char *, const char *);
static int nexpr(enum token);
static int nextopt(const char *);
static int oexpr(enum token);
static int olderf(const char *, const char *);
static int64_t openhere(union node *);
static int openredirect(union node *);
static int options(int);
static int padvance_magic(const char **, const char *, int);
static int patmatch(char *, const char *);
static int peektoken(void);
static int pgetc(void);
static int pgetc2(void);
static int pgetc_eatbnl();
static int pmatch(const char *, const char *);
static int preadbuffer(void);
static ssize_t preadfd(void);
static int primary1(enum token);
static int procargs(int, char **);
static int readtoken(void);
static int readtoken1(int, char const *, char *, int);
static int redirectsafe(union node *, int);
static int savefd(int, int);
static int setinputfile(const char *, int);
static int showvars(const char *, int, int);
static int stoppedjobs(void);
static int test_file_access(const char *, int);
static int unalias(const char *);
static int waitforjob(struct job *);
static int xxreadtoken(void);
static int64_t arith(const char *);
static int64_t assignment(int var_, int noeval);
static int64_t lookupvarint(const char *);
static long varvalue(char *, int, int, int);
static int64_t setvarint(const char *name, int64_t val, int flags);
static struct Var *setvar(const char *name, const char *val, int flags);
static struct Var *setvareq(char *s, int flags);
static struct alias **__lookupalias(const char *);
static struct alias *freealias(struct alias *);
static struct alias *lookupalias(const char *, int);
static struct funcnode *copyfunc(union node *);
static struct job *makejob(union node *, int);
static struct job *vforkexec(union node *n, char **argv, const char *path, int idx);
static struct localvar_list *pushlocalvars(int push);
static struct nodelist *copynodelist(struct nodelist *);
static struct redirtab *pushredir(union node *redir);
static struct strlist *expsort(struct strlist *);
static struct strlist *msort(struct strlist *, int);
static struct tblentry *cmdlookup(const char *, int);
static uint64_t getuintmax(int);
static union node *andor(void);
static union node *command(void);
static union node *copynode(union node *);
static union node *list(int);
static union node *makename(void);
static union node *parsecmd(int);
static union node *pipeline(void);
static union node *simplecmd(void);
static unsigned esclen(const char *, const char *);
static unsigned memtodest(const char *p, unsigned len, int flags);
static unsigned strtodest(const char *p, int flags);
static void addcmdentry(char *, struct cmdentry *);
static void addfname(char *);
static void check_conversion(const char *, const char *);
static void clear_traps(void);
static void clearcmdentry(void);
static void closescript(void);
static void defun(union node *);
static void delete_cmd_entry(void);
static void dotrap(void);
static void dupredirect(union node *, int);
static void exitreset(void);
static void expandarg(union node *arg, struct arglist *arglist, int flag);
static void expandmeta(struct strlist *, int);
static void expbackq(union node *, int);
static void expmeta(char *, unsigned, unsigned);
static void expredir(union node *);
static void find_command(char *, struct cmdentry *, int, const char *);
static void fixredir(union node *, const char *, int);
static void freeparam(volatile struct shparam *);
static void hashcd(void);
static void ignoresig(int);
static void init(void);
static void minus_o(char *, int);
static void mklocal(char *name, int flags);
static void onsig(int);
static void optschanged(void);
static void parsefname(void);
static void parseheredoc(void);
static void popallfiles(void);
static void popfile(void);
static void poplocalvars(int);
static void popredir(int);
static void popstring(void);
static void prehash(union node *);
static void printalias(const struct alias *);
static void printentry(struct tblentry *);
static void pungetc(void);
static void pushfile(void);
static void pushstring(char *, void *);
static void read_profile(const char *);
static void redirect(union node *, int);
static void reset(void);
static void rmaliases(void);
static void setalias(const char *, const char *);
static void setinputfd(int fd, int push);
static void setinputstring(char *);
static void setinteractive(int);
static void setjobctl(int);
static void setparam(char **);
static void setprompt(int);
static void setsignal(int);
static void showjobs(struct output *, int);
static void sigblockall(sigset_t *oldmask);
static void sizenodelist(struct nodelist *);
static void syntax(const char *, const char *);
static void tryexec(char *, char **, char **);
static void unsetfunc(const char *);
static void unsetvar(const char *);
static void unwindfiles(struct parsefile *);
static void unwindlocalvars(struct localvar_list *stop);
static void unwindredir(struct redirtab *stop);
static unsigned cvtnum(int64_t num, int flags);
static int getchr(void) {
int val = 0;
if (*gargv) val = **gargv++;
return val;
}
static char *getstr(void) {
char *val = nullstr;
if (*gargv) val = *gargv++;
return val;
}
/*
* Check for a valid number. This should be elsewhere.
*/
static int is_number(const char *p) {
do {
if (!is_digit(*p)) return 0;
} while (*++p != '\0');
return 1;
}
static inline void freestdout() {
output.nextc = output.buf;
output.flags = 0;
}
static inline void outc(int ch, struct output *file) {
if (file->nextc == file->end)
outcslow(ch, file);
else {
*file->nextc = ch;
file->nextc++;
}
}
static inline char *_STPUTC(int c, char *p) {
if (p == sstrend) p = growstackstr();
*p++ = c;
return p;
}
static void ifsfree(void) {
struct ifsregion *p = ifsfirst.next;
if (!p) goto out;
INTOFF;
do {
struct ifsregion *ifsp;
ifsp = p->next;
ckfree(p);
p = ifsp;
} while (p);
ifsfirst.next = NULL;
INTON;
out:
ifslastp = NULL;
}
static void setalias(const char *name, const char *val) {
struct alias *ap, **app;
app = __lookupalias(name);
ap = *app;
INTOFF;
if (ap) {
if (!(ap->flag & ALIASINUSE)) {
ckfree(ap->val);
}
ap->val = savestr(val);
ap->flag &= ~ALIASDEAD;
} else {
/* not found */
ap = ckmalloc(sizeof(struct alias));
ap->name = savestr(name);
ap->val = savestr(val);
ap->flag = 0;
ap->next = 0;
*app = ap;
}
INTON;
}
static int unalias(const char *name) {
struct alias **app;
app = __lookupalias(name);
if (*app) {
INTOFF;
*app = freealias(*app);
INTON;
return (0);
}
return (1);
}
static void rmaliases(void) {
struct alias *ap, **app;
int i;
INTOFF;
for (i = 0; i < ATABSIZE; i++) {
app = &atab[i];
for (ap = *app; ap; ap = *app) {
*app = freealias(*app);
if (ap == *app) {
app = &ap->next;
}
}
}
INTON;
}
struct alias *lookupalias(const char *name, int check) {
struct alias *ap = *__lookupalias(name);
if (check && ap && (ap->flag & ALIASINUSE)) return (NULL);
return (ap);
}
static int aliascmd(int argc, char **argv) {
/* TODO - sort output */
char *n, *v;
int ret = 0;
struct alias *ap;
if (argc == 1) {
int i;
for (i = 0; i < ATABSIZE; i++)
for (ap = atab[i]; ap; ap = ap->next) {
printalias(ap);
}
return (0);
}
while ((n = *++argv) != NULL) {
if ((v = strchr(n + 1, '=')) == NULL) { /* n+1: funny ksh stuff */
if ((ap = *__lookupalias(n)) == NULL) {
outfmt(out2, "%s: %s not found\n", "alias", n);
ret = 1;
} else
printalias(ap);
} else {
*v++ = '\0';
setalias(n, v);
}
}
return (ret);
}
static int unaliascmd(int argc, char **argv) {
int i;
while ((i = nextopt("a")) != '\0') {
if (i == 'a') {
rmaliases();
return (0);
}
}
for (i = 0; *argptr; argptr++) {
if (unalias(*argptr)) {
outfmt(out2, "%s: %s not found\n", "unalias", *argptr);
i = 1;
}
}
return i;
}
static struct alias *freealias(struct alias *ap) {
struct alias *next;
if (ap->flag & ALIASINUSE) {
ap->flag |= ALIASDEAD;
return ap;
}
next = ap->next;
ckfree(ap->name);
ckfree(ap->val);
ckfree(ap);
return next;
}
static void printalias(const struct alias *ap) {
out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
}
static struct alias **__lookupalias(const char *name) {
unsigned int hashval;
struct alias **app;
const char *p;
unsigned int ch;
p = name;
ch = (unsigned char)*p;
hashval = ch << 4;
while (ch) {
hashval += ch;
ch = (unsigned char)*++p;
}
app = &