diff options
Diffstat (limited to 'bin/ksh')
40 files changed, 1404 insertions, 531 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 |