diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/csh | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/csh')
42 files changed, 17931 insertions, 0 deletions
diff --git a/usr/src/cmd/csh/Makefile b/usr/src/cmd/csh/Makefile new file mode 100644 index 0000000000..869fe7efc3 --- /dev/null +++ b/usr/src/cmd/csh/Makefile @@ -0,0 +1,101 @@ +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +# Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + +# Copyright (c) 1980 Regents of the University of California. +# All rights reserved. The Berkeley Software License Agreement +# specifies the terms and conditions for redistribution. + +# ident "%Z%%M% %I% %E% SMI" + +# +# C Shell with process control; VM/UNIX VAX Makefile +# Bill Joy UC Berkeley; Jim Kulp IIASA, Austria +# + +include ../Makefile.cmd + +ED= ed +# +# For message catalogue files +# +GREP= egrep +SED= sed +POFILE= csh.po +CLOBBERFILES += sh.tconst.h + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint + +PFOBJS = sh_policy.o + +POBJS = \ +sh.o sh.char.o sh.debug.o\ +sh.dir.o sh.dol.o sh.err.o sh.exec.o sh.exp.o\ +sh.file.o sh.func.o sh.glob.o sh.hist.o\ +sh.init.o sh.lex.o sh.misc.o sh.parse.o\ +sh.print.o sh.proc.o sh.sem.o sh.set.o\ +sh.tchar.o sh.time.o stubs.o wait3.o printf.o + +CPPFLAGS += -D_FILE_OFFSET_BITS=64 +CPPFLAGS += -I ../sh + +.KEEP_STATE: + +all install clean clobber lint: $(MACH) + +PFPOFILES= $(PFOBJS:%.o=%.po) + +POFILES= \ +sh.po sh.char.po sh.debug.po\ +sh.dir.po sh.dol.po sh.err.po sh.exec.po sh.exp.po\ +sh.file.po sh.func.po sh.glob.po sh.hist.po\ +sh.init.po sh.lex.po sh.misc.po sh.parse.po\ +sh.print.po sh.proc.po sh.sem.po sh.set.po\ +sh.tchar.po sh.time.po stubs.po wait3.po\ +$(PFPOFILES) + +XGETFLAGS= -a -x csh.xcl + + +# +# For message catalogue files +# +_msg: $(MSGDOMAIN) sh.tconst.h .WAIT $(POFILE) + $(RM) $(POFILE) + cat $(POFILES) > $(POFILE) + $(RM) $(MSGDOMAIN)/$(POFILE) + $(CP) $(POFILE) $(MSGDOMAIN) + + +$(POFILE): $(POFILES) + $(RM) $@; cat $(POFILES) > $@ + +# based on .c.po, BUILD.po from Makefile.master +%.po: ../sh/%.c + $(COMPILE.cpp) $< > $(<F).i + $(XGETTEXT) $(XGETFLAGS) $(<F).i ;\ + $(RM) $@ ;\ + sed "/^domain/d" < messages.po > $@ ;\ + $(RM) messages.po $(<F).i + +$(PROG): $$(POBJS) + $(LINK.c) $(POBJS) -o $@ + +sh.tconst.h: + $(RM) $@; $(ED) sh.tconst.c < make.sh.tconst.h.ed + +clobber: local_clobber $(MACH) + +local_clobber: + $(RM) $(CLOBBERFILES) + +$(MACH): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/cmd/csh/README b/usr/src/cmd/csh/README new file mode 100644 index 0000000000..95e57a8378 --- /dev/null +++ b/usr/src/cmd/csh/README @@ -0,0 +1,27 @@ +[seizo:09/09/92] +To fix #1098866, the directory structure is modified. +This directory contains machine independent source codes of csh. +Two files are currently machine dependent. These are + signal.c and signal.h +Machine dependent files are moved under $(MACH) directory. + +So, if you are going to port csh for other architecture, +you have to do: + 1) create a directory for the new architecture. + (Say, intel) + + 2) create following files. + (You can use sparc/{Makefile, signal.c, signal.h} as templates. + intel/Makefile + intel/signal.c + You need to modify sigvechandler() + intel/signal.h + You need to modify struct sigcontext. + +In future, I am planning to rewrite csh so it uses modern +signal interface so csh will not have any machine dependent +portion. + +usr/src/cmd/csh/SCCS/{s.signal.c, s.signal.h} are move under +usr/src/cmd/csh/sparc/SCCS. So, if you wish to see sccs histories +of signal.c and signal.h, please refer to usr/src/cmd/csh/sparc/SCCS. diff --git a/usr/src/cmd/csh/csh.xcl b/usr/src/cmd/csh/csh.xcl new file mode 100644 index 0000000000..0ce57aa03f --- /dev/null +++ b/usr/src/cmd/csh/csh.xcl @@ -0,0 +1,43 @@ +msgid " " +msgid " " +msgid " \b\b" +msgid "" +msgid "%ld %t" +msgid "&" +msgid "(null)" +msgid "+" +msgid "-" +msgid "../" +msgid "./" +msgid "/" +msgid "/.login" +msgid "/bin/login" +msgid "/dev/null" +msgid "/etc" +msgid "0123456789ABCDEF" +msgid "0123456789abcdef" +msgid "0X" +msgid "0x" +msgid ":" +msgid "? " +msgid "HOME" +msgid "LC_CTYPE" +msgid "LC_MESSAGES" +msgid "Mail" +msgid "PATH" +msgid "Signal 32" +msgid "TERM" +msgid "TS" +msgid "USER" +msgid "\015\n" +msgid "\07" +msgid "\b\b \b\b" +msgid "\b\b" +msgid "\r" +msgid "^R\n" +msgid "login" +msgid "vb" +msgid "getwd: can't stat /" +msgid "getwd: can't stat ." +msgid "getwd: can't open .." +msgid "getwd: read error in .." diff --git a/usr/src/cmd/csh/i386/Makefile b/usr/src/cmd/csh/i386/Makefile new file mode 100644 index 0000000000..8abdf43d36 --- /dev/null +++ b/usr/src/cmd/csh/i386/Makefile @@ -0,0 +1,89 @@ +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved +# +# Copyright (c) 1980 Regents of the University of California. +# All rights reserved. The Berkeley Software License Agreement +# specifies the terms and conditions for redistribution. + +# ident "%Z%%M% %I% %E% SMI" + +# +# C Shell with process control; VM/UNIX VAX Makefile +# Bill Joy UC Berkeley; Jim Kulp IIASA, Austria +# + +CSH_PROG = csh +PROG = $(CSH_PROG) +PFCSH_PROG= pfcsh +ROOTPFCSH= $(ROOTBIN)/$(PFCSH_PROG) + +include ../../Makefile.cmd + +MBCHAR = -DMBCHAR # Define this line to include multibyte input support +DEFS = -DVFORK -DFILEC -DBSD_COMP -DFIVE # No TELL when MBCHAR +CPPFLAGS= -I. -s $(DEFS) $(MBCHAR) $(CPPFLAGS.master) +CPPFLAGS += -I ../../sh +CPPFLAGS += -D_FILE_OFFSET_BITS=64 +LAZYLIBS = $(ZLAZYLOAD) -lsecdb $(ZNOLAZYLOAD) +lint := LAZYLIBS = -lsecdb +LDLIBS += -lcurses $(LAZYLIBS) + +PFOBJS = sh_policy.o + +HDDEP = sh.o sh.dir.o sh.dol.o sh.err.o sh.exec.o sh.exp.o sh.file.o \ + sh.func.o sh.glob.o sh.hist.o sh.init.o sh.lex.o sh.misc.o \ + sh.parse.o sh.proc.o sh.sem.o sh.set.o sh.time.o + +COMMONOBJS= printf.o sh.char.o sh.dir.o sh.dol.o sh.err.o \ + sh.exec.o sh.exp.o sh.file.o sh.func.o sh.glob.o sh.hist.o sh.init.o \ + sh.lex.o sh.misc.o sh.parse.o sh.print.o sh.proc.o sh.sem.o sh.set.o \ + sh.time.o sh.tchar.o sh.tconst.o sh.o \ + wait3.o + +LOCALOBJS= signal.o + +COMMONSRCS= $(COMMONOBJS:%.o=../%.c) +PFSRCS= $(PFOBJS:%=pfcsh_objs/%) + +.KEEP_STATE: + +.PARALLEL: $(COMMONOBJS) $(LOCALOBJS) + +all: $(PROG) + +# build rule for common source above +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.o: ../../sh/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +$(CSH_PROG): sh.tconst.h .WAIT $(COMMONOBJS) $(PFOBJS) $(LOCALOBJS) + $(LINK.c) $(COMMONOBJS) $(PFOBJS) $(LOCALOBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(ROOTPFCSH): $(ROOTPROG) + $(RM) $@ + $(LN) $(ROOTPROG) $@ + +sh.tconst.h: ../sh.tconst.c ../make.sh.tconst.h.ed + $(RM) $@ + ed ../sh.tconst.c < ../make.sh.tconst.h.ed + +$(HDDEP): sh.tconst.h + +install: all $(ROOTBINPROG) $(ROOTPROG) $(ROOTPFCSH) + +lint: sh.tconst.h + $(LINT.c) $(COMMONSRCS) $(PFSRCS) signal.c + +clean: + $(RM) $(LOCALOBJS) $(COMMONOBJS) $(PFOBJS) sh.tconst.h + +clobber: clean + $(RM) $(PROG) diff --git a/usr/src/cmd/csh/i386/signal.c b/usr/src/cmd/csh/i386/signal.c new file mode 100644 index 0000000000..092a200ab5 --- /dev/null +++ b/usr/src/cmd/csh/i386/signal.c @@ -0,0 +1,247 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 4.3BSD signal compatibility functions + * + * the implementation interprets signal masks equal to -1 as "all of the + * signals in the signal set", thereby allowing signals with numbers + * above 32 to be blocked when referenced in code such as: + * + * for (i = 0; i < NSIG; i++) + * mask |= sigmask(i) + */ + +#include <sys/types.h> +#include <sys/siginfo.h> +#include <sys/ucontext.h> +#include <signal.h> +#include "signal.h" +#include <errno.h> +#include <stdio.h> + +#define set2mask(setp) ((setp)->__sigbits[0]) +#define mask2set(mask, setp) \ + ((mask) == -1 ? sigfillset(setp) : sigemptyset(setp), (((setp)->__sigbits[0]) = (mask))) + +void (*_siguhandler[NSIG])() = { 0 }; + +/* + * sigstack is emulated with sigaltstack by guessing an appropriate + * value for the stack size - on machines that have stacks that grow + * upwards, the ss_sp arguments for both functions mean the same thing, + * (the initial stack pointer sigstack() is also the stack base + * sigaltstack()), so a "very large" value should be chosen for the + * stack size - on machines that have stacks that grow downwards, the + * ss_sp arguments mean opposite things, so 0 should be used (hopefully + * these machines don't have hardware stack bounds registers that pay + * attention to sigaltstack()'s size argument. + */ + +#ifdef sun +#define SIGSTACKSIZE 0 +#endif + + +/* + * sigvechandler is the real signal handler installed for all + * signals handled in the 4.3BSD compatibility interface - it translates + * SVR4 signal hander arguments into 4.3BSD signal handler arguments + * and then calls the real handler + */ + +static void +sigvechandler(sig, sip, ucp) + int sig; + siginfo_t *sip; + ucontext_t *ucp; +{ + struct sigcontext sc; + int code; + char *addr; + register int i, j; + int gwinswitch = 0; + + sc.sc_onstack = ((ucp->uc_stack.ss_flags & SS_ONSTACK) != 0); + sc.sc_mask = set2mask(&ucp->uc_sigmask); + + /* + * Machine dependent code begins + */ + sc.sc_sp = (int) ucp->uc_mcontext.gregs[UESP]; + sc.sc_pc = (int) ucp->uc_mcontext.gregs[EIP]; + sc.sc_ps = (int) ucp->uc_mcontext.gregs[EFL]; + sc.sc_eax = (int) ucp->uc_mcontext.gregs[EAX]; + sc.sc_edx = (int) ucp->uc_mcontext.gregs[EDX]; + + /* + * Machine dependent code ends + */ + + if (sip != NULL) + if ((code = sip->si_code) == BUS_OBJERR) + code = SEGV_MAKE_ERR(sip->si_errno); + + if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) + if (sip != NULL) + addr = (char *)sip->si_addr; + else + addr = SIG_NOADDR; + + (*_siguhandler[sig])(sig, code, &sc, addr); + + if (sc.sc_onstack) + ucp->uc_stack.ss_flags |= SS_ONSTACK; + else + ucp->uc_stack.ss_flags &= ~SS_ONSTACK; + mask2set(sc.sc_mask, &ucp->uc_sigmask); + + /* + * Machine dependent code begins + */ + ucp->uc_mcontext.gregs[UESP] = (int) sc.sc_sp; + ucp->uc_mcontext.gregs[EIP] = (int) sc.sc_pc; + ucp->uc_mcontext.gregs[EFL] = (int) sc.sc_ps; + ucp->uc_mcontext.gregs[EAX] = (int) sc.sc_eax; + ucp->uc_mcontext.gregs[EDX] = (int) sc.sc_edx; + /* + * Machine dependent code ends + */ + + setcontext (ucp); +} + +sigsetmask(mask) + int mask; +{ + sigset_t oset; + sigset_t nset; + + (void) sigprocmask(0, (sigset_t *)0, &nset); + mask2set(mask, &nset); + (void) sigprocmask(SIG_SETMASK, &nset, &oset); + return set2mask(&oset); +} + +sigblock(mask) + int mask; +{ + sigset_t oset; + sigset_t nset; + + (void) sigprocmask(0, (sigset_t *)0, &nset); + mask2set(mask, &nset); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + return set2mask(&oset); +} + +sigpause(mask) + int mask; +{ + sigset_t set; + + (void) sigprocmask(0, (sigset_t *)0, &set); + mask2set(mask, &set); + return (sigsuspend(&set)); +} + +sigvec(sig, nvec, ovec) + int sig; + struct sigvec *nvec; + struct sigvec *ovec; +{ + struct sigaction nact; + struct sigaction oact; + struct sigaction *nactp; + void (*ohandler)(), (*nhandler)(); + + if (sig <= 0 || sig >= NSIG) { + errno = EINVAL; + return -1; + } + + ohandler = _siguhandler[sig]; + + if (nvec) { + _sigaction(sig, (struct sigaction *)0, &nact); + nhandler = nvec->sv_handler; + _siguhandler[sig] = nhandler; + if (nhandler != SIG_DFL && nhandler != SIG_IGN) + nact.sa_handler = (void (*)())sigvechandler; + else + nact.sa_handler = nhandler; + mask2set(nvec->sv_mask, &nact.sa_mask); + /* + if ( sig == SIGTSTP || sig == SIGSTOP ) + nact.sa_handler = SIG_DFL; */ + nact.sa_flags = SA_SIGINFO; + if (!(nvec->sv_flags & SV_INTERRUPT)) + nact.sa_flags |= SA_RESTART; + if (nvec->sv_flags & SV_RESETHAND) + nact.sa_flags |= SA_RESETHAND; + if (nvec->sv_flags & SV_ONSTACK) + nact.sa_flags |= SA_ONSTACK; + nactp = &nact; + } else + nactp = (struct sigaction *)0; + + if (_sigaction(sig, nactp, &oact) < 0) { + _siguhandler[sig] = ohandler; + return -1; + } + + if (ovec) { + if (oact.sa_handler == SIG_DFL || oact.sa_handler == SIG_IGN) + ovec->sv_handler = oact.sa_handler; + else + ovec->sv_handler = ohandler; + ovec->sv_mask = set2mask(&oact.sa_mask); + ovec->sv_flags = 0; + if (oact.sa_flags & SA_ONSTACK) + ovec->sv_flags |= SV_ONSTACK; + if (oact.sa_flags & SA_RESETHAND) + ovec->sv_flags |= SV_RESETHAND; + if (!(oact.sa_flags & SA_RESTART)) + ovec->sv_flags |= SV_INTERRUPT; + } + + return 0; +} + + +void (* +signal(s, a))() + int s; + void (*a)(); +{ + struct sigvec osv; + struct sigvec nsv; + static int mask[NSIG]; + static int flags[NSIG]; + + nsv.sv_handler = a; + nsv.sv_mask = mask[s]; + nsv.sv_flags = flags[s]; + if (sigvec(s, &nsv, &osv) < 0) + return (SIG_ERR); + if (nsv.sv_mask != osv.sv_mask || nsv.sv_flags != osv.sv_flags) { + mask[s] = nsv.sv_mask = osv.sv_mask; + flags[s] = nsv.sv_flags = osv.sv_flags & ~SV_RESETHAND; + if (sigvec(s, &nsv, (struct sigvec *)0) < 0) + return (SIG_ERR); + } + return (osv.sv_handler); +} diff --git a/usr/src/cmd/csh/i386/signal.h b/usr/src/cmd/csh/i386/signal.h new file mode 100644 index 0000000000..51cfd8945a --- /dev/null +++ b/usr/src/cmd/csh/i386/signal.h @@ -0,0 +1,73 @@ +/* + * Copyright 1996 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + + +#ifndef CSH_SIGNAL_H +#define CSH_SIGNAL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 4.3BSD signal compatibility header + * + */ +#define sigmask(m) (m > 32 ? 0 : (1 << ((m)-1))) + +/* + * 4.3BSD signal vector structure used in sigvec call. + */ +struct sigvec { + void (*sv_handler)(); /* signal handler */ + int sv_mask; /* signal mask to apply */ + int sv_flags; /* see signal options below */ +}; + +#define SV_ONSTACK 0x0001 /* take signal on signal stack */ +#define SV_INTERRUPT 0x0002 /* do not restart system on signal return */ +#define SV_RESETHAND 0x0004 /* reset handler to SIG_DFL when signal taken */ + +#define sv_onstack sv_flags + +/* + * Machine dependent deta structure + */ +struct sigcontext { + int sc_onstack; /* sigstack state to restore */ + int sc_mask; /* signal mask to restore */ + int sc_sp; /* sp to restore */ + int sc_pc; /* pc to retore */ + int sc_ps; /* psl to restore */ + int sc_eax; /* eax to restore */ + int sc_edx; /* edx to restore */ +}; + +#define SI_DFLCODE 1 + +#define BUS_HWERR BUS_ADRERR /* misc hardware error (e.g. timeout) */ +#define BUS_ALIGN BUS_ADRALN /* hardware alignment error */ + +#define SEGV_NOMAP SEGV_MAPERR /* no mapping at the fault address */ +#define SEGV_PROT SEGV_ACCERR /* access exceeded protections */ + +/* + * The SEGV_CODE(code) will be SEGV_NOMAP, SEGV_PROT, or SEGV_OBJERR. + * In the SEGV_OBJERR case, doing a SEGV_ERRNO(code) gives an errno value + * reported by the underlying file object mapped at the fault address. + */ + +#define SIG_NOADDR ((char *)~0) + +#define SEGV_MAKE_ERR(e) (((e) << 8) | SEGV_MAPERR) + +#endif /* CSH_SIGNAL_H */ diff --git a/usr/src/cmd/csh/make.sh.tconst.h.ed b/usr/src/cmd/csh/make.sh.tconst.h.ed new file mode 100644 index 0000000000..2e5c9443fc --- /dev/null +++ b/usr/src/cmd/csh/make.sh.tconst.h.ed @@ -0,0 +1,11 @@ +!#ident "%Z%%M% %I% %E% SMI" +v/^tchar/d +1,$s/=.*/;/ +1,$s/^tchar/extern tchar/ +1i +/* DON'T EDIT THIS FILE. + * This file was made from sh.tconst by the ed script make.sh.tconst.h.ed. + */ +. +w sh.tconst.h +q diff --git a/usr/src/cmd/csh/printf.c b/usr/src/cmd/csh/printf.c new file mode 100644 index 0000000000..68272cb5a4 --- /dev/null +++ b/usr/src/cmd/csh/printf.c @@ -0,0 +1,762 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Hacked "printf" which prints through putbyte and Putchar. + * putbyte() is used to send a pure byte, which might be a part + * of a mutlibyte character, mainly for %s. A control character + * for putbyte() may be QUOTE'd meaning not to convert it to ^x + * sequence. In all other cases Putchar() is used to send a character + * in tchar (== wchar_t + * optional QUOE.) + * DONT USE WITH STDIO! + * This printf has been hacked again so that it understands tchar string + * when the format specifier %t is used. Also %c has been expanded + * to take a tchar character as well as normal int. + * %t is supported in its simplest form; no width or precision will + * be understood. + * Assumption here is that sizeof(tchar)<=sizeof(int) so that tchar is + * passed as int. Otherwise, %T must be specified instead of %c to + * print a character in tchar. + */ + +#include <stdarg.h> +#include <values.h> +#include "sh.h" /* For tchar. */ + +#define HIBITLL (1ULL << 63) + +void _print(char *format, va_list *args); + +static char *p; + +int +printf(const char *format, ...) +{ + va_list stupid; + + p = (char *)gettext(format); + va_start(stupid, format); + _print(p, &stupid); + va_end(stupid); +} + +/* + * Floating-point code is included or not, depending + * on whether the preprocessor variable FLOAT is 1 or 0. + */ + +/* Maximum number of digits in any integer (long) representation */ +#define MAXDIGS 20 + +/* Convert a digit character to the corresponding number */ +#define tonumber(x) ((x) - '0') + +/* Convert a number between 0 and 9 to the corresponding digit */ +#define todigit(x) ((x) + '0') + +/* Maximum total number of digits in E format */ +#define MAXECVT 17 + +/* Maximum number of digits after decimal point in F format */ +#define MAXFCVT 60 + +/* Maximum significant figures in a floating-point number */ +#define MAXFSIG 17 + +/* Maximum number of characters in an exponent */ +#define MAXESIZ 4 + +/* Maximum (positive) exponent or greater */ +#define MAXEXP 40 + + + +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(a, b) ((a) < (b) ? (a) : (b)) + +/* If this symbol is nonzero, allow '0' as a flag */ +#define FZERO 1 + +#if FLOAT +/* + * System-supplied routines for floating conversion + */ +char *fcvt(); +char *ecvt(); +#endif + +void +_print(char *format, va_list *args) +{ + /* Current position in format */ + char *cp; + + /* Starting and ending points for value to be printed */ + char *bp, *p; + tchar *tbp, *tep; /* For "%t". */ + tchar tcbuf[2]; /* For "%c" or "%T". */ + + /* Field width and precision */ + int width, prec; + + /* Format code */ + char fcode; + + /* Number of padding zeroes required on the left */ + int lzero; + + /* Flags - nonzero if corresponding character appears in format */ + bool length; /* l */ + bool double_length; /* ll */ + bool fplus; /* + */ + bool fminus; /* - */ + bool fblank; /* blank */ + bool fsharp; /* # */ +#if FZERO + bool fzero; /* 0 */ +#endif + + /* Pointer to sign, "0x", "0X", or empty */ + char *prefix; +#if FLOAT + /* Exponent or empty */ + char *suffix; + + /* Buffer to create exponent */ + char expbuf[MAXESIZ + 1]; + + /* Number of padding zeroes required on the right */ + int rzero; + + /* The value being converted, if real */ + double dval; + + /* Output values from fcvt and ecvt */ + int decpt, sign; + + /* Scratch */ + int k; + + /* Values are developed in this buffer */ + char buf[max(MAXDIGS, max(MAXFCVT + DMAXEXP, MAXECVT) + 1)]; +#else + char buf[MAXDIGS]; +#endif + /* The value being converted, if integer */ + long long val; + + /* Set to point to a translate table for digits of whatever radix */ + char *tab; + + /* Work variables */ + int n, hradix, lowbit; + + cp = format; + + /* + * The main loop -- this loop goes through one iteration + * for each ordinary character or format specification. + */ + while (*cp) + if (*cp != '%') { + /* Ordinary (non-%) character */ + putbyte (*cp++); + } else { + /* + * % has been found. + * First, parse the format specification. + */ + + /* Scan the <flags> */ + fplus = fminus = fblank = fsharp = 0; +#if FZERO + fzero = 0; +#endif +scan: + switch (*++cp) { + case '+': + fplus = 1; + goto scan; + case '-': + fminus = 1; + goto scan; + case ' ': + fblank = 1; + goto scan; + case '#': + fsharp = 1; + goto scan; +#if FZERO + case '0': + fzero = 1; + goto scan; +#endif + } + + /* Scan the field width */ + if (*cp == '*') { + width = va_arg (*args, int); + if (width < 0) { + width = -width; + fminus = 1; + } + cp++; + } else { + width = 0; + while (isdigit(*cp)) { + n = tonumber(*cp++); + width = width * 10 + n; + } + } + + /* Scan the precision */ + if (*cp == '.') { + + /* '*' instead of digits? */ + if (*++cp == '*') { + prec = va_arg(*args, int); + cp++; + } else { + prec = 0; + while (isdigit(*cp)) { + n = tonumber(*cp++); + prec = prec * 10 + n; + } + } + } else { + prec = -1; + } + + /* Scan the length modifier */ + double_length = length = 0; + switch (*cp) { + case 'l': + if (*(cp + 1) == 'l') { + cp++; + double_length = 1; + } else { + length = 1; + } + /* No break */ + case 'h': + cp++; + } + + /* + * The character addressed by cp must be the + * format letter -- there is nothing left for + * it to be. + * + * The status of the +, -, #, blank, and 0 + * flags are reflected in the variables + * "fplus", "fminus", "fsharp", "fblank", + * and "fzero", respectively. + * "width" and "prec" contain numbers + * corresponding to the digit strings + * before and after the decimal point, + * respectively. If there was no decimal + * point, "prec" is -1. + * + * The following switch sets things up + * for printing. What ultimately gets + * printed will be padding blanks, a prefix, + * left padding zeroes, a value, right padding + * zeroes, a suffix, and more padding + * blanks. Padding blanks will not appear + * simultaneously on both the left and the + * right. Each case in this switch will + * compute the value, and leave in several + * variables the information necessary to + * construct what is to be printed. + * + * The prefix is a sign, a blank, "0x", "0X", + * or null, and is addressed by "prefix". + * + * The suffix is either null or an exponent, + * and is addressed by "suffix". + * + * The value to be printed starts at "bp" + * and continues up to and not including "p". + * + * "lzero" and "rzero" will contain the number + * of padding zeroes required on the left + * and right, respectively. If either of + * these variables is negative, it will be + * treated as if it were zero. + * + * The number of padding blanks, and whether + * they go on the left or the right, will be + * computed on exit from the switch. + */ + + lzero = 0; + prefix = ""; +#if FLOAT + rzero = lzero; + suffix = prefix; +#endif + switch (fcode = *cp++) { + + /* + * fixed point representations + * + * "hradix" is half the radix for the conversion. + * Conversion is unsigned unless fcode is 'd'. + * HIBITLL is 1000...000 binary, and is equal to + * the maximum negative number. + * We assume a 2's complement machine + */ + + case 'D': + case 'U': + length = 1; + case 'd': + case 'u': + hradix = 5; + goto fixed; + + case 'O': + length = 1; + case 'o': + hradix = 4; + goto fixed; + + case 'X': + case 'x': + hradix = 8; + +fixed: + /* Establish default precision */ + if (prec < 0) { + prec = 1; + } + + /* Fetch the argument to be printed */ + if (double_length) { + val = va_arg(*args, long long); + } else if (length) { + val = va_arg(*args, long); + } else if (fcode == 'd') { + val = va_arg(*args, int); + } else { + val = va_arg(*args, unsigned); + } + + /* If signed conversion, establish sign */ + if (fcode == 'd' || fcode == 'D') { + if (val < 0) { + prefix = "-"; + /* + * Negate, checking in + * advance for possible + * overflow. + */ + if (val != HIBITLL) { + val = -val; + } + } else if (fplus) { + prefix = "+"; + } else if (fblank) { + prefix = " "; + } + } +#if FZERO + if (fzero) { + int n = width - strlen(prefix); + if (n > prec) { + prec = n; + } + } +#endif + /* Set translate table for digits */ + if (fcode == 'X') { + tab = "0123456789ABCDEF"; + } else { + tab = "0123456789abcdef"; + } + + /* Develop the digits of the value */ + p = bp = buf + MAXDIGS; + while (val) { + lowbit = val & 1; + val = (val >> 1) & ~HIBITLL; + *--bp = tab[val % hradix * 2 + lowbit]; + val /= hradix; + } + + /* Calculate padding zero requirement */ + lzero = bp - p + prec; + + /* Handle the # flag */ + if (fsharp && bp != p) { + switch (fcode) { + case 'o': + if (lzero < 1) + lzero = 1; + break; + case 'x': + prefix = "0x"; + break; + case 'X': + prefix = "0X"; + break; + } + } + + break; +#if FLOAT + case 'E': + case 'e': + /* + * E-format. The general strategy + * here is fairly easy: we take + * what ecvt gives us and re-format it. + */ + + /* Establish default precision */ + if (prec < 0) { + prec = 6; + } + + /* Fetch the value */ + dval = va_arg(*args, double); + + /* Develop the mantissa */ + bp = ecvt(dval, + min(prec + 1, MAXECVT), + &decpt, + &sign); + + /* Determine the prefix */ +e_merge: + if (sign) { + prefix = "-"; + } else if (fplus) { + prefix = "+"; + } else if (fblank) { + prefix = " "; + } + + /* Place the first digit in the buffer */ + p = &buf[0]; + *p++ = *bp != '\0' ? *bp++ : '0'; + + /* Put in a decimal point if needed */ + if (prec != 0 || fsharp) { + *p++ = '.'; + } + + /* Create the rest of the mantissa */ + rzero = prec; + while (rzero > 0 && *bp != '\0') { + --rzero; + *p++ = *bp++; + } + + bp = &buf[0]; + + /* Create the exponent */ + suffix = &expbuf[MAXESIZ]; + *suffix = '\0'; + if (dval != 0) { + n = decpt - 1; + if (n < 0) { + n = -n; + } + while (n != 0) { + *--suffix = todigit(n % 10); + n /= 10; + } + } + + /* Prepend leading zeroes to the exponent */ + while (suffix > &expbuf[MAXESIZ - 2]) { + *--suffix = '0'; + } + + /* Put in the exponent sign */ + *--suffix = (decpt > 0 || dval == 0) ? + '+' : '-'; + + /* Put in the e */ + *--suffix = isupper(fcode) ? 'E' : 'e'; + + break; + + case 'f': + /* + * F-format floating point. This is + * a good deal less simple than E-format. + * The overall strategy will be to call + * fcvt, reformat its result into buf, + * and calculate how many trailing + * zeroes will be required. There will + * never be any leading zeroes needed. + */ + + /* Establish default precision */ + if (prec < 0) { + prec = 6; + } + + /* Fetch the value */ + dval = va_arg(*args, double); + + /* Do the conversion */ + bp = fcvt(dval, + min(prec, MAXFCVT), + &decpt, + &sign); + + /* Determine the prefix */ +f_merge: + if (sign && decpt > -prec && + *bp != '\0' && *bp != '0') { + prefix = "-"; + } else if (fplus) { + prefix = "+"; + } else if (fblank) { + prefix = " "; + } + + /* Initialize buffer pointer */ + p = &buf[0]; + + /* Emit the digits before the decimal point */ + n = decpt; + k = 0; + if (n <= 0) { + *p++ = '0'; + } else { + do { + if (*bp == '\0' || + k >= MAXFSIG) { + *p++ = '0'; + } else { + *p++ = *bp++; + ++k; + } + } while (--n != 0); + } + + /* Decide whether we need a decimal point */ + if (fsharp || prec > 0) { + *p++ = '.'; + } + + /* Digits (if any) after the decimal point */ + n = min(prec, MAXFCVT); + rzero = prec - n; + while (--n >= 0) { + if (++decpt <= 0 || *bp == '\0' || + k >= MAXFSIG) { + *p++ = '0'; + } else { + *p++ = *bp++; + ++k; + } + } + + bp = &buf[0]; + + break; + + case 'G': + case 'g': + /* + * g-format. We play around a bit + * and then jump into e or f, as needed. + */ + + /* Establish default precision */ + if (prec < 0) { + prec = 6; + } + + /* Fetch the value */ + dval = va_arg(*args, double); + + /* Do the conversion */ + bp = ecvt(dval, + min(prec, MAXECVT), + &decpt, + &sign); + if (dval == 0) { + decpt = 1; + } + + k = prec; + if (!fsharp) { + n = strlen(bp); + if (n < k) { + k = n; + } + while (k >= 1 && bp[k-1] == '0') { + --k; + } + } + + if (decpt < -3 || decpt > prec) { + prec = k - 1; + goto e_merge; + } else { + prec = k - decpt; + goto f_merge; + } + +#endif + case 'c': +#ifdef MBCHAR_1 /* sizeof(int)>=sizeof(tchar) */ +/* + * A tchar arg is passed as int so we used the normal %c to specify + * such an arugument. + */ + tcbuf[0] = va_arg(*args, int); + tbp = &tcbuf[0]; + tep = tbp + 1; + fcode = 't'; /* Fake the rest of code. */ + break; +#else +/* + * We would have to invent another new format speficier such as "%T" to + * take a tchar arg. Let's worry about when that time comes. + */ + /* + * Following code take care of a char arg + * only. + */ + buf[0] = va_arg(*args, int); + bp = &buf[0]; + p = bp + 1; + break; + case 'T': /* Corresponding arg is tchar. */ + tcbuf[0] = va_arg(*args, tchar); + tbp = &tcbuf[0]; + tep = tbp + 1; + fcode = 't'; /* Fake the rest of code. */ + break; +#endif + case 's': + bp = va_arg(*args, char *); + if (bp == 0) { +nullstr: bp = "(null)"; + p = bp + strlen("(null)"); + break; + } + if (prec < 0) { + prec = MAXINT; + } + for (n = 0; *bp++ != '\0' && n < prec; n++) + ; + p = --bp; + bp -= n; + break; + + case 't': + /* + * Special format specifier "%t" tells + * printf() to print char strings written + * as tchar string. + */ + tbp = va_arg(*args, tchar *); + if (tbp == 0) { + fcode = 's'; /* Act as if it were %s. */ + goto nullstr; + } + if (prec < 0) { + prec = MAXINT; + } + for (n = 0; *tbp++ != 0 && n < prec; n++) + ; + tep = --tbp; + tbp -= n; + + /* + * Just to make the following padding + * calculation not to go very crazy... + */ + bp = NULL; + p = bp + n; + break; + + case '\0': + cp--; + break; + + default: + p = bp = &fcode; + p++; + break; + + } + if (fcode != '\0') { + /* Calculate number of padding blanks */ + int nblank; + nblank = width +#if FLOAT + - (rzero < 0 ? 0: rzero) + - strlen(suffix) +#endif + - (p - bp) + - (lzero < 0 ? 0 : lzero) + - strlen(prefix); + + /* Blanks on left if required */ + if (!fminus) { + while (--nblank >= 0) { + Putchar(' '); + } + } + + /* Prefix, if any */ + while (*prefix != '\0') { + Putchar(*prefix++); + } + + /* Zeroes on the left */ + while (--lzero >= 0) { + Putchar('0'); + } + + /* The value itself */ + if (fcode == 't') { /* %t is special. */ + while (tbp < tep) { + Putchar(*tbp++); + } + } else { /* For rest of the cases. */ + while (bp < p) { + putbyte(*bp++); + } + } +#if FLOAT + /* Zeroes on the right */ + while (--rzero >= 0) + Putchar('0'); + + /* The suffix */ + while (*suffix != '\0') { + Putchar(*suffix++); + } +#endif + /* Blanks on the right if required */ + if (fminus) { + while (--nblank >= 0) { + Putchar(' '); + } + } + } + } +} diff --git a/usr/src/cmd/csh/sh.c b/usr/src/cmd/csh/sh.c new file mode 100644 index 0000000000..9d3e30860a --- /dev/null +++ b/usr/src/cmd/csh/sh.c @@ -0,0 +1,1398 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <locale.h> +#include "sh.h" +/* #include <sys/ioctl.h> */ +#include <fcntl.h> +#include <sys/filio.h> +#include "sh.tconst.h" +#include <pwd.h> +#include <stdlib.h> +#include "sh_policy.h" /* for pfcsh */ + +/* + * We use these csh(1) private versions of the select macros, (see select(3C)) + * so as not to be limited by the size of struct fd_set (ie 1024). + */ +#define CSH_FD_SET(n, p) ((*((p) + ((n)/NFDBITS))) |= (1 << ((n) % NFDBITS))) +#define CSH_FD_CLR(n, p) ((*((p) + ((n)/NFDBITS))) &= ~(1 << ((n) % NFDBITS))) +#define CSH_FD_ISSET(n, p) ((*((p) + ((n)/NFDBITS))) & (1 << ((n) % NFDBITS))) +#define CSH_FD_ZERO(p, n) memset((void *)(p), 0, (n)) + +tchar *pathlist[] = { S_usrbin/*"/usr/bin"*/, S_DOT /*"."*/, 0 }; +tchar *dumphist[] = { S_history /*"history"*/, S_h /*"-h"*/, 0, 0 }; +tchar *loadhist[] = { S_source /*"source"*/, S_h /*"-h"*/, S_NDOThistory /*"~/.history"*/, 0 }; +tchar HIST = '!'; +tchar HISTSUB = '^'; +int nofile; +bool reenter; +bool nverbose; +bool nexececho; +bool quitit; +bool fast; +bool batch; +bool prompt = 1; +bool enterhist = 0; + +extern gid_t getegid(), getgid(); +extern uid_t geteuid(), getuid(); +extern tchar **strblktotsblk(/* char **, int */); + +int siglwp(); +int sigwaiting(); + +main(c, av) + int c; + char **av; +{ + register tchar **v, *cp, *p, *q, *r; + register int f; + struct sigvec osv; + struct sigaction sa; + tchar s_prompt[MAXHOSTNAMELEN+3]; + + pfcshflag = 0; + + /* + * set up the error exit, if there is an error before + * this is done, it will core dump, and we don't + * tolerate core dumps + */ + haderr = 0; + setexit(); + if ( haderr ) { + /* + * if were here, there was an error in the csh + * startup so just punt + */ + printf("csh startup error, csh exiting...\n"); + flush(); + exitstat(); + } + + + (void) setlocale(LC_ALL, ""); +#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ +#endif + (void) textdomain(TEXT_DOMAIN); + + /* + * This is a profile shell if the simple name of argv[0] is + * pfcsh or -pfcsh + */ + p = strtots(NOSTR, "pfcsh"); + r = strtots(NOSTR, "-pfcsh"); + if ((p != NOSTR) && (r != NOSTR) && + ((q = strtots(NOSTR, *av)) != NOSTR)) { + if (c > 0 && (eq(p, simple(q)) || eq(r, simple(q)))) { + pfcshflag = 1; + } + XFREE(q); + } + + if (p != NOSTR) + XFREE(p); + if (r != NOSTR) + XFREE(r); + + if (pfcshflag == 1) { + secpolicy_init(); + } + + /* Copy arguments */ + v = strblktotsblk(av, c); + + /* + * Initialize paraml list + */ + paraml.next = paraml.prev = ¶ml; + + settimes(); /* Immed. estab. timing base */ + + if (eq(v[0], S_aout/*"a.out"*/)) /* A.out's are quittable */ + quitit = 1; + uid = getuid(); + loginsh = **v == '-'; + if (loginsh) + (void) time(&chktim); + + /* + * Move the descriptors to safe places. + * The variable didfds is 0 while we have only FSH* to work with. + * When didfds is true, we have 0,1,2 and prefer to use these. + * + * Also, setup data for csh internal file descriptor book keeping. + */ + initdesc(c, av); + + /* + * Initialize the shell variables. + * ARGV and PROMPT are initialized later. + * STATUS is also munged in several places. + * CHILD is munged when forking/waiting + */ + + /* don't do globbing here, just set exact copies */ + setNS(S_noglob); + + set(S_status /* "status" */, S_0 /* "0" */); + dinit(cp = getenvs_("HOME")); /* dinit thinks that HOME==cwd in a */ + /* login shell */ + if (cp == NOSTR) + fast++; /* No home -> can't read scripts */ + else { + if (strlen_(cp) >= BUFSIZ - 10) { + cp = NOSTR; + fast++; + printf("%s\n", gettext("Pathname too long")); + set(S_home /* "home" */, savestr(cp)); + local_setenv(S_HOME, savestr(cp)); + } + set(S_home /* "home" */, savestr(cp)); + } + /* + * Grab other useful things from the environment. + * Should we grab everything?? + */ + if ((cp = getenvs_("USER")) != NOSTR) + set(S_user/*"user"*/, savestr(cp)); + else { + /* + * If USER is not defined, set it here. + */ + struct passwd *pw; + pw = getpwuid(getuid()); + + if (pw != NULL) { + set(S_user, strtots((tchar *)0, pw->pw_name )); + local_setenv(S_USER, strtots((tchar *)0, pw->pw_name)); + } + else if (loginsh) { /* Give up setting USER variable. */ + printf("Warning: USER environment variable could not be set.\n"); + } + } + if ((cp = getenvs_("TERM")) != NOSTR) + set(S_term/*"term"*/, savestr(cp)); + /* + * Re-initialize path if set in environment + */ + if ((cp = getenvs_("PATH")) == NOSTR) + set1(S_path/*"path"*/, saveblk(pathlist), &shvhed); + else + importpath(cp); + set(S_shell/*"shell"*/, S_SHELLPATH); + + doldol = putn(getpid()); /* For $$ */ + + /* restore globbing until the user says otherwise */ + unsetv(S_noglob); + + /* + * Record the interrupt states from the parent process. + * If the parent is non-interruptible our hand must be forced + * or we (and our children) won't be either. + * Our children inherit termination from our parent. + * We catch it only if we are the login shell. + */ + /* parents interruptibility */ + (void) sigvec(SIGINT, (struct sigvec *)0, &osv); + parintr = osv.sv_handler; + /* parents terminability */ + (void) sigvec(SIGTERM, (struct sigvec *)0, &osv); + parterm = osv.sv_handler; + + _signal(SIGLWP, siglwp); + _signal(SIGWAITING, sigwaiting); + if (loginsh) { + (void) signal(SIGHUP, phup); /* exit processing on HUP */ + (void) signal(SIGXCPU, phup); /* ...and on XCPU */ + (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ + } + + /* + * Process the arguments. + * + * Note that processing of -v/-x is actually delayed till after + * script processing. + */ + c--, v++; + while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) { + do switch (*cp++) { + + case 'b': /* -b Next arg is input file */ + batch++; + break; + + case 'c': /* -c Command input from arg */ + if (c == 1) + exit(0); + c--, v++; + arginp = v[0]; + prompt = 0; + nofile++; + cflg++; + break; + + case 'e': /* -e Exit on any error */ + exiterr++; + break; + + case 'f': /* -f Fast start */ + fast++; + break; + + case 'i': /* -i Interactive, even if !intty */ + intact++; + nofile++; + break; + + case 'n': /* -n Don't execute */ + noexec++; + break; + + case 'q': /* -q (Undoc'd) ... die on quit */ + quitit = 1; + break; + + case 's': /* -s Read from std input */ + nofile++; + break; + + case 't': /* -t Read one line from input */ + onelflg = 2; + prompt = 0; + nofile++; + break; +#ifdef TRACE + case 'T': /* -T trace switch on */ + trace_init(); + break; +#endif + + case 'v': /* -v Echo hist expanded input */ + nverbose = 1; /* ... later */ + break; + + case 'x': /* -x Echo just before execution */ + nexececho = 1; /* ... later */ + break; + + case 'V': /* -V Echo hist expanded input */ + setNS(S_verbose/*"verbose"*/); /* NOW! */ + break; + + case 'X': /* -X Echo just before execution */ + setNS(S_echo/*"echo"*/); /* NOW! */ + break; + + } while (*cp); + v++, c--; + } + + if (quitit) /* With all due haste, for debugging */ + (void) signal(SIGQUIT, SIG_DFL); + + /* + * Unless prevented by -c, -i, -s, or -t, if there + * are remaining arguments the first of them is the name + * of a shell file from which to read commands. + */ + if (!batch && (uid != geteuid() || getgid() != getegid())) { + errno = EACCES; + child++; /* So this ... */ + Perror(S_csh/*"csh"*/); /* ... doesn't return */ + } + + if (nofile == 0 && c > 0) { + nofile = open_(v[0], 0); + if (nofile < 0) { + child++; /* So this ... */ + Perror(v[0]); /* ... doesn't return */ + } + file = v[0]; + SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ + (void) fcntl(SHIN, F_SETFD, 1); + prompt = 0; + c--, v++; + } + + /* + * Consider input a tty if it really is or we are interactive. + */ + intty = intact || isatty(SHIN); + + /* + * Decide whether we should play with signals or not. + * If we are explicitly told (via -i, or -) or we are a login + * shell (arg0 starts with -) or the input and output are both + * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") + * Note that in only the login shell is it likely that parent + * may have set signals to be ignored + */ + if (loginsh || intact || intty && isatty(SHOUT)) + setintr = 1; +#ifdef TELL + settell(); +#endif + /* + * Save the remaining arguments in argv. + */ + setq(S_argv/*"argv"*/, v, &shvhed); + + /* + * Set up the prompt. + */ + if (prompt) { + gethostname_(s_prompt, MAXHOSTNAMELEN); + strcat_(s_prompt, uid == 0 ? S_SHARPSP/*"# "*/ : S_PERSENTSP/*"% "*/); + set(S_prompt/*"prompt"*/, s_prompt); + } + + /* + * If we are an interactive shell, then start fiddling + * with the signals; this is a tricky game. + */ + shpgrp = getpgid(0); + opgrp = tpgrp = -1; + if (setintr) { + **av = '-'; + if (!quitit) /* Wary! */ + (void) signal(SIGQUIT, SIG_IGN); + (void) signal(SIGINT, pintr); + (void) sigblock(sigmask(SIGINT)); + (void) signal(SIGTERM, SIG_IGN); + if (quitit == 0 && arginp == 0) { + (void) signal(SIGTSTP, SIG_IGN); + (void) signal(SIGTTIN, SIG_IGN); + (void) signal(SIGTTOU, SIG_IGN); + /* + * Wait till in foreground, in case someone + * stupidly runs + * csh & + * dont want to try to grab away the tty. + */ + if (isatty(FSHDIAG)) + f = FSHDIAG; + else if (isatty(FSHOUT)) + f = FSHOUT; + else if (isatty(OLDSTD)) + f = OLDSTD; + else + f = -1; +retry: + if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 && + tpgrp != -1) { + if (tpgrp != shpgrp) { + void (*old)() = (void (*)())signal(SIGTTIN, SIG_DFL); + (void) kill(0, SIGTTIN); + (void) signal(SIGTTIN, old); + goto retry; + } + opgrp = shpgrp; + shpgrp = getpid(); + tpgrp = shpgrp; + (void) setpgid(0, shpgrp); + (void) ioctl(f, TIOCSPGRP, (char *)&shpgrp); + (void) fcntl(dcopy(f, FSHTTY), F_SETFD, 1); + } else { +notty: + printf("Warning: no access to tty; thus no job control in this shell...\n"); + tpgrp = -1; + } + } + } + if (setintr == 0 && parintr == SIG_DFL) + setintr++; + + /* + * Set SIGCHLD handler, making sure that reads restart after it runs. + */ + sigemptyset(&sa.sa_mask); + sa.sa_handler = pchild; + sa.sa_flags = SA_RESTART; + (void) sigaction(SIGCHLD, &sa, (struct sigaction *) NULL); + + /* + * Set an exit here in case of an interrupt or error reading + * the shell start-up scripts. + */ + setexit(); + haderr = 0; /* In case second time through */ + if (!fast && reenter == 0) { + reenter++; + + /* + * If this is a login csh, and /etc/.login exists, + * source /etc/.login first. + */ + if (loginsh) { + tchar tmp_etc[4+1]; /*strlen("/etc")+1 */ + tchar tmp_login[7+1]; /*strlen("/.login")+1*/ + + strtots(tmp_etc, "/etc"); + strtots(tmp_login, "/.login"); + srccat_inlogin(tmp_etc, tmp_login); + } + + /* Will have value("home") here because set fast if don't */ + srccat(value(S_home/*"home"*/), S_SLADOTcshrc/*"/.cshrc"*/); + + /*Hash path*/ + if (!fast && !arginp && !onelflg && !havhash) + dohash(xhash); + + + /* + * Reconstruct the history list now, so that it's + * available from within .login. + */ + dosource(loadhist); + if (loginsh) { + srccat_inlogin(value(S_home/*"home"*/), S_SLADOTlogin/*"/.login"*/); + } + + /* + * To get cdpath hashing $cdpath must have a + * value, not $CDPATH. So if after reading + * the startup files ( .cshrc ), and + * user has specified a value for cdpath, then + * cache $cdpath paths. xhash2 is global array + * for $cdpath caching. + */ + if (!fast && !arginp && !onelflg && !havhash2 ) + dohash(xhash2); + } + + /* + * Now are ready for the -v and -x flags + */ + if (nverbose) + setNS(S_verbose/*"verbose"*/); + if (nexececho) + setNS(S_echo/*"echo"*/); + + /* + * All the rest of the world is inside this call. + * The argument to process indicates whether it should + * catch "error unwinds". Thus if we are a interactive shell + * our call here will never return by being blown past on an error. + */ + process(setintr); + + /* + * Mop-up. + */ + if (loginsh) { + printf("logout\n"); + (void) close(SHIN); /* No need for unsetfd(). */ + child++; + goodbye(); + } + rechist(); + exitstat(); +} + +untty() +{ + + if (tpgrp > 0) { + (void) setpgid(0, opgrp); + (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp); + } +} + +importpath(cp) + tchar *cp; +{ + register int i = 0; + register tchar *dp; + register tchar **pv; + int c; + static tchar dot[2] = {'.', 0}; + + for (dp = cp; *dp; dp++) + if (*dp == ':') + i++; + /* + * i+2 where i is the number of colons in the path. + * There are i+1 directories in the path plus we need + * room for a zero terminator. + */ + pv = (tchar **) calloc((unsigned) (i + 2), sizeof (tchar **)); + dp = cp; + i = 0; + if (*dp) + for (;;) { + if ((c = *dp) == ':' || c == 0) { + *dp = 0; + pv[i++] = savestr(*cp ? cp : dot); + if (c) { + cp = dp + 1; + *dp = ':'; + } else + break; + } + dp++; + } + pv[i] = 0; + set1(S_path /*"path"*/, pv, &shvhed); +} + +/* + * Source to the file which is the catenation of the argument names. + */ +srccat(cp, dp) + tchar *cp, *dp; +{ + register tchar *ep = strspl(cp, dp); + register int unit = dmove(open_(ep, 0), -1); + + (void) fcntl(unit, F_SETFD, 1); + xfree(ep); +#ifdef INGRES + srcunit(unit, 0, 0); +#else + srcunit(unit, 1, 0); +#endif +} + +/* + * Source to the file which is the catenation of the argument names. + * This one does not check the ownership. + */ +srccat_inlogin(cp, dp) + tchar *cp, *dp; +{ + register tchar *ep = strspl(cp, dp); + register int unit = dmove(open_(ep, 0), -1); + + (void) fcntl(unit, F_SETFD, 1); + xfree(ep); + srcunit(unit, 0, 0); +} + +/* + * Source to a unit. If onlyown it must be our file or our group or + * we don't chance it. This occurs on ".cshrc"s and the like. + */ +srcunit(unit, onlyown, hflg) + register int unit; + bool onlyown; + bool hflg; +{ + /* We have to push down a lot of state here */ + /* All this could go into a structure */ + int oSHIN = -1, oldintty = intty; + struct whyle *oldwhyl = whyles; + tchar *ogointr = gointr, *oarginp = arginp; + tchar *oevalp = evalp, **oevalvec = evalvec; + int oonelflg = onelflg; + bool oenterhist = enterhist; + tchar OHIST = HIST; +#ifdef TELL + bool otell = cantell; +#endif + struct Bin saveB; + + /* The (few) real local variables */ + jmp_buf oldexit; + int reenter, omask; + + if (unit < 0) + return; + if (didfds) + donefds(); + if (onlyown) { + struct stat stb; + + if (fstat(unit, &stb) < 0 || + (stb.st_uid != uid && stb.st_gid != getgid())) { + (void) close(unit); + unsetfd(unit); + return; + } + } + + /* + * There is a critical section here while we are pushing down the + * input stream since we have stuff in different structures. + * If we weren't careful an interrupt could corrupt SHIN's Bin + * structure and kill the shell. + * + * We could avoid the critical region by grouping all the stuff + * in a single structure and pointing at it to move it all at + * once. This is less efficient globally on many variable references + * however. + */ + getexit(oldexit); + reenter = 0; + if (setintr) + omask = sigblock(sigmask(SIGINT)); + setexit(); + reenter++; + if (reenter == 1) { + /* Setup the new values of the state stuff saved above */ + copy( (char *)&saveB, (char *)&B, sizeof saveB); + fbuf = (tchar **) 0; + fseekp = feobp = fblocks = 0; + oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; + intty = isatty(SHIN), whyles = 0, gointr = 0; + evalvec = 0; evalp = 0; + enterhist = hflg; + if (enterhist) + HIST = '\0'; + /* + * Now if we are allowing commands to be interrupted, + * we let ourselves be interrupted. + */ + if (setintr) + (void) sigsetmask(omask); +#ifdef TELL + settell(); +#endif + process(0); /* 0 -> blow away on errors */ + } + if (setintr) + (void) sigsetmask(omask); + if (oSHIN >= 0) { + register int i; + + /* We made it to the new state... free up its storage */ + /* This code could get run twice but xfree doesn't care */ + for (i = 0; i < fblocks; i++) + xfree(fbuf[i]); + xfree( (char *)fbuf); + + /* Reset input arena */ + copy( (char *)&B, (char *)&saveB, sizeof B); + + (void) close(SHIN), SHIN = oSHIN; + unsetfd(SHIN); + arginp = oarginp, onelflg = oonelflg; + evalp = oevalp, evalvec = oevalvec; + intty = oldintty, whyles = oldwhyl, gointr = ogointr; + if (enterhist) + HIST = OHIST; + enterhist = oenterhist; +#ifdef TELL + cantell = otell; +#endif + } + + resexit(oldexit); + /* + * If process reset() (effectively an unwind) then + * we must also unwind. + */ + if (reenter >= 2) + error(NULL); +} + +rechist() +{ + tchar buf[BUFSIZ]; + int fp, ftmp, oldidfds; + + if (!fast) { + if (value(S_savehist/*"savehist"*/)[0] == '\0') + return; + (void) strcpy_(buf, value(S_home/*"home"*/)); + (void) strcat_(buf, S_SLADOThistory/*"/.history"*/); + fp = creat_(buf, 0666); + if (fp == -1) + return; + oldidfds = didfds; + didfds = 0; + ftmp = SHOUT; + SHOUT = fp; + (void) strcpy_(buf, value(S_savehist/*"savehist"*/)); + dumphist[2] = buf; + dohist(dumphist); + (void) close(fp); + unsetfd(fp); + SHOUT = ftmp; + didfds = oldidfds; + } +} + +goodbye() +{ + if (loginsh) { + (void) signal(SIGQUIT, SIG_IGN); + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGTERM, SIG_IGN); + setintr = 0; /* No interrupts after "logout" */ + if (adrof(S_home/*"home"*/)) + srccat(value(S_home/*"home"*/), S_SLADOTlogout/*"/.logout"*/); + } + rechist(); + exitstat(); +} + +exitstat() +{ + +#ifdef PROF + monitor(0); +#endif + /* + * Note that if STATUS is corrupted (i.e. getn bombs) + * then error will exit directly because we poke child here. + * Otherwise we might continue unwarrantedly (sic). + */ + child++; + untty(); + exit(getn(value(S_status/*"status"*/))); +} + +/* + * in the event of a HUP we want to save the history + */ +void +phup() +{ + rechist(); + exit(1); +} + +tchar *jobargv[2] = { S_jobs/*"jobs"*/, 0 }; +/* + * Catch an interrupt, e.g. during lexical input. + * If we are an interactive shell, we reset the interrupt catch + * immediately. In any case we drain the shell output, + * and finally go through the normal error mechanism, which + * gets a chance to make the shell go away. + */ +void +pintr() +{ + pintr1(1); +} + +pintr1(wantnl) + bool wantnl; +{ + register tchar **v; + int omask; + + omask = sigblock(0); + if (setintr) { + (void) sigsetmask(omask & ~sigmask(SIGINT)); + if (pjobs) { + pjobs = 0; + printf("\n"); + dojobs(jobargv); + bferr("Interrupted"); + } + } + (void) sigsetmask(omask & ~sigmask(SIGCHLD)); + draino(); + + /* + * If we have an active "onintr" then we search for the label. + * Note that if one does "onintr -" then we shan't be interruptible + * so we needn't worry about that here. + */ + if (gointr) { + search(ZGOTO, 0, gointr); + timflg = 0; + if (v = pargv) + pargv = 0, blkfree(v); + if (v = gargv) + gargv = 0, blkfree(v); + reset(); + } else if (intty && wantnl) + printf("\n"); /* Some like this, others don't */ + error(NULL); +} + +/* + * Process is the main driving routine for the shell. + * It runs all command processing, except for those within { ... } + * in expressions (which is run by a routine evalav in sh.exp.c which + * is a stripped down process), and `...` evaluation which is run + * also by a subset of this code in sh.glob.c in the routine backeval. + * + * The code here is a little strange because part of it is interruptible + * and hence freeing of structures appears to occur when none is necessary + * if this is ignored. + * + * Note that if catch is not set then we will unwind on any error. + * If an end-of-file occurs, we return. + */ +process(catch) + bool catch; +{ + jmp_buf osetexit; + register struct command *t; + + getexit(osetexit); + for (;;) { + pendjob(); + paraml.next = paraml.prev = ¶ml; + paraml.word = S_ /*""*/; + t = 0; + setexit(); + justpr = enterhist; /* execute if not entering history */ + + /* + * Interruptible during interactive reads + */ + if (setintr) + (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); + + /* + * For the sake of reset() + */ + freelex(¶ml), freesyn(t), t = 0; + + if (haderr) { + if (!catch) { + /* unwind */ + doneinp = 0; + resexit(osetexit); + reset(); + } + haderr = 0; + /* + * Every error is eventually caught here or + * the shell dies. It is at this + * point that we clean up any left-over open + * files, by closing all but a fixed number + * of pre-defined files. Thus routines don't + * have to worry about leaving files open due + * to deeper errors... they will get closed here. + */ + closem(); + continue; + } + if (doneinp) { + doneinp = 0; + break; + } + if (chkstop) + chkstop--; + if (neednote) + pnote(); + if (intty && prompt && evalvec == 0) { + mailchk(); + /* + * If we are at the end of the input buffer + * then we are going to read fresh stuff. + * Otherwise, we are rereading input and don't + * need or want to prompt. + */ + if (fseekp == feobp) + printprompt(); + } + err = 0; + + /* + * Echo not only on VERBOSE, but also with history expansion. + */ + if (lex(¶ml) && intty || + adrof(S_verbose /*"verbose"*/)) { + haderr = 1; + prlex(¶ml); + haderr = 0; + } + + /* + * The parser may lose space if interrupted. + */ + if (setintr) + (void) sigblock(sigmask(SIGINT)); + + /* + * Save input text on the history list if + * reading in old history, or it + * is from the terminal at the top level and not + * in a loop. + */ + if (enterhist || catch && intty && !whyles) + savehist(¶ml); + + /* + * Print lexical error messages, except when sourcing + * history lists. + */ + if (!enterhist && err) + error("%s", gettext(err)); + + /* + * If had a history command :p modifier then + * this is as far as we should go + */ + if (justpr) + reset(); + + alias(¶ml); + + /* + * Parse the words of the input into a parse tree. + */ + t = syntax(paraml.next, ¶ml, 0); + if (err) + error("%s", gettext(err)); + + /* + * Execute the parse tree + */ + { + /* + * POSIX requires SIGCHLD to be held + * until all processes have joined the + * process group in order to avoid race + * condition. + */ + int omask; + + omask = sigblock(sigmask(SIGCHLD)); + execute(t, tpgrp); + (void)sigsetmask(omask &~ sigmask(SIGCHLD)); + } + + if (err) + error("%s", gettext(err)); + /* + * Made it! + */ + freelex(¶ml), freesyn(t); + } + resexit(osetexit); +} + +dosource(t) + register tchar **t; +{ + register tchar *f; + register int u; + bool hflg = 0; + tchar buf[BUFSIZ]; + + t++; + if (*t && eq(*t, S_h /*"-h"*/)) { + if (*++t == NOSTR) + bferr("Too few arguments."); + hflg++; + } + (void) strcpy_(buf, *t); + f = globone(buf); + u = dmove(open_(f, 0), -1); + xfree(f); + freelex(¶ml); + if (u < 0 && !hflg) + Perror(f); + (void) fcntl(u, F_SETFD, 1); + srcunit(u, 0, hflg); +} + +/* + * Check for mail. + * If we are a login shell, then we don't want to tell + * about any mail file unless its been modified + * after the time we started. + * This prevents us from telling the user things he already + * knows, since the login program insists on saying + * "You have mail." + */ +mailchk() +{ + register struct varent *v; + register tchar **vp; + time_t t; + int intvl, cnt; + struct stat stb; + bool new; + + v = adrof(S_mail /*"mail"*/); + if (v == 0) + return; + (void) time(&t); + vp = v->vec; + cnt = blklen(vp); + intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; + if (intvl < 1) + intvl = 1; + if (chktim + intvl > t) + return; + for (; *vp; vp++) { + if (stat_(*vp, &stb) < 0) + continue; + new = stb.st_mtime > time0.tv_sec; + if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime || + (stb.st_atime <= chktim && stb.st_mtime <= chktim) || + loginsh && !new) + continue; + if (cnt == 1) + printf("You have %smail.\n", new ? "new " : ""); + else + printf("%s in %t.\n", new ? "New mail" : "Mail", *vp); + } + chktim = t; +} + +/* + * Extract a home directory from the password file + * The argument points to a buffer where the name of the + * user whose home directory is sought is currently. + * We write the home directory of the user back there. + */ +gethdir(home) + tchar *home; +{ + /* getpwname will not be modified, so we need temp. buffer */ + char home_str[BUFSIZ]; + tchar home_ts[BUFSIZ]; + register struct passwd *pp /*= getpwnam(home)*/; + + pp = getpwnam(tstostr(home_str, home)); + if (pp == 0) + return (1); + (void) strcpy_(home, strtots(home_ts, pp->pw_dir)); + return (0); +} + + +/* +#ifdef PROF +done(i) +#else +exit(i) +#endif + int i; +{ + + untty(); + _exit(i); +} +*/ + +printprompt() +{ + register tchar *cp; + + if (!whyles) { + /* + * Print the prompt string + */ + for (cp = value(S_prompt /*"prompt"*/); *cp; cp++) + if (*cp == HIST) + printf("%d", eventno + 1); + else { + if (*cp == '\\' && cp[1] == HIST) + cp++; + Putchar(*cp | QUOTE); + } + } else + /* + * Prompt for forward reading loop + * body content. + */ + printf("? "); + flush(); +} + +/* + * Save char * block. + */ +tchar ** +strblktotsblk(v, num) + register char **v; + int num; +{ + register tchar **newv = + (tchar **) calloc((unsigned) (num+ 1), sizeof (tchar **)); + tchar **onewv = newv; + + while (*v && num--) + *newv++ = strtots(NOSTR,*v++); + *newv = 0; + return (onewv); +} + + +sigwaiting() +{ + _signal(SIGWAITING, sigwaiting); +} + +siglwp() +{ + _signal(SIGLWP, siglwp); +} + + +/* + * Following functions and data are used for csh to do its + * file descriptors book keeping. + */ + +static int *fdinuse = NULL; /* The list of files opened by csh */ +static int nbytesused = 0; /* no of bytes allocated to fdinuse */ +static int max_fd = 0; /* The maximum descriptor in fdinuse */ +static int my_pid; /* The process id set in initdesc() */ +static int NoFile = NOFILE; /* The number of files I can use. */ + +/* + * Get the number of files this csh can use. + * + * Move the initial descriptors to their eventual + * resting places, closing all other units. + * + * Also, reserve 0/1/2, so NIS+ routines do not get + * hold of them. And initialize fdinuse list and set + * the current process id. + * + * If this csh was invoked from setuid'ed script file, + * do not close the third argument passed. The file + * must be one of /dev/fd/0,1,2,,, + * (execv() always passes three arguments when it execs a script + * file in a form of #! /bin/csh -b.) + * + * If is_reinit is set in initdesc_x(), then we only close the file + * descriptors that we actually opened (as recorded in fdinuse). + */ +initdesc(argc, argv) + int argc; + char *argv[]; +{ + initdesc_x(argc, argv, 0); +} + +reinitdesc(argc, argv) + int argc; + char *argv[]; +{ + initdesc_x(argc, argv, 1); +} + +/* + * Callback functions for closing all file descriptors. + */ +static int +close_except(void *cd, int fd) +{ + int script_fd = *(int *)cd; + + if (fd >= 3 && fd < NoFile && fd != script_fd) + (void) close(fd); + return (0); +} + +static int +close_inuse(void *cd, int fd) +{ + int script_fd = *(int *)cd; + + if (fd >= 3 && fd < NoFile && fd != script_fd && + CSH_FD_ISSET(fd, fdinuse)) { + (void) close(fd); + unsetfd(fd); + } + return (0); +} + +initdesc_x(argc, argv, is_reinit) + int argc; + char *argv[]; + int is_reinit; +{ + + int script_fd = -1; + struct stat buf; + struct rlimit rlp; + + /* + * Get pid of this shell + */ + my_pid = getpid(); + + /* + * Get the hard limit numbers of descriptors + * this csh can use. + */ + if (getrlimit(RLIMIT_NOFILE, &rlp) == 0) + NoFile = rlp.rlim_cur; + + /* + * If this csh was invoked for executing setuid script file, + * the third argument passed is the special file name + * which should not be closed. This special file name is + * in the form /dev/fd/X. + */ + if (argc >= 3) + if (sscanf(argv[2], "/dev/fd/%d", &script_fd) != 1) + script_fd = -1; + else + fcntl(script_fd, F_SETFD, 1); /* Make sure to close + * this file on exec. + */ + + if (fdinuse == NULL) { + nbytesused = sizeof(int) * howmany(NoFile, sizeof(int) * NBBY); + fdinuse = (int *) xalloc(nbytesused); + } + + /* + * Close all files except 0/1/2 to get a clean + * file descritor space. + */ + if (!is_reinit) + (void) fdwalk(close_except, &script_fd); + else + (void) fdwalk(close_inuse, &script_fd); + + didfds = 0; /* 0, 1, 2 aren't set up */ + + if (fstat(0, &buf) < 0) + open("/dev/null", 0); + + (void) fcntl(SHIN = dcopy(0, FSHIN), F_SETFD, 1); + (void) fcntl(SHOUT = dcopy(1, FSHOUT), F_SETFD, 1); + (void) fcntl(SHDIAG = dcopy(2, FSHDIAG), F_SETFD, 1); + (void) fcntl(OLDSTD = dcopy(SHIN, FOLDSTD), F_SETFD, 1); + + /* + * Open 0/1/2 to avoid Nis+ functions to pick them up. + * Now, 0/1/2 are saved, close them and open them. + */ + close(0); close(1); close(2); + open("/dev/null", 0); + dup(0); + dup(0); + + /* + * Clear fd_set mask + */ + if ( ! is_reinit) + CSH_FD_ZERO(fdinuse, nbytesused); +} + +/* + * This routine is called after an error to close up + * any units which may have been left open accidentally. + * + * You only need to remove files in fdinuse list. + * After you have removed the files, you can clear the + * list and max_fd. + */ +closem() +{ + register int f; + + for (f = 3; f <= max_fd; f++) { + if (CSH_FD_ISSET(f, fdinuse) && + f != SHIN && f != SHOUT && f != SHDIAG && + f != OLDSTD && f != FSHTTY) + close(f); + } + CSH_FD_ZERO(fdinuse, nbytesused); + max_fd = 0; +} + +/* + * Reset my_pid when a new process is created. Only call this + * if you want the process to affect fdinuse (e.g., fork, but + * not vfork). + */ +new_process() +{ + my_pid = getpid(); +} + + +/* + * Whenever Csh open/create/dup/pipe a file or files, + * Csh keeps track of its open files. The open files + * are kept in "fdinuse, Fd In Use" list. + * + * When a file descriptor is newly allocated, setfd() is + * used to mark the fact in "fdinuse" list. + * For example, + * fd = open("newfile", 0); + * setfd(fd); + * + * When a file is freed by close() function, unsetfd() is + * used to remove the fd from "fdinuse" list. + * For example, + * close(fd); + * unsetfd(fd); + */ +setfd(fd) + int fd; +{ + /* + * Because you want to avoid + * conflict due to vfork(). + */ + if (my_pid != getpid()) + return; + + if (fd >= NoFile || fd < 0) + return; + + if (fd > max_fd) + max_fd = fd; + CSH_FD_SET(fd, fdinuse); +} + +unsetfd(fd) + int fd; +{ + register int i; + + /* + * Because you want to avoid + * conflict due to vfork(). + */ + if (my_pid != getpid()) + return; + + if (fd >= NoFile || fd < 0) + return; + + CSH_FD_CLR(fd, fdinuse); + if (fd == max_fd) { + for (i = max_fd-1; i >= 3; i--) + if (CSH_FD_ISSET(i, fdinuse)) { + max_fd = i; + return; + } + max_fd = 0; + } +} + +/* + * A generic call back routine to output error messages from the + * policy backing functions called by pfcsh. + */ +void +secpolicy_print(int level, const char *msg) +{ + switch (level) { + case SECPOLICY_WARN: + default: + haderr = 1; + printf("%s: ", msg); /* printf() does gettext() */ + break; + case SECPOLICY_ERROR: + bferr(msg); /* bferr() does gettext() */ + break; + } +} diff --git a/usr/src/cmd/csh/sh.char.c b/usr/src/cmd/csh/sh.char.c new file mode 100644 index 0000000000..8c79005dd7 --- /dev/null +++ b/usr/src/cmd/csh/sh.char.c @@ -0,0 +1,115 @@ +/* + * Copyright 1990 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.char.h" + +unsigned short _cmap[128] = { +/* nul soh stx etx */ + 0, 0, 0, 0, + +/* eot enq ack bel */ + 0, 0, 0, 0, + +/* bs ht nl vt */ + 0, _SP|_META, _NL|_META, 0, + +/* np cr so si */ + 0, 0, 0, 0, + +/* dle dc1 dc2 dc3 */ + 0, 0, 0, 0, + +/* dc4 nak syn etb */ + 0, 0, 0, 0, + +/* can em sub esc */ + 0, 0, 0, 0, + +/* fs gs rs us */ + 0, 0, 0, 0, + +/* sp ! " # */ + _SP|_META, 0, _Q, _META, + +/* $ % & ' */ + _DOL, 0, _META, _Q, + +/* ( ) * + */ + _META, _META, _GLOB, 0, + +/* , - . / */ + 0, 0, 0, 0, + +/* 0 1 2 3 */ + _DIG, _DIG, _DIG, _DIG, + +/* 4 5 6 7 */ + _DIG, _DIG, _DIG, _DIG, + +/* 8 9 : ; */ + _DIG, _DIG, 0, _META, + +/* < = > ? */ + _META, 0, _META, _GLOB, + +/* @ A B C */ + 0, _LET, _LET, _LET, + +/* D E F G */ + _LET, _LET, _LET, _LET, + +/* H I J K */ + _LET, _LET, _LET, _LET, + +/* L M N O */ + _LET, _LET, _LET, _LET, + +/* P Q R S */ + _LET, _LET, _LET, _LET, + +/* T U V W */ + _LET, _LET, _LET, _LET, + +/* X Y Z [ */ + _LET, _LET, _LET, _GLOB, + +/* \ ] ^ _ */ + _ESC, 0, 0, _LET, + +/* ` a b c */ + _Q1|_GLOB, _LET, _LET, _LET, + +/* d e f g */ + _LET, _LET, _LET, _LET, + +/* h i j k */ + _LET, _LET, _LET, _LET, + +/* l m n o */ + _LET, _LET, _LET, _LET, + +/* p q r s */ + _LET, _LET, _LET, _LET, + +/* t u v w */ + _LET, _LET, _LET, _LET, + +/* x y z { */ + _LET, _LET, _LET, _GLOB, + +/* | } ~ del */ + _META, 0, 0, 0, +}; diff --git a/usr/src/cmd/csh/sh.char.h b/usr/src/cmd/csh/sh.char.h new file mode 100644 index 0000000000..3a46db7ba9 --- /dev/null +++ b/usr/src/cmd/csh/sh.char.h @@ -0,0 +1,68 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Macros to classify characters. + */ + +#ifdef MBCHAR +#include <wchar.h> +#include <wctype.h> +#define isauxspZ (!isascii(Z)&&!(Z"E)&&iswspace(Z)) +#define isauxsp(c) (Z=((unsigned)(c)), isauxspZ) +/* Regocnizes non-ASCII space characters. */ +#else +#include <ctype.h> +/* macros of macros to reduce further #ifdef MBCHAR later. Please be patient!*/ +#define iswdigit(c) isdigit(c) +#define iswalpha(c) isalpha(c) +#define isphonogram(c) 0 +#define isideogram(c) 0 +#define isauxsp(c) 0 +#define isauxspZ 0 +#endif +extern unsigned short _cmap[];/* Defined in sh.char.c */ +unsigned int Z; /* A place to save macro arg to avoid side-effect!*/ + +#define _Q 0x01 /* '" */ +#define _Q1 0x02 /* ` */ +#define _SP 0x04 /* space and tab */ +#define _NL 0x08 /* \n */ +#define _META 0x10 /* lex meta characters, sp #'`";&<>()|\t\n */ +#define _GLOB 0x20 /* glob characters, *?{[` */ +#define _ESC 0x40 /* \ */ +#define _DOL 0x80 /* $ */ +#define _DIG 0x100 /* 0-9 */ +#define _LET 0x200 /* a-z, A-Z, _ NO LONGER OF REAL USE. */ + + +#define quoted(c) ((unsigned)(c) & QUOTE) + +#define cmapZ(bits) (isascii(Z)?(_cmap[Z] & (bits)):0) +#define cmap(c, bits) (Z=((unsigned)(c)), cmapZ(bits)) + +#define isglob(c) cmap(c, _GLOB) +#define ismeta(c) cmap(c, _META) +#define digit(c) cmap(c, _DIG) +#define issp(c) (Z=((unsigned)(c)), cmapZ( _SP)||isauxspZ) +/*WAS isspace(c)*/ +#define isspnl(c) (Z=((unsigned)(c)), cmapZ( _SP|_NL)||isauxspZ) +#define letter(c) \ + (Z=((unsigned)(c)), !quoted(Z)&&(iswalpha(Z)||((Z)=='_')\ + ||isphonogram(Z)||isideogram(Z))) +#define alnum(c) \ + (Z=((unsigned)(c)), !quoted(Z)&&(iswalpha(Z)||((Z)=='_')\ + ||iswdigit(Z)||isphonogram(Z)||isideogram(Z))) diff --git a/usr/src/cmd/csh/sh.debug.c b/usr/src/cmd/csh/sh.debug.c new file mode 100644 index 0000000000..379a327f93 --- /dev/null +++ b/usr/src/cmd/csh/sh.debug.c @@ -0,0 +1,55 @@ +/* + * Copyright 1990 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" + + +#ifdef TRACE +#include <stdio.h> +FILE *trace; +/* + * Trace routines + */ +#define TRACEFILE "/tmp/trace.XXXXXX" + +/* + * Initialie trace file. + * Called from main. + */ +trace_init() +{ + extern char *mktemp(); + char name[128]; + char *p; + + strcpy(name, TRACEFILE); + p = mktemp(name); + trace = fopen(p, "w"); +} + +/* + * write message to trace file + */ +/*VARARGS1*/ +tprintf(fmt,a,b,c,d,e,f,g,h,i,j) + char *fmt; +{ + if (trace) { + fprintf(trace, fmt, a,b,c,d,e,f,g,h,i,j); + fflush(trace); + } +} +#endif diff --git a/usr/src/cmd/csh/sh.dir.c b/usr/src/cmd/csh/sh.dir.c new file mode 100644 index 0000000000..407b1ec6cc --- /dev/null +++ b/usr/src/cmd/csh/sh.dir.c @@ -0,0 +1,743 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.dir.h" +#include "sh.tconst.h" + +/* + * C Shell - directory management + */ + +struct directory *dfind(); +tchar *dfollow(); +tchar *dcanon(); +struct directory dhead; /* "head" of loop */ +int printd; /* force name to be printed */ +static tchar *fakev[] = { S_dirs, NOSTR }; + +/* + * dinit - initialize current working directory + */ +dinit(hp) + tchar *hp; +{ + register tchar *cp; + register struct directory *dp; + tchar path[MAXPATHLEN]; + +#ifdef TRACE + tprintf("TRACE- dinit()\n"); +#endif + /* + * If this is a login shell, we should have a home directory. But, + * if we got here via 'su - <user>' where the user has no directory + * in his passwd file, then su has passed HOME=<nothing>, so hp is + * non-null, but has zero length. Thus, we do not know the current + * working directory based on the home directory. + */ + if (loginsh && hp && *hp) + cp = hp; + else { + cp = getwd_(path); + if (cp == NULL) { + printf ("Warning: cannot determine current directory\n"); + cp = S_DOT; + } + } + dp = (struct directory *)calloc(sizeof (struct directory), 1); + dp->di_name = savestr(cp); + dp->di_count = 0; + dhead.di_next = dhead.di_prev = dp; + dp->di_next = dp->di_prev = &dhead; + printd = 0; + dnewcwd(dp); +} + +/* + * dodirs - list all directories in directory loop + */ +dodirs(v) + tchar **v; +{ + register struct directory *dp; + bool lflag; + tchar *hp = value(S_home); + +#ifdef TRACE + tprintf("TRACE- dodirs()\n"); +#endif + if (*hp == '\0') + hp = NOSTR; + if (*++v != NOSTR) + if (eq(*v, S_MINl /* "-l" */) && *++v == NOSTR) + lflag = 1; + else + error("Usage: dirs [ -l ]"); + else + lflag = 0; + dp = dcwd; + do { + if (dp == &dhead) + continue; + if (!lflag && hp != NOSTR) { + dtildepr(hp, dp->di_name); + } else + printf("%t", dp->di_name); + printf(" "); + } while ((dp = dp->di_prev) != dcwd); + printf("\n"); +} + +dtildepr(home, dir) + register tchar *home, *dir; +{ + +#ifdef TRACE + tprintf("TRACE- dtildepr()\n"); +#endif + if (!eq(home, S_SLASH /* "/" */) && prefix(home, dir)) + printf("~%t", dir + strlen_(home)); + else + printf("%t", dir); +} + +/* + * dochngd - implement chdir command. + */ +dochngd(v) + tchar **v; +{ + register tchar *cp; + register struct directory *dp; + +#ifdef TRACE + tprintf("TRACE- dochngd()\n"); +#endif + printd = 0; + if (*++v == NOSTR) { + if ((cp = value(S_home)) == NOSTR || *cp == 0) + bferr("No home directory"); + if (chdir_(cp) < 0) + bferr("Can't change to home directory"); + cp = savestr(cp); + } else if ((dp = dfind(*v)) != 0) { + printd = 1; + if (chdir_(dp->di_name) < 0) + Perror(dp->di_name); + dcwd->di_prev->di_next = dcwd->di_next; + dcwd->di_next->di_prev = dcwd->di_prev; + goto flushcwd; + } else + cp = dfollow(*v); + dp = (struct directory *)calloc(sizeof (struct directory), 1); + dp->di_name = cp; + dp->di_count = 0; + dp->di_next = dcwd->di_next; + dp->di_prev = dcwd->di_prev; + dp->di_prev->di_next = dp; + dp->di_next->di_prev = dp; +flushcwd: + dfree(dcwd); + dnewcwd(dp); +} + +/* + * dfollow - change to arg directory; fall back on cdpath if not valid + */ +tchar * +dfollow(cp) + register tchar *cp; +{ + register tchar *dp; + struct varent *c; + int cdhashval, cdhashval1; + int index; + int slash; /*slashes in the argument*/ + tchar *fullpath; + tchar *slashcp; /*cp string prepended with a slash*/ + +#ifdef TRACE + tprintf("TRACE- dfollow()\n"); +#endif + cp = globone(cp); + if (chdir_(cp) >= 0) + goto gotcha; + + /* + * If the directory argument has a slash in it, + * for example, directory/directory, then can't + * find that in the cache table. + */ + slash = any('/', cp); + + /* + * Try interpreting wrt successive components of cdpath. + * cdpath caching is turned off or directory argument + * has a slash in it. + */ + if (cp[0] != '/' + && !prefix(S_DOTSLA /* "./" */, cp) + && !prefix(S_DOTDOTSLA /* "../" */, cp) + && (c = adrof(S_cdpath)) + && ( !havhash2 || slash) ) { + tchar **cdp; + register tchar *p; + tchar buf[MAXPATHLEN]; + + for (cdp = c->vec; *cdp; cdp++) { + for (dp = buf, p = *cdp; *dp++ = *p++;) + ; + dp[-1] = '/'; + for (p = cp; *dp++ = *p++;) + ; + if (chdir_(buf) >= 0) { + printd = 1; + xfree(cp); + cp = savestr(buf); + goto gotcha; + } + } + } + + /* cdpath caching turned on */ + if (cp[0] != '/' + && !prefix(S_DOTSLA /* "./" */, cp) + && !prefix(S_DOTDOTSLA /* "../" */, cp) + && (c = adrof(S_cdpath)) + && havhash2 && !slash ) { + tchar **pv; + + /* If no cdpath or no paths in cdpath, leave */ + if ( c == 0 || c->vec[0]== 0 ) + pv = justabs; + else + pv = c->vec; + + slashcp = strspl(S_SLASH, cp); + + cdhashval = hashname(cp); + + /*index points to next path component to test*/ + index=0; + + /* + * Look at each path in cdpath until get a match. + * Only look at those path beginning with a slash + */ + do { + /* only check cache for absolute pathnames */ + if ( pv[0][0] == '/' ) { + cdhashval1 = hash(cdhashval, index); + if (bit(xhash2, cdhashval1)) { + /* + * concatenate found path with + * arg directory + */ + fullpath = strspl(*pv, slashcp); + if (chdir_(fullpath) >= 0) { + printd = 1; + xfree(cp); + cp = savestr(fullpath); + xfree(slashcp); + xfree(fullpath); + goto gotcha; + } + } + } + /* + * relative pathnames are not cached, and must be + * checked manually + */ + else { + register tchar *p; + tchar buf[MAXPATHLEN]; + + for (dp = buf, p = *pv; *dp++ = *p++; ) + ; + dp[-1] = '/'; + for (p = cp; *dp++ = *p++; ) + ; + if (chdir_(buf) >= 0) { + printd = 1; + xfree(cp); + cp = savestr(buf); + xfree(slashcp); + goto gotcha; + } + } + pv++; + index++; + } while (*pv); + } + + /* + * Try dereferencing the variable named by the argument. + */ + dp = value(cp); + if ((dp[0] == '/' || dp[0] == '.') && chdir_(dp) >= 0) { + xfree(cp); + cp = savestr(dp); + printd = 1; + goto gotcha; + } + xfree(cp); /* XXX, use after free */ + Perror(cp); + +gotcha: + if (*cp != '/') { + register tchar *p, *q; + int cwdlen; + int len; + + /* + * All in the name of efficiency? + */ + + if ( ( cwdlen = (strlen_(dcwd->di_name) ) ) == 1 ){ + if ( *dcwd->di_name == '/' ) /* root */ + cwdlen = 0; + else + { + /* + * if we are here, when the shell started + * it was unable to getwd(), lets try it again + */ + tchar path[MAXPATHLEN]; + + p = getwd_(path); + if (p == NULL) + error("cannot determine current directory"); + else + { + xfree(dcwd->di_name); + dcwd->di_name = savestr(p); + xfree(cp); + cp = savestr(p); + return dcanon(cp, cp); + } + + } + } + /* + * + * for (p = cp; *p++;) + * ; + * dp = (tchar *)xalloc((unsigned) (cwdlen + (p - cp) + 1)*sizeof (tchar)) + */ + len = strlen_(cp); + dp = (tchar *)xalloc((unsigned) (cwdlen + len + 2)*sizeof(tchar)); + for (p = dp, q = dcwd->di_name; *p++ = *q++;) + ; + if (cwdlen) + p[-1] = '/'; + else + p--; /* don't add a / after root */ + for (q = cp; *p++ = *q++;) + ; + xfree(cp); + cp = dp; + dp += cwdlen; + } else + dp = cp; + return dcanon(cp, dp); +} + +/* + * dopushd - push new directory onto directory stack. + * with no arguments exchange top and second. + * with numeric argument (+n) bring it to top. + */ +dopushd(v) + tchar **v; +{ + register struct directory *dp; + +#ifdef TRACE + tprintf("TRACE- dopushd()\n"); +#endif + printd = 1; + if (*++v == NOSTR) { + if ((dp = dcwd->di_prev) == &dhead) + dp = dhead.di_prev; + if (dp == dcwd) + bferr("No other directory"); + if (chdir_(dp->di_name) < 0) + Perror(dp->di_name); + dp->di_prev->di_next = dp->di_next; + dp->di_next->di_prev = dp->di_prev; + dp->di_next = dcwd->di_next; + dp->di_prev = dcwd; + dcwd->di_next->di_prev = dp; + dcwd->di_next = dp; + } else if (dp = dfind(*v)) { + if (chdir_(dp->di_name) < 0) + Perror(dp->di_name); + } else { + register tchar *cp; + + cp = dfollow(*v); + dp = (struct directory *)calloc(sizeof (struct directory), 1); + dp->di_name = cp; + dp->di_count = 0; + dp->di_prev = dcwd; + dp->di_next = dcwd->di_next; + dcwd->di_next = dp; + dp->di_next->di_prev = dp; + } + dnewcwd(dp); +} + +/* + * dfind - find a directory if specified by numeric (+n) argument + */ +struct directory * +dfind(cp) + register tchar *cp; +{ + register struct directory *dp; + register int i; + register tchar *ep; + +#ifdef TRACE + tprintf("TRACE- dfind()\n"); +#endif + if (*cp++ != '+') + return (0); + for (ep = cp; digit(*ep); ep++) + continue; + if (*ep) + return (0); + i = getn(cp); + if (i <= 0) + return (0); + for (dp = dcwd; i != 0; i--) { + if ((dp = dp->di_prev) == &dhead) + dp = dp->di_prev; + if (dp == dcwd) + bferr("Directory stack not that deep"); + } + return (dp); +} + +/* + * dopopd - pop a directory out of the directory stack + * with a numeric argument just discard it. + */ +dopopd(v) + tchar **v; +{ + register struct directory *dp, *p; + +#ifdef TRACE + tprintf("TRACE- dopopd()\n"); +#endif + printd = 1; + if (*++v == NOSTR) + dp = dcwd; + else if ((dp = dfind(*v)) == 0) + bferr("Invalid argument"); + if (dp->di_prev == &dhead && dp->di_next == &dhead) + bferr("Directory stack empty"); + if (dp == dcwd) { + if ((p = dp->di_prev) == &dhead) + p = dhead.di_prev; + if (chdir_(p->di_name) < 0) + Perror(p->di_name); + } + dp->di_prev->di_next = dp->di_next; + dp->di_next->di_prev = dp->di_prev; + if (dp == dcwd) + dnewcwd(p); + else + dodirs(fakev); + dfree(dp); +} + +/* + * dfree - free the directory (or keep it if it still has ref count) + */ +dfree(dp) + register struct directory *dp; +{ + +#ifdef TRACE + tprintf("TRACE- dfree()\n"); +#endif + if (dp->di_count != 0) + dp->di_next = dp->di_prev = 0; + else + xfree(dp->di_name), xfree( (tchar *)dp); +} + +/* + * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. + * We are of course assuming that the file system is standardly + * constructed (always have ..'s, directories have links). + * + * If the hardpaths shell variable is set, resolve the + * resulting pathname to contain no symbolic link components. + */ +tchar * +dcanon(cp, p) + register tchar *cp, *p; +{ + register tchar *sp; /* rightmost component currently under + consideration */ + register tchar *p1, /* general purpose */ + *p2; + bool slash, dotdot, hardpaths; + +#ifdef TRACE + tprintf("TRACE- dcannon()\n"); +#endif + + if (*cp != '/') + abort(); + + if (hardpaths = (adrof(S_hardpaths) != NULL)) { + /* + * Be paranoid: don't trust the initial prefix + * to be symlink-free. + */ + p = cp; + } + + /* + * Loop invariant: cp points to the overall path start, + * p to its as yet uncanonicalized trailing suffix. + */ + while (*p) { /* for each component */ + sp = p; /* save slash address */ + + while (*++p == '/') /* flush extra slashes */ + ; + if (p != ++sp) + for (p1 = sp, p2 = p; *p1++ = *p2++;) + ; + + p = sp; /* save start of component */ + slash = 0; + if (*p) + while (*++p) /* find next slash or end of path */ + if (*p == '/') { + slash = 1; + *p = '\0'; + break; + } + + if (*sp == '\0') { + /* component is null */ + if (--sp == cp) /* if path is one tchar (i.e. /) */ + break; + else + *sp = '\0'; + continue; + } + + if (sp[0] == '.' && sp[1] == '\0') { + /* Squeeze out component consisting of "." */ + if (slash) { + for (p1 = sp, p2 = p + 1; *p1++ = *p2++;) + ; + p = --sp; + } else if (--sp != cp) + *sp = '\0'; + continue; + } + + /* + * At this point we have a path of the form "x/yz", + * where "x" is null or rooted at "/", "y" is a single + * component, and "z" is possibly null. The pointer cp + * points to the start of "x", sp to the start of "y", + * and p to the beginning of "z", which has been forced + * to a null. + */ + /* + * Process symbolic link component. Provided that either + * the hardpaths shell variable is set or "y" is really + * ".." we replace the symlink with its contents. The + * second condition for replacement is necessary to make + * the command "cd x/.." produce the same results as the + * sequence "cd x; cd ..". + * + * Note that the two conditions correspond to different + * potential symlinks. When hardpaths is set, we must + * check "x/y"; otherwise, when "y" is known to be "..", + * we check "x". + */ + dotdot = sp[0] == '.' && sp[1] == '.' && sp[2] == '\0'; + if (hardpaths || dotdot) { + tchar link[MAXPATHLEN]; + int cc; + tchar *newcp; + + /* + * Isolate the end of the component that is to + * be checked for symlink-hood. + */ + sp--; + if (! hardpaths) + *sp = '\0'; + + /* + * See whether the component is really a symlink by + * trying to read it. If the read succeeds, it is. + */ + if ((hardpaths || sp > cp) && + (cc = readlink_(cp, link, MAXPATHLEN)) >= 0) { + /* + * readlink_ put null, so we don't need this. + */ + /* link[cc] = '\0'; */ + + /* Restore path. */ + if (slash) + *p = '/'; + + /* + * Point p at the start of the trailing + * path following the symlink component. + * It's already there is hardpaths is set. + */ + if (! hardpaths) { + /* Restore path as well. */ + *(p = sp) = '/'; + } + + /* + * Find length of p. + */ + for (p1 = p; *p1++;) + ; + + if (*link != '/') { + /* + * Relative path: replace the symlink + * component with its value. First, + * set sp to point to the slash at + * its beginning. If hardpaths is + * set, this is already the case. + */ + if (! hardpaths) { + while (*--sp != '/') + ; + } + + /* + * Terminate the leading part of the + * path, including trailing slash. + */ + sp++; + *sp = '\0'; + + /* + * New length is: "x/" + link + "z" + */ + p1 = newcp = (tchar *)xalloc((unsigned) + ((sp - cp) + cc + (p1 - p))*sizeof (tchar)); + /* + * Copy new path into newcp + */ + for (p2 = cp; *p1++ = *p2++;) + ; + for (p1--, p2 = link; *p1++ = *p2++;) + ; + for (p1--, p2 = p; *p1++ = *p2++;) + ; + /* + * Restart canonicalization at + * expanded "/y". + */ + p = sp - cp - 1 + newcp; + } else { + /* + * New length is: link + "z" + */ + p1 = newcp = (tchar *)xalloc((unsigned) + (cc + (p1 - p))*sizeof (tchar)); + /* + * Copy new path into newcp + */ + for (p2 = link; *p1++ = *p2++;) + ; + for (p1--, p2 = p; *p1++ = *p2++;) + ; + /* + * Restart canonicalization at beginning + */ + p = newcp; + } + xfree(cp); + cp = newcp; + continue; /* canonicalize the link */ + } + + /* The component wasn't a symlink after all. */ + if (! hardpaths) + *sp = '/'; + } + + if (dotdot) { + if (sp != cp) + while (*--sp != '/') + ; + if (slash) { + for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;) + ; + p = sp; + } else if (cp == sp) + *++sp = '\0'; + else + *sp = '\0'; + continue; + } + + if (slash) + *p = '/'; + } + return cp; +} + +/* + * dnewcwd - make a new directory in the loop the current one + * and export its name to the PWD environment variable. + */ +dnewcwd(dp) + register struct directory *dp; +{ + +#ifdef TRACE + tprintf("TRACE- dnewcwd()\n"); +#endif + dcwd = dp; +#ifdef notdef + /* + * If we have a fast version of getwd available + * and hardpaths is set, it would be reasonable + * here to verify that dcwd->di_name really does + * name the current directory. Later... + */ +#endif notdef + + didchdir=1; + set(S_cwd, savestr(dcwd->di_name)); + didchdir=0; + local_setenv(S_PWD, dcwd->di_name); + if (printd) + dodirs(fakev); +} diff --git a/usr/src/cmd/csh/sh.dir.h b/usr/src/cmd/csh/sh.dir.h new file mode 100644 index 0000000000..78ece4a533 --- /dev/null +++ b/usr/src/cmd/csh/sh.dir.h @@ -0,0 +1,26 @@ +/* + * Copyright 1990 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Structure for entries in directory stack. + */ +struct directory { + struct directory *di_next; /* next in loop */ + struct directory *di_prev; /* prev in loop */ + unsigned short *di_count; /* refcount of processes */ + tchar *di_name; /* actual name */ +}; +struct directory *dcwd; /* the one we are in now */ diff --git a/usr/src/cmd/csh/sh.dol.c b/usr/src/cmd/csh/sh.dol.c new file mode 100644 index 0000000000..ed97399eb5 --- /dev/null +++ b/usr/src/cmd/csh/sh.dol.c @@ -0,0 +1,756 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> /* for lseek prototype */ +#include "sh.h" +#include "sh.tconst.h" + +/* + * C shell + */ + +/* + * These routines perform variable substitution and quoting via ' and ". + * To this point these constructs have been preserved in the divided + * input words. Here we expand variables and turn quoting via ' and " into + * QUOTE bits on characters (which prevent further interpretation). + * If the `:q' modifier was applied during history expansion, then + * some QUOTEing may have occurred already, so we dont "trim()" here. + */ + +int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ +tchar *Dcp, **Dvp; /* Input vector for Dreadc */ + +#define DEOF -1 + +#define unDgetC(c) Dpeekc = c + +#define QUOTES (_Q|_Q1|_ESC) /* \ ' " ` */ + +/* + * The following variables give the information about the current + * $ expansion, recording the current word position, the remaining + * words within this expansion, the count of remaining words, and the + * information about any : modifier which is being applied. + */ +tchar *dolp; /* Remaining chars from this word */ +tchar **dolnxt; /* Further words */ +int dolcnt; /* Count of further words */ +tchar dolmod; /* : modifier character */ +int dolmcnt; /* :gx -> 10000, else 1 */ + +/* + * Fix up the $ expansions and quotations in the + * argument list to command t. + */ +Dfix(t) + register struct command *t; +{ + register tchar **pp; + register tchar *p; + +#ifdef TRACE + tprintf("TRACE- Dfix()\n"); +#endif + if (noexec) + return; + /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ + for (pp = t->t_dcom; p = *pp++;) + while (*p) + if (cmap(*p++, _DOL|QUOTES)) { /* $, \, ', ", ` */ + Dfix2(t->t_dcom); /* found one */ + blkfree(t->t_dcom); + t->t_dcom = gargv; + gargv = 0; + return; + } +} + +/* + * $ substitute one word, for i/o redirection + */ +tchar * +Dfix1(cp) + register tchar *cp; +{ + tchar *Dv[2]; + +#ifdef TRACE + tprintf("TRACE- Dfix1()\n"); +#endif + if (noexec) + return (0); + Dv[0] = cp; Dv[1] = NOSTR; + Dfix2(Dv); + if (gargc != 1) { + setname(cp); + bferr("Ambiguous"); + } + cp = savestr(gargv[0]); + blkfree(gargv), gargv = 0; + return (cp); +} + +/* + * Subroutine to do actual fixing after state initialization. + */ +Dfix2(v) + tchar **v; +{ + tchar *agargv[GAVSIZ]; + +#ifdef TRACE + tprintf("TRACE- Dfix2()\n"); +#endif + ginit(agargv); /* Initialize glob's area pointers */ + Dvp = v; Dcp = S_ /* "" */;/* Setup input vector for Dreadc */ + unDgetC(0); unDredc(0); /* Clear out any old peeks (at error) */ + dolp = 0; dolcnt = 0; /* Clear out residual $ expands (...) */ + while (Dword()) + continue; + gargv = copyblk(gargv); +} + +/* + * Get a word. This routine is analogous to the routine + * word() in sh.lex.c for the main lexical input. One difference + * here is that we don't get a newline to terminate our expansion. + * Rather, DgetC will return a DEOF when we hit the end-of-input. + */ +Dword() +{ + register int c, c1; + static tchar *wbuf = NULL; + static int wbufsiz = BUFSIZ; + register int wp = 0; + register bool dolflg; + bool sofar = 0; +#define DYNAMICBUFFER() \ + do { \ + if (wp >= wbufsiz) { \ + wbufsiz += BUFSIZ; \ + wbuf = xrealloc(wbuf, (wbufsiz+1) * sizeof (tchar)); \ + } \ + } while (0) + +#ifdef TRACE + tprintf("TRACE- Dword()\n"); +#endif + if (wbuf == NULL) + wbuf = xalloc((wbufsiz+1) * sizeof (tchar)); +loop: + c = DgetC(DODOL); + switch (c) { + + case DEOF: +deof: + if (sofar == 0) + return (0); + /* finish this word and catch the code above the next time */ + unDredc(c); + /* fall into ... */ + + case '\n': + wbuf[wp] = 0; + goto ret; + + case ' ': + case '\t': + goto loop; + + case '`': + /* We preserve ` quotations which are done yet later */ + wbuf[wp++] = c; + case '\'': + case '"': + /* + * Note that DgetC never returns a QUOTES character + * from an expansion, so only true input quotes will + * get us here or out. + */ + c1 = c; + dolflg = c1 == '"' ? DODOL : 0; + for (;;) { + c = DgetC(dolflg); + if (c == c1) + break; + if (c == '\n' || c == DEOF) + error("Unmatched %c", (tchar) c1); + if ((c & (QUOTE|TRIM)) == ('\n' | QUOTE)) + --wp; + DYNAMICBUFFER(); + switch (c1) { + + case '"': + /* + * Leave any `s alone for later. + * Other chars are all quoted, thus `...` + * can tell it was within "...". + */ + wbuf[wp++] = c == '`' ? '`' : c | QUOTE; + break; + + case '\'': + /* Prevent all further interpretation */ + wbuf[wp++] = c | QUOTE; + break; + + case '`': + /* Leave all text alone for later */ + wbuf[wp++] = c; + break; + } + } + if (c1 == '`') { + DYNAMICBUFFER(); + wbuf[wp++] = '`'; + } + goto pack; /* continue the word */ + + case '\\': + c = DgetC(0); /* No $ subst! */ + if (c == '\n' || c == DEOF) + goto loop; + c |= QUOTE; + break; +#ifdef MBCHAR /* Could be a space char from aux. codeset. */ + default: + if (isauxsp(c)) goto loop; +#endif /* MBCHAR */ + } + unDgetC(c); +pack: + sofar = 1; + /* pack up more characters in this word */ + for (;;) { + c = DgetC(DODOL); + if (c == '\\') { + c = DgetC(0); + if (c == DEOF) + goto deof; + if (c == '\n') + c = ' '; + else + c |= QUOTE; + } + if (c == DEOF) + goto deof; + if (cmap(c, _SP|_NL|_Q|_Q1) || + isauxsp(c)) { /* sp \t\n'"` or aux. sp */ + unDgetC(c); + if (cmap(c, QUOTES)) + goto loop; + DYNAMICBUFFER(); + wbuf[wp++] = 0; + goto ret; + } + DYNAMICBUFFER(); + wbuf[wp++] = c; + } +ret: + Gcat(S_ /* "" */, wbuf); + return (1); +} + +/* + * Get a character, performing $ substitution unless flag is 0. + * Any QUOTES character which is returned from a $ expansion is + * QUOTEd so that it will not be recognized above. + */ +DgetC(flag) + register int flag; +{ + register int c; + +top: + if (c = Dpeekc) { + Dpeekc = 0; + return (c); + } + if (lap) { + c = *lap++ & (QUOTE|TRIM); + if (c == 0) { + lap = 0; + goto top; + } +quotspec: + /* + * don't quote things if there was an error (err!=0) + * the input is original, not from a substitution and + * therefore should not be quoted + */ + if (!err && cmap(c, QUOTES)) + return (c | QUOTE); + return (c); + } + if (dolp) { + if (c = *dolp++ & (QUOTE|TRIM)) + goto quotspec; + if (dolcnt > 0) { + setDolp(*dolnxt++); + --dolcnt; + return (' '); + } + dolp = 0; + } + if (dolcnt > 0) { + setDolp(*dolnxt++); + --dolcnt; + goto top; + } + c = Dredc(); + if (c == '$' && flag) { + Dgetdol(); + goto top; + } + return (c); +} + +tchar *nulvec[] = { 0 }; +struct varent nulargv = { nulvec, S_argv, 0 }; + +/* + * Handle the multitudinous $ expansion forms. + * Ugh. + */ +Dgetdol() +{ + register tchar *np; + register struct varent *vp; + tchar name[MAX_VREF_LEN]; + int c, sc; + int subscr = 0, lwb = 1, upb = 0; + bool dimen = 0, bitset = 0; + tchar wbuf[BUFSIZ]; + +#ifdef TRACE + tprintf("TRACE- Dgetdol()\n"); +#endif + dolmod = dolmcnt = 0; + c = sc = DgetC(0); + if (c == '{') + c = DgetC(0); /* sc is { to take } later */ + if ((c & TRIM) == '#') + dimen++, c = DgetC(0); /* $# takes dimension */ + else if (c == '?') + bitset++, c = DgetC(0); /* $? tests existence */ + switch (c) { + + case '$': + if (dimen || bitset) +syntax: + error("Variable syntax"); /* No $?$, $#$ */ + setDolp(doldol); + goto eatbrac; + + case '<'|QUOTE: + if (dimen || bitset) + goto syntax; /* No $?<, $#< */ + for (np = wbuf; read_(OLDSTD, np, 1) == 1; np++) { + if (np >= &wbuf[BUFSIZ-1]) + error("$< line too long"); + if (*np <= 0 || *np == '\n') + break; + } + *np = 0; + /* + * KLUDGE: dolmod is set here because it will + * cause setDolp to call domod and thus to copy wbuf. + * Otherwise setDolp would use it directly. If we saved + * it ourselves, no one would know when to free it. + * The actual function of the 'q' causes filename + * expansion not to be done on the interpolated value. + */ + dolmod = 'q'; + dolmcnt = 10000; + setDolp(wbuf); + goto eatbrac; + + case DEOF: + case '\n': + goto syntax; + + case '*': + (void) strcpy_(name, S_argv); + vp = adrof(S_argv); + subscr = -1; /* Prevent eating [...] */ + break; + + default: + np = name; + if (digit(c)) { + if (dimen) + goto syntax; /* No $#1, e.g. */ + subscr = 0; + do { + subscr = subscr * 10 + c - '0'; + c = DgetC(0); + } while (digit(c)); + unDredc(c); + if (subscr < 0) + error("Subscript out of range"); + if (subscr == 0) { + if (bitset) { + dolp = file ? S_1/*"1"*/ : S_0/*"0"*/; + goto eatbrac; + } + if (file == 0) + error("No file for $0"); + setDolp(file); + goto eatbrac; + } + if (bitset) + goto syntax; + vp = adrof(S_argv); + if (vp == 0) { + vp = &nulargv; + goto eatmod; + } + break; + } + if (!alnum(c)) + goto syntax; + for (;;) { + *np++ = c; + c = DgetC(0); + if (!alnum(c)) + break; + /* if variable name is > 20, complain */ + if (np >= &name[MAX_VAR_LEN]) + error("Variable name too long"); + + } + *np++ = 0; + unDredc(c); + vp = adrof(name); + } + if (bitset) { + /* getenv() to getenv_(), because 'name''s type is now tchar * */ + /* no need to xalloc */ + dolp = (vp || getenv_(name)) ? S_1 /*"1"*/ : S_0/*"0"*/; + goto eatbrac; + } + if (vp == 0) { + /* getenv() to getenv_(), because 'name''s type is now tchar * */ + /* no need to xalloc */ + np = getenv_(name); + if (np) { + addla(np); + goto eatbrac; + } + udvar(name); + /*NOTREACHED*/ + } + c = DgetC(0); + upb = blklen(vp->vec); + if (dimen == 0 && subscr == 0 && c == '[') { + np = name; + for (;;) { + c = DgetC(DODOL); /* Allow $ expand within [ ] */ + if (c == ']') + break; + if (c == '\n' || c == DEOF) + goto syntax; + if (np >= &name[MAX_VREF_LEN]) + error("Variable reference too long"); + *np++ = c; + } + *np = 0, np = name; + if (dolp || dolcnt) /* $ exp must end before ] */ + goto syntax; + if (!*np) + goto syntax; + if (digit(*np)) { + register int i = 0; + + while (digit(*np)) + i = i * 10 + *np++ - '0'; +/* if ((i < 0 || i > upb) && !any(*np, "-*")) {*/ + if ((i < 0 || i > upb) && (*np!='-') && (*np!='*')) { +oob: + setname(vp->v_name); + error("Subscript out of range"); + } + lwb = i; + if (!*np) + upb = lwb, np = S_AST/*"*"*/; + } + if (*np == '*') + np++; + else if (*np != '-') + goto syntax; + else { + register int i = upb; + + np++; + if (digit(*np)) { + i = 0; + while (digit(*np)) + i = i * 10 + *np++ - '0'; + if (i < 0 || i > upb) + goto oob; + } + if (i < lwb) + upb = lwb - 1; + else + upb = i; + } + if (lwb == 0) { + if (upb != 0) + goto oob; + upb = -1; + } + if (*np) + goto syntax; + } else { + if (subscr > 0) + if (subscr > upb) + lwb = 1, upb = 0; + else + lwb = upb = subscr; + unDredc(c); + } + if (dimen) { + tchar *cp = putn(upb - lwb + 1); + + addla(cp); + xfree(cp); + } else { +eatmod: + c = DgetC(0); + if (c == ':') { + c = DgetC(0), dolmcnt = 1; + if (c == 'g') + c = DgetC(0), dolmcnt = 10000; + if (!any(c, S_htrqxe)) + error("Bad : mod in $"); + dolmod = c; + if (c == 'q') + dolmcnt = 10000; + } else + unDredc(c); + dolnxt = &vp->vec[lwb - 1]; + dolcnt = upb - lwb + 1; + } +eatbrac: + if (sc == '{') { + c = Dredc(); + if (c != '}') + goto syntax; + } +} + +setDolp(cp) + register tchar *cp; +{ + register tchar *dp; + +#ifdef TRACE + tprintf("TRACE- setDolp()\n"); +#endif + if (dolmod == 0 || dolmcnt == 0) { + dolp = cp; + return; + } + dp = domod(cp, dolmod); + if (dp) { + dolmcnt--; + addla(dp); + xfree(dp); + } else + addla(cp); + dolp = S_/*""*/; +} + +unDredc(c) + int c; +{ + + Dpeekrd = c; +} + +Dredc() +{ + register int c; + + if (c = Dpeekrd) { + Dpeekrd = 0; + return (c); + } + if (Dcp && (c = *Dcp++)) + return (c&(QUOTE|TRIM)); + if (*Dvp == 0) { + Dcp = 0; + return (DEOF); + } + Dcp = *Dvp++; + return (' '); +} + +Dtestq(c) + register int c; +{ + + if (cmap(c, QUOTES)) + gflag = 1; +} + +/* + * Form a shell temporary file (in unit 0) from the words + * of the shell input up to a line the same as "term". + * Unit 0 should have been closed before this call. + */ +heredoc(term) + tchar *term; +{ + register int c; + tchar *Dv[2]; + tchar obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ]; + int ocnt, lcnt, mcnt; + register tchar *lbp, *obp, *mbp; + tchar **vp; + bool quoted; + tchar shtemp[] = {'/', 't', 'm', 'p', '/', 's', 'h', 'X', 'X', 'X', +'X', 'X', 'X', 0}; + int fd1; + +#ifdef TRACE + tprintf("TRACE- heredoc()\n"); +#endif + if ((fd1 = mkstemp_(shtemp)) < 0) + Perror(shtemp); + (void) unlink_(shtemp); /* 0 0 inode! */ + unsetfd(fd1); + Dv[0] = term; Dv[1] = NOSTR; gflag = 0; + trim(Dv); rscan(Dv, Dtestq); quoted = gflag; + ocnt = BUFSIZ; obp = obuf; + for (;;) { + /* + * Read up a line + */ + lbp = lbuf; lcnt = BUFSIZ - 4; + for (;;) { + c = readc(1); /* 1 -> Want EOF returns */ + if (c < 0) { + setname(term); + bferr("<< terminator not found"); + } + if (c == '\n') + break; + if (c &= TRIM) { + *lbp++ = c; + if (--lcnt < 0) { + setname(S_LESLES/*"<<"*/); + error("Line overflow"); + } + } + } + *lbp = 0; + + /* + * Compare to terminator -- before expansion + */ + if (eq(lbuf, term)) { + (void) write_(0, obuf, BUFSIZ - ocnt); + (void) lseek(0, (off_t)0, 0); + return; + } + + /* + * If term was quoted or -n just pass it on + */ + if (quoted || noexec) { + *lbp++ = '\n'; *lbp = 0; + for (lbp = lbuf; c = *lbp++;) { + *obp++ = c; + if (--ocnt == 0) { + (void) write_(0, obuf, BUFSIZ); + obp = obuf; ocnt = BUFSIZ; + } + } + continue; + } + + /* + * Term wasn't quoted so variable and then command + * expand the input line + */ + Dcp = lbuf; Dvp = Dv + 1; mbp = mbuf; mcnt = BUFSIZ - 4; + for (;;) { + c = DgetC(DODOL); + if (c == DEOF) + break; + if ((c &= TRIM) == 0) + continue; + /* \ quotes \ $ ` here */ + if (c =='\\') { + c = DgetC(0); +/* if (!any(c, "$\\`"))*/ + if ((c!='$')&&(c!='\\')&&(c!='`')) + unDgetC(c | QUOTE), c = '\\'; + else + c |= QUOTE; + } + *mbp++ = c; + if (--mcnt == 0) { + setname(S_LESLES/*"<<"*/); + bferr("Line overflow"); + } + } + *mbp++ = 0; + + /* + * If any ` in line do command substitution + */ + mbp = mbuf; + if (any('`', mbp)) { + /* + * 1 arg to dobackp causes substitution to be literal. + * Words are broken only at newlines so that all blanks + * and tabs are preserved. Blank lines (null words) + * are not discarded. + */ + vp = dobackp(mbuf, 1); + } else + /* Setup trivial vector similar to return of dobackp */ + Dv[0] = mbp, Dv[1] = NOSTR, vp = Dv; + + /* + * Resurrect the words from the command substitution + * each separated by a newline. Note that the last + * newline of a command substitution will have been + * discarded, but we put a newline after the last word + * because this represents the newline after the last + * input line! + */ + for (; *vp; vp++) { + for (mbp = *vp; *mbp; mbp++) { + *obp++ = *mbp & TRIM; + if (--ocnt == 0) { + (void) write_(0, obuf, BUFSIZ); + obp = obuf; ocnt = BUFSIZ; + } + } + *obp++ = '\n'; + if (--ocnt == 0) { + (void) write_(0, obuf, BUFSIZ); + obp = obuf; ocnt = BUFSIZ; + } + } + if (pargv) + blkfree(pargv), pargv = 0; + } +} diff --git a/usr/src/cmd/csh/sh.err.c b/usr/src/cmd/csh/sh.err.c new file mode 100644 index 0000000000..ee4ebfbb89 --- /dev/null +++ b/usr/src/cmd/csh/sh.err.c @@ -0,0 +1,211 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include <locale.h> +#include <dirent.h> +/* + * #include <sys/ioctl.h> + * #include <stdlib.h> + */ +#include "sh.tconst.h" +/* + * C Shell + */ + + +bool errspl; /* Argument to error was spliced by seterr2 */ +tchar one[2] = { '1', 0 }; +tchar *onev[2] = { one, NOSTR }; +/* + * contains DIR * for last opendir_(), its left open if an error + * longjmp (reset) occurs before it gets closed via closedir. + * if its not null in the error handler, then closedir it. + */ +DIR *Dirp = NULL; + +/* + * Print error string s with optional argument arg. + * This routine always resets or exits. The flag haderr + * is set so the routine who catches the unwind can propogate + * it if they want. + * + * Note that any open files at the point of error will eventually + * be closed in the routine process in sh.c which is the only + * place error unwinds are ever caught. + */ +/*VARARGS1*/ +error(s, a1, a2) + char *s; +{ + register tchar **v; + register char *ep; + + /* + * Must flush before we print as we wish output before the error + * to go on (some form of) standard output, while output after + * goes on (some form of) diagnostic output. + * If didfds then output will go to 1/2 else to FSHOUT/FSHDIAG. + * See flush in sh.print.c. + */ + flush(); + haderr = 1; /* Now to diagnostic output */ + timflg = 0; /* This isn't otherwise reset */ + if (v = pargv) + pargv = 0, blkfree(v); + if (v = gargv) + gargv = 0, blkfree(v); + + /* + * A zero arguments causes no printing, else print + * an error diagnostic here. + */ + if (s) { + printf(s, a1, a2), printf("\n"); + } + + + didfds = 0; /* Forget about 0,1,2 */ + if ((ep = err) && errspl) { + errspl = 0; + xfree(ep); + } + errspl = 0; + + if ( Dirp ){ + closedir(Dirp); + Dirp = NULL; + } + + /* + * Go away if -e or we are a child shell + */ + if (exiterr || child) { + exit(1); + } + + /* + * Reset the state of the input. + * This buffered seek to end of file will also + * clear the while/foreach stack. + */ + btoeof(); + + setq(S_status, onev, &shvhed); + if (tpgrp > 0) + (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&tpgrp); + reset(); /* Unwind */ +} + +/* + * Perror is the shells version of perror which should otherwise + * never be called. + */ +Perror(s) + tchar *s; +{ + char chbuf[BUFSIZ]; + + /* + * Perror uses unit 2, thus if we didn't set up the fd's + * we must set up unit 2 now else the diagnostic will disappear + */ + if (!didfds) { + register int oerrno = errno; + + (void) dcopy(SHDIAG, 2); + errno = oerrno; + } + tstostr(chbuf, s); + perror(chbuf); + error(NULL); /* To exit or unwind */ +} + +bferr(cp) + char *cp; +{ + + flush(); + haderr = 1; + if( bname) printf("%t: ", bname); + error("%s", gettext(cp)); +} + +/* + * The parser and scanner set up errors for later by calling seterr, + * which sets the variable err as a side effect; later to be tested, + * e.g. in process. + */ +seterr(s) + char *s; +{ + + if (err == 0) + err = s, errspl = 0; +} + +/* Set err to a splice of cp and dp, to be freed later in error() */ +seterr2(cp, dp) + tchar *cp; + char *dp; +{ + char chbuf[BUFSIZ]; + char *gdp; + + if (err) + return; + + /* Concatinate cp and dp in the allocated space. */ + tstostr(chbuf, cp); + gdp = gettext(dp); + err = (char *)xalloc(strlen(chbuf)+strlen(gdp)+1); + strcpy(err, chbuf); + strcat(err, gdp); + + errspl++;/* Remember to xfree(err). */ +} + +/* Set err to a splice of cp with a string form of character d */ +seterrc(cp, d) + char *cp; + tchar d; +{ + char chbuf[MB_LEN_MAX+1]; + + /* don't overwrite an existing error message */ + if (err) + return; + +#ifdef MBCHAR + { + wchar_t wcd=(wchar_t)(d&TRIM); + int i; + + i = wctomb(chbuf, wcd); /* chbuf holds d in multibyte representation. */ + chbuf[(i>0)?i:0] = (char) 0; + } +#else + chbuf[0]=(char)(d&TRIM); chbuf[1]=(char)0; +#endif + + + /* Concatinate cp and d in the allocated space. */ + err = (char *)xalloc(strlen(cp)+strlen(chbuf)+1); + strcpy(err, cp); + strcat(err, chbuf); + + errspl++; /* Remember to xfree(err). */ +} diff --git a/usr/src/cmd/csh/sh.exec.c b/usr/src/cmd/csh/sh.exec.c new file mode 100644 index 0000000000..d0dd6dea7f --- /dev/null +++ b/usr/src/cmd/csh/sh.exec.c @@ -0,0 +1,466 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include <dirent.h> +#include <string.h> +#include "sh.tconst.h" +#include "sh_policy.h" + + +/* + * C shell + */ + +/* + * System level search and execute of a command. + * We look in each directory for the specified command name. + * If the name contains a '/' then we execute only the full path name. + * If there is no search path then we execute only full path names. + */ + +/* + * As we search for the command we note the first non-trivial error + * message for presentation to the user. This allows us often + * to show that a file has the wrong mode/no access when the file + * is not in the last component of the search path, so we must + * go on after first detecting the error. + */ +char *exerr; /* Execution error message */ + + +extern DIR *opendir_(); + + +doexec(t) + register struct command *t; +{ + tchar *sav; + register tchar *dp, **pv, **av; + register struct varent *v; + bool slash; + int hashval, hashval1, i; + tchar *blk[2]; +#ifdef TRACE + tprintf("TRACE- doexec()\n"); +#endif + + /* + * Glob the command name. If this does anything, then we + * will execute the command only relative to ".". One special + * case: if there is no PATH, then we execute only commands + * which start with '/'. + */ + dp = globone(t->t_dcom[0]); + sav = t->t_dcom[0]; + exerr = 0; t->t_dcom[0] = dp; + setname(dp); + xfree(sav); + v = adrof(S_path /*"path"*/); + if (v == 0 && dp[0] != '/') { + pexerr(); + } + slash = gflag; + + /* + * Glob the argument list, if necessary. + * Otherwise trim off the quote bits. + */ + gflag = 0; av = &t->t_dcom[1]; + tglob(av); + if (gflag) { + av = glob(av); + if (av == 0) + error("No match"); + } + blk[0] = t->t_dcom[0]; + blk[1] = 0; + av = blkspl(blk, av); +#ifdef VFORK + Vav = av; +#endif + trim(av); + slash |= any('/', av[0]); + + xechoit(av); /* Echo command if -x */ + /* + * Since all internal file descriptors are set to close on exec, + * we don't need to close them explicitly here. Just reorient + * ourselves for error messages. + */ + SHIN = 0; SHOUT = 1; SHDIAG = 2; OLDSTD = 0; + + /* + * We must do this AFTER any possible forking (like `foo` + * in glob) so that this shell can still do subprocesses. + */ + (void) sigsetmask(0); + + /* + * If no path, no words in path, or a / in the filename + * then restrict the command search. + */ + if (v == 0 || v->vec[0] == 0 || slash) + pv = justabs; + else + pv = v->vec; + sav = strspl(S_SLASH /* "/" */, *av); /* / command name for postpending */ +#ifdef VFORK + Vsav = sav; +#endif + if (havhash) + hashval = hashname(*av); + i = 0; +#ifdef VFORK + hits++; +#endif + do { + if (!slash && pv[0][0] == '/' && havhash) { + hashval1 = hash(hashval, i); + if (!bit(xhash, hashval1)) + goto cont; + } + + if (pv[0][0] == 0 || eq(pv[0], S_DOT/*"."*/)) { /* don't make ./xxx */ + texec(t, *av, av); + } else { + dp = strspl(*pv, sav); +#ifdef VFORK + Vdp = dp; +#endif + texec(t, dp, av); +#ifdef VFORK + Vdp = 0; +#endif + xfree(dp); + } +#ifdef VFORK + misses++; +#endif +cont: + pv++; + i++; + } while (*pv); +#ifdef VFORK + hits--; +#endif +#ifdef VFORK + Vsav = 0; + Vav = 0; +#endif + xfree(sav); + xfree( (char *)av); + pexerr(); +} + +pexerr() +{ + +#ifdef TRACE + tprintf("TRACE- pexerr()\n"); +#endif + /* Couldn't find the damn thing */ + if (exerr) + bferr(exerr); + bferr("Command not found"); +} + +/* + * Execute command f, arg list t. + * Record error message if not found. + * Also do shell scripts here. + */ +texec(cmd, f, t) + register struct command *cmd; + tchar *f; + register tchar **t; +{ + register int pfstatus = 0; + register struct varent *v; + register tchar **vp; + tchar *lastsh[2]; + +#ifdef TRACE + tprintf("TRACE- texec()\n"); +#endif + /* convert cfname and cargs from tchar to char */ + tconvert(cmd, f, t); + + if (pfcshflag == 1) { + pfstatus = secpolicy_pfexec((const char *)(cmd->cfname), + cmd->cargs, (const char **)NULL); + if (pfstatus != NOATTRS) { + errno = pfstatus; + } + } + if ((pfcshflag == 0) || (pfstatus == NOATTRS)) { + execv(cmd->cfname, cmd->cargs); + } + + /* + * exec returned, free up allocations from above + * tconvert(), zero cfname and cargs to prevent + * duplicate free() in freesyn() + */ + xfree(cmd->cfname); + chr_blkfree(cmd->cargs); + cmd->cfname = (char *) 0; + cmd->cargs = (char **) 0; + + switch (errno) { + case ENOEXEC: + /* check that this is not a binary file */ + { + register int ff = open_(f, 0); + tchar ch; + + if (ff != -1 && read_(ff, &ch, 1) == 1 && !isprint(ch) + && !isspace(ch)) { + printf("Cannot execute binary file.\n"); + Perror(f); + (void) close(ff); + unsetfd(ff); + return; + } + (void) close(ff); + unsetfd(ff); + } + /* + * If there is an alias for shell, then + * put the words of the alias in front of the + * argument list replacing the command name. + * Note no interpretation of the words at this point. + */ + v = adrof1(S_shell /*"shell"*/, &aliases); + if (v == 0) { +#ifdef OTHERSH + register int ff = open_(f, 0); + tchar ch; +#endif + + vp = lastsh; + vp[0] = adrof(S_shell /*"shell"*/) ? value(S_shell /*"shell"*/) : S_SHELLPATH/*SHELLPATH*/; + vp[1] = (tchar *) NULL; +#ifdef OTHERSH + if (ff != -1 && read_(ff, &ch, 1) == 1 && ch != '#') + vp[0] = S_OTHERSH/*OTHERSH*/; + (void) close(ff); + unsetfd(ff); +#endif + } else + vp = v->vec; + t[0] = f; + t = blkspl(vp, t); /* Splice up the new arglst */ + f = *t; + + tconvert(cmd, f, t); /* convert tchar to char */ + + /* + * now done with tchar arg list t, + * free the space calloc'd by above blkspl() + */ + xfree((char *) t); + + execv(cmd->cfname, cmd->cargs); /* exec the command */ + + /* exec returned, same free'ing as above */ + xfree(cmd->cfname); + chr_blkfree(cmd->cargs); + cmd->cfname = (char *) 0; + cmd->cargs = (char **) 0; + + /* The sky is falling, the sky is falling! */ + + case ENOMEM: + Perror(f); + + case ENOENT: + break; + + default: + if (exerr == 0) { + exerr = strerror(errno); + setname(f); + } + } +} + + +static +tconvert(cmd, fname, list) +register struct command *cmd; +register tchar *fname, **list; +{ + register char **rc; + register int len; + + cmd->cfname = tstostr(NULL, fname); + + len = blklen(list); + rc = cmd->cargs = (char **) + calloc((u_int) (len + 1), sizeof(char **)); + while (len--) + *rc++ = tstostr(NULL, *list++); + *rc = NULL; +} + + +/*ARGSUSED*/ +execash(t, kp) + tchar **t; + register struct command *kp; +{ +#ifdef TRACE + tprintf("TRACE- execash()\n"); +#endif + + rechist(); + (void) signal(SIGINT, parintr); + (void) signal(SIGQUIT, parintr); + (void) signal(SIGTERM, parterm); /* if doexec loses, screw */ + lshift(kp->t_dcom, 1); + exiterr++; + doexec(kp); + /*NOTREACHED*/ +} + +xechoit(t) + tchar **t; +{ +#ifdef TRACE + tprintf("TRACE- xechoit()\n"); +#endif + + if (adrof(S_echo /*"echo"*/)) { + flush(); + haderr = 1; + blkpr(t), Putchar('\n'); + haderr = 0; + } +} + +/* + * This routine called when user enters "rehash". + * Both the path and cdpath caching arrays will + * be rehashed, via calling dohash. If either + * variable is not set with a value, then dohash + * just exits. + */ + +dorehash() +{ + dohash(xhash); + dohash(xhash2); +} + +/* + * Fill up caching arrays for path and cdpath + */ +dohash(cachearray) +char cachearray[]; +{ + struct stat stb; + DIR *dirp; + register struct dirent *dp; + register int cnt; + int i = 0; + struct varent *v; + tchar **pv; + int hashval; + tchar curdir_[MAXNAMLEN+1]; + +#ifdef TRACE + tprintf("TRACE- dohash()\n"); +#endif + /* Caching $path */ + if ( cachearray == xhash ) { + havhash = 1; + v = adrof(S_path /*"path"*/); + }else { /* Caching $cdpath */ + havhash2 = 1; + v = adrof(S_cdpath /*"cdpath"*/); + } + + for (cnt = 0; cnt < ( HSHSIZ / 8 ); cnt++) + cachearray[cnt] = 0; + if (v == 0) + { + return; + } + for (pv = v->vec; *pv; pv++, i++) { + if (pv[0][0] != '/') + continue; + dirp = opendir_(*pv); + if (dirp == NULL) + continue; + if (fstat(dirp->dd_fd, &stb) < 0 || !isdir(stb)) { + unsetfd(dirp->dd_fd); + closedir_(dirp); + continue; + } + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_ino == 0) + continue; + if (dp->d_name[0] == '.' && + (dp->d_name[1] == '\0' || + dp->d_name[1] == '.' && dp->d_name[2] == '\0')) + continue; + hashval = hash(hashname(strtots(curdir_,dp->d_name)), i); + bis(cachearray, hashval); + } + unsetfd(dirp->dd_fd); + closedir_(dirp); + } +} + +dounhash() +{ + +#ifdef TRACE + tprintf("TRACE- dounhash()\n"); +#endif + havhash = 0; + havhash2 = 0; +} + +#ifdef VFORK +hashstat() +{ +#ifdef TRACE + tprintf("TRACE- hashstat_()\n"); +#endif + + if (hits+misses) + printf("%d hits, %d misses, %d%%\n", + hits, misses, 100 * hits / (hits + misses)); +} +#endif + +/* + * Hash a command name. + */ +hashname(cp) + register tchar *cp; +{ + register long h = 0; + +#ifdef TRACE + tprintf("TRACE- hashname()\n"); +#endif + while (*cp) + h = hash(h, *cp++); + return ((int) h); +} diff --git a/usr/src/cmd/csh/sh.exp.c b/usr/src/cmd/csh/sh.exp.c new file mode 100644 index 0000000000..2fab609670 --- /dev/null +++ b/usr/src/cmd/csh/sh.exp.c @@ -0,0 +1,707 @@ +/* + * Copyright 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.tconst.h" + +/* + * C shell + */ + +#define IGNORE 1 /* in ignore, it means to ignore value, just parse */ +#define NOGLOB 2 /* in ignore, it means not to globone */ + +#define ADDOP 1 +#define MULOP 2 +#define EQOP 4 +#define RELOP 8 +#define RESTOP 16 +#define ANYOP 31 + +#define EQEQ 1 +#define GTR 2 +#define LSS 4 +#define NOTEQ 6 +#define EQMATCH 7 +#define NOTEQMATCH 8 + +/* + * Determine if file given by name is accessible with permissions + * given by mode. + * + * Borrowed from the Bourne sh, and modified a bit + * + * If the requested access is permitted, a value of 0 is + * returned. Otherwise, a value of -1 is returned and errno is + * set to indicate the error + */ + +chk_access(path, mode) +register tchar *path; +mode_t mode; +{ + static int flag; + static uid_t euid; + struct stat statb; + mode_t ftype; + unsigned char name[MAXPATHLEN*MB_LEN_MAX]; /* General use buffer. */ + + /* convert tchar * to char * */ + tstostr(name, path); + + if(flag == 0) { + euid = geteuid(); + flag = 1; + } + if (stat((char *)name, &statb) == 0) { + ftype = statb.st_mode & S_IFMT; + if(access((char *)name, 010|(mode>>6)) == 0) { + if(euid == 0) { + if (ftype != S_IFREG || mode != S_IEXEC) + return(0); + /* root can execute file as long as it has execute + permission for someone */ + if (statb.st_mode & (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))) + return(0); + return(-1); + } + return(0); + } + } + return(-1); +} + +exp(vp) + register tchar ***vp; +{ +#ifdef TRACE + tprintf("TRACE- exp()\n"); +#endif + + return (exp0(vp, 0)); +} + +exp0(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register int p1 = exp1(vp, ignore); +#ifdef TRACE + tprintf("TRACE- exp0()\n"); +#endif + +#ifdef EDEBUG + etraci("exp0 p1", p1, vp); +#endif + if (**vp && eq(**vp, S_BARBAR /*"||"*/)) { + register int p2; + + (*vp)++; + p2 = exp0(vp, (ignore&IGNORE) || p1); +#ifdef EDEBUG + etraci("exp0 p2", p2, vp); +#endif + return (p1 || p2); + } + return (p1); +} + +exp1(vp, ignore) + register tchar ***vp; +{ + register int p1 = exp2(vp, ignore); + +#ifdef TRACE + tprintf("TRACE- exp1()\n"); +#endif +#ifdef EDEBUG + etraci("exp1 p1", p1, vp); +#endif + if (**vp && eq(**vp, S_ANDAND /*"&&" */)) { + register int p2; + + (*vp)++; + p2 = exp1(vp, (ignore&IGNORE) || !p1); +#ifdef EDEBUG + etraci("exp1 p2", p2, vp); +#endif + return (p1 && p2); + } + return (p1); +} + +exp2(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register int p1 = exp2a(vp, ignore); + +#ifdef TRACE + tprintf("TRACE- exp2()\n"); +#endif +#ifdef EDEBUG + etraci("exp3 p1", p1, vp); +#endif + if (**vp && eq(**vp, S_BAR /*"|" */)) { + register int p2; + + (*vp)++; + p2 = exp2(vp, ignore); +#ifdef EDEBUG + etraci("exp3 p2", p2, vp); +#endif + return (p1 | p2); + } + return (p1); +} + +exp2a(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register int p1 = exp2b(vp, ignore); + +#ifdef TRACE + tprintf("TRACE- exp2a()\n"); +#endif +#ifdef EDEBUG + etraci("exp2a p1", p1, vp); +#endif + if (**vp && eq(**vp, S_HAT /*"^" */)) { + register int p2; + + (*vp)++; + p2 = exp2a(vp, ignore); +#ifdef EDEBUG + etraci("exp2a p2", p2, vp); +#endif + return (p1 ^ p2); + } + return (p1); +} + +exp2b(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register int p1 = exp2c(vp, ignore); + +#ifdef TRACE + tprintf("TRACE- exp2b()\n"); +#endif +#ifdef EDEBUG + etraci("exp2b p1", p1, vp); +#endif + if (**vp && eq(**vp, S_AND /*"&"*/)) { + register int p2; + + (*vp)++; + p2 = exp2b(vp, ignore); +#ifdef EDEBUG + etraci("exp2b p2", p2, vp); +#endif + return (p1 & p2); + } + return (p1); +} + +exp2c(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register tchar *p1 = exp3(vp, ignore); + register tchar *p2; + register int i; + +#ifdef TRACE + tprintf("TRACE- exp2c()\n"); +#endif +#ifdef EDEBUG + etracc("exp2c p1", p1, vp); +#endif + if (i = isa(**vp, EQOP)) { + (*vp)++; + if (i == EQMATCH || i == NOTEQMATCH) + ignore |= NOGLOB; + p2 = exp3(vp, ignore); +#ifdef EDEBUG + etracc("exp2c p2", p2, vp); +#endif + if (!(ignore&IGNORE)) switch (i) { + + case EQEQ: + i = eq(p1, p2); + break; + + case NOTEQ: + i = !eq(p1, p2); + break; + + case EQMATCH: + i = Gmatch(p1, p2); + break; + + case NOTEQMATCH: + i = !Gmatch(p1, p2); + break; + } + xfree(p1), xfree(p2); + return (i); + } + i = egetn(p1); + xfree(p1); + return (i); +} + +tchar * +exp3(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register tchar *p1, *p2; + register int i; + +#ifdef TRACE + tprintf("TRACE- exp3()\n"); +#endif + p1 = exp3a(vp, ignore); +#ifdef EDEBUG + etracc("exp3 p1", p1, vp); +#endif + if (i = isa(**vp, RELOP)) { + (*vp)++; + if (**vp && eq(**vp, S_EQ /*"=" */)) + i |= 1, (*vp)++; + p2 = exp3(vp, ignore); +#ifdef EDEBUG + etracc("exp3 p2", p2, vp); +#endif + if (!(ignore&IGNORE)) switch (i) { + + case GTR: + i = egetn(p1) > egetn(p2); + break; + + case GTR|1: + i = egetn(p1) >= egetn(p2); + break; + + case LSS: + i = egetn(p1) < egetn(p2); + break; + + case LSS|1: + i = egetn(p1) <= egetn(p2); + break; + } + xfree(p1), xfree(p2); + return (putn(i)); + } + return (p1); +} + +tchar * +exp3a(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register tchar *p1, *p2, *op; + register int i; + +#ifdef TRACE + tprintf("TRACE- exp3a()\n"); +#endif + p1 = exp4(vp, ignore); +#ifdef EDEBUG + etracc("exp3a p1", p1, vp); +#endif + op = **vp; + /* if (op && any(op[0], "<>") && op[0] == op[1]) { */ + if (op && (op[0] == '<' || op[0] == '>') && op[0] == op[1]) { + (*vp)++; + p2 = exp3a(vp, ignore); +#ifdef EDEBUG + etracc("exp3a p2", p2, vp); +#endif + if (op[0] == '<') + i = egetn(p1) << egetn(p2); + else + i = egetn(p1) >> egetn(p2); + xfree(p1), xfree(p2); + return (putn(i)); + } + return (p1); +} + +tchar * +exp4(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register tchar *p1, *p2; + register int i = 0; + +#ifdef TRACE + tprintf("TRACE- exp4()\n"); +#endif + p1 = exp5(vp, ignore); +#ifdef EDEBUG + etracc("exp4 p1", p1, vp); +#endif + if (isa(**vp, ADDOP)) { + register tchar *op = *(*vp)++; + + p2 = exp4(vp, ignore); +#ifdef EDEBUG + etracc("exp4 p2", p2, vp); +#endif + if (!(ignore&IGNORE)) switch (op[0]) { + + case '+': + i = egetn(p1) + egetn(p2); + break; + + case '-': + i = egetn(p1) - egetn(p2); + break; + } + xfree(p1), xfree(p2); + return (putn(i)); + } + return (p1); +} + +tchar * +exp5(vp, ignore) + register tchar ***vp; + bool ignore; +{ + register tchar *p1, *p2; + register int i = 0; + +#ifdef TRACE + tprintf("TRACE- exp5()\n"); +#endif + p1 = exp6(vp, ignore); +#ifdef EDEBUG + etracc("exp5 p1", p1, vp); +#endif + if (isa(**vp, MULOP)) { + register tchar *op = *(*vp)++; + + p2 = exp5(vp, ignore); +#ifdef EDEBUG + etracc("exp5 p2", p2, vp); +#endif + if (!(ignore&IGNORE)) switch (op[0]) { + + case '*': + i = egetn(p1) * egetn(p2); + break; + + case '/': + i = egetn(p2); + if (i == 0) + error("Divide by 0"); + i = egetn(p1) / i; + break; + + case '%': + i = egetn(p2); + if (i == 0) + error("Mod by 0"); + i = egetn(p1) % i; + break; + } + xfree(p1), xfree(p2); + return (putn(i)); + } + return (p1); +} + +tchar * +exp6(vp, ignore) + register tchar ***vp; +{ + int ccode, i; + register tchar *cp, *dp, *ep; + +#ifdef TRACE + tprintf("TRACE- exp6()\n"); +#endif + if (**vp == 0) + bferr("Expression syntax"); + if (eq(**vp, S_EXAS /* "!" */)) { + (*vp)++; + cp = exp6(vp, ignore); +#ifdef EDEBUG + etracc("exp6 ! cp", cp, vp); +#endif + i = egetn(cp); + xfree(cp); + return (putn(!i)); + } + if (eq(**vp, S_TIL /*"~" */)) { + (*vp)++; + cp = exp6(vp, ignore); +#ifdef EDEBUG + etracc("exp6 ~ cp", cp, vp); +#endif + i = egetn(cp); + xfree(cp); + return (putn(~i)); + } + if (eq(**vp, S_LPAR /*"(" */)) { + (*vp)++; + ccode = exp0(vp, ignore); +#ifdef EDEBUG + etraci("exp6 () ccode", ccode, vp); +#endif + if (*vp == 0 || **vp == 0 || ***vp != ')') + bferr("Expression syntax"); + (*vp)++; + return (putn(ccode)); + } + if (eq(**vp, S_LBRA /* "{" */)) { + register tchar **v; + struct command faket; + tchar *fakecom[2]; + + faket.t_dtyp = TCOM; + faket.t_dflg = 0; + faket.t_dcar = faket.t_dcdr = faket.t_dspr = (struct command *)0; + faket.t_dcom = fakecom; + fakecom[0] = S_BRAPPPBRA /*"{ ... }" */; + fakecom[1] = NOSTR; + (*vp)++; + v = *vp; + for (;;) { + if (!**vp) + bferr("Missing }"); + if (eq(*(*vp)++, S_RBRA /*"}" */)) + break; + } + if (ignore&IGNORE) + return (S_ /*""*/); + psavejob(); + if (pfork(&faket, -1) == 0) { + *--(*vp) = 0; + evalav(v); + exitstat(); + } + pwait(); + prestjob(); +#ifdef EDEBUG + etraci("exp6 {} status", egetn(value("status")), vp); +#endif + return (putn(egetn(value(S_status /*"status" */)) == 0)); + } + if (isa(**vp, ANYOP)) + return (S_ /*""*/); + cp = *(*vp)++; + if (*cp == '-' && any(cp[1], S_erwxfdzo /*"erwxfdzo" */)) { + struct stat stb; + + if (cp[2] != '\0') + bferr("Malformed file inquiry"); + + /* + * Detect missing file names by checking for operator + * in the file name position. However, if an operator + * name appears there, we must make sure that there's + * no file by that name (e.g., "/") before announcing + * an error. Even this check isn't quite right, since + * it doesn't take globbing into account. + */ + if (isa(**vp, ANYOP) && stat_(**vp, &stb)) + bferr("Missing file name"); + dp = *(*vp)++; + + if (ignore&IGNORE) + return (S_ /*""*/); + ep = globone(dp); + switch (cp[1]) { + + case 'r': + i = !chk_access(ep, S_IREAD); + break; + + case 'w': + i = !chk_access(ep, S_IWRITE); + break; + + case 'x': + i = !chk_access(ep, S_IEXEC); + break; + + default: + if (stat_(ep, &stb)) { + xfree(ep); + return (S_0 /*"0"*/); + } + switch (cp[1]) { + + case 'f': + i = (stb.st_mode & S_IFMT) == S_IFREG; + break; + + case 'd': + i = (stb.st_mode & S_IFMT) == S_IFDIR; + break; + + case 'z': + i = stb.st_size == 0; + break; + + case 'e': + i = 1; + break; + + case 'o': + i = stb.st_uid == uid; + break; + } + } +#ifdef EDEBUG + etraci("exp6 -? i", i, vp); +#endif + xfree(ep); + return (putn(i)); + } +#ifdef EDEBUG + etracc("exp6 default", cp, vp); +#endif + return (ignore&NOGLOB ? savestr(cp) : globone(cp)); +} + +evalav(v) + register tchar **v; +{ + struct wordent paraml; + register struct wordent *hp = ¶ml; + struct command *t; + register struct wordent *wdp = hp; + +#ifdef TRACE + tprintf("TRACE- evalav()\n"); +#endif + set(S_status /*"status" */, S_0 /*"0"*/); + hp->prev = hp->next = hp; + hp->word = S_ /*""*/; + while (*v) { + register struct wordent *new = (struct wordent *) calloc(1, sizeof *wdp); + + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + wdp->word = savestr(*v++); + } + hp->prev = wdp; + alias(¶ml); + t = syntax(paraml.next, ¶ml, 0); + if (err) + error("%s", gettext(err)); + execute(t, -1); + freelex(¶ml), freesyn(t); +} + +isa(cp, what) + register tchar *cp; + register int what; +{ + +#ifdef TRACE + tprintf("TRACE- isa()\n"); +#endif + if (cp == 0) + return ((what & RESTOP) != 0); + if (cp[1] == 0) { + if (what & ADDOP && (*cp == '+' || *cp == '-')) + return (1); + if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%')) + return (1); + if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' || + *cp == '~' || *cp == '^' || *cp == '"')) + return (1); + } else if (cp[2] == 0) { + if (what & RESTOP) { + if (cp[0] == '|' && cp[1] == '&') + return (1); + if (cp[0] == '<' && cp[1] == '<') + return (1); + if (cp[0] == '>' && cp[1] == '>') + return (1); + } + if (what & EQOP) { + if (cp[0] == '=') { + if (cp[1] == '=') + return (EQEQ); + if (cp[1] == '~') + return (EQMATCH); + } else if (cp[0] == '!') { + if (cp[1] == '=') + return (NOTEQ); + if (cp[1] == '~') + return (NOTEQMATCH); + } + } + } + if (what & RELOP) { + if (*cp == '<') + return (LSS); + if (*cp == '>') + return (GTR); + } + return (0); +} + +egetn(cp) + register tchar *cp; +{ + +#ifdef TRACE + tprintf("TRACE- egetn()\n"); +#endif + if (*cp && *cp != '-' && !digit(*cp)) + bferr("Expression syntax"); + return (getn(cp)); +} + +/* Phew! */ + +#ifdef EDEBUG +etraci(str, i, vp) + tchar *str; + int i; + tchar ***vp; +{ + + printf("%s=%d\t", str, i); + blkpr(*vp); + printf("\n"); +} + +etracc(str, cp, vp) + tchar *str, *cp; + tchar ***vp; +{ + + printf("%s=%s\t", str, cp); + blkpr(*vp); + printf("\n"); +} +#endif diff --git a/usr/src/cmd/csh/sh.file.c b/usr/src/cmd/csh/sh.file.c new file mode 100644 index 0000000000..af9e543c52 --- /dev/null +++ b/usr/src/cmd/csh/sh.file.c @@ -0,0 +1,821 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef FILEC +/* + * Tenex style file name recognition, .. and more. + * History: + * Author: Ken Greer, Sept. 1975, CMU. + * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. + */ + +#include "sh.h" +#include <sys/types.h> +#include <dirent.h> +#include <pwd.h> +#include "sh.tconst.h" + +#define TRUE 1 +#define FALSE 0 +#define ON 1 +#define OFF 0 + +#define ESC '\033' + +extern DIR *opendir_(); + +static char *BELL = "\07"; +static char *CTRLR = "^R\n"; + +typedef enum {LIST, RECOGNIZE} COMMAND; + +static jmp_buf osetexit; /* saved setexit() state */ +static struct termios tty_save; /* saved terminal state */ +static struct termios tty_new; /* new terminal state */ + +/* + * Put this here so the binary can be patched with adb to enable file + * completion by default. Filec controls completion, nobeep controls + * ringing the terminal bell on incomplete expansions. + */ +bool filec = 0; + +static +setup_tty(on) + int on; +{ + int omask; +#ifdef TRACE + tprintf("TRACE- setup_tty()\n"); +#endif + + omask = sigblock(sigmask(SIGINT)); + if (on) { + /* + * The shell makes sure that the tty is not in some weird state + * and fixes it if it is. But it should be noted that the + * tenex routine will not work correctly in CBREAK or RAW mode + * so this code below is, therefore, mandatory. + * + * Also, in order to recognize the ESC (filename-completion) + * character, set EOL to ESC. This way, ESC will terminate + * the line, but still be in the input stream. + * EOT (filename list) will also terminate the line, + * but will not appear in the input stream. + * + * The getexit/setexit contortions ensure that the + * tty state will be restored if the user types ^C. + */ + (void) ioctl(SHIN, TCGETS, (char *)&tty_save); + getexit(osetexit); + if (setjmp(reslab)) { + (void) ioctl(SHIN, TCSETSW, (char *)&tty_save); + resexit(osetexit); + reset(); + } + tty_new = tty_save; + tty_new.c_cc[VEOL] = ESC; + tty_new.c_iflag |= IMAXBEL | BRKINT | IGNPAR; + tty_new.c_lflag |= ICANON; + tty_new.c_lflag |= ECHOCTL; + tty_new.c_oflag &= ~OCRNL; + (void) ioctl(SHIN, TCSETSW, (char *)&tty_new); + } else { + /* + * Reset terminal state to what user had when invoked + */ + (void) ioctl(SHIN, TCSETSW, (char *)&tty_save); + resexit(osetexit); + } + (void) sigsetmask(omask); +} + +static +termchars() +{ + extern char *tgetstr(); + char bp[1024]; + static char area[256]; + static int been_here = 0; + char *ap = area; + register char *s; + char *term; + +#ifdef TRACE + tprintf("TRACE- termchars()\n"); +#endif + if (been_here) + return; + been_here = TRUE; + + if ((term = getenv("TERM")) == NULL) + return; + if (tgetent(bp, term) != 1) + return; + if (s = tgetstr("vb", &ap)) /* Visible Bell */ + BELL = s; +} + +/* + * Move back to beginning of current line + */ +static +back_to_col_1() +{ + int omask; + +#ifdef TRACE + tprintf("TRACE- back_to_col_1()\n"); +#endif + omask = sigblock(sigmask(SIGINT)); + (void) write(SHOUT, "\r", 1); + (void) sigsetmask(omask); +} + +/* + * Push string contents back into tty queue + */ +static +pushback(string, echoflag) + tchar *string; + int echoflag; +{ + register tchar *p; + struct termios tty; + int omask; + +#ifdef TRACE + tprintf("TRACE- pushback()\n"); +#endif + omask = sigblock(sigmask(SIGINT)); + tty = tty_new; + if (!echoflag) + tty.c_lflag &= ~ECHO; + (void) ioctl(SHIN, TCSETSF, (char *)&tty); + + for (p = string; *p; p++){ + char mbc[MB_LEN_MAX]; + int i, j = wctomb(mbc, (wchar_t)*p); + + if (j < 0) { + /* Error! But else what can we do? */ + continue; + } + for (i = 0; i < j; ++i) { + /* XXX: no error recovery provision. */ + (void) ioctl(SHIN, TIOCSTI, mbc + i); + } + } + + if (tty.c_lflag != tty_new.c_lflag) + (void) ioctl(SHIN, TCSETS, (char *)&tty_new); + (void) sigsetmask(omask); +} + +/* + * Concatenate src onto tail of des. + * Des is a string whose maximum length is count. + * Always null terminate. + */ +catn(des, src, count) + register tchar *des, *src; + register count; +{ +#ifdef TRACE + tprintf("TRACE- catn()\n"); +#endif + + while (--count >= 0 && *des) + des++; + while (--count >= 0) + if ((*des++ = *src++) == '\0') + return; + *des = '\0'; +} + +static +max(a, b) +{ + + return (a > b ? a : b); +} + +/* + * Like strncpy but always leave room for trailing \0 + * and always null terminate. + */ +copyn(des, src, count) + register tchar *des, *src; + register count; +{ + +#ifdef TRACE + tprintf("TRACE- copyn()\n"); +#endif + while (--count >= 0) + if ((*des++ = *src++) == '\0') + return; + *des = '\0'; +} + +/* + * For qsort() + */ +static +fcompare(file1, file2) + tchar **file1, **file2; +{ + +#ifdef TRACE + tprintf("TRACE- fcompare()\n"); +#endif + return (strcoll_(*file1, *file2)); +} + +static char +filetype(dir, file, nosym) + tchar *dir, *file; + int nosym; +{ + tchar path[MAXPATHLEN + 1]; + struct stat statb; + +#ifdef TRACE + tprintf("TRACE- filetype()\n"); +#endif + if (dir) { + catn(strcpy_(path, dir), file, MAXPATHLEN); + if (nosym) { + if (stat_(path, &statb) < 0) + return (' '); + } else { + if (lstat_(path, &statb) < 0) + return (' '); + } + if ((statb.st_mode & S_IFMT) == S_IFLNK) + return ('@'); + if ((statb.st_mode & S_IFMT) == S_IFDIR) + return ('/'); + if (((statb.st_mode & S_IFMT) == S_IFREG) && + (statb.st_mode & 011)) + return ('*'); + } + return (' '); +} + +/* + * Print sorted down columns + */ +static +print_by_column(dir, items, count, looking_for_command) + tchar *dir, *items[]; + int looking_for_command; +{ + register int i, rows, r, c, maxwidth = 0, columns; + +#ifdef TRACE + tprintf("TRACE- print_by_column()\n"); +#endif + for (i = 0; i < count; i++) + maxwidth = max(maxwidth, tswidth(items[i])); + + /* for the file tag and space */ + maxwidth += looking_for_command ? 1 : 2; + columns = max(78 / maxwidth, 1); + rows = (count + (columns - 1)) / columns; + + for (r = 0; r < rows; r++) { + for (c = 0; c < columns; c++) { + i = c * rows + r; + if (i < count) { + register int w; + + /* + * Print filename followed by + * '@' or '/' or '*' or ' ' + */ + printf("%t", items[i]); + w = tswidth(items[i]); + if (!looking_for_command) { + printf("%c", + (tchar) filetype(dir, items[i], 0)); + w++; + } + if (c < columns - 1) /* last column? */ + for (; w < maxwidth; w++) + printf(" "); + } + } + printf("\n"); + } +} + +/* + * Expand file name with possible tilde usage + * ~person/mumble + * expands to + * home_directory_of_person/mumble + */ +tchar * +tilde(new, old) + tchar *new, *old; +{ + register tchar *o, *p; + register struct passwd *pw; + static tchar person[40]; + char person_[40]; /* work */ + tchar *pw_dir; /* work */ + +#ifdef TRACE + tprintf("TRACE- tilde()\n"); +#endif + if (old[0] != '~') + return (strcpy_(new, old)); + + for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) + ; + *p = '\0'; + if (person[0] == '\0') + (void) strcpy_(new, value(S_home /*"home"*/)); + else { + pw = getpwnam(tstostr(person_,person)); + if (pw == NULL) + return (NULL); + pw_dir = strtots((tchar *)NULL, pw->pw_dir); /* allocate */ + (void) strcpy_(new, pw_dir); + xfree(pw_dir); /* free it */ + } + (void) strcat_(new, o); + return (new); +} + +/* + * Cause pending line to be printed + */ +static +sim_retype() +{ +#ifdef notdef + struct termios tty_pending; + +#ifdef TRACE + tprintf("TRACE- sim_retypr()\n"); +#endif + tty_pending = tty_new; + tty_pending.c_lflag |= PENDIN; + + (void) ioctl(SHIN, TCSETS, (char *)&tty_pending); +#else +#ifdef TRACE + tprintf("TRACE- sim_retype()\n"); +#endif + (void) write(SHOUT, CTRLR, strlen(CTRLR)); + printprompt(); +#endif +} + +static +beep_outc (c) { + char buf[1]; + + buf[0] = c; + + (void) write (SHOUT, buf, 1); + + return 0; +} + +static +beep() +{ + +#ifdef TRACE + tprintf("TRACE- beep()\n"); +#endif + if (adrof(S_nobeep /*"nobeep" */) == 0) + (void) tputs (BELL, 0, beep_outc); +} + +/* + * Erase that silly ^[ and print the recognized part of the string. + */ +static +print_recognized_stuff(recognized_part) + tchar *recognized_part; +{ + int unit = didfds ? 1 : SHOUT; + +#ifdef TRACE + tprintf("TRACE- print_recognized_stuff()\n"); +#endif + + /* + * An optimized erasing of that silly ^[ + * + * One would think that line speeds have become fast enough that this + * isn't necessary, but it turns out that the visual difference is + * quite noticeable. + */ + flush(); + switch (tswidth(recognized_part)) { + case 0: + /* erase two characters: ^[ */ + write(unit, "\b\b \b\b", sizeof "\b\b \b\b" - 1); + break; + + case 1: + /* overstrike the ^, erase the [ */ + write(unit, "\b\b", 2); + printf("%t", recognized_part); + write(unit, " \b\b", 4); + break; + + default: + /* overstrike both characters ^[ */ + write(unit, "\b\b", 2); + printf("%t", recognized_part); + break; + } + flush(); +} + +/* + * Parse full path in file into 2 parts: directory and file names + * Should leave final slash (/) at end of dir. + */ +static +extract_dir_and_name(path, dir, name) + tchar *path, *dir, *name; +{ + register tchar *p; + +#ifdef TRACE + tprintf("TRACE- extract_dir_and_name()\n"); +#endif + p = rindex_(path, '/'); + if (p == NOSTR) { + copyn(name, path, MAXNAMLEN); + dir[0] = '\0'; + } else { + copyn(name, ++p, MAXNAMLEN); + copyn(dir, path, p - path); + } +} + +tchar * +getentry(dir_fd, looking_for_lognames) + DIR *dir_fd; +{ + register struct passwd *pw; + register struct dirent *dirp; + /* + * For char * -> tchar * Conversion + */ + static tchar strbuf[MAXNAMLEN+1]; + +#ifdef TRACE + tprintf("TRACE- getentry()\n"); +#endif + if (looking_for_lognames) { + if ((pw = getpwent ()) == NULL) + return (NULL); + return (strtots(strbuf,pw->pw_name)); + } + if (dirp = readdir(dir_fd)) + return (strtots(strbuf,dirp->d_name)); + return (NULL); +} + +static +free_items(items) + register tchar **items; +{ + register int i; + +#ifdef TRACE + tprintf("TRACE- free_items()\n"); +#endif + for (i = 0; items[i]; i++) + free(items[i]); + free( (char *)items); +} + +#define FREE_ITEMS(items) { \ + int omask;\ +\ + omask = sigblock(sigmask(SIGINT));\ + free_items(items);\ + items = NULL;\ + (void) sigsetmask(omask);\ +} + +/* + * Perform a RECOGNIZE or LIST command on string "word". + */ +static +search2(word, command, max_word_length) + tchar *word; + COMMAND command; +{ + static tchar **items = NULL; + register DIR *dir_fd; + register numitems = 0, ignoring = TRUE, nignored = 0; + register name_length, looking_for_lognames; + tchar tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; + tchar name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1]; + tchar *entry; +#define MAXITEMS 1024 +#ifdef TRACE + tprintf("TRACE- search2()\n"); +#endif + + if (items != NULL) + FREE_ITEMS(items); + + looking_for_lognames = (*word == '~') && (index_(word, '/') == NULL); + if (looking_for_lognames) { + (void) setpwent(); + copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ + } else { + extract_dir_and_name(word, dir, name); + if (tilde(tilded_dir, dir) == 0) + return (0); + dir_fd = opendir_(*tilded_dir ? tilded_dir : S_DOT /*"."*/); + if (dir_fd == NULL) + return (0); + } + +again: /* search for matches */ + name_length = strlen_(name); + for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) { + if (!is_prefix(name, entry)) + continue; + /* Don't match . files on null prefix match */ + if (name_length == 0 && entry[0] == '.' && + !looking_for_lognames) + continue; + if (command == LIST) { + if (numitems >= MAXITEMS) { + printf ("\nYikes!! Too many %s!!\n", + looking_for_lognames ? + "names in password file":"files"); + break; + } + if (items == NULL) + items = (tchar **) calloc(sizeof (items[1]), + MAXITEMS+1); + items[numitems] = (tchar *)xalloc((unsigned)(strlen_(entry) + 1)*sizeof(tchar)); + copyn(items[numitems], entry, MAXNAMLEN); + numitems++; + } else { /* RECOGNIZE command */ + if (ignoring && ignored(entry)) + nignored++; + else if (recognize(extended_name, + entry, name_length, ++numitems)) + break; + } + } + if (ignoring && numitems == 0 && nignored > 0) { + ignoring = FALSE; + nignored = 0; + if (looking_for_lognames) + (void)setpwent(); + else + rewinddir(dir_fd); + goto again; + } + + if (looking_for_lognames) + (void) endpwent(); + else { + unsetfd(dir_fd->dd_fd); + closedir_(dir_fd); + } + if (command == RECOGNIZE && numitems > 0) { + if (looking_for_lognames) + copyn(word, S_TIL /*"~" */, 1); + else + /* put back dir part */ + copyn(word, dir, max_word_length); + /* add extended name */ + catn(word, extended_name, max_word_length); + return (numitems); + } + if (command == LIST) { + qsort( (char *)items, numitems, sizeof(items[1]), + (int (*)(const void *, const void *))fcompare); + /* + * Never looking for commands in this version, so final + * argument forced to 0. If command name completion is + * reinstated, this must change. + */ + print_by_column(looking_for_lognames ? NULL : tilded_dir, + items, numitems, 0); + if (items != NULL) + FREE_ITEMS(items); + } + return (0); +} + +/* + * Object: extend what user typed up to an ambiguity. + * Algorithm: + * On first match, copy full entry (assume it'll be the only match) + * On subsequent matches, shorten extended_name to the first + * character mismatch between extended_name and entry. + * If we shorten it back to the prefix length, stop searching. + */ +recognize(extended_name, entry, name_length, numitems) + tchar *extended_name, *entry; +{ + +#ifdef TRACE + tprintf("TRACE- recognize()\n"); +#endif + if (numitems == 1) /* 1st match */ + copyn(extended_name, entry, MAXNAMLEN); + else { /* 2nd and subsequent matches */ + register tchar *x, *ent; + register int len = 0; + + x = extended_name; + for (ent = entry; *x && *x == *ent++; x++, len++) + ; + *x = '\0'; /* Shorten at 1st char diff */ + if (len == name_length) /* Ambiguous to prefix? */ + return (-1); /* So stop now and save time */ + } + return (0); +} + +/* + * Return true if check items initial chars in template + * This differs from PWB imatch in that if check is null + * it items anything + */ +static +is_prefix(check, template) + register tchar *check, *template; +{ +#ifdef TRACE + tprintf("TRACE- is_prefix()\n"); +#endif + + do + if (*check == 0) + return (TRUE); + while (*check++ == *template++); + return (FALSE); +} + +/* + * Return true if the chars in template appear at the + * end of check, i.e., are its suffix. + */ +static +is_suffix(check, template) + tchar *check, *template; +{ + register tchar *c, *t; + +#ifdef TRACE + tprintf("TRACE- is_suffix()\n"); +#endif + for (c = check; *c++;) + ; + for (t = template; *t++;) + ; + for (;;) { + if (t == template) + return (TRUE); + if (c == check || *--t != *--c) + return (FALSE); + } +} + +tenex(inputline, inputline_size) + tchar *inputline; + int inputline_size; +{ + register int numitems, num_read, should_retype; + int i; + +#ifdef TRACE + tprintf("TRACE- tenex()\n"); +#endif + setup_tty(ON); + termchars(); + num_read = 0; + should_retype = FALSE; + while ((i = read_(SHIN, inputline+num_read, inputline_size-num_read)) + > 0) { + static tchar *delims = S_DELIM /*" '\"\t;&<>()|`"*/; + register tchar *str_end, *word_start, last_char; + register int space_left; + struct termios tty; + COMMAND command; + + num_read += i; + inputline[num_read] = '\0'; + last_char = inputline[num_read - 1] & TRIM; + + if ((num_read == inputline_size) || (last_char == '\n')) + break; + + str_end = &inputline[num_read]; + if (last_char == ESC) { + command = RECOGNIZE; + *--str_end = '\0'; /* wipe out trailing ESC */ + } else + command = LIST; + + tty = tty_new; + tty.c_lflag &= ~ECHO; + (void) ioctl(SHIN, TCSETSF, (char *)&tty); + + if (command == LIST) + printf("\n"); + /* + * Find LAST occurence of a delimiter in the inputline. + * The word start is one character past it. + */ + for (word_start = str_end; word_start > inputline; + --word_start) { + if (index_(delims, word_start[-1]) || + isauxsp(word_start[-1])) + break; + } + space_left = inputline_size - (word_start - inputline) - 1; + numitems = search2(word_start, command, space_left); + + /* + * Tabs in the input line cause trouble after a pushback. + * tty driver won't backspace over them because column + * positions are now incorrect. This is solved by retyping + * over current line. + */ + if (index_(inputline, '\t')) { /* tab tchar in input line? */ + back_to_col_1(); + should_retype = TRUE; + } + if (command == LIST) /* Always retype after a LIST */ + should_retype = TRUE; + if (should_retype) + printprompt(); + pushback(inputline, should_retype); + num_read = 0; /* chars will be reread */ + should_retype = FALSE; + + /* + * Avoid a race condition by echoing what we're recognized + * _after_ pushing back the command line. This way, if the + * user waits until seeing this output before typing more + * stuff, the resulting keystrokes won't race with the STIed + * input we've pushed back. (Of course, if the user types + * ahead, the race still exists and it's quite possible that + * the pushed back input line will interleave with the + * keystrokes in unexpected ways.) + */ + if (command == RECOGNIZE) { + /* print from str_end on */ + print_recognized_stuff(str_end); + if (numitems != 1) /* Beep = No match/ambiguous */ + beep(); + } + } + setup_tty(OFF); + return (num_read); +} + +static +ignored(entry) + register tchar *entry; +{ + struct varent *vp; + register tchar **cp; + +#ifdef TRACE + tprintf("TRACE- ignored()\n"); +#endif + if ((vp = adrof(S_fignore /*"fignore"*/)) == NULL || + (cp = vp->vec) == NULL) + return (FALSE); + for (; *cp != NULL; cp++) + if (is_suffix(entry, *cp)) + return (TRUE); + return (FALSE); +} +#endif FILEC diff --git a/usr/src/cmd/csh/sh.func.c b/usr/src/cmd/csh/sh.func.c new file mode 100644 index 0000000000..c69f8882e1 --- /dev/null +++ b/usr/src/cmd/csh/sh.func.c @@ -0,0 +1,1645 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include <locale.h> /* For LC_ALL */ +#include "sh.tconst.h" +#include <sys/types.h> +#include <stdlib.h> + +/* + * N.B.: Some of the limits change from SunOS 4.x to SunOS 5.0. In + * particular, RLIMIT_RSS is gone and RLIMIT_VMEM is new. Beware of consusing + * the keywords that the command prints for these two. The old one was + * "memoryuse" and the new one is "memorysize". Note also that a given limit + * doesn't necessarily appear in the same position in the two releases. + */ +struct limits { + int limconst; + tchar *limname; + int limdiv; + tchar *limscale; +} limits[] = { + RLIMIT_CPU, S_cputime, /* "cputime" */ + 1, S_seconds, /* "seconds" */ + RLIMIT_FSIZE, S_filesize, /* "filesize" */ + 1024, S_kbytes, /* "kbytes" */ + RLIMIT_DATA, S_datasize, /* "datasize" */ + 1024, S_kbytes, /* "kbytes" */ + RLIMIT_STACK, S_stacksize, /* "stacksize" */ + 1024, S_kbytes, /* "kbytes" */ + RLIMIT_CORE, S_coredumpsize, /* "coredumpsize" */ + 1024, S_kbytes, /* "kbytes" */ + RLIMIT_NOFILE, S_descriptors, /* "descriptors" */ + 1, S_, /* "" */ + RLIMIT_VMEM, S_memorysize, /* "memorysize" */ + 1024, S_kbytes, /* "kbytes" */ + -1, 0, +}; + +static int getval(struct limits *lp, tchar **v, rlim_t *); +void islogin(); +int dolabel(); +void reexecute(struct command *kp); +void preread_(); +void doagain(); +void toend(); +void wfree(); +void echo(tchar sep, tchar **v); +void local_setenv(tchar *name, tchar *val); +void local_unsetenv(tchar *name); +void limtail(tchar *cp, tchar *str0); +void plim(struct limits *lp, tchar hard); +void search(); + +#define BUFSZ 1028 + +/* + * C shell + */ + +struct +biltins * +isbfunc(struct command *t) +{ + tchar *cp = t->t_dcom[0]; + struct biltins *bp, *bp1, *bp2; + int dofg1(), dobg1(); + + static struct biltins label = { S_, dolabel, 0, 0 }; + static struct biltins foregnd = { S_Pjob, dofg1, 0, 0 }; + static struct biltins backgnd = { S_PjobAND, dobg1, 0, 0 }; +#ifdef TRACE + tprintf("TRACE- isbfunc()\n"); +#endif + if (lastchr(cp) == ':') { + label.bname = cp; + return (&label); + } + if (*cp == '%') { + if (t->t_dflg & FAND) { + t->t_dflg &= ~FAND; + backgnd.bname = cp; + return (&backgnd); + } + foregnd.bname = cp; + return (&foregnd); + } + /* + * Binary search + * Bp1 is the beginning of the current search range. + * Bp2 is one past the end. + */ + for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2; ) { + int i; + + bp = bp1 + (bp2 - bp1 >> 1); + if ((i = *cp - *bp->bname) == 0 && + (i = strcmp_(cp, bp->bname)) == 0) { + return (bp); + } + if (i < 0) { + bp2 = bp; + } else { + bp1 = bp + 1; + } + } + return (0); +} + +void +func(struct command *t, struct biltins *bp) +{ + int i; + +#ifdef TRACE + tprintf("TRACE- func()\n"); +#endif + xechoit(t->t_dcom); + setname(bp->bname); + i = blklen(t->t_dcom) - 1; + if (i < bp->minargs) { + bferr("Too few arguments"); + } + if (i > bp->maxargs) { + bferr("Too many arguments"); + } + (*bp->bfunct)(t->t_dcom, t); +} + +int +dolabel() +{ +#ifdef TRACE + tprintf("TRACE- dolabel()\n"); +#endif + +} + +void +doonintr(tchar **v) +{ + tchar *cp; + tchar *vv = v[1]; + +#ifdef TRACE + tprintf("TRACE- doonintr()\n"); +#endif + if (parintr == SIG_IGN) { + return; + } + if (setintr && intty) { + bferr("Can't from terminal"); + } + cp = gointr, gointr = 0, xfree(cp); + if (vv == 0) { + if (setintr) { + (void) sigblock(sigmask(SIGINT)); + } else { + (void) signal(SIGINT, SIG_DFL); + } + gointr = 0; + } else if (eq((vv = strip(vv)), S_MINUS)) { + (void) signal(SIGINT, SIG_IGN); + gointr = S_MINUS; + } else { + gointr = savestr(vv); + (void) signal(SIGINT, pintr); + } +} + +void +donohup() +{ + +#ifdef TRACE + tprintf("TRACE- donohup()\n"); +#endif + if (intty) { + bferr("Can't from terminal"); + } + if (setintr == 0) { + (void) signal(SIGHUP, SIG_IGN); +#ifdef CC + submit(getpid()); +#endif + } +} + +void +dozip() +{ + ; +} + +void +prvars() +{ +#ifdef TRACE + tprintf("TRACE- prvars()\n"); +#endif + + plist(&shvhed); +} + +void +doalias(tchar **v) +{ + struct varent *vp; + tchar *p; + +#ifdef TRACE + tprintf("TRACE- doalias()\n"); +#endif + v++; + p = *v++; + if (p == 0) { + plist(&aliases); + } else if (*v == 0) { + vp = adrof1(strip(p), &aliases); + if (vp) { + blkpr(vp->vec), printf("\n"); + } + } else { + if (eq(p, S_alias) || + eq(p, S_unalias)) { + setname(p); + bferr("Too dangerous to alias that"); + } + set1(strip(p), saveblk(v), &aliases); + } +} + +void +unalias(tchar **v) +{ + +#ifdef TRACE + tprintf("TRACE- unalias()\n"); +#endif + unset1(v, &aliases); +} + +void +dologout() +{ + +#ifdef TRACE + tprintf("TRACE- dologout()\n"); +#endif + islogin(); + goodbye(); +} + +void +dologin(tchar **v) +{ + + char *v_; /* work */ +#ifdef TRACE + tprintf("TRACE- dologin()\n"); +#endif + islogin(); + rechist(); + (void) signal(SIGTERM, parterm); + if (v[1] != NULL) { + v_ = tstostr(NULL, v[1]); /* No need to free */ + } else { + v_ = 0; + } + execl("/bin/login", "login", v_, 0); + untty(); + exit(1); +} + +#ifdef NEWGRP +void +donewgrp(tchar **v) +{ + + char *v_; /* work */ +#ifdef TRACE + tprintf("TRACE- donewgrp()\n"); +#endif + if (chkstop == 0 && setintr) { + panystop(0); + } + (void) signal(SIGTERM, parterm); + + if (v[1] != NULL) { + v_ = tstostr(NOSTR, v[1]); /* No need to free */ + } else { + v_ = 0; + } + execl("/bin/newgrp", "newgrp", v_, 0); + execl("/usr/bin/newgrp", "newgrp", v_, 0); + untty(); + exit(1); +} +#endif + +void +islogin() +{ + +#ifdef TRACE + tprintf("TRACE- islogin()\n"); +#endif + if (chkstop == 0 && setintr) { + panystop(0); + } + if (loginsh) { + return; + } + error("Not login shell"); +} + +void +doif(tchar **v, struct command *kp) +{ + int i; + tchar **vv; + +#ifdef TRACE + tprintf("TRACE- doif()\n"); +#endif + v++; + i = exp(&v); + vv = v; + if (*vv == NOSTR) { + bferr("Empty if"); + } + if (eq(*vv, S_then)) { + if (*++vv) { + bferr("Improper then"); + } + setname(S_then); + /* + * If expression was zero, then scan to else, + * otherwise just fall into following code. + */ + if (!i) { + search(ZIF, 0); + } + return; + } + /* + * Simple command attached to this if. + * Left shift the node in this tree, munging it + * so we can reexecute it. + */ + if (i) { + lshift(kp->t_dcom, vv - kp->t_dcom); + reexecute(kp); + donefds(); + } +} + +/* + * Reexecute a command, being careful not + * to redo i/o redirection, which is already set up. + */ +void +reexecute(struct command *kp) +{ + +#ifdef TRACE + tprintf("TRACE- reexecute()\n"); +#endif + kp->t_dflg &= FSAVE; + kp->t_dflg |= FREDO; + /* + * If tty is still ours to arbitrate, arbitrate it; + * otherwise dont even set pgrp's as the jobs would + * then have no way to get the tty (we can't give it + * to them, and our parent wouldn't know their pgrp, etc. + */ + execute(kp, tpgrp > 0 ? tpgrp : -1); +} + +void +doelse() +{ + +#ifdef TRACE + tprintf("TRACE- doelse()\n"); +#endif + search(ZELSE, 0); +} + +void +dogoto(tchar **v) +{ + struct whyle *wp; + tchar *lp; +#ifdef TRACE + tprintf("TRACE- dogoto()\n"); +#endif + + /* + * While we still can, locate any unknown ends of existing loops. + * This obscure code is the WORST result of the fact that we + * don't really parse. + */ + for (wp = whyles; wp; wp = wp->w_next) { + if (wp->w_end == 0) { + search(ZBREAK, 0); + wp->w_end = btell(); + } else { + bseek(wp->w_end); + } + } + search(ZGOTO, 0, lp = globone(v[1])); + xfree(lp); + /* + * Eliminate loops which were exited. + */ + wfree(); +} + +void +doswitch(tchar **v) +{ + tchar *cp, *lp; + +#ifdef TRACE + tprintf("TRACE- doswitch()\n"); +#endif + v++; + if (!*v || *(*v++) != '(') { + goto syntax; + } + cp = **v == ')' ? S_ : *v++; + if (*(*v++) != ')') { + v--; + } + if (*v) { +syntax: + error("Syntax error"); + } + search(ZSWITCH, 0, lp = globone(cp)); + xfree(lp); +} + +void +dobreak() +{ + +#ifdef TRACE + tprintf("TRACE- dobreak()\n"); +#endif + if (whyles) { + toend(); + } else { + bferr("Not in while/foreach"); + } +} + +void +doexit(tchar **v) +{ + +#ifdef TRACE + tprintf("TRACE- doexit()\n"); +#endif + if (chkstop == 0) { + panystop(0); + } + /* + * Don't DEMAND parentheses here either. + */ + v++; + if (*v) { + set(S_status, putn(exp(&v))); + if (*v) { + bferr("Expression syntax"); + } + } + btoeof(); + if (intty) { + (void) close(SHIN); + unsetfd(SHIN); + } +} + +void +doforeach(tchar **v) +{ + tchar *cp; + struct whyle *nwp; + +#ifdef TRACE + tprintf("TRACE- doforeach()\n"); +#endif + v++; + cp = strip(*v); + while (*cp && alnum(*cp)) { + cp++; + } + if (*cp || strlen_(*v) >= 20 || !letter(**v)) { + bferr("Invalid variable"); + } + cp = *v++; + if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') { + bferr("Words not ()'ed"); + } + v++; + gflag = 0, tglob(v); + v = glob(v); + if (v == 0) { + bferr("No match"); + } + nwp = (struct whyle *)calloc(1, sizeof (*nwp)); + nwp->w_fe = nwp->w_fe0 = v; gargv = 0; + nwp->w_start = btell(); + nwp->w_fename = savestr(cp); + nwp->w_next = whyles; + whyles = nwp; + /* + * Pre-read the loop so as to be more + * comprehensible to a terminal user. + */ + if (intty) { + preread_(); + } + doagain(); +} + +void +dowhile(tchar **v) +{ + int status; + bool again = whyles != 0 && whyles->w_start == lineloc && + whyles->w_fename == 0; + +#ifdef TRACE + tprintf("TRACE- dowhile()\n"); +#endif + v++; + /* + * Implement prereading here also, taking care not to + * evaluate the expression before the loop has been read up + * from a terminal. + */ + if (intty && !again) { + status = !exp0(&v, 1); + } else { + status = !exp(&v); + } + if (*v) { + bferr("Expression syntax"); + } + if (!again) { + struct whyle *nwp = (struct whyle *)calloc(1, sizeof (*nwp)); + + nwp->w_start = lineloc; + nwp->w_end = 0; + nwp->w_next = whyles; + whyles = nwp; + if (intty) { + /* + * The tty preread + */ + preread_(); + doagain(); + return; + } + } + if (status) { + /* We ain't gonna loop no more, no more! */ + toend(); + } +} + +void +preread_() +{ +#ifdef TRACE + tprintf("TRACE- preread()\n"); +#endif + + whyles->w_end = -1; + if (setintr) { + (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); + } + search(ZBREAK, 0); + if (setintr) { + (void) sigblock(sigmask(SIGINT)); + } + whyles->w_end = btell(); +} + +void +doend() +{ + +#ifdef TRACE + tprintf("TRACE- doend()\n"); +#endif + if (!whyles) { + bferr("Not in while/foreach"); + } + whyles->w_end = btell(); + doagain(); +} + +void +docontin() +{ +#ifdef TRACE + tprintf("TRACE- docontin()\n"); +#endif + + if (!whyles) { + bferr("Not in while/foreach"); + } + doagain(); +} + +void +doagain() +{ + +#ifdef TRACE + tprintf("TRACE- doagain()\n"); +#endif + /* Repeating a while is simple */ + if (whyles->w_fename == 0) { + bseek(whyles->w_start); + return; + } + /* + * The foreach variable list actually has a spurious word + * ")" at the end of the w_fe list. Thus we are at the + * of the list if one word beyond this is 0. + */ + if (!whyles->w_fe[1]) { + dobreak(); + return; + } + set(whyles->w_fename, savestr(*whyles->w_fe++)); + bseek(whyles->w_start); +} + +void +dorepeat(tchar **v, struct command *kp) +{ + int i, omask; + +#ifdef TRACE + tprintf("TRACE- dorepeat()\n"); +#endif + i = getn(v[1]); + if (setintr) { + omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT); + } + lshift(v, 2); + while (i > 0) { + if (setintr) { + (void) sigsetmask(omask); + } + reexecute(kp); + --i; + } + donefds(); + if (setintr) { + (void) sigsetmask(omask); + } +} + +void +doswbrk() +{ + +#ifdef TRACE + tprintf("TRACE- doswbrk()\n"); +#endif + search(ZBRKSW, 0); +} + +int +srchx(tchar *cp) +{ + struct srch *sp, *sp1, *sp2; + int i; + +#ifdef TRACE + tprintf("TRACE- srchx()\n"); +#endif + /* + * Binary search + * Sp1 is the beginning of the current search range. + * Sp2 is one past the end. + */ + for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2; ) { + sp = sp1 + (sp2 - sp1 >> 1); + if ((i = *cp - *sp->s_name) == 0 && + (i = strcmp_(cp, sp->s_name)) == 0) { + return (sp->s_value); + } + if (i < 0) { + sp2 = sp; + } else { + sp1 = sp + 1; + } + } + return (-1); +} + +tchar Stype; +tchar *Sgoal; + +/*VARARGS2*/ +void +search(type, level, goal) + int type; int level; tchar *goal; +{ + tchar wordbuf[BUFSIZ]; + tchar *aword = wordbuf; + tchar *cp; + +#ifdef TRACE + tprintf("TRACE- search()\n"); +#endif + Stype = type; Sgoal = goal; + if (type == ZGOTO) { + bseek((off_t)0); + } + do { + if (intty && fseekp == feobp) { + printf("? "), flush(); + } + aword[0] = 0; + (void) getword(aword); + + switch (srchx(aword)) { + + case ZELSE: + if (level == 0 && type == ZIF) { + return; + } + break; + + case ZIF: + while (getword(aword)) { + continue; + } + if ((type == ZIF || type == ZELSE) && + eq(aword, S_then)) { + level++; + } + break; + + case ZENDIF: + if (type == ZIF || type == ZELSE) { + level--; + } + break; + + case ZFOREACH: + case ZWHILE: + if (type == ZBREAK) { + level++; + } + break; + + case ZEND: + if (type == ZBREAK) { + level--; + } + break; + + case ZSWITCH: + if (type == ZSWITCH || type == ZBRKSW) { + level++; + } + break; + + case ZENDSW: + if (type == ZSWITCH || type == ZBRKSW) { + level--; + } + break; + + case ZLABEL: + if (type == ZGOTO && getword(aword) && + eq(aword, goal)) { + level = -1; + } + break; + + default: + if (type != ZGOTO && (type != ZSWITCH || level != 0)) { + break; + } + if (lastchr(aword) != ':') { + break; + } + aword[strlen_(aword) - 1] = 0; + if (type == ZGOTO && eq(aword, goal) || + type == ZSWITCH && eq(aword, S_default)) { + level = -1; + } + break; + + case ZCASE: + if (type != ZSWITCH || level != 0) { + break; + } + (void) getword(aword); + if (lastchr(aword) == ':') { + aword[strlen_(aword) - 1] = 0; + } + cp = strip(Dfix1(aword)); + if (Gmatch(goal, cp)) { + level = -1; + } + xfree(cp); + break; + + case ZDEFAULT: + if (type == ZSWITCH && level == 0) { + level = -1; + } + break; + } + (void) getword(NOSTR); + } while (level >= 0); +} + +int +getword(tchar *wp) +{ + int found = 0; + int c, d; +#ifdef TRACE + tprintf("TRACE- getword()\n"); +#endif + + c = readc(1); + d = 0; + do { + while (issp(c)) { + c = readc(1); + } + if (c == '#') { + do { + c = readc(1); + } while (c >= 0 && c != '\n'); + } + if (c < 0) { + goto past; + } + if (c == '\n') { + if (wp) { + break; + } + return (0); + } + + /* ( and ) form separate words */ + if (c == '(' || c == ')') { + return (1); + } + + unreadc(c); + found = 1; + do { + c = readc(1); + if (c == '\\' && (c = readc(1)) == '\n') { + c = ' '; + } + if (c == '\'' || c == '"') { + if (d == 0) { + d = c; + } else if (d == c) { + d = 0; + } + } + if (c < 0) { + goto past; + } + if (wp) { + *wp++ = c; + } + } while ((d || !issp(c) && c != '(' && c != ')') && c != '\n'); + } while (wp == 0); + unreadc(c); + if (found) { + *--wp = 0; + } + return (found); + +past: + switch (Stype) { + + case ZIF: + bferr("then/endif not found"); + + case ZELSE: + bferr("endif not found"); + + case ZBRKSW: + case ZSWITCH: + bferr("endsw not found"); + + case ZBREAK: + bferr("end not found"); + + case ZGOTO: + setname(Sgoal); + bferr("label not found"); + } + /*NOTREACHED*/ +} + +void +toend() +{ + +#ifdef TRACE + tprintf("TRACE- toend()\n"); +#endif + if (whyles->w_end == 0) { + search(ZBREAK, 0); + whyles->w_end = btell() - 1; + } else { + bseek(whyles->w_end); + } + wfree(); +} + +void +wfree() +{ + long o = btell(); + +#ifdef TRACE + tprintf("TRACE- wfree()\n"); +#endif + while (whyles) { + struct whyle *wp = whyles; + struct whyle *nwp = wp->w_next; + + if (o >= wp->w_start && (wp->w_end == 0 || o < wp->w_end)) { + break; + } + if (wp->w_fe0) { + blkfree(wp->w_fe0); + } + if (wp->w_fename) { + xfree(wp->w_fename); + } + xfree((char *)wp); + whyles = nwp; + } +} + +void +doecho(tchar **v) +{ + +#ifdef TRACE + tprintf("TRACE- doecho()\n"); +#endif + echo(' ', v); +} + +void +doglob(tchar **v) +{ + +#ifdef TRACE + tprintf("TRACE- doglob()\n"); +#endif + echo(0, v); + flush(); +} + +void +echo(tchar sep, tchar **v) +{ + tchar *cp; + int nonl = 0; + +#ifdef TRACE + tprintf("TRACE- echo()\n"); +#endif + if (setintr) { + (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); + } + v++; + if (*v == 0) { + /* + * echo command needs to have newline when there are no + * flags or arguments. glob should have no newline. If + * the separator is a blank, we are doing an echo. If the + * separator is zero, we are globbing. + */ + if (sep == (tchar)' ') + Putchar('\n'); + return; + } + gflag = 0, tglob(v); + if (gflag) { + v = glob(v); + if (v == 0) { + bferr("No match"); + } + } + /* check for -n arg, NOTE: it might be quoted */ + if (sep == ' ' && *v && strlen_(*v) == 2 && + ((**v&TRIM) == '-' && (*(*v + 1) & TRIM) == 'n' && + (*(*v+2)&TRIM) == 0)) { + nonl++, v++; + } + while (cp = *v++) { + int c; + + while (c = *cp++) { + Putchar(c | QUOTE); + } + if (*v) { + Putchar(sep | QUOTE); + } + } + if (sep && nonl == 0) { + Putchar('\n'); + } else { + flush(); + } + if (setintr) { + (void) sigblock(sigmask(SIGINT)); + } + if (gargv) { + blkfree(gargv), gargv = 0; + } +} + +extern char **environ; + +/* + * Check if the environment variable vp affects this csh's behavior + * and therefore we should call setlocale() or not. + * This function has two side effects when it returns 1: + * variable islocalevar_catnum is set to the LC_xxx value. + * variable islocalevar_catname is set to the string "LC_xxx" + */ +static int islocalevar_catnum; +static char *islocalevar_catname; + +static +bool +islocalevar(tchar *vp) +{ + static struct lcinfo { + tchar * evname; /* The name of the env. var. */ + } categories_we_care[] = { + S_LANG, S_LC_ALL, S_LC_CTYPE, S_LC_MESSAGES, + NOSTR /* assumption: LC_xxx >= 0 */ + }; + struct lcinfo *p = categories_we_care; + + do { + if (strcmp_(vp, p->evname) == 0) { + return (1); + } + } while (((++p)->evname) != NOSTR); + return (0); +} + +void +dosetenv(tchar **v) +{ + tchar *vp, *lp; + +#ifdef TRACE + tprintf("TRACE- dosetenv()\n"); +#endif + v++; + if ((vp = *v++) == 0) { + char **ep; + + if (setintr) { + (void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT)); + } + for (ep = environ; *ep; ep++) { + printf("%s\n", *ep); + } + return; + } + + if ((lp = *v++) == 0) { + lp = S_; /* "" */ + } + local_setenv(vp, lp = globone(lp)); + if (eq(vp, S_PATH)) { + importpath(lp); + dohash(xhash); + } else if (islocalevar(vp)) { + if (!setlocale(LC_ALL, "")) { + error("Locale could not be set properly"); + } + } + + xfree(lp); +} + +void +dounsetenv(tchar **v) +{ +#ifdef TRACE + tprintf("TRACE- dounsetenv()\n"); +#endif + v++; + do { + local_unsetenv(*v); + if (islocalevar(*v++)) { + setlocale(LC_ALL, ""); /* Hope no error! */ + } + } while (*v); +} + +void +local_setenv(tchar *name, tchar *val) +{ + char **ep = environ; + tchar *cp; + char *dp; + tchar *ep_; /* temporary */ + char *blk[2], **oep = ep; + +#ifdef TRACE + /* tprintf("TRACE- local_setenv(%t, %t)\n", name, val); */ + /* printf("IN local_setenv args = (%t)\n", val); */ +#endif + for (; *ep; ep++) { +#ifdef MBCHAR + for (cp = name, dp = *ep; *cp && *dp; cp++) { + /* + * This loop compares two chars in different + * representations, EUC (as char *) and wchar_t + * (in tchar), and ends when they are different. + */ + wchar_t dwc; + int n; + + n = mbtowc(&dwc, dp, MB_CUR_MAX); + if (n <= 0) { + break; /* Illegal multibyte. */ + } + dp += n; /* Advance to next multibyte char. */ + if (dwc == (wchar_t)(*cp & TRIM)) { + continue; + } else { + break; + } + } +#else /* !MBCHAR */ + for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) { + continue; + } +#endif /* !MBCHAR */ + if (*cp != 0 || *dp != '=') { + continue; + } + cp = strspl(S_EQ, val); + xfree(*ep); + ep_ = strspl(name, cp); /* ep_ is xalloc'ed */ + xfree(cp); + /* + * Trimming is not needed here. + * trim(); + */ + *ep = tstostr(NULL, ep_); + xfree(ep_); /* because temp. use */ + return; + } + ep_ = strspl(name, S_EQ); /* ep_ is xalloc'ed */ + blk[0] = tstostr(NULL, ep_); + blk[1] = 0; + xfree(ep_); + environ = (char **)blkspl_((unsigned char **)environ, blk); + xfree((void *)oep); + local_setenv(name, val); +} + +void +local_unsetenv(tchar *name) +{ + char **ep = environ; + tchar *cp; + char *dp; + char **oep = ep; + char *cp_; /* tmp use */ + static cnt = 0; /* delete counter */ + +#ifdef TRACE + tprintf("TRACE- local_unsetenv()\n"); +#endif + for (; *ep; ep++) { +#ifdef MBCHAR + for (cp = name, dp = *ep; *cp && *dp; cp++) { + /* + * This loop compares two chars in different + * representations, EUC (as char *) and wchar_t + * (in tchar), and ends when they are different. + */ + wchar_t dwc; + int n; + + n = mbtowc(&dwc, dp, MB_CUR_MAX); + if (n <= 0) { + break; /* Illegal multibyte. */ + } + dp += n; /* Advance to next multibyte char. */ + if (dwc == (wchar_t)(*cp & TRIM)) { + continue; + } else { + break; + } + } +#else /* !MBCHAR */ + for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) { + continue; + } +#endif /* !MBCHAR */ + if (*cp != 0 || *dp != '=') { + continue; + } + cp_ = *ep; + *ep = 0; + environ = (char **)blkspl_((unsigned char **)environ, ep+1); + *ep = cp_; + xfree(cp_); + xfree((void *)oep); + return; + } +} + +void +doumask(tchar **v) +{ + tchar *cp = v[1]; + int i; + +#ifdef TRACE + tprintf("TRACE- dounmask()\n"); +#endif + if (cp == 0) { + i = umask(0); + (void) umask(i); + printf("%o\n", i); + return; + } + i = 0; + while (digit(*cp) && *cp != '8' && *cp != '9') { + i = i * 8 + *cp++ - '0'; + } + if (*cp || i < 0 || i > 0777) { + bferr("Improper mask"); + } + (void) umask(i); +} + + +struct limits * +findlim(tchar *cp) +{ + struct limits *lp, *res; + +#ifdef TRACE + tprintf("TRACE- findlim()\n"); +#endif + res = 0; + for (lp = limits; lp->limconst >= 0; lp++) { + if (prefix(cp, lp->limname)) { + if (res) { + bferr("Ambiguous"); + } + res = lp; + } + } + if (res) { + return (res); + } + bferr("No such limit"); + /*NOTREACHED*/ +} + +void +dolimit(tchar **v) +{ + struct limits *lp; + rlim_t limit; + tchar hard = 0; + +#ifdef TRACE + tprintf("TRACE- dolimit()\n"); +#endif + v++; + if (*v && eq(*v, S_h)) { + hard = 1; + v++; + } + if (*v == 0) { + for (lp = limits; lp->limconst >= 0; lp++) { + plim(lp, hard); + } + return; + } + lp = findlim(v[0]); + if (v[1] == 0) { + plim(lp, hard); + return; + } + switch (getval(lp, v+1, &limit)) { + case 0: + error("Value specified for limit is too large"); + return; + case (-1): + error("Numeric conversion failed"); + return; + default: + if (setlim(lp, hard, limit) < 0) { + error(NOSTR); + } + } +} + +static int +getval(struct limits *lp, tchar **v, rlim_t *retval) +{ + rlim_t value, tmp, tmp2; + tchar *cp = *v++; + char chbuf[BUFSIZ * MB_LEN_MAX]; + +#ifdef TRACE + tprintf("TRACE- getval()\n"); +#endif + + tstostr(chbuf, cp); + errno = 0; + value = strtoull(chbuf, NULL, 0); +/* + * we must accept zero, but the conversion can fail and give us + * zero as well...try to deal with it as gracefully as possible + * by checking for EINVAL + */ + if (value == 0 && errno == EINVAL) + return (-1); + + while (digit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') { + cp++; + } + if (*cp == 0) { + if (*v == 0) { + tmp = value * (rlim_t)lp->limdiv; + /* Check for overflow */ + if (tmp >= value) { + *retval = tmp; + return (1); + } else { + return (0); + } + } + cp = *v; + } + switch (*cp) { + + case ':': + if (lp->limconst != RLIMIT_CPU) { + goto badscal; + } + tstostr(chbuf, cp + 1); + tmp = strtoull(chbuf, NULL, 0); + tmp2 = value * 60 + tmp; + if (tmp2 >= value) { + *retval = tmp2; + return (1); + } else { + return (0); + } + + case 'h': + if (lp->limconst != RLIMIT_CPU) { + goto badscal; + } + limtail(cp, S_hours); + tmp = value * 3600; + if (tmp < value) { + return (0); + } + value = tmp; + break; + + case 'm': + if (lp->limconst == RLIMIT_CPU) { + limtail(cp, S_minutes); + tmp = value * 60; + if (tmp < value) { + return (0); + } + value = tmp; + break; + } + case 'M': + if (lp->limconst == RLIMIT_CPU) { + goto badscal; + } + *cp = 'm'; + limtail(cp, S_megabytes); + tmp = value * 1024 * 1024; + if (tmp < value) { + return (0); + } + value = tmp; + break; + + case 's': + if (lp->limconst != RLIMIT_CPU) { + goto badscal; + } + limtail(cp, S_seconds); + break; + + case 'k': + if (lp->limconst == RLIMIT_CPU) { + goto badscal; + } + limtail(cp, S_kbytes); + tmp = value * 1024; + if (tmp < value) { + return (0); + } + value = tmp; + break; + + case 'u': + limtail(cp, S_unlimited); + *retval = RLIM_INFINITY; + return (1); + + default: +badscal: + bferr("Improper or unknown scale factor"); + } + *retval = value; + return (1); +} + +void +limtail(tchar *cp, tchar *str0) +{ + tchar *str = str0; +#ifdef TRACE + tprintf("TRACE- limtail()\n"); +#endif + + while (*cp && *cp == *str) { + cp++, str++; + } + if (*cp) { + error("Bad scaling; did you mean ``%t''?", str0); + } +} + +void +plim(struct limits *lp, tchar hard) +{ + struct rlimit rlim; + char buf[BUFSZ]; + char *pbuf; + rlim_t limit; + +#ifdef TRACE + tprintf("TRACE- plim()\n"); +#endif + printf("%t \t", lp->limname); + (void) getrlimit(lp->limconst, &rlim); + limit = hard ? rlim.rlim_max : rlim.rlim_cur; + if (limit == RLIM_INFINITY) { + printf("unlimited"); + } else if (lp->limconst == RLIMIT_CPU) { + psecs_ull(limit); + } else { + buf[BUFSZ - 1] = '\0'; + pbuf = ulltostr((limit / lp->limdiv), &buf[BUFSZ - 1]); + printf("%s %t", pbuf, lp->limscale); + } + printf("\n"); +} + +void +dounlimit(tchar **v) +{ + struct limits *lp; + int err = 0; + tchar hard = 0; +#ifdef TRACE + tprintf("TRACE- dounlimit()\n"); +#endif + + v++; + if (*v && eq(*v, S_h)) { + hard = 1; + v++; + } + if (*v == 0) { + for (lp = limits; lp->limconst >= 0; lp++) { + if (setlim(lp, hard, RLIM_INFINITY) < 0) { + err++; + } + } + if (err) { + error(NULL); + } + return; + } + while (*v) { + lp = findlim(*v++); + if (setlim(lp, hard, RLIM_INFINITY) < 0) { + error(NULL); + } + } +} + +int +setlim(struct limits *lp, tchar hard, rlim_t limit) +{ + struct rlimit rlim; + +#ifdef TRACE + tprintf("TRACE- setlim()\n"); +#endif + (void) getrlimit(lp->limconst, &rlim); + if (hard) { + rlim.rlim_max = limit; + } else if (limit == RLIM_INFINITY && geteuid() != 0) { + rlim.rlim_cur = rlim.rlim_max; + } else { + rlim.rlim_cur = limit; + } + if (setrlimit(lp->limconst, &rlim) < 0) { + printf("%t: %t: Can't %s%s limit\n", bname, lp->limname, + limit == RLIM_INFINITY ? "remove" : "set", + hard ? " hard" : ""); + return (-1); + } + return (0); +} + +void +dosuspend() +{ + int ctpgrp; + void (*old)(); + +#ifdef TRACE + tprintf("TRACE- dosuspend()\n"); +#endif + if (loginsh) { + error("Can't suspend a login shell (yet)"); + } + if (getpid() == getsid(0)) { + error("Can't suspend this shell"); + } + untty(); + old = (void (*)())signal(SIGTSTP, SIG_DFL); + (void) kill(0, SIGTSTP); + /* the shell stops here */ + (void) signal(SIGTSTP, old); + if (tpgrp != -1) { +retry: + (void) ioctl(FSHTTY, TIOCGPGRP, (char *)&ctpgrp); + if (ctpgrp != opgrp) { + old = (void (*)())signal(SIGTTIN, SIG_DFL); + (void) kill(0, SIGTTIN); + (void) signal(SIGTTIN, old); + goto retry; + } + (void) setpgid(0, shpgrp); + (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&shpgrp); + } +} + +void +doeval(tchar **v) +{ + tchar **oevalvec = evalvec; + tchar *oevalp = evalp; + jmp_buf osetexit; + int reenter; + tchar **gv = 0; + +#ifdef TRACE + tprintf("TRACE- doeval()\n"); +#endif + v++; + if (*v == 0) { + return; + } + gflag = 0, tglob(v); + if (gflag) { + gv = v = glob(v); + gargv = 0; + if (v == 0) { + error("No match"); + } + v = copyblk(v); + } else { + trim(v); + } + getexit(osetexit); + reenter = 0; + setexit(); + reenter++; + if (reenter == 1) { + evalvec = v; + evalp = 0; + process(0); + } + evalvec = oevalvec; + evalp = oevalp; + doneinp = 0; + if (gv) { + blkfree(gv); + } + resexit(osetexit); + if (reenter >= 2) { + error(NULL); + } +} diff --git a/usr/src/cmd/csh/sh.glob.c b/usr/src/cmd/csh/sh.glob.c new file mode 100644 index 0000000000..1d639392e5 --- /dev/null +++ b/usr/src/cmd/csh/sh.glob.c @@ -0,0 +1,972 @@ +/* + * Copyright 2001 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.tconst.h" +#include <dirent.h> +#ifdef MBCHAR +#include <widec.h> /* wcsetno() */ +#include <fnmatch.h> /* fnmatch() */ +#endif /* MBCHAR */ + +/* + * C Shell + */ + +int globcnt; + +tchar *gpath, *gpathp, *lastgpathp; +int globbed; +bool noglob; +bool nonomatch; +tchar *entp; +tchar **sortbas; +int sortscmp(); +extern DIR *opendir_(); + +#define sort() qsort((char *)sortbas, &gargv[gargc] - sortbas, \ + sizeof (*sortbas), (int (*)(const void *, \ + const void *)) sortscmp), sortbas = &gargv[gargc] + + +tchar ** +glob(v) + register tchar **v; +{ + tchar agpath[BUFSIZ]; + tchar *agargv[GAVSIZ]; + + gpath = agpath; gpathp = gpath; *gpathp = 0; + lastgpathp = &gpath[BUFSIZ - 2]; + ginit(agargv); globcnt = 0; +#ifdef TRACE + tprintf("TRACE- glob()\n"); +#endif +#ifdef GDEBUG + printf("glob entered: "); blkpr(v); printf("\n"); +#endif + noglob = adrof(S_noglob /* "noglob" */) != 0; + nonomatch = adrof(S_nonomatch /* "nonomatch" */) != 0; + globcnt = noglob | nonomatch; + while (*v) + collect(*v++); +#ifdef GDEBUG + printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag); + blkpr(gargv); printf("\n"); +#endif + if (globcnt == 0 && (gflag&1)) { + blkfree(gargv), gargv = 0; + return (0); + } else + return (gargv = copyblk(gargv)); +} + +ginit(agargv) + tchar **agargv; +{ + + agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0; + gnleft = NCARGS - 4; +} + +collect(as) + register tchar *as; +{ + register int i; + +#ifdef TRACE + tprintf("TRACE- collect()\n"); +#endif + if (any('`', as)) { +#ifdef GDEBUG + printf("doing backp of %t\n", as); +#endif + (void) dobackp(as, 0); +#ifdef GDEBUG + printf("backp done, acollect'ing\n"); +#endif + /* + * dobackp has the side effect of messing with + * gflag, since it does more globbing, so check + * if the results is still globbable + */ + tglob(pargv); + + for (i = 0; i < pargc; i++) + if (noglob) { + Gcat(pargv[i], S_ /* "" */); + sortbas = &gargv[gargc]; + } else + acollect(pargv[i]); + if (pargv) + blkfree(pargv), pargv = 0; +#ifdef GDEBUG + printf("acollect done\n"); +#endif + } else if (noglob || eq(as, S_LBRA /* "{" */) || + eq(as, S_BRABRA /* "{}" */)) { + Gcat(as, S_ /* "" */); + sort(); + } else + acollect(as); +} + +acollect(as) + register tchar *as; +{ + register long ogargc = gargc; + +#ifdef TRACE + tprintf("TRACE- acollect()\n"); +#endif + gpathp = gpath; *gpathp = 0; globbed = 0; + expand(as); + if (gargc == ogargc) { + if (nonomatch) { + Gcat(as, S_ /* "" */); + sort(); + } + } else + sort(); +} + +/* + * String compare for qsort. Also used by filec code in sh.file.c. + */ +sortscmp(a1, a2) + tchar **a1, **a2; +{ + + return (strcoll_(*a1, *a2)); +} + +expand(as) + tchar *as; +{ + register tchar *cs; + register tchar *sgpathp, *oldcs; + struct stat stb; + +#ifdef TRACE + tprintf("TRACE- expand()\n"); +#endif + sgpathp = gpathp; + cs = as; + if (*cs == '~' && gpathp == gpath) { + addpath('~'); + for (cs++; alnum(*cs) || *cs == '-'; ) + addpath(*cs++); + if (!*cs || *cs == '/') { + if (gpathp != gpath + 1) { + *gpathp = 0; + if (gethdir(gpath + 1)) + /* + * modified from %s to %t + */ + error("Unknown user: %t", gpath + 1); + (void) strcpy_(gpath, gpath + 1); + } else + (void) strcpy_(gpath, + value(S_home /* "home" */)); + gpathp = strend(gpath); + } + } + while (!isglob(*cs)) { + if (*cs == 0) { + if (!globbed) + Gcat(gpath, S_ /* "" */); + else if (lstat_(gpath, &stb) >= 0) { + Gcat(gpath, S_ /* "" */); + globcnt++; + } + goto endit; + } + addpath(*cs++); + } + oldcs = cs; + while (cs > as && *cs != '/') + cs--, gpathp--; + if (*cs == '/') + cs++, gpathp++; + *gpathp = 0; + if (*oldcs == '{') { + (void) execbrc(cs, NOSTR); + return; + } + matchdir_(cs); +endit: + gpathp = sgpathp; + *gpathp = 0; +} + +matchdir_(pattern) + tchar *pattern; +{ + struct stat stb; + register struct dirent *dp; + register DIR *dirp; + tchar curdir_[MAXNAMLEN+1]; + int slproc = 0; + +#ifdef TRACE + tprintf("TRACE- matchdir()\n"); +#endif + /* + * BSD's opendir would open "." if argument is NULL, but not S5 + */ + + if (*gpath == NULL) + dirp = opendir_(S_DOT /* "." */); + else + dirp = opendir_(gpath); + if (dirp == NULL) { + if (globbed) + return; + goto patherr2; + } + if (fstat(dirp->dd_fd, &stb) < 0) + goto patherr1; + if (!isdir(stb)) { + errno = ENOTDIR; + goto patherr1; + } + while ((dp = readdir(dirp)) != NULL) { + + if (dp->d_ino == 0) + continue; + strtots(curdir_, dp->d_name); + slproc = 0; + if (match(curdir_, pattern, &slproc)) { + Gcat(gpath, curdir_); + globcnt++; + } + } + unsetfd(dirp->dd_fd); + closedir_(dirp); + return; + +patherr1: + unsetfd(dirp->dd_fd); + closedir_(dirp); +patherr2: + Perror(gpath); +} + +execbrc(p, s) + tchar *p, *s; +{ + tchar restbuf[BUFSIZ + 2]; + register tchar *pe, *pm, *pl; + int brclev = 0; + tchar *lm, savec, *sgpathp; + int slproc = 0; + +#ifdef TRACE + tprintf("TRACE- execbrc()\n"); +#endif + for (lm = restbuf; *p != '{'; *lm++ = *p++) + continue; + for (pe = ++p; *pe; pe++) + switch (*pe) { + + case '{': + brclev++; + continue; + + case '}': + if (brclev == 0) + goto pend; + brclev--; + continue; + + case '[': + for (pe++; *pe && *pe != ']'; pe++) + continue; + if (!*pe) + error("Missing ]"); + continue; + } +pend: + if (brclev || !*pe) + error("Missing }"); + for (pl = pm = p; pm <= pe; pm++) + switch (*pm & (QUOTE|TRIM)) { + + case '{': + brclev++; + continue; + + case '}': + if (brclev) { + brclev--; + continue; + } + goto doit; + + case ',': + if (brclev) + continue; +doit: + savec = *pm; + *pm = 0; + (void) strcpy_(lm, pl); + (void) strcat_(restbuf, pe + 1); + *pm = savec; + if (s == 0) { + sgpathp = gpathp; + expand(restbuf); + gpathp = sgpathp; + *gpathp = 0; + } else if (amatch(s, restbuf, &slproc)) + return (1); + sort(); + pl = pm + 1; + continue; + + case '[': + for (pm++; *pm && *pm != ']'; pm++) + continue; + if (!*pm) + error("Missing ]"); + continue; + } + return (0); +} + +match(s, p, slproc) + tchar *s, *p; + int *slproc; +{ + register int c; + register tchar *sentp; + tchar sglobbed = globbed; + +#ifdef TRACE + tprintf("TRACE- match()\n"); +#endif + if (*s == '.' && *p != '.') + return (0); + sentp = entp; + entp = s; + c = amatch(s, p, slproc); + entp = sentp; + globbed = sglobbed; + return (c); +} + +amatch(s, p, slproc) + register tchar *s, *p; + int *slproc; +{ + register int scc; + int ok, lc; + tchar *sgpathp; + struct stat stb; + int c, cc; + +#ifdef TRACE + tprintf("TRACE- amatch()\n"); +#endif + globbed = 1; + for (;;) { + scc = *s++ & TRIM; + switch (c = *p++) { + + case '{': + return (execbrc(p - 1, s - 1)); + + case '[': + ok = 0; + lc = TRIM; + while (cc = *p++) { + if (cc == ']') { + if (ok) + break; + return (0); + } + if (cc == '-') { +#ifdef MBCHAR + wchar_t rc = *p++; + if (rc == ']') { + p--; + continue; + } + /* + * Both ends of the char range + * must belong to the same codeset. + */ + if (sh_bracket_exp(scc, lc, rc)) + ok++; +#else /* !MBCHAR */ + if (lc <= scc && scc <= (int) *p++) + ok++; +#endif /* !MBCHAR */ + } else + if (scc == (lc = cc)) + ok++; + } + if (cc == 0) + error("Missing ]"); + continue; + + case '*': + if (!*p) + return (1); + if (*p == '/') { + p++; + goto slash; + } else if (*p == '*') { + s--; + continue; + } + + for (s--; *s; s++) + if (amatch(s, p, slproc)) + return (1); + + return (0); + + case 0: + return (scc == 0); + + default: + if ((c & TRIM) != scc) + return (0); + continue; + + case '?': + if (scc == 0) + return (0); + continue; + + case '/': + if (scc) + return (0); +slash: + if (*slproc) /* Need to expand "/" only once */ + return (0); + else + *slproc = 1; + + s = entp; + sgpathp = gpathp; + while (*s) + addpath(*s++); + addpath('/'); + if (stat_(gpath, &stb) == 0 && isdir(stb)) + if (*p == 0) { + Gcat(gpath, S_ /* "" */); + globcnt++; + } else + expand(p); + gpathp = sgpathp; + *gpathp = 0; + return (0); + } + } +} + +Gmatch(s, p) + register tchar *s, *p; +{ + register int scc; + int ok, lc; + int c, cc; + +#ifdef TRACE + tprintf("TRACE- Gmatch()\n"); +#endif + for (;;) { + scc = *s++ & TRIM; + switch (c = *p++) { + + case '[': + ok = 0; + lc = TRIM; + while (cc = *p++) { + if (cc == ']') { + if (ok) + break; + return (0); + } + if (cc == '-') { +#ifdef MBCHAR + wchar_t rc = *p++; + /* + * Both ends of the char range + * must belong to the same codeset... + */ + if (sh_bracket_exp(scc, lc, rc)) + ok++; +#else /* !MBCHAR */ + if (lc <= scc && scc <= (int) *p++) + ok++; +#endif /* !MBCHAR */ + } else + if (scc == (lc = cc)) + ok++; + } + if (cc == 0) + bferr("Missing ]"); + continue; + + case '*': + if (!*p) + return (1); + for (s--; *s; s++) + if (Gmatch(s, p)) + return (1); + return (0); + + case 0: + return (scc == 0); + + default: + if ((c & TRIM) != scc) + return (0); + continue; + + case '?': + if (scc == 0) + return (0); + continue; + + } + } +} + +Gcat(s1, s2) + tchar *s1, *s2; +{ + register tchar *p, *q; + int n; + +#ifdef TRACE + tprintf("TRACE- Gcat()\n"); +#endif + for (p = s1; *p++; ) + ; + for (q = s2; *q++; ) + ; + gnleft -= (n = (p - s1) + (q - s2) - 1); + if (gnleft <= 0 || ++gargc >= GAVSIZ) + error("Arguments too long"); + gargv[gargc] = 0; + p = gargv[gargc - 1] = (tchar *) xalloc((unsigned)n*sizeof (tchar)); + + for (q = s1; *p++ = *q++; ) + ; + for (p--, q = s2; *p++ = *q++; ) + ; +} + +addpath(c) + tchar c; +{ + +#ifdef TRACE + tprintf("TRACE- addpath()\n"); +#endif + if (gpathp >= lastgpathp) + error("Pathname too long"); + *gpathp++ = c & TRIM; + *gpathp = 0; +} + +rscan(t, f) + register tchar **t; + int (*f)(); +{ + register tchar *p; + +#ifdef TRACE + tprintf("TRACE- rscan()\n"); +#endif + while (p = *t++) + while (*p) + (*f)(*p++); +} + +trim(t) + register tchar **t; +{ + register tchar *p; + +#ifdef TRACE + tprintf("TRACE- trim()\n"); +#endif + while (p = *t++) + while (*p) + *p++ &= TRIM; +} + +tglob(t) + register tchar **t; +{ + register tchar *p, c; + +#ifdef TRACE + tprintf("TRACE- tglob()\n"); +#endif + while (p = *t++) { + if (*p == '~') + gflag |= 2; + else if (*p == '{' && (p[1] == '\0' || + p[1] == '}' && p[2] == '\0')) + continue; + while (c = *p++) + if (isglob(c)) + gflag |= c == '{' ? 2 : 1; + } +} + +tchar * +globone(str) + register tchar *str; +{ + tchar *gv[2]; + register tchar **gvp; + register tchar *cp; + +#ifdef TRACE + tprintf("TRACE- globone()\n"); +#endif + gv[0] = str; + gv[1] = 0; + gflag = 0; + tglob(gv); + if (gflag) { + gvp = glob(gv); + if (gvp == 0) { + setname(str); + bferr("No match"); + } + cp = *gvp++; + if (cp == 0) + cp = S_ /* "" */; + else if (*gvp) { + setname(str); + bferr("Ambiguous"); + } else + cp = strip(cp); +/* + if (cp == 0 || *gvp) { + setname(str); + bferr(cp ? "Ambiguous" : "No output"); + } +*/ + xfree((char *)gargv); gargv = 0; + } else { + trim(gv); + cp = savestr(gv[0]); + } + return (cp); +} + +/* + * Command substitute cp. If literal, then this is + * a substitution from a << redirection, and so we should + * not crunch blanks and tabs, separating words only at newlines. + */ +tchar ** +dobackp(cp, literal) + tchar *cp; + bool literal; +{ + register tchar *lp, *rp; + tchar *ep; + tchar word[BUFSIZ]; + tchar *apargv[GAVSIZ + 2]; + +#ifdef TRACE + tprintf("TRACE- dobackp()\n"); +#endif + if (pargv) { + blkfree(pargv); + } + pargv = apargv; + pargv[0] = NOSTR; + pargcp = pargs = word; + pargc = 0; + pnleft = BUFSIZ - 4; + for (;;) { + for (lp = cp; *lp != '`'; lp++) { + if (*lp == 0) { + if (pargcp != pargs) + pword(); +#ifdef GDEBUG + printf("leaving dobackp\n"); +#endif + return (pargv = copyblk(pargv)); + } + psave(*lp); + } + lp++; + for (rp = lp; *rp && *rp != '`'; rp++) + if (*rp == '\\') { + rp++; + if (!*rp) + goto oops; + } + if (!*rp) +oops: + error("Unmatched `"); + ep = savestr(lp); + ep[rp - lp] = 0; + backeval(ep, literal); +#ifdef GDEBUG + printf("back from backeval\n"); +#endif + cp = rp + 1; + } +} + +backeval(cp, literal) + tchar *cp; + bool literal; +{ + int pvec[2]; + int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0; + tchar ibuf[BUFSIZ]; + register int icnt = 0, c; + register tchar *ip; + bool hadnl = 0; + tchar *fakecom[2]; + struct command faket; + +#ifdef TRACE + tprintf("TRACE- backeval()\n"); +#endif + faket.t_dtyp = TCOM; + faket.t_dflg = 0; + faket.t_dlef = 0; + faket.t_drit = 0; + faket.t_dspr = 0; + faket.t_dcom = fakecom; + fakecom[0] = S_QPPPQ; /* "` ... `" */; + fakecom[1] = 0; + /* + * We do the psave job to temporarily change the current job + * so that the following fork is considered a separate job. + * This is so that when backquotes are used in a + * builtin function that calls glob the "current job" is not corrupted. + * We only need one level of pushed jobs as long as we are sure to + * fork here. + */ + psavejob(); + /* + * It would be nicer if we could integrate this redirection more + * with the routines in sh.sem.c by doing a fake execute on a builtin + * function that was piped out. + */ + mypipe(pvec); + if (pfork(&faket, -1) == 0) { + struct wordent paraml; + struct command *t; + tchar oHIST; + + new_process(); + (void) close(pvec[0]); + unsetfd(pvec[0]); + (void) dmove(pvec[1], 1); + (void) dmove(SHDIAG, 2); + reinitdesc(0, NULL); + arginp = cp; + while (*cp) + *cp++ &= TRIM; + /* + * disable history subsitution in sub-shell + * of `` evaluation prevents possible + * infinite recursion of `` evaluation + */ + oHIST = HIST; + HIST = 0; + (void) lex(¶ml); + HIST = oHIST; + if (err) + error("%s", gettext(err)); + alias(¶ml); + t = syntax(paraml.next, ¶ml, 0); + if (err) + error("%s", gettext(err)); + if (t) + t->t_dflg |= FPAR; + (void) signal(SIGTSTP, SIG_IGN); + (void) signal(SIGTTIN, SIG_IGN); + (void) signal(SIGTTOU, SIG_IGN); + execute(t, -1); + exitstat(); + } + xfree(cp); + (void) close(pvec[1]); + unsetfd(pvec[1]); + do { + int cnt = 0; + for (;;) { + if (icnt == 0) { + ip = ibuf; + icnt = read_(pvec[0], ip, BUFSIZ); + if (icnt <= 0) { + c = -1; + break; + } + } + if (hadnl) + break; + --icnt; + c = (*ip++ & TRIM); + if (c == 0) + break; + if (c == '\n') { + /* + * Continue around the loop one + * more time, so that we can eat + * the last newline without terminating + * this word. + */ + hadnl = 1; + continue; + } + if (!quoted && issp(c)) + break; + cnt++; + psave(c | quoted); + } + /* + * Unless at end-of-file, we will form a new word + * here if there were characters in the word, or in + * any case when we take text literally. If + * we didn't make empty words here when literal was + * set then we would lose blank lines. + */ + if (c != -1 && (cnt || literal)) { + if (pargc == GAVSIZ) + break; + pword(); + } + hadnl = 0; + } while (c >= 0); +#ifdef GDEBUG + printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]); + printf("also c = %c <%o>\n", (tchar) c, (tchar) c); +#endif + (void) close(pvec[0]); + unsetfd(pvec[0]); + pwait(); + prestjob(); +} + +psave(c) + tchar c; +{ +#ifdef TRACE + tprintf("TRACE- psave()\n"); +#endif + + if (--pnleft <= 0) + error("Word too long"); + *pargcp++ = c; +} + +pword() +{ +#ifdef TRACE + tprintf("TRACE- pword()\n"); +#endif + + psave(0); + if (pargc == GAVSIZ) + error("Too many words from ``"); + pargv[pargc++] = savestr(pargs); + pargv[pargc] = NOSTR; +#ifdef GDEBUG + printf("got word %t\n", pargv[pargc-1]); +#endif + pargcp = pargs; + pnleft = BUFSIZ - 4; +} + + + +/* + * returns pathname of the form dir/file; + * dir is a null-terminated string; + */ +char * +makename(dir, file) + char *dir; + char *file; +{ + /* + * Maximum length of a + * file/dir name in ls-command; + * dfile is static as this is returned + * by makename(); + */ + static char dfile[MAXNAMLEN]; + + register char *dp, *fp; + + dp = dfile; + fp = dir; + while (*fp) + *dp++ = *fp++; + if (dp > dfile && *(dp - 1) != '/') + *dp++ = '/'; + fp = file; + while (*fp) + *dp++ = *fp++; + *dp = '\0'; + /* + * dfile points to the absolute pathname. We are + * only interested in the last component. + */ + return (rindex(dfile, '/') + 1); +} + +sh_bracket_exp(t_ch, t_fch, t_lch) +tchar t_ch; +tchar t_fch; +tchar t_lch; +{ + char t_char[MB_LEN_MAX + 1]; + char t_patan[MB_LEN_MAX * 2 + 8]; + char *p; + int i; + + if ((t_ch == t_fch) || (t_ch == t_lch)) + return(1); + + p = t_patan; + if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0) + return(0); + t_char[i] = 0; + + *p++ = '['; + if ((i = wctomb(p, (wchar_t)t_fch)) <= 0) + return(0); + p += i; + *p++ = '-'; + if ((i = wctomb(p, (wchar_t)t_lch)) <= 0) + return(0); + p += i; + *p++ = ']'; + *p = 0; + + if (fnmatch(t_patan, t_char, FNM_NOESCAPE)) + return(0); + return(1); +} diff --git a/usr/src/cmd/csh/sh.h b/usr/src/cmd/csh/sh.h new file mode 100644 index 0000000000..72cebe7275 --- /dev/null +++ b/usr/src/cmd/csh/sh.h @@ -0,0 +1,627 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> /* MB_xxx, mbxxx(), wcxxx() etc. */ +#include <limits.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/siginfo.h> +#include <sys/ucontext.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/termios.h> +#include <sys/ttold.h> +#include <errno.h> +#include <signal.h> /* std sysV signal.h */ +#include <setjmp.h> +#include <sys/resource.h> +#include "signal.h" /* mainly BSD related signal.h */ +#include "sh.local.h" +#include "sh.char.h" + +/* + * MAXHOSTNAMELEN is defined in param.h under SunOS + */ +#define MAXHOSTNAMELEN 64 + +#ifdef MBCHAR +# if !defined(MB_LEN_MAX) || !defined(MB_CUR_MAX) + Error: I need both ANSI macros! +# endif +#else +# if !defined(MB_LEN_MAX) +# define MB_LEN_MAX 1 +# endif +# if !defined(MB_CUR_MAX) +# define MB_CUR_MAX 1 +# endif +#endif + +#ifndef MBCHAR /* Let's replace the ANSI functions with our own macro + * for efficiency! + */ +#define mbtowc(pwc, pmb, n_is_ignored) ((*(pwc)=*(pmb)), 1) +#define wctomb(pmb, wc) ((*(pmb)=((char)wc)), 1) +#endif/*!MBCHAR*/ + +/* + * C shell + * + * Bill Joy, UC Berkeley + * October, 1978; May 1980 + * + * Jim Kulp, IIASA, Laxenburg Austria + * April, 1980 + */ + +/*If we are setting the $cwd variable becuz we did a + cd, chdir, pushd, popd command, then set didchdir to + 1. This prevents globbing down when setting $cwd. + However, if the user set $cwd, we want the globbing + done; so, didchdir would be equal to 0 in that case. + */ +int didchdir; + +#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR) + +typedef char bool; + +/* tchar (Tagged CHARacter) is a place holder to keep a QUOTE bit and + * a character. + * For European language handling, lower 8 bits of tchar is used + * to store a character. For other languages, especially Asian, 16 bits + * are used to store a character. + * Following typedef's assume short int is a 16-bit entity and long int is + * a 32-bit entity. + * The QUOTE bit tells whether the character is subject to further + * interpretation such as history substitution, file mathing, command + * subsitution. TRIM is a mask to strip off the QUOTE bit. + */ +#ifdef MBCHAR /* For multibyte character handling. */ +typedef long int tchar; +#define QUOTE 0x80000000 +#define TRIM 0x7fffffff +#else/*!MBCHAR*/ /* European language requires only 8 bits. */ +typedef unsigned short int tchar; +#define QUOTE 0x8000 +#define TRIM 0x00ff +#endif/*!MBCHAR*/ +#define eq(a, b) (strcmp_(a, b) == 0) + +extern int Putchar(tchar); + +/* + * Global flags + */ +bool chkstop; /* Warned of stopped jobs... allow exit */ +bool didfds; /* Have setup i/o fd's for child */ +bool doneinp; /* EOF indicator after reset from readc */ +bool exiterr; /* Exit if error or non-zero exit status */ +bool child; /* Child shell ... errors cause exit */ +bool haderr; /* Reset was because of an error */ +bool intty; /* Input is a tty */ +bool cflg; /* invoked with -c option */ +bool intact; /* We are interactive... therefore prompt */ +bool justpr; /* Just print because of :p hist mod */ +bool loginsh; /* We are a loginsh -> .login/.logout */ +bool neednote; /* Need to pnotify() */ +bool noexec; /* Don't execute, just syntax check */ +bool pjobs; /* want to print jobs if interrupted */ +bool pfcshflag; /* set to 0 for pfcsh */ +bool setintr; /* Set interrupts on/off -> Wait intr... */ +bool timflg; /* Time the next waited for command */ +bool havhash; /* path hashing is available */ +bool havhash2; /* cdpath hashing is available */ +#ifdef FILEC +bool filec; /* doing filename expansion */ +#endif + +/* + * Global i/o info + */ +tchar *arginp; /* Argument input for sh -c and internal `xx` */ +int onelflg; /* 2 -> need line for -t, 1 -> exit on read */ +tchar *file; /* Name of shell file for $0 */ + +char *err; /* Error message from scanner/parser */ +struct timeval time0; /* Time at which the shell started */ +struct rusage ru0; + +/* + * Miscellany + */ +tchar *doldol; /* Character pid for $$ */ +int uid; /* Invokers uid */ +time_t chktim; /* Time mail last checked */ +int shpgrp; /* Pgrp of shell */ +int tpgrp; /* Terminal process group */ +/* If tpgrp is -1, leave tty alone! */ +int opgrp; /* Initial pgrp and tty pgrp */ +int oldisc; /* Initial line discipline or -1 */ + +/* + * These are declared here because they want to be + * initialized in sh.init.c (to allow them to be made readonly) + */ + +extern struct biltins { + tchar *bname; + int (*bfunct)(); + short minargs, maxargs; +} bfunc[]; +extern int nbfunc; + +extern struct srch { + tchar *s_name; + short s_value; +} srchn[]; +extern int nsrchn; + +/* + * To be able to redirect i/o for builtins easily, the shell moves the i/o + * descriptors it uses away from 0,1,2. + * Ideally these should be in units which are closed across exec's + * (this saves work) but for version 6, this is not usually possible. + * The desired initial values for these descriptors are defined in + * sh.local.h. + */ +short SHIN; /* Current shell input (script) */ +short SHOUT; /* Shell output */ +short SHDIAG; /* Diagnostic output... shell errs go here */ +short OLDSTD; /* Old standard input (def for cmds) */ + +/* + * Error control + * + * Errors in scanning and parsing set up an error message to be printed + * at the end and complete. Other errors always cause a reset. + * Because of source commands and .cshrc we need nested error catches. + */ + +jmp_buf reslab; + +#define setexit() ((void) setjmp(reslab)) +#define reset() longjmp(reslab, 0) + /* Should use structure assignment here */ +#define getexit(a) copy((void *)(a), (void *)reslab, sizeof reslab) +#define resexit(a) copy((void *)reslab, ((void *)(a)), sizeof reslab) + +tchar *gointr; /* Label for an onintr transfer */ +void (*parintr)(); /* Parents interrupt catch */ +void (*parterm)(); /* Parents terminate catch */ + + +/* + * Each level of input has a buffered input structure. + * There are one or more blocks of buffered input for each level, + * exactly one if the input is seekable and tell is available. + * In other cases, the shell buffers enough blocks to keep all loops + * in the buffer. + */ +struct Bin { + off_t Bfseekp; /* Seek pointer */ + off_t Bfbobp; /* Seekp of beginning of buffers */ + off_t Bfeobp; /* Seekp of end of buffers */ + short Bfblocks; /* Number of buffer blocks */ + tchar **Bfbuf; /* The array of buffer blocks */ +} B; + +#define fseekp B.Bfseekp +#define fbobp B.Bfbobp +#define feobp B.Bfeobp +#define fblocks B.Bfblocks +#define fbuf B.Bfbuf + +#define btell() fseekp + +#ifndef btell +off_t btell(); +#endif + +/* + * The shell finds commands in loops by reseeking the input + * For whiles, in particular, it reseeks to the beginning of the + * line the while was on; hence the while placement restrictions. + */ +off_t lineloc; + +#ifdef TELL +bool cantell; /* Is current source tellable ? */ +#endif + +/* + * Input lines are parsed into doubly linked circular + * lists of words of the following form. + */ +struct wordent { + tchar *word; + struct wordent *prev; + struct wordent *next; +}; + +/* + * During word building, both in the initial lexical phase and + * when expanding $ variable substitutions, expansion by `!' and `$' + * must be inhibited when reading ahead in routines which are themselves + * processing `!' and `$' expansion or after characters such as `\' or in + * quotations. The following flags are passed to the getC routines + * telling them which of these substitutions are appropriate for the + * next character to be returned. + */ +#define DODOL 1 +#define DOEXCL 2 +#define DOALL DODOL|DOEXCL + +/* + * Labuf implements a general buffer for lookahead during lexical operations. + * Text which is to be placed in the input stream can be stuck here. + * We stick parsed ahead $ constructs during initial input, + * process id's from `$$', and modified variable values (from qualifiers + * during expansion in sh.dol.c) here. + */ +tchar *labuf; + +tchar *lap; + +/* + * Parser structure + * + * Each command is parsed to a tree of command structures and + * flags are set bottom up during this process, to be propagated down + * as needed during the semantics/exeuction pass (sh.sem.c). + */ +struct command { + short t_dtyp; /* Type of node */ + short t_dflg; /* Flags, e.g. FAND|... */ + union { + tchar *T_dlef; /* Input redirect word */ + struct command *T_dcar; /* Left part of list/pipe */ + } L; + union { + tchar *T_drit; /* Output redirect word */ + struct command *T_dcdr; /* Right part of list/pipe */ + } R; +#define t_dlef L.T_dlef +#define t_dcar L.T_dcar +#define t_drit R.T_drit +#define t_dcdr R.T_dcdr + tchar **t_dcom; /* Command/argument vector */ + char *cfname; /* char pathname for execv */ + char **cargs; /* char arg vec for execv */ + struct command *t_dspr; /* Pointer to ()'d subtree */ + short t_nice; +}; + +#define TCOM 1 /* t_dcom <t_dlef >t_drit */ +#define TPAR 2 /* ( t_dspr ) <t_dlef >t_drit */ +#define TFIL 3 /* t_dlef | t_drit */ +#define TLST 4 /* t_dlef ; t_drit */ +#define TOR 5 /* t_dlef || t_drit */ +#define TAND 6 /* t_dlef && t_drit */ + +#define FSAVE (FNICE|FTIME|FNOHUP) /* save these when re-doing */ + +#define FAND (1<<0) /* executes in background */ +#define FCAT (1<<1) /* output is redirected >> */ +#define FPIN (1<<2) /* input is a pipe */ +#define FPOU (1<<3) /* output is a pipe */ +#define FPAR (1<<4) /* don't fork, last ()ized cmd */ +#define FINT (1<<5) /* should be immune from intr's */ +/* spare */ +#define FDIAG (1<<7) /* redirect unit 2 with unit 1 */ +#define FANY (1<<8) /* output was ! */ +#define FHERE (1<<9) /* input redirection is << */ +#define FREDO (1<<10) /* reexec aft if, repeat,... */ +#define FNICE (1<<11) /* t_nice is meaningful */ +#define FNOHUP (1<<12) /* nohup this command */ +#define FTIME (1<<13) /* time this command */ + +/* + * The keywords for the parser + */ +#define ZBREAK 0 +#define ZBRKSW 1 +#define ZCASE 2 +#define ZDEFAULT 3 +#define ZELSE 4 +#define ZEND 5 +#define ZENDIF 6 +#define ZENDSW 7 +#define ZEXIT 8 +#define ZFOREACH 9 +#define ZGOTO 10 +#define ZIF 11 +#define ZLABEL 12 +#define ZLET 13 +#define ZSET 14 +#define ZSWITCH 15 +#define ZTEST 16 +#define ZTHEN 17 +#define ZWHILE 18 + +/* + * Structure defining the existing while/foreach loops at this + * source level. Loops are implemented by seeking back in the + * input. For foreach (fe), the word list is attached here. + */ +struct whyle { + off_t w_start; /* Point to restart loop */ + off_t w_end; /* End of loop (0 if unknown) */ + tchar **w_fe, **w_fe0; /* Current/initial wordlist for fe */ + tchar *w_fename; /* Name for fe */ + struct whyle *w_next; /* Next (more outer) loop */ +} *whyles; + +/* + * Variable structure + * + * Aliases and variables are stored in AVL balanced binary trees. + */ +struct varent { + tchar **vec; /* Array of words which is the value */ + tchar *v_name; /* Name of variable/alias */ + struct varent *v_link[3]; /* The links, see below */ + int v_bal; /* Balance factor */ +} shvhed, aliases; +#define v_left v_link[0] +#define v_right v_link[1] +#define v_parent v_link[2] + +struct varent *adrof1(); +#define adrof(v) adrof1(v, &shvhed) +#define value(v) value1(v, &shvhed) + +/* + * MAX_VAR_LEN - maximum variable name defined by csh man page to be 20 + */ +#define MAX_VAR_LEN 20 + +/* + * MAX_VREF_LEN - maximum variable reference $name[...] + * it can be as big as a csh word, which is 1024 + */ +#define MAX_VREF_LEN 1024 + + +/* + * The following are for interfacing redo substitution in + * aliases to the lexical routines. + */ +struct wordent *alhistp; /* Argument list (first) */ +struct wordent *alhistt; /* Node after last in arg list */ +tchar **alvec; /* The (remnants of) alias vector */ + +/* + * Filename/command name expansion variables + */ +short gflag; /* After tglob -> is globbing needed? */ + +/* + * A reasonable limit on number of arguments would seem to be + * the maximum number of characters in an arg list / 6. + * + * XXX: With the new VM system, NCARGS has become enormous, making + * it impractical to allocate arrays with NCARGS / 6 entries on + * the stack. The proper fix is to revamp code elsewhere (in + * sh.dol.c and sh.glob.c) to use a different technique for handling + * command line arguments. In the meantime, we simply fall back + * on using the old value of NCARGS. + */ +#ifdef notyet +#define GAVSIZ (NCARGS / 6) +#else notyet +#define GAVSIZ (10240 / 6) +#endif notyet + +/* + * Variables for filename expansion + */ +tchar **gargv; /* Pointer to the (stack) arglist */ +long gargc; /* Number args in gargv */ +long gnleft; + +/* + * Variables for command expansion. + */ +tchar **pargv; /* Pointer to the argv list space */ +tchar *pargs; /* Pointer to start current word */ +long pargc; /* Count of arguments in pargv */ +long pnleft; /* Number of chars left in pargs */ +tchar *pargcp; /* Current index into pargs */ + +/* + * History list + * + * Each history list entry contains an embedded wordlist + * from the scanner, a number for the event, and a reference count + * to aid in discarding old entries. + * + * Essentially "invisible" entries are put on the history list + * when history substitution includes modifiers, and thrown away + * at the next discarding since their event numbers are very negative. + */ +struct Hist { + struct wordent Hlex; + int Hnum; + int Href; + struct Hist *Hnext; +} Histlist; + +struct wordent paraml; /* Current lexical word list */ +int eventno; /* Next events number */ +int lastev; /* Last event reference (default) */ + +tchar HIST; /* history invocation character */ +tchar HISTSUB; /* auto-substitute character */ + +/* + * In lines for frequently called functions + * + * WARNING: changes here also need to occur in the xfree() function in + * sh.misc.c. + */ +#if defined(sparc) +#define XFREE(cp) { \ + extern char end[]; \ + char stack; \ +/*??*/ if (((char *)(cp)) >= end && ((char *)(cp)) < &stack) \ +/*??*/ free((void *)(cp)); \ +} +#elif defined(i386) +#define XFREE(cp) { \ + extern char end[]; \ + if (((char *)(cp)) >= end) \ + free((void *)(cp)); \ +} +#else +#error XFREE macro is machine dependant and no machine type is recognized +#endif +void *alloctmp; +#define xalloc(i) ((alloctmp = (void *)malloc(i)) ? alloctmp : (void *)nomem(i))/*??*/ +#define xrealloc(buf, i) ((alloctmp = (void *)realloc(buf, i)) ? alloctmp : \ + (void *)nomem(i)) + +tchar *Dfix1(); +tchar **blkcat(); +tchar **blkcpy(); +tchar **blkend(); +tchar **blkspl(); +char **blkspl_(); +void *malloc(); +tchar *cname(); +tchar **copyblk(); +tchar **dobackp(); +tchar *domod(); +struct wordent *dosub(); +tchar *exp3(); +tchar *exp3a(); +tchar *exp4(); +tchar *exp5(); +tchar *exp6(); +struct Hist *enthist(); +struct Hist *findev(); +struct wordent *freenod(); +char *getenv(); +tchar *getenv_(/* tchar * */); +tchar *getenvs_(/* char * */); +tchar *getinx(); +struct varent *getvx(); +struct passwd *getpwnam(); +struct wordent *gethent(); +struct wordent *getsub(); +char *getwd(); +tchar *getwd_(); +tchar **glob(); +tchar *globone(); +char *index(); +tchar *index_(); +struct biltins *isbfunc(); +off_t lseek(); +tchar *operate(); +void phup(); +void pintr(); +void pchild(); +tchar *putn(); +char *rindex(); +tchar *rindex_(); +tchar **saveblk(); +tchar *savestr(); +char *strcat(); +tchar *strcat_(); +int strlen_(tchar *); +char *strcpy(); +tchar *strcpy_(); +tchar *strend(); +tchar *strip(); +tchar *strspl(); +tchar *subword(); +struct command *syntax(); +struct command *syn0(); +struct command *syn1(); +struct command *syn1a(); +struct command *syn1b(); +struct command *syn2(); +struct command *syn3(); +tchar *value1(); +tchar *xhome(); +tchar *xname(); +tchar *xset(); + +#define NOSTR ((tchar *) 0) + +/* + * setname is a macro to copy the path in bname. (see sh.err.c) + * Here we are dynamically reallocating the bname to the new length + * to store the new path + */ +tchar *bname; +#define setname(a) { \ + bname = xrealloc(bname, (strlen_(a)+1) * sizeof (tchar)); \ + strcpy_(bname, a); \ + bname[strlen_(a)] = '\0'; \ +} + +#ifdef VFORK +tchar *Vsav; +tchar **Vav; +tchar *Vdp; +#endif + +tchar **evalvec; +tchar *evalp; + +/* Conversion functions between char and tchar strings. */ +tchar *strtots(/* tchar * , char * */); +char *tstostr(/* char * , tchar * */); + +#ifndef NULL +#define NULL 0 +#endif + + +/* + * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used + * to hash execs. If it is allocated (havhash true), then to tell + * whether ``name'' is (possibly) present in the i'th component + * of the variable path, you look at the bit in xhash indexed by + * hash(hashname("name"), i). This is setup automatically + * after .login is executed, and recomputed whenever ``path'' is + * changed. + * The two part hash function is designed to let texec() call the + * more expensive hashname() only once and the simple hash() several + * times (once for each path component checked). + * Byte size is assumed to be 8. + */ +#define HSHSIZ (32*1024) /* 4k bytes */ +#define HSHMASK (HSHSIZ - 1) +#define HSHMUL 243 + +/* + * The following two arrays are used for caching. xhash + * is for caching path variable and xhash2 is for cdpath + * variable. + */ + +char xhash[HSHSIZ / 8]; +char xhash2[HSHSIZ / 8]; +#define hash(a, b) ((a) * HSHMUL + (b) & HSHMASK) +#define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ +#define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ +#ifdef VFORK +int hits, misses; +#endif + + diff --git a/usr/src/cmd/csh/sh.hist.c b/usr/src/cmd/csh/sh.hist.c new file mode 100644 index 0000000000..a4418a95d4 --- /dev/null +++ b/usr/src/cmd/csh/sh.hist.c @@ -0,0 +1,167 @@ +/* + * Copyright 1990 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.tconst.h" + +/* + * C shell + */ + +savehist(sp) + struct wordent *sp; +{ + register struct Hist *hp, *np; + register int histlen = 0; + tchar *cp; + +#ifdef TRACE + tprintf("TRACE- savehist()\n"); +#endif + /* throw away null lines */ + if (sp->next->word[0] == '\n') + return; + cp = value(S_history /*"history"*/); + if (*cp) { + register tchar *p = cp; + + while (*p) { + if (!digit(*p)) { + histlen = 0; + break; + } + histlen = histlen * 10 + *p++ - '0'; + } + } + for (hp = &Histlist; np = hp->Hnext;) + if (eventno - np->Href >= histlen || histlen == 0) + hp->Hnext = np->Hnext, hfree(np); + else + hp = np; + (void) enthist(++eventno, sp, 1); +} + +struct Hist * +enthist(event, lp, docopy) + int event; + register struct wordent *lp; + bool docopy; +{ + register struct Hist *np; + +#ifdef TRACE + tprintf("TRACE- enthist()\n"); +#endif + np = (struct Hist *) xalloc(sizeof *np); + np->Hnum = np->Href = event; + if (docopy) + copylex(&np->Hlex, lp); + else { + np->Hlex.next = lp->next; + lp->next->prev = &np->Hlex; + np->Hlex.prev = lp->prev; + lp->prev->next = &np->Hlex; + } + np->Hnext = Histlist.Hnext; + Histlist.Hnext = np; + return (np); +} + +hfree(hp) + register struct Hist *hp; +{ +#ifdef TRACE + tprintf("TRACE- hfree()\n"); +#endif + + freelex(&hp->Hlex); + xfree( (tchar *)hp); +} + +dohist(vp) + tchar **vp; +{ + int n, rflg = 0, hflg = 0; +#ifdef TRACE + tprintf("TRACE- dohist()\n"); +#endif + if (getn(value(S_history /*"history"*/)) == 0) + return; + if (setintr) + (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); + while (*++vp && **vp == '-') { + tchar *vp2 = *vp; + + while (*++vp2) + switch (*vp2) { + case 'h': + hflg++; + break; + case 'r': + rflg++; + break; + case '-': /* ignore multiple '-'s */ + break; + default: + printf("Unknown flag: -%c\n", *vp2); + error("Usage: history [-rh] [# number of events]"); + } + } + if (*vp) + n = getn(*vp); + else { + n = getn(value(S_history /*"history"*/)); + } + dohist1(Histlist.Hnext, &n, rflg, hflg); +} + +dohist1(hp, np, rflg, hflg) + struct Hist *hp; + int *np, rflg, hflg; +{ + bool print = (*np) > 0; +#ifdef TRACE + tprintf("TRACE- dohist1()\n"); +#endif +top: + if (hp == 0) + return; + (*np)--; + hp->Href++; + if (rflg == 0) { + dohist1(hp->Hnext, np, rflg, hflg); + if (print) + phist(hp, hflg); + return; + } + if (*np >= 0) + phist(hp, hflg); + hp = hp->Hnext; + goto top; +} + +phist(hp, hflg) + register struct Hist *hp; + int hflg; +{ +#ifdef TRACE + tprintf("TRACE- phist()\n"); +#endif + + if (hflg == 0) + printf("%6d\t", hp->Hnum); + prlex(&hp->Hlex); +} diff --git a/usr/src/cmd/csh/sh.init.c b/usr/src/cmd/csh/sh.init.c new file mode 100644 index 0000000000..0f6d5e5c22 --- /dev/null +++ b/usr/src/cmd/csh/sh.init.c @@ -0,0 +1,206 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.tconst.h" + +/* + * C shell + */ + +extern int doalias(); +extern int dobg(); +extern int dobreak(); +extern int dochngd(); +extern int docontin(); +extern int dodirs(); +extern int doecho(); +extern int doelse(); +extern int doend(); +extern int doendif(); +extern int doendsw(); +extern int doeval(); +extern int doexit(); +extern int dofg(); +extern int doforeach(); +extern int doglob(); +extern int dogoto(); +extern int dohash(); +extern int dorehash(); +extern int dohist(); +extern int doif(); +extern int dojobs(); +extern int dokill(); +extern int dolet(); +extern int dolimit(); +extern int dologin(); +extern int dologout(); +#ifdef NEWGRP +extern int donewgrp(); +#endif +extern int donice(); +extern int donotify(); +extern int donohup(); +extern int doonintr(); +extern int dopopd(); +extern int dopushd(); +extern int dorepeat(); +extern int doset(); +extern int dosetenv(); +extern int dosource(); +extern int dostop(); +extern int dosuspend(); +extern int doswbrk(); +extern int doswitch(); +extern int dotime(); +extern int dounlimit(); +extern int doumask(); +extern int dowait(); +extern int dowhile(); +extern int dozip(); +extern int execash(); +extern int goodbye(); +#ifdef VFORK +extern int hashstat(); +#endif +extern int shift(); +#ifdef OLDMALLOC +extern int showall(); +#endif +extern int unalias(); +extern int dounhash(); +extern int unset(); +extern int dounsetenv(); + +#define INF 1000 + +struct biltins bfunc[] = { + S_AT, dolet, 0, INF, + S_alias, doalias, 0, INF, +#ifdef OLDMALLOC + S_alloc, showall, 0, 1, +#endif + S_bg, dobg, 0, INF, + S_break, dobreak, 0, 0, + S_breaksw, doswbrk, 0, 0, +#ifdef IIASA + S_bye, goodbye, 0, 0, +#endif + S_case, dozip, 0, 1, + S_cd, dochngd, 0, 1, + S_chdir, dochngd, 0, 1, + S_continue, docontin, 0, 0, + S_default, dozip, 0, 0, + S_dirs, dodirs, 0, 1, + S_echo, doecho, 0, INF, + S_else, doelse, 0, INF, + S_end, doend, 0, 0, + S_endif, dozip, 0, 0, + S_endsw, dozip, 0, 0, + S_eval, doeval, 0, INF, + S_exec, execash, 1, INF, + S_exit, doexit, 0, INF, + S_fg, dofg, 0, INF, + S_foreach, doforeach, 3, INF, +#ifdef IIASA + S_gd, dopushd, 0, 1, +#endif + S_glob, doglob, 0, INF, + S_goto, dogoto, 1, 1, +#ifdef VFORK + S_hashstat, hashstat, 0, 0, +#endif + S_history, dohist, 0, 2, + S_if, doif, 1, INF, + S_jobs, dojobs, 0, 1, + S_kill, dokill, 1, INF, + S_limit, dolimit, 0, 3, + S_login, dologin, 0, 1, + S_logout, dologout, 0, 0, +#ifdef NEWGRP + S_newgrp, donewgrp, 1, 1, +#endif + S_nice, donice, 0, INF, + S_nohup, donohup, 0, INF, + S_notify, donotify, 0, INF, + S_onintr, doonintr, 0, 2, + S_popd, dopopd, 0, 1, + S_pushd, dopushd, 0, 1, +#ifdef IIASA + S_rd, dopopd, 0, 1, +#endif + S_rehash, dorehash, 0, 0, + S_repeat, dorepeat, 2, INF, + S_set, doset, 0, INF, + S_setenv, dosetenv, 0, 2, + S_shift, shift, 0, 1, + S_source, dosource, 1, 2, + S_stop, dostop, 1, INF, + S_suspend, dosuspend, 0, 0, + S_switch, doswitch, 1, INF, + S_time, dotime, 0, INF, + S_umask, doumask, 0, 1, + S_unalias, unalias, 1, INF, + S_unhash, dounhash, 0, 0, + S_unlimit, dounlimit, 0, INF, + S_unset, unset, 1, INF, + S_unsetenv, dounsetenv, 1, INF, + S_wait, dowait, 0, 0, + S_while, dowhile, 1, INF, +}; +int nbfunc = sizeof bfunc / sizeof *bfunc; + +#define ZBREAK 0 +#define ZBRKSW 1 +#define ZCASE 2 +#define ZDEFAULT 3 +#define ZELSE 4 +#define ZEND 5 +#define ZENDIF 6 +#define ZENDSW 7 +#define ZEXIT 8 +#define ZFOREACH 9 +#define ZGOTO 10 +#define ZIF 11 +#define ZLABEL 12 +#define ZLET 13 +#define ZSET 14 +#define ZSWITCH 15 +#define ZTEST 16 +#define ZTHEN 17 +#define ZWHILE 18 + +struct srch srchn[] = { + S_AT, ZLET, + S_break, ZBREAK, + S_breaksw, ZBRKSW, + S_case, ZCASE, + S_default, ZDEFAULT, + S_else, ZELSE, + S_end, ZEND, + S_endif, ZENDIF, + S_endsw, ZENDSW, + S_exit, ZEXIT, + S_foreach, ZFOREACH, + S_goto, ZGOTO, + S_if, ZIF, + S_label, ZLABEL, + S_set, ZSET, + S_switch, ZSWITCH, + S_while, ZWHILE +}; +int nsrchn = sizeof srchn / sizeof *srchn; + diff --git a/usr/src/cmd/csh/sh.lex.c b/usr/src/cmd/csh/sh.lex.c new file mode 100644 index 0000000000..4a743e8e65 --- /dev/null +++ b/usr/src/cmd/csh/sh.lex.c @@ -0,0 +1,1429 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> /* for lseek prototype */ +#include "sh.h" +#include "sh.tconst.h" +#include <sys/filio.h> +#include <sys/ttold.h> +#define RAW O_RAW +/* + * C shell + */ + +/* + * These lexical routines read input and form lists of words. + * There is some involved processing here, because of the complications + * of input buffering, and especially because of history substitution. + */ + +tchar *word(); + +/* + * Peekc is a peek characer for getC, peekread for readc. + * There is a subtlety here in many places... history routines + * will read ahead and then insert stuff into the input stream. + * If they push back a character then they must push it behind + * the text substituted by the history substitution. On the other + * hand in several places we need 2 peek characters. To make this + * all work, the history routines read with getC, and make use both + * of ungetC and unreadc. The key observation is that the state + * of getC at the call of a history reference is such that calls + * to getC from the history routines will always yield calls of + * readc, unless this peeking is involved. That is to say that during + * getexcl the variables lap, exclp, and exclnxt are all zero. + * + * Getdol invokes history substitution, hence the extra peek, peekd, + * which it can ungetD to be before history substitutions. + */ +tchar peekc, peekd; +tchar peekread; + +tchar *exclp; /* (Tail of) current word from ! subst */ +struct wordent *exclnxt; /* The rest of the ! subst words */ +int exclc; /* Count of remainig words in ! subst */ +tchar *alvecp; /* "Globp" for alias resubstitution */ + +/* + * Lex returns to its caller not only a wordlist (as a "var" parameter) + * but also whether a history substitution occurred. This is used in + * the main (process) routine to determine whether to echo, and also + * when called by the alias routine to determine whether to keep the + * argument list. + */ +bool hadhist; + +tchar getCtmp; +#define getC(f) ((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f)) +#define ungetC(c) peekc = c +#define ungetD(c) peekd = c + +lex(hp) + register struct wordent *hp; +{ + register struct wordent *wdp; + int c; + +#ifdef TRACE + tprintf("TRACE- lex()\n"); +#endif + lineloc = btell(); + hp->next = hp->prev = hp; + hp->word = S_ /*""*/; + alvecp = 0, hadhist = 0; + do + c = readc(0); + while (issp(c)); + /* make sure history is enabled */ + if (HIST && c == HISTSUB && intty) + /* ^lef^rit from tty is short !:s^lef^rit */ + getexcl(c); + else + unreadc(c); + wdp = hp; + /* + * The following loop is written so that the links needed + * by freelex will be ready and rarin to go even if it is + * interrupted. + */ + do { + register struct wordent *new = (struct wordent *) xalloc(sizeof *wdp); + + new->word = 0; + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + wdp->word = word(); + } while (wdp->word[0] != '\n'); +#ifdef TRACE + tprintf("Exiting lex()\n"); +#endif + hp->prev = wdp; + return (hadhist); +} + +prlex(sp0) + struct wordent *sp0; +{ + register struct wordent *sp = sp0->next; + +#ifdef TRACE + tprintf("TRACE- prlex()\n"); +#endif + for (;;) { + printf("%t", sp->word); + sp = sp->next; + if (sp == sp0) + break; + if (sp->word[0] != '\n') + Putchar(' '); + } +} + +copylex(hp, fp) + register struct wordent *hp; + register struct wordent *fp; +{ + register struct wordent *wdp; + +#ifdef TRACE + tprintf("TRACE- copylex()\n"); +#endif + wdp = hp; + fp = fp->next; + do { + register struct wordent *new = (struct wordent *) xalloc(sizeof *wdp); + + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + wdp->word = savestr(fp->word); + fp = fp->next; + } while (wdp->word[0] != '\n'); + hp->prev = wdp; +} + +freelex(vp) + register struct wordent *vp; +{ + register struct wordent *fp; + +#ifdef TRACE + tprintf("TRACE- freelex()\n"); +#endif + while (vp->next != vp) { + fp = vp->next; + vp->next = fp->next; + XFREE(fp->word) + XFREE( (char *)fp) + } + vp->prev = vp; +} + +tchar * +word() +{ + register tchar c, c1; + register tchar *wp; + tchar wbuf[BUFSIZ]; + register bool dolflg; + register int i; + +#ifdef TRACE + tprintf("TRACE- word()\n"); +#endif + wp = wbuf; + i = BUFSIZ - 4; +loop: + while (issp(c = getC(DOALL))) + ; + if (cmap(c, _META|_ESC)||isauxsp(c)) + switch (c) { + case '&': + case '|': + case '<': + case '>': + *wp++ = c; + c1 = getC(DOALL); + if (c1 == c) + *wp++ = c1; + else + ungetC(c1); + goto ret; + + case '#': + if (intty) + break; + c = 0; + do { + c1 = c; + c = getC(0); + } while (c != '\n'); + if (c1 == '\\') + goto loop; + /* fall into ... */ + + case ';': + case '(': + case ')': + case '\n': + *wp++ = c; + goto ret; + + case '\\': + c = getC(0); + if (c == '\n') { + if (onelflg == 1) + onelflg = 2; + goto loop; + } + if (c != HIST) + *wp++ = '\\', --i; + c |= QUOTE; + } + c1 = 0; + dolflg = DOALL; + for (;;) { + if (c1) { + if (c == c1) { + c1 = 0; + dolflg = DOALL; + } else if (c == '\\') { + c = getC(0); + if (c == HIST) + c |= QUOTE; + else { + if (c == '\n') + /* + if (c1 == '`') + c = ' '; + else + */ + c |= QUOTE; + ungetC(c); + c = '\\'; + } + } else if (c == '\n') { + seterrc(gettext("Unmatched "), c1); + ungetC(c); + break; + } + } else if (cmap(c, _META|_Q|_Q1|_ESC)||isauxsp(c)) { + if (c == '\\') { + c = getC(0); + if (c == '\n') { + if (onelflg == 1) + onelflg = 2; + break; + } + if (c != HIST) + *wp++ = '\\', --i; + c |= QUOTE; + } else if (cmap(c, _Q|_Q1)) { /* '"` */ + c1 = c; + dolflg = c == '"' ? DOALL : DOEXCL; + } else if (c != '#' || !intty) { + ungetC(c); + break; + } + } + if (--i > 0) { + *wp++ = c; + c = getC(dolflg); + } else { + seterr("Word too long"); + wp = &wbuf[1]; + break; + } + } +ret: + *wp = 0; +#ifdef TRACE + tprintf("word() returning:%t\n", wbuf); +#endif + return (savestr(wbuf)); +} + +getC1(flag) + register int flag; +{ + register tchar c; + +top: + if (c = peekc) { + peekc = 0; + return (c); + } + if (lap) { + if ((c = *lap++) == 0) + lap = 0; + else { + /* + * don't quote things if there was an error (err!=0) + * the input is original, not from a substitution and + * therefore should not be quoted + */ + if (!err && cmap(c, _META|_Q|_Q1)||isauxsp(c)) + c |= QUOTE; + return (c); + } + } + if (c = peekd) { + peekd = 0; + return (c); + } + if (exclp) { + if (c = *exclp++) + return (c); + if (exclnxt && --exclc >= 0) { + exclnxt = exclnxt->next; + setexclp(exclnxt->word); + return (' '); + } + exclp = 0; + exclnxt = 0; + } + if (exclnxt) { + exclnxt = exclnxt->next; + if (--exclc < 0) + exclnxt = 0; + else + setexclp(exclnxt->word); + goto top; + } + c = readc(0); + if (c == '$' && (flag & DODOL)) { + getdol(); + goto top; + } + if (c == HIST && (flag & DOEXCL)) { + getexcl(0); + goto top; + } + return (c); +} + +getdol() +{ + register tchar *np, *p; + tchar name[MAX_VREF_LEN]; + register int c; + int sc; + bool special = 0; + +#ifdef TRACE + tprintf("TRACE- getdol()\n"); +#endif + np = name, *np++ = '$'; + c = sc = getC(DOEXCL); + if (isspnl(c)) { + ungetD(c); + ungetC('$' | QUOTE); + return; + } + if (c == '{') + *np++ = c, c = getC(DOEXCL); + if (c == '#' || c == '?') + special++, *np++ = c, c = getC(DOEXCL); + *np++ = c; + switch (c) { + + case '<': + case '$': + case '*': + if (special) + goto vsyn; + goto ret; + + case '\n': + ungetD(c); + np--; + goto vsyn; + + default: + p = np; + if (digit(c)) { + /* make sure the variable names are MAX_VAR_LEN chars or less */ + while (digit(c = getC(DOEXCL)) && (np-p) < MAX_VAR_LEN) { + *np++ = c; + } + } else if (letter(c)) { + while ((letter(c = getC(DOEXCL)) || digit(c)) && + (np-p) < MAX_VAR_LEN ) { + *np++ = c; + } + } + else + goto vsyn; + + if( (np - p) > MAX_VAR_LEN ) + { + seterr("Variable name too long"); + goto ret; + } + } + if (c == '[') { + *np++ = c; + do { + c = getC(DOEXCL); + if (c == '\n') { + ungetD(c); + np--; + goto vsyn; + } + /* need to leave space for possible modifiers */ + if (np >= &name[MAX_VREF_LEN - 8]) + { + seterr("Variable reference too long"); + goto ret; + } + *np++ = c; + } while (c != ']'); + c = getC(DOEXCL); + } + if (c == ':') { + *np++ = c, c = getC(DOEXCL); + if (c == 'g') + *np++ = c, c = getC(DOEXCL); + *np++ = c; + if (!any(c, S_htrqxe)) + goto vsyn; + } else + ungetD(c); + if (sc == '{') { + c = getC(DOEXCL); + if (c != '}') { + ungetC(c); + goto vsyn; + } + *np++ = c; + } +ret: + *np = 0; + addla(name); + return; + +vsyn: + seterr("Variable syntax"); + goto ret; +} + +addla(cp) + tchar *cp; +{ + tchar *buf; + int len = 0; + +#ifdef TRACE + tprintf("TRACE- addla()\n"); +#endif + if (lap) { + len = strlen_(lap); + buf = xalloc((len+1) * sizeof (tchar)); + (void) strcpy_(buf, lap); + } + len += strlen_(cp); + + /* len+5 is allow 4 additional charecters just to be safe */ + labuf = xrealloc(labuf, (len+5) * sizeof (tchar)); + (void) strcpy_(labuf, cp); + if (lap) { + (void) strcat_(labuf, buf); + free(buf); + } + lap = labuf; +} + +tchar lhsb[32]; +tchar slhs[32]; +tchar rhsb[64]; +int quesarg; + +getexcl(sc) + tchar sc; +{ + register struct wordent *hp, *ip; + int left, right, dol; + register int c; + +#ifdef TRACE + tprintf("TRACE- getexcl()\n"); +#endif + if (sc == 0) { + sc = getC(0); + if (sc != '{') { + ungetC(sc); + sc = 0; + } + } + quesarg = -1; + lastev = eventno; + hp = gethent(sc); + if (hp == 0) + return; + hadhist = 1; + dol = 0; + if (hp == alhistp) + for (ip = hp->next->next; ip != alhistt; ip = ip->next) + dol++; + else + for (ip = hp->next->next; ip != hp->prev; ip = ip->next) + dol++; + left = 0, right = dol; + if (sc == HISTSUB) { + ungetC('s'), unreadc(HISTSUB), c = ':'; + goto subst; + } + c = getC(0); + /* if (!any(c, ":^$*-%")) */ /* change needed for char -> tchar */ + if (! (c == ':' || c == '^' || c == '$' || c == '*' || + c == '-' || c == '%' )) + goto subst; + left = right = -1; + if (c == ':') { + c = getC(0); + unreadc(c); + if (letter(c) || c == '&') { + c = ':'; + left = 0, right = dol; + goto subst; + } + } else + ungetC(c); + if (!getsel(&left, &right, dol)) + return; + c = getC(0); + if (c == '*') + ungetC(c), c = '-'; + if (c == '-') { + if (!getsel(&left, &right, dol)) + return; + c = getC(0); + } +subst: + exclc = right - left + 1; + while (--left >= 0) + hp = hp->next; + if (sc == HISTSUB || c == ':') { + do { + hp = getsub(hp); + c = getC(0); + } while (c == ':'); + } + unreadc(c); + if (sc == '{') { + c = getC(0); + if (c != '}') + seterr("Bad ! form"); + } + exclnxt = hp; +} + +struct wordent * +getsub(en) + struct wordent *en; +{ + register tchar *cp; + int delim; + register int c; + int sc; + bool global = 0; + tchar orhsb[(sizeof rhsb)/(sizeof rhsb[0])]; + +#ifdef TRACE + tprintf("TRACE- getsub()\n"); +#endif + exclnxt = 0; + sc = c = getC(0); + if (c == 'g') + global++, c = getC(0); + switch (c) { + + case 'p': + justpr++; + goto ret; + + case 'x': + case 'q': + global++; + /* fall into ... */ + + case 'h': + case 'r': + case 't': + case 'e': + break; + + case '&': + if (slhs[0] == 0) { + seterr("No prev sub"); + goto ret; + } + (void) strcpy_(lhsb, slhs); + break; + +/* + case '~': + if (lhsb[0] == 0) + goto badlhs; + break; +*/ + + case 's': + delim = getC(0); + if (alnum(delim) || isspnl(delim)){ + unreadc(delim); +bads: + lhsb[0] = 0; + seterr("Bad substitute"); + goto ret; + } + cp = lhsb; + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == delim) + break; + if (cp > &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2]) + goto bads; + if (c == '\\') { + c = getC(0); + if (c != delim && c != '\\') + *cp++ = '\\'; + } + *cp++ = c; + } + if (cp != lhsb) + *cp++ = 0; + else if (lhsb[0] == 0) { +/*badlhs:*/ + seterr("No prev lhs"); + goto ret; + } + cp = rhsb; + (void) strcpy_(orhsb, cp); + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == delim) + break; +/* + if (c == '~') { + if (&cp[strlen_(orhsb)] + > &rhsb[(sizeof rhsb)/(sizeof rhsb[0]) - 2]) + goto toorhs; + (void) strcpy_(cp, orhsb); + cp = strend(cp); + continue; + } +*/ + if (cp > &rhsb[(sizeof rhsb)/(sizeof rhsb[0]) - 2]) { +/*toorhs:*/ + seterr("Rhs too long"); + goto ret; + } + if (c == '\\') { + c = getC(0); + if (c != delim /* && c != '~' */) + *cp++ = '\\'; + } + *cp++ = c; + } + *cp++ = 0; + break; + + default: + if (c == '\n') + unreadc(c); + seterrc(gettext("Bad ! modifier: "), c); + goto ret; + } + (void) strcpy_(slhs, lhsb); + if (exclc) + en = dosub(sc, en, global); +ret: + return (en); +} + +struct wordent * +dosub(sc, en, global) + int sc; + struct wordent *en; + bool global; +{ + struct wordent lex; + bool didsub = 0; + struct wordent *hp = &lex; + register struct wordent *wdp; + register int i = exclc; + +#ifdef TRACE + tprintf("TRACE- dosub()\n"); +#endif + wdp = hp; + while (--i >= 0) { + register struct wordent *new = (struct wordent *) calloc(1, sizeof *wdp); + + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + en = en->next; + wdp->word = global || didsub == 0 ? + subword(en->word, sc, &didsub) : savestr(en->word); + } + if (didsub == 0) + seterr("Modifier failed"); + hp->prev = wdp; + return (&enthist(-1000, &lex, 0)->Hlex); +} + +tchar * +subword(cp, type, adid) + tchar *cp; + int type; + bool *adid; +{ + tchar wbuf[BUFSIZ]; + register tchar *wp, *mp, *np; + register int i; + +#ifdef TRACE + tprintf("TRACE- subword()\n"); +#endif + switch (type) { + + case 'r': + case 'e': + case 'h': + case 't': + case 'q': + case 'x': + wp = domod(cp, type); + if (wp == 0) + return (savestr(cp)); + *adid = 1; + return (wp); + + default: + wp = wbuf; + i = BUFSIZ - 4; + for (mp = cp; *mp; mp++) + if (matchs(mp, lhsb)) { + for (np = cp; np < mp;) + *wp++ = *np++, --i; + for (np = rhsb; *np; np++) switch (*np) { + + case '\\': + if (np[1] == '&') + np++; + /* fall into ... */ + + default: + if (--i < 0) + goto ovflo; + *wp++ = *np; + continue; + + case '&': + i -= strlen_(lhsb); + if (i < 0) + goto ovflo; + *wp = 0; + (void) strcat_(wp, lhsb); + wp = strend(wp); + continue; + } + mp += strlen_(lhsb); + i -= strlen_(mp); + if (i < 0) { +ovflo: + seterr("Subst buf ovflo"); + return (S_ /*""*/); + } + *wp = 0; + (void) strcat_(wp, mp); + *adid = 1; + return (savestr(wbuf)); + } + return (savestr(cp)); + } +} + +tchar * +domod(cp, type) + tchar *cp; + int type; +{ + register tchar *wp, *xp; + register int c; + +#ifdef TRACE + tprintf("TRACE- domod()\n"); +#endif + switch (type) { + + case 'x': + case 'q': + wp = savestr(cp); + for (xp = wp; c = *xp; xp++) + if (!issp(c) || type == 'q') + *xp |= QUOTE; + return (wp); + + case 'h': + case 't': + if (!any('/', cp)) + return (type == 't' ? savestr(cp) : 0); + wp = strend(cp); + while (*--wp != '/') + continue; + if (type == 'h') + xp = savestr(cp), xp[wp - cp] = 0; + else + xp = savestr(wp + 1); + return (xp); + + case 'e': + case 'r': + wp = strend(cp); + for (wp--; wp >= cp && *wp != '/'; wp--) + if (*wp == '.') { + if (type == 'e') + xp = savestr(wp + 1); + else + xp = savestr(cp), xp[wp - cp] = 0; + return (xp); + } + return (savestr(type == 'e' ? S_ /*""*/ : cp)); + } + return (0); +} + +matchs(str, pat) + register tchar *str, *pat; +{ + +#ifdef TRACE + tprintf("TRACE- matchs()\n"); +#endif + while (*str && *pat && *str == *pat) + str++, pat++; + return (*pat == 0); +} + +getsel(al, ar, dol) + register int *al, *ar; + int dol; +{ + register int c = getC(0); + register int i; + bool first = *al < 0; + +#ifdef TRACE + tprintf("TRACE- getsel()\n"); +#endif + switch (c) { + + case '%': + if (quesarg == -1) + goto bad; + if (*al < 0) + *al = quesarg; + *ar = quesarg; + break; + + case '-': + if (*al < 0) { + *al = 0; + *ar = dol - 1; + unreadc(c); + } + return (1); + + case '^': + if (*al < 0) + *al = 1; + *ar = 1; + break; + + case '$': + if (*al < 0) + *al = dol; + *ar = dol; + break; + + case '*': + if (*al < 0) + *al = 1; + *ar = dol; + if (*ar < *al) { + *ar = 0; + *al = 1; + return (1); + } + break; + + default: + if (digit(c)) { + i = 0; + while (digit(c)) { + i = i * 10 + c - '0'; + c = getC(0); + } + if (i < 0) + i = dol + 1; + if (*al < 0) + *al = i; + *ar = i; + } else + if (*al < 0) + *al = 0, *ar = dol; + else + *ar = dol - 1; + unreadc(c); + break; + } + if (first) { + c = getC(0); + unreadc(c); + /* if (any(c, "-$*")) */ /* char -> tchar */ + if (c == '-' || c == '$' || c == '*') + return (1); + } + if (*al > *ar || *ar > dol) { +bad: + seterr("Bad ! arg selector"); + return (0); + } + return (1); + +} + +struct wordent * +gethent(sc) + int sc; +{ + register struct Hist *hp; + register tchar *np; + register int c; + int event; + bool back = 0; + +#ifdef TRACE + tprintf("TRACE- gethent()\n"); +#endif + c = sc == HISTSUB ? HIST : getC(0); + if (c == HIST) { + if (alhistp) + return (alhistp); + event = eventno; + goto skip; + } + switch (c) { + + case ':': + case '^': + case '$': + case '*': + case '%': + ungetC(c); + if (lastev == eventno && alhistp) + return (alhistp); + event = lastev; + break; + + case '-': + back = 1; + c = getC(0); + goto number; + + case '#': /* !# is command being typed in (mrh) */ + return(¶ml); + + default: + /*if (any(c, "(=~")) {*/ + if (c == '(' || c == '=' || c == '~') { + unreadc(c); + ungetC(HIST); + return (0); + } + if (digit(c)) + goto number; + np = lhsb; + /* while (!any(c, ": \t\\\n}")) { */ + while (! (c == ':' || c == '\\' || isspnl(c) || c == '}')) { + if (np < &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2]) + *np++ = c; + c = getC(0); + } + unreadc(c); + if (np == lhsb) { + ungetC(HIST); + return (0); + } + *np++ = 0; + hp = findev(lhsb, 0); + if (hp) + lastev = hp->Hnum; + return (&hp->Hlex); + + case '?': + np = lhsb; + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == '?') + break; + if (np < &lhsb[(sizeof lhsb)/(sizeof lhsb[0]) - 2]) + *np++ = c; + } + if (np == lhsb) { + if (lhsb[0] == 0) { + seterr("No prev search"); + return (0); + } + } else + *np++ = 0; + hp = findev(lhsb, 1); + if (hp) + lastev = hp->Hnum; + return (&hp->Hlex); + + number: + event = 0; + while (digit(c)) { + event = event * 10 + c - '0'; + c = getC(0); + } + if (back) + event = eventno + (alhistp == 0) - (event ? event : 0); + unreadc(c); + break; + } +skip: + for (hp = Histlist.Hnext; hp; hp = hp->Hnext) + if (hp->Hnum == event) { + hp->Href = eventno; + lastev = hp->Hnum; + return (&hp->Hlex); + } + np = putn(event); + noev(np); + return (0); +} + +struct Hist * +findev(cp, anyarg) + tchar *cp; + bool anyarg; +{ + register struct Hist *hp; + +#ifdef TRACE + tprintf("TRACE- findev()\n"); +#endif + for (hp = Histlist.Hnext; hp; hp = hp->Hnext) { + tchar *dp; + register tchar *p, *q; + register struct wordent *lp = hp->Hlex.next; + int argno = 0; + + if (lp->word[0] == '\n') + continue; + if (!anyarg) { + p = cp; + q = lp->word; + do + if (!*p) + return (hp); + while (*p++ == *q++); + continue; + } + do { + for (dp = lp->word; *dp; dp++) { + p = cp; + q = dp; + do + if (!*p) { + quesarg = argno; + return (hp); + } + while (*p++ == *q++); + } + lp = lp->next; + argno++; + } while (lp->word[0] != '\n'); + } + noev(cp); + return (0); +} + +noev(cp) + tchar *cp; +{ + +#ifdef TRACE + tprintf("TRACE- noev()\n"); +#endif + seterr2(cp, ": Event not found"); +} + +setexclp(cp) + register tchar *cp; +{ + +#ifdef TRACE + tprintf("TRACE- setexclp()\n"); +#endif + if (cp && cp[0] == '\n') + return; + exclp = cp; +} + +unreadc(c) + tchar c; +{ + + peekread = c; +} + +readc(wanteof) + bool wanteof; +{ + register int c; + static sincereal; + + if (c = peekread) { + peekread = 0; + return (c); + } +top: + if (alvecp) { + if (c = *alvecp++) + return (c); + if (*alvec) { + alvecp = *alvec++; + return (' '); + } + } + if (alvec) { + if (alvecp = *alvec) { + alvec++; + goto top; + } + /* Infinite source! */ + return ('\n'); + } + if (evalp) { + if (c = *evalp++) + return (c); + if (*evalvec) { + evalp = *evalvec++; + return (' '); + } + evalp = 0; + } + if (evalvec) { + if (evalvec == (tchar **)1) { + doneinp = 1; + reset(); + } + if (evalp = *evalvec) { + evalvec++; + goto top; + } + evalvec = (tchar **)1; + return ('\n'); + } + do { + if (arginp == (tchar *) 1 || onelflg == 1) { + if (wanteof) + return (-1); + exitstat(); + } + if (arginp) { + if ((c = *arginp++) == 0) { + arginp = (tchar *) 1; + return ('\n'); + } + return (c); + } +reread: + c = bgetc(); + if (c < 0) { + struct sgttyb tty; + + if (wanteof) + return (-1); + /* was isatty but raw with ignoreeof yields problems */ + if (ioctl(SHIN, TIOCGETP, (char *)&tty) == 0 && + (tty.sg_flags & RAW) == 0) { + /* was 'short' for FILEC */ + int ctpgrp; + + if (++sincereal > 25) + goto oops; + if (tpgrp != -1 && + ioctl(FSHTTY, TIOCGPGRP, (char *)&ctpgrp) == 0 && + tpgrp != ctpgrp) { + (void) ioctl(FSHTTY, TIOCSPGRP, + (char *)&tpgrp); + (void) killpg(ctpgrp, SIGHUP); +printf("Reset tty pgrp from %d to %d\n", ctpgrp, tpgrp); + goto reread; + } + if (adrof(S_ignoreeof/*"ignoreeof"*/)) { + if (loginsh) + printf("\nUse \"logout\" to logout.\n"); + else + printf("\nUse \"exit\" to leave csh.\n"); + reset(); + } + if (chkstop == 0) { + panystop(1); + } + } +oops: + doneinp = 1; + reset(); + } + sincereal = 0; + if (c == '\n' && onelflg) + onelflg--; + } while (c == 0); + return (c); +} + +bgetc() +{ + register int buf, off, c; +#ifdef FILEC + tchar ttyline[BUFSIZ]; + register int numleft = 0, roomleft; +#endif + +#ifdef TELL + if (cantell) { + if (fseekp < fbobp || fseekp > feobp) { + fbobp = feobp = fseekp; + (void) lseek(SHIN, fseekp, 0); + } + if (fseekp == feobp) { + fbobp = feobp; + do + c = read_(SHIN, fbuf[0], BUFSIZ); + while (c < 0 && errno == EINTR); + if (c <= 0) + return (-1); + feobp += c; + } + c = fbuf[0][fseekp - fbobp]; + fseekp++; + return (c); + } +#endif +again: + buf = (int) fseekp / BUFSIZ; + if (buf >= fblocks) { + register tchar **nfbuf = + (tchar **) calloc((unsigned) (fblocks + 2), + sizeof (tchar **)); + + if (fbuf) { + (void) blkcpy(nfbuf, fbuf); + xfree( (char *)fbuf); + } + fbuf = nfbuf; + fbuf[fblocks] = (tchar *)calloc(BUFSIZ, sizeof (tchar)); + fblocks++; + goto again; + } + if (fseekp >= feobp) { + buf = (int) feobp / BUFSIZ; + off = (int) feobp % BUFSIZ; +#ifndef FILEC + for (;;) { + c = read_(SHIN, fbuf[buf] + off, BUFSIZ - off); +#else + roomleft = BUFSIZ - off; + for (;;) { + if (filec && intty) { + c = numleft ? numleft : tenex(ttyline, BUFSIZ); + if (c > roomleft) { + /* start with fresh buffer */ + feobp = fseekp = fblocks * BUFSIZ; + numleft = c; + goto again; + } + if (c > 0) + copy(fbuf[buf] + off, ttyline, c*sizeof(tchar)); + numleft = 0; + } else + c = read_(SHIN, fbuf[buf] + off, roomleft); +#endif + if (c >= 0) + break; + if (errno == EWOULDBLOCK) { + int off = 0; + + (void) ioctl(SHIN, FIONBIO, (char *)&off); + } else if (errno != EINTR) + break; + } + if (c <= 0) + return (-1); + feobp += c; +#ifndef FILEC + goto again; +#else + if (filec && !intty) + goto again; +#endif + } + c = fbuf[buf][(int) fseekp % BUFSIZ]; + fseekp++; + return (c); +} + +bfree() +{ + register int sb, i; + +#ifdef TELL + if (cantell) + return; +#endif + if (whyles) + return; + sb = (int) (fseekp - 1) / BUFSIZ; + if (sb > 0) { + for (i = 0; i < sb; i++) + xfree(fbuf[i]); + (void) blkcpy(fbuf, &fbuf[sb]); + fseekp -= BUFSIZ * sb; + feobp -= BUFSIZ * sb; + fblocks -= sb; + } +} + +bseek(l) + off_t l; +{ + register struct whyle *wp; + + fseekp = l; +#ifdef TELL + if (!cantell) { +#endif + if (!whyles) + return; + for (wp = whyles; wp->w_next; wp = wp->w_next) + continue; + if (wp->w_start > l) + l = wp->w_start; +#ifdef TELL + } +#endif +} + +/* any similarity to bell telephone is purely accidental */ +#ifndef btell +off_t +btell() +{ + + return (fseekp); +} +#endif + +btoeof() +{ + + (void) lseek(SHIN, (off_t)0, 2); + fseekp = feobp; + wfree(); + bfree(); +} + +#ifdef TELL +settell() +{ + + cantell = 0; + if (arginp || onelflg || intty) + return; + if (lseek(SHIN, (off_t)0, 1) < 0 || errno == ESPIPE) + return; + fbuf = (tchar **) calloc(2, sizeof (tchar **)); + fblocks = 1; + fbuf[0] = (tchar *)calloc(BUFSIZ, sizeof (tchar)); + fseekp = fbobp = feobp = lseek(SHIN, (off_t)0, 1); + cantell = 1; +} +#endif + diff --git a/usr/src/cmd/csh/sh.local.h b/usr/src/cmd/csh/sh.local.h new file mode 100644 index 0000000000..78bc22bb6b --- /dev/null +++ b/usr/src/cmd/csh/sh.local.h @@ -0,0 +1,58 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines certain local parameters + * A symbol should be defined in Makefile for local conditional + * compilation, e.g. IIASA or ERNIE, to be tested here and elsewhere. + */ + +/* + * Fundamental definitions which may vary from system to system. + * + * BUFSIZ The i/o buffering size; also limits word size + * SHELLPATH Where the shell will live; initalizes $shell + * MAILINTVL How often to mailcheck; more often is more expensive + * OTHERSH Shell for scripts which don't start with # + */ + +#define BUFSIZ 1024 /* default buffer size */ +#define PATHSIZ 16384 /* allow longer PATH environment variables */ +#define SHELLPATH "/bin/csh" +#define OTHERSH "/bin/sh" +#define FORKSLEEP 10 /* delay loop on non-interactive fork failure */ +#define MAILINTVL 600 /* 10 minutes */ + +/* + * The shell moves std in/out/diag and the old std input away from units + * 0, 1, and 2 so that it is easy to set up these standards for invoked + * commands. + */ +#define FSHTTY 15 /* /dev/tty when manip pgrps */ +#define FSHIN 16 /* Preferred desc for shell input */ +#define FSHOUT 17 /* ... shell output */ +#define FSHDIAG 18 /* ... shell diagnostics */ +#define FOLDSTD 19 /* ... old std input */ + +#ifdef IIASA +#undef OTHERSH +#endif + +#define copy(to, from, size) bcopy(from, to, size) + +#ifdef PROF +#define exit(n) done(n) +#endif diff --git a/usr/src/cmd/csh/sh.misc.c b/usr/src/cmd/csh/sh.misc.c new file mode 100644 index 0000000000..4f67243a5f --- /dev/null +++ b/usr/src/cmd/csh/sh.misc.c @@ -0,0 +1,482 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.tconst.h" +#include <fcntl.h> +#include <unistd.h> + +/* + * C Shell + */ + +any(c, s) + register int c; + register tchar *s; +{ + + while (s && *s) + if (*s++ == c) + return (1); + return (0); +} + +onlyread(cp) + tchar *cp; +{ + extern char end[]; + + return ((char *)cp < end); +} + + +/* + * WARNING: changes here also need to occur in the XFREE macro in sh.h. + */ + +xfree(cp) + char *cp; +{ + extern char end[]; + +#if defined(sparc) + if ((char *)cp >= end && (char *)cp < (char *)&cp) + free(cp); +#elif defined(i386) + if ((char *)cp >= end) + free(cp); +#else +#error xfree function is machine dependent and no machine type is recognized +#endif +} + +tchar * +savestr(s) + register tchar *s; +{ + tchar *n; + register tchar *p; + + if (s == 0) + s = S_ /* "" */; +#ifndef m32 + for (p = s; *p++; ) + ; + n = p = (tchar *)xalloc((unsigned) (p - s)*sizeof (tchar)); + while (*p++ = *s++) + ; + return (n); +#else + p = (tchar *) xalloc((strlen_(s) + 1)*sizeof (tchar)); + strcpy_(p, s); + return (p); +#endif +} + +void * +calloc(i, j) + register unsigned i; + unsigned j; +{ + register char *cp; + + i *= j; + cp = (char *)xalloc(i); + bzero(cp, (int)i); + return (cp); +} + +nomem(i) + unsigned i; +{ +#ifdef debug + static tchar *av[2] = {0, 0}; +#endif + + child++; +#ifndef debug + error("Out of memory"); +#ifdef lint + i = i; +#endif +#else + showall(av); + printf("i=%d: Out of memory\n", i); + chdir("/usr/bill/cshcore"); + abort(); +#endif + return (0); /* fool lint */ +} + +tchar ** +blkend(up) + register tchar **up; +{ + + while (*up) + up++; + return (up); +} + +blkpr(av) + register tchar **av; +{ + + for (; *av; av++) { + printf("%t", *av); + if (av[1]) + printf(" "); + } +} + +blklen(av) + register tchar **av; +{ + register int i = 0; + + while (*av++) + i++; + return (i); +} + +tchar ** +blkcpy(oav, bv) + tchar **oav; + register tchar **bv; +{ + register tchar **av = oav; + + while (*av++ = *bv++) + continue; + return (oav); +} + +tchar ** +blkcat(up, vp) + tchar **up, **vp; +{ + + (void) blkcpy(blkend(up), vp); + return (up); +} + +blkfree(av0) + tchar **av0; +{ + register tchar **av = av0; + + for (; *av; av++) + XFREE(*av) + XFREE((tchar *)av0) +} + +tchar ** +saveblk(v) + register tchar **v; +{ + register tchar **newv = + (tchar **) calloc((unsigned) (blklen(v) + 1), + sizeof (tchar **)); + tchar **onewv = newv; + + while (*v) + *newv++ = savestr(*v++); + return (onewv); +} + +tchar * +strspl(cp, dp) + tchar *cp, *dp; +{ + tchar *ep; + register tchar *p, *q; + +#ifndef m32 + for (p = cp; *p++; ) + ; + for (q = dp; *q++; ) + ; + ep = (tchar *) xalloc((unsigned) (((p - cp) + + (q - dp) - 1))*sizeof (tchar)); + for (p = ep, q = cp; *p++ = *q++; ) + ; + for (p--, q = dp; *p++ = *q++; ) + ; +#else + int len1 = strlen_(cp); + int len2 = strlen_(dp); + + ep = (tchar *)xalloc((unsigned) (len1 + len2 + 1)*sizeof (tchar)); + strcpy_(ep, cp); + strcat_(ep, dp); +#endif + return (ep); +} + +tchar ** +blkspl(up, vp) + register tchar **up, **vp; +{ + register tchar **wp = + (tchar **) calloc((unsigned) (blklen(up) + blklen(vp) + 1), + sizeof (tchar **)); + + (void) blkcpy(wp, up); + return (blkcat(wp, vp)); +} + +lastchr(cp) + register tchar *cp; +{ + + if (!*cp) + return (0); + while (cp[1]) + cp++; + return (*cp); +} + +donefds() +{ + (void) close(0); + (void) close(1); + (void) close(2); + + /* + * To avoid NIS+ functions to get hold of 0/1/2, + * use descriptor 0, and dup it to 1 and 2. + */ + open("/dev/null", 0); + dup(0); dup(0); + didfds = 0; +} + +/* + * Move descriptor i to j. + * If j is -1 then we just want to get i to a safe place, + * i.e. to a unit > 2. This also happens in dcopy. + */ +dmove(i, j) + register int i, j; +{ + int fd; + + if (i == j || i < 0) + return (i); + if (j >= 0) { + fd = dup2(i, j); + if (fd != -1) + setfd(fd); + } else + j = dcopy(i, j); + if (j != i) { + (void) close(i); + unsetfd(i); + } + return (j); +} + +dcopy(i, j) + register int i, j; +{ + + int fd; + + if (i == j || i < 0 || j < 0 && i > 2) + return (i); + if (j >= 0) { + fd = dup2(i, j); + if (fd != -1) + setfd(fd); + return (j); + } + (void) close(j); + unsetfd(j); + return (renum(i, j)); +} + +renum(i, j) + register int i, j; +{ + register int k = dup(i); + + if (k < 0) + return (-1); + if (j == -1 && k > 2) { + setfd(k); + return (k); + } + if (k != j) { + j = renum(k, j); + (void) close(k); /* no need ofr unsetfd() */ + return (j); + } + return (k); +} + +#ifndef copy +copy(to, from, size) + register tchar *to, *from; + register int size; +{ + + if (size) + do + *to++ = *from++; + while (--size != 0); +} +#endif + +/* + * Left shift a command argument list, discarding + * the first c arguments. Used in "shift" commands + * as well as by commands like "repeat". + */ +lshift(v, c) + register tchar **v; + register int c; +{ + register tchar **u = v; + + while (*u && --c >= 0) + xfree(*u++); + (void) blkcpy(v, u); +} + +number(cp) + tchar *cp; +{ + + if (*cp == '-') { + cp++; + if (!digit(*cp++)) + return (0); + } + while (*cp && digit(*cp)) + cp++; + return (*cp == 0); +} + +tchar ** +copyblk(v) + register tchar **v; +{ + register tchar **nv = + (tchar **) calloc((unsigned) (blklen(v) + 1), + sizeof (tchar **)); + + return (blkcpy(nv, v)); +} + +tchar * +strend(cp) + register tchar *cp; +{ + + while (*cp) + cp++; + return (cp); +} + +tchar * +strip(cp) + tchar *cp; +{ + register tchar *dp = cp; + + while (*dp++ &= TRIM) + continue; + return (cp); +} + +udvar(name) + tchar *name; +{ + + setname(name); + bferr("Undefined variable"); +} + +prefix(sub, str) + register tchar *sub, *str; +{ + + for (;;) { + if (*sub == 0) + return (1); + if (*str == 0) + return (0); + if (*sub++ != *str++) + return (0); + } +} + +/* + * blk*_ routines + */ + +char ** +blkend_(up) + register char **up; +{ + + while (*up) + up++; + return (up); +} + +blklen_(av) + register char **av; +{ + register int i = 0; + + while (*av++) + i++; + return (i); +} + +char ** +blkcpy_(oav, bv) + char **oav; + register char **bv; +{ + register char **av = oav; + + while (*av++ = *bv++) + continue; + return (oav); +} + +char ** +blkcat_(up, vp) + char **up, **vp; +{ + + (void) blkcpy_(blkend_(up), vp); + return (up); +} + +char ** +blkspl_(up, vp) + register char **up, **vp; +{ + register char **wp = + (char **) calloc((unsigned) (blklen_(up) + blklen_(vp) + 1), + sizeof (char **)); + + (void) blkcpy_(wp, up); + return (blkcat_(wp, vp)); +} diff --git a/usr/src/cmd/csh/sh.parse.c b/usr/src/cmd/csh/sh.parse.c new file mode 100644 index 0000000000..f77feca7bc --- /dev/null +++ b/usr/src/cmd/csh/sh.parse.c @@ -0,0 +1,696 @@ +/* + * Copyright 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.tconst.h" + +/* + * C shell + */ + +/* + * Perform aliasing on the word list lex + * Do a (very rudimentary) parse to separate into commands. + * If word 0 of a command has an alias, do it. + * Repeat a maximum of 20 times. + */ +alias(lex) + register struct wordent *lex; +{ + int aleft = 21; + jmp_buf osetexit; + +#ifdef TRACE + tprintf("TRACE- alias()\n"); +#endif + getexit(osetexit); + setexit(); + if (haderr) { + resexit(osetexit); + reset(); + } + if (--aleft == 0) + error("Alias loop"); + asyntax(lex->next, lex); + resexit(osetexit); +} + +asyntax(p1, p2) + register struct wordent *p1, *p2; +{ +#ifdef TRACE + tprintf("TRACE- asyntax()\n"); +#endif + + while (p1 != p2) + /* if (any(p1->word[0], ";&\n")) */ /* For char -> tchar */ + if (p1->word[0] == ';' || + p1->word[0] == '&' || + p1->word[0] == '\n') + p1 = p1->next; + else { + asyn0(p1, p2); + return; + } +} + +asyn0(p1, p2) + struct wordent *p1; + register struct wordent *p2; +{ + register struct wordent *p; + register int l = 0; + +#ifdef TRACE + tprintf("TRACE- asyn0()\n"); +#endif + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + if (l < 0) + error("Too many )'s"); + continue; + + case '>': + if (p->next != p2 && eq(p->next->word, S_AND /* "&"*/)) + p = p->next; + continue; + + case '&': + case '|': + case ';': + case '\n': + if (l != 0) + continue; + asyn3(p1, p); + asyntax(p->next, p2); + return; + } + if (l == 0) + asyn3(p1, p2); +} + +asyn3(p1, p2) + struct wordent *p1; + register struct wordent *p2; +{ + register struct varent *ap; + struct wordent alout; + register bool redid; + +#ifdef TRACE + tprintf("TRACE- asyn3()\n"); +#endif + if (p1 == p2) + return; + if (p1->word[0] == '(') { + for (p2 = p2->prev; p2->word[0] != ')'; p2 = p2->prev) + if (p2 == p1) + return; + if (p2 == p1->next) + return; + asyn0(p1->next, p2); + return; + } + ap = adrof1(p1->word, &aliases); + if (ap == 0) + return; + alhistp = p1->prev; + alhistt = p2; + alvec = ap->vec; + redid = lex(&alout); + alhistp = alhistt = 0; + alvec = 0; + if (err) { + freelex(&alout); + error("%s", gettext(err)); + } + if (p1->word[0] && eq(p1->word, alout.next->word)) { + tchar *cp = alout.next->word; + + alout.next->word = strspl(S_TOPBIT /*"\200"*/, cp); + XFREE(cp) + } + p1 = freenod(p1, redid ? p2 : p1->next); + if (alout.next != &alout) { + p1->next->prev = alout.prev->prev; + alout.prev->prev->next = p1->next; + alout.next->prev = p1; + p1->next = alout.next; + XFREE(alout.prev->word) + XFREE( (tchar *)alout.prev) + } + reset(); /* throw! */ +} + +struct wordent * +freenod(p1, p2) + register struct wordent *p1, *p2; +{ + register struct wordent *retp = p1->prev; + +#ifdef TRACE + tprintf("TRACE- freenod()\n"); +#endif + while (p1 != p2) { + XFREE(p1->word) + p1 = p1->next; + XFREE( (tchar *)p1->prev) + } + retp->next = p2; + p2->prev = retp; + return (retp); +} + +#define PHERE 1 +#define PIN 2 +#define POUT 4 +#define PDIAG 8 + +/* + * syntax + * empty + * syn0 + */ +struct command * +syntax(p1, p2, flags) + register struct wordent *p1, *p2; + int flags; +{ +#ifdef TRACE + tprintf("TRACE- syntax()\n"); +#endif + + while (p1 != p2) + /* if (any(p1->word[0], ";&\n")) */ /* for char -> tchar */ + if (p1->word[0] == ';' || + p1->word[0] == '&' || + p1->word[0] == '\n') + p1 = p1->next; + else + return (syn0(p1, p2, flags)); + return (0); +} + +/* + * syn0 + * syn1 + * syn1 & syntax + */ +struct command * +syn0(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + register struct command *t, *t1; + int l; + +#ifdef TRACE + tprintf("TRACE- syn0()\n"); +#endif + l = 0; + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + if (l < 0) + seterr("Too many )'s"); + continue; + + case '|': + if (p->word[1] == '|') + continue; + /* fall into ... */ + + case '>': + if (p->next != p2 && eq(p->next->word, S_AND /*"&"*/)) + p = p->next; + continue; + + case '&': + if (l != 0) + break; + if (p->word[1] == '&') + continue; + t1 = syn1(p1, p, flags); + if (t1->t_dtyp == TLST || + t1->t_dtyp == TAND || + t1->t_dtyp == TOR) { + t = (struct command *) calloc(1, sizeof (*t)); + t->t_dtyp = TPAR; + t->t_dflg = FAND|FINT; + t->t_dspr = t1; + t1 = t; + } else + t1->t_dflg |= FAND|FINT; + t = (struct command *) calloc(1, sizeof (*t)); + t->t_dtyp = TLST; + t->t_dflg = 0; + t->t_dcar = t1; + t->t_dcdr = syntax(p, p2, flags); + return(t); + } + if (l == 0) + return (syn1(p1, p2, flags)); + seterr("Too many ('s"); + return (0); +} + +/* + * syn1 + * syn1a + * syn1a ; syntax + */ +struct command * +syn1(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + register struct command *t; + int l; + +#ifdef TRACE + tprintf("TRACE- syn1()\n"); +#endif + l = 0; + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + continue; + + case ';': + case '\n': + if (l != 0) + break; + t = (struct command *) calloc(1, sizeof (*t)); + t->t_dtyp = TLST; + t->t_dcar = syn1a(p1, p, flags); + t->t_dcdr = syntax(p->next, p2, flags); + if (t->t_dcdr == 0) + t->t_dcdr = t->t_dcar, t->t_dcar = 0; + return (t); + } + return (syn1a(p1, p2, flags)); +} + +/* + * syn1a + * syn1b + * syn1b || syn1a + */ +struct command * +syn1a(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + register struct command *t; + register int l = 0; + +#ifdef TRACE + tprintf("TRACE- syn1a()\n"); +#endif + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + continue; + + case '|': + if (p->word[1] != '|') + continue; + if (l == 0) { + t = (struct command *) calloc(1, sizeof (*t)); + t->t_dtyp = TOR; + t->t_dcar = syn1b(p1, p, flags); + t->t_dcdr = syn1a(p->next, p2, flags); + t->t_dflg = 0; + return (t); + } + continue; + } + return (syn1b(p1, p2, flags)); +} + +/* + * syn1b + * syn2 + * syn2 && syn1b + */ +struct command * +syn1b(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + register struct command *t; + register int l = 0; + +#ifdef TRACE + tprintf("TRACE- syn1b()\n"); +#endif + l = 0; + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + continue; + + case '&': + if (p->word[1] == '&' && l == 0) { + t = (struct command *) calloc(1, sizeof (*t)); + t->t_dtyp = TAND; + t->t_dcar = syn2(p1, p, flags); + t->t_dcdr = syn1b(p->next, p2, flags); + t->t_dflg = 0; + return (t); + } + continue; + } + return (syn2(p1, p2, flags)); +} + +/* + * syn2 + * syn3 + * syn3 | syn2 + * syn3 |& syn2 + */ +struct command * +syn2(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p, *pn; + register struct command *t; + register int l = 0; + int f; + +#ifdef TRACE + tprintf("TRACE- syn2()\n"); +#endif + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + continue; + + case '|': + if (l != 0) + continue; + t = (struct command *) calloc(1, sizeof (*t)); + f = flags | POUT; + pn = p->next; + if (pn != p2 && pn->word[0] == '&') { + f |= PDIAG; + t->t_dflg |= FDIAG; + } + t->t_dtyp = TFIL; + t->t_dcar = syn3(p1, p, f); + if (pn != p2 && pn->word[0] == '&') + p = pn; + t->t_dcdr = syn2(p->next, p2, flags | PIN); + return (t); + } + return (syn3(p1, p2, flags)); +} + +tchar RELPAR[] = {'<', '>', '(', ')', 0}; /* "<>()" */ + +/* + * syn3 + * ( syn0 ) [ < in ] [ > out ] + * word word* [ < in ] [ > out ] + * KEYWORD ( word* ) word* [ < in ] [ > out ] + * + * KEYWORD = (@ exit foreach if set switch test while) + */ +struct command * +syn3(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + struct wordent *lp, *rp; + register struct command *t; + register int l; + tchar **av; + int n, c; + bool specp = 0; + +#ifdef TRACE + tprintf("TRACE- syn3()\n"); +#endif + if (p1 != p2) { + p = p1; +again: + switch (srchx(p->word)) { + + case ZELSE: + p = p->next; + if (p != p2) + goto again; + break; + + case ZEXIT: + case ZFOREACH: + case ZIF: + case ZLET: + case ZSET: + case ZSWITCH: + case ZWHILE: + specp = 1; + break; + } + } + n = 0; + l = 0; + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + if (specp) + n++; + l++; + continue; + + case ')': + if (specp) + n++; + l--; + continue; + + case '>': + case '<': + if (l != 0) { + if (specp) + n++; + continue; + } + if (p->next == p2) + continue; + if (any(p->next->word[0], RELPAR)) + continue; + n--; + continue; + + default: + if (!specp && l != 0) + continue; + n++; + continue; + } + if (n < 0) + n = 0; + t = (struct command *) calloc(1, sizeof (*t)); + av = (tchar **) calloc((unsigned) (n + 1), sizeof (tchar **)); + t->t_dcom = av; + n = 0; + if (p2->word[0] == ')') + t->t_dflg = FPAR; + lp = 0; + rp = 0; + l = 0; + for (p = p1; p != p2; p = p->next) { + c = p->word[0]; + switch (c) { + + case '(': + if (l == 0) { + if (lp != 0 && !specp) + seterr("Badly placed ("); + lp = p->next; + } + l++; + goto savep; + + case ')': + l--; + if (l == 0) + rp = p; + goto savep; + + case '>': + if (l != 0) + goto savep; + if (p->word[1] == '>') + t->t_dflg |= FCAT; + if (p->next != p2 && eq(p->next->word, S_AND /*"&"*/)) { + t->t_dflg |= FDIAG, p = p->next; + if (flags & (POUT|PDIAG)) + goto badout; + } + if (p->next != p2 && eq(p->next->word, S_EXAS /*"!"*/)) + t->t_dflg |= FANY, p = p->next; + if (p->next == p2) { +missfile: + seterr("Missing name for redirect"); + continue; + } + p = p->next; + if (any(p->word[0], RELPAR)) + goto missfile; + if ((flags & POUT) && (flags & PDIAG) == 0 || t->t_drit) +badout: + seterr("Ambiguous output redirect"); + else + t->t_drit = savestr(p->word); + continue; + + case '<': + if (l != 0) + goto savep; + if (p->word[1] == '<') + t->t_dflg |= FHERE; + if (p->next == p2) + goto missfile; + p = p->next; + if (any(p->word[0], RELPAR)) + goto missfile; + if ((flags & PHERE) && (t->t_dflg & FHERE)) + seterr("Can't << within ()'s"); + else if ((flags & PIN) || t->t_dlef) + seterr("Ambiguous input redirect"); + else + t->t_dlef = savestr(p->word); + continue; + +savep: + if (!specp) + continue; + default: + if (l != 0 && !specp) + continue; + if (err == 0) + av[n] = savestr(p->word); + n++; + continue; + } + } + if (lp != 0 && !specp) { + if (n != 0) + seterr("Badly placed ()'s"); + t->t_dtyp = TPAR; + t->t_dspr = syn0(lp, rp, PHERE); + } else { + if (n == 0) + seterr("Invalid null command"); + t->t_dtyp = TCOM; + } + return (t); +} + +freesyn(t) + register struct command *t; +{ +#ifdef TRACE + tprintf("TRACE- freesyn()\n"); +#endif + if (t == 0) + return; + switch (t->t_dtyp) { + + case TCOM: + blkfree(t->t_dcom); + if (t->cfname) + xfree(t->cfname); + if (t->cargs) + chr_blkfree(t->cargs); + goto lr; + + case TPAR: + freesyn(t->t_dspr); + /* fall into ... */ + +lr: + XFREE(t->t_dlef) + XFREE(t->t_drit) + break; + + case TAND: + case TOR: + case TFIL: + case TLST: + freesyn(t->t_dcar), freesyn(t->t_dcdr); + break; + } + XFREE( (tchar *)t) +} + + +chr_blkfree(vec) +register char **vec; +{ + register char **av; + + for (av = vec; *av; av++) + xfree(*av); + xfree(vec); +} diff --git a/usr/src/cmd/csh/sh.print.c b/usr/src/cmd/csh/sh.print.c new file mode 100644 index 0000000000..29fc570a09 --- /dev/null +++ b/usr/src/cmd/csh/sh.print.c @@ -0,0 +1,228 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" + +void p2dig_ull(unsigned long long i); +void p2dig_int(int i); +void flush(); + +/* + * C Shell + */ + +void +psecs_ull(unsigned long long l) +{ + unsigned long long i; + + i = l / 3600; + if (i) { + printf("%llu:", i); + i = l % 3600; + p2dig_ull(i / 60); + goto minsec; + } + i = l; + printf("%llu", i / 60); +minsec: + i %= 60; + printf(":"); + p2dig_ull(i); +} + +void +psecs_int(int l) +{ + int i; + + i = l / 3600; + if (i) { + printf("%d:", i); + i = l % 3600; + p2dig_int(i / 60); + goto minsec; + } + i = l; + printf("%d", i / 60); +minsec: + i %= 60; + printf(":"); + p2dig_int(i); +} + +void +p2dig_ull(unsigned long long i) +{ + printf("%llu%llu", i / 10, i % 10); +} + +void +p2dig_int(int i) +{ + printf("%d%d", i / 10, i % 10); +} + +char linbuf[128]; +char *linp = linbuf; + +#ifdef MBCHAR + +/* + * putbyte() send a byte to SHOUT. No interpretation is done + * except an un-QUOTE'd control character, which is displayed + * as ^x. + */ +void +putbyte(int c) +{ + + if ((c & QUOTE) == 0 && (c == 0177 || c < ' ' && c != '\t' && + c != '\n')) { + putbyte('^'); + if (c == 0177) { + c = '?'; + } else { + c |= 'A' - 1; + } + } + c &= TRIM; + *linp++ = c; + + if (c == '\n' || linp >= &linbuf[sizeof (linbuf) - 1 - MB_CUR_MAX]) { + /* 'cause the next Putchar() call may overflow the buffer. */ + flush(); + } +} + +/* + * Putchar(tc) does what putbyte(c) do for a byte c. + * Note that putbyte(c) just send the byte c (provided c is not + * a control character) as it is, while Putchar(tc) may expand the + * character tc to some byte sequnce that represents the character + * in EUC form. + */ +Putchar(tchar tc) +{ + int n; + + if (isascii(tc&TRIM)) { + putbyte((int)tc); + return; + } + tc &= TRIM; + n = wctomb(linp, tc); + if (n == -1) { + return; + } + linp += n; + if (linp >= &linbuf[sizeof (linbuf) - 1 - MB_CUR_MAX]) { + flush(); + } +} + +#else /* !MBCHAR */ + +/* + * putbyte() send a byte to SHOUT. No interpretation is done + * except an un-QUOTE'd control character, which is displayed + * as ^x. + */ +void +putbyte(int c) +{ + + if ((c & QUOTE) == 0 && (c == 0177 || c < ' ' && c != '\t' && + c != '\n')) { + putbyte('^'); + if (c == 0177) { + c = '?'; + } else { + c |= 'A' - 1; + } + } + c &= TRIM; + *linp++ = c; + if (c == '\n' || linp >= &linbuf[sizeof (linbuf) - 2]) { + flush(); + } +} + +/* + * Putchar(tc) does what putbyte(c) do for a byte c. + * For single-byte character only environment, there is no + * difference between Putchar() and putbyte() though. + */ +Putchar(tc) + tchar tc; +{ + putbyte((int)tc); +} + +#endif /* !MBCHAR */ + +void +draino() +{ + linp = linbuf; +} + +void +flush() +{ + int unit; + int lmode; + + if (linp == linbuf) { + return; + } + if (haderr) { + unit = didfds ? 2 : SHDIAG; + } else { + unit = didfds ? 1 : SHOUT; + } +#ifdef TIOCLGET + if (didfds == 0 && ioctl(unit, TIOCLGET, (char *)&lmode) == 0 && + lmode&LFLUSHO) { + lmode = LFLUSHO; + (void) ioctl(unit, TIOCLBIC, (char *)&lmode); + (void) write(unit, "\n", 1); + } +#endif + (void) write(unit, linbuf, linp - linbuf); + linp = linbuf; +} + +/* + * Should not be needed. + */ +void +write_string(char *s) +{ + int unit; + /* + * First let's make it sure to flush out things. + */ + flush(); + + if (haderr) { + unit = didfds ? 2 : SHDIAG; + } else { + unit = didfds ? 1 : SHOUT; + } + + (void) write(unit, s, strlen(s)); +} diff --git a/usr/src/cmd/csh/sh.proc.c b/usr/src/cmd/csh/sh.proc.c new file mode 100644 index 0000000000..195d2b07a1 --- /dev/null +++ b/usr/src/cmd/csh/sh.proc.c @@ -0,0 +1,1247 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.dir.h" +#include "sh.proc.h" +#include "wait.h" +#include "sh.tconst.h" + +/* + * C Shell - functions that manage processes, handling hanging, termination + */ + +#define BIGINDEX 9 /* largest desirable job index */ + +/* + * pchild - called at interrupt level by the SIGCHLD signal + * indicating that at least one child has terminated or stopped + * thus at least one wait system call will definitely return a + * childs status. Top level routines (like pwait) must be sure + * to mask interrupts when playing with the proclist data structures! + */ +void +pchild() +{ + register struct process *pp; + register struct process *fp; + register int pid; + union wait w; + int jobflags; + struct rusage ru; + +#ifdef TRACE + tprintf("TRACE- pchile()\n"); +#endif +loop: + pid = csh_wait3(&w, (setintr ? WNOHANG|WUNTRACED:WNOHANG), &ru); + /* + * SysV sends a SIGCHLD when the child process + * receives a SIGCONT, and result of that action is ignored here + */ + if ( w.w_status == WCONTFLG ) + return; + if (pid <= 0) { + if (errno == EINTR) { + errno = 0; + goto loop; + } + pnoprocesses = pid == -1; + return; + } + for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) + if (pid == pp->p_pid) + goto found; + goto loop; +found: + if (pid == atoi_(value(S_child /*"child"*/))) + unsetv(S_child /*"child"*/); + pp->p_flags &= ~(PRUNNING|PSTOPPED|PREPORTED); + if (WIFSTOPPED(w)) { + pp->p_flags |= PSTOPPED; + pp->p_reason = w.w_stopsig; + } else { + if (pp->p_flags & (PTIME|PPTIME) || adrof(S_time /*"time"*/)) + (void) gettimeofday(&pp->p_etime, (struct timezone *)0); + pp->p_rusage = ru; + if (WIFSIGNALED(w)) { + if (w.w_termsig == SIGINT) + pp->p_flags |= PINTERRUPTED; + else + pp->p_flags |= PSIGNALED; + if (w.w_coredump) + pp->p_flags |= PDUMPED; + pp->p_reason = w.w_termsig; + } else { + pp->p_reason = w.w_retcode; + if (pp->p_reason != 0) + pp->p_flags |= PAEXITED; + else + pp->p_flags |= PNEXITED; + } + } + jobflags = 0; + fp = pp; + do { + if ((fp->p_flags & (PPTIME|PRUNNING|PSTOPPED)) == 0 && + !child && adrof(S_time /*"time"*/) && + fp->p_rusage.ru_utime.tv_sec+fp->p_rusage.ru_stime.tv_sec >= + atoi_(value(S_time /*"time"*/))) + fp->p_flags |= PTIME; + jobflags |= fp->p_flags; + } while ((fp = fp->p_friends) != pp); + pp->p_flags &= ~PFOREGND; + if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { + pp->p_flags &= ~PPTIME; + pp->p_flags |= PTIME; + } + if ((jobflags & (PRUNNING|PREPORTED)) == 0) { + fp = pp; + do { + if (fp->p_flags&PSTOPPED) + fp->p_flags |= PREPORTED; + } while((fp = fp->p_friends) != pp); + while(fp->p_pid != fp->p_jobid) + fp = fp->p_friends; + if (jobflags&PSTOPPED) { + if (pcurrent && pcurrent != fp) + pprevious = pcurrent; + pcurrent = fp; + } else + pclrcurr(fp); + if (jobflags&PFOREGND) { + if (jobflags & (PSIGNALED|PSTOPPED|PPTIME) || +#ifdef IIASA + jobflags & PAEXITED || +#endif + !eq(dcwd->di_name, fp->p_cwd->di_name)) { + ; /* print in pjwait */ + } + } else { + if (jobflags&PNOTIFY || adrof(S_notify /*"notify"*/)) { + write_string("\015\n"); + flush(); + (void) pprint(pp, NUMBER|NAME|REASON); + if ((jobflags&PSTOPPED) == 0) + pflush(pp); + } else { + fp->p_flags |= PNEEDNOTE; + neednote++; + } + } + } + goto loop; +} + +pnote() +{ + register struct process *pp; + int flags, omask; + +#ifdef TRACE + tprintf("TRACE- pnote()\n"); +#endif + neednote = 0; + for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) { + if (pp->p_flags & PNEEDNOTE) { + omask = sigblock(sigmask(SIGCHLD)); + pp->p_flags &= ~PNEEDNOTE; + flags = pprint(pp, NUMBER|NAME|REASON); + if ((flags&(PRUNNING|PSTOPPED)) == 0) + pflush(pp); + (void) sigsetmask(omask); + } + } +} + +/* + * pwait - wait for current job to terminate, maintaining integrity + * of current and previous job indicators. + */ +pwait() +{ + register struct process *fp, *pp; + int omask; + +#ifdef TRACE + tprintf("TRACE- pwait()\n"); +#endif + /* + * Here's where dead procs get flushed. + */ + omask = sigblock(sigmask(SIGCHLD)); + for (pp = (fp = &proclist)->p_next; pp != PNULL; pp = (fp = pp)->p_next) + if (pp->p_pid == 0) { + fp->p_next = pp->p_next; + xfree(pp->p_command); + if (pp->p_cwd && --pp->p_cwd->di_count == 0) + if (pp->p_cwd->di_next == 0) + dfree(pp->p_cwd); + xfree( (tchar *)pp); + pp = fp; + } + (void) sigsetmask(omask); + pjwait(pcurrjob); +} + +/* + * pjwait - wait for a job to finish or become stopped + * It is assumed to be in the foreground state (PFOREGND) + */ +pjwait(pp) + register struct process *pp; +{ + register struct process *fp; + int jobflags, reason, omask; + +#ifdef TRACE + tprintf("TRACE- pjwait()\n"); +#endif + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + fp = pp; + do { + if ((fp->p_flags&(PFOREGND|PRUNNING)) == PRUNNING) + printf("BUG: waiting for background job!\n"); + } while ((fp = fp->p_friends) != pp); + /* + * Now keep pausing as long as we are not interrupted (SIGINT), + * and the target process, or any of its friends, are running + */ + fp = pp; + omask = sigblock(sigmask(SIGCHLD)); + for (;;) { + jobflags = 0; + do + jobflags |= fp->p_flags; + while ((fp = (fp->p_friends)) != pp); + if ((jobflags & PRUNNING) == 0) + break; + sigpause(sigblock(0) &~ sigmask(SIGCHLD)); + } + (void) sigsetmask(omask); + if (tpgrp > 0) /* get tty back */ + (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&tpgrp); + if ((jobflags&(PSIGNALED|PSTOPPED|PTIME)) || + !eq(dcwd->di_name, fp->p_cwd->di_name)) { + if (jobflags&PSTOPPED) + printf("\n"); + (void) pprint(pp, AREASON|SHELLDIR); + } + if ((jobflags&(PINTERRUPTED|PSTOPPED)) && setintr && + (!gointr || !eq(gointr, S_MINUS /*"-"*/))) { + if ((jobflags & PSTOPPED) == 0) + pflush(pp); + pintr1(0); + /*NOTREACHED*/ + } + reason = 0; + fp = pp; + do { + if (fp->p_reason) + reason = fp->p_flags & (PSIGNALED|PINTERRUPTED) ? + fp->p_reason | ABN_TERM : fp->p_reason; + } while ((fp = fp->p_friends) != pp); + set(S_status/*"status"*/, putn(reason)); + if (reason && exiterr) + exitstat(); + pflush(pp); +} + +/* + * dowait - wait for all processes to finish + */ +dowait() +{ + register struct process *pp; + int omask; + +#ifdef TRACE + tprintf("TRACE- dowait()\n"); +#endif + pjobs++; + omask = sigblock(sigmask(SIGCHLD)); +loop: + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_pid && /* pp->p_pid == pp->p_jobid && */ + pp->p_flags&PRUNNING) { + sigpause(0); + goto loop; + } + (void) sigsetmask(omask); + pjobs = 0; +} + +/* + * pflushall - flush all jobs from list (e.g. at fork()) + */ +pflushall() +{ + register struct process *pp; + +#ifdef TRACE + tprintf("TRACE- pflush()\n"); +#endif + for (pp = proclist.p_next; pp != PNULL; pp = pp->p_next) + if (pp->p_pid) + pflush(pp); +} + +/* + * pflush - flag all process structures in the same job as the + * the argument process for deletion. The actual free of the + * space is not done here since pflush is called at interrupt level. + */ +pflush(pp) + register struct process *pp; +{ + register struct process *np; + register int index; + +#ifdef TRACE + tprintf("TRACE- pflush()\n"); +#endif + if (pp->p_pid == 0) { + printf("BUG: process flushed twice"); + return; + } + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + pclrcurr(pp); + if (pp == pcurrjob) + pcurrjob = 0; + index = pp->p_index; + np = pp; + do { + np->p_index = np->p_pid = 0; + np->p_flags &= ~PNEEDNOTE; + } while ((np = np->p_friends) != pp); + if (index == pmaxindex) { + for (np = proclist.p_next, index = 0; np; np = np->p_next) + if (np->p_index > (tchar)index) + index = np->p_index; + pmaxindex = index; + } +} + +/* + * pclrcurr - make sure the given job is not the current or previous job; + * pp MUST be the job leader + */ +pclrcurr(pp) + register struct process *pp; +{ + +#ifdef TRACE + tprintf("TRACE- pclrcurr()\n"); +#endif + if (pp == pcurrent) + if (pprevious != PNULL) { + pcurrent = pprevious; + pprevious = pgetcurr(pp); + } else { + pcurrent = pgetcurr(pp); + pprevious = pgetcurr(pp); + } + else if (pp == pprevious) + pprevious = pgetcurr(pp); +} + +/* +4 here is 1 for '\0', 1 ea for << >& >> */ +tchar command[PMAXLEN+4]; +int cmdlen; +tchar *cmdp; +/* + * palloc - allocate a process structure and fill it up. + * an important assumption is made that the process is running. + */ +palloc(pid, t) + int pid; + register struct command *t; +{ + register struct process *pp; + int i; + +#ifdef TRACE + tprintf("TRACE- palloc()\n"); +#endif + pp = (struct process *)calloc(1, sizeof(struct process)); + pp->p_pid = pid; + pp->p_flags = t->t_dflg & FAND ? PRUNNING : PRUNNING|PFOREGND; + if (t->t_dflg & FTIME) + pp->p_flags |= PPTIME; + cmdp = command; + cmdlen = 0; + padd(t); + *cmdp++ = 0; + if (t->t_dflg & FPOU) { + pp->p_flags |= PPOU; + if (t->t_dflg & FDIAG) + pp->p_flags |= PDIAG; + } + pp->p_command = savestr(command); + if (pcurrjob) { + struct process *fp; + /* careful here with interrupt level */ + pp->p_cwd = 0; + pp->p_index = pcurrjob->p_index; + pp->p_friends = pcurrjob; + pp->p_jobid = pcurrjob->p_pid; + for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) + ; + fp->p_friends = pp; + } else { + pcurrjob = pp; + pp->p_jobid = pid; + pp->p_friends = pp; + pp->p_cwd = dcwd; + dcwd->di_count++; + if (pmaxindex < BIGINDEX) + pp->p_index = ++pmaxindex; + else { + struct process *np; + + for (i = 1; ; i++) { + for (np = proclist.p_next; np; np = np->p_next) + if (np->p_index == i) + goto tryagain; + pp->p_index = i; + if (i > pmaxindex) + pmaxindex = i; + break; + tryagain:; + } + } + pprevious = pcurrent; + pcurrent = pp; + } + pp->p_next = proclist.p_next; + proclist.p_next = pp; + (void) gettimeofday(&pp->p_btime, (struct timezone *)0); +} + +padd(t) + register struct command *t; +{ + tchar **argp; + +#ifdef TRACE + tprintf("TRACE- padd()\n"); +#endif + if (t == 0) + return; + switch (t->t_dtyp) { + + case TPAR: + pads(S_LBRASP /*"( "*/); + padd(t->t_dspr); + pads(S_SPRBRA /*" )"*/); + break; + + case TCOM: + for (argp = t->t_dcom; *argp; argp++) { + pads(*argp); + if (argp[1]) + pads(S_SP /*" "*/); + } + break; + + case TOR: + case TAND: + case TFIL: + case TLST: + padd(t->t_dcar); + switch (t->t_dtyp) { + case TOR: + pads(S_SPBARBARSP /*" || " */); + break; + case TAND: + pads(S_SPANDANDSP /*" && "*/); + break; + case TFIL: + pads(S_SPBARSP /*" | "*/); + break; + case TLST: + pads(S_SEMICOLONSP /*"; "*/); + break; + } + padd(t->t_dcdr); + return; + } + if ((t->t_dflg & FPIN) == 0 && t->t_dlef) { + pads((t->t_dflg & FHERE) ? S_SPLESLESSP /*" << " */ : S_SPLESSP /*" < "*/); + pads(t->t_dlef); + } + if ((t->t_dflg & FPOU) == 0 && t->t_drit) { + pads((t->t_dflg & FCAT) ? S_SPGTRGTRSP /*" >>" */ : S_SPGTR /*" >"*/); + if (t->t_dflg & FDIAG) + pads(S_AND /*"&"*/); + pads(S_SP /*" "*/); + pads(t->t_drit); + } +} + +pads(cp) + tchar *cp; +{ + register int i = strlen_(cp); + +#ifdef TRACE + tprintf("TRACE- pads()\n"); +#endif + if (cmdlen >= PMAXLEN) + return; + if (cmdlen + i >= PMAXLEN) { + (void) strcpy_(cmdp, S_SPPPP /*" ..."*/); + cmdlen = PMAXLEN; + cmdp += 4; + return; + } + (void) strcpy_(cmdp, cp); + cmdp += i; + cmdlen += i; +} + +/* + * psavejob - temporarily save the current job on a one level stack + * so another job can be created. Used for { } in exp6 + * and `` in globbing. + */ +psavejob() +{ + +#ifdef TRACE + tprintf("TRACE- psavejob()\n"); +#endif + pholdjob = pcurrjob; + pcurrjob = PNULL; +} + +/* + * prestjob - opposite of psavejob. This may be missed if we are interrupted + * somewhere, but pendjob cleans up anyway. + */ +prestjob() +{ + +#ifdef TRACE + tprintf("TRACE- prestjob()\n"); +#endif + pcurrjob = pholdjob; + pholdjob = PNULL; +} + +/* + * pendjob - indicate that a job (set of commands) has been completed + * or is about to begin. + */ +pendjob() +{ + register struct process *pp, *tp; + +#ifdef TRACE + tprintf("TRACE- pendjob()\n"); +#endif + if (pcurrjob && (pcurrjob->p_flags&(PFOREGND|PSTOPPED)) == 0) { + pp = pcurrjob; + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + printf("[%d]", pp->p_index); + tp = pp; + do { + printf(" %d", pp->p_pid); + pp = pp->p_friends; + } while (pp != tp); + printf("\n"); + } + pholdjob = pcurrjob = 0; +} + +/* + * pprint - print a job + */ +pprint(pp, flag) + register struct process *pp; +{ + register status, reason; + struct process *tp; + extern char *linp, linbuf[]; + int jobflags, pstatus; + char *format; + +#ifdef TRACE + tprintf("TRACE- pprint()\n"); +#endif + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { + pp->p_flags &= ~PPTIME; + pp->p_flags |= PTIME; + } + tp = pp; + status = reason = -1; + jobflags = 0; + do { + jobflags |= pp->p_flags; + pstatus = pp->p_flags & PALLSTATES; + if (tp != pp && linp != linbuf && !(flag&FANCY) && + (pstatus == status && pp->p_reason == reason || + !(flag&REASON))) + printf(" "); + else { + if (tp != pp && linp != linbuf) + printf("\n"); + if(flag&NUMBER) + if (pp == tp) + printf("[%d]%s %c ", pp->p_index, + pp->p_index < 10 ? " " : "", + pp==pcurrent ? '+' : + (pp == pprevious ? (tchar) '-' + : (tchar) ' ')); + else + printf(" "); + if (flag&FANCY) + printf("%5d ", pp->p_pid); + if (flag&(REASON|AREASON)) { + if (flag&NAME) + format = "%-21s"; + else + format = "%s"; + if (pstatus == status) + if (pp->p_reason == reason) { + printf(format, ""); + goto prcomd; + } else + reason = pp->p_reason; + else { + status = pstatus; + reason = pp->p_reason; + } + switch (status) { + + case PRUNNING: + printf(format, "Running "); + break; + + case PINTERRUPTED: + case PSTOPPED: + case PSIGNALED: + if ((flag&(REASON|AREASON)) + && reason != SIGINT + && reason != SIGPIPE) + printf(format, + strsignal(pp->p_reason)); + break; + + case PNEXITED: + case PAEXITED: + if (flag & REASON) + if (pp->p_reason) + printf("Exit %-16d", pp->p_reason); + else + printf(format, "Done"); + break; + + default: + printf("BUG: status=%-9o", status); + } + } + } +prcomd: + if (flag&NAME) { + printf("%t", pp->p_command); + if (pp->p_flags & PPOU) + printf(" |"); + if (pp->p_flags & PDIAG) + printf("&"); + } + if (flag&(REASON|AREASON) && pp->p_flags&PDUMPED) + printf(" (core dumped)"); + if (tp == pp->p_friends) { + if (flag&ERSAND) + printf(" &"); + if (flag&JOBDIR && + !eq(tp->p_cwd->di_name, dcwd->di_name)) { + printf(" (wd: "); + dtildepr(value(S_home /*"home"*/), tp->p_cwd->di_name); + printf(")"); + } + } + if (pp->p_flags&PPTIME && !(status&(PSTOPPED|PRUNNING))) { + if (linp != linbuf) + printf("\n\t"); + { static struct rusage zru; + prusage(&zru, &pp->p_rusage, &pp->p_etime, + &pp->p_btime); + } + } + if (tp == pp->p_friends) { + if (linp != linbuf) + printf("\n"); + if (flag&SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { + printf("(wd now: "); + dtildepr(value(S_home /* "home" */), dcwd->di_name); + printf(")\n"); + } + } + } while ((pp = pp->p_friends) != tp); + if (jobflags&PTIME && (jobflags&(PSTOPPED|PRUNNING)) == 0) { + if (jobflags & NUMBER) + printf(" "); + ptprint(tp); + } + return (jobflags); +} + +ptprint(tp) + register struct process *tp; +{ + struct timeval tetime, diff; + static struct timeval ztime; + struct rusage ru; + static struct rusage zru; + register struct process *pp = tp; + +#ifdef TRACE + tprintf("TRACE- ptprint()\n"); +#endif + ru = zru; + tetime = ztime; + do { + ruadd(&ru, &pp->p_rusage); + tvsub(&diff, &pp->p_etime, &pp->p_btime); + if (timercmp(&diff, &tetime, >)) + tetime = diff; + } while ((pp = pp->p_friends) != tp); + prusage(&zru, &ru, &tetime, &ztime); +} + +/* + * dojobs - print all jobs + */ +dojobs(v) + tchar **v; +{ + register struct process *pp; + register int flag = NUMBER|NAME|REASON; + int i; + +#ifdef TRACE + tprintf("TRACE- dojobs()\n"); +#endif + if (chkstop) + chkstop = 2; + if (*++v) { + if (v[1] || !eq(*v, S_DASHl /*"-l"*/)) + error("Usage: jobs [ -l ]"); + flag |= FANCY|JOBDIR; + } + for (i = 1; i <= pmaxindex; i++) + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_index == i && pp->p_pid == pp->p_jobid) { + pp->p_flags &= ~PNEEDNOTE; + if (!(pprint(pp, flag) & (PRUNNING|PSTOPPED))) + pflush(pp); + break; + } +} + +/* + * dofg - builtin - put the job into the foreground + */ +dofg(v) + tchar **v; +{ + register struct process *pp; + +#ifdef TRACE + tprintf("TRACE- dofg()\n"); +#endif + okpcntl(); + ++v; + do { + pp = pfind(*v); + pstart(pp, 1); + pjwait(pp); + } while (*v && *++v); +} + +/* + * %... - builtin - put the job into the foreground + */ +dofg1(v) + tchar **v; +{ + register struct process *pp; + +#ifdef TRACE + tprintf("TRACE- untty()\n"); +#endif + okpcntl(); + pp = pfind(v[0]); + pstart(pp, 1); + pjwait(pp); +} + +/* + * dobg - builtin - put the job into the background + */ +dobg(v) + tchar **v; +{ + register struct process *pp; + +#ifdef TRACE + tprintf("TRACE- dobg()\n"); +#endif + okpcntl(); + ++v; + do { + pp = pfind(*v); + pstart(pp, 0); + } while (*v && *++v); +} + +/* + * %... & - builtin - put the job into the background + */ +dobg1(v) + tchar **v; +{ + register struct process *pp; + +#ifdef TRACE + tprintf("TRACE- dobg1()\n"); +#endif + pp = pfind(v[0]); + pstart(pp, 0); +} + +/* + * dostop - builtin - stop the job + */ +dostop(v) + tchar **v; +{ + +#ifdef TRACE + tprintf("TRACE- dostop()\n"); +#endif + pkill(++v, SIGSTOP); +} + +/* + * dokill - builtin - superset of kill (1) + */ +dokill(v) + tchar **v; +{ + register int signum; + register tchar *name; + +#ifdef TRACE + tprintf("TRACE- dokill()\n"); +#endif + v++; + if (v[0] && v[0][0] == '-') { + if (v[0][1] == 'l') { + for (signum = 1; signum <= NSIG-1; signum++) { + char sbuf[BUFSIZ]; + if (sig2str(signum, sbuf) == 0) + printf("%s ", sbuf); + if (signum % 8 == 0) + Putchar('\n'); + } + Putchar('\n'); + return; + } + if (digit(v[0][1])) { + signum = atoi_(v[0]+1); + if (signum < 0 || signum > NSIG) + bferr("Bad signal number"); + } else { + int signo; + char sbuf[BUFSIZ]; + name = &v[0][1]; + tstostr(sbuf, name); + if (str2sig(sbuf, &signo) == 0) { + signum = signo; + goto gotsig; + } + if (eq(name, S_IOT /*"IOT"*/)) { + signum = SIGABRT; + goto gotsig; + } + setname(name); + bferr("Unknown signal; kill -l lists signals"); + } +gotsig: + v++; + } else + signum = SIGTERM; + pkill(v, signum); +} + +pkill(v, signum) + tchar **v; + int signum; +{ + register struct process *pp, *np; + register int jobflags = 0; + int omask, pid, err = 0; + tchar *cp; + +#ifdef TRACE + tprintf("TRACE- pkill()\n"); +#endif + omask = sigmask(SIGCHLD); + if (setintr) + omask |= sigmask(SIGINT); + omask = sigblock(omask) & ~omask; + while (*v) { + cp = globone(*v); + if (*cp == '%') { + np = pp = pfind(cp); + do + jobflags |= np->p_flags; + while ((np = np->p_friends) != pp); + switch (signum) { + + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + if ((jobflags & PRUNNING) == 0) { + /* %s -> %t */ + printf("%t: Already stopped\n", cp); + err++; + goto cont; + } + } + if (killpg(pp->p_jobid, signum) < 0) { + /* %s -> %t */ + printf("%t: ", cp); + printf("%s\n", strerror(errno)); + err++; + } + if (signum == SIGTERM || signum == SIGHUP) + (void) killpg(pp->p_jobid, SIGCONT); + } else if (!(digit(*cp) || *cp == '-')) + bferr("Arguments should be jobs or process id's"); + else { + pid = atoi_(cp); + if (kill(pid, signum) < 0) { + printf("%d: ", pid); + printf("%s\n", strerror(errno)); + err++; + goto cont; + } + if (signum == SIGTERM || signum == SIGHUP) + (void) kill(pid, SIGCONT); + } +cont: + xfree(cp); + v++; + } + (void) sigsetmask(omask); + if (err) + error(NULL); +} + +/* + * pstart - start the job in foreground/background + */ +pstart(pp, foregnd) + register struct process *pp; + int foregnd; +{ + register struct process *np; + int omask, jobflags = 0; + +#ifdef TRACE + tprintf("TRACE- pstart()\n"); +#endif + omask = sigblock(sigmask(SIGCHLD)); + np = pp; + do { + jobflags |= np->p_flags; + if (np->p_flags&(PRUNNING|PSTOPPED)) { + np->p_flags |= PRUNNING; + np->p_flags &= ~PSTOPPED; + if (foregnd) + np->p_flags |= PFOREGND; + else + np->p_flags &= ~PFOREGND; + } + } while((np = np->p_friends) != pp); + + if (foregnd) + pclrcurr(pp); + else + { + if ( pprevious && (pprevious->p_flags & PSTOPPED) ) + { + pcurrent = pprevious; + pprevious = pgetcurr(PNULL); + } + else + { + pcurrent = pgetcurr(pp); + if ( !pcurrent || (pcurrent->p_flags & PRUNNING) ) + pcurrent = pp; + else + pprevious = pp; + } + } + (void) pprint(pp, foregnd ? NAME|JOBDIR : NUMBER|NAME|AMPERSAND); + if (foregnd) + (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&pp->p_jobid); + if (jobflags&PSTOPPED) + (void) killpg(pp->p_jobid, SIGCONT); + (void) sigsetmask(omask); +} + +panystop(neednl) +{ + register struct process *pp; + +#ifdef TRACE + tprintf("TRACE- panystop()\n"); +#endif + chkstop = 2; + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_flags & PSTOPPED) + error("\nThere are stopped jobs" + 1 - neednl); +} + +struct process * +pfind(cp) + tchar *cp; +{ + register struct process *pp, *np; + +#ifdef TRACE + tprintf("TRACE- pfind()\n"); +#endif + if (cp == 0 || cp[1] == 0 || eq(cp, S_PARCENTPARCENT /*"%%"*/) || + eq(cp, S_PARCENTPLUS /*"%+"*/)) { + if (pcurrent == PNULL) + if ( (pcurrent = pgetcurr(PNULL)) == PNULL ) + bferr("No current job"); + return (pcurrent); + } + if (eq(cp, S_PARCENTMINUS /*"%-"*/) || + eq(cp, S_PARCENTSHARP /*"%#"*/)) { + if (pprevious == PNULL) + bferr("No previous job"); + return (pprevious); + } + if (digit(cp[1])) { + int index = atoi_(cp+1); + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_index == index && pp->p_pid == pp->p_jobid) + return (pp); + bferr("No such job"); + } + np = PNULL; + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_pid == pp->p_jobid) { + if (cp[1] == '?') { + register tchar *dp; + for (dp = pp->p_command; *dp; dp++) { + if (*dp != cp[2]) + continue; + if (prefix(cp+2, dp)) + goto match; + } + } else if (prefix(cp+1, pp->p_command)) { +match: + if (np) + bferr("Ambiguous"); + np = pp; + } + } + if (np) + return (np); + if (cp[1] == '?') + bferr("No job matches pattern"); + else + bferr("No such job"); + /*NOTREACHED*/ +} + +/* + * pgetcurr - find most recent job that is not pp, preferably stopped + */ +struct process * +pgetcurr(pp) + register struct process *pp; +{ + register struct process *np; + register struct process *xp = PNULL; + +#ifdef TRACE + tprintf("TRACE- pgetcurr()\n"); +#endif + for (np = proclist.p_next; np; np = np->p_next) + if (np != pcurrent && np != pp && np->p_pid && + np->p_pid == np->p_jobid) { + if (np->p_flags & PSTOPPED) + return (np); + if (xp == PNULL) + xp = np; + } + return (xp); +} + +/* + * donotify - flag the job so as to report termination asynchronously + */ +donotify(v) + tchar **v; +{ + register struct process *pp; + +#ifdef TRACE + tprintf("TRACE- donotify()\n"); +#endif + pp = pfind(*++v); + pp->p_flags |= PNOTIFY; +} + +/* + * Do the fork and whatever should be done in the child side that + * should not be done if we are not forking at all (like for simple builtin's) + * Also do everything that needs any signals fiddled with in the parent side + * + * Wanttty tells whether process and/or tty pgrps are to be manipulated: + * -1: leave tty alone; inherit pgrp from parent + * 0: already have tty; manipulate process pgrps only + * 1: want to claim tty; manipulate process and tty pgrps + * It is usually just the value of tpgrp. + */ +pfork(t, wanttty) + struct command *t; /* command we are forking for */ + int wanttty; +{ + register int pid; + bool ignint = 0; + int pgrp, omask; + int child_pid; + +#ifdef TRACE + tprintf("TRACE- pfork()\n"); +#endif + /* + * A child will be uninterruptible only under very special + * conditions. Remember that the semantics of '&' is + * implemented by disconnecting the process from the tty so + * signals do not need to ignored just for '&'. + * Thus signals are set to default action for children unless: + * we have had an "onintr -" (then specifically ignored) + * we are not playing with signals (inherit action) + */ + if (setintr) + ignint = (tpgrp == -1 && (t->t_dflg&FINT)) + || (gointr && eq(gointr, S_MINUS /*"-"*/)); + /* + * Hold SIGCHLD until we have the process installed in our table. + */ + omask = sigblock(sigmask(SIGCHLD)); + while ((pid = fork()) < 0) + if (setintr == 0) + sleep(FORKSLEEP); + else { + (void) sigsetmask(omask); + error("Fork failed"); + } + + /* + * setup the process group + */ + if (pid == 0) + child_pid = getpid(); + else + child_pid = pid; + pgrp = pcurrjob ? pcurrjob->p_jobid : child_pid; + + if (pid == 0) { + int sigttou; + settimes(); + pflushall(); + pcurrjob = PNULL; + child++; + if (setintr) { + setintr = 0; /* until I think otherwise */ + /* + * Children just get blown away on SIGINT, SIGQUIT + * unless "onintr -" seen. + */ + (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); + (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); + if (wanttty >= 0) { + /* make stoppable */ + (void) signal(SIGTSTP, SIG_DFL); + (void) signal(SIGTTIN, SIG_DFL); + (void) signal(SIGTTOU, SIG_DFL); + } + (void) signal(SIGTERM, parterm); + } else if (tpgrp == -1 && (t->t_dflg&FINT)) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + if (wanttty >= 0 && tpgrp >= 0) + (void) setpgid(0, pgrp); + if (wanttty > 0) { + sigttou = sigblock (sigmask(SIGTTOU)| + sigmask(SIGTTIN)| + sigmask(SIGTSTP)); + (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&pgrp); + sigsetmask (sigttou); + } + if (tpgrp > 0) + tpgrp = 0; /* gave tty away */ + /* + * Nohup and nice apply only to TCOM's but it would be + * nice (?!?) if you could say "nohup (foo;bar)" + * Then the parser would have to know about nice/nohup/time + */ + if (t->t_dflg & FNOHUP) + (void) signal(SIGHUP, SIG_IGN); + if (t->t_dflg & FNICE) + (void) setpriority(PRIO_PROCESS, 0, t->t_nice); + } else { + if (wanttty >= 0 && tpgrp >= 0) + setpgid(pid, pgrp); + palloc(pid, t); + (void) sigsetmask(omask); + } + + return (pid); +} + +okpcntl() +{ +#ifdef TRACE + tprintf("TRACE- okpcntl()\n"); +#endif + + if (tpgrp == -1) + error("No job control in this shell"); + if (tpgrp == 0) + error("No job control in subshells"); +} + diff --git a/usr/src/cmd/csh/sh.proc.h b/usr/src/cmd/csh/sh.proc.h new file mode 100644 index 0000000000..63e39ef437 --- /dev/null +++ b/usr/src/cmd/csh/sh.proc.h @@ -0,0 +1,95 @@ +/* + * Copyright 1999 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * C shell - process structure declarations + */ + +/* + * Structure for each process the shell knows about: + * allocated and filled by pcreate. + * flushed by pflush; freeing always happens at top level + * so the interrupt level has less to worry about. + * processes are related to "friends" when in a pipeline; + * p_friends links makes a circular list of such jobs + */ +struct process { + struct process *p_next; /* next in global "proclist" */ + struct process *p_friends; /* next in job list (or self) */ + struct directory *p_cwd; /* cwd of the job (only in head) */ + short unsigned p_flags; /* various job status flags */ + tchar p_reason; /* reason for entering this state */ + tchar p_index; /* shorthand job index */ + int p_pid; + int p_jobid; /* pid of job leader */ + /* if a job is stopped/background p_jobid gives its pgrp */ + struct timeval p_btime; /* begin time */ + struct timeval p_etime; /* end time */ + struct rusage p_rusage; + tchar *p_command; /* first PMAXLEN chars of command */ +}; + +/* added for status */ +#define ABN_TERM 0200 + +/* flag values for p_flags */ +#define PRUNNING (1<<0) /* running */ +#define PSTOPPED (1<<1) /* stopped */ +#define PNEXITED (1<<2) /* normally exited */ +#define PAEXITED (1<<3) /* abnormally exited */ +#define PSIGNALED (1<<4) /* terminated by a signal != SIGINT */ + +#define PALLSTATES (PRUNNING|PSTOPPED|PNEXITED|PAEXITED|PSIGNALED|PINTERRUPTED) +#define PNOTIFY (1<<5) /* notify async when done */ +#define PTIME (1<<6) /* job times should be printed */ +#define PAWAITED (1<<7) /* top level is waiting for it */ +#define PFOREGND (1<<8) /* started in shells pgrp */ +#define PDUMPED (1<<9) /* process dumped core */ +#define PDIAG (1<<10) /* diagnostic output also piped out */ +#define PPOU (1<<11) /* piped output */ +#define PREPORTED (1<<12) /* status has been reported */ +#define PINTERRUPTED (1<<13) /* job stopped via interrupt signal */ +#define PPTIME (1<<14) /* time individual process */ +#define PNEEDNOTE (1<<15) /* notify as soon as practical */ + +#define PNULL (struct process *)0 +#define PMAXLEN 80 + +/* defines for arguments to pprint */ +#define NUMBER 01 +#define NAME 02 +#define REASON 04 +#define AMPERSAND 010 +#define FANCY 020 +#define SHELLDIR 040 /* print shell's dir if not the same */ +#define JOBDIR 0100 /* print job's dir if not the same */ +#define AREASON 0200 + +struct process proclist; /* list head of all processes */ +bool pnoprocesses; /* pchild found nothing to wait for */ + +struct process *pholdjob; /* one level stack of current jobs */ + +struct process *pcurrjob; /* current job */ +struct process *pcurrent; /* current job in table */ +struct process *pprevious; /* previous job in table */ + +short pmaxindex; /* current maximum job index */ + +int psigint(); +struct process *pgetcurr(); +struct process *plookup(); +struct process *pfind(); diff --git a/usr/src/cmd/csh/sh.sem.c b/usr/src/cmd/csh/sh.sem.c new file mode 100644 index 0000000000..e55a747e12 --- /dev/null +++ b/usr/src/cmd/csh/sh.sem.c @@ -0,0 +1,523 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <fcntl.h> +#include "sh.h" +#include "sh.proc.h" +#include "sh.tconst.h" + +/* + * C shell + */ + +/* + * Return true if there is a back-quote (`) anywhere in the argument list. + * Its presence would cause glob() to be invoked in the child process + * and this would cause chaos if the child is created with vfork(). + */ +static bool +AnyBquote(struct command *t) +{ + tchar **pp; + tchar *p; + + if (noexec) + return (0); + for (pp = t->t_dcom; p = *pp++;) { + if (any('`', p)) + return (1); + } + return (0); +} + +/*VARARGS 1*/ +execute(t, wanttty, pipein, pipeout) + register struct command *t; + int wanttty, *pipein, *pipeout; +{ + bool forked = 0; + struct biltins *bifunc; + int pid = 0; + int pv[2]; + extern int globcnt; +#ifdef TRACE + tprintf("TRACE- execute()\n"); +#endif + + if (t == 0) + return; + if ((t->t_dflg & FAND) && wanttty > 0) + wanttty = 0; + switch (t->t_dtyp) { + + case TCOM: + if (t->t_dcom[0][0] == (tchar)S_TOPBIT[0]) + (void) strcpy_(t->t_dcom[0], t->t_dcom[0] + 1); + if ((t->t_dflg & FREDO) == 0) + Dfix(t); /* $ " ' \ */ + if (t->t_dcom[0] == 0) + return; + /* fall into... */ + + case TPAR: + if (t->t_dflg & FPOU) + mypipe(pipeout); + /* + * Must do << early so parent will know + * where input pointer should be. + * If noexec then this is all we do. + */ + if (t->t_dflg & FHERE) { + (void) close(0); + unsetfd(0); + heredoc(t->t_dlef); + if (noexec) { + (void) close(0); + unsetfd(0); + } + } + if (noexec) + break; + + set(S_status, S_0); + + /* + * This mess is the necessary kludge to handle the prefix + * builtins: nice, nohup, time. These commands can also + * be used by themselves, and this is not handled here. + * This will also work when loops are parsed. + */ + while (t->t_dtyp == TCOM) + if (eq(t->t_dcom[0], S_nice /*"nice"*/)) + if (t->t_dcom[1]) + /*if (any(t->t_dcom[1][0], "+-"))*/ + if (t->t_dcom[1][0] == '+' || + t->t_dcom[1][0] == '-') + if (t->t_dcom[2]) { + setname(S_nice /*"nice"*/); + t->t_nice = getn(t->t_dcom[1]); + lshift(t->t_dcom, 2); + t->t_dflg |= FNICE; + } else + break; + else { + t->t_nice = 4; + lshift(t->t_dcom, 1); + t->t_dflg |= FNICE; + } + else + break; + else if (eq(t->t_dcom[0], S_nohup /*"nohup"*/)) + if (t->t_dcom[1]) { + t->t_dflg |= FNOHUP; + lshift(t->t_dcom, 1); + } else + break; + else if (eq(t->t_dcom[0], S_time /*"time"*/)) + if (t->t_dcom[1]) { + t->t_dflg |= FTIME; + lshift(t->t_dcom, 1); + } else + break; + else + break; + /* + * Check if we have a builtin function and remember which one. + */ + bifunc = t->t_dtyp == TCOM ? isbfunc(t) : (struct biltins *) 0; + + /* + * We fork only if we are timed, or are not the end of + * a parenthesized list and not a simple builtin function. + * Simple meaning one that is not pipedout, niced, nohupped, + * or &'d. + * It would be nice(?) to not fork in some of these cases. + */ + if (((t->t_dflg & FTIME) || (t->t_dflg & FPAR) == 0 && + (!bifunc || t->t_dflg & (FPOU|FAND|FNICE|FNOHUP)))) +#ifdef VFORK + if (t->t_dtyp == TPAR || t->t_dflg&(FREDO|FAND) || + bifunc || AnyBquote(t)) +#endif + { forked++; pid = pfork(t, wanttty); } +#ifdef VFORK + else { + void vffree(); + struct sv { + int mask, child, setintr, haderr, didfds; + int SHIN, SHOUT, SHDIAG, OLDSTD, tpgrp; + struct sigvec sigv; + } sv; + + /* + * Prepare for the vfork by saving everything + * that the child corrupts before it exec's. + * Note that in some signal implementations + * which keep the signal info in user space + * (e.g. Sun's) it will also be necessary to + * save and restore the current sigvec's for + * the signals the child touches before it + * exec's. + */ + sv.mask = sigblock(sigmask(SIGCHLD)); + sv.child = child; sv.setintr = setintr; + sv.haderr = haderr; sv.didfds = didfds; + sv.SHIN = SHIN; sv.SHOUT = SHOUT; + sv.SHDIAG = SHDIAG; sv.OLDSTD = OLDSTD; + sv.tpgrp = tpgrp; + Vsav = Vdp = 0; Vav = 0; + (void) sigvec(SIGINT, (struct sigvec *)0, &sv.sigv); + pid = vfork(); + if (pid < 0) { + (void) sigsetmask(sv.mask); + error("Vfork failed"); + } + forked++; + if (pid) { /* parent */ + int ppid; + closelog(); + child = sv.child; setintr = sv.setintr; + haderr = sv.haderr; didfds = sv.didfds; + SHIN = sv.SHIN; + SHOUT = sv.SHOUT; SHDIAG = sv.SHDIAG; + OLDSTD = sv.OLDSTD; tpgrp = sv.tpgrp; + xfree(Vsav); Vsav = 0; + xfree(Vdp); Vdp = 0; + xfree( (tchar *)Vav); Vav = 0; + /* this is from pfork() */ + ppid = pcurrjob ? pcurrjob->p_jobid : pid; + if (wanttty >= 0 && tpgrp >= 0) + setpgid (ppid, ppid); + palloc(pid, t); + /* + * Restore SIGINT handler. + */ + (void) sigvec(SIGINT, &sv.sigv, (struct sigvec *)0); + (void) sigsetmask(sv.mask); + } else { /* child */ + /* this is from pfork() */ + int pgrp; + bool ignint = 0; + int sigttou; + if (setintr) + ignint = + (tpgrp == -1 && (t->t_dflg&FINT)) + || gointr + && eq(gointr, S_MINUS/*"-"*/); + pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); + child++; + if (setintr) { + setintr = 0; +#ifdef notdef + (void) signal(SIGCHLD, SIG_DFL); +#endif + (void) signal(SIGINT, ignint ? + SIG_IGN : vffree); + (void) signal(SIGQUIT, ignint ? + SIG_IGN : SIG_DFL); + if (wanttty >= 0) { + (void) signal(SIGTSTP, SIG_DFL); + (void) signal(SIGTTIN, SIG_DFL); + (void) signal(SIGTTOU, SIG_DFL); + } + (void) signal(SIGTERM, parterm); + } else if (tpgrp == -1 && (t->t_dflg&FINT)) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + if (wanttty >= 0 && tpgrp >= 0) + (void) setpgid(0, pgrp); + if (wanttty > 0) { + sigttou = sigblock ( + sigmask(SIGTTOU) | + sigmask(SIGTTIN) | + sigmask(SIGTSTP)); + (void) ioctl(FSHTTY, TIOCSPGRP, + (tchar *)&pgrp); + sigsetmask (sigttou); + } + if (tpgrp > 0) + tpgrp = 0; + if (t->t_dflg & FNOHUP) + (void) signal(SIGHUP, SIG_IGN); + if (t->t_dflg & FNICE) + (void) setpriority(PRIO_PROCESS, + 0, t->t_nice); + } + + } +#endif + if (pid != 0) { + /* + * It would be better if we could wait for the + * whole job when we knew the last process + * had been started. Pwait, in fact, does + * wait for the whole job anyway, but this test + * doesn't really express our intentions. + */ + if (didfds==0 && t->t_dflg&FPIN) { + (void) close(pipein[0]); + unsetfd(pipein[0]); + (void) close(pipein[1]); + unsetfd(pipein[1]); + } + if ((t->t_dflg & (FPOU|FAND)) == 0) + pwait(); + break; + } + doio(t, pipein, pipeout); + if (t->t_dflg & FPOU) { + (void) close(pipeout[0]); + (void) unsetfd(pipeout[0]); + (void) close(pipeout[1]); + (void) unsetfd(pipeout[1]); + } + + /* + * Perform a builtin function. + * If we are not forked, arrange for possible stopping + */ + if (bifunc) { + func(t, bifunc); + if (forked) + exitstat(); + break; + } + if (t->t_dtyp != TPAR) { + doexec(t); + /*NOTREACHED*/ + } + /* + * For () commands must put new 0,1,2 in FSH* and recurse + */ + OLDSTD = dcopy(0, FOLDSTD); + SHOUT = dcopy(1, FSHOUT); + SHDIAG = dcopy(2, FSHDIAG); + (void) close(SHIN); + (void) unsetfd(SHIN); + SHIN = -1; + didfds = 0; + wanttty = -1; + t->t_dspr->t_dflg |= t->t_dflg & FINT; + execute(t->t_dspr, wanttty); + exitstat(); + + case TFIL: + t->t_dcar->t_dflg |= FPOU | + (t->t_dflg & (FPIN|FAND|FDIAG|FINT)); + execute(t->t_dcar, wanttty, pipein, pv); + t->t_dcdr->t_dflg |= FPIN | + (t->t_dflg & (FPOU|FAND|FPAR|FINT)); + if (wanttty > 0) + wanttty = 0; /* got tty already */ + execute(t->t_dcdr, wanttty, pv, pipeout); + break; + + case TLST: + if (t->t_dcar) { + t->t_dcar->t_dflg |= t->t_dflg & FINT; + execute(t->t_dcar, wanttty); + /* + * In strange case of A&B make a new job after A + */ + if (t->t_dcar->t_dflg&FAND && t->t_dcdr && + (t->t_dcdr->t_dflg&FAND) == 0) + pendjob(); + } + if (t->t_dcdr) { + t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT); + execute(t->t_dcdr, wanttty); + } + break; + + case TOR: + case TAND: + if (t->t_dcar) { + t->t_dcar->t_dflg |= t->t_dflg & FINT; + execute(t->t_dcar, wanttty); + if ((getn(value(S_status/*"status"*/)) == 0) != (t->t_dtyp == TAND)) + return; + } + if (t->t_dcdr) { + t->t_dcdr->t_dflg |= t->t_dflg & (FPAR|FINT); + execute(t->t_dcdr, wanttty); + } + break; + } + /* + * Fall through for all breaks from switch + * + * If there will be no more executions of this + * command, flush all file descriptors. + * Places that turn on the FREDO bit are responsible + * for doing donefds after the last re-execution + */ + if (didfds && !(t->t_dflg & FREDO)) + donefds(); + + /* + * If glob() was called and arguments list is not yet + * free'ed, free them here. + */ + if (gargv) { + blkfree(gargv); + gargv = 0; + globcnt = 0; + } +} + +#ifdef VFORK +void +vffree() +{ + register tchar **v; + +#ifdef TRACE + tprintf("TRACE- vffree()\n"); +#endif + if (v = gargv) + gargv = 0, xfree( (tchar *)v); + if (v = pargv) + pargv = 0, xfree( (tchar *)v); + _exit(1); +} +#endif + +/* + * Perform io redirection. + * We may or maynot be forked here. + */ +doio(t, pipein, pipeout) + register struct command *t; + int *pipein, *pipeout; +{ + register tchar *cp, *dp; + register int flags = t->t_dflg; + int fd; + +#ifdef TRACE + tprintf("TRACE- doio()\n"); +#endif + if (didfds || (flags & FREDO)) + return; + if ((flags & FHERE) == 0) { /* FHERE already done */ + (void) close(0); + (void) unsetfd(0); + if (cp = t->t_dlef) { + dp = Dfix1(cp); + cp = globone(dp); + xfree(dp); + xfree(cp); + if (open_(cp, 0) < 0) + Perror(cp); + } else if (flags & FPIN) { + fd = dup(pipein[0]); + if (fd != -1) + setfd(fd); + (void) close(pipein[0]); + (void) unsetfd(pipein[0]); + (void) close(pipein[1]); + (void) unsetfd(pipein[1]); + } else if ((flags & FINT) && tpgrp == -1) { + (void) close(0); /* no need for unsetfd */ + (void) open("/dev/null", 0); /* no need for setfd */ + } else { + fd = dup(OLDSTD); + if (fd != -1) + setfd(fd); + } + } + (void) close(1); + (void) unsetfd(1); + if (cp = t->t_drit) { + dp = Dfix1(cp); + cp = globone(dp); + xfree(dp); + if ((flags & FCAT) && open_(cp, 1) >= 0) + (void) lseek(1, (off_t)0, 2); + else { + if (!(flags & FANY) && adrof(S_noclobber/*"noclobber"*/)) { + if (flags & FCAT) + Perror(cp); + chkclob(cp); + } + if (creat_(cp, 0666) < 0) + Perror(cp); + } + xfree(cp); + } else if (flags & FPOU) { + fd = dup(pipeout[1]); + if (fd != -1) + setfd (fd); + } + else { + fd = dup(SHOUT); + if (fd != -1) + setfd(fd); + } + + (void) close(2); + (void) unsetfd(2); + if (flags & FDIAG) { + fd = dup(1); + if (fd != -1) + setfd(fd); + } + else { + fd = dup(SHDIAG); + if (fd != -1) + setfd(fd); + } + didfds = 1; +} + +mypipe(pv) + register int *pv; +{ + +#ifdef TRACE + tprintf("TRACE- mypipe()\n"); +#endif + if (pipe(pv) < 0) + goto oops; + setfd(pv[0]); + setfd(pv[1]); + + pv[0] = dmove(pv[0], -1); + pv[1] = dmove(pv[1], -1); + if (pv[0] >= 0 && pv[1] >= 0) + return; +oops: + error("Can't make pipe"); +} + +chkclob(cp) + register tchar *cp; +{ + struct stat stb; + unsigned short type; + +#ifdef TRACE + tprintf("TRACE- chkclob()\n"); +#endif + if (stat_(cp, &stb) < 0) + return; + type = stb.st_mode & S_IFMT; + if (type == S_IFCHR || type == S_IFIFO) + return; + error("%t: File exists", cp); +} diff --git a/usr/src/cmd/csh/sh.set.c b/usr/src/cmd/csh/sh.set.c new file mode 100644 index 0000000000..b6d03027b6 --- /dev/null +++ b/usr/src/cmd/csh/sh.set.c @@ -0,0 +1,804 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.tconst.h" +extern didchdir; + +/* + * C Shell + */ + +doset(v) + register tchar **v; +{ + register tchar *p; + tchar *vp, op; + tchar **vecp; + bool hadsub; + int subscr; + tchar *retp; + +#ifdef TRACE + tprintf("TRACE- doset()\n"); +#endif + v++; + p = *v++; + if (p == 0) { + prvars(); + return; + } + do { + hadsub = 0; + /* + * check for proper variable syntax + * must be alphanumeric, start with a letter and + * be at most 20 characters + */ + for (vp = p; alnum(*p); p++) + continue; + if (vp == p || !letter(*vp)) + goto setsyn; + if ( (p - vp) > MAX_VAR_LEN ) + bferr("Variable name too long"); + if (*p == '[') { + hadsub++; + p = getinx(p, &subscr); + } + if (op = *p) { + *p++ = 0; + if (*p == 0 && *v && **v == '(') + p = *v++; + } else if (*v && eq(*v, S_EQ/*"="*/)) { + op = '=', v++; + if (*v) + p = *v++; + } + if (op && op != '=') +setsyn: + bferr("Syntax error"); + if (eq(p, S_LPAR/*"("*/)) { + register tchar **e = v; + + if (hadsub) + goto setsyn; + for (;;) { + if (!*e) + bferr("Missing )"); + if (**e == ')') + break; + e++; + } + p = *e; + *e = 0; + vecp = saveblk(v); + set1(vp, vecp, &shvhed); + *e = p; + v = e + 1; + } else if (hadsub) { + retp = savestr(p); + asx(vp, subscr, retp); + xfree(retp); + retp = 0; + } else + set(vp, savestr(p)); + if (eq(vp, S_path/*"path"*/)) { + exportpath(adrof(S_path/*"path"*/)->vec); + dohash(xhash); + } else if (eq(vp, S_histchars/*"histchars"*/)) { + register tchar *p = value(S_histchars/*"histchars"*/); + HIST = *p++; + HISTSUB = *p; + } else if (eq(vp, S_user/*"user"*/)) + local_setenv(S_USER/*"USER"*/, value(vp)); + else if (eq(vp, S_term/*"term"*/)) + local_setenv(S_TERM/*"TERM"*/, value(vp)); + else if (eq(vp, S_home/*"home"*/)) + local_setenv(S_HOME/*"HOME"*/, value(vp)); +#ifdef FILEC + else if (eq(vp, S_filec/*"filec"*/)) + filec = 1; + else if (eq(vp, S_cdpath/*"cdpath"*/)) + dohash(xhash2); +#endif + } while (p = *v++); +} + +tchar * +getinx(cp, ip) + register tchar *cp; + register int *ip; +{ + +#ifdef TRACE + tprintf("TRACE- getinx()\n"); +#endif + *ip = 0; + *cp++ = 0; + while (*cp && digit(*cp)) + *ip = *ip * 10 + *cp++ - '0'; + if (*cp++ != ']') + bferr("Subscript error"); + return (cp); +} + +asx(vp, subscr, p) + tchar *vp; + int subscr; + tchar *p; +{ + register struct varent *v = getvx(vp, subscr); + +#ifdef TRACE + tprintf("TRACE- asx()\n"); +#endif + xfree(v->vec[subscr - 1]); + v->vec[subscr - 1] = globone(p); +} + +struct varent * +getvx(vp, subscr) + tchar *vp; +{ + register struct varent *v = adrof(vp); + +#ifdef TRACE + tprintf("TRACE- getvx()\n"); +#endif + if (v == 0) + udvar(vp); + if (subscr < 1 || subscr > blklen(v->vec)) + bferr("Subscript out of range"); + return (v); +} + +tchar plusplus[2] = { '1', 0 }; + +dolet(v) + tchar **v; +{ + register tchar *p; + tchar *vp, c, op; + bool hadsub; + int subscr; + + v++; + p = *v++; + if (p == 0) { + prvars(); + return; + } + do { + hadsub = 0; + for (vp = p; alnum(*p); p++) + continue; + if (vp == p || !letter(*vp)) + goto letsyn; + if (*p == '[') { + hadsub++; + p = getinx(p, &subscr); + } + if (*p == 0 && *v) + p = *v++; + if (op = *p) + *p++ = 0; + else + goto letsyn; + vp = savestr(vp); + if (op == '=') { + c = '='; + p = xset(p, &v); + } else { + c = *p++; + /* if (any(c, "+-")) { */ + if (c == '+' || c == '-') { + if (c != op || *p) + goto letsyn; + p = plusplus; + } else { + /*if (any(op, "<>")) {*/ + if (op == '<' || op == '>') { + if (c != op) + goto letsyn; + c = *p++; +letsyn: + bferr("Syntax error"); + } + if (c != '=') + goto letsyn; + p = xset(p, &v); + } + } + if (op == '=') + if (hadsub) + asx(vp, subscr, p); + else + set(vp, p); + else + if (hadsub) +#ifndef V6 + /* avoid bug in vax CC */ + { + struct varent *gv = getvx(vp, subscr); + + asx(vp, subscr, operate(op, gv->vec[subscr - 1], p)); + } +#else + asx(vp, subscr, operate(op, getvx(vp, subscr)->vec[subscr - 1], p)); +#endif + else + set(vp, operate(op, value(vp), p)); + if (eq(vp, S_path/*"path"*/)) { + exportpath(adrof(S_path/*"path"*/)->vec); + dohash(xhash); + } + + if (eq(vp, S_cdpath/*"cdpath"*/)) + dohash(xhash2); + + XFREE(vp) + if (c != '=') + XFREE(p) + } while (p = *v++); +} + +tchar * +xset(cp, vp) + tchar *cp, ***vp; +{ + register tchar *dp; + +#ifdef TRACE + tprintf("TRACE- xset()\n"); +#endif + if (*cp) { + dp = savestr(cp); + --(*vp); + xfree(**vp); + **vp = dp; + } + return (putn(exp(vp))); +} + +tchar * +operate(op, vp, p) + tchar op, *vp, *p; +{ + tchar opr[2]; + tchar *vec[5]; + register tchar **v = vec; + tchar **vecp = v; + register int i; + + if (op != '=') { + if (*vp) + *v++ = vp; + opr[0] = op; + opr[1] = 0; + *v++ = opr; + if (op == '<' || op == '>') + *v++ = opr; + } + *v++ = p; + *v++ = 0; + i = exp(&vecp); + if (*vecp) + bferr("Expression syntax"); + return (putn(i)); +} + +static tchar *putp; + +tchar * +putn(n) + register int n; +{ + static tchar number[15]; + +#ifdef TRACE + tprintf("TRACE- putn()\n"); +#endif + putp = number; + if (n < 0) { + n = -n; + *putp++ = '-'; + } + if (sizeof (int) == 2 && n == -32768) { + *putp++ = '3'; + n = 2768; +#ifdef pdp11 + } +#else + } else if (sizeof (int) == 4 && n == 0x80000000) { + *putp++ = '2'; + n = 147483648; + } +#endif + putn1(n); + *putp = 0; + return (savestr(number)); +} + +putn1(n) + register int n; +{ +#ifdef TRACE + tprintf("TRACE- putn1()\n"); +#endif + if (n > 9) + putn1(n / 10); + *putp++ = n % 10 + '0'; +} + +getn(cp) + register tchar *cp; +{ + register int n; + int sign; + +#ifdef TRACE + tprintf("TRACE- getn()\n"); +#endif + sign = 0; + if (cp[0] == '+' && cp[1]) + cp++; + if (*cp == '-') { + sign++; + cp++; + if (!digit(*cp)) + goto badnum; + } + n = 0; + while (digit(*cp)) + n = n * 10 + *cp++ - '0'; + if (*cp) + goto badnum; + return (sign ? -n : n); +badnum: + bferr("Badly formed number"); + return (0); +} + +tchar * +value1(var, head) + tchar *var; + struct varent *head; +{ + register struct varent *vp; + +#ifdef TRACE + tprintf("TRACE- value1()\n"); +#endif + vp = adrof1(var, head); + return (vp == 0 || vp->vec[0] == 0 ? S_/*""*/ : vp->vec[0]); +} + +struct varent * +madrof(pat, vp) + tchar *pat; + register struct varent *vp; +{ + register struct varent *vp1; + +#ifdef TRACE + tprintf("TRACE- madrof()\n"); +#endif + for (; vp; vp = vp->v_right) { + if (vp->v_left && (vp1 = madrof(pat, vp->v_left))) + return vp1; + if (Gmatch(vp->v_name, pat)) + return vp; + } + return vp; +} + +struct varent * +adrof1(name, v) + register tchar *name; + register struct varent *v; +{ + register cmp; + +#ifdef TRACE + tprintf("TRACE- adrof1()\n"); +#endif + v = v->v_left; + while (v && ((cmp = *name - *v->v_name) || + (cmp = strcmp_(name, v->v_name)))) + if (cmp < 0) + v = v->v_left; + else + v = v->v_right; + return v; +} + +/* + * The caller is responsible for putting value in a safe place + */ +set(var, val) + tchar *var, *val; +{ + register tchar **vec = (tchar **) xalloc(2 * sizeof (tchar **)); + +#ifdef TRACE + tprintf("TRACE- set()\n"); +#endif + vec[0] = onlyread(val) ? savestr(val) : val; + vec[1] = 0; + set1(var, vec, &shvhed); +} + +set1(var, vec, head) + tchar *var, **vec; + struct varent *head; +{ + register tchar **oldv = vec; + +#ifdef TRACE + tprintf("TRACE- set1()\n"); +#endif + gflag = 0; + /* If setting cwd variable via "set cwd=/tmp/something" + * then do globbing. But if we are setting the cwd + * becuz of a cd, chdir, pushd, popd, do not do globbing. + */ + if ( (!(eq(var,S_cwd))) || (eq(var,S_cwd) && (didchdir == 0)) ) + { + tglob(oldv); + } + if (gflag) { + vec = glob(oldv); + if (vec == 0) { + bferr("No match"); + blkfree(oldv); + return; + } + blkfree(oldv); + gargv = 0; + } + setq(var, vec, head); +} + +setq(name, vec, p) + tchar *name, **vec; + register struct varent *p; +{ + register struct varent *c; + register f; + +#ifdef TRACE + tprintf("TRACE- setq()\n"); +#endif + f = 0; /* tree hangs off the header's left link */ + while (c = p->v_link[f]) { + if ((f = *name - *c->v_name) == 0 && + (f = strcmp_(name, c->v_name)) == 0) { + blkfree(c->vec); + goto found; + } + p = c; + f = f > 0; + } + p->v_link[f] = c = (struct varent *)xalloc(sizeof (struct varent)); + c->v_name = savestr(name); + c->v_bal = 0; + c->v_left = c->v_right = 0; + c->v_parent = p; + balance(p, f, 0); +found: + trim(c->vec = vec); +} + +unset(v) + tchar *v[]; +{ + +#ifdef TRACE + tprintf("TRACE- unset()\n"); +#endif + unset1(v, &shvhed); + if (adrof(S_histchars/*"histchars"*/) == 0) { + HIST = '!'; + HISTSUB = '^'; + } +#ifdef FILEC + if (adrof(S_filec/*"filec"*/) == 0) + filec = 0; +#endif +} + +unset1(v, head) + register tchar *v[]; + struct varent *head; +{ + register struct varent *vp; + register int cnt; + +#ifdef TRACE + tprintf("TRACE- unset1()\n"); +#endif + while (*++v) { + cnt = 0; + while (vp = madrof(*v, head->v_left)) + unsetv1(vp), cnt++; + if (cnt == 0) + setname(*v); + } +} + +unsetv(var) + tchar *var; +{ + register struct varent *vp; + +#ifdef TRACE + tprintf("TRACE- unsetv()\n"); +#endif + if ((vp = adrof1(var, &shvhed)) == 0) + udvar(var); + unsetv1(vp); +} + +unsetv1(p) + register struct varent *p; +{ + register struct varent *c, *pp; + register f; + +#ifdef TRACE + tprintf("TRACE- unsetv1()\n"); +#endif + /* + * Free associated memory first to avoid complications. + */ + blkfree(p->vec); + XFREE(p->v_name); + /* + * If p is missing one child, then we can move the other + * into where p is. Otherwise, we find the predecessor + * of p, which is guaranteed to have no right child, copy + * it into p, and move it's left child into it. + */ + if (p->v_right == 0) + c = p->v_left; + else if (p->v_left == 0) + c = p->v_right; + else { + for (c = p->v_left; c->v_right; c = c->v_right) + ; + p->v_name = c->v_name; + p->vec = c->vec; + p = c; + c = p->v_left; + } + /* + * Move c into where p is. + */ + pp = p->v_parent; + f = pp->v_right == p; + if (pp->v_link[f] = c) + c->v_parent = pp; + /* + * Free the deleted node, and rebalance. + */ + XFREE( (tchar *)p); + balance(pp, f, 1); +} + +setNS(cp) + tchar *cp; +{ +#ifdef TRACE + tprintf("TRACE- setNS()\n"); +#endif + + set(cp, S_/*""*/); +} + +shift(v) + register tchar **v; +{ + register struct varent *argv; + register tchar *name; + +#ifdef TRACE + tprintf("TRACE- shift()\n"); +#endif + v++; + name = *v; + if (name == 0) + name = S_argv/*"argv"*/; + else + (void) strip(name); + argv = adrof(name); + if (argv == 0) + udvar(name); + if (argv->vec[0] == 0) + bferr("No more words"); + lshift(argv->vec, 1); +} + +exportpath(val) + tchar **val; +{ + tchar exppath[PATHSIZ]; + +#ifdef TRACE + tprintf("TRACE- exportpath()\n"); +#endif + exppath[0] = 0; + if (val) + while (*val) { + if (strlen_(*val) + strlen_(exppath) + 2 > PATHSIZ) { + printf("Warning: ridiculously long PATH truncated\n"); + break; + } + (void) strcat_(exppath, *val++); + if (*val == 0 || eq(*val, S_RPAR/*")"*/)) + break; + (void) strcat_(exppath, S_COLON/*":"*/); + } + local_setenv(S_PATH/*"PATH"*/, exppath); +} + + /* macros to do single rotations on node p */ +#define rright(p) (\ + t = (p)->v_left,\ + (t)->v_parent = (p)->v_parent,\ + ((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\ + (t->v_right = (p))->v_parent = t,\ + (p) = t) +#define rleft(p) (\ + t = (p)->v_right,\ + (t)->v_parent = (p)->v_parent,\ + ((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\ + (t->v_left = (p))->v_parent = t,\ + (p) = t) + +/* + * Rebalance a tree, starting at p and up. + * F == 0 means we've come from p's left child. + * D == 1 means we've just done a delete, otherwise an insert. + */ +balance(p, f, d) + register struct varent *p; + register f; +{ + register struct varent *pp; + register struct varent *t; /* used by the rotate macros */ + register ff; + +#ifdef TRACE + tprintf("TRACE- balance()\n"); +#endif + /* + * Ok, from here on, p is the node we're operating on; + * pp is it's parent; f is the branch of p from which we have come; + * ff is the branch of pp which is p. + */ + for (; pp = p->v_parent; p = pp, f = ff) { + ff = pp->v_right == p; + if (f ^ d) { /* right heavy */ + switch (p->v_bal) { + case -1: /* was left heavy */ + p->v_bal = 0; + break; + case 0: /* was balanced */ + p->v_bal = 1; + break; + case 1: /* was already right heavy */ + switch (p->v_right->v_bal) { + case 1: /* sigle rotate */ + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = 0; + p->v_bal = 0; + break; + case 0: /* single rotate */ + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = 1; + p->v_bal = -1; + break; + case -1: /* double rotate */ + rright(p->v_right); + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = + p->v_bal < 1 ? 0 : -1; + p->v_right->v_bal = + p->v_bal > -1 ? 0 : 1; + p->v_bal = 0; + break; + } + break; + } + } else { /* left heavy */ + switch (p->v_bal) { + case 1: /* was right heavy */ + p->v_bal = 0; + break; + case 0: /* was balanced */ + p->v_bal = -1; + break; + case -1: /* was already left heavy */ + switch (p->v_left->v_bal) { + case -1: /* single rotate */ + pp->v_link[ff] = rright(p); + p->v_right->v_bal = 0; + p->v_bal = 0; + break; + case 0: /* signle rotate */ + pp->v_link[ff] = rright(p); + p->v_right->v_bal = -1; + p->v_bal = 1; + break; + case 1: /* double rotate */ + rleft(p->v_left); + pp->v_link[ff] = rright(p); + p->v_left->v_bal = + p->v_bal < 1 ? 0 : -1; + p->v_right->v_bal = + p->v_bal > -1 ? 0 : 1; + p->v_bal = 0; + break; + } + break; + } + } + /* + * If from insert, then we terminate when p is balanced. + * If from delete, then we terminate when p is unbalanced. + */ + if ((p->v_bal == 0) ^ d) + break; + } +} + +plist(p) + register struct varent *p; +{ + register struct varent *c; + register len; + +#ifdef TRACE + tprintf("TRACE- plist()\n"); +#endif + if (setintr) + (void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT)); + for (;;) { + while (p->v_left) + p = p->v_left; + x: + if (p->v_parent == 0) /* is it the header? */ + return; + len = blklen(p->vec); + printf("%t", p->v_name); + Putchar('\t'); + if (len != 1) + Putchar('('); + blkpr(p->vec); + if (len != 1) + Putchar(')'); + Putchar('\n'); + if (p->v_right) { + p = p->v_right; + continue; + } + do { + c = p; + p = p->v_parent; + } while (p->v_right == c); + goto x; + } +} diff --git a/usr/src/cmd/csh/sh.tchar.c b/usr/src/cmd/csh/sh.tchar.c new file mode 100644 index 0000000000..9c75f0e72d --- /dev/null +++ b/usr/src/cmd/csh/sh.tchar.c @@ -0,0 +1,857 @@ +/* + * Copyright 2000 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This module provides with system/library function substitutes for tchar + * datatype. This also includes two conversion functions between tchar and + * char arrays. + * + * T. Kurosaka, Palo Alto, California, USA + * March 1989 + * + * Implementation Notes: + * Many functions defined here use a "char" buffer chbuf[]. In the + * first attempt, there used to be only one chbuf defined as static + * (private) variable and shared by these functions. csh linked with that + * version of this file misbehaved in interpreting "eval `tset ....`". + * (in general, builtin function with back-quoted expression). + * This bug seemed to be caused by sharing of chbuf + * by these functions simultanously (thru vfork() mechanism?). We could not + * identify which two functions interfere each other so we decided to + * have each of these function its private instance of chbuf. + * The size of chbuf[] might be much bigger than necessary for some functions. + */ +#ifdef DBG +#include <stdio.h> /* For <assert.h> needs stderr defined. */ +#else /* !DBG */ +#define NDEBUG /* Disable assert(). */ +#endif /* !DBG */ + +#include <assert.h> +#include "sh.h" + +#ifdef MBCHAR +#include <widec.h> /* For wcsetno() */ +#endif + +#include <sys/param.h> /* MAXPATHLEN */ +#include <fcntl.h> +#include <unistd.h> + + +/* + * strtots(to, from): convert a char string 'from' into a tchar buffer 'to'. + * 'to' is assumed to have the enough size to hold the conversion result. + * When 'to' is NOSTR(=(tchar *)0), strtots() attempts to allocate a space + * automatically using xalloc(). It is caller's responsibility to + * free the space allocated in this way, by calling XFREE(ptr). + * In either case, strtots() returns the pointer to the conversion + * result (i.e. 'to', if 'to' wasn't NOSTR, or the allocated space.). + * When a conversion or allocateion failed, NOSTR is returned. + */ + +tchar * +strtots(tchar *to, char *from) +{ + int i; + + if (to == NOSTR) { /* Need to xalloc(). */ + int i; + + i = mbstotcs(NOSTR, from, 0); + if (i < 0) { + return (NOSTR); + } + + /* Allocate space for the resulting tchar array. */ + to = (tchar *)xalloc(i * sizeof (tchar)); + } + i = mbstotcs(to, from, INT_MAX); + if (i < 0) { + return (NOSTR); + } + return (to); +} + +char * +tstostr(char *to, tchar *from) +{ + tchar *ptc; + wchar_t wc; + char *pmb; + int len; + + if (to == (char *)NULL) { /* Need to xalloc(). */ + int i; + int i1; + char junk[MB_LEN_MAX]; + + /* Get sum of byte counts for each char in from. */ + i = 0; + ptc = from; + while (wc = (wchar_t)((*ptc++)&TRIM)) { + if ((i1 = wctomb(junk, wc)) <= 0) { + i1 = 1; + } + i += i1; + } + + /* Allocate that much. */ + to = (char *)xalloc(i + 1); + } + + ptc = from; + pmb = to; + while (wc = (wchar_t)((*ptc++)&TRIM)) { + if ((len = wctomb(pmb, wc)) <= 0) { + *pmb = (unsigned char)wc; + len = 1; + } + pmb += len; + } + *pmb = (char)0; + return (to); +} + +/* + * mbstotcs(to, from, tosize) is similar to strtots() except that + * this returns # of tchars of the resulting tchar string. + * When NULL is give as the destination, no real conversion is carried out, + * and the function reports how many tchar characters would be made in + * the converted result including the terminating 0. + * tchar *to; - Destination buffer, or NULL. + * char *from; - Source string. + * int tosize; - Size of to, in terms of # of tchars. + */ +int +mbstotcs(tchar *to, char *from, int tosize) +{ + tchar *ptc = to; + char *pmb = from; + wchar_t wc; + int chcnt = 0; + int j; + + + /* Just count how many tchar would be in the result. */ + if (to == (tchar *)NULL) { + while (*pmb) { + if ((j = mbtowc(&wc, pmb, MB_CUR_MAX)) <= 0) { + j = 1; + } + pmb += j; + chcnt++; + } + chcnt++; /* For terminator. */ + return (chcnt); /* # of chars including terminating zero. */ + } else { /* Do the real conversion. */ + while (*pmb) { + if ((j = mbtowc(&wc, pmb, MB_CUR_MAX)) <= 0) { + wc = (unsigned char)*pmb; + j = 1; + } + pmb += j; + *(ptc++) = (tchar)wc; + if (++chcnt >= tosize) { + break; + } + } + /* Terminate with zero only when space is left. */ + if (chcnt < tosize) { + *ptc = (tchar)0; + ++chcnt; + } + return (chcnt); /* # of chars including terminating zero. */ + } +} + + +/* tchar version of STRING functions. */ + +/* + * Returns the number of + * non-NULL tchar elements in tchar string argument. + */ +int +strlen_(tchar *s) +{ + int n; + + n = 0; + while (*s++) { + n++; + } + return (n); +} + +/* + * Concatenate tchar string s2 on the end of s1. S1's space must be large + * enough. Return s1. + */ +tchar * +strcat_(tchar *s1, tchar *s2) +{ + tchar *os1; + + os1 = s1; + while (*s1++) + ; + --s1; + while (*s1++ = *s2++) + ; + return (os1); +} + +/* + * Compare tchar strings: s1>s2: >0 s1==s2: 0 s1<s2: <0 + * BUGS: Comparison between two characters are done by subtracting two chars + * after converting each to an unsigned long int value. It might not make + * a whole lot of sense to do that if the characters are in represented + * as wide characters and the two characters belong to different codesets. + * Therefore, this function should be used only to test the equallness. + */ +int +strcmp_(tchar *s1, tchar *s2) +{ + while (*s1 == *s2++) { + if (*s1++ == (tchar)0) { + return (0); + } + } + return (((unsigned long)*s1) - ((unsigned long)*(--s2))); +} + +/* + * This is only used in sh.glob.c for sorting purpose. + */ +int +strcoll_(tchar *s1, tchar *s2) +{ + char buf1[BUFSIZ]; + char buf2[BUFSIZ]; + + tstostr(buf1, s1); + tstostr(buf2, s2); + return (strcoll(buf1, buf2)); +} + +/* + * Copy tchar string s2 to s1. s1 must be large enough. + * return s1 + */ +tchar * +strcpy_(tchar *s1, tchar *s2) +{ + tchar *os1; + + os1 = s1; + while (*s1++ = *s2++) + ; + return (os1); +} + +/* + * Return the ptr in sp at which the character c appears; + * NULL if not found + */ +tchar * +index_(tchar *sp, tchar c) +{ + + do { + if (*sp == c) { + return (sp); + } + } while (*sp++); + return (NULL); +} + +/* + * Return the ptr in sp at which the character c last + * appears; NOSTR if not found + */ + +tchar * +rindex_(tchar *sp, tchar c) +{ + tchar *r; + + r = NOSTR; + do { + if (*sp == c) { + r = sp; + } + } while (*sp++); + return (r); +} + +/* Additional misc functions. */ + +/* Calculate the display width of a string. */ +tswidth(tchar *ts) +{ +#ifdef MBCHAR + wchar_t tc; + int w = 0; + int p_col; + + while (tc = *ts++) { + if ((p_col = wcwidth((wchar_t)tc)) > 0) + w += p_col; + } + return (w); +#else /* !MBCHAR --- one char always occupies one column. */ + return (strlen_(ts)); +#endif +} + +/* + * Two getenv() substitute functions. They differ in the type of arguments. + * BUGS: Both returns the pointer to an allocated space where the env var's + * values is stored. This space is freed automatically on the successive + * call of either function. Therefore the caller must copy the contents + * if it needs to access two env vars. There is an arbitary limitation + * on the number of chars of a env var name. + */ +#define LONGEST_ENVVARNAME 256 /* Too big? */ +tchar * +getenv_(tchar *name_) +{ + char name[LONGEST_ENVVARNAME * MB_LEN_MAX]; + + assert(strlen_(name_) < LONGEST_ENVVARNAME); + return (getenvs_(tstostr(name, name_))); +} + +tchar * +getenvs_(char *name) +{ + static tchar *pbuf = (tchar *)NULL; + char *val; + + if (pbuf) { + XFREE((void *)pbuf); + pbuf = NOSTR; + } + val = getenv(name); + if (val == (char *)NULL) { + return (NOSTR); + } + return (pbuf = strtots(NOSTR, val)); +} + +/* Followings are the system call interface for tchar strings. */ + +/* + * creat() and open() replacement. + * BUGS: An unusually long file name could be dangerous. + */ +int +creat_(tchar *name_, int mode) +{ + int fd; + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + + tstostr(chbuf, name_); + fd = creat((char *)chbuf, mode); + if (fd != -1) { + setfd(fd); + } + return (fd); +} + +/*VARARGS2*/ +int +open_(path_, flags, mode) + tchar *path_; + int flags; + int mode; /* May be omitted. */ +{ + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + int fd; + + tstostr(chbuf, path_); + fd = open((char *)chbuf, flags, mode); + if (fd != -1) { + setfd(fd); + } + return (fd); +} + +/* + * mkstemp replacement + */ +int +mkstemp_(tchar *name_) +{ + int fd; + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + + tstostr(chbuf, name_); + fd = mkstemp((char *)chbuf); + if (fd != -1) { + setfd(fd); + strtots(name_, chbuf); + } + return (fd); +} + +/* + * read() and write() reaplacement. + * int d; + * tchar *buf; - where the result be stored. Not NULL terminated. + * int nchreq; - # of tchars requrested. + */ +int +read_(int d, tchar *buf, int nchreq) +{ + unsigned char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ +#ifdef MBCHAR + /* + * We would have to read more than tchar bytes + * when there are multibyte characters in the file. + */ + int i, j, fflags; + unsigned char *s; /* Byte being scanned for a multibyte char. */ + /* Points to the pos where next read() to read the data into. */ + unsigned char *p; + tchar *t; + wchar_t wc; + int b_len; + int nchread = 0; /* Count how many bytes has been read. */ + int nbytread = 0; /* Total # of bytes read. */ + /* # of bytes needed to complete the last char just read. */ + int delta; + unsigned char *q; /* q points to the first invalid byte. */ +#ifdef DBG + tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n", + d, buf, nchreq); +#endif /* DBG */ + /* + * Step 1: We collect the exact number of bytes that make + * nchreq characters into chbuf. + * We must be careful not to read too many bytes as we + * cannot push back such over-read bytes. + * The idea we use here is that n multibyte characters are stored + * in no less than n but less than n*MB_CUR_MAX bytes. + */ + assert(nchreq <= BUFSIZ); + delta = 0; + p = s = chbuf; + t = buf; + while (nchread < nchreq) { + int m; /* # of bytes to try to read this time. */ + int k; /* # of bytes successfully read. */ + +retry: + /* + * Let's say the (N+1)'th byte bN is actually the first + * byte of a three-byte character c. + * In that case, p, s, q look like this: + * + * /-- already read--\ /-- not yet read --\ + * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... + * ^ ^ ^ + * | | | + * p s q + * \----------/ + * c hasn't been completed + * + * Just after the next read(), p and q will be adavanced to: + * + * /-- already read-----------------------\ /-- not yet - + * chbuf[]: b0 b1 ..... bN bN+1 bN+2 bN+2 ... bX bX+1 bX+2... + * ^ ^ ^ + * | | | + * s p q + * \----------/ + * c has been completed + * but hasn't been scanned + */ + m = nchreq - nchread; + assert(p + m < chbuf + sizeof (chbuf)); + k = read(d, p, m); + /* + * when child sets O_NDELAY or O_NONBLOCK on stdin + * and exits and we are interactive then turn the modes off + * and retry + */ + if (k == 0) { + if ((intty && !onelflg && !cflg) && + ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) { + fflags &= ~O_NDELAY; + fcntl(d, F_SETFL, fflags); + goto retry; + } + } else if (k < 0) { + if (errno == EAGAIN) { + fflags = fcntl(d, F_GETFL, 0); + fflags &= ~O_NONBLOCK; + fcntl(d, F_SETFL, fflags); + goto retry; + } + return (-1); + } + nbytread += k; + q = p + k; + delta = 0; + + /* Try scaning characters in s..q-1 */ + while (s < q) { + /* Convert the collected bytes into tchar array. */ + if (*s == 0) { + /* NUL is treated as a normal char here. */ + *t++ = 0; + s++; + nchread++; + continue; + } + + if ((b_len = q - s) > (int)MB_CUR_MAX) { + b_len = MB_CUR_MAX; + } + if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { + if (b_len < (unsigned int)MB_CUR_MAX) { + /* + * Needs more byte to complete this char + * In order to read() more than delta + * bytes. + */ + break; + } + + wc = (unsigned char)*s; + j = 1; + } + + *t++ = wc; + nchread++; + s += j; + } + + if (k < m) { + /* We've read as many bytes as possible. */ + while (s < q) { + if ((b_len = q - s) > (int)MB_CUR_MAX) { + b_len = MB_CUR_MAX; + } + if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { + wc = (unsigned char)*s; + j = 1; + } + *t++ = wc; + nchread++; + s += j; + } + return (nchread); + } + + p = q; + } + + if ((delta = q - s) == 0) { + return (nchread); + } + + if (*(s + delta - 1) == '\n') { + while (s < q) { + if ((b_len = q - s) > (int)MB_CUR_MAX) { + b_len = MB_CUR_MAX; + } + if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { + wc = (unsigned char)*s; + j = 1; + } + *t++ = wc; + nchread++; + s += j; + } + return (nchread); + } + + for (; delta < (int)MB_CUR_MAX; delta++, q++) { + assert((q + 1) < (chbuf + sizeof (chbuf))); + if (read(d, q, 1) != 1) { + break; + } + if (*q == '\n') { + break; + } + if (mbtowc(&wc, (char *)s, delta) > 0) { + *t = wc; + return (nchread + 1); + } + } + + while (s < q) { + if ((b_len = q - s) > (int)MB_CUR_MAX) { + b_len = MB_CUR_MAX; + } + if ((j = mbtowc(&wc, (char *)s, b_len)) <= 0) { + wc = (unsigned char)*s; + j = 1; + } + *t++ = wc; + nchread++; + s += j; + } + return (nchread); +#else /* !MBCHAR */ + /* One byte always represents one tchar. Easy! */ + int i; + unsigned char *s; + tchar *t; + int nchread; + +#ifdef DBG + tprintf("Entering read_(d=%d, buf=0x%x, nchreq=%d);\n", + d, buf, nchreq); +#endif /* DBG */ + assert(nchreq <= BUFSIZ); +retry: + nchread = read(d, (char *)chbuf, nchreq); + /* + * when child sets O_NDELAY or O_NONBLOCK on stdin + * and exits and we are interactive then turn the modes off + * and retry + */ + if (nchread == 0) { + if ((intty && !onelflg && !cflg) && + ((fflags = fcntl(d, F_GETFL, 0)) & O_NDELAY)) { + fflags &= ~O_NDELAY; + fcntl(d, F_SETFL, fflags); + goto retry; + } + } else if (nchread < 0) { + if (errno == EAGAIN) { + fflags = fcntl(d, F_GETFL, 0); + fflags &= ~O_NONBLOCK; + fcntl(d, F_SETFL, fflags); + goto retry; + } + len = 0; + } else { + for (i = 0, t = buf, s = chbuf; i < nchread; ++i) { + *t++ = ((tchar)*s++); + } + } + return (nchread); +#endif +} + +/* + * BUG: write_() returns -1 on failure, or # of BYTEs it has written. + * For consistency and symmetry, it should return the number of + * characters it has actually written, but that is technically + * difficult although not impossible. Anyway, the return + * value of write() has never been used by the original csh, + * so this bug should be OK. + */ +int +write_(int d, tchar *buf, int nch) +{ + unsigned char chbuf[BUFSIZ*MB_LEN_MAX]; /* General use buffer. */ +#ifdef MBCHAR + tchar *pt; + unsigned char *pc; + wchar_t wc; + int i, j; + +#ifdef DBG + tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n", + d, buf, nch); /* Hope printf() doesn't call write_() itself! */ +#endif /* DBG */ + assert(nch * MB_CUR_MAX < sizeof (chbuf)); + i = nch; + pt = buf; + pc = chbuf; + while (i--) { + /* + * Convert to tchar string. + * NUL is treated as normal char here. + */ + wc = (wchar_t)((*pt++)&TRIM); + if (wc == (wchar_t)0) { + *pc++ = 0; + } else { + if ((j = wctomb((char *)pc, wc)) <= 0) { + *pc = (unsigned char)wc; + j = 1; + } + pc += j; + } + } + return (write(d, chbuf, pc - chbuf)); +#else /* !MBCHAR */ + /* One byte always represents one tchar. Easy! */ + int i; + unsigned char *s; + tchar *t; + +#ifdef DBG + tprintf("Entering write_(d=%d, buf=0x%x, nch=%d);\n", + d, buf, nch); /* Hope printf() doesn't call write_() itself! */ +#endif /* DBG */ + assert(nch <= sizeof (chbuf)); + for (i = 0, t = buf, s = chbuf; i < nch; ++i) { + *s++ = (char)((*t++)&0xff); + } + return (write(d, (char *)chbuf, nch)); +#endif +} + +#undef chbuf + +#include <sys/types.h> +#include <sys/stat.h> /* satruct stat */ +#include <dirent.h> /* DIR */ + +extern DIR *Dirp; + +int +stat_(tchar *path, struct stat *buf) +{ + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + + tstostr(chbuf, path); + return (stat((char *)chbuf, buf)); +} + +int +lstat_(tchar *path, struct stat *buf) +{ + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + + tstostr(chbuf, path); + return (lstat((char *)chbuf, buf)); +} + +int +chdir_(tchar *path) +{ + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + + tstostr(chbuf, path); + return (chdir((char *)chbuf)); +} + +tchar * +getwd_(tchar *path) +{ + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + int rc; + + rc = (int)getwd((char *)chbuf); + if (rc == 0) { + return (0); + } else { + return (strtots(path, chbuf)); + } +} + +int +unlink_(tchar *path) +{ + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + + tstostr(chbuf, path); + return (unlink((char *)chbuf)); +} + +DIR * +opendir_(tchar *dirname) +{ + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + + extern DIR *opendir(); + DIR *dir; + + dir = opendir(tstostr(chbuf, dirname)); + if (dir != NULL) { + setfd(dir->dd_fd); + } + return (Dirp = dir); +} + +int +closedir_(DIR *dirp) +{ + int ret; + extern int closedir(); + + ret = closedir(dirp); + Dirp = NULL; + return (ret); +} + +int +gethostname_(tchar *name, int namelen) +{ + char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ + + assert(namelen < BUFSIZ); + if (gethostname((char *)chbuf, sizeof (chbuf)) != 0) { + return (-1); + } + if (mbstotcs(name, chbuf, namelen) < 0) { + return (-1); + } + return (0); /* Succeeded. */ +} + +int +readlink_(tchar *path, tchar *buf, int bufsiz) +{ + char chbuf[MAXPATHLEN * MB_LEN_MAX]; /* General use buffer. */ + char chpath[MAXPATHLEN + 1]; + int i; + + tstostr(chpath, path); + i = readlink(chpath, (char *)chbuf, sizeof (chbuf)); + if (i < 0) { + return (-1); + } + chbuf[i] = (char)0; /* readlink() doesn't put NULL. */ + i = mbstotcs(buf, chbuf, bufsiz); + if (i < 0) { + return (-1); + } + return (i - 1); /* Return # of tchars EXCLUDING the terminating NULL. */ +} + +int +atoi_(tchar *str) +{ + char chbuf[BUFSIZ * MB_LEN_MAX]; /* General use buffer. */ + + tstostr(chbuf, str); + return (atoi((char *)chbuf)); +} + +tchar * +simple(tchar *s) +{ + register tchar *sname = s; + + while (1) { + if (any('/', sname)) { + while (*sname++ != '/') + ; + } else { + return (sname); + } + } +} diff --git a/usr/src/cmd/csh/sh.tconst.c b/usr/src/cmd/csh/sh.tconst.c new file mode 100644 index 0000000000..e8f16a346f --- /dev/null +++ b/usr/src/cmd/csh/sh.tconst.c @@ -0,0 +1,205 @@ +/* + * Copyright 2000 by Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * C shell + */ + +/* + * These tchar constants used to be defined as + * character string constants. + */ + +#include "sh.h" + +tchar S_[] = {0}; +tchar S_0[]={'0', 0}; +tchar S_1[]={'1', 0}; +tchar S_AND[] = {'&', 0}; /* & */ +tchar S_ANDAND[] = {'&', '&', 0}; /* && */ +tchar S_AST[]={'*', 0}; +tchar S_AT[] = { '@', 0 }; +tchar S_BAR[] = {'|', 0}; /* | */ +tchar S_BARBAR[] = {'|','|', 0}; /* || */ +tchar S_BRABRA[] = {'{', '}', 0}; /* {} */ +tchar S_BRAPPPBRA[] = {'{', ' ', '.', '.', '.', ' ', '}', 0}; /* { ... } */ +tchar S_COLON[] = {':', 0}; /*:*/ +tchar S_DASHl[] = {'-', 'l', 0}; /*-l */ +tchar S_DELIM[] = {' ','\'','"','\t',';','&','<','>','(',')','|','`',0}; +tchar S_DOT[] = {'.', 0}; +tchar S_DOTDOTSLA[]={'.', '.', '/', 0}; +tchar S_DOTSLA[]={'.', '/', 0}; +tchar S_EQ[] = {'=', 0}; /*=*/ +tchar S_EXAS[] = {'!', 0}; /* ! */ +tchar S_HAT[] = {'^', 0}; /* ^ */ +tchar S_HOME[] = {'H','O','M','E',0};/*HOME*/ +tchar S_IOT[] = {'I', 'O', 'T', 0}; /*IOT*/ +tchar S_LANG[]={'L', 'A', 'N', 'G', 0}; /*LANG*/ +tchar S_LBRA[] = {'{', 0}; /* { */ +tchar S_LBRASP[] = {'(', ' ', 0}; /*( */ +tchar S_LC_ALL[]={'L', 'C', '_', 'A', 'L', 'L', 0}; /*LC_ALL*/ +tchar S_LC_CTYPE[]={'L', 'C', '_', 'C', 'T', 'Y', 'P', 'E', 0}; /*LC_CTYPE*/ +tchar S_LC_MESSAGES[]={'L', 'C', '_', + 'M', 'E', 'S', 'S', 'A', 'G', 'E', 'S', 0}; /*LC_MESSAGES*/ +tchar S_LESLES[]={'<', '<', 0}; +tchar S_LPAR[] = {'(', 0}; /* ( */ +tchar S_MINUS[] = {'-',0};/*"-"*/ +tchar S_MINl[]={'-', 'l', 0}; +tchar S_NDOThistory[] = {'~','/','.','h','i','s','t','o','r','y',0}; +tchar S_OTHERSH[] = {'/','b','i','n','/','s','h',0}; +tchar S_PARCENTMINUS[] = {'%', '-', 0}; /*%-*/ +tchar S_PARCENTPARCENT[] = {'%', '%', 0}; /*%%*/ +tchar S_PARCENTPLUS[] = {'%', '+', 0}; /*%+*/ +tchar S_PARCENTSHARP[] = {'%', '#', 0}; /*%#*/ +tchar S_PATH[] = {'P','A','T','H',0};/*"PATH"*/ +tchar S_PERSENTSP[] = {'%',' ',0}; +tchar S_PWD[]={'P', 'W', 'D', 0}; +tchar S_Pjob[] = {'%','j','o','b', 0}; /*"%job"*/ +tchar S_PjobAND[] = {'%','j','o','b',' ','&',0}; /*"%job &"*/ +tchar S_QPPPQ[] = {'`', ' ', '.', '.', '.', ' ', '`', 0}; /*` ... `*/ +tchar S_RBRA[] = {'}', 0}; /* } */ +tchar S_RPAR[] = {')', 0}; /*)*/ +tchar S_SEMICOLONSP[] = {';', ' ', 0}; /* | */ +tchar S_SHARPSP[] = {'#',' ',0}; +tchar S_SHELLPATH[] = {'/','b','i','n','/','c','s','h',0}; +tchar S_SLADOTcshrc[] = {'/','.','c','s','h','r','c', 0}; +tchar S_SLADOThistory[] = {'/','.','h','i','s','t','o','r','y', 0}; +tchar S_SLADOTlogin[] = {'/','.','l','o','g','i','n', 0}; +tchar S_SLADOTlogout[] = {'/','.','l','o','g','o','u','t', 0}; +tchar S_SLASH[] = {'/', 0}; /* "/" */ +tchar S_SP[] = {' ', 0}; /* */ +tchar S_SPANDANDSP[] = {' ', '&', '&', ' ', 0}; /* && */ +tchar S_SPBARBARSP[] = {' ', '|', '|', ' ', 0}; /* || */ +tchar S_SPBARSP[] = {' ', '|', ' ', 0}; /* | */ +tchar S_SPGTRGTRSP[] = {' ', '>', '>', ' ', 0}; /* >> */ +tchar S_SPGTR[] = {' ', '>',0}; /* > */ +tchar S_SPLESLESSP[] = {' ', '<', '<', ' ', 0}; /* << */ +tchar S_SPLESSP[] = {' ', '<', ' ', 0}; /* < */ +tchar S_SPPPP[] = {' ', '.', '.', '.', 0}; /* ... */ +tchar S_SPRBRA[] = {' ', ')', 0}; /* )*/ +tchar S_TERM[] = {'T','E','R','M',0};/*TERM*/ +tchar S_TIL[] = {'~', 0}; /* ~ */ +tchar S_TOPBIT[] = {(tchar)QUOTE, 0}; /* Was "\200". A hack! */ +tchar S_USAGEFORMAT[] = {'%','U','u',' ','%','S','s',' ','%','E',' ','%','P', ' ','%','X','+','%','D','k',' ','%','I','+','%','O','i','o',' ','%','F','p','f','+', '%','W','w',0}; +tchar S_USER[] = {'U','S','E','R',0};/*USER*/ +tchar S_alias[] = { 'a','l','i','a','s', 0 }; +tchar S_alloc[] = { 'a','l','l','o','c', 0}; +tchar S_aout[] = {'a','.','o','u','t',0}; +tchar S_argv[]={'a', 'r', 'g', 'v', 0}; +tchar S_bg[] = { 'b','g', 0}; +tchar S_bin[] = {'/','b','i','n',0}; +tchar S_break[] = { 'b','r','e','a','k', 0}; +tchar S_breaksw[] = { 'b','r','e','a','k','s','w', 0}; +tchar S_bye[] = { 'b','y','e', 0}; +tchar S_case[] = { 'c','a','s','e', 0}; +tchar S_cd[] = { 'c','d', 0}; +tchar S_cdpath[]={'c', 'd', 'p', 'a', 't', 'h', 0}; +tchar S_chdir[] = { 'c','h','d','i','r', 0}; +tchar S_child[] = {'c', 'h', 'i', 'l', 'd', 0}; /*child */ +tchar S_continue[] = { 'c','o','n','t','i','n','u','e', 0}; +tchar S_coredumpsize[] = {'c','o','r','e','d','u','m','p','s','i','z','e',0};/*"coredumpsize"*/ +tchar S_cputime[] = {'c','p','u','t','i','m','e',0};/*"cputime"*/ +tchar S_csh[]={'c', 's', 'h', 0}; +tchar S_cwd[]={'c', 'w', 'd', 0}; +tchar S_datasize[] = {'d','a','t','a','s','i','z','e',0};/*"datasize"*/ +tchar S_default[] = { 'd','e','f','a','u','l','t', 0 }; +tchar S_descriptors[] = {'d', 'e', 's', 'c', 'r', 'i', 'p', 't', 'o', 'r', 's', 0}; +tchar S_dirs[] = { 'd','i','r','s', 0 }; +tchar S_echo[] = {'e','c','h','o', 0}; +tchar S_else[] = { 'e','l','s','e', 0 }; +tchar S_end[] = { 'e','n','d', 0 }; +tchar S_endif[] = { 'e','n','d','i','f', 0 }; +tchar S_endsw[] = { 'e','n','d','s','w', 0 }; +tchar S_erwxfdzo[] = {'e', 'r', 'w', 'x', 'f', 'd', 'z', 'o', 0}; /* erwxfdzo */ +tchar S_eval[] = { 'e','v','a','l', 0 }; +tchar S_exec[] = { 'e','x','e','c', 0 }; +tchar S_exit[] = { 'e','x','i','t', 0 }; +tchar S_fg[] = { 'f','g', 0 }; +tchar S_fignore[] = {'f','i','g','n','o','r','e',0}; +tchar S_filec[] = {'f','i','l','e','c',0};/*filec*/ +tchar S_filesize[] = {'f','i','l','e','s','i','z','e',0};/*"filesize"*/ +tchar S_foreach[] = { 'f','o','r','e','a','c','h', 0 }; +tchar S_gd[] = { 'g','d', 0 }; +tchar S_glob[] = { 'g','l','o','b', 0 }; +tchar S_goto[] = { 'g','o','t','o', 0 }; +tchar S_h[] = {'-','h',0}; +tchar S_hardpaths[]={'h', 'a', 'r', 'd', 'p', 'a', 't', 'h', 's', 0}; +tchar S_hashstat[] = { 'h','a','s','h','s','t','a','t', 0 }; +tchar S_histchars[] = {'h','i','s','t','c','h','a','r','s',0}; /*histchars*/ +tchar S_history[] = {'h','i','s','t','o','r','y',0}; +tchar S_home[]={'h', 'o', 'm', 'e', 0}; +tchar S_hours[] = {'h','o','u','r','s',0};/*"hours"*/ +tchar S_htrqxe[]={'h', 't', 'r', 'q', 'x', 'e', 0}; +tchar S_if[] = { 'i','f', 0 }; +tchar S_ignoreeof[] = {'i','g','n','o','r','e','e','o','f',0}; /*"ignoreeof"*/ +tchar S_jobs[] = {'j','o','b','s', 0}; +tchar S_kbytes[] = {'k','b','y','t','e','s',0};/*"kbytes"*/ +tchar S_kill[] = { 'k','i','l','l', 0 }; +tchar S_label[] = { 'l','a','b','e','l', 0 }; +tchar S_limit[] = { 'l','i','m','i','t', 0 }; +tchar S_login[] = { 'l','o','g','i','n', 0 }; +tchar S_logout[] = { 'l','o','g','o','u','t', 0 }; +tchar S_mail[] = {'m','a','i','l', 0}; +tchar S_megabytes[] = {'m','e','g','a','b','y','t','e','s',0};/*"megabytes"*/ +tchar S_memorysize[] = {'m','e','m','o','r','y','s','i','z','e',0};/*"memorysize"*/ +tchar S_minutes[]={'m','i','n','u','t','e','s',0};/*"minutes"*/ +tchar S_n[] = {'-','n',0};/*"-n"*/ +tchar S_newgrp[] = { 'n','e','w','g','r','p', 0 }; +tchar S_nice[] = { 'n','i','c','e', 0 }; +tchar S_nobeep[] = {'n', 'o', 'b', 'e', 'e', 'p', 0}; +tchar S_noclobber[] = {'n','o','c','l','o','b','b','e','r',0};/*noclobber*/ +tchar S_noglob[] = {'n', 'o', 'g', 'l', 'o', 'b', 0}; /*noglob */ +tchar S_nohup[] = {'n', 'o', 'h', 'u', 'p', 0}; /*nohup */ +tchar S_nonomatch[] = {'n', 'o', 'n', 'o', 'm', 'a', 't', 'c', 'h', 0}; /*nonomatch */ +tchar S_notify[] = {'n', 'o', 't', 'i', 'f', 'y', 0}; /*nofify */ +tchar S_onintr[] = { 'o','n','i','n','t','r', 0 }; +tchar S_path[] = {'p','a','t','h', 0}; /*path*/ +tchar S_popd[] = { 'p','o','p','d', 0 }; +tchar S_prompt[] = {'p','r','o','m','p','t', 0}; +tchar S_pushd[] = { 'p','u','s','h','d', 0 }; +tchar S_rd[] = { 'r','d', 0 }; +tchar S_rehash[] = { 'r','e','h','a','s','h', 0 }; +tchar S_repeat[] = { 'r','e','p','e','a','t', 0 }; +tchar S_savehist[] = {'s','a','v','e','h','i','s','t', 0}; +tchar S_seconds[] = {'s','e','c','o','n','d','s',0};/*"seconds"*/ +tchar S_set[] = { 's','e','t', 0 }; +tchar S_setenv[] = { 's','e','t','e','n','v', 0 }; +tchar S_shell[] = {'s','h','e','l','l', 0}; +tchar S_shift[] = { 's','h','i','f','t', 0 }; +tchar S_source[] = {'s','o','u','r','c','e',0}; +tchar S_stacksize[] = {'s','t','a','c','k','s','i','z','e',0};/*"stacksize"*/ +tchar S_status[]={'s', 't', 'a', 't', 'u', 's', 0}; +tchar S_stop[] = { 's','t','o','p', 0 }; +tchar S_suspend[] = { 's','u','s','p','e','n','d', 0 }; +tchar S_switch[] = { 's','w','i','t','c','h', 0 }; +tchar S_term[] = {'t','e','r','m', 0}; +tchar S_then[] = {'t','h','e','n',0}; /*"then"*/ +tchar S_time[] = {'t', 'i', 'm', 'e', 0}; /*time*/ +tchar S_umask[] = { 'u','m','a','s','k', 0 }; +tchar S_unalias[] = { 'u','n','a','l','i','a','s', 0 }; +tchar S_unhash[] = { 'u','n','h','a','s','h', 0 }; +tchar S_unlimit[] = { 'u','n','l','i','m','i','t', 0 }; +tchar S_unlimited[] = {'u','n','l','i','m','i','t','e','d',0};/*"unlimited"*/ +tchar S_unset[] = { 'u','n','s','e','t', 0 }; +tchar S_unsetenv[] = { 'u','n','s','e','t','e','n','v', 0 }; +tchar S_user[] = {'u','s','e','r', 0}; +tchar S_usrbin[] = {'/','u','s','r','/','b','i','n',0}; +tchar S_usrucb[] = {'/','u','s','r','/','u','c','b',0}; +tchar S_verbose[] = {'v','e','r','b','o','s','e', 0}; +tchar S_wait[] = { 'w','a','i','t', 0 }; +tchar S_while[] = { 'w','h','i','l','e', 0 }; +/* Dummy search path for just absolute search when no path */ +tchar *justabs[] = { S_ /* "" */, 0 }; diff --git a/usr/src/cmd/csh/sh.time.c b/usr/src/cmd/csh/sh.time.c new file mode 100644 index 0000000000..0564f29e07 --- /dev/null +++ b/usr/src/cmd/csh/sh.time.c @@ -0,0 +1,262 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sh.h" +#include "sh.tconst.h" + +void ruadd(struct rusage *ru, struct rusage *ru2); +void prusage(struct rusage *r0, struct rusage *r1, struct timeval *e, + struct timeval *b); +void pdeltat(struct timeval *t1, struct timeval *t0); +void tvadd(struct timeval *tsum, struct timeval *t0); +void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0); + +/* + * C Shell - routines handling process timing and niceing + */ + +void +settimes() +{ + struct rusage ruch; + +#ifdef TRACE + tprintf("TRACE- settimes()\n"); +#endif + (void) gettimeofday(&time0, (struct timezone *)0); + (void) getrusage(RUSAGE_SELF, &ru0); + (void) getrusage(RUSAGE_CHILDREN, &ruch); + ruadd(&ru0, &ruch); +} + +/* + * dotime is only called if it is truly a builtin function and not a + * prefix to another command + */ +void +dotime() +{ + struct timeval timedol; + struct rusage ru1, ruch; + +#ifdef TRACE + tprintf("TRACE- dotime()\n"); +#endif + (void) getrusage(RUSAGE_SELF, &ru1); + (void) getrusage(RUSAGE_CHILDREN, &ruch); + ruadd(&ru1, &ruch); + (void) gettimeofday(&timedol, (struct timezone *)0); + prusage(&ru0, &ru1, &timedol, &time0); +} + +/* + * donice is only called when it's on the line by itself or with a +- value + */ +void +donice(tchar **v) +{ + tchar *cp; + int nval; + +#ifdef TRACE + tprintf("TRACE- donice()\n"); +#endif + v++; + cp = *v++; + if (cp == 0) { + nval = 4; + } else if (*v == 0 && (cp[0] == '+' || cp[0] == '-')) { + nval = getn(cp); + } + (void) setpriority(PRIO_PROCESS, 0, nval); +} + +void +ruadd(struct rusage *ru, struct rusage *ru2) +{ + long *lp, *lp2; + int cnt; + /* + * The SunOS 4.x <sys/rusage.h> has ru_first and ru_last #defines + * as below. + * The SVR4/POSIX <sys/resource.h> does not have these defined for + * struct rusage + * The #defines below are here so that the original csh logic + * for ruadd remains clear now that there is no longer a private copy + * of the old <sys/resource.h> + */ +#define ru_first ru_ixrss +#define ru_last ru_nivcsw + +#ifdef TRACE + tprintf("TRACE- ruadd()\n"); +#endif + tvadd(&ru->ru_utime, &ru2->ru_utime); + tvadd(&ru->ru_stime, &ru2->ru_stime); + if (ru2->ru_maxrss > ru->ru_maxrss) { + ru->ru_maxrss = ru2->ru_maxrss; + } + cnt = &ru->ru_last - &ru->ru_first + 1; + lp = &ru->ru_first; + lp2 = &ru2->ru_first; + do { + *lp++ += *lp2++; + } while (--cnt > 0); +} + +void +prusage(struct rusage *r0, struct rusage *r1, struct timeval *e, + struct timeval *b) +{ +#define pgtok(p) ((p * pgsize) / 1024) + static int pgsize; + + time_t t = + (r1->ru_utime.tv_sec - r0->ru_utime.tv_sec) * 100 + + (r1->ru_utime.tv_usec - r0->ru_utime.tv_usec) / 10000 + + (r1->ru_stime.tv_sec - r0->ru_stime.tv_sec) * 100 + + (r1->ru_stime.tv_usec - r0->ru_stime.tv_usec) / 10000; + tchar *cp; + int i; + struct varent *vp = adrof(S_time); + int ms = + (e->tv_sec - b->tv_sec) * 100 + (e->tv_usec - b->tv_usec) / 10000; + +#ifdef TRACE + tprintf("TRACE- prusage()\n"); +#endif + if (pgsize == 0) { + pgsize = getpagesize(); + } + + cp = S_USAGEFORMAT; /* "%Uu %Ss %E %P %X+%Dk %I+%Oio %Fpf+%Ww" */ + if (vp && vp->vec[0] && vp->vec[1]) { + cp = vp->vec[1]; + } + for (; *cp; cp++) { + if (*cp != '%') { + Putchar(*cp); + } else if (cp[1]) { + switch (*++cp) { + + case 'U': + pdeltat(&r1->ru_utime, &r0->ru_utime); + break; + + case 'S': + pdeltat(&r1->ru_stime, &r0->ru_stime); + break; + + case 'E': + psecs_int(ms / 100); + break; + + case 'P': + printf("%d%%", (int)(t * 100 / + ((ms ? ms : 1)))); + break; + + case 'W': + i = r1->ru_nswap - r0->ru_nswap; + printf("%d", i); + break; + + case 'X': + printf("%d", t == 0 ? 0 : + pgtok((r1->ru_ixrss - r0->ru_ixrss) / t)); + break; + + case 'D': + printf("%d", t == 0 ? 0 : + pgtok((r1->ru_idrss + r1->ru_isrss- + (r0->ru_idrss + r0->ru_isrss)) / t)); + break; + + case 'K': + printf("%d", t == 0 ? 0 : + pgtok(((r1->ru_ixrss + r1->ru_isrss + + r1->ru_idrss) - (r0->ru_ixrss + + r0->ru_idrss + r0->ru_isrss)) / t)); + break; + + case 'M': + printf("%d", r1->ru_maxrss / 2); + break; + + case 'F': + printf("%d", r1->ru_majflt - r0->ru_majflt); + break; + + case 'R': + printf("%d", r1->ru_minflt - r0->ru_minflt); + break; + + case 'I': + printf("%d", r1->ru_inblock - r0->ru_inblock); + break; + + case 'O': + printf("%d", r1->ru_oublock - r0->ru_oublock); + break; + } + } + } + Putchar('\n'); +#undef pgtok +} + +void +pdeltat(struct timeval *t1, struct timeval *t0) +{ + struct timeval td; + +#ifdef TRACE + tprintf("TRACE- pdeltat()\n"); +#endif + tvsub(&td, t1, t0); + /* change printf formats */ + printf("%d.%01d", td.tv_sec, td.tv_usec / 100000); +} + +void +tvadd(struct timeval *tsum, struct timeval *t0) +{ + +#ifdef TRACE + tprintf("TRACE- tvadd()\n"); +#endif + tsum->tv_sec += t0->tv_sec; + tsum->tv_usec += t0->tv_usec; + if (tsum->tv_usec > 1000000) { + tsum->tv_sec++; + tsum->tv_usec -= 1000000; + } +} + +void +tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0) +{ + +#ifdef TRACE + tprintf("TRACE- tvsub()\n"); +#endif + tdiff->tv_sec = t1->tv_sec - t0->tv_sec; + tdiff->tv_usec = t1->tv_usec - t0->tv_usec; + if (tdiff->tv_usec < 0) { + tdiff->tv_sec--; + tdiff->tv_usec += 1000000; + } +} diff --git a/usr/src/cmd/csh/sparc/Makefile b/usr/src/cmd/csh/sparc/Makefile new file mode 100644 index 0000000000..a70d8110c7 --- /dev/null +++ b/usr/src/cmd/csh/sparc/Makefile @@ -0,0 +1,89 @@ +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +# Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + +# Copyright (c) 1980 Regents of the University of California. +# All rights reserved. The Berkeley Software License Agreement +# specifies the terms and conditions for redistribution. + +# ident "%Z%%M% %I% %E% SMI" + +# +# C Shell with process control; VM/UNIX VAX Makefile +# Bill Joy UC Berkeley; Jim Kulp IIASA, Austria +# + +CSH_PROG = csh +PROG = $(CSH_PROG) +PFCSH_PROG= pfcsh +ROOTPFCSH= $(ROOTBIN)/$(PFCSH_PROG) + +include ../../Makefile.cmd + +MBCHAR = -DMBCHAR # Define this line to include multibyte input support +DEFS = -DVFORK -DFILEC -DBSD_COMP -DFIVE # No TELL when MBCHAR +CPPFLAGS= -I. -s $(DEFS) $(MBCHAR) $(CPPFLAGS.master) +CPPFLAGS += -I ../../sh +CPPFLAGS += -D_FILE_OFFSET_BITS=64 +LAZYLIBS = $(ZLAZYLOAD) -lsecdb $(ZNOLAZYLOAD) +lint := LAZYLIBS = -lsecdb +LDLIBS += -lcurses $(LAZYLIBS) + +PFOBJS = sh_policy.o + +HDDEP = sh.o sh.dir.o sh.dol.o sh.err.o sh.exec.o sh.exp.o sh.file.o \ + sh.func.o sh.glob.o sh.hist.o sh.init.o sh.lex.o sh.misc.o \ + sh.parse.o sh.proc.o sh.sem.o sh.set.o sh.time.o + +COMMONOBJS= printf.o sh.char.o sh.dir.o sh.dol.o sh.err.o \ + sh.exec.o sh.exp.o sh.file.o sh.func.o sh.glob.o sh.hist.o sh.init.o \ + sh.lex.o sh.misc.o sh.parse.o sh.print.o sh.proc.o sh.sem.o sh.set.o \ + sh.time.o sh.tchar.o sh.tconst.o sh.o \ + wait3.o + +LOCALOBJS= signal.o + +COMMONSRCS= $(COMMONOBJS:%.o=../%.c) +PFSRCS= ../../sh/sh_policy.c + +.KEEP_STATE: + +.PARALLEL: $(COMMONOBJS) $(LOCALOBJS) + +all: $(PROG) + +# build rule for common source above +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.o: ../../sh/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +$(CSH_PROG): sh.tconst.h .WAIT $(COMMONOBJS) $(PFOBJS) $(LOCALOBJS) + $(LINK.c) $(COMMONOBJS) $(PFOBJS) $(LOCALOBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +$(ROOTPFCSH): $(ROOTPROG) + $(RM) $@ + $(LN) $(ROOTPROG) $@ + +sh.tconst.h: ../sh.tconst.c ../make.sh.tconst.h.ed + $(RM) $@ + ed ../sh.tconst.c < ../make.sh.tconst.h.ed + +$(HDDEP): sh.tconst.h + +install: all $(ROOTBINPROG) $(ROOTPROG) $(ROOTPFCSH) + +lint: sh.tconst.h + $(LINT.c) $(COMMONSRCS) $(PFSRCS) signal.c $(LDLIBS) + +clean: + $(RM) $(LOCALOBJS) $(COMMONOBJS) $(PFOBJS) sh.tconst.h + +clobber: clean + $(RM) $(PROG) diff --git a/usr/src/cmd/csh/sparc/signal.c b/usr/src/cmd/csh/sparc/signal.c new file mode 100644 index 0000000000..02447723ff --- /dev/null +++ b/usr/src/cmd/csh/sparc/signal.c @@ -0,0 +1,271 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 4.3BSD signal compatibility functions + * + * the implementation interprets signal masks equal to -1 as "all of the + * signals in the signal set", thereby allowing signals with numbers + * above 32 to be blocked when referenced in code such as: + * + * for (i = 0; i < NSIG; i++) + * mask |= sigmask(i) + */ + +#include <sys/types.h> +#include <sys/siginfo.h> +#include <sys/ucontext.h> +#include <signal.h> +#include "signal.h" +#include <errno.h> +#include <stdio.h> + +#define set2mask(setp) ((setp)->__sigbits[0]) +#define mask2set(mask, setp) \ + ((mask) == -1 ? sigfillset(setp) : sigemptyset(setp), (((setp)->__sigbits[0]) = (mask))) + +void (*_siguhandler[NSIG])() = { 0 }; + +/* + * sigstack is emulated with sigaltstack by guessing an appropriate + * value for the stack size - on machines that have stacks that grow + * upwards, the ss_sp arguments for both functions mean the same thing, + * (the initial stack pointer sigstack() is also the stack base + * sigaltstack()), so a "very large" value should be chosen for the + * stack size - on machines that have stacks that grow downwards, the + * ss_sp arguments mean opposite things, so 0 should be used (hopefully + * these machines don't have hardware stack bounds registers that pay + * attention to sigaltstack()'s size argument. + */ + +#ifdef sun +#define SIGSTACKSIZE 0 +#endif + + +/* + * sigvechandler is the real signal handler installed for all + * signals handled in the 4.3BSD compatibility interface - it translates + * SVR4 signal hander arguments into 4.3BSD signal handler arguments + * and then calls the real handler + */ + +static void +sigvechandler(sig, sip, ucp) + int sig; + siginfo_t *sip; + ucontext_t *ucp; +{ + struct sigcontext sc; + int code; + char *addr; + register int i, j; + int gwinswitch = 0; + + sc.sc_onstack = ((ucp->uc_stack.ss_flags & SS_ONSTACK) != 0); + sc.sc_mask = set2mask(&ucp->uc_sigmask); + + /* + * Machine dependent code begins + */ + sc.sc_sp = ucp->uc_mcontext.gregs[REG_O6]; + sc.sc_pc = ucp->uc_mcontext.gregs[REG_PC]; + sc.sc_npc = ucp->uc_mcontext.gregs[REG_nPC]; + sc.sc_psr = ucp->uc_mcontext.gregs[REG_PSR]; + sc.sc_g1 = ucp->uc_mcontext.gregs[REG_G1]; + sc.sc_o0 = ucp->uc_mcontext.gregs[REG_O0]; + if (ucp->uc_mcontext.gwins != (gwindows_t *)0) { + gwinswitch = 1; + sc.sc_wbcnt = ucp->uc_mcontext.gwins->wbcnt; + for (i = 0; i < MAXWINDOW; i++) { + for (j = 0; j < 16; j++) + sc.sc_spbuf[i][j] = (int)ucp->uc_mcontext.gwins->spbuf[j]; + for (j = 0; j < 8; j++) + sc.sc_wbuf[i][j] = ucp->uc_mcontext.gwins->wbuf[i].rw_local[j]; + for (j = 0; j < 8; j++) + sc.sc_wbuf[i][j+8] = ucp->uc_mcontext.gwins->wbuf[i].rw_in[j]; + } + } + /* + * Machine dependent code ends + */ + + if (sip != NULL) + if ((code = sip->si_code) == BUS_OBJERR) + code = SEGV_MAKE_ERR(sip->si_errno); + + if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) + if (sip != NULL) + addr = (char *)sip->si_addr; + else + addr = SIG_NOADDR; + + (*_siguhandler[sig])(sig, code, &sc, addr); + + if (sc.sc_onstack) + ucp->uc_stack.ss_flags |= SS_ONSTACK; + else + ucp->uc_stack.ss_flags &= ~SS_ONSTACK; + mask2set(sc.sc_mask, &ucp->uc_sigmask); + + /* + * Machine dependent code begins + */ + ucp->uc_mcontext.gregs[REG_O6] = sc.sc_sp; + ucp->uc_mcontext.gregs[REG_PC] = sc.sc_pc; + ucp->uc_mcontext.gregs[REG_nPC] = sc.sc_npc; + ucp->uc_mcontext.gregs[REG_PSR] = sc.sc_psr; + ucp->uc_mcontext.gregs[REG_G1] = sc.sc_g1; + ucp->uc_mcontext.gregs[REG_O0] = sc.sc_o0; + if (gwinswitch == 1) { + ucp->uc_mcontext.gwins->wbcnt = sc.sc_wbcnt; + for (i = 0; i < MAXWINDOW; i++) { + for (j = 0; j < 16; j++) + ucp->uc_mcontext.gwins->spbuf[j] = (greg_t *)sc.sc_spbuf[i][j]; + for (j = 0; j < 8; j++) + ucp->uc_mcontext.gwins->wbuf[i].rw_local[j] = sc.sc_wbuf[i][j]; + for (j = 0; j < 8; j++) + ucp->uc_mcontext.gwins->wbuf[i].rw_in[j] = sc.sc_wbuf[i][j+8]; + } + } + /* + * Machine dependent code ends + */ + + setcontext (ucp); +} + +sigsetmask(mask) + int mask; +{ + sigset_t oset; + sigset_t nset; + + (void) sigprocmask(0, (sigset_t *)0, &nset); + mask2set(mask, &nset); + (void) sigprocmask(SIG_SETMASK, &nset, &oset); + return set2mask(&oset); +} + +sigblock(mask) + int mask; +{ + sigset_t oset; + sigset_t nset; + + (void) sigprocmask(0, (sigset_t *)0, &nset); + mask2set(mask, &nset); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + return set2mask(&oset); +} + +sigpause(mask) + int mask; +{ + sigset_t set; + + (void) sigprocmask(0, (sigset_t *)0, &set); + mask2set(mask, &set); + return (sigsuspend(&set)); +} + +sigvec(sig, nvec, ovec) + int sig; + struct sigvec *nvec; + struct sigvec *ovec; +{ + struct sigaction nact; + struct sigaction oact; + struct sigaction *nactp; + void (*ohandler)(), (*nhandler)(); + + if (sig <= 0 || sig >= NSIG) { + errno = EINVAL; + return -1; + } + + ohandler = _siguhandler[sig]; + + if (nvec) { + _sigaction(sig, (struct sigaction *)0, &nact); + nhandler = nvec->sv_handler; + _siguhandler[sig] = nhandler; + if (nhandler != SIG_DFL && nhandler != SIG_IGN) + nact.sa_handler = (void (*)())sigvechandler; + else + nact.sa_handler = nhandler; + mask2set(nvec->sv_mask, &nact.sa_mask); + /* + if ( sig == SIGTSTP || sig == SIGSTOP ) + nact.sa_handler = SIG_DFL; */ + nact.sa_flags = SA_SIGINFO; + if (!(nvec->sv_flags & SV_INTERRUPT)) + nact.sa_flags |= SA_RESTART; + if (nvec->sv_flags & SV_RESETHAND) + nact.sa_flags |= SA_RESETHAND; + if (nvec->sv_flags & SV_ONSTACK) + nact.sa_flags |= SA_ONSTACK; + nactp = &nact; + } else + nactp = (struct sigaction *)0; + + if (_sigaction(sig, nactp, &oact) < 0) { + _siguhandler[sig] = ohandler; + return -1; + } + + if (ovec) { + if (oact.sa_handler == SIG_DFL || oact.sa_handler == SIG_IGN) + ovec->sv_handler = oact.sa_handler; + else + ovec->sv_handler = ohandler; + ovec->sv_mask = set2mask(&oact.sa_mask); + ovec->sv_flags = 0; + if (oact.sa_flags & SA_ONSTACK) + ovec->sv_flags |= SV_ONSTACK; + if (oact.sa_flags & SA_RESETHAND) + ovec->sv_flags |= SV_RESETHAND; + if (!(oact.sa_flags & SA_RESTART)) + ovec->sv_flags |= SV_INTERRUPT; + } + + return 0; +} + + +void (* +signal(s, a))() + int s; + void (*a)(); +{ + struct sigvec osv; + struct sigvec nsv; + static int mask[NSIG]; + static int flags[NSIG]; + + nsv.sv_handler = a; + nsv.sv_mask = mask[s]; + nsv.sv_flags = flags[s]; + if (sigvec(s, &nsv, &osv) < 0) + return (SIG_ERR); + if (nsv.sv_mask != osv.sv_mask || nsv.sv_flags != osv.sv_flags) { + mask[s] = nsv.sv_mask = osv.sv_mask; + flags[s] = nsv.sv_flags = osv.sv_flags & ~SV_RESETHAND; + if (sigvec(s, &nsv, (struct sigvec *)0) < 0) + return (SIG_ERR); + } + return (osv.sv_handler); +} diff --git a/usr/src/cmd/csh/sparc/signal.h b/usr/src/cmd/csh/sparc/signal.h new file mode 100644 index 0000000000..8861cbac9e --- /dev/null +++ b/usr/src/cmd/csh/sparc/signal.h @@ -0,0 +1,77 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ifndef CSH_SIGNAL_H +#define CSH_SIGNAL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 4.3BSD signal compatibility header + * + */ +#define sigmask(m) (m > 32 ? 0 : (1 << ((m)-1))) + +/* + * 4.3BSD signal vector structure used in sigvec call. + */ +struct sigvec { + void (*sv_handler)(); /* signal handler */ + int sv_mask; /* signal mask to apply */ + int sv_flags; /* see signal options below */ +}; + +#define SV_ONSTACK 0x0001 /* take signal on signal stack */ +#define SV_INTERRUPT 0x0002 /* do not restart system on signal return */ +#define SV_RESETHAND 0x0004 /* reset handler to SIG_DFL when signal taken */ + +#define sv_onstack sv_flags + +/* + * Machine dependent data structure + */ +struct sigcontext { + int sc_onstack; /* sigstack state to restore */ + int sc_mask; /* signal mask to restore */ +#define MAXWINDOW 31 /* max usable windows in sparc */ + long sc_sp; /* sp to restore */ + long sc_pc; /* pc to retore */ + long sc_npc; /* next pc to restore */ + long sc_psr; /* psr to restore */ + long sc_g1; /* register that must be restored */ + long sc_o0; + long sc_wbcnt; /* number of outstanding windows */ + long *sc_spbuf[MAXWINDOW]; /* sp's for each wbuf */ + long sc_wbuf[MAXWINDOW][16]; /* outstanding window save buffer */ +}; + +#define SI_DFLCODE 1 + +#define BUS_HWERR BUS_ADRERR /* misc hardware error (e.g. timeout) */ +#define BUS_ALIGN BUS_ADRALN /* hardware alignment error */ + +#define SEGV_NOMAP SEGV_MAPERR /* no mapping at the fault address */ +#define SEGV_PROT SEGV_ACCERR /* access exceeded protections */ + +/* + * The SEGV_CODE(code) will be SEGV_NOMAP, SEGV_PROT, or SEGV_OBJERR. + * In the SEGV_OBJERR case, doing a SEGV_ERRNO(code) gives an errno value + * reported by the underlying file object mapped at the fault address. + */ + +#define SIG_NOADDR ((char *)~0) + +#define SEGV_MAKE_ERR(e) (((e) << 8) | SEGV_MAPERR) + +#endif /* CSH_SIGNAL_H */ diff --git a/usr/src/cmd/csh/stubs.c b/usr/src/cmd/csh/stubs.c new file mode 100644 index 0000000000..8237fc9fb9 --- /dev/null +++ b/usr/src/cmd/csh/stubs.c @@ -0,0 +1,26 @@ +/* + * Copyright 1990 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +getdomainname() +{ + return; +} + +yp_unbind() +{ + return; +} diff --git a/usr/src/cmd/csh/wait.h b/usr/src/cmd/csh/wait.h new file mode 100644 index 0000000000..97b3579bc8 --- /dev/null +++ b/usr/src/cmd/csh/wait.h @@ -0,0 +1,110 @@ +/* + * Copyright 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T + * All Rights Reserved + * + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file holds definitions relevent to the wait system call. + * Some of the options here are available only through the ``wait3'' + * entry point; the old entry point with one argument has more fixed + * semantics, never returning status of unstopped children, hanging until + * a process terminates if any are outstanding, and never returns + * detailed information about process resource utilization (<vtimes.h>). + */ + +#ifndef _sys_wait_h +#define _sys_wait_h + +#include <sys/isa_defs.h> /* included for _LITTLE_ENDIAN define */ + +/* + * Structure of the information in the first word returned by both + * wait and wait3. If w_stopval==WSTOPPED, then the second structure + * describes the information returned, else the first. See WUNTRACED below. + */ +union wait { + int w_status; /* used in syscall */ + /* + * Terminated process status. + */ + struct { +#if defined(_LITTLE_ENDIAN) + unsigned short w_Termsig:7; /* termination signal */ + unsigned short w_Coredump:1; /* core dump indicator */ + unsigned short w_Retcode:8; /* exit code if w_termsig==0 */ +#elif defined(_BIG_ENDIAN) + unsigned short w_Fill1:16; /* high 16 bits unused */ + unsigned short w_Retcode:8; /* exit code if w_termsig==0 */ + unsigned short w_Coredump:1; /* core dump indicator */ + unsigned short w_Termsig:7; /* termination signal */ +#else +#error NO ENDIAN defined. +#endif + } w_T; + /* + * Stopped process status. Returned + * only for traced children unless requested + * with the WUNTRACED option bit. + */ + struct { +#if defined(_LITTLE_ENDIAN) + unsigned short w_Stopval:8; /* == W_STOPPED if stopped */ + unsigned short w_Stopsig:8; /* signal that stopped us */ +#elif defined(_BIG_ENDIAN) + unsigned short w_Fill2:16; /* high 16 bits unused */ + unsigned short w_Stopsig:8; /* signal that stopped us */ + unsigned short w_Stopval:8; /* == W_STOPPED if stopped */ +#else +#error NO ENDIAN defined. +#endif + } w_S; +}; +#define w_termsig w_T.w_Termsig +#define w_coredump w_T.w_Coredump +#define w_retcode w_T.w_Retcode +#define w_stopval w_S.w_Stopval +#define w_stopsig w_S.w_Stopsig + + +#define WSTOPPED 0177 /* value of s.stopval if process is stopped */ +#define WCONTFLG 0177777 /* value of w.w_status due to SIGCONT, sysV */ + +/* + * Option bits for the second argument of wait3. WNOHANG causes the + * wait to not hang if there are no stopped or terminated processes, rather + * returning an error indication in this case (pid==0). WUNTRACED + * indicates that the caller should receive status about untraced children + * which stop due to signals. If children are stopped and a wait without + * this option is done, it is as though they were still running... nothing + * about them is returned. + */ +#define WNOHANG 1 /* dont hang in wait */ +#define WUNTRACED 2 /* tell about stopped, untraced children */ + +#define WIFSTOPPED(x) ((x).w_stopval == WSTOPPED) +#define WIFSIGNALED(x) ((x).w_stopval != WSTOPPED && (x).w_termsig != 0) +#define WIFEXITED(x) ((x).w_stopval != WSTOPPED && (x).w_termsig == 0) + +#ifdef KERNEL +/* + * Arguments to wait4() system call, included here so it may be called by + * other routines in the kernel + */ +struct wait4_args { + int pid; + union wait *status; + int options; + struct rusage *rusage; +}; +#endif KERNEL + +#endif /*!_sys_wait_h*/ diff --git a/usr/src/cmd/csh/wait3.c b/usr/src/cmd/csh/wait3.c new file mode 100644 index 0000000000..b2ae68c95a --- /dev/null +++ b/usr/src/cmd/csh/wait3.c @@ -0,0 +1,136 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Compatibility lib for BSD's wait3(). It is not + * binary compatible, since BSD's WNOHANG and WUNTRACED + * carry different #define values. + */ +#include <errno.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/times.h> +#include <wait.h> +#include <sys/siginfo.h> +#include <sys/procset.h> +#include <sys/param.h> +#include <sys/resource.h> + +/* + * Since sysV does not support rusage as in BSD, an approximate approach + * is: + * ... + * call times + * call waitid + * if ( a child is found ) + * call times again + * rusage ~= diff in the 2 times call + * ... + * + */ + +/* + * XXX: There is now a wait3 function in libc which should be used instead + * of this local version of wait3. With the addition of a wait3 prototype + * in <sys/wait.h> as per the X/Open XPG4v2 specification, compilation of + * the csh utility will result in warnings, hence the renaming of the local + * version. Using the libc wait3 rather than the local version results in + * a failure with csh, however, this version should eventually be dropped + * in favor of the libc wait3 with appropriate updates made to sh.proc.c + * to account for the difference in implementation of the local versus + * the libc versions. This should probably be done as part of an overall + * effort to rid csh of local versions of functions now in libc. + */ + +csh_wait3(status, options, rp) + int *status; + int options; + struct rusage *rp; + +{ + struct tms before_tms; + struct tms after_tms; + siginfo_t info; + int error; + + if (rp) + memset((void *)rp, 0, sizeof(struct rusage)); + memset((void *)&info, 0, sizeof(siginfo_t)); + if ( times(&before_tms) == -1 ) + return (-1); /* errno is set by times() */ + + /* + * BSD's wait3() only supports WNOHANG & WUNTRACED + */ + options |= (WNOHANG|WUNTRACED|WEXITED|WSTOPPED|WTRAPPED|WCONTINUED); + error = waitid(P_ALL, 0, &info, options); + if ( error == 0 ) { + clock_t diffu; /* difference in usertime (ticks) */ + clock_t diffs; /* difference in systemtime (ticks) */ + + if ( (options & WNOHANG) && (info.si_pid == 0) ) + return (0); /* no child found */ + + if (rp) { + if ( times(&after_tms) == -1 ) + return (-1); /* errno set by times() */ + /* + * The system/user time is an approximation only !!! + */ + diffu = after_tms.tms_cutime - before_tms.tms_cutime; + diffs = after_tms.tms_cstime - before_tms.tms_cstime; + rp->ru_utime.tv_sec = diffu/HZ; + rp->ru_utime.tv_usec = (diffu % HZ) / HZ * 1000000; + rp->ru_stime.tv_sec = diffs/HZ; + rp->ru_stime.tv_usec = (diffs % HZ) / HZ * 1000000; + } + *status = wstat(info.si_code, info.si_status); + return (info.si_pid); + + } else { + return (-1); /* error number is set by waitid() */ + } +} + +/* + * Convert the status code to old style wait status + */ +wstat(code, status) + int code; + int status; +{ + register stat = (status & 0377); + + switch (code) { + case CLD_EXITED: + stat <<= 8; + break; + case CLD_DUMPED: + stat |= WCOREFLG; + break; + case CLD_KILLED: + break; + case CLD_TRAPPED: + case CLD_STOPPED: + stat <<= 8; + stat |= WSTOPFLG; + break; + case CLD_CONTINUED: + stat = WCONTFLG; + break; + } + return (stat); +} |