├── .gitignore ├── .travis.yml ├── CONTRIBUTORS ├── CVS ├── Entries ├── Entries.Log ├── Repository └── Root ├── LEGAL ├── NOTES ├── PROJECTS ├── README.md ├── README.pdksh ├── alloc.c ├── asprintf.c ├── c_ksh.c ├── c_sh.c ├── c_test.c ├── c_test.h ├── c_ulimit.c ├── charclass.h ├── config.h ├── configure ├── confstr.c ├── dot.profile ├── edit.c ├── edit.h ├── emacs.c ├── eval.c ├── exec.c ├── expand.h ├── expr.c ├── history.c ├── io.c ├── issetugid.c ├── jobs.c ├── ksh.1 ├── ksh.kshrc ├── lex.c ├── lex.h ├── mail.c ├── main.c ├── misc.c ├── oksh.1 ├── path.c ├── portable.h ├── reallocarray.c ├── sh.1 ├── sh.h ├── shf.c ├── shf.h ├── siglist.c ├── signame.c ├── strlcat.c ├── strlcpy.c ├── strtonum.c ├── syn.c ├── sys-queue.h ├── table.c ├── table.h ├── trap.c ├── tree.c ├── tree.h ├── tty.c ├── tty.h ├── unvis.c ├── var.c ├── version.c ├── vi.c ├── vis.c └── vis.h /.gitignore: -------------------------------------------------------------------------------- 1 | oksh 2 | Makefile 3 | README 4 | *.o 5 | pconfig.h 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | os: 3 | - linux 4 | - osx 5 | compiler: 6 | - clang 7 | - gcc 8 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | $OpenBSD: CONTRIBUTORS,v 1.11 2019/01/25 00:19:25 millert Exp $ 2 | 3 | This is a partial history of this shell gleened from old change logs and 4 | readmes (most of which are still in the misc directory) and the source 5 | code. Hopefully it is correct and no contributors have been left out 6 | (file a bug report if you spot a problem :-)). 7 | 8 | Release history: 9 | * Eric Gisin (egisin@math.uwaterloo.ca), created pdksh, using 10 | Charles Forsyth's public domain V7 shell as a base; also used parts 11 | of the BRL shell (written by Doug A Gwyn, Doug Kingston, Ron Natalie, 12 | Arnold Robbins, Lou Salkind, and others?, circa '87; the parts used in 13 | pdksh included getopts, test builtin, ulimit, tty setting/getting, emacs 14 | editing, and job control; the test builtin was based on code by Erik 15 | Baalbergen). 16 | '87..'89 ? 17 | Released versions: .. 3.2 18 | * John R MacMillan (@yonge.csri.toronto.edu:chance!john@sq.sq.com) 19 | takes over as maintainer 20 | dates? 21 | Released versions: 3.3 (?) 22 | * Simon J. Gerraty (sjg@zen.void.oz.au) takes over as maintainer 23 | Nov '91..July '94 ? 24 | Released versions: 4.0 .. 4.9 25 | * Michael Rendell (michael@cs.mun.ca) takes over as maintainer 26 | July, 1994 27 | Released versions: 5.0 .. 5.2 28 | 29 | Major contributions: 30 | * John R MacMillan (@yonge.csri.toronto.edu:chance!john@sq.sq.com), ?: 31 | cleaned up configuration, many bug fixes (see misc/Changes.jrm). 32 | * Simon Gerraty, (sjg@zen.void.oz.au), Nov '91..?: much improved emacs mode 33 | ala at&t ksh, 386bsd port, sigaction routines for non-POSIX systems 34 | (see misc/ChangeLog.sjg and misc/ReadME.sjg). 35 | * Peter Collinson (pc@hillside.co.uk), July '92: added select, at&t ksh 36 | style history file, original csh-style {} globbing, BSD/386 port, 37 | misc bug fixes. 38 | * Larry Bouzane (larry@compusult.nf.ca), Mar '89..'93: re-wrote job control, 39 | added async job notification, added CDPATH and other cd fixes, misc bug 40 | fixes. 41 | * John Rochester (jr@cs.mun.ca), '87: wrote vi command line editor; various 42 | bug fixes/enhancements. 43 | * Jeff Sparkes (jsparkes@bnr.ca), Mar '89..Mar '90: added arrays, 44 | merged John Rochester's vi code into pdksh, misc bug fixes. 45 | * Michael Haardt (u31b3hs@POOL.Informatik.RWTH-Aachen.DE), Sept '94: 46 | organized man page, filled in many of its copious blank spots; added 47 | KSH ifdefs. 48 | * Dale DePriest (daled@cadence.com): ported to OS/2 (initially based on 49 | port of pdksh4.9 to OS/2 by Kai Rommel (rommel@ars.muc.de)); maintains 50 | OS/2 port; misc bug fixes. 51 | 52 | Other contributors: 53 | * Piercarlo Grandi (pcg@aber.ac.uk), Dec '93: fixes for linux port 54 | * Neil Smithline (Neil.Smithline@eng.sun.com), Aug '92: emacs-style 55 | filename completion. 56 | * Mike Jetzer [mlj] (jetzer@studsys.mscs.mu.edu), ?;Nov '94: fixes for vi 57 | mode (see misc/Changes.mlj), added v to vi, fixes for history; fixed 58 | command redoing in vi; fixes to vi globbing. 59 | * Robert J Gibson: mailbox checking code that was adapted for pdksh by 60 | John R. MacMillan. 61 | * ? (guy@demon.co.uk), ?: promptlen() function. 62 | * J.T. Conklin (jtc@cygnus.com): POSIXized test builtin; miscellaneous 63 | fixes/enhancements. 64 | * Sean Hogan (sean@neweast.ca): fixes for ICS 3.0 Unix, found and helped 65 | fix numerous problems. 66 | * Gordan Larson (hoh@approve.se): fix to compile sans VI, ksh.1 typo. 67 | * Thomas Gellekum (thomas@ghpc8.ihf.rwth-aachen.de): fixes for Makefile 68 | typos, fixed CLK_TCK for FreeBSD, man page fixes. 69 | * Ed Ferguson (Ed.Ferguson@dseg.ti.com): fix to compile sans VI. 70 | * Brian Campbell (brianc@qnx.com): fixes to compile under QNX and 71 | to compile with dmake. 72 | * (guy@netapp.com), Oct '94: patch to use gmacs flag. 73 | * Andrew Moore (alm@netcom.com): reported many bugs, fixes. 74 | * William Bader (wbader@CSEE.Lehigh.Edu): fix to compile on SCO Unix 75 | (struct winsize). 76 | * Mike Long (mike.long@analog.com): makefile fix - use $manext, not 1. 77 | * Art Mills (aem@hpbs9162.bio.hp.com): bug fix for vi file completion in 78 | command mode. 79 | * Tory Bollinger (tboll@authstin.ibm.com): allow ~ in vi mode to take 80 | a count. 81 | * Frank Edwards (): added macros to vi (@char). 82 | * Fritz Heinrichmeyer (): fixes 83 | to allow compile under Linux 1.4.3. 84 | * Gabor Zahemszky (): SVR3_PGRP vs SYSV_PGRP, many 85 | bug reports and man page fixes. 86 | * Dave Kinchlea (): DEFAULT_ENV patches. 87 | * Paul Borman (): j_exit: send HUP, then CONT; HUP fg process. 88 | * DaviD W. Sanderson (): patches to allow { .. } instead 89 | of in .. esac in case statements. 90 | * ? (): partial patches to handle SIGWINCH for command line 91 | editing. 92 | * Jason Tyler (): fixes for bugs in fc. 93 | * Stefan Dalibor (): fix for 94 | COLUMNS never being set in x_init(). 95 | * Arnon Kanfi (): fix for prompt. 96 | * Marc Olzheim (): patches to ifdef KSH the mail check 97 | code and aliases; enum patches for old K&R compilers; handle missing dup2. 98 | * Lars Hecking (): fixes so shell compiles as sh 99 | again. 100 | * Bill Kish (): added prompt delimiter hack for 101 | hidden characters (eg, escape codes). 102 | * Andrew S. Townley (): fixes for NeXT machines: 103 | get a controlling if one needed, use correct profile. 104 | * Eric J. Chet (): fix for core dump in . (quitenv() called 105 | too soon). 106 | * Greg A. Woods : fix to make ^[_ in emacs work 107 | as in at&t ksh. 108 | * George Robbins : fix for sh mode to 109 | keep exec'd file descriptors open. 110 | * George White : fix here-doc problem under OS/2 111 | (memory allocated incorrectly). 112 | * David E. Wexelblat : fix to avoid memory overrun 113 | in aresize(); fix to not print un-named options. 114 | * Clifford Wolf (): fix memory overrun in aresize(); 115 | fixed sys_siglist[] problem. 116 | * Theo de Raadt (): allow ". /dev/null". 117 | * Eric Youngdale (): flag field incorrectly changed 118 | in exec.c(flushcom). 119 | * Todd. C Miller (Todd C. Miller ): fix 120 | for coredump in jobs. 121 | * Kevin Schoedel : fix for word location in file 122 | completion. 123 | * Martin Lucina : fix for argument parsing in exit command, 124 | fix for KSH_CHECK_H_TYPE. 125 | * Mark Funkenhauser : added $LINENO support. 126 | * Corinna Vinschen and Steven Hein : 127 | port to cyngin environment on win95/winnt. 128 | * Martin Dalecki : changes for 8 bit emacs mode. 129 | * Dave Hillman : patch for bug in test -nt. 130 | -------------------------------------------------------------------------------- /CVS/Entries: -------------------------------------------------------------------------------- 1 | /config.h/1.19/Tue Jan 16 02:21:56 2018// 2 | /io.c/1.38/Result of merge// 3 | /trap.c/1.33/Tue Dec 17 00:39:35 2019// 4 | /tty.c/1.19/Result of merge// 5 | /misc.c/1.78/Result of merge// 6 | /emacs.c/1.90/Result of merge// 7 | /eval.c/1.67/Result of merge// 8 | /exec.c/1.77/Result of merge// 9 | /main.c/1.100/Result of merge// 10 | /sh.h/1.77/Result of merge// 11 | /var.c/1.73/Result of merge// 12 | /c_sh.c/1.65/Sun Nov 19 00:26:55 2023// 13 | /edit.c/1.71/Result of merge// 14 | /history.c/1.86/Result of merge// 15 | /CONTRIBUTORS/1.11/Fri May 9 13:17:08 2025// 16 | /LEGAL/1.2/Fri May 9 13:17:08 2025// 17 | /Makefile/1.39/Fri May 9 13:17:15 2025// 18 | /NOTES/1.16/Fri May 9 13:17:08 2025// 19 | /PROJECTS/1.9/Fri May 9 13:17:08 2025// 20 | /README/1.16/Fri May 9 13:17:15 2025// 21 | /alloc.c/1.19/Fri May 9 13:17:08 2025// 22 | /c_ksh.c/1.63/Fri May 9 13:17:08 2025// 23 | /c_test.c/1.29/Result of merge// 24 | /c_test.h/1.4/Fri May 9 13:17:08 2025// 25 | /c_ulimit.c/1.29/Fri May 9 13:17:08 2025// 26 | /edit.h/1.13/Fri May 9 13:17:08 2025// 27 | /expand.h/1.15/Fri May 9 13:17:08 2025// 28 | /expr.c/1.34/Fri May 9 13:17:08 2025// 29 | /jobs.c/1.62/Fri May 9 13:17:08 2025// 30 | /ksh.1/1.222/Fri May 9 13:17:16 2025// 31 | /lex.c/1.80/Fri May 9 13:17:08 2025// 32 | /lex.h/1.21/Fri May 9 13:17:08 2025// 33 | /mail.c/1.27/Fri May 9 13:17:08 2025// 34 | /path.c/1.23/Fri May 9 13:17:08 2025// 35 | /sh.1/1.158/Fri May 9 13:17:08 2025// 36 | /shf.c/1.35/Fri May 9 13:17:08 2025// 37 | /shf.h/1.8/Fri May 9 13:17:08 2025// 38 | /syn.c/1.40/Fri May 9 13:17:08 2025// 39 | /table.c/1.25/Fri May 9 13:17:08 2025// 40 | /table.h/1.15/Fri May 9 13:17:08 2025// 41 | /tree.c/1.34/Fri May 9 13:17:08 2025// 42 | /tree.h/1.12/Fri May 9 13:17:08 2025// 43 | /tty.h/1.6/Fri May 9 13:17:08 2025// 44 | /version.c/1.12/Fri May 9 13:17:08 2025// 45 | /vi.c/1.63/Result of merge// 46 | D 47 | -------------------------------------------------------------------------------- /CVS/Entries.Log: -------------------------------------------------------------------------------- 1 | A D/tests//// 2 | R D/tests//// 3 | -------------------------------------------------------------------------------- /CVS/Repository: -------------------------------------------------------------------------------- 1 | src/bin/ksh 2 | -------------------------------------------------------------------------------- /CVS/Root: -------------------------------------------------------------------------------- 1 | anoncvs@ftp4.usa.openbsd.org:/cvs 2 | -------------------------------------------------------------------------------- /LEGAL: -------------------------------------------------------------------------------- 1 | $OpenBSD: LEGAL,v 1.2 2003/07/17 20:59:43 deraadt Exp $ 2 | 3 | pdksh is provided AS IS, with NO WARRANTY, either expressed or implied. 4 | 5 | The vast majority of the code that makes pdksh is in the public domain. 6 | The exceptions are: 7 | sigact.c and sigact.h 8 | [REMOVED] 9 | aclocal.m4 10 | [REMOVED] 11 | 12 | That's it. Short and simple. 13 | -------------------------------------------------------------------------------- /NOTES: -------------------------------------------------------------------------------- 1 | $OpenBSD: NOTES,v 1.16 2018/01/12 14:20:57 jca Exp $ 2 | 3 | General features of at&t ksh88 that are not (yet) in pdksh: 4 | - exported aliases and functions (not in ksh93). 5 | - set -t. 6 | - signals/traps not cleared during functions. 7 | - trap DEBUG, local ERR and EXIT traps in functions. 8 | - ERRNO parameter. 9 | - read/select aren't hooked in to the command line editor 10 | - the last command of a pipeline is not run in the parent shell 11 | 12 | Known bugs (see also PROJECTS files): 13 | Variable parsing, Expansion: 14 | - some specials behave differently when unset (eg, IFS behaves like 15 | " \t\n") others lose their special meaning. IFS/PATH taken care of, 16 | still need to sort out some others (eg, TMOUT). 17 | Parsing,Lexing: 18 | - line numbers in errors are wrong for nested constructs. Need to 19 | keep track of the line a command started on (can use for LINENO 20 | parameter as well). 21 | - a $(..) expression nested inside double quotes inside another $(..) 22 | isn't parsed correctly (eg, $(echo "foo$(echo ")")") ) 23 | Commands,Execution: 24 | - setting special parameters that have side effects when 25 | changed/restored (ie, HISTFILE, OPTIND, RANDOM) in front 26 | of a command (eg, HISTFILE=/foo/bar echo hi) affects the parent 27 | shell. Note that setting other (not so special) parameters 28 | does not affect the parent shell. 29 | - `echo hi | exec cat -n' causes at&t to exit, `exec echo hi | cat -n' 30 | does not. pdksh exits for neither. Don't think POSIX requires 31 | an exit, but not sure. 32 | - `echo foo | read bar; echo $bar' prints foo in at&t ksh, nothing 33 | in pdksh (ie, the read is done in a separate process in pdksh). 34 | 35 | Known problems not caused by ksh: 36 | - after stoping a job, emacs/vi is not re-entered. Hitting return 37 | prints the prompt and everything is fine again. Problem (often 38 | involving a pager like less) is related to order of process 39 | scheduling (shell runs before `stop'ed (sub) processes have had a chance 40 | to clean up the screen/terminal). 41 | 42 | Known differences between pdksh & at&t ksh (that may change) 43 | - vi: 44 | - `^U': at&t: kills only what has been inserted, pdksh: kills to 45 | start of line 46 | - at&t ksh login shells say "Warning: you have running jobs" if you 47 | try to exit when there are running jobs. An immediate second attempt 48 | to exit will kill the jobs and exit. pdksh does not print a warning, 49 | nor does it kill running jobs when it exits (it does warn/kill for 50 | stopped jobs). 51 | - TMOUT: at&t prints warning, then waits another 60 seconds. If on screwed 52 | up serial line, the output could cause more input, so pdksh just 53 | prints a message and exits. (Also, in at&t ksh, setting TMOUT has no 54 | effect after the sequence "TMOUT=60; unset TMOUT", which could be 55 | useful - pdksh may do this in the future). 56 | - in pdksh, if the last command of a pipeline is a shell builtin, it is 57 | not executed in the parent shell, so "echo a b | read foo bar" does not 58 | set foo and bar in the parent shell (at&t ksh will). 59 | This may get fixed in the future, but it may take a while. 60 | - in pdksh, set +o lists the options that are currently set, in at&t ksh 61 | it is the same as set -o. 62 | - in pdksh emacs mode, ^T does what gnu emacs does, not what at&t ksh 63 | does. 64 | - in ksh93, `. name' calls a function (defined with function) with POSIX 65 | semantics (instead of ksh semantics). in pdksh, . does not call 66 | functions. 67 | - test: "test -f foo bar blah" is the same as "test -f foo" (the extra 68 | arguments, of which there must be at least 2, are ignored) - pdksh 69 | generates an error message (unexpected operator/operand "bar") as it 70 | should. Sometimes used to test file globs (e.g., if test -f *.o; ...). 71 | - if the command 'sleep 5 && /bin/echo blah' is run interactively and 72 | is the sleep is stopped (^Z), the echo is run immediately in pdksh. 73 | In at&t ksh, the whole thing is stopped. 74 | - LINENO: 75 | - in ksh88 variable is always 1 (can't be changed) in interac mode; 76 | in pdksh it changes. 77 | - Value of LINENO after it has been set by the script in one file 78 | is bizarre when used in another file. 79 | 80 | Known differences between pdksh & at&t ksh (that are not likely to change) 81 | - at&t ksh seems to catch or ignore SIGALRM - pdksh dies upon receipt 82 | (unless it's traped of course) 83 | - typeset: 84 | - at&t ksh overloads -u/-l options: for integers, means unsigned/long, 85 | for strings means uppercase/lowercase; pdksh just has the 86 | upper/lower case (which can be useful for integers when base > 10). 87 | unsigned/long really should have their own options. 88 | - at&t ksh can't have justified integer variables 89 | (eg, typeset -iR5 j=10), pdksh can. 90 | - in pdksh, number arguments for -L/-R/-Z/-i must follow the option 91 | character, at&t allows it at the end of the option group (eg, 92 | at&t ksh likes "typeset -iu5 j", pdksh wants "typeset -i5 -u j" 93 | or "typeset -ui5 j"). Also, pdksh allows "typeset -i 5 j" (same 94 | as "typeset -i5 j"), at&t ksh does not allow this. 95 | - typeset -R: pdksh strips trailing space type characters (ie, 96 | uses isspace()), at&t ksh only skips blanks. 97 | - at&t ksh allows attributes of read-only variables to be changed, 98 | pdksh allows only the export attribute to be set. 99 | - (some) at&t ksh allows set -A of readonly variables, pdksh does not. 100 | - at&t ksh allows command assignments of readonly variables (eg, YY=2 cat), 101 | pdksh does not. 102 | - at&t ksh does not exit scripts when an implicit assignment to an integer 103 | variable fails due to an expression error: eg, 104 | echo 2+ > /tmp/x 105 | unset x; typeset -i x 106 | read x < /tmp/x 107 | echo still here 108 | prints an error and then prints "still here", similarly for 109 | unset x; typeset -i x 110 | set +A x 1 2+ 3 111 | echo still here 112 | and 113 | unset x y; typeset -i x y; set +A y 10 20 30 114 | set +A x 1 1+y[2+] 3 115 | echo still here 116 | pdksh exits a script in all the above cases. (note that both shells 117 | exit for: 118 | unset x; typeset -i x 119 | for x in 1 2+ 3; do echo x=$x; done 120 | echo still here 121 | ). 122 | - at&t ksh seems to allow function calls inside expressions 123 | (eg, typeset -i x='y(2)') but they do not seem to be regular functions 124 | nor math functions (eg, pow, exp) - anyone known anything about this? 125 | - `set -o nounset; unset foo; echo ${#foo}`: at&t ksh prints 0; pdksh 126 | generates error. Same for ${#foo[*]} and ${#foo[@]}. 127 | - . file: at&t ksh parses the whole file before executing anything, 128 | pdksh executes as it parses. This means aliases defined in the file 129 | will affect how pdksh parses the file, but won't affect how at&t ksh 130 | parses the file. Also means pdksh will not parse statements occurring 131 | after a (executed) return statement. 132 | - a return in $ENV in at&t ksh will cause the shell to exit, while in 133 | pdksh it will stop executing the script (this is consistent with 134 | what a return in .profile does in both shells). 135 | - at&t ksh does file globbing for `echo "${foo:-"*"}"`, pdksh does not 136 | (POSIX would seem to indicate pdksh is right). 137 | - at&t ksh thinks ${a:##foo} is ok, pdksh doesn't. 138 | - at&t does tilde expansion on here-document delimiters, pdksh does 139 | not. eg. 140 | $ cat << ~michael 141 | ~michael 142 | $ 143 | works for pdksh, not for at&t ksh (POSIX seems to agree with pdksh). 144 | - in at&t ksh, tracked aliases have the export flag implicitly set 145 | and tracked aliases and normal aliases live in the same name space 146 | (eg, "alias" will list both tracked and normal aliases). 147 | in pdksh, -t does not imply -x (since -x doesn't do anything yet), and 148 | tracked/normal aliases live in separate name spaces. 149 | in at&t ksh, alias accepts + options (eg, +x, +t) - pdksh does not. 150 | in pdksh, alias has a -d option to allow examination/changing of 151 | cached ~ entries, also unalias has -d and -t options (unalias -d 152 | is useful if the ~ cache gets out of date - not sure how at&t deals 153 | with this problem (it does cache ~ entries)). 154 | - at&t ksh will stop a recursive function after about 60 calls; pdksh 155 | will not since the limit is arbitrary and can't be controlled 156 | by the user (hit ^C if you get in trouble). 157 | - the wait command (with and without arguments) in at&t ksh will wait for 158 | stopped jobs when job control is enabled. pdksh doesn't. 159 | - at&t ksh automatically sets the bgnice option for interactive shells; 160 | pdksh does not. 161 | - in at&t ksh, "eval `false`; echo $?" prints 1, pdksh prints 0 (which 162 | is what POSIX says it should). Same goes for "wait `false`; echo $?". 163 | (same goes for "set `false`; echo $?" if posix option is set - some 164 | scripts that use the old getopt depend on this, so be careful about 165 | setting the posix option). 166 | - in at&t ksh, print -uX and read -uX are interrperted as -u with no 167 | argument (defaults to 1 and 0 respectively) and -X (which may or 168 | may not be a valid flag). In pdksh, -uX is interpreted as file 169 | descriptor X. 170 | - in at&t ksh, some signals (HUP, INT, QUIT) cause the read to exit, others 171 | (ie, everything else) do not. When it does cause exiting, anything read 172 | to that point is used (usually an empty line) and read returns with 0 173 | status. pdksh currently does similar things, but for TERM as well and 174 | the exit status is 128+ - in future, pdksh's read will 175 | do this for all signals that are normally fatal as required by POSIX. 176 | (POSIX does not require the setting of variables to null so applications 177 | shouldn't rely on this). 178 | - in pdksh, ! substitution done before variable substitution; in at&t ksh 179 | it is done after substitution (and therefore may do ! substitutions on 180 | the result of variable substitutions). POSIX doesn't say which is to be 181 | done. 182 | - pwd: in at&t ksh, it ignores arguments; in pdksh, it complains when given 183 | arguments. 184 | - the at&t ksh does not do command substition on PS1, pdksh does. 185 | - ksh93 allows ". foo" to run the function foo if there is no file 186 | called foo (go figure). 187 | - field splitting (IFS): ksh88/ksh93 strip leading non-white space IFS 188 | chars, pdksh (and POSIX, I think) leave them intact. e.g. 189 | $ IFS="$IFS:"; read x; echo "<$x>" 190 | :: 191 | prints "<>" in at&t ksh, "<::>" in pdksh. 192 | - command completion: at&t ksh will do completion on a blank line (matching 193 | all commands), pdksh does not (as this isn't very useful - use * if 194 | you really want the list). 195 | - co-processes: if ksh93, the write portion of the co-process output is 196 | closed when the most recently started co-process exits. pdksh closes 197 | it when all the co-processes using it have exited. 198 | - pdksh accepts empty command lists for while and for statements, while 199 | at&t ksh (and sh) don't. Eg., pdksh likes 200 | while false ; do done 201 | but ksh88 doesn't like it. 202 | - pdksh bumps RANDOM in parent after a fork, at&t ksh bumps it in both 203 | parent and child: 204 | RANDOM=1 205 | echo child: `echo $RANDOM` 206 | echo parent: $RANDOM 207 | will produce "child: 16838 parent: 5758" in pdksh, while at&t ksh 208 | will produce "child: 5758 parent: 5758". 209 | 210 | Oddities in ksh (pd & at&t): 211 | - array references inside (())/$(()) are strange: 212 | $(( x[2] )) does the expected, $(( $x[2] )) doesn't. 213 | - `typeset -R3 X='x '; echo "($X)"` produces ( x) - trailing 214 | spaces are stripped. 215 | - typeset -R turns off Z flag. 216 | - both shells have the following mis-feature: 217 | $ x='function xx { 218 | cat -n <<- EOF 219 | here we are in xx 220 | EOF 221 | }' 222 | $ (eval "$x"; (sleep 2; xx) & echo bye) 223 | [1] 1234 224 | bye 225 | $ xx: /tmp/sh1234.1: cannot open 226 | - bizarre special handling of alias/export/readonly/typeset arguments 227 | $ touch a=a; typeset a=[ab]; echo "$a" 228 | a=[ab] 229 | $ x=typeset; $x a=[ab]; echo "$a" 230 | a=a 231 | $ 232 | - both ignore SIGTSTP,SIGTTIN,SIGTTOU in exec'd processes when talking 233 | and not monitoring (at&t ksh kind of does this). Doesn't really make 234 | sense. 235 | (Note that ksh.att -ic 'set +m; check-sigs' shows TSTP et al aren't 236 | ignored, while ksh.att -ic 'set +m^J check-sigs' does... very strange) 237 | - when tracing (set -x), and a command's stderr is redirected, the trace 238 | output is also redirected. so "set -x; echo foo 2> /tmp/O > /dev/null" 239 | will create /tmp/foo with the lines "+ > /dev/null" and "+ echo foo". 240 | - undocumented at&t ksh88, documented in ksh93: FPATH is searched 241 | after PATH if no executable is found, even if typeset -uf wasn't used. 242 | 243 | POSIX sh questions (references are to POSIX 1003.2-1992) 244 | - arithmetic expressions: how are empty expressions treated? 245 | (eg, echo $(( ))). at&t ksh (and now pdksh) echo 0. 246 | Same question goes for `test "" -eq 0' - does this generate an error 247 | or, if not, what is the exit code? 248 | - if a signal is received during the execution of a built-in, 249 | does the builtin command exit or the whole shell? 250 | - is it legal to execute last command of pipeline in current 251 | execution environment (eg, can "echo foo | read bar" set 252 | bar?) 253 | - what action should be taken if there is an error doing a dup due 254 | to system limits (eg, not enough file destriptors): is this 255 | a "redirection error" (in which case a script will exit iff the 256 | error occured while executing a special built-in)? 257 | IMHO, shell should exit script. Couldn't find a blanket statement 258 | like "if shell encounters an unexpected system error, it shall 259 | exit non-interactive scripts"... 260 | 261 | POSIX sh bugs (references are to POSIX 1003.2-1992) 262 | - in vi insert mode, ^W deletes to beginning of line or to the first 263 | blank/punct character (para at line 9124, section 3). This means 264 | "foo ^W" will do nothing. This is inconsistent with the vi 265 | spec, which says delete preceding word including and interceding 266 | blanks (para at line 5189, section 5). 267 | - parameter expansion, section 3.6.2, line 391: `in each case that a 268 | value of word is needed (..), word shall be subjected to tilde 269 | expansion, parameter expansion, ...'. Various expansions should not 270 | be performed if parameter is in double quotes. 271 | - the getopts description says assigning OPTIND a value other than 1 272 | produces undefined results, while the rationale for getopts suggests 273 | saving/restoring the OPTIND value inside functions (since POSIX 274 | functions don't do the save/restore automatically). Restoring 275 | OPTIND is kind of dumb since getopts may have been in the middle 276 | of parsing a group of flags (eg, -abc). 277 | - `...` definition (3.6.3) says nothing about backslash followed by 278 | a newline, which sh and at&t ksh strip out completely. e.g., 279 | $ show-args `echo 'X 280 | Y'` 281 | Number of args: 1 282 | 1: 283 | $ 284 | POSIX would indicate the backslash-newline would be preserved. 285 | - does not say how "cat << ''" is to be treated (illegal, read 'til 286 | blank line, or read 'til eof). at&t ksh reads til eof, bourne shell 287 | reads 'til blank line. pdksh reads 'til blank line. 288 | -------------------------------------------------------------------------------- /PROJECTS: -------------------------------------------------------------------------------- 1 | $OpenBSD: PROJECTS,v 1.9 2018/01/08 12:08:17 jca Exp $ 2 | 3 | Things to be done in pdksh (see also the NOTES file): 4 | 5 | * builtin utilities: 6 | pdksh has most if not all POSIX/at&t ksh builtins, but they need to 7 | be checked that they conform to POSIX/at&t manual. Part of the 8 | process is changing the builtins to use the ksh_getopt() routine. 9 | 10 | The following builtins, which are defined by POSIX, haven't been 11 | examined: 12 | eval 13 | 14 | The first pass has been done on the following commands: 15 | . : alias bg break cd continue echo exec exit export false fc fg 16 | getopts jobs kill pwd read readonly return set shift time trap true 17 | umask unalias unset wait 18 | 19 | The second pass (ie, believed to be completely POSIX) has been done on 20 | the following commands: 21 | test 22 | 23 | (ulimit also needs to be examined to check that it fits the posix style) 24 | 25 | * trap code 26 | * add the DEBUG trap. 27 | * fix up signal handling code. In particular, fatal vs tty signals, 28 | have signal routine to call to check for pending/fatal traps, etc. 29 | 30 | * lexing 31 | the lexing may need a re-write since it currently doesn't parse $( .. ), 32 | $(( .. )), (( ... )) properly. 33 | * need to ignore contents of quoted strings (and escaped chars?) 34 | inside $( .. ) and $(( .. )) when counting parentheses. 35 | * need to put bounds check on states[] array (if it still exists after 36 | the re-write) 37 | 38 | * variables 39 | * The "struct tbl" that is currently used for variables needs work since 40 | more information (eg, array stuff, fields) are needed for variables 41 | but not for the other things that use "struct tbl". 42 | * Arrays need to be implemented differently: currently does a linear 43 | search of a linked list to find element i; the linked list is not 44 | freed when a variable is unset. 45 | 46 | * functions 47 | finish the differences between function x and x(): trap EXIT, traps 48 | in general, treatment of OPTIND/OPTARG, 49 | 50 | * history 51 | * Add multiline knowledge 52 | * bring history code up to POSIX standards (see POSIX description 53 | of fc, etc.). 54 | 55 | * miscellaneous 56 | * POSIX specifies what happens when various kinds of errors occur 57 | in special built-ins commands vs regular commands (builtin or 58 | otherwise) (see POSIX.2:3.8.1). Some of this has been taken 59 | care of, but more needs doing. 60 | 61 | * remove static limits created by fixed sized arrays 62 | (eg, ident[], heres[], PATH, buffer size in emacs/vi code) 63 | 64 | * merge the emacs and vi code (should reduce the size of the shell and 65 | make maintenance easier); handle SIGWINCH while editing a line. 66 | [John Rochester is working on the merge] 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | oksh 2 | ==== 3 | Portable OpenBSD `ksh(1)`. Not an official OpenBSD project. 4 | 5 | Why? 6 | ---- 7 | Because all operating systems deserve a good shell. 8 | 9 | Unlike other ports of OpenBSD ksh, this port is entirely self-contained and aims to be maximally portable across operating systems and C compilers. 10 | We are always looking for new combinations to add support for. 11 | 12 | Supported systems 13 | ----------------- 14 | `oksh` is known to run on the following Operating Systems: 15 | * OpenBSD 16 | * FreeBSD 17 | * DragonFly BSD 18 | * NetBSD 19 | * HardenedBSD 20 | * SoloBSD (as the default shell) 21 | * Mac OS X (port originally by @geoff-nixon) 22 | * Linux (glibc and musl) 23 | * Cygwin 24 | * Android (via Termux) 25 | * AIX (with major thanks to @tssva and @NattyNarwhal) 26 | * IBM i PASE 27 | * Solaris 28 | * Illumos 29 | * midipix 30 | * WSL 31 | * WSL2 32 | * Unixware 7 33 | * Haiku 34 | * HP-UX (gcc only) 35 | * SerenityOS 36 | * MSYS2 37 | 38 | Running on a system not listed here? Add it and send a pull request! 39 | 40 | Believed working 41 | ---------------- 42 | We believe that `oksh` will work on the following platforms, but testing is needed. 43 | Help is greatly appreciated and encouraged! 44 | * Irix 45 | 46 | Supported compilers 47 | ------------------- 48 | `oksh` is known to build with the following C compilers: 49 | * [clang](https://llvm.org/) 50 | * [gcc](https://gcc.gnu.org/) 51 | * [pcc](http://pcc.ludd.ltu.se/) 52 | * [cparser](https://pp.ipd.kit.edu/firm/) 53 | * [xlc](https://www.ibm.com/us-en/marketplace/ibm-c-and-c-plus-plus-compiler-family) 54 | * [Sun Studio compiler](https://www.oracle.com/technetwork/server-storage/developerstudio/overview/index.html) 55 | * [lacc](https://github.com/larmel/lacc) 56 | * Optimizing C Compilation System (CCS) 4.2 03/27/14 (uw714mp5.bl4s) 57 | * [Tiny C Compiler](https://bellard.org/tcc/) 58 | * [CompCert](https://compcert.org/) 59 | * [Nils Weller's C compiler](http://nwcc.sourceforge.net/) 60 | * [cproc](https://sr.ht/~mcf/cproc/) (Currently requires a small tweak to ignore a volatile store error) 61 | * [vbcc](http://www.compilers.de/vbcc.html) (Only tested on OpenBSD/i386) 62 | * [chibicc](https://github.com/rui314/chibicc) 63 | * [kefir](https://git.sr.ht/~jprotopopov/kefir) 64 | * IBM XL C/C++ Advanced Edition Version 6.0 for Mac OS X (tested on Tiger) 65 | 66 | Building with a compiler not listed here? Add it and send a pull request! 67 | 68 | Packages 69 | -------- 70 | `oksh` is included in some package systems. 71 | 72 | [![Packaging status](https://repology.org/badge/vertical-allrepos/oksh.svg)](https://repology.org/project/oksh/versions) 73 | 74 | In addition, there are some unofficial packages: 75 | * [Ubuntu PPA](https://launchpad.net/~dysfunctionalprogramming/+archive/ubuntu/oksh) 76 | * [Debian](https://software.opensuse.org//download.html?project=home%3AHead_on_a_Stick%3Aoksh&package=oksh) 77 | 78 | Using a package not listed here? Add it and send a pull request! 79 | 80 | Dependencies 81 | ------------ 82 | A C99 compiler is the easiest way to ensure that `oksh` will build correctly. 83 | Please see the list of C compilers above for a list of known working compilers. 84 | 85 | Though not required, the `ncurses` library will be used for screen clearing 86 | routines if the library is found during the `configure` stage. This can be 87 | turned off by the user by passing the `--disable-curses` flag to `configure`. 88 | 89 | A `configure` script that produces a `POSIX` `Makefile` is provided to 90 | ease building and installation and can be run by: 91 | ``` 92 | $ ./configure 93 | $ make && sudo make install 94 | ``` 95 | 96 | Out-of-tree builds 97 | ------------------ 98 | The `configure` script will detect out-of-tree builds if you prefer to 99 | build out-of-tree. In order for this to work, the `VPATH` make extension 100 | is used. While not POSIX, `VPATH` is known to work with BSD make and GNU 101 | make. In-tree builds create a fully POSIX `Makefile`. 102 | 103 | Cross compiling 104 | --------------- 105 | Cross compiling can be achieved by running `configure` as follows: 106 | ``` 107 | CC=/path/to/cross/cc CFLAGS="any needed cflags" LDFLAGS="any needed ldflags" ./configure --no-thanks 108 | ``` 109 | 110 | This will skip all `configure` checks and write out a generic `Makefile` 111 | and `pconfig.h` with nearly no options turned on. If using a cross gcc 112 | or clang, this very well may just work (with all compat compiled in). 113 | You can edit these files to reflect your system before running `make`. 114 | 115 | All environment variables and configure flags are respected when using 116 | `--no-thanks`. Further specifying `--no-link` after `--no-thanks` will 117 | only compile the source files into object files, to be transfered onto 118 | the target machine and linked there. 119 | 120 | The `--no-thanks` flag can also be used to compile a native `oksh` with 121 | all the compatibility functions compiled in, rather than relying on the 122 | system's version of those functions. 123 | 124 | Submitting patches 125 | ------------------ 126 | Patches that add new platforms and improve support for existing platforms 127 | are always welcome. 128 | 129 | Patches that cause `oksh` to deviate from upstream OpenBSD ksh behavior 130 | are better suited to be sent to the 131 | [OpenBSD tech@](https://www.openbsd.org/mail.html) 132 | mailing list. Please make sure to test your patch on an OpenBSD machine 133 | first before submitting it to tech@. I will sync with the upstream 134 | OpenBSD code once your patch is accepted. If you'd like to open an issue 135 | here to track progress of your patch on tech@, that's fine. 136 | 137 | License 138 | ------- 139 | The main Korn shell files are public domain (see `LEGAL`). 140 | Portability files are BSD or ISC licensed; see individual file headers 141 | for details. 142 | 143 | Get a tarball 144 | ------------- 145 | See releases tab. The latest release is oksh-7.7, which matches the ksh(1) 146 | from OpenBSD 7.7, released April 28, 2025. 147 | -------------------------------------------------------------------------------- /README.pdksh: -------------------------------------------------------------------------------- 1 | $OpenBSD: README,v 1.16 2017/05/11 20:17:17 jmc Exp $ 2 | 3 | Last updated Jul '99 for pdksh-5.2.14. 4 | 5 | PDksh is a mostly complete AT&T ksh look-alike (see NOTES file for a list 6 | of things not supported). Work is mostly finished to make it fully 7 | compatible with both POSIX and AT&T ksh (when the two don't conflict). 8 | 9 | PDksh was being maintained by Michael Rendell (michael@cs.mun.ca), 10 | who took over from Simon J. Gerraty (sjg@zen.void.oz.au) at the latter's 11 | suggestion. 12 | 13 | Files of interest: 14 | CONTRIBUTORS short history of pdksh, people who contributed, etc. 15 | NOTES lists of known bugs in pdksh, at&t ksh, and posix. 16 | PROJECTS list of things that need to be done in pdksh. 17 | LEGAL A file detailing legal issues concerning pdksh. 18 | 19 | 20 | BTW, THE MOST FREQUENTLY REPORTED BUG IS 21 | echo hi | read a; echo $a # Does not print hi 22 | I'm aware of this and there is no need to report it. 23 | -------------------------------------------------------------------------------- /alloc.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: alloc.c,v 1.19 2018/01/16 22:52:32 jca Exp $ */ 2 | 3 | /* Public domain, like most of the rest of ksh */ 4 | 5 | /* 6 | * area-based allocation built on malloc/free 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include "sh.h" 13 | 14 | struct link { 15 | struct link *prev; 16 | struct link *next; 17 | }; 18 | 19 | Area * 20 | ainit(Area *ap) 21 | { 22 | ap->freelist = NULL; 23 | return ap; 24 | } 25 | 26 | void 27 | afreeall(Area *ap) 28 | { 29 | struct link *l, *l2; 30 | 31 | for (l = ap->freelist; l != NULL; l = l2) { 32 | l2 = l->next; 33 | free(l); 34 | } 35 | ap->freelist = NULL; 36 | } 37 | 38 | #define L2P(l) ( (void *)(((char *)(l)) + sizeof(struct link)) ) 39 | #define P2L(p) ( (struct link *)(((char *)(p)) - sizeof(struct link)) ) 40 | 41 | void * 42 | alloc(size_t size, Area *ap) 43 | { 44 | struct link *l; 45 | 46 | /* ensure that we don't overflow by allocating space for link */ 47 | if (size > SIZE_MAX - sizeof(struct link)) 48 | internal_errorf("unable to allocate memory"); 49 | 50 | l = malloc(sizeof(struct link) + size); 51 | if (l == NULL) 52 | internal_errorf("unable to allocate memory"); 53 | l->next = ap->freelist; 54 | l->prev = NULL; 55 | if (ap->freelist) 56 | ap->freelist->prev = l; 57 | ap->freelist = l; 58 | 59 | return L2P(l); 60 | } 61 | 62 | /* 63 | * Copied from calloc(). 64 | * 65 | * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX 66 | * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW 67 | */ 68 | #define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4)) 69 | 70 | void * 71 | areallocarray(void *ptr, size_t nmemb, size_t size, Area *ap) 72 | { 73 | /* condition logic cloned from calloc() */ 74 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && 75 | nmemb > 0 && SIZE_MAX / nmemb < size) { 76 | internal_errorf("unable to allocate memory"); 77 | } 78 | 79 | return aresize(ptr, nmemb * size, ap); 80 | } 81 | 82 | void * 83 | aresize(void *ptr, size_t size, Area *ap) 84 | { 85 | struct link *l, *l2, *lprev, *lnext; 86 | 87 | if (ptr == NULL) 88 | return alloc(size, ap); 89 | 90 | /* ensure that we don't overflow by allocating space for link */ 91 | if (size > SIZE_MAX - sizeof(struct link)) 92 | internal_errorf("unable to allocate memory"); 93 | 94 | l = P2L(ptr); 95 | lprev = l->prev; 96 | lnext = l->next; 97 | 98 | l2 = realloc(l, sizeof(struct link) + size); 99 | if (l2 == NULL) 100 | internal_errorf("unable to allocate memory"); 101 | if (lprev) 102 | lprev->next = l2; 103 | else 104 | ap->freelist = l2; 105 | if (lnext) 106 | lnext->prev = l2; 107 | 108 | return L2P(l2); 109 | } 110 | 111 | void 112 | afree(void *ptr, Area *ap) 113 | { 114 | struct link *l; 115 | 116 | if (!ptr) 117 | return; 118 | 119 | l = P2L(ptr); 120 | if (l->prev) 121 | l->prev->next = l->next; 122 | else 123 | ap->freelist = l->next; 124 | if (l->next) 125 | l->next->prev = l->prev; 126 | 127 | free(l); 128 | } 129 | -------------------------------------------------------------------------------- /asprintf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004 Darren Tucker. 3 | * 4 | * Based originally on asprintf.c from OpenBSD: 5 | * Copyright (c) 1997 Todd C. Miller 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | #include "pconfig.h" 21 | 22 | #ifndef HAVE_ASPRINTF 23 | 24 | #include 25 | #include /* for INT_MAX */ 26 | #include 27 | #include /* for vsnprintf */ 28 | #include 29 | 30 | #define INIT_SZ 128 31 | 32 | int 33 | vasprintf(char **str, const char *fmt, va_list ap) 34 | { 35 | int ret; 36 | va_list ap2; 37 | char *string, *newstr; 38 | size_t len; 39 | 40 | if ((string = malloc(INIT_SZ)) == NULL) 41 | goto fail; 42 | 43 | va_copy(ap2, ap); 44 | ret = vsnprintf(string, INIT_SZ, fmt, ap2); 45 | va_end(ap2); 46 | if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */ 47 | *str = string; 48 | } else if (ret == INT_MAX || ret < 0) { /* Bad length */ 49 | free(string); 50 | goto fail; 51 | } else { /* bigger than initial, realloc allowing for nul */ 52 | len = (size_t)ret + 1; 53 | if ((newstr = realloc(string, len)) == NULL) { 54 | free(string); 55 | goto fail; 56 | } 57 | va_copy(ap2, ap); 58 | ret = vsnprintf(newstr, len, fmt, ap2); 59 | va_end(ap2); 60 | if (ret < 0 || (size_t)ret >= len) { /* failed with realloc'ed string */ 61 | free(newstr); 62 | goto fail; 63 | } 64 | *str = newstr; 65 | } 66 | return (ret); 67 | 68 | fail: 69 | *str = NULL; 70 | errno = ENOMEM; 71 | return (-1); 72 | } 73 | 74 | int asprintf(char **str, const char *fmt, ...) 75 | { 76 | va_list ap; 77 | int ret; 78 | 79 | *str = NULL; 80 | va_start(ap, fmt); 81 | ret = vasprintf(str, fmt, ap); 82 | va_end(ap); 83 | 84 | return ret; 85 | } 86 | #endif 87 | -------------------------------------------------------------------------------- /c_test.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: c_test.c,v 1.29 2025/03/24 22:19:42 millert Exp $ */ 2 | 3 | /* 4 | * test(1); version 7-like -- author Erik Baalbergen 5 | * modified by Eric Gisin to be used as built-in. 6 | * modified by Arnold Robbins to add SVR3 compatibility 7 | * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). 8 | * modified by Michael Rendell to add Korn's [[ .. ]] expressions. 9 | * modified by J.T. Conklin to add POSIX compatibility. 10 | */ 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "sh.h" 18 | #include "c_test.h" 19 | 20 | /* test(1) accepts the following grammar: 21 | oexpr ::= aexpr | aexpr "-o" oexpr ; 22 | aexpr ::= nexpr | nexpr "-a" aexpr ; 23 | nexpr ::= primary | "!" nexpr ; 24 | primary ::= unary-operator operand 25 | | operand binary-operator operand 26 | | operand 27 | | "(" oexpr ")" 28 | ; 29 | 30 | unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"| 31 | "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"| 32 | "-L"|"-h"|"-S"|"-H"; 33 | 34 | binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 35 | "-nt"|"-ot"|"-ef"|"<"|">" 36 | ; 37 | operand ::= 38 | */ 39 | 40 | #define T_ERR_EXIT 2 /* POSIX says > 1 for errors */ 41 | 42 | struct t_op { 43 | char op_text[4]; 44 | Test_op op_num; 45 | }; 46 | static const struct t_op u_ops [] = { 47 | {"-a", TO_FILAXST }, 48 | {"-b", TO_FILBDEV }, 49 | {"-c", TO_FILCDEV }, 50 | {"-d", TO_FILID }, 51 | {"-e", TO_FILEXST }, 52 | {"-f", TO_FILREG }, 53 | {"-G", TO_FILGID }, 54 | {"-g", TO_FILSETG }, 55 | {"-h", TO_FILSYM }, 56 | {"-H", TO_FILCDF }, 57 | {"-k", TO_FILSTCK }, 58 | {"-L", TO_FILSYM }, 59 | {"-n", TO_STNZE }, 60 | {"-O", TO_FILUID }, 61 | {"-o", TO_OPTION }, 62 | {"-p", TO_FILFIFO }, 63 | {"-r", TO_FILRD }, 64 | {"-s", TO_FILGZ }, 65 | {"-S", TO_FILSOCK }, 66 | {"-t", TO_FILTT }, 67 | {"-u", TO_FILSETU }, 68 | {"-w", TO_FILWR }, 69 | {"-x", TO_FILEX }, 70 | {"-z", TO_STZER }, 71 | {"", TO_NONOP } 72 | }; 73 | static const struct t_op b_ops [] = { 74 | {"=", TO_STEQL }, 75 | {"==", TO_STEQL }, 76 | {"!=", TO_STNEQ }, 77 | {"<", TO_STLT }, 78 | {">", TO_STGT }, 79 | {"-eq", TO_INTEQ }, 80 | {"-ne", TO_INTNE }, 81 | {"-gt", TO_INTGT }, 82 | {"-ge", TO_INTGE }, 83 | {"-lt", TO_INTLT }, 84 | {"-le", TO_INTLE }, 85 | {"-ef", TO_FILEQ }, 86 | {"-nt", TO_FILNT }, 87 | {"-ot", TO_FILOT }, 88 | {"", TO_NONOP } 89 | }; 90 | 91 | static int test_eaccess(const char *, int); 92 | static int test_oexpr(Test_env *, int); 93 | static int test_aexpr(Test_env *, int); 94 | static int test_nexpr(Test_env *, int); 95 | static int test_primary(Test_env *, int); 96 | static int ptest_isa(Test_env *, Test_meta); 97 | static const char *ptest_getopnd(Test_env *, Test_op, int); 98 | static int ptest_eval(Test_env *, Test_op, const char *, 99 | const char *, int); 100 | static void ptest_error(Test_env *, int, const char *); 101 | 102 | int 103 | c_test(char **wp) 104 | { 105 | int argc; 106 | int res; 107 | Test_env te; 108 | 109 | te.flags = 0; 110 | te.isa = ptest_isa; 111 | te.getopnd = ptest_getopnd; 112 | te.eval = ptest_eval; 113 | te.error = ptest_error; 114 | 115 | for (argc = 0; wp[argc]; argc++) 116 | ; 117 | 118 | if (strcmp(wp[0], "[") == 0) { 119 | if (strcmp(wp[--argc], "]") != 0) { 120 | bi_errorf("missing ]"); 121 | return T_ERR_EXIT; 122 | } 123 | } 124 | 125 | te.pos.wp = wp + 1; 126 | te.wp_end = wp + argc; 127 | 128 | /* 129 | * Handle the special cases from POSIX.2, section 4.62.4. 130 | * Implementation of all the rules isn't necessary since 131 | * our parser does the right thing for the omitted steps. 132 | */ 133 | if (argc <= 5) { 134 | char **owp = wp; 135 | int invert = 0; 136 | Test_op op; 137 | const char *opnd1, *opnd2; 138 | 139 | while (--argc >= 0) { 140 | if ((*te.isa)(&te, TM_END)) 141 | return !0; 142 | if (argc == 3) { 143 | opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); 144 | if ((op = (Test_op) (*te.isa)(&te, TM_BINOP))) { 145 | opnd2 = (*te.getopnd)(&te, op, 1); 146 | res = (*te.eval)(&te, op, opnd1, 147 | opnd2, 1); 148 | if (te.flags & TEF_ERROR) 149 | return T_ERR_EXIT; 150 | if (invert & 1) 151 | res = !res; 152 | return !res; 153 | } 154 | /* back up to opnd1 */ 155 | te.pos.wp--; 156 | } 157 | if (argc == 1) { 158 | opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); 159 | res = (*te.eval)(&te, TO_STNZE, opnd1, 160 | NULL, 1); 161 | if (invert & 1) 162 | res = !res; 163 | return !res; 164 | } 165 | if ((*te.isa)(&te, TM_NOT)) { 166 | invert++; 167 | } else 168 | break; 169 | } 170 | te.pos.wp = owp + 1; 171 | } 172 | 173 | return test_parse(&te); 174 | } 175 | 176 | /* 177 | * Generic test routines. 178 | */ 179 | 180 | Test_op 181 | test_isop(Test_env *te, Test_meta meta, const char *s) 182 | { 183 | char sc1; 184 | const struct t_op *otab; 185 | 186 | otab = meta == TM_UNOP ? u_ops : b_ops; 187 | if (*s) { 188 | sc1 = s[1]; 189 | for (; otab->op_text[0]; otab++) 190 | if (sc1 == otab->op_text[1] && 191 | strcmp(s, otab->op_text) == 0) 192 | return otab->op_num; 193 | } 194 | return TO_NONOP; 195 | } 196 | 197 | int 198 | test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, 199 | int do_eval) 200 | { 201 | int res; 202 | int not; 203 | struct stat b1, b2; 204 | 205 | if (!do_eval) 206 | return 0; 207 | 208 | switch ((int) op) { 209 | /* 210 | * Unary Operators 211 | */ 212 | case TO_STNZE: /* -n */ 213 | return *opnd1 != '\0'; 214 | case TO_STZER: /* -z */ 215 | return *opnd1 == '\0'; 216 | case TO_OPTION: /* -o */ 217 | if ((not = *opnd1 == '!')) 218 | opnd1++; 219 | if ((res = option(opnd1)) < 0) 220 | res = 0; 221 | else { 222 | res = Flag(res); 223 | if (not) 224 | res = !res; 225 | } 226 | return res; 227 | case TO_FILRD: /* -r */ 228 | return test_eaccess(opnd1, R_OK) == 0; 229 | case TO_FILWR: /* -w */ 230 | return test_eaccess(opnd1, W_OK) == 0; 231 | case TO_FILEX: /* -x */ 232 | return test_eaccess(opnd1, X_OK) == 0; 233 | case TO_FILAXST: /* -a */ 234 | return stat(opnd1, &b1) == 0; 235 | case TO_FILEXST: /* -e */ 236 | /* at&t ksh does not appear to do the /dev/fd/ thing for 237 | * this (unless the os itself handles it) 238 | */ 239 | return stat(opnd1, &b1) == 0; 240 | case TO_FILREG: /* -r */ 241 | return stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode); 242 | case TO_FILID: /* -d */ 243 | return stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode); 244 | case TO_FILCDEV: /* -c */ 245 | return stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode); 246 | case TO_FILBDEV: /* -b */ 247 | return stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode); 248 | case TO_FILFIFO: /* -p */ 249 | return stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode); 250 | case TO_FILSYM: /* -h -L */ 251 | return lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode); 252 | case TO_FILSOCK: /* -S */ 253 | return stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode); 254 | case TO_FILCDF:/* -H HP context dependent files (directories) */ 255 | return 0; 256 | case TO_FILSETU: /* -u */ 257 | return stat(opnd1, &b1) == 0 && 258 | (b1.st_mode & S_ISUID) == S_ISUID; 259 | case TO_FILSETG: /* -g */ 260 | return stat(opnd1, &b1) == 0 && 261 | (b1.st_mode & S_ISGID) == S_ISGID; 262 | case TO_FILSTCK: /* -k */ 263 | return stat(opnd1, &b1) == 0 && 264 | (b1.st_mode & S_ISVTX) == S_ISVTX; 265 | case TO_FILGZ: /* -s */ 266 | return stat(opnd1, &b1) == 0 && b1.st_size > 0L; 267 | case TO_FILTT: /* -t */ 268 | if (!bi_getn(opnd1, &res)) { 269 | te->flags |= TEF_ERROR; 270 | return 0; 271 | } 272 | return isatty(res); 273 | case TO_FILUID: /* -O */ 274 | return stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid; 275 | case TO_FILGID: /* -G */ 276 | return stat(opnd1, &b1) == 0 && b1.st_gid == getegid(); 277 | /* 278 | * Binary Operators 279 | */ 280 | case TO_STEQL: /* = */ 281 | if (te->flags & TEF_DBRACKET) 282 | return gmatch_(opnd1, opnd2, false); 283 | return strcmp(opnd1, opnd2) == 0; 284 | case TO_STNEQ: /* != */ 285 | if (te->flags & TEF_DBRACKET) 286 | return !gmatch_(opnd1, opnd2, false); 287 | return strcmp(opnd1, opnd2) != 0; 288 | case TO_STLT: /* < */ 289 | return strcmp(opnd1, opnd2) < 0; 290 | case TO_STGT: /* > */ 291 | return strcmp(opnd1, opnd2) > 0; 292 | case TO_INTEQ: /* -eq */ 293 | case TO_INTNE: /* -ne */ 294 | case TO_INTGE: /* -ge */ 295 | case TO_INTGT: /* -gt */ 296 | case TO_INTLE: /* -le */ 297 | case TO_INTLT: /* -lt */ 298 | { 299 | int64_t v1, v2; 300 | 301 | if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) || 302 | !evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) { 303 | /* error already printed.. */ 304 | te->flags |= TEF_ERROR; 305 | return 1; 306 | } 307 | switch ((int) op) { 308 | case TO_INTEQ: 309 | return v1 == v2; 310 | case TO_INTNE: 311 | return v1 != v2; 312 | case TO_INTGE: 313 | return v1 >= v2; 314 | case TO_INTGT: 315 | return v1 > v2; 316 | case TO_INTLE: 317 | return v1 <= v2; 318 | case TO_INTLT: 319 | return v1 < v2; 320 | } 321 | } 322 | case TO_FILNT: /* -nt */ 323 | { 324 | int s2; 325 | /* ksh88/ksh93 succeed if file2 can't be stated 326 | * (subtly different from `does not exist'). 327 | */ 328 | return stat(opnd1, &b1) == 0 && 329 | (((s2 = stat(opnd2, &b2)) == 0 && 330 | timespeccmp(&b1.st_mtim, &b2.st_mtim, >)) || 331 | s2 != 0); 332 | } 333 | case TO_FILOT: /* -ot */ 334 | { 335 | int s1; 336 | /* ksh88/ksh93 succeed if file1 can't be stated 337 | * (subtly different from `does not exist'). 338 | */ 339 | return stat(opnd2, &b2) == 0 && 340 | (((s1 = stat(opnd1, &b1)) == 0 && 341 | timespeccmp(&b1.st_mtim, &b2.st_mtim, <)) || 342 | s1 != 0); 343 | } 344 | case TO_FILEQ: /* -ef */ 345 | return stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 && 346 | b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; 347 | } 348 | (*te->error)(te, 0, "internal error: unknown op"); 349 | return 1; 350 | } 351 | 352 | /* Routine to deal with X_OK on non-directories when running as root. 353 | */ 354 | static int 355 | test_eaccess(const char *path, int amode) 356 | { 357 | int res; 358 | 359 | res = access(path, amode); 360 | /* 361 | * On most (all?) unixes, access() says everything is executable for 362 | * root - avoid this on files by using stat(). 363 | */ 364 | if (res == 0 && ksheuid == 0 && (amode & X_OK)) { 365 | struct stat statb; 366 | 367 | if (stat(path, &statb) == -1) 368 | res = -1; 369 | else if (S_ISDIR(statb.st_mode)) 370 | res = 0; 371 | else 372 | res = (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) ? 373 | 0 : -1; 374 | } 375 | 376 | return res; 377 | } 378 | 379 | int 380 | test_parse(Test_env *te) 381 | { 382 | int res; 383 | 384 | res = test_oexpr(te, 1); 385 | 386 | if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END)) 387 | (*te->error)(te, 0, "unexpected operator/operand"); 388 | 389 | return (te->flags & TEF_ERROR) ? T_ERR_EXIT : !res; 390 | } 391 | 392 | static int 393 | test_oexpr(Test_env *te, int do_eval) 394 | { 395 | int res; 396 | 397 | res = test_aexpr(te, do_eval); 398 | if (res) 399 | do_eval = 0; 400 | if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR)) 401 | return test_oexpr(te, do_eval) || res; 402 | return res; 403 | } 404 | 405 | static int 406 | test_aexpr(Test_env *te, int do_eval) 407 | { 408 | int res; 409 | 410 | res = test_nexpr(te, do_eval); 411 | if (!res) 412 | do_eval = 0; 413 | if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND)) 414 | return test_aexpr(te, do_eval) && res; 415 | return res; 416 | } 417 | 418 | static int 419 | test_nexpr(Test_env *te, int do_eval) 420 | { 421 | if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT)) 422 | return !test_nexpr(te, do_eval); 423 | return test_primary(te, do_eval); 424 | } 425 | 426 | static int 427 | test_primary(Test_env *te, int do_eval) 428 | { 429 | const char *opnd1, *opnd2; 430 | int res; 431 | Test_op op; 432 | 433 | if (te->flags & TEF_ERROR) 434 | return 0; 435 | if ((*te->isa)(te, TM_OPAREN)) { 436 | res = test_oexpr(te, do_eval); 437 | if (te->flags & TEF_ERROR) 438 | return 0; 439 | if (!(*te->isa)(te, TM_CPAREN)) { 440 | (*te->error)(te, 0, "missing closing paren"); 441 | return 0; 442 | } 443 | return res; 444 | } 445 | /* 446 | * Binary should have precedence over unary in this case 447 | * so that something like test \( -f = -f \) is accepted 448 | */ 449 | if ((te->flags & TEF_DBRACKET) || (&te->pos.wp[1] < te->wp_end && 450 | !test_isop(te, TM_BINOP, te->pos.wp[1]))) { 451 | if ((op = (Test_op) (*te->isa)(te, TM_UNOP))) { 452 | /* unary expression */ 453 | opnd1 = (*te->getopnd)(te, op, do_eval); 454 | if (!opnd1) { 455 | (*te->error)(te, -1, "missing argument"); 456 | return 0; 457 | } 458 | 459 | return (*te->eval)(te, op, opnd1, NULL, 460 | do_eval); 461 | } 462 | } 463 | opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval); 464 | if (!opnd1) { 465 | (*te->error)(te, 0, "expression expected"); 466 | return 0; 467 | } 468 | if ((op = (Test_op) (*te->isa)(te, TM_BINOP))) { 469 | /* binary expression */ 470 | opnd2 = (*te->getopnd)(te, op, do_eval); 471 | if (!opnd2) { 472 | (*te->error)(te, -1, "missing second argument"); 473 | return 0; 474 | } 475 | 476 | return (*te->eval)(te, op, opnd1, opnd2, do_eval); 477 | } 478 | if (te->flags & TEF_DBRACKET) { 479 | (*te->error)(te, -1, "missing expression operator"); 480 | return 0; 481 | } 482 | return (*te->eval)(te, TO_STNZE, opnd1, NULL, do_eval); 483 | } 484 | 485 | /* 486 | * Plain test (test and [ .. ]) specific routines. 487 | */ 488 | 489 | /* Test if the current token is a whatever. Accepts the current token if 490 | * it is. Returns 0 if it is not, non-zero if it is (in the case of 491 | * TM_UNOP and TM_BINOP, the returned value is a Test_op). 492 | */ 493 | static int 494 | ptest_isa(Test_env *te, Test_meta meta) 495 | { 496 | /* Order important - indexed by Test_meta values */ 497 | static const char *const tokens[] = { 498 | "-o", "-a", "!", "(", ")" 499 | }; 500 | int ret; 501 | 502 | if (te->pos.wp >= te->wp_end) 503 | return meta == TM_END; 504 | 505 | if (meta == TM_UNOP || meta == TM_BINOP) 506 | ret = (int) test_isop(te, meta, *te->pos.wp); 507 | else if (meta == TM_END) 508 | ret = 0; 509 | else 510 | ret = strcmp(*te->pos.wp, tokens[(int) meta]) == 0; 511 | 512 | /* Accept the token? */ 513 | if (ret) 514 | te->pos.wp++; 515 | 516 | return ret; 517 | } 518 | 519 | static const char * 520 | ptest_getopnd(Test_env *te, Test_op op, int do_eval) 521 | { 522 | if (te->pos.wp >= te->wp_end) 523 | return NULL; 524 | return *te->pos.wp++; 525 | } 526 | 527 | static int 528 | ptest_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, 529 | int do_eval) 530 | { 531 | return test_eval(te, op, opnd1, opnd2, do_eval); 532 | } 533 | 534 | static void 535 | ptest_error(Test_env *te, int offset, const char *msg) 536 | { 537 | const char *op = te->pos.wp + offset >= te->wp_end ? 538 | NULL : te->pos.wp[offset]; 539 | 540 | te->flags |= TEF_ERROR; 541 | if (op) 542 | bi_errorf("%s: %s", op, msg); 543 | else 544 | bi_errorf("%s", msg); 545 | } 546 | -------------------------------------------------------------------------------- /c_test.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $ */ 2 | 3 | /* Various types of operations. Keeping things grouped nicely 4 | * (unary,binary) makes switch() statements more efficient. 5 | */ 6 | enum Test_op { 7 | TO_NONOP = 0, /* non-operator */ 8 | /* unary operators */ 9 | TO_STNZE, TO_STZER, TO_OPTION, 10 | TO_FILAXST, 11 | TO_FILEXST, 12 | TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK, 13 | TO_FILCDF, TO_FILID, TO_FILGID, TO_FILSETG, TO_FILSTCK, TO_FILUID, 14 | TO_FILRD, TO_FILGZ, TO_FILTT, TO_FILSETU, TO_FILWR, TO_FILEX, 15 | /* binary operators */ 16 | TO_STEQL, TO_STNEQ, TO_STLT, TO_STGT, TO_INTEQ, TO_INTNE, TO_INTGT, 17 | TO_INTGE, TO_INTLT, TO_INTLE, TO_FILEQ, TO_FILNT, TO_FILOT 18 | }; 19 | typedef enum Test_op Test_op; 20 | 21 | /* Used by Test_env.isa() (order important - used to index *_tokens[] arrays) */ 22 | enum Test_meta { 23 | TM_OR, /* -o or || */ 24 | TM_AND, /* -a or && */ 25 | TM_NOT, /* ! */ 26 | TM_OPAREN, /* ( */ 27 | TM_CPAREN, /* ) */ 28 | TM_UNOP, /* unary operator */ 29 | TM_BINOP, /* binary operator */ 30 | TM_END /* end of input */ 31 | }; 32 | typedef enum Test_meta Test_meta; 33 | 34 | #define TEF_ERROR BIT(0) /* set if we've hit an error */ 35 | #define TEF_DBRACKET BIT(1) /* set if [[ .. ]] test */ 36 | 37 | typedef struct test_env Test_env; 38 | struct test_env { 39 | int flags; /* TEF_* */ 40 | union { 41 | char **wp; /* used by ptest_* */ 42 | XPtrV *av; /* used by dbtestp_* */ 43 | } pos; 44 | char **wp_end; /* used by ptest_* */ 45 | int (*isa)(Test_env *, Test_meta); 46 | const char *(*getopnd) (Test_env *, Test_op, int); 47 | int (*eval)(Test_env *, Test_op, const char *, const char *, int); 48 | void (*error)(Test_env *, int, const char *); 49 | }; 50 | 51 | Test_op test_isop(Test_env *, Test_meta, const char *); 52 | int test_eval(Test_env *, Test_op, const char *, const char *, int); 53 | int test_parse(Test_env *); 54 | -------------------------------------------------------------------------------- /c_ulimit.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: c_ulimit.c,v 1.29 2019/06/28 13:34:59 deraadt Exp $ */ 2 | 3 | /* 4 | ulimit -- handle "ulimit" builtin 5 | 6 | Reworked to use getrusage() and ulimit() at once (as needed on 7 | some schizophrenic systems, eg, HP-UX 9.01), made argument parsing 8 | conform to at&t ksh, added autoconf support. Michael Rendell, May, '94 9 | 10 | Eric Gisin, September 1988 11 | Adapted to PD KornShell. Removed AT&T code. 12 | 13 | last edit: 06-Jun-1987 D A Gwyn 14 | 15 | This started out as the BRL UNIX System V system call emulation 16 | for 4.nBSD, and was later extended by Doug Kingston to handle 17 | the extended 4.nBSD resource limits. It now includes the code 18 | that was originally under case SYSULIMIT in source file "xec.c". 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "sh.h" 29 | 30 | #define SOFT 0x1 31 | #define HARD 0x2 32 | 33 | struct limits { 34 | const char *name; 35 | int resource; /* resource to get/set */ 36 | int factor; /* multiply by to get rlim_{cur,max} values */ 37 | char option; /* option character (-d, -f, ...) */ 38 | }; 39 | 40 | static void print_ulimit(const struct limits *, int); 41 | static int set_ulimit(const struct limits *, const char *, int); 42 | 43 | int 44 | c_ulimit(char **wp) 45 | { 46 | static const struct limits limits[] = { 47 | /* Do not use options -H, -S or -a or change the order. */ 48 | { "time(cpu-seconds)", RLIMIT_CPU, 1, 't' }, 49 | { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, 50 | { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, 51 | { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, 52 | { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, 53 | { "lockedmem(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, 54 | { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, 55 | { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, 56 | { "processes", RLIMIT_NPROC, 1, 'p' }, 57 | { NULL } 58 | }; 59 | const char *options = "HSat#f#c#d#s#l#m#n#p#"; 60 | int how = SOFT | HARD; 61 | const struct limits *l; 62 | int optc, all = 0; 63 | 64 | /* First check for -a, -H and -S. */ 65 | while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1) 66 | switch (optc) { 67 | case 'H': 68 | how = HARD; 69 | break; 70 | case 'S': 71 | how = SOFT; 72 | break; 73 | case 'a': 74 | all = 1; 75 | break; 76 | case '?': 77 | return 1; 78 | default: 79 | break; 80 | } 81 | 82 | if (wp[builtin_opt.optind] != NULL) { 83 | bi_errorf("usage: ulimit [-acdfHlmnpSst] [value]"); 84 | return 1; 85 | } 86 | 87 | /* Then parse and act on the actual limits, one at a time */ 88 | ksh_getopt_reset(&builtin_opt, GF_ERROR); 89 | while ((optc = ksh_getopt(wp, &builtin_opt, options)) != -1) 90 | switch (optc) { 91 | case 'a': 92 | case 'H': 93 | case 'S': 94 | break; 95 | case '?': 96 | return 1; 97 | default: 98 | for (l = limits; l->name && l->option != optc; l++) 99 | ; 100 | if (!l->name) { 101 | internal_warningf("%s: %c", __func__, optc); 102 | return 1; 103 | } 104 | if (builtin_opt.optarg) { 105 | if (set_ulimit(l, builtin_opt.optarg, how)) 106 | return 1; 107 | } else 108 | print_ulimit(l, how); 109 | break; 110 | } 111 | 112 | wp += builtin_opt.optind; 113 | 114 | if (all) { 115 | for (l = limits; l->name; l++) { 116 | shprintf("%-20s ", l->name); 117 | print_ulimit(l, how); 118 | } 119 | } else if (builtin_opt.optind == 1) { 120 | /* No limit specified, use file size */ 121 | l = &limits[1]; 122 | if (wp[0] != NULL) { 123 | if (set_ulimit(l, wp[0], how)) 124 | return 1; 125 | wp++; 126 | } else { 127 | print_ulimit(l, how); 128 | } 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | static int 135 | set_ulimit(const struct limits *l, const char *v, int how) 136 | { 137 | rlim_t val = 0; 138 | struct rlimit limit; 139 | 140 | if (strcmp(v, "unlimited") == 0) 141 | val = RLIM_INFINITY; 142 | else { 143 | int64_t rval; 144 | 145 | if (!evaluate(v, &rval, KSH_RETURN_ERROR, false)) 146 | return 1; 147 | /* 148 | * Avoid problems caused by typos that evaluate misses due 149 | * to evaluating unset parameters to 0... 150 | * If this causes problems, will have to add parameter to 151 | * evaluate() to control if unset params are 0 or an error. 152 | */ 153 | if (!rval && !digit(v[0])) { 154 | bi_errorf("invalid limit: %s", v); 155 | return 1; 156 | } 157 | val = (rlim_t)rval * l->factor; 158 | } 159 | 160 | getrlimit(l->resource, &limit); 161 | if (how & SOFT) 162 | limit.rlim_cur = val; 163 | if (how & HARD) 164 | limit.rlim_max = val; 165 | if (setrlimit(l->resource, &limit) == -1) { 166 | if (errno == EPERM) 167 | bi_errorf("-%c exceeds allowable limit", l->option); 168 | else 169 | bi_errorf("bad -%c limit: %s", l->option, 170 | strerror(errno)); 171 | return 1; 172 | } 173 | return 0; 174 | } 175 | 176 | static void 177 | print_ulimit(const struct limits *l, int how) 178 | { 179 | rlim_t val = 0; 180 | struct rlimit limit; 181 | 182 | getrlimit(l->resource, &limit); 183 | if (how & SOFT) 184 | val = limit.rlim_cur; 185 | else if (how & HARD) 186 | val = limit.rlim_max; 187 | if (val == RLIM_INFINITY) 188 | shprintf("unlimited\n"); 189 | else { 190 | val /= l->factor; 191 | shprintf("%" PRIi64 "\n", (int64_t) val); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /charclass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Public domain, 2008, Todd C. Miller 3 | * 4 | * $OpenBSD: charclass.h,v 1.3 2020/10/13 04:42:28 guenther Exp $ 5 | */ 6 | 7 | /* 8 | * POSIX character class support for fnmatch() and glob(). 9 | */ 10 | static const struct cclass { 11 | const char *name; 12 | int (*isctype)(int); 13 | } cclasses[] = { 14 | { "alnum", isalnum }, 15 | { "alpha", isalpha }, 16 | { "blank", isblank }, 17 | { "cntrl", iscntrl }, 18 | { "digit", isdigit }, 19 | { "graph", isgraph }, 20 | { "lower", islower }, 21 | { "print", isprint }, 22 | { "punct", ispunct }, 23 | { "space", isspace }, 24 | { "upper", isupper }, 25 | { "xdigit", isxdigit }, 26 | { NULL, NULL } 27 | }; 28 | 29 | #define NCCLASSES (sizeof(cclasses) / sizeof(cclasses[0]) - 1) 30 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: config.h,v 1.19 2018/01/15 14:58:05 jca Exp $ */ 2 | 3 | /* config.h. NOT generated automatically. */ 4 | 5 | /* 6 | * This file, config.h, which is a part of pdksh (the public domain ksh), 7 | * is placed in the public domain. It comes with no licence, warranty 8 | * or guarantee of any kind (i.e., at your own risk). 9 | */ 10 | 11 | #ifndef CONFIG_H 12 | #define CONFIG_H 13 | 14 | /* Strict POSIX behaviour? */ 15 | /* #undef POSIXLY_CORRECT */ 16 | 17 | /* Specify default $ENV? */ 18 | /* #undef DEFAULT_ENV */ 19 | 20 | /* 21 | * End of configuration stuff for PD ksh. 22 | */ 23 | 24 | #if !defined(EMACS) && !defined(VI) 25 | # error "Define either EMACS or VI." 26 | #endif 27 | 28 | #include "portable.h" 29 | 30 | #endif /* CONFIG_H */ 31 | -------------------------------------------------------------------------------- /confstr.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: confstr.c,v 1.10 2013/03/07 06:00:18 guenther Exp $ */ 2 | /*- 3 | * Copyright (c) 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "pconfig.h" 32 | 33 | #ifndef HAVE_CONFSTR 34 | 35 | #include 36 | #include 37 | 38 | size_t 39 | confstr(int name, char *buf, size_t len) 40 | { 41 | 42 | return (strlcpy(buf, _PATH_STDPATH, len) + 1); 43 | } 44 | 45 | #endif /* !HAVE_CONFSTR */ 46 | -------------------------------------------------------------------------------- /dot.profile: -------------------------------------------------------------------------------- 1 | # oksh initialization 2 | # Sample .profile 3 | 4 | PATH=$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin:/usr/games 5 | export PATH HOME TERM 6 | 7 | # Uncomment to set default prompt to the current working directory. 8 | # PS1='$PWD $ ' 9 | -------------------------------------------------------------------------------- /edit.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: edit.h,v 1.13 2023/06/21 22:22:08 millert Exp $ */ 2 | 3 | /* NAME: 4 | * edit.h - globals for edit modes 5 | * 6 | * DESCRIPTION: 7 | * This header defines various global edit objects. 8 | * 9 | * SEE ALSO: 10 | * 11 | * 12 | * RCSid: 13 | * $From: edit.h,v 1.2 1994/05/19 18:32:40 michael Exp michael $ 14 | * 15 | */ 16 | 17 | #define BEL 0x07 18 | 19 | #undef CTRL 20 | #define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */ 21 | #define UNCTRL(x) ((x) == 0x7F ? '?' : (x) | 0x40) /* ASCII */ 22 | 23 | /* tty driver characters we are interested in */ 24 | typedef struct { 25 | int erase; 26 | int kill; 27 | int werase; 28 | int intr; 29 | int quit; 30 | int eof; 31 | } X_chars; 32 | 33 | extern X_chars edchars; 34 | 35 | /* x_cf_glob() flags */ 36 | #define XCF_COMMAND BIT(0) /* Do command completion */ 37 | #define XCF_FILE BIT(1) /* Do file completion */ 38 | #define XCF_FULLPATH BIT(2) /* command completion: store full path */ 39 | #define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE) 40 | 41 | /* edit.c */ 42 | int x_getc(void); 43 | void x_flush(void); 44 | int x_putc(int); 45 | void x_puts(const char *); 46 | bool x_mode(bool); 47 | int promptlen(const char *, const char **); 48 | int x_do_comment(char *, int, int *); 49 | void x_print_expansions(int, char *const *, int); 50 | int x_cf_glob(int, const char *, int, int, int *, int *, char ***, int *); 51 | int x_longest_prefix(int , char *const *); 52 | int x_basename(const char *, const char *); 53 | void x_free_words(int, char **); 54 | int x_escape(const char *, size_t, int (*)(const char *, size_t)); 55 | /* emacs.c */ 56 | int x_emacs(char *, size_t); 57 | void x_init_emacs(void); 58 | void x_emacs_keys(X_chars *); 59 | /* vi.c */ 60 | int x_vi(char *, size_t); 61 | -------------------------------------------------------------------------------- /expand.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: expand.h,v 1.15 2018/01/06 16:28:58 millert Exp $ */ 2 | 3 | /* 4 | * Expanding strings 5 | */ 6 | 7 | #define X_EXTRA 8 /* this many extra bytes in X string */ 8 | 9 | #if 0 /* Usage */ 10 | XString xs; 11 | char *xp; 12 | 13 | Xinit(xs, xp, 128, ATEMP); /* allocate initial string */ 14 | while ((c = generate()) { 15 | Xcheck(xs, xp); /* expand string if necessary */ 16 | Xput(xs, xp, c); /* add character */ 17 | } 18 | return Xclose(xs, xp); /* resize string */ 19 | /* 20 | * NOTE: 21 | * The Xcheck and Xinit macros have a magic + X_EXTRA in the lengths. 22 | * This is so that you can put up to X_EXTRA characters in a XString 23 | * before calling Xcheck. (See yylex in lex.c) 24 | */ 25 | #endif /* 0 */ 26 | 27 | typedef struct XString { 28 | char *end, *beg; /* end, begin of string */ 29 | size_t len; /* length */ 30 | Area *areap; /* area to allocate/free from */ 31 | } XString; 32 | 33 | typedef char * XStringP; 34 | 35 | /* initialize expandable string */ 36 | #define Xinit(xs, xp, length, area) do { \ 37 | (xs).len = length; \ 38 | (xs).areap = (area); \ 39 | (xs).beg = alloc((xs).len + X_EXTRA, (xs).areap); \ 40 | (xs).end = (xs).beg + (xs).len; \ 41 | xp = (xs).beg; \ 42 | } while (0) 43 | 44 | /* stuff char into string */ 45 | #define Xput(xs, xp, c) (*xp++ = (c)) 46 | 47 | /* check if there are at least n bytes left */ 48 | #define XcheckN(xs, xp, n) do { \ 49 | ptrdiff_t more = ((xp) + (n)) - (xs).end; \ 50 | if (more > 0) \ 51 | xp = Xcheck_grow_(&xs, xp, more); \ 52 | } while (0) 53 | 54 | /* check for overflow, expand string */ 55 | #define Xcheck(xs, xp) XcheckN(xs, xp, 1) 56 | 57 | /* free string */ 58 | #define Xfree(xs, xp) afree((xs).beg, (xs).areap) 59 | 60 | /* close, return string */ 61 | #define Xclose(xs, xp) aresize((xs).beg, ((xp) - (xs).beg), (xs).areap) 62 | /* begin of string */ 63 | #define Xstring(xs, xp) ((xs).beg) 64 | 65 | #define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */ 66 | #define Xlength(xs, xp) ((xp) - (xs).beg) 67 | #define Xsize(xs, xp) ((xs).end - (xs).beg) 68 | #define Xsavepos(xs, xp) ((xp) - (xs).beg) 69 | #define Xrestpos(xs, xp, n) ((xs).beg + (n)) 70 | 71 | char * Xcheck_grow_(XString *xsp, char *xp, size_t more); 72 | 73 | /* 74 | * expandable vector of generic pointers 75 | */ 76 | 77 | typedef struct XPtrV { 78 | void **cur; /* next avail pointer */ 79 | void **beg, **end; /* begin, end of vector */ 80 | } XPtrV; 81 | 82 | #define XPinit(x, n) do { \ 83 | void **vp__; \ 84 | vp__ = areallocarray(NULL, n, sizeof(void *), ATEMP); \ 85 | (x).cur = (x).beg = vp__; \ 86 | (x).end = vp__ + n; \ 87 | } while (0) 88 | 89 | #define XPput(x, p) do { \ 90 | if ((x).cur >= (x).end) { \ 91 | int n = XPsize(x); \ 92 | (x).beg = areallocarray((x).beg, n, \ 93 | 2 * sizeof(void *), ATEMP); \ 94 | (x).cur = (x).beg + n; \ 95 | (x).end = (x).cur + n; \ 96 | } \ 97 | *(x).cur++ = (p); \ 98 | } while (0) 99 | 100 | #define XPptrv(x) ((x).beg) 101 | #define XPsize(x) ((x).cur - (x).beg) 102 | 103 | #define XPclose(x) areallocarray((x).beg, XPsize(x), \ 104 | sizeof(void *), ATEMP) 105 | 106 | #define XPfree(x) afree((x).beg, ATEMP) 107 | -------------------------------------------------------------------------------- /expr.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: expr.c,v 1.34 2019/02/20 23:59:17 schwarze Exp $ */ 2 | 3 | /* 4 | * Korn expression evaluation 5 | */ 6 | /* 7 | * todo: better error handling: if in builtin, should be builtin error, etc. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "sh.h" 15 | 16 | /* The order of these enums is constrained by the order of opinfo[] */ 17 | enum token { 18 | /* some (long) unary operators */ 19 | O_PLUSPLUS = 0, O_MINUSMINUS, 20 | /* binary operators */ 21 | O_EQ, O_NE, 22 | /* assignments are assumed to be in range O_ASN .. O_BORASN */ 23 | O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN, 24 | O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN, 25 | O_LSHIFT, O_RSHIFT, 26 | O_LE, O_GE, O_LT, O_GT, 27 | O_LAND, 28 | O_LOR, 29 | O_TIMES, O_DIV, O_MOD, 30 | O_PLUS, O_MINUS, 31 | O_BAND, 32 | O_BXOR, 33 | O_BOR, 34 | O_TERN, 35 | O_COMMA, 36 | /* things after this aren't used as binary operators */ 37 | /* unary that are not also binaries */ 38 | O_BNOT, O_LNOT, 39 | /* misc */ 40 | OPEN_PAREN, CLOSE_PAREN, CTERN, 41 | /* things that don't appear in the opinfo[] table */ 42 | VAR, LIT, END, BAD 43 | }; 44 | #define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA) 45 | #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN) 46 | 47 | enum prec { 48 | P_PRIMARY = 0, /* VAR, LIT, (), ~ ! - + */ 49 | P_MULT, /* * / % */ 50 | P_ADD, /* + - */ 51 | P_SHIFT, /* << >> */ 52 | P_RELATION, /* < <= > >= */ 53 | P_EQUALITY, /* == != */ 54 | P_BAND, /* & */ 55 | P_BXOR, /* ^ */ 56 | P_BOR, /* | */ 57 | P_LAND, /* && */ 58 | P_LOR, /* || */ 59 | P_TERN, /* ?: */ 60 | P_ASSIGN, /* = *= /= %= += -= <<= >>= &= ^= |= */ 61 | P_COMMA /* , */ 62 | }; 63 | #define MAX_PREC P_COMMA 64 | 65 | struct opinfo { 66 | char name[4]; 67 | int len; /* name length */ 68 | enum prec prec; /* precedence: lower is higher */ 69 | }; 70 | 71 | /* Tokens in this table must be ordered so the longest are first 72 | * (eg, += before +). If you change something, change the order 73 | * of enum token too. 74 | */ 75 | static const struct opinfo opinfo[] = { 76 | { "++", 2, P_PRIMARY }, /* before + */ 77 | { "--", 2, P_PRIMARY }, /* before - */ 78 | { "==", 2, P_EQUALITY }, /* before = */ 79 | { "!=", 2, P_EQUALITY }, /* before ! */ 80 | { "=", 1, P_ASSIGN }, /* keep assigns in a block */ 81 | { "*=", 2, P_ASSIGN }, 82 | { "/=", 2, P_ASSIGN }, 83 | { "%=", 2, P_ASSIGN }, 84 | { "+=", 2, P_ASSIGN }, 85 | { "-=", 2, P_ASSIGN }, 86 | { "<<=", 3, P_ASSIGN }, 87 | { ">>=", 3, P_ASSIGN }, 88 | { "&=", 2, P_ASSIGN }, 89 | { "^=", 2, P_ASSIGN }, 90 | { "|=", 2, P_ASSIGN }, 91 | { "<<", 2, P_SHIFT }, 92 | { ">>", 2, P_SHIFT }, 93 | { "<=", 2, P_RELATION }, 94 | { ">=", 2, P_RELATION }, 95 | { "<", 1, P_RELATION }, 96 | { ">", 1, P_RELATION }, 97 | { "&&", 2, P_LAND }, 98 | { "||", 2, P_LOR }, 99 | { "*", 1, P_MULT }, 100 | { "/", 1, P_MULT }, 101 | { "%", 1, P_MULT }, 102 | { "+", 1, P_ADD }, 103 | { "-", 1, P_ADD }, 104 | { "&", 1, P_BAND }, 105 | { "^", 1, P_BXOR }, 106 | { "|", 1, P_BOR }, 107 | { "?", 1, P_TERN }, 108 | { ",", 1, P_COMMA }, 109 | { "~", 1, P_PRIMARY }, 110 | { "!", 1, P_PRIMARY }, 111 | { "(", 1, P_PRIMARY }, 112 | { ")", 1, P_PRIMARY }, 113 | { ":", 1, P_PRIMARY }, 114 | { "", 0, P_PRIMARY } /* end of table */ 115 | }; 116 | 117 | 118 | typedef struct expr_state Expr_state; 119 | struct expr_state { 120 | const char *expression; /* expression being evaluated */ 121 | const char *tokp; /* lexical position */ 122 | enum token tok; /* token from token() */ 123 | int noassign; /* don't do assigns (for ?:,&&,||) */ 124 | bool arith; /* true if evaluating an $(()) 125 | * expression 126 | */ 127 | struct tbl *val; /* value from token() */ 128 | struct tbl *evaling; /* variable that is being recursively 129 | * expanded (EXPRINEVAL flag set) 130 | */ 131 | }; 132 | 133 | enum error_type { 134 | ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE, 135 | ET_LVALUE, ET_RDONLY, ET_STR 136 | }; 137 | 138 | static void evalerr(Expr_state *, enum error_type, const char *) 139 | __attribute__((__noreturn__)); 140 | static struct tbl *evalexpr(Expr_state *, enum prec); 141 | static void token(Expr_state *); 142 | static struct tbl *do_ppmm(Expr_state *, enum token, struct tbl *, bool); 143 | static void assign_check(Expr_state *, enum token, struct tbl *); 144 | static struct tbl *tempvar(void); 145 | static struct tbl *intvar(Expr_state *, struct tbl *); 146 | 147 | /* 148 | * parse and evaluate expression 149 | */ 150 | int 151 | evaluate(const char *expr, int64_t *rval, int error_ok, bool arith) 152 | { 153 | struct tbl v; 154 | int ret; 155 | 156 | v.flag = DEFINED|INTEGER; 157 | v.type = 0; 158 | ret = v_evaluate(&v, expr, error_ok, arith); 159 | *rval = v.val.i; 160 | return ret; 161 | } 162 | 163 | /* 164 | * parse and evaluate expression, storing result in vp. 165 | */ 166 | int 167 | v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok, 168 | bool arith) 169 | { 170 | struct tbl *v; 171 | Expr_state curstate; 172 | Expr_state * const es = &curstate; 173 | int save_disable_subst; 174 | int i; 175 | 176 | /* save state to allow recursive calls */ 177 | curstate.expression = curstate.tokp = expr; 178 | curstate.noassign = 0; 179 | curstate.arith = arith; 180 | curstate.evaling = NULL; 181 | curstate.val = NULL; 182 | 183 | newenv(E_ERRH); 184 | save_disable_subst = disable_subst; 185 | i = sigsetjmp(genv->jbuf, 0); 186 | if (i) { 187 | disable_subst = save_disable_subst; 188 | /* Clear EXPRINEVAL in of any variables we were playing with */ 189 | if (curstate.evaling) 190 | curstate.evaling->flag &= ~EXPRINEVAL; 191 | quitenv(NULL); 192 | if (i == LAEXPR) { 193 | if (error_ok == KSH_RETURN_ERROR) 194 | return 0; 195 | errorf(NULL); 196 | } 197 | unwind(i); 198 | /* NOTREACHED */ 199 | } 200 | 201 | token(es); 202 | #if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */ 203 | if (es->tok == END) { 204 | es->tok = LIT; 205 | es->val = tempvar(); 206 | } 207 | #endif /* 0 */ 208 | v = intvar(es, evalexpr(es, MAX_PREC)); 209 | 210 | if (es->tok != END) 211 | evalerr(es, ET_UNEXPECTED, NULL); 212 | 213 | if (vp->flag & INTEGER) 214 | setint_v(vp, v, es->arith); 215 | else 216 | /* can fail if readonly */ 217 | setstr(vp, str_val(v), error_ok); 218 | 219 | quitenv(NULL); 220 | 221 | return 1; 222 | } 223 | 224 | static void 225 | evalerr(Expr_state *es, enum error_type type, const char *str) 226 | { 227 | char tbuf[2]; 228 | const char *s; 229 | 230 | es->arith = false; 231 | switch (type) { 232 | case ET_UNEXPECTED: 233 | switch (es->tok) { 234 | case VAR: 235 | s = es->val->name; 236 | break; 237 | case LIT: 238 | s = str_val(es->val); 239 | break; 240 | case END: 241 | s = "end of expression"; 242 | break; 243 | case BAD: 244 | tbuf[0] = *es->tokp; 245 | tbuf[1] = '\0'; 246 | s = tbuf; 247 | break; 248 | default: 249 | s = opinfo[(int)es->tok].name; 250 | } 251 | warningf(true, "%s: unexpected `%s'", es->expression, s); 252 | break; 253 | 254 | case ET_BADLIT: 255 | warningf(true, "%s: bad number `%s'", es->expression, str); 256 | break; 257 | 258 | case ET_RECURSIVE: 259 | warningf(true, "%s: expression recurses on parameter `%s'", 260 | es->expression, str); 261 | break; 262 | 263 | case ET_LVALUE: 264 | warningf(true, "%s: %s requires lvalue", 265 | es->expression, str); 266 | break; 267 | 268 | case ET_RDONLY: 269 | warningf(true, "%s: %s applied to read only variable", 270 | es->expression, str); 271 | break; 272 | 273 | default: /* keep gcc happy */ 274 | case ET_STR: 275 | warningf(true, "%s: %s", es->expression, str); 276 | break; 277 | } 278 | unwind(LAEXPR); 279 | } 280 | 281 | static struct tbl * 282 | evalexpr(Expr_state *es, enum prec prec) 283 | { 284 | struct tbl *vl, *vr = NULL, *vasn; 285 | enum token op; 286 | int64_t res = 0; 287 | 288 | if (prec == P_PRIMARY) { 289 | op = es->tok; 290 | if (op == O_BNOT || op == O_LNOT || op == O_MINUS || 291 | op == O_PLUS) { 292 | token(es); 293 | vl = intvar(es, evalexpr(es, P_PRIMARY)); 294 | if (op == O_BNOT) 295 | vl->val.i = ~vl->val.i; 296 | else if (op == O_LNOT) 297 | vl->val.i = !vl->val.i; 298 | else if (op == O_MINUS) 299 | vl->val.i = -vl->val.i; 300 | /* op == O_PLUS is a no-op */ 301 | } else if (op == OPEN_PAREN) { 302 | token(es); 303 | vl = evalexpr(es, MAX_PREC); 304 | if (es->tok != CLOSE_PAREN) 305 | evalerr(es, ET_STR, "missing )"); 306 | token(es); 307 | } else if (op == O_PLUSPLUS || op == O_MINUSMINUS) { 308 | token(es); 309 | vl = do_ppmm(es, op, es->val, true); 310 | token(es); 311 | } else if (op == VAR || op == LIT) { 312 | vl = es->val; 313 | token(es); 314 | } else { 315 | evalerr(es, ET_UNEXPECTED, NULL); 316 | /* NOTREACHED */ 317 | } 318 | if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) { 319 | vl = do_ppmm(es, es->tok, vl, false); 320 | token(es); 321 | } 322 | return vl; 323 | } 324 | vl = evalexpr(es, ((int) prec) - 1); 325 | for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec; 326 | op = es->tok) { 327 | token(es); 328 | vasn = vl; 329 | if (op != O_ASN) /* vl may not have a value yet */ 330 | vl = intvar(es, vl); 331 | if (IS_ASSIGNOP(op)) { 332 | assign_check(es, op, vasn); 333 | vr = intvar(es, evalexpr(es, P_ASSIGN)); 334 | } else if (op != O_TERN && op != O_LAND && op != O_LOR) 335 | vr = intvar(es, evalexpr(es, ((int) prec) - 1)); 336 | if ((op == O_DIV || op == O_MOD || op == O_DIVASN || 337 | op == O_MODASN) && vr->val.i == 0) { 338 | if (es->noassign) 339 | vr->val.i = 1; 340 | else 341 | evalerr(es, ET_STR, "zero divisor"); 342 | } 343 | switch ((int) op) { 344 | case O_TIMES: 345 | case O_TIMESASN: 346 | res = vl->val.i * vr->val.i; 347 | break; 348 | case O_DIV: 349 | case O_DIVASN: 350 | if (vl->val.i == LONG_MIN && vr->val.i == -1) 351 | res = LONG_MIN; 352 | else 353 | res = vl->val.i / vr->val.i; 354 | break; 355 | case O_MOD: 356 | case O_MODASN: 357 | if (vl->val.i == LONG_MIN && vr->val.i == -1) 358 | res = 0; 359 | else 360 | res = vl->val.i % vr->val.i; 361 | break; 362 | case O_PLUS: 363 | case O_PLUSASN: 364 | res = vl->val.i + vr->val.i; 365 | break; 366 | case O_MINUS: 367 | case O_MINUSASN: 368 | res = vl->val.i - vr->val.i; 369 | break; 370 | case O_LSHIFT: 371 | case O_LSHIFTASN: 372 | res = vl->val.i << vr->val.i; 373 | break; 374 | case O_RSHIFT: 375 | case O_RSHIFTASN: 376 | res = vl->val.i >> vr->val.i; 377 | break; 378 | case O_LT: 379 | res = vl->val.i < vr->val.i; 380 | break; 381 | case O_LE: 382 | res = vl->val.i <= vr->val.i; 383 | break; 384 | case O_GT: 385 | res = vl->val.i > vr->val.i; 386 | break; 387 | case O_GE: 388 | res = vl->val.i >= vr->val.i; 389 | break; 390 | case O_EQ: 391 | res = vl->val.i == vr->val.i; 392 | break; 393 | case O_NE: 394 | res = vl->val.i != vr->val.i; 395 | break; 396 | case O_BAND: 397 | case O_BANDASN: 398 | res = vl->val.i & vr->val.i; 399 | break; 400 | case O_BXOR: 401 | case O_BXORASN: 402 | res = vl->val.i ^ vr->val.i; 403 | break; 404 | case O_BOR: 405 | case O_BORASN: 406 | res = vl->val.i | vr->val.i; 407 | break; 408 | case O_LAND: 409 | if (!vl->val.i) 410 | es->noassign++; 411 | vr = intvar(es, evalexpr(es, ((int) prec) - 1)); 412 | res = vl->val.i && vr->val.i; 413 | if (!vl->val.i) 414 | es->noassign--; 415 | break; 416 | case O_LOR: 417 | if (vl->val.i) 418 | es->noassign++; 419 | vr = intvar(es, evalexpr(es, ((int) prec) - 1)); 420 | res = vl->val.i || vr->val.i; 421 | if (vl->val.i) 422 | es->noassign--; 423 | break; 424 | case O_TERN: 425 | { 426 | int e = vl->val.i != 0; 427 | 428 | if (!e) 429 | es->noassign++; 430 | vl = evalexpr(es, MAX_PREC); 431 | if (!e) 432 | es->noassign--; 433 | if (es->tok != CTERN) 434 | evalerr(es, ET_STR, "missing :"); 435 | token(es); 436 | if (e) 437 | es->noassign++; 438 | vr = evalexpr(es, P_TERN); 439 | if (e) 440 | es->noassign--; 441 | vl = e ? vl : vr; 442 | } 443 | break; 444 | case O_ASN: 445 | res = vr->val.i; 446 | break; 447 | case O_COMMA: 448 | res = vr->val.i; 449 | break; 450 | } 451 | if (IS_ASSIGNOP(op)) { 452 | vr->val.i = res; 453 | if (vasn->flag & INTEGER) 454 | setint_v(vasn, vr, es->arith); 455 | else 456 | setint(vasn, res); 457 | vl = vr; 458 | } else if (op != O_TERN) 459 | vl->val.i = res; 460 | } 461 | return vl; 462 | } 463 | 464 | static void 465 | token(Expr_state *es) 466 | { 467 | const char *cp; 468 | int c; 469 | char *tvar; 470 | 471 | /* skip white space */ 472 | for (cp = es->tokp; (c = *cp), isspace((unsigned char)c); cp++) 473 | ; 474 | es->tokp = cp; 475 | 476 | if (c == '\0') 477 | es->tok = END; 478 | else if (letter(c)) { 479 | for (; letnum(c); c = *cp) 480 | cp++; 481 | if (c == '[') { 482 | int len; 483 | 484 | len = array_ref_len(cp); 485 | if (len == 0) 486 | evalerr(es, ET_STR, "missing ]"); 487 | cp += len; 488 | } else if (c == '(' /*)*/ ) { 489 | /* todo: add math functions (all take single argument): 490 | * abs acos asin atan cos cosh exp int log sin sinh sqrt 491 | * tan tanh 492 | */ 493 | ; 494 | } 495 | if (es->noassign) { 496 | es->val = tempvar(); 497 | es->val->flag |= EXPRLVALUE; 498 | } else { 499 | tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP); 500 | es->val = global(tvar); 501 | afree(tvar, ATEMP); 502 | } 503 | es->tok = VAR; 504 | } else if (digit(c)) { 505 | for (; c != '_' && (letnum(c) || c == '#'); c = *cp++) 506 | ; 507 | tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP); 508 | es->val = tempvar(); 509 | es->val->flag &= ~INTEGER; 510 | es->val->type = 0; 511 | es->val->val.s = tvar; 512 | if (setint_v(es->val, es->val, es->arith) == NULL) 513 | evalerr(es, ET_BADLIT, tvar); 514 | afree(tvar, ATEMP); 515 | es->tok = LIT; 516 | } else { 517 | int i, n0; 518 | 519 | for (i = 0; (n0 = opinfo[i].name[0]); i++) 520 | if (c == n0 && 521 | strncmp(cp, opinfo[i].name, opinfo[i].len) == 0) { 522 | es->tok = (enum token) i; 523 | cp += opinfo[i].len; 524 | break; 525 | } 526 | if (!n0) 527 | es->tok = BAD; 528 | } 529 | es->tokp = cp; 530 | } 531 | 532 | /* Do a ++ or -- operation */ 533 | static struct tbl * 534 | do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix) 535 | { 536 | struct tbl *vl; 537 | int oval; 538 | 539 | assign_check(es, op, vasn); 540 | 541 | vl = intvar(es, vasn); 542 | oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--; 543 | if (vasn->flag & INTEGER) 544 | setint_v(vasn, vl, es->arith); 545 | else 546 | setint(vasn, vl->val.i); 547 | if (!is_prefix) /* undo the inc/dec */ 548 | vl->val.i = oval; 549 | 550 | return vl; 551 | } 552 | 553 | static void 554 | assign_check(Expr_state *es, enum token op, struct tbl *vasn) 555 | { 556 | if (es->tok == END || vasn == NULL || 557 | (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE))) 558 | evalerr(es, ET_LVALUE, opinfo[(int) op].name); 559 | else if (vasn->flag & RDONLY) 560 | evalerr(es, ET_RDONLY, opinfo[(int) op].name); 561 | } 562 | 563 | static struct tbl * 564 | tempvar(void) 565 | { 566 | struct tbl *vp; 567 | 568 | vp = alloc(sizeof(struct tbl), ATEMP); 569 | vp->flag = ISSET|INTEGER; 570 | vp->type = 0; 571 | vp->areap = ATEMP; 572 | vp->val.i = 0; 573 | vp->name[0] = '\0'; 574 | return vp; 575 | } 576 | 577 | /* cast (string) variable to temporary integer variable */ 578 | static struct tbl * 579 | intvar(Expr_state *es, struct tbl *vp) 580 | { 581 | struct tbl *vq; 582 | 583 | /* try to avoid replacing a temp var with another temp var */ 584 | if (vp->name[0] == '\0' && 585 | (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER)) 586 | return vp; 587 | 588 | vq = tempvar(); 589 | if (setint_v(vq, vp, es->arith) == NULL) { 590 | if (vp->flag & EXPRINEVAL) 591 | evalerr(es, ET_RECURSIVE, vp->name); 592 | es->evaling = vp; 593 | vp->flag |= EXPRINEVAL; 594 | disable_subst++; 595 | v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR, es->arith); 596 | disable_subst--; 597 | vp->flag &= ~EXPRINEVAL; 598 | es->evaling = NULL; 599 | } 600 | return vq; 601 | } 602 | -------------------------------------------------------------------------------- /io.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: io.c,v 1.38 2019/07/24 14:33:16 bcallah Exp $ */ 2 | 3 | /* 4 | * shell buffered IO and formatted output 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "sh.h" 17 | 18 | static int initio_done; 19 | 20 | /* 21 | * formatted output functions 22 | */ 23 | 24 | 25 | /* A shell error occurred (eg, syntax error, etc.) */ 26 | void 27 | errorf(const char *fmt, ...) 28 | { 29 | va_list va; 30 | 31 | shl_stdout_ok = 0; /* debugging: note that stdout not valid */ 32 | exstat = 1; 33 | if (fmt != NULL && *fmt != '\0') { 34 | error_prefix(true); 35 | va_start(va, fmt); 36 | shf_vfprintf(shl_out, fmt, va); 37 | va_end(va); 38 | shf_putchar('\n', shl_out); 39 | } 40 | shf_flush(shl_out); 41 | unwind(LERROR); 42 | } 43 | 44 | /* like errorf(), but no unwind is done */ 45 | void 46 | warningf(bool show_lineno, const char *fmt, ...) 47 | { 48 | va_list va; 49 | 50 | error_prefix(show_lineno); 51 | va_start(va, fmt); 52 | shf_vfprintf(shl_out, fmt, va); 53 | va_end(va); 54 | shf_putchar('\n', shl_out); 55 | shf_flush(shl_out); 56 | } 57 | 58 | /* Used by built-in utilities to prefix shell and utility name to message 59 | * (also unwinds environments for special builtins). 60 | */ 61 | void 62 | bi_errorf(const char *fmt, ...) 63 | { 64 | va_list va; 65 | 66 | shl_stdout_ok = 0; /* debugging: note that stdout not valid */ 67 | exstat = 1; 68 | if (fmt != NULL && *fmt != '\0') { 69 | error_prefix(true); 70 | /* not set when main() calls parse_args() */ 71 | if (builtin_argv0) 72 | shf_fprintf(shl_out, "%s: ", builtin_argv0); 73 | va_start(va, fmt); 74 | shf_vfprintf(shl_out, fmt, va); 75 | va_end(va); 76 | shf_putchar('\n', shl_out); 77 | } 78 | shf_flush(shl_out); 79 | /* POSIX special builtins and ksh special builtins cause 80 | * non-interactive shells to exit. 81 | * XXX odd use of KEEPASN; also may not want LERROR here 82 | */ 83 | if ((builtin_flag & SPEC_BI) || 84 | (Flag(FPOSIX) && (builtin_flag & KEEPASN))) { 85 | builtin_argv0 = NULL; 86 | unwind(LERROR); 87 | } 88 | } 89 | 90 | static void 91 | internal_error_vwarn(const char *fmt, va_list va) 92 | { 93 | error_prefix(true); 94 | shf_fprintf(shl_out, "internal error: "); 95 | shf_vfprintf(shl_out, fmt, va); 96 | shf_putchar('\n', shl_out); 97 | shf_flush(shl_out); 98 | } 99 | 100 | /* Warn when something that shouldn't happen does */ 101 | void 102 | internal_warningf(const char *fmt, ...) 103 | { 104 | va_list va; 105 | 106 | va_start(va, fmt); 107 | internal_error_vwarn(fmt, va); 108 | va_end(va); 109 | } 110 | 111 | /* Warn and unwind when something that shouldn't happen does */ 112 | __dead void 113 | internal_errorf(const char *fmt, ...) 114 | { 115 | va_list va; 116 | 117 | va_start(va, fmt); 118 | internal_error_vwarn(fmt, va); 119 | va_end(va); 120 | unwind(LERROR); 121 | } 122 | 123 | /* used by error reporting functions to print "ksh: .kshrc[25]: " */ 124 | void 125 | error_prefix(int fileline) 126 | { 127 | /* Avoid foo: foo[2]: ... */ 128 | if (!fileline || !source || !source->file || 129 | strcmp(source->file, kshname) != 0) 130 | shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-')); 131 | if (fileline && source && source->file != NULL) { 132 | shf_fprintf(shl_out, "%s[%d]: ", source->file, 133 | source->errline > 0 ? source->errline : source->line); 134 | source->errline = 0; 135 | } 136 | } 137 | 138 | /* printf to shl_out (stderr) with flush */ 139 | void 140 | shellf(const char *fmt, ...) 141 | { 142 | va_list va; 143 | 144 | if (!initio_done) /* shl_out may not be set up yet... */ 145 | return; 146 | va_start(va, fmt); 147 | shf_vfprintf(shl_out, fmt, va); 148 | va_end(va); 149 | shf_flush(shl_out); 150 | } 151 | 152 | /* printf to shl_stdout (stdout) */ 153 | void 154 | shprintf(const char *fmt, ...) 155 | { 156 | va_list va; 157 | 158 | if (!shl_stdout_ok) 159 | internal_errorf("shl_stdout not valid"); 160 | va_start(va, fmt); 161 | shf_vfprintf(shl_stdout, fmt, va); 162 | va_end(va); 163 | } 164 | 165 | #ifdef KSH_DEBUG 166 | static struct shf *kshdebug_shf; 167 | 168 | void 169 | kshdebug_init_(void) 170 | { 171 | if (kshdebug_shf) 172 | shf_close(kshdebug_shf); 173 | kshdebug_shf = shf_open("/tmp/ksh-debug.log", 174 | O_WRONLY|O_APPEND|O_CREAT, 0600, SHF_WR|SHF_MAPHI); 175 | if (kshdebug_shf) { 176 | shf_fprintf(kshdebug_shf, "\nNew shell[pid %d]\n", getpid()); 177 | shf_flush(kshdebug_shf); 178 | } 179 | } 180 | 181 | /* print to debugging log */ 182 | void 183 | kshdebug_printf_(const char *fmt, ...) 184 | { 185 | va_list va; 186 | 187 | if (!kshdebug_shf) 188 | return; 189 | va_start(va, fmt); 190 | shf_fprintf(kshdebug_shf, "[%d] ", getpid()); 191 | shf_vfprintf(kshdebug_shf, fmt, va); 192 | va_end(va); 193 | shf_flush(kshdebug_shf); 194 | } 195 | 196 | void 197 | kshdebug_dump_(const char *str, const void *mem, int nbytes) 198 | { 199 | int i, j; 200 | int nprow = 16; 201 | 202 | if (!kshdebug_shf) 203 | return; 204 | shf_fprintf(kshdebug_shf, "[%d] %s:\n", getpid(), str); 205 | for (i = 0; i < nbytes; i += nprow) { 206 | char c = '\t'; 207 | 208 | for (j = 0; j < nprow && i + j < nbytes; j++) { 209 | shf_fprintf(kshdebug_shf, "%c%02x", c, 210 | ((const unsigned char *) mem)[i + j]); 211 | c = ' '; 212 | } 213 | shf_fprintf(kshdebug_shf, "\n"); 214 | } 215 | shf_flush(kshdebug_shf); 216 | } 217 | #endif /* KSH_DEBUG */ 218 | 219 | /* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */ 220 | int 221 | can_seek(int fd) 222 | { 223 | struct stat statb; 224 | 225 | return fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ? 226 | SHF_UNBUF : 0; 227 | } 228 | 229 | struct shf shf_iob[3]; 230 | 231 | void 232 | initio(void) 233 | { 234 | shf_fdopen(1, SHF_WR, shl_stdout); /* force buffer allocation */ 235 | shf_fdopen(2, SHF_WR, shl_out); 236 | shf_fdopen(2, SHF_WR, shl_spare); /* force buffer allocation */ 237 | initio_done = 1; 238 | kshdebug_init(); 239 | } 240 | 241 | /* A dup2() with error checking */ 242 | int 243 | ksh_dup2(int ofd, int nfd, int errok) 244 | { 245 | int ret = dup2(ofd, nfd); 246 | 247 | if (ret == -1 && errno != EBADF && !errok) 248 | errorf("too many files open in shell"); 249 | 250 | return ret; 251 | } 252 | 253 | /* 254 | * move fd from user space (0<=fd<10) to shell space (fd>=10), 255 | * set close-on-exec flag. 256 | */ 257 | int 258 | savefd(int fd) 259 | { 260 | int nfd; 261 | 262 | if (fd < FDBASE) { 263 | #ifdef F_DUPFD_CLOEXEC 264 | nfd = fcntl(fd, F_DUPFD_CLOEXEC, FDBASE); 265 | if (nfd == -1) { 266 | if (errno == EBADF) 267 | return -1; 268 | else 269 | errorf("too many files open in shell"); 270 | } 271 | #else 272 | nfd = fcntl(fd, F_DUPFD, FDBASE); 273 | if (nfd < 0) { 274 | if (errno == EBADF) 275 | return -1; 276 | else 277 | errorf("too many files open in shell"); 278 | } 279 | fcntl(nfd, F_SETFD, FD_CLOEXEC); 280 | #endif 281 | } else { 282 | nfd = fd; 283 | fcntl(nfd, F_SETFD, FD_CLOEXEC); 284 | } 285 | return nfd; 286 | } 287 | 288 | void 289 | restfd(int fd, int ofd) 290 | { 291 | if (fd == 2) 292 | shf_flush(&shf_iob[fd]); 293 | if (ofd < 0) /* original fd closed */ 294 | close(fd); 295 | else if (fd != ofd) { 296 | ksh_dup2(ofd, fd, true); /* XXX: what to do if this fails? */ 297 | close(ofd); 298 | } 299 | } 300 | 301 | void 302 | openpipe(int *pv) 303 | { 304 | int lpv[2]; 305 | 306 | if (pipe(lpv) == -1) 307 | errorf("can't create pipe - try again"); 308 | pv[0] = savefd(lpv[0]); 309 | if (pv[0] != lpv[0]) 310 | close(lpv[0]); 311 | pv[1] = savefd(lpv[1]); 312 | if (pv[1] != lpv[1]) 313 | close(lpv[1]); 314 | } 315 | 316 | void 317 | closepipe(int *pv) 318 | { 319 | close(pv[0]); 320 | close(pv[1]); 321 | } 322 | 323 | /* Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn 324 | * a string (the X in 2>&X, read -uX, print -uX) into a file descriptor. 325 | */ 326 | int 327 | check_fd(char *name, int mode, const char **emsgp) 328 | { 329 | int fd, fl; 330 | 331 | if (isdigit((unsigned char)name[0]) && !name[1]) { 332 | fd = name[0] - '0'; 333 | if ((fl = fcntl(fd, F_GETFL)) == -1) { 334 | if (emsgp) 335 | *emsgp = "bad file descriptor"; 336 | return -1; 337 | } 338 | fl &= O_ACCMODE; 339 | /* X_OK is a kludge to disable this check for dups (x<&1): 340 | * historical shells never did this check (XXX don't know what 341 | * posix has to say). 342 | */ 343 | if (!(mode & X_OK) && fl != O_RDWR && 344 | (((mode & R_OK) && fl != O_RDONLY) || 345 | ((mode & W_OK) && fl != O_WRONLY))) { 346 | if (emsgp) 347 | *emsgp = (fl == O_WRONLY) ? 348 | "fd not open for reading" : 349 | "fd not open for writing"; 350 | return -1; 351 | } 352 | return fd; 353 | } else if (name[0] == 'p' && !name[1]) 354 | return coproc_getfd(mode, emsgp); 355 | if (emsgp) 356 | *emsgp = "illegal file descriptor name"; 357 | return -1; 358 | } 359 | 360 | /* Called once from main */ 361 | void 362 | coproc_init(void) 363 | { 364 | coproc.read = coproc.readw = coproc.write = -1; 365 | coproc.njobs = 0; 366 | coproc.id = 0; 367 | } 368 | 369 | /* Called by c_read() when eof is read - close fd if it is the co-process fd */ 370 | void 371 | coproc_read_close(int fd) 372 | { 373 | if (coproc.read >= 0 && fd == coproc.read) { 374 | coproc_readw_close(fd); 375 | close(coproc.read); 376 | coproc.read = -1; 377 | } 378 | } 379 | 380 | /* Called by c_read() and by iosetup() to close the other side of the 381 | * read pipe, so reads will actually terminate. 382 | */ 383 | void 384 | coproc_readw_close(int fd) 385 | { 386 | if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) { 387 | close(coproc.readw); 388 | coproc.readw = -1; 389 | } 390 | } 391 | 392 | /* Called by c_print when a write to a fd fails with EPIPE and by iosetup 393 | * when co-process input is dup'd 394 | */ 395 | void 396 | coproc_write_close(int fd) 397 | { 398 | if (coproc.write >= 0 && fd == coproc.write) { 399 | close(coproc.write); 400 | coproc.write = -1; 401 | } 402 | } 403 | 404 | /* Called to check for existence of/value of the co-process file descriptor. 405 | * (Used by check_fd() and by c_read/c_print to deal with -p option). 406 | */ 407 | int 408 | coproc_getfd(int mode, const char **emsgp) 409 | { 410 | int fd = (mode & R_OK) ? coproc.read : coproc.write; 411 | 412 | if (fd >= 0) 413 | return fd; 414 | if (emsgp) 415 | *emsgp = "no coprocess"; 416 | return -1; 417 | } 418 | 419 | /* called to close file descriptors related to the coprocess (if any) 420 | * Should be called with SIGCHLD blocked. 421 | */ 422 | void 423 | coproc_cleanup(int reuse) 424 | { 425 | /* This to allow co-processes to share output pipe */ 426 | if (!reuse || coproc.readw < 0 || coproc.read < 0) { 427 | if (coproc.read >= 0) { 428 | close(coproc.read); 429 | coproc.read = -1; 430 | } 431 | if (coproc.readw >= 0) { 432 | close(coproc.readw); 433 | coproc.readw = -1; 434 | } 435 | } 436 | if (coproc.write >= 0) { 437 | close(coproc.write); 438 | coproc.write = -1; 439 | } 440 | } 441 | 442 | 443 | /* 444 | * temporary files 445 | */ 446 | 447 | struct temp * 448 | maketemp(Area *ap, Temp_type type, struct temp **tlist) 449 | { 450 | struct temp *tp; 451 | int len; 452 | int fd; 453 | char *path; 454 | const char *dir; 455 | 456 | dir = tmpdir ? tmpdir : "/tmp"; 457 | /* The 20 + 20 is a paranoid worst case for pid/inc */ 458 | len = strlen(dir) + 3 + 20 + 20 + 1; 459 | tp = alloc(sizeof(struct temp) + len, ap); 460 | tp->name = path = (char *) &tp[1]; 461 | tp->shf = NULL; 462 | tp->type = type; 463 | shf_snprintf(path, len, "%s/shXXXXXXXX", dir); 464 | fd = mkstemp(path); 465 | if (fd >= 0) 466 | tp->shf = shf_fdopen(fd, SHF_WR, NULL); 467 | tp->pid = procpid; 468 | 469 | tp->next = *tlist; 470 | *tlist = tp; 471 | return tp; 472 | } 473 | -------------------------------------------------------------------------------- /issetugid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Brian Callahan 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "pconfig.h" 18 | 19 | #if defined(HAVE_ISSETUGID) 20 | #include 21 | #elif defined(HAVE_GETAUXVAL) 22 | #include 23 | #endif 24 | 25 | int 26 | oksh_issetugid(void) 27 | { 28 | 29 | #if defined(HAVE_ISSETUGID) 30 | return issetugid(); 31 | #elif defined(HAVE_GETAUXVAL) 32 | return (int) getauxval(AT_SECURE); 33 | #else 34 | return 0; 35 | #endif 36 | } 37 | -------------------------------------------------------------------------------- /ksh.kshrc: -------------------------------------------------------------------------------- 1 | # $OpenBSD: ksh.kshrc,v 1.32 2018/05/16 14:01:41 mpf Exp $ 2 | # 3 | # NAME: 4 | # ksh.kshrc - global initialization for ksh 5 | # 6 | # DESCRIPTION: 7 | # Each invocation of /bin/ksh processes the file pointed 8 | # to by $ENV (usually $HOME/.kshrc). 9 | # This file is intended as a global .kshrc file for the 10 | # Korn shell. A user's $HOME/.kshrc file simply requires 11 | # the line: 12 | # . /etc/ksh.kshrc 13 | # at or near the start to pick up the defaults in this 14 | # file which can then be overridden as desired. 15 | # 16 | # SEE ALSO: 17 | # $HOME/.kshrc 18 | # 19 | 20 | # RCSid: 21 | # $From: ksh.kshrc,v 1.4 1992/12/05 13:14:48 sjg Exp $ 22 | # 23 | # @(#)Copyright (c) 1991 Simon J. Gerraty 24 | # 25 | # This file is provided in the hope that it will 26 | # be of use. There is absolutely NO WARRANTY. 27 | # Permission to copy, redistribute or otherwise 28 | # use this file is hereby granted provided that 29 | # the above copyright notice and this notice are 30 | # left intact. 31 | 32 | case "$-" in 33 | *i*) # we are interactive 34 | # we may have su'ed so reset these 35 | USER=$(id -un) 36 | UID=$(id -u) 37 | case $UID in 38 | 0) PS1S='# ';; 39 | esac 40 | PS1S=${PS1S:-'$ '} 41 | HOSTNAME=${HOSTNAME:-$(uname -n)} 42 | HOST=${HOSTNAME%%.*} 43 | 44 | PROMPT="$USER:!$PS1S" 45 | #PROMPT="<$USER@$HOST:!>$PS1S" 46 | PPROMPT='$USER:$PWD:!'"$PS1S" 47 | #PPROMPT='<$USER@$HOST:$PWD:!>'"$PS1S" 48 | PS1=$PPROMPT 49 | # $TTY is the tty we logged in on, 50 | # $tty is that which we are in now (might by pty) 51 | tty=$(tty) 52 | tty=${tty##*/} 53 | TTY=${TTY:-$tty} 54 | # $console is the system console device 55 | console=$(sysctl kern.consdev) 56 | console=${console#*=} 57 | 58 | set -o emacs 59 | 60 | alias ls='ls -F' 61 | alias h='fc -l | more' 62 | 63 | case "$TERM" in 64 | sun*-s) 65 | # sun console with status line 66 | if [[ $tty != $console ]]; then 67 | # ilabel 68 | ILS='\033]L'; ILE='\033\\' 69 | # window title bar 70 | WLS='\033]l'; WLE='\033\\' 71 | fi 72 | ;; 73 | xterm*) 74 | ILS='\033]1;'; ILE='\007' 75 | WLS='\033]2;'; WLE='\007' 76 | pgrep -qxs $PPID telnet && export TERM=xterms 77 | ;; 78 | *) ;; 79 | esac 80 | # do we want window decorations? 81 | if [[ -n $ILS ]]; then 82 | function ilabel { print -n "${ILS}$*${ILE}">/dev/tty; } 83 | function label { print -n "${WLS}$*${WLE}">/dev/tty; } 84 | 85 | alias stripe='label "$USER@$HOST ($tty) - $PWD"' 86 | alias istripe='ilabel "$USER@$HOST ($tty)"' 87 | 88 | # Run stuff through this to preserve the exit code 89 | function _ignore { local rc=$?; "$@"; return $rc; } 90 | 91 | function wftp { ilabel "ftp $*"; "ftp" "$@"; _ignore eval istripe; } 92 | 93 | function wcd { \cd "$@"; _ignore eval stripe; } 94 | 95 | function wssh { \ssh "$@"; _ignore eval 'istripe; stripe'; } 96 | function wtelnet { \telnet "$@"; _ignore eval 'istripe; stripe'; } 97 | function wsu { \su "$@"; _ignore eval 'istripe; stripe'; } 98 | 99 | alias su=wsu 100 | alias cd=wcd 101 | alias ftp=wftp 102 | alias ssh=wssh 103 | alias telnet=wtelnet 104 | eval stripe 105 | eval istripe 106 | PS1=$PROMPT 107 | fi 108 | alias quit=exit 109 | alias cls=clear 110 | alias logout=exit 111 | alias bye=exit 112 | alias p='ps -l' 113 | alias j=jobs 114 | alias o='fg %-' 115 | alias df='df -k' 116 | alias du='du -k' 117 | alias rsize='eval $(resize)' 118 | ;; 119 | *) # non-interactive 120 | ;; 121 | esac 122 | -------------------------------------------------------------------------------- /lex.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: lex.h,v 1.21 2018/01/15 14:58:05 jca Exp $ */ 2 | 3 | /* 4 | * Source input, lexer and parser 5 | */ 6 | 7 | /* $From: lex.h,v 1.4 1994/05/31 13:34:34 michael Exp $ */ 8 | 9 | #define IDENT 64 10 | 11 | typedef struct source Source; 12 | struct source { 13 | const char *str; /* input pointer */ 14 | int type; /* input type */ 15 | const char *start; /* start of current buffer */ 16 | union { 17 | char **strv; /* string [] */ 18 | struct shf *shf; /* shell file */ 19 | struct tbl *tblp; /* alias (SALIAS) */ 20 | char *freeme; /* also for SREREAD */ 21 | } u; 22 | char ugbuf[2]; /* buffer for ungetsc() (SREREAD) and 23 | * alias (SALIAS) */ 24 | int line; /* line number */ 25 | int cmd_offset; /* line number - command number */ 26 | int errline; /* line the error occurred on (0 if not set) */ 27 | const char *file; /* input file name */ 28 | int flags; /* SF_* */ 29 | Area *areap; 30 | XString xs; /* input buffer */ 31 | Source *next; /* stacked source */ 32 | }; 33 | 34 | /* Source.type values */ 35 | #define SEOF 0 /* input EOF */ 36 | #define SFILE 1 /* file input */ 37 | #define SSTDIN 2 /* read stdin */ 38 | #define SSTRING 3 /* string */ 39 | #define SWSTR 4 /* string without \n */ 40 | #define SWORDS 5 /* string[] */ 41 | #define SWORDSEP 6 /* string[] separator */ 42 | #define SALIAS 7 /* alias expansion */ 43 | #define SREREAD 8 /* read ahead to be re-scanned */ 44 | 45 | /* Source.flags values */ 46 | #define SF_ECHO BIT(0) /* echo input to shlout */ 47 | #define SF_ALIAS BIT(1) /* faking space at end of alias */ 48 | #define SF_ALIASEND BIT(2) /* faking space at end of alias */ 49 | #define SF_TTY BIT(3) /* type == SSTDIN & it is a tty */ 50 | 51 | typedef union { 52 | int i; 53 | char *cp; 54 | char **wp; 55 | struct op *o; 56 | struct ioword *iop; 57 | } YYSTYPE; 58 | 59 | /* If something is added here, add it to tokentab[] in syn.c as well */ 60 | #define LWORD 256 61 | #define LOGAND 257 /* && */ 62 | #define LOGOR 258 /* || */ 63 | #define BREAK 259 /* ;; */ 64 | #define IF 260 65 | #define THEN 261 66 | #define ELSE 262 67 | #define ELIF 263 68 | #define FI 264 69 | #define CASE 265 70 | #define ESAC 266 71 | #define FOR 267 72 | #define SELECT 268 73 | #define WHILE 269 74 | #define UNTIL 270 75 | #define DO 271 76 | #define DONE 272 77 | #define IN 273 78 | #define FUNCTION 274 79 | #define TIME 275 80 | #define REDIR 276 81 | #define MDPAREN 277 /* (( )) */ 82 | #define BANG 278 /* ! */ 83 | #define DBRACKET 279 /* [[ .. ]] */ 84 | #define COPROC 280 /* |& */ 85 | #define YYERRCODE 300 86 | 87 | /* flags to yylex */ 88 | #define CONTIN BIT(0) /* skip new lines to complete command */ 89 | #define ONEWORD BIT(1) /* single word for substitute() */ 90 | #define ALIAS BIT(2) /* recognize alias */ 91 | #define KEYWORD BIT(3) /* recognize keywords */ 92 | #define LETEXPR BIT(4) /* get expression inside (( )) */ 93 | #define VARASN BIT(5) /* check for var=word */ 94 | #define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */ 95 | #define ESACONLY BIT(7) /* only accept esac keyword */ 96 | #define CMDWORD BIT(8) /* parsing simple command (alias related) */ 97 | #define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */ 98 | #define HEREDOC BIT(10) /* parsing heredoc */ 99 | #define UNESCAPE BIT(11) /* remove backslashes */ 100 | 101 | #define HERES 10 /* max << in line */ 102 | 103 | extern Source *source; /* yyparse/yylex source */ 104 | extern YYSTYPE yylval; /* result from yylex */ 105 | extern struct ioword *heres[HERES], **herep; 106 | extern char ident[IDENT+1]; 107 | 108 | #define HISTORYSIZE 500 /* size of saved history */ 109 | 110 | extern char **history; /* saved commands */ 111 | extern char **histptr; /* last history item */ 112 | extern uint32_t histsize; /* history size */ 113 | 114 | int yylex(int); 115 | void yyerror(const char *, ...) 116 | __attribute__((__noreturn__, __format__ (printf, 1, 2))); 117 | Source * pushs(int, Area *); 118 | void set_prompt(int); 119 | void pprompt(const char *, int); 120 | -------------------------------------------------------------------------------- /mail.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: mail.c,v 1.27 2019/01/14 08:48:16 schwarze Exp $ */ 2 | 3 | /* 4 | * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by 5 | * John R. MacMillan 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "config.h" 15 | #include "sh.h" 16 | 17 | #define MBMESSAGE "you have mail in $_" 18 | 19 | typedef struct mbox { 20 | struct mbox *mb_next; /* next mbox in list */ 21 | char *mb_path; /* path to mail file */ 22 | char *mb_msg; /* to announce arrival of new mail */ 23 | time_t mb_mtime; /* mtime of mail file */ 24 | } mbox_t; 25 | 26 | /* 27 | * $MAILPATH is a linked list of mboxes. $MAIL is a treated as a 28 | * special case of $MAILPATH, where the list has only one node. The 29 | * same list is used for both since they are exclusive. 30 | */ 31 | 32 | static mbox_t *mplist; 33 | static mbox_t mbox; 34 | static struct timespec mlastchkd; /* when mail was last checked */ 35 | static time_t mailcheck_interval; 36 | 37 | static void munset(mbox_t *); /* free mlist and mval */ 38 | static mbox_t * mballoc(char *, char *); /* allocate a new mbox */ 39 | static void mprintit(mbox_t *); 40 | 41 | void 42 | mcheck(void) 43 | { 44 | mbox_t *mbp; 45 | struct timespec elapsed, now; 46 | struct tbl *vp; 47 | struct stat stbuf; 48 | static int first = 1; 49 | 50 | if (mplist) 51 | mbp = mplist; 52 | else if ((vp = global("MAIL")) && (vp->flag & ISSET)) 53 | mbp = &mbox; 54 | else 55 | mbp = NULL; 56 | if (mbp == NULL) 57 | return; 58 | 59 | clock_gettime(CLOCK_MONOTONIC, &now); 60 | if (first) { 61 | mlastchkd = now; 62 | first = 0; 63 | } 64 | timespecsub(&now, &mlastchkd, &elapsed); 65 | if (elapsed.tv_sec >= mailcheck_interval) { 66 | mlastchkd = now; 67 | 68 | while (mbp) { 69 | if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0 && 70 | S_ISREG(stbuf.st_mode)) { 71 | if (stbuf.st_size && 72 | mbp->mb_mtime != stbuf.st_mtime && 73 | stbuf.st_atime <= stbuf.st_mtime) 74 | mprintit(mbp); 75 | mbp->mb_mtime = stbuf.st_mtime; 76 | } else { 77 | /* 78 | * Some mail readers remove the mail 79 | * file if all mail is read. If file 80 | * does not exist, assume this is the 81 | * case and set mtime to zero. 82 | */ 83 | mbp->mb_mtime = 0; 84 | } 85 | mbp = mbp->mb_next; 86 | } 87 | } 88 | } 89 | 90 | void 91 | mcset(int64_t interval) 92 | { 93 | mailcheck_interval = interval; 94 | } 95 | 96 | void 97 | mbset(char *p) 98 | { 99 | struct stat stbuf; 100 | 101 | afree(mbox.mb_msg, APERM); 102 | afree(mbox.mb_path, APERM); 103 | /* Save a copy to protect from export (which munges the string) */ 104 | mbox.mb_path = str_save(p, APERM); 105 | mbox.mb_msg = NULL; 106 | if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode)) 107 | mbox.mb_mtime = stbuf.st_mtime; 108 | else 109 | mbox.mb_mtime = 0; 110 | } 111 | 112 | void 113 | mpset(char *mptoparse) 114 | { 115 | mbox_t *mbp; 116 | char *mpath, *mmsg, *mval; 117 | char *p; 118 | 119 | munset( mplist ); 120 | mplist = NULL; 121 | mval = str_save(mptoparse, APERM); 122 | while (mval) { 123 | mpath = mval; 124 | if ((mval = strchr(mval, ':')) != NULL) { 125 | *mval = '\0'; 126 | mval++; 127 | } 128 | /* POSIX/bourne-shell say file%message */ 129 | for (p = mpath; (mmsg = strchr(p, '%')); ) { 130 | /* a literal percent? (POSIXism) */ 131 | if (mmsg > mpath && mmsg[-1] == '\\') { 132 | /* use memmove() to avoid overlap problems */ 133 | memmove(mmsg - 1, mmsg, strlen(mmsg) + 1); 134 | p = mmsg; 135 | continue; 136 | } 137 | break; 138 | } 139 | /* at&t ksh says file?message */ 140 | if (!mmsg && !Flag(FPOSIX)) 141 | mmsg = strchr(mpath, '?'); 142 | if (mmsg) { 143 | *mmsg = '\0'; 144 | mmsg++; 145 | if (*mmsg == '\0') 146 | mmsg = NULL; 147 | } 148 | if (*mpath == '\0') 149 | continue; 150 | mbp = mballoc(mpath, mmsg); 151 | mbp->mb_next = mplist; 152 | mplist = mbp; 153 | } 154 | } 155 | 156 | static void 157 | munset(mbox_t *mlist) 158 | { 159 | mbox_t *mbp; 160 | 161 | while (mlist != NULL) { 162 | mbp = mlist; 163 | mlist = mbp->mb_next; 164 | if (!mlist) 165 | afree(mbp->mb_path, APERM); 166 | afree(mbp, APERM); 167 | } 168 | } 169 | 170 | static mbox_t * 171 | mballoc(char *p, char *m) 172 | { 173 | struct stat stbuf; 174 | mbox_t *mbp; 175 | 176 | mbp = alloc(sizeof(mbox_t), APERM); 177 | mbp->mb_next = NULL; 178 | mbp->mb_path = p; 179 | mbp->mb_msg = m; 180 | if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode)) 181 | mbp->mb_mtime = stbuf.st_mtime; 182 | else 183 | mbp->mb_mtime = 0; 184 | return(mbp); 185 | } 186 | 187 | static void 188 | mprintit(mbox_t *mbp) 189 | { 190 | struct tbl *vp; 191 | 192 | #if 0 193 | /* 194 | * I doubt this $_ overloading is bad in /bin/sh mode. Anyhow, we 195 | * crash as the code looks now if we do not set vp. Now, this is 196 | * easy to fix too, but I'd like to see what POSIX says before doing 197 | * a change like that. 198 | */ 199 | if (!Flag(FSH)) 200 | #endif 201 | /* Ignore setstr errors here (arbitrary) */ 202 | setstr((vp = local("_", false)), mbp->mb_path, KSH_RETURN_ERROR); 203 | 204 | shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0)); 205 | 206 | unset(vp, 0); 207 | } 208 | -------------------------------------------------------------------------------- /path.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: path.c,v 1.23 2019/06/28 13:34:59 deraadt Exp $ */ 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "sh.h" 10 | 11 | /* 12 | * Contains a routine to search a : separated list of 13 | * paths (a la CDPATH) and make appropriate file names. 14 | * Also contains a routine to simplify .'s and ..'s out of 15 | * a path name. 16 | * 17 | * Larry Bouzane (larry@cs.mun.ca) 18 | */ 19 | 20 | static char *do_phys_path(XString *, char *, const char *); 21 | 22 | /* 23 | * Makes a filename into result using the following algorithm. 24 | * - make result NULL 25 | * - if file starts with '/', append file to result & set cdpathp to NULL 26 | * - if file starts with ./ or ../ append cwd and file to result 27 | * and set cdpathp to NULL 28 | * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx 29 | * then cwd is appended to result. 30 | * - the first element of cdpathp is appended to result 31 | * - file is appended to result 32 | * - cdpathp is set to the start of the next element in cdpathp (or NULL 33 | * if there are no more elements. 34 | * The return value indicates whether a non-null element from cdpathp 35 | * was appended to result. 36 | */ 37 | int 38 | make_path(const char *cwd, const char *file, 39 | char **cdpathp, /* & of : separated list */ 40 | XString *xsp, 41 | int *phys_pathp) 42 | { 43 | int rval = 0; 44 | int use_cdpath = 1; 45 | char *plist; 46 | int len; 47 | int plen = 0; 48 | char *xp = Xstring(*xsp, xp); 49 | 50 | if (!file) 51 | file = null; 52 | 53 | if (file[0] == '/') { 54 | *phys_pathp = 0; 55 | use_cdpath = 0; 56 | } else { 57 | if (file[0] == '.') { 58 | char c = file[1]; 59 | 60 | if (c == '.') 61 | c = file[2]; 62 | if (c == '/' || c == '\0') 63 | use_cdpath = 0; 64 | } 65 | 66 | plist = *cdpathp; 67 | if (!plist) 68 | use_cdpath = 0; 69 | else if (use_cdpath) { 70 | char *pend; 71 | 72 | for (pend = plist; *pend && *pend != ':'; pend++) 73 | ; 74 | plen = pend - plist; 75 | *cdpathp = *pend ? ++pend : NULL; 76 | } 77 | 78 | if ((use_cdpath == 0 || !plen || plist[0] != '/') && 79 | (cwd && *cwd)) { 80 | len = strlen(cwd); 81 | XcheckN(*xsp, xp, len); 82 | memcpy(xp, cwd, len); 83 | xp += len; 84 | if (cwd[len - 1] != '/') 85 | Xput(*xsp, xp, '/'); 86 | } 87 | *phys_pathp = Xlength(*xsp, xp); 88 | if (use_cdpath && plen) { 89 | XcheckN(*xsp, xp, plen); 90 | memcpy(xp, plist, plen); 91 | xp += plen; 92 | if (plist[plen - 1] != '/') 93 | Xput(*xsp, xp, '/'); 94 | rval = 1; 95 | } 96 | } 97 | 98 | len = strlen(file) + 1; 99 | XcheckN(*xsp, xp, len); 100 | memcpy(xp, file, len); 101 | 102 | if (!use_cdpath) 103 | *cdpathp = NULL; 104 | 105 | return rval; 106 | } 107 | 108 | /* 109 | * Simplify pathnames containing "." and ".." entries. 110 | * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b" 111 | */ 112 | void 113 | simplify_path(char *path) 114 | { 115 | char *cur; 116 | char *t; 117 | int isrooted; 118 | char *very_start = path; 119 | char *start; 120 | 121 | if (!*path) 122 | return; 123 | 124 | if ((isrooted = (path[0] == '/'))) 125 | very_start++; 126 | 127 | /* Before After 128 | * /foo/ /foo 129 | * /foo/../../bar /bar 130 | * /foo/./blah/.. /foo 131 | * . . 132 | * .. .. 133 | * ./foo foo 134 | * foo/../../../bar ../../bar 135 | */ 136 | 137 | for (cur = t = start = very_start; ; ) { 138 | /* treat multiple '/'s as one '/' */ 139 | while (*t == '/') 140 | t++; 141 | 142 | if (*t == '\0') { 143 | if (cur == path) 144 | /* convert empty path to dot */ 145 | *cur++ = '.'; 146 | *cur = '\0'; 147 | break; 148 | } 149 | 150 | if (t[0] == '.') { 151 | if (!t[1] || t[1] == '/') { 152 | t += 1; 153 | continue; 154 | } else if (t[1] == '.' && (!t[2] || t[2] == '/')) { 155 | if (!isrooted && cur == start) { 156 | if (cur != very_start) 157 | *cur++ = '/'; 158 | *cur++ = '.'; 159 | *cur++ = '.'; 160 | start = cur; 161 | } else if (cur != start) 162 | while (--cur > start && *cur != '/') 163 | ; 164 | t += 2; 165 | continue; 166 | } 167 | } 168 | 169 | if (cur != very_start) 170 | *cur++ = '/'; 171 | 172 | /* find/copy next component of pathname */ 173 | while (*t && *t != '/') 174 | *cur++ = *t++; 175 | } 176 | } 177 | 178 | 179 | void 180 | set_current_wd(char *path) 181 | { 182 | int len; 183 | char *p = path; 184 | 185 | if (!p && !(p = ksh_get_wd(NULL, 0))) 186 | p = null; 187 | 188 | len = strlen(p) + 1; 189 | 190 | if (len > current_wd_size) 191 | current_wd = aresize(current_wd, current_wd_size = len, APERM); 192 | memcpy(current_wd, p, len); 193 | if (p != path && p != null) 194 | afree(p, ATEMP); 195 | } 196 | 197 | char * 198 | get_phys_path(const char *path) 199 | { 200 | XString xs; 201 | char *xp; 202 | 203 | Xinit(xs, xp, strlen(path) + 1, ATEMP); 204 | 205 | xp = do_phys_path(&xs, xp, path); 206 | 207 | if (!xp) 208 | return NULL; 209 | 210 | if (Xlength(xs, xp) == 0) 211 | Xput(xs, xp, '/'); 212 | Xput(xs, xp, '\0'); 213 | 214 | return Xclose(xs, xp); 215 | } 216 | 217 | static char * 218 | do_phys_path(XString *xsp, char *xp, const char *path) 219 | { 220 | const char *p, *q; 221 | int len, llen; 222 | int savepos; 223 | char lbuf[PATH_MAX]; 224 | 225 | Xcheck(*xsp, xp); 226 | for (p = path; p; p = q) { 227 | while (*p == '/') 228 | p++; 229 | if (!*p) 230 | break; 231 | len = (q = strchr(p, '/')) ? (size_t)(q - p) : strlen(p); 232 | if (len == 1 && p[0] == '.') 233 | continue; 234 | if (len == 2 && p[0] == '.' && p[1] == '.') { 235 | while (xp > Xstring(*xsp, xp)) { 236 | xp--; 237 | if (*xp == '/') 238 | break; 239 | } 240 | continue; 241 | } 242 | 243 | savepos = Xsavepos(*xsp, xp); 244 | Xput(*xsp, xp, '/'); 245 | XcheckN(*xsp, xp, len + 1); 246 | memcpy(xp, p, len); 247 | xp += len; 248 | *xp = '\0'; 249 | 250 | llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1); 251 | if (llen == -1) { 252 | /* EINVAL means it wasn't a symlink... */ 253 | if (errno != EINVAL) 254 | return NULL; 255 | continue; 256 | } 257 | lbuf[llen] = '\0'; 258 | 259 | /* If absolute path, start from scratch.. */ 260 | xp = lbuf[0] == '/' ? Xstring(*xsp, xp) : 261 | Xrestpos(*xsp, xp, savepos); 262 | if (!(xp = do_phys_path(xsp, xp, lbuf))) 263 | return NULL; 264 | } 265 | return xp; 266 | } 267 | -------------------------------------------------------------------------------- /portable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Multi-platform support. 3 | */ 4 | 5 | #ifndef _OKSH_PORTABLE_H_ 6 | #define _OKSH_PORTABLE_H_ 7 | 8 | /* 9 | * Includes 10 | */ 11 | 12 | #if defined(__linux__) || defined(__CYGWIN__) || defined(__midipix__) 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #endif /* __linux__ || __CYGWIN__ || __midipix__ */ 20 | 21 | #include 22 | #include 23 | 24 | #ifdef __APPLE__ 25 | #include 26 | #include 27 | #endif /* __APPLE__ */ 28 | 29 | #if defined(_AIX) || defined(__sun) 30 | #include 31 | #endif /* _AIX || __sun */ 32 | 33 | #if defined(__HAIKU__) 34 | #include 35 | #endif 36 | 37 | #include 38 | 39 | #include "pconfig.h" 40 | 41 | /* 42 | * Defines 43 | */ 44 | 45 | #ifndef CHILD_MAX 46 | #define CHILD_MAX 80 47 | #endif /* !CHILD_MAX */ 48 | 49 | #ifndef O_EXLOCK 50 | #define O_EXLOCK 0 51 | #endif /* !O_EXLOCK */ 52 | 53 | #ifndef _PATH_BSHELL 54 | #define _PATH_BSHELL "/bin/sh" 55 | #endif /* !_PATH_BSHELL */ 56 | 57 | #ifndef _PW_NAME_LEN 58 | #if defined(__linux__) || defined(__CYGWIN__) || defined(_AIX) || defined(__midipix__) || defined(__HAIKU__) 59 | #define _PW_NAME_LEN LOGIN_NAME_MAX 60 | #elif defined(__NetBSD__) 61 | #define _PW_NAME_LEN MAXLOGNAME 62 | #elif defined(__sun) 63 | #define _PW_NAME_LEN LOGNAME_MAX 64 | #elif defined(__hpux) 65 | #define _PW_NAME_LEN 8 66 | #else 67 | #define _PW_NAME_LEN MAXLOGNAME - 1 68 | #endif /* __linux__ || __CYGWIN__ || _AIX || __NetBSD__ || __sun || __midipix__ || __HAIKU__ */ 69 | #endif /* !_PW_NAME_LEN */ 70 | 71 | #ifndef LOCK_EX 72 | #define LOCK_EX 0x02 73 | #endif /* !LOCK_EX */ 74 | 75 | #ifndef LOCK_UN 76 | #define LOCK_UN 0x08 77 | #endif /* !LOCK_UN */ 78 | 79 | #ifndef RLIMIT_RSS 80 | #define RLIMIT_RSS 5 /* resident set size */ 81 | #endif /* !RLIMIT_RSS */ 82 | 83 | #ifndef RLIMIT_MEMLOCK 84 | #define RLIMIT_MEMLOCK 6 /* locked-in-memory address space */ 85 | #endif /* !RLIMIT_MEMLOCK */ 86 | 87 | #ifndef RLIMIT_NPROC 88 | #define RLIMIT_NPROC 7 /* number of processes */ 89 | #endif /* !RLIMIT_NPROC */ 90 | 91 | /* Convert clock_gettime() to clock_get_time() on Max OS X < 10.12 */ 92 | #if defined(__APPLE__) && defined(__MACH__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 93 | #define clock_gettime(x, y) \ 94 | clock_serv_t cclock; \ 95 | mach_timespec_t mts; \ 96 | host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); \ 97 | clock_get_time(cclock, &mts); \ 98 | mach_port_deallocate(mach_task_self(), cclock); \ 99 | (y)->tv_sec = mts.tv_sec; \ 100 | (y)->tv_nsec = mts.tv_nsec; 101 | #endif /* __APPLE__ && __MACH__ && < 10.12 */ 102 | 103 | #ifdef _AIX 104 | #define VWERASE VWERSE 105 | #define VDISCARD VDISCRD 106 | #define _PATH_DEFPATH "/usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin" 107 | #define WCOREFLAG 0200 108 | #define WCOREDUMP(x) ((x) & WCOREFLAG) 109 | #undef BAD 110 | #endif /* _AIX */ 111 | 112 | #ifdef __HAIKU__ 113 | #define _PATH_DEFPATH ".:/boot/home/config/non-packaged/bin:/boot/home/config/bin:/boot/system/non-packaged/bin:/bin:/boot/system/apps:/boot/system/preferences" 114 | #define WCOREFLAG 0200 115 | #define WCOREDUMP(x) ((x) & WCOREFLAG) 116 | #define nice(x) (int)0 117 | #endif /* __HAIKU__ */ 118 | 119 | #ifndef HAVE_SETRESGID 120 | #define setresgid(x, y, z) setgid(x); setegid(y); setgid(z) 121 | #endif /* !HAVE_SETRESGID */ 122 | 123 | #ifndef HAVE_SETRESUID 124 | #define setresuid(x, y, z) setuid(x); seteuid(y); setuid(z) 125 | #endif /* !HAVE_SETRESUID */ 126 | 127 | #ifndef HAVE_SRAND_DETERMINISTIC 128 | #define srand_deterministic(x) srand(x) 129 | #endif /* !HAVE_SRAND_DETERMINISTIC */ 130 | 131 | #ifndef HAVE_TIMERADD 132 | #define timeradd(tvp, uvp, vvp) \ 133 | do { \ 134 | (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ 135 | (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ 136 | if ((vvp)->tv_usec >= 1000000) { \ 137 | (vvp)->tv_sec++; \ 138 | (vvp)->tv_usec -= 1000000; \ 139 | } \ 140 | } while (0) 141 | #endif /* !HAVE_TIMERADD */ 142 | 143 | #ifndef HAVE_TIMERCLEAR 144 | #define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) 145 | #endif /* !HAVE_TIMERCLEAR */ 146 | 147 | #ifndef HAVE_TIMERSUB 148 | #define timersub(tvp, uvp, vvp) \ 149 | do { \ 150 | (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ 151 | (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ 152 | if ((vvp)->tv_usec < 0) { \ 153 | (vvp)->tv_sec--; \ 154 | (vvp)->tv_usec += 1000000; \ 155 | } \ 156 | } while (0) 157 | #endif /* !HAVE_TIMERSUB */ 158 | 159 | /* struct stat compatibility */ 160 | #ifndef HAVE_ST_MTIM 161 | #ifndef HAVE_ST_MTIMESPEC 162 | #define st_mtim st_mtime 163 | #define timespeccmp(tsp, usp, cmp) (tsp) cmp (usp) 164 | #else 165 | #define st_mtim st_mtimespec 166 | #endif /* !HAVE_ST_TIMESPEC */ 167 | #endif /* !HAVE_ST_MTIM */ 168 | 169 | /* Cygwin already has a sys_signame but we want to use our own */ 170 | #ifdef __CYGWIN__ 171 | #undef sys_signame 172 | #endif /* __CYGWIN__ */ 173 | 174 | /* Android is missing _CS_PATH */ 175 | #if defined(__linux__) && defined(__ANDROID__) 176 | #ifndef _CS_PATH 177 | #define _CS_PATH 1 178 | #endif 179 | #endif 180 | 181 | /* From OpenBSD sys/time.h */ 182 | #ifndef timespeccmp 183 | #define timespeccmp(tsp, usp, cmp) \ 184 | (((tsp)->tv_sec == (usp)->tv_sec) ? \ 185 | ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ 186 | ((tsp)->tv_sec cmp (usp)->tv_sec)) 187 | #endif 188 | 189 | #ifndef timespecsub 190 | #define timespecsub(tsp, usp, vsp) \ 191 | do { \ 192 | (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ 193 | (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ 194 | if ((vsp)->tv_nsec < 0) { \ 195 | (vsp)->tv_sec--; \ 196 | (vsp)->tv_nsec += 1000000000L; \ 197 | } \ 198 | } while (0) 199 | #endif 200 | 201 | /* 202 | * Prototypes 203 | */ 204 | 205 | #ifndef HAVE_ASPRINTF 206 | int asprintf(char **str, const char *fmt, ...); 207 | #endif /* !HAVE_ASPRINTF */ 208 | 209 | #ifndef HAVE_CONFSTR 210 | size_t confstr(int, char *, size_t); 211 | #endif /* !HAVE_CONFSTR */ 212 | 213 | #ifndef HAVE_REALLOCARRAY 214 | void *reallocarray(void *, size_t, size_t); 215 | #endif /* !HAVE_REALLOCARRAY */ 216 | 217 | #ifndef HAVE_STRAVIS 218 | int stravis(char **, const char *, int); 219 | #endif /* !HAVE_STRAVIS */ 220 | 221 | #ifndef HAVE_STRLCAT 222 | size_t strlcat(char *, const char *, size_t); 223 | #endif /* !HAVE_STRLCAT */ 224 | 225 | #ifndef HAVE_STRLCPY 226 | size_t strlcpy(char *, const char *, size_t); 227 | #endif /* !HAVE_STRLCPY */ 228 | 229 | #ifndef HAVE_STRTONUM 230 | long long strtonum(const char *numstr, long long minval, long long maxval, 231 | const char **errstrp); 232 | #endif /* !HAVE_STRTONUM */ 233 | 234 | #ifndef HAVE_STRUNVIS 235 | int strunvis(char *, const char *); 236 | #endif /* !HAVE_STRUNVIS */ 237 | 238 | int oksh_issetugid(void); 239 | 240 | /* 241 | * Externs 242 | */ 243 | 244 | #if !defined(HAVE_SIGLIST) || !defined(HAVE_SIGNAME) 245 | #ifdef NSIG 246 | #undef NSIG 247 | #endif /* NSIG */ 248 | #define NSIG 33 249 | #ifndef HAVE_SIGLIST 250 | extern const char *const sys_siglist[NSIG]; 251 | #endif /* !HAVE_SIGLIST */ 252 | #ifndef HAVE_SIGNAME 253 | extern const char *const sys_signame[NSIG]; 254 | #endif /* !HAVE_SIGNAME */ 255 | #endif /* !HAVE_SIGLIST || !HAVE_SIGNAME */ 256 | 257 | /* 258 | * Types 259 | */ 260 | 261 | #ifndef HAVE_SIG_T 262 | typedef void (*sig_t) (int); 263 | #endif /* !HAVE_SIG_T */ 264 | 265 | #endif /* !_OKSH_PORTABLE_H_ */ 266 | -------------------------------------------------------------------------------- /reallocarray.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ 2 | /* 3 | * Copyright (c) 2008 Otto Moerbeek 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include "pconfig.h" 19 | 20 | #ifndef HAVE_REALLOCARRAY 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /* 28 | * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX 29 | * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW 30 | */ 31 | #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) 32 | 33 | void * 34 | reallocarray(void *optr, size_t nmemb, size_t size) 35 | { 36 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && 37 | nmemb > 0 && SIZE_MAX / nmemb < size) { 38 | errno = ENOMEM; 39 | return NULL; 40 | } 41 | return realloc(optr, size * nmemb); 42 | } 43 | 44 | #endif /* !HAVE_REALLOCARRAY */ 45 | -------------------------------------------------------------------------------- /shf.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: shf.h,v 1.8 2015/12/14 06:09:43 mmcc Exp $ */ 2 | 3 | #ifndef SHF_H 4 | # define SHF_H 5 | 6 | /* 7 | * Shell file I/O routines 8 | */ 9 | 10 | #define SHF_BSIZE 512 11 | 12 | #define shf_getc(shf) ((shf)->rnleft > 0 ? (shf)->rnleft--, *(shf)->rp++ : \ 13 | shf_getchar(shf)) 14 | #define shf_putc(c, shf) ((shf)->wnleft == 0 ? shf_putchar((c), (shf)) : \ 15 | ((shf)->wnleft--, *(shf)->wp++ = (c))) 16 | #define shf_eof(shf) ((shf)->flags & SHF_EOF) 17 | #define shf_error(shf) ((shf)->flags & SHF_ERROR) 18 | #define shf_clearerr(shf) ((shf)->flags &= ~(SHF_EOF | SHF_ERROR)) 19 | 20 | /* Flags passed to shf_*open() */ 21 | #define SHF_RD 0x0001 22 | #define SHF_WR 0x0002 23 | #define SHF_RDWR (SHF_RD|SHF_WR) 24 | #define SHF_ACCMODE 0x0003 /* mask */ 25 | #define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */ 26 | #define SHF_UNBUF 0x0008 /* unbuffered I/O */ 27 | #define SHF_CLEXEC 0x0010 /* set close on exec flag */ 28 | #define SHF_MAPHI 0x0020 /* make fd > FDBASE (and close orig) 29 | * (shf_open() only) */ 30 | #define SHF_DYNAMIC 0x0040 /* string: increase buffer as needed */ 31 | #define SHF_INTERRUPT 0x0080 /* EINTR in read/write causes error */ 32 | /* Flags used internally */ 33 | #define SHF_STRING 0x0100 /* a string, not a file */ 34 | #define SHF_ALLOCS 0x0200 /* shf and shf->buf were alloc()ed */ 35 | #define SHF_ALLOCB 0x0400 /* shf->buf was alloc()ed */ 36 | #define SHF_ERROR 0x0800 /* read()/write() error */ 37 | #define SHF_EOF 0x1000 /* read eof (sticky) */ 38 | #define SHF_READING 0x2000 /* currently reading: rnleft,rp valid */ 39 | #define SHF_WRITING 0x4000 /* currently writing: wnleft,wp valid */ 40 | 41 | 42 | struct shf { 43 | int flags; /* see SHF_* */ 44 | unsigned char *rp; /* read: current position in buffer */ 45 | int rbsize; /* size of buffer (1 if SHF_UNBUF) */ 46 | int rnleft; /* read: how much data left in buffer */ 47 | unsigned char *wp; /* write: current position in buffer */ 48 | int wbsize; /* size of buffer (0 if SHF_UNBUF) */ 49 | int wnleft; /* write: how much space left in buffer */ 50 | unsigned char *buf; /* buffer */ 51 | int fd; /* file descriptor */ 52 | int errno_; /* saved value of errno after error */ 53 | int bsize; /* actual size of buf */ 54 | Area *areap; /* area shf/buf were allocated in */ 55 | }; 56 | 57 | extern struct shf shf_iob[]; 58 | 59 | struct shf *shf_open(const char *, int, int, int); 60 | struct shf *shf_fdopen(int, int, struct shf *); 61 | struct shf *shf_reopen(int, int, struct shf *); 62 | struct shf *shf_sopen(char *, int, int, struct shf *); 63 | int shf_close(struct shf *); 64 | int shf_fdclose(struct shf *); 65 | char *shf_sclose(struct shf *); 66 | int shf_flush(struct shf *); 67 | int shf_read(char *, int, struct shf *); 68 | char *shf_getse(char *, int, struct shf *); 69 | int shf_getchar(struct shf *s); 70 | int shf_ungetc(int, struct shf *); 71 | int shf_putchar(int, struct shf *); 72 | int shf_puts(const char *, struct shf *); 73 | int shf_write(const char *, int, struct shf *); 74 | int shf_fprintf(struct shf *, const char *, ...); 75 | int shf_snprintf(char *, int, const char *, ...); 76 | char *shf_smprintf(const char *, ...); 77 | int shf_vfprintf(struct shf *, const char *, va_list); 78 | 79 | #endif /* SHF_H */ 80 | -------------------------------------------------------------------------------- /siglist.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: siglist.c,v 1.8 2015/09/19 04:02:21 guenther Exp $ */ 2 | /* 3 | * Copyright (c) 1983, 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "pconfig.h" 32 | 33 | #ifndef HAVE_SIGLIST 34 | 35 | #include 36 | 37 | const char *const sys_siglist[NSIG] = { 38 | "Signal 0", 39 | "Hangup", /* SIGHUP */ 40 | "Interrupt", /* SIGINT */ 41 | "Quit", /* SIGQUIT */ 42 | "Illegal instruction", /* SIGILL */ 43 | "Trace/BPT trap", /* SIGTRAP */ 44 | "Abort trap", /* SIGABRT */ 45 | "EMT trap", /* SIGEMT */ 46 | "Floating point exception", /* SIGFPE */ 47 | "Killed", /* SIGKILL */ 48 | "Bus error", /* SIGBUS */ 49 | "Segmentation fault", /* SIGSEGV */ 50 | "Bad system call", /* SIGSYS */ 51 | "Broken pipe", /* SIGPIPE */ 52 | "Alarm clock", /* SIGALRM */ 53 | "Terminated", /* SIGTERM */ 54 | "Urgent I/O condition", /* SIGURG */ 55 | "Suspended (signal)", /* SIGSTOP */ 56 | "Suspended", /* SIGTSTP */ 57 | "Continued", /* SIGCONT */ 58 | "Child exited", /* SIGCHLD */ 59 | "Stopped (tty input)", /* SIGTTIN */ 60 | "Stopped (tty output)", /* SIGTTOU */ 61 | "I/O possible", /* SIGIO */ 62 | "Cputime limit exceeded", /* SIGXCPU */ 63 | "Filesize limit exceeded", /* SIGXFSZ */ 64 | "Virtual timer expired", /* SIGVTALRM */ 65 | "Profiling timer expired", /* SIGPROF */ 66 | "Window size changes", /* SIGWINCH */ 67 | "Information request", /* SIGINFO */ 68 | "User defined signal 1", /* SIGUSR1 */ 69 | "User defined signal 2", /* SIGUSR2 */ 70 | "Thread AST", /* SIGTHR */ 71 | }; 72 | 73 | #endif /* !HAVE_SIGLIST */ 74 | -------------------------------------------------------------------------------- /signame.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: signame.c,v 1.5 2009/11/27 19:47:45 guenther Exp $ */ 2 | /* 3 | * Copyright (c) 1983 Regents of the University of California. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "pconfig.h" 32 | 33 | #ifndef HAVE_SIGNAME 34 | 35 | #include 36 | #include 37 | 38 | static const struct { 39 | int sig; 40 | const char *name; 41 | } signame[] = { 42 | { 0, "Signal 0" }, 43 | #ifdef SIGHUP 44 | { SIGHUP, "HUP" }, 45 | #endif 46 | #ifdef SIGINT 47 | { SIGINT, "INT" }, 48 | #endif 49 | #ifdef SIGQUIT 50 | { SIGQUIT, "QUIT" }, 51 | #endif 52 | #ifdef SIGILL 53 | { SIGILL, "ILL" }, 54 | #endif 55 | #ifdef SIGTRAP 56 | { SIGTRAP, "TRAP" }, 57 | #endif 58 | #ifdef SIGABRT 59 | { SIGABRT, "ABRT" }, 60 | #endif 61 | #ifdef SIGEMT 62 | { SIGEMT, "EMT" }, 63 | #endif 64 | #ifdef SIGFPE 65 | { SIGFPE, "FPE" }, 66 | #endif 67 | #ifdef SIGKILL 68 | { SIGKILL, "KILL" }, 69 | #endif 70 | #ifdef SIGBUS 71 | { SIGBUS, "BUS" }, 72 | #endif 73 | #ifdef SIGSEGV 74 | { SIGSEGV, "SEGV" }, 75 | #endif 76 | #ifdef SIGSYS 77 | { SIGSYS, "SYS" }, 78 | #endif 79 | #ifdef SIGPIPE 80 | { SIGPIPE, "PIPE" }, 81 | #endif 82 | #ifdef SIGALRM 83 | { SIGALRM, "ALRM" }, 84 | #endif 85 | #ifdef SIGTERM 86 | { SIGTERM, "TERM" }, 87 | #endif 88 | #ifdef SIGURG 89 | { SIGURG, "URG" }, 90 | #endif 91 | #ifdef SIGSTOP 92 | { SIGSTOP, "STOP" }, 93 | #endif 94 | #ifdef SIGTSTP 95 | { SIGTSTP, "TSTP" }, 96 | #endif 97 | #ifdef SIGCONT 98 | { SIGCONT, "CONT" }, 99 | #endif 100 | #ifdef SIGCHLD 101 | { SIGCHLD, "CHLD" }, 102 | #endif 103 | #ifdef SIGTTIN 104 | { SIGTTIN, "TTIN" }, 105 | #endif 106 | #ifdef SIGTTOU 107 | { SIGTTOU, "TTOU" }, 108 | #endif 109 | #ifdef SIGIO 110 | { SIGIO, "IO" }, 111 | #endif 112 | #ifdef SIGXCPU 113 | { SIGXCPU, "XCPU" }, 114 | #endif 115 | #ifdef SIGXFSZ 116 | { SIGXFSZ, "XFSZ" }, 117 | #endif 118 | #ifdef SIGVTALRM 119 | { SIGVTALRM, "VTALRM" }, 120 | #endif 121 | #ifdef SIGPROF 122 | { SIGPROF, "PROF" }, 123 | #endif 124 | #ifdef SIGWINCH 125 | { SIGWINCH, "WINCH" }, 126 | #endif 127 | #ifdef SIGINFO 128 | { SIGINFO, "INFO" }, 129 | #endif 130 | #ifdef SIGUSR1 131 | { SIGUSR1, "USR1" }, 132 | #endif 133 | #ifdef SIGUSR2 134 | { SIGUSR2, "USR2" }, 135 | #endif 136 | #ifdef SIGPWR 137 | { SIGPWR, "PWR" }, 138 | #endif 139 | #ifdef SIGSTKFLT 140 | { SIGSTKFLT, "STKFLT" }, 141 | #endif 142 | }; 143 | 144 | const char * 145 | oksh_sig2str(int sig) 146 | { 147 | int i; 148 | 149 | for (i = 0; i < sizeof(signame) / sizeof(*signame); i++) { 150 | if (signame[i].sig == sig) 151 | return signame[i].name; 152 | } 153 | 154 | return "UNKNOWN"; 155 | } 156 | 157 | #endif /* !HAVE_SIGNAME */ 158 | -------------------------------------------------------------------------------- /strlcat.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "pconfig.h" 20 | 21 | #ifndef HAVE_STRLCAT 22 | 23 | #include 24 | #include 25 | 26 | /* 27 | * Appends src to string dst of size dsize (unlike strncat, dsize is the 28 | * full size of dst, not space left). At most dsize-1 characters 29 | * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). 30 | * Returns strlen(src) + MIN(dsize, strlen(initial dst)). 31 | * If retval >= dsize, truncation occurred. 32 | */ 33 | size_t 34 | strlcat(char *dst, const char *src, size_t dsize) 35 | { 36 | const char *odst = dst; 37 | const char *osrc = src; 38 | size_t n = dsize; 39 | size_t dlen; 40 | 41 | /* Find the end of dst and adjust bytes left but don't go past end. */ 42 | while (n-- != 0 && *dst != '\0') 43 | dst++; 44 | dlen = dst - odst; 45 | n = dsize - dlen; 46 | 47 | if (n-- == 0) 48 | return(dlen + strlen(src)); 49 | while (*src != '\0') { 50 | if (n != 0) { 51 | *dst++ = *src; 52 | n--; 53 | } 54 | src++; 55 | } 56 | *dst = '\0'; 57 | 58 | return(dlen + (src - osrc)); /* count does not include NUL */ 59 | } 60 | 61 | #endif /* !HAVE_STRLCAT */ 62 | -------------------------------------------------------------------------------- /strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "pconfig.h" 20 | 21 | #ifndef HAVE_STRLCPY 22 | 23 | #include 24 | #include 25 | 26 | /* 27 | * Copy string src to buffer dst of size dsize. At most dsize-1 28 | * chars will be copied. Always NUL terminates (unless dsize == 0). 29 | * Returns strlen(src); if retval >= dsize, truncation occurred. 30 | */ 31 | size_t 32 | strlcpy(char *dst, const char *src, size_t dsize) 33 | { 34 | const char *osrc = src; 35 | size_t nleft = dsize; 36 | 37 | /* Copy as many bytes as will fit. */ 38 | if (nleft != 0) { 39 | while (--nleft != 0) { 40 | if ((*dst++ = *src++) == '\0') 41 | break; 42 | } 43 | } 44 | 45 | /* Not enough room in dst, add NUL and traverse rest of src. */ 46 | if (nleft == 0) { 47 | if (dsize != 0) 48 | *dst = '\0'; /* NUL-terminate dst */ 49 | while (*src++) 50 | ; 51 | } 52 | 53 | return(src - osrc - 1); /* count does not include NUL */ 54 | } 55 | 56 | #endif /* !HAVE_STRLCPY */ 57 | -------------------------------------------------------------------------------- /strtonum.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2004 Ted Unangst and Todd Miller 5 | * All rights reserved. 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | #include "pconfig.h" 21 | 22 | #ifndef HAVE_STRTONUM 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #define INVALID 1 29 | #define TOOSMALL 2 30 | #define TOOLARGE 3 31 | 32 | #ifndef LLONG_MAX 33 | #define LLONG_MAX 0x7fffffffffffffffLL 34 | #endif 35 | 36 | #ifndef LLONG_MIN 37 | #define LLONG_MIN (-0x7fffffffffffffffLL-1) 38 | #endif 39 | 40 | long long 41 | strtonum(const char *numstr, long long minval, long long maxval, 42 | const char **errstrp) 43 | { 44 | long long ll = 0; 45 | int error = 0; 46 | char *ep; 47 | struct errval { 48 | const char *errstr; 49 | int err; 50 | } ev[4] = { 51 | { NULL, 0 }, 52 | { "invalid", EINVAL }, 53 | { "too small", ERANGE }, 54 | { "too large", ERANGE }, 55 | }; 56 | 57 | ev[0].err = errno; 58 | errno = 0; 59 | if (minval > maxval) { 60 | error = INVALID; 61 | } else { 62 | ll = strtoll(numstr, &ep, 10); 63 | if (numstr == ep || *ep != '\0') 64 | error = INVALID; 65 | else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) 66 | error = TOOSMALL; 67 | else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) 68 | error = TOOLARGE; 69 | } 70 | if (errstrp != NULL) 71 | *errstrp = ev[error].errstr; 72 | errno = ev[error].err; 73 | if (error) 74 | ll = 0; 75 | 76 | return (ll); 77 | } 78 | 79 | #endif /* !HAVE_STRTONUM */ 80 | -------------------------------------------------------------------------------- /table.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: table.c,v 1.25 2018/01/16 22:52:32 jca Exp $ */ 2 | 3 | /* 4 | * dynamic hashed associative table for commands and variables 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "sh.h" 12 | 13 | #define INIT_TBLS 8 /* initial table size (power of 2) */ 14 | 15 | struct table taliases; /* tracked aliases */ 16 | struct table builtins; /* built-in commands */ 17 | struct table aliases; /* aliases */ 18 | struct table keywords; /* keywords */ 19 | struct table homedirs; /* homedir() cache */ 20 | 21 | char *search_path; /* copy of either PATH or def_path */ 22 | const char *def_path; /* path to use if PATH not set */ 23 | char *tmpdir; /* TMPDIR value */ 24 | const char *prompt; 25 | int cur_prompt; /* PS1 or PS2 */ 26 | int current_lineno; /* LINENO value */ 27 | 28 | static void texpand(struct table *, int); 29 | static int tnamecmp(const void *, const void *); 30 | 31 | 32 | unsigned int 33 | hash(const char *n) 34 | { 35 | unsigned int h = 0; 36 | 37 | while (*n != '\0') 38 | h = 33*h + (unsigned char)(*n++); 39 | return h; 40 | } 41 | 42 | void 43 | ktinit(struct table *tp, Area *ap, int tsize) 44 | { 45 | tp->areap = ap; 46 | tp->tbls = NULL; 47 | tp->size = tp->nfree = 0; 48 | if (tsize) 49 | texpand(tp, tsize); 50 | } 51 | 52 | static void 53 | texpand(struct table *tp, int nsize) 54 | { 55 | int i; 56 | struct tbl *tblp, **p; 57 | struct tbl **ntblp, **otblp = tp->tbls; 58 | int osize = tp->size; 59 | 60 | ntblp = areallocarray(NULL, nsize, sizeof(struct tbl *), tp->areap); 61 | for (i = 0; i < nsize; i++) 62 | ntblp[i] = NULL; 63 | tp->size = nsize; 64 | tp->nfree = 7*nsize/10; /* table can get 70% full */ 65 | tp->tbls = ntblp; 66 | if (otblp == NULL) 67 | return; 68 | for (i = 0; i < osize; i++) 69 | if ((tblp = otblp[i]) != NULL) { 70 | if ((tblp->flag&DEFINED)) { 71 | for (p = &ntblp[hash(tblp->name) & 72 | (tp->size-1)]; *p != NULL; p--) 73 | if (p == ntblp) /* wrap */ 74 | p += tp->size; 75 | *p = tblp; 76 | tp->nfree--; 77 | } else if (!(tblp->flag & FINUSE)) { 78 | afree(tblp, tp->areap); 79 | } 80 | } 81 | afree(otblp, tp->areap); 82 | } 83 | 84 | /* table */ 85 | /* name to enter */ 86 | /* hash(n) */ 87 | struct tbl * 88 | ktsearch(struct table *tp, const char *n, unsigned int h) 89 | { 90 | struct tbl **pp, *p; 91 | 92 | if (tp->size == 0) 93 | return NULL; 94 | 95 | /* search for name in hashed table */ 96 | for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) { 97 | if (*p->name == *n && strcmp(p->name, n) == 0 && 98 | (p->flag&DEFINED)) 99 | return p; 100 | if (pp == tp->tbls) /* wrap */ 101 | pp += tp->size; 102 | } 103 | 104 | return NULL; 105 | } 106 | 107 | /* table */ 108 | /* name to enter */ 109 | /* hash(n) */ 110 | struct tbl * 111 | ktenter(struct table *tp, const char *n, unsigned int h) 112 | { 113 | struct tbl **pp, *p; 114 | int len; 115 | 116 | if (tp->size == 0) 117 | texpand(tp, INIT_TBLS); 118 | Search: 119 | /* search for name in hashed table */ 120 | for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) { 121 | if (*p->name == *n && strcmp(p->name, n) == 0) 122 | return p; /* found */ 123 | if (pp == tp->tbls) /* wrap */ 124 | pp += tp->size; 125 | } 126 | 127 | if (tp->nfree <= 0) { /* too full */ 128 | if (tp->size <= INT_MAX/2) 129 | texpand(tp, 2*tp->size); 130 | else 131 | internal_errorf("too many vars"); 132 | goto Search; 133 | } 134 | 135 | /* create new tbl entry */ 136 | len = strlen(n) + 1; 137 | p = alloc(offsetof(struct tbl, name[0]) + len, 138 | tp->areap); 139 | p->flag = 0; 140 | p->type = 0; 141 | p->areap = tp->areap; 142 | p->u2.field = 0; 143 | p->u.array = NULL; 144 | memcpy(p->name, n, len); 145 | 146 | /* enter in tp->tbls */ 147 | tp->nfree--; 148 | *pp = p; 149 | return p; 150 | } 151 | 152 | void 153 | ktdelete(struct tbl *p) 154 | { 155 | p->flag = 0; 156 | } 157 | 158 | void 159 | ktwalk(struct tstate *ts, struct table *tp) 160 | { 161 | ts->left = tp->size; 162 | ts->next = tp->tbls; 163 | } 164 | 165 | struct tbl * 166 | ktnext(struct tstate *ts) 167 | { 168 | while (--ts->left >= 0) { 169 | struct tbl *p = *ts->next++; 170 | if (p != NULL && (p->flag&DEFINED)) 171 | return p; 172 | } 173 | return NULL; 174 | } 175 | 176 | static int 177 | tnamecmp(const void *p1, const void *p2) 178 | { 179 | char *name1 = (*(struct tbl **)p1)->name; 180 | char *name2 = (*(struct tbl **)p2)->name; 181 | return strcmp(name1, name2); 182 | } 183 | 184 | struct tbl ** 185 | ktsort(struct table *tp) 186 | { 187 | int i; 188 | struct tbl **p, **sp, **dp; 189 | 190 | p = areallocarray(NULL, tp->size + 1, 191 | sizeof(struct tbl *), ATEMP); 192 | sp = tp->tbls; /* source */ 193 | dp = p; /* dest */ 194 | for (i = 0; i < tp->size; i++) 195 | if ((*dp = *sp++) != NULL && (((*dp)->flag&DEFINED) || 196 | ((*dp)->flag&ARRAY))) 197 | dp++; 198 | i = dp - p; 199 | qsortp((void**)p, (size_t)i, tnamecmp); 200 | p[i] = NULL; 201 | return p; 202 | } 203 | 204 | #ifdef PERF_DEBUG /* performance debugging */ 205 | 206 | void tprintinfo(struct table *tp); 207 | 208 | void 209 | tprintinfo(struct table *tp) 210 | { 211 | struct tbl *te; 212 | char *n; 213 | unsigned int h; 214 | int ncmp; 215 | int totncmp = 0, maxncmp = 0; 216 | int nentries = 0; 217 | struct tstate ts; 218 | 219 | shellf("table size %d, nfree %d\n", tp->size, tp->nfree); 220 | shellf(" Ncmp name\n"); 221 | ktwalk(&ts, tp); 222 | while ((te = ktnext(&ts))) { 223 | struct tbl **pp, *p; 224 | 225 | h = hash(n = te->name); 226 | ncmp = 0; 227 | 228 | /* taken from ktsearch() and added counter */ 229 | for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) { 230 | ncmp++; 231 | if (*p->name == *n && strcmp(p->name, n) == 0 && 232 | (p->flag&DEFINED)) 233 | break; /* return p; */ 234 | if (pp == tp->tbls) /* wrap */ 235 | pp += tp->size; 236 | } 237 | shellf(" %4d %s\n", ncmp, n); 238 | totncmp += ncmp; 239 | nentries++; 240 | if (ncmp > maxncmp) 241 | maxncmp = ncmp; 242 | } 243 | if (nentries) 244 | shellf(" %d entries, worst ncmp %d, avg ncmp %d.%02d\n", 245 | nentries, maxncmp, 246 | totncmp / nentries, 247 | (totncmp % nentries) * 100 / nentries); 248 | } 249 | #endif /* PERF_DEBUG */ 250 | -------------------------------------------------------------------------------- /table.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: table.h,v 1.15 2018/06/18 17:03:58 millert Exp $ */ 2 | 3 | /* $From: table.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */ 4 | 5 | /* 6 | * generic hashed associative table for commands and variables. 7 | */ 8 | 9 | struct table { 10 | Area *areap; /* area to allocate entries */ 11 | int size, nfree; /* hash size (always 2^^n), free entries */ 12 | struct tbl **tbls; /* hashed table items */ 13 | }; 14 | 15 | struct tbl { /* table item */ 16 | int flag; /* flags */ 17 | int type; /* command type (see below), base (if INTEGER), 18 | * or offset from val.s of value (if EXPORT) */ 19 | Area *areap; /* area to allocate from */ 20 | union { 21 | char *s; /* string */ 22 | int64_t i; /* integer */ 23 | int (*f)(char **); /* int function */ 24 | struct op *t; /* "function" tree */ 25 | } val; /* value */ 26 | int index; /* index for an array */ 27 | union { 28 | int field; /* field with for -L/-R/-Z */ 29 | int errno_; /* CEXEC/CTALIAS */ 30 | } u2; 31 | union { 32 | struct tbl *array; /* array values */ 33 | char *fpath; /* temporary path to undef function */ 34 | } u; 35 | char name[4]; /* name -- variable length */ 36 | }; 37 | 38 | /* common flag bits */ 39 | #define ALLOC BIT(0) /* val.s has been allocated */ 40 | #define DEFINED BIT(1) /* is defined in block */ 41 | #define ISSET BIT(2) /* has value, vp->val.[si] */ 42 | #define EXPORT BIT(3) /* exported variable/function */ 43 | #define TRACE BIT(4) /* var: user flagged, func: execution tracing */ 44 | /* (start non-common flags at 8) */ 45 | /* flag bits used for variables */ 46 | #define SPECIAL BIT(8) /* PATH, IFS, SECONDS, etc */ 47 | #define INTEGER BIT(9) /* val.i contains integer value */ 48 | #define RDONLY BIT(10) /* read-only variable */ 49 | #define LOCAL BIT(11) /* for local typeset() */ 50 | #define ARRAY BIT(13) /* array */ 51 | #define LJUST BIT(14) /* left justify */ 52 | #define RJUST BIT(15) /* right justify */ 53 | #define ZEROFIL BIT(16) /* 0 filled if RJUSTIFY, strip 0s if LJUSTIFY */ 54 | #define LCASEV BIT(17) /* convert to lower case */ 55 | #define UCASEV_AL BIT(18)/* convert to upper case / autoload function */ 56 | #define INT_U BIT(19) /* unsigned integer */ 57 | #define INT_L BIT(20) /* long integer (no-op) */ 58 | #define IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */ 59 | #define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */ 60 | #define EXPRINEVAL BIT(23) /* contents currently being evaluated */ 61 | #define EXPRLVALUE BIT(24) /* useable as lvalue (temp flag) */ 62 | /* flag bits used for taliases/builtins/aliases/keywords/functions */ 63 | #define KEEPASN BIT(8) /* keep command assignments (eg, var=x cmd) */ 64 | #define FINUSE BIT(9) /* function being executed */ 65 | #define FDELETE BIT(10) /* function deleted while it was executing */ 66 | #define FKSH BIT(11) /* function defined with function x (vs x()) */ 67 | #define SPEC_BI BIT(12) /* a POSIX special builtin */ 68 | #define REG_BI BIT(13) /* a POSIX regular builtin */ 69 | /* Attributes that can be set by the user (used to decide if an unset param 70 | * should be repoted by set/typeset). Does not include ARRAY or LOCAL. 71 | */ 72 | #define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL\ 73 | |LCASEV|UCASEV_AL|INT_U|INT_L) 74 | 75 | /* command types */ 76 | #define CNONE 0 /* undefined */ 77 | #define CSHELL 1 /* built-in */ 78 | #define CFUNC 2 /* function */ 79 | #define CEXEC 4 /* executable command */ 80 | #define CALIAS 5 /* alias */ 81 | #define CKEYWD 6 /* keyword */ 82 | #define CTALIAS 7 /* tracked alias */ 83 | 84 | /* Flags for findcom()/comexec() */ 85 | #define FC_SPECBI BIT(0) /* special builtin */ 86 | #define FC_FUNC BIT(1) /* function builtin */ 87 | #define FC_REGBI BIT(2) /* regular builtin */ 88 | #define FC_UNREGBI BIT(3) /* un-regular builtin (!special,!regular) */ 89 | #define FC_BI (FC_SPECBI|FC_REGBI|FC_UNREGBI) 90 | #define FC_PATH BIT(4) /* do path search */ 91 | #define FC_DEFPATH BIT(5) /* use default path in path search */ 92 | 93 | 94 | #define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */ 95 | #define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */ 96 | #define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip]) 97 | #define AI_ARGC(a) ((a).argc_ - (a).skip) 98 | 99 | /* Argument info. Used for $#, $* for shell, functions, includes, etc. */ 100 | struct arg_info { 101 | int flags; /* AF_* */ 102 | char **argv; 103 | int argc_; 104 | int skip; /* first arg is argv[0], second is argv[1 + skip] */ 105 | }; 106 | 107 | /* 108 | * activation record for function blocks 109 | */ 110 | struct block { 111 | Area area; /* area to allocate things */ 112 | /*struct arg_info argi;*/ 113 | char **argv; 114 | int argc; 115 | int flags; /* see BF_* */ 116 | struct table vars; /* local variables */ 117 | struct table funs; /* local functions */ 118 | Getopt getopts_state; 119 | #if 1 120 | char * error; /* error handler */ 121 | char * exit; /* exit handler */ 122 | #else 123 | Trap error, exit; 124 | #endif 125 | struct block *next; /* enclosing block */ 126 | }; 127 | 128 | /* Values for struct block.flags */ 129 | #define BF_DOGETOPTS BIT(0) /* save/restore getopts state */ 130 | 131 | /* 132 | * Used by ktwalk() and ktnext() routines. 133 | */ 134 | struct tstate { 135 | int left; 136 | struct tbl **next; 137 | }; 138 | 139 | extern struct table taliases; /* tracked aliases */ 140 | extern struct table builtins; /* built-in commands */ 141 | extern struct table aliases; /* aliases */ 142 | extern struct table keywords; /* keywords */ 143 | extern struct table homedirs; /* homedir() cache */ 144 | 145 | struct builtin { 146 | const char *name; 147 | int (*func)(char **); 148 | }; 149 | 150 | /* these really are externs! Look in table.c for them */ 151 | extern const struct builtin shbuiltins [], kshbuiltins []; 152 | 153 | /* var spec values */ 154 | #define V_NONE 0 155 | #define V_PATH 1 156 | #define V_IFS 2 157 | #define V_SECONDS 3 158 | #define V_OPTIND 4 159 | #define V_MAIL 5 160 | #define V_MAILPATH 6 161 | #define V_MAILCHECK 7 162 | #define V_RANDOM 8 163 | #define V_HISTCONTROL 9 164 | #define V_HISTSIZE 10 165 | #define V_HISTFILE 11 166 | #define V_VISUAL 12 167 | #define V_EDITOR 13 168 | #define V_COLUMNS 14 169 | #define V_POSIXLY_CORRECT 15 170 | #define V_TMOUT 16 171 | #define V_TMPDIR 17 172 | #define V_LINENO 18 173 | #define V_TERM 19 174 | 175 | /* values for set_prompt() */ 176 | #define PS1 0 /* command */ 177 | #define PS2 1 /* command continuation */ 178 | 179 | extern char *search_path; /* copy of either PATH or def_path */ 180 | extern const char *def_path; /* path to use if PATH not set */ 181 | extern char *tmpdir; /* TMPDIR value */ 182 | extern const char *prompt; 183 | extern int cur_prompt; /* PS1 or PS2 */ 184 | extern int current_lineno; /* LINENO value */ 185 | 186 | unsigned int hash(const char *); 187 | void ktinit(struct table *, Area *, int); 188 | struct tbl * ktsearch(struct table *, const char *, unsigned int); 189 | struct tbl * ktenter(struct table *, const char *, unsigned int); 190 | void ktdelete(struct tbl *); 191 | void ktwalk(struct tstate *, struct table *); 192 | struct tbl * ktnext(struct tstate *); 193 | struct tbl ** ktsort(struct table *); 194 | -------------------------------------------------------------------------------- /trap.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: trap.c,v 1.33 2018/12/08 21:03:51 jca Exp $ */ 2 | 3 | /* 4 | * signal handling 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "sh.h" 13 | 14 | Trap sigtraps[NSIG + 1]; 15 | 16 | static struct sigaction Sigact_ign, Sigact_trap; 17 | 18 | #ifndef HAVE_SIGNAME 19 | extern const char *oksh_sig2str(int); 20 | #endif 21 | 22 | void 23 | inittraps(void) 24 | { 25 | int i; 26 | 27 | /* Populate sigtraps based on sys_signame and sys_siglist. */ 28 | for (i = 0; i <= NSIG; i++) { 29 | sigtraps[i].signal = i; 30 | if (i == SIGERR_) { 31 | sigtraps[i].name = "ERR"; 32 | sigtraps[i].mess = "Error handler"; 33 | } else { 34 | #ifdef HAVE_SIGNAME 35 | sigtraps[i].name = sys_signame[i]; 36 | #else 37 | sigtraps[i].name = oksh_sig2str(i); 38 | #endif 39 | #if defined(HAVE_SIGLIST) && !defined(__linux__) 40 | sigtraps[i].mess = sys_siglist[i]; 41 | #else 42 | static char *mess[NSIG + 1] = { NULL }; 43 | if (!mess[i]) 44 | mess[i] = strdup(strsignal(i)); 45 | sigtraps[i].mess = mess[i]; 46 | #endif 47 | } 48 | } 49 | sigtraps[SIGEXIT_].name = "EXIT"; /* our name for signal 0 */ 50 | 51 | sigemptyset(&Sigact_ign.sa_mask); 52 | Sigact_ign.sa_flags = 0; /* interruptible */ 53 | Sigact_ign.sa_handler = SIG_IGN; 54 | Sigact_trap = Sigact_ign; 55 | Sigact_trap.sa_handler = trapsig; 56 | 57 | sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; 58 | sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; 59 | sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */ 60 | sigtraps[SIGHUP].flags |= TF_FATAL; 61 | sigtraps[SIGCHLD].flags |= TF_SHELL_USES; 62 | 63 | /* these are always caught so we can clean up any temporary files. */ 64 | setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG); 65 | setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG); 66 | setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG); 67 | setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); 68 | } 69 | 70 | static void alarm_catcher(int sig); 71 | 72 | void 73 | alarm_init(void) 74 | { 75 | sigtraps[SIGALRM].flags |= TF_SHELL_USES; 76 | setsig(&sigtraps[SIGALRM], alarm_catcher, 77 | SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); 78 | } 79 | 80 | static void 81 | alarm_catcher(int sig) 82 | { 83 | int errno_ = errno; 84 | 85 | if (ksh_tmout_state == TMOUT_READING) { 86 | int left = alarm(0); 87 | 88 | if (left == 0) { 89 | ksh_tmout_state = TMOUT_LEAVING; 90 | intrsig = 1; 91 | } else 92 | alarm(left); 93 | } 94 | errno = errno_; 95 | } 96 | 97 | Trap * 98 | gettrap(const char *name, int igncase) 99 | { 100 | int i; 101 | Trap *p; 102 | 103 | if (digit(*name)) { 104 | int n; 105 | 106 | if (getn(name, &n) && 0 <= n && n < NSIG) 107 | return &sigtraps[n]; 108 | return NULL; 109 | } 110 | 111 | if (igncase && strncasecmp(name, "SIG", 3) == 0) 112 | name += 3; 113 | if (!igncase && strncmp(name, "SIG", 3) == 0) 114 | name += 3; 115 | 116 | for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 117 | if (p->name) { 118 | if (igncase && strcasecmp(p->name, name) == 0) 119 | return p; 120 | if (!igncase && strcmp(p->name, name) == 0) 121 | return p; 122 | } 123 | return NULL; 124 | } 125 | 126 | /* 127 | * trap signal handler 128 | */ 129 | void 130 | trapsig(int i) 131 | { 132 | Trap *p = &sigtraps[i]; 133 | int errno_ = errno; 134 | 135 | trap = p->set = 1; 136 | if (p->flags & TF_DFL_INTR) 137 | intrsig = 1; 138 | if ((p->flags & TF_FATAL) && !p->trap) { 139 | fatal_trap = 1; 140 | intrsig = 1; 141 | } 142 | if (p->shtrap) 143 | (*p->shtrap)(i); 144 | errno = errno_; 145 | } 146 | 147 | /* called when we want to allow the user to ^C out of something - won't 148 | * work if user has trapped SIGINT. 149 | */ 150 | void 151 | intrcheck(void) 152 | { 153 | if (intrsig) 154 | runtraps(TF_DFL_INTR|TF_FATAL); 155 | } 156 | 157 | /* called after EINTR to check if a signal with normally causes process 158 | * termination has been received. 159 | */ 160 | int 161 | fatal_trap_check(void) 162 | { 163 | int i; 164 | Trap *p; 165 | 166 | /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ 167 | for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 168 | if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) 169 | /* return value is used as an exit code */ 170 | return 128 + p->signal; 171 | return 0; 172 | } 173 | 174 | /* Returns the signal number of any pending traps: ie, a signal which has 175 | * occurred for which a trap has been set or for which the TF_DFL_INTR flag 176 | * is set. 177 | */ 178 | int 179 | trap_pending(void) 180 | { 181 | int i; 182 | Trap *p; 183 | 184 | for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 185 | if (p->set && ((p->trap && p->trap[0]) || 186 | ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap))) 187 | return p->signal; 188 | return 0; 189 | } 190 | 191 | /* 192 | * run any pending traps. If intr is set, only run traps that 193 | * can interrupt commands. 194 | */ 195 | void 196 | runtraps(int flag) 197 | { 198 | int i; 199 | Trap *p; 200 | 201 | if (ksh_tmout_state == TMOUT_LEAVING) { 202 | ksh_tmout_state = TMOUT_EXECUTING; 203 | warningf(false, "timed out waiting for input"); 204 | unwind(LEXIT); 205 | } else 206 | /* XXX: this means the alarm will have no effect if a trap 207 | * is caught after the alarm() was started...not good. 208 | */ 209 | ksh_tmout_state = TMOUT_EXECUTING; 210 | if (!flag) 211 | trap = 0; 212 | if (flag & TF_DFL_INTR) 213 | intrsig = 0; 214 | if (flag & TF_FATAL) 215 | fatal_trap = 0; 216 | for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 217 | if (p->set && (!flag || 218 | ((p->flags & flag) && p->trap == NULL))) 219 | runtrap(p); 220 | } 221 | 222 | void 223 | runtrap(Trap *p) 224 | { 225 | int i = p->signal; 226 | char *trapstr = p->trap; 227 | int oexstat; 228 | int old_changed = 0; 229 | 230 | p->set = 0; 231 | if (trapstr == NULL) { /* SIG_DFL */ 232 | if (p->flags & TF_FATAL) { 233 | /* eg, SIGHUP */ 234 | exstat = 128 + i; 235 | unwind(LLEAVE); 236 | } 237 | if (p->flags & TF_DFL_INTR) { 238 | /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */ 239 | exstat = 128 + i; 240 | unwind(LINTR); 241 | } 242 | return; 243 | } 244 | if (trapstr[0] == '\0') /* SIG_IGN */ 245 | return; 246 | if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */ 247 | old_changed = p->flags & TF_CHANGED; 248 | p->flags &= ~TF_CHANGED; 249 | p->trap = NULL; 250 | } 251 | oexstat = exstat; 252 | /* Note: trapstr is fully parsed before anything is executed, thus 253 | * no problem with afree(p->trap) in settrap() while still in use. 254 | */ 255 | command(trapstr, current_lineno); 256 | exstat = oexstat; 257 | if (i == SIGEXIT_ || i == SIGERR_) { 258 | if (p->flags & TF_CHANGED) 259 | /* don't clear TF_CHANGED */ 260 | afree(trapstr, APERM); 261 | else 262 | p->trap = trapstr; 263 | p->flags |= old_changed; 264 | } 265 | } 266 | 267 | /* clear pending traps and reset user's trap handlers; used after fork(2) */ 268 | void 269 | cleartraps(void) 270 | { 271 | int i; 272 | Trap *p; 273 | 274 | trap = 0; 275 | intrsig = 0; 276 | fatal_trap = 0; 277 | for (i = NSIG+1, p = sigtraps; --i >= 0; p++) { 278 | p->set = 0; 279 | if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) 280 | settrap(p, NULL); 281 | } 282 | } 283 | 284 | /* restore signals just before an exec(2) */ 285 | void 286 | restoresigs(void) 287 | { 288 | int i; 289 | Trap *p; 290 | 291 | for (i = NSIG+1, p = sigtraps; --i >= 0; p++) 292 | if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) 293 | setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, 294 | SS_RESTORE_CURR|SS_FORCE); 295 | } 296 | 297 | void 298 | settrap(Trap *p, char *s) 299 | { 300 | sig_t f; 301 | 302 | afree(p->trap, APERM); 303 | p->trap = str_save(s, APERM); /* handles s == 0 */ 304 | p->flags |= TF_CHANGED; 305 | f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN; 306 | 307 | p->flags |= TF_USER_SET; 308 | if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) 309 | f = trapsig; 310 | else if (p->flags & TF_SHELL_USES) { 311 | if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) { 312 | /* do what user wants at exec time */ 313 | p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 314 | if (f == SIG_IGN) 315 | p->flags |= TF_EXEC_IGN; 316 | else 317 | p->flags |= TF_EXEC_DFL; 318 | } 319 | 320 | /* assumes handler already set to what shell wants it 321 | * (normally trapsig, but could be j_sigchld() or SIG_IGN) 322 | */ 323 | return; 324 | } 325 | 326 | /* todo: should we let user know signal is ignored? how? */ 327 | setsig(p, f, SS_RESTORE_CURR|SS_USER); 328 | } 329 | 330 | /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't 331 | * kill shell (unless user catches it and exits) 332 | */ 333 | int 334 | block_pipe(void) 335 | { 336 | int restore_dfl = 0; 337 | Trap *p = &sigtraps[SIGPIPE]; 338 | 339 | if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 340 | setsig(p, SIG_IGN, SS_RESTORE_CURR); 341 | if (p->flags & TF_ORIG_DFL) 342 | restore_dfl = 1; 343 | } else if (p->cursig == SIG_DFL) { 344 | setsig(p, SIG_IGN, SS_RESTORE_CURR); 345 | restore_dfl = 1; /* restore to SIG_DFL */ 346 | } 347 | return restore_dfl; 348 | } 349 | 350 | /* Called by c_print() to undo whatever block_pipe() did */ 351 | void 352 | restore_pipe(int restore_dfl) 353 | { 354 | if (restore_dfl) 355 | setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); 356 | } 357 | 358 | /* Set action for a signal. Action may not be set if original 359 | * action was SIG_IGN, depending on the value of flags and 360 | * FTALKING. 361 | */ 362 | int 363 | setsig(Trap *p, sig_t f, int flags) 364 | { 365 | struct sigaction sigact; 366 | 367 | if (p->signal == SIGEXIT_ || p->signal == SIGERR_) 368 | return 1; 369 | 370 | /* First time setting this signal? If so, get and note the current 371 | * setting. 372 | */ 373 | if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 374 | sigaction(p->signal, &Sigact_ign, &sigact); 375 | p->flags |= sigact.sa_handler == SIG_IGN ? 376 | TF_ORIG_IGN : TF_ORIG_DFL; 377 | p->cursig = SIG_IGN; 378 | } 379 | 380 | /* Generally, an ignored signal stays ignored, except if 381 | * - the user of an interactive shell wants to change it 382 | * - the shell wants for force a change 383 | */ 384 | if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && 385 | (!(flags & SS_USER) || !Flag(FTALKING))) 386 | return 0; 387 | 388 | setexecsig(p, flags & SS_RESTORE_MASK); 389 | 390 | /* This is here 'cause there should be a way of clearing shtraps, but 391 | * don't know if this is a sane way of doing it. At the moment, 392 | * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH). 393 | */ 394 | if (!(flags & SS_USER)) 395 | p->shtrap = NULL; 396 | if (flags & SS_SHTRAP) { 397 | p->shtrap = f; 398 | f = trapsig; 399 | } 400 | 401 | if (p->cursig != f) { 402 | p->cursig = f; 403 | sigemptyset(&sigact.sa_mask); 404 | sigact.sa_flags = 0 /* interruptible */; 405 | sigact.sa_handler = f; 406 | sigaction(p->signal, &sigact, NULL); 407 | } 408 | 409 | return 1; 410 | } 411 | 412 | /* control what signal is set to before an exec() */ 413 | void 414 | setexecsig(Trap *p, int restore) 415 | { 416 | /* XXX debugging */ 417 | if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) 418 | internal_errorf("%s: unset signal %d(%s)", 419 | __func__, p->signal, p->name); 420 | 421 | /* restore original value for exec'd kids */ 422 | p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 423 | switch (restore & SS_RESTORE_MASK) { 424 | case SS_RESTORE_CURR: /* leave things as they currently are */ 425 | break; 426 | case SS_RESTORE_ORIG: 427 | p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; 428 | break; 429 | case SS_RESTORE_DFL: 430 | p->flags |= TF_EXEC_DFL; 431 | break; 432 | case SS_RESTORE_IGN: 433 | p->flags |= TF_EXEC_IGN; 434 | break; 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /tree.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tree.c,v 1.34 2018/04/09 17:53:36 tobias Exp $ */ 2 | 3 | /* 4 | * command tree climbing 5 | */ 6 | 7 | #include 8 | 9 | #include "sh.h" 10 | 11 | #define INDENT 4 12 | 13 | #define tputc(c, shf) shf_putchar(c, shf); 14 | static void ptree(struct op *, int, struct shf *); 15 | static void pioact(struct shf *, int, struct ioword *); 16 | static void tputC(int, struct shf *); 17 | static void tputS(char *, struct shf *); 18 | static void vfptreef(struct shf *, int, const char *, va_list); 19 | static struct ioword **iocopy(struct ioword **, Area *); 20 | static void iofree(struct ioword **, Area *); 21 | 22 | /* 23 | * print a command tree 24 | */ 25 | 26 | static void 27 | ptree(struct op *t, int indent, struct shf *shf) 28 | { 29 | char **w; 30 | struct ioword **ioact; 31 | struct op *t1; 32 | 33 | Chain: 34 | if (t == NULL) 35 | return; 36 | switch (t->type) { 37 | case TCOM: 38 | if (t->vars) 39 | for (w = t->vars; *w != NULL; ) 40 | fptreef(shf, indent, "%S ", *w++); 41 | else 42 | fptreef(shf, indent, "#no-vars# "); 43 | if (t->args) 44 | for (w = t->args; *w != NULL; ) 45 | fptreef(shf, indent, "%S ", *w++); 46 | else 47 | fptreef(shf, indent, "#no-args# "); 48 | break; 49 | case TEXEC: 50 | t = t->left; 51 | goto Chain; 52 | case TPAREN: 53 | fptreef(shf, indent + 2, "( %T) ", t->left); 54 | break; 55 | case TPIPE: 56 | fptreef(shf, indent, "%T| ", t->left); 57 | t = t->right; 58 | goto Chain; 59 | case TLIST: 60 | fptreef(shf, indent, "%T%;", t->left); 61 | t = t->right; 62 | goto Chain; 63 | case TOR: 64 | case TAND: 65 | fptreef(shf, indent, "%T%s %T", 66 | t->left, (t->type==TOR) ? "||" : "&&", t->right); 67 | break; 68 | case TBANG: 69 | fptreef(shf, indent, "! "); 70 | t = t->right; 71 | goto Chain; 72 | case TDBRACKET: 73 | { 74 | int i; 75 | 76 | fptreef(shf, indent, "[["); 77 | for (i = 0; t->args[i]; i++) 78 | fptreef(shf, indent, " %S", t->args[i]); 79 | fptreef(shf, indent, " ]] "); 80 | break; 81 | } 82 | case TSELECT: 83 | fptreef(shf, indent, "select %s ", t->str); 84 | /* FALLTHROUGH */ 85 | case TFOR: 86 | if (t->type == TFOR) 87 | fptreef(shf, indent, "for %s ", t->str); 88 | if (t->vars != NULL) { 89 | fptreef(shf, indent, "in "); 90 | for (w = t->vars; *w; ) 91 | fptreef(shf, indent, "%S ", *w++); 92 | fptreef(shf, indent, "%;"); 93 | } 94 | fptreef(shf, indent + INDENT, "do%N%T", t->left); 95 | fptreef(shf, indent, "%;done "); 96 | break; 97 | case TCASE: 98 | fptreef(shf, indent, "case %S in", t->str); 99 | for (t1 = t->left; t1 != NULL; t1 = t1->right) { 100 | fptreef(shf, indent, "%N("); 101 | for (w = t1->vars; *w != NULL; w++) 102 | fptreef(shf, indent, "%S%c", *w, 103 | (w[1] != NULL) ? '|' : ')'); 104 | fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left); 105 | } 106 | fptreef(shf, indent, "%Nesac "); 107 | break; 108 | case TIF: 109 | case TELIF: 110 | /* 3 == strlen("if ") */ 111 | fptreef(shf, indent + 3, "if %T", t->left); 112 | for (;;) { 113 | t = t->right; 114 | if (t->left != NULL) { 115 | fptreef(shf, indent, "%;"); 116 | fptreef(shf, indent + INDENT, "then%N%T", 117 | t->left); 118 | } 119 | if (t->right == NULL || t->right->type != TELIF) 120 | break; 121 | t = t->right; 122 | fptreef(shf, indent, "%;"); 123 | /* 5 == strlen("elif ") */ 124 | fptreef(shf, indent + 5, "elif %T", t->left); 125 | } 126 | if (t->right != NULL) { 127 | fptreef(shf, indent, "%;"); 128 | fptreef(shf, indent + INDENT, "else%;%T", t->right); 129 | } 130 | fptreef(shf, indent, "%;fi "); 131 | break; 132 | case TWHILE: 133 | case TUNTIL: 134 | /* 6 == strlen("while"/"until") */ 135 | fptreef(shf, indent + 6, "%s %T", 136 | (t->type==TWHILE) ? "while" : "until", 137 | t->left); 138 | fptreef(shf, indent, "%;do"); 139 | fptreef(shf, indent + INDENT, "%;%T", t->right); 140 | fptreef(shf, indent, "%;done "); 141 | break; 142 | case TBRACE: 143 | fptreef(shf, indent + INDENT, "{%;%T", t->left); 144 | fptreef(shf, indent, "%;} "); 145 | break; 146 | case TCOPROC: 147 | fptreef(shf, indent, "%T|& ", t->left); 148 | break; 149 | case TASYNC: 150 | fptreef(shf, indent, "%T& ", t->left); 151 | break; 152 | case TFUNCT: 153 | fptreef(shf, indent, 154 | t->u.ksh_func ? "function %s %T" : "%s() %T", 155 | t->str, t->left); 156 | break; 157 | case TTIME: 158 | fptreef(shf, indent, "time %T", t->left); 159 | break; 160 | default: 161 | fptreef(shf, indent, ""); 162 | break; 163 | } 164 | if ((ioact = t->ioact) != NULL) { 165 | int need_nl = 0; 166 | 167 | while (*ioact != NULL) 168 | pioact(shf, indent, *ioact++); 169 | /* Print here documents after everything else... */ 170 | for (ioact = t->ioact; *ioact != NULL; ) { 171 | struct ioword *iop = *ioact++; 172 | 173 | /* heredoc is 0 when tracing (set -x) */ 174 | if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc) { 175 | tputc('\n', shf); 176 | shf_puts(iop->heredoc, shf); 177 | fptreef(shf, indent, "%s", 178 | evalstr(iop->delim, 0)); 179 | need_nl = 1; 180 | } 181 | } 182 | /* Last delimiter must be followed by a newline (this often 183 | * leads to an extra blank line, but its not worth worrying 184 | * about) 185 | */ 186 | if (need_nl) 187 | tputc('\n', shf); 188 | } 189 | } 190 | 191 | static void 192 | pioact(struct shf *shf, int indent, struct ioword *iop) 193 | { 194 | int flag = iop->flag; 195 | int type = flag & IOTYPE; 196 | int expected; 197 | 198 | expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 : 199 | (type == IOCAT || type == IOWRITE) ? 1 : 200 | (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit : 201 | iop->unit + 1; 202 | if (iop->unit != expected) 203 | tputc('0' + iop->unit, shf); 204 | 205 | switch (type) { 206 | case IOREAD: 207 | fptreef(shf, indent, "< "); 208 | break; 209 | case IOHERE: 210 | if (flag&IOSKIP) 211 | fptreef(shf, indent, "<<- "); 212 | else 213 | fptreef(shf, indent, "<< "); 214 | break; 215 | case IOCAT: 216 | fptreef(shf, indent, ">> "); 217 | break; 218 | case IOWRITE: 219 | if (flag&IOCLOB) 220 | fptreef(shf, indent, ">| "); 221 | else 222 | fptreef(shf, indent, "> "); 223 | break; 224 | case IORDWR: 225 | fptreef(shf, indent, "<> "); 226 | break; 227 | case IODUP: 228 | if (flag & IORDUP) 229 | fptreef(shf, indent, "<&"); 230 | else 231 | fptreef(shf, indent, ">&"); 232 | break; 233 | } 234 | /* name/delim are 0 when printing syntax errors */ 235 | if (type == IOHERE) { 236 | if (iop->delim) 237 | fptreef(shf, indent, "%S ", iop->delim); 238 | } else if (iop->name) 239 | fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ", 240 | iop->name); 241 | } 242 | 243 | 244 | /* 245 | * variants of fputc, fputs for ptreef and snptreef 246 | */ 247 | 248 | static void 249 | tputC(int c, struct shf *shf) 250 | { 251 | if ((c&0x60) == 0) { /* C0|C1 */ 252 | tputc((c&0x80) ? '$' : '^', shf); 253 | tputc(((c&0x7F)|0x40), shf); 254 | } else if ((c&0x7F) == 0x7F) { /* DEL */ 255 | tputc((c&0x80) ? '$' : '^', shf); 256 | tputc('?', shf); 257 | } else 258 | tputc(c, shf); 259 | } 260 | 261 | static void 262 | tputS(char *wp, struct shf *shf) 263 | { 264 | int c, quoted=0; 265 | 266 | /* problems: 267 | * `...` -> $(...) 268 | * 'foo' -> "foo" 269 | * could change encoding to: 270 | * OQUOTE ["'] ... CQUOTE ["'] 271 | * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) 272 | */ 273 | while (1) 274 | switch ((c = *wp++)) { 275 | case EOS: 276 | return; 277 | case CHAR: 278 | tputC(*wp++, shf); 279 | break; 280 | case QCHAR: 281 | c = *wp++; 282 | if (!quoted || (c == '"' || c == '`' || c == '$')) 283 | tputc('\\', shf); 284 | tputC(c, shf); 285 | break; 286 | case COMSUB: 287 | tputc('$', shf); 288 | tputc('(', shf); 289 | while (*wp != 0) 290 | tputC(*wp++, shf); 291 | tputc(')', shf); 292 | wp++; 293 | break; 294 | case EXPRSUB: 295 | tputc('$', shf); 296 | tputc('(', shf); 297 | tputc('(', shf); 298 | while (*wp != 0) 299 | tputC(*wp++, shf); 300 | tputc(')', shf); 301 | tputc(')', shf); 302 | wp++; 303 | break; 304 | case OQUOTE: 305 | quoted = 1; 306 | tputc('"', shf); 307 | break; 308 | case CQUOTE: 309 | quoted = 0; 310 | tputc('"', shf); 311 | break; 312 | case OSUBST: 313 | tputc('$', shf); 314 | if (*wp++ == '{') 315 | tputc('{', shf); 316 | while ((c = *wp++) != 0) 317 | tputC(c, shf); 318 | break; 319 | case CSUBST: 320 | if (*wp++ == '}') 321 | tputc('}', shf); 322 | break; 323 | case OPAT: 324 | tputc(*wp++, shf); 325 | tputc('(', shf); 326 | break; 327 | case SPAT: 328 | tputc('|', shf); 329 | break; 330 | case CPAT: 331 | tputc(')', shf); 332 | break; 333 | } 334 | } 335 | 336 | void 337 | fptreef(struct shf *shf, int indent, const char *fmt, ...) 338 | { 339 | va_list va; 340 | 341 | va_start(va, fmt); 342 | vfptreef(shf, indent, fmt, va); 343 | va_end(va); 344 | } 345 | 346 | char * 347 | snptreef(char *s, int n, const char *fmt, ...) 348 | { 349 | va_list va; 350 | struct shf shf; 351 | 352 | shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf); 353 | 354 | va_start(va, fmt); 355 | vfptreef(&shf, 0, fmt, va); 356 | va_end(va); 357 | 358 | return shf_sclose(&shf); /* null terminates */ 359 | } 360 | 361 | static void 362 | vfptreef(struct shf *shf, int indent, const char *fmt, va_list va) 363 | { 364 | int c; 365 | 366 | while ((c = *fmt++)) { 367 | if (c == '%') { 368 | int64_t n; 369 | char *p; 370 | int neg; 371 | 372 | switch ((c = *fmt++)) { 373 | case 'c': 374 | tputc(va_arg(va, int), shf); 375 | break; 376 | case 'd': /* decimal */ 377 | n = va_arg(va, int); 378 | neg = n < 0; 379 | p = u64ton(neg ? -n : n, 10); 380 | if (neg) 381 | *--p = '-'; 382 | while (*p) 383 | tputc(*p++, shf); 384 | break; 385 | case 's': 386 | p = va_arg(va, char *); 387 | while (*p) 388 | tputc(*p++, shf); 389 | break; 390 | case 'S': /* word */ 391 | p = va_arg(va, char *); 392 | tputS(p, shf); 393 | break; 394 | case 'u': /* unsigned decimal */ 395 | p = u64ton(va_arg(va, unsigned int), 10); 396 | while (*p) 397 | tputc(*p++, shf); 398 | break; 399 | case 'T': /* format tree */ 400 | ptree(va_arg(va, struct op *), indent, shf); 401 | break; 402 | case ';': /* newline or ; */ 403 | case 'N': /* newline or space */ 404 | if (shf->flags & SHF_STRING) { 405 | if (c == ';') 406 | tputc(';', shf); 407 | tputc(' ', shf); 408 | } else { 409 | int i; 410 | 411 | tputc('\n', shf); 412 | for (i = indent; i >= 8; i -= 8) 413 | tputc('\t', shf); 414 | for (; i > 0; --i) 415 | tputc(' ', shf); 416 | } 417 | break; 418 | case 'R': 419 | pioact(shf, indent, va_arg(va, struct ioword *)); 420 | break; 421 | default: 422 | tputc(c, shf); 423 | break; 424 | } 425 | } else 426 | tputc(c, shf); 427 | } 428 | } 429 | 430 | /* 431 | * copy tree (for function definition) 432 | */ 433 | 434 | struct op * 435 | tcopy(struct op *t, Area *ap) 436 | { 437 | struct op *r; 438 | char **tw, **rw; 439 | 440 | if (t == NULL) 441 | return NULL; 442 | 443 | r = alloc(sizeof(struct op), ap); 444 | 445 | r->type = t->type; 446 | r->u.evalflags = t->u.evalflags; 447 | 448 | r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap); 449 | 450 | if (t->vars == NULL) 451 | r->vars = NULL; 452 | else { 453 | for (tw = t->vars; *tw++ != NULL; ) 454 | ; 455 | rw = r->vars = areallocarray(NULL, tw - t->vars + 1, 456 | sizeof(*tw), ap); 457 | for (tw = t->vars; *tw != NULL; ) 458 | *rw++ = wdcopy(*tw++, ap); 459 | *rw = NULL; 460 | } 461 | 462 | if (t->args == NULL) 463 | r->args = NULL; 464 | else { 465 | for (tw = t->args; *tw++ != NULL; ) 466 | ; 467 | rw = r->args = areallocarray(NULL, tw - t->args + 1, 468 | sizeof(*tw), ap); 469 | for (tw = t->args; *tw != NULL; ) 470 | *rw++ = wdcopy(*tw++, ap); 471 | *rw = NULL; 472 | } 473 | 474 | r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap); 475 | 476 | r->left = tcopy(t->left, ap); 477 | r->right = tcopy(t->right, ap); 478 | r->lineno = t->lineno; 479 | 480 | return r; 481 | } 482 | 483 | char * 484 | wdcopy(const char *wp, Area *ap) 485 | { 486 | size_t len = wdscan(wp, EOS) - wp; 487 | return memcpy(alloc(len, ap), wp, len); 488 | } 489 | 490 | /* return the position of prefix c in wp plus 1 */ 491 | char * 492 | wdscan(const char *wp, int c) 493 | { 494 | int nest = 0; 495 | 496 | while (1) 497 | switch (*wp++) { 498 | case EOS: 499 | return (char *) wp; 500 | case CHAR: 501 | case QCHAR: 502 | wp++; 503 | break; 504 | case COMSUB: 505 | case EXPRSUB: 506 | while (*wp++ != 0) 507 | ; 508 | break; 509 | case OQUOTE: 510 | case CQUOTE: 511 | break; 512 | case OSUBST: 513 | nest++; 514 | while (*wp++ != '\0') 515 | ; 516 | break; 517 | case CSUBST: 518 | wp++; 519 | if (c == CSUBST && nest == 0) 520 | return (char *) wp; 521 | nest--; 522 | break; 523 | case OPAT: 524 | nest++; 525 | wp++; 526 | break; 527 | case SPAT: 528 | case CPAT: 529 | if (c == wp[-1] && nest == 0) 530 | return (char *) wp; 531 | if (wp[-1] == CPAT) 532 | nest--; 533 | break; 534 | default: 535 | internal_warningf( 536 | "%s: unknown char 0x%x (carrying on)", 537 | __func__, wp[-1]); 538 | } 539 | } 540 | 541 | /* return a copy of wp without any of the mark up characters and 542 | * with quote characters (" ' \) stripped. 543 | * (string is allocated from ATEMP) 544 | */ 545 | char * 546 | wdstrip(const char *wp) 547 | { 548 | struct shf shf; 549 | int c; 550 | 551 | shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf); 552 | 553 | /* problems: 554 | * `...` -> $(...) 555 | * x${foo:-"hi"} -> x${foo:-hi} 556 | * x${foo:-'hi'} -> x${foo:-hi} 557 | */ 558 | while (1) 559 | switch ((c = *wp++)) { 560 | case EOS: 561 | return shf_sclose(&shf); /* null terminates */ 562 | case CHAR: 563 | case QCHAR: 564 | shf_putchar(*wp++, &shf); 565 | break; 566 | case COMSUB: 567 | shf_putchar('$', &shf); 568 | shf_putchar('(', &shf); 569 | while (*wp != 0) 570 | shf_putchar(*wp++, &shf); 571 | shf_putchar(')', &shf); 572 | break; 573 | case EXPRSUB: 574 | shf_putchar('$', &shf); 575 | shf_putchar('(', &shf); 576 | shf_putchar('(', &shf); 577 | while (*wp != 0) 578 | shf_putchar(*wp++, &shf); 579 | shf_putchar(')', &shf); 580 | shf_putchar(')', &shf); 581 | break; 582 | case OQUOTE: 583 | break; 584 | case CQUOTE: 585 | break; 586 | case OSUBST: 587 | shf_putchar('$', &shf); 588 | if (*wp++ == '{') 589 | shf_putchar('{', &shf); 590 | while ((c = *wp++) != 0) 591 | shf_putchar(c, &shf); 592 | break; 593 | case CSUBST: 594 | if (*wp++ == '}') 595 | shf_putchar('}', &shf); 596 | break; 597 | case OPAT: 598 | shf_putchar(*wp++, &shf); 599 | shf_putchar('(', &shf); 600 | break; 601 | case SPAT: 602 | shf_putchar('|', &shf); 603 | break; 604 | case CPAT: 605 | shf_putchar(')', &shf); 606 | break; 607 | } 608 | } 609 | 610 | static struct ioword ** 611 | iocopy(struct ioword **iow, Area *ap) 612 | { 613 | struct ioword **ior; 614 | int i; 615 | 616 | for (ior = iow; *ior++ != NULL; ) 617 | ; 618 | ior = areallocarray(NULL, ior - iow + 1, sizeof(*ior), ap); 619 | 620 | for (i = 0; iow[i] != NULL; i++) { 621 | struct ioword *p, *q; 622 | 623 | p = iow[i]; 624 | q = alloc(sizeof(*p), ap); 625 | ior[i] = q; 626 | *q = *p; 627 | if (p->name != NULL) 628 | q->name = wdcopy(p->name, ap); 629 | if (p->delim != NULL) 630 | q->delim = wdcopy(p->delim, ap); 631 | if (p->heredoc != NULL) 632 | q->heredoc = str_save(p->heredoc, ap); 633 | } 634 | ior[i] = NULL; 635 | 636 | return ior; 637 | } 638 | 639 | /* 640 | * free tree (for function definition) 641 | */ 642 | 643 | void 644 | tfree(struct op *t, Area *ap) 645 | { 646 | char **w; 647 | 648 | if (t == NULL) 649 | return; 650 | 651 | afree(t->str, ap); 652 | 653 | if (t->vars != NULL) { 654 | for (w = t->vars; *w != NULL; w++) 655 | afree(*w, ap); 656 | afree(t->vars, ap); 657 | } 658 | 659 | if (t->args != NULL) { 660 | for (w = t->args; *w != NULL; w++) 661 | afree(*w, ap); 662 | afree(t->args, ap); 663 | } 664 | 665 | if (t->ioact != NULL) 666 | iofree(t->ioact, ap); 667 | 668 | tfree(t->left, ap); 669 | tfree(t->right, ap); 670 | 671 | afree(t, ap); 672 | } 673 | 674 | static void 675 | iofree(struct ioword **iow, Area *ap) 676 | { 677 | struct ioword **iop; 678 | struct ioword *p; 679 | 680 | for (iop = iow; (p = *iop++) != NULL; ) { 681 | afree(p->name, ap); 682 | afree(p->delim, ap); 683 | afree(p->heredoc, ap); 684 | afree(p, ap); 685 | } 686 | afree(iow, ap); 687 | } 688 | -------------------------------------------------------------------------------- /tree.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tree.h,v 1.12 2015/10/15 22:53:50 mmcc Exp $ */ 2 | 3 | /* 4 | * command trees for compile/execute 5 | */ 6 | 7 | /* $From: tree.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */ 8 | 9 | /* 10 | * Description of a command or an operation on commands. 11 | */ 12 | struct op { 13 | short type; /* operation type, see below */ 14 | union { /* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */ 15 | short evalflags; /* TCOM: arg expansion eval() flags */ 16 | short ksh_func; /* TFUNC: function x (vs x()) */ 17 | } u; 18 | char **args; /* arguments to a command */ 19 | char **vars; /* variable assignments */ 20 | struct ioword **ioact; /* IO actions (eg, < > >>) */ 21 | struct op *left, *right; /* descendents */ 22 | char *str; /* word for case; identifier for for, 23 | * select, and functions; 24 | * path to execute for TEXEC; 25 | * time hook for TCOM. 26 | */ 27 | int lineno; /* TCOM/TFUNC: LINENO for this */ 28 | }; 29 | 30 | /* Tree.type values */ 31 | #define TEOF 0 32 | #define TCOM 1 /* command */ 33 | #define TPAREN 2 /* (c-list) */ 34 | #define TPIPE 3 /* a | b */ 35 | #define TLIST 4 /* a ; b */ 36 | #define TOR 5 /* || */ 37 | #define TAND 6 /* && */ 38 | #define TBANG 7 /* ! */ 39 | #define TDBRACKET 8 /* [[ .. ]] */ 40 | #define TFOR 9 41 | #define TSELECT 10 42 | #define TCASE 11 43 | #define TIF 12 44 | #define TWHILE 13 45 | #define TUNTIL 14 46 | #define TELIF 15 47 | #define TPAT 16 /* pattern in case */ 48 | #define TBRACE 17 /* {c-list} */ 49 | #define TASYNC 18 /* c & */ 50 | #define TFUNCT 19 /* function name { command; } */ 51 | #define TTIME 20 /* time pipeline */ 52 | #define TEXEC 21 /* fork/exec eval'd TCOM */ 53 | #define TCOPROC 22 /* coprocess |& */ 54 | 55 | /* 56 | * prefix codes for words in command tree 57 | */ 58 | #define EOS 0 /* end of string */ 59 | #define CHAR 1 /* unquoted character */ 60 | #define QCHAR 2 /* quoted character */ 61 | #define COMSUB 3 /* $() substitution (0 terminated) */ 62 | #define EXPRSUB 4 /* $(()) substitution (0 terminated) */ 63 | #define OQUOTE 5 /* opening " or ' */ 64 | #define CQUOTE 6 /* closing " or ' */ 65 | #define OSUBST 7 /* opening ${ subst (followed by { or X) */ 66 | #define CSUBST 8 /* closing } of above (followed by } or X) */ 67 | #define OPAT 9 /* open pattern: *(, @(, etc. */ 68 | #define SPAT 10 /* separate pattern: | */ 69 | #define CPAT 11 /* close pattern: ) */ 70 | 71 | /* 72 | * IO redirection 73 | */ 74 | struct ioword { 75 | int unit; /* unit affected */ 76 | int flag; /* action (below) */ 77 | char *name; /* file name (unused if heredoc) */ 78 | char *delim; /* delimiter for <<,<<- */ 79 | char *heredoc;/* content of heredoc */ 80 | }; 81 | 82 | /* ioword.flag - type of redirection */ 83 | #define IOTYPE 0xF /* type: bits 0:3 */ 84 | #define IOREAD 0x1 /* < */ 85 | #define IOWRITE 0x2 /* > */ 86 | #define IORDWR 0x3 /* <>: todo */ 87 | #define IOHERE 0x4 /* << (here file) */ 88 | #define IOCAT 0x5 /* >> */ 89 | #define IODUP 0x6 /* <&/>& */ 90 | #define IOEVAL BIT(4) /* expand in << */ 91 | #define IOSKIP BIT(5) /* <<-, skip ^\t* */ 92 | #define IOCLOB BIT(6) /* >|, override -o noclobber */ 93 | #define IORDUP BIT(7) /* x<&y (as opposed to x>&y) */ 94 | #define IONAMEXP BIT(8) /* name has been expanded */ 95 | 96 | /* execute/exchild flags */ 97 | #define XEXEC BIT(0) /* execute without forking */ 98 | #define XFORK BIT(1) /* fork before executing */ 99 | #define XBGND BIT(2) /* command & */ 100 | #define XPIPEI BIT(3) /* input is pipe */ 101 | #define XPIPEO BIT(4) /* output is pipe */ 102 | #define XPIPE (XPIPEI|XPIPEO) /* member of pipe */ 103 | #define XXCOM BIT(5) /* `...` command */ 104 | #define XPCLOSE BIT(6) /* exchild: close close_fd in parent */ 105 | #define XCCLOSE BIT(7) /* exchild: close close_fd in child */ 106 | #define XERROK BIT(8) /* non-zero exit ok (for set -e) */ 107 | #define XCOPROC BIT(9) /* starting a co-process */ 108 | #define XTIME BIT(10) /* timing TCOM command */ 109 | 110 | /* 111 | * flags to control expansion of words (assumed by t->evalflags to fit 112 | * in a short) 113 | */ 114 | #define DOBLANK BIT(0) /* perform blank interpretation */ 115 | #define DOGLOB BIT(1) /* expand [?* */ 116 | #define DOPAT BIT(2) /* quote *?[ */ 117 | #define DOTILDE BIT(3) /* normal ~ expansion (first char) */ 118 | #define DONTRUNCOMMAND BIT(4) /* do not run $(command) things */ 119 | #define DOASNTILDE BIT(5) /* assignment ~ expansion (after =, :) */ 120 | #define DOBRACE_ BIT(6) /* used by expand(): do brace expansion */ 121 | #define DOMAGIC_ BIT(7) /* used by expand(): string contains MAGIC */ 122 | #define DOTEMP_ BIT(8) /* ditto : in word part of ${..[%#=?]..} */ 123 | #define DOVACHECK BIT(9) /* var assign check (for typeset, set, etc) */ 124 | #define DOMARKDIRS BIT(10) /* force markdirs behaviour */ 125 | 126 | /* 127 | * The arguments of [[ .. ]] expressions are kept in t->args[] and flags 128 | * indicating how the arguments have been munged are kept in t->vars[]. 129 | * The contents of t->vars[] are stuffed strings (so they can be treated 130 | * like all other t->vars[]) in which the second character is the one that 131 | * is examined. The DB_* defines are the values for these second characters. 132 | */ 133 | #define DB_NORM 1 /* normal argument */ 134 | #define DB_OR 2 /* || -> -o conversion */ 135 | #define DB_AND 3 /* && -> -a conversion */ 136 | #define DB_BE 4 /* an inserted -BE */ 137 | #define DB_PAT 5 /* a pattern argument */ 138 | 139 | void fptreef(struct shf *, int, const char *, ...); 140 | char * snptreef(char *, int, const char *, ...); 141 | struct op * tcopy(struct op *, Area *); 142 | char * wdcopy(const char *, Area *); 143 | char * wdscan(const char *, int); 144 | char * wdstrip(const char *); 145 | void tfree(struct op *, Area *); 146 | -------------------------------------------------------------------------------- /tty.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tty.c,v 1.19 2021/10/24 21:24:21 deraadt Exp $ */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "sh.h" 9 | #include "tty.h" 10 | 11 | int tty_fd = -1; /* dup'd tty file descriptor */ 12 | int tty_devtty; /* true if tty_fd is from /dev/tty */ 13 | struct termios tty_state; /* saved tty state */ 14 | 15 | void 16 | tty_close(void) 17 | { 18 | if (tty_fd >= 0) { 19 | close(tty_fd); 20 | tty_fd = -1; 21 | } 22 | } 23 | 24 | /* Initialize tty_fd. Used for saving/resetting tty modes upon 25 | * foreground job completion and for setting up tty process group. 26 | */ 27 | void 28 | tty_init(int init_ttystate) 29 | { 30 | int do_close = 1; 31 | int tfd; 32 | 33 | tty_close(); 34 | tty_devtty = 1; 35 | 36 | tfd = open("/dev/tty", O_RDWR); 37 | if (tfd == -1) { 38 | tty_devtty = 0; 39 | warningf(false, "No controlling tty (open /dev/tty: %s)", 40 | strerror(errno)); 41 | 42 | do_close = 0; 43 | if (isatty(0)) 44 | tfd = 0; 45 | else if (isatty(2)) 46 | tfd = 2; 47 | else { 48 | warningf(false, "Can't find tty file descriptor"); 49 | return; 50 | } 51 | } 52 | #ifdef F_DUPFD_CLOEXEC 53 | if ((tty_fd = fcntl(tfd, F_DUPFD_CLOEXEC, FDBASE)) == -1) { 54 | warningf(false, "%s: dup of tty fd failed: %s", 55 | __func__, strerror(errno)); 56 | #else 57 | if ((tty_fd = fcntl(tfd, F_DUPFD, FDBASE)) == -1) { 58 | warningf(false, "%s: dup of tty fd failed: %s", 59 | __func__, strerror(errno)); 60 | } else if (fcntl(tty_fd, F_SETFD, FD_CLOEXEC) == -1) { 61 | warningf(false, "%s: set tty fd close-on-exec flag failed: %s", 62 | __func__, strerror(errno)); 63 | close(tty_fd); 64 | tty_fd = -1; 65 | #endif 66 | } else if (init_ttystate) 67 | tcgetattr(tty_fd, &tty_state); 68 | if (do_close) 69 | close(tfd); 70 | } 71 | -------------------------------------------------------------------------------- /tty.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tty.h,v 1.6 2015/09/25 11:58:14 nicm Exp $ */ 2 | 3 | /* 4 | tty.h -- centralized definitions for a variety of terminal interfaces 5 | 6 | created by DPK, Oct. 1986 7 | 8 | Rearranged to work with autoconf, added TTY_state, get_tty/set_tty 9 | Michael Rendell, May '94 10 | 11 | last edit: 30-Jul-1987 D A Gwyn 12 | */ 13 | 14 | #include 15 | 16 | extern int tty_fd; /* dup'd tty file descriptor */ 17 | extern int tty_devtty; /* true if tty_fd is from /dev/tty */ 18 | extern struct termios tty_state; /* saved tty state */ 19 | 20 | extern void tty_init(int); 21 | extern void tty_close(void); 22 | -------------------------------------------------------------------------------- /unvis.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: unvis.c,v 1.17 2015/09/13 11:32:51 guenther Exp $ */ 2 | /*- 3 | * Copyright (c) 1989, 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "pconfig.h" 32 | 33 | #ifndef HAVE_STRUNVIS 34 | 35 | #include 36 | #include 37 | 38 | #include "vis.h" 39 | 40 | /* 41 | * decode driven by state machine 42 | */ 43 | #define S_GROUND 0 /* haven't seen escape char */ 44 | #define S_START 1 /* start decoding special sequence */ 45 | #define S_META 2 /* metachar started (M) */ 46 | #define S_META1 3 /* metachar more, regular char (-) */ 47 | #define S_CTRL 4 /* control char started (^) */ 48 | #define S_OCTAL2 5 /* octal digit 2 */ 49 | #define S_OCTAL3 6 /* octal digit 3 */ 50 | 51 | #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') 52 | 53 | /* 54 | * unvis - decode characters previously encoded by vis 55 | */ 56 | static int 57 | eunvis(char *cp, char c, int *astate, int flag) 58 | { 59 | 60 | if (flag & UNVIS_END) { 61 | if (*astate == S_OCTAL2 || *astate == S_OCTAL3) { 62 | *astate = S_GROUND; 63 | return (UNVIS_VALID); 64 | } 65 | return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); 66 | } 67 | 68 | switch (*astate) { 69 | 70 | case S_GROUND: 71 | *cp = 0; 72 | if (c == '\\') { 73 | *astate = S_START; 74 | return (0); 75 | } 76 | *cp = c; 77 | return (UNVIS_VALID); 78 | 79 | case S_START: 80 | switch(c) { 81 | case '-': 82 | *cp = 0; 83 | *astate = S_GROUND; 84 | return (0); 85 | case '\\': 86 | case '"': 87 | *cp = c; 88 | *astate = S_GROUND; 89 | return (UNVIS_VALID); 90 | case '0': case '1': case '2': case '3': 91 | case '4': case '5': case '6': case '7': 92 | *cp = (c - '0'); 93 | *astate = S_OCTAL2; 94 | return (0); 95 | case 'M': 96 | *cp = (char) 0200; 97 | *astate = S_META; 98 | return (0); 99 | case '^': 100 | *astate = S_CTRL; 101 | return (0); 102 | case 'n': 103 | *cp = '\n'; 104 | *astate = S_GROUND; 105 | return (UNVIS_VALID); 106 | case 'r': 107 | *cp = '\r'; 108 | *astate = S_GROUND; 109 | return (UNVIS_VALID); 110 | case 'b': 111 | *cp = '\b'; 112 | *astate = S_GROUND; 113 | return (UNVIS_VALID); 114 | case 'a': 115 | *cp = '\007'; 116 | *astate = S_GROUND; 117 | return (UNVIS_VALID); 118 | case 'v': 119 | *cp = '\v'; 120 | *astate = S_GROUND; 121 | return (UNVIS_VALID); 122 | case 't': 123 | *cp = '\t'; 124 | *astate = S_GROUND; 125 | return (UNVIS_VALID); 126 | case 'f': 127 | *cp = '\f'; 128 | *astate = S_GROUND; 129 | return (UNVIS_VALID); 130 | case 's': 131 | *cp = ' '; 132 | *astate = S_GROUND; 133 | return (UNVIS_VALID); 134 | case 'E': 135 | *cp = '\033'; 136 | *astate = S_GROUND; 137 | return (UNVIS_VALID); 138 | case '\n': 139 | /* 140 | * hidden newline 141 | */ 142 | *astate = S_GROUND; 143 | return (UNVIS_NOCHAR); 144 | case '$': 145 | /* 146 | * hidden marker 147 | */ 148 | *astate = S_GROUND; 149 | return (UNVIS_NOCHAR); 150 | } 151 | *astate = S_GROUND; 152 | return (UNVIS_SYNBAD); 153 | 154 | case S_META: 155 | if (c == '-') 156 | *astate = S_META1; 157 | else if (c == '^') 158 | *astate = S_CTRL; 159 | else { 160 | *astate = S_GROUND; 161 | return (UNVIS_SYNBAD); 162 | } 163 | return (0); 164 | 165 | case S_META1: 166 | *astate = S_GROUND; 167 | *cp |= c; 168 | return (UNVIS_VALID); 169 | 170 | case S_CTRL: 171 | if (c == '?') 172 | *cp |= 0177; 173 | else 174 | *cp |= c & 037; 175 | *astate = S_GROUND; 176 | return (UNVIS_VALID); 177 | 178 | case S_OCTAL2: /* second possible octal digit */ 179 | if (isoctal(c)) { 180 | /* 181 | * yes - and maybe a third 182 | */ 183 | *cp = (*cp << 3) + (c - '0'); 184 | *astate = S_OCTAL3; 185 | return (0); 186 | } 187 | /* 188 | * no - done with current sequence, push back passed char 189 | */ 190 | *astate = S_GROUND; 191 | return (UNVIS_VALIDPUSH); 192 | 193 | case S_OCTAL3: /* third possible octal digit */ 194 | *astate = S_GROUND; 195 | if (isoctal(c)) { 196 | *cp = (*cp << 3) + (c - '0'); 197 | return (UNVIS_VALID); 198 | } 199 | /* 200 | * we were done, push back passed char 201 | */ 202 | return (UNVIS_VALIDPUSH); 203 | 204 | default: 205 | /* 206 | * decoder in unknown state - (probably uninitialized) 207 | */ 208 | *astate = S_GROUND; 209 | return (UNVIS_SYNBAD); 210 | } 211 | } 212 | 213 | /* 214 | * strunvis - decode src into dst 215 | * 216 | * Number of chars decoded into dst is returned, -1 on error. 217 | * Dst is null terminated. 218 | */ 219 | 220 | int 221 | strunvis(char *dst, const char *src) 222 | { 223 | char c; 224 | char *start = dst; 225 | int state = 0; 226 | 227 | while ((c = *src++)) { 228 | again: 229 | switch (eunvis(dst, c, &state, 0)) { 230 | case UNVIS_VALID: 231 | dst++; 232 | break; 233 | case UNVIS_VALIDPUSH: 234 | dst++; 235 | goto again; 236 | case 0: 237 | case UNVIS_NOCHAR: 238 | break; 239 | default: 240 | *dst = '\0'; 241 | return (-1); 242 | } 243 | } 244 | if (eunvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) 245 | dst++; 246 | *dst = '\0'; 247 | return (dst - start); 248 | } 249 | 250 | #endif /* !HAVE_STRUNVIS */ 251 | -------------------------------------------------------------------------------- /version.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: version.c,v 1.12 1999/07/14 13:37:24 millert Exp $ */ 2 | 3 | /* 4 | * value of $KSH_VERSION (or $SH_VERSION) 5 | */ 6 | 7 | #include "sh.h" 8 | 9 | const char ksh_version [] = 10 | "@(#)PD KSH v5.2.14 99/07/13.2"; 11 | -------------------------------------------------------------------------------- /vis.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: vis.c,v 1.25 2015/09/13 11:32:51 guenther Exp $ */ 2 | /*- 3 | * Copyright (c) 1989, 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "portable.h" 32 | 33 | #ifndef HAVE_STRAVIS 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "vis.h" 43 | 44 | #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') 45 | #define isvisible(c,flag) \ 46 | (((c) == '\\' || (flag & VIS_ALL) == 0) && \ 47 | (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ 48 | (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ 49 | (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ 50 | ((flag & VIS_SP) == 0 && (c) == ' ') || \ 51 | ((flag & VIS_TAB) == 0 && (c) == '\t') || \ 52 | ((flag & VIS_NL) == 0 && (c) == '\n') || \ 53 | ((flag & VIS_SAFE) && ((c) == '\b' || \ 54 | (c) == '\007' || (c) == '\r' || \ 55 | isgraph((u_char)(c)))))) 56 | 57 | /* 58 | * vis - visually encode characters 59 | */ 60 | static char * 61 | evis(char *dst, int c, int flag, int nextc) 62 | { 63 | if (isvisible(c, flag)) { 64 | if ((c == '"' && (flag & VIS_DQ) != 0) || 65 | (c == '\\' && (flag & VIS_NOSLASH) == 0)) 66 | *dst++ = '\\'; 67 | *dst++ = c; 68 | *dst = '\0'; 69 | return (dst); 70 | } 71 | 72 | if (flag & VIS_CSTYLE) { 73 | switch(c) { 74 | case '\n': 75 | *dst++ = '\\'; 76 | *dst++ = 'n'; 77 | goto done; 78 | case '\r': 79 | *dst++ = '\\'; 80 | *dst++ = 'r'; 81 | goto done; 82 | case '\b': 83 | *dst++ = '\\'; 84 | *dst++ = 'b'; 85 | goto done; 86 | case '\a': 87 | *dst++ = '\\'; 88 | *dst++ = 'a'; 89 | goto done; 90 | case '\v': 91 | *dst++ = '\\'; 92 | *dst++ = 'v'; 93 | goto done; 94 | case '\t': 95 | *dst++ = '\\'; 96 | *dst++ = 't'; 97 | goto done; 98 | case '\f': 99 | *dst++ = '\\'; 100 | *dst++ = 'f'; 101 | goto done; 102 | case ' ': 103 | *dst++ = '\\'; 104 | *dst++ = 's'; 105 | goto done; 106 | case '\0': 107 | *dst++ = '\\'; 108 | *dst++ = '0'; 109 | if (isoctal(nextc)) { 110 | *dst++ = '0'; 111 | *dst++ = '0'; 112 | } 113 | goto done; 114 | } 115 | } 116 | if (((c & 0177) == ' ') || (flag & VIS_OCTAL) || 117 | ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) { 118 | *dst++ = '\\'; 119 | *dst++ = ((u_char)c >> 6 & 07) + '0'; 120 | *dst++ = ((u_char)c >> 3 & 07) + '0'; 121 | *dst++ = ((u_char)c & 07) + '0'; 122 | goto done; 123 | } 124 | if ((flag & VIS_NOSLASH) == 0) 125 | *dst++ = '\\'; 126 | if (c & 0200) { 127 | c &= 0177; 128 | *dst++ = 'M'; 129 | } 130 | if (iscntrl((u_char)c)) { 131 | *dst++ = '^'; 132 | if (c == 0177) 133 | *dst++ = '?'; 134 | else 135 | *dst++ = c + '@'; 136 | } else { 137 | *dst++ = '-'; 138 | *dst++ = c; 139 | } 140 | done: 141 | *dst = '\0'; 142 | return (dst); 143 | } 144 | 145 | /* 146 | * strvis, strnvis, strvisx - visually encode characters from src into dst 147 | * 148 | * Dst must be 4 times the size of src to account for possible 149 | * expansion. The length of dst, not including the trailing NULL, 150 | * is returned. 151 | * 152 | * Strnvis will write no more than siz-1 bytes (and will NULL terminate). 153 | * The number of bytes needed to fully encode the string is returned. 154 | * 155 | * Strvisx encodes exactly len bytes from src into dst. 156 | * This is useful for encoding a block of data. 157 | */ 158 | static int 159 | estrvis(char *dst, const char *src, int flag) 160 | { 161 | char c; 162 | char *start; 163 | 164 | for (start = dst; (c = *src);) 165 | dst = evis(dst, c, flag, *++src); 166 | *dst = '\0'; 167 | return (dst - start); 168 | } 169 | 170 | int 171 | stravis(char **outp, const char *src, int flag) 172 | { 173 | char *buf; 174 | int len, serrno; 175 | 176 | buf = reallocarray(NULL, 4, strlen(src) + 1); 177 | if (buf == NULL) 178 | return -1; 179 | len = estrvis(buf, src, flag); 180 | serrno = errno; 181 | *outp = realloc(buf, len + 1); 182 | if (*outp == NULL) { 183 | *outp = buf; 184 | errno = serrno; 185 | } 186 | return (len); 187 | } 188 | 189 | #endif /* !HAVE_STRAVIS */ 190 | -------------------------------------------------------------------------------- /vis.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ 2 | /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ 3 | 4 | /*- 5 | * Copyright (c) 1990 The Regents of the University of California. 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the University nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | * 32 | * @(#)vis.h 5.9 (Berkeley) 4/3/91 33 | */ 34 | 35 | #ifndef _VIS_H_ 36 | #define _VIS_H_ 37 | 38 | /* 39 | * to select alternate encoding format 40 | */ 41 | #define VIS_OCTAL 0x01 /* use octal \ddd format */ 42 | #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ 43 | 44 | /* 45 | * to alter set of characters encoded (default is to encode all 46 | * non-graphic except space, tab, and newline). 47 | */ 48 | #define VIS_SP 0x04 /* also encode space */ 49 | #define VIS_TAB 0x08 /* also encode tab */ 50 | #define VIS_NL 0x10 /* also encode newline */ 51 | #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) 52 | #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ 53 | #define VIS_DQ 0x200 /* backslash-escape double quotes */ 54 | #define VIS_ALL 0x400 /* encode all characters */ 55 | 56 | /* 57 | * other 58 | */ 59 | #define VIS_NOSLASH 0x40 /* inhibit printing '\' */ 60 | #define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ 61 | 62 | /* 63 | * unvis return codes 64 | */ 65 | #define UNVIS_VALID 1 /* character valid */ 66 | #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ 67 | #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ 68 | #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ 69 | #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ 70 | 71 | /* 72 | * unvis flags 73 | */ 74 | #define UNVIS_END 1 /* no more characters */ 75 | 76 | char *vis(char *, int, int, int); 77 | int strvis(char *, const char *, int); 78 | int stravis(char **, const char *, int); 79 | int strnvis(char *, const char *, size_t, int); 80 | int strvisx(char *, const char *, size_t, int); 81 | int strunvis(char *, const char *); 82 | int unvis(char *, char, int *, int); 83 | ssize_t strnunvis(char *, const char *, size_t); 84 | 85 | #endif /* !_VIS_H_ */ 86 | --------------------------------------------------------------------------------