diff options
Diffstat (limited to 'bin')
81 files changed, 2986 insertions, 1195 deletions
diff --git a/bin/ksh/BUG-REPORTS b/bin/ksh/BUG-REPORTS index 2b98b829d48..f889fe108a8 100644 --- a/bin/ksh/BUG-REPORTS +++ b/bin/ksh/BUG-REPORTS @@ -1,4 +1,4 @@ -$OpenBSD: BUG-REPORTS,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: BUG-REPORTS,v 1.2 1996/08/19 20:08:39 downsj Exp $ List of reported problems (problems reported and fixed before 5.0.4 not included). Unresolved problems (may or may not still exist) marked by *, @@ -23,7 +23,7 @@ problems believed to be fixed marked by x. * pdksh 5.0.8, - (reported by Sean Hogan): attempting file name completion on a word with a single backquote causes a "no closing quote" error and - looses the partially entered command (vi mode). + loses the partially entered command (vi mode). [see Mail.2:48] * pdksh 5.0.10, - (reported by Andrew Moore): no overflow checking is done @@ -47,7 +47,7 @@ problems believed to be fixed marked by x. shell (at&t ksh will). [see Mail.7:32,Mail.9:65] -* pdksh 5.1.3, - (reported by Gabor Zahemszky): emacs doesn't have \ as quote +* pdksh 5.1.3, - (reported by Gabor Zahemszky): emacs/vi doesn't have \ as quote character. [see Mail.7:87] @@ -65,7 +65,6 @@ problems believed to be fixed marked by x. * enhancements that haven't been merged yet - Mail.6:36-39,78,84 recursive function diffs - - Mail.7:7 partial sigwinch diffs * pdksh 5.2.3, - (reported by Mike Jetzer): in vi, <ESC>= on word with ~ but no /, beeps (or prints final path comonent?). @@ -84,11 +83,24 @@ problems believed to be fixed marked by x. multiline commands - should go to start of command. [see Mail.XXX:XXX] -* pdksh 5.2.5, - (reported by Adrian M): configuration on Linux FT fails. +* pdksh 5.2.5, - (reported by Adrian Marsh): configuration on Linux FT fails. Caused by configure script using -g flag - gcc passes -lg to ld, ld fails to find -lg (autoconf or Linux FT bug). [see Mail.XXX:XXX] +* pdksh 5.2.7, - (reported by Adrian Marsh): typeset -L20u xxx is ok is ksh88 + but not in pdksh. + [see Mail.XXX:XXX] + +* pdksh 5.2.7, - (reported by Gabor Zahemszky): TMOUT doesn't effect + select and read operations. + [see Mail.XXX:XXX] + +* pdksh 5.2.7, - (reported by Gabor Zahemszky): exec 3<&p doesn't close + shells copy of the coprocess file desc. + [see Mail.XXX:XXX] + + --------------------- put fixed problems below this line --------------------- x pdksh 5.0.3, NetBSD 0.9a (reported by Simon J. Gerraty): pipelines @@ -926,3 +938,91 @@ x pdksh 5.2.5, - (reported by Gabor Zahemszky): vi: # removes comment and executes if command already commented. [see Mail.XXX:XXX] [fixed in 5.2.6: added vi.c(do_comment)] + +x pdksh 5.2.7, - (reported by Adrian Marsh): test doesn't have == operator. + [see Mail.XXX:XXX] + [fixed in 5.2.8: added == to c_test.c operator table] + +x pdksh 5.2.7, - (reported by Mike Jetzer): pdksh sets/exports COLUMNS/LINES + which causes applications not to respond to window size changes. + [see Mail.XXX:XXX] + [fixed in 5.2.8: COLUMNS/LINES no longer exported automatically] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): getopts sets OPTIND differently + that at&t ksh when a bad option is given. + [see Mail.XXX:XXX] + [fixed in 5.2.8: OPTIND not set if option was bad - fragile fix - may go away] + +x pdksh 5.2.7, - (reported with fix by Marc Olzheim): sh version shouldn't + have mail check stuff, macro expansion in PS[0-9]. + [fixed in 5.2.8: added lots of ifdefs] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): sub commands in PS1 cause + a warning message to be printed. + [see Mail.XXX:XXX] + [fixed in 5.2.8: lex.c(set_prompt) - don't print the warning message] + +x pdksh 5.2.7, - (reported by Tom Watson): some environment variables + (eg, PATH) are converted to uppercase on 16-bit int machine. + [see Mail.XXX:XXX] + [fixed in 5.2.8: struct tbl.flag (32 bit thing) was being treated as an + int in some places] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): unset always returns 0 - should + return 1 if variable/function is not set. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed c_sh.c(c_unset)] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): select should only print the + menu the first time, if REPLAY is empty, or if a blank line is entered. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed up exec.c(execute,do_selectargs)] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): shell reports "cannot execute" + error if file exists, even if . not in path. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed up exec.c(comexec)] + +x pdksh 5.1.3, - (reported with partial fix by ra@rhi.hi.is): shell doesn't + listen to sigwinch. + [see Mail.7:7 and related] + [fixed in 5.2.8: changed edit.c(x_init) to catch sigwinch] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): typeset doesn't report + variables that have attributes (like export) but no values. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed up c_ksh.c(c_typeset)] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): error message printed as + a result of "set -o nounset" is different from at7t ksh. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed error messges in eval.c] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): vi/emacs: when listing + command/file completions, should go back at most one space. Also, should + allow completions on zero length names. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed edit.c(x_locate_word); now allows zero-length + file completions (but not command)] + +* pdksh 5.2.7, - (reported by Gabor Zahemszky): emacs: <esc># doesn't do + the comment thing. + [see Mail.XXX:XXX] + [fixed in 5.2.8: added emacs.c(x_comment) et al.] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): arithmatic expressions + containing variables not expanded as in at&t ksh. eg, "x=1+2, let y=x" + fails. + [see Mail.XXX:XXX] + [fixed in 5.2.8: added evaling/INEXPREVAL/ET_RECURSIVE code to expr.c] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): unsetting the 0th element + of an array kills the whole array. + [see Mail.XXX:XXX] + [fixed in 5.2.8: var.c(unset) - allow ARRAY to be preserved] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): unsetting a function while + it is being executed can result in core dump. + [see Mail.XXX:XXX] + [fixed in 5.2.8: table.c(texpand) - dont free if FINUSE is set] + diff --git a/bin/ksh/CONTRIBUTORS b/bin/ksh/CONTRIBUTORS index 3b549ca6a3c..34a1e6e6caa 100644 --- a/bin/ksh/CONTRIBUTORS +++ b/bin/ksh/CONTRIBUTORS @@ -1,4 +1,4 @@ -$OpenBSD: CONTRIBUTORS,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: CONTRIBUTORS,v 1.2 1996/08/19 20:08:40 downsj Exp $ This is a partial history of this shell gleened from old change logs and readmes (most of which are still in the misc directory) and the source @@ -87,7 +87,11 @@ Other contributors: * Paul Borman (<prb@bsdi.com>): j_exit: send HUP, then CONT; HUP fg process. * DaviD W. Sanderson (<dws@ssec.wisc.edu>): patches to allow { .. } instead of in .. esac in case statements. + * ? (<ra@rhi.hi.is>): partial patches to handle SIGWINCH for command line + editing. * Jason Tyler (<jason@nc.bhpese.oz.au>): fixes for bugs in fc. * Stefan Dalibor (<Stefan.Dalibor@informatik.uni-erlangen.de>): fix for COLUMNS never being set in x_init(). * Arnon Kanfi (<arnon@gilly.datatools.com>): fix for prompt. + * Marc Olzheim (<marcolz@stack.urc.tue.nl>): patches to ifdef KSH the + mail check code and aliases. diff --git a/bin/ksh/ChangeLog b/bin/ksh/ChangeLog index 430eaebb5dc..a5bab4fa1f0 100644 --- a/bin/ksh/ChangeLog +++ b/bin/ksh/ChangeLog @@ -1,8 +1,248 @@ -$OpenBSD: ChangeLog,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: ChangeLog,v 1.2 1996/08/19 20:08:41 downsj Exp $ + +Mon Aug 19 14:26:08 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * made pdksh-5.2.8 distribution + +Mon Aug 19 11:38:16 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * table.c(texpand): don't free entry if FINUSE is set. + + * var.c(unset): preserve ARRAY and DEFINED if unsetting foo[0]. + +Thu Aug 15 15:08:52 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * jobs.c(sm_sigchld,sm_default): moved to sh.h. + * sh.h(Coproc_id, struct coproc): new typedef; added njobs and + id fields to struct coproc. + * exec.c(execute): case TCOPROC: re-did coprocess stuff to use + njobs/coprocess id. + * jobs.c(struct Job): added coproc_id field. + * jobs.c(exchild): initialize coproc_id to 0; set job coproc_id + and increment coproc.njobs in parent. + * jobs.c(checkjob): check coproc_id and close co-process input/output + if needed. + + * exec.c(iosetup): only play with coprocess fds if this is an + empty exec. + * c_sh.c(c_read): commented out coproc_readw_close() call and eof call. + * c_ksh.c(c_print): commented out closing coprocess fd on EPIPE. + + * jobs.c(exchild): in parent, last part of job: use orig_flags (not + flags) when checking XCOPROC. + +Thu Aug 15 15:00:42 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * io.c(get_coproc_fd,cleanup_coproc): renamed to coproc_getfd() and + coproc_cleanup(), respecitively; changed all calls. + +Tue Aug 13 16:56:59 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * expr.c(O_COMMA,P_COMMA): new enums. + * expr.c(evalexpr): added case for O_COMMA. + +Tue Aug 13 15:18:28 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * expr.c(do_ppmm): new function to handle ++/--. + * expr.c(evalexpr): call do_ppmm() in P_PRIMARY code. + * expr.c(LAST_BINOP): deleted. + * expr.c(IS_BINOP): new define. + * expr.c(evalexpr): use IS_BINOP. + * expr.c(O_PLUSPLUS,O_MINUSMUNS,opinfo[]): new enums; updated opinfo + * expr.c(ET_LVALUE,ET_RDONLY): new enums. + + * expr.c(token): var code: don't increment cp in iter part of for loop, + do it in body; don't correct for off by 1 in array or !noasign code. + * table.h(EXPRLVALUE): new define. + * expr.c(token): var code: set EXPRLVALUE flag if noassign. + * expr.c(intvar): copy temp var if EXPRLVALUE set. + * expr.c(assign_check): new function. + * expr.c(evalexpr): if assign-op, call assign_check(). + +Tue Aug 13 11:02:32 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * vi.c(do_comment),edit.c(x_do_comment): made do_comment generic, + renamed and moved to edit.c; changed all calls. + * emacs.c(x_ftab[]): added x_comment. + * emacs.c(x_defbindings[]): added XFUNC_comment as <esc>#. + * emacs.c(x_comment): new function. + +Mon Aug 12 16:13:36 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * expr.c(ET_BADVAR): deleted. + * expr.c(ET_RECURSIVE, struct expr.evaling),table.h(EXPRNEVAL): added. + * expr.c(v_evaluate): if curstate.evaling set, clear EXPRINEVAL. + * expr.c(evalerr): added ET_RECURSIVE case, removed ET_BADVAR case. + * expr.c(intvar): do recursion check, call v_evaluate() on value. + +Mon Aug 12 14:25:23 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * io.c(coproc_read_close): call coproc_readw_close() instead of + duplicating code. + +Mon Aug 12 11:21:39 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * edit.c(x_locate_word): changed to allow at most 1 leading blank + before the word. + * edit.c(x_file_glob,x_command_glob,add_glob): allow zero length word. + * edit.c(x_cf_glob): allow zero length globs on when doing file + completion. + + * edit.c(x_complete_word): #if 0 - it isn't used... + * edit.c(x_file_glob,x_command_glob,x_locate_word): made static. + + * eval.c(varsub): changed FNOUNSET error from "unset variable" + to "parameter no set", ala at&t ksh. + + * c_ksh.c(c_typeset): print variables that aren't set (just + leave out the =...). + +Mon Aug 12 11:03:22 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * exec.c(findfunc): removed redundent DEFINED check after tsearch(). + +Fri Aug 9 22:16:21 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * jobs.c(j_change): when turning off FMONITOR and not FTALKING, + changed SS_RESTORE_CURR to SS_RESTORE_ORIG. + + * edit.c(x_sigwinch): new function. + * edit.c(x_init): set up signal handler for SIGWINCH; moved + code to get window size into x_sigwinch(); call x_sigwinch(). + * emacs.c(xx_cols): new variable. + * emacs.c(x_init): set xx_cols_to x_cols; change all uses of x_cols + to xx_cols. + * vi.c(display): when displaying morec, changed x_cols-2 to + pwidth+winwidth+1. + +Fri Aug 9 12:49:00 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * table.h(FKSH): new define. + * tree.h(struct op): put evalflags into new union u, added ksh_func + to union; changed all uses of evalflags. + * syn.c(function_body): set u.ksh_func. + * exec.c(execute): changed define() arg to t (was t->left). + * exec.c(define): copy t->left (was t); set FKSH in flag if is + a ksh function. + * exec.c(comexec): don't keep assignments for x() style functions. + * exec.c(comexec: case CFUNC: set kshname ($0) for ksh style functions + only (was FPOSIX). + + * exec.c(execute): case TAND/TOR: pass XERROK on when executing right + hand side. + + * jobs.c(exchild): deleted redundant code to set j->flags + (near new_job() call). + + * sh.h(ksh_tmout),main.c(alarm_init),trap.c(alarm_init,alarm_catcher): + ifdef'd KSH. + + * sh.h(SS_SHTRAP,Trap.shtrap): added. + * trap.c(trapsig): if shtrap is non-zero, call it. + * trap.c(setsig): set shtrap if SS_SHTRAP set. + * jobs.c(j_init),trap.c(alarm_init): pass SS_SHTRAP. + * jobs.c(j_sigchld),trap.c(alarm_catcher): don't call trapsig(). + * trap.c(Sigact_alarm): removed. + +Thu Aug 8 15:57:14 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * exec.c(comexec): case CEXEC: print cannot execute error only + if / in pathname; also, set exit code to 126. + + * exec.c(do_selectargs): added print_menu arg; only print + menu if this is set, or if REPLY is null; removed "while isspace" + loop. + * exec.c(execute): case TSELECT: call do_selectargs with print_menu + of TRUE on first call only. + + * exec.c(define): added was_set variable and logic. + * c_sh.c(c_unset): return 1 if variable/function to be unset wasn't + set to begin with. + +Wed Jul 31 10:33:00 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * sh.h(Tflag): new type. + * sh.h(builtin_flag): changed type to Tflag. + * table.h(struct tbl): changed type of flag field to Tflag. + * c_ksh.c(typeset): changed type of flag, fset, fclr to Tflag. + * c_ksh.c(c_alias): changed type of xflag to Tflag. + * exec.c(comexec): changed type of old_inuse to Tflag. + * exec.c(builtin): changed type of flag to Tflag. + * var.c(typeset): changed set, clr args to Tflag; convert second + arg of call to local() to boolean. + +Wed Jul 31 10:26:25 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * sh.h(C_QUOTE): new define. + * sh.h(ctypes[]),misc.c(ctypes[]): changed from char to short. + * misc.c(initctypes): set C_QUOTE bits in ctypes[]. + * misc.c(print_value_quoted): use C_QUOTE. + +Mon Jul 29 11:38:36 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * lex.c(set_prompt): don't print warning message if setjmp returns + non-zero. + +Fri Jul 26 10:16:27 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * lex.c(set_prompt): don't do ! and parameter expansion if !KSH. + + * table.h(V_MAIL,V_MAILPATH,V_MAILCHECK): ifdef KSH. + * var.c(initvar,setspec,unsetspec): ifdef KSH use of MAIL stuff. + * mail.c: ifdef KSH whole file. + * main.c(shell): ifdef KSH call to mcheck(). + * main.c(initcoms[]): ifdef KSH the MAILCHECK=600. + (based on patches from Marc Olzheim). + + * exec.c(PS4_SUBSTITUTE): new macro. + * exec.c(execute, comexec, iosetup): use PS4_SUBSTITUTE. + +Thu Jul 25 17:19:17 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * sh.h(F_VIESCCOMPLETE): new define. + * misc.c(options[]): added vi-esccomplete. + * vi.c(classify[]): make ^[ a repeatable command. + * vi.c(vi_cmd): check F_VIESCCOMPLETE for ^[. + +Mon Jul 22 16:54:38 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * c_ksh.c(c_getopts): return if variable is readonly; don't change + OPTIND if option is bad (fragile). + * c_sh.c(c_brkcont): use ksh_getopt(); changed error message if + n <= 0. + * c_sh.c(c_dot,c_eval,c_exitreturn): use ksh_getopt(). + * misc.c(ksh_getopt): print `unknown option' instead of `bad option'. + +Mon Jul 22 16:08:40 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * edit.c(x_init): do NOT export COLUMNS/LINES - causes more problems + than it fixes. + +Mon Jul 22 15:49:35 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * syn.c(get_command): fixed test for '< foo (command)' so it + works. + +Fri Jun 21 09:57:47 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * aclocal.m4(KSH_OPENDIR_CHECK): include dirent.h if HAVE_DIRENT_H + defined (was DIRENT || _POSIX_VERSION). + * aclocal.m4(KSH_UNISTD_H): don't test HAVE_DIRENT_H when including + dirent.h. + +Wed Jun 12 11:02:32 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * c_test.c(b_ops[]): added "==" entry (ksh93ism). + +Mon Jun 10 14:00:21 1996 Michael Rendell (michael@lyman.cs.mun.ca) + + * ksh_stat.h: undef S_ISSOCK if STAT_MACROS_BROKEN defined. + * aclocal.m4(AC_HEADER_STAT): redefine autoconf's version to handle + FreeBSD's S_ISSOCK. Tue Jun 4 08:41:19 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) - * made pdksh-5.2.6 distribution + * made pdksh-5.2.7 distribution * vi.c(CMDLEN): changed from 16 back to 1024. diff --git a/bin/ksh/IAFA-PACKAGE b/bin/ksh/IAFA-PACKAGE index fd04bc405bf..26a19b94d19 100644 --- a/bin/ksh/IAFA-PACKAGE +++ b/bin/ksh/IAFA-PACKAGE @@ -1,7 +1,7 @@ -$OpenBSD: IAFA-PACKAGE,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: IAFA-PACKAGE,v 1.2 1996/08/19 20:08:42 downsj Exp $ Title: pdksh -Version: 5.2.7 +Version: 5.2.8 Description: A public domain implementation of the Korn shell (ksh88), a UNIX command line interpreter / scripting language; the few missing ksh features are being added and the shell is being diff --git a/bin/ksh/NEWS b/bin/ksh/NEWS index 4daaaf1ac67..9c5f173d6ba 100644 --- a/bin/ksh/NEWS +++ b/bin/ksh/NEWS @@ -1,4 +1,39 @@ -$OpenBSD: NEWS,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: NEWS,v 1.2 1996/08/19 20:08:42 downsj Exp $ + +Version 5.2.8 + +* bug fixes + * configuration: handle FreeBSD's strange S_ISSOCK. + * test: added == operator. + * configuration: fixed opendir/dirent usage. + * redirections before subshells handled correctly. + * COLUMNS/LINES are no longer exported when they are automatically set. + * mail checks and PS1/PS4 expansions removed if compiled as sh. + * subcommands in PS1 no longer genereate bogus warning messages. + * environment variables not longer messed up on 16-bit machines. + * unset: now returns non-zero if variable/function isn't set. + * select: only prints menu first time, if REPLY is null or on blank line. + * check for `cannot execute' imporved, error message says why. + * typeset: now reports variables with attributes but now value. + * vi/emacs file completion: does directory listing on zero length names. + * arithmetic: non-numeric parameters expanded recursively. + * arithmetic: identifiers in unevaluated part of ?:,&&,|| parsed correctly. + * functions: unsetting a function within itself is now safe. + * arrays: unsetting element 0 of an array no longer kills the whole array. + * co-processes now behave like ksh93 co-processes (and less like ksh88). + +* functions declared with "function foo" are treated differently (from those + declared with "foo()"): $0 is (not) set to the function name, assignments + before function calls aren't (are) kept in the parent shell. + +* vi: added vi-esccomplete option for people who want ESC-ESC completion. + +* vi/emacs: now notice window size changes (but not while editing a line). + +* emacs: <esc># now does the comment/uncomment thing. + +* arithmetic: ++, -- and , added. + Version 5.2.7 diff --git a/bin/ksh/NOTES b/bin/ksh/NOTES index 45bab7b2752..96a2af3aada 100644 --- a/bin/ksh/NOTES +++ b/bin/ksh/NOTES @@ -1,4 +1,4 @@ -$OpenBSD: NOTES,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: NOTES,v 1.2 1996/08/19 20:08:43 downsj Exp $ General features of at&t ksh that are not (yet) in pdksh: - exported aliases. @@ -6,10 +6,12 @@ General features of at&t ksh that are not (yet) in pdksh: - set -t. - signals/traps not cleared during functions. - trap DEBUG, local ERR and EXIT traps in functions. - - ERRNO, LINENO, LINES parameters. + - ERRNO, LINENO parameters. - doesn't have posix file globbing (eg, [[:alpha:]], etc.). - use of an `agent' to execute unreadable/setuid/setgid shell scripts (don't ask). + - read/select aren't hooked in to the the command line editor + - the last command of a pipeline is not run in the parent shell Known bugs (see also BUG-REPORTS and PROJECTS files): Variable parsing, Expansion: @@ -46,11 +48,6 @@ Known differences between pdksh & at&t ksh (that may change) prints a message and exits. (Also, in at&t ksh, setting TMOUT has no effect after the sequence "TMOUT=60; unset TMOUT", which could be useful - pdksh may do this in the future). - - co-processes: in at&t ksh, accessing the co-process in a redirection - always closes the shells copies of the file descriptors; in pdksh - only redirections in an empty exec command has this effect. This - may change if the at&t style proves more useful (doubt it, though) - or if many scripts depend on it. - in pdksh, if the last command of a pipeline is a shell builtin, it is not executed in the parent shell, so "echo a b | read foo bar" does not set foo and bar in the parent shell (at&t ksh will). @@ -59,6 +56,9 @@ Known differences between pdksh & at&t ksh (that may change) it is the same as set -o. - in pdksh emacs mode, ^T does what gnu emacs does, not what at&t ksh does. + - in ksh93, `. name' calls a function (defined with function) with POSIX + semantics (instead of ksh semantics). in pdksh, . does not call + functions. Known differences between pdksh & at&t ksh (that are not likely to change) - at&t ksh seems to catch or ignore SIGALRM - pdksh dies upon receipt @@ -79,7 +79,7 @@ Known differences between pdksh & at&t ksh (that are not likely to change) uses isspace()), at&t ksh only skips blanks. - at&t ksh allows attributes of read-only variables to be changed, pdksh allows only the export attribute to be set. - - at&t ksh allows set -A of readonly variables, pdksh does not. + - (some) at&t ksh allows set -A of readonly variables, pdksh does not. - at&t ksh allows command assignments of readonly variables (eg, YY=2 cat), pdksh does not. - at&t ksh does not exit scripts when an implicit assignment to an integer @@ -165,6 +165,19 @@ Known differences between pdksh & at&t ksh (that are not likely to change) - pwd: in at&t ksh, it ignores arguments; in pdksh, it complains when given arguments. - the at&t ksh does not do command substition on PS1, pdksh does. + - ksh93 allows ". foo" to run the function foo if there is no file + called foo (go figure). + - field splitting (IFS): ksh88/ksh93 strip leading non-white space IFS + chars, pdksh (and POSIX, I think) leave them intact. e.g. + $ IFS="$IFS:"; read x; echo "<$x>" + :: + prints "<>" in at&t ksh, "<::>" in pdksh. + - command completion: at&t ksh will do completion on a blank line (matching + all commands), pdksh does not (as this isn't very useful - use * if + you really want the list). + - co-processes: if ksh93, the write portion of the co-process output is + closed when the most recently started co-process exits. pdksh closes + it when all the co-processes using it have exited. Oddities in ksh (pd & at&t): - array references inside (())/$(()) are strange: diff --git a/bin/ksh/PROJECTS b/bin/ksh/PROJECTS index 52913caf817..271dc3ccad9 100644 --- a/bin/ksh/PROJECTS +++ b/bin/ksh/PROJECTS @@ -1,4 +1,4 @@ -$OpenBSD: PROJECTS,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: PROJECTS,v 1.2 1996/08/19 20:08:44 downsj Exp $ Things to be done in pdksh (see also the NOTES file): @@ -40,7 +40,7 @@ Things to be done in pdksh (see also the NOTES file): * trap code * add the DEBUG trap. * fix up signal handling code. In particular, fatal vs tty signals, - have single routine to call to check for pending/fatal traps, etc. + have signal routine to call to check for pending/fatal traps, etc. * parsing * the time keyword needs to be hacked to accept options (!) since @@ -64,12 +64,8 @@ Things to be done in pdksh (see also the NOTES file): freed when a variable is unset. * functions - POSIX and at&t ksh functions are different in that POSIX functions - don't change disable/restore traps and option parsing (OPTIND/OPTARG - plus internal state) isn't saved/restored. The suggestion made in - POSIX.2 rationale is to have x() { .. } do the POSIX thing, and have - function x { ..} do the at&t ksh thing. So, should have two types of - functions. + finish the differences between function x and x(): trap EXIT, traps + in general, treatment of OPTIND/OPTARG, * history There are two versions of the history code, COMPLEX_HISTORY and @@ -102,31 +98,19 @@ Things to be done in pdksh (see also the NOTES file): otherwise) (see POSIX.2:3.8.1). Some of this has been taken care of, but more needs doing. - * POSIX says if an exec fails, the exit code should be 127 (not found) - or 126 (not executable)... - * remove static limits created by fixed sized arrays (eg, getsc_(line[]), ident[], heres[], PATH, states(lex.c), buffer size in emacs/vi code) * merge the emacs and vi code (should reduce the size of the shell and - make maintenance easier). + make maintenance easier); handle SIGWINCH while editing a line. [John Rochester is working on the merge] * add POSIX globbing (eg, [[:alnum:]]), see POSIX.2:2.8.3.2. - * catch SIGWINCH and update the COLUMNS and LINES parameters (also, - need to let the command line editor know of change - ideally this - would work even if the editor was currently reading commands). - * teach shf_vfprintf() about long long's (%lld); also make %p use long longs if appropriate. * add \[...\] parsing to prompt printing (don't count width of chars inside the \[..\] - used to keep escape sequences in prompts from messing up command-line-editor's idea of where the cursor is) - - * file(command) completion list in vi/emacs: change so a number-prefix - picks one of the possibilities (eg, if in vi: foo^[= lists fooa, foob - and fooc as possible completions, ^[2= would choose the second - possibility (foob)). diff --git a/bin/ksh/README b/bin/ksh/README index d22b8e40c84..86575be9c34 100644 --- a/bin/ksh/README +++ b/bin/ksh/README @@ -1,6 +1,6 @@ -$OpenBSD: README,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: README,v 1.2 1996/08/19 20:08:44 downsj Exp $ -Last updated June '96 for pdksh-5.2.6. +Last updated August '96 for pdksh-5.2.8. (check ftp://ftp.cs.mun.ca:/pub/pdksh/ or http://www.cs.mun.ca/~michael/pdksh/ for new versions/patches) diff --git a/bin/ksh/c_ksh.c b/bin/ksh/c_ksh.c index 01bef267466..ea27c7ddcbe 100644 --- a/bin/ksh/c_ksh.c +++ b/bin/ksh/c_ksh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: c_ksh.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: c_ksh.c,v 1.2 1996/08/19 20:08:45 downsj Exp $ */ /* * built-in Korn commands: c_* @@ -280,7 +280,7 @@ c_print(wp) break; #ifdef KSH case 'p': - if ((fd = get_coproc_fd(W_OK, &emsg)) < 0) { + if ((fd = coproc_getfd(W_OK, &emsg)) < 0) { bi_errorf("-p: %s", emsg); return 1; } @@ -378,9 +378,9 @@ c_print(wp) Xfree(xs, xp); } else { int n, len = Xlength(xs, xp); +#ifdef KSH int UNINITIALIZED(opipe); -#ifdef KSH /* Ensure we aren't killed by a SIGPIPE while writing to * a coprocess. at&t ksh doesn't seem to do this (seems * to just check that the co-process is alive, which is @@ -404,16 +404,22 @@ c_print(wp) continue; } #ifdef KSH - if (errno == EPIPE) - coproc_write_close(fd); + /* This doesn't really make sense - could + * break scripts (print -p generates + * error message). + *if (errno == EPIPE) + * coproc_write_close(fd); + */ #endif /* KSH */ return 1; } s += n; len -= n; } +#ifdef KSH if (flags & PO_COPROC) restore_pipe(opipe); +#endif /* KSH */ } return 0; @@ -559,12 +565,13 @@ c_typeset(wp) { struct block *l = e->loc; struct tbl *vp, **p; - int fset = 0, fclr = 0; + Tflag fset = 0, fclr = 0; int thing = 0, func = 0, local = 0; const char *options = "L#R#UZ#fi#lrtux"; /* see comment below */ char *fieldstr, *basestr; int field, base; - int optc, flag; + int optc; + Tflag flag; int pflag = 0; switch (**wp) { @@ -757,8 +764,6 @@ c_typeset(wp) for (l = e->loc; l; l = l->next) { for (p = tsort(&l->vars); (vp = *p++); ) for (; vp; vp = vp->u.array) { - if (!(vp->flag&ISSET)) - continue; if (flag && (vp->flag & flag) == 0) continue; /* no arguments */ @@ -777,9 +782,9 @@ c_typeset(wp) if ((vp->flag&TRACE)) shprintf("-t "); if ((vp->flag&LJUST)) - shprintf("-L%d ", vp->field); + shprintf("-L%d ", vp->u2.field); if ((vp->flag&RJUST)) - shprintf("-R%d ", vp->field); + shprintf("-R%d ", vp->u2.field); if ((vp->flag&ZEROFIL)) shprintf("-Z "); if ((vp->flag&LCASEV)) @@ -800,7 +805,7 @@ c_typeset(wp) shprintf("%s[%d]", vp->name, vp->index); else shprintf("%s", vp->name); - if (thing == '-') { + if (thing == '-' && (vp->flag&ISSET)) { char *s = str_val(vp); shprintf("="); @@ -824,7 +829,8 @@ c_alias(wp) char **wp; { struct table *t = &aliases; - int rv = 0, rflag = 0, tflag, Uflag = 0, xflag = 0; + int rv = 0, rflag = 0, tflag, Uflag = 0; + Tflag xflag = 0; int optc; while ((optc = ksh_getopt(wp, &builtin_opt, "drtUx")) != EOF) @@ -910,7 +916,8 @@ c_alias(wp) afree((void*)ap->val.s, APERM); } /* ignore values for -t (at&t ksh does this) */ - newval = tflag ? search(alias, path, X_OK) : val; + newval = tflag ? search(alias, path, X_OK, (int *) 0) + : val; if (newval) { ap->val.s = str_save(newval, APERM); ap->flag |= ALLOC|ISSET; @@ -1281,26 +1288,32 @@ c_getopts(wp) buf[0] = optc < 0 ? '?' : optc; buf[1] = '\0'; } - vq = global(var); - if (vq->flag & RDONLY) - bi_errorf("%s is readonly", var); - if (Flag(FEXPORT)) - typeset(var, EXPORT, 0, 0, 0); - setstr(vq, buf); - getopts_noset = 1; - setint(global("OPTIND"), (long) user_opt.optind); - getopts_noset = 0; + /* at&t ksh does not change OPTIND if it was an unknown option. + * Scripts counting on this are prone to break... (ie, don't count + * on this staying). + */ + if (optc != '?') { + getopts_noset = 1; + setint(global("OPTIND"), (long) user_opt.optind); + getopts_noset = 0; + } if (user_opt.optarg == (char *) 0) unset(global("OPTARG"), 0); else setstr(global("OPTARG"), user_opt.optarg); - if (optc < 0) + vq = global(var); + if (vq->flag & RDONLY) { + bi_errorf("%s is readonly", var); return 1; + } + if (Flag(FEXPORT)) + typeset(var, EXPORT, 0, 0, 0); + setstr(vq, buf); - return 0; + return optc < 0 ? 1 : 0; } #ifdef EMACS diff --git a/bin/ksh/c_sh.c b/bin/ksh/c_sh.c index 7fd2a645b12..691abee19bf 100644 --- a/bin/ksh/c_sh.c +++ b/bin/ksh/c_sh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: c_sh.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: c_sh.c,v 1.2 1996/08/19 20:08:46 downsj Exp $ */ /* * built-in Bourne commands @@ -26,14 +26,19 @@ c_shift(wp) register struct block *l = e->loc; register int n; long val; + char *arg; - if (wp[1]) { - evaluate(wp[1], &val, FALSE); + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; + arg = wp[builtin_opt.optind]; + + if (arg) { + evaluate(arg, &val, FALSE); n = val; } else n = 1; if (n < 0) { - bi_errorf("%s: bad number", wp[1]); + bi_errorf("%s: bad number", arg); return (1); } if (l->argc < n) { @@ -175,19 +180,22 @@ c_dot(wp) int argc; int i; - if ((cp = wp[1]) == NULL) + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; + + if ((cp = wp[builtin_opt.optind]) == NULL) return 0; - file = search(cp, path, R_OK); + file = search(cp, path, R_OK, (int *) 0); if (file == NULL) { bi_errorf("%s: not found", cp); return 1; } /* Set positional parameters? */ - if (wp[2]) { - argv = ++wp; + if (wp[builtin_opt.optind + 1]) { + argv = wp + builtin_opt.optind; argv[0] = e->loc->argv[0]; /* preserve $0 */ - for (argc = -1; *wp++; argc++) + for (argc = 0; argv[argc + 1]; argc++) ; } else { argc = 0; @@ -245,7 +253,7 @@ c_read(wp) switch (optc) { #ifdef KSH case 'p': - if ((fd = get_coproc_fd(R_OK, &emsg)) < 0) { + if ((fd = coproc_getfd(R_OK, &emsg)) < 0) { bi_errorf("-p: %s", emsg); return 1; } @@ -290,9 +298,15 @@ c_read(wp) #ifdef KSH /* If we are reading from the co-process for the first time, - * make sure the other side of the pipe is closed first. + * make sure the other side of the pipe is closed first. This allows + * the detection of eof. + * + * This is not compatiable with at&t ksh... the fd is kept so another + * coproc can be started with same ouput, however, this means eof + * can't be detected... This is why it is closed here. + * If this call is removed, remove the eof check below, too. + * coproc_readw_close(fd); */ - coproc_readw_close(fd); #endif /* KSH */ if (history) @@ -387,7 +401,10 @@ c_read(wp) Xfree(xs, xp); } #ifdef KSH - /* if this is the co-process fd, close the file descriptor */ + /* if this is the co-process fd, close the file descriptor + * (can get eof if and only if all processes are have died, ie, + * coproc.njobs is 0 and the pipe is closed). + */ if (c == EOF && !ecode) coproc_read_close(fd); #endif /* KSH */ @@ -401,8 +418,10 @@ c_eval(wp) { register struct source *s; + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; s = pushs(SWORDS, ATEMP); - s->u.strv = wp+1; + s->u.strv = wp + builtin_opt.optind; return shell(s, FALSE); } @@ -466,10 +485,15 @@ c_exitreturn(wp) char **wp; { int how = LEXIT; + char *arg; - if (wp[1] != NULL && !getn(wp[1], &exstat)) { + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; + arg = wp[builtin_opt.optind]; + + if (arg != NULL && !getn(arg, &exstat)) { exstat = 1; - warningf(TRUE, "%s: bad number", wp[1]); + warningf(TRUE, "%s: bad number", arg); } if (wp[0][0] == 'r') { /* return */ struct env *ep; @@ -501,15 +525,20 @@ c_brkcont(wp) { int n, quit; struct env *ep, *last_ep = (struct env *) 0; + char *arg; + + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; + arg = wp[builtin_opt.optind]; - if (!wp[1]) + if (!arg) n = 1; - else if (!bi_getn(wp[1], &n)) + else if (!bi_getn(arg, &n)) return 1; quit = n; if (quit <= 0) { /* at&t ksh does this for non-interactive shells only - weird */ - bi_errorf("bad option `%s'", wp[1]); + bi_errorf("%s: bad value", arg); return 1; } @@ -586,6 +615,7 @@ c_unset(wp) { register char *id; int optc, unset_var = 1; + int ret = 0; while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != EOF) switch (optc) { @@ -603,14 +633,18 @@ c_unset(wp) if (unset_var) { /* unset variable */ struct tbl *vp = global(id); + if (!(vp->flag & ISSET)) + ret = 1; if ((vp->flag&RDONLY)) { bi_errorf("%s is read only", vp->name); return 1; } unset(vp, strchr(id, '[') ? 1 : 0); - } else /* unset function */ - define(id, (struct op *)NULL); - return 0; + } else { /* unset function */ + if (define(id, (struct op *) NULL)) + ret = 1; + } + return ret; } int diff --git a/bin/ksh/c_test.c b/bin/ksh/c_test.c index 7ac8c03a007..63a0c94e80c 100644 --- a/bin/ksh/c_test.c +++ b/bin/ksh/c_test.c @@ -1,4 +1,4 @@ -/* $OpenBSD: c_test.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: c_test.c,v 1.2 1996/08/19 20:08:47 downsj Exp $ */ /* * test(1); version 7-like -- author Erik Baalbergen @@ -27,7 +27,7 @@ "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"| "-L"|"-h"|"-S"|"-H"; - binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| + binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| "-nt"|"-ot"|"-ef"| "<"|">" # rules used for [[ .. ]] expressions ; @@ -69,6 +69,9 @@ static const struct t_op u_ops [] = { }; static const struct t_op b_ops [] = { {"=", TO_STEQL }, +#ifdef KSH + {"==", TO_STEQL }, +#endif /* KSH */ {"!=", TO_STNEQ }, {"<", TO_STLT }, {">", TO_STGT }, diff --git a/bin/ksh/edit.c b/bin/ksh/edit.c index c009ed726d0..12e4fed77d5 100644 --- a/bin/ksh/edit.c +++ b/bin/ksh/edit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: edit.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: edit.c,v 1.2 1996/08/19 20:08:47 downsj Exp $ */ /* * Command line editing - common code @@ -20,7 +20,21 @@ #include <ctype.h> #include "ksh_stat.h" -static char vdisable_c; + +#if defined(TIOCGWINSZ) +static RETSIGTYPE x_sigwinch ARGS((int sig)); +static int got_sigwinch; +static void check_sigwinch ARGS((void)); +#endif /* TIOCGWINSZ */ + +static int x_file_glob ARGS((int flags, const char *str, int slen, + char ***wordsp)); +static int x_command_glob ARGS((int flags, const char *str, int slen, + char ***wordsp)); +static int x_locate_word ARGS((const char *buf, int buflen, int pos, + int *startp, int *is_command)); + +static char vdisable_c; /* Called from main */ @@ -32,26 +46,15 @@ x_init() = edchars.eof = -1; /* default value for deficient systems */ edchars.werase = 027; /* ^W */ -#ifdef TIOCGWINSZ - { - struct winsize ws; - - if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) { - struct tbl *vp; - if (ws.ws_col) { - x_cols = ws.ws_col < MIN_COLS ? MIN_COLS - : ws.ws_col; - - if ((vp = typeset("COLUMNS", EXPORT, 0, 0, 0))) - setint(vp, (long) ws.ws_col); - } - if (ws.ws_row - && (vp = typeset("LINES", EXPORT, 0, 0, 0))) - setint(vp, (long) ws.ws_row); - } - } +#ifdef TIOCGWINSZ +# ifdef SIGWINCH + if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP)) + sigtraps[SIGWINCH].flags |= TF_SHELL_USES; +# endif /* SIGWINCH */ + check_sigwinch(); #endif /* TIOCGWINSZ */ + #ifdef EMACS x_init_emacs(); #endif /* EMACS */ @@ -74,6 +77,46 @@ x_init() #endif /* _POSIX_VDISABLE */ } +#if defined(TIOCGWINSZ) +static RETSIGTYPE +x_sigwinch(sig) + int sig; +{ + got_sigwinch = 1; + return RETSIGVAL; +} + +static void +check_sigwinch ARGS((void)) +{ + if (got_sigwinch) { + struct winsize ws; + + got_sigwinch = 0; + if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) { + struct tbl *vp; + + /* Do NOT export COLUMNS/LINES. Many applications + * check COLUMNS/LINES before checking ws.ws_col/row, + * so if the app is started with C/L in the environ + * and the window is then resized, the app won't + * see the change cause the environ doesn't change. + */ + if (ws.ws_col) { + x_cols = ws.ws_col < MIN_COLS ? MIN_COLS + : ws.ws_col; + + if ((vp = typeset("COLUMNS", 0, 0, 0, 0))) + setint(vp, (long) ws.ws_col); + } + if (ws.ws_row + && (vp = typeset("LINES", 0, 0, 0, 0))) + setint(vp, (long) ws.ws_row); + } + } +} +#endif /* TIOCGWINSZ */ + /* * read an edited command line */ @@ -84,6 +127,11 @@ x_read(buf, len) { int i; +#if defined(TIOCGWINSZ) + if (got_sigwinch) + check_sigwinch(); +#endif /* TIOCGWINSZ */ + x_mode(TRUE); #ifdef EMACS if (Flag(FEMACS) || Flag(FGMACS)) @@ -330,6 +378,61 @@ set_editmode(ed) } /* ------------------------------------------------------------------------- */ +/* Misc common code for vi/emacs */ + +/* Handle the commenting/uncommenting of a line. + * Returns: + * 1 if a carriage return is indicated (comment added) + * 0 if no return (comment removed) + * -1 if there is an error (not enough room for comment chars) + * If successful, *lenp contains the new length. Note: cursor should be + * moved to the start of the line after (un)commenting. + */ +int +x_do_comment(buf, bsize, lenp) + char *buf; + int bsize; + int *lenp; +{ + int i, j; + int len = *lenp; + + if (len == 0) + return 1; /* somewhat arbitrary - it's what at&t ksh does */ + + /* Already commented? */ + if (buf[0] == '#') { + int saw_nl = 0; + + for (j = 0, i = 1; i < len; i++) { + if (!saw_nl || buf[i] != '#') + buf[j++] = buf[i]; + saw_nl = buf[i] == '\n'; + } + *lenp = j; + return 0; + } else { + int n = 1; + + /* See if there's room for the #'s - 1 per \n */ + for (i = 0; i < len; i++) + if (buf[i] == '\n') + n++; + if (len + n >= bsize) + return -1; + /* Now add them... */ + for (i = len, j = len + n; --i >= 0; ) { + if (buf[i] == '\n') + buf[--j] = '#'; + buf[--j] = buf[i]; + } + buf[0] = '#'; + *lenp += n; + return 1; + } +} + +/* ------------------------------------------------------------------------- */ /* Common file/command completion code for vi/emacs */ @@ -338,7 +441,9 @@ static void glob_table ARGS((const char *pat, XPtrV *wp, struct table *tp)); static void glob_path ARGS((int flags, const char *pat, XPtrV *wp, const char *path)); -/* XXX not used... */ +#if 0 /* not used... */ +int x_complete_word ARGS((const char *str, int slen, int is_command, + int *multiple, char **ret)); int x_complete_word(str, slen, is_command, nwordsp, ret) const char *str; @@ -364,6 +469,7 @@ x_complete_word(str, slen, is_command, nwordsp, ret) x_free_words(nwords, words); return prefix_len; } +#endif /* 0 */ void x_print_expansions(nwords, words, is_command) @@ -422,8 +528,7 @@ x_print_expansions(nwords, words, is_command) * - sets *wordsp to array of matching strings * - returns number of matching strings */ -/* XXX static? */ -int +static int x_file_glob(flags, str, slen, wordsp) int flags; const char *str; @@ -436,7 +541,7 @@ x_file_glob(flags, str, slen, wordsp) XPtrV w; struct source *s, *sold; - if (slen <= 0) + if (slen < 0) return 0; toglob = add_glob(str, slen); @@ -507,8 +612,7 @@ path_order_cmp(aa, bb) return t ? t : a->path_order - b->path_order; } -/* XXX static? */ -int +static int x_command_glob(flags, str, slen, wordsp) int flags; const char *str; @@ -522,7 +626,7 @@ x_command_glob(flags, str, slen, wordsp) XPtrV w; struct block *l; - if (slen <= 0) + if (slen < 0) return 0; toglob = add_glob(str, slen); @@ -605,8 +709,7 @@ x_command_glob(flags, str, slen, wordsp) #define IS_WORDC(c) !isspace(c) -/* XXX static? */ -int +static int x_locate_word(buf, buflen, pos, startp, is_commandp) const char *buf; int buflen; @@ -617,25 +720,28 @@ x_locate_word(buf, buflen, pos, startp, is_commandp) int p; int start, end; - if (pos == buflen) - pos--; - if (pos < 0 || pos >= buflen) { + /* Bad call? Probably should report error */ + if (pos < 0 || pos > buflen) { *startp = pos; *is_commandp = 0; return 0; } - /* Go backwards 'til we are in a word */ - for (start = pos; start >= 0 && !IS_WORDC(buf[start]); start--) - ; - /* No word found? */ - if (start < 0) - return 0; - /* Keep going backwards to start of word */ - for (; start >= 0 && IS_WORDC(buf[start]); start--) - ; - start++; + if (pos == buflen) { + if (pos == 0) { /* empty buffer? */ + *startp = pos; + *is_commandp = 1; + return 0; + } + pos--; + } + start = pos; + /* Keep going backwards to start of word (has effect of allowing + * one blank after the end of a word) + */ + for (; start > 0 && IS_WORDC(buf[start - 1]); start--) + ; /* Go forwards to end of word */ for (end = start; end < buflen && IS_WORDC(buf[end]); end++) ; @@ -682,11 +788,15 @@ x_cf_glob(flags, buf, buflen, pos, startp, endp, wordsp, is_commandp) int is_command; len = x_locate_word(buf, buflen, pos, startp, &is_command); - if (len == 0) - return 0; - if (!(flags & XCF_COMMAND)) is_command = 0; + /* Don't do command globing on zero length strings - it takes too + * long and isn't very useful. File globs are more likely to be + * useful, so allow these. + */ + if (len == 0 && is_command) + return 0; + nwords = (is_command ? x_command_glob : x_file_glob)(flags, buf + *startp, len, &words); if (nwords == 0) { @@ -713,7 +823,7 @@ add_glob(str, slen) char *toglob; char *s; - if (slen <= 0) + if (slen < 0) return (char *) 0; toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */ @@ -880,7 +990,7 @@ glob_path(flags, pat, wp, path) /* Check that each match is executable... */ words = (char **) XPptrv(*wp); for (i = j = oldsize; i < newsize; i++) { - if (search_access(words[i], X_OK) >= 0) { + if (search_access(words[i], X_OK, (int *) 0) >= 0) { words[j] = words[i]; if (!(flags & XCF_FULLPATH)) memmove(words[j], words[j] + pathlen, diff --git a/bin/ksh/edit.h b/bin/ksh/edit.h index c60b4121666..fd29701cc06 100644 --- a/bin/ksh/edit.h +++ b/bin/ksh/edit.h @@ -1,4 +1,4 @@ -/* $OpenBSD: edit.h,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: edit.h,v 1.2 1996/08/19 20:08:48 downsj Exp $ */ /* NAME: * edit.h - globals for edit modes @@ -50,13 +50,10 @@ void x_putc ARGS((int c)); void x_puts ARGS((const char *s)); bool_t x_mode ARGS((bool_t onoff)); int promptlen ARGS((const char *cp, const char **spp)); -int x_complete_word ARGS((const char *str, int slen, int is_command, int *multiple, char **ret)); +int x_do_comment ARGS((char *buf, int bsize, int *lenp)); void x_print_expansions ARGS((int nwords, char *const *words, int is_command)); -int x_locate_word ARGS((const char *buf, int buflen, int pos, int *startp, int *is_command)); int x_cf_glob ARGS((int flags, const char *buf, int buflen, int pos, int *startp, int *endp, char ***wordsp, int *is_commandp)); -int x_file_glob ARGS((int flags, const char *str, int slen, char ***wordsp)); -int x_command_glob ARGS((int flags, const char *str, int slen, char ***wordsp)); int x_longest_prefix ARGS((int nwords, char *const *words)); int x_basename ARGS((const char *s, const char *se)); void x_free_words ARGS((int nwords, char **words)); diff --git a/bin/ksh/emacs.c b/bin/ksh/emacs.c index a682f32fb5b..f572f87b5f6 100644 --- a/bin/ksh/emacs.c +++ b/bin/ksh/emacs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: emacs.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: emacs.c,v 1.2 1996/08/19 20:08:48 downsj Exp $ */ /* * Emacs-like command line editing and history @@ -94,6 +94,7 @@ static int x_adj_ok; */ static int x_adj_done; +static int xx_cols; static int x_col; static int x_displen; static int x_arg; /* general purpose arg */ @@ -138,10 +139,8 @@ static void x_adjust ARGS((void)); static void x_e_ungetc ARGS((int c)); static int x_e_getc ARGS((void)); static void x_e_putc ARGS((int c)); -#ifdef DEBUG -static int x_debug_info ARGS((void)); -#endif /* DEBUG */ static void x_e_puts ARGS((const char *s)); +static int x_comment ARGS((int c)); static int x_fold_case ARGS((int c)); static char *x_lastcp ARGS((void)); static void do_complete ARGS((int flags, Comp_type type)); @@ -213,6 +212,7 @@ static const struct x_ftab x_ftab[] = { { x_fold_lower, "downcase-word", XF_ARG }, { x_fold_upper, "upcase-word", XF_ARG }, { x_set_arg, "set-arg", XF_NOBIND }, + { x_comment, "comment", 0 }, #ifdef SILLY { x_game_of_life, "play-game-of-life", 0 }, #else @@ -265,6 +265,7 @@ static struct x_defbindings const x_defbindings[] = { { XFUNC_yank, 0, CTRL('Y') }, { XFUNC_meta_yank, 1, 'y' }, { XFUNC_literal, 0, CTRL('^') }, + { XFUNC_comment, 1, '#' }, #if defined(BRL) && defined(TIOCSTI) { XFUNC_stuff, 0, CTRL('T') }, #else @@ -342,10 +343,11 @@ x_emacs(buf, len) x_histp = histptr + 1; x_last_command = XFUNC_error; + xx_cols = x_cols; x_col = promptlen(prompt, &p); prompt_skip = p - prompt; x_adj_ok = 1; - x_displen = x_cols - 2 - x_col; + x_displen = xx_cols - 2 - x_col; x_adj_done = 0; pprompt(prompt, 0); @@ -542,7 +544,7 @@ x_delete(nc, force_push) * there is no need to ' ','\b'. * But if we must, make sure we do the minimum. */ - if ((i = x_cols - 2 - x_col) > 0) + if ((i = xx_cols - 2 - x_col) > 0) { j = (j < i) ? j : i; i = j; @@ -1031,6 +1033,10 @@ x_draw_line(c) } +/* Redraw (part of) the line. If limit is < 0, the everything is redrawn + * on a NEW line, otherwise limit is the screen column up to which needs + * redrawing. + */ static void x_redraw(limit) int limit; @@ -1049,12 +1055,12 @@ x_redraw(limit) pprompt(prompt + prompt_skip, 0); x_col = promptlen(prompt, (const char **) 0); } - x_displen = x_cols - 2 - x_col; + x_displen = xx_cols - 2 - x_col; xlp_valid = FALSE; cp = x_lastcp(); x_zots(xbp); if (xbp != xbuf || xep > xlp) - limit = x_cols; + limit = xx_cols; if (limit >= 0) { if (xep > xlp) @@ -1062,7 +1068,7 @@ x_redraw(limit) else i = limit - (xlp - xbp); - for (j = 0; j < i && x_col < (x_cols - 2); j++) + for (j = 0; j < i && x_col < (xx_cols - 2); j++) x_e_putc(' '); i = ' '; if (xep > xlp) /* more off screen */ @@ -1832,12 +1838,12 @@ x_adjust() { x_adj_done++; /* flag the fact that we were called. */ /* - * we had a problem if the prompt length > x_cols / 2 + * we had a problem if the prompt length > xx_cols / 2 */ if ((xbp = xcp - (x_displen / 2)) < xbuf) xbp = xbuf; xlp_valid = FALSE; - x_redraw(x_cols); + x_redraw(xx_cols); x_flush(); } @@ -1876,7 +1882,7 @@ x_e_putc(c) { if (c == '\r' || c == '\n') x_col = 0; - if (x_col < x_cols) + if (x_col < xx_cols) { x_putc(c); switch(c) @@ -1894,7 +1900,7 @@ x_e_putc(c) break; } } - if (x_adj_ok && (x_col < 0 || x_col >= (x_cols - 2))) + if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2))) { x_adjust(); } @@ -1902,19 +1908,20 @@ x_e_putc(c) #ifdef DEBUG static int -x_debug_info() +x_debug_info(c) + int c; { - x_flush(); - printf("\nksh debug:\n"); - printf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n", - x_col, x_cols, x_displen); - printf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep); - printf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf); - printf("\txlp == 0x%lx\n", (long) xlp); - printf("\txlp == 0x%lx\n", (long) x_lastcp()); - printf(newline); - x_redraw(-1); - return 0; + x_flush(); + shellf("\nksh debug:\n"); + shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n", + x_col, xx_cols, x_displen); + shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep); + shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf); + shellf("\txlp == 0x%lx\n", (long) xlp); + shellf("\txlp == 0x%lx\n", (long) x_lastcp()); + shellf(newline); + x_redraw(-1); + return 0; } #endif @@ -1961,6 +1968,29 @@ x_set_arg(c) } +/* Comment or uncomment the current line. */ +static int +x_comment(c) + int c; +{ + int oldsize = x_size_str(xbuf); + int len = xep - xbuf; + int ret = x_do_comment(xbuf, xend - xbuf, &len); + + if (ret < 0) + x_e_putc(BEL); + else { + xep = xbuf + len; + *xep = '\0'; + xcp = xbp = xbuf; + x_redraw(oldsize); + if (ret > 0) + return x_newline('\n'); + } + return KSTD; +} + + /* NAME: * x_prev_histword - recover word from prev command * diff --git a/bin/ksh/eval.c b/bin/ksh/eval.c index da952584d96..b8b339eccda 100644 --- a/bin/ksh/eval.c +++ b/bin/ksh/eval.c @@ -1,4 +1,4 @@ -/* $OpenBSD: eval.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: eval.c,v 1.2 1996/08/19 20:08:49 downsj Exp $ */ /* * Expansion - quoting, separation, substitution, globbing @@ -723,7 +723,7 @@ varsub(xp, sp, word, stypep) c = strlen(p); } if (Flag(FNOUNSET) && c == 0 && !zero_ok) - errorf("%s: unset variable", sp); + errorf("%s: parameter not set", sp); *stypep = 0; /* unqualified variable/string substitution */ xp->str = str_save(ulton((unsigned long)c, 10), ATEMP); return XSUB; @@ -813,7 +813,7 @@ varsub(xp, sp, word, stypep) state = XBASE; /* expand word instead of variable value */ if (Flag(FNOUNSET) && xp->str == null && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) - errorf("%s: unset variable", sp); + errorf("%s: parameter not set", sp); return state; } diff --git a/bin/ksh/exec.c b/bin/ksh/exec.c index 7563b2bb9d0..ce4ca7668c5 100644 --- a/bin/ksh/exec.c +++ b/bin/ksh/exec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: exec.c,v 1.2 1996/08/19 20:08:50 downsj Exp $ */ /* * execute command tree @@ -9,14 +9,21 @@ #include <ctype.h> #include "ksh_stat.h" +/* Does ps4 get parameter substitutions done? */ +#ifdef KSH +# define PS4_SUBSTITUTE(s) substitute((s), 0) +#else +# define PS4_SUBSTITUTE(s) (s) +#endif /* KSH */ + static int comexec ARGS((struct op *t, struct tbl *volatile tp, char **ap, int volatile flags)); static void scriptexec ARGS((struct op *tp, char **ap)); static int call_builtin ARGS((struct tbl *tp, char **wp)); -static int iosetup ARGS((struct ioword *iop)); +static int iosetup ARGS((struct ioword *iop, struct tbl *tp)); static int herein ARGS((char *hname, int sub)); #ifdef KSH -static char *do_selectargs ARGS((char **ap)); +static char *do_selectargs ARGS((char **ap, bool_t print_menu)); #endif /* KSH */ #ifdef KSH static int dbteste_isa ARGS((Test_env *te, Test_meta meta)); @@ -80,6 +87,14 @@ execute(t, flags) if (t == NULL) return 0; + /* Is this the end of a pipeline? If so, we want to evaluate the + * command arguments + bool_t eval_done = FALSE; + if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) { + eval_done = TRUE; + tp = eval_execute_args(t, &ap); + } + */ if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) return exchild(t, flags, -1); /* run in sub-process */ @@ -96,10 +111,10 @@ execute(t, flags) /* POSIX says expand command words first, then redirections, * and assignments last.. */ - ap = eval(t->args, t->evalflags | DOBLANK | DOGLOB | DOTILDE); + ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); if (Flag(FXTRACE) && ap[0]) { shf_fprintf(shl_out, "%s", - substitute(str_val(global("PS4")), 0)); + PS4_SUBSTITUTE(str_val(global("PS4")))); for (i = 0; ap[i]; i++) shf_fprintf(shl_out, "%s%s", ap[i], ap[i + 1] ? space : newline); @@ -118,7 +133,7 @@ execute(t, flags) /* do redirection, to be restored in quitenv() */ if (t->ioact != NULL) for (iowp = t->ioact; *iowp != NULL; iowp++) { - if (iosetup(*iowp) < 0) { + if (iosetup(*iowp, tp) < 0) { exstat = rv = 1; /* Redirection failures for special commands * cause (non-interactive) shell to exit. @@ -179,10 +194,31 @@ execute(t, flags) #ifdef KSH case TCOPROC: { +# ifdef JOB_SIGS + sigset_t omask; +# endif /* JOB_SIGS */ + +# ifdef JOB_SIGS + /* Block sigchild as we are using things changed in the + * signal handler + */ + sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); + e->type = E_ERRH; + i = ksh_sigsetjmp(e->jbuf, 0); + if (i) { + sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); + quitenv(); + unwind(i); + /*NOTREACHED*/ + } +# endif /* JOB_SIGS */ + /* Already have a (live) co-process? */ if (coproc.job && coproc.write >= 0) errorf("coprocess already exists"); + /* Can we re-use the existing co-process pipe? */ - cleanup_coproc(TRUE); + coproc_cleanup(TRUE); + /* do this before opening pipes, in case these fail */ e->savefd[0] = savefd(0, 0); e->savefd[1] = savefd(1, 0); @@ -191,6 +227,7 @@ execute(t, flags) ksh_dup2(pv[0], 0, FALSE); close(pv[0]); coproc.write = pv[1]; + coproc.job = (void *) 0; if (coproc.readw >= 0) ksh_dup2(coproc.readw, 1, FALSE); @@ -199,9 +236,19 @@ execute(t, flags) coproc.read = pv[0]; ksh_dup2(pv[1], 1, FALSE); coproc.readw = pv[1]; /* closed before first read */ + coproc.njobs = 0; + /* create new coprocess id */ + ++coproc.id; } - - /* exchild() closes coproc.* in child after fork */ +# ifdef JOB_SIGS + sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); + e->type = E_EXEC; /* no more need for error handler */ +# endif /* JOB_SIGS */ + + /* exchild() closes coproc.* in child after fork, + * will also increment coproc.njobs when the + * job is actually created. + */ flags &= ~XEXEC; exchild(t->left, flags|XBGND|XFORK|XCOPROC|XCCLOSE, coproc.readw); @@ -221,7 +268,7 @@ execute(t, flags) case TAND: rv = execute(t->left, XERROK); if (t->right != NULL && (rv == 0) == (t->type == TAND)) - rv = execute(t->right, 0); + rv = execute(t->right, flags & XERROK); else flags |= XERROK; break; @@ -250,6 +297,8 @@ execute(t, flags) case TFOR: #ifdef KSH case TSELECT: + { + volatile bool_t is_first = TRUE; #endif /* KSH */ ap = (t->vars != NULL) ? eval(t->vars, DOBLANK|DOGLOB|DOTILDE) @@ -286,10 +335,11 @@ execute(t, flags) struct tbl *vq; for (;;) { - if ((cp = do_selectargs(ap)) == (char *) 0) { + if (!(cp = do_selectargs(ap, is_first))) { rv = 1; break; } + is_first = FALSE; vq = global(t->str); if (vq->flag & RDONLY) errorf("%s is read only", t->str); @@ -297,6 +347,7 @@ execute(t, flags) rv = execute(t->left, flags & XERROK); } } + } #endif /* KSH */ break; @@ -348,7 +399,7 @@ execute(t, flags) break; case TFUNCT: - rv = define(t->str, t->left); + rv = define(t->str, t); break; case TTIME: @@ -483,15 +534,17 @@ comexec(t, tp, ap, flags) break; tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); } - /* todo: POSIX says assignments preceding a function are kept, at&t - * ksh does not do this - */ - if (keepasn_ok && (!ap[0] || (tp && tp->flag & KEEPASN))) + if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN)))) type_flags = 0; else { /* create new variable/function block */ newblock(); - type_flags = LOCAL|LOCAL_COPY|EXPORT; + /* ksh functions don't keep assignments, POSIX functions do. */ + if (keepasn_ok && tp && tp->type == CFUNC + && !(tp->flag & FKSH)) + type_flags = 0; + else + type_flags = LOCAL|LOCAL_COPY|EXPORT; } if (Flag(FEXPORT)) type_flags |= EXPORT; @@ -500,7 +553,7 @@ comexec(t, tp, ap, flags) if (Flag(FXTRACE)) { if (i == 0) shf_fprintf(shl_out, "%s", - substitute(str_val(global("PS4")), 0)); + PS4_SUBSTITUTE(str_val(global("PS4")))); shf_fprintf(shl_out, "%s%s", cp, t->vars[i + 1] ? space : newline); if (!t->vars[i + 1]) @@ -529,24 +582,30 @@ comexec(t, tp, ap, flags) case CFUNC: /* function call */ { volatile int old_xflag; - volatile int old_inuse; + volatile Tflag old_inuse; const char *volatile old_kshname; if (!(tp->flag & ISSET)) { struct tbl *ftp; if (!tp->u.fpath) { - /* XXX: exit code 126 vs 127 */ - warningf(TRUE, + if (tp->u2.errno_) { + warningf(TRUE, + "%s: can't find function definition file - %s", + cp, strerror(tp->u2.errno_)); + rv = 126; + } else { + warningf(TRUE, "%s: can't find function definition file", cp); - rv = 127; + rv = 127; + } break; } if (include(tp->u.fpath, 0, (char **) 0, 0) < 0) { warningf(TRUE, "%s: can't open function definition file %s - %s", cp, tp->u.fpath, strerror(errno)); - rv = 126; + rv = 127; break; } if (!(ftp = findfunc(cp, hash(cp), FALSE)) @@ -561,9 +620,11 @@ comexec(t, tp, ap, flags) tp = ftp; } - /* posix says $0 remains unchanged, at&t ksh changes it */ + /* ksh functions set $0 to function name, POSIX functions leave + * $0 unchanged. + */ old_kshname = kshname; - if (!Flag(FPOSIX)) + if (tp->flag & FKSH) kshname = ap[0]; e->loc->argv = ap; for (i = 0; *ap++ != NULL; i++) @@ -588,7 +649,8 @@ comexec(t, tp, ap, flags) Flag(FXTRACE) = old_xflag; tp->flag = (tp->flag & ~FINUSE) | old_inuse; /* Were we deleted while executing? If so, free the execution - * tree. Unfortunately, the table entry is never re-used. + * tree. todo: Unfortunately, the table entry is never re-used + * until the lookup table is expanded. */ if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { if (tp->flag & ALLOC) { @@ -619,38 +681,26 @@ comexec(t, tp, ap, flags) case CEXEC: /* executable command */ case CTALIAS: /* tracked alias */ if (!(tp->flag&ISSET)) { - /* - * mlj addition: - * - * If you specify a full path to a file - * (or type the name of a file in .) which - * doesn't have execute priv's, it used to - * just say "not found". Kind of annoying, - * particularly if you just wrote a script - * but forgot to say chmod 755 script. - * - * This should probably be done in eaccess(), - * but it works here (at least I haven't noticed - * changing errno here breaking something - * else). - * - * So, we assume that if the file exists, it - * doesn't have execute privs; else, it really - * is not found. + /* errno_ will be set if the named command was found + * but could not be executed (permissions, no execute + * bit, directory, etc). Print out a (hopefully) + * useful error message and set the exit status to 126. */ - if (access(cp, F_OK) < 0) + if (tp->u2.errno_) { + warningf(TRUE, "%s: cannot execute - %s", cp, + strerror(tp->u2.errno_)); + rv = 126; /* POSIX */ + } else { warningf(TRUE, "%s: not found", cp); - else - warningf(TRUE, "%s: cannot execute", cp); - /* XXX posix says 126 if in path and cannot execute */ - rv = 127; + rv = 127; + } break; } /* set $_ to program's full path */ setstr(typeset("_", LOCAL|EXPORT, 0, 0, 0), tp->val.s); - if ((flags&XEXEC)) { + if (flags&XEXEC) { j_exit(); if (!(flags&XBGND) || Flag(FMONITOR)) { setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG); @@ -683,7 +733,7 @@ scriptexec(tp, ap) shell = str_val(global(EXECSHELL_STR)); if (shell && *shell) - shell = search(shell, path, X_OK); + shell = search(shell, path, X_OK, (int *) 0); if (!shell || !*shell) shell = EXECSHELL; @@ -746,7 +796,7 @@ scriptexec(tp, ap) if (a1) *tp->args-- = a1; # ifdef OS2 - if (a0 != a2 && search_access(a0, X_OK)) + if (a0 != a2 && search_access(a0, X_OK, (int *) 0)) a0 = a2; # endif /* OS2 */ shell = a0; @@ -762,7 +812,7 @@ scriptexec(tp, ap) shell = str_val(global("EXECSHELL")); if (shell && *shell) - shell = search(shell, path, X_OK); + shell = search(shell, path, X_OK, (int *) 0); if (!shell || !*shell) { shell = p; *tp->args-- = "/c"; @@ -809,7 +859,7 @@ findfunc(name, h, create) for (l = e->loc; l; l = l->next) { tp = tsearch(&l->funs, name, h); - if (tp && (tp->flag & DEFINED)) + if (tp) break; if (!l->next && create) { tp = tenter(&l->funs, name, h); @@ -823,25 +873,31 @@ findfunc(name, h, create) } /* - * define function + * define function. Returns 1 if function is being undefined (t == 0) and + * function did not exist, returns 0 otherwise. */ int define(name, t) const char *name; struct op *t; { - register struct tbl *tp; + struct tbl *tp; + int was_set = 0; - tp = findfunc(name, hash(name), TRUE); + while (1) { + tp = findfunc(name, hash(name), TRUE); - /* If this function is currently being executed, we zap this - * table entry so findfunc() won't see it - */ - if (tp->flag & FINUSE) { - tp->name[0] = '\0'; - tp->flag &= ~DEFINED; /* ensure it won't be found */ - tp->flag |= FDELETE; - return define(name, t); + if (tp->flag & ISSET) + was_set = 1; + /* If this function is currently being executed, we zap this + * table entry so findfunc() won't see it + */ + if (tp->flag & FINUSE) { + tp->name[0] = '\0'; + tp->flag &= ~DEFINED; /* ensure it won't be found */ + tp->flag |= FDELETE; + } else + break; } if (tp->flag & ALLOC) { @@ -851,11 +907,13 @@ define(name, t) if (t == NULL) { /* undefine */ tdelete(tp); - return 0; + return was_set ? 0 : 1; } - tp->val.t = tcopy(t, tp->areap); + tp->val.t = tcopy(t->left, tp->areap); tp->flag |= (ISSET|ALLOC); + if (t->u.ksh_func) + tp->flag |= FKSH; return 0; } @@ -869,7 +927,7 @@ builtin(name, func) int (*func) ARGS((char **)); { register struct tbl *tp; - int flag; + Tflag flag; /* see if any flags should be set for this builtin */ for (flag = 0; ; name++) { @@ -920,10 +978,12 @@ findcom(name, flags) if (!tp && (flags & FC_FUNC)) { tp = findfunc(name, h, FALSE); if (tp && !(tp->flag & ISSET)) { - if ((fpath = str_val(global("FPATH"))) == null) + if ((fpath = str_val(global("FPATH"))) == null) { tp->u.fpath = (char *) 0; - else - tp->u.fpath = search(name, fpath, R_OK); + tp->u2.errno_ = 0; + } else + tp->u.fpath = search(name, fpath, R_OK, + &tp->u2.errno_); } } if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI)) @@ -964,13 +1024,14 @@ findcom(name, flags) tp->flag = DEFINED; /* make ~ISSET */ } npath = search(name, flags & FC_DEFPATH ? def_path : path, - X_OK); + X_OK, &tp->u2.errno_); if (npath) { tp->val.s = tp == &temp ? npath : str_save(npath, APERM); tp->flag |= ISSET|ALLOC; } else if ((flags & FC_FUNC) && (fpath = str_val(global("FPATH"))) != null - && (npath = search(name, fpath, R_OK)) != (char *) 0) + && (npath = search(name, fpath, R_OK, + &tp->u2.errno_)) != (char *) 0) { /* An undocumented feature of at&t ksh is that it * searches FPATH if a command is not found, even @@ -1008,20 +1069,29 @@ flushcom(all) /* Check if path is something we want to find. Returns -1 for failure. */ int -search_access(path, mode) +search_access(path, mode, errnop) const char *path; int mode; + int *errnop; /* set if candidate found, but not suitable */ { #ifndef OS2 - int ret = eaccess(path, mode); + int ret, err = 0; struct stat statb; - /* if executable pipes come along, this will have to change */ - if (ret == 0 && (mode == X_OK) - && (stat(path, &statb) < 0 || !S_ISREG(statb.st_mode) - /* This 'cause access() says root can execute everything */ - || !(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) + if (stat(path, &statb) < 0) + return -1; + ret = eaccess(path, mode); + if (ret < 0) + err = errno; /* File exists, but we can't access it */ + else if (mode == X_OK && (!S_ISREG(statb.st_mode) + /* This 'cause access() says root can execute everything */ + || !(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) + { ret = -1; + err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES; + } + if (err && errnop) + *errnop = err; return ret; #else /* !OS2 */ /* @@ -1050,7 +1120,7 @@ search_access(path, mode) if ((p = strrchr((p = ksh_strrchr_dirsep(mpath)) ? p : mpath, '.'))) { if (mode == X_OK) mode = R_OK; - return search_access1(mpath, mode); + return search_access1(mpath, mode, errnop); } /* Try appending the various suffixes. Different suffixes for * read and execute 'cause we don't want to read an executable... @@ -1058,7 +1128,7 @@ search_access(path, mode) sfx = mode == R_OK ? rsuffixes : xsuffixes; for (i = 0; sfx[i]; i++) { strcpy(tp, p = sfx[i]); - if (search_access1(mpath, R_OK) == 0) + if (search_access1(mpath, R_OK, errnop) == 0) return 0; *tp = '\0'; } @@ -1068,17 +1138,25 @@ search_access(path, mode) #ifdef OS2 static int -search_access1(path, mode) +search_access1(path, mode, errnop) const char *path; int mode; + int *errnop; /* set if candidate found, but not suitable */ { - int ret = eaccess(path, mode); + int ret, err = 0; struct stat statb; - /* if executable pipes come along, this will have to change */ - if (ret == 0 && (mode == X_OK || mode == R_OK) - && (stat(path, &statb) < 0 || !S_ISREG(statb.st_mode))) + if (stat(path, &statb) < 0) + return -1; + ret = eaccess(path, mode); + if (ret < 0) + err = errno; /* File exists, but we can't access it */ + else if (!S_ISREG(statb.st_mode)) { ret = -1; + err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES; + } + if (err && errnop) + *errnop = err; return ret; } #endif /* OS2 */ @@ -1087,16 +1165,19 @@ search_access1(path, mode) * search for command with PATH */ char * -search(name, path, mode) +search(name, path, mode, errnop) const char *name; const char *path; int mode; /* R_OK or X_OK */ + int *errnop; /* set if candidate found, but not suitable */ { const char *sp, *p; char *xp; XString xs; int namelen; + if (errnop) + *errnop = 0; #ifdef OS2 /* Xinit() allocates 8 additional bytes, so appended suffixes won't * overflow the memory. @@ -1106,18 +1187,18 @@ search(name, path, mode) memcpy(Xstring(xs, xp), name, namelen); if (ksh_strchr_dirsep(name)) { - if (search_access(Xstring(xs, xp), mode) >= 0) + if (search_access(Xstring(xs, xp), mode, errnop) >= 0) return Xstring(xs, xp); /* not Xclose() - see above */ Xfree(xs, xp); return NULL; } /* Look in current context always. (os2 style) */ - if (search_access(Xstring(xs, xp), mode) == 0) + if (search_access(Xstring(xs, xp), mode, errnop) == 0) return Xstring(xs, xp); /* not Xclose() - xp may be wrong */ #else /* OS2 */ if (ksh_strchr_dirsep(name)) { - if (search_access(name, mode) == 0) + if (search_access(name, mode, errnop) == 0) return (char *) name; return NULL; } @@ -1140,7 +1221,7 @@ search(name, path, mode) sp = p; XcheckN(xs, xp, namelen); memcpy(xp, name, namelen); - if (search_access(Xstring(xs, xp), mode) == 0) + if (search_access(Xstring(xs, xp), mode, errnop) == 0) #ifdef OS2 return Xstring(xs, xp); /* Not Xclose() - see above */ #else /* OS2 */ @@ -1177,8 +1258,9 @@ call_builtin(tp, wp) * set up redirection, saving old fd's in e->savefd */ static int -iosetup(iop) +iosetup(iop, tp) register struct ioword *iop; + struct tbl *tp; { register int u = -1; char *cp = iop->name; @@ -1197,7 +1279,7 @@ iosetup(iop) if (Flag(FXTRACE)) shellf("%s%s\n", - substitute(str_val(global("PS4")), 0), + PS4_SUBSTITUTE(str_val(global("PS4"))), snptreef((char *) 0, 32, "%R", &iotmp)); switch (iotype) { @@ -1287,23 +1369,19 @@ iosetup(iop) close(u); return -1; } - if (iotype == IODUP) { + if (iotype != IODUP) + close(u); #ifdef KSH - if (iop->flag & IORDUP) /* possible <&p */ - /* Ensure other side of read pipe is - * closed so EOF can be read properly - */ - coproc_readw_close(u); - else /* possible >&p */ - /* If co-process input is duped, - * close shell's copy - */ + /* Touching any co-process fd in an empty exec + * causes the shell to close its copies + */ + else if (tp && tp->type == CSHELL && tp->val.f == c_exec) { + if (iop->flag & IORDUP) /* possible exec <&p */ + coproc_read_close(u); + else /* possible exec >&p */ coproc_write_close(u); -#else /* KSH */ - ; + } #endif /* KSH */ - } else - close(u); } if (u == 2) /* Clear any write errors */ shf_reopen(2, SHF_WR, shl_out); @@ -1387,27 +1465,35 @@ herein(hname, sub) * print the args in column form - assuming that we can */ static char * -do_selectargs(ap) +do_selectargs(ap, print_menu) register char **ap; + bool_t print_menu; { static const char *const read_args[] = { "read", "-r", "REPLY", (char *) 0 }; char *s; - int i, UNINITIALIZED(argct); + int i, argct; + for (argct = 0; ap[argct]; argct++) + ; while (1) { - argct = pr_menu(ap); + /* Menu is printed if + * - this is the first time around the select loop + * - the user enters a blank line + * - the REPLY parameter is empty + */ + if (print_menu || !*str_val(global("REPLY"))) + pr_menu(ap); shellf("%s", str_val(global("PS3"))); if (call_builtin(findcom("read", FC_BI), (char **) read_args)) return (char *) 0; s = str_val(global("REPLY")); - while (*s && isspace(*s)) - s++; if (*s) { i = atoi(s); return (i >= 1 && i <= argct) ? ap[i - 1] : null; } + print_menu = 1; } } diff --git a/bin/ksh/expr.c b/bin/ksh/expr.c index bd94ca68f27..2d70ed1cd1d 100644 --- a/bin/ksh/expr.c +++ b/bin/ksh/expr.c @@ -1,21 +1,10 @@ -/* $OpenBSD: expr.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: expr.c,v 1.2 1996/08/19 20:08:50 downsj Exp $ */ /* * Korn expression evaluation */ /* * todo: better error handling: if in builtin, should be builtin error, etc. - * todo: add ++ -- - * todo: recursive variable expansion (y=1;x=y; let x) - how to deal with allowing: - i=0 - set -A x 'x[1]' 'x[2]' 'x[3]' 99 - let z=x[i+=1] - echo $z - 99 - and disallowing: - x='y[x]' - let z=x */ #include "sh.h" @@ -24,8 +13,10 @@ /* The order of these enums is constrained by the order of opinfo[] */ enum token { + /* some (long) unary operators */ + O_PLUSPLUS = 0, O_MINUSMINUS, /* binary operators */ - O_EQ = 0, O_NE, + O_EQ, O_NE, /* assignments are assumed to be in range O_ASN .. O_BORASN */ O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN, O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN, @@ -39,6 +30,7 @@ enum token { O_BXOR, O_BOR, O_TERN, + O_COMMA, /* things after this aren't used as binary operators */ /* unary that are not also binaries */ O_BNOT, O_LNOT, @@ -47,7 +39,7 @@ enum token { /* things that don't appear in the opinfo[] table */ VAR, LIT, END, BAD }; -#define LAST_BINOP O_TERN +#define IS_BINOP(op) (((int)op) >= O_EQ && ((int)op) <= O_COMMA) #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN) enum prec { @@ -63,9 +55,10 @@ enum prec { P_LAND, /* && */ P_LOR, /* || */ P_TERN, /* ?: */ - P_ASSIGN /* = *= /= %= += -= <<= >>= &= ^= |= */ + P_ASSIGN, /* = *= /= %= += -= <<= >>= &= ^= |= */ + P_COMMA /* , */ }; -#define MAX_PREC P_ASSIGN +#define MAX_PREC P_COMMA struct opinfo { char name[4]; @@ -78,6 +71,8 @@ struct opinfo { * of enum token too. */ static const struct opinfo opinfo[] = { + { "++", 2, P_PRIMARY }, /* before + */ + { "--", 2, P_PRIMARY }, /* before - */ { "==", 2, P_EQUALITY }, /* before = */ { "!=", 2, P_EQUALITY }, /* before ! */ { "=", 1, P_ASSIGN }, /* keep assigns in a block */ @@ -108,6 +103,7 @@ static const struct opinfo opinfo[] = { { "^", 1, P_BXOR }, { "|", 1, P_BOR }, { "?", 1, P_TERN }, + { ",", 1, P_COMMA }, { "~", 1, P_PRIMARY }, { "!", 1, P_PRIMARY }, { "(", 1, P_PRIMARY }, @@ -122,12 +118,16 @@ struct expr_state { const char *expression; /* expression being evaluated */ const char *tokp; /* lexical position */ enum token tok; /* token from token() */ - int noassign; /* don't do assignments (for ?:) */ + int noassign; /* don't do assigns (for ?:,&&,||) */ struct tbl *val; /* value from token() */ + struct tbl *evaling; /* variable that is being recursively + * expanded (EXPRINEVAL flag set) + */ Expr_state *volatile prev; /* previous state */ }; -enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_BADVAR, ET_STR }; +enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE, + ET_LVALUE, ET_RDONLY, ET_STR }; static Expr_state *es; @@ -135,6 +135,9 @@ static void evalerr ARGS((enum error_type type, const char *str)) GCC_FUNC_ATTR(noreturn); static struct tbl *evalexpr ARGS((enum prec prec)); static void token ARGS((void)); +static struct tbl *do_ppmm ARGS((enum token op, struct tbl *vasn, + bool_t is_prefix)); +static void assign_check ARGS((enum token op, struct tbl *vasn)); static struct tbl *tempvar ARGS((void)); static struct tbl *intvar ARGS((struct tbl *vp)); @@ -174,11 +177,15 @@ v_evaluate(vp, expr, error_ok) curstate.expression = curstate.tokp = expr; curstate.noassign = 0; curstate.prev = es; + curstate.evaling = (struct tbl *) 0; es = &curstate; newenv(E_ERRH); i = ksh_sigsetjmp(e->jbuf, 0); if (i) { + /* Clear EXPRINEVAL in of any variables we were playing with */ + if (curstate.evaling) + curstate.evaling->flag &= ~EXPRINEVAL; quitenv(); es = curstate.prev; if (i == LAEXPR) { @@ -248,8 +255,18 @@ evalerr(type, str) warningf(TRUE, "%s: bad number `%s'", es->expression, str); break; - case ET_BADVAR: - warningf(TRUE, "%s: value of variable `%s' not a number", + case ET_RECURSIVE: + warningf(TRUE, "%s: expression recurses on parameter `%s'", + es->expression, str); + break; + + case ET_LVALUE: + warningf(TRUE, "%s: %s requires lvalue", + es->expression, str); + break; + + case ET_RDONLY: + warningf(TRUE, "%s: %s applied to read only variable", es->expression, str); break; @@ -289,6 +306,10 @@ evalexpr(prec) if (es->tok != CLOSE_PAREN) evalerr(ET_STR, "missing )"); token(); + } else if (op == O_PLUSPLUS || op == O_MINUSMINUS) { + token(); + vl = do_ppmm(op, es->val, TRUE); + token(); } else if (op == VAR || op == LIT) { vl = es->val; token(); @@ -296,20 +317,22 @@ evalexpr(prec) evalerr(ET_UNEXPECTED, (char *) 0); /*NOTREACHED*/ } + if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) { + vl = do_ppmm(es->tok, vl, FALSE); + token(); + } return vl; } vl = evalexpr(((int) prec) - 1); - while ((int) (op = es->tok) <= (int) LAST_BINOP && opinfo[(int) op].prec == prec) { + for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec; + op = es->tok) + { token(); vasn = vl; if (op != O_ASN) /* vl may not have a value yet */ vl = intvar(vl); if (IS_ASSIGNOP(op)) { - if (vasn->name[0] == '\0') - evalerr(ET_STR, "assignment to non-lvalue"); - else if (vasn->flag & RDONLY) - evalerr(ET_STR, - "assignment to read only variable"); + assign_check(op, vasn); vr = intvar(evalexpr(P_ASSIGN)); } else if (op != O_TERN && op != O_LAND && op != O_LOR) vr = intvar(evalexpr(((int) prec) - 1)); @@ -409,7 +432,7 @@ evalexpr(prec) token(); if (e) es->noassign++; - vr = evalexpr(MAX_PREC); + vr = evalexpr(P_TERN); if (e) es->noassign--; vl = e ? vl : vr; @@ -418,6 +441,9 @@ evalexpr(prec) case O_ASN: res = vr->val.i; break; + case O_COMMA: + res = vr->val.i; + break; } if (IS_ASSIGNOP(op)) { vr->val.i = res; @@ -447,20 +473,21 @@ token() if (c == '\0') es->tok = END; else if (letter(c)) { - for (; letnum(c); c = *cp++) - ; + for (; letnum(c); c = *cp) + cp++; if (c == '[') { int len; - len = array_ref_len(cp - 1); + len = array_ref_len(cp); if (len == 0) evalerr(ET_STR, "missing ]"); cp += len; } - if (es->noassign) + if (es->noassign) { es->val = tempvar(); - else { - tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP); + es->val->flag |= EXPRLVALUE; + } else { + tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP); es->val = global(tvar); afree(tvar, ATEMP); } @@ -494,6 +521,41 @@ token() es->tokp = cp; } +/* Do a ++ or -- operation */ +static struct tbl * +do_ppmm(op, vasn, is_prefix) + enum token op; + struct tbl *vasn; + bool_t is_prefix; +{ + struct tbl *vl; + int oval; + + assign_check(op, vasn); + + vl = intvar(vasn); + oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--; + if (vasn->flag & INTEGER) + setint_v(vasn, vl); + else + setint(vasn, vl->val.i); + if (!is_prefix) /* undo the inc/dec */ + vl->val.i = oval; + + return vl; +} + +static void +assign_check(op, vasn) + enum token op; + struct tbl *vasn; +{ + if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)) + evalerr(ET_LVALUE, opinfo[op].name); + else if (vasn->flag & RDONLY) + evalerr(ET_RDONLY, opinfo[op].name); +} + static struct tbl * tempvar() { @@ -517,22 +579,18 @@ intvar(vp) /* try to avoid replacing a temp var with another temp var */ if (vp->name[0] == '\0' - && (vp->flag & (ISSET|INTEGER)) == (ISSET|INTEGER)) + && (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER)) return vp; vq = tempvar(); - vq->type = 0; if (setint_v(vq, vp) == NULL) { - evalerr(ET_BADVAR, vp->name); - /* - if ((vp->flag&ISSET) && vp->val.s && *(vp->val.s)) { - evalerr("bad number"); - } else { - vq->flag |= (ISSET|INTEGER); - vq->type = 10; - vq->val.i = 0; - } - */ + if (vp->flag & EXPRINEVAL) + evalerr(ET_RECURSIVE, vp->name); + es->evaling = vp; + vp->flag |= EXPRINEVAL; + v_evaluate(vq, str_val(vp), FALSE); + vp->flag &= ~EXPRINEVAL; + es->evaling = (struct tbl *) 0; } return vq; } diff --git a/bin/ksh/io.c b/bin/ksh/io.c index 492c82e3b99..a5bfe1569a9 100644 --- a/bin/ksh/io.c +++ b/bin/ksh/io.c @@ -1,4 +1,4 @@ -/* $OpenBSD: io.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: io.c,v 1.2 1996/08/19 20:08:51 downsj Exp $ */ /* * shell buffered IO and formatted output @@ -324,7 +324,7 @@ check_fd(name, mode, emsgp) } #ifdef KSH else if (name[0] == 'p' && !name[1]) - return get_coproc_fd(mode, emsgp); + return coproc_getfd(mode, emsgp); #endif /* KSH */ if (emsgp) *emsgp = "illegal file descriptor name"; @@ -337,6 +337,8 @@ void coproc_init() { coproc.read = coproc.readw = coproc.write = -1; + coproc.njobs = 0; + coproc.id = 0; } /* Called by c_read() when eof is read - close fd if it is the co-process fd */ @@ -345,12 +347,9 @@ coproc_read_close(fd) int fd; { if (coproc.read >= 0 && fd == coproc.read) { + coproc_readw_close(fd); close(coproc.read); coproc.read = -1; - if (coproc.readw >= 0) { - close(coproc.readw); - coproc.readw = -1; - } } } @@ -361,7 +360,7 @@ void coproc_readw_close(fd) int fd; { - if (coproc.read >= 0 && fd == coproc.read && coproc.readw >= 0) { + if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) { close(coproc.readw); coproc.readw = -1; } @@ -384,7 +383,7 @@ coproc_write_close(fd) * (Used by check_fd() and by c_read/c_print to deal with -p option). */ int -get_coproc_fd(mode, emsgp) +coproc_getfd(mode, emsgp) int mode; const char **emsgp; { @@ -397,12 +396,13 @@ get_coproc_fd(mode, emsgp) return -1; } -/* called to close file descriptors related to the coprocess (if any) */ +/* called to close file descriptors related to the coprocess (if any) + * Should be called with SIGCHLD blocked. + */ void -cleanup_coproc(reuse) +coproc_cleanup(reuse) int reuse; { - coproc.job = (void *) 0; /* This to allow co-processes to share output pipe */ if (!reuse || coproc.readw < 0 || coproc.read < 0) { if (coproc.read >= 0) { diff --git a/bin/ksh/jobs.c b/bin/ksh/jobs.c index d40a092ac4b..e1c11fcd04a 100644 --- a/bin/ksh/jobs.c +++ b/bin/ksh/jobs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: jobs.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: jobs.c,v 1.2 1996/08/19 20:08:52 downsj Exp $ */ /* * Process and job control @@ -140,6 +140,7 @@ struct job { clock_t usrtime; /* user time used by job */ Proc *proc_list; /* process list */ Proc *last_proc; /* last process in list */ + Coproc_id coproc_id; /* 0 or id of coprocess output pipe */ #ifdef TTY_PGRP TTY_state ttystate; /* saved tty state for stopped jobs */ #endif /* TTY_PGRP */ @@ -177,7 +178,6 @@ static int child_max; /* CHILD_MAX */ #ifdef JOB_SIGS -static sigset_t sm_default, sm_sigchld; /* held_sigchld is set if sigchld occurs before a job is completely started */ static int held_sigchld; #endif /* JOB_SIGS */ @@ -234,7 +234,8 @@ j_init(mflagset) sigemptyset(&sm_sigchld); sigaddset(&sm_sigchld, SIGCHLD); - setsig(&sigtraps[SIGCHLD], j_sigchld, SS_RESTORE_ORIG|SS_FORCE); + setsig(&sigtraps[SIGCHLD], j_sigchld, + SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); #else /* JOB_SIGS */ /* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */ setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE); @@ -253,9 +254,12 @@ j_init(mflagset) if (Flag(FMONITOR) || Flag(FTALKING)) { int i; - /* j_change() sets these to SS_RESTORE_DFL if FMONITOR */ + /* the TF_SHELL_USES test is a kludge that lets us know if + * if the signals have been changed by the shell. + */ for (i = NELEM(tt_sigs); --i >= 0; ) { sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES; + /* j_change() sets this to SS_RESTORE_DFL if FMONITOR */ setsig(&sigtraps[tt_sigs[i]], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); } @@ -397,9 +401,6 @@ j_change() } else { # ifdef TTY_PGRP ttypgrp_ok = 0; - /* the TF_SHELL_USES test is a kludge that lets us know if - * if the signals have been changed by the shell. - */ if (Flag(FTALKING)) for (i = NELEM(tt_sigs); --i >= 0; ) setsig(&sigtraps[tt_sigs[i]], SIG_IGN, @@ -410,7 +411,7 @@ j_change() |TF_ORIG_DFL)) setsig(&sigtraps[tt_sigs[i]], (sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ? SIG_IGN : SIG_DFL, - SS_RESTORE_CURR|SS_FORCE); + SS_RESTORE_ORIG|SS_FORCE); } # endif /* TTY_PGRP */ if (!Flag(FTALKING)) @@ -483,12 +484,9 @@ exchild(t, flags, close_fd) j->ppid = procpid; j->age = ++njobs; j->proc_list = p; + j->coproc_id = 0; last_job = j; last_proc = p; - if (flags & XXCOM) - j->flags |= JF_XXCOM; - else if (!(flags & XBGND)) - j->flags |= JF_FG; put_job(j, PJ_PAST_STOPPED); } @@ -535,8 +533,8 @@ exchild(t, flags, close_fd) dotty = 1; # ifdef NEED_PGRP_SYNC if (j_sync_open) { - close(j_sync_pipe[ischild ? 1 : 0]); - j_sync_pipe[ischild ? 1 : 0] = -1; + close(j_sync_pipe[ischild]); + j_sync_pipe[ischild] = -1; dosync = ischild; } # endif /* NEED_PGRP_SYNC */ @@ -571,18 +569,19 @@ exchild(t, flags, close_fd) #endif /* JOBS */ /* used to close pipe input fd */ - if (close_fd >= 0 && (((orig_flags & XPCLOSE) && i != 0) - || ((orig_flags & XCCLOSE) && i == 0))) + if (close_fd >= 0 && (((orig_flags & XPCLOSE) && !ischild) + || ((orig_flags & XCCLOSE) && ischild))) close(close_fd); - if (i == 0) { /* child */ + if (ischild) { /* child */ +#ifdef KSH + /* Do this before restoring signal */ + if (orig_flags & XCOPROC) + coproc_cleanup(FALSE); +#endif /* KSH */ #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ cleanup_parents_env(); -#ifdef KSH - if (orig_flags & XCOPROC) - cleanup_coproc(FALSE); -#endif /* KSH */ #ifdef TTY_PGRP /* If FMONITOR or FTALKING is set, these signals are ignored, * if neither FMONITOR nor FTALKING are set, the signals have @@ -604,9 +603,9 @@ exchild(t, flags, close_fd) setsig(&sigtraps[SIGQUIT], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); if (!(orig_flags & (XPIPEI | XCOPROC))) { - i = open("/dev/null", 0); - (void) ksh_dup2(i, 0, TRUE); - close(i); + int fd = open("/dev/null", 0); + (void) ksh_dup2(fd, 0, TRUE); + close(fd); } } remove_job(j, "child"); /* in case of `jobs` command */ @@ -634,8 +633,11 @@ exchild(t, flags, close_fd) #endif /* TTY_PGRP */ j_startjob(j); #ifdef KSH - if (flags & XCOPROC) - coproc.job = (void *) j; + if (orig_flags & XCOPROC) { + j->coproc_id = coproc.id; + coproc.njobs++; /* n jobs using co-process output */ + coproc.job = (void *) j; /* j using co-process input */ + } #endif /* KSH */ if (flags & XBGND) { j_set_async(j); @@ -1268,8 +1270,6 @@ j_sigchld(sig) WAIT_T status; struct tms t0, t1; - trapsig(sig); - #ifdef JOB_SIGS /* Don't wait for any processes if a job is partially started. * This is so we don't do away with the process group leader @@ -1386,9 +1386,24 @@ check_job(j) * remove_job() since neither may be called for non-interactive * shells. */ - if ((j->state == PEXITED || j->state == PSIGNALLED) - && coproc.job == (void *) j) - coproc.job = (void *) 0; + if (j->state == PEXITED || j->state == PSIGNALLED) { + /* No need to keep co-process input any more + * (at leasst, this is what ksh93d thinks) + */ + if (coproc.job == j) { + coproc.job = (void *) 0; + /* XXX would be nice to get the closes out of here + * so they aren't done in the signal handler. + * Would mean a check in coproc_getfd() to + * do "if job == 0 && write >= 0, close write". + */ + coproc_write_close(coproc.write); + } + /* Do we need to keep the output? */ + if (j->coproc_id && j->coproc_id == coproc.id + && --coproc.njobs == 0) + coproc_readw_close(coproc.read); + } #endif /* KSH */ j->flags |= JF_CHANGED; diff --git a/bin/ksh/ksh_stat.h b/bin/ksh/ksh_stat.h index 0c6e5244f57..447b7b3f385 100644 --- a/bin/ksh/ksh_stat.h +++ b/bin/ksh/ksh_stat.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ksh_stat.h,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: ksh_stat.h,v 1.2 1996/08/19 20:08:54 downsj Exp $ */ /* Wrapper around the ugly sys/stat includes/ifdefs */ @@ -15,6 +15,7 @@ # undef S_ISCHR # undef S_ISBLK # undef S_ISFIFO +# undef S_ISSOCK # undef S_ISLNK #endif /* STAT_MACROS_BROKEN */ diff --git a/bin/ksh/lex.c b/bin/ksh/lex.c index 06e912fae09..74ed0748fef 100644 --- a/bin/ksh/lex.c +++ b/bin/ksh/lex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lex.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: lex.c,v 1.2 1996/08/19 20:08:55 downsj Exp $ */ /* * lexical analysis and source input @@ -968,6 +968,7 @@ set_prompt(to, s) switch (to) { case PS1: /* command */ +#ifdef KSH /* Substitute ! and !! here, before substitutions are done * so ! in expanded variables are not expanded. * NOTE: this is not what at&t ksh does (it does it after @@ -993,12 +994,20 @@ set_prompt(to, s) newenv(E_ERRH); if (ksh_sigsetjmp(e->jbuf, 0)) { prompt = safe_prompt; - warningf(TRUE, "error during expansion of PS1"); + /* Don't print an error - assume it has already + * been printed. Reason is we may have forked + * to run a command and the child may be + * unwinding its stack through this code as it + * exits. + */ } else prompt = str_save(substitute(ps1, 0), saved_atemp); quitenv(); } +#else /* KSH */ + prompt = str_val(global("PS1")); +#endif /* KSH */ break; case PS2: /* command continuation */ diff --git a/bin/ksh/mail.c b/bin/ksh/mail.c index 3422a5873cc..a26bde87579 100644 --- a/bin/ksh/mail.c +++ b/bin/ksh/mail.c @@ -1,10 +1,13 @@ -/* $OpenBSD: mail.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: mail.c,v 1.2 1996/08/19 20:08:55 downsj Exp $ */ /* * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by * John R. MacMillan */ +#include "config.h" + +#ifdef KSH #include "sh.h" #include "ksh_stat.h" #include "ksh_time.h" @@ -183,3 +186,4 @@ mbox_t *mbp; unset(vp, 0); } +#endif /* KSH */ diff --git a/bin/ksh/main.c b/bin/ksh/main.c index 10290b31135..74118b7f037 100644 --- a/bin/ksh/main.c +++ b/bin/ksh/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: main.c,v 1.2 1996/08/19 20:08:56 downsj Exp $ */ /* * startup, main loop, enviroments and error handling @@ -41,9 +41,9 @@ static const char *const initcoms [] = { "typeset", "-x", "SHELL", "PATH", "HOME", NULL, "typeset", "-r", version_param, NULL, "typeset", "-ri", "PPID", NULL, - "typeset", "-i", "OPTIND=1", "MAILCHECK=600", + "typeset", "-i", "OPTIND=1", #ifdef KSH - "SECONDS=0", "RANDOM", "TMOUT=0", + "MAILCHECK=600", "RANDOM", "SECONDS=0", "TMOUT=0", #endif /* KSH */ NULL, "alias", @@ -278,7 +278,7 @@ main(argc, argv) * This changes the behavior of 'ksh arg' to search * the users search path but it can't be helped. */ - s->file = search(argv[argi++], path, R_OK); + s->file = search(argv[argi++], path, R_OK, (int *) 0); if (!s->file || !*s->file) s->file = argv[argi - 1]; #else @@ -406,7 +406,9 @@ main(argc, argv) if (Flag(FTALKING)) { hist_init(s); +#ifdef KSH alarm_init(); +#endif /* KSH */ } else Flag(FTRACKALL) = 1; /* set after ENV */ @@ -568,7 +570,9 @@ shell(s, toplevel) if (interactive) { j_notify(); +#ifdef KSH mcheck(); +#endif /* KSH */ set_prompt(PS1, s); } diff --git a/bin/ksh/misc.c b/bin/ksh/misc.c index dcf7cc9bf63..dcbd8130b18 100644 --- a/bin/ksh/misc.c +++ b/bin/ksh/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: misc.c,v 1.2 1996/08/19 20:08:57 downsj Exp $ */ /* * Miscellaneous functions @@ -14,7 +14,7 @@ # define UCHAR_MAX 0xFF #endif -char ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */ +short ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */ static int do_gmatch ARGS((const unsigned char *s, const unsigned char *p, const unsigned char *se, const unsigned char *pe, @@ -31,7 +31,7 @@ setctypes(s, t) { register int i; - if ((t&C_IFS)) { + if (t & C_IFS) { for (i = 0; i < UCHAR_MAX+1; i++) ctypes[i] &= ~C_IFS; ctypes[0] |= C_IFS; /* include \0 in C_IFS */ @@ -56,6 +56,7 @@ initctypes() setctypes(" \t\n", C_IFSWS); setctypes("=-+?", C_SUBOP1); setctypes("#%", C_SUBOP2); + setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE); } /* convert unsigned long to base N string */ @@ -168,6 +169,7 @@ const struct option options[] = { { "viraw", 0, OF_ANY }, /* no effect */ { "vi-show8", 0, OF_ANY }, /* non-standard */ { "vi-tabcomplete", 0, OF_ANY }, /* non-standard */ + { "vi-esccomplete", 0, OF_ANY }, /* non-standard */ #endif { "xtrace", 'x', OF_ANY }, { NULL, 0, 0 } @@ -982,7 +984,7 @@ ksh_getopt(argv, go, options) go->buf[0] = c; go->optarg = go->buf; } else { - warningf(TRUE, "%s%s-%c: bad option", + warningf(TRUE, "%s%s-%c: unknown option", (go->flags & GF_NONAME) ? "" : argv[0], (go->flags & GF_NONAME) ? "" : ": ", c); if (go->flags & GF_ERROR) @@ -1055,7 +1057,7 @@ print_value_quoted(s) /* Test if any quotes are needed */ for (p = s; *p; p++) - if (!letnum(*p) && *p != '/') + if (ctype(*p, C_QUOTE)) break; if (!*p) { shprintf("%s", s); diff --git a/bin/ksh/proto.h b/bin/ksh/proto.h index 067f28c64d0..a290e18a205 100644 --- a/bin/ksh/proto.h +++ b/bin/ksh/proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proto.h,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: proto.h,v 1.2 1996/08/19 20:08:57 downsj Exp $ */ /* * prototypes for PD-KSH @@ -73,8 +73,9 @@ int define ARGS((const char *name, struct op *t)); void builtin ARGS((const char *name, int (*func)(char **))); struct tbl * findcom ARGS((const char *name, int flags)); void flushcom ARGS((int all)); -char * search ARGS((const char *name, const char *path, int mode)); -int search_access ARGS((const char *path, int mode)); +char * search ARGS((const char *name, const char *path, int mode, + int *errnop)); +int search_access ARGS((const char *path, int mode, int *errnop)); int pr_menu ARGS((char *const *ap)); /* expr.c */ int evaluate ARGS((const char *expr, long *rval, int error_ok)); @@ -124,8 +125,8 @@ void coproc_init ARGS((void)); void coproc_read_close ARGS((int fd)); void coproc_readw_close ARGS((int fd)); void coproc_write_close ARGS((int fd)); -int get_coproc_fd ARGS((int mode, const char **emsgp)); -void cleanup_coproc ARGS((int reuse)); +int coproc_getfd ARGS((int mode, const char **emsgp)); +void coproc_cleanup ARGS((int reuse)); #endif /* KSH */ struct temp *maketemp ARGS((Area *ap)); /* jobs.c */ @@ -150,9 +151,11 @@ Source * pushs ARGS((int type, Area *areap)); void set_prompt ARGS((int to, Source *s)); void pprompt ARGS((const char *cp, int ntruncate)); /* mail.c */ +#ifdef KSH void mcheck ARGS((void)); void mbset ARGS((char *p)); void mpset ARGS((char *mptoparse)); +#endif /* KSH */ /* main.c */ int include ARGS((const char *name, int argc, char **argv, int intr_ok)); @@ -215,7 +218,9 @@ struct tbl ** tsort ARGS((struct table *tp)); /* trace.c */ /* trap.c */ void inittraps ARGS((void)); +#ifdef KSH void alarm_init ARGS((void)); +#endif /* KSH */ Trap * gettrap ARGS((const char *name)); RETSIGTYPE trapsig ARGS((int i)); void intrcheck ARGS((void)); @@ -249,7 +254,7 @@ void setstr ARGS((struct tbl *vq, const char *s)); struct tbl *setint_v ARGS((struct tbl *vq, struct tbl *vp)); void setint ARGS((struct tbl *vq, long n)); int getint ARGS((struct tbl *vp, long *nump)); -struct tbl * typeset ARGS((const char *var, int set, int clr, int field, int base)); +struct tbl * typeset ARGS((const char *var, Tflag set, Tflag clr, int field, int base)); void unset ARGS((struct tbl *vp, int array_ref)); char * skip_varname ARGS((const char *s, int aok)); char *skip_wdvarname ARGS((const char *s, int aok)); diff --git a/bin/ksh/sh.h b/bin/ksh/sh.h index b7fcde1ac93..f6578427d9c 100644 --- a/bin/ksh/sh.h +++ b/bin/ksh/sh.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sh.h,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: sh.h,v 1.2 1996/08/19 20:08:58 downsj Exp $ */ /* * Public Domain Bourne/Korn shell @@ -331,6 +331,9 @@ typedef int bool_t; #define sizeofN(type, n) (sizeof(type) * (n)) #define BIT(i) (1<<(i)) /* define bit in flag */ +/* Table flag type - needs > 16 and < 32 bits */ +typedef INT32 Tflag; + #define NUFILE 10 /* Number of user-accessible files */ #define FDBASE 10 /* First file usable by Shell */ @@ -477,6 +480,7 @@ enum sh_flag { FVIRAW, /* always read in raw mode (ignored) */ FVISHOW8, /* display chars with 8th bit set as is (versus M-) */ FVITABCOMPLETE, /* enable tab as file name completion char */ + FVIESCCOMPLETE, /* enable ESC as file name completion in command mode */ #endif FXTRACE, /* -x: execution trace */ FNFLAGS /* (place holder: how many flags are there) */ @@ -523,6 +527,7 @@ typedef struct trap { int volatile set; /* trap pending */ int flags; /* TF_* */ handler_t cursig; /* current handler (valid if TF_ORIG_* set) */ + handler_t shtrap; /* shell signal handler */ } Trap; /* values for Trap.flags */ @@ -541,10 +546,11 @@ typedef struct trap { #define SS_RESTORE_MASK 0x3 /* how to restore a signal before an exec() */ #define SS_RESTORE_CURR 0 /* leave current handler in place */ #define SS_RESTORE_ORIG 1 /* restore original handler */ -#define SS_RESTORE_DFL 2 /* restore SIG_DFL */ -#define SS_RESTORE_IGN 3 /* restore SIG_IGN */ +#define SS_RESTORE_DFL 2 /* restore to SIG_DFL */ +#define SS_RESTORE_IGN 3 /* restore to SIG_IGN */ #define SS_FORCE BIT(3) /* set signal even if original signal ignored */ #define SS_USER BIT(4) /* user is doing the set (ie, trap command) */ +#define SS_SHTRAP BIT(5) /* trap for internal use (CHLD,ALRM,WINCH) */ #define SIGEXIT_ 0 /* for trap EXIT */ #define SIGERR_ SIGNALS /* for trap ERR */ @@ -558,6 +564,7 @@ extern Trap sigtraps[SIGNALS+1]; #endif /* !FROM_TRAP_C */ +#ifdef KSH /* * TMOUT support */ @@ -569,6 +576,7 @@ enum tmout_enum { }; EXTERN unsigned int ksh_tmout; EXTERN enum tmout_enum ksh_tmout_state I__(TMOUT_EXECUTING); +#endif /* KSH */ /* For "You have stopped jobs" message */ @@ -578,16 +586,17 @@ EXTERN int really_exit; /* * fast character classes */ -#define C_ALPHA 0x01 /* a-z_A-Z */ -#define C_DIGIT 0x02 /* 0-9 */ -#define C_LEX1 0x04 /* \0 \t\n|&;<>() */ -#define C_VAR1 0x08 /* *@#!$-? */ -#define C_IFSWS 0x10 /* \t \n (IFS white space) */ -#define C_SUBOP1 0x20 /* "=-+?" */ -#define C_SUBOP2 0x40 /* "#%" */ -#define C_IFS 0x80 /* $IFS */ - -extern char ctypes []; +#define C_ALPHA BIT(0) /* a-z_A-Z */ +#define C_DIGIT BIT(1) /* 0-9 */ +#define C_LEX1 BIT(2) /* \0 \t\n|&;<>() */ +#define C_VAR1 BIT(3) /* *@#!$-? */ +#define C_IFSWS BIT(4) /* \t \n (IFS white space) */ +#define C_SUBOP1 BIT(5) /* "=-+?" */ +#define C_SUBOP2 BIT(6) /* "#%" */ +#define C_IFS BIT(7) /* $IFS */ +#define C_QUOTE BIT(8) /* \n\t"#$&'()*;<>?[\`| (needing quoting) */ + +extern short ctypes []; #define ctype(c, t) !!(ctypes[(unsigned char)(c)]&(t)) #define letter(c) ctype(c, C_ALPHA) @@ -623,20 +632,29 @@ EXTERN Getopt builtin_opt; /* for shell builtin commands */ #ifdef KSH /* This for co-processes */ + +typedef INT32 Coproc_id; /* something that won't (realisticly) wrap */ struct coproc { int read; /* pipe from co-process's stdout */ int readw; /* other side of read (saved temporarily) */ int write; /* pipe to co-process's stdin */ - void *job; /* 0 if no co-process, or co-process died */ + Coproc_id id; /* id of current output pipe */ + int njobs; /* number of live jobs using output pipe */ + void *job; /* 0 or job of co-process using input pipe */ }; EXTERN struct coproc coproc; #endif /* KSH */ +/* Used in jobs.c and by coprocess stuff in exec.c */ +#ifdef JOB_SIGS +EXTERN sigset_t sm_default, sm_sigchld; +#endif /* JOB_SIGS */ + extern const char ksh_version[]; /* name of called builtin function (used by error functions) */ EXTERN char *builtin_argv0; -EXTERN int builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */ +EXTERN Tflag builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */ /* current working directory, and size of memory allocated for same */ EXTERN char *current_wd; diff --git a/bin/ksh/syn.c b/bin/ksh/syn.c index bd5db3740dd..8779c315841 100644 --- a/bin/ksh/syn.c +++ b/bin/ksh/syn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: syn.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: syn.c,v 1.2 1996/08/19 20:08:59 downsj Exp $ */ /* * shell parser (C version) @@ -224,7 +224,7 @@ get_command(cf) syniocf &= ~(KEYWORD|ALIAS); t = newtp(TCOM); while (1) { - cf = (t->evalflags ? ARRAYVAR : 0) + cf = (t->u.evalflags ? ARRAYVAR : 0) | (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD); switch (tpeek(cf)) { case REDIR: @@ -241,7 +241,7 @@ get_command(cf) if (iopn == 0 && XPsize(vars) == 0 && XPsize(args) == 0 && assign_command(ident)) - t->evalflags = DOVACHECK; + t->u.evalflags = DOVACHECK; if ((XPsize(args) == 0 || Flag(FKEYWORD)) && is_wdvarassign(yylval.cp)) XPput(vars, yylval.cp); @@ -254,7 +254,7 @@ get_command(cf) * allows (not POSIX, but not disallowed) */ afree(t, ATEMP); - if (XPsize(args) == 0 && XPsize(vars) != 0) { + if (XPsize(args) == 0 && XPsize(vars) == 0) { ACCEPT; goto Subshell; } @@ -334,7 +334,7 @@ get_command(cf) case WHILE: case UNTIL: multiline_push(&old_multiline, c); - t = newtp((c == WHILE) ? TWHILE: TUNTIL); + t = newtp((c == WHILE) ? TWHILE : TUNTIL); t->left = c_list(); t->right = dogroup(); multiline_pop(&old_multiline); @@ -552,6 +552,7 @@ function_body(name, ksh_func) } t = newtp(TFUNCT); t->str = Xclose(xs, xp); + t->u.ksh_func = ksh_func; /* Note that POSIX allows only compound statements after foo(), sh and * at&t ksh allow any command, go with the later since it shouldn't @@ -763,7 +764,7 @@ newtp(type) t = (struct op *) alloc(sizeof(*t), ATEMP); t->type = type; - t->evalflags = 0; + t->u.evalflags = 0; t->args = t->vars = NULL; t->ioact = NULL; t->left = t->right = NULL; diff --git a/bin/ksh/table.c b/bin/ksh/table.c index 03558fa137a..3b2861bb63f 100644 --- a/bin/ksh/table.c +++ b/bin/ksh/table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: table.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: table.c,v 1.2 1996/08/19 20:08:59 downsj Exp $ */ /* * dynamic hashed associative table for commands and variables @@ -64,7 +64,7 @@ texpand(tp, nsize) p += tp->size; *p = tblp; tp->nfree--; - } else { + } else if (!(tblp->flag & FINUSE)) { afree((void*)tblp, tp->areap); } afree((void*)otblp, tp->areap); @@ -125,7 +125,7 @@ tenter(tp, n, h) p->flag = 0; p->type = 0; p->areap = tp->areap; - p->field = 0; + p->u2.field = 0; p->u.array = (struct tbl *)0; memcpy(p->name, n, len); diff --git a/bin/ksh/table.h b/bin/ksh/table.h index 1dfc08942fa..753339c33bc 100644 --- a/bin/ksh/table.h +++ b/bin/ksh/table.h @@ -1,4 +1,4 @@ -/* $OpenBSD: table.h,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: table.h,v 1.2 1996/08/19 20:09:00 downsj Exp $ */ /* $From: table.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */ @@ -13,7 +13,7 @@ struct table { }; struct tbl { /* table item */ - INT32 flag; /* flags */ + Tflag flag; /* flags */ int type; /* command type (see below), base (if INTEGER), * or offset from val.s of value (if EXPORT) */ Area *areap; /* area to allocate from */ @@ -24,7 +24,10 @@ struct tbl { /* table item */ struct op *t; /* "function" tree */ } val; /* value */ int index; /* index for an array */ - int field; /* field with for -L/-R/-Z */ + union { + int field; /* field with for -L/-R/-Z */ + int errno_; /* CEXEC/CTALIAS */ + } u2; union { struct tbl *array; /* array values */ char *fpath; /* temporary path to undef function */ @@ -54,12 +57,15 @@ struct tbl { /* table item */ #define INT_L BIT(20) /* long integer (no-op) */ #define IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */ #define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */ +#define EXPRINEVAL BIT(23) /* contents currently being evaluated */ +#define EXPRLVALUE BIT(24) /* useable as lvalue (temp flag) */ /* flag bits used for taliases/builtins/aliases/keywords/functions */ #define KEEPASN BIT(8) /* keep command assignments (eg, var=x cmd) */ #define FINUSE BIT(9) /* function being executed */ #define FDELETE BIT(10) /* function deleted while it was executing */ -#define SPEC_BI BIT(11) /* a POSIX special builtin */ -#define REG_BI BIT(12) /* a POSIX regular builtin */ +#define FKSH BIT(11) /* function defined with function x (vs x()) */ +#define SPEC_BI BIT(12) /* a POSIX special builtin */ +#define REG_BI BIT(13) /* a POSIX regular builtin */ /* command types */ #define CNONE 0 /* undefined */ diff --git a/bin/ksh/tests/arith.t b/bin/ksh/tests/arith.t new file mode 100644 index 00000000000..e18ea2e9d78 --- /dev/null +++ b/bin/ksh/tests/arith.t @@ -0,0 +1,79 @@ +name: arith-lazy-1 +description: + Check that only one side of ternary operator is evaluated +stdin: + x=i+=2 + y=j+=2 + typeset -i i=1 j=1 + echo $((1 ? 20 : (x+=2))) + echo $i,$x + echo $((0 ? (y+=2) : 30)) + echo $j,$y +expected-stdout: + 20 + 1,i+=2 + 30 + 1,j+=2 +--- + +name: arith-lazy-2 +description: + Check that assignments not done on non-evaluated side of ternary + operator +stdin: + x=i+=2 + y=j+=2 + typeset -i i=1 j=1 + echo $((1 ? 20 : (x+=2))) + echo $i,$x + echo $((0 ? (y+=2) : 30)) + echo $i,$y +expected-stdout: + 20 + 1,i+=2 + 30 + 1,j+=2 +--- + +name: arith-ternary-prec-1 +description: + Check precidance of ternary operator vs assignment +stdin: + typeset -i x=2 + y=$((1 ? 20 : x+=2)) +expected-exit: e != 0 +expected-stderr-pattern: + /.*:.*1 \? 20 : x\+=2.*lvalue.*\n$/ +--- + +name: arith-ternary-prec-2 +description: + Check precidance of ternary operator vs assignment +stdin: + typeset -i x=2 + echo $((0 ? x+=2 : 20)) +expected-stdout: + 20 +--- + +name: arith-div-assoc-1 +description: + Check associativity of division operator +stdin: + echo $((20 / 2 / 2)) +expected-stdout: + 5 +--- + +name: arith-assop-assoc-1 +description: + Check associativity of assignment-operator operator +stdin: + typeset -i i=1 j=2 k=3 + echo $((i += j += k)) + echo $i,$j,$k +expected-stdout: + 6 + 6,5,3 +--- + diff --git a/bin/ksh/tests/regress.t b/bin/ksh/tests/regress.t index 95fe97b6b7c..d78a02641de 100644 --- a/bin/ksh/tests/regress.t +++ b/bin/ksh/tests/regress.t @@ -639,3 +639,27 @@ expected-exit: e != 0 expected-stderr-pattern: /.*read *only.*/ --- + +name: regression-43 +description: + Can subshells be prefixed by redirections (historical shells allow + this) +stdin: + < /dev/null (cat -n) +--- + +name: regression-44 +description: + getopts sets OPTIND correctly for unparsed option +stdin: + set -- -a -a -x + while getopts :a optc; do + echo "OPTARG=$OPTARG, OPTIND=$OPTIND, optc=$optc." + done + echo done +expected-stdout: + OPTARG=, OPTIND=2, optc=a. + OPTARG=, OPTIND=3, optc=a. + OPTARG=x, OPTIND=3, optc=?. + done +--- diff --git a/bin/ksh/tests/th b/bin/ksh/tests/th index 496058ccd4e..340cbe2d5dc 100644 --- a/bin/ksh/tests/th +++ b/bin/ksh/tests/th @@ -169,7 +169,13 @@ foreach $env (('USER', 'LOGNAME', 'HOME', 'PATH', 'SHELL')) { $new_env{$env} = $ENV{$env} if defined $ENV{$env}; } %old_env = %ENV; -%ENV = %new_env; + +# The following doesn't work with perl5... Need to do it explicitly - yuck. +#%ENV = %new_env; +foreach $k (keys(%ENV)) { + delete $ENV{$k}; +} +$ENV{$k} = $v while ($k,$v) = each %new_env; die "$prog: couldn't make directory $tempdir - $!\n" if !mkdir($tempdir, 0777); diff --git a/bin/ksh/tests/th.sh b/bin/ksh/tests/th.sh index 6e40c19dcf9..f26b8699e94 100644 --- a/bin/ksh/tests/th.sh +++ b/bin/ksh/tests/th.sh @@ -4,12 +4,18 @@ # Simple script to find perl and run it # +# Avoid common problems with ENV (though perl shouldn't let it through) +unset ENV + +x=x +[ -x /bin/sh ] 2> /dev/null || x=f + IFS=:$IFS perl= for i in $PATH; do [ X"$i" = X ] && i=. for j in perl perl4 perl5 ; do - [ -x "$i/$j" ] && perl=$i/$j && break 2 + [ -$x "$i/$j" ] && perl=$i/$j && break 2 done done diff --git a/bin/ksh/tests/version.t b/bin/ksh/tests/version.t index aed94d01760..0ec6fbac7b5 100644 --- a/bin/ksh/tests/version.t +++ b/bin/ksh/tests/version.t @@ -4,5 +4,5 @@ description: stdin: echo $KSH_VERSION expected-stdout: - @(#)PD KSH v5.2.7 96/06/04 + @(#)PD KSH v5.2.8 96/08/19 --- diff --git a/bin/ksh/trap.c b/bin/ksh/trap.c index e4ecdd39efa..7933f2d7e31 100644 --- a/bin/ksh/trap.c +++ b/bin/ksh/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: trap.c,v 1.2 1996/08/19 20:09:00 downsj Exp $ */ /* * signal handling @@ -19,9 +19,7 @@ Trap sigtraps[SIGNALS+1] = { { SIGERR_, "ERR", "Error handler" }, }; -static RETSIGTYPE alarm_catcher ARGS((int sig)); - -static struct sigaction Sigact_ign, Sigact_trap, Sigact_alarm; +static struct sigaction Sigact_ign, Sigact_trap; void inittraps() @@ -43,8 +41,6 @@ inittraps() Sigact_ign.sa_handler = SIG_IGN; Sigact_trap = Sigact_ign; Sigact_trap.sa_handler = trapsig; - Sigact_alarm = Sigact_ign; - Sigact_alarm.sa_handler = alarm_catcher; sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; @@ -59,20 +55,21 @@ inittraps() setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); } +#ifdef KSH +static RETSIGTYPE alarm_catcher ARGS((int sig)); + void alarm_init() { sigtraps[SIGALRM].flags |= TF_SHELL_USES; setsig(&sigtraps[SIGALRM], alarm_catcher, - SS_RESTORE_ORIG|SS_FORCE); + SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); } static RETSIGTYPE alarm_catcher(sig) int sig; { - trapsig(sig); -#ifdef KSH if (ksh_tmout_state == TMOUT_READING) { int left = alarm(0); @@ -82,12 +79,9 @@ alarm_catcher(sig) } else alarm(left); } -#endif /* KSH */ -#ifdef V7_SIGNALS - sigaction(sig, &Sigact_alarm, (struct sigaction *) 0); -#endif /* V7_SIGNALS */ return RETSIGVAL; } +#endif /* KSH */ Trap * gettrap(name) @@ -125,6 +119,8 @@ trapsig(i) fatal_trap = 1; intrsig = 1; } + if (p->shtrap) + (*p->shtrap)(i); #ifdef V7_SIGNALS if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */ sigaction(i, &Sigact_trap, (struct sigaction *) 0); @@ -363,22 +359,37 @@ setsig(p, f, flags) if (p->signal == SIGEXIT_ || p->signal == SIGERR_) return 1; + /* First time setting this signal? If so, get and note the current + * setting. + */ if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { sigaction(p->signal, &Sigact_ign, &sigact); p->flags |= sigact.sa_handler == SIG_IGN ? TF_ORIG_IGN : TF_ORIG_DFL; p->cursig = SIG_IGN; } - if ((p->flags & TF_ORIG_IGN) && (flags & SS_USER) - && !(flags & SS_FORCE) && !Flag(FTALKING)) + + /* Generally, an ignored signal stays ignored, except if + * - the user of an interactive shell wants to change it + * - the shell wants for force a change + */ + if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) + && (!(flags & SS_USER) || !Flag(FTALKING))) return 0; - if (!(flags & SS_USER)) { - if (!(flags & SS_FORCE) && (p->flags & TF_ORIG_IGN)) - return 0; - } setexecsig(p, flags & SS_RESTORE_MASK); + /* This is here 'cause there should be a way of clearing shtraps, but + * don't know if this is a sane way of doing it. At the moment, + * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH). + */ + if (!(flags & SS_USER)) + p->shtrap = (handler_t) 0; + if (flags & SS_SHTRAP) { + p->shtrap = f; + f = trapsig; + } + if (p->cursig != f) { p->cursig = f; sigemptyset(&sigact.sa_mask); diff --git a/bin/ksh/tree.c b/bin/ksh/tree.c index 3ceb7756cf7..105158787b3 100644 --- a/bin/ksh/tree.c +++ b/bin/ksh/tree.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tree.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: tree.c,v 1.2 1996/08/19 20:09:01 downsj Exp $ */ /* * command tree climbing @@ -478,7 +478,7 @@ tcopy(t, ap) r = (struct op *) alloc(sizeof(struct op), ap); r->type = t->type; - r->evalflags = t->evalflags; + r->u.evalflags = t->u.evalflags; r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap); diff --git a/bin/ksh/tree.h b/bin/ksh/tree.h index 29be89ccc4a..863061dd91d 100644 --- a/bin/ksh/tree.h +++ b/bin/ksh/tree.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tree.h,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: tree.h,v 1.2 1996/08/19 20:09:02 downsj Exp $ */ /* * command trees for compile/execute @@ -15,7 +15,10 @@ */ struct op { short type; /* operation type, see below */ - short evalflags; /* eval() flags for arg expansion */ + union { /* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */ + short evalflags; /* TCOM: arg expansion eval() flags */ + short ksh_func; /* TFUNC: function x (vs x()) */ + } u; char **args; /* arguments to a command */ char **vars; /* variable assignments */ struct ioword **ioact; /* IO actions (eg, < > >>) */ @@ -31,7 +34,7 @@ struct op { #define TCOM 1 /* command */ #define TPAREN 2 /* (c-list) */ #define TPIPE 3 /* a | b */ -#define TLIST 4 /* a [&;] b */ +#define TLIST 4 /* a ; b */ #define TOR 5 /* || */ #define TAND 6 /* && */ #define TBANG 7 /* ! */ diff --git a/bin/ksh/var.c b/bin/ksh/var.c index 8a4922b4903..b31b41d48a1 100644 --- a/bin/ksh/var.c +++ b/bin/ksh/var.c @@ -1,4 +1,4 @@ -/* $OpenBSD: var.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: var.c,v 1.2 1996/08/19 20:09:02 downsj Exp $ */ #include "sh.h" #include "ksh_time.h" @@ -82,9 +82,6 @@ initvar() } names[] = { { "COLUMNS", V_COLUMNS }, { "IFS", V_IFS }, - { "MAIL", V_MAIL }, - { "MAILCHECK", V_MAILCHECK }, - { "MAILPATH", V_MAILPATH }, { "OPTIND", V_OPTIND }, { "PATH", V_PATH }, { "POSIXLY_CORRECT", V_POSIXLY_CORRECT }, @@ -98,6 +95,9 @@ initvar() { "VISUAL", V_VISUAL }, #endif /* EDIT */ #ifdef KSH + { "MAIL", V_MAIL }, + { "MAILCHECK", V_MAILCHECK }, + { "MAILPATH", V_MAILPATH }, { "RANDOM", V_RANDOM }, { "SECONDS", V_SECONDS }, { "TMOUT", V_TMOUT }, @@ -247,7 +247,7 @@ local(n, copy) h = hash(n); if (!letter(*n)) { vp = &vtemp; - vp->flag = (DEFINED|RDONLY); + vp->flag = DEFINED|RDONLY; vp->type = 0; vp->areap = ATEMP; return vp; @@ -265,7 +265,7 @@ local(n, copy) |LCASEV|UCASEV_AL|INT_U|INT_L); if (vq->flag & INTEGER) vp->type = vq->type; - vp->field = vq->field; + vp->u2.field = vq->u2.field; } } if (array) @@ -352,7 +352,8 @@ setstr(vq, s) if (s >= vq->val.s && s <= vq->val.s + strlen(vq->val.s)) internal_errorf(TRUE, - "setstr: assigning to self"); + "setstr: %s=%s: assigning to self", + vq->name, s); afree((void*)vq->val.s, vq->areap); } vq->flag &= ~(ISSET|ALLOC); @@ -486,9 +487,9 @@ formatstr(vp, s) olen = strlen(s); if (vp->flag & (RJUST|LJUST)) { - if (!vp->field) /* default field width */ - vp->field = olen; - nlen = vp->field; + if (!vp->u2.field) /* default field width */ + vp->u2.field = olen; + nlen = vp->u2.field; } else nlen = olen; @@ -502,14 +503,14 @@ formatstr(vp, s) while (q > s && isspace(q[-1])) --q; slen = q - s; - if (slen > vp->field) { - s += slen - vp->field; - slen = vp->field; + if (slen > vp->u2.field) { + s += slen - vp->u2.field; + slen = vp->u2.field; } shf_snprintf(p, nlen + 1, ((vp->flag & ZEROFIL) && digit(*s)) ? "%0*s%.*s" : "%*s%.*s", - vp->field - slen, null, slen, s); + vp->u2.field - slen, null, slen, s); } else { /* strip leading spaces/zeros */ while (isspace(*s)) @@ -518,7 +519,7 @@ formatstr(vp, s) while (*s == '0') s++; shf_snprintf(p, nlen + 1, "%-*.*s", - vp->field, vp->field, s); + vp->u2.field, vp->u2.field, s); } } else memcpy(p, s, olen + 1); @@ -568,7 +569,7 @@ export(vp, val) struct tbl * typeset(var, set, clr, field, base) register const char *var; - int clr, set; + Tflag clr, set; int field, base; { register struct tbl *vp; @@ -616,7 +617,8 @@ typeset(var, set, clr, field, base) || strcmp(tvar, "SHELL") == 0)) errorf("%s: restricted", tvar); - vp = (set&LOCAL) ? local(tvar, set & LOCAL_COPY) : global(tvar); + vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? TRUE : FALSE) + : global(tvar); set &= ~(LOCAL|LOCAL_COPY); vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp; @@ -668,7 +670,7 @@ typeset(var, set, clr, field, base) if ((set & INTEGER) && base > 0 && (!val || t != vp)) t->type = base; if (set & (LJUST|RJUST|ZEROFIL)) - t->field = field; + t->u2.field = field; if (fake_assign) { setstr(t, s); if (free_me) @@ -719,9 +721,10 @@ unset(vp, array_ref) } vp->u.array = (struct tbl *) 0; } - vp->flag &= SPECIAL; /* Should ``unspecial'' some vars */ + /* If foo[0] is being unset, the remainder of the array is kept... */ + vp->flag &= SPECIAL | (array_ref ? ARRAY|DEFINED : 0); if (vp->flag & SPECIAL) - unsetspec(vp); + unsetspec(vp); /* responsible for `unspecial'ing var */ } /* return a pointer to the first char past a legal variable name (returns the @@ -901,15 +904,6 @@ setspec(vp) case V_OPTIND: getopts_reset((int) intval(vp)); break; - case V_MAIL: - mbset(str_val(vp)); - break; - case V_MAILPATH: - mpset(str_val(vp)); - break; - case V_MAILCHECK: - /* mail_check_set(intval(vp)); */ - break; case V_POSIXLY_CORRECT: change_flag(FPOSIX, OF_SPECIAL, 1); break; @@ -953,6 +947,15 @@ setspec(vp) break; #endif /* EDIT */ #ifdef KSH + case V_MAIL: + mbset(str_val(vp)); + break; + case V_MAILPATH: + mpset(str_val(vp)); + break; + case V_MAILCHECK: + /* mail_check_set(intval(vp)); */ + break; case V_RANDOM: vp->flag &= ~SPECIAL; srand((unsigned int)intval(vp)); @@ -985,12 +988,6 @@ unsetspec(vp) setctypes(" \t\n", C_IFS); ifs0 = ' '; break; - case V_MAIL: - mbset((char *) 0); - break; - case V_MAILPATH: - mpset((char *) 0); - break; case V_TMPDIR: /* should not become unspecial */ if (tmpdir) { @@ -999,6 +996,12 @@ unsetspec(vp) } break; #ifdef KSH + case V_MAIL: + mbset((char *) 0); + break; + case V_MAILPATH: + mpset((char *) 0); + break; case V_TMOUT: /* at&t ksh doesn't do this. TMOUT becomes unspecial so * future assignments don't have effect. Could be @@ -1057,7 +1060,7 @@ arraysearch(vp, val) new->flag = vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL); new->type = vp->type; new->areap = vp->areap; - new->field = vp->field; + new->u2.field = vp->u2.field; new->index = val; if (curr != new) { /* not reusing old array entry */ prev->u.array = new; @@ -1123,6 +1126,10 @@ set_array(var, reset, vals) if (reset > 0) /* trash existing values and attributes */ unset(vp, 0); + /* todo: would be nice for assignment to completely succeed or + * completely fail. Only really effects integer arrays: + * evaluation of some of vals[] may fail... + */ for (i = 0; vals[i]; i++) { vq = arraysearch(vp, i); setstr(vq, vals[i]); diff --git a/bin/ksh/version.c b/bin/ksh/version.c index 5c71a81d14b..edd71480d4c 100644 --- a/bin/ksh/version.c +++ b/bin/ksh/version.c @@ -1,4 +1,4 @@ -/* $OpenBSD: version.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: version.c,v 1.2 1996/08/19 20:09:03 downsj Exp $ */ /* * value of $KSH_VERSION (or $SH_VERSION) @@ -7,4 +7,4 @@ #include "sh.h" const char ksh_version [] = - "@(#)PD KSH v5.2.7 96/06/04"; + "@(#)PD KSH v5.2.8 96/08/19"; diff --git a/bin/ksh/vi.c b/bin/ksh/vi.c index c4b2aeeee33..4fc7b75c511 100644 --- a/bin/ksh/vi.c +++ b/bin/ksh/vi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vi.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: vi.c,v 1.2 1996/08/19 20:09:04 downsj Exp $ */ /* * vi command editing @@ -57,7 +57,6 @@ static void rewindow ARGS((void)); static int newcol ARGS((int ch, int col)); static void display ARGS((char *wb1, char *wb2, int leftside)); static void ed_mov_opt ARGS((int col, char *wb)); -static int do_comment ARGS((void)); static int expand_word ARGS((int command)); static int complete_word ARGS((int command, int count)); static int print_expansions ARGS((struct edstate *e, int command)); @@ -94,7 +93,7 @@ const unsigned char classify[128] = { /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */ C_, 0, C_|U_, 0, 0, 0, C_, 0, /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ - C_, 0, 0, 0, 0, 0, 0, 0, + C_, 0, 0, C_|Z_, 0, 0, 0, 0, /* 04 <space> ! " # $ % & ' */ M_, 0, 0, C_, M_, M_, 0, 0, /* 05 ( ) * + , - . / */ @@ -1121,7 +1120,13 @@ vi_cmd(argcnt, cmd) } case '#': - return do_comment(); + { + int ret = x_do_comment(es->cbuf, es->cbufsize, + &es->linelen); + if (ret >= 0) + es->cursor = 0; + return ret; + } case '=': /* at&t ksh */ case Ctrl('e'): /* Nonstandard vi/ksh */ @@ -1135,6 +1140,8 @@ vi_cmd(argcnt, cmd) /* FALLTHROUGH */ case Ctrl('['): /* some annoying at&t ksh's */ + if (!Flag(FVIESCCOMPLETE)) + return -1; case '\\': /* at&t ksh */ case Ctrl('f'): /* Nonstandard vi/ksh */ complete_word(1, argcnt); @@ -1887,7 +1894,7 @@ display(wb1, wb2, leftside) else mc = ' '; if (mc != morec) { - ed_mov_opt(x_cols - 2, wb1); + ed_mov_opt(pwidth + winwidth + 1, wb1); x_putc(mc); cur_col++; morec = mc; @@ -1920,48 +1927,6 @@ ed_mov_opt(col, wb) cur_col = col; } -/* Handle the commenting/uncommenting of a line */ -static int -do_comment() -{ - int i, j; - - if (es->linelen == 0) - return 1; /* somewhat arbitrary - it's what at&t ksh does */ - - /* Already commented? */ - if (es->cbuf[0] == '#') { - int saw_nl = 0; - - for (j = 0, i = 1; i < es->linelen; i++) { - if (!saw_nl || es->cbuf[i] != '#') - es->cbuf[j++] = es->cbuf[i]; - saw_nl = es->cbuf[i] == '\n'; - } - es->linelen = j; - es->cursor = 0; - return 0; - } else { - int n = 1; - - /* See if there's room for the #'s - 1 per \n */ - for (i = 0; i < es->linelen; i++) - if (es->cbuf[i] == '\n') - n++; - if (es->linelen + n >= es->cbufsize) - return -1; - /* Now add them... */ - for (i = es->linelen, j = es->linelen + n; --i >= 0; ) { - if (es->cbuf[i] == '\n') - es->cbuf[--j] = '#'; - es->cbuf[--j] = es->cbuf[i]; - } - es->cbuf[0] = '#'; - es->linelen += n; - es->cursor = 0; - return 1; - } -} /* replace word with all expansions (ie, expand word*) */ static int diff --git a/bin/pdksh/BUG-REPORTS b/bin/pdksh/BUG-REPORTS index 2b98b829d48..f889fe108a8 100644 --- a/bin/pdksh/BUG-REPORTS +++ b/bin/pdksh/BUG-REPORTS @@ -1,4 +1,4 @@ -$OpenBSD: BUG-REPORTS,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: BUG-REPORTS,v 1.2 1996/08/19 20:08:39 downsj Exp $ List of reported problems (problems reported and fixed before 5.0.4 not included). Unresolved problems (may or may not still exist) marked by *, @@ -23,7 +23,7 @@ problems believed to be fixed marked by x. * pdksh 5.0.8, - (reported by Sean Hogan): attempting file name completion on a word with a single backquote causes a "no closing quote" error and - looses the partially entered command (vi mode). + loses the partially entered command (vi mode). [see Mail.2:48] * pdksh 5.0.10, - (reported by Andrew Moore): no overflow checking is done @@ -47,7 +47,7 @@ problems believed to be fixed marked by x. shell (at&t ksh will). [see Mail.7:32,Mail.9:65] -* pdksh 5.1.3, - (reported by Gabor Zahemszky): emacs doesn't have \ as quote +* pdksh 5.1.3, - (reported by Gabor Zahemszky): emacs/vi doesn't have \ as quote character. [see Mail.7:87] @@ -65,7 +65,6 @@ problems believed to be fixed marked by x. * enhancements that haven't been merged yet - Mail.6:36-39,78,84 recursive function diffs - - Mail.7:7 partial sigwinch diffs * pdksh 5.2.3, - (reported by Mike Jetzer): in vi, <ESC>= on word with ~ but no /, beeps (or prints final path comonent?). @@ -84,11 +83,24 @@ problems believed to be fixed marked by x. multiline commands - should go to start of command. [see Mail.XXX:XXX] -* pdksh 5.2.5, - (reported by Adrian M): configuration on Linux FT fails. +* pdksh 5.2.5, - (reported by Adrian Marsh): configuration on Linux FT fails. Caused by configure script using -g flag - gcc passes -lg to ld, ld fails to find -lg (autoconf or Linux FT bug). [see Mail.XXX:XXX] +* pdksh 5.2.7, - (reported by Adrian Marsh): typeset -L20u xxx is ok is ksh88 + but not in pdksh. + [see Mail.XXX:XXX] + +* pdksh 5.2.7, - (reported by Gabor Zahemszky): TMOUT doesn't effect + select and read operations. + [see Mail.XXX:XXX] + +* pdksh 5.2.7, - (reported by Gabor Zahemszky): exec 3<&p doesn't close + shells copy of the coprocess file desc. + [see Mail.XXX:XXX] + + --------------------- put fixed problems below this line --------------------- x pdksh 5.0.3, NetBSD 0.9a (reported by Simon J. Gerraty): pipelines @@ -926,3 +938,91 @@ x pdksh 5.2.5, - (reported by Gabor Zahemszky): vi: # removes comment and executes if command already commented. [see Mail.XXX:XXX] [fixed in 5.2.6: added vi.c(do_comment)] + +x pdksh 5.2.7, - (reported by Adrian Marsh): test doesn't have == operator. + [see Mail.XXX:XXX] + [fixed in 5.2.8: added == to c_test.c operator table] + +x pdksh 5.2.7, - (reported by Mike Jetzer): pdksh sets/exports COLUMNS/LINES + which causes applications not to respond to window size changes. + [see Mail.XXX:XXX] + [fixed in 5.2.8: COLUMNS/LINES no longer exported automatically] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): getopts sets OPTIND differently + that at&t ksh when a bad option is given. + [see Mail.XXX:XXX] + [fixed in 5.2.8: OPTIND not set if option was bad - fragile fix - may go away] + +x pdksh 5.2.7, - (reported with fix by Marc Olzheim): sh version shouldn't + have mail check stuff, macro expansion in PS[0-9]. + [fixed in 5.2.8: added lots of ifdefs] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): sub commands in PS1 cause + a warning message to be printed. + [see Mail.XXX:XXX] + [fixed in 5.2.8: lex.c(set_prompt) - don't print the warning message] + +x pdksh 5.2.7, - (reported by Tom Watson): some environment variables + (eg, PATH) are converted to uppercase on 16-bit int machine. + [see Mail.XXX:XXX] + [fixed in 5.2.8: struct tbl.flag (32 bit thing) was being treated as an + int in some places] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): unset always returns 0 - should + return 1 if variable/function is not set. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed c_sh.c(c_unset)] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): select should only print the + menu the first time, if REPLAY is empty, or if a blank line is entered. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed up exec.c(execute,do_selectargs)] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): shell reports "cannot execute" + error if file exists, even if . not in path. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed up exec.c(comexec)] + +x pdksh 5.1.3, - (reported with partial fix by ra@rhi.hi.is): shell doesn't + listen to sigwinch. + [see Mail.7:7 and related] + [fixed in 5.2.8: changed edit.c(x_init) to catch sigwinch] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): typeset doesn't report + variables that have attributes (like export) but no values. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed up c_ksh.c(c_typeset)] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): error message printed as + a result of "set -o nounset" is different from at7t ksh. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed error messges in eval.c] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): vi/emacs: when listing + command/file completions, should go back at most one space. Also, should + allow completions on zero length names. + [see Mail.XXX:XXX] + [fixed in 5.2.8: fixed edit.c(x_locate_word); now allows zero-length + file completions (but not command)] + +* pdksh 5.2.7, - (reported by Gabor Zahemszky): emacs: <esc># doesn't do + the comment thing. + [see Mail.XXX:XXX] + [fixed in 5.2.8: added emacs.c(x_comment) et al.] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): arithmatic expressions + containing variables not expanded as in at&t ksh. eg, "x=1+2, let y=x" + fails. + [see Mail.XXX:XXX] + [fixed in 5.2.8: added evaling/INEXPREVAL/ET_RECURSIVE code to expr.c] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): unsetting the 0th element + of an array kills the whole array. + [see Mail.XXX:XXX] + [fixed in 5.2.8: var.c(unset) - allow ARRAY to be preserved] + +x pdksh 5.2.7, - (reported by Gabor Zahemszky): unsetting a function while + it is being executed can result in core dump. + [see Mail.XXX:XXX] + [fixed in 5.2.8: table.c(texpand) - dont free if FINUSE is set] + diff --git a/bin/pdksh/CONTRIBUTORS b/bin/pdksh/CONTRIBUTORS index 3b549ca6a3c..34a1e6e6caa 100644 --- a/bin/pdksh/CONTRIBUTORS +++ b/bin/pdksh/CONTRIBUTORS @@ -1,4 +1,4 @@ -$OpenBSD: CONTRIBUTORS,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: CONTRIBUTORS,v 1.2 1996/08/19 20:08:40 downsj Exp $ This is a partial history of this shell gleened from old change logs and readmes (most of which are still in the misc directory) and the source @@ -87,7 +87,11 @@ Other contributors: * Paul Borman (<prb@bsdi.com>): j_exit: send HUP, then CONT; HUP fg process. * DaviD W. Sanderson (<dws@ssec.wisc.edu>): patches to allow { .. } instead of in .. esac in case statements. + * ? (<ra@rhi.hi.is>): partial patches to handle SIGWINCH for command line + editing. * Jason Tyler (<jason@nc.bhpese.oz.au>): fixes for bugs in fc. * Stefan Dalibor (<Stefan.Dalibor@informatik.uni-erlangen.de>): fix for COLUMNS never being set in x_init(). * Arnon Kanfi (<arnon@gilly.datatools.com>): fix for prompt. + * Marc Olzheim (<marcolz@stack.urc.tue.nl>): patches to ifdef KSH the + mail check code and aliases. diff --git a/bin/pdksh/ChangeLog b/bin/pdksh/ChangeLog index 430eaebb5dc..a5bab4fa1f0 100644 --- a/bin/pdksh/ChangeLog +++ b/bin/pdksh/ChangeLog @@ -1,8 +1,248 @@ -$OpenBSD: ChangeLog,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: ChangeLog,v 1.2 1996/08/19 20:08:41 downsj Exp $ + +Mon Aug 19 14:26:08 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * made pdksh-5.2.8 distribution + +Mon Aug 19 11:38:16 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * table.c(texpand): don't free entry if FINUSE is set. + + * var.c(unset): preserve ARRAY and DEFINED if unsetting foo[0]. + +Thu Aug 15 15:08:52 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * jobs.c(sm_sigchld,sm_default): moved to sh.h. + * sh.h(Coproc_id, struct coproc): new typedef; added njobs and + id fields to struct coproc. + * exec.c(execute): case TCOPROC: re-did coprocess stuff to use + njobs/coprocess id. + * jobs.c(struct Job): added coproc_id field. + * jobs.c(exchild): initialize coproc_id to 0; set job coproc_id + and increment coproc.njobs in parent. + * jobs.c(checkjob): check coproc_id and close co-process input/output + if needed. + + * exec.c(iosetup): only play with coprocess fds if this is an + empty exec. + * c_sh.c(c_read): commented out coproc_readw_close() call and eof call. + * c_ksh.c(c_print): commented out closing coprocess fd on EPIPE. + + * jobs.c(exchild): in parent, last part of job: use orig_flags (not + flags) when checking XCOPROC. + +Thu Aug 15 15:00:42 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * io.c(get_coproc_fd,cleanup_coproc): renamed to coproc_getfd() and + coproc_cleanup(), respecitively; changed all calls. + +Tue Aug 13 16:56:59 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * expr.c(O_COMMA,P_COMMA): new enums. + * expr.c(evalexpr): added case for O_COMMA. + +Tue Aug 13 15:18:28 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * expr.c(do_ppmm): new function to handle ++/--. + * expr.c(evalexpr): call do_ppmm() in P_PRIMARY code. + * expr.c(LAST_BINOP): deleted. + * expr.c(IS_BINOP): new define. + * expr.c(evalexpr): use IS_BINOP. + * expr.c(O_PLUSPLUS,O_MINUSMUNS,opinfo[]): new enums; updated opinfo + * expr.c(ET_LVALUE,ET_RDONLY): new enums. + + * expr.c(token): var code: don't increment cp in iter part of for loop, + do it in body; don't correct for off by 1 in array or !noasign code. + * table.h(EXPRLVALUE): new define. + * expr.c(token): var code: set EXPRLVALUE flag if noassign. + * expr.c(intvar): copy temp var if EXPRLVALUE set. + * expr.c(assign_check): new function. + * expr.c(evalexpr): if assign-op, call assign_check(). + +Tue Aug 13 11:02:32 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * vi.c(do_comment),edit.c(x_do_comment): made do_comment generic, + renamed and moved to edit.c; changed all calls. + * emacs.c(x_ftab[]): added x_comment. + * emacs.c(x_defbindings[]): added XFUNC_comment as <esc>#. + * emacs.c(x_comment): new function. + +Mon Aug 12 16:13:36 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * expr.c(ET_BADVAR): deleted. + * expr.c(ET_RECURSIVE, struct expr.evaling),table.h(EXPRNEVAL): added. + * expr.c(v_evaluate): if curstate.evaling set, clear EXPRINEVAL. + * expr.c(evalerr): added ET_RECURSIVE case, removed ET_BADVAR case. + * expr.c(intvar): do recursion check, call v_evaluate() on value. + +Mon Aug 12 14:25:23 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * io.c(coproc_read_close): call coproc_readw_close() instead of + duplicating code. + +Mon Aug 12 11:21:39 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * edit.c(x_locate_word): changed to allow at most 1 leading blank + before the word. + * edit.c(x_file_glob,x_command_glob,add_glob): allow zero length word. + * edit.c(x_cf_glob): allow zero length globs on when doing file + completion. + + * edit.c(x_complete_word): #if 0 - it isn't used... + * edit.c(x_file_glob,x_command_glob,x_locate_word): made static. + + * eval.c(varsub): changed FNOUNSET error from "unset variable" + to "parameter no set", ala at&t ksh. + + * c_ksh.c(c_typeset): print variables that aren't set (just + leave out the =...). + +Mon Aug 12 11:03:22 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * exec.c(findfunc): removed redundent DEFINED check after tsearch(). + +Fri Aug 9 22:16:21 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * jobs.c(j_change): when turning off FMONITOR and not FTALKING, + changed SS_RESTORE_CURR to SS_RESTORE_ORIG. + + * edit.c(x_sigwinch): new function. + * edit.c(x_init): set up signal handler for SIGWINCH; moved + code to get window size into x_sigwinch(); call x_sigwinch(). + * emacs.c(xx_cols): new variable. + * emacs.c(x_init): set xx_cols_to x_cols; change all uses of x_cols + to xx_cols. + * vi.c(display): when displaying morec, changed x_cols-2 to + pwidth+winwidth+1. + +Fri Aug 9 12:49:00 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * table.h(FKSH): new define. + * tree.h(struct op): put evalflags into new union u, added ksh_func + to union; changed all uses of evalflags. + * syn.c(function_body): set u.ksh_func. + * exec.c(execute): changed define() arg to t (was t->left). + * exec.c(define): copy t->left (was t); set FKSH in flag if is + a ksh function. + * exec.c(comexec): don't keep assignments for x() style functions. + * exec.c(comexec: case CFUNC: set kshname ($0) for ksh style functions + only (was FPOSIX). + + * exec.c(execute): case TAND/TOR: pass XERROK on when executing right + hand side. + + * jobs.c(exchild): deleted redundant code to set j->flags + (near new_job() call). + + * sh.h(ksh_tmout),main.c(alarm_init),trap.c(alarm_init,alarm_catcher): + ifdef'd KSH. + + * sh.h(SS_SHTRAP,Trap.shtrap): added. + * trap.c(trapsig): if shtrap is non-zero, call it. + * trap.c(setsig): set shtrap if SS_SHTRAP set. + * jobs.c(j_init),trap.c(alarm_init): pass SS_SHTRAP. + * jobs.c(j_sigchld),trap.c(alarm_catcher): don't call trapsig(). + * trap.c(Sigact_alarm): removed. + +Thu Aug 8 15:57:14 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * exec.c(comexec): case CEXEC: print cannot execute error only + if / in pathname; also, set exit code to 126. + + * exec.c(do_selectargs): added print_menu arg; only print + menu if this is set, or if REPLY is null; removed "while isspace" + loop. + * exec.c(execute): case TSELECT: call do_selectargs with print_menu + of TRUE on first call only. + + * exec.c(define): added was_set variable and logic. + * c_sh.c(c_unset): return 1 if variable/function to be unset wasn't + set to begin with. + +Wed Jul 31 10:33:00 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * sh.h(Tflag): new type. + * sh.h(builtin_flag): changed type to Tflag. + * table.h(struct tbl): changed type of flag field to Tflag. + * c_ksh.c(typeset): changed type of flag, fset, fclr to Tflag. + * c_ksh.c(c_alias): changed type of xflag to Tflag. + * exec.c(comexec): changed type of old_inuse to Tflag. + * exec.c(builtin): changed type of flag to Tflag. + * var.c(typeset): changed set, clr args to Tflag; convert second + arg of call to local() to boolean. + +Wed Jul 31 10:26:25 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * sh.h(C_QUOTE): new define. + * sh.h(ctypes[]),misc.c(ctypes[]): changed from char to short. + * misc.c(initctypes): set C_QUOTE bits in ctypes[]. + * misc.c(print_value_quoted): use C_QUOTE. + +Mon Jul 29 11:38:36 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * lex.c(set_prompt): don't print warning message if setjmp returns + non-zero. + +Fri Jul 26 10:16:27 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * lex.c(set_prompt): don't do ! and parameter expansion if !KSH. + + * table.h(V_MAIL,V_MAILPATH,V_MAILCHECK): ifdef KSH. + * var.c(initvar,setspec,unsetspec): ifdef KSH use of MAIL stuff. + * mail.c: ifdef KSH whole file. + * main.c(shell): ifdef KSH call to mcheck(). + * main.c(initcoms[]): ifdef KSH the MAILCHECK=600. + (based on patches from Marc Olzheim). + + * exec.c(PS4_SUBSTITUTE): new macro. + * exec.c(execute, comexec, iosetup): use PS4_SUBSTITUTE. + +Thu Jul 25 17:19:17 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * sh.h(F_VIESCCOMPLETE): new define. + * misc.c(options[]): added vi-esccomplete. + * vi.c(classify[]): make ^[ a repeatable command. + * vi.c(vi_cmd): check F_VIESCCOMPLETE for ^[. + +Mon Jul 22 16:54:38 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * c_ksh.c(c_getopts): return if variable is readonly; don't change + OPTIND if option is bad (fragile). + * c_sh.c(c_brkcont): use ksh_getopt(); changed error message if + n <= 0. + * c_sh.c(c_dot,c_eval,c_exitreturn): use ksh_getopt(). + * misc.c(ksh_getopt): print `unknown option' instead of `bad option'. + +Mon Jul 22 16:08:40 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * edit.c(x_init): do NOT export COLUMNS/LINES - causes more problems + than it fixes. + +Mon Jul 22 15:49:35 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * syn.c(get_command): fixed test for '< foo (command)' so it + works. + +Fri Jun 21 09:57:47 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * aclocal.m4(KSH_OPENDIR_CHECK): include dirent.h if HAVE_DIRENT_H + defined (was DIRENT || _POSIX_VERSION). + * aclocal.m4(KSH_UNISTD_H): don't test HAVE_DIRENT_H when including + dirent.h. + +Wed Jun 12 11:02:32 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) + + * c_test.c(b_ops[]): added "==" entry (ksh93ism). + +Mon Jun 10 14:00:21 1996 Michael Rendell (michael@lyman.cs.mun.ca) + + * ksh_stat.h: undef S_ISSOCK if STAT_MACROS_BROKEN defined. + * aclocal.m4(AC_HEADER_STAT): redefine autoconf's version to handle + FreeBSD's S_ISSOCK. Tue Jun 4 08:41:19 NDT 1996 Michael Rendell (michael@panda.cs.mun.ca) - * made pdksh-5.2.6 distribution + * made pdksh-5.2.7 distribution * vi.c(CMDLEN): changed from 16 back to 1024. diff --git a/bin/pdksh/IAFA-PACKAGE b/bin/pdksh/IAFA-PACKAGE index fd04bc405bf..26a19b94d19 100644 --- a/bin/pdksh/IAFA-PACKAGE +++ b/bin/pdksh/IAFA-PACKAGE @@ -1,7 +1,7 @@ -$OpenBSD: IAFA-PACKAGE,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: IAFA-PACKAGE,v 1.2 1996/08/19 20:08:42 downsj Exp $ Title: pdksh -Version: 5.2.7 +Version: 5.2.8 Description: A public domain implementation of the Korn shell (ksh88), a UNIX command line interpreter / scripting language; the few missing ksh features are being added and the shell is being diff --git a/bin/pdksh/NEWS b/bin/pdksh/NEWS index 4daaaf1ac67..9c5f173d6ba 100644 --- a/bin/pdksh/NEWS +++ b/bin/pdksh/NEWS @@ -1,4 +1,39 @@ -$OpenBSD: NEWS,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: NEWS,v 1.2 1996/08/19 20:08:42 downsj Exp $ + +Version 5.2.8 + +* bug fixes + * configuration: handle FreeBSD's strange S_ISSOCK. + * test: added == operator. + * configuration: fixed opendir/dirent usage. + * redirections before subshells handled correctly. + * COLUMNS/LINES are no longer exported when they are automatically set. + * mail checks and PS1/PS4 expansions removed if compiled as sh. + * subcommands in PS1 no longer genereate bogus warning messages. + * environment variables not longer messed up on 16-bit machines. + * unset: now returns non-zero if variable/function isn't set. + * select: only prints menu first time, if REPLY is null or on blank line. + * check for `cannot execute' imporved, error message says why. + * typeset: now reports variables with attributes but now value. + * vi/emacs file completion: does directory listing on zero length names. + * arithmetic: non-numeric parameters expanded recursively. + * arithmetic: identifiers in unevaluated part of ?:,&&,|| parsed correctly. + * functions: unsetting a function within itself is now safe. + * arrays: unsetting element 0 of an array no longer kills the whole array. + * co-processes now behave like ksh93 co-processes (and less like ksh88). + +* functions declared with "function foo" are treated differently (from those + declared with "foo()"): $0 is (not) set to the function name, assignments + before function calls aren't (are) kept in the parent shell. + +* vi: added vi-esccomplete option for people who want ESC-ESC completion. + +* vi/emacs: now notice window size changes (but not while editing a line). + +* emacs: <esc># now does the comment/uncomment thing. + +* arithmetic: ++, -- and , added. + Version 5.2.7 diff --git a/bin/pdksh/NOTES b/bin/pdksh/NOTES index 45bab7b2752..96a2af3aada 100644 --- a/bin/pdksh/NOTES +++ b/bin/pdksh/NOTES @@ -1,4 +1,4 @@ -$OpenBSD: NOTES,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: NOTES,v 1.2 1996/08/19 20:08:43 downsj Exp $ General features of at&t ksh that are not (yet) in pdksh: - exported aliases. @@ -6,10 +6,12 @@ General features of at&t ksh that are not (yet) in pdksh: - set -t. - signals/traps not cleared during functions. - trap DEBUG, local ERR and EXIT traps in functions. - - ERRNO, LINENO, LINES parameters. + - ERRNO, LINENO parameters. - doesn't have posix file globbing (eg, [[:alpha:]], etc.). - use of an `agent' to execute unreadable/setuid/setgid shell scripts (don't ask). + - read/select aren't hooked in to the the command line editor + - the last command of a pipeline is not run in the parent shell Known bugs (see also BUG-REPORTS and PROJECTS files): Variable parsing, Expansion: @@ -46,11 +48,6 @@ Known differences between pdksh & at&t ksh (that may change) prints a message and exits. (Also, in at&t ksh, setting TMOUT has no effect after the sequence "TMOUT=60; unset TMOUT", which could be useful - pdksh may do this in the future). - - co-processes: in at&t ksh, accessing the co-process in a redirection - always closes the shells copies of the file descriptors; in pdksh - only redirections in an empty exec command has this effect. This - may change if the at&t style proves more useful (doubt it, though) - or if many scripts depend on it. - in pdksh, if the last command of a pipeline is a shell builtin, it is not executed in the parent shell, so "echo a b | read foo bar" does not set foo and bar in the parent shell (at&t ksh will). @@ -59,6 +56,9 @@ Known differences between pdksh & at&t ksh (that may change) it is the same as set -o. - in pdksh emacs mode, ^T does what gnu emacs does, not what at&t ksh does. + - in ksh93, `. name' calls a function (defined with function) with POSIX + semantics (instead of ksh semantics). in pdksh, . does not call + functions. Known differences between pdksh & at&t ksh (that are not likely to change) - at&t ksh seems to catch or ignore SIGALRM - pdksh dies upon receipt @@ -79,7 +79,7 @@ Known differences between pdksh & at&t ksh (that are not likely to change) uses isspace()), at&t ksh only skips blanks. - at&t ksh allows attributes of read-only variables to be changed, pdksh allows only the export attribute to be set. - - at&t ksh allows set -A of readonly variables, pdksh does not. + - (some) at&t ksh allows set -A of readonly variables, pdksh does not. - at&t ksh allows command assignments of readonly variables (eg, YY=2 cat), pdksh does not. - at&t ksh does not exit scripts when an implicit assignment to an integer @@ -165,6 +165,19 @@ Known differences between pdksh & at&t ksh (that are not likely to change) - pwd: in at&t ksh, it ignores arguments; in pdksh, it complains when given arguments. - the at&t ksh does not do command substition on PS1, pdksh does. + - ksh93 allows ". foo" to run the function foo if there is no file + called foo (go figure). + - field splitting (IFS): ksh88/ksh93 strip leading non-white space IFS + chars, pdksh (and POSIX, I think) leave them intact. e.g. + $ IFS="$IFS:"; read x; echo "<$x>" + :: + prints "<>" in at&t ksh, "<::>" in pdksh. + - command completion: at&t ksh will do completion on a blank line (matching + all commands), pdksh does not (as this isn't very useful - use * if + you really want the list). + - co-processes: if ksh93, the write portion of the co-process output is + closed when the most recently started co-process exits. pdksh closes + it when all the co-processes using it have exited. Oddities in ksh (pd & at&t): - array references inside (())/$(()) are strange: diff --git a/bin/pdksh/PROJECTS b/bin/pdksh/PROJECTS index 52913caf817..271dc3ccad9 100644 --- a/bin/pdksh/PROJECTS +++ b/bin/pdksh/PROJECTS @@ -1,4 +1,4 @@ -$OpenBSD: PROJECTS,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: PROJECTS,v 1.2 1996/08/19 20:08:44 downsj Exp $ Things to be done in pdksh (see also the NOTES file): @@ -40,7 +40,7 @@ Things to be done in pdksh (see also the NOTES file): * trap code * add the DEBUG trap. * fix up signal handling code. In particular, fatal vs tty signals, - have single routine to call to check for pending/fatal traps, etc. + have signal routine to call to check for pending/fatal traps, etc. * parsing * the time keyword needs to be hacked to accept options (!) since @@ -64,12 +64,8 @@ Things to be done in pdksh (see also the NOTES file): freed when a variable is unset. * functions - POSIX and at&t ksh functions are different in that POSIX functions - don't change disable/restore traps and option parsing (OPTIND/OPTARG - plus internal state) isn't saved/restored. The suggestion made in - POSIX.2 rationale is to have x() { .. } do the POSIX thing, and have - function x { ..} do the at&t ksh thing. So, should have two types of - functions. + finish the differences between function x and x(): trap EXIT, traps + in general, treatment of OPTIND/OPTARG, * history There are two versions of the history code, COMPLEX_HISTORY and @@ -102,31 +98,19 @@ Things to be done in pdksh (see also the NOTES file): otherwise) (see POSIX.2:3.8.1). Some of this has been taken care of, but more needs doing. - * POSIX says if an exec fails, the exit code should be 127 (not found) - or 126 (not executable)... - * remove static limits created by fixed sized arrays (eg, getsc_(line[]), ident[], heres[], PATH, states(lex.c), buffer size in emacs/vi code) * merge the emacs and vi code (should reduce the size of the shell and - make maintenance easier). + make maintenance easier); handle SIGWINCH while editing a line. [John Rochester is working on the merge] * add POSIX globbing (eg, [[:alnum:]]), see POSIX.2:2.8.3.2. - * catch SIGWINCH and update the COLUMNS and LINES parameters (also, - need to let the command line editor know of change - ideally this - would work even if the editor was currently reading commands). - * teach shf_vfprintf() about long long's (%lld); also make %p use long longs if appropriate. * add \[...\] parsing to prompt printing (don't count width of chars inside the \[..\] - used to keep escape sequences in prompts from messing up command-line-editor's idea of where the cursor is) - - * file(command) completion list in vi/emacs: change so a number-prefix - picks one of the possibilities (eg, if in vi: foo^[= lists fooa, foob - and fooc as possible completions, ^[2= would choose the second - possibility (foob)). diff --git a/bin/pdksh/README b/bin/pdksh/README index d22b8e40c84..86575be9c34 100644 --- a/bin/pdksh/README +++ b/bin/pdksh/README @@ -1,6 +1,6 @@ -$OpenBSD: README,v 1.1 1996/08/14 06:19:10 downsj Exp $ +$OpenBSD: README,v 1.2 1996/08/19 20:08:44 downsj Exp $ -Last updated June '96 for pdksh-5.2.6. +Last updated August '96 for pdksh-5.2.8. (check ftp://ftp.cs.mun.ca:/pub/pdksh/ or http://www.cs.mun.ca/~michael/pdksh/ for new versions/patches) diff --git a/bin/pdksh/c_ksh.c b/bin/pdksh/c_ksh.c index 01bef267466..ea27c7ddcbe 100644 --- a/bin/pdksh/c_ksh.c +++ b/bin/pdksh/c_ksh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: c_ksh.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: c_ksh.c,v 1.2 1996/08/19 20:08:45 downsj Exp $ */ /* * built-in Korn commands: c_* @@ -280,7 +280,7 @@ c_print(wp) break; #ifdef KSH case 'p': - if ((fd = get_coproc_fd(W_OK, &emsg)) < 0) { + if ((fd = coproc_getfd(W_OK, &emsg)) < 0) { bi_errorf("-p: %s", emsg); return 1; } @@ -378,9 +378,9 @@ c_print(wp) Xfree(xs, xp); } else { int n, len = Xlength(xs, xp); +#ifdef KSH int UNINITIALIZED(opipe); -#ifdef KSH /* Ensure we aren't killed by a SIGPIPE while writing to * a coprocess. at&t ksh doesn't seem to do this (seems * to just check that the co-process is alive, which is @@ -404,16 +404,22 @@ c_print(wp) continue; } #ifdef KSH - if (errno == EPIPE) - coproc_write_close(fd); + /* This doesn't really make sense - could + * break scripts (print -p generates + * error message). + *if (errno == EPIPE) + * coproc_write_close(fd); + */ #endif /* KSH */ return 1; } s += n; len -= n; } +#ifdef KSH if (flags & PO_COPROC) restore_pipe(opipe); +#endif /* KSH */ } return 0; @@ -559,12 +565,13 @@ c_typeset(wp) { struct block *l = e->loc; struct tbl *vp, **p; - int fset = 0, fclr = 0; + Tflag fset = 0, fclr = 0; int thing = 0, func = 0, local = 0; const char *options = "L#R#UZ#fi#lrtux"; /* see comment below */ char *fieldstr, *basestr; int field, base; - int optc, flag; + int optc; + Tflag flag; int pflag = 0; switch (**wp) { @@ -757,8 +764,6 @@ c_typeset(wp) for (l = e->loc; l; l = l->next) { for (p = tsort(&l->vars); (vp = *p++); ) for (; vp; vp = vp->u.array) { - if (!(vp->flag&ISSET)) - continue; if (flag && (vp->flag & flag) == 0) continue; /* no arguments */ @@ -777,9 +782,9 @@ c_typeset(wp) if ((vp->flag&TRACE)) shprintf("-t "); if ((vp->flag&LJUST)) - shprintf("-L%d ", vp->field); + shprintf("-L%d ", vp->u2.field); if ((vp->flag&RJUST)) - shprintf("-R%d ", vp->field); + shprintf("-R%d ", vp->u2.field); if ((vp->flag&ZEROFIL)) shprintf("-Z "); if ((vp->flag&LCASEV)) @@ -800,7 +805,7 @@ c_typeset(wp) shprintf("%s[%d]", vp->name, vp->index); else shprintf("%s", vp->name); - if (thing == '-') { + if (thing == '-' && (vp->flag&ISSET)) { char *s = str_val(vp); shprintf("="); @@ -824,7 +829,8 @@ c_alias(wp) char **wp; { struct table *t = &aliases; - int rv = 0, rflag = 0, tflag, Uflag = 0, xflag = 0; + int rv = 0, rflag = 0, tflag, Uflag = 0; + Tflag xflag = 0; int optc; while ((optc = ksh_getopt(wp, &builtin_opt, "drtUx")) != EOF) @@ -910,7 +916,8 @@ c_alias(wp) afree((void*)ap->val.s, APERM); } /* ignore values for -t (at&t ksh does this) */ - newval = tflag ? search(alias, path, X_OK) : val; + newval = tflag ? search(alias, path, X_OK, (int *) 0) + : val; if (newval) { ap->val.s = str_save(newval, APERM); ap->flag |= ALLOC|ISSET; @@ -1281,26 +1288,32 @@ c_getopts(wp) buf[0] = optc < 0 ? '?' : optc; buf[1] = '\0'; } - vq = global(var); - if (vq->flag & RDONLY) - bi_errorf("%s is readonly", var); - if (Flag(FEXPORT)) - typeset(var, EXPORT, 0, 0, 0); - setstr(vq, buf); - getopts_noset = 1; - setint(global("OPTIND"), (long) user_opt.optind); - getopts_noset = 0; + /* at&t ksh does not change OPTIND if it was an unknown option. + * Scripts counting on this are prone to break... (ie, don't count + * on this staying). + */ + if (optc != '?') { + getopts_noset = 1; + setint(global("OPTIND"), (long) user_opt.optind); + getopts_noset = 0; + } if (user_opt.optarg == (char *) 0) unset(global("OPTARG"), 0); else setstr(global("OPTARG"), user_opt.optarg); - if (optc < 0) + vq = global(var); + if (vq->flag & RDONLY) { + bi_errorf("%s is readonly", var); return 1; + } + if (Flag(FEXPORT)) + typeset(var, EXPORT, 0, 0, 0); + setstr(vq, buf); - return 0; + return optc < 0 ? 1 : 0; } #ifdef EMACS diff --git a/bin/pdksh/c_sh.c b/bin/pdksh/c_sh.c index 7fd2a645b12..691abee19bf 100644 --- a/bin/pdksh/c_sh.c +++ b/bin/pdksh/c_sh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: c_sh.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: c_sh.c,v 1.2 1996/08/19 20:08:46 downsj Exp $ */ /* * built-in Bourne commands @@ -26,14 +26,19 @@ c_shift(wp) register struct block *l = e->loc; register int n; long val; + char *arg; - if (wp[1]) { - evaluate(wp[1], &val, FALSE); + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; + arg = wp[builtin_opt.optind]; + + if (arg) { + evaluate(arg, &val, FALSE); n = val; } else n = 1; if (n < 0) { - bi_errorf("%s: bad number", wp[1]); + bi_errorf("%s: bad number", arg); return (1); } if (l->argc < n) { @@ -175,19 +180,22 @@ c_dot(wp) int argc; int i; - if ((cp = wp[1]) == NULL) + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; + + if ((cp = wp[builtin_opt.optind]) == NULL) return 0; - file = search(cp, path, R_OK); + file = search(cp, path, R_OK, (int *) 0); if (file == NULL) { bi_errorf("%s: not found", cp); return 1; } /* Set positional parameters? */ - if (wp[2]) { - argv = ++wp; + if (wp[builtin_opt.optind + 1]) { + argv = wp + builtin_opt.optind; argv[0] = e->loc->argv[0]; /* preserve $0 */ - for (argc = -1; *wp++; argc++) + for (argc = 0; argv[argc + 1]; argc++) ; } else { argc = 0; @@ -245,7 +253,7 @@ c_read(wp) switch (optc) { #ifdef KSH case 'p': - if ((fd = get_coproc_fd(R_OK, &emsg)) < 0) { + if ((fd = coproc_getfd(R_OK, &emsg)) < 0) { bi_errorf("-p: %s", emsg); return 1; } @@ -290,9 +298,15 @@ c_read(wp) #ifdef KSH /* If we are reading from the co-process for the first time, - * make sure the other side of the pipe is closed first. + * make sure the other side of the pipe is closed first. This allows + * the detection of eof. + * + * This is not compatiable with at&t ksh... the fd is kept so another + * coproc can be started with same ouput, however, this means eof + * can't be detected... This is why it is closed here. + * If this call is removed, remove the eof check below, too. + * coproc_readw_close(fd); */ - coproc_readw_close(fd); #endif /* KSH */ if (history) @@ -387,7 +401,10 @@ c_read(wp) Xfree(xs, xp); } #ifdef KSH - /* if this is the co-process fd, close the file descriptor */ + /* if this is the co-process fd, close the file descriptor + * (can get eof if and only if all processes are have died, ie, + * coproc.njobs is 0 and the pipe is closed). + */ if (c == EOF && !ecode) coproc_read_close(fd); #endif /* KSH */ @@ -401,8 +418,10 @@ c_eval(wp) { register struct source *s; + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; s = pushs(SWORDS, ATEMP); - s->u.strv = wp+1; + s->u.strv = wp + builtin_opt.optind; return shell(s, FALSE); } @@ -466,10 +485,15 @@ c_exitreturn(wp) char **wp; { int how = LEXIT; + char *arg; - if (wp[1] != NULL && !getn(wp[1], &exstat)) { + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; + arg = wp[builtin_opt.optind]; + + if (arg != NULL && !getn(arg, &exstat)) { exstat = 1; - warningf(TRUE, "%s: bad number", wp[1]); + warningf(TRUE, "%s: bad number", arg); } if (wp[0][0] == 'r') { /* return */ struct env *ep; @@ -501,15 +525,20 @@ c_brkcont(wp) { int n, quit; struct env *ep, *last_ep = (struct env *) 0; + char *arg; + + if (ksh_getopt(wp, &builtin_opt, null) == '?') + return 1; + arg = wp[builtin_opt.optind]; - if (!wp[1]) + if (!arg) n = 1; - else if (!bi_getn(wp[1], &n)) + else if (!bi_getn(arg, &n)) return 1; quit = n; if (quit <= 0) { /* at&t ksh does this for non-interactive shells only - weird */ - bi_errorf("bad option `%s'", wp[1]); + bi_errorf("%s: bad value", arg); return 1; } @@ -586,6 +615,7 @@ c_unset(wp) { register char *id; int optc, unset_var = 1; + int ret = 0; while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != EOF) switch (optc) { @@ -603,14 +633,18 @@ c_unset(wp) if (unset_var) { /* unset variable */ struct tbl *vp = global(id); + if (!(vp->flag & ISSET)) + ret = 1; if ((vp->flag&RDONLY)) { bi_errorf("%s is read only", vp->name); return 1; } unset(vp, strchr(id, '[') ? 1 : 0); - } else /* unset function */ - define(id, (struct op *)NULL); - return 0; + } else { /* unset function */ + if (define(id, (struct op *) NULL)) + ret = 1; + } + return ret; } int diff --git a/bin/pdksh/c_test.c b/bin/pdksh/c_test.c index 7ac8c03a007..63a0c94e80c 100644 --- a/bin/pdksh/c_test.c +++ b/bin/pdksh/c_test.c @@ -1,4 +1,4 @@ -/* $OpenBSD: c_test.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: c_test.c,v 1.2 1996/08/19 20:08:47 downsj Exp $ */ /* * test(1); version 7-like -- author Erik Baalbergen @@ -27,7 +27,7 @@ "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"| "-L"|"-h"|"-S"|"-H"; - binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| + binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| "-nt"|"-ot"|"-ef"| "<"|">" # rules used for [[ .. ]] expressions ; @@ -69,6 +69,9 @@ static const struct t_op u_ops [] = { }; static const struct t_op b_ops [] = { {"=", TO_STEQL }, +#ifdef KSH + {"==", TO_STEQL }, +#endif /* KSH */ {"!=", TO_STNEQ }, {"<", TO_STLT }, {">", TO_STGT }, diff --git a/bin/pdksh/edit.c b/bin/pdksh/edit.c index c009ed726d0..12e4fed77d5 100644 --- a/bin/pdksh/edit.c +++ b/bin/pdksh/edit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: edit.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: edit.c,v 1.2 1996/08/19 20:08:47 downsj Exp $ */ /* * Command line editing - common code @@ -20,7 +20,21 @@ #include <ctype.h> #include "ksh_stat.h" -static char vdisable_c; + +#if defined(TIOCGWINSZ) +static RETSIGTYPE x_sigwinch ARGS((int sig)); +static int got_sigwinch; +static void check_sigwinch ARGS((void)); +#endif /* TIOCGWINSZ */ + +static int x_file_glob ARGS((int flags, const char *str, int slen, + char ***wordsp)); +static int x_command_glob ARGS((int flags, const char *str, int slen, + char ***wordsp)); +static int x_locate_word ARGS((const char *buf, int buflen, int pos, + int *startp, int *is_command)); + +static char vdisable_c; /* Called from main */ @@ -32,26 +46,15 @@ x_init() = edchars.eof = -1; /* default value for deficient systems */ edchars.werase = 027; /* ^W */ -#ifdef TIOCGWINSZ - { - struct winsize ws; - - if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) { - struct tbl *vp; - if (ws.ws_col) { - x_cols = ws.ws_col < MIN_COLS ? MIN_COLS - : ws.ws_col; - - if ((vp = typeset("COLUMNS", EXPORT, 0, 0, 0))) - setint(vp, (long) ws.ws_col); - } - if (ws.ws_row - && (vp = typeset("LINES", EXPORT, 0, 0, 0))) - setint(vp, (long) ws.ws_row); - } - } +#ifdef TIOCGWINSZ +# ifdef SIGWINCH + if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP)) + sigtraps[SIGWINCH].flags |= TF_SHELL_USES; +# endif /* SIGWINCH */ + check_sigwinch(); #endif /* TIOCGWINSZ */ + #ifdef EMACS x_init_emacs(); #endif /* EMACS */ @@ -74,6 +77,46 @@ x_init() #endif /* _POSIX_VDISABLE */ } +#if defined(TIOCGWINSZ) +static RETSIGTYPE +x_sigwinch(sig) + int sig; +{ + got_sigwinch = 1; + return RETSIGVAL; +} + +static void +check_sigwinch ARGS((void)) +{ + if (got_sigwinch) { + struct winsize ws; + + got_sigwinch = 0; + if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) { + struct tbl *vp; + + /* Do NOT export COLUMNS/LINES. Many applications + * check COLUMNS/LINES before checking ws.ws_col/row, + * so if the app is started with C/L in the environ + * and the window is then resized, the app won't + * see the change cause the environ doesn't change. + */ + if (ws.ws_col) { + x_cols = ws.ws_col < MIN_COLS ? MIN_COLS + : ws.ws_col; + + if ((vp = typeset("COLUMNS", 0, 0, 0, 0))) + setint(vp, (long) ws.ws_col); + } + if (ws.ws_row + && (vp = typeset("LINES", 0, 0, 0, 0))) + setint(vp, (long) ws.ws_row); + } + } +} +#endif /* TIOCGWINSZ */ + /* * read an edited command line */ @@ -84,6 +127,11 @@ x_read(buf, len) { int i; +#if defined(TIOCGWINSZ) + if (got_sigwinch) + check_sigwinch(); +#endif /* TIOCGWINSZ */ + x_mode(TRUE); #ifdef EMACS if (Flag(FEMACS) || Flag(FGMACS)) @@ -330,6 +378,61 @@ set_editmode(ed) } /* ------------------------------------------------------------------------- */ +/* Misc common code for vi/emacs */ + +/* Handle the commenting/uncommenting of a line. + * Returns: + * 1 if a carriage return is indicated (comment added) + * 0 if no return (comment removed) + * -1 if there is an error (not enough room for comment chars) + * If successful, *lenp contains the new length. Note: cursor should be + * moved to the start of the line after (un)commenting. + */ +int +x_do_comment(buf, bsize, lenp) + char *buf; + int bsize; + int *lenp; +{ + int i, j; + int len = *lenp; + + if (len == 0) + return 1; /* somewhat arbitrary - it's what at&t ksh does */ + + /* Already commented? */ + if (buf[0] == '#') { + int saw_nl = 0; + + for (j = 0, i = 1; i < len; i++) { + if (!saw_nl || buf[i] != '#') + buf[j++] = buf[i]; + saw_nl = buf[i] == '\n'; + } + *lenp = j; + return 0; + } else { + int n = 1; + + /* See if there's room for the #'s - 1 per \n */ + for (i = 0; i < len; i++) + if (buf[i] == '\n') + n++; + if (len + n >= bsize) + return -1; + /* Now add them... */ + for (i = len, j = len + n; --i >= 0; ) { + if (buf[i] == '\n') + buf[--j] = '#'; + buf[--j] = buf[i]; + } + buf[0] = '#'; + *lenp += n; + return 1; + } +} + +/* ------------------------------------------------------------------------- */ /* Common file/command completion code for vi/emacs */ @@ -338,7 +441,9 @@ static void glob_table ARGS((const char *pat, XPtrV *wp, struct table *tp)); static void glob_path ARGS((int flags, const char *pat, XPtrV *wp, const char *path)); -/* XXX not used... */ +#if 0 /* not used... */ +int x_complete_word ARGS((const char *str, int slen, int is_command, + int *multiple, char **ret)); int x_complete_word(str, slen, is_command, nwordsp, ret) const char *str; @@ -364,6 +469,7 @@ x_complete_word(str, slen, is_command, nwordsp, ret) x_free_words(nwords, words); return prefix_len; } +#endif /* 0 */ void x_print_expansions(nwords, words, is_command) @@ -422,8 +528,7 @@ x_print_expansions(nwords, words, is_command) * - sets *wordsp to array of matching strings * - returns number of matching strings */ -/* XXX static? */ -int +static int x_file_glob(flags, str, slen, wordsp) int flags; const char *str; @@ -436,7 +541,7 @@ x_file_glob(flags, str, slen, wordsp) XPtrV w; struct source *s, *sold; - if (slen <= 0) + if (slen < 0) return 0; toglob = add_glob(str, slen); @@ -507,8 +612,7 @@ path_order_cmp(aa, bb) return t ? t : a->path_order - b->path_order; } -/* XXX static? */ -int +static int x_command_glob(flags, str, slen, wordsp) int flags; const char *str; @@ -522,7 +626,7 @@ x_command_glob(flags, str, slen, wordsp) XPtrV w; struct block *l; - if (slen <= 0) + if (slen < 0) return 0; toglob = add_glob(str, slen); @@ -605,8 +709,7 @@ x_command_glob(flags, str, slen, wordsp) #define IS_WORDC(c) !isspace(c) -/* XXX static? */ -int +static int x_locate_word(buf, buflen, pos, startp, is_commandp) const char *buf; int buflen; @@ -617,25 +720,28 @@ x_locate_word(buf, buflen, pos, startp, is_commandp) int p; int start, end; - if (pos == buflen) - pos--; - if (pos < 0 || pos >= buflen) { + /* Bad call? Probably should report error */ + if (pos < 0 || pos > buflen) { *startp = pos; *is_commandp = 0; return 0; } - /* Go backwards 'til we are in a word */ - for (start = pos; start >= 0 && !IS_WORDC(buf[start]); start--) - ; - /* No word found? */ - if (start < 0) - return 0; - /* Keep going backwards to start of word */ - for (; start >= 0 && IS_WORDC(buf[start]); start--) - ; - start++; + if (pos == buflen) { + if (pos == 0) { /* empty buffer? */ + *startp = pos; + *is_commandp = 1; + return 0; + } + pos--; + } + start = pos; + /* Keep going backwards to start of word (has effect of allowing + * one blank after the end of a word) + */ + for (; start > 0 && IS_WORDC(buf[start - 1]); start--) + ; /* Go forwards to end of word */ for (end = start; end < buflen && IS_WORDC(buf[end]); end++) ; @@ -682,11 +788,15 @@ x_cf_glob(flags, buf, buflen, pos, startp, endp, wordsp, is_commandp) int is_command; len = x_locate_word(buf, buflen, pos, startp, &is_command); - if (len == 0) - return 0; - if (!(flags & XCF_COMMAND)) is_command = 0; + /* Don't do command globing on zero length strings - it takes too + * long and isn't very useful. File globs are more likely to be + * useful, so allow these. + */ + if (len == 0 && is_command) + return 0; + nwords = (is_command ? x_command_glob : x_file_glob)(flags, buf + *startp, len, &words); if (nwords == 0) { @@ -713,7 +823,7 @@ add_glob(str, slen) char *toglob; char *s; - if (slen <= 0) + if (slen < 0) return (char *) 0; toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */ @@ -880,7 +990,7 @@ glob_path(flags, pat, wp, path) /* Check that each match is executable... */ words = (char **) XPptrv(*wp); for (i = j = oldsize; i < newsize; i++) { - if (search_access(words[i], X_OK) >= 0) { + if (search_access(words[i], X_OK, (int *) 0) >= 0) { words[j] = words[i]; if (!(flags & XCF_FULLPATH)) memmove(words[j], words[j] + pathlen, diff --git a/bin/pdksh/edit.h b/bin/pdksh/edit.h index c60b4121666..fd29701cc06 100644 --- a/bin/pdksh/edit.h +++ b/bin/pdksh/edit.h @@ -1,4 +1,4 @@ -/* $OpenBSD: edit.h,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: edit.h,v 1.2 1996/08/19 20:08:48 downsj Exp $ */ /* NAME: * edit.h - globals for edit modes @@ -50,13 +50,10 @@ void x_putc ARGS((int c)); void x_puts ARGS((const char *s)); bool_t x_mode ARGS((bool_t onoff)); int promptlen ARGS((const char *cp, const char **spp)); -int x_complete_word ARGS((const char *str, int slen, int is_command, int *multiple, char **ret)); +int x_do_comment ARGS((char *buf, int bsize, int *lenp)); void x_print_expansions ARGS((int nwords, char *const *words, int is_command)); -int x_locate_word ARGS((const char *buf, int buflen, int pos, int *startp, int *is_command)); int x_cf_glob ARGS((int flags, const char *buf, int buflen, int pos, int *startp, int *endp, char ***wordsp, int *is_commandp)); -int x_file_glob ARGS((int flags, const char *str, int slen, char ***wordsp)); -int x_command_glob ARGS((int flags, const char *str, int slen, char ***wordsp)); int x_longest_prefix ARGS((int nwords, char *const *words)); int x_basename ARGS((const char *s, const char *se)); void x_free_words ARGS((int nwords, char **words)); diff --git a/bin/pdksh/emacs.c b/bin/pdksh/emacs.c index a682f32fb5b..f572f87b5f6 100644 --- a/bin/pdksh/emacs.c +++ b/bin/pdksh/emacs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: emacs.c,v 1.1 1996/08/14 06:19:10 downsj Exp $ */ +/* $OpenBSD: emacs.c,v 1.2 1996/08/19 20:08:48 downsj Exp $ */ /* * Emacs-like command line editing and history @@ -94,6 +94,7 @@ static int x_adj_ok; */ static int x_adj_done; +static int xx_cols; static int x_col; static int x_displen; static int x_arg; /* general purpose arg */ @@ -138,10 +139,8 @@ static void x_adjust ARGS((void)); static void x_e_ungetc ARGS((int c)); static int x_e_getc ARGS((void)); static void x_e_putc ARGS((int c)); -#ifdef DEBUG -static int x_debug_info ARGS((void)); -#endif /* DEBUG */ static void x_e_puts ARGS((const char *s)); +static int x_comment ARGS((int c)); static int x_fold_case ARGS((int c)); static char *x_lastcp ARGS((void)); static void do_complete ARGS((int flags, Comp_type type)); @@ -213,6 +212,7 @@ static const struct x_ftab x_ftab[] = { { x_fold_lower, "downcase-word", XF_ARG }, { x_fold_upper, "upcase-word", XF_ARG }, { x_set_arg, "set-arg", XF_NOBIND }, + { x_comment, "comment", 0 }, #ifdef SILLY { x_game_of_life, "play-game-of-life", 0 }, #else @@ -265,6 +265,7 @@ static struct x_defbindings const x_defbindings[] = { { XFUNC_yank, 0, CTRL('Y') }, { XFUNC_meta_yank, 1, 'y' }, { XFUNC_literal, 0, CTRL('^') }, + { XFUNC_comment, 1, '#' }, #if defined(BRL) && defined(TIOCSTI) { XFUNC_stuff, 0, CTRL('T') }, #else @@ -342,10 +343,11 @@ x_emacs(buf, len) x_histp = histptr + 1; x_last_command = XFUNC_error; + xx_cols = x_cols; x_col = promptlen(prompt, &p); prompt_skip = p - prompt; x_adj_ok = 1; - x_displen = x_cols - 2 - x_col; + x_displen = xx_cols - 2 - x_col; x_adj_done = 0; pprompt(prompt, 0); @@ -542,7 +544,7 @@ x_delete(nc, force_push) * there is no need to ' ','\b'. * But if we must, make sure we do the minimum. */ - if ((i = x_cols - 2 - x_col) > 0) + if ((i = xx_cols - 2 - x_col) > 0) { j = (j < i) ? j : i; i = j; @@ -1031,6 +1033,10 @@ x_draw_line(c) } +/* Redraw (part of) the line. If limit is < 0, the everything is redrawn + * on a NEW line, otherwise limit is the screen column up to which needs + * redrawing. + */ static void x_redraw(limit) int limit; @@ -1049,12 +1055,12 @@ x_redraw(limit) pprompt(prompt + prompt_skip, 0); x_col = promptlen(prompt, (const char **) 0); } - x_displen = x_cols - 2 - x_col; + x_displen = xx_cols - 2 - x_col; xlp_valid = FALSE; cp = x_lastcp(); x_zots(xbp); if (xbp != xbuf || xep > xlp) - limit = x_cols; + limit = xx_cols; if (limit >= 0) { if (xep > xlp) @@ -1062,7 +1068,7 @@ x_redraw(limit) else i = limit - (xlp - xbp); - for (j = 0; j < i && x_col < (x_cols - 2); j++) + for (j = 0; j < i && x_col < (xx_cols - 2); j++) x_e_putc(' '); i = ' '; if (xep > xlp) /* more off screen */ @@ -1832,12 +1838,12 @@ x_adjust() { x_adj_done++; /* flag the fact that we were called. */ /* - * we had a problem if the prompt length > x_cols / 2 + * we had a problem if the prompt length > xx_cols / 2 */ if ((xbp = xcp - (x_displen / 2)) < xbuf) xbp = xbuf; xlp_valid = FALSE; - x_redraw(x_cols); + x_redraw(xx_cols); x_flush(); } @@ -1876,7 +1882,7 @@ x_e_putc(c) { if (c == '\r' || c == '\n') x_col = 0; - if (x_col < x_cols) + if (x_col < xx_cols) { x_putc(c); switch(c) @@ -1894,7 +1900,7 @@ x_e_putc(c) break; } } - if (x_adj_ok && (x_col < 0 || x_col >= (x_cols - 2))) + if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2))) { x_adjust(); } @@ -1902,19 +1908,20 @@ x_e_putc(c) #ifdef DEBUG static int -x_debug_info() +x_debug_info(c) + int c; { - x_flush(); - printf("\nksh debug:\n"); - printf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n", - x_col, x_cols, x_displen); - printf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep); - printf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf); - printf("\txlp == 0x%lx\n", (long) xlp); - printf("\txlp == 0x%lx\n", (long) x_lastcp()); - printf(newline); - x_redraw(-1); - return 0; + x_flush(); + shellf("\nksh debug:\n"); + shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n", + x_col, xx_cols, x_displen); + shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep); + shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf); + shellf("\txlp == 0x%lx\n", (long) xlp); + shellf("\txlp == 0x%lx\n", (long) x_lastcp()); + shellf(newline); + x_redraw(-1); + return 0; } #endif @@ -1961,6 +1968,29 @@ x_set_arg(c) } +/* Comment or uncomment the current line. */ +static int +x_comment(c) + int c; +{ + int oldsize = x_size_str(xbuf); + int len = xep - xbuf; + int ret = x_do_comment(xbuf, xend - xbuf, &len); + + if (ret < 0) + x_e_putc(BEL); + else { + xep = xbuf + len; + *xep = '\0'; + xcp = xbp = xbuf; + x_redraw(oldsize); + if (ret > 0) + return x_newline('\n'); + } + return KSTD; +} + + /* NAME: * x_prev_histword - recover word from prev command * diff --git a/bin/pdksh/eval.c b/bin/pdksh/eval.c index da952584d96..b8b339eccda 100644 --- a/bin/pdksh/eval.c +++ b/bin/pdksh/eval.c @@ -1,4 +1,4 @@ -/* $OpenBSD: eval.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: eval.c,v 1.2 1996/08/19 20:08:49 downsj Exp $ */ /* * Expansion - quoting, separation, substitution, globbing @@ -723,7 +723,7 @@ varsub(xp, sp, word, stypep) c = strlen(p); } if (Flag(FNOUNSET) && c == 0 && !zero_ok) - errorf("%s: unset variable", sp); + errorf("%s: parameter not set", sp); *stypep = 0; /* unqualified variable/string substitution */ xp->str = str_save(ulton((unsigned long)c, 10), ATEMP); return XSUB; @@ -813,7 +813,7 @@ varsub(xp, sp, word, stypep) state = XBASE; /* expand word instead of variable value */ if (Flag(FNOUNSET) && xp->str == null && (ctype(c, C_SUBOP2) || (state != XBASE && c != '+'))) - errorf("%s: unset variable", sp); + errorf("%s: parameter not set", sp); return state; } diff --git a/bin/pdksh/exec.c b/bin/pdksh/exec.c index 7563b2bb9d0..ce4ca7668c5 100644 --- a/bin/pdksh/exec.c +++ b/bin/pdksh/exec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: exec.c,v 1.2 1996/08/19 20:08:50 downsj Exp $ */ /* * execute command tree @@ -9,14 +9,21 @@ #include <ctype.h> #include "ksh_stat.h" +/* Does ps4 get parameter substitutions done? */ +#ifdef KSH +# define PS4_SUBSTITUTE(s) substitute((s), 0) +#else +# define PS4_SUBSTITUTE(s) (s) +#endif /* KSH */ + static int comexec ARGS((struct op *t, struct tbl *volatile tp, char **ap, int volatile flags)); static void scriptexec ARGS((struct op *tp, char **ap)); static int call_builtin ARGS((struct tbl *tp, char **wp)); -static int iosetup ARGS((struct ioword *iop)); +static int iosetup ARGS((struct ioword *iop, struct tbl *tp)); static int herein ARGS((char *hname, int sub)); #ifdef KSH -static char *do_selectargs ARGS((char **ap)); +static char *do_selectargs ARGS((char **ap, bool_t print_menu)); #endif /* KSH */ #ifdef KSH static int dbteste_isa ARGS((Test_env *te, Test_meta meta)); @@ -80,6 +87,14 @@ execute(t, flags) if (t == NULL) return 0; + /* Is this the end of a pipeline? If so, we want to evaluate the + * command arguments + bool_t eval_done = FALSE; + if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) { + eval_done = TRUE; + tp = eval_execute_args(t, &ap); + } + */ if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) return exchild(t, flags, -1); /* run in sub-process */ @@ -96,10 +111,10 @@ execute(t, flags) /* POSIX says expand command words first, then redirections, * and assignments last.. */ - ap = eval(t->args, t->evalflags | DOBLANK | DOGLOB | DOTILDE); + ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); if (Flag(FXTRACE) && ap[0]) { shf_fprintf(shl_out, "%s", - substitute(str_val(global("PS4")), 0)); + PS4_SUBSTITUTE(str_val(global("PS4")))); for (i = 0; ap[i]; i++) shf_fprintf(shl_out, "%s%s", ap[i], ap[i + 1] ? space : newline); @@ -118,7 +133,7 @@ execute(t, flags) /* do redirection, to be restored in quitenv() */ if (t->ioact != NULL) for (iowp = t->ioact; *iowp != NULL; iowp++) { - if (iosetup(*iowp) < 0) { + if (iosetup(*iowp, tp) < 0) { exstat = rv = 1; /* Redirection failures for special commands * cause (non-interactive) shell to exit. @@ -179,10 +194,31 @@ execute(t, flags) #ifdef KSH case TCOPROC: { +# ifdef JOB_SIGS + sigset_t omask; +# endif /* JOB_SIGS */ + +# ifdef JOB_SIGS + /* Block sigchild as we are using things changed in the + * signal handler + */ + sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); + e->type = E_ERRH; + i = ksh_sigsetjmp(e->jbuf, 0); + if (i) { + sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); + quitenv(); + unwind(i); + /*NOTREACHED*/ + } +# endif /* JOB_SIGS */ + /* Already have a (live) co-process? */ if (coproc.job && coproc.write >= 0) errorf("coprocess already exists"); + /* Can we re-use the existing co-process pipe? */ - cleanup_coproc(TRUE); + coproc_cleanup(TRUE); + /* do this before opening pipes, in case these fail */ e->savefd[0] = savefd(0, 0); e->savefd[1] = savefd(1, 0); @@ -191,6 +227,7 @@ execute(t, flags) ksh_dup2(pv[0], 0, FALSE); close(pv[0]); coproc.write = pv[1]; + coproc.job = (void *) 0; if (coproc.readw >= 0) ksh_dup2(coproc.readw, 1, FALSE); @@ -199,9 +236,19 @@ execute(t, flags) coproc.read = pv[0]; ksh_dup2(pv[1], 1, FALSE); coproc.readw = pv[1]; /* closed before first read */ + coproc.njobs = 0; + /* create new coprocess id */ + ++coproc.id; } - - /* exchild() closes coproc.* in child after fork */ +# ifdef JOB_SIGS + sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); + e->type = E_EXEC; /* no more need for error handler */ +# endif /* JOB_SIGS */ + + /* exchild() closes coproc.* in child after fork, + * will also increment coproc.njobs when the + * job is actually created. + */ flags &= ~XEXEC; exchild(t->left, flags|XBGND|XFORK|XCOPROC|XCCLOSE, coproc.readw); @@ -221,7 +268,7 @@ execute(t, flags) case TAND: rv = execute(t->left, XERROK); if (t->right != NULL && (rv == 0) == (t->type == TAND)) - rv = execute(t->right, 0); + rv = execute(t->right, flags & XERROK); else flags |= XERROK; break; @@ -250,6 +297,8 @@ execute(t, flags) case TFOR: #ifdef KSH case TSELECT: + { + volatile bool_t is_first = TRUE; #endif /* KSH */ ap = (t->vars != NULL) ? eval(t->vars, DOBLANK|DOGLOB|DOTILDE) @@ -286,10 +335,11 @@ execute(t, flags) struct tbl *vq; for (;;) { - if ((cp = do_selectargs(ap)) == (char *) 0) { + if (!(cp = do_selectargs(ap, is_first))) { rv = 1; break; } + is_first = FALSE; vq = global(t->str); if (vq->flag & RDONLY) errorf("%s is read only", t->str); @@ -297,6 +347,7 @@ execute(t, flags) rv = execute(t->left, flags & XERROK); } } + } #endif /* KSH */ break; @@ -348,7 +399,7 @@ execute(t, flags) break; case TFUNCT: - rv = define(t->str, t->left); + rv = define(t->str, t); break; case TTIME: @@ -483,15 +534,17 @@ comexec(t, tp, ap, flags) break; tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); } - /* todo: POSIX says assignments preceding a function are kept, at&t - * ksh does not do this - */ - if (keepasn_ok && (!ap[0] || (tp && tp->flag & KEEPASN))) + if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN)))) type_flags = 0; else { /* create new variable/function block */ newblock(); - type_flags = LOCAL|LOCAL_COPY|EXPORT; + /* ksh functions don't keep assignments, POSIX functions do. */ + if (keepasn_ok && tp && tp->type == CFUNC + && !(tp->flag & FKSH)) + type_flags = 0; + else + type_flags = LOCAL|LOCAL_COPY|EXPORT; } if (Flag(FEXPORT)) type_flags |= EXPORT; @@ -500,7 +553,7 @@ comexec(t, tp, ap, flags) if (Flag(FXTRACE)) { if (i == 0) shf_fprintf(shl_out, "%s", - substitute(str_val(global("PS4")), 0)); + PS4_SUBSTITUTE(str_val(global("PS4")))); shf_fprintf(shl_out, "%s%s", cp, t->vars[i + 1] ? space : newline); if (!t->vars[i + 1]) @@ -529,24 +582,30 @@ comexec(t, tp, ap, flags) case CFUNC: /* function call */ { volatile int old_xflag; - volatile int old_inuse; + volatile Tflag old_inuse; const char *volatile old_kshname; if (!(tp->flag & ISSET)) { struct tbl *ftp; if (!tp->u.fpath) { - /* XXX: exit code 126 vs 127 */ - warningf(TRUE, + if (tp->u2.errno_) { + warningf(TRUE, + "%s: can't find function definition file - %s", + cp, strerror(tp->u2.errno_)); + rv = 126; + } else { + warningf(TRUE, "%s: can't find function definition file", cp); - rv = 127; + rv = 127; + } break; } if (include(tp->u.fpath, 0, (char **) 0, 0) < 0) { warningf(TRUE, "%s: can't open function definition file %s - %s", cp, tp->u.fpath, strerror(errno)); - rv = 126; + rv = 127; break; } if (!(ftp = findfunc(cp, hash(cp), FALSE)) @@ -561,9 +620,11 @@ comexec(t, tp, ap, flags) tp = ftp; } - /* posix says $0 remains unchanged, at&t ksh changes it */ + /* ksh functions set $0 to function name, POSIX functions leave + * $0 unchanged. + */ old_kshname = kshname; - if (!Flag(FPOSIX)) + if (tp->flag & FKSH) kshname = ap[0]; e->loc->argv = ap; for (i = 0; *ap++ != NULL; i++) @@ -588,7 +649,8 @@ comexec(t, tp, ap, flags) Flag(FXTRACE) = old_xflag; tp->flag = (tp->flag & ~FINUSE) | old_inuse; /* Were we deleted while executing? If so, free the execution - * tree. Unfortunately, the table entry is never re-used. + * tree. todo: Unfortunately, the table entry is never re-used + * until the lookup table is expanded. */ if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { if (tp->flag & ALLOC) { @@ -619,38 +681,26 @@ comexec(t, tp, ap, flags) case CEXEC: /* executable command */ case CTALIAS: /* tracked alias */ if (!(tp->flag&ISSET)) { - /* - * mlj addition: - * - * If you specify a full path to a file - * (or type the name of a file in .) which - * doesn't have execute priv's, it used to - * just say "not found". Kind of annoying, - * particularly if you just wrote a script - * but forgot to say chmod 755 script. - * - * This should probably be done in eaccess(), - * but it works here (at least I haven't noticed - * changing errno here breaking something - * else). - * - * So, we assume that if the file exists, it - * doesn't have execute privs; else, it really - * is not found. + /* errno_ will be set if the named command was found + * but could not be executed (permissions, no execute + * bit, directory, etc). Print out a (hopefully) + * useful error message and set the exit status to 126. */ - if (access(cp, F_OK) < 0) + if (tp->u2.errno_) { + warningf(TRUE, "%s: cannot execute - %s", cp, + strerror(tp->u2.errno_)); + rv = 126; /* POSIX */ + } else { warningf(TRUE, "%s: not found", cp); - else - warningf(TRUE, "%s: cannot execute", cp); - /* XXX posix says 126 if in path and cannot execute */ - rv = 127; + rv = 127; + } break; } /* set $_ to program's full path */ setstr(typeset("_", LOCAL|EXPORT, 0, 0, 0), tp->val.s); - if ((flags&XEXEC)) { + if (flags&XEXEC) { j_exit(); if (!(flags&XBGND) || Flag(FMONITOR)) { setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG); @@ -683,7 +733,7 @@ scriptexec(tp, ap) shell = str_val(global(EXECSHELL_STR)); if (shell && *shell) - shell = search(shell, path, X_OK); + shell = search(shell, path, X_OK, (int *) 0); if (!shell || !*shell) shell = EXECSHELL; @@ -746,7 +796,7 @@ scriptexec(tp, ap) if (a1) *tp->args-- = a1; # ifdef OS2 - if (a0 != a2 && search_access(a0, X_OK)) + if (a0 != a2 && search_access(a0, X_OK, (int *) 0)) a0 = a2; # endif /* OS2 */ shell = a0; @@ -762,7 +812,7 @@ scriptexec(tp, ap) shell = str_val(global("EXECSHELL")); if (shell && *shell) - shell = search(shell, path, X_OK); + shell = search(shell, path, X_OK, (int *) 0); if (!shell || !*shell) { shell = p; *tp->args-- = "/c"; @@ -809,7 +859,7 @@ findfunc(name, h, create) for (l = e->loc; l; l = l->next) { tp = tsearch(&l->funs, name, h); - if (tp && (tp->flag & DEFINED)) + if (tp) break; if (!l->next && create) { tp = tenter(&l->funs, name, h); @@ -823,25 +873,31 @@ findfunc(name, h, create) } /* - * define function + * define function. Returns 1 if function is being undefined (t == 0) and + * function did not exist, returns 0 otherwise. */ int define(name, t) const char *name; struct op *t; { - register struct tbl *tp; + struct tbl *tp; + int was_set = 0; - tp = findfunc(name, hash(name), TRUE); + while (1) { + tp = findfunc(name, hash(name), TRUE); - /* If this function is currently being executed, we zap this - * table entry so findfunc() won't see it - */ - if (tp->flag & FINUSE) { - tp->name[0] = '\0'; - tp->flag &= ~DEFINED; /* ensure it won't be found */ - tp->flag |= FDELETE; - return define(name, t); + if (tp->flag & ISSET) + was_set = 1; + /* If this function is currently being executed, we zap this + * table entry so findfunc() won't see it + */ + if (tp->flag & FINUSE) { + tp->name[0] = '\0'; + tp->flag &= ~DEFINED; /* ensure it won't be found */ + tp->flag |= FDELETE; + } else + break; } if (tp->flag & ALLOC) { @@ -851,11 +907,13 @@ define(name, t) if (t == NULL) { /* undefine */ tdelete(tp); - return 0; + return was_set ? 0 : 1; } - tp->val.t = tcopy(t, tp->areap); + tp->val.t = tcopy(t->left, tp->areap); tp->flag |= (ISSET|ALLOC); + if (t->u.ksh_func) + tp->flag |= FKSH; return 0; } @@ -869,7 +927,7 @@ builtin(name, func) int (*func) ARGS((char **)); { register struct tbl *tp; - int flag; + Tflag flag; /* see if any flags should be set for this builtin */ for (flag = 0; ; name++) { @@ -920,10 +978,12 @@ findcom(name, flags) if (!tp && (flags & FC_FUNC)) { tp = findfunc(name, h, FALSE); if (tp && !(tp->flag & ISSET)) { - if ((fpath = str_val(global("FPATH"))) == null) + if ((fpath = str_val(global("FPATH"))) == null) { tp->u.fpath = (char *) 0; - else - tp->u.fpath = search(name, fpath, R_OK); + tp->u2.errno_ = 0; + } else + tp->u.fpath = search(name, fpath, R_OK, + &tp->u2.errno_); } } if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI)) @@ -964,13 +1024,14 @@ findcom(name, flags) tp->flag = DEFINED; /* make ~ISSET */ } npath = search(name, flags & FC_DEFPATH ? def_path : path, - X_OK); + X_OK, &tp->u2.errno_); if (npath) { tp->val.s = tp == &temp ? npath : str_save(npath, APERM); tp->flag |= ISSET|ALLOC; } else if ((flags & FC_FUNC) && (fpath = str_val(global("FPATH"))) != null - && (npath = search(name, fpath, R_OK)) != (char *) 0) + && (npath = search(name, fpath, R_OK, + &tp->u2.errno_)) != (char *) 0) { /* An undocumented feature of at&t ksh is that it * searches FPATH if a command is not found, even @@ -1008,20 +1069,29 @@ flushcom(all) /* Check if path is something we want to find. Returns -1 for failure. */ int -search_access(path, mode) +search_access(path, mode, errnop) const char *path; int mode; + int *errnop; /* set if candidate found, but not suitable */ { #ifndef OS2 - int ret = eaccess(path, mode); + int ret, err = 0; struct stat statb; - /* if executable pipes come along, this will have to change */ - if (ret == 0 && (mode == X_OK) - && (stat(path, &statb) < 0 || !S_ISREG(statb.st_mode) - /* This 'cause access() says root can execute everything */ - || !(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) + if (stat(path, &statb) < 0) + return -1; + ret = eaccess(path, mode); + if (ret < 0) + err = errno; /* File exists, but we can't access it */ + else if (mode == X_OK && (!S_ISREG(statb.st_mode) + /* This 'cause access() says root can execute everything */ + || !(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) + { ret = -1; + err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES; + } + if (err && errnop) + *errnop = err; return ret; #else /* !OS2 */ /* @@ -1050,7 +1120,7 @@ search_access(path, mode) if ((p = strrchr((p = ksh_strrchr_dirsep(mpath)) ? p : mpath, '.'))) { if (mode == X_OK) mode = R_OK; - return search_access1(mpath, mode); + return search_access1(mpath, mode, errnop); } /* Try appending the various suffixes. Different suffixes for * read and execute 'cause we don't want to read an executable... @@ -1058,7 +1128,7 @@ search_access(path, mode) sfx = mode == R_OK ? rsuffixes : xsuffixes; for (i = 0; sfx[i]; i++) { strcpy(tp, p = sfx[i]); - if (search_access1(mpath, R_OK) == 0) + if (search_access1(mpath, R_OK, errnop) == 0) return 0; *tp = '\0'; } @@ -1068,17 +1138,25 @@ search_access(path, mode) #ifdef OS2 static int -search_access1(path, mode) +search_access1(path, mode, errnop) const char *path; int mode; + int *errnop; /* set if candidate found, but not suitable */ { - int ret = eaccess(path, mode); + int ret, err = 0; struct stat statb; - /* if executable pipes come along, this will have to change */ - if (ret == 0 && (mode == X_OK || mode == R_OK) - && (stat(path, &statb) < 0 || !S_ISREG(statb.st_mode))) + if (stat(path, &statb) < 0) + return -1; + ret = eaccess(path, mode); + if (ret < 0) + err = errno; /* File exists, but we can't access it */ + else if (!S_ISREG(statb.st_mode)) { ret = -1; + err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES; + } + if (err && errnop) + *errnop = err; return ret; } #endif /* OS2 */ @@ -1087,16 +1165,19 @@ search_access1(path, mode) * search for command with PATH */ char * -search(name, path, mode) +search(name, path, mode, errnop) const char *name; const char *path; int mode; /* R_OK or X_OK */ + int *errnop; /* set if candidate found, but not suitable */ { const char *sp, *p; char *xp; XString xs; int namelen; + if (errnop) + *errnop = 0; #ifdef OS2 /* Xinit() allocates 8 additional bytes, so appended suffixes won't * overflow the memory. @@ -1106,18 +1187,18 @@ search(name, path, mode) memcpy(Xstring(xs, xp), name, namelen); if (ksh_strchr_dirsep(name)) { - if (search_access(Xstring(xs, xp), mode) >= 0) + if (search_access(Xstring(xs, xp), mode, errnop) >= 0) return Xstring(xs, xp); /* not Xclose() - see above */ Xfree(xs, xp); return NULL; } /* Look in current context always. (os2 style) */ - if (search_access(Xstring(xs, xp), mode) == 0) + if (search_access(Xstring(xs, xp), mode, errnop) == 0) return Xstring(xs, xp); /* not Xclose() - xp may be wrong */ #else /* OS2 */ if (ksh_strchr_dirsep(name)) { - if (search_access(name, mode) == 0) + if (search_access(name, mode, errnop) == 0) return (char *) name; return NULL; } @@ -1140,7 +1221,7 @@ search(name, path, mode) sp = p; XcheckN(xs, xp, namelen); memcpy(xp, name, namelen); - if (search_access(Xstring(xs, xp), mode) == 0) + if (search_access(Xstring(xs, xp), mode, errnop) == 0) #ifdef OS2 return Xstring(xs, xp); /* Not Xclose() - see above */ #else /* OS2 */ @@ -1177,8 +1258,9 @@ call_builtin(tp, wp) * set up redirection, saving old fd's in e->savefd */ static int -iosetup(iop) +iosetup(iop, tp) register struct ioword *iop; + struct tbl *tp; { register int u = -1; char *cp = iop->name; @@ -1197,7 +1279,7 @@ iosetup(iop) if (Flag(FXTRACE)) shellf("%s%s\n", - substitute(str_val(global("PS4")), 0), + PS4_SUBSTITUTE(str_val(global("PS4"))), snptreef((char *) 0, 32, "%R", &iotmp)); switch (iotype) { @@ -1287,23 +1369,19 @@ iosetup(iop) close(u); return -1; } - if (iotype == IODUP) { + if (iotype != IODUP) + close(u); #ifdef KSH - if (iop->flag & IORDUP) /* possible <&p */ - /* Ensure other side of read pipe is - * closed so EOF can be read properly - */ - coproc_readw_close(u); - else /* possible >&p */ - /* If co-process input is duped, - * close shell's copy - */ + /* Touching any co-process fd in an empty exec + * causes the shell to close its copies + */ + else if (tp && tp->type == CSHELL && tp->val.f == c_exec) { + if (iop->flag & IORDUP) /* possible exec <&p */ + coproc_read_close(u); + else /* possible exec >&p */ coproc_write_close(u); -#else /* KSH */ - ; + } #endif /* KSH */ - } else - close(u); } if (u == 2) /* Clear any write errors */ shf_reopen(2, SHF_WR, shl_out); @@ -1387,27 +1465,35 @@ herein(hname, sub) * print the args in column form - assuming that we can */ static char * -do_selectargs(ap) +do_selectargs(ap, print_menu) register char **ap; + bool_t print_menu; { static const char *const read_args[] = { "read", "-r", "REPLY", (char *) 0 }; char *s; - int i, UNINITIALIZED(argct); + int i, argct; + for (argct = 0; ap[argct]; argct++) + ; while (1) { - argct = pr_menu(ap); + /* Menu is printed if + * - this is the first time around the select loop + * - the user enters a blank line + * - the REPLY parameter is empty + */ + if (print_menu || !*str_val(global("REPLY"))) + pr_menu(ap); shellf("%s", str_val(global("PS3"))); if (call_builtin(findcom("read", FC_BI), (char **) read_args)) return (char *) 0; s = str_val(global("REPLY")); - while (*s && isspace(*s)) - s++; if (*s) { i = atoi(s); return (i >= 1 && i <= argct) ? ap[i - 1] : null; } + print_menu = 1; } } diff --git a/bin/pdksh/expr.c b/bin/pdksh/expr.c index bd94ca68f27..2d70ed1cd1d 100644 --- a/bin/pdksh/expr.c +++ b/bin/pdksh/expr.c @@ -1,21 +1,10 @@ -/* $OpenBSD: expr.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: expr.c,v 1.2 1996/08/19 20:08:50 downsj Exp $ */ /* * Korn expression evaluation */ /* * todo: better error handling: if in builtin, should be builtin error, etc. - * todo: add ++ -- - * todo: recursive variable expansion (y=1;x=y; let x) - how to deal with allowing: - i=0 - set -A x 'x[1]' 'x[2]' 'x[3]' 99 - let z=x[i+=1] - echo $z - 99 - and disallowing: - x='y[x]' - let z=x */ #include "sh.h" @@ -24,8 +13,10 @@ /* The order of these enums is constrained by the order of opinfo[] */ enum token { + /* some (long) unary operators */ + O_PLUSPLUS = 0, O_MINUSMINUS, /* binary operators */ - O_EQ = 0, O_NE, + O_EQ, O_NE, /* assignments are assumed to be in range O_ASN .. O_BORASN */ O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN, O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN, @@ -39,6 +30,7 @@ enum token { O_BXOR, O_BOR, O_TERN, + O_COMMA, /* things after this aren't used as binary operators */ /* unary that are not also binaries */ O_BNOT, O_LNOT, @@ -47,7 +39,7 @@ enum token { /* things that don't appear in the opinfo[] table */ VAR, LIT, END, BAD }; -#define LAST_BINOP O_TERN +#define IS_BINOP(op) (((int)op) >= O_EQ && ((int)op) <= O_COMMA) #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN) enum prec { @@ -63,9 +55,10 @@ enum prec { P_LAND, /* && */ P_LOR, /* || */ P_TERN, /* ?: */ - P_ASSIGN /* = *= /= %= += -= <<= >>= &= ^= |= */ + P_ASSIGN, /* = *= /= %= += -= <<= >>= &= ^= |= */ + P_COMMA /* , */ }; -#define MAX_PREC P_ASSIGN +#define MAX_PREC P_COMMA struct opinfo { char name[4]; @@ -78,6 +71,8 @@ struct opinfo { * of enum token too. */ static const struct opinfo opinfo[] = { + { "++", 2, P_PRIMARY }, /* before + */ + { "--", 2, P_PRIMARY }, /* before - */ { "==", 2, P_EQUALITY }, /* before = */ { "!=", 2, P_EQUALITY }, /* before ! */ { "=", 1, P_ASSIGN }, /* keep assigns in a block */ @@ -108,6 +103,7 @@ static const struct opinfo opinfo[] = { { "^", 1, P_BXOR }, { "|", 1, P_BOR }, { "?", 1, P_TERN }, + { ",", 1, P_COMMA }, { "~", 1, P_PRIMARY }, { "!", 1, P_PRIMARY }, { "(", 1, P_PRIMARY }, @@ -122,12 +118,16 @@ struct expr_state { const char *expression; /* expression being evaluated */ const char *tokp; /* lexical position */ enum token tok; /* token from token() */ - int noassign; /* don't do assignments (for ?:) */ + int noassign; /* don't do assigns (for ?:,&&,||) */ struct tbl *val; /* value from token() */ + struct tbl *evaling; /* variable that is being recursively + * expanded (EXPRINEVAL flag set) + */ Expr_state *volatile prev; /* previous state */ }; -enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_BADVAR, ET_STR }; +enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE, + ET_LVALUE, ET_RDONLY, ET_STR }; static Expr_state *es; @@ -135,6 +135,9 @@ static void evalerr ARGS((enum error_type type, const char *str)) GCC_FUNC_ATTR(noreturn); static struct tbl *evalexpr ARGS((enum prec prec)); static void token ARGS((void)); +static struct tbl *do_ppmm ARGS((enum token op, struct tbl *vasn, + bool_t is_prefix)); +static void assign_check ARGS((enum token op, struct tbl *vasn)); static struct tbl *tempvar ARGS((void)); static struct tbl *intvar ARGS((struct tbl *vp)); @@ -174,11 +177,15 @@ v_evaluate(vp, expr, error_ok) curstate.expression = curstate.tokp = expr; curstate.noassign = 0; curstate.prev = es; + curstate.evaling = (struct tbl *) 0; es = &curstate; newenv(E_ERRH); i = ksh_sigsetjmp(e->jbuf, 0); if (i) { + /* Clear EXPRINEVAL in of any variables we were playing with */ + if (curstate.evaling) + curstate.evaling->flag &= ~EXPRINEVAL; quitenv(); es = curstate.prev; if (i == LAEXPR) { @@ -248,8 +255,18 @@ evalerr(type, str) warningf(TRUE, "%s: bad number `%s'", es->expression, str); break; - case ET_BADVAR: - warningf(TRUE, "%s: value of variable `%s' not a number", + case ET_RECURSIVE: + warningf(TRUE, "%s: expression recurses on parameter `%s'", + es->expression, str); + break; + + case ET_LVALUE: + warningf(TRUE, "%s: %s requires lvalue", + es->expression, str); + break; + + case ET_RDONLY: + warningf(TRUE, "%s: %s applied to read only variable", es->expression, str); break; @@ -289,6 +306,10 @@ evalexpr(prec) if (es->tok != CLOSE_PAREN) evalerr(ET_STR, "missing )"); token(); + } else if (op == O_PLUSPLUS || op == O_MINUSMINUS) { + token(); + vl = do_ppmm(op, es->val, TRUE); + token(); } else if (op == VAR || op == LIT) { vl = es->val; token(); @@ -296,20 +317,22 @@ evalexpr(prec) evalerr(ET_UNEXPECTED, (char *) 0); /*NOTREACHED*/ } + if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) { + vl = do_ppmm(es->tok, vl, FALSE); + token(); + } return vl; } vl = evalexpr(((int) prec) - 1); - while ((int) (op = es->tok) <= (int) LAST_BINOP && opinfo[(int) op].prec == prec) { + for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec; + op = es->tok) + { token(); vasn = vl; if (op != O_ASN) /* vl may not have a value yet */ vl = intvar(vl); if (IS_ASSIGNOP(op)) { - if (vasn->name[0] == '\0') - evalerr(ET_STR, "assignment to non-lvalue"); - else if (vasn->flag & RDONLY) - evalerr(ET_STR, - "assignment to read only variable"); + assign_check(op, vasn); vr = intvar(evalexpr(P_ASSIGN)); } else if (op != O_TERN && op != O_LAND && op != O_LOR) vr = intvar(evalexpr(((int) prec) - 1)); @@ -409,7 +432,7 @@ evalexpr(prec) token(); if (e) es->noassign++; - vr = evalexpr(MAX_PREC); + vr = evalexpr(P_TERN); if (e) es->noassign--; vl = e ? vl : vr; @@ -418,6 +441,9 @@ evalexpr(prec) case O_ASN: res = vr->val.i; break; + case O_COMMA: + res = vr->val.i; + break; } if (IS_ASSIGNOP(op)) { vr->val.i = res; @@ -447,20 +473,21 @@ token() if (c == '\0') es->tok = END; else if (letter(c)) { - for (; letnum(c); c = *cp++) - ; + for (; letnum(c); c = *cp) + cp++; if (c == '[') { int len; - len = array_ref_len(cp - 1); + len = array_ref_len(cp); if (len == 0) evalerr(ET_STR, "missing ]"); cp += len; } - if (es->noassign) + if (es->noassign) { es->val = tempvar(); - else { - tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP); + es->val->flag |= EXPRLVALUE; + } else { + tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP); es->val = global(tvar); afree(tvar, ATEMP); } @@ -494,6 +521,41 @@ token() es->tokp = cp; } +/* Do a ++ or -- operation */ +static struct tbl * +do_ppmm(op, vasn, is_prefix) + enum token op; + struct tbl *vasn; + bool_t is_prefix; +{ + struct tbl *vl; + int oval; + + assign_check(op, vasn); + + vl = intvar(vasn); + oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--; + if (vasn->flag & INTEGER) + setint_v(vasn, vl); + else + setint(vasn, vl->val.i); + if (!is_prefix) /* undo the inc/dec */ + vl->val.i = oval; + + return vl; +} + +static void +assign_check(op, vasn) + enum token op; + struct tbl *vasn; +{ + if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)) + evalerr(ET_LVALUE, opinfo[op].name); + else if (vasn->flag & RDONLY) + evalerr(ET_RDONLY, opinfo[op].name); +} + static struct tbl * tempvar() { @@ -517,22 +579,18 @@ intvar(vp) /* try to avoid replacing a temp var with another temp var */ if (vp->name[0] == '\0' - && (vp->flag & (ISSET|INTEGER)) == (ISSET|INTEGER)) + && (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER)) return vp; vq = tempvar(); - vq->type = 0; if (setint_v(vq, vp) == NULL) { - evalerr(ET_BADVAR, vp->name); - /* - if ((vp->flag&ISSET) && vp->val.s && *(vp->val.s)) { - evalerr("bad number"); - } else { - vq->flag |= (ISSET|INTEGER); - vq->type = 10; - vq->val.i = 0; - } - */ + if (vp->flag & EXPRINEVAL) + evalerr(ET_RECURSIVE, vp->name); + es->evaling = vp; + vp->flag |= EXPRINEVAL; + v_evaluate(vq, str_val(vp), FALSE); + vp->flag &= ~EXPRINEVAL; + es->evaling = (struct tbl *) 0; } return vq; } diff --git a/bin/pdksh/io.c b/bin/pdksh/io.c index 492c82e3b99..a5bfe1569a9 100644 --- a/bin/pdksh/io.c +++ b/bin/pdksh/io.c @@ -1,4 +1,4 @@ -/* $OpenBSD: io.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: io.c,v 1.2 1996/08/19 20:08:51 downsj Exp $ */ /* * shell buffered IO and formatted output @@ -324,7 +324,7 @@ check_fd(name, mode, emsgp) } #ifdef KSH else if (name[0] == 'p' && !name[1]) - return get_coproc_fd(mode, emsgp); + return coproc_getfd(mode, emsgp); #endif /* KSH */ if (emsgp) *emsgp = "illegal file descriptor name"; @@ -337,6 +337,8 @@ void coproc_init() { coproc.read = coproc.readw = coproc.write = -1; + coproc.njobs = 0; + coproc.id = 0; } /* Called by c_read() when eof is read - close fd if it is the co-process fd */ @@ -345,12 +347,9 @@ coproc_read_close(fd) int fd; { if (coproc.read >= 0 && fd == coproc.read) { + coproc_readw_close(fd); close(coproc.read); coproc.read = -1; - if (coproc.readw >= 0) { - close(coproc.readw); - coproc.readw = -1; - } } } @@ -361,7 +360,7 @@ void coproc_readw_close(fd) int fd; { - if (coproc.read >= 0 && fd == coproc.read && coproc.readw >= 0) { + if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) { close(coproc.readw); coproc.readw = -1; } @@ -384,7 +383,7 @@ coproc_write_close(fd) * (Used by check_fd() and by c_read/c_print to deal with -p option). */ int -get_coproc_fd(mode, emsgp) +coproc_getfd(mode, emsgp) int mode; const char **emsgp; { @@ -397,12 +396,13 @@ get_coproc_fd(mode, emsgp) return -1; } -/* called to close file descriptors related to the coprocess (if any) */ +/* called to close file descriptors related to the coprocess (if any) + * Should be called with SIGCHLD blocked. + */ void -cleanup_coproc(reuse) +coproc_cleanup(reuse) int reuse; { - coproc.job = (void *) 0; /* This to allow co-processes to share output pipe */ if (!reuse || coproc.readw < 0 || coproc.read < 0) { if (coproc.read >= 0) { diff --git a/bin/pdksh/jobs.c b/bin/pdksh/jobs.c index d40a092ac4b..e1c11fcd04a 100644 --- a/bin/pdksh/jobs.c +++ b/bin/pdksh/jobs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: jobs.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: jobs.c,v 1.2 1996/08/19 20:08:52 downsj Exp $ */ /* * Process and job control @@ -140,6 +140,7 @@ struct job { clock_t usrtime; /* user time used by job */ Proc *proc_list; /* process list */ Proc *last_proc; /* last process in list */ + Coproc_id coproc_id; /* 0 or id of coprocess output pipe */ #ifdef TTY_PGRP TTY_state ttystate; /* saved tty state for stopped jobs */ #endif /* TTY_PGRP */ @@ -177,7 +178,6 @@ static int child_max; /* CHILD_MAX */ #ifdef JOB_SIGS -static sigset_t sm_default, sm_sigchld; /* held_sigchld is set if sigchld occurs before a job is completely started */ static int held_sigchld; #endif /* JOB_SIGS */ @@ -234,7 +234,8 @@ j_init(mflagset) sigemptyset(&sm_sigchld); sigaddset(&sm_sigchld, SIGCHLD); - setsig(&sigtraps[SIGCHLD], j_sigchld, SS_RESTORE_ORIG|SS_FORCE); + setsig(&sigtraps[SIGCHLD], j_sigchld, + SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); #else /* JOB_SIGS */ /* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */ setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE); @@ -253,9 +254,12 @@ j_init(mflagset) if (Flag(FMONITOR) || Flag(FTALKING)) { int i; - /* j_change() sets these to SS_RESTORE_DFL if FMONITOR */ + /* the TF_SHELL_USES test is a kludge that lets us know if + * if the signals have been changed by the shell. + */ for (i = NELEM(tt_sigs); --i >= 0; ) { sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES; + /* j_change() sets this to SS_RESTORE_DFL if FMONITOR */ setsig(&sigtraps[tt_sigs[i]], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); } @@ -397,9 +401,6 @@ j_change() } else { # ifdef TTY_PGRP ttypgrp_ok = 0; - /* the TF_SHELL_USES test is a kludge that lets us know if - * if the signals have been changed by the shell. - */ if (Flag(FTALKING)) for (i = NELEM(tt_sigs); --i >= 0; ) setsig(&sigtraps[tt_sigs[i]], SIG_IGN, @@ -410,7 +411,7 @@ j_change() |TF_ORIG_DFL)) setsig(&sigtraps[tt_sigs[i]], (sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ? SIG_IGN : SIG_DFL, - SS_RESTORE_CURR|SS_FORCE); + SS_RESTORE_ORIG|SS_FORCE); } # endif /* TTY_PGRP */ if (!Flag(FTALKING)) @@ -483,12 +484,9 @@ exchild(t, flags, close_fd) j->ppid = procpid; j->age = ++njobs; j->proc_list = p; + j->coproc_id = 0; last_job = j; last_proc = p; - if (flags & XXCOM) - j->flags |= JF_XXCOM; - else if (!(flags & XBGND)) - j->flags |= JF_FG; put_job(j, PJ_PAST_STOPPED); } @@ -535,8 +533,8 @@ exchild(t, flags, close_fd) dotty = 1; # ifdef NEED_PGRP_SYNC if (j_sync_open) { - close(j_sync_pipe[ischild ? 1 : 0]); - j_sync_pipe[ischild ? 1 : 0] = -1; + close(j_sync_pipe[ischild]); + j_sync_pipe[ischild] = -1; dosync = ischild; } # endif /* NEED_PGRP_SYNC */ @@ -571,18 +569,19 @@ exchild(t, flags, close_fd) #endif /* JOBS */ /* used to close pipe input fd */ - if (close_fd >= 0 && (((orig_flags & XPCLOSE) && i != 0) - || ((orig_flags & XCCLOSE) && i == 0))) + if (close_fd >= 0 && (((orig_flags & XPCLOSE) && !ischild) + || ((orig_flags & XCCLOSE) && ischild))) close(close_fd); - if (i == 0) { /* child */ + if (ischild) { /* child */ +#ifdef KSH + /* Do this before restoring signal */ + if (orig_flags & XCOPROC) + coproc_cleanup(FALSE); +#endif /* KSH */ #ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); #endif /* JOB_SIGS */ cleanup_parents_env(); -#ifdef KSH - if (orig_flags & XCOPROC) - cleanup_coproc(FALSE); -#endif /* KSH */ #ifdef TTY_PGRP /* If FMONITOR or FTALKING is set, these signals are ignored, * if neither FMONITOR nor FTALKING are set, the signals have @@ -604,9 +603,9 @@ exchild(t, flags, close_fd) setsig(&sigtraps[SIGQUIT], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); if (!(orig_flags & (XPIPEI | XCOPROC))) { - i = open("/dev/null", 0); - (void) ksh_dup2(i, 0, TRUE); - close(i); + int fd = open("/dev/null", 0); + (void) ksh_dup2(fd, 0, TRUE); + close(fd); } } remove_job(j, "child"); /* in case of `jobs` command */ @@ -634,8 +633,11 @@ exchild(t, flags, close_fd) #endif /* TTY_PGRP */ j_startjob(j); #ifdef KSH - if (flags & XCOPROC) - coproc.job = (void *) j; + if (orig_flags & XCOPROC) { + j->coproc_id = coproc.id; + coproc.njobs++; /* n jobs using co-process output */ + coproc.job = (void *) j; /* j using co-process input */ + } #endif /* KSH */ if (flags & XBGND) { j_set_async(j); @@ -1268,8 +1270,6 @@ j_sigchld(sig) WAIT_T status; struct tms t0, t1; - trapsig(sig); - #ifdef JOB_SIGS /* Don't wait for any processes if a job is partially started. * This is so we don't do away with the process group leader @@ -1386,9 +1386,24 @@ check_job(j) * remove_job() since neither may be called for non-interactive * shells. */ - if ((j->state == PEXITED || j->state == PSIGNALLED) - && coproc.job == (void *) j) - coproc.job = (void *) 0; + if (j->state == PEXITED || j->state == PSIGNALLED) { + /* No need to keep co-process input any more + * (at leasst, this is what ksh93d thinks) + */ + if (coproc.job == j) { + coproc.job = (void *) 0; + /* XXX would be nice to get the closes out of here + * so they aren't done in the signal handler. + * Would mean a check in coproc_getfd() to + * do "if job == 0 && write >= 0, close write". + */ + coproc_write_close(coproc.write); + } + /* Do we need to keep the output? */ + if (j->coproc_id && j->coproc_id == coproc.id + && --coproc.njobs == 0) + coproc_readw_close(coproc.read); + } #endif /* KSH */ j->flags |= JF_CHANGED; diff --git a/bin/pdksh/ksh.1 b/bin/pdksh/ksh.1 index a23f13d9594..6b0721aecdb 100644 --- a/bin/pdksh/ksh.1 +++ b/bin/pdksh/ksh.1 @@ -1,9 +1,10 @@ '\" t -.\" $OpenBSD: ksh.1,v 1.1 1996/08/14 06:19:12 downsj Exp $ +.\" $OpenBSD: ksh.1,v 1.2 1996/08/19 20:08:53 downsj Exp $ .\"{{{}}} .\"{{{ Notes about man page .\" - use the pseudo-macros .sh( and .sh) to begin and end sh-specific .\" text and .ksh( and .ksh) for ksh specific text. +.\" - put i.e., e.g. and etc. in italics .\"}}} .\"{{{ To do .\" todo: Things not covered that should be: @@ -58,11 +59,11 @@ If neither the \fB\-c\fP nor the \fB\-s\fP options are specified, the first non-option argument specifies the name of a file the shell reads commands from; if there are no non-option arguments, the shell reads commands from standard input. -The name of the shell (i.e., the contents of the \fB$0\fP) parameter +The name of the shell (\fIi.e.\fP, the contents of the \fB$0\fP) parameter is determined as follows: if the \fB\-c\fP option is used and there is a non-option argument, it is used as the name; if commands are being read from a file, the file is used as the name; otherwise the name -the shell was called with (i.e., argv[0]) is used. +the shell was called with (\fIi.e.\fP, argv[0]) is used. .PP A shell is \fBinteractive\fP if the \fB\-i\fP option is used or if both standard input and standard error are attached to a tty. @@ -74,7 +75,7 @@ For non-interactive shells, the \fBtrackall\fP option is on by default .PP A shell is \fBrestricted\fP if the \fB\-r\fP option is used or if either the basename of the name the shell is invoked with or the \fBSHELL\fP -parameter match the pattern *r*sh (e.g., rsh, rksh, rpdksh, etc.). +parameter match the pattern *r*sh (\fIe.g.\fP, rsh, rksh, rpdksh, \fIetc.\fP). The following restrictions come into effect after the shell processes any profile and \fB$ENV\fP files: .nr P2 \n(PD @@ -88,7 +89,7 @@ command names can't be specified with absolute or relative paths .IP \ \ \(bu the \fB\-p\fP option of the \fBcommand\fP built-in can't be used .IP \ \ \(bu -redirections that create files can't be used (i.e., \fB>\fP, +redirections that create files can't be used (\fIi.e.\fP, \fB>\fP, \fB>|\fP, \fB>>\fP, \fB<>\fP) .nr PD \n(P2 .PP @@ -100,9 +101,9 @@ parameter (see below), instead the file /etc/suid_profile is processed. Clearing the privileged option causes the shell to set its effective user-id (group-id) to its real user-id (group-id). .PP -If the basename of the name the shell is called with (i.e., argv[0]) starts -with \fB\-\fP or if the \fB\-l\fP option is used, the shell is assumed to be -a login shell and the shell reads and executes the contents of +If the basename of the name the shell is called with (\fIi.e.\fP, argv[0]) +starts with \fB\-\fP or if the \fB\-l\fP option is used, the shell is assumed +to be a login shell and the shell reads and executes the contents of \fB/etc/profile\fP and \fB$HOME/.profile\fP if they exist and are readable. .PP If the \fBENV\fP parameter is set when the shell starts (or, in the @@ -129,8 +130,8 @@ Words, which are sequences of characters, are delimited by unquoted Aside from delimiting words, spaces and tabs are ignored, while newlines usually delimit commands. The meta-characters are used in building the following tokens: -\fB<\fP, \fB<&\fP, \fB<<\fP, \fB>\fP, \fB>&\fP, \fB>>\fP, etc. are used to -specify redirections (see Input/Output Redirection below); +\fB<\fP, \fB<&\fP, \fB<<\fP, \fB>\fP, \fB>&\fP, \fB>>\fP, \fIetc.\fP are +used to specify redirections (see Input/Output Redirection below); \fB|\fP is used to create pipelines; \fB|&\fP is used to create co-processes (see Co-Processes below); \fB;\fP is used to separate commands; @@ -176,12 +177,12 @@ come before any command words. The command words, if any, define the command that is to be executed and its arguments. The command may be a shell built-in command, a function or an \fIexternal -command\fP, i.e., a separate executable file that is located using the +command\fP, \fIi.e.\fP, a separate executable file that is located using the \fBPATH\fP parameter (see Command Execution below). Note that all command constructs have an \fIexit status\fP: for external commands, this is related to the status returned by \fIwait\fP(2); the exit status of other command constructs (built-in commands, functions, -compound-commands, pipelines, lists, etc.) are all well defined and are +compound-commands, pipelines, lists, \fIetc.\fP) are all well defined and are described where the construct is described. The exit status of a command consisting only of parameter assignments is that of the last command substitution performed during the parameter assignment @@ -214,10 +215,10 @@ The \fB&\fP token causes the preceding command to be executed asynchronously, that is, the shell starts the command, but does not wait for it to complete (the shell does keep track of the status of asynchronous commands \(em see Job Control below). -When an asynchronous command is started when job control is disabled (i.e., -in most scripts), the command is started with signals INT and QUIT ignored -and with input redirected from /dev/null (however, redirections specified in -the asynchronous command have precedence). +When an asynchronous command is started when job control is disabled +(\fIi.e.\fP, in most scripts), the command is started with signals INT +and QUIT ignored and with input redirected from /dev/null +(however, redirections specified in the asynchronous command have precedence). The \fB|&\fP operator starts a \fIco-process\fP which is special kind of asynchronous process (see Co-Processes below). Note that a command must follow the \fB&&\fP and \fB||\fP operators, while @@ -229,7 +230,7 @@ exception of asynchronous lists, for which the exit status is 0. .PP Compound commands are created using the following reserved words \(em these words are only recognized if they are unquoted and if they are used as -the first word of a command (i.e., they can't be preceded by parameter +the first word of a command (\fIi.e.\fP, they can't be preceded by parameter assignments or redirections): .TS center; @@ -282,7 +283,7 @@ after a pattern is stripped; any space with a pattern must be quoted. Both the word and the patterns are subject to parameter, command, and arithmetic substitution as well as tilde substitution. For historical reasons, open and close braces may be used instead -of \fBin\fP and \fBesac\fP (e.g., \fBcase $foo { *) echo bar; }\fP). +of \fBin\fP and \fBesac\fP (\fIe.g.\fP, \fBcase $foo { *) echo bar; }\fP). The exit status of a \fBcase\fP statement is that of the executed \fIlist\fP; if no \fIlist\fP is executed, the exit status is zero. .\"}}} @@ -291,10 +292,10 @@ if no \fIlist\fP is executed, the exit status is zero. where \fIterm\fP is either a newline or a \fB;\fP. For each \fIword\fP in the specified word list, the parameter \fIname\fP is set to the word and \fIlist\fP is executed. If \fBin\fP is not used to -specify a word list, the positional parameters (\fB"$1"\fP, \fB"$2"\fP, etc.) -are used instead. +specify a word list, the positional parameters (\fB"$1"\fP, \fB"$2"\fP, +\fIetc.\fP) are used instead. For historical reasons, open and close braces may be used instead -of \fBdo\fP and \fBdone\fP (e.g., \fBfor i; { echo $i; }\fP). +of \fBdo\fP and \fBdone\fP (\fIe.g.\fP, \fBfor i; { echo $i; }\fP). The exit status of a \fBfor\fP statement is the last exit status of \fIlist\fP; if \fIlist\fP is never executed, the exit status is zero. .\"}}} @@ -303,8 +304,9 @@ of \fIlist\fP; if \fIlist\fP is never executed, the exit status is zero. If the exit status of the first \fIlist\fP is zero, the second \fIlist\fP is executed; otherwise the \fIlist\fP following the \fBelif\fP, if any, is executed with similar consequences. If all the lists following the \fBif\fP -and \fBelif\fPs fail (i.e., exit with non-zero status), the \fIlist\fP following -the \fBelse\fP is executed. The exit status of an \fBif\fP statement is that +and \fBelif\fPs fail (\fIi.e.\fP, exit with non-zero status), the \fIlist\fP +following the \fBelse\fP is executed. +The exit status of an \fBif\fP statement is that of non-conditional \fIlist\fP that is executed; if no non-conditional \fIlist\fP is executed, the exit status is zero. .\"}}} @@ -313,18 +315,23 @@ of non-conditional \fIlist\fP that is executed; if no non-conditional where \fIterm\fP is either a newline or a \fB;\fP. The \fBselect\fP statement provides an automatic method of presenting the user with a menu and selecting from it. -An enumerated list of the specified \fIwords\fP are printed on standard +An enumerated list of the specified \fIwords\fP is printed on standard error, followed by a prompt (\fBPS3\fP, normally `\fB#? \fP'). A number corresponding to one of the enumerated words is then read from standard input, \fIname\fP is set to the selected word (or is unset if the selection is not valid), \fBREPLY\fP -is set to what was read, and \fIlist\fP is executed. -This process is repeated until an end-of-file is read, an interrupt is +is set to what was read (leading/trailing space is stripped), +and \fIlist\fP is executed. +If a blank line (\fIi.e.\fP, zero or more \fBIFS\fP characters) is entered, +the menu is re-printed without executing \fIlist\fP. +When \fIlist\fP completes, the enumerated list is printed if \fBREPLY\fP +is null, the prompt is printed and so on. +This process is continues until an end-of-file is read, an interrupt is received or a break statement is executed inside the loop. If \fBin\fP \fIword\fP \fB\&...\fP is omitted, the positional parameters -are used (i.e., \fB"$1"\fP, \fB"$2"\fP, etc.). +are used (\fIi.e.\fP, \fB"$1"\fP, \fB"$2"\fP, \fIetc.\fP). For historical reasons, open and close braces may be used instead -of \fBdo\fP and \fBdone\fP (e.g., \fBselect i; { echo $i; }\fP). +of \fBdo\fP and \fBdone\fP (\fIe.g.\fP, \fBselect i; { echo $i; }\fP). The exit status of a \fBselect\fP statement is zero if a break statement is used to exit the loop, non-zero otherwise. .\"}}} @@ -374,10 +381,10 @@ arguments. The \fB\-a\fP (and) and \fB\-o\fP (or) operators are replaced with \fB&&\fP and \fB||\fP, respectively. .IP \ \ \(bu -Operators (e.g. \fB\-f\fP, \fB=\fP, \fB!\fP, etc.) must be unquoted. +Operators (\fIe.g.\fP, \fB\-f\fP, \fB=\fP, \fB!\fP, \fIetc.\fP) must be unquoted. .IP \ \ \(bu The second operand of \fB!=\fP and \fB=\fP -expressions are patterns (e.g., the comparison in +expressions are patterns (\fIe.g.\fP, the comparison in .ce \fB[[ foobar = f*r ]]\fP succeeds). @@ -388,7 +395,7 @@ or greater than, their second string operand, respectively. .IP \ \ \(bu The single argument form of \fBtest\fP, which tests if the argument has non-zero length, is not valid -- explicit operators must be always be used, e.g., instead of +- explicit operators must be always be used, \fIe.g.\fP, instead of .ce \fB[\fP \fIstr\fP \fB]\fP use @@ -419,7 +426,7 @@ Second, a single quote (\fB'\fP) quotes everything up to the next single quote (this may span lines). Third, a double quote (\fB"\fP) quotes all characters, except \fB$\fP, \fB`\fP and \fB\e\fP, up to the next unquoted double quote. -\fB$\fP and \fB`\fP inside double quotes have their usual meaning (i.e., +\fB$\fP and \fB`\fP inside double quotes have their usual meaning (\fIi.e.\fP, parameter, command or arithmetic substitution) except no field splitting is carried out on the results of double-quoted substitutions. If a \fB\e\fP inside a double-quoted string is followed by \fB\e\fP, \fB$\fP, @@ -434,7 +441,7 @@ of the form \fB"\fP...\fB`\fP...\fB\e"\fP...\fB`\fP..\fB"\fP. .SS "Aliases" There are two types of aliases: normal command aliases and tracked aliases. Command aliases are normally used as a short hand for a long -or often used command. The shell expands command aliases (i.e., +or often used command. The shell expands command aliases (\fIi.e.\fP, substitutes the alias name for its value) when it reads the first word of a command. An expanded alias is re-processed to check for more aliases. If a command alias ends in a space or tab, the following word @@ -480,7 +487,7 @@ The next time the command is executed, the shell checks the saved path to see that it is still valid, and if so, avoids repeating the path search. Tracked aliases can be listed and created using \fBalias \-t\fP. Note that changing the \fBPATH\fP parameter clears the saved -paths for all tracked aliases. If the \fBtrackall\fP option is set (i.e., +paths for all tracked aliases. If the \fBtrackall\fP option is set (\fIi.e.\fP, \fBset \-o trackall\fP or \fBset \-h\fP), the shell tracks all commands. This option is set automatically for non-interactive shells. For interactive shells, only the following commands are automatically @@ -507,7 +514,7 @@ any characters from the set space, tab and newline that appear in the IFS characters are called \fIIFS white space\fP. Sequences of one or more IFS white space characters, in combination with zero or one non-IFS white space characters delimit a field. -As a special case, leading and trailing IFS white space is stripped (i.e., +As a special case, leading and trailing IFS white space is stripped (\fIi.e.\fP, no leading or trailing empty field is created by it); leading or trailing non-IFS white space does create an empty field. Example: if \fBIFS\fP is set to `<space>:', the sequence of characters @@ -649,7 +656,8 @@ processes have been started, the parameter is not set. .\"}}} .\"{{{ # .IP \fB#\fP -The number of positional parameters (i.e., \fB$1\fP, \fB$2\fP, etc.). +The number of positional parameters (\fIi.e.\fP, \fB$1\fP, \fB$2\fP, +\fIetc.\fP). .\"}}} .\"{{{ $ .IP \fB$\fP @@ -669,7 +677,7 @@ the signal number. .\"}}} .\"{{{ 0 .IP "\fB0\fP" -The name the shell was invoked with (i.e., \fBargv[0]\fP), or the +The name the shell was invoked with (\fIi.e.\fP, \fBargv[0]\fP), or the \fBcommand-name\fP if it was invoked with the \fB\-c\fP option and the \fBcommand-name\fP was supplied, or the \fIfile\fP argument, if it was supplied. @@ -686,7 +694,7 @@ Further positional parameters may be accessed using .\"{{{ * .IP \fB*\fP All positional parameters (except parameter 0), -i.e., \fB$1 $2 $3\fP.... +\fIi.e.\fP, \fB$1 $2 $3\fP.... If used outside of double quotes, parameters are separate words (which are subjected to word splitting); if used within double quotes, parameters are separated by the first character of the \fBIFS\fP parameter @@ -836,7 +844,7 @@ before each prompt. The default is 600 (10 minutes). .IP \fBMAILPATH\fP A list of files to be checked for mail. The list is colon separated, and each file may be followed by a \fB?\fP and a message to be printed -if new mail has arrived. Command and parameter substitution is +if new mail has arrived. Command, parameter and arithmetic substitution is performed on the message, and, during substitution, the parameter \fB$_\fP contains the name of the file. The default message is \fByou have mail in $_\fP. @@ -880,7 +888,7 @@ The process ID of the shell's parent (readonly). Parameter, command and arithmetic substitutions are performed, and \fB!\fP is replaced with the current command number (see \fBfc\fP command below). A literal ! can be put in the prompt by placing !! in PS1. -Default is `\fB$\ \fP'. +Default is `\fB$\ \fP' for non-root users, `\fB#\ \fP' for root.. .\"}}} .\"{{{ PS2 .IP \fBPS2\fP @@ -966,7 +974,7 @@ and \fBtypeset\fP), tilde expansion is done after any unquoted colon .PP The home directory of previously expanded login names are cached and re-used. The \fBalias \-d\fP command may be used to list, change and -add to this cache (e.g., `alias \-d fac=/usr/local/facilities; cd +add to this cache (\fIe.g.\fP, `alias \-d fac=/usr/local/facilities; cd ~fac/bin'). .\"}}} .\"{{{ Brace Expansion @@ -977,11 +985,11 @@ Brace expressions, which take the form .RE are expanded to N words, each of which is the concatenation of \fIprefix\fP, \fIstr\fPi and \fIsuffix\fP -(e.g., `a{c,b{X,Y},d}e' expands to four word: ace, abXe, abYe, and ade). +(\fIe.g.\fP, `a{c,b{X,Y},d}e' expands to four word: ace, abXe, abYe, and ade). As noted in the example, brace expressions can be nested and the resulting words are not sorted. Brace expressions must contain an unquoted comma (\fB,\fP) for expansion -to occur (i.e., \fB{}\fP and \fB{foo}\fP are not expanded). +to occur (\fIi.e.\fP, \fB{}\fP and \fB{foo}\fP are not expanded). Brace expansion is carried out after parameter substitution and before file name generation. .\"}}} @@ -999,8 +1007,9 @@ matches any single character. matches any sequence of characters. .IP \fB[\fP..\fB]\fP matches any of the characters inside the brackets. Ranges of characters -can be specified by separating two characters by a \fB\-\fP, e.g., \fB[a0\-9]\fP -matches the letter \fBa\fP or any digit. In order to represent itself, a +can be specified by separating two characters by a \fB\-\fP, \fIe.g.\fP, +\fB[a0\-9]\fP matches the letter \fBa\fP or any digit. +In order to represent itself, a \fB\-\fP must either be quoted or the first or last character in the character list. Similarly, a \fB]\fP must be quoted or the first character in the list if it is represent itself instead of the end of the list. Also, a \fB!\fP @@ -1012,12 +1021,12 @@ like \fB[\fP..\fB]\fP, except it matches any character not inside the brackets. matches any string of characters that matches zero or more occurances of the specified patterns. Example: the pattern \fB*(foo|bar)\fP matches the strings -`', `foo', `bar', `foobarfoo', etc.. +`', `foo', `bar', `foobarfoo', \fIetc.\fP. .IP "\fB+(\fP\fIpattern\fP\fB|\fP ... \fP|\fP\fIpattern\fP\fB)\fP" matches any string of characters that matches one or more occurances of the specified patterns. Example: the pattern \fB+(foo|bar)\fP matches the strings -`foo', `bar', `foobarfoo', etc.. +`foo', `bar', `foobarfoo', \fIetc.\fP. .IP "\fB?(\fP\fIpattern\fP\fB|\fP ... \fP|\fP\fIpattern\fP\fB)\fP" matches the empty string or a string that matches one of the specified patterns. @@ -1046,8 +1055,8 @@ are never matched, even by the pattern \fB.*\fP. If the \fBmarkdirs\fP option is set, any directories that result from file name generation are marked with a trailing \fB/\fP. .PP -.\" todo: implement this ([[:alpha:]], etc.) -The POSIX character classes (i.e., +.\" todo: implement this ([[:alpha:]], \fIetc.\fP) +The POSIX character classes (\fIi.e.\fP, \fB[:\fP\fIclass-name\fP\fB:]\fP inside a \fB[\fP..\fB]\fP expression) are not yet implemented. .\"}}} @@ -1105,8 +1114,8 @@ the character \fB\-\fP, indicating standard input is to be closed. same as \fB<&\fP, except the operation is done on standard output. .PP In any of the above redirections, the file descriptor that is redirected -(i.e., standard input or standard output) can be explicitly given by preceding -the redirection with a single digit. +(\fIi.e.\fP, standard input or standard output) can be explicitly given by +preceding the redirection with a single digit. Parameter, command and arithmetic substitutions, tilde substitutions and file name generation are all performed on the \fIfile\fP, \fImarker\fP and \fIfd\fP arguments of redirections. @@ -1117,7 +1126,7 @@ Note that in restricted shells, redirections which can create files cannot be used. .PP For simple-commands, redirections may appear anywhere in the command, for -compound-commands (\fBif\fP statements, etc.), any redirections must +compound-commands (\fBif\fP statements, \fIetc.\fP), any redirections must appear at the end. Redirections are processed after pipelines are created and in the order they are given, so @@ -1130,7 +1139,7 @@ will print an error with a line number prepended to it. .SS "Arithmetic Expressions" Integer arithmetic expressions can be used with the \fBlet\fP command, inside \fB$((\fP..\fB))\fP expressions, -inside array references (e.g., \fIname\fP\fB[\fP\fIexpr\fP\fB]\fP), +inside array references (\fIe.g.\fP, \fIname\fP\fB[\fP\fIexpr\fP\fB]\fP), as numeric arguments to the \fBtest\fP command, and as the value of an assignment to an integer parameter. .PP @@ -1139,9 +1148,11 @@ references, and integer constants and may be combined with the following C operators (listed and grouped in increasing order of precedence). .TP Unary operators: -\fB+ \- ! ~\fP +\fB+ \- ! ~ ++ --\fP .TP Binary operators: +\fB,\fP +.br \fB= *= /= %= += \-= <<= >>= &= ^= |=\fP .br \fB||\fP @@ -1226,40 +1237,54 @@ otherwise \fI<arg3>\fP. .\"}}} .\"{{{ Co-Processes .SS "Co-Processes" -A Co-process, which is a pipeline created with the \fB|&\fP operator, +A co-process, which is a pipeline created with the \fB|&\fP operator, is an asynchronous process that the shell can both write to (using \fBprint \-p\fP) and read from (using \fBread \-p\fP). -Only one co-process may be active at any given time, unless the input -of an existing co-process has been duplicated using a \fB>&p\fP redirection. -In this case, the shell closes its copy of the co-process input (note -that a \fB<&p\fP redirection does not cause the shell to close its copy -of the co-process output). -Once \fBread \-p\fP has read the end-of-file, the co-process input is -closed, and further attempts to read will produce diagnostics. -Similarly, once \fBprint \-p\fP fails due to an EPIPE, the co-process -output is closed. -Note also, that \fBprint \-p\fP will ignore SIGPIPE signals during writes +The input and output of the co-process can also be manipulated +using \fB>&p\fP and \fB<&p\fP redirections, respectively. +Once a co-process has been started, another can't be started until +the co-process exits, or until the co-process input has been redirected using +an \fBexec \fP\fIn\fP\fB>&p\fP redirection. +If a co-process's input is redirected in this way, the next +co-process to be started will share the output with the first co-process, +unless the output of the initial co-process has been redirected using an +\fBexec \fP\fIn\fP\fB<&p\fP redirection. +.PP +Some notes concerning co-processes: +.nr P2 \n(PD +.nr PD 0 +.IP \ \ \(bu +the only way to close the co-process input (so the co-process reads +an end-of-file) is to redirect the input to a numbered file descriptor +and then close that file descriptor (\fIe.g.\fP, \fBexec 3>&p;exec 3>&-\fP). +.IP \ \ \(bu +in order for co-processes to share a common output, the shell must keep +the write portion of the output pipe open. +This means that end of file will not be detected until all co-processes +sharing the co-process output have exited (when they all exit, the shell +closes its copy of the pipe). +This can be avoided by redirecting the output to a numbered +file descriptor (as this also causes the shell to close its copy). +Note that this behaviour is slightly different from the original Korn shell +which closes its copy of the write portion of the co-processs output when the +most recently started co-process (instead of when all sharing co-processes) +exits. +.IP \ \ \(bu +\fBprint \-p\fP will ignore SIGPIPE signals during writes if the signal is not being trapped or ignored; the same is not true if the co-process input has been duplicated to another file descriptor and \fBprint \-u\fP\fIn\fP is used. -.PP -If another co-process is started before doing a read or \fB<&p\fP redirection -from existing co-processes, the output of the co-processes will be shared -(e.g., \fBecho hi |& echo there|& cat <&p\fP will print both "hi" and "there"). -This is slightly different from the original Korn shell in which output is -shared as long as the existing co-process job has not exited. -As a side affect, end-of-file can not be read reliably in the original Korn -shell (e.g., if the co-process closes its output but does not exit, the -end of file will not be read). +.nr PD \n(P2 .\"}}} .\"{{{ Functions .SS "Functions" Functions are defined using either Korn shell \fBfunction\fP \fIname\fP -syntax or the Bourne/POSIX shell \fIname\fP\fB()\fP syntax. -At the moment, there is no difference between the two, but see below. +syntax or the Bourne/POSIX shell \fIname\fP\fB()\fP syntax +(see below for the difference between the two forms). Functions are like \fB.\fP-scripts in that they are executed in the current environment, however, unlike \fB.\fP-scripts, shell arguments -(i.e., positional parameters, \fB$1\fP, etc.) are never visible inside them. +(\fIi.e.\fP, positional parameters, \fB$1\fP, \fIetc.\fP) are never visible +inside them. When the shell is determining the location of a command, functions are searched after special built-in commands, and before regular and non-regular built-ins, and before the \fBPATH\fP is searched. @@ -1273,7 +1298,8 @@ shell searches the path specified in the \fBFPATH\fP parameter for a file with the same name as the function, which, if found is read and executed. If after executing the file, the named function is found to be defined, the function is executed, otherwise, the normal command search is continued -(i.e., the shell searches the regular built-in command table and \fBPATH\fP). +(\fIi.e.\fP, the shell searches the regular built-in command table +and \fBPATH\fP). Note that if a command is not found using \fBPATH\fP, an attempt is made to autoload a function using \fBFPATH\fP (this is an undocumented feature of the original Korn shell). @@ -1289,20 +1315,31 @@ Since functions are executed in the current shell environment, parameter assignments made inside functions are visible after the function completes. If this is not the desired effect, the \fBtypeset\fP command can be used inside a function to create a local parameter. -Note that special parameters (e.g., \fB$$\fP, \fB$!\fP) can't be scoped in -this way. +Note that special parameters (\fIe.g.\fP, \fB$$\fP, \fB$!\fP) can't be +scoped in this way. .PP The exit status of a function is that of the last command executed in the function. A function can be made to finish immediately using the \fBreturn\fP command; this may also be used to explicitly specify the exit status. .PP -In the future, functions defined with the \fBfunction\fP reserved word will -be treated differently in the following ways from functions defined with +Functions defined with the \fBfunction\fP reserved word are +treated differently in the following ways from functions defined with the \fB()\fP notation: .nr P2 \n(PD .nr PD 0 .IP \ \ \(bu +the \fB$0\fP parameter is set to the name of the function +(Bourne-style functions leave \fB$0\fP untouched). +.IP \ \ \(bu +parameter assignments preceeding function calls are not kept in +the shell environment +(executing Bourne-style functions will keep assignments). +.nr PD \n(P2 +In the future, the following differences will also be added: +.nr P2 \n(PD +.nr PD 0 +.IP \ \ \(bu A separate trap/signal environment will be used during the execution of functions. This will mean that traps set inside a function will not affect the shell's @@ -1311,9 +1348,6 @@ have their default effect in a function. .IP \ \ \(bu The EXIT trap, if set in a function, will be executed after the function returns. -.IP \ \ \(bu -the \fB$0\fP parameter will be set to the name of the function (this is -currently the case for both styles of function). .nr PD \n(P2 .\"}}} .\"{{{ POSIX mode @@ -1350,10 +1384,6 @@ the two behaviours. \fBfg\fP exit status: in posix mode, the exit status is 0 if no errors occur; in non-posix mode, the exit status is that of the last foregrounded job. .IP \ \ \(bu -\fB$0\fP: in posix mode, it is always the name of the shell; in non-posix -mode, it is the name of the current function or script while a function or -script is being executed. -.IP \ \ \(bu \fBgetopts\fP: in posix mode, options must start with a \fB\-\fP; in non-posix mode, options can start with either \fB\-\fP or \fB+\fP. .IP \ \ \(bu @@ -1380,7 +1410,7 @@ in non-posix mode, field splitting, file globing, brace expansion and is turned on. .IP \ \ \(bu signal specification: in posix mode, signals can be specified as digits only -if signal numbers match POSIX values (i.e., HUP=1, INT=2, QUIT=3, ABRT=6, +if signal numbers match POSIX values (\fIi.e.\fP, HUP=1, INT=2, QUIT=3, ABRT=6, KILL=9, ALRM=14, and TERM=15); in non-posix mode, signals can be always digits. .IP \ \ \(bu alias expansion: in posix mode, alias expansion is only carried out when @@ -1535,9 +1565,9 @@ If neither the \fB\-v\fP nor \fB\-V\fP options are given, \fIcmd\fP is executed exactly as if the \fBcommand\fP had not been specified, with two exceptions: first, \fIcmd\fP cannot be a shell function, and -second, special built-in commands lose their specialness (i.e., redirection -and utility errors do not cause the shell to exit, and command assignments -are not permanent). +second, special built-in commands lose their specialness (\fIi.e.\fP, +redirection and utility errors do not cause the shell to exit, and command +assignments are not permanent). If the \fB\-p\fP option is given, a default search path is used instead of the current value of \fBPATH\fP (the actual value of the default path is system dependent: on POSIXish systems, it is the value returned by @@ -1553,8 +1583,8 @@ their names are simply printed, for aliases, a command that defines them is printed, and for commands found by searching the \fBPATH\fP parameter, the full path of the command is printed. -If no command is be found, (i.e., the path search fails), nothing is printed -and \fBcommand\fP exits with a non-zero status. +If no command is be found, (\fIi.e.\fP, the path search fails), nothing +is printed and \fBcommand\fP exits with a non-zero status. The \fB\-V\fP option is like the \fB\-v\fP option, except it is more verbose. .\"}}} .\"{{{ continue [levels] @@ -1590,7 +1620,7 @@ The command is executed without forking, replacing the shell process. If no arguments are given, any IO redirection is permanent and the shell is not replaced. Any file descriptors greater than 2 which are opened or \fIdup\fP(2)-ed -in this way are not made available to other executed commands (i.e., +in this way are not made available to other executed commands (\fIi.e.\fP, commands that are not built-in to the shell). .\"}}} .\"{{{ exit [status] @@ -1752,7 +1782,7 @@ As above, the \fB\-n\fP option suppresses the trailing newline. Print the present working directory. If \fB\-L\fP option is used or if the \fBphysical\fP option (see \fBset\fP command below) isn't set, the logical path is printed -(i.e., the path used to \fBcd\fP to the current directory). +(\fIi.e.\fP, the path used to \fBcd\fP to the current directory). If \fB\-P\fP option (physical path) is used or if the \fBphysical\fP option is set, the path determined from the filesystem (by following \fB..\fP directories to the root directory) is printed. @@ -1772,10 +1802,11 @@ If no input is read, \fBread\fP exits with a non-zero status. .sp A prompt, which is printed to standard error before any input is read, may be specified by appending and question mark and the prompt to the -first parameter (e.g., \fBread nfoo?'number of foos: '\fP). +first parameter (\fIe.g.\fP, \fBread nfoo?'number of foos: '\fP). .sp The \fB\-u\fP\fIn\fP and \fB\-p\fP options cause input to be read -from file descriptor \fIn\fP or the current co-process, respectively. +from file descriptor \fIn\fP or the current co-process (see Co-Processes above +for comments on this), respectively. If the \fB\-s\fP option is used, input is saved to the history file. .\"}}} .\"{{{ readonly [-p] [parameter[=value] ...] @@ -1816,7 +1847,7 @@ expand; afB lfB lw(3i). \-A T{ Sets the elements of the array parameter \fIname\fP to \fIarg\fP ...; -If \fB\-A\fP is used, the array is reset (i.e., emptied) first; +If \fB\-A\fP is used, the array is reset (\fIi.e.\fP, emptied) first; if \fB+A\fP is used, the first N elements are set (where N is the number of \fIarg\fPs), the rest are left untouched. T} @@ -1834,7 +1865,7 @@ be used to force an overwrite). T} \-e errexit T{ Exit (after executing the \fBERR\fP trap) as soon as an error occurs or -a command fails (i.e., exits with a non-zero status). +a command fails (\fIi.e.\fP, exits with a non-zero status). This does not apply to commands whose exit status is explicitly tested by a shell construct such as \fBif\fP, \fBuntil\fP, \fBwhile\fP, \fB&&\fP or \fB||\fP statements. @@ -1928,9 +1959,10 @@ from being stored in the history file. T} physical T{ Causes the \fBcd\fP and \fBpwd\fP commands to use `physical' -(i.e., the filesystem's) \fB..\fP directories instead of `logical' directories -(i.e., the shell handles \fB..\fP, which allows the user to be obliveous of -symlink links to directories). Clear by default. Note that setting +(\fIi.e.\fP, the filesystem's) \fB..\fP directories instead of `logical' +directories (\fIi.e.\fP, the shell handles \fB..\fP, which allows the user +to be obliveous of symlink links to directories). +Clear by default. Note that setting this option does not effect the current value of the \fBPWD\fP parameter; only the \fBcd\fP command changes \fBPWD\fP. See the \fBcd\fP and \fBpwd\fP commands above for more details. @@ -1966,7 +1998,7 @@ are currently on. .sp Remaining arguments, if any, are positional parameters and are assigned, in order, to the -positional parameters (i.e., \fB1\fP, \fB2\fP, etc.). +positional parameters (\fIi.e.\fP, \fB1\fP, \fB2\fP, \fIetc.\fP). If options are ended with \fB\-\-\fP and there are no remaining arguments, all positional parameters are cleared. If no options or arguments are given, then the values of all names are printed. @@ -1975,8 +2007,8 @@ it clears both the \fB\-x\fP and \fB\-v\fP options. .\"}}} .\"{{{ shift [number] .IP "\fBshift\fP [\fInumber\fP]" -The positional parameters \fInumber\fP+1, \fInumber\fP+2 etc.\& are renamed -to \fB1\fP, \fB2\fP, etc. +The positional parameters \fInumber\fP+1, \fInumber\fP+2 \fIetc.\fP\& are +renamed to \fB1\fP, \fB2\fP, \fIetc.\fP \fInumber\fP defaults to 1. .\"}}} .\"{{{ test expression, [ expression ] @@ -1992,7 +2024,7 @@ The following basic expressions are available: afB ltw(2.8i). \fIstr\fP T{ \fIstr\fP has non-zero length. Note that there is the potential -for problems if \fIstr\fP turns out to be an operator (e.g., \fB-r\fP) +for problems if \fIstr\fP turns out to be an operator (\fIe.g.\fP, \fB-r\fP) - it is generally better to use a test like .RS \fB[ X"\fP\fIstr\fP\fB" != X ]\fP @@ -2094,6 +2126,9 @@ T} \fIstring\fP = \fIstring\fP T{ strings are equal T} +\fIstring\fP == \fIstring\fP T{ +strings are equal +T} \fIstring\fP != \fIstring\fP T{ strings are not equal T} @@ -2147,7 +2182,7 @@ operator, including an unstripped \fB!\fP). .sp \fBNote:\fP A common mistake is to use \fBif [ $foo = bar ]\fP which fails if parameter \fBfoo\fP is null or unset, if it has embedded spaces -(i.e., \fBIFS\fP characters), or if it is a unary operator like \fB!\fP or +(\fIi.e.\fP, \fBIFS\fP characters), or if it is a unary operator like \fB!\fP or \fB\-n\fP. Use tests like \fBif [ "X$foo" = Xbar ]\fP instead. .\"}}} .\"{{{ times @@ -2162,10 +2197,10 @@ are received. \fBHandler\fP is either a null string, indicating the signals are to be ignored, a minus (\fB\-\fP), indicating that the default action is to be taken for the signals (see signal(2 or 3)), or a string containing shell -commands to be evaluated and executed at the first opportunity (i.e., when the -current command completes, or before printing the next \fBPS1\fP prompt) after -receipt of one of the signals. -\fBSignal\fP is the name of a signal (e.g., PIPE or ALRM) or the number +commands to be evaluated and executed at the first opportunity (\fIi.e.\fP, +when the current command completes, or before printing the next \fBPS1\fP +prompt) after receipt of one of the signals. +\fBSignal\fP is the name of a signal (\fIe.g.\fP, PIPE or ALRM) or the number of the signal (see \fBkill \-l\fP command above). There are two special signals: \fBEXIT\fP (also known as \fB0\fP), which is executed when the shell is about to exit, and \fBERR\fP which is @@ -2205,7 +2240,7 @@ to the function. .sp When \fB\-f\fP is used, typeset operates on the attributes of functions. As with parameters, if no \fIname\fPs are given, functions are listed -with their values (i.e., definitions) unless options are introduced with +with their values (\fIi.e.\fP, definitions) unless options are introduced with \fB+\fP, in which case only the function names are reported. .sp .TS @@ -2394,6 +2429,8 @@ are carried out on tracked or directory aliases, respectively. .\"{{{ unset [-fv] parameter ... .IP "\fBunset\fP [\fB\-fv\fP] \fIparameter\fP ..." Unset the named parameters (\fB\-v\fP, the default) or functions (\fB\-f\fP). +The exit status is non-zero if any of the parameters were already unset, +zero otherwise. .\"}}} .\"{{{ wait [job] .IP "\fBwait\fP [\fIjob\fP]" @@ -2417,7 +2454,7 @@ printed (this is not the case when jobs are explicitly specified). For each name, the type of command is listed (reserved word, built-in, alias, function, tracked alias or executable). If the \fB\-p\fP option is used, a path search done even if \fIname\fP -is a reserved word, alias, etc. +is a reserved word, alias, \fIetc.\fP Without the \fB\-v\fP option, \fBwhence\fP is similar to \fBcommand \-v\fP except that \fBwhence\fP will find reserved words and won't print aliases as alias commands; @@ -2433,7 +2470,7 @@ exit status is non-zero. Job control refers to the shell's ability to monitor and control \fBjobs\fP, which are processes or groups of processes created for commands or pipelines. At a minimum, the shell keeps track of the status of the background -(i.e., asynchronous) jobs that currently exist; this information can be +(\fIi.e.\fP, asynchronous) jobs that currently exist; this information can be displayed using the \fBjobs\fP command. If job control is fully enabled (using \fBset \-m\fP or \fBset \-o monitor\fP), as it is for interactive shells, @@ -2445,9 +2482,9 @@ or background, using the \fBfg\fP and \fBbg\fP commands, respectively, and the state of the terminal is saved or restored when a foreground job is stopped or restarted, respectively. .sp -Note that only commands that create processes (e.g., asynchronous commands, -subshell commands, and non-built-in, non-function commands) can be -stopped; commands like \fBread\fP cannot be. +Note that only commands that create processes (\fIe.g.\fP, +asynchronous commands, subshell commands, and non-built-in, +non-function commands) can be stopped; commands like \fBread\fP cannot be. .sp When a job is created, it is assigned a job-number. For interactive shells, this number is printed inside \fB[\fP..\fB]\fP, @@ -2485,7 +2522,7 @@ are matched). T} .TE .sp -When a job changes state (e.g., a background job finishes or foreground +When a job changes state (\fIe.g.\fP, a background job finishes or foreground job is stopped), the shell prints the following status information: .RS \fB[\fP\fInumber\fP\fB]\fP \fIflag status command\fP @@ -2510,7 +2547,8 @@ omitted if the status is zero. the job was stopped by the indicated \fIsignal\fP (if no signal is given, the job was stopped by SIGTSTP). .IP "\fIsignal-description\fP [\fB(core dumped)\fP]" -the job was killed by a signal (e.g., Memory\ fault, Hangup, etc. \(em use +the job was killed by a signal (\fIe.g.\fP, Memory\ fault, +Hangup, \fIetc.\fP \(em use \fBkill \-l\fP for a list of signal descriptions). The \fB(core\ dumped)\fP message indicates the process created a core file. .RE @@ -2584,7 +2622,7 @@ The following is a list of editing commands available. Each description starts with the name of the command, a \fIn\fP, if the command can be prefixed with a count, and any keys the command is bound to by default (written using -caret notation, e.g., ASCII ESC character is written as ^[). +caret notation, \fIe.g.\fP, ASCII ESC character is written as ^[). A count prefix for a command is entered using the sequence \fB^[\fP\fIn\fP, where \fIn\fP is a sequence of 1 or more digits; unless otherwise specified, if a count is omitted, it defaults to 1. @@ -2592,7 +2630,7 @@ Note that editing command names are used only with the \fBbind\fP command. Furthermore, many editing commands are useful only on terminals with a visible cursor. The default bindings were chosen to resemble corresponding EMACS key -bindings. The users tty characters (e.g., ERASE) are bound to +bindings. The users tty characters (\fIe.g.\fP, ERASE) are bound to reasonable substitutes and override the default bindings. .\"{{{ abort ^G .IP "\fBabort ^G\fP" @@ -2626,6 +2664,12 @@ Moves the cursor to the beginning of the edited input line. Uppercase the first character in the next \fIn\fP words, leaving the cursor past the end of the last word. .\"}}} +.\"{{{ comment ^[# +If the current line does not begin with a comment character, one +is added at the beginning of the line and the line is entered (as if +return had been pressed), otherwise the existing comment characters +are removed and the cursor is placed at the beginning of the line. +.\"}}} .\"{{{ complete ^[^[ .IP "\fBcomplete ^[^[\fP" Automatically completes as much as is unique of the command name @@ -2875,7 +2919,7 @@ the \fB/\fP and \fBG\fP commands move in the opposite direction as the \fBj\fP command .IP \ \ \(bu and commands which don't make sense in a single line editor are not available -(e.g., screen movement commands, ex \fB:\fP commands, etc.). +(\fIe.g.\fP, screen movement commands, ex \fB:\fP commands, \fIetc.\fP). .nr PD \n(P2 .LP Note that the \fB^X\fP stands for control-X; also \fB<esc>\fP, \fB<space>\fP @@ -2941,14 +2985,14 @@ In command mode, each character is interpreted as a command. Characters that don't correspond to commands, are illegal combinations of commands or are commands that can't be carried out all cause beeps. In the following command descriptions, a \fIn\fP indicates the -command may be prefixed by a number (e.g., \fB10l\fP moves right 10 +command may be prefixed by a number (\fIe.g.\fP, \fB10l\fP moves right 10 characters); if no number prefix is used, \fIn\fP is assumed to be 1 unless otherwise specified. The term `current position' refers to the position between the cursor and the character preceding the cursor. A `word' is a sequence of letters, digits and underscore characters or a sequence of non-letter, non-digit, non-underscore, non-white-space characters -(e.g., ab2*&^ contains two words) and a `big-word' is a sequence of +(\fIe.g.\fP, ab2*&^ contains two words) and a `big-word' is a sequence of non-white-space characters. .\"{{{ Special ksh vi commands .IP "Special ksh vi commands" @@ -3083,31 +3127,32 @@ direction of the search is the opposite of the last search. .IP "\fIn\fP\fBa\fP" append text \fIn\fP times: goes into insert mode just after the current position. -The append is only replicated if command mode is re-entered (i.e., <esc> is -used). +The append is only replicated if command mode is re-entered (\fIi.e.\fP, +<esc> is used). .IP "\fIn\fP\fBA\fP" same as \fBa\fP, except it appends at the end of the line. .IP "\fIn\fP\fBi\fP" insert text \fIn\fP times: goes into insert mode at the current position. -The insertion is only replicated if command mode is re-entered (i.e., <esc> is -used). +The insertion is only replicated if command mode is re-entered (\fIi.e.\fP, +<esc> is used). .IP "\fIn\fP\fBI\fP" same as \fBi\fP, except the insertion is done just before the first non-blank character. .IP "\fIn\fP\fBs\fP" -substitute the next \fIn\fP characters (i.e., delete the characters +substitute the next \fIn\fP characters (\fIi.e.\fP, delete the characters and go into insert mode). .IP "\fBS\fP" substitute whole line: all characters from the first non-blank character to the end of line are deleted and insert mode is entered. .IP "\fIn\fP\fBc\fP\fImove-cmd\fP" change from the current position to the position resulting from \fIn\fP -\fImove-cmd\fPs (i.e., delete the indicated region and go into insert mode); +\fImove-cmd\fPs (\fIi.e.\fP, delete the indicated region and go into insert +mode); if \fImove-cmd\fP is \fBc\fP, the line starting from the first non-blank character is changed. .IP "\fBC\fP" -change from the current position to the end of the line (i.e., delete to +change from the current position to the end of the line (\fIi.e.\fP, delete to the end of the line and go into insert mode). .IP "\fIn\fP\fBx\fP" delete the next \fIn\fP characters. diff --git a/bin/pdksh/ksh_stat.h b/bin/pdksh/ksh_stat.h index 0c6e5244f57..447b7b3f385 100644 --- a/bin/pdksh/ksh_stat.h +++ b/bin/pdksh/ksh_stat.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ksh_stat.h,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: ksh_stat.h,v 1.2 1996/08/19 20:08:54 downsj Exp $ */ /* Wrapper around the ugly sys/stat includes/ifdefs */ @@ -15,6 +15,7 @@ # undef S_ISCHR # undef S_ISBLK # undef S_ISFIFO +# undef S_ISSOCK # undef S_ISLNK #endif /* STAT_MACROS_BROKEN */ diff --git a/bin/pdksh/lex.c b/bin/pdksh/lex.c index 06e912fae09..74ed0748fef 100644 --- a/bin/pdksh/lex.c +++ b/bin/pdksh/lex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lex.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: lex.c,v 1.2 1996/08/19 20:08:55 downsj Exp $ */ /* * lexical analysis and source input @@ -968,6 +968,7 @@ set_prompt(to, s) switch (to) { case PS1: /* command */ +#ifdef KSH /* Substitute ! and !! here, before substitutions are done * so ! in expanded variables are not expanded. * NOTE: this is not what at&t ksh does (it does it after @@ -993,12 +994,20 @@ set_prompt(to, s) newenv(E_ERRH); if (ksh_sigsetjmp(e->jbuf, 0)) { prompt = safe_prompt; - warningf(TRUE, "error during expansion of PS1"); + /* Don't print an error - assume it has already + * been printed. Reason is we may have forked + * to run a command and the child may be + * unwinding its stack through this code as it + * exits. + */ } else prompt = str_save(substitute(ps1, 0), saved_atemp); quitenv(); } +#else /* KSH */ + prompt = str_val(global("PS1")); +#endif /* KSH */ break; case PS2: /* command continuation */ diff --git a/bin/pdksh/mail.c b/bin/pdksh/mail.c index 3422a5873cc..a26bde87579 100644 --- a/bin/pdksh/mail.c +++ b/bin/pdksh/mail.c @@ -1,10 +1,13 @@ -/* $OpenBSD: mail.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: mail.c,v 1.2 1996/08/19 20:08:55 downsj Exp $ */ /* * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by * John R. MacMillan */ +#include "config.h" + +#ifdef KSH #include "sh.h" #include "ksh_stat.h" #include "ksh_time.h" @@ -183,3 +186,4 @@ mbox_t *mbp; unset(vp, 0); } +#endif /* KSH */ diff --git a/bin/pdksh/main.c b/bin/pdksh/main.c index 10290b31135..74118b7f037 100644 --- a/bin/pdksh/main.c +++ b/bin/pdksh/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: main.c,v 1.2 1996/08/19 20:08:56 downsj Exp $ */ /* * startup, main loop, enviroments and error handling @@ -41,9 +41,9 @@ static const char *const initcoms [] = { "typeset", "-x", "SHELL", "PATH", "HOME", NULL, "typeset", "-r", version_param, NULL, "typeset", "-ri", "PPID", NULL, - "typeset", "-i", "OPTIND=1", "MAILCHECK=600", + "typeset", "-i", "OPTIND=1", #ifdef KSH - "SECONDS=0", "RANDOM", "TMOUT=0", + "MAILCHECK=600", "RANDOM", "SECONDS=0", "TMOUT=0", #endif /* KSH */ NULL, "alias", @@ -278,7 +278,7 @@ main(argc, argv) * This changes the behavior of 'ksh arg' to search * the users search path but it can't be helped. */ - s->file = search(argv[argi++], path, R_OK); + s->file = search(argv[argi++], path, R_OK, (int *) 0); if (!s->file || !*s->file) s->file = argv[argi - 1]; #else @@ -406,7 +406,9 @@ main(argc, argv) if (Flag(FTALKING)) { hist_init(s); +#ifdef KSH alarm_init(); +#endif /* KSH */ } else Flag(FTRACKALL) = 1; /* set after ENV */ @@ -568,7 +570,9 @@ shell(s, toplevel) if (interactive) { j_notify(); +#ifdef KSH mcheck(); +#endif /* KSH */ set_prompt(PS1, s); } diff --git a/bin/pdksh/misc.c b/bin/pdksh/misc.c index dcf7cc9bf63..dcbd8130b18 100644 --- a/bin/pdksh/misc.c +++ b/bin/pdksh/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: misc.c,v 1.2 1996/08/19 20:08:57 downsj Exp $ */ /* * Miscellaneous functions @@ -14,7 +14,7 @@ # define UCHAR_MAX 0xFF #endif -char ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */ +short ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */ static int do_gmatch ARGS((const unsigned char *s, const unsigned char *p, const unsigned char *se, const unsigned char *pe, @@ -31,7 +31,7 @@ setctypes(s, t) { register int i; - if ((t&C_IFS)) { + if (t & C_IFS) { for (i = 0; i < UCHAR_MAX+1; i++) ctypes[i] &= ~C_IFS; ctypes[0] |= C_IFS; /* include \0 in C_IFS */ @@ -56,6 +56,7 @@ initctypes() setctypes(" \t\n", C_IFSWS); setctypes("=-+?", C_SUBOP1); setctypes("#%", C_SUBOP2); + setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE); } /* convert unsigned long to base N string */ @@ -168,6 +169,7 @@ const struct option options[] = { { "viraw", 0, OF_ANY }, /* no effect */ { "vi-show8", 0, OF_ANY }, /* non-standard */ { "vi-tabcomplete", 0, OF_ANY }, /* non-standard */ + { "vi-esccomplete", 0, OF_ANY }, /* non-standard */ #endif { "xtrace", 'x', OF_ANY }, { NULL, 0, 0 } @@ -982,7 +984,7 @@ ksh_getopt(argv, go, options) go->buf[0] = c; go->optarg = go->buf; } else { - warningf(TRUE, "%s%s-%c: bad option", + warningf(TRUE, "%s%s-%c: unknown option", (go->flags & GF_NONAME) ? "" : argv[0], (go->flags & GF_NONAME) ? "" : ": ", c); if (go->flags & GF_ERROR) @@ -1055,7 +1057,7 @@ print_value_quoted(s) /* Test if any quotes are needed */ for (p = s; *p; p++) - if (!letnum(*p) && *p != '/') + if (ctype(*p, C_QUOTE)) break; if (!*p) { shprintf("%s", s); diff --git a/bin/pdksh/proto.h b/bin/pdksh/proto.h index 067f28c64d0..a290e18a205 100644 --- a/bin/pdksh/proto.h +++ b/bin/pdksh/proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proto.h,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: proto.h,v 1.2 1996/08/19 20:08:57 downsj Exp $ */ /* * prototypes for PD-KSH @@ -73,8 +73,9 @@ int define ARGS((const char *name, struct op *t)); void builtin ARGS((const char *name, int (*func)(char **))); struct tbl * findcom ARGS((const char *name, int flags)); void flushcom ARGS((int all)); -char * search ARGS((const char *name, const char *path, int mode)); -int search_access ARGS((const char *path, int mode)); +char * search ARGS((const char *name, const char *path, int mode, + int *errnop)); +int search_access ARGS((const char *path, int mode, int *errnop)); int pr_menu ARGS((char *const *ap)); /* expr.c */ int evaluate ARGS((const char *expr, long *rval, int error_ok)); @@ -124,8 +125,8 @@ void coproc_init ARGS((void)); void coproc_read_close ARGS((int fd)); void coproc_readw_close ARGS((int fd)); void coproc_write_close ARGS((int fd)); -int get_coproc_fd ARGS((int mode, const char **emsgp)); -void cleanup_coproc ARGS((int reuse)); +int coproc_getfd ARGS((int mode, const char **emsgp)); +void coproc_cleanup ARGS((int reuse)); #endif /* KSH */ struct temp *maketemp ARGS((Area *ap)); /* jobs.c */ @@ -150,9 +151,11 @@ Source * pushs ARGS((int type, Area *areap)); void set_prompt ARGS((int to, Source *s)); void pprompt ARGS((const char *cp, int ntruncate)); /* mail.c */ +#ifdef KSH void mcheck ARGS((void)); void mbset ARGS((char *p)); void mpset ARGS((char *mptoparse)); +#endif /* KSH */ /* main.c */ int include ARGS((const char *name, int argc, char **argv, int intr_ok)); @@ -215,7 +218,9 @@ struct tbl ** tsort ARGS((struct table *tp)); /* trace.c */ /* trap.c */ void inittraps ARGS((void)); +#ifdef KSH void alarm_init ARGS((void)); +#endif /* KSH */ Trap * gettrap ARGS((const char *name)); RETSIGTYPE trapsig ARGS((int i)); void intrcheck ARGS((void)); @@ -249,7 +254,7 @@ void setstr ARGS((struct tbl *vq, const char *s)); struct tbl *setint_v ARGS((struct tbl *vq, struct tbl *vp)); void setint ARGS((struct tbl *vq, long n)); int getint ARGS((struct tbl *vp, long *nump)); -struct tbl * typeset ARGS((const char *var, int set, int clr, int field, int base)); +struct tbl * typeset ARGS((const char *var, Tflag set, Tflag clr, int field, int base)); void unset ARGS((struct tbl *vp, int array_ref)); char * skip_varname ARGS((const char *s, int aok)); char *skip_wdvarname ARGS((const char *s, int aok)); diff --git a/bin/pdksh/sh.h b/bin/pdksh/sh.h index b7fcde1ac93..f6578427d9c 100644 --- a/bin/pdksh/sh.h +++ b/bin/pdksh/sh.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sh.h,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: sh.h,v 1.2 1996/08/19 20:08:58 downsj Exp $ */ /* * Public Domain Bourne/Korn shell @@ -331,6 +331,9 @@ typedef int bool_t; #define sizeofN(type, n) (sizeof(type) * (n)) #define BIT(i) (1<<(i)) /* define bit in flag */ +/* Table flag type - needs > 16 and < 32 bits */ +typedef INT32 Tflag; + #define NUFILE 10 /* Number of user-accessible files */ #define FDBASE 10 /* First file usable by Shell */ @@ -477,6 +480,7 @@ enum sh_flag { FVIRAW, /* always read in raw mode (ignored) */ FVISHOW8, /* display chars with 8th bit set as is (versus M-) */ FVITABCOMPLETE, /* enable tab as file name completion char */ + FVIESCCOMPLETE, /* enable ESC as file name completion in command mode */ #endif FXTRACE, /* -x: execution trace */ FNFLAGS /* (place holder: how many flags are there) */ @@ -523,6 +527,7 @@ typedef struct trap { int volatile set; /* trap pending */ int flags; /* TF_* */ handler_t cursig; /* current handler (valid if TF_ORIG_* set) */ + handler_t shtrap; /* shell signal handler */ } Trap; /* values for Trap.flags */ @@ -541,10 +546,11 @@ typedef struct trap { #define SS_RESTORE_MASK 0x3 /* how to restore a signal before an exec() */ #define SS_RESTORE_CURR 0 /* leave current handler in place */ #define SS_RESTORE_ORIG 1 /* restore original handler */ -#define SS_RESTORE_DFL 2 /* restore SIG_DFL */ -#define SS_RESTORE_IGN 3 /* restore SIG_IGN */ +#define SS_RESTORE_DFL 2 /* restore to SIG_DFL */ +#define SS_RESTORE_IGN 3 /* restore to SIG_IGN */ #define SS_FORCE BIT(3) /* set signal even if original signal ignored */ #define SS_USER BIT(4) /* user is doing the set (ie, trap command) */ +#define SS_SHTRAP BIT(5) /* trap for internal use (CHLD,ALRM,WINCH) */ #define SIGEXIT_ 0 /* for trap EXIT */ #define SIGERR_ SIGNALS /* for trap ERR */ @@ -558,6 +564,7 @@ extern Trap sigtraps[SIGNALS+1]; #endif /* !FROM_TRAP_C */ +#ifdef KSH /* * TMOUT support */ @@ -569,6 +576,7 @@ enum tmout_enum { }; EXTERN unsigned int ksh_tmout; EXTERN enum tmout_enum ksh_tmout_state I__(TMOUT_EXECUTING); +#endif /* KSH */ /* For "You have stopped jobs" message */ @@ -578,16 +586,17 @@ EXTERN int really_exit; /* * fast character classes */ -#define C_ALPHA 0x01 /* a-z_A-Z */ -#define C_DIGIT 0x02 /* 0-9 */ -#define C_LEX1 0x04 /* \0 \t\n|&;<>() */ -#define C_VAR1 0x08 /* *@#!$-? */ -#define C_IFSWS 0x10 /* \t \n (IFS white space) */ -#define C_SUBOP1 0x20 /* "=-+?" */ -#define C_SUBOP2 0x40 /* "#%" */ -#define C_IFS 0x80 /* $IFS */ - -extern char ctypes []; +#define C_ALPHA BIT(0) /* a-z_A-Z */ +#define C_DIGIT BIT(1) /* 0-9 */ +#define C_LEX1 BIT(2) /* \0 \t\n|&;<>() */ +#define C_VAR1 BIT(3) /* *@#!$-? */ +#define C_IFSWS BIT(4) /* \t \n (IFS white space) */ +#define C_SUBOP1 BIT(5) /* "=-+?" */ +#define C_SUBOP2 BIT(6) /* "#%" */ +#define C_IFS BIT(7) /* $IFS */ +#define C_QUOTE BIT(8) /* \n\t"#$&'()*;<>?[\`| (needing quoting) */ + +extern short ctypes []; #define ctype(c, t) !!(ctypes[(unsigned char)(c)]&(t)) #define letter(c) ctype(c, C_ALPHA) @@ -623,20 +632,29 @@ EXTERN Getopt builtin_opt; /* for shell builtin commands */ #ifdef KSH /* This for co-processes */ + +typedef INT32 Coproc_id; /* something that won't (realisticly) wrap */ struct coproc { int read; /* pipe from co-process's stdout */ int readw; /* other side of read (saved temporarily) */ int write; /* pipe to co-process's stdin */ - void *job; /* 0 if no co-process, or co-process died */ + Coproc_id id; /* id of current output pipe */ + int njobs; /* number of live jobs using output pipe */ + void *job; /* 0 or job of co-process using input pipe */ }; EXTERN struct coproc coproc; #endif /* KSH */ +/* Used in jobs.c and by coprocess stuff in exec.c */ +#ifdef JOB_SIGS +EXTERN sigset_t sm_default, sm_sigchld; +#endif /* JOB_SIGS */ + extern const char ksh_version[]; /* name of called builtin function (used by error functions) */ EXTERN char *builtin_argv0; -EXTERN int builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */ +EXTERN Tflag builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */ /* current working directory, and size of memory allocated for same */ EXTERN char *current_wd; diff --git a/bin/pdksh/syn.c b/bin/pdksh/syn.c index bd5db3740dd..8779c315841 100644 --- a/bin/pdksh/syn.c +++ b/bin/pdksh/syn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: syn.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: syn.c,v 1.2 1996/08/19 20:08:59 downsj Exp $ */ /* * shell parser (C version) @@ -224,7 +224,7 @@ get_command(cf) syniocf &= ~(KEYWORD|ALIAS); t = newtp(TCOM); while (1) { - cf = (t->evalflags ? ARRAYVAR : 0) + cf = (t->u.evalflags ? ARRAYVAR : 0) | (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD); switch (tpeek(cf)) { case REDIR: @@ -241,7 +241,7 @@ get_command(cf) if (iopn == 0 && XPsize(vars) == 0 && XPsize(args) == 0 && assign_command(ident)) - t->evalflags = DOVACHECK; + t->u.evalflags = DOVACHECK; if ((XPsize(args) == 0 || Flag(FKEYWORD)) && is_wdvarassign(yylval.cp)) XPput(vars, yylval.cp); @@ -254,7 +254,7 @@ get_command(cf) * allows (not POSIX, but not disallowed) */ afree(t, ATEMP); - if (XPsize(args) == 0 && XPsize(vars) != 0) { + if (XPsize(args) == 0 && XPsize(vars) == 0) { ACCEPT; goto Subshell; } @@ -334,7 +334,7 @@ get_command(cf) case WHILE: case UNTIL: multiline_push(&old_multiline, c); - t = newtp((c == WHILE) ? TWHILE: TUNTIL); + t = newtp((c == WHILE) ? TWHILE : TUNTIL); t->left = c_list(); t->right = dogroup(); multiline_pop(&old_multiline); @@ -552,6 +552,7 @@ function_body(name, ksh_func) } t = newtp(TFUNCT); t->str = Xclose(xs, xp); + t->u.ksh_func = ksh_func; /* Note that POSIX allows only compound statements after foo(), sh and * at&t ksh allow any command, go with the later since it shouldn't @@ -763,7 +764,7 @@ newtp(type) t = (struct op *) alloc(sizeof(*t), ATEMP); t->type = type; - t->evalflags = 0; + t->u.evalflags = 0; t->args = t->vars = NULL; t->ioact = NULL; t->left = t->right = NULL; diff --git a/bin/pdksh/table.c b/bin/pdksh/table.c index 03558fa137a..3b2861bb63f 100644 --- a/bin/pdksh/table.c +++ b/bin/pdksh/table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: table.c,v 1.1 1996/08/14 06:19:11 downsj Exp $ */ +/* $OpenBSD: table.c,v 1.2 1996/08/19 20:08:59 downsj Exp $ */ /* * dynamic hashed associative table for commands and variables @@ -64,7 +64,7 @@ texpand(tp, nsize) p += tp->size; *p = tblp; tp->nfree--; - } else { + } else if (!(tblp->flag & FINUSE)) { afree((void*)tblp, tp->areap); } afree((void*)otblp, tp->areap); @@ -125,7 +125,7 @@ tenter(tp, n, h) p->flag = 0; p->type = 0; p->areap = tp->areap; - p->field = 0; + p->u2.field = 0; p->u.array = (struct tbl *)0; memcpy(p->name, n, len); diff --git a/bin/pdksh/table.h b/bin/pdksh/table.h index 1dfc08942fa..753339c33bc 100644 --- a/bin/pdksh/table.h +++ b/bin/pdksh/table.h @@ -1,4 +1,4 @@ -/* $OpenBSD: table.h,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: table.h,v 1.2 1996/08/19 20:09:00 downsj Exp $ */ /* $From: table.h,v 1.3 1994/05/31 13:34:34 michael Exp $ */ @@ -13,7 +13,7 @@ struct table { }; struct tbl { /* table item */ - INT32 flag; /* flags */ + Tflag flag; /* flags */ int type; /* command type (see below), base (if INTEGER), * or offset from val.s of value (if EXPORT) */ Area *areap; /* area to allocate from */ @@ -24,7 +24,10 @@ struct tbl { /* table item */ struct op *t; /* "function" tree */ } val; /* value */ int index; /* index for an array */ - int field; /* field with for -L/-R/-Z */ + union { + int field; /* field with for -L/-R/-Z */ + int errno_; /* CEXEC/CTALIAS */ + } u2; union { struct tbl *array; /* array values */ char *fpath; /* temporary path to undef function */ @@ -54,12 +57,15 @@ struct tbl { /* table item */ #define INT_L BIT(20) /* long integer (no-op) */ #define IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */ #define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */ +#define EXPRINEVAL BIT(23) /* contents currently being evaluated */ +#define EXPRLVALUE BIT(24) /* useable as lvalue (temp flag) */ /* flag bits used for taliases/builtins/aliases/keywords/functions */ #define KEEPASN BIT(8) /* keep command assignments (eg, var=x cmd) */ #define FINUSE BIT(9) /* function being executed */ #define FDELETE BIT(10) /* function deleted while it was executing */ -#define SPEC_BI BIT(11) /* a POSIX special builtin */ -#define REG_BI BIT(12) /* a POSIX regular builtin */ +#define FKSH BIT(11) /* function defined with function x (vs x()) */ +#define SPEC_BI BIT(12) /* a POSIX special builtin */ +#define REG_BI BIT(13) /* a POSIX regular builtin */ /* command types */ #define CNONE 0 /* undefined */ diff --git a/bin/pdksh/tests/arith.t b/bin/pdksh/tests/arith.t new file mode 100644 index 00000000000..e18ea2e9d78 --- /dev/null +++ b/bin/pdksh/tests/arith.t @@ -0,0 +1,79 @@ +name: arith-lazy-1 +description: + Check that only one side of ternary operator is evaluated +stdin: + x=i+=2 + y=j+=2 + typeset -i i=1 j=1 + echo $((1 ? 20 : (x+=2))) + echo $i,$x + echo $((0 ? (y+=2) : 30)) + echo $j,$y +expected-stdout: + 20 + 1,i+=2 + 30 + 1,j+=2 +--- + +name: arith-lazy-2 +description: + Check that assignments not done on non-evaluated side of ternary + operator +stdin: + x=i+=2 + y=j+=2 + typeset -i i=1 j=1 + echo $((1 ? 20 : (x+=2))) + echo $i,$x + echo $((0 ? (y+=2) : 30)) + echo $i,$y +expected-stdout: + 20 + 1,i+=2 + 30 + 1,j+=2 +--- + +name: arith-ternary-prec-1 +description: + Check precidance of ternary operator vs assignment +stdin: + typeset -i x=2 + y=$((1 ? 20 : x+=2)) +expected-exit: e != 0 +expected-stderr-pattern: + /.*:.*1 \? 20 : x\+=2.*lvalue.*\n$/ +--- + +name: arith-ternary-prec-2 +description: + Check precidance of ternary operator vs assignment +stdin: + typeset -i x=2 + echo $((0 ? x+=2 : 20)) +expected-stdout: + 20 +--- + +name: arith-div-assoc-1 +description: + Check associativity of division operator +stdin: + echo $((20 / 2 / 2)) +expected-stdout: + 5 +--- + +name: arith-assop-assoc-1 +description: + Check associativity of assignment-operator operator +stdin: + typeset -i i=1 j=2 k=3 + echo $((i += j += k)) + echo $i,$j,$k +expected-stdout: + 6 + 6,5,3 +--- + diff --git a/bin/pdksh/tests/regress.t b/bin/pdksh/tests/regress.t index 95fe97b6b7c..d78a02641de 100644 --- a/bin/pdksh/tests/regress.t +++ b/bin/pdksh/tests/regress.t @@ -639,3 +639,27 @@ expected-exit: e != 0 expected-stderr-pattern: /.*read *only.*/ --- + +name: regression-43 +description: + Can subshells be prefixed by redirections (historical shells allow + this) +stdin: + < /dev/null (cat -n) +--- + +name: regression-44 +description: + getopts sets OPTIND correctly for unparsed option +stdin: + set -- -a -a -x + while getopts :a optc; do + echo "OPTARG=$OPTARG, OPTIND=$OPTIND, optc=$optc." + done + echo done +expected-stdout: + OPTARG=, OPTIND=2, optc=a. + OPTARG=, OPTIND=3, optc=a. + OPTARG=x, OPTIND=3, optc=?. + done +--- diff --git a/bin/pdksh/tests/th b/bin/pdksh/tests/th index 496058ccd4e..340cbe2d5dc 100644 --- a/bin/pdksh/tests/th +++ b/bin/pdksh/tests/th @@ -169,7 +169,13 @@ foreach $env (('USER', 'LOGNAME', 'HOME', 'PATH', 'SHELL')) { $new_env{$env} = $ENV{$env} if defined $ENV{$env}; } %old_env = %ENV; -%ENV = %new_env; + +# The following doesn't work with perl5... Need to do it explicitly - yuck. +#%ENV = %new_env; +foreach $k (keys(%ENV)) { + delete $ENV{$k}; +} +$ENV{$k} = $v while ($k,$v) = each %new_env; die "$prog: couldn't make directory $tempdir - $!\n" if !mkdir($tempdir, 0777); diff --git a/bin/pdksh/tests/th.sh b/bin/pdksh/tests/th.sh index 6e40c19dcf9..f26b8699e94 100644 --- a/bin/pdksh/tests/th.sh +++ b/bin/pdksh/tests/th.sh @@ -4,12 +4,18 @@ # Simple script to find perl and run it # +# Avoid common problems with ENV (though perl shouldn't let it through) +unset ENV + +x=x +[ -x /bin/sh ] 2> /dev/null || x=f + IFS=:$IFS perl= for i in $PATH; do [ X"$i" = X ] && i=. for j in perl perl4 perl5 ; do - [ -x "$i/$j" ] && perl=$i/$j && break 2 + [ -$x "$i/$j" ] && perl=$i/$j && break 2 done done diff --git a/bin/pdksh/tests/version.t b/bin/pdksh/tests/version.t index aed94d01760..0ec6fbac7b5 100644 --- a/bin/pdksh/tests/version.t +++ b/bin/pdksh/tests/version.t @@ -4,5 +4,5 @@ description: stdin: echo $KSH_VERSION expected-stdout: - @(#)PD KSH v5.2.7 96/06/04 + @(#)PD KSH v5.2.8 96/08/19 --- diff --git a/bin/pdksh/trap.c b/bin/pdksh/trap.c index e4ecdd39efa..7933f2d7e31 100644 --- a/bin/pdksh/trap.c +++ b/bin/pdksh/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: trap.c,v 1.2 1996/08/19 20:09:00 downsj Exp $ */ /* * signal handling @@ -19,9 +19,7 @@ Trap sigtraps[SIGNALS+1] = { { SIGERR_, "ERR", "Error handler" }, }; -static RETSIGTYPE alarm_catcher ARGS((int sig)); - -static struct sigaction Sigact_ign, Sigact_trap, Sigact_alarm; +static struct sigaction Sigact_ign, Sigact_trap; void inittraps() @@ -43,8 +41,6 @@ inittraps() Sigact_ign.sa_handler = SIG_IGN; Sigact_trap = Sigact_ign; Sigact_trap.sa_handler = trapsig; - Sigact_alarm = Sigact_ign; - Sigact_alarm.sa_handler = alarm_catcher; sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; @@ -59,20 +55,21 @@ inittraps() setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); } +#ifdef KSH +static RETSIGTYPE alarm_catcher ARGS((int sig)); + void alarm_init() { sigtraps[SIGALRM].flags |= TF_SHELL_USES; setsig(&sigtraps[SIGALRM], alarm_catcher, - SS_RESTORE_ORIG|SS_FORCE); + SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); } static RETSIGTYPE alarm_catcher(sig) int sig; { - trapsig(sig); -#ifdef KSH if (ksh_tmout_state == TMOUT_READING) { int left = alarm(0); @@ -82,12 +79,9 @@ alarm_catcher(sig) } else alarm(left); } -#endif /* KSH */ -#ifdef V7_SIGNALS - sigaction(sig, &Sigact_alarm, (struct sigaction *) 0); -#endif /* V7_SIGNALS */ return RETSIGVAL; } +#endif /* KSH */ Trap * gettrap(name) @@ -125,6 +119,8 @@ trapsig(i) fatal_trap = 1; intrsig = 1; } + if (p->shtrap) + (*p->shtrap)(i); #ifdef V7_SIGNALS if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */ sigaction(i, &Sigact_trap, (struct sigaction *) 0); @@ -363,22 +359,37 @@ setsig(p, f, flags) if (p->signal == SIGEXIT_ || p->signal == SIGERR_) return 1; + /* First time setting this signal? If so, get and note the current + * setting. + */ if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { sigaction(p->signal, &Sigact_ign, &sigact); p->flags |= sigact.sa_handler == SIG_IGN ? TF_ORIG_IGN : TF_ORIG_DFL; p->cursig = SIG_IGN; } - if ((p->flags & TF_ORIG_IGN) && (flags & SS_USER) - && !(flags & SS_FORCE) && !Flag(FTALKING)) + + /* Generally, an ignored signal stays ignored, except if + * - the user of an interactive shell wants to change it + * - the shell wants for force a change + */ + if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) + && (!(flags & SS_USER) || !Flag(FTALKING))) return 0; - if (!(flags & SS_USER)) { - if (!(flags & SS_FORCE) && (p->flags & TF_ORIG_IGN)) - return 0; - } setexecsig(p, flags & SS_RESTORE_MASK); + /* This is here 'cause there should be a way of clearing shtraps, but + * don't know if this is a sane way of doing it. At the moment, + * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH). + */ + if (!(flags & SS_USER)) + p->shtrap = (handler_t) 0; + if (flags & SS_SHTRAP) { + p->shtrap = f; + f = trapsig; + } + if (p->cursig != f) { p->cursig = f; sigemptyset(&sigact.sa_mask); diff --git a/bin/pdksh/tree.c b/bin/pdksh/tree.c index 3ceb7756cf7..105158787b3 100644 --- a/bin/pdksh/tree.c +++ b/bin/pdksh/tree.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tree.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: tree.c,v 1.2 1996/08/19 20:09:01 downsj Exp $ */ /* * command tree climbing @@ -478,7 +478,7 @@ tcopy(t, ap) r = (struct op *) alloc(sizeof(struct op), ap); r->type = t->type; - r->evalflags = t->evalflags; + r->u.evalflags = t->u.evalflags; r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap); diff --git a/bin/pdksh/tree.h b/bin/pdksh/tree.h index 29be89ccc4a..863061dd91d 100644 --- a/bin/pdksh/tree.h +++ b/bin/pdksh/tree.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tree.h,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: tree.h,v 1.2 1996/08/19 20:09:02 downsj Exp $ */ /* * command trees for compile/execute @@ -15,7 +15,10 @@ */ struct op { short type; /* operation type, see below */ - short evalflags; /* eval() flags for arg expansion */ + union { /* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */ + short evalflags; /* TCOM: arg expansion eval() flags */ + short ksh_func; /* TFUNC: function x (vs x()) */ + } u; char **args; /* arguments to a command */ char **vars; /* variable assignments */ struct ioword **ioact; /* IO actions (eg, < > >>) */ @@ -31,7 +34,7 @@ struct op { #define TCOM 1 /* command */ #define TPAREN 2 /* (c-list) */ #define TPIPE 3 /* a | b */ -#define TLIST 4 /* a [&;] b */ +#define TLIST 4 /* a ; b */ #define TOR 5 /* || */ #define TAND 6 /* && */ #define TBANG 7 /* ! */ diff --git a/bin/pdksh/var.c b/bin/pdksh/var.c index 8a4922b4903..b31b41d48a1 100644 --- a/bin/pdksh/var.c +++ b/bin/pdksh/var.c @@ -1,4 +1,4 @@ -/* $OpenBSD: var.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: var.c,v 1.2 1996/08/19 20:09:02 downsj Exp $ */ #include "sh.h" #include "ksh_time.h" @@ -82,9 +82,6 @@ initvar() } names[] = { { "COLUMNS", V_COLUMNS }, { "IFS", V_IFS }, - { "MAIL", V_MAIL }, - { "MAILCHECK", V_MAILCHECK }, - { "MAILPATH", V_MAILPATH }, { "OPTIND", V_OPTIND }, { "PATH", V_PATH }, { "POSIXLY_CORRECT", V_POSIXLY_CORRECT }, @@ -98,6 +95,9 @@ initvar() { "VISUAL", V_VISUAL }, #endif /* EDIT */ #ifdef KSH + { "MAIL", V_MAIL }, + { "MAILCHECK", V_MAILCHECK }, + { "MAILPATH", V_MAILPATH }, { "RANDOM", V_RANDOM }, { "SECONDS", V_SECONDS }, { "TMOUT", V_TMOUT }, @@ -247,7 +247,7 @@ local(n, copy) h = hash(n); if (!letter(*n)) { vp = &vtemp; - vp->flag = (DEFINED|RDONLY); + vp->flag = DEFINED|RDONLY; vp->type = 0; vp->areap = ATEMP; return vp; @@ -265,7 +265,7 @@ local(n, copy) |LCASEV|UCASEV_AL|INT_U|INT_L); if (vq->flag & INTEGER) vp->type = vq->type; - vp->field = vq->field; + vp->u2.field = vq->u2.field; } } if (array) @@ -352,7 +352,8 @@ setstr(vq, s) if (s >= vq->val.s && s <= vq->val.s + strlen(vq->val.s)) internal_errorf(TRUE, - "setstr: assigning to self"); + "setstr: %s=%s: assigning to self", + vq->name, s); afree((void*)vq->val.s, vq->areap); } vq->flag &= ~(ISSET|ALLOC); @@ -486,9 +487,9 @@ formatstr(vp, s) olen = strlen(s); if (vp->flag & (RJUST|LJUST)) { - if (!vp->field) /* default field width */ - vp->field = olen; - nlen = vp->field; + if (!vp->u2.field) /* default field width */ + vp->u2.field = olen; + nlen = vp->u2.field; } else nlen = olen; @@ -502,14 +503,14 @@ formatstr(vp, s) while (q > s && isspace(q[-1])) --q; slen = q - s; - if (slen > vp->field) { - s += slen - vp->field; - slen = vp->field; + if (slen > vp->u2.field) { + s += slen - vp->u2.field; + slen = vp->u2.field; } shf_snprintf(p, nlen + 1, ((vp->flag & ZEROFIL) && digit(*s)) ? "%0*s%.*s" : "%*s%.*s", - vp->field - slen, null, slen, s); + vp->u2.field - slen, null, slen, s); } else { /* strip leading spaces/zeros */ while (isspace(*s)) @@ -518,7 +519,7 @@ formatstr(vp, s) while (*s == '0') s++; shf_snprintf(p, nlen + 1, "%-*.*s", - vp->field, vp->field, s); + vp->u2.field, vp->u2.field, s); } } else memcpy(p, s, olen + 1); @@ -568,7 +569,7 @@ export(vp, val) struct tbl * typeset(var, set, clr, field, base) register const char *var; - int clr, set; + Tflag clr, set; int field, base; { register struct tbl *vp; @@ -616,7 +617,8 @@ typeset(var, set, clr, field, base) || strcmp(tvar, "SHELL") == 0)) errorf("%s: restricted", tvar); - vp = (set&LOCAL) ? local(tvar, set & LOCAL_COPY) : global(tvar); + vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? TRUE : FALSE) + : global(tvar); set &= ~(LOCAL|LOCAL_COPY); vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp; @@ -668,7 +670,7 @@ typeset(var, set, clr, field, base) if ((set & INTEGER) && base > 0 && (!val || t != vp)) t->type = base; if (set & (LJUST|RJUST|ZEROFIL)) - t->field = field; + t->u2.field = field; if (fake_assign) { setstr(t, s); if (free_me) @@ -719,9 +721,10 @@ unset(vp, array_ref) } vp->u.array = (struct tbl *) 0; } - vp->flag &= SPECIAL; /* Should ``unspecial'' some vars */ + /* If foo[0] is being unset, the remainder of the array is kept... */ + vp->flag &= SPECIAL | (array_ref ? ARRAY|DEFINED : 0); if (vp->flag & SPECIAL) - unsetspec(vp); + unsetspec(vp); /* responsible for `unspecial'ing var */ } /* return a pointer to the first char past a legal variable name (returns the @@ -901,15 +904,6 @@ setspec(vp) case V_OPTIND: getopts_reset((int) intval(vp)); break; - case V_MAIL: - mbset(str_val(vp)); - break; - case V_MAILPATH: - mpset(str_val(vp)); - break; - case V_MAILCHECK: - /* mail_check_set(intval(vp)); */ - break; case V_POSIXLY_CORRECT: change_flag(FPOSIX, OF_SPECIAL, 1); break; @@ -953,6 +947,15 @@ setspec(vp) break; #endif /* EDIT */ #ifdef KSH + case V_MAIL: + mbset(str_val(vp)); + break; + case V_MAILPATH: + mpset(str_val(vp)); + break; + case V_MAILCHECK: + /* mail_check_set(intval(vp)); */ + break; case V_RANDOM: vp->flag &= ~SPECIAL; srand((unsigned int)intval(vp)); @@ -985,12 +988,6 @@ unsetspec(vp) setctypes(" \t\n", C_IFS); ifs0 = ' '; break; - case V_MAIL: - mbset((char *) 0); - break; - case V_MAILPATH: - mpset((char *) 0); - break; case V_TMPDIR: /* should not become unspecial */ if (tmpdir) { @@ -999,6 +996,12 @@ unsetspec(vp) } break; #ifdef KSH + case V_MAIL: + mbset((char *) 0); + break; + case V_MAILPATH: + mpset((char *) 0); + break; case V_TMOUT: /* at&t ksh doesn't do this. TMOUT becomes unspecial so * future assignments don't have effect. Could be @@ -1057,7 +1060,7 @@ arraysearch(vp, val) new->flag = vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL); new->type = vp->type; new->areap = vp->areap; - new->field = vp->field; + new->u2.field = vp->u2.field; new->index = val; if (curr != new) { /* not reusing old array entry */ prev->u.array = new; @@ -1123,6 +1126,10 @@ set_array(var, reset, vals) if (reset > 0) /* trash existing values and attributes */ unset(vp, 0); + /* todo: would be nice for assignment to completely succeed or + * completely fail. Only really effects integer arrays: + * evaluation of some of vals[] may fail... + */ for (i = 0; vals[i]; i++) { vq = arraysearch(vp, i); setstr(vq, vals[i]); diff --git a/bin/pdksh/version.c b/bin/pdksh/version.c index 5c71a81d14b..edd71480d4c 100644 --- a/bin/pdksh/version.c +++ b/bin/pdksh/version.c @@ -1,4 +1,4 @@ -/* $OpenBSD: version.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: version.c,v 1.2 1996/08/19 20:09:03 downsj Exp $ */ /* * value of $KSH_VERSION (or $SH_VERSION) @@ -7,4 +7,4 @@ #include "sh.h" const char ksh_version [] = - "@(#)PD KSH v5.2.7 96/06/04"; + "@(#)PD KSH v5.2.8 96/08/19"; diff --git a/bin/pdksh/vi.c b/bin/pdksh/vi.c index c4b2aeeee33..4fc7b75c511 100644 --- a/bin/pdksh/vi.c +++ b/bin/pdksh/vi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vi.c,v 1.1 1996/08/14 06:19:12 downsj Exp $ */ +/* $OpenBSD: vi.c,v 1.2 1996/08/19 20:09:04 downsj Exp $ */ /* * vi command editing @@ -57,7 +57,6 @@ static void rewindow ARGS((void)); static int newcol ARGS((int ch, int col)); static void display ARGS((char *wb1, char *wb2, int leftside)); static void ed_mov_opt ARGS((int col, char *wb)); -static int do_comment ARGS((void)); static int expand_word ARGS((int command)); static int complete_word ARGS((int command, int count)); static int print_expansions ARGS((struct edstate *e, int command)); @@ -94,7 +93,7 @@ const unsigned char classify[128] = { /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */ C_, 0, C_|U_, 0, 0, 0, C_, 0, /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ - C_, 0, 0, 0, 0, 0, 0, 0, + C_, 0, 0, C_|Z_, 0, 0, 0, 0, /* 04 <space> ! " # $ % & ' */ M_, 0, 0, C_, M_, M_, 0, 0, /* 05 ( ) * + , - . / */ @@ -1121,7 +1120,13 @@ vi_cmd(argcnt, cmd) } case '#': - return do_comment(); + { + int ret = x_do_comment(es->cbuf, es->cbufsize, + &es->linelen); + if (ret >= 0) + es->cursor = 0; + return ret; + } case '=': /* at&t ksh */ case Ctrl('e'): /* Nonstandard vi/ksh */ @@ -1135,6 +1140,8 @@ vi_cmd(argcnt, cmd) /* FALLTHROUGH */ case Ctrl('['): /* some annoying at&t ksh's */ + if (!Flag(FVIESCCOMPLETE)) + return -1; case '\\': /* at&t ksh */ case Ctrl('f'): /* Nonstandard vi/ksh */ complete_word(1, argcnt); @@ -1887,7 +1894,7 @@ display(wb1, wb2, leftside) else mc = ' '; if (mc != morec) { - ed_mov_opt(x_cols - 2, wb1); + ed_mov_opt(pwidth + winwidth + 1, wb1); x_putc(mc); cur_col++; morec = mc; @@ -1920,48 +1927,6 @@ ed_mov_opt(col, wb) cur_col = col; } -/* Handle the commenting/uncommenting of a line */ -static int -do_comment() -{ - int i, j; - - if (es->linelen == 0) - return 1; /* somewhat arbitrary - it's what at&t ksh does */ - - /* Already commented? */ - if (es->cbuf[0] == '#') { - int saw_nl = 0; - - for (j = 0, i = 1; i < es->linelen; i++) { - if (!saw_nl || es->cbuf[i] != '#') - es->cbuf[j++] = es->cbuf[i]; - saw_nl = es->cbuf[i] == '\n'; - } - es->linelen = j; - es->cursor = 0; - return 0; - } else { - int n = 1; - - /* See if there's room for the #'s - 1 per \n */ - for (i = 0; i < es->linelen; i++) - if (es->cbuf[i] == '\n') - n++; - if (es->linelen + n >= es->cbufsize) - return -1; - /* Now add them... */ - for (i = es->linelen, j = es->linelen + n; --i >= 0; ) { - if (es->cbuf[i] == '\n') - es->cbuf[--j] = '#'; - es->cbuf[--j] = es->cbuf[i]; - } - es->cbuf[0] = '#'; - es->linelen += n; - es->cursor = 0; - return 1; - } -} /* replace word with all expansions (ie, expand word*) */ static int |