Importing bin/ksh
This commit is contained in:
parent
173f4de7a1
commit
2718b5688b
58 changed files with 32827 additions and 1 deletions
|
@ -1,7 +1,7 @@
|
|||
# $NetBSD: Makefile,v 1.22 2007/12/31 15:31:24 ad Exp $
|
||||
# @(#)Makefile 8.1 (Berkeley) 5/31/93
|
||||
|
||||
SUBDIR= cat date echo ed expr kill ln ls \
|
||||
SUBDIR= cat date echo ed expr kill ksh ln ls \
|
||||
mkdir pax pwd rm rmdir sync test
|
||||
|
||||
.include <bsd.subdir.mk>
|
||||
|
|
50
bin/ksh/Makefile
Normal file
50
bin/ksh/Makefile
Normal file
|
@ -0,0 +1,50 @@
|
|||
# $NetBSD: Makefile,v 1.30 2011/10/16 17:12:11 joerg Exp $
|
||||
|
||||
WARNS=3
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
CPPFLAGS+= -I.
|
||||
|
||||
PROG= ksh
|
||||
SRCS= alloc.c c_ksh.c c_sh.c c_test.c c_ulimit.c edit.c emacs.c \
|
||||
eval.c exec.c expr.c history.c io.c jobs.c lex.c mail.c \
|
||||
main.c misc.c path.c shf.c sigact.c syn.c table.c trap.c \
|
||||
tree.c tty.c var.c version.c vi.c
|
||||
DPSRCS= emacs.out siglist.out
|
||||
.if (${MKMAN} != "no")
|
||||
DPSRCS+=ksh.1
|
||||
.endif
|
||||
|
||||
# needs tbl for the man page.
|
||||
USETBL=
|
||||
|
||||
# Environment for scripts executed during build.
|
||||
SCRIPT_ENV= \
|
||||
AWK=${TOOL_AWK:Q} \
|
||||
SED=${TOOL_SED:Q}
|
||||
|
||||
CLEANFILES+= siglist.out siglist.out.tmp
|
||||
# two steps to prevent the creation of a bogus siglist.out
|
||||
siglist.out: config.h sh.h siglist.in siglist.sh
|
||||
${_MKTARGET_CREATE}
|
||||
${SCRIPT_ENV} \
|
||||
${HOST_SH} $(.CURDIR)/siglist.sh "$(CC) -E $(CPPFLAGS) $(DEFS) -I. -I$(.CURDIR)" < $(.CURDIR)/siglist.in > siglist.out.tmp \
|
||||
&& mv siglist.out.tmp siglist.out
|
||||
|
||||
# two steps to prevent the creation of a bogus emacs.out
|
||||
CLEANFILES+= emacs.out emacs.out.tmp
|
||||
emacs.out: emacs.c
|
||||
${_MKTARGET_CREATE}
|
||||
${SCRIPT_ENV} \
|
||||
${HOST_SH} $(.CURDIR)/emacs-gen.sh $(.CURDIR)/emacs.c > emacs.out.tmp \
|
||||
&& mv emacs.out.tmp emacs.out
|
||||
|
||||
CLEANFILES+= ksh.1 ksh.1.tmp
|
||||
ksh.1: ksh.Man mkman
|
||||
${_MKTARGET_CREATE}
|
||||
${SCRIPT_ENV} \
|
||||
${HOST_SH} $(.CURDIR)/mkman ksh $(.CURDIR)/ksh.Man >ksh.1.tmp \
|
||||
&& mv ksh.1.tmp ksh.1
|
||||
|
||||
.include <bsd.prog.mk>
|
127
bin/ksh/alloc.c
Normal file
127
bin/ksh/alloc.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
/* $NetBSD: alloc.c,v 1.10 2007/12/12 22:55:42 lukem Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Marc Espie.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
|
||||
* PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* area-based allocation built on malloc/free
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: alloc.c,v 1.10 2007/12/12 22:55:42 lukem Exp $");
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
struct link {
|
||||
struct link *prev;
|
||||
struct link *next;
|
||||
};
|
||||
|
||||
Area *
|
||||
ainit(Area *ap)
|
||||
{
|
||||
ap->freelist = NULL;
|
||||
return ap;
|
||||
}
|
||||
|
||||
void
|
||||
afreeall(Area *ap)
|
||||
{
|
||||
struct link *l, *l2;
|
||||
|
||||
for (l = ap->freelist; l != NULL; l = l2) {
|
||||
l2 = l->next;
|
||||
free(l);
|
||||
}
|
||||
ap->freelist = NULL;
|
||||
}
|
||||
|
||||
#define L2P(l) ( (void *)(((char *)(l)) + sizeof(struct link)) )
|
||||
#define P2L(p) ( (struct link *)(((char *)(p)) - sizeof(struct link)) )
|
||||
|
||||
/* coverity[+alloc] */
|
||||
void *
|
||||
alloc(size_t size, Area *ap)
|
||||
{
|
||||
struct link *l;
|
||||
|
||||
l = malloc(sizeof(struct link) + size);
|
||||
if (l == NULL)
|
||||
internal_errorf(1, "unable to allocate memory");
|
||||
l->next = ap->freelist;
|
||||
l->prev = NULL;
|
||||
if (ap->freelist)
|
||||
ap->freelist->prev = l;
|
||||
ap->freelist = l;
|
||||
|
||||
return L2P(l);
|
||||
}
|
||||
|
||||
/* coverity[+alloc] */
|
||||
/* coverity[+free : arg-0] */
|
||||
void *
|
||||
aresize(void *ptr, size_t size, Area *ap)
|
||||
{
|
||||
struct link *l, *l2, *lprev, *lnext;
|
||||
|
||||
if (ptr == NULL)
|
||||
return alloc(size, ap);
|
||||
|
||||
l = P2L(ptr);
|
||||
lprev = l->prev;
|
||||
lnext = l->next;
|
||||
|
||||
l2 = realloc(l, sizeof(struct link) + size);
|
||||
if (l2 == NULL)
|
||||
internal_errorf(1, "unable to allocate memory");
|
||||
if (lprev)
|
||||
lprev->next = l2;
|
||||
else
|
||||
ap->freelist = l2;
|
||||
if (lnext)
|
||||
lnext->prev = l2;
|
||||
|
||||
return L2P(l2);
|
||||
}
|
||||
|
||||
/* coverity[+free : arg-0] */
|
||||
void
|
||||
afree(void *ptr, Area *ap)
|
||||
{
|
||||
struct link *l;
|
||||
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
l = P2L(ptr);
|
||||
|
||||
if (l->prev)
|
||||
l->prev->next = l->next;
|
||||
else
|
||||
ap->freelist = l->next;
|
||||
if (l->next)
|
||||
l->next->prev = l->prev;
|
||||
|
||||
free(l);
|
||||
}
|
1490
bin/ksh/c_ksh.c
Normal file
1490
bin/ksh/c_ksh.c
Normal file
File diff suppressed because it is too large
Load diff
917
bin/ksh/c_sh.c
Normal file
917
bin/ksh/c_sh.c
Normal file
|
@ -0,0 +1,917 @@
|
|||
/* $NetBSD: c_sh.c,v 1.14 2011/08/31 16:24:54 plunky Exp $ */
|
||||
|
||||
/*
|
||||
* built-in Bourne commands
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: c_sh.c,v 1.14 2011/08/31 16:24:54 plunky Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
#include "ksh_stat.h" /* umask() */
|
||||
#include "ksh_time.h"
|
||||
#include "ksh_times.h"
|
||||
|
||||
static char *clocktos ARGS((clock_t t));
|
||||
|
||||
|
||||
/* :, false and true */
|
||||
int
|
||||
c_label(wp)
|
||||
char **wp;
|
||||
{
|
||||
return wp[0][0] == 'f' ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
c_shift(wp)
|
||||
char **wp;
|
||||
{
|
||||
register struct block *l = e->loc;
|
||||
register int n;
|
||||
long val;
|
||||
char *arg;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
return 1;
|
||||
arg = wp[builtin_opt.optind];
|
||||
|
||||
if (arg) {
|
||||
evaluate(arg, &val, KSH_UNWIND_ERROR);
|
||||
n = val;
|
||||
} else
|
||||
n = 1;
|
||||
if (n < 0) {
|
||||
bi_errorf("%s: bad number", arg);
|
||||
return (1);
|
||||
}
|
||||
if (l->argc < n) {
|
||||
bi_errorf("nothing to shift");
|
||||
return (1);
|
||||
}
|
||||
l->argv[n] = l->argv[0];
|
||||
l->argv += n;
|
||||
l->argc -= n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
c_umask(wp)
|
||||
char **wp;
|
||||
{
|
||||
register int i;
|
||||
register char *cp;
|
||||
int symbolic = 0;
|
||||
int old_umask;
|
||||
int optc;
|
||||
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != EOF)
|
||||
switch (optc) {
|
||||
case 'S':
|
||||
symbolic = 1;
|
||||
break;
|
||||
case '?':
|
||||
return 1;
|
||||
}
|
||||
cp = wp[builtin_opt.optind];
|
||||
if (cp == NULL) {
|
||||
old_umask = umask(0);
|
||||
umask(old_umask);
|
||||
if (symbolic) {
|
||||
char buf[18];
|
||||
int j;
|
||||
|
||||
old_umask = ~old_umask;
|
||||
cp = buf;
|
||||
for (i = 0; i < 3; i++) {
|
||||
*cp++ = "ugo"[i];
|
||||
*cp++ = '=';
|
||||
for (j = 0; j < 3; j++)
|
||||
if (old_umask & (1 << (8 - (3*i + j))))
|
||||
*cp++ = "rwx"[j];
|
||||
*cp++ = ',';
|
||||
}
|
||||
cp[-1] = '\0';
|
||||
shprintf("%s\n", buf);
|
||||
} else
|
||||
shprintf("%#3.3o\n", old_umask);
|
||||
} else {
|
||||
int new_umask;
|
||||
|
||||
if (digit(*cp)) {
|
||||
for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
|
||||
new_umask = new_umask * 8 + (*cp - '0');
|
||||
if (*cp) {
|
||||
bi_errorf("bad number");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
/* symbolic format */
|
||||
int positions, new_val;
|
||||
char op;
|
||||
|
||||
old_umask = umask(0);
|
||||
umask(old_umask); /* in case of error */
|
||||
old_umask = ~old_umask;
|
||||
new_umask = old_umask;
|
||||
positions = 0;
|
||||
while (*cp) {
|
||||
while (*cp && strchr("augo", *cp))
|
||||
switch (*cp++) {
|
||||
case 'a': positions |= 0111; break;
|
||||
case 'u': positions |= 0100; break;
|
||||
case 'g': positions |= 0010; break;
|
||||
case 'o': positions |= 0001; break;
|
||||
}
|
||||
if (!positions)
|
||||
positions = 0111; /* default is a */
|
||||
if (!strchr("=+-", op = *cp))
|
||||
break;
|
||||
cp++;
|
||||
new_val = 0;
|
||||
while (*cp && strchr("rwxugoXs", *cp))
|
||||
switch (*cp++) {
|
||||
case 'r': new_val |= 04; break;
|
||||
case 'w': new_val |= 02; break;
|
||||
case 'x': new_val |= 01; break;
|
||||
case 'u': new_val |= old_umask >> 6;
|
||||
break;
|
||||
case 'g': new_val |= old_umask >> 3;
|
||||
break;
|
||||
case 'o': new_val |= old_umask >> 0;
|
||||
break;
|
||||
case 'X': if (old_umask & 0111)
|
||||
new_val |= 01;
|
||||
break;
|
||||
case 's': /* ignored */
|
||||
break;
|
||||
}
|
||||
new_val = (new_val & 07) * positions;
|
||||
switch (op) {
|
||||
case '-':
|
||||
new_umask &= ~new_val;
|
||||
break;
|
||||
case '=':
|
||||
new_umask = new_val
|
||||
| (new_umask & ~(positions * 07));
|
||||
break;
|
||||
case '+':
|
||||
new_umask |= new_val;
|
||||
}
|
||||
if (*cp == ',') {
|
||||
positions = 0;
|
||||
cp++;
|
||||
} else if (!strchr("=+-", *cp))
|
||||
break;
|
||||
}
|
||||
if (*cp) {
|
||||
bi_errorf("bad mask");
|
||||
return 1;
|
||||
}
|
||||
new_umask = ~new_umask;
|
||||
}
|
||||
umask(new_umask);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
c_dot(wp)
|
||||
char **wp;
|
||||
{
|
||||
char *file, *cp;
|
||||
char **argv;
|
||||
int argc;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
return 1;
|
||||
|
||||
if ((cp = wp[builtin_opt.optind]) == NULL)
|
||||
return 0;
|
||||
file = search(cp, path, R_OK, &err);
|
||||
if (file == NULL) {
|
||||
bi_errorf("%s: %s", cp, err ? strerror(err) : "not found");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Set positional parameters? */
|
||||
if (wp[builtin_opt.optind + 1]) {
|
||||
argv = wp + builtin_opt.optind;
|
||||
argv[0] = e->loc->argv[0]; /* preserve $0 */
|
||||
for (argc = 0; argv[argc + 1]; argc++)
|
||||
;
|
||||
} else {
|
||||
argc = 0;
|
||||
argv = (char **) 0;
|
||||
}
|
||||
i = include(file, argc, argv, 0);
|
||||
if (i < 0) { /* should not happen */
|
||||
bi_errorf("%s: %s", cp, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
c_wait(wp)
|
||||
char **wp;
|
||||
{
|
||||
int UNINITIALIZED(rv);
|
||||
int sig;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
return 1;
|
||||
wp += builtin_opt.optind;
|
||||
if (*wp == (char *) 0) {
|
||||
while (waitfor((char *) 0, &sig) >= 0)
|
||||
;
|
||||
rv = sig;
|
||||
} else {
|
||||
for (; *wp; wp++)
|
||||
rv = waitfor(*wp, &sig);
|
||||
if (rv < 0)
|
||||
rv = sig ? sig : 127; /* magic exit code: bad job-id */
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
c_read(wp)
|
||||
char **wp;
|
||||
{
|
||||
register int c = 0;
|
||||
int expandv = 1, history = 0;
|
||||
int expanding;
|
||||
int ecode = 0;
|
||||
register char *cp;
|
||||
int fd = 0;
|
||||
struct shf *shf;
|
||||
int optc;
|
||||
const char *emsg;
|
||||
XString cs, xs;
|
||||
struct tbl *vp;
|
||||
char UNINITIALIZED(*xp);
|
||||
static char REPLY[] = "REPLY";
|
||||
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != EOF)
|
||||
switch (optc) {
|
||||
#ifdef KSH
|
||||
case 'p':
|
||||
if ((fd = coproc_getfd(R_OK, &emsg)) < 0) {
|
||||
bi_errorf("-p: %s", emsg);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
#endif /* KSH */
|
||||
case 'r':
|
||||
expandv = 0;
|
||||
break;
|
||||
case 's':
|
||||
history = 1;
|
||||
break;
|
||||
case 'u':
|
||||
if (!*(cp = builtin_opt.optarg))
|
||||
fd = 0;
|
||||
else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) {
|
||||
bi_errorf("-u: %s: %s", cp, emsg);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
return 1;
|
||||
}
|
||||
wp += builtin_opt.optind;
|
||||
|
||||
if (*wp == NULL)
|
||||
*--wp = REPLY;
|
||||
|
||||
/* Since we can't necessarily seek backwards on non-regular files,
|
||||
* don't buffer them so we can't read too much.
|
||||
*/
|
||||
shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare);
|
||||
|
||||
if ((cp = strchr(*wp, '?')) != NULL) {
|
||||
*cp = 0;
|
||||
if (isatty(fd)) {
|
||||
/* at&t ksh says it prints prompt on fd if it's open
|
||||
* for writing and is a tty, but it doesn't do it
|
||||
* (it also doesn't check the interactive flag,
|
||||
* as is indicated in the Kornshell book).
|
||||
*/
|
||||
shellf("%s", cp+1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef KSH
|
||||
/* If we are reading from the co-process for the first time,
|
||||
* make sure the other side of the pipe is closed first. This allows
|
||||
* the detection of eof.
|
||||
*
|
||||
* This is not compatible with at&t ksh... the fd is kept so another
|
||||
* coproc can be started with same output, however, this means eof
|
||||
* can't be detected... This is why it is closed here.
|
||||
* If this call is removed, remove the eof check below, too.
|
||||
* coproc_readw_close(fd);
|
||||
*/
|
||||
#endif /* KSH */
|
||||
|
||||
if (history)
|
||||
Xinit(xs, xp, 128, ATEMP);
|
||||
expanding = 0;
|
||||
Xinit(cs, cp, 128, ATEMP);
|
||||
for (; *wp != NULL; wp++) {
|
||||
for (cp = Xstring(cs, cp); ; ) {
|
||||
if (c == '\n' || c == EOF)
|
||||
break;
|
||||
while (1) {
|
||||
c = shf_getc(shf);
|
||||
if (c == '\0'
|
||||
#ifdef OS2
|
||||
|| c == '\r'
|
||||
#endif /* OS2 */
|
||||
)
|
||||
continue;
|
||||
if (c == EOF && shf_error(shf)
|
||||
&& shf_errno(shf) == EINTR)
|
||||
{
|
||||
/* Was the offending signal one that
|
||||
* would normally kill a process?
|
||||
* If so, pretend the read was killed.
|
||||
*/
|
||||
ecode = fatal_trap_check();
|
||||
|
||||
/* non fatal (eg, CHLD), carry on */
|
||||
if (!ecode) {
|
||||
shf_clearerr(shf);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (history) {
|
||||
Xcheck(xs, xp);
|
||||
Xput(xs, xp, c);
|
||||
}
|
||||
Xcheck(cs, cp);
|
||||
if (expanding) {
|
||||
expanding = 0;
|
||||
if (c == '\n') {
|
||||
c = 0;
|
||||
if (Flag(FTALKING_I) && isatty(fd)) {
|
||||
/* set prompt in case this is
|
||||
* called from .profile or $ENV
|
||||
*/
|
||||
set_prompt(PS2, (Source *) 0);
|
||||
pprompt(prompt, 0);
|
||||
}
|
||||
} else if (c != EOF)
|
||||
Xput(cs, cp, c);
|
||||
continue;
|
||||
}
|
||||
if (expandv && c == '\\') {
|
||||
expanding = 1;
|
||||
continue;
|
||||
}
|
||||
if (c == '\n' || c == EOF)
|
||||
break;
|
||||
if (ctype(c, C_IFS)) {
|
||||
if (Xlength(cs, cp) == 0 && ctype(c, C_IFSWS))
|
||||
continue;
|
||||
if (wp[1])
|
||||
break;
|
||||
}
|
||||
Xput(cs, cp, c);
|
||||
}
|
||||
/* strip trailing IFS white space from last variable */
|
||||
if (!wp[1])
|
||||
while (Xlength(cs, cp) && ctype(cp[-1], C_IFS)
|
||||
&& ctype(cp[-1], C_IFSWS))
|
||||
cp--;
|
||||
Xput(cs, cp, '\0');
|
||||
vp = global(*wp);
|
||||
/* Must be done before setting export. */
|
||||
if (vp->flag & RDONLY) {
|
||||
shf_flush(shf);
|
||||
bi_errorf("%s is read only", *wp);
|
||||
return 1;
|
||||
}
|
||||
if (Flag(FEXPORT))
|
||||
typeset(*wp, EXPORT, 0, 0, 0);
|
||||
if (!setstr(vp, Xstring(cs, cp), KSH_RETURN_ERROR)) {
|
||||
shf_flush(shf);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
shf_flush(shf);
|
||||
if (history) {
|
||||
Xput(xs, xp, '\0');
|
||||
source->line++;
|
||||
histsave(source->line, Xstring(xs, xp), 1);
|
||||
Xfree(xs, xp);
|
||||
}
|
||||
#ifdef KSH
|
||||
/* if this is the co-process fd, close the file descriptor
|
||||
* (can get eof if and only if all processes are have died, ie,
|
||||
* coproc.njobs is 0 and the pipe is closed).
|
||||
*/
|
||||
if (c == EOF && !ecode)
|
||||
coproc_read_close(fd);
|
||||
#endif /* KSH */
|
||||
|
||||
return ecode ? ecode : c == EOF;
|
||||
}
|
||||
|
||||
int
|
||||
c_eval(wp)
|
||||
char **wp;
|
||||
{
|
||||
register struct source *s;
|
||||
int rv;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
return 1;
|
||||
s = pushs(SWORDS, ATEMP);
|
||||
s->u.strv = wp + builtin_opt.optind;
|
||||
if (!Flag(FPOSIX)) {
|
||||
/*
|
||||
* Handle case where the command is empty due to failed
|
||||
* command substitution, eg, eval "$(false)".
|
||||
* In this case, shell() will not set/change exstat (because
|
||||
* compiled tree is empty), so will use this value.
|
||||
* subst_exstat is cleared in execute(), so should be 0 if
|
||||
* there were no substitutions.
|
||||
*
|
||||
* A strict reading of POSIX says we don't do this (though
|
||||
* it is traditionally done). [from 1003.2-1992]
|
||||
* 3.9.1: Simple Commands
|
||||
* ... If there is a command name, execution shall
|
||||
* continue as described in 3.9.1.1. If there
|
||||
* is no command name, but the command contained a command
|
||||
* substitution, the command shall complete with the exit
|
||||
* status of the last command substitution
|
||||
* 3.9.1.1: Command Search and Execution
|
||||
* ...(1)...(a) If the command name matches the name of
|
||||
* a special built-in utility, that special built-in
|
||||
* utility shall be invoked.
|
||||
* 3.14.5: Eval
|
||||
* ... If there are no arguments, or only null arguments,
|
||||
* eval shall return an exit status of zero.
|
||||
*/
|
||||
exstat = subst_exstat;
|
||||
}
|
||||
|
||||
rv = shell(s, FALSE);
|
||||
afree(s, ATEMP);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
c_trap(wp)
|
||||
char **wp;
|
||||
{
|
||||
int i;
|
||||
char *s;
|
||||
register Trap *p;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
return 1;
|
||||
wp += builtin_opt.optind;
|
||||
|
||||
if (*wp == NULL) {
|
||||
int anydfl = 0;
|
||||
|
||||
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) {
|
||||
if (p->trap == NULL)
|
||||
anydfl = 1;
|
||||
else {
|
||||
shprintf("trap -- ");
|
||||
print_value_quoted(p->trap);
|
||||
shprintf(" %s\n", p->name);
|
||||
}
|
||||
}
|
||||
#if 0 /* this is ugly and not clear POSIX needs it */
|
||||
/* POSIX may need this so output of trap can be saved and
|
||||
* used to restore trap conditions
|
||||
*/
|
||||
if (anydfl) {
|
||||
shprintf("trap -- -");
|
||||
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
||||
if (p->trap == NULL && p->name)
|
||||
shprintf(" %s", p->name);
|
||||
shprintf(newline);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use case sensitive lookup for first arg so the
|
||||
* command 'exit' isn't confused with the pseudo-signal
|
||||
* 'EXIT'.
|
||||
*/
|
||||
s = (gettrap(*wp, FALSE) == NULL) ? *wp++ : NULL; /* get command */
|
||||
if (s != NULL && s[0] == '-' && s[1] == '\0')
|
||||
s = NULL;
|
||||
|
||||
/* set/clear traps */
|
||||
while (*wp != NULL) {
|
||||
p = gettrap(*wp++, TRUE);
|
||||
if (p == NULL) {
|
||||
bi_errorf("bad signal %s", wp[-1]);
|
||||
return 1;
|
||||
}
|
||||
settrap(p, s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
c_exitreturn(wp)
|
||||
char **wp;
|
||||
{
|
||||
int how = LEXIT;
|
||||
int n;
|
||||
char *arg;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
return 1;
|
||||
arg = wp[builtin_opt.optind];
|
||||
|
||||
if (arg) {
|
||||
if (!getn(arg, &n)) {
|
||||
exstat = 1;
|
||||
warningf(TRUE, "%s: bad number", arg);
|
||||
} else
|
||||
exstat = n;
|
||||
}
|
||||
if (wp[0][0] == 'r') { /* return */
|
||||
struct env *ep;
|
||||
|
||||
/* need to tell if this is exit or return so trap exit will
|
||||
* work right (POSIX)
|
||||
*/
|
||||
for (ep = e; ep; ep = ep->oenv)
|
||||
if (STOP_RETURN(ep->type)) {
|
||||
how = LRETURN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (how == LEXIT && !really_exit && j_stopped_running()) {
|
||||
really_exit = 1;
|
||||
how = LSHELL;
|
||||
}
|
||||
|
||||
quitenv(); /* get rid of any i/o redirections */
|
||||
unwind(how);
|
||||
/*NOTREACHED*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
c_brkcont(wp)
|
||||
char **wp;
|
||||
{
|
||||
int n, quit;
|
||||
struct env *ep, *last_ep = (struct env *) 0;
|
||||
char *arg;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
return 1;
|
||||
arg = wp[builtin_opt.optind];
|
||||
|
||||
if (!arg)
|
||||
n = 1;
|
||||
else if (!bi_getn(arg, &n))
|
||||
return 1;
|
||||
quit = n;
|
||||
if (quit <= 0) {
|
||||
/* at&t ksh does this for non-interactive shells only - weird */
|
||||
bi_errorf("%s: bad value", arg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
|
||||
for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
|
||||
if (ep->type == E_LOOP) {
|
||||
if (--quit == 0)
|
||||
break;
|
||||
ep->flags |= EF_BRKCONT_PASS;
|
||||
last_ep = ep;
|
||||
}
|
||||
|
||||
if (quit) {
|
||||
/* at&t ksh doesn't print a message - just does what it
|
||||
* can. We print a message 'cause it helps in debugging
|
||||
* scripts, but don't generate an error (ie, keep going).
|
||||
*/
|
||||
if (n == quit) {
|
||||
warningf(TRUE, "%s: cannot %s", wp[0], wp[0]);
|
||||
return 0;
|
||||
}
|
||||
/* POSIX says if n is too big, the last enclosing loop
|
||||
* shall be used. Doesn't say to print an error but we
|
||||
* do anyway 'cause the user messed up.
|
||||
*/
|
||||
if (last_ep)
|
||||
last_ep->flags &= ~EF_BRKCONT_PASS;
|
||||
warningf(TRUE, "%s: can only %s %d level(s)",
|
||||
wp[0], wp[0], n - quit);
|
||||
}
|
||||
|
||||
unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
||||
int
|
||||
c_set(wp)
|
||||
char **wp;
|
||||
{
|
||||
int argi, setargs;
|
||||
struct block *l = e->loc;
|
||||
register char **owp = wp;
|
||||
|
||||
if (wp[1] == NULL) {
|
||||
static const char *const args [] = { "set", "-", NULL };
|
||||
return c_typeset((char **)__UNCONST(args));
|
||||
}
|
||||
|
||||
argi = parse_args(wp, OF_SET, &setargs);
|
||||
if (argi < 0)
|
||||
return 1;
|
||||
/* set $# and $* */
|
||||
if (setargs) {
|
||||
owp = wp += argi - 1;
|
||||
wp[0] = l->argv[0]; /* save $0 */
|
||||
while (*++wp != NULL)
|
||||
*wp = str_save(*wp, &l->area);
|
||||
l->argc = wp - owp - 1;
|
||||
l->argv = (char **) alloc(sizeofN(char *, l->argc+2), &l->area);
|
||||
for (wp = l->argv; (*wp++ = *owp++) != NULL; )
|
||||
;
|
||||
}
|
||||
/* POSIX says set exit status is 0, but old scripts that use
|
||||
* getopt(1), use the construct: set -- `getopt ab:c "$@"`
|
||||
* which assumes the exit value set will be that of the ``
|
||||
* (subst_exstat is cleared in execute() so that it will be 0
|
||||
* if there are no command substitutions).
|
||||
*/
|
||||
return Flag(FPOSIX) ? 0 : subst_exstat;
|
||||
}
|
||||
|
||||
int
|
||||
c_unset(wp)
|
||||
char **wp;
|
||||
{
|
||||
register char *id;
|
||||
int optc, unset_var = 1;
|
||||
int ret = 0;
|
||||
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != EOF)
|
||||
switch (optc) {
|
||||
case 'f':
|
||||
unset_var = 0;
|
||||
break;
|
||||
case 'v':
|
||||
unset_var = 1;
|
||||
break;
|
||||
case '?':
|
||||
return 1;
|
||||
}
|
||||
wp += builtin_opt.optind;
|
||||
for (; (id = *wp) != NULL; wp++)
|
||||
if (unset_var) { /* unset variable */
|
||||
struct tbl *vp = global(id);
|
||||
|
||||
if (!(vp->flag & ISSET))
|
||||
ret = 1;
|
||||
if ((vp->flag&RDONLY)) {
|
||||
bi_errorf("%s is read only", vp->name);
|
||||
return 1;
|
||||
}
|
||||
unset(vp, strchr(id, '[') ? 1 : 0);
|
||||
} else { /* unset function */
|
||||
if (define(id, NULL))
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
c_times(wp)
|
||||
char **wp;
|
||||
{
|
||||
struct tms all;
|
||||
|
||||
(void) ksh_times(&all);
|
||||
shprintf("Shell: %8ss user ", clocktos(all.tms_utime));
|
||||
shprintf("%8ss system\n", clocktos(all.tms_stime));
|
||||
shprintf("Kids: %8ss user ", clocktos(all.tms_cutime));
|
||||
shprintf("%8ss system\n", clocktos(all.tms_cstime));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* time pipeline (really a statement, not a built-in command)
|
||||
*/
|
||||
int
|
||||
timex(t, f)
|
||||
struct op *t;
|
||||
int f;
|
||||
{
|
||||
#define TF_NOARGS BIT(0)
|
||||
#define TF_NOREAL BIT(1) /* don't report real time */
|
||||
#define TF_POSIX BIT(2) /* report in posix format */
|
||||
int rv = 0;
|
||||
struct tms t0, t1, tms;
|
||||
clock_t t0t, t1t = 0;
|
||||
int tf = 0;
|
||||
extern clock_t j_usrtime, j_systime; /* computed by j_wait */
|
||||
char opts[1];
|
||||
|
||||
t0t = ksh_times(&t0);
|
||||
if (t->left) {
|
||||
/*
|
||||
* Two ways of getting cpu usage of a command: just use t0
|
||||
* and t1 (which will get cpu usage from other jobs that
|
||||
* finish while we are executing t->left), or get the
|
||||
* cpu usage of t->left. at&t ksh does the former, while
|
||||
* pdksh tries to do the later (the j_usrtime hack doesn't
|
||||
* really work as it only counts the last job).
|
||||
*/
|
||||
j_usrtime = j_systime = 0;
|
||||
if (t->left->type == TCOM)
|
||||
t->left->str = opts;
|
||||
opts[0] = 0;
|
||||
rv = execute(t->left, f | XTIME);
|
||||
tf |= opts[0];
|
||||
t1t = ksh_times(&t1);
|
||||
} else
|
||||
tf = TF_NOARGS;
|
||||
|
||||
if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */
|
||||
tf |= TF_NOREAL;
|
||||
tms.tms_utime = t0.tms_utime + t0.tms_cutime;
|
||||
tms.tms_stime = t0.tms_stime + t0.tms_cstime;
|
||||
} else {
|
||||
tms.tms_utime = t1.tms_utime - t0.tms_utime + j_usrtime;
|
||||
tms.tms_stime = t1.tms_stime - t0.tms_stime + j_systime;
|
||||
}
|
||||
|
||||
if (!(tf & TF_NOREAL))
|
||||
shf_fprintf(shl_out,
|
||||
tf & TF_POSIX ? "real %8s\n" : "%8ss real ",
|
||||
clocktos(t1t - t0t));
|
||||
shf_fprintf(shl_out, tf & TF_POSIX ? "user %8s\n" : "%8ss user ",
|
||||
clocktos(tms.tms_utime));
|
||||
shf_fprintf(shl_out, tf & TF_POSIX ? "sys %8s\n" : "%8ss system\n",
|
||||
clocktos(tms.tms_stime));
|
||||
shf_flush(shl_out);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
timex_hook(t, app)
|
||||
struct op *t;
|
||||
char ** volatile *app;
|
||||
{
|
||||
char **wp = *app;
|
||||
int optc;
|
||||
int i, j;
|
||||
Getopt opt;
|
||||
|
||||
ksh_getopt_reset(&opt, 0);
|
||||
opt.optind = 0; /* start at the start */
|
||||
while ((optc = ksh_getopt(wp, &opt, ":p")) != EOF)
|
||||
switch (optc) {
|
||||
case 'p':
|
||||
t->str[0] |= TF_POSIX;
|
||||
break;
|
||||
case '?':
|
||||
errorf("time: -%s unknown option", opt.optarg);
|
||||
case ':':
|
||||
errorf("time: -%s requires an argument",
|
||||
opt.optarg);
|
||||
}
|
||||
/* Copy command words down over options. */
|
||||
if (opt.optind != 0) {
|
||||
for (i = 0; i < opt.optind; i++)
|
||||
afree(wp[i], ATEMP);
|
||||
for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++)
|
||||
;
|
||||
}
|
||||
if (!wp[0])
|
||||
t->str[0] |= TF_NOARGS;
|
||||
*app = wp;
|
||||
}
|
||||
|
||||
static char *
|
||||
clocktos(t)
|
||||
clock_t t;
|
||||
{
|
||||
static char temp[22]; /* enough for 64 bit clock_t */
|
||||
register int i;
|
||||
register char *cp = temp + sizeof(temp);
|
||||
|
||||
/* note: posix says must use max precision, ie, if clk_tck is
|
||||
* 1000, must print 3 places after decimal (if non-zero, else 1).
|
||||
*/
|
||||
if (CLK_TCK != 100) /* convert to 1/100'ths */
|
||||
t = (t < (clock_t)(1000000000/CLK_TCK)) ?
|
||||
(t * 100) / CLK_TCK : (t / CLK_TCK) * 100;
|
||||
|
||||
*--cp = '\0';
|
||||
for (i = -2; i <= 0 || t > 0; i++) {
|
||||
if (i == 0)
|
||||
*--cp = '.';
|
||||
*--cp = '0' + (char)(t%10);
|
||||
t /= 10;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
/* exec with no args - args case is taken care of in comexec() */
|
||||
int
|
||||
c_exec(wp)
|
||||
char ** wp;
|
||||
{
|
||||
int i;
|
||||
|
||||
/* make sure redirects stay in place */
|
||||
if (e->savefd != NULL) {
|
||||
for (i = 0; i < NUFILE; i++) {
|
||||
if (e->savefd[i] > 0)
|
||||
close(e->savefd[i]);
|
||||
/*
|
||||
* For ksh keep anything > 2 private,
|
||||
* for sh, let them be (POSIX says what
|
||||
* happens is unspecified and the bourne shell
|
||||
* keeps them open).
|
||||
*/
|
||||
#ifdef KSH
|
||||
if (i > 2 && e->savefd[i])
|
||||
fd_clexec(i);
|
||||
#endif /* KSH */
|
||||
}
|
||||
e->savefd = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* dummy function, special case in comexec() */
|
||||
int
|
||||
c_builtin(wp)
|
||||
char ** wp;
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int c_test ARGS((char **wp)); /* in c_test.c */
|
||||
extern int c_ulimit ARGS((char **wp)); /* in c_ulimit.c */
|
||||
|
||||
/* A leading = means assignments before command are kept;
|
||||
* a leading * means a POSIX special builtin;
|
||||
* a leading + means a POSIX regular builtin
|
||||
* (* and + should not be combined).
|
||||
*/
|
||||
const struct builtin shbuiltins [] = {
|
||||
{"*=.", c_dot},
|
||||
{"*=:", c_label},
|
||||
{"[", c_test},
|
||||
{"*=break", c_brkcont},
|
||||
{"=builtin", c_builtin},
|
||||
{"*=continue", c_brkcont},
|
||||
{"*=eval", c_eval},
|
||||
{"*=exec", c_exec},
|
||||
{"*=exit", c_exitreturn},
|
||||
{"+false", c_label},
|
||||
{"*=return", c_exitreturn},
|
||||
{"*=set", c_set},
|
||||
{"*=shift", c_shift},
|
||||
{"=times", c_times},
|
||||
{"*=trap", c_trap},
|
||||
{"+=wait", c_wait},
|
||||
{"+read", c_read},
|
||||
{"test", c_test},
|
||||
{"+true", c_label},
|
||||
{"ulimit", c_ulimit},
|
||||
{"+umask", c_umask},
|
||||
{"*=unset", c_unset},
|
||||
#ifdef OS2
|
||||
/* In OS2, the first line of a file can be "extproc name", which
|
||||
* tells the command interpreter (cmd.exe) to use name to execute
|
||||
* the file. For this to be useful, ksh must ignore commands
|
||||
* starting with extproc and this does the trick...
|
||||
*/
|
||||
{"extproc", c_label},
|
||||
#endif /* OS2 */
|
||||
{NULL, NULL}
|
||||
};
|
666
bin/ksh/c_test.c
Normal file
666
bin/ksh/c_test.c
Normal file
|
@ -0,0 +1,666 @@
|
|||
/* $NetBSD: c_test.c,v 1.6 2005/06/26 19:09:00 christos Exp $ */
|
||||
|
||||
/*
|
||||
* test(1); version 7-like -- author Erik Baalbergen
|
||||
* modified by Eric Gisin to be used as built-in.
|
||||
* modified by Arnold Robbins to add SVR3 compatibility
|
||||
* (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
|
||||
* modified by Michael Rendell to add Korn's [[ .. ]] expressions.
|
||||
* modified by J.T. Conklin to add POSIX compatibility.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: c_test.c,v 1.6 2005/06/26 19:09:00 christos Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
#include "ksh_stat.h"
|
||||
#include "c_test.h"
|
||||
|
||||
/* test(1) accepts the following grammar:
|
||||
oexpr ::= aexpr | aexpr "-o" oexpr ;
|
||||
aexpr ::= nexpr | nexpr "-a" aexpr ;
|
||||
nexpr ::= primary | "!" nexpr ;
|
||||
primary ::= unary-operator operand
|
||||
| operand binary-operator operand
|
||||
| operand
|
||||
| "(" oexpr ")"
|
||||
;
|
||||
|
||||
unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
|
||||
"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
|
||||
"-L"|"-h"|"-S"|"-H";
|
||||
|
||||
binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
|
||||
"-nt"|"-ot"|"-ef"|
|
||||
"<"|">" # rules used for [[ .. ]] expressions
|
||||
;
|
||||
operand ::= <any thing>
|
||||
*/
|
||||
|
||||
#define T_ERR_EXIT 2 /* POSIX says > 1 for errors */
|
||||
|
||||
struct t_op {
|
||||
char op_text[4];
|
||||
Test_op op_num;
|
||||
};
|
||||
static const struct t_op u_ops [] = {
|
||||
{"-a", TO_FILAXST },
|
||||
{"-b", TO_FILBDEV },
|
||||
{"-c", TO_FILCDEV },
|
||||
{"-d", TO_FILID },
|
||||
{"-e", TO_FILEXST },
|
||||
{"-f", TO_FILREG },
|
||||
{"-G", TO_FILGID },
|
||||
{"-g", TO_FILSETG },
|
||||
{"-h", TO_FILSYM },
|
||||
{"-H", TO_FILCDF },
|
||||
{"-k", TO_FILSTCK },
|
||||
{"-L", TO_FILSYM },
|
||||
{"-n", TO_STNZE },
|
||||
{"-O", TO_FILUID },
|
||||
{"-o", TO_OPTION },
|
||||
{"-p", TO_FILFIFO },
|
||||
{"-r", TO_FILRD },
|
||||
{"-s", TO_FILGZ },
|
||||
{"-S", TO_FILSOCK },
|
||||
{"-t", TO_FILTT },
|
||||
{"-u", TO_FILSETU },
|
||||
{"-w", TO_FILWR },
|
||||
{"-x", TO_FILEX },
|
||||
{"-z", TO_STZER },
|
||||
{"", TO_NONOP }
|
||||
};
|
||||
static const struct t_op b_ops [] = {
|
||||
{"=", TO_STEQL },
|
||||
#ifdef KSH
|
||||
{"==", TO_STEQL },
|
||||
#endif /* KSH */
|
||||
{"!=", TO_STNEQ },
|
||||
{"<", TO_STLT },
|
||||
{">", TO_STGT },
|
||||
{"-eq", TO_INTEQ },
|
||||
{"-ne", TO_INTNE },
|
||||
{"-gt", TO_INTGT },
|
||||
{"-ge", TO_INTGE },
|
||||
{"-lt", TO_INTLT },
|
||||
{"-le", TO_INTLE },
|
||||
{"-ef", TO_FILEQ },
|
||||
{"-nt", TO_FILNT },
|
||||
{"-ot", TO_FILOT },
|
||||
{"", TO_NONOP }
|
||||
};
|
||||
|
||||
static int test_stat ARGS((const char *, struct stat *));
|
||||
static int test_eaccess ARGS((const char *, int));
|
||||
static int test_oexpr ARGS((Test_env *, int));
|
||||
static int test_aexpr ARGS((Test_env *, int));
|
||||
static int test_nexpr ARGS((Test_env *, int));
|
||||
static int test_primary ARGS((Test_env *, int));
|
||||
static int ptest_isa ARGS((Test_env *, Test_meta));
|
||||
static const char *ptest_getopnd ARGS((Test_env *, Test_op, int));
|
||||
static int ptest_eval ARGS((Test_env *, Test_op, const char *,
|
||||
const char *, int));
|
||||
static void ptest_error ARGS((Test_env *, int, const char *));
|
||||
|
||||
int
|
||||
c_test(wp)
|
||||
char **wp;
|
||||
{
|
||||
int argc;
|
||||
int res;
|
||||
Test_env te;
|
||||
|
||||
te.flags = 0;
|
||||
te.isa = ptest_isa;
|
||||
te.getopnd = ptest_getopnd;
|
||||
te.eval = ptest_eval;
|
||||
te.error = ptest_error;
|
||||
|
||||
for (argc = 0; wp[argc]; argc++)
|
||||
;
|
||||
|
||||
if (strcmp(wp[0], "[") == 0) {
|
||||
if (strcmp(wp[--argc], "]") != 0) {
|
||||
bi_errorf("missing ]");
|
||||
return T_ERR_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
te.pos.wp = wp + 1;
|
||||
te.wp_end = wp + argc;
|
||||
|
||||
/*
|
||||
* Handle the special cases from POSIX.2, section 4.62.4.
|
||||
* Implementation of all the rules isn't necessary since
|
||||
* our parser does the right thing for the omitted steps.
|
||||
*/
|
||||
if (argc <= 5) {
|
||||
char **owp = wp;
|
||||
int invert = 0;
|
||||
Test_op op;
|
||||
const char *opnd1, *opnd2;
|
||||
|
||||
while (--argc >= 0) {
|
||||
if ((*te.isa)(&te, TM_END))
|
||||
return !0;
|
||||
if (argc == 3) {
|
||||
opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
|
||||
if ((op = (Test_op) (*te.isa)(&te, TM_BINOP))) {
|
||||
opnd2 = (*te.getopnd)(&te, op, 1);
|
||||
res = (*te.eval)(&te, op, opnd1, opnd2,
|
||||
1);
|
||||
if (te.flags & TEF_ERROR)
|
||||
return T_ERR_EXIT;
|
||||
if (invert & 1)
|
||||
res = !res;
|
||||
return !res;
|
||||
}
|
||||
/* back up to opnd1 */
|
||||
te.pos.wp--;
|
||||
}
|
||||
if (argc == 1) {
|
||||
opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
|
||||
/* Historically, -t by itself test if fd 1
|
||||
* is a file descriptor, but POSIX says its
|
||||
* a string test...
|
||||
*/
|
||||
if (!Flag(FPOSIX) && strcmp(opnd1, "-t") == 0)
|
||||
break;
|
||||
res = (*te.eval)(&te, TO_STNZE, opnd1,
|
||||
(char *) 0, 1);
|
||||
if (invert & 1)
|
||||
res = !res;
|
||||
return !res;
|
||||
}
|
||||
if ((*te.isa)(&te, TM_NOT)) {
|
||||
invert++;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
te.pos.wp = owp + 1;
|
||||
}
|
||||
|
||||
return test_parse(&te);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic test routines.
|
||||
*/
|
||||
|
||||
Test_op
|
||||
test_isop(te, meta, s)
|
||||
Test_env *te;
|
||||
Test_meta meta;
|
||||
const char *s;
|
||||
{
|
||||
char sc1;
|
||||
const struct t_op *otab;
|
||||
|
||||
otab = meta == TM_UNOP ? u_ops : b_ops;
|
||||
if (*s) {
|
||||
sc1 = s[1];
|
||||
for (; otab->op_text[0]; otab++)
|
||||
if (sc1 == otab->op_text[1]
|
||||
&& strcmp(s, otab->op_text) == 0
|
||||
&& ((te->flags & TEF_DBRACKET)
|
||||
|| (otab->op_num != TO_STLT
|
||||
&& otab->op_num != TO_STGT)))
|
||||
return otab->op_num;
|
||||
}
|
||||
return TO_NONOP;
|
||||
}
|
||||
|
||||
int
|
||||
test_eval(te, op, opnd1, opnd2, do_eval)
|
||||
Test_env *te;
|
||||
Test_op op;
|
||||
const char *opnd1;
|
||||
const char *opnd2;
|
||||
int do_eval;
|
||||
{
|
||||
int res;
|
||||
int not;
|
||||
struct stat b1, b2;
|
||||
|
||||
if (!do_eval)
|
||||
return 0;
|
||||
|
||||
switch ((int) op) {
|
||||
/*
|
||||
* Unary Operators
|
||||
*/
|
||||
case TO_STNZE: /* -n */
|
||||
return *opnd1 != '\0';
|
||||
case TO_STZER: /* -z */
|
||||
return *opnd1 == '\0';
|
||||
case TO_OPTION: /* -o */
|
||||
if ((not = *opnd1 == '!'))
|
||||
opnd1++;
|
||||
if ((res = option(opnd1)) < 0)
|
||||
res = 0;
|
||||
else {
|
||||
res = Flag(res);
|
||||
if (not)
|
||||
res = !res;
|
||||
}
|
||||
return res;
|
||||
case TO_FILRD: /* -r */
|
||||
return test_eaccess(opnd1, R_OK) == 0;
|
||||
case TO_FILWR: /* -w */
|
||||
return test_eaccess(opnd1, W_OK) == 0;
|
||||
case TO_FILEX: /* -x */
|
||||
return test_eaccess(opnd1, X_OK) == 0;
|
||||
case TO_FILAXST: /* -a */
|
||||
return test_stat(opnd1, &b1) == 0;
|
||||
case TO_FILEXST: /* -e */
|
||||
/* at&t ksh does not appear to do the /dev/fd/ thing for
|
||||
* this (unless the os itself handles it)
|
||||
*/
|
||||
return stat(opnd1, &b1) == 0;
|
||||
case TO_FILREG: /* -r */
|
||||
return test_stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode);
|
||||
case TO_FILID: /* -d */
|
||||
return test_stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode);
|
||||
case TO_FILCDEV: /* -c */
|
||||
#ifdef S_ISCHR
|
||||
return test_stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TO_FILBDEV: /* -b */
|
||||
#ifdef S_ISBLK
|
||||
return test_stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TO_FILFIFO: /* -p */
|
||||
#ifdef S_ISFIFO
|
||||
return test_stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TO_FILSYM: /* -h -L */
|
||||
#ifdef S_ISLNK
|
||||
return lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TO_FILSOCK: /* -S */
|
||||
#ifdef S_ISSOCK
|
||||
return test_stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TO_FILCDF:/* -H HP context dependent files (directories) */
|
||||
#ifdef S_ISCDF
|
||||
{
|
||||
/* Append a + to filename and check to see if result is a
|
||||
* setuid directory. CDF stuff in general is hookey, since
|
||||
* it breaks for the following sequence: echo hi > foo+;
|
||||
* mkdir foo; echo bye > foo/default; chmod u+s foo
|
||||
* (foo+ refers to the file with hi in it, there is no way
|
||||
* to get at the file with bye in it - please correct me if
|
||||
* I'm wrong about this).
|
||||
*/
|
||||
int len = strlen(opnd1);
|
||||
char *p = str_nsave(opnd1, len + 1, ATEMP);
|
||||
|
||||
p[len++] = '+';
|
||||
p[len] = '\0';
|
||||
return stat(p, &b1) == 0 && S_ISCDF(b1.st_mode);
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TO_FILSETU: /* -u */
|
||||
#ifdef S_ISUID
|
||||
return test_stat(opnd1, &b1) == 0
|
||||
&& (b1.st_mode & S_ISUID) == S_ISUID;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TO_FILSETG: /* -g */
|
||||
#ifdef S_ISGID
|
||||
return test_stat(opnd1, &b1) == 0
|
||||
&& (b1.st_mode & S_ISGID) == S_ISGID;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TO_FILSTCK: /* -k */
|
||||
return test_stat(opnd1, &b1) == 0
|
||||
&& (b1.st_mode & S_ISVTX) == S_ISVTX;
|
||||
case TO_FILGZ: /* -s */
|
||||
return test_stat(opnd1, &b1) == 0 && b1.st_size > 0L;
|
||||
case TO_FILTT: /* -t */
|
||||
if (opnd1 && !bi_getn(opnd1, &res)) {
|
||||
te->flags |= TEF_ERROR;
|
||||
res = 0;
|
||||
} else {
|
||||
/* generate error if in FPOSIX mode? */
|
||||
res = isatty(opnd1 ? res : 0);
|
||||
}
|
||||
return res;
|
||||
case TO_FILUID: /* -O */
|
||||
return test_stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid;
|
||||
case TO_FILGID: /* -G */
|
||||
return test_stat(opnd1, &b1) == 0 && b1.st_gid == getegid();
|
||||
/*
|
||||
* Binary Operators
|
||||
*/
|
||||
case TO_STEQL: /* = */
|
||||
if (te->flags & TEF_DBRACKET)
|
||||
return gmatch(opnd1, opnd2, FALSE);
|
||||
return strcmp(opnd1, opnd2) == 0;
|
||||
case TO_STNEQ: /* != */
|
||||
if (te->flags & TEF_DBRACKET)
|
||||
return !gmatch(opnd1, opnd2, FALSE);
|
||||
return strcmp(opnd1, opnd2) != 0;
|
||||
case TO_STLT: /* < */
|
||||
return strcmp(opnd1, opnd2) < 0;
|
||||
case TO_STGT: /* > */
|
||||
return strcmp(opnd1, opnd2) > 0;
|
||||
case TO_INTEQ: /* -eq */
|
||||
case TO_INTNE: /* -ne */
|
||||
case TO_INTGE: /* -ge */
|
||||
case TO_INTGT: /* -gt */
|
||||
case TO_INTLE: /* -le */
|
||||
case TO_INTLT: /* -lt */
|
||||
{
|
||||
long v1, v2;
|
||||
|
||||
if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR)
|
||||
|| !evaluate(opnd2, &v2, KSH_RETURN_ERROR))
|
||||
{
|
||||
/* error already printed.. */
|
||||
te->flags |= TEF_ERROR;
|
||||
return 1;
|
||||
}
|
||||
switch ((int) op) {
|
||||
case TO_INTEQ:
|
||||
return v1 == v2;
|
||||
case TO_INTNE:
|
||||
return v1 != v2;
|
||||
case TO_INTGE:
|
||||
return v1 >= v2;
|
||||
case TO_INTGT:
|
||||
return v1 > v2;
|
||||
case TO_INTLE:
|
||||
return v1 <= v2;
|
||||
case TO_INTLT:
|
||||
return v1 < v2;
|
||||
}
|
||||
}
|
||||
case TO_FILNT: /* -nt */
|
||||
{
|
||||
int s2;
|
||||
/* ksh88/ksh93 succeed if file2 can't be stated
|
||||
* (subtly different from `does not exist').
|
||||
*/
|
||||
return stat(opnd1, &b1) == 0
|
||||
&& (((s2 = stat(opnd2, &b2)) == 0
|
||||
&& b1.st_mtime > b2.st_mtime) || s2 < 0);
|
||||
}
|
||||
case TO_FILOT: /* -ot */
|
||||
{
|
||||
int s1;
|
||||
/* ksh88/ksh93 succeed if file1 can't be stated
|
||||
* (subtly different from `does not exist').
|
||||
*/
|
||||
return stat(opnd2, &b2) == 0
|
||||
&& (((s1 = stat(opnd1, &b1)) == 0
|
||||
&& b1.st_mtime < b2.st_mtime) || s1 < 0);
|
||||
}
|
||||
case TO_FILEQ: /* -ef */
|
||||
return stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0
|
||||
&& b1.st_dev == b2.st_dev
|
||||
&& b1.st_ino == b2.st_ino;
|
||||
}
|
||||
(*te->error)(te, 0, "internal error: unknown op");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Nasty kludge to handle Korn's bizarre /dev/fd hack */
|
||||
static int
|
||||
test_stat(pathx, statb)
|
||||
const char *pathx;
|
||||
struct stat *statb;
|
||||
{
|
||||
#if !defined(HAVE_DEV_FD)
|
||||
int fd;
|
||||
|
||||
if (strncmp(pathx, "/dev/fd/", 8) == 0 && getn(pathx + 8, &fd))
|
||||
return fstat(fd, statb);
|
||||
#endif /* !HAVE_DEV_FD */
|
||||
|
||||
return stat(pathx, statb);
|
||||
}
|
||||
|
||||
/* Routine to handle Korn's /dev/fd hack, and to deal with X_OK on
|
||||
* non-directories when running as root.
|
||||
*/
|
||||
static int
|
||||
test_eaccess(pathx, mode)
|
||||
const char *pathx;
|
||||
int mode;
|
||||
{
|
||||
int res;
|
||||
|
||||
#if !defined(HAVE_DEV_FD)
|
||||
int fd;
|
||||
|
||||
/* Note: doesn't handle //dev/fd, etc.. (this is ok) */
|
||||
if (strncmp(pathx, "/dev/fd/", 8) == 0 && getn(pathx + 8, &fd)) {
|
||||
int flags;
|
||||
|
||||
if ((flags = fcntl(fd, F_GETFL, 0)) < 0
|
||||
|| (mode & X_OK)
|
||||
|| ((mode & W_OK) && (flags & O_ACCMODE) == O_RDONLY)
|
||||
|| ((mode & R_OK) && (flags & O_ACCMODE) == O_WRONLY))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#endif /* !HAVE_DEV_FD */
|
||||
|
||||
res = eaccess(pathx, mode);
|
||||
/*
|
||||
* On most (all?) unixes, access() says everything is executable for
|
||||
* root - avoid this on files by using stat().
|
||||
*/
|
||||
if (res == 0 && ksheuid == 0 && (mode & X_OK)) {
|
||||
struct stat statb;
|
||||
|
||||
if (stat(pathx, &statb) < 0)
|
||||
res = -1;
|
||||
else if (S_ISDIR(statb.st_mode))
|
||||
res = 0;
|
||||
else
|
||||
res = (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
|
||||
? 0 : -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
test_parse(te)
|
||||
Test_env *te;
|
||||
{
|
||||
int res;
|
||||
|
||||
res = test_oexpr(te, 1);
|
||||
|
||||
if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END))
|
||||
(*te->error)(te, 0, "unexpected operator/operand");
|
||||
|
||||
return (te->flags & TEF_ERROR) ? T_ERR_EXIT : !res;
|
||||
}
|
||||
|
||||
static int
|
||||
test_oexpr(te, do_eval)
|
||||
Test_env *te;
|
||||
int do_eval;
|
||||
{
|
||||
int res;
|
||||
|
||||
res = test_aexpr(te, do_eval);
|
||||
if (res)
|
||||
do_eval = 0;
|
||||
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR))
|
||||
return test_oexpr(te, do_eval) || res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
test_aexpr(te, do_eval)
|
||||
Test_env *te;
|
||||
int do_eval;
|
||||
{
|
||||
int res;
|
||||
|
||||
res = test_nexpr(te, do_eval);
|
||||
if (!res)
|
||||
do_eval = 0;
|
||||
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND))
|
||||
return test_aexpr(te, do_eval) && res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
test_nexpr(te, do_eval)
|
||||
Test_env *te;
|
||||
int do_eval;
|
||||
{
|
||||
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT))
|
||||
return !test_nexpr(te, do_eval);
|
||||
return test_primary(te, do_eval);
|
||||
}
|
||||
|
||||
static int
|
||||
test_primary(te, do_eval)
|
||||
Test_env *te;
|
||||
int do_eval;
|
||||
{
|
||||
const char *opnd1, *opnd2;
|
||||
int res;
|
||||
Test_op op;
|
||||
|
||||
if (te->flags & TEF_ERROR)
|
||||
return 0;
|
||||
if ((*te->isa)(te, TM_OPAREN)) {
|
||||
res = test_oexpr(te, do_eval);
|
||||
if (te->flags & TEF_ERROR)
|
||||
return 0;
|
||||
if (!(*te->isa)(te, TM_CPAREN)) {
|
||||
(*te->error)(te, 0, "missing closing paren");
|
||||
return 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if ((op = (Test_op) (*te->isa)(te, TM_UNOP))) {
|
||||
/* unary expression */
|
||||
opnd1 = (*te->getopnd)(te, op, do_eval);
|
||||
if (!opnd1) {
|
||||
(*te->error)(te, -1, "missing argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (*te->eval)(te, op, opnd1, (const char *) 0, do_eval);
|
||||
}
|
||||
opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval);
|
||||
if (!opnd1) {
|
||||
(*te->error)(te, 0, "expression expected");
|
||||
return 0;
|
||||
}
|
||||
if ((op = (Test_op) (*te->isa)(te, TM_BINOP))) {
|
||||
/* binary expression */
|
||||
opnd2 = (*te->getopnd)(te, op, do_eval);
|
||||
if (!opnd2) {
|
||||
(*te->error)(te, -1, "missing second argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (*te->eval)(te, op, opnd1, opnd2, do_eval);
|
||||
}
|
||||
if (te->flags & TEF_DBRACKET) {
|
||||
(*te->error)(te, -1, "missing expression operator");
|
||||
return 0;
|
||||
}
|
||||
return (*te->eval)(te, TO_STNZE, opnd1, (const char *) 0, do_eval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Plain test (test and [ .. ]) specific routines.
|
||||
*/
|
||||
|
||||
/* Test if the current token is a whatever. Accepts the current token if
|
||||
* it is. Returns 0 if it is not, non-zero if it is (in the case of
|
||||
* TM_UNOP and TM_BINOP, the returned value is a Test_op).
|
||||
*/
|
||||
static int
|
||||
ptest_isa(te, meta)
|
||||
Test_env *te;
|
||||
Test_meta meta;
|
||||
{
|
||||
/* Order important - indexed by Test_meta values */
|
||||
static const char *const tokens[] = {
|
||||
"-o", "-a", "!", "(", ")"
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (te->pos.wp >= te->wp_end)
|
||||
return meta == TM_END;
|
||||
|
||||
if (meta == TM_UNOP || meta == TM_BINOP)
|
||||
ret = (int) test_isop(te, meta, *te->pos.wp);
|
||||
else if (meta == TM_END)
|
||||
ret = 0;
|
||||
else
|
||||
ret = strcmp(*te->pos.wp, tokens[(int) meta]) == 0;
|
||||
|
||||
/* Accept the token? */
|
||||
if (ret)
|
||||
te->pos.wp++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *
|
||||
ptest_getopnd(te, op, do_eval)
|
||||
Test_env *te;
|
||||
Test_op op;
|
||||
int do_eval;
|
||||
{
|
||||
if (te->pos.wp >= te->wp_end)
|
||||
return op == TO_FILTT ? "1" : (const char *) 0;
|
||||
return *te->pos.wp++;
|
||||
}
|
||||
|
||||
static int
|
||||
ptest_eval(te, op, opnd1, opnd2, do_eval)
|
||||
Test_env *te;
|
||||
Test_op op;
|
||||
const char *opnd1;
|
||||
const char *opnd2;
|
||||
int do_eval;
|
||||
{
|
||||
return test_eval(te, op, opnd1, opnd2, do_eval);
|
||||
}
|
||||
|
||||
static void
|
||||
ptest_error(te, offset, msg)
|
||||
Test_env *te;
|
||||
int offset;
|
||||
const char *msg;
|
||||
{
|
||||
const char *op = te->pos.wp + offset >= te->wp_end ?
|
||||
(const char *) 0 : te->pos.wp[offset];
|
||||
|
||||
te->flags |= TEF_ERROR;
|
||||
if (op)
|
||||
bi_errorf("%s: %s", op, msg);
|
||||
else
|
||||
bi_errorf("%s", msg);
|
||||
}
|
55
bin/ksh/c_test.h
Normal file
55
bin/ksh/c_test.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* $NetBSD: c_test.h,v 1.3 2004/07/07 19:20:09 mycroft Exp $ */
|
||||
|
||||
/* Various types of operations. Keeping things grouped nicely
|
||||
* (unary,binary) makes switch() statements more efficient.
|
||||
*/
|
||||
enum Test_op {
|
||||
TO_NONOP = 0, /* non-operator */
|
||||
/* unary operators */
|
||||
TO_STNZE, TO_STZER, TO_OPTION,
|
||||
TO_FILAXST,
|
||||
TO_FILEXST,
|
||||
TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK,
|
||||
TO_FILCDF, TO_FILID, TO_FILGID, TO_FILSETG, TO_FILSTCK, TO_FILUID,
|
||||
TO_FILRD, TO_FILGZ, TO_FILTT, TO_FILSETU, TO_FILWR, TO_FILEX,
|
||||
/* binary operators */
|
||||
TO_STEQL, TO_STNEQ, TO_STLT, TO_STGT, TO_INTEQ, TO_INTNE, TO_INTGT,
|
||||
TO_INTGE, TO_INTLT, TO_INTLE, TO_FILEQ, TO_FILNT, TO_FILOT
|
||||
};
|
||||
typedef enum Test_op Test_op;
|
||||
|
||||
/* Used by Test_env.isa() (order important - used to index *_tokens[] arrays) */
|
||||
enum Test_meta {
|
||||
TM_OR, /* -o or || */
|
||||
TM_AND, /* -a or && */
|
||||
TM_NOT, /* ! */
|
||||
TM_OPAREN, /* ( */
|
||||
TM_CPAREN, /* ) */
|
||||
TM_UNOP, /* unary operator */
|
||||
TM_BINOP, /* binary operator */
|
||||
TM_END /* end of input */
|
||||
};
|
||||
typedef enum Test_meta Test_meta;
|
||||
|
||||
#define TEF_ERROR BIT(0) /* set if we've hit an error */
|
||||
#define TEF_DBRACKET BIT(1) /* set if [[ .. ]] test */
|
||||
|
||||
typedef struct test_env Test_env;
|
||||
struct test_env {
|
||||
int flags; /* TEF_* */
|
||||
union {
|
||||
char **wp; /* used by ptest_* */
|
||||
XPtrV *av; /* used by dbtestp_* */
|
||||
} pos;
|
||||
char **wp_end; /* used by ptest_* */
|
||||
int (*isa) ARGS((Test_env *te, Test_meta meta));
|
||||
const char *(*getopnd) ARGS((Test_env *te, Test_op op, int do_eval));
|
||||
int (*eval) ARGS((Test_env *te, Test_op op, const char *opnd1,
|
||||
const char *opnd2, int do_eval));
|
||||
void (*error) ARGS((Test_env *te, int offset, const char *msg));
|
||||
};
|
||||
|
||||
Test_op test_isop ARGS((Test_env *te, Test_meta meta, const char *s));
|
||||
int test_eval ARGS((Test_env *te, Test_op op, const char *opnd1,
|
||||
const char *opnd2, int do_eval));
|
||||
int test_parse ARGS((Test_env *te));
|
285
bin/ksh/c_ulimit.c
Normal file
285
bin/ksh/c_ulimit.c
Normal file
|
@ -0,0 +1,285 @@
|
|||
/* $NetBSD: c_ulimit.c,v 1.10 2012/06/09 02:51:50 christos Exp $ */
|
||||
|
||||
/*
|
||||
ulimit -- handle "ulimit" builtin
|
||||
|
||||
Reworked to use getrusage() and ulimit() at once (as needed on
|
||||
some schizophrenic systems, eg, HP-UX 9.01), made argument parsing
|
||||
conform to at&t ksh, added autoconf support. Michael Rendell, May, '94
|
||||
|
||||
Eric Gisin, September 1988
|
||||
Adapted to PD KornShell. Removed AT&T code.
|
||||
|
||||
last edit: 06-Jun-1987 D A Gwyn
|
||||
|
||||
This started out as the BRL UNIX System V system call emulation
|
||||
for 4.nBSD, and was later extended by Doug Kingston to handle
|
||||
the extended 4.nBSD resource limits. It now includes the code
|
||||
that was originally under case SYSULIMIT in source file "xec.c".
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: c_ulimit.c,v 1.10 2012/06/09 02:51:50 christos Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
#include "ksh_time.h"
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
# include <sys/resource.h>
|
||||
#endif /* HAVE_SYS_RESOURCE_H */
|
||||
#ifdef HAVE_ULIMIT_H
|
||||
# include <ulimit.h>
|
||||
#else /* HAVE_ULIMIT_H */
|
||||
# ifdef HAVE_ULIMIT
|
||||
extern long ulimit();
|
||||
# endif /* HAVE_ULIMIT */
|
||||
#endif /* HAVE_ULIMIT_H */
|
||||
|
||||
#define SOFT 0x1
|
||||
#define HARD 0x2
|
||||
|
||||
#ifdef RLIM_INFINITY
|
||||
# define KSH_RLIM_INFINITY RLIM_INFINITY
|
||||
#else
|
||||
# define KSH_RLIM_INFINITY ((rlim_t) 1 << (sizeof(rlim_t) * 8 - 1) - 1)
|
||||
#endif /* RLIM_INFINITY */
|
||||
|
||||
int
|
||||
c_ulimit(wp)
|
||||
char **wp;
|
||||
{
|
||||
static const struct limits {
|
||||
const char *name;
|
||||
enum { RLIMIT, ULIMIT } which;
|
||||
int gcmd; /* get command */
|
||||
int scmd; /* set command (or -1, if no set command) */
|
||||
int factor; /* multiply by to get rlim_{cur,max} values */
|
||||
char option;
|
||||
} limits[] = {
|
||||
/* Do not use options -H, -S or -a */
|
||||
#ifdef RLIMIT_CPU
|
||||
{ "time(cpu-seconds)", RLIMIT, RLIMIT_CPU, RLIMIT_CPU, 1, 't' },
|
||||
#endif
|
||||
#ifdef RLIMIT_FSIZE
|
||||
{ "file(blocks)", RLIMIT, RLIMIT_FSIZE, RLIMIT_FSIZE, 512, 'f' },
|
||||
#else /* RLIMIT_FSIZE */
|
||||
# ifdef UL_GETFSIZE /* x/open */
|
||||
{ "file(blocks)", ULIMIT, UL_GETFSIZE, UL_SETFSIZE, 1, 'f' },
|
||||
# else /* UL_GETFSIZE */
|
||||
# ifdef UL_GFILLIM /* svr4/xenix */
|
||||
{ "file(blocks)", ULIMIT, UL_GFILLIM, UL_SFILLIM, 1, 'f' },
|
||||
# else /* UL_GFILLIM */
|
||||
{ "file(blocks)", ULIMIT, 1, 2, 1, 'f' },
|
||||
# endif /* UL_GFILLIM */
|
||||
# endif /* UL_GETFSIZE */
|
||||
#endif /* RLIMIT_FSIZE */
|
||||
#ifdef RLIMIT_CORE
|
||||
{ "coredump(blocks)", RLIMIT, RLIMIT_CORE, RLIMIT_CORE, 512, 'c' },
|
||||
#endif
|
||||
#ifdef RLIMIT_DATA
|
||||
{ "data(kbytes)", RLIMIT, RLIMIT_DATA, RLIMIT_DATA, 1024, 'd' },
|
||||
#endif
|
||||
#ifdef RLIMIT_STACK
|
||||
{ "stack(kbytes)", RLIMIT, RLIMIT_STACK, RLIMIT_STACK, 1024, 's' },
|
||||
#endif
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
{ "lockedmem(kbytes)", RLIMIT, RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, 1024, 'l' },
|
||||
#endif
|
||||
#ifdef RLIMIT_RSS
|
||||
{ "memory(kbytes)", RLIMIT, RLIMIT_RSS, RLIMIT_RSS, 1024, 'm' },
|
||||
#endif
|
||||
#ifdef RLIMIT_NOFILE
|
||||
{ "nofiles(descriptors)", RLIMIT, RLIMIT_NOFILE, RLIMIT_NOFILE, 1, 'n' },
|
||||
#else /* RLIMIT_NOFILE */
|
||||
# ifdef UL_GDESLIM /* svr4/xenix */
|
||||
{ "nofiles(descriptors)", ULIMIT, UL_GDESLIM, -1, 1, 'n' },
|
||||
# endif /* UL_GDESLIM */
|
||||
#endif /* RLIMIT_NOFILE */
|
||||
#ifdef RLIMIT_NPROC
|
||||
{ "processes", RLIMIT, RLIMIT_NPROC, RLIMIT_NPROC, 1, 'p' },
|
||||
#endif
|
||||
#ifdef RLIMIT_NTHR
|
||||
{ "threads", RLIMIT, RLIMIT_NTHR, RLIMIT_NTHR, 1, 'r' },
|
||||
#endif
|
||||
#ifdef RLIMIT_VMEM
|
||||
{ "vmemory(kbytes)", RLIMIT, RLIMIT_VMEM, RLIMIT_VMEM, 1024, 'v' },
|
||||
#else /* RLIMIT_VMEM */
|
||||
/* These are not quite right - really should subtract etext or something */
|
||||
# ifdef UL_GMEMLIM /* svr4/xenix */
|
||||
{ "vmemory(maxaddr)", ULIMIT, UL_GMEMLIM, -1, 1, 'v' },
|
||||
# else /* UL_GMEMLIM */
|
||||
# ifdef UL_GETBREAK /* osf/1 */
|
||||
{ "vmemory(maxaddr)", ULIMIT, UL_GETBREAK, -1, 1, 'v' },
|
||||
# else /* UL_GETBREAK */
|
||||
# ifdef UL_GETMAXBRK /* hpux */
|
||||
{ "vmemory(maxaddr)", ULIMIT, UL_GETMAXBRK, -1, 1, 'v' },
|
||||
# endif /* UL_GETMAXBRK */
|
||||
# endif /* UL_GETBREAK */
|
||||
# endif /* UL_GMEMLIM */
|
||||
#endif /* RLIMIT_VMEM */
|
||||
#ifdef RLIMIT_SWAP
|
||||
{ "swap(kbytes)", RLIMIT, RLIMIT_SWAP, RLIMIT_SWAP, 1024, 'w' },
|
||||
#endif
|
||||
#ifdef RLIMIT_SBSIZE
|
||||
{ "sbsize(bytes)", RLIMIT, RLIMIT_SBSIZE, RLIMIT_SBSIZE, 1, 'b' },
|
||||
#endif
|
||||
{ .name = NULL }
|
||||
};
|
||||
static char options[3 + NELEM(limits)];
|
||||
rlim_t UNINITIALIZED(val);
|
||||
int how = SOFT | HARD;
|
||||
const struct limits *l;
|
||||
int set, all = 0;
|
||||
int optc, what;
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
struct rlimit limit;
|
||||
#endif /* HAVE_SETRLIMIT */
|
||||
|
||||
if (!options[0]) {
|
||||
/* build options string on first call - yuck */
|
||||
char *p = options;
|
||||
|
||||
*p++ = 'H'; *p++ = 'S'; *p++ = 'a';
|
||||
for (l = limits; l->name; l++)
|
||||
*p++ = l->option;
|
||||
*p = '\0';
|
||||
}
|
||||
what = 'f';
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF)
|
||||
switch (optc) {
|
||||
case 'H':
|
||||
how = HARD;
|
||||
break;
|
||||
case 'S':
|
||||
how = SOFT;
|
||||
break;
|
||||
case 'a':
|
||||
all = 1;
|
||||
break;
|
||||
case '?':
|
||||
return 1;
|
||||
default:
|
||||
what = optc;
|
||||
}
|
||||
|
||||
for (l = limits; l->name && l->option != what; l++)
|
||||
;
|
||||
if (!l->name) {
|
||||
internal_errorf(0, "ulimit: %c", what);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wp += builtin_opt.optind;
|
||||
set = *wp ? 1 : 0;
|
||||
if (set) {
|
||||
if (all || wp[1]) {
|
||||
bi_errorf("too many arguments");
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(wp[0], "unlimited") == 0)
|
||||
val = KSH_RLIM_INFINITY;
|
||||
else {
|
||||
long rval;
|
||||
|
||||
if (!evaluate(wp[0], &rval, KSH_RETURN_ERROR))
|
||||
return 1;
|
||||
/* Avoid problems caused by typos that
|
||||
* evaluate misses due to evaluating unset
|
||||
* parameters to 0...
|
||||
* If this causes problems, will have to
|
||||
* add parameter to evaluate() to control
|
||||
* if unset params are 0 or an error.
|
||||
*/
|
||||
if (!rval && !digit(wp[0][0])) {
|
||||
bi_errorf("invalid limit: %s", wp[0]);
|
||||
return 1;
|
||||
}
|
||||
val = (u_long)rval * l->factor;
|
||||
}
|
||||
}
|
||||
if (all) {
|
||||
for (l = limits; l->name; l++) {
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
if (l->which == RLIMIT) {
|
||||
getrlimit(l->gcmd, &limit);
|
||||
if (how & SOFT)
|
||||
val = limit.rlim_cur;
|
||||
else if (how & HARD)
|
||||
val = limit.rlim_max;
|
||||
} else
|
||||
#endif /* HAVE_SETRLIMIT */
|
||||
#ifdef HAVE_ULIMIT
|
||||
{
|
||||
val = ulimit(l->gcmd, (rlim_t) 0);
|
||||
}
|
||||
#else /* HAVE_ULIMIT */
|
||||
;
|
||||
#endif /* HAVE_ULIMIT */
|
||||
shprintf("%-20s ", l->name);
|
||||
#ifdef RLIM_INFINITY
|
||||
if (val == RLIM_INFINITY)
|
||||
shprintf("unlimited\n");
|
||||
else
|
||||
#endif /* RLIM_INFINITY */
|
||||
{
|
||||
val /= l->factor;
|
||||
shprintf("%ld\n", (long) val);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
if (l->which == RLIMIT) {
|
||||
getrlimit(l->gcmd, &limit);
|
||||
if (set) {
|
||||
if (how & SOFT)
|
||||
limit.rlim_cur = val;
|
||||
if (how & HARD)
|
||||
limit.rlim_max = val;
|
||||
if (setrlimit(l->scmd, &limit) < 0) {
|
||||
if (errno == EPERM)
|
||||
bi_errorf("exceeds allowable limit");
|
||||
else
|
||||
bi_errorf("bad limit: %s",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (how & SOFT)
|
||||
val = limit.rlim_cur;
|
||||
else if (how & HARD)
|
||||
val = limit.rlim_max;
|
||||
}
|
||||
} else
|
||||
#endif /* HAVE_SETRLIMIT */
|
||||
#ifdef HAVE_ULIMIT
|
||||
{
|
||||
if (set) {
|
||||
if (l->scmd == -1) {
|
||||
bi_errorf("can't change limit");
|
||||
return 1;
|
||||
} else if (ulimit(l->scmd, val) < 0) {
|
||||
bi_errorf("bad limit: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
val = ulimit(l->gcmd, (rlim_t) 0);
|
||||
}
|
||||
#else /* HAVE_ULIMIT */
|
||||
;
|
||||
#endif /* HAVE_ULIMIT */
|
||||
if (!set) {
|
||||
#ifdef RLIM_INFINITY
|
||||
if (val == RLIM_INFINITY)
|
||||
shprintf("unlimited\n");
|
||||
else
|
||||
#endif /* RLIM_INFINITY */
|
||||
{
|
||||
val /= l->factor;
|
||||
shprintf("%ld\n", (long) val);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
57
bin/ksh/conf-end.h
Normal file
57
bin/ksh/conf-end.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* $NetBSD: conf-end.h,v 1.2 1997/01/12 19:11:43 tls Exp $ */
|
||||
|
||||
/*
|
||||
* End of configuration stuff for PD ksh.
|
||||
*
|
||||
* RCSid: $NetBSD: conf-end.h,v 1.2 1997/01/12 19:11:43 tls Exp $
|
||||
*/
|
||||
|
||||
#if defined(EMACS) || defined(VI)
|
||||
# define EDIT
|
||||
#else
|
||||
# undef EDIT
|
||||
#endif
|
||||
|
||||
/* Editing implies history */
|
||||
#if defined(EDIT) && !defined(HISTORY)
|
||||
# define HISTORY
|
||||
#endif /* EDIT */
|
||||
|
||||
/*
|
||||
* if you don't have mmap() you can't use Peter Collinson's history
|
||||
* mechanism. If that is the case, then define EASY_HISTORY
|
||||
*/
|
||||
#if defined(HISTORY) && (!defined(COMPLEX_HISTORY) || !defined(HAVE_MMAP) || !defined(HAVE_FLOCK))
|
||||
# undef COMPLEX_HISTORY
|
||||
# define EASY_HISTORY /* sjg's trivial history file */
|
||||
#endif
|
||||
|
||||
/* Can we safely catch sigchld and wait for processes? */
|
||||
#if (defined(HAVE_WAITPID) || defined(HAVE_WAIT3)) \
|
||||
&& (defined(POSIX_SIGNALS) || defined(BSD42_SIGNALS))
|
||||
# define JOB_SIGS
|
||||
#endif
|
||||
|
||||
#if !defined(JOB_SIGS) || !(defined(POSIX_PGRP) || defined(BSD_PGRP))
|
||||
# undef JOBS /* if no JOB_SIGS, no job control support */
|
||||
#endif
|
||||
|
||||
/* pdksh assumes system calls return EINTR if a signal happened (this so
|
||||
* the signal handler doesn't have to longjmp()). I don't know if this
|
||||
* happens (or can be made to happen) with sigset() et. al. (the bsd41 signal
|
||||
* routines), so, the autoconf stuff checks what they do and defines
|
||||
* SIGNALS_DONT_INTERRUPT if signals don't interrupt read().
|
||||
* If SIGNALS_DONT_INTERRUPT isn't defined and your compiler chokes on this,
|
||||
* delete the hash in front of the error (and file a bug report).
|
||||
*/
|
||||
#ifdef SIGNALS_DONT_INTERRUPT
|
||||
# error pdksh needs interruptable system calls.
|
||||
#endif /* SIGNALS_DONT_INTERRUPT */
|
||||
|
||||
#ifdef HAVE_GCC_FUNC_ATTR
|
||||
# define GCC_FUNC_ATTR(x) __attribute__((x))
|
||||
# define GCC_FUNC_ATTR2(x,y) __attribute__((x,y))
|
||||
#else
|
||||
# define GCC_FUNC_ATTR(x)
|
||||
# define GCC_FUNC_ATTR2(x,y)
|
||||
#endif /* HAVE_GCC_FUNC_ATTR */
|
366
bin/ksh/config.h
Normal file
366
bin/ksh/config.h
Normal file
|
@ -0,0 +1,366 @@
|
|||
/* config.h. Generated automatically by configure. */
|
||||
/* config.h.in. Generated automatically from configure.in by autoheader. */
|
||||
/*
|
||||
* This file, acconfig.h, which is a part of pdksh (the public domain ksh),
|
||||
* is placed in the public domain. It comes with no licence, warranty
|
||||
* or guarantee of any kind (i.e., at your own risk).
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
|
||||
/* Define if on AIX 3.
|
||||
System headers sometimes define this.
|
||||
We just want to avoid a redefinition error message. */
|
||||
#ifndef _ALL_SOURCE
|
||||
/* #undef _ALL_SOURCE */
|
||||
#endif
|
||||
|
||||
/* Define if the closedir function returns void instead of int. */
|
||||
/* #undef CLOSEDIR_VOID */
|
||||
|
||||
/* Define to empty if the keyword does not work. */
|
||||
/* #undef const */
|
||||
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
/* #undef gid_t */
|
||||
|
||||
/* Define if you have a working `mmap' system call. */
|
||||
/* #undef HAVE_MMAP */
|
||||
|
||||
/* Define if your struct stat has st_rdev. */
|
||||
#define HAVE_ST_RDEV 1
|
||||
|
||||
/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
|
||||
#define HAVE_SYS_WAIT_H 1
|
||||
|
||||
/* Define if you have <unistd.h>. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define if on MINIX. */
|
||||
#define _MINIX 1
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
/* #undef mode_t */
|
||||
|
||||
/* Define to `long' if <sys/types.h> doesn't define. */
|
||||
/* #undef off_t */
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
/* #undef pid_t */
|
||||
|
||||
/* Define if the system does not provide POSIX.1 features except
|
||||
with this defined. */
|
||||
#define _POSIX_1_SOURCE 2
|
||||
|
||||
/* Define if you need to in order for stat and other things to work. */
|
||||
#define _POSIX_SOURCE 1
|
||||
|
||||
/* Define as the return type of signal handlers (int or void). */
|
||||
#define RETSIGTYPE void
|
||||
|
||||
/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */
|
||||
/* #undef STAT_MACROS_BROKEN */
|
||||
|
||||
/* Define if `sys_siglist' is declared by <signal.h>. */
|
||||
#define SYS_SIGLIST_DECLARED 1
|
||||
|
||||
/* Define if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
/* #undef uid_t */
|
||||
|
||||
/* Define if the closedir function returns void instead of int. */
|
||||
/* #undef VOID_CLOSEDIR */
|
||||
|
||||
/* Define if your kernal doesn't handle scripts starting with #! */
|
||||
/* #undef SHARPBANG */
|
||||
|
||||
/* Define if dup2() preserves the close-on-exec flag (ultrix does this) */
|
||||
/* #undef DUP2_BROKEN */
|
||||
|
||||
/* Define as the return value of signal handlers (0 or ). */
|
||||
#define RETSIGVAL
|
||||
|
||||
/* Define if you have posix signal routines (sigaction(), et. al.) */
|
||||
#define POSIX_SIGNALS 1
|
||||
|
||||
/* Define if you have BSD4.2 signal routines (sigsetmask(), et. al.) */
|
||||
/* #undef BSD42_SIGNALS */
|
||||
|
||||
/* Define if you have BSD4.1 signal routines (sigset(), et. al.) */
|
||||
/* #undef BSD41_SIGNALS */
|
||||
|
||||
/* Define if you have v7 signal routines (signal(), signal reset on delivery) */
|
||||
/* #undef V7_SIGNALS */
|
||||
|
||||
/* Define to use the fake posix signal routines (sigact.[ch]) */
|
||||
/* #undef USE_FAKE_SIGACT */
|
||||
|
||||
/* Define if signals don't interrupt read() */
|
||||
/* #undef SIGNALS_DONT_INTERRUPT */
|
||||
|
||||
/* Define if you have bsd versions of the setpgrp() and getpgrp() routines */
|
||||
/* #undef BSD_PGRP */
|
||||
|
||||
/* Define if you have POSIX versions of the setpgid() and getpgrp() routines */
|
||||
/* #undef POSIX_PGRP */
|
||||
|
||||
/* Define if you have sysV versions of the setpgrp() and getpgrp() routines */
|
||||
/* #undef SYSV_PGRP */
|
||||
|
||||
/* Define if you don't have setpgrp(), setpgid() or getpgrp() routines */
|
||||
#define NO_PGRP 1
|
||||
|
||||
/* Define to char if your compiler doesn't like the void keyword */
|
||||
/* #undef void */
|
||||
|
||||
/* Define to nothing if compiler doesn't like the volatile keyword */
|
||||
/* #undef volatile */
|
||||
|
||||
/* Define if C compiler groks function prototypes */
|
||||
#define HAVE_PROTOTYPES 1
|
||||
|
||||
/* Define if C compiler groks __attribute__((...)) (const, noreturn, format) */
|
||||
#define HAVE_GCC_FUNC_ATTR 1
|
||||
|
||||
/* Define to 32-bit signed integer type if <sys/types.h> doesn't define */
|
||||
/* #undef clock_t */
|
||||
|
||||
/* Define to the type of struct rlimit fields if the rlim_t type is missing */
|
||||
/* #undef rlim_t */
|
||||
|
||||
/* Define if time() is declared in <time.h> */
|
||||
#define TIME_DECLARED 1
|
||||
|
||||
/* Define to `unsigned' if <signal.h> doesn't define */
|
||||
/* #undef sigset_t */
|
||||
|
||||
/* Define if sys_errlist[] and sys_nerr are in the C library */
|
||||
#define HAVE_SYS_ERRLIST 1
|
||||
|
||||
/* Define if sys_errlist[] and sys_nerr are defined in <errno.h> */
|
||||
#define SYS_ERRLIST_DECLARED 1
|
||||
|
||||
/* Define if sys_siglist[] is in the C library */
|
||||
/* #undef HAVE_SYS_SIGLIST */
|
||||
|
||||
/* Define if you have a sane <termios.h> header file */
|
||||
#define HAVE_TERMIOS_H 1
|
||||
|
||||
/* Define if you have a memset() function in your C library */
|
||||
#define HAVE_MEMSET 1
|
||||
|
||||
/* Define if you have a memmove() function in your C library */
|
||||
#define HAVE_MEMMOVE 1
|
||||
|
||||
/* Define if you have a bcopy() function in your C library */
|
||||
/* #undef HAVE_BCOPY */
|
||||
|
||||
/* Define if you have a lstat() function in your C library */
|
||||
#define HAVE_LSTAT 1
|
||||
|
||||
/* Define if you have a sane <termio.h> header file */
|
||||
/* #undef HAVE_TERMIO_H */
|
||||
|
||||
/* Define if you don't have times() or if it always returns 0 */
|
||||
/* #undef TIMES_BROKEN */
|
||||
|
||||
/* Define if opendir() will open non-directory files */
|
||||
/* #undef OPENDIR_DOES_NONDIR */
|
||||
|
||||
/* Define if the pgrp of setpgrp() can't be the pid of a zombie process */
|
||||
/* #undef NEED_PGRP_SYNC */
|
||||
|
||||
/* Define if you arg running SCO unix */
|
||||
/* #undef OS_SCO */
|
||||
|
||||
/* Define if you arg running ISC unix */
|
||||
/* #undef OS_ISC */
|
||||
|
||||
/* Define if you arg running OS2 with the EMX library */
|
||||
/* #undef OS2 */
|
||||
|
||||
/* Define if you have a POSIX.1 compatiable <sys/wait.h> */
|
||||
#define POSIX_SYS_WAIT 1
|
||||
|
||||
/* Define if your OS maps references to /dev/fd/n to file descriptor n */
|
||||
/* #undef HAVE_DEV_FD */
|
||||
|
||||
/* Define if your C library's getwd/getcwd function dumps core in unreadable
|
||||
* directories. */
|
||||
/* #undef HPUX_GETWD_BUG */
|
||||
|
||||
/* Default PATH (see comments in configure.in for more details) */
|
||||
#define DEFAULT_PATH "/bin:/usr/bin:/usr/ucb"
|
||||
|
||||
/* Include ksh features? (see comments in configure.in for more details) */
|
||||
#define KSH 1
|
||||
|
||||
/* Include emacs editing? (see comments in configure.in for more details) */
|
||||
#define EMACS 1
|
||||
|
||||
/* Include vi editing? (see comments in configure.in for more details) */
|
||||
#define VI 1
|
||||
|
||||
/* Include job control? (see comments in configure.in for more details) */
|
||||
#define JOBS 1
|
||||
|
||||
/* Include brace-expansion? (see comments in configure.in for more details) */
|
||||
#define BRACE_EXPAND 1
|
||||
|
||||
/* Include any history? (see comments in configure.in for more details) */
|
||||
#define HISTORY 1
|
||||
|
||||
/* Include complex history? (see comments in configure.in for more details) */
|
||||
/* #undef COMPLEX_HISTORY */
|
||||
|
||||
/* Strict POSIX behaviour? (see comments in configure.in for more details) */
|
||||
/* #undef POSIXLY_CORRECT */
|
||||
|
||||
/* Specify default $ENV? (see comments in configure.in for more details) */
|
||||
/* #undef DEFAULT_ENV */
|
||||
|
||||
/* Include shl(1) support? (see comments in configure.in for more details) */
|
||||
/* #undef SWTCH */
|
||||
|
||||
/* Include game-of-life? (see comments in configure.in for more details) */
|
||||
/* #undef SILLY */
|
||||
|
||||
/* The number of bytes in a int. */
|
||||
#define SIZEOF_INT 4
|
||||
|
||||
/* The number of bytes in a long. */
|
||||
#define SIZEOF_LONG 4
|
||||
|
||||
/* Define if you have the _setjmp function. */
|
||||
/* #undef HAVE__SETJMP */
|
||||
|
||||
/* Define if you have the confstr function. */
|
||||
/* #undef HAVE_CONFSTR */
|
||||
|
||||
/* Define if you have the dup2 function. */
|
||||
#define HAVE_DUP2 1
|
||||
|
||||
/* Define if you have the flock function. */
|
||||
#define HAVE_FLOCK 1
|
||||
|
||||
/* Define if you have the getcwd function. */
|
||||
#define HAVE_GETCWD 1
|
||||
|
||||
/* Define if you have the getgroups function. */
|
||||
/* #undef HAVE_GETGROUPS */
|
||||
|
||||
/* Define if you have the getpagesize function. */
|
||||
#define HAVE_GETPAGESIZE 1
|
||||
|
||||
/* Define if you have the getrusage function. */
|
||||
/* #undef HAVE_GETRUSAGE */
|
||||
|
||||
/* Define if you have the getwd function. */
|
||||
/* #undef HAVE_GETWD */
|
||||
|
||||
/* Define if you have the killpg function. */
|
||||
#define HAVE_KILLPG 1
|
||||
|
||||
/* Define if you have the nice function. */
|
||||
#define HAVE_NICE 1
|
||||
|
||||
/* Define if you have the setrlimit function. */
|
||||
/* #undef HAVE_SETRLIMIT */
|
||||
|
||||
/* Define if you have the sigsetjmp function. */
|
||||
#define HAVE_SIGSETJMP 1
|
||||
|
||||
/* Define if you have the strcasecmp function. */
|
||||
#define HAVE_STRCASECMP 1
|
||||
|
||||
/* Define if you have the strerror function. */
|
||||
#define HAVE_STRERROR 1
|
||||
|
||||
/* Define if you have the strlcat function. */
|
||||
#define HAVE_STRLCAT 1
|
||||
/* Define if you have the strlcpy function. */
|
||||
#define HAVE_STRLCPY 1
|
||||
|
||||
/* Define if you have the strstr function. */
|
||||
#define HAVE_STRSTR 1
|
||||
|
||||
/* Define if you have the sysconf function. */
|
||||
#define HAVE_SYSCONF 1
|
||||
|
||||
/* Define if you have the tcsetpgrp function. */
|
||||
#define HAVE_TCSETPGRP 1
|
||||
|
||||
/* Define if you have the ulimit function. */
|
||||
#define HAVE_ULIMIT 1
|
||||
|
||||
/* Define if you have the valloc function. */
|
||||
#define HAVE_VALLOC 1
|
||||
|
||||
/* Define if you have the wait3 function. */
|
||||
/* #undef HAVE_WAIT3 */
|
||||
|
||||
/* Define if you have the waitpid function. */
|
||||
#define HAVE_WAITPID 1
|
||||
|
||||
/* Define if you have the <dirent.h> header file. */
|
||||
#define HAVE_DIRENT_H 1
|
||||
|
||||
/* Define if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define if you have the <limits.h> header file. */
|
||||
#define HAVE_LIMITS_H 1
|
||||
|
||||
/* Define if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define if you have the <ndir.h> header file. */
|
||||
/* #undef HAVE_NDIR_H */
|
||||
|
||||
/* Define if you have the <paths.h> header file. */
|
||||
#define HAVE_PATHS_H 1
|
||||
|
||||
/* Define if you have the <stddef.h> header file. */
|
||||
#define HAVE_STDDEF_H 1
|
||||
|
||||
/* Define if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define if you have the <sys/dir.h> header file. */
|
||||
/* #undef HAVE_SYS_DIR_H */
|
||||
|
||||
/* Define if you have the <sys/ndir.h> header file. */
|
||||
/* #undef HAVE_SYS_NDIR_H */
|
||||
|
||||
/* Define if you have the <sys/param.h> header file. */
|
||||
#define HAVE_SYS_PARAM_H 1
|
||||
|
||||
/* Define if you have the <sys/resource.h> header file. */
|
||||
#define HAVE_SYS_RESOURCE_H 1
|
||||
|
||||
/* Define if you have the <sys/time.h> header file. */
|
||||
#define HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define if you have the <sys/wait.h> header file. */
|
||||
#define HAVE_SYS_WAIT_H 1
|
||||
|
||||
/* Define if you have the <ulimit.h> header file. */
|
||||
#define HAVE_ULIMIT_H 1
|
||||
|
||||
/* Define if you have the <values.h> header file. */
|
||||
/* #undef HAVE_VALUES_H */
|
||||
|
||||
/* Need to use a separate file to keep the configure script from commenting
|
||||
* out the undefs....
|
||||
*/
|
||||
#include "conf-end.h"
|
||||
|
||||
#endif /* CONFIG_H */
|
1095
bin/ksh/edit.c
Normal file
1095
bin/ksh/edit.c
Normal file
File diff suppressed because it is too large
Load diff
87
bin/ksh/edit.h
Normal file
87
bin/ksh/edit.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/* $NetBSD: edit.h,v 1.3 1999/11/02 22:06:45 jdolecek Exp $ */
|
||||
|
||||
/* NAME:
|
||||
* edit.h - globals for edit modes
|
||||
*
|
||||
* DESCRIPTION:
|
||||
* This header defines various global edit objects.
|
||||
*
|
||||
* SEE ALSO:
|
||||
*
|
||||
*
|
||||
* RCSid:
|
||||
* $NetBSD: edit.h,v 1.3 1999/11/02 22:06:45 jdolecek Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
/* some useful #defines */
|
||||
#ifdef EXTERN
|
||||
# define I__(i) = i
|
||||
#else
|
||||
# define I__(i)
|
||||
# define EXTERN extern
|
||||
# define EXTERN_DEFINED
|
||||
#endif
|
||||
|
||||
#define BEL 0x07
|
||||
|
||||
/* tty driver characters we are interested in */
|
||||
typedef struct {
|
||||
int erase;
|
||||
int kill;
|
||||
int werase;
|
||||
int intr;
|
||||
int quit;
|
||||
int eof;
|
||||
} X_chars;
|
||||
|
||||
EXTERN X_chars edchars;
|
||||
|
||||
/* x_fc_glob() flags */
|
||||
#define XCF_COMMAND BIT(0) /* Do command completion */
|
||||
#define XCF_FILE BIT(1) /* Do file completion */
|
||||
#define XCF_FULLPATH BIT(2) /* command completion: store full path */
|
||||
#define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE)
|
||||
|
||||
/* edit.c */
|
||||
int x_getc ARGS((void));
|
||||
void x_flush ARGS((void));
|
||||
void x_putc ARGS((int c));
|
||||
void x_puts ARGS((const char *s));
|
||||
bool_t x_mode ARGS((bool_t onoff));
|
||||
int promptlen ARGS((const char *cp, const char **spp));
|
||||
int x_do_comment ARGS((char *buf, int bsize, int *lenp));
|
||||
void x_print_expansions ARGS((int nwords, char *const *words, int is_command));
|
||||
int x_cf_glob ARGS((int flags, const char *buf, int buflen, int pos, int *startp,
|
||||
int *endp, char ***wordsp, int *is_commandp));
|
||||
int x_longest_prefix ARGS((int nwords, char *const *words));
|
||||
int x_basename ARGS((const char *s, const char *se));
|
||||
void x_free_words ARGS((int nwords, char **words));
|
||||
int x_escape ARGS((const char *, size_t, int (*)(const char *s, size_t len)));
|
||||
/* emacs.c */
|
||||
int x_emacs ARGS((char *buf, size_t len));
|
||||
void x_init_emacs ARGS((void));
|
||||
void x_emacs_keys ARGS((X_chars *ec));
|
||||
/* vi.c */
|
||||
int x_vi ARGS((char *buf, size_t len));
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
# define D__(x) x
|
||||
#else
|
||||
# define D__(x)
|
||||
#endif
|
||||
|
||||
/* This lot goes at the END */
|
||||
/* be sure not to interfere with anyone else's idea about EXTERN */
|
||||
#ifdef EXTERN_DEFINED
|
||||
# undef EXTERN_DEFINED
|
||||
# undef EXTERN
|
||||
#endif
|
||||
#undef I__
|
||||
/*
|
||||
* Local Variables:
|
||||
* version-control:t
|
||||
* comment-column:40
|
||||
* End:
|
||||
*/
|
47
bin/ksh/emacs-gen.sh
Executable file
47
bin/ksh/emacs-gen.sh
Executable file
|
@ -0,0 +1,47 @@
|
|||
#!/bin/sh
|
||||
# $NetBSD: emacs-gen.sh,v 1.4 2008/10/25 22:18:15 apb Exp $
|
||||
|
||||
: ${AWK:=awk}
|
||||
: ${SED:=sed}
|
||||
|
||||
case $# in
|
||||
1) file=$1;;
|
||||
*)
|
||||
echo "$0: Usage: $0 path-to-emacs.c" 1>&2
|
||||
exit 1
|
||||
esac;
|
||||
|
||||
if [ ! -r "$file" ] ;then
|
||||
echo "$0: can't read $file" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat << E_O_F || exit 1
|
||||
/*
|
||||
* NOTE: THIS FILE WAS GENERATED AUTOMATICALLY FROM $file
|
||||
*
|
||||
* DO NOT BOTHER EDITING THIS FILE
|
||||
*/
|
||||
E_O_F
|
||||
|
||||
# Pass 1: print out lines before @START-FUNC-TAB@
|
||||
# and generate defines and function declarations,
|
||||
${SED} -e '1,/@START-FUNC-TAB@/d' -e '/@END-FUNC-TAB@/,$d' < $file |
|
||||
${AWK} 'BEGIN { nfunc = 0; }
|
||||
/^[ ]*#/ {
|
||||
print $0;
|
||||
next;
|
||||
}
|
||||
{
|
||||
fname = $2;
|
||||
c = substr(fname, length(fname), 1);
|
||||
if (c == ",")
|
||||
fname = substr(fname, 1, length(fname) - 1);
|
||||
if (fname != "0") {
|
||||
printf "#define XFUNC_%s %d\n", substr(fname, 3, length(fname) - 2), nfunc;
|
||||
printf "static int %s ARGS((int c));\n", fname;
|
||||
nfunc++;
|
||||
}
|
||||
}' || exit 1
|
||||
|
||||
exit 0
|
2221
bin/ksh/emacs.c
Normal file
2221
bin/ksh/emacs.c
Normal file
File diff suppressed because it is too large
Load diff
1405
bin/ksh/eval.c
Normal file
1405
bin/ksh/eval.c
Normal file
File diff suppressed because it is too large
Load diff
1749
bin/ksh/exec.c
Normal file
1749
bin/ksh/exec.c
Normal file
File diff suppressed because it is too large
Load diff
108
bin/ksh/expand.h
Normal file
108
bin/ksh/expand.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
/* $NetBSD: expand.h,v 1.4 2001/07/26 15:05:07 wiz Exp $ */
|
||||
|
||||
/*
|
||||
* Expanding strings
|
||||
*/
|
||||
/* $Id: expand.h,v 1.4 2001/07/26 15:05:07 wiz Exp $ */
|
||||
|
||||
#define X_EXTRA 8 /* this many extra bytes in X string */
|
||||
|
||||
#if 0 /* Usage */
|
||||
XString xs;
|
||||
char *xp;
|
||||
|
||||
Xinit(xs, xp, 128, ATEMP); /* allocate initial string */
|
||||
while ((c = generate()) {
|
||||
Xcheck(xs, xp); /* expand string if necessary */
|
||||
Xput(xs, xp, c); /* add character */
|
||||
}
|
||||
return Xclose(xs, xp); /* resize string */
|
||||
/*
|
||||
* NOTE:
|
||||
* The Xcheck and Xinit macros have a magic + X_EXTRA in the lengths.
|
||||
* This is so that you can put up to X_EXTRA characters in a XString
|
||||
* before calling Xcheck. (See yylex in lex.c)
|
||||
*/
|
||||
#endif /* 0 */
|
||||
|
||||
typedef struct XString {
|
||||
char *end, *beg; /* end, begin of string */
|
||||
size_t len; /* length */
|
||||
Area *areap; /* area to allocate/free from */
|
||||
} XString;
|
||||
|
||||
typedef char * XStringP;
|
||||
|
||||
/* initialize expandable string */
|
||||
#define Xinit(xs, xp, length, area) do { \
|
||||
(xs).len = length; \
|
||||
(xs).areap = (area); \
|
||||
(xs).beg = alloc((xs).len + X_EXTRA, (xs).areap); \
|
||||
(xs).end = (xs).beg + (xs).len; \
|
||||
xp = (xs).beg; \
|
||||
} while (0)
|
||||
|
||||
/* stuff char into string */
|
||||
#define Xput(xs, xp, c) (*xp++ = (c))
|
||||
|
||||
/* check if there are at least n bytes left */
|
||||
#define XcheckN(xs, xp, n) do { \
|
||||
int more = ((xp) + (n)) - (xs).end; \
|
||||
if (more > 0) \
|
||||
xp = Xcheck_grow_(&xs, xp, more); \
|
||||
} while (0)
|
||||
|
||||
/* check for overflow, expand string */
|
||||
#define Xcheck(xs, xp) XcheckN(xs, xp, 1)
|
||||
|
||||
/* free string */
|
||||
#define Xfree(xs, xp) afree((void*) (xs).beg, (xs).areap)
|
||||
|
||||
/* close, return string */
|
||||
#define Xclose(xs, xp) (char*) aresize((void*)(xs).beg, \
|
||||
(size_t)((xp) - (xs).beg), (xs).areap)
|
||||
/* begin of string */
|
||||
#define Xstring(xs, xp) ((xs).beg)
|
||||
|
||||
#define Xnleft(xs, xp) ((xs).end - (xp)) /* may be less than 0 */
|
||||
#define Xlength(xs, xp) ((xp) - (xs).beg)
|
||||
#define Xsize(xs, xp) ((xs).end - (xs).beg)
|
||||
#define Xsavepos(xs, xp) ((xp) - (xs).beg)
|
||||
#define Xrestpos(xs, xp, n) ((xs).beg + (n))
|
||||
|
||||
char * Xcheck_grow_ ARGS((XString *xsp, char *xp, int more));
|
||||
|
||||
/*
|
||||
* expandable vector of generic pointers
|
||||
*/
|
||||
|
||||
typedef struct XPtrV {
|
||||
void **cur; /* next avail pointer */
|
||||
void **beg, **end; /* begin, end of vector */
|
||||
} XPtrV;
|
||||
|
||||
#define XPinit(x, n) do { \
|
||||
register void **vp__; \
|
||||
vp__ = (void**) alloc(sizeofN(void*, n), ATEMP); \
|
||||
(x).cur = (x).beg = vp__; \
|
||||
(x).end = vp__ + n; \
|
||||
} while (0)
|
||||
|
||||
#define XPput(x, p) do { \
|
||||
if ((x).cur >= (x).end) { \
|
||||
int n = XPsize(x); \
|
||||
(x).beg = (void**) aresize((void*) (x).beg, \
|
||||
sizeofN(void*, n*2), ATEMP); \
|
||||
(x).cur = (x).beg + n; \
|
||||
(x).end = (x).cur + n; \
|
||||
} \
|
||||
*(x).cur++ = (p); \
|
||||
} while (0)
|
||||
|
||||
#define XPptrv(x) ((x).beg)
|
||||
#define XPsize(x) ((x).cur - (x).beg)
|
||||
|
||||
#define XPclose(x) (void**) aresize((void*)(x).beg, \
|
||||
sizeofN(void*, XPsize(x)), ATEMP)
|
||||
|
||||
#define XPfree(x) afree((void*) (x).beg, ATEMP)
|
613
bin/ksh/expr.c
Normal file
613
bin/ksh/expr.c
Normal file
|
@ -0,0 +1,613 @@
|
|||
/* $NetBSD: expr.c,v 1.9 2011/10/16 17:12:11 joerg Exp $ */
|
||||
|
||||
/*
|
||||
* Korn expression evaluation
|
||||
*/
|
||||
/*
|
||||
* todo: better error handling: if in builtin, should be builtin error, etc.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: expr.c,v 1.9 2011/10/16 17:12:11 joerg Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
/* The order of these enums is constrained by the order of opinfo[] */
|
||||
enum token {
|
||||
/* some (long) unary operators */
|
||||
O_PLUSPLUS = 0, O_MINUSMINUS,
|
||||
/* binary operators */
|
||||
O_EQ, O_NE,
|
||||
/* assignments are assumed to be in range O_ASN .. O_BORASN */
|
||||
O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
|
||||
O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
|
||||
O_LSHIFT, O_RSHIFT,
|
||||
O_LE, O_GE, O_LT, O_GT,
|
||||
O_LAND,
|
||||
O_LOR,
|
||||
O_TIMES, O_DIV, O_MOD,
|
||||
O_PLUS, O_MINUS,
|
||||
O_BAND,
|
||||
O_BXOR,
|
||||
O_BOR,
|
||||
O_TERN,
|
||||
O_COMMA,
|
||||
/* things after this aren't used as binary operators */
|
||||
/* unary that are not also binaries */
|
||||
O_BNOT, O_LNOT,
|
||||
/* misc */
|
||||
OPEN_PAREN, CLOSE_PAREN, CTERN,
|
||||
/* things that don't appear in the opinfo[] table */
|
||||
VAR, LIT, END, BAD
|
||||
};
|
||||
#define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
|
||||
#define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
|
||||
|
||||
enum prec {
|
||||
P_PRIMARY = 0, /* VAR, LIT, (), ~ ! - + */
|
||||
P_MULT, /* * / % */
|
||||
P_ADD, /* + - */
|
||||
P_SHIFT, /* << >> */
|
||||
P_RELATION, /* < <= > >= */
|
||||
P_EQUALITY, /* == != */
|
||||
P_BAND, /* & */
|
||||
P_BXOR, /* ^ */
|
||||
P_BOR, /* | */
|
||||
P_LAND, /* && */
|
||||
P_LOR, /* || */
|
||||
P_TERN, /* ?: */
|
||||
P_ASSIGN, /* = *= /= %= += -= <<= >>= &= ^= |= */
|
||||
P_COMMA /* , */
|
||||
};
|
||||
#define MAX_PREC P_COMMA
|
||||
|
||||
struct opinfo {
|
||||
char name[4];
|
||||
int len; /* name length */
|
||||
enum prec prec; /* precedence: lower is higher */
|
||||
};
|
||||
|
||||
/* Tokens in this table must be ordered so the longest are first
|
||||
* (eg, += before +). If you change something, change the order
|
||||
* of enum token too.
|
||||
*/
|
||||
static const struct opinfo opinfo[] = {
|
||||
{ "++", 2, P_PRIMARY }, /* before + */
|
||||
{ "--", 2, P_PRIMARY }, /* before - */
|
||||
{ "==", 2, P_EQUALITY }, /* before = */
|
||||
{ "!=", 2, P_EQUALITY }, /* before ! */
|
||||
{ "=", 1, P_ASSIGN }, /* keep assigns in a block */
|
||||
{ "*=", 2, P_ASSIGN },
|
||||
{ "/=", 2, P_ASSIGN },
|
||||
{ "%=", 2, P_ASSIGN },
|
||||
{ "+=", 2, P_ASSIGN },
|
||||
{ "-=", 2, P_ASSIGN },
|
||||
{ "<<=", 3, P_ASSIGN },
|
||||
{ ">>=", 3, P_ASSIGN },
|
||||
{ "&=", 2, P_ASSIGN },
|
||||
{ "^=", 2, P_ASSIGN },
|
||||
{ "|=", 2, P_ASSIGN },
|
||||
{ "<<", 2, P_SHIFT },
|
||||
{ ">>", 2, P_SHIFT },
|
||||
{ "<=", 2, P_RELATION },
|
||||
{ ">=", 2, P_RELATION },
|
||||
{ "<", 1, P_RELATION },
|
||||
{ ">", 1, P_RELATION },
|
||||
{ "&&", 2, P_LAND },
|
||||
{ "||", 2, P_LOR },
|
||||
{ "*", 1, P_MULT },
|
||||
{ "/", 1, P_MULT },
|
||||
{ "%", 1, P_MULT },
|
||||
{ "+", 1, P_ADD },
|
||||
{ "-", 1, P_ADD },
|
||||
{ "&", 1, P_BAND },
|
||||
{ "^", 1, P_BXOR },
|
||||
{ "|", 1, P_BOR },
|
||||
{ "?", 1, P_TERN },
|
||||
{ ",", 1, P_COMMA },
|
||||
{ "~", 1, P_PRIMARY },
|
||||
{ "!", 1, P_PRIMARY },
|
||||
{ "(", 1, P_PRIMARY },
|
||||
{ ")", 1, P_PRIMARY },
|
||||
{ ":", 1, P_PRIMARY },
|
||||
{ "", 0, P_PRIMARY } /* end of table */
|
||||
};
|
||||
|
||||
|
||||
typedef struct expr_state Expr_state;
|
||||
struct expr_state {
|
||||
const char *expression; /* expression being evaluated */
|
||||
const char *tokp; /* lexical position */
|
||||
enum token tok; /* token from token() */
|
||||
int noassign; /* don't do assigns (for ?:,&&,||) */
|
||||
struct tbl *val; /* value from token() */
|
||||
struct tbl *evaling; /* variable that is being recursively
|
||||
* expanded (EXPRINEVAL flag set)
|
||||
*/
|
||||
};
|
||||
|
||||
enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
|
||||
ET_LVALUE, ET_RDONLY, ET_STR };
|
||||
|
||||
static void evalerr ARGS((Expr_state *es, enum error_type type,
|
||||
const char *str)) GCC_FUNC_ATTR(noreturn);
|
||||
static struct tbl *evalexpr ARGS((Expr_state *es, enum prec prec));
|
||||
static void token ARGS((Expr_state *es));
|
||||
static struct tbl *do_ppmm ARGS((Expr_state *es, enum token op,
|
||||
struct tbl *vasn, bool_t is_prefix));
|
||||
static void assign_check ARGS((Expr_state *es, enum token op,
|
||||
struct tbl *vasn));
|
||||
static struct tbl *tempvar ARGS((void));
|
||||
static struct tbl *intvar ARGS((Expr_state *es, struct tbl *vp));
|
||||
|
||||
/*
|
||||
* parse and evaluate expression
|
||||
*/
|
||||
int
|
||||
evaluate(expr, rval, error_ok)
|
||||
const char *expr;
|
||||
long *rval;
|
||||
int error_ok;
|
||||
{
|
||||
struct tbl v;
|
||||
int ret;
|
||||
|
||||
v.flag = DEFINED|INTEGER;
|
||||
v.type = 0;
|
||||
ret = v_evaluate(&v, expr, error_ok);
|
||||
*rval = v.val.i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse and evaluate expression, storing result in vp.
|
||||
*/
|
||||
int
|
||||
v_evaluate(vp, expr, error_ok)
|
||||
struct tbl *vp;
|
||||
const char *expr;
|
||||
volatile int error_ok;
|
||||
{
|
||||
struct tbl *v;
|
||||
Expr_state curstate;
|
||||
Expr_state * const es = &curstate;
|
||||
int i;
|
||||
|
||||
/* save state to allow recursive calls */
|
||||
curstate.expression = curstate.tokp = expr;
|
||||
curstate.noassign = 0;
|
||||
curstate.evaling = (struct tbl *) 0;
|
||||
|
||||
newenv(E_ERRH);
|
||||
i = ksh_sigsetjmp(e->jbuf, 0);
|
||||
if (i) {
|
||||
/* Clear EXPRINEVAL in of any variables we were playing with */
|
||||
if (curstate.evaling)
|
||||
curstate.evaling->flag &= ~EXPRINEVAL;
|
||||
quitenv();
|
||||
if (i == LAEXPR) {
|
||||
if (error_ok == KSH_RETURN_ERROR)
|
||||
return 0;
|
||||
errorf("%s", null);
|
||||
}
|
||||
unwind(i);
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
||||
token(es);
|
||||
#if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */
|
||||
if (es->tok == END) {
|
||||
es->tok = LIT;
|
||||
es->val = tempvar();
|
||||
}
|
||||
#endif /* 0 */
|
||||
v = intvar(es, evalexpr(es, MAX_PREC));
|
||||
|
||||
if (es->tok != END)
|
||||
evalerr(es, ET_UNEXPECTED, (char *) 0);
|
||||
|
||||
if (vp->flag & INTEGER)
|
||||
setint_v(vp, v);
|
||||
else
|
||||
/* can fail if readonly */
|
||||
setstr(vp, str_val(v), error_ok);
|
||||
|
||||
quitenv();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
evalerr(es, type, str)
|
||||
Expr_state *es;
|
||||
enum error_type type;
|
||||
const char *str;
|
||||
{
|
||||
char tbuf[2];
|
||||
const char *s;
|
||||
|
||||
switch (type) {
|
||||
case ET_UNEXPECTED:
|
||||
switch (es->tok) {
|
||||
case VAR:
|
||||
s = es->val->name;
|
||||
break;
|
||||
case LIT:
|
||||
s = str_val(es->val);
|
||||
break;
|
||||
case END:
|
||||
s = "end of expression";
|
||||
break;
|
||||
case BAD:
|
||||
tbuf[0] = *es->tokp;
|
||||
tbuf[1] = '\0';
|
||||
s = tbuf;
|
||||
break;
|
||||
default:
|
||||
s = opinfo[(int)es->tok].name;
|
||||
}
|
||||
warningf(TRUE, "%s: unexpected `%s'", es->expression, s);
|
||||
break;
|
||||
|
||||
case ET_BADLIT:
|
||||
warningf(TRUE, "%s: bad number `%s'", es->expression, str);
|
||||
break;
|
||||
|
||||
case ET_RECURSIVE:
|
||||
warningf(TRUE, "%s: expression recurses on parameter `%s'",
|
||||
es->expression, str);
|
||||
break;
|
||||
|
||||
case ET_LVALUE:
|
||||
warningf(TRUE, "%s: %s requires lvalue",
|
||||
es->expression, str);
|
||||
break;
|
||||
|
||||
case ET_RDONLY:
|
||||
warningf(TRUE, "%s: %s applied to read only variable",
|
||||
es->expression, str);
|
||||
break;
|
||||
|
||||
default: /* keep gcc happy */
|
||||
case ET_STR:
|
||||
warningf(TRUE, "%s: %s", es->expression, str);
|
||||
break;
|
||||
}
|
||||
unwind(LAEXPR);
|
||||
}
|
||||
|
||||
static struct tbl *
|
||||
evalexpr(es, prec)
|
||||
Expr_state *es;
|
||||
enum prec prec;
|
||||
{
|
||||
struct tbl *vl, UNINITIALIZED(*vr), *vasn;
|
||||
enum token op;
|
||||
long UNINITIALIZED(res);
|
||||
|
||||
if (prec == P_PRIMARY) {
|
||||
op = es->tok;
|
||||
if (op == O_BNOT || op == O_LNOT || op == O_MINUS
|
||||
|| op == O_PLUS)
|
||||
{
|
||||
token(es);
|
||||
vl = intvar(es, evalexpr(es, P_PRIMARY));
|
||||
if (op == O_BNOT)
|
||||
vl->val.i = ~vl->val.i;
|
||||
else if (op == O_LNOT)
|
||||
vl->val.i = !vl->val.i;
|
||||
else if (op == O_MINUS)
|
||||
vl->val.i = -vl->val.i;
|
||||
/* op == O_PLUS is a no-op */
|
||||
} else if (op == OPEN_PAREN) {
|
||||
token(es);
|
||||
vl = evalexpr(es, MAX_PREC);
|
||||
if (es->tok != CLOSE_PAREN)
|
||||
evalerr(es, ET_STR, "missing )");
|
||||
token(es);
|
||||
} else if (op == O_PLUSPLUS || op == O_MINUSMINUS) {
|
||||
token(es);
|
||||
vl = do_ppmm(es, op, es->val, TRUE);
|
||||
token(es);
|
||||
} else if (op == VAR || op == LIT) {
|
||||
vl = es->val;
|
||||
token(es);
|
||||
} else {
|
||||
evalerr(es, ET_UNEXPECTED, (char *) 0);
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
|
||||
vl = do_ppmm(es, es->tok, vl, FALSE);
|
||||
token(es);
|
||||
}
|
||||
return vl;
|
||||
}
|
||||
vl = evalexpr(es, ((int) prec) - 1);
|
||||
for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec;
|
||||
op = es->tok)
|
||||
{
|
||||
token(es);
|
||||
vasn = vl;
|
||||
if (op != O_ASN) /* vl may not have a value yet */
|
||||
vl = intvar(es, vl);
|
||||
if (IS_ASSIGNOP(op)) {
|
||||
assign_check(es, op, vasn);
|
||||
vr = intvar(es, evalexpr(es, P_ASSIGN));
|
||||
} else if (op != O_TERN && op != O_LAND && op != O_LOR)
|
||||
vr = intvar(es, evalexpr(es, ((int) prec) - 1));
|
||||
if ((op == O_DIV || op == O_MOD || op == O_DIVASN
|
||||
|| op == O_MODASN) && vr->val.i == 0)
|
||||
{
|
||||
if (es->noassign)
|
||||
vr->val.i = 1;
|
||||
else
|
||||
evalerr(es, ET_STR, "zero divisor");
|
||||
}
|
||||
switch ((int) op) {
|
||||
case O_TIMES:
|
||||
case O_TIMESASN:
|
||||
res = vl->val.i * vr->val.i;
|
||||
break;
|
||||
case O_DIV:
|
||||
case O_DIVASN:
|
||||
res = vl->val.i / vr->val.i;
|
||||
break;
|
||||
case O_MOD:
|
||||
case O_MODASN:
|
||||
res = vl->val.i % vr->val.i;
|
||||
break;
|
||||
case O_PLUS:
|
||||
case O_PLUSASN:
|
||||
res = vl->val.i + vr->val.i;
|
||||
break;
|
||||
case O_MINUS:
|
||||
case O_MINUSASN:
|
||||
res = vl->val.i - vr->val.i;
|
||||
break;
|
||||
case O_LSHIFT:
|
||||
case O_LSHIFTASN:
|
||||
res = vl->val.i << vr->val.i;
|
||||
break;
|
||||
case O_RSHIFT:
|
||||
case O_RSHIFTASN:
|
||||
res = vl->val.i >> vr->val.i;
|
||||
break;
|
||||
case O_LT:
|
||||
res = vl->val.i < vr->val.i;
|
||||
break;
|
||||
case O_LE:
|
||||
res = vl->val.i <= vr->val.i;
|
||||
break;
|
||||
case O_GT:
|
||||
res = vl->val.i > vr->val.i;
|
||||
break;
|
||||
case O_GE:
|
||||
res = vl->val.i >= vr->val.i;
|
||||
break;
|
||||
case O_EQ:
|
||||
res = vl->val.i == vr->val.i;
|
||||
break;
|
||||
case O_NE:
|
||||
res = vl->val.i != vr->val.i;
|
||||
break;
|
||||
case O_BAND:
|
||||
case O_BANDASN:
|
||||
res = vl->val.i & vr->val.i;
|
||||
break;
|
||||
case O_BXOR:
|
||||
case O_BXORASN:
|
||||
res = vl->val.i ^ vr->val.i;
|
||||
break;
|
||||
case O_BOR:
|
||||
case O_BORASN:
|
||||
res = vl->val.i | vr->val.i;
|
||||
break;
|
||||
case O_LAND:
|
||||
if (!vl->val.i)
|
||||
es->noassign++;
|
||||
vr = intvar(es, evalexpr(es, ((int) prec) - 1));
|
||||
res = vl->val.i && vr->val.i;
|
||||
if (!vl->val.i)
|
||||
es->noassign--;
|
||||
break;
|
||||
case O_LOR:
|
||||
if (vl->val.i)
|
||||
es->noassign++;
|
||||
vr = intvar(es, evalexpr(es, ((int) prec) - 1));
|
||||
res = vl->val.i || vr->val.i;
|
||||
if (vl->val.i)
|
||||
es->noassign--;
|
||||
break;
|
||||
case O_TERN:
|
||||
{
|
||||
int ex = vl->val.i != 0;
|
||||
if (!ex)
|
||||
es->noassign++;
|
||||
vl = evalexpr(es, MAX_PREC);
|
||||
if (!ex)
|
||||
es->noassign--;
|
||||
if (es->tok != CTERN)
|
||||
evalerr(es, ET_STR, "missing :");
|
||||
token(es);
|
||||
if (ex)
|
||||
es->noassign++;
|
||||
vr = evalexpr(es, P_TERN);
|
||||
if (ex)
|
||||
es->noassign--;
|
||||
vl = ex ? vl : vr;
|
||||
}
|
||||
break;
|
||||
case O_ASN:
|
||||
res = vr->val.i;
|
||||
break;
|
||||
case O_COMMA:
|
||||
res = vr->val.i;
|
||||
break;
|
||||
}
|
||||
if (IS_ASSIGNOP(op)) {
|
||||
vr->val.i = res;
|
||||
if (vasn->flag & INTEGER)
|
||||
setint_v(vasn, vr);
|
||||
else
|
||||
setint(vasn, res);
|
||||
vl = vr;
|
||||
} else if (op != O_TERN)
|
||||
vl->val.i = res;
|
||||
}
|
||||
return vl;
|
||||
}
|
||||
|
||||
static void
|
||||
token(es)
|
||||
Expr_state *es;
|
||||
{
|
||||
const char *cp;
|
||||
int c;
|
||||
char *tvar;
|
||||
|
||||
/* skip white space */
|
||||
for (cp = es->tokp; (c = *cp), isspace((unsigned char)c); cp++)
|
||||
;
|
||||
es->tokp = cp;
|
||||
|
||||
if (c == '\0')
|
||||
es->tok = END;
|
||||
else if (letter(c)) {
|
||||
for (; letnum(c); c = *cp)
|
||||
cp++;
|
||||
if (c == '[') {
|
||||
int len;
|
||||
|
||||
len = array_ref_len(cp);
|
||||
if (len == 0)
|
||||
evalerr(es, ET_STR, "missing ]");
|
||||
cp += len;
|
||||
}
|
||||
#ifdef KSH
|
||||
else if (c == '(' /*)*/ ) {
|
||||
/* todo: add math functions (all take single argument):
|
||||
* abs acos asin atan cos cosh exp int log sin sinh sqrt
|
||||
* tan tanh
|
||||
*/
|
||||
;
|
||||
}
|
||||
#endif /* KSH */
|
||||
if (es->noassign) {
|
||||
es->val = tempvar();
|
||||
es->val->flag |= EXPRLVALUE;
|
||||
} else {
|
||||
tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP);
|
||||
es->val = global(tvar);
|
||||
afree(tvar, ATEMP);
|
||||
}
|
||||
es->tok = VAR;
|
||||
} else if (digit(c)) {
|
||||
for (; c != '_' && (letnum(c) || c == '#'); c = *cp++)
|
||||
;
|
||||
tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP);
|
||||
es->val = tempvar();
|
||||
es->val->flag &= ~INTEGER;
|
||||
es->val->type = 0;
|
||||
es->val->val.s = tvar;
|
||||
if (setint_v(es->val, es->val) == NULL)
|
||||
evalerr(es, ET_BADLIT, tvar);
|
||||
afree(tvar, ATEMP);
|
||||
es->tok = LIT;
|
||||
} else {
|
||||
int i, n0;
|
||||
|
||||
for (i = 0; (n0 = opinfo[i].name[0]); i++)
|
||||
if (c == n0
|
||||
&& strncmp(cp, opinfo[i].name, opinfo[i].len) == 0)
|
||||
{
|
||||
es->tok = (enum token) i;
|
||||
cp += opinfo[i].len;
|
||||
break;
|
||||
}
|
||||
if (!n0)
|
||||
es->tok = BAD;
|
||||
}
|
||||
es->tokp = cp;
|
||||
}
|
||||
|
||||
/* Do a ++ or -- operation */
|
||||
static struct tbl *
|
||||
do_ppmm(es, op, vasn, is_prefix)
|
||||
Expr_state *es;
|
||||
enum token op;
|
||||
struct tbl *vasn;
|
||||
bool_t is_prefix;
|
||||
{
|
||||
struct tbl *vl;
|
||||
int oval;
|
||||
|
||||
assign_check(es, op, vasn);
|
||||
|
||||
vl = intvar(es, vasn);
|
||||
oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--;
|
||||
if (vasn->flag & INTEGER)
|
||||
setint_v(vasn, vl);
|
||||
else
|
||||
setint(vasn, vl->val.i);
|
||||
if (!is_prefix) /* undo the inc/dec */
|
||||
vl->val.i = oval;
|
||||
|
||||
return vl;
|
||||
}
|
||||
|
||||
static void
|
||||
assign_check(es, op, vasn)
|
||||
Expr_state *es;
|
||||
enum token op;
|
||||
struct tbl *vasn;
|
||||
{
|
||||
if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE))
|
||||
evalerr(es, ET_LVALUE, opinfo[(int) op].name);
|
||||
else if (vasn->flag & RDONLY)
|
||||
evalerr(es, ET_RDONLY, opinfo[(int) op].name);
|
||||
}
|
||||
|
||||
static struct tbl *
|
||||
tempvar()
|
||||
{
|
||||
register struct tbl *vp;
|
||||
|
||||
vp = (struct tbl*) alloc(sizeof(struct tbl), ATEMP);
|
||||
vp->flag = ISSET|INTEGER;
|
||||
vp->type = 0;
|
||||
vp->areap = ATEMP;
|
||||
vp->val.i = 0;
|
||||
vp->name[0] = '\0';
|
||||
return vp;
|
||||
}
|
||||
|
||||
/* cast (string) variable to temporary integer variable */
|
||||
static struct tbl *
|
||||
intvar(es, vp)
|
||||
Expr_state *es;
|
||||
struct tbl *vp;
|
||||
{
|
||||
struct tbl *vq;
|
||||
|
||||
/* try to avoid replacing a temp var with another temp var */
|
||||
if (vp->name[0] == '\0'
|
||||
&& (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
|
||||
return vp;
|
||||
|
||||
vq = tempvar();
|
||||
if (setint_v(vq, vp) == NULL) {
|
||||
if (vp->flag & EXPRINEVAL)
|
||||
evalerr(es, ET_RECURSIVE, vp->name);
|
||||
es->evaling = vp;
|
||||
vp->flag |= EXPRINEVAL;
|
||||
v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR);
|
||||
vp->flag &= ~EXPRINEVAL;
|
||||
es->evaling = (struct tbl *) 0;
|
||||
}
|
||||
return vq;
|
||||
}
|
1229
bin/ksh/history.c
Normal file
1229
bin/ksh/history.c
Normal file
File diff suppressed because it is too large
Load diff
568
bin/ksh/io.c
Normal file
568
bin/ksh/io.c
Normal file
|
@ -0,0 +1,568 @@
|
|||
/* $NetBSD: io.c,v 1.9 2005/06/26 19:09:00 christos Exp $ */
|
||||
|
||||
/*
|
||||
* shell buffered IO and formatted output
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: io.c,v 1.9 2005/06/26 19:09:00 christos Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include <ctype.h>
|
||||
#include "sh.h"
|
||||
#include "ksh_stat.h"
|
||||
|
||||
static int initio_done;
|
||||
|
||||
/*
|
||||
* formatted output functions
|
||||
*/
|
||||
|
||||
|
||||
/* A shell error occurred (eg, syntax error, etc.) */
|
||||
void
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
errorf(const char *fmt, ...)
|
||||
#else
|
||||
errorf(fmt, va_alist)
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list va;
|
||||
|
||||
shl_stdout_ok = 0; /* debugging: note that stdout not valid */
|
||||
exstat = 1;
|
||||
if (*fmt) {
|
||||
error_prefix(TRUE);
|
||||
SH_VA_START(va, fmt);
|
||||
shf_vfprintf(shl_out, fmt, va);
|
||||
va_end(va);
|
||||
shf_putchar('\n', shl_out);
|
||||
}
|
||||
shf_flush(shl_out);
|
||||
unwind(LERROR);
|
||||
}
|
||||
|
||||
/* like errorf(), but no unwind is done */
|
||||
void
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
warningf(int fileline, const char *fmt, ...)
|
||||
#else
|
||||
warningf(fileline, fmt, va_alist)
|
||||
int fileline;
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list va;
|
||||
|
||||
error_prefix(fileline);
|
||||
SH_VA_START(va, fmt);
|
||||
shf_vfprintf(shl_out, fmt, va);
|
||||
va_end(va);
|
||||
shf_putchar('\n', shl_out);
|
||||
shf_flush(shl_out);
|
||||
}
|
||||
|
||||
/* Used by built-in utilities to prefix shell and utility name to message
|
||||
* (also unwinds environments for special builtins).
|
||||
*/
|
||||
void
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
bi_errorf(const char *fmt, ...)
|
||||
#else
|
||||
bi_errorf(fmt, va_alist)
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list va;
|
||||
|
||||
shl_stdout_ok = 0; /* debugging: note that stdout not valid */
|
||||
exstat = 1;
|
||||
if (*fmt) {
|
||||
error_prefix(TRUE);
|
||||
/* not set when main() calls parse_args() */
|
||||
if (builtin_argv0)
|
||||
shf_fprintf(shl_out, "%s: ", builtin_argv0);
|
||||
SH_VA_START(va, fmt);
|
||||
shf_vfprintf(shl_out, fmt, va);
|
||||
va_end(va);
|
||||
shf_putchar('\n', shl_out);
|
||||
}
|
||||
shf_flush(shl_out);
|
||||
/* POSIX special builtins and ksh special builtins cause
|
||||
* non-interactive shells to exit.
|
||||
* XXX odd use of KEEPASN; also may not want LERROR here
|
||||
*/
|
||||
if ((builtin_flag & SPEC_BI)
|
||||
|| (Flag(FPOSIX) && (builtin_flag & KEEPASN)))
|
||||
{
|
||||
builtin_argv0 = (char *) 0;
|
||||
unwind(LERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when something that shouldn't happen does */
|
||||
void
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
internal_errorf(int jump, const char *fmt, ...)
|
||||
#else
|
||||
internal_errorf(jump, fmt, va_alist)
|
||||
int jump;
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list va;
|
||||
|
||||
error_prefix(TRUE);
|
||||
shf_fprintf(shl_out, "internal error: ");
|
||||
SH_VA_START(va, fmt);
|
||||
shf_vfprintf(shl_out, fmt, va);
|
||||
va_end(va);
|
||||
shf_putchar('\n', shl_out);
|
||||
shf_flush(shl_out);
|
||||
if (jump)
|
||||
unwind(LERROR);
|
||||
}
|
||||
|
||||
/* used by error reporting functions to print "ksh: .kshrc[25]: " */
|
||||
void
|
||||
error_prefix(fileline)
|
||||
int fileline;
|
||||
{
|
||||
/* Avoid foo: foo[2]: ... */
|
||||
if (!fileline || !source || !source->file
|
||||
|| strcmp(source->file, kshname) != 0)
|
||||
shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
|
||||
if (fileline && source && source->file != NULL) {
|
||||
shf_fprintf(shl_out, "%s[%d]: ", source->file,
|
||||
source->errline > 0 ? source->errline : source->line);
|
||||
source->errline = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* printf to shl_out (stderr) with flush */
|
||||
void
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
shellf(const char *fmt, ...)
|
||||
#else
|
||||
shellf(fmt, va_alist)
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list va;
|
||||
|
||||
if (!initio_done) /* shl_out may not be set up yet... */
|
||||
return;
|
||||
SH_VA_START(va, fmt);
|
||||
shf_vfprintf(shl_out, fmt, va);
|
||||
va_end(va);
|
||||
shf_flush(shl_out);
|
||||
}
|
||||
|
||||
/* printf to shl_stdout (stdout) */
|
||||
void
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
shprintf(const char *fmt, ...)
|
||||
#else
|
||||
shprintf(fmt, va_alist)
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list va;
|
||||
|
||||
if (!shl_stdout_ok)
|
||||
internal_errorf(1, "shl_stdout not valid");
|
||||
SH_VA_START(va, fmt);
|
||||
shf_vfprintf(shl_stdout, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
#ifdef KSH_DEBUG
|
||||
static struct shf *kshdebug_shf;
|
||||
|
||||
void
|
||||
kshdebug_init_()
|
||||
{
|
||||
if (kshdebug_shf)
|
||||
shf_close(kshdebug_shf);
|
||||
kshdebug_shf = shf_open("/tmp/ksh-debug.log",
|
||||
O_WRONLY|O_APPEND|O_CREAT, 0600,
|
||||
SHF_WR|SHF_MAPHI);
|
||||
if (kshdebug_shf) {
|
||||
shf_fprintf(kshdebug_shf, "\nNew shell[pid %d]\n", getpid());
|
||||
shf_flush(kshdebug_shf);
|
||||
}
|
||||
}
|
||||
|
||||
/* print to debugging log */
|
||||
void
|
||||
# ifdef HAVE_PROTOTYPES
|
||||
kshdebug_printf_(const char *fmt, ...)
|
||||
# else
|
||||
kshdebug_printf_(fmt, va_alist)
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
# endif
|
||||
{
|
||||
va_list va;
|
||||
|
||||
if (!kshdebug_shf)
|
||||
return;
|
||||
SH_VA_START(va, fmt);
|
||||
shf_fprintf(kshdebug_shf, "[%d] ", getpid());
|
||||
shf_vfprintf(kshdebug_shf, fmt, va);
|
||||
va_end(va);
|
||||
shf_flush(kshdebug_shf);
|
||||
}
|
||||
|
||||
void
|
||||
kshdebug_dump_(str, mem, nbytes)
|
||||
const char *str;
|
||||
const void *mem;
|
||||
int nbytes;
|
||||
{
|
||||
int i, j;
|
||||
int nprow = 16;
|
||||
|
||||
if (!kshdebug_shf)
|
||||
return;
|
||||
shf_fprintf(kshdebug_shf, "[%d] %s:\n", getpid(), str);
|
||||
for (i = 0; i < nbytes; i += nprow) {
|
||||
char c = '\t';
|
||||
for (j = 0; j < nprow && i + j < nbytes; j++) {
|
||||
shf_fprintf(kshdebug_shf, "%c%02x",
|
||||
c, ((const unsigned char *) mem)[i + j]);
|
||||
c = ' ';
|
||||
}
|
||||
shf_fprintf(kshdebug_shf, "\n");
|
||||
}
|
||||
shf_flush(kshdebug_shf);
|
||||
}
|
||||
#endif /* KSH_DEBUG */
|
||||
|
||||
/* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */
|
||||
int
|
||||
can_seek(fd)
|
||||
int fd;
|
||||
{
|
||||
struct stat statb;
|
||||
|
||||
return fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ?
|
||||
SHF_UNBUF : 0;
|
||||
}
|
||||
|
||||
struct shf shf_iob[3];
|
||||
|
||||
void
|
||||
initio()
|
||||
{
|
||||
shf_fdopen(1, SHF_WR, shl_stdout); /* force buffer allocation */
|
||||
shf_fdopen(2, SHF_WR, shl_out);
|
||||
shf_fdopen(2, SHF_WR, shl_spare); /* force buffer allocation */
|
||||
initio_done = 1;
|
||||
kshdebug_init();
|
||||
}
|
||||
|
||||
/* A dup2() with error checking */
|
||||
int
|
||||
ksh_dup2(ofd, nfd, errok)
|
||||
int ofd;
|
||||
int nfd;
|
||||
int errok;
|
||||
{
|
||||
int ret = dup2(ofd, nfd);
|
||||
|
||||
if (ret < 0 && errno != EBADF && !errok)
|
||||
errorf("too many files open in shell");
|
||||
|
||||
#ifdef DUP2_BROKEN
|
||||
/* Ultrix systems like to preserve the close-on-exec flag */
|
||||
if (ret >= 0)
|
||||
(void) fcntl(nfd, F_SETFD, 0);
|
||||
#endif /* DUP2_BROKEN */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* move fd from user space (0<=fd<10) to shell space (fd>=10),
|
||||
* set close-on-exec flag.
|
||||
*/
|
||||
int
|
||||
savefd(fd, noclose)
|
||||
int fd;
|
||||
int noclose;
|
||||
{
|
||||
int nfd;
|
||||
|
||||
if (fd < FDBASE) {
|
||||
nfd = ksh_dupbase(fd, FDBASE);
|
||||
if (nfd < 0) {
|
||||
if (errno == EBADF)
|
||||
return -1;
|
||||
else
|
||||
errorf("too many files open in shell");
|
||||
}
|
||||
if (!noclose)
|
||||
close(fd);
|
||||
} else
|
||||
nfd = fd;
|
||||
fd_clexec(nfd);
|
||||
return nfd;
|
||||
}
|
||||
|
||||
void
|
||||
restfd(fd, ofd)
|
||||
int fd, ofd;
|
||||
{
|
||||
if (fd == 2)
|
||||
shf_flush(&shf_iob[fd]);
|
||||
if (ofd < 0) /* original fd closed */
|
||||
close(fd);
|
||||
else if (fd != ofd) {
|
||||
ksh_dup2(ofd, fd, TRUE); /* XXX: what to do if this fails? */
|
||||
close(ofd);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
openpipe(pv)
|
||||
register int *pv;
|
||||
{
|
||||
if (pipe(pv) < 0)
|
||||
errorf("can't create pipe - try again");
|
||||
pv[0] = savefd(pv[0], 0);
|
||||
pv[1] = savefd(pv[1], 0);
|
||||
}
|
||||
|
||||
void
|
||||
closepipe(pv)
|
||||
register int *pv;
|
||||
{
|
||||
close(pv[0]);
|
||||
close(pv[1]);
|
||||
}
|
||||
|
||||
/* Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
|
||||
* a string (the X in 2>&X, read -uX, print -uX) into a file descriptor.
|
||||
*/
|
||||
int
|
||||
check_fd(name, mode, emsgp)
|
||||
char *name;
|
||||
int mode;
|
||||
const char **emsgp;
|
||||
{
|
||||
int fd, fl;
|
||||
|
||||
if (isdigit((unsigned char)name[0]) && !name[1]) {
|
||||
fd = name[0] - '0';
|
||||
if ((fl = fcntl(fd = name[0] - '0', F_GETFL, 0)) < 0) {
|
||||
if (emsgp)
|
||||
*emsgp = "bad file descriptor";
|
||||
return -1;
|
||||
}
|
||||
fl &= O_ACCMODE;
|
||||
#ifdef OS2
|
||||
if (mode == W_OK ) {
|
||||
if (setmode(fd, O_TEXT) == -1) {
|
||||
if (emsgp)
|
||||
*emsgp = "couldn't set write mode";
|
||||
return -1;
|
||||
}
|
||||
} else if (mode == R_OK)
|
||||
if (setmode(fd, O_BINARY) == -1) {
|
||||
if (emsgp)
|
||||
*emsgp = "couldn't set read mode";
|
||||
return -1;
|
||||
}
|
||||
#else /* OS2 */
|
||||
/* X_OK is a kludge to disable this check for dups (x<&1):
|
||||
* historical shells never did this check (XXX don't know what
|
||||
* posix has to say).
|
||||
*/
|
||||
if (!(mode & X_OK) && fl != O_RDWR
|
||||
&& (((mode & R_OK) && fl != O_RDONLY)
|
||||
|| ((mode & W_OK) && fl != O_WRONLY)))
|
||||
{
|
||||
if (emsgp)
|
||||
*emsgp = (fl == O_WRONLY) ?
|
||||
"fd not open for reading"
|
||||
: "fd not open for writing";
|
||||
return -1;
|
||||
}
|
||||
#endif /* OS2 */
|
||||
return fd;
|
||||
}
|
||||
#ifdef KSH
|
||||
else if (name[0] == 'p' && !name[1])
|
||||
return coproc_getfd(mode, emsgp);
|
||||
#endif /* KSH */
|
||||
if (emsgp)
|
||||
*emsgp = "illegal file descriptor name";
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef KSH
|
||||
/* Called once from main */
|
||||
void
|
||||
coproc_init()
|
||||
{
|
||||
coproc.read = coproc.readw = coproc.write = -1;
|
||||
coproc.njobs = 0;
|
||||
coproc.id = 0;
|
||||
}
|
||||
|
||||
/* Called by c_read() when eof is read - close fd if it is the co-process fd */
|
||||
void
|
||||
coproc_read_close(fd)
|
||||
int fd;
|
||||
{
|
||||
if (coproc.read >= 0 && fd == coproc.read) {
|
||||
coproc_readw_close(fd);
|
||||
close(coproc.read);
|
||||
coproc.read = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Called by c_read() and by iosetup() to close the other side of the
|
||||
* read pipe, so reads will actually terminate.
|
||||
*/
|
||||
void
|
||||
coproc_readw_close(fd)
|
||||
int fd;
|
||||
{
|
||||
if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) {
|
||||
close(coproc.readw);
|
||||
coproc.readw = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Called by c_print when a write to a fd fails with EPIPE and by iosetup
|
||||
* when co-process input is dup'd
|
||||
*/
|
||||
void
|
||||
coproc_write_close(fd)
|
||||
int fd;
|
||||
{
|
||||
if (coproc.write >= 0 && fd == coproc.write) {
|
||||
close(coproc.write);
|
||||
coproc.write = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Called to check for existence of/value of the co-process file descriptor.
|
||||
* (Used by check_fd() and by c_read/c_print to deal with -p option).
|
||||
*/
|
||||
int
|
||||
coproc_getfd(mode, emsgp)
|
||||
int mode;
|
||||
const char **emsgp;
|
||||
{
|
||||
int fd = (mode & R_OK) ? coproc.read : coproc.write;
|
||||
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
if (emsgp)
|
||||
*emsgp = "no coprocess";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* called to close file descriptors related to the coprocess (if any)
|
||||
* Should be called with SIGCHLD blocked.
|
||||
*/
|
||||
void
|
||||
coproc_cleanup(reuse)
|
||||
int reuse;
|
||||
{
|
||||
/* This to allow co-processes to share output pipe */
|
||||
if (!reuse || coproc.readw < 0 || coproc.read < 0) {
|
||||
if (coproc.read >= 0) {
|
||||
close(coproc.read);
|
||||
coproc.read = -1;
|
||||
}
|
||||
if (coproc.readw >= 0) {
|
||||
close(coproc.readw);
|
||||
coproc.readw = -1;
|
||||
}
|
||||
}
|
||||
if (coproc.write >= 0) {
|
||||
close(coproc.write);
|
||||
coproc.write = -1;
|
||||
}
|
||||
}
|
||||
#endif /* KSH */
|
||||
|
||||
|
||||
/*
|
||||
* temporary files
|
||||
*/
|
||||
|
||||
struct temp *
|
||||
maketemp(ap, type, tlist)
|
||||
Area *ap;
|
||||
Temp_type type;
|
||||
struct temp **tlist;
|
||||
{
|
||||
#ifndef __NetBSD__
|
||||
static unsigned int inc;
|
||||
#endif
|
||||
struct temp *tp;
|
||||
int len;
|
||||
int fd;
|
||||
char *pathx;
|
||||
const char *dir;
|
||||
|
||||
dir = tmpdir ? tmpdir : "/tmp";
|
||||
/* The 20 + 20 is a paranoid worst case for pid/inc */
|
||||
len = strlen(dir) + 3 + 20 + 20 + 1;
|
||||
tp = (struct temp *) alloc(sizeof(struct temp) + len, ap);
|
||||
tp->name = pathx = (char *) &tp[1];
|
||||
tp->shf = (struct shf *) 0;
|
||||
tp->type = type;
|
||||
#ifdef __NetBSD__
|
||||
shf_snprintf(pathx, len, "%s/shXXXXXXXX", dir);
|
||||
fd = mkstemp(pathx);
|
||||
if (fd >= 0)
|
||||
tp->shf = shf_fdopen(fd, SHF_WR, (struct shf *) 0);
|
||||
#else
|
||||
while (1) {
|
||||
/* Note that temp files need to fit 8.3 DOS limits */
|
||||
shf_snprintf(pathx, len, "%s/sh%05u.%03x",
|
||||
dir, (unsigned) procpid, inc++);
|
||||
/* Mode 0600 to be paranoid, O_TRUNC in case O_EXCL isn't
|
||||
* really there.
|
||||
*/
|
||||
fd = open(pathx, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0600);
|
||||
if (fd >= 0) {
|
||||
tp->shf = shf_fdopen(fd, SHF_WR, (struct shf *) 0);
|
||||
break;
|
||||
}
|
||||
if (errno != EINTR
|
||||
#ifdef EEXIST
|
||||
&& errno != EEXIST
|
||||
#endif /* EEXIST */
|
||||
#ifdef EISDIR
|
||||
&& errno != EISDIR
|
||||
#endif /* EISDIR */
|
||||
)
|
||||
/* Error must be printed by caller: don't know here if
|
||||
* errorf() or bi_errorf() should be used.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
#endif /* __NetBSD__ */
|
||||
tp->pid = procpid;
|
||||
|
||||
tp->next = *tlist;
|
||||
*tlist = tp;
|
||||
|
||||
return tp;
|
||||
}
|
1860
bin/ksh/jobs.c
Normal file
1860
bin/ksh/jobs.c
Normal file
File diff suppressed because it is too large
Load diff
3617
bin/ksh/ksh.Man
Normal file
3617
bin/ksh/ksh.Man
Normal file
File diff suppressed because it is too large
Load diff
27
bin/ksh/ksh_dir.h
Normal file
27
bin/ksh/ksh_dir.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* $NetBSD: ksh_dir.h,v 1.2 1997/01/12 19:11:59 tls Exp $ */
|
||||
|
||||
/* Wrapper around the ugly dir includes/ifdefs */
|
||||
/* $NetBSD: ksh_dir.h,v 1.2 1997/01/12 19:11:59 tls Exp $ */
|
||||
|
||||
#if defined(HAVE_DIRENT_H)
|
||||
# include <dirent.h>
|
||||
# define NLENGTH(dirent) (strlen(dirent->d_name))
|
||||
#else
|
||||
# define dirent direct
|
||||
# define NLENGTH(dirent) (dirent->d_namlen)
|
||||
# ifdef HAVE_SYS_NDIR_H
|
||||
# include <sys/ndir.h>
|
||||
# endif /* HAVE_SYS_NDIR_H */
|
||||
# ifdef HAVE_SYS_DIR_H
|
||||
# include <sys/dir.h>
|
||||
# endif /* HAVE_SYSDIR_H */
|
||||
# ifdef HAVE_NDIR_H
|
||||
# include <ndir.h>
|
||||
# endif /* HAVE_NDIR_H */
|
||||
#endif /* HAVE_DIRENT_H */
|
||||
|
||||
#ifdef OPENDIR_DOES_NONDIR
|
||||
extern DIR *ksh_opendir ARGS((const char *d));
|
||||
#else /* OPENDIR_DOES_NONDIR */
|
||||
# define ksh_opendir(d) opendir(d)
|
||||
#endif /* OPENDIR_DOES_NONDIR */
|
25
bin/ksh/ksh_limval.h
Normal file
25
bin/ksh/ksh_limval.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* $NetBSD: ksh_limval.h,v 1.2 1997/01/12 19:11:59 tls Exp $ */
|
||||
|
||||
/* Wrapper around the values.h/limits.h includes/ifdefs */
|
||||
/* $NetBSD: ksh_limval.h,v 1.2 1997/01/12 19:11:59 tls Exp $ */
|
||||
|
||||
#ifdef HAVE_VALUES_H
|
||||
# include <values.h>
|
||||
#endif /* HAVE_VALUES_H */
|
||||
/* limits.h is included in sh.h */
|
||||
|
||||
#ifndef DMAXEXP
|
||||
# define DMAXEXP 128 /* should be big enough */
|
||||
#endif
|
||||
|
||||
#ifndef BITSPERBYTE
|
||||
# ifdef CHAR_BIT
|
||||
# define BITSPERBYTE CHAR_BIT
|
||||
# else
|
||||
# define BITSPERBYTE 8 /* probably true.. */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef BITS
|
||||
# define BITS(t) (BITSPERBYTE * sizeof(t))
|
||||
#endif
|
60
bin/ksh/ksh_stat.h
Normal file
60
bin/ksh/ksh_stat.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* $NetBSD: ksh_stat.h,v 1.2 1997/01/12 19:12:00 tls Exp $ */
|
||||
|
||||
/* Wrapper around the ugly sys/stat includes/ifdefs */
|
||||
/* $NetBSD: ksh_stat.h,v 1.2 1997/01/12 19:12:00 tls Exp $ */
|
||||
|
||||
/* assumes <sys/types.h> already included */
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef HAVE_LSTAT
|
||||
# define lstat(path, buf) stat(path, buf)
|
||||
#endif /* HAVE_LSTAT */
|
||||
|
||||
#ifdef STAT_MACROS_BROKEN
|
||||
# undef S_ISREG
|
||||
# undef S_ISDIR
|
||||
# undef S_ISCHR
|
||||
# undef S_ISBLK
|
||||
# undef S_ISFIFO
|
||||
# undef S_ISSOCK
|
||||
# undef S_ISLNK
|
||||
#endif /* STAT_MACROS_BROKEN */
|
||||
|
||||
#if !defined(S_ISREG) && defined(S_IFREG)
|
||||
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif /* S_ISREG */
|
||||
#if !defined(S_ISDIR) && defined(S_IFDIR)
|
||||
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif /* S_ISDIR */
|
||||
#if !defined(S_ISCHR) && defined(S_IFCHR)
|
||||
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||
#endif /* S_ISCHR */
|
||||
#if !defined(S_ISBLK) && defined(S_IFBLK)
|
||||
# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
||||
#endif /* S_ISBLK */
|
||||
#if !defined(S_ISFIFO) && defined(S_IFIFO)
|
||||
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
||||
#endif /* S_ISFIFO */
|
||||
#if !defined(S_ISLNK) && defined(S_IFLNK)
|
||||
# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
#endif /* S_ISLNK */
|
||||
#if !defined(S_ISSOCK) && defined(S_IFSOCK)
|
||||
# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||
#endif /* S_ISSOCK */
|
||||
#if !defined(S_ISCDF) && defined(S_CDF)
|
||||
# define S_ISCDF(m) (S_ISDIR(m) && ((m) & S_CDF))
|
||||
#endif /* S_ISSOCK */
|
||||
|
||||
#ifndef S_ISVTX
|
||||
# define S_ISVTX 01000 /* sticky bit */
|
||||
#endif /* S_ISVTX */
|
||||
|
||||
#ifndef S_IXUSR
|
||||
# define S_IXUSR 00100 /* user execute bit */
|
||||
#endif /* S_IXUSR */
|
||||
#ifndef S_IXGRP
|
||||
# define S_IXGRP 00010 /* user execute bit */
|
||||
#endif /* S_IXGRP */
|
||||
#ifndef S_IXOTH
|
||||
# define S_IXOTH 00001 /* user execute bit */
|
||||
#endif /* S_IXOTH */
|
27
bin/ksh/ksh_time.h
Normal file
27
bin/ksh/ksh_time.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* $NetBSD: ksh_time.h,v 1.2 1997/01/12 19:12:01 tls Exp $ */
|
||||
|
||||
#ifndef KSH_TIME_H
|
||||
# define KSH_TIME_H
|
||||
|
||||
/* Wrapper around the ugly time.h,sys/time.h includes/ifdefs */
|
||||
/* $NetBSD: ksh_time.h,v 1.2 1997/01/12 19:12:01 tls Exp $ */
|
||||
|
||||
#ifdef TIME_WITH_SYS_TIME
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
#else /* TIME_WITH_SYS_TIME */
|
||||
# ifdef HAVE_SYS_TIME_H
|
||||
# include <sys/time.h>
|
||||
# else
|
||||
# include <time.h>
|
||||
# endif
|
||||
#endif /* TIME_WITH_SYS_TIME */
|
||||
|
||||
#ifndef TIME_DECLARED
|
||||
extern time_t time ARGS((time_t *));
|
||||
#endif
|
||||
|
||||
#ifndef CLK_TCK
|
||||
# define CLK_TCK 60 /* 60HZ */
|
||||
#endif
|
||||
#endif /* KSH_TIME_H */
|
20
bin/ksh/ksh_times.h
Normal file
20
bin/ksh/ksh_times.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* $NetBSD: ksh_times.h,v 1.2 1997/01/12 19:12:02 tls Exp $ */
|
||||
|
||||
#ifndef KSH_TIMES_H
|
||||
# define KSH_TIMES_H
|
||||
|
||||
/* Needed for clock_t on some systems (ie, NeXT in non-posix mode) */
|
||||
#include "ksh_time.h"
|
||||
|
||||
#include <sys/times.h>
|
||||
|
||||
#ifdef TIMES_BROKEN
|
||||
extern clock_t ksh_times ARGS((struct tms *));
|
||||
#else /* TIMES_BROKEN */
|
||||
# define ksh_times times
|
||||
#endif /* TIMES_BROKEN */
|
||||
|
||||
#ifdef HAVE_TIMES
|
||||
extern clock_t times ARGS((struct tms *));
|
||||
#endif /* HAVE_TIMES */
|
||||
#endif /* KSH_TIMES_H */
|
52
bin/ksh/ksh_wait.h
Normal file
52
bin/ksh/ksh_wait.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* $NetBSD: ksh_wait.h,v 1.2 1997/01/12 19:12:03 tls Exp $ */
|
||||
|
||||
/* Wrapper around the ugly sys/wait includes/ifdefs */
|
||||
/* $NetBSD: ksh_wait.h,v 1.2 1997/01/12 19:12:03 tls Exp $ */
|
||||
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#ifndef POSIX_SYS_WAIT
|
||||
/* Get rid of system macros (which probably use union wait) */
|
||||
# undef WIFCORED
|
||||
# undef WIFEXITED
|
||||
# undef WEXITSTATUS
|
||||
# undef WIFSIGNALED
|
||||
# undef WTERMSIG
|
||||
# undef WIFSTOPPED
|
||||
# undef WSTOPSIG
|
||||
#endif /* POSIX_SYS_WAIT */
|
||||
|
||||
typedef int WAIT_T;
|
||||
|
||||
#ifndef WIFCORED
|
||||
# define WIFCORED(s) ((s) & 0x80)
|
||||
#endif
|
||||
#define WSTATUS(s) (s)
|
||||
|
||||
#ifndef WIFEXITED
|
||||
# define WIFEXITED(s) (((s) & 0xff) == 0)
|
||||
#endif
|
||||
#ifndef WEXITSTATUS
|
||||
# define WEXITSTATUS(s) (((s) >> 8) & 0xff)
|
||||
#endif
|
||||
#ifndef WIFSIGNALED
|
||||
# define WIFSIGNALED(s) (((s) & 0xff) != 0 && ((s) & 0xff) != 0x7f)
|
||||
#endif
|
||||
#ifndef WTERMSIG
|
||||
# define WTERMSIG(s) ((s) & 0x7f)
|
||||
#endif
|
||||
#ifndef WIFSTOPPED
|
||||
# define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
|
||||
#endif
|
||||
#ifndef WSTOPSIG
|
||||
# define WSTOPSIG(s) (((s) >> 8) & 0xff)
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_WAITPID) && defined(HAVE_WAIT3)
|
||||
/* always used with p == -1 */
|
||||
# define ksh_waitpid(p, s, o) wait3((s), (o), (struct rusage *) 0)
|
||||
#else /* !HAVE_WAITPID && HAVE_WAIT3 */
|
||||
# define ksh_waitpid(p, s, o) waitpid((p), (s), (o))
|
||||
#endif /* !HAVE_WAITPID && HAVE_WAIT3 */
|
1398
bin/ksh/lex.c
Normal file
1398
bin/ksh/lex.c
Normal file
File diff suppressed because it is too large
Load diff
133
bin/ksh/lex.h
Normal file
133
bin/ksh/lex.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
/* $NetBSD: lex.h,v 1.7 2005/09/11 22:16:00 christos Exp $ */
|
||||
|
||||
/*
|
||||
* Source input, lexer and parser
|
||||
*/
|
||||
|
||||
/* $Id: lex.h,v 1.7 2005/09/11 22:16:00 christos Exp $ */
|
||||
|
||||
#define IDENT 64
|
||||
|
||||
typedef struct source Source;
|
||||
struct source {
|
||||
const char *str; /* input pointer */
|
||||
int type; /* input type */
|
||||
const char *start; /* start of current buffer */
|
||||
union {
|
||||
char **strv; /* string [] */
|
||||
struct shf *shf; /* shell file */
|
||||
struct tbl *tblp; /* alias (SALIAS) */
|
||||
char *freeme; /* also for SREREAD */
|
||||
} u;
|
||||
char ugbuf[2]; /* buffer for ungetsc() (SREREAD) and
|
||||
* alias (SALIAS) */
|
||||
int line; /* line number */
|
||||
int errline; /* line the error occurred on (0 if not set) */
|
||||
const char *file; /* input file name */
|
||||
int flags; /* SF_* */
|
||||
Area *areap;
|
||||
XString xs; /* input buffer */
|
||||
Source *next; /* stacked source */
|
||||
};
|
||||
|
||||
/* Source.type values */
|
||||
#define SEOF 0 /* input EOF */
|
||||
#define SFILE 1 /* file input */
|
||||
#define SSTDIN 2 /* read stdin */
|
||||
#define SSTRING 3 /* string */
|
||||
#define SWSTR 4 /* string without \n */
|
||||
#define SWORDS 5 /* string[] */
|
||||
#define SWORDSEP 6 /* string[] separator */
|
||||
#define SALIAS 7 /* alias expansion */
|
||||
#define SREREAD 8 /* read ahead to be re-scanned */
|
||||
|
||||
/* Source.flags values */
|
||||
#define SF_ECHO BIT(0) /* echo input to shlout */
|
||||
#define SF_ALIAS BIT(1) /* faking space at end of alias */
|
||||
#define SF_ALIASEND BIT(2) /* faking space at end of alias */
|
||||
#define SF_TTY BIT(3) /* type == SSTDIN & it is a tty */
|
||||
|
||||
/*
|
||||
* states while lexing word
|
||||
*/
|
||||
#define SBASE 0 /* outside any lexical constructs */
|
||||
#define SWORD 1 /* implicit quoting for substitute() */
|
||||
#ifdef KSH
|
||||
#define SLETPAREN 2 /* inside (( )), implicit quoting */
|
||||
#endif /* KSH */
|
||||
#define SSQUOTE 3 /* inside '' */
|
||||
#define SDQUOTE 4 /* inside "" */
|
||||
#define SBRACE 5 /* inside ${} */
|
||||
#define SCSPAREN 6 /* inside $() */
|
||||
#define SBQUOTE 7 /* inside `` */
|
||||
#define SASPAREN 8 /* inside $(( )) */
|
||||
#define SHEREDELIM 9 /* parsing <<,<<- delimiter */
|
||||
#define SHEREDQUOTE 10 /* parsing " in <<,<<- delimiter */
|
||||
#define SPATTERN 11 /* parsing *(...|...) pattern (*+?@!) */
|
||||
#define STBRACE 12 /* parsing ${..[#%]..} */
|
||||
|
||||
typedef union {
|
||||
int i;
|
||||
char *cp;
|
||||
char **wp;
|
||||
struct op *o;
|
||||
struct ioword *iop;
|
||||
} YYSTYPE;
|
||||
|
||||
/* If something is added here, add it to tokentab[] in syn.c as well */
|
||||
#define LWORD 256
|
||||
#define LOGAND 257 /* && */
|
||||
#define LOGOR 258 /* || */
|
||||
#define BREAK 259 /* ;; */
|
||||
#define IF 260
|
||||
#define THEN 261
|
||||
#define ELSE 262
|
||||
#define ELIF 263
|
||||
#define FI 264
|
||||
#define CASE 265
|
||||
#define ESAC 266
|
||||
#define FOR 267
|
||||
#define SELECT 268
|
||||
#define WHILE 269
|
||||
#define UNTIL 270
|
||||
#define DO 271
|
||||
#define DONE 272
|
||||
#define IN 273
|
||||
#define FUNCTION 274
|
||||
#define TIME 275
|
||||
#define REDIR 276
|
||||
#ifdef KSH
|
||||
#define MDPAREN 277 /* (( )) */
|
||||
#endif /* KSH */
|
||||
#define BANG 278 /* ! */
|
||||
#define DBRACKET 279 /* [[ .. ]] */
|
||||
#define COPROC 280 /* |& */
|
||||
#define YYERRCODE 300
|
||||
|
||||
/* flags to yylex */
|
||||
#define CONTIN BIT(0) /* skip new lines to complete command */
|
||||
#define ONEWORD BIT(1) /* single word for substitute() */
|
||||
#define ALIAS BIT(2) /* recognize alias */
|
||||
#define KEYWORD BIT(3) /* recognize keywords */
|
||||
#define LETEXPR BIT(4) /* get expression inside (( )) */
|
||||
#define VARASN BIT(5) /* check for var=word */
|
||||
#define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */
|
||||
#define ESACONLY BIT(7) /* only accept esac keyword */
|
||||
#define CMDWORD BIT(8) /* parsing simple command (alias related) */
|
||||
#define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */
|
||||
#define HEREDOC BIT(10) /* parsing heredoc */
|
||||
|
||||
#define HERES 10 /* max << in line */
|
||||
|
||||
EXTERN Source *source; /* yyparse/yylex source */
|
||||
EXTERN YYSTYPE yylval; /* result from yylex */
|
||||
EXTERN struct ioword *heres [HERES], **herep;
|
||||
EXTERN char ident [IDENT+1];
|
||||
|
||||
#ifdef HISTORY
|
||||
# define HISTORYSIZE 128 /* size of saved history */
|
||||
|
||||
EXTERN char **histlist; /* saved commands */
|
||||
EXTERN char **histptr; /* last history item */
|
||||
EXTERN int histsize; /* history size */
|
||||
#endif /* HISTORY */
|
202
bin/ksh/mail.c
Normal file
202
bin/ksh/mail.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
/* $NetBSD: mail.c,v 1.5 2006/01/15 18:16:30 jschauma Exp $ */
|
||||
|
||||
/*
|
||||
* Mailbox checking code by Robert J. Gibson, adapted for PD ksh by
|
||||
* John R. MacMillan
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: mail.c,v 1.5 2006/01/15 18:16:30 jschauma Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef KSH
|
||||
#include "sh.h"
|
||||
#include "ksh_stat.h"
|
||||
#include "ksh_time.h"
|
||||
|
||||
#define MBMESSAGE "You have mail in $_"
|
||||
|
||||
typedef struct mbox {
|
||||
struct mbox *mb_next; /* next mbox in list */
|
||||
char *mb_path; /* path to mail file */
|
||||
char *mb_msg; /* to announce arrival of new mail */
|
||||
time_t mb_mtime; /* mtime of mail file */
|
||||
} mbox_t;
|
||||
|
||||
/*
|
||||
* $MAILPATH is a linked list of mboxes. $MAIL is a treated as a
|
||||
* special case of $MAILPATH, where the list has only one node. The
|
||||
* same list is used for both since they are exclusive.
|
||||
*/
|
||||
|
||||
static mbox_t *mplist;
|
||||
static mbox_t mbox;
|
||||
static time_t mlastchkd; /* when mail was last checked */
|
||||
static time_t mailcheck_interval;
|
||||
|
||||
static void munset ARGS((mbox_t *mlist)); /* free mlist and mval */
|
||||
static mbox_t * mballoc ARGS((char *p, char *m)); /* allocate a new mbox */
|
||||
static void mprintit ARGS((mbox_t *mbp));
|
||||
|
||||
void
|
||||
mcheck()
|
||||
{
|
||||
register mbox_t *mbp;
|
||||
time_t now;
|
||||
struct tbl *vp;
|
||||
struct stat stbuf;
|
||||
|
||||
now = time((time_t *) 0);
|
||||
if (mlastchkd == 0)
|
||||
mlastchkd = now;
|
||||
if (now - mlastchkd >= mailcheck_interval) {
|
||||
mlastchkd = now;
|
||||
|
||||
if (mplist)
|
||||
mbp = mplist;
|
||||
else if ((vp = global("MAIL")) && (vp->flag & ISSET))
|
||||
mbp = &mbox;
|
||||
else
|
||||
mbp = NULL;
|
||||
|
||||
while (mbp) {
|
||||
if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0
|
||||
&& S_ISREG(stbuf.st_mode))
|
||||
{
|
||||
if (stbuf.st_size
|
||||
&& mbp->mb_mtime != stbuf.st_mtime
|
||||
&& stbuf.st_atime <= stbuf.st_mtime)
|
||||
mprintit(mbp);
|
||||
mbp->mb_mtime = stbuf.st_mtime;
|
||||
} else {
|
||||
/*
|
||||
* Some mail readers remove the mail
|
||||
* file if all mail is read. If file
|
||||
* does not exist, assume this is the
|
||||
* case and set mtime to zero.
|
||||
*/
|
||||
mbp->mb_mtime = 0;
|
||||
}
|
||||
mbp = mbp->mb_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mcset(interval)
|
||||
long interval;
|
||||
{
|
||||
mailcheck_interval = interval;
|
||||
}
|
||||
|
||||
void
|
||||
mbset(p)
|
||||
register char *p;
|
||||
{
|
||||
struct stat stbuf;
|
||||
|
||||
if (mbox.mb_msg)
|
||||
afree((void *)mbox.mb_msg, APERM);
|
||||
if (mbox.mb_path)
|
||||
afree((void *)mbox.mb_path, APERM);
|
||||
/* Save a copy to protect from export (which munges the string) */
|
||||
mbox.mb_path = str_save(p, APERM);
|
||||
mbox.mb_msg = NULL;
|
||||
if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
|
||||
mbox.mb_mtime = stbuf.st_mtime;
|
||||
else
|
||||
mbox.mb_mtime = 0;
|
||||
}
|
||||
|
||||
void
|
||||
mpset(mptoparse)
|
||||
register char *mptoparse;
|
||||
{
|
||||
register mbox_t *mbp;
|
||||
register char *mpath, *mmsg, *mval;
|
||||
char *p;
|
||||
|
||||
munset( mplist );
|
||||
mplist = NULL;
|
||||
mval = str_save(mptoparse, APERM);
|
||||
while (mval) {
|
||||
mpath = mval;
|
||||
if ((mval = strchr(mval, PATHSEP)) != NULL) {
|
||||
*mval = '\0', mval++;
|
||||
}
|
||||
/* POSIX/bourne-shell say file%message */
|
||||
for (p = mpath; (mmsg = strchr(p, '%')); ) {
|
||||
/* a literal percent? (POSIXism) */
|
||||
if (mmsg[-1] == '\\') {
|
||||
/* use memmove() to avoid overlap problems */
|
||||
memmove(mmsg - 1, mmsg, strlen(mmsg) + 1);
|
||||
p = mmsg + 1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* at&t ksh says file?message */
|
||||
if (!mmsg && !Flag(FPOSIX))
|
||||
mmsg = strchr(mpath, '?');
|
||||
if (mmsg) {
|
||||
*mmsg = '\0';
|
||||
mmsg++;
|
||||
}
|
||||
mbp = mballoc(mpath, mmsg);
|
||||
mbp->mb_next = mplist;
|
||||
mplist = mbp;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
munset(mlist)
|
||||
register mbox_t *mlist;
|
||||
{
|
||||
register mbox_t *mbp;
|
||||
|
||||
while (mlist != NULL) {
|
||||
mbp = mlist;
|
||||
mlist = mbp->mb_next;
|
||||
if (!mlist)
|
||||
afree((void *)mbp->mb_path, APERM);
|
||||
afree((void *)mbp, APERM);
|
||||
}
|
||||
}
|
||||
|
||||
static mbox_t *
|
||||
mballoc(p, m)
|
||||
char *p;
|
||||
char *m;
|
||||
{
|
||||
struct stat stbuf;
|
||||
register mbox_t *mbp;
|
||||
|
||||
mbp = (mbox_t *)alloc(sizeof(mbox_t), APERM);
|
||||
mbp->mb_next = NULL;
|
||||
mbp->mb_path = p;
|
||||
mbp->mb_msg = m;
|
||||
if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
|
||||
mbp->mb_mtime = stbuf.st_mtime;
|
||||
else
|
||||
mbp->mb_mtime = 0;
|
||||
return(mbp);
|
||||
}
|
||||
|
||||
static void
|
||||
mprintit( mbp )
|
||||
mbox_t *mbp;
|
||||
{
|
||||
struct tbl *vp;
|
||||
|
||||
/* Ignore setstr errors here (arbitrary) */
|
||||
setstr((vp = local("_", FALSE)), mbp->mb_path, KSH_RETURN_ERROR);
|
||||
|
||||
shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0));
|
||||
|
||||
unset(vp, 0);
|
||||
}
|
||||
#endif /* KSH */
|
869
bin/ksh/main.c
Normal file
869
bin/ksh/main.c
Normal file
|
@ -0,0 +1,869 @@
|
|||
/* $NetBSD: main.c,v 1.15 2011/10/16 17:12:11 joerg Exp $ */
|
||||
|
||||
/*
|
||||
* startup, main loop, environments and error handling
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
#include <locale.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: main.c,v 1.15 2011/10/16 17:12:11 joerg Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#define EXTERN /* define EXTERNs in sh.h */
|
||||
|
||||
#include "sh.h"
|
||||
#include "ksh_stat.h"
|
||||
#include "ksh_time.h"
|
||||
|
||||
extern char **environ;
|
||||
|
||||
/*
|
||||
* global data
|
||||
*/
|
||||
|
||||
static void reclaim ARGS((void));
|
||||
static void remove_temps ARGS((struct temp *tp));
|
||||
static int is_restricted ARGS((char *name));
|
||||
|
||||
/*
|
||||
* shell initialization
|
||||
*/
|
||||
|
||||
static const char initifs[] = "IFS= \t\n";
|
||||
|
||||
static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }";
|
||||
|
||||
static const char version_param[] =
|
||||
#ifdef KSH
|
||||
"KSH_VERSION"
|
||||
#else /* KSH */
|
||||
"SH_VERSION"
|
||||
#endif /* KSH */
|
||||
;
|
||||
|
||||
static const char *const initcoms [] = {
|
||||
"typeset", "-x", "SHELL", "PATH", "HOME", NULL,
|
||||
"typeset", "-r", version_param, NULL,
|
||||
"typeset", "-i", "PPID", NULL,
|
||||
"typeset", "-i", "OPTIND=1", NULL,
|
||||
#ifdef KSH
|
||||
"eval", "typeset -i RANDOM MAILCHECK=\"${MAILCHECK-600}\" SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL,
|
||||
#endif /* KSH */
|
||||
"alias",
|
||||
/* Standard ksh aliases */
|
||||
"hash=alias -t", /* not "alias -t --": hash -r needs to work */
|
||||
"type=whence -v",
|
||||
#ifdef JOBS
|
||||
"stop=kill -STOP",
|
||||
"suspend=kill -STOP $$",
|
||||
#endif
|
||||
#ifdef KSH
|
||||
"autoload=typeset -fu",
|
||||
"functions=typeset -f",
|
||||
# ifdef HISTORY
|
||||
"history=fc -l",
|
||||
# endif /* HISTORY */
|
||||
"integer=typeset -i",
|
||||
"nohup=nohup ",
|
||||
"local=typeset",
|
||||
"r=fc -e -",
|
||||
#endif /* KSH */
|
||||
#ifdef KSH
|
||||
/* Aliases that are builtin commands in at&t */
|
||||
"login=exec login",
|
||||
#ifndef __NetBSD__
|
||||
"newgrp=exec newgrp",
|
||||
#endif /* __NetBSD__ */
|
||||
#endif /* KSH */
|
||||
NULL,
|
||||
/* this is what at&t ksh seems to track, with the addition of emacs */
|
||||
"alias", "-tU",
|
||||
"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
|
||||
"mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who",
|
||||
NULL,
|
||||
#ifdef EXTRA_INITCOMS
|
||||
EXTRA_INITCOMS, NULL,
|
||||
#endif /* EXTRA_INITCOMS */
|
||||
NULL
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
register int i;
|
||||
int argi;
|
||||
Source *s;
|
||||
struct block *l;
|
||||
int restricted, errexit;
|
||||
char **wp;
|
||||
struct env env;
|
||||
pid_t ppid;
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
chmem_set_defaults("ct", 1);
|
||||
/* chmem_push("+c", 1); */
|
||||
#endif /* MEM_DEBUG */
|
||||
|
||||
#ifdef OS2
|
||||
setmode (0, O_BINARY);
|
||||
setmode (1, O_TEXT);
|
||||
#endif
|
||||
|
||||
/* make sure argv[] is sane */
|
||||
if (!*argv) {
|
||||
static const char *empty_argv[] = {
|
||||
"pdksh", (char *) 0
|
||||
};
|
||||
|
||||
argv = (char **)__UNCONST(empty_argv);
|
||||
argc = 1;
|
||||
}
|
||||
kshname = *argv;
|
||||
|
||||
ainit(&aperm); /* initialize permanent Area */
|
||||
|
||||
/* set up base environment */
|
||||
memset(&env, 0, sizeof(env));
|
||||
env.type = E_NONE;
|
||||
ainit(&env.area);
|
||||
e = &env;
|
||||
newblock(); /* set up global l->vars and l->funs */
|
||||
|
||||
/* Do this first so output routines (eg, errorf, shellf) can work */
|
||||
initio();
|
||||
|
||||
initvar();
|
||||
|
||||
initctypes();
|
||||
|
||||
inittraps();
|
||||
|
||||
#ifdef KSH
|
||||
coproc_init();
|
||||
#endif /* KSH */
|
||||
|
||||
/* set up variable and command dictionaries */
|
||||
tinit(&taliases, APERM, 0);
|
||||
tinit(&aliases, APERM, 0);
|
||||
tinit(&homedirs, APERM, 0);
|
||||
|
||||
/* define shell keywords */
|
||||
initkeywords();
|
||||
|
||||
/* define built-in commands */
|
||||
tinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */
|
||||
for (i = 0; shbuiltins[i].name != NULL; i++)
|
||||
builtin(shbuiltins[i].name, shbuiltins[i].func);
|
||||
for (i = 0; kshbuiltins[i].name != NULL; i++)
|
||||
builtin(kshbuiltins[i].name, kshbuiltins[i].func);
|
||||
|
||||
init_histvec();
|
||||
|
||||
def_path = DEFAULT__PATH;
|
||||
#if defined(HAVE_CONFSTR) && defined(_CS_PATH)
|
||||
{
|
||||
size_t len = confstr(_CS_PATH, (char *) 0, 0);
|
||||
char *new;
|
||||
|
||||
if (len > 0) {
|
||||
confstr(_CS_PATH, new = alloc(len + 1, APERM), len + 1);
|
||||
def_path = new;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_CONFSTR && _CS_PATH */
|
||||
|
||||
/* Set PATH to def_path (will set the path global variable).
|
||||
* (import of environment below will probably change this setting).
|
||||
*/
|
||||
{
|
||||
struct tbl *vp = global("PATH");
|
||||
/* setstr can't fail here */
|
||||
setstr(vp, def_path, KSH_RETURN_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/* Turn on nohup by default for now - will change to off
|
||||
* by default once people are aware of its existence
|
||||
* (at&t ksh does not have a nohup option - it always sends
|
||||
* the hup).
|
||||
*/
|
||||
Flag(FNOHUP) = 1;
|
||||
|
||||
/* Turn on brace expansion by default. At&t ksh's that have
|
||||
* alternation always have it on. BUT, posix doesn't have
|
||||
* brace expansion, so set this before setting up FPOSIX
|
||||
* (change_flag() clears FBRACEEXPAND when FPOSIX is set).
|
||||
*/
|
||||
#ifdef BRACE_EXPAND
|
||||
Flag(FBRACEEXPAND) = 1;
|
||||
#endif /* BRACE_EXPAND */
|
||||
|
||||
/* set posix flag just before environment so that it will have
|
||||
* exactly the same effect as the POSIXLY_CORRECT environment
|
||||
* variable. If this needs to be done sooner to ensure correct posix
|
||||
* operation, an initial scan of the environment will also have
|
||||
* done sooner.
|
||||
*/
|
||||
#ifdef POSIXLY_CORRECT
|
||||
change_flag(FPOSIX, OF_SPECIAL, 1);
|
||||
#endif /* POSIXLY_CORRECT */
|
||||
|
||||
/* Set edit mode to emacs by default, may be overridden
|
||||
* by the environment or the user. Also, we want tab completion
|
||||
* on in vi by default. */
|
||||
#if defined(EDIT) && defined(EMACS)
|
||||
change_flag(FEMACS, OF_SPECIAL, 1);
|
||||
#endif /* EDIT && EMACS */
|
||||
#if defined(EDIT) && defined(VI)
|
||||
Flag(FVITABCOMPLETE) = 1;
|
||||
#endif /* EDIT && VI */
|
||||
|
||||
/* import environment */
|
||||
if (environ != NULL)
|
||||
for (wp = environ; *wp != NULL; wp++)
|
||||
typeset(*wp, IMPORT|EXPORT, 0, 0, 0);
|
||||
|
||||
kshpid = procpid = getpid();
|
||||
typeset(initifs, 0, 0, 0, 0); /* for security */
|
||||
|
||||
/* assign default shell variable values */
|
||||
substitute(initsubs, 0);
|
||||
|
||||
/* Figure out the current working directory and set $PWD */
|
||||
{
|
||||
struct stat s_pwd, s_dot;
|
||||
struct tbl *pwd_v = global("PWD");
|
||||
char *pwd = str_val(pwd_v);
|
||||
char *pwdx = pwd;
|
||||
|
||||
/* Try to use existing $PWD if it is valid */
|
||||
if (!ISABSPATH(pwd)
|
||||
|| stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0
|
||||
|| s_pwd.st_dev != s_dot.st_dev
|
||||
|| s_pwd.st_ino != s_dot.st_ino)
|
||||
pwdx = (char *) 0;
|
||||
set_current_wd(pwdx);
|
||||
if (current_wd[0])
|
||||
simplify_path(current_wd);
|
||||
/* Only set pwd if we know where we are or if it had a
|
||||
* bogus value
|
||||
*/
|
||||
if (current_wd[0] || pwd != null)
|
||||
/* setstr can't fail here */
|
||||
setstr(pwd_v, current_wd, KSH_RETURN_ERROR);
|
||||
}
|
||||
ppid = getppid();
|
||||
setint(global("PPID"), (long) ppid);
|
||||
#ifdef KSH
|
||||
setint(global("RANDOM"), (long) (time((time_t *)0) * kshpid * ppid));
|
||||
#endif /* KSH */
|
||||
/* setstr can't fail here */
|
||||
setstr(global(version_param), ksh_version, KSH_RETURN_ERROR);
|
||||
|
||||
/* execute initialization statements */
|
||||
for (wp = (char**)__UNCONST(initcoms); *wp != NULL; wp++) {
|
||||
shcomexec(wp);
|
||||
for (; *wp != NULL; wp++)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
ksheuid = geteuid();
|
||||
safe_prompt = ksheuid ? "$ " : "# ";
|
||||
{
|
||||
struct tbl *vp = global("PS1");
|
||||
|
||||
/* Set PS1 if it isn't set, or we are root and prompt doesn't
|
||||
* contain a #.
|
||||
*/
|
||||
if (!(vp->flag & ISSET)
|
||||
|| (!ksheuid && !strchr(str_val(vp), '#')))
|
||||
/* setstr can't fail here */
|
||||
setstr(vp, safe_prompt, KSH_RETURN_ERROR);
|
||||
}
|
||||
|
||||
/* Set this before parsing arguments */
|
||||
Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid();
|
||||
|
||||
/* this to note if monitor is set on command line (see below) */
|
||||
Flag(FMONITOR) = 127;
|
||||
argi = parse_args(argv, OF_CMDLINE, (int *) 0);
|
||||
if (argi < 0) {
|
||||
exit(1);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
if (Flag(FCOMMAND)) {
|
||||
s = pushs(SSTRING, ATEMP);
|
||||
if (!(s->start = s->str = argv[argi++]))
|
||||
errorf("-c requires an argument");
|
||||
if (argv[argi])
|
||||
kshname = argv[argi++];
|
||||
} else if (argi < argc && !Flag(FSTDIN)) {
|
||||
s = pushs(SFILE, ATEMP);
|
||||
#ifdef OS2
|
||||
/* a bug in os2 extproc shell processing doesn't
|
||||
* pass full pathnames so we have to search for it.
|
||||
* This changes the behavior of 'ksh arg' to search
|
||||
* the users search path but it can't be helped.
|
||||
*/
|
||||
s->file = search(argv[argi++], path, R_OK, (int *) 0);
|
||||
if (!s->file || !*s->file)
|
||||
s->file = argv[argi - 1];
|
||||
#else
|
||||
s->file = argv[argi++];
|
||||
#endif /* OS2 */
|
||||
s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
|
||||
if (s->u.shf == NULL) {
|
||||
exstat = 127; /* POSIX */
|
||||
errorf("%s: %s", s->file, strerror(errno));
|
||||
}
|
||||
kshname = s->file;
|
||||
} else {
|
||||
Flag(FSTDIN) = 1;
|
||||
s = pushs(SSTDIN, ATEMP);
|
||||
s->file = "<stdin>";
|
||||
s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
|
||||
(struct shf *) 0);
|
||||
if (isatty(0) && isatty(2)) {
|
||||
Flag(FTALKING) = Flag(FTALKING_I) = 1;
|
||||
/* The following only if isatty(0) */
|
||||
s->flags |= SF_TTY;
|
||||
s->u.shf->flags |= SHF_INTERRUPT;
|
||||
s->file = (char *) 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* This bizarreness is mandated by POSIX */
|
||||
{
|
||||
struct stat s_stdin;
|
||||
|
||||
if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) &&
|
||||
Flag(FTALKING))
|
||||
reset_nonblock(0);
|
||||
}
|
||||
|
||||
/* initialize job control */
|
||||
i = Flag(FMONITOR) != 127;
|
||||
Flag(FMONITOR) = 0;
|
||||
j_init(i);
|
||||
#ifdef EDIT
|
||||
/* Do this after j_init(), as tty_fd is not initialized 'til then */
|
||||
if (Flag(FTALKING))
|
||||
x_init();
|
||||
#endif
|
||||
|
||||
l = e->loc;
|
||||
l->argv = &argv[argi - 1];
|
||||
l->argc = argc - argi;
|
||||
l->argv[0] = (char *)__UNCONST(kshname);
|
||||
getopts_reset(1);
|
||||
|
||||
/* Disable during .profile/ENV reading */
|
||||
restricted = Flag(FRESTRICTED);
|
||||
Flag(FRESTRICTED) = 0;
|
||||
errexit = Flag(FERREXIT);
|
||||
Flag(FERREXIT) = 0;
|
||||
|
||||
/* Do this before profile/$ENV so that if it causes problems in them,
|
||||
* user will know why things broke.
|
||||
*/
|
||||
if (!current_wd[0] && Flag(FTALKING))
|
||||
warningf(FALSE, "Cannot determine current working directory");
|
||||
|
||||
if (Flag(FLOGIN)) {
|
||||
#ifdef OS2
|
||||
char *profile;
|
||||
|
||||
/* Try to find a profile - first see if $INIT has a value,
|
||||
* then try /etc/profile.ksh, then c:/usr/etc/profile.ksh.
|
||||
*/
|
||||
if (!Flag(FPRIVILEGED)
|
||||
&& strcmp(profile = substitute("$INIT/profile.ksh", 0),
|
||||
"/profile.ksh"))
|
||||
include(profile, 0, (char **) 0, 1);
|
||||
else if (include("/etc/profile.ksh", 0, (char **) 0, 1) < 0)
|
||||
include("c:/usr/etc/profile.ksh", 0, (char **) 0, 1);
|
||||
if (!Flag(FPRIVILEGED))
|
||||
include(substitute("$HOME/profile.ksh", 0), 0,
|
||||
(char **) 0, 1);
|
||||
#else /* OS2 */
|
||||
include(KSH_SYSTEM_PROFILE, 0, (char **) 0, 1);
|
||||
if (!Flag(FPRIVILEGED))
|
||||
include(substitute("$HOME/.profile", 0), 0,
|
||||
(char **) 0, 1);
|
||||
#endif /* OS2 */
|
||||
}
|
||||
|
||||
if (Flag(FPRIVILEGED))
|
||||
include("/etc/suid_profile", 0, (char **) 0, 1);
|
||||
else {
|
||||
char *env_file;
|
||||
|
||||
#ifndef KSH
|
||||
if (!Flag(FPOSIX))
|
||||
env_file = null;
|
||||
else
|
||||
#endif /* !KSH */
|
||||
/* include $ENV */
|
||||
env_file = str_val(global("ENV"));
|
||||
|
||||
#ifdef DEFAULT_ENV
|
||||
/* If env isn't set, include default environment */
|
||||
if (env_file == null)
|
||||
env_file = __UNCONST(DEFAULT_ENV);
|
||||
#endif /* DEFAULT_ENV */
|
||||
env_file = substitute(env_file, DOTILDE);
|
||||
if (*env_file != '\0')
|
||||
include(env_file, 0, (char **) 0, 1);
|
||||
#ifdef OS2
|
||||
else if (Flag(FTALKING))
|
||||
include(substitute("$HOME/kshrc.ksh", 0), 0,
|
||||
(char **) 0, 1);
|
||||
#endif /* OS2 */
|
||||
}
|
||||
|
||||
if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL"))))
|
||||
restricted = 1;
|
||||
if (restricted) {
|
||||
static const char *const restr_com[] = {
|
||||
"typeset", "-r", "PATH",
|
||||
"ENV", "SHELL",
|
||||
(char *) 0
|
||||
};
|
||||
shcomexec((char **)__UNCONST(restr_com));
|
||||
/* After typeset command... */
|
||||
Flag(FRESTRICTED) = 1;
|
||||
}
|
||||
if (errexit)
|
||||
Flag(FERREXIT) = 1;
|
||||
|
||||
if (Flag(FTALKING)) {
|
||||
hist_init(s);
|
||||
#ifdef KSH
|
||||
alarm_init();
|
||||
#endif /* KSH */
|
||||
} else
|
||||
Flag(FTRACKALL) = 1; /* set after ENV */
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
shell(s, TRUE); /* doesn't return */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
include(name, argc, argv, intr_ok)
|
||||
const char *name;
|
||||
int argc;
|
||||
char **argv;
|
||||
int intr_ok;
|
||||
{
|
||||
register Source *volatile s = NULL;
|
||||
struct shf *shf;
|
||||
char **volatile old_argv;
|
||||
volatile int old_argc;
|
||||
int i;
|
||||
|
||||
shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
|
||||
if (shf == NULL)
|
||||
return -1;
|
||||
|
||||
if (argv) {
|
||||
old_argv = e->loc->argv;
|
||||
old_argc = e->loc->argc;
|
||||
} else {
|
||||
old_argv = (char **) 0;
|
||||
old_argc = 0;
|
||||
}
|
||||
newenv(E_INCL);
|
||||
i = ksh_sigsetjmp(e->jbuf, 0);
|
||||
if (i) {
|
||||
if (s) /* Do this before quitenv(), which frees the memory */
|
||||
shf_close(s->u.shf);
|
||||
quitenv();
|
||||
if (old_argv) {
|
||||
e->loc->argv = old_argv;
|
||||
e->loc->argc = old_argc;
|
||||
}
|
||||
switch (i) {
|
||||
case LRETURN:
|
||||
case LERROR:
|
||||
return exstat & 0xff; /* see below */
|
||||
case LINTR:
|
||||
/* intr_ok is set if we are including .profile or $ENV.
|
||||
* If user ^C's out, we don't want to kill the shell...
|
||||
*/
|
||||
if (intr_ok && (exstat - 128) != SIGTERM)
|
||||
return 1;
|
||||
/* fall through... */
|
||||
case LEXIT:
|
||||
case LLEAVE:
|
||||
case LSHELL:
|
||||
unwind(i);
|
||||
/*NOREACHED*/
|
||||
default:
|
||||
internal_errorf(1, "include: %d", i);
|
||||
/*NOREACHED*/
|
||||
}
|
||||
}
|
||||
if (argv) {
|
||||
e->loc->argv = argv;
|
||||
e->loc->argc = argc;
|
||||
}
|
||||
s = pushs(SFILE, ATEMP);
|
||||
s->u.shf = shf;
|
||||
s->file = str_save(name, ATEMP);
|
||||
i = shell(s, FALSE);
|
||||
shf_close(s->u.shf);
|
||||
quitenv();
|
||||
if (old_argv) {
|
||||
e->loc->argv = old_argv;
|
||||
e->loc->argc = old_argc;
|
||||
}
|
||||
return i & 0xff; /* & 0xff to ensure value not -1 */
|
||||
}
|
||||
|
||||
int
|
||||
command(comm)
|
||||
const char *comm;
|
||||
{
|
||||
register Source *s;
|
||||
int r;
|
||||
|
||||
s = pushs(SSTRING, ATEMP);
|
||||
s->start = s->str = comm;
|
||||
r = shell(s, FALSE);
|
||||
afree(s, ATEMP);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* run the commands from the input source, returning status.
|
||||
*/
|
||||
int
|
||||
shell(s, toplevel)
|
||||
Source *volatile s; /* input source */
|
||||
int volatile toplevel;
|
||||
{
|
||||
struct op *t;
|
||||
volatile int wastty = s->flags & SF_TTY;
|
||||
volatile int attempts = 13;
|
||||
volatile int interactive = Flag(FTALKING) && toplevel;
|
||||
Source *volatile old_source = source;
|
||||
int i;
|
||||
|
||||
newenv(E_PARSE);
|
||||
if (interactive)
|
||||
really_exit = 0;
|
||||
i = ksh_sigsetjmp(e->jbuf, 0);
|
||||
if (i) {
|
||||
switch (i) {
|
||||
case LINTR: /* we get here if SIGINT not caught or ignored */
|
||||
case LERROR:
|
||||
case LSHELL:
|
||||
if (interactive) {
|
||||
if (i == LINTR)
|
||||
shellf("%s", newline);
|
||||
/* Reset any eof that was read as part of a
|
||||
* multiline command.
|
||||
*/
|
||||
if (Flag(FIGNOREEOF) && s->type == SEOF
|
||||
&& wastty)
|
||||
s->type = SSTDIN;
|
||||
/* Used by exit command to get back to
|
||||
* top level shell. Kind of strange since
|
||||
* interactive is set if we are reading from
|
||||
* a tty, but to have stopped jobs, one only
|
||||
* needs FMONITOR set (not FTALKING/SF_TTY)...
|
||||
*/
|
||||
/* toss any input we have so far */
|
||||
s->start = s->str = null;
|
||||
break;
|
||||
}
|
||||
/* fall through... */
|
||||
case LEXIT:
|
||||
case LLEAVE:
|
||||
case LRETURN:
|
||||
source = old_source;
|
||||
quitenv();
|
||||
unwind(i); /* keep on going */
|
||||
/*NOREACHED*/
|
||||
default:
|
||||
source = old_source;
|
||||
quitenv();
|
||||
internal_errorf(1, "shell: %d", i);
|
||||
/*NOREACHED*/
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (trap)
|
||||
runtraps(0);
|
||||
|
||||
if (s->next == NULL) {
|
||||
if (Flag(FVERBOSE))
|
||||
s->flags |= SF_ECHO;
|
||||
else
|
||||
s->flags &= ~SF_ECHO;
|
||||
}
|
||||
|
||||
if (interactive) {
|
||||
j_notify();
|
||||
#ifdef KSH
|
||||
mcheck();
|
||||
#endif /* KSH */
|
||||
set_prompt(PS1, s);
|
||||
}
|
||||
|
||||
t = compile(s);
|
||||
if (t != NULL && t->type == TEOF) {
|
||||
if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
|
||||
shellf("Use `exit' to leave ksh\n");
|
||||
s->type = SSTDIN;
|
||||
} else if (wastty && !really_exit
|
||||
&& j_stopped_running())
|
||||
{
|
||||
really_exit = 1;
|
||||
s->type = SSTDIN;
|
||||
} else {
|
||||
/* this for POSIX, which says EXIT traps
|
||||
* shall be taken in the environment
|
||||
* immediately after the last command
|
||||
* executed.
|
||||
*/
|
||||
if (toplevel)
|
||||
unwind(LEXIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
|
||||
exstat = execute(t, 0);
|
||||
|
||||
if (t != NULL && t->type != TEOF && interactive && really_exit)
|
||||
really_exit = 0;
|
||||
|
||||
reclaim();
|
||||
}
|
||||
quitenv();
|
||||
source = old_source;
|
||||
return exstat;
|
||||
}
|
||||
|
||||
/* return to closest error handler or shell(), exit if none found */
|
||||
void
|
||||
unwind(i)
|
||||
int i;
|
||||
{
|
||||
/* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
|
||||
if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR)
|
||||
&& sigtraps[SIGEXIT_].trap))
|
||||
{
|
||||
runtrap(&sigtraps[SIGEXIT_]);
|
||||
i = LLEAVE;
|
||||
} else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
|
||||
runtrap(&sigtraps[SIGERR_]);
|
||||
i = LLEAVE;
|
||||
}
|
||||
while (1) {
|
||||
switch (e->type) {
|
||||
case E_PARSE:
|
||||
case E_FUNC:
|
||||
case E_INCL:
|
||||
case E_LOOP:
|
||||
case E_ERRH:
|
||||
ksh_siglongjmp(e->jbuf, i);
|
||||
/*NOTREACHED*/
|
||||
|
||||
case E_NONE:
|
||||
if (i == LINTR)
|
||||
e->flags |= EF_FAKE_SIGDIE;
|
||||
/* Fall through... */
|
||||
|
||||
default:
|
||||
quitenv();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
newenv(type)
|
||||
int type;
|
||||
{
|
||||
register struct env *ep;
|
||||
|
||||
ep = (struct env *) alloc(sizeof(*ep), ATEMP);
|
||||
ep->type = type;
|
||||
ep->flags = 0;
|
||||
ainit(&ep->area);
|
||||
ep->loc = e->loc;
|
||||
ep->savefd = NULL;
|
||||
ep->oenv = e;
|
||||
ep->temps = NULL;
|
||||
e = ep;
|
||||
}
|
||||
|
||||
void
|
||||
quitenv()
|
||||
{
|
||||
register struct env *ep = e;
|
||||
register int fd;
|
||||
|
||||
if (ep->oenv && ep->oenv->loc != ep->loc)
|
||||
popblock();
|
||||
if (ep->savefd != NULL) {
|
||||
for (fd = 0; fd < NUFILE; fd++)
|
||||
/* if ep->savefd[fd] < 0, means fd was closed */
|
||||
if (ep->savefd[fd])
|
||||
restfd(fd, ep->savefd[fd]);
|
||||
if (ep->savefd[2]) /* Clear any write errors */
|
||||
shf_reopen(2, SHF_WR, shl_out);
|
||||
}
|
||||
reclaim();
|
||||
|
||||
/* Bottom of the stack.
|
||||
* Either main shell is exiting or cleanup_parents_env() was called.
|
||||
*/
|
||||
if (ep->oenv == NULL) {
|
||||
if (ep->type == E_NONE) { /* Main shell exiting? */
|
||||
if (Flag(FTALKING))
|
||||
hist_finish();
|
||||
j_exit();
|
||||
if (ep->flags & EF_FAKE_SIGDIE) {
|
||||
int sig = exstat - 128;
|
||||
|
||||
/* ham up our death a bit (at&t ksh
|
||||
* only seems to do this for SIGTERM)
|
||||
* Don't do it for SIGQUIT, since we'd
|
||||
* dump a core..
|
||||
*/
|
||||
if (sig == SIGINT || sig == SIGTERM) {
|
||||
setsig(&sigtraps[sig], SIG_DFL,
|
||||
SS_RESTORE_CURR|SS_FORCE);
|
||||
kill(0, sig);
|
||||
}
|
||||
}
|
||||
#ifdef MEM_DEBUG
|
||||
chmem_allfree();
|
||||
#endif /* MEM_DEBUG */
|
||||
}
|
||||
exit(exstat);
|
||||
}
|
||||
|
||||
e = e->oenv;
|
||||
afree(ep, ATEMP);
|
||||
}
|
||||
|
||||
/* Called after a fork to cleanup stuff left over from parents environment */
|
||||
void
|
||||
cleanup_parents_env()
|
||||
{
|
||||
struct env *ep;
|
||||
int fd;
|
||||
|
||||
/* Don't clean up temporary files - parent will probably need them.
|
||||
* Also, can't easily reclaim memory since variables, etc. could be
|
||||
* anywhere.
|
||||
*/
|
||||
|
||||
/* close all file descriptors hiding in savefd */
|
||||
for (ep = e; ep; ep = ep->oenv) {
|
||||
if (ep->savefd) {
|
||||
for (fd = 0; fd < NUFILE; fd++)
|
||||
if (ep->savefd[fd] > 0)
|
||||
close(ep->savefd[fd]);
|
||||
afree(ep->savefd, &ep->area);
|
||||
ep->savefd = (short *) 0;
|
||||
}
|
||||
}
|
||||
e->oenv = (struct env *) 0;
|
||||
}
|
||||
|
||||
/* Called just before an execve cleanup stuff temporary files */
|
||||
void
|
||||
cleanup_proc_env()
|
||||
{
|
||||
struct env *ep;
|
||||
|
||||
for (ep = e; ep; ep = ep->oenv)
|
||||
remove_temps(ep->temps);
|
||||
}
|
||||
|
||||
/* remove temp files and free ATEMP Area */
|
||||
static void
|
||||
reclaim()
|
||||
{
|
||||
remove_temps(e->temps);
|
||||
e->temps = NULL;
|
||||
afreeall(&e->area);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_temps(tp)
|
||||
struct temp *tp;
|
||||
{
|
||||
#ifdef OS2
|
||||
static struct temp *delayed_remove;
|
||||
struct temp *t, **tprev;
|
||||
|
||||
if (delayed_remove) {
|
||||
for (tprev = &delayed_remove, t = delayed_remove; t; t = *tprev)
|
||||
/* No need to check t->pid here... */
|
||||
if (unlink(t->name) >= 0 || errno == ENOENT) {
|
||||
*tprev = t->next;
|
||||
afree(t, APERM);
|
||||
} else
|
||||
tprev = &t->next;
|
||||
}
|
||||
#endif /* OS2 */
|
||||
|
||||
for (; tp != NULL; tp = tp->next)
|
||||
if (tp->pid == procpid) {
|
||||
#ifdef OS2
|
||||
/* OS/2 (and dos) do not allow files that are currently
|
||||
* open to be removed, so we cache it away for future
|
||||
* removal.
|
||||
* XXX should only do this if errno
|
||||
* is Efile-still-open-can't-remove
|
||||
* (but I don't know what that is...)
|
||||
*/
|
||||
if (unlink(tp->name) < 0 && errno != ENOENT) {
|
||||
t = (struct temp *) alloc(
|
||||
sizeof(struct temp) + strlen(tp->name) + 1,
|
||||
APERM);
|
||||
memset(t, 0, sizeof(struct temp));
|
||||
t->name = (char *) &t[1];
|
||||
strlcpy(t->name, tp->name, strlen(tp->name) + 1);
|
||||
t->next = delayed_remove;
|
||||
delayed_remove = t;
|
||||
}
|
||||
#else /* OS2 */
|
||||
unlink(tp->name);
|
||||
#endif /* OS2 */
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if name refers to a restricted shell */
|
||||
static int
|
||||
is_restricted(name)
|
||||
char *name;
|
||||
{
|
||||
char *p;
|
||||
|
||||
if ((p = ksh_strrchr_dirsep(name)))
|
||||
name = p;
|
||||
/* accepts rsh, rksh, rpdksh, pdrksh, etc. */
|
||||
return (p = strchr(name, 'r')) && strstr(p, "sh");
|
||||
}
|
||||
|
||||
void
|
||||
aerror(ap, msg)
|
||||
Area *ap;
|
||||
const char *msg;
|
||||
{
|
||||
internal_errorf(1, "alloc: %s", msg);
|
||||
errorf("%s", null); /* this is never executed - keeps gcc quiet */
|
||||
/*NOTREACHED*/
|
||||
}
|
1364
bin/ksh/misc.c
Normal file
1364
bin/ksh/misc.c
Normal file
File diff suppressed because it is too large
Load diff
46
bin/ksh/mkman
Executable file
46
bin/ksh/mkman
Executable file
|
@ -0,0 +1,46 @@
|
|||
#!/bin/sh
|
||||
|
||||
: ${AWK:=awk}
|
||||
|
||||
verbose=no
|
||||
|
||||
if [ X"$1" = X-v ] ; then
|
||||
verbose=yes
|
||||
shift
|
||||
fi
|
||||
if [ $# != 2 ] ; then
|
||||
echo "usage: $0 [-v] which-shell ksh.Man-file" 1>&2
|
||||
exit 1;
|
||||
fi
|
||||
shell=$1
|
||||
man=$2
|
||||
|
||||
case $shell in
|
||||
sh) which=0;;
|
||||
ksh) which=1;;
|
||||
*)
|
||||
echo "$0: bad shell option (must be sh or ksh)" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
if [ ! -r "$man" ] ; then
|
||||
echo "$0: can't read $man file" 1>&2
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
|
||||
#
|
||||
# Now generate the appropriate man page...
|
||||
#
|
||||
[ $verbose = yes ] && echo "$0: Generating $which man page (0=sh,1=ksh)..." 1>&2
|
||||
|
||||
${AWK} 'BEGIN { ksh = '$which'; pr = 1 }
|
||||
/^\.sh\(/ { pr = ksh - 1; next }
|
||||
/^\.sh\)/ { pr = 1; next }
|
||||
/^\.ksh\(/ { pr = ksh; next }
|
||||
/^\.ksh\)/ { pr = 1; next }
|
||||
{ if (pr) print $0 } ' < $man
|
||||
|
||||
[ $verbose = yes ] && echo "$0: All done" 1>&2
|
||||
|
||||
exit 0
|
318
bin/ksh/path.c
Normal file
318
bin/ksh/path.c
Normal file
|
@ -0,0 +1,318 @@
|
|||
/* $NetBSD: path.c,v 1.8 2009/04/25 05:11:37 lukem Exp $ */
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: path.c,v 1.8 2009/04/25 05:11:37 lukem Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
#include "ksh_stat.h"
|
||||
|
||||
/*
|
||||
* Contains a routine to search a : separated list of
|
||||
* paths (a la CDPATH) and make appropriate file names.
|
||||
* Also contains a routine to simplify .'s and ..'s out of
|
||||
* a path name.
|
||||
*
|
||||
* Larry Bouzane (larry@cs.mun.ca)
|
||||
*/
|
||||
|
||||
#ifdef S_ISLNK
|
||||
static char *do_phys_path ARGS((XString *, char *, const char *));
|
||||
#endif /* S_ISLNK */
|
||||
|
||||
/*
|
||||
* Makes a filename into result using the following algorithm.
|
||||
* - make result NULL
|
||||
* - if file starts with '/', append file to result & set cdpathp to NULL
|
||||
* - if file starts with ./ or ../ append cwd and file to result
|
||||
* and set cdpathp to NULL
|
||||
* - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
|
||||
* then cwd is appended to result.
|
||||
* - the first element of cdpathp is appended to result
|
||||
* - file is appended to result
|
||||
* - cdpathp is set to the start of the next element in cdpathp (or NULL
|
||||
* if there are no more elements.
|
||||
* The return value indicates whether a non-null element from cdpathp
|
||||
* was appended to result.
|
||||
*/
|
||||
int
|
||||
make_path(cwd, file, cdpathp, xsp, phys_pathp)
|
||||
const char *cwd;
|
||||
const char *file;
|
||||
char **cdpathp; /* & of : separated list */
|
||||
XString *xsp;
|
||||
int *phys_pathp;
|
||||
{
|
||||
int rval = 0;
|
||||
int use_cdpath = 1;
|
||||
char *plist;
|
||||
int len;
|
||||
int plen = 0;
|
||||
char *xp = Xstring(*xsp, xp);
|
||||
|
||||
if (!file)
|
||||
file = null;
|
||||
|
||||
if (!ISRELPATH(file)) {
|
||||
*phys_pathp = 0;
|
||||
use_cdpath = 0;
|
||||
} else {
|
||||
if (file[0] == '.') {
|
||||
char c = file[1];
|
||||
|
||||
if (c == '.')
|
||||
c = file[2];
|
||||
if (ISDIRSEP(c) || c == '\0')
|
||||
use_cdpath = 0;
|
||||
}
|
||||
|
||||
plist = *cdpathp;
|
||||
if (!plist)
|
||||
use_cdpath = 0;
|
||||
else if (use_cdpath) {
|
||||
char *pend;
|
||||
|
||||
for (pend = plist; *pend && *pend != PATHSEP; pend++)
|
||||
;
|
||||
plen = pend - plist;
|
||||
*cdpathp = *pend ? ++pend : (char *) 0;
|
||||
}
|
||||
|
||||
if ((use_cdpath == 0 || !plen || ISRELPATH(plist))
|
||||
&& (cwd && *cwd))
|
||||
{
|
||||
len = strlen(cwd);
|
||||
XcheckN(*xsp, xp, len);
|
||||
memcpy(xp, cwd, len);
|
||||
xp += len;
|
||||
if (!ISDIRSEP(cwd[len - 1]))
|
||||
Xput(*xsp, xp, DIRSEP);
|
||||
}
|
||||
*phys_pathp = Xlength(*xsp, xp);
|
||||
if (use_cdpath && plen) {
|
||||
XcheckN(*xsp, xp, plen);
|
||||
memcpy(xp, plist, plen);
|
||||
xp += plen;
|
||||
if (!ISDIRSEP(plist[plen - 1]))
|
||||
Xput(*xsp, xp, DIRSEP);
|
||||
rval = 1;
|
||||
}
|
||||
}
|
||||
|
||||
len = strlen(file) + 1;
|
||||
XcheckN(*xsp, xp, len);
|
||||
memcpy(xp, file, len);
|
||||
|
||||
if (!use_cdpath)
|
||||
*cdpathp = (char *) 0;
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simplify pathnames containing "." and ".." entries.
|
||||
* ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
|
||||
*/
|
||||
void
|
||||
simplify_path(pathx)
|
||||
char *pathx;
|
||||
{
|
||||
char *cur;
|
||||
char *t;
|
||||
int isrooted;
|
||||
char *very_start = pathx;
|
||||
char *start;
|
||||
|
||||
if (!*pathx)
|
||||
return;
|
||||
|
||||
if ((isrooted = ISROOTEDPATH(pathx)))
|
||||
very_start++;
|
||||
#if defined (OS2) || defined (__CYGWIN__)
|
||||
if (pathx[0] && pathx[1] == ':') /* skip a: */
|
||||
very_start += 2;
|
||||
#endif /* OS2 || __CYGWIN__ */
|
||||
|
||||
/* Before After
|
||||
* /foo/ /foo
|
||||
* /foo/../../bar /bar
|
||||
* /foo/./blah/.. /foo
|
||||
* . .
|
||||
* .. ..
|
||||
* ./foo foo
|
||||
* foo/../../../bar ../../bar
|
||||
* OS2 and CYGWIN:
|
||||
* a:/foo/../.. a:/
|
||||
* a:. a:
|
||||
* a:.. a:..
|
||||
* a:foo/../../blah a:../blah
|
||||
*/
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
/* preserve leading double-slash on pathnames (for UNC paths) */
|
||||
if (pathx[0] && ISDIRSEP(pathx[0]) && pathx[1] && ISDIRSEP(pathx[1]))
|
||||
very_start++;
|
||||
#endif /* __CYGWIN__ */
|
||||
|
||||
for (cur = t = start = very_start; ; ) {
|
||||
/* treat multiple '/'s as one '/' */
|
||||
while (ISDIRSEP(*t))
|
||||
t++;
|
||||
|
||||
if (*t == '\0') {
|
||||
if (cur == pathx)
|
||||
/* convert empty path to dot */
|
||||
*cur++ = '.';
|
||||
*cur = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
if (t[0] == '.') {
|
||||
if (!t[1] || ISDIRSEP(t[1])) {
|
||||
t += 1;
|
||||
continue;
|
||||
} else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
|
||||
if (!isrooted && cur == start) {
|
||||
if (cur != very_start)
|
||||
*cur++ = DIRSEP;
|
||||
*cur++ = '.';
|
||||
*cur++ = '.';
|
||||
start = cur;
|
||||
} else if (cur != start)
|
||||
while (--cur > start && !ISDIRSEP(*cur))
|
||||
;
|
||||
t += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur != very_start)
|
||||
*cur++ = DIRSEP;
|
||||
|
||||
/* find/copy next component of pathname */
|
||||
while (*t && !ISDIRSEP(*t))
|
||||
*cur++ = *t++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
set_current_wd(pathx)
|
||||
char *pathx;
|
||||
{
|
||||
int len;
|
||||
char *p = pathx;
|
||||
|
||||
if (!p && !(p = ksh_get_wd((char *) 0, 0)))
|
||||
p = null;
|
||||
|
||||
len = strlen(p) + 1;
|
||||
|
||||
if (len > current_wd_size)
|
||||
current_wd = aresize(current_wd, current_wd_size = len, APERM);
|
||||
memcpy(current_wd, p, len);
|
||||
if (p != pathx && p != null)
|
||||
afree(p, ATEMP);
|
||||
}
|
||||
|
||||
#ifdef S_ISLNK
|
||||
char *
|
||||
get_phys_path(pathx)
|
||||
const char *pathx;
|
||||
{
|
||||
XString xs;
|
||||
char *xp;
|
||||
|
||||
Xinit(xs, xp, strlen(pathx) + 1, ATEMP);
|
||||
|
||||
xp = do_phys_path(&xs, xp, pathx);
|
||||
|
||||
if (!xp)
|
||||
return (char *) 0;
|
||||
|
||||
if (Xlength(xs, xp) == 0)
|
||||
Xput(xs, xp, DIRSEP);
|
||||
Xput(xs, xp, '\0');
|
||||
|
||||
return Xclose(xs, xp);
|
||||
}
|
||||
|
||||
static char *
|
||||
do_phys_path(xsp, xp, pathx)
|
||||
XString *xsp;
|
||||
char *xp;
|
||||
const char *pathx;
|
||||
{
|
||||
const char *p, *q;
|
||||
int len, llen;
|
||||
int savepos;
|
||||
char lbuf[PATH];
|
||||
|
||||
Xcheck(*xsp, xp);
|
||||
for (p = pathx; p; p = q) {
|
||||
while (ISDIRSEP(*p))
|
||||
p++;
|
||||
if (!*p)
|
||||
break;
|
||||
len = (q = ksh_strchr_dirsep(p)) ? q - p : (int)strlen(p);
|
||||
if (len == 1 && p[0] == '.')
|
||||
continue;
|
||||
if (len == 2 && p[0] == '.' && p[1] == '.') {
|
||||
while (xp > Xstring(*xsp, xp)) {
|
||||
xp--;
|
||||
if (ISDIRSEP(*xp))
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
savepos = Xsavepos(*xsp, xp);
|
||||
Xput(*xsp, xp, DIRSEP);
|
||||
XcheckN(*xsp, xp, len + 1);
|
||||
memcpy(xp, p, len);
|
||||
xp += len;
|
||||
*xp = '\0';
|
||||
|
||||
llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1);
|
||||
if (llen < 0) {
|
||||
/* EINVAL means it wasn't a symlink... */
|
||||
if (errno != EINVAL)
|
||||
return (char *) 0;
|
||||
continue;
|
||||
}
|
||||
lbuf[llen] = '\0';
|
||||
|
||||
/* If absolute path, start from scratch.. */
|
||||
xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp)
|
||||
: Xrestpos(*xsp, xp, savepos);
|
||||
if (!(xp = do_phys_path(xsp, xp, lbuf)))
|
||||
return (char *) 0;
|
||||
}
|
||||
return xp;
|
||||
}
|
||||
#endif /* S_ISLNK */
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
main(argc, argv)
|
||||
{
|
||||
int rv;
|
||||
char *cp, cdpath[256], pwd[256], file[256], result[256];
|
||||
|
||||
printf("enter CDPATH: "); gets(cdpath);
|
||||
printf("enter PWD: "); gets(pwd);
|
||||
while (1) {
|
||||
if (printf("Enter file: "), gets(file) == 0)
|
||||
return 0;
|
||||
cp = cdpath;
|
||||
do {
|
||||
rv = make_path(pwd, file, &cp, result, sizeof(result));
|
||||
printf("make_path returns (%d), \"%s\" ", rv, result);
|
||||
simplify_path(result);
|
||||
printf("(simpifies to \"%s\")\n", result);
|
||||
} while (cp);
|
||||
}
|
||||
}
|
||||
#endif /* TEST */
|
298
bin/ksh/proto.h
Normal file
298
bin/ksh/proto.h
Normal file
|
@ -0,0 +1,298 @@
|
|||
/* $NetBSD: proto.h,v 1.7 2005/06/26 19:09:00 christos Exp $ */
|
||||
|
||||
/*
|
||||
* prototypes for PD-KSH
|
||||
* originally generated using "cproto.c 3.5 92/04/11 19:28:01 cthuang "
|
||||
* $Id: proto.h,v 1.7 2005/06/26 19:09:00 christos Exp $
|
||||
*/
|
||||
|
||||
/* alloc.c */
|
||||
Area * ainit ARGS((Area *));
|
||||
void afreeall ARGS((Area *));
|
||||
void * alloc ARGS((size_t, Area *));
|
||||
void * aresize ARGS((void *, size_t, Area *));
|
||||
void afree ARGS((void *, Area *));
|
||||
/* c_ksh.c */
|
||||
int c_hash ARGS((char **));
|
||||
int c_cd ARGS((char **));
|
||||
int c_pwd ARGS((char **));
|
||||
int c_print ARGS((char **));
|
||||
int c_whence ARGS((char **));
|
||||
int c_command ARGS((char **));
|
||||
int c_typeset ARGS((char **));
|
||||
int c_alias ARGS((char **));
|
||||
int c_unalias ARGS((char **));
|
||||
int c_let ARGS((char **));
|
||||
int c_jobs ARGS((char **));
|
||||
int c_fgbg ARGS((char **));
|
||||
int c_kill ARGS((char **));
|
||||
void getopts_reset ARGS((int));
|
||||
int c_getopts ARGS((char **));
|
||||
int c_bind ARGS((char **));
|
||||
/* c_sh.c */
|
||||
int c_label ARGS((char **));
|
||||
int c_shift ARGS((char **));
|
||||
int c_umask ARGS((char **));
|
||||
int c_dot ARGS((char **));
|
||||
int c_wait ARGS((char **));
|
||||
int c_read ARGS((char **));
|
||||
int c_eval ARGS((char **));
|
||||
int c_trap ARGS((char **));
|
||||
int c_brkcont ARGS((char **));
|
||||
int c_exitreturn ARGS((char **));
|
||||
int c_set ARGS((char **));
|
||||
int c_unset ARGS((char **));
|
||||
int c_ulimit ARGS((char **));
|
||||
int c_times ARGS((char **));
|
||||
int timex ARGS((struct op *, int));
|
||||
void timex_hook ARGS((struct op *, char ** volatile *));
|
||||
int c_exec ARGS((char **));
|
||||
int c_builtin ARGS((char **));
|
||||
/* c_test.c */
|
||||
int c_test ARGS((char **));
|
||||
/* edit.c: most prototypes in edit.h */
|
||||
void x_init ARGS((void));
|
||||
int x_read ARGS((char *, size_t));
|
||||
void set_editmode ARGS((const char *));
|
||||
/* emacs.c: most prototypes in edit.h */
|
||||
int x_bind ARGS((const char *, const char *, int, int));
|
||||
/* eval.c */
|
||||
char * substitute ARGS((const char *, int));
|
||||
char ** eval ARGS((char **, int));
|
||||
char * evalstr ARGS((char *, int));
|
||||
char * evalonestr ARGS((char *, int));
|
||||
char *debunk ARGS((char *, const char *, size_t));
|
||||
void expand ARGS((char *, XPtrV *, int));
|
||||
int glob_str ARGS((char *, XPtrV *, int));
|
||||
/* exec.c */
|
||||
int fd_clexec ARGS((int));
|
||||
int execute ARGS((struct op * volatile, volatile int));
|
||||
int shcomexec ARGS((char **));
|
||||
struct tbl * findfunc ARGS((const char *, unsigned int, int));
|
||||
int define ARGS((const char *, struct op *));
|
||||
void builtin ARGS((const char *, int (*)(char **)));
|
||||
struct tbl * findcom ARGS((const char *, int));
|
||||
void flushcom ARGS((int all));
|
||||
char * search ARGS((const char *, const char *, int, int *));
|
||||
int search_access ARGS((const char *, int, int *));
|
||||
int pr_menu ARGS((char *const *));
|
||||
int pr_list ARGS((char *const *));
|
||||
/* expr.c */
|
||||
int evaluate ARGS((const char *, long *, int));
|
||||
int v_evaluate ARGS((struct tbl *, const char *, volatile int));
|
||||
/* history.c */
|
||||
void init_histvec ARGS((void));
|
||||
void hist_init ARGS((Source *));
|
||||
void hist_finish ARGS((void));
|
||||
void histsave ARGS((int, const char *, int));
|
||||
#ifdef HISTORY
|
||||
int c_fc ARGS((char **));
|
||||
void sethistsize ARGS((int));
|
||||
void sethistfile ARGS((const char *));
|
||||
# ifdef EASY_HISTORY
|
||||
void histappend ARGS((const char *, int));
|
||||
# endif
|
||||
char ** histpos ARGS((void));
|
||||
int histN ARGS((void));
|
||||
int histnum ARGS((int));
|
||||
int findhist ARGS((int, int, const char *, int));
|
||||
#endif /* HISTORY */
|
||||
/* io.c */
|
||||
void errorf ARGS((const char *, ...))
|
||||
GCC_FUNC_ATTR2(noreturn, format(printf, 1, 2));
|
||||
void warningf ARGS((int, const char *, ...))
|
||||
GCC_FUNC_ATTR(format(printf, 2, 3));
|
||||
void bi_errorf ARGS((const char *, ...))
|
||||
GCC_FUNC_ATTR(format(printf, 1, 2));
|
||||
void internal_errorf ARGS((int, const char *, ...))
|
||||
GCC_FUNC_ATTR(format(printf, 2, 3));
|
||||
void error_prefix ARGS((int));
|
||||
void shellf ARGS((const char *, ...))
|
||||
GCC_FUNC_ATTR(format(printf, 1, 2));
|
||||
void shprintf ARGS((const char *, ...))
|
||||
GCC_FUNC_ATTR(format(printf, 1, 2));
|
||||
#ifdef KSH_DEBUG
|
||||
void kshdebug_init_ ARGS((void));
|
||||
void kshdebug_printf_ ARGS((const char *, ...))
|
||||
GCC_FUNC_ATTR(format(printf, 1, 2));
|
||||
void kshdebug_dump_ ARGS((const char *, const void *, int));
|
||||
#endif /* KSH_DEBUG */
|
||||
int can_seek ARGS((int));
|
||||
void initio ARGS((void));
|
||||
int ksh_dup2 ARGS((int, int, int));
|
||||
int savefd ARGS((int, int));
|
||||
void restfd ARGS((int, int));
|
||||
void openpipe ARGS((int *));
|
||||
void closepipe ARGS((int *));
|
||||
int check_fd ARGS((char *, int, const char **));
|
||||
#ifdef KSH
|
||||
void coproc_init ARGS((void));
|
||||
void coproc_read_close ARGS((int));
|
||||
void coproc_readw_close ARGS((int));
|
||||
void coproc_write_close ARGS((int));
|
||||
int coproc_getfd ARGS((int, const char **));
|
||||
void coproc_cleanup ARGS((int));
|
||||
#endif /* KSH */
|
||||
struct temp *maketemp ARGS((Area *, Temp_type, struct temp **));
|
||||
/* jobs.c */
|
||||
void j_init ARGS((int));
|
||||
void j_exit ARGS((void));
|
||||
void j_change ARGS((void));
|
||||
int exchild ARGS((struct op *, int, int));
|
||||
void startlast ARGS((void));
|
||||
int waitlast ARGS((void));
|
||||
int waitfor ARGS((const char *, int *));
|
||||
int j_kill ARGS((const char *, int));
|
||||
int j_resume ARGS((const char *, int));
|
||||
int j_jobs ARGS((const char *, int, int));
|
||||
void j_notify ARGS((void));
|
||||
pid_t j_async ARGS((void));
|
||||
int j_stopped_running ARGS((void));
|
||||
/* lex.c */
|
||||
int yylex ARGS((int));
|
||||
void yyerror ARGS((const char *, ...))
|
||||
GCC_FUNC_ATTR2(noreturn, format(printf, 1, 2));
|
||||
Source * pushs ARGS((int, Area *));
|
||||
void set_prompt ARGS((int, Source *));
|
||||
void pprompt ARGS((const char *, int));
|
||||
/* mail.c */
|
||||
#ifdef KSH
|
||||
void mcheck ARGS((void));
|
||||
void mcset ARGS((long));
|
||||
void mbset ARGS((char *));
|
||||
void mpset ARGS((char *));
|
||||
#endif /* KSH */
|
||||
/* main.c */
|
||||
int include ARGS((const char *, int, char **, int));
|
||||
int command ARGS((const char *));
|
||||
int shell ARGS((Source *volatile, int volatile));
|
||||
void unwind ARGS((int)) GCC_FUNC_ATTR(noreturn);
|
||||
void newenv ARGS((int));
|
||||
void quitenv ARGS((void));
|
||||
void cleanup_parents_env ARGS((void));
|
||||
void cleanup_proc_env ARGS((void));
|
||||
void aerror ARGS((Area *, const char *))
|
||||
GCC_FUNC_ATTR(noreturn);
|
||||
/* misc.c */
|
||||
void setctypes ARGS((const char *, int));
|
||||
void initctypes ARGS((void));
|
||||
char * ulton ARGS((unsigned long, int));
|
||||
char * str_save ARGS((const char *, Area *));
|
||||
char * str_nsave ARGS((const char *, int, Area *));
|
||||
int option ARGS((const char *));
|
||||
char * getoptions ARGS((void));
|
||||
void change_flag ARGS((enum sh_flag, int, int));
|
||||
int parse_args ARGS((char **v, int what, int *));
|
||||
int getn ARGS((const char *, int *));
|
||||
int bi_getn ARGS((const char *, int *));
|
||||
int gmatch ARGS((const char *, const char *, int));
|
||||
int has_globbing ARGS((const char *, const char *));
|
||||
const unsigned char *pat_scan ARGS((const unsigned char *,
|
||||
const unsigned char *, int));
|
||||
void qsortp ARGS((void **, size_t, int (*)(void *, void *)));
|
||||
int xstrcmp ARGS((void *, void *));
|
||||
void ksh_getopt_reset ARGS((Getopt *, int));
|
||||
int ksh_getopt ARGS((char **, Getopt *, const char *));
|
||||
void print_value_quoted ARGS((const char *));
|
||||
void print_columns ARGS((struct shf *, int,
|
||||
char *(*)(void *, int, char *, int),
|
||||
void *, int, int));
|
||||
int strip_nuls ARGS((char *, int));
|
||||
char *str_zcpy ARGS((char *, const char *, int));
|
||||
int blocking_read ARGS((int, char *, int));
|
||||
int reset_nonblock ARGS((int));
|
||||
char *ksh_get_wd ARGS((char *, int));
|
||||
/* path.c */
|
||||
int make_path ARGS((const char *, const char *,
|
||||
char **, XString *, int *));
|
||||
void simplify_path ARGS((char *));
|
||||
char *get_phys_path ARGS((const char *));
|
||||
void set_current_wd ARGS((char *));
|
||||
/* syn.c */
|
||||
void initkeywords ARGS((void));
|
||||
struct op * compile ARGS((Source *));
|
||||
/* table.c */
|
||||
unsigned int hash ARGS((const char *));
|
||||
void tinit ARGS((struct table *, Area *, int));
|
||||
struct tbl * tsearch ARGS((struct table *, const char *, unsigned int));
|
||||
struct tbl * tenter ARGS((struct table *, const char *, unsigned int));
|
||||
void tdelete ARGS((struct tbl *));
|
||||
void twalk ARGS((struct tstate *, struct table *));
|
||||
struct tbl * tnext ARGS((struct tstate *));
|
||||
struct tbl ** tsort ARGS((struct table *));
|
||||
/* trace.c */
|
||||
/* trap.c */
|
||||
void inittraps ARGS((void));
|
||||
#ifdef KSH
|
||||
void alarm_init ARGS((void));
|
||||
#endif /* KSH */
|
||||
Trap * gettrap ARGS((const char *, int));
|
||||
RETSIGTYPE trapsig ARGS((int));
|
||||
void intrcheck ARGS((void));
|
||||
int fatal_trap_check ARGS((void));
|
||||
int trap_pending ARGS((void));
|
||||
void runtraps ARGS((int));
|
||||
void runtrap ARGS((Trap *));
|
||||
void cleartraps ARGS((void));
|
||||
void restoresigs ARGS((void));
|
||||
void settrap ARGS((Trap *, char *));
|
||||
int block_pipe ARGS((void));
|
||||
void restore_pipe ARGS((int));
|
||||
int setsig ARGS((Trap *, handler_t, int));
|
||||
void setexecsig ARGS((Trap *, int));
|
||||
/* tree.c */
|
||||
int fptreef ARGS((struct shf *, int, const char *, ...));
|
||||
char * snptreef ARGS((char *, int, const char *, ...));
|
||||
struct op * tcopy ARGS((struct op *, Area *));
|
||||
char * wdcopy ARGS((const char *, Area *));
|
||||
char * wdscan ARGS((const char *, int));
|
||||
char * wdstrip ARGS((const char *));
|
||||
void tfree ARGS((struct op *, Area *));
|
||||
/* var.c */
|
||||
void newblock ARGS((void));
|
||||
void popblock ARGS((void));
|
||||
void initvar ARGS((void));
|
||||
struct tbl * global ARGS((const char *));
|
||||
struct tbl * local ARGS((const char *, bool_t));
|
||||
char * str_val ARGS((struct tbl *));
|
||||
long intval ARGS((struct tbl *));
|
||||
int setstr ARGS((struct tbl *, const char *, int));
|
||||
struct tbl *setint_v ARGS((struct tbl *, struct tbl *));
|
||||
void setint ARGS((struct tbl *, long));
|
||||
int getint ARGS((struct tbl *, long *));
|
||||
struct tbl * typeset ARGS((const char *, Tflag, Tflag, int, int));
|
||||
void unset ARGS((struct tbl *, int));
|
||||
char * skip_varname ARGS((const char *, int));
|
||||
char *skip_wdvarname ARGS((const char *, int));
|
||||
int is_wdvarname ARGS((const char *, int));
|
||||
int is_wdvarassign ARGS((const char *));
|
||||
char ** makenv ARGS((void));
|
||||
void change_random ARGS((void));
|
||||
int array_ref_len ARGS((const char *));
|
||||
char * arrayname ARGS((const char *));
|
||||
void set_array ARGS((const char *, int, char **));
|
||||
/* version.c */
|
||||
/* vi.c: see edit.h */
|
||||
|
||||
|
||||
/* Hack to avoid billions of compile warnings on SunOS 4.1.x */
|
||||
#if defined(MUN) && defined(sun) && !defined(__svr4__)
|
||||
extern void bcopy ARGS((const void *, void *, size_t));
|
||||
extern intclose ARGS((FILE *));
|
||||
extern intprintf ARGS((FILE *, const char *, ...));
|
||||
extern intread ARGS((void *, int, int, FILE *));
|
||||
extern int ioctl ARGS((int, int, void *));
|
||||
extern int killpg ARGS((int, int));
|
||||
extern int nice ARGS((int));
|
||||
extern int readlink ARGS((const char *, char *, int));
|
||||
extern int setpgrp ARGS((int, int));
|
||||
extern int strcasecmp ARGS((const char *, const char *));
|
||||
extern int tolower ARGS((int));
|
||||
extern int toupper ARGS((int));
|
||||
/* Include files aren't included yet */
|
||||
extern int getrlimit ARGS(( /* int, struct rlimit * */ ));
|
||||
extern int getrusage ARGS(( /* int, struct rusage * */ ));
|
||||
extern int gettimeofday ARGS(( /* struct timeval *, struct timezone * */ ));
|
||||
extern int setrlimit ARGS(( /* int, struct rlimit * */ ));
|
||||
extern int lstat ARGS(( /* const char *, struct stat * */ ));
|
||||
#endif
|
735
bin/ksh/sh.h
Normal file
735
bin/ksh/sh.h
Normal file
|
@ -0,0 +1,735 @@
|
|||
/* $NetBSD: sh.h,v 1.7 2005/06/26 19:09:00 christos Exp $ */
|
||||
|
||||
/*
|
||||
* Public Domain Bourne/Korn shell
|
||||
*/
|
||||
|
||||
/* $Id: sh.h,v 1.7 2005/06/26 19:09:00 christos Exp $ */
|
||||
|
||||
#include "config.h" /* system and option configuration info */
|
||||
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
# define ARGS(args) args /* prototype declaration */
|
||||
#else
|
||||
# define ARGS(args) () /* K&R declaration */
|
||||
#endif
|
||||
|
||||
/* Start of common headers */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <setjmp.h>
|
||||
#ifdef HAVE_STDDEF_H
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
#else
|
||||
/* just a useful subset of what stdlib.h would have */
|
||||
extern char * getenv ARGS((const char *));
|
||||
extern void * malloc ARGS((size_t));
|
||||
extern void * realloc ARGS((void *, size_t));
|
||||
extern int free ARGS((void *));
|
||||
extern int exit ARGS((int));
|
||||
extern int rand ARGS((void));
|
||||
extern void srand ARGS((unsigned int));
|
||||
extern int atoi ARGS((const char *));
|
||||
#endif /* HAVE_STDLIB_H */
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#else
|
||||
/* just a useful subset of what unistd.h would have */
|
||||
extern int access ARGS((const char *, int));
|
||||
extern int open ARGS((const char *, int, ...));
|
||||
extern int creat ARGS((const char *, mode_t));
|
||||
extern int read ARGS((int, char *, unsigned));
|
||||
extern int write ARGS((int, const char *, unsigned));
|
||||
extern off_t lseek ARGS((int, off_t, int));
|
||||
extern int close ARGS((int));
|
||||
extern int pipe ARGS((int []));
|
||||
extern int dup2 ARGS((int, int));
|
||||
extern int unlink ARGS((const char *));
|
||||
extern int fork ARGS((void));
|
||||
extern int execve ARGS((const char *, char * const[], char * const[]));
|
||||
extern int chdir ARGS((const char *));
|
||||
extern int kill ARGS((pid_t, int));
|
||||
extern char *getcwd(); /* no ARGS here - differs on different machines */
|
||||
extern int geteuid ARGS((void));
|
||||
extern int readlink ARGS((const char *, char *, int));
|
||||
extern int getegid ARGS((void));
|
||||
extern int getpid ARGS((void));
|
||||
extern int getppid ARGS((void));
|
||||
extern unsigned int sleep ARGS((unsigned int));
|
||||
extern int isatty ARGS((int));
|
||||
# ifdef POSIX_PGRP
|
||||
extern int getpgrp ARGS((void));
|
||||
extern int setpgid ARGS((pid_t, pid_t));
|
||||
# endif /* POSIX_PGRP */
|
||||
# ifdef BSD_PGRP
|
||||
extern int getpgrp ARGS((pid_t));
|
||||
extern int setpgrp ARGS((pid_t, pid_t));
|
||||
# endif /* BSD_PGRP */
|
||||
# ifdef SYSV_PGRP
|
||||
extern int getpgrp ARGS((void));
|
||||
extern int setpgrp ARGS((void));
|
||||
# endif /* SYSV_PGRP */
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#else
|
||||
# include <strings.h>
|
||||
# define strchr index
|
||||
# define strrchr rindex
|
||||
#endif /* HAVE_STRING_H */
|
||||
#ifndef HAVE_STRSTR
|
||||
char *strstr ARGS((const char *s, const char *p));
|
||||
#endif /* HAVE_STRSTR */
|
||||
#ifndef HAVE_STRCASECMP
|
||||
int strcasecmp ARGS((const char *s1, const char *s2));
|
||||
int strncasecmp ARGS((const char *s1, const char *s2, int n));
|
||||
#endif /* HAVE_STRCASECMP */
|
||||
|
||||
#ifdef HAVE_MEMORY_H
|
||||
# include <memory.h>
|
||||
#endif
|
||||
#ifndef HAVE_MEMSET
|
||||
# define memcpy(d, s, n) bcopy(s, d, n)
|
||||
# define memcmp(s1, s2, n) bcmp(s1, s2, n)
|
||||
void *memset ARGS((void *d, int c, size_t n));
|
||||
#endif /* HAVE_MEMSET */
|
||||
#ifndef HAVE_MEMMOVE
|
||||
# ifdef HAVE_BCOPY
|
||||
# define memmove(d, s, n) bcopy(s, d, n)
|
||||
# else
|
||||
void *memmove ARGS((void *d, const void *s, size_t n));
|
||||
# endif
|
||||
#endif /* HAVE_MEMMOVE */
|
||||
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
# include <stdarg.h>
|
||||
# define SH_VA_START(va, argn) va_start(va, argn)
|
||||
#else
|
||||
# include <varargs.h>
|
||||
# define SH_VA_START(va, argn) va_start(va)
|
||||
#endif /* HAVE_PROTOTYPES */
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
#else
|
||||
# include <sys/file.h>
|
||||
#endif /* HAVE_FCNTL_H */
|
||||
#ifndef O_ACCMODE
|
||||
# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
|
||||
#endif /* !O_ACCMODE */
|
||||
|
||||
#ifndef F_OK /* access() arguments */
|
||||
# define F_OK 0
|
||||
# define X_OK 1
|
||||
# define W_OK 2
|
||||
# define R_OK 4
|
||||
#endif /* !F_OK */
|
||||
|
||||
#ifndef SEEK_SET
|
||||
# ifdef L_SET
|
||||
# define SEEK_SET L_SET
|
||||
# define SEEK_CUR L_INCR
|
||||
# define SEEK_END L_XTND
|
||||
# else /* L_SET */
|
||||
# define SEEK_SET 0
|
||||
# define SEEK_CUR 1
|
||||
# define SEEK_END 2
|
||||
# endif /* L_SET */
|
||||
#endif /* !SEEK_SET */
|
||||
|
||||
/* Some machines (eg, FreeBSD 1.1.5) define CLK_TCK in limits.h
|
||||
* (ksh_limval.h assumes limits has been included, if available)
|
||||
*/
|
||||
#ifdef HAVE_LIMITS_H
|
||||
# include <limits.h>
|
||||
#endif /* HAVE_LIMITS_H */
|
||||
|
||||
#include <signal.h>
|
||||
#ifdef NSIG
|
||||
# define SIGNALS NSIG
|
||||
#else
|
||||
# ifdef _MINIX
|
||||
# define SIGNALS (_NSIG+1) /* _NSIG is # of signals used, excluding 0. */
|
||||
# else
|
||||
# ifdef _SIGMAX /* QNX */
|
||||
# define SIGNALS _SIGMAX
|
||||
# else /* _SIGMAX */
|
||||
# define SIGNALS 32
|
||||
# endif /* _SIGMAX */
|
||||
# endif /* _MINIX */
|
||||
#endif /* NSIG */
|
||||
#ifndef SIGCHLD
|
||||
# define SIGCHLD SIGCLD
|
||||
#endif
|
||||
/* struct sigaction.sa_flags is set to KSH_SA_FLAGS. Used to ensure
|
||||
* system calls are interrupted
|
||||
*/
|
||||
#ifdef SA_INTERRUPT
|
||||
# define KSH_SA_FLAGS SA_INTERRUPT
|
||||
#else /* SA_INTERRUPT */
|
||||
# define KSH_SA_FLAGS 0
|
||||
#endif /* SA_INTERRUPT */
|
||||
|
||||
typedef RETSIGTYPE (*handler_t) ARGS((int)); /* signal handler */
|
||||
|
||||
#ifdef USE_FAKE_SIGACT
|
||||
# include "sigact.h" /* use sjg's fake sigaction() */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PATHS_H
|
||||
# include <paths.h>
|
||||
#endif /* HAVE_PATHS_H */
|
||||
#ifdef _PATH_DEFPATH
|
||||
# define DEFAULT__PATH _PATH_DEFPATH
|
||||
#else /* _PATH_DEFPATH */
|
||||
# define DEFAULT__PATH DEFAULT_PATH
|
||||
#endif /* _PATH_DEFPATH */
|
||||
|
||||
#ifndef offsetof
|
||||
# define offsetof(type,id) ((size_t)&((type*)NULL)->id)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_KILLPG
|
||||
# define killpg(p, s) kill(-(p), (s))
|
||||
#endif /* !HAVE_KILLPG */
|
||||
|
||||
/* Special cases for execve(2) */
|
||||
#ifdef OS2
|
||||
extern int ksh_execve(char *cmd, char **args, char **env, int flags);
|
||||
#else /* OS2 */
|
||||
# if defined(OS_ISC) && defined(_POSIX_SOURCE)
|
||||
/* Kludge for ISC 3.2 (and other versions?) so programs will run correctly. */
|
||||
# define ksh_execve(p, av, ev, flags) \
|
||||
do { \
|
||||
__setostype(0); \
|
||||
execve(p, av, ev); \
|
||||
__setostype(1); \
|
||||
} while (0)
|
||||
# else /* OS_ISC && _POSIX */
|
||||
# define ksh_execve(p, av, ev, flags) execve(p, av, ev)
|
||||
# endif /* OS_ISC && _POSIX */
|
||||
#endif /* OS2 */
|
||||
|
||||
/* this is a hang-over from older versions of the os2 port */
|
||||
#define ksh_dupbase(fd, base) fcntl(fd, F_DUPFD, base)
|
||||
|
||||
#ifdef HAVE_SIGSETJMP
|
||||
# define ksh_sigsetjmp(env,sm) sigsetjmp((env), (sm))
|
||||
# define ksh_siglongjmp(env,v) siglongjmp((env), (v))
|
||||
# define ksh_jmp_buf sigjmp_buf
|
||||
#else /* HAVE_SIGSETJMP */
|
||||
# ifdef HAVE__SETJMP
|
||||
# define ksh_sigsetjmp(env,sm) _setjmp(env)
|
||||
# define ksh_siglongjmp(env,v) _longjmp((env), (v))
|
||||
# else /* HAVE__SETJMP */
|
||||
# define ksh_sigsetjmp(env,sm) setjmp(env)
|
||||
# define ksh_siglongjmp(env,v) longjmp((env), (v))
|
||||
# endif /* HAVE__SETJMP */
|
||||
# define ksh_jmp_buf jmp_buf
|
||||
#endif /* HAVE_SIGSETJMP */
|
||||
|
||||
#ifndef HAVE_DUP2
|
||||
extern int dup2 ARGS((int, int));
|
||||
#endif /* !HAVE_DUP2 */
|
||||
|
||||
/* Find a integer type that is at least 32 bits (or die) - SIZEOF_* defined
|
||||
* by autoconf (assumes an 8 bit byte, but I'm not concerned).
|
||||
* NOTE: INT32 may end up being more than 32 bits.
|
||||
*/
|
||||
#if SIZEOF_INT >= 4
|
||||
# define INT32 int
|
||||
#else /* SIZEOF_INT */
|
||||
# if SIZEOF_LONG >= 4
|
||||
# define INT32 long
|
||||
# else /* SIZEOF_LONG */
|
||||
#error cannot find 32 bit type...
|
||||
# endif /* SIZEOF_LONG */
|
||||
#endif /* SIZEOF_INT */
|
||||
|
||||
/* end of common headers */
|
||||
|
||||
/* Stop gcc and lint from complaining about possibly uninitialized variables */
|
||||
#if defined(__GNUC__) || defined(lint)
|
||||
# define UNINITIALIZED(var) var = 0
|
||||
#else
|
||||
# define UNINITIALIZED(var) var
|
||||
#endif /* GNUC || lint */
|
||||
|
||||
/* some useful #defines */
|
||||
#ifdef EXTERN
|
||||
# define I__(i) = i
|
||||
#else
|
||||
# define I__(i)
|
||||
# define EXTERN extern
|
||||
# define EXTERN_DEFINED
|
||||
#endif
|
||||
|
||||
#ifdef OS2
|
||||
# define inDOS() (!(_emx_env & 0x200))
|
||||
#endif
|
||||
|
||||
#ifndef EXECSHELL
|
||||
/* shell to exec scripts (see also $SHELL initialization in main.c) */
|
||||
# ifdef OS2
|
||||
# define EXECSHELL (inDOS() ? "c:\\command.com" : "c:\\os2\\cmd.exe")
|
||||
# define EXECSHELL_STR (inDOS() ? "COMSPEC" : "OS2_SHELL")
|
||||
# else /* OS2 */
|
||||
# define EXECSHELL "/bin/sh"
|
||||
# define EXECSHELL_STR "EXECSHELL"
|
||||
# endif /* OS2 */
|
||||
#endif
|
||||
|
||||
/* ISABSPATH() means path is fully and completely specified,
|
||||
* ISROOTEDPATH() means a .. as the first component is a no-op,
|
||||
* ISRELPATH() means $PWD can be tacked on to get an absolute path.
|
||||
*
|
||||
* OS Path ISABSPATH ISROOTEDPATH ISRELPATH
|
||||
* unix /foo yes yes no
|
||||
* unix foo no no yes
|
||||
* unix ../foo no no yes
|
||||
* os2+cyg a:/foo yes yes no
|
||||
* os2+cyg a:foo no no no
|
||||
* os2+cyg /foo no yes no
|
||||
* os2+cyg foo no no yes
|
||||
* os2+cyg ../foo no no yes
|
||||
* cyg //foo yes yes no
|
||||
*/
|
||||
#ifdef OS2
|
||||
# define PATHSEP ';'
|
||||
# define DIRSEP '/' /* even though \ is native */
|
||||
# define DIRSEPSTR "\\"
|
||||
# define ISDIRSEP(c) ((c) == '\\' || (c) == '/')
|
||||
# define ISABSPATH(s) (((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2])))
|
||||
# define ISROOTEDPATH(s) (ISDIRSEP((s)[0]) || ISABSPATH(s))
|
||||
# define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0])))
|
||||
# define FILECHCONV(c) (isascii(c) && isupper(c) ? tolower(c) : c)
|
||||
# define FILECMP(s1, s2) stricmp(s1, s2)
|
||||
# define FILENCMP(s1, s2, n) strnicmp(s1, s2, n)
|
||||
extern char *ksh_strchr_dirsep(const char *path);
|
||||
extern char *ksh_strrchr_dirsep(const char *path);
|
||||
# define chdir _chdir2
|
||||
# define getcwd _getcwd2
|
||||
#else
|
||||
# define PATHSEP ':'
|
||||
# define DIRSEP '/'
|
||||
# define DIRSEPSTR "/"
|
||||
# define ISDIRSEP(c) ((c) == '/')
|
||||
#ifdef __CYGWIN__
|
||||
# define ISABSPATH(s) \
|
||||
(((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2])) || ISDIRSEP((s)[0]))
|
||||
# define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0])))
|
||||
#else /* __CYGWIN__ */
|
||||
# define ISABSPATH(s) ISDIRSEP((s)[0])
|
||||
# define ISRELPATH(s) (!ISABSPATH(s))
|
||||
#endif /* __CYGWIN__ */
|
||||
# define ISROOTEDPATH(s) ISABSPATH(s)
|
||||
# define FILECHCONV(c) c
|
||||
# define FILECMP(s1, s2) strcmp(s1, s2)
|
||||
# define FILENCMP(s1, s2, n) strncmp(s1, s2, n)
|
||||
# define ksh_strchr_dirsep(p) strchr(p, DIRSEP)
|
||||
# define ksh_strrchr_dirsep(p) strrchr(p, DIRSEP)
|
||||
#endif
|
||||
|
||||
typedef int bool_t;
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
|
||||
#define NELEM(a) (sizeof(a) / sizeof((a)[0]))
|
||||
#define sizeofN(type, n) (sizeof(type) * (n))
|
||||
#define BIT(i) (1<<(i)) /* define bit in flag */
|
||||
|
||||
/* Table flag type - needs > 16 and < 32 bits */
|
||||
typedef INT32 Tflag;
|
||||
|
||||
#define NUFILE 32 /* Number of user-accessible files */
|
||||
#define FDBASE 10 /* First file usable by Shell */
|
||||
|
||||
/* you're not going to run setuid shell scripts, are you? */
|
||||
#define eaccess(path, mode) access(path, mode)
|
||||
|
||||
/* Make MAGIC a char that might be printed to make bugs more obvious, but
|
||||
* not a char that is used often. Also, can't use the high bit as it causes
|
||||
* portability problems (calling strchr(x, 0x80|'x') is error prone).
|
||||
*/
|
||||
#define MAGIC (7) /* prefix for *?[!{,} during expand */
|
||||
#define ISMAGIC(c) ((unsigned char)(c) == MAGIC)
|
||||
#define NOT '!' /* might use ^ (ie, [!...] vs [^..]) */
|
||||
|
||||
#define LINE 1024 /* input line size */
|
||||
#define PATH 1024 /* pathname size (todo: PATH_MAX/pathconf()) */
|
||||
#define ARRAYMAX 1023 /* max array index */
|
||||
|
||||
EXTERN const char *kshname; /* $0 */
|
||||
EXTERN pid_t kshpid; /* $$, shell pid */
|
||||
EXTERN pid_t procpid; /* pid of executing process */
|
||||
EXTERN uid_t ksheuid; /* effective uid of shell */
|
||||
EXTERN int exstat; /* exit status */
|
||||
EXTERN int subst_exstat; /* exit status of last $(..)/`..` */
|
||||
EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */
|
||||
|
||||
/*
|
||||
* Area-based allocation built on malloc/free
|
||||
*/
|
||||
typedef struct Area {
|
||||
struct link *freelist; /* free list */
|
||||
} Area;
|
||||
|
||||
EXTERN Area aperm; /* permanent object space */
|
||||
#define APERM &aperm
|
||||
#define ATEMP &e->area
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
# include "chmem.h" /* a debugging front end for malloc et. al. */
|
||||
#endif /* MEM_DEBUG */
|
||||
|
||||
#ifdef KSH_DEBUG
|
||||
# define kshdebug_init() kshdebug_init_()
|
||||
# define kshdebug_printf(a) kshdebug_printf_ a
|
||||
# define kshdebug_dump(a) kshdebug_dump_ a
|
||||
#else /* KSH_DEBUG */
|
||||
# define kshdebug_init()
|
||||
# define kshdebug_printf(a)
|
||||
# define kshdebug_dump(a)
|
||||
#endif /* KSH_DEBUG */
|
||||
|
||||
/*
|
||||
* parsing & execution environment
|
||||
*/
|
||||
EXTERN struct env {
|
||||
short type; /* environment type - see below */
|
||||
short flags; /* EF_* */
|
||||
Area area; /* temporary allocation area */
|
||||
struct block *loc; /* local variables and functions */
|
||||
short *savefd; /* original redirected fd's */
|
||||
struct env *oenv; /* link to previous environment */
|
||||
ksh_jmp_buf jbuf; /* long jump back to env creator */
|
||||
struct temp *temps; /* temp files */
|
||||
} *e;
|
||||
|
||||
/* struct env.type values */
|
||||
#define E_NONE 0 /* dummy environment */
|
||||
#define E_PARSE 1 /* parsing command # */
|
||||
#define E_FUNC 2 /* executing function # */
|
||||
#define E_INCL 3 /* including a file via . # */
|
||||
#define E_EXEC 4 /* executing command tree */
|
||||
#define E_LOOP 5 /* executing for/while # */
|
||||
#define E_ERRH 6 /* general error handler # */
|
||||
/* # indicates env has valid jbuf (see unwind()) */
|
||||
|
||||
/* struct env.flag values */
|
||||
#define EF_FUNC_PARSE BIT(0) /* function being parsed */
|
||||
#define EF_BRKCONT_PASS BIT(1) /* set if E_LOOP must pass break/continue on */
|
||||
#define EF_FAKE_SIGDIE BIT(2) /* hack to get info from unwind to quitenv */
|
||||
|
||||
/* Do breaks/continues stop at env type e? */
|
||||
#define STOP_BRKCONT(t) ((t) == E_NONE || (t) == E_PARSE \
|
||||
|| (t) == E_FUNC || (t) == E_INCL)
|
||||
/* Do returns stop at env type e? */
|
||||
#define STOP_RETURN(t) ((t) == E_FUNC || (t) == E_INCL)
|
||||
|
||||
/* values for ksh_siglongjmp(e->jbuf, 0) */
|
||||
#define LRETURN 1 /* return statement */
|
||||
#define LEXIT 2 /* exit statement */
|
||||
#define LERROR 3 /* errorf() called */
|
||||
#define LLEAVE 4 /* untrappable exit/error */
|
||||
#define LINTR 5 /* ^C noticed */
|
||||
#define LBREAK 6 /* break statement */
|
||||
#define LCONTIN 7 /* continue statement */
|
||||
#define LSHELL 8 /* return to interactive shell() */
|
||||
#define LAEXPR 9 /* error in arithmetic expression */
|
||||
|
||||
/* option processing */
|
||||
#define OF_CMDLINE 0x01 /* command line */
|
||||
#define OF_SET 0x02 /* set builtin */
|
||||
#define OF_SPECIAL 0x04 /* a special variable changing */
|
||||
#define OF_INTERNAL 0x08 /* set internally by shell */
|
||||
#define OF_ANY (OF_CMDLINE | OF_SET | OF_SPECIAL | OF_INTERNAL)
|
||||
|
||||
struct option {
|
||||
const char *name; /* long name of option */
|
||||
char c; /* character flag (if any) */
|
||||
short flags; /* OF_* */
|
||||
};
|
||||
extern const struct option goptions[];
|
||||
|
||||
/*
|
||||
* flags (the order of these enums MUST match the order in misc.c(options[]))
|
||||
*/
|
||||
enum sh_flag {
|
||||
FEXPORT = 0, /* -a: export all */
|
||||
#ifdef BRACE_EXPAND
|
||||
FBRACEEXPAND, /* enable {} globbing */
|
||||
#endif
|
||||
FBGNICE, /* bgnice */
|
||||
FCOMMAND, /* -c: (invocation) execute specified command */
|
||||
#ifdef EMACS
|
||||
FEMACS, /* emacs command editing */
|
||||
FEMACSUSEMETA, /* use 8th bit as meta */
|
||||
#endif
|
||||
FERREXIT, /* -e: quit on error */
|
||||
#ifdef EMACS
|
||||
FGMACS, /* gmacs command editing */
|
||||
#endif
|
||||
FIGNOREEOF, /* eof does not exit */
|
||||
FTALKING, /* -i: interactive */
|
||||
FKEYWORD, /* -k: name=value anywhere */
|
||||
FLOGIN, /* -l: a login shell */
|
||||
FMARKDIRS, /* mark dirs with / in file name completion */
|
||||
FMONITOR, /* -m: job control monitoring */
|
||||
FNOCLOBBER, /* -C: don't overwrite existing files */
|
||||
FNOEXEC, /* -n: don't execute any commands */
|
||||
FNOGLOB, /* -f: don't do file globbing */
|
||||
FNOHUP, /* -H: don't kill running jobs when login shell exits */
|
||||
FNOLOG, /* don't save functions in history (ignored) */
|
||||
#ifdef JOBS
|
||||
FNOTIFY, /* -b: asynchronous job completion notification */
|
||||
#endif
|
||||
FNOUNSET, /* -u: using an unset var is an error */
|
||||
FPHYSICAL, /* -o physical: don't do logical cd's/pwd's */
|
||||
FPOSIX, /* -o posix: be posixly correct */
|
||||
FPRIVILEGED, /* -p: use suid_profile */
|
||||
FRESTRICTED, /* -r: restricted shell */
|
||||
FSTDIN, /* -s: (invocation) parse stdin */
|
||||
FTRACKALL, /* -h: create tracked aliases for all commands */
|
||||
FVERBOSE, /* -v: echo input */
|
||||
#ifdef VI
|
||||
FVI, /* vi command editing */
|
||||
FVIRAW, /* always read in raw mode (ignored) */
|
||||
FVISHOW8, /* display chars with 8th bit set as is (versus M-) */
|
||||
FVITABCOMPLETE, /* enable tab as file name completion char */
|
||||
FVIESCCOMPLETE, /* enable ESC as file name completion in command mode */
|
||||
#endif
|
||||
FXTRACE, /* -x: execution trace */
|
||||
FTALKING_I, /* (internal): initial shell was interactive */
|
||||
FNFLAGS /* (place holder: how many flags are there) */
|
||||
};
|
||||
|
||||
#define Flag(f) (shell_flags[(int) (f)])
|
||||
|
||||
EXTERN char shell_flags [FNFLAGS];
|
||||
|
||||
EXTERN char null [] I__(""); /* null value for variable */
|
||||
EXTERN char space [] I__(" ");
|
||||
EXTERN char newline [] I__("\n");
|
||||
EXTERN char slash [] I__("/");
|
||||
|
||||
enum temp_type {
|
||||
TT_HEREDOC_EXP, /* expanded heredoc */
|
||||
TT_HIST_EDIT /* temp file used for history editing (fc -e) */
|
||||
};
|
||||
typedef enum temp_type Temp_type;
|
||||
/* temp/heredoc files. The file is removed when the struct is freed. */
|
||||
struct temp {
|
||||
struct temp *next;
|
||||
struct shf *shf;
|
||||
int pid; /* pid of process parsed here-doc */
|
||||
Temp_type type;
|
||||
char *name;
|
||||
};
|
||||
|
||||
/*
|
||||
* stdio and our IO routines
|
||||
*/
|
||||
|
||||
#define shl_spare (&shf_iob[0]) /* for c_read()/c_print() */
|
||||
#define shl_stdout (&shf_iob[1])
|
||||
#define shl_out (&shf_iob[2])
|
||||
EXTERN int shl_stdout_ok;
|
||||
|
||||
/*
|
||||
* trap handlers
|
||||
*/
|
||||
typedef struct trap {
|
||||
int signal; /* signal number */
|
||||
const char *name; /* short name */
|
||||
const char *mess; /* descriptive name */
|
||||
char *trap; /* trap command */
|
||||
int volatile set; /* trap pending */
|
||||
int flags; /* TF_* */
|
||||
handler_t cursig; /* current handler (valid if TF_ORIG_* set) */
|
||||
handler_t shtrap; /* shell signal handler */
|
||||
} Trap;
|
||||
|
||||
/* values for Trap.flags */
|
||||
#define TF_SHELL_USES BIT(0) /* shell uses signal, user can't change */
|
||||
#define TF_USER_SET BIT(1) /* user has (tried to) set trap */
|
||||
#define TF_ORIG_IGN BIT(2) /* original action was SIG_IGN */
|
||||
#define TF_ORIG_DFL BIT(3) /* original action was SIG_DFL */
|
||||
#define TF_EXEC_IGN BIT(4) /* restore SIG_IGN just before exec */
|
||||
#define TF_EXEC_DFL BIT(5) /* restore SIG_DFL just before exec */
|
||||
#define TF_DFL_INTR BIT(6) /* when received, default action is LINTR */
|
||||
#define TF_TTY_INTR BIT(7) /* tty generated signal (see j_waitj) */
|
||||
#define TF_CHANGED BIT(8) /* used by runtrap() to detect trap changes */
|
||||
#define TF_FATAL BIT(9) /* causes termination if not trapped */
|
||||
|
||||
/* values for setsig()/setexecsig() flags argument */
|
||||
#define SS_RESTORE_MASK 0x3 /* how to restore a signal before an exec() */
|
||||
#define SS_RESTORE_CURR 0 /* leave current handler in place */
|
||||
#define SS_RESTORE_ORIG 1 /* restore original handler */
|
||||
#define SS_RESTORE_DFL 2 /* restore to SIG_DFL */
|
||||
#define SS_RESTORE_IGN 3 /* restore to SIG_IGN */
|
||||
#define SS_FORCE BIT(3) /* set signal even if original signal ignored */
|
||||
#define SS_USER BIT(4) /* user is doing the set (ie, trap command) */
|
||||
#define SS_SHTRAP BIT(5) /* trap for internal use (CHLD,ALRM,WINCH) */
|
||||
|
||||
#define SIGEXIT_ 0 /* for trap EXIT */
|
||||
#define SIGERR_ SIGNALS /* for trap ERR */
|
||||
|
||||
EXTERN int volatile trap; /* traps pending? */
|
||||
EXTERN int volatile intrsig; /* pending trap interrupts executing command */
|
||||
EXTERN int volatile fatal_trap;/* received a fatal signal */
|
||||
#ifndef FROM_TRAP_C
|
||||
/* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
|
||||
extern Trap sigtraps[SIGNALS+1];
|
||||
#endif /* !FROM_TRAP_C */
|
||||
|
||||
#ifdef KSH
|
||||
/*
|
||||
* TMOUT support
|
||||
*/
|
||||
/* values for ksh_tmout_state */
|
||||
enum tmout_enum {
|
||||
TMOUT_EXECUTING = 0, /* executing commands */
|
||||
TMOUT_READING, /* waiting for input */
|
||||
TMOUT_LEAVING /* have timed out */
|
||||
};
|
||||
EXTERN unsigned int ksh_tmout;
|
||||
EXTERN enum tmout_enum ksh_tmout_state I__(TMOUT_EXECUTING);
|
||||
#endif /* KSH */
|
||||
|
||||
/* For "You have stopped jobs" message */
|
||||
EXTERN int really_exit;
|
||||
|
||||
/*
|
||||
* fast character classes
|
||||
*/
|
||||
#define C_ALPHA BIT(0) /* a-z_A-Z */
|
||||
#define C_DIGIT BIT(1) /* 0-9 */
|
||||
#define C_LEX1 BIT(2) /* \0 \t\n|&;<>() */
|
||||
#define C_VAR1 BIT(3) /* *@#!$-? */
|
||||
#define C_IFSWS BIT(4) /* \t \n (IFS white space) */
|
||||
#define C_SUBOP1 BIT(5) /* "=-+?" */
|
||||
#define C_SUBOP2 BIT(6) /* "#%" */
|
||||
#define C_IFS BIT(7) /* $IFS */
|
||||
#define C_QUOTE BIT(8) /* \n\t"#$&'()*;<>?[\`| (needing quoting) */
|
||||
|
||||
extern short ctypes [];
|
||||
|
||||
#define ctype(c, t) !!(ctypes[(unsigned char)(c)]&(t))
|
||||
#define letter(c) ctype(c, C_ALPHA)
|
||||
#define digit(c) ctype(c, C_DIGIT)
|
||||
#define letnum(c) ctype(c, C_ALPHA|C_DIGIT)
|
||||
|
||||
EXTERN int ifs0 I__(' '); /* for "$*" */
|
||||
|
||||
/* Argument parsing for built-in commands and getopts command */
|
||||
|
||||
/* Values for Getopt.flags */
|
||||
#define GF_ERROR BIT(0) /* call errorf() if there is an error */
|
||||
#define GF_PLUSOPT BIT(1) /* allow +c as an option */
|
||||
#define GF_NONAME BIT(2) /* don't print argv[0] in errors */
|
||||
|
||||
/* Values for Getopt.info */
|
||||
#define GI_MINUS BIT(0) /* an option started with -... */
|
||||
#define GI_PLUS BIT(1) /* an option started with +... */
|
||||
#define GI_MINUSMINUS BIT(2) /* arguments were ended with -- */
|
||||
|
||||
typedef struct {
|
||||
int optind;
|
||||
int uoptind;/* what user sees in $OPTIND */
|
||||
char *optarg;
|
||||
int flags; /* see GF_* */
|
||||
int info; /* see GI_* */
|
||||
unsigned int p; /* 0 or index into argv[optind - 1] */
|
||||
char buf[2]; /* for bad option OPTARG value */
|
||||
} Getopt;
|
||||
|
||||
EXTERN Getopt builtin_opt; /* for shell builtin commands */
|
||||
EXTERN Getopt user_opt; /* parsing state for getopts builtin command */
|
||||
|
||||
#ifdef KSH
|
||||
/* This for co-processes */
|
||||
|
||||
typedef INT32 Coproc_id; /* something that won't (realisticly) wrap */
|
||||
struct coproc {
|
||||
int read; /* pipe from co-process's stdout */
|
||||
int readw; /* other side of read (saved temporarily) */
|
||||
int write; /* pipe to co-process's stdin */
|
||||
Coproc_id id; /* id of current output pipe */
|
||||
int njobs; /* number of live jobs using output pipe */
|
||||
void *job; /* 0 or job of co-process using input pipe */
|
||||
};
|
||||
EXTERN struct coproc coproc;
|
||||
#endif /* KSH */
|
||||
|
||||
/* Used in jobs.c and by coprocess stuff in exec.c */
|
||||
#ifdef JOB_SIGS
|
||||
EXTERN sigset_t sm_default, sm_sigchld;
|
||||
#endif /* JOB_SIGS */
|
||||
|
||||
extern char ksh_version[];
|
||||
|
||||
/* name of called builtin function (used by error functions) */
|
||||
EXTERN char *builtin_argv0;
|
||||
EXTERN Tflag builtin_flag; /* flags of called builtin (SPEC_BI, etc.) */
|
||||
|
||||
/* current working directory, and size of memory allocated for same */
|
||||
EXTERN char *current_wd;
|
||||
EXTERN int current_wd_size;
|
||||
|
||||
#ifdef EDIT
|
||||
/* Minimum required space to work with on a line - if the prompt leaves less
|
||||
* space than this on a line, the prompt is truncated.
|
||||
*/
|
||||
# define MIN_EDIT_SPACE 7
|
||||
/* Minimum allowed value for x_cols: 2 for prompt, 3 for " < " at end of line
|
||||
*/
|
||||
# define MIN_COLS (2 + MIN_EDIT_SPACE + 3)
|
||||
EXTERN int x_cols I__(80); /* tty columns */
|
||||
#else
|
||||
# define x_cols 80 /* for pr_menu(exec.c) */
|
||||
#endif
|
||||
|
||||
/* These to avoid bracket matching problems */
|
||||
#define OPAREN '('
|
||||
#define CPAREN ')'
|
||||
#define OBRACK '['
|
||||
#define CBRACK ']'
|
||||
#define OBRACE '{'
|
||||
#define CBRACE '}'
|
||||
|
||||
/* Determine the location of the system (common) profile */
|
||||
#ifndef KSH_SYSTEM_PROFILE
|
||||
# ifdef __NeXT
|
||||
# define KSH_SYSTEM_PROFILE "/etc/profile.std"
|
||||
# else /* __NeXT */
|
||||
# define KSH_SYSTEM_PROFILE "/etc/profile"
|
||||
# endif /* __NeXT */
|
||||
#endif /* KSH_SYSTEM_PROFILE */
|
||||
|
||||
/* Used by v_evaluate() and setstr() to control action when error occurs */
|
||||
#define KSH_UNWIND_ERROR 0 /* unwind the stack (longjmp) */
|
||||
#define KSH_RETURN_ERROR 1 /* return 1/0 for success/failure */
|
||||
|
||||
#include "shf.h"
|
||||
#include "table.h"
|
||||
#include "tree.h"
|
||||
#include "expand.h"
|
||||
#include "lex.h"
|
||||
#include "proto.h"
|
||||
|
||||
/* be sure not to interfere with anyone else's idea about EXTERN */
|
||||
#ifdef EXTERN_DEFINED
|
||||
# undef EXTERN_DEFINED
|
||||
# undef EXTERN
|
||||
#endif
|
||||
#undef I__
|
1302
bin/ksh/shf.c
Normal file
1302
bin/ksh/shf.c
Normal file
File diff suppressed because it is too large
Load diff
87
bin/ksh/shf.h
Normal file
87
bin/ksh/shf.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/* $NetBSD: shf.h,v 1.3 1999/10/20 15:10:00 hubertf Exp $ */
|
||||
|
||||
#ifndef SHF_H
|
||||
# define SHF_H
|
||||
|
||||
/*
|
||||
* Shell file I/O routines
|
||||
*/
|
||||
/* $Id: shf.h,v 1.3 1999/10/20 15:10:00 hubertf Exp $ */
|
||||
|
||||
#define SHF_BSIZE 512
|
||||
|
||||
#define shf_fileno(shf) ((shf)->fd)
|
||||
#define shf_setfileno(shf,nfd) ((shf)->fd = (nfd))
|
||||
#define shf_getc(shf) ((shf)->rnleft > 0 ? (shf)->rnleft--, *(shf)->rp++ : \
|
||||
shf_getchar(shf))
|
||||
#define shf_putc(c, shf) ((shf)->wnleft == 0 ? shf_putchar((c), (shf)) \
|
||||
: ((shf)->wnleft--, *(shf)->wp++ = (c)))
|
||||
#define shf_eof(shf) ((shf)->flags & SHF_EOF)
|
||||
#define shf_error(shf) ((shf)->flags & SHF_ERROR)
|
||||
#define shf_errno(shf) ((shf)->errno_)
|
||||
#define shf_clearerr(shf) ((shf)->flags &= ~(SHF_EOF | SHF_ERROR))
|
||||
|
||||
/* Flags passed to shf_*open() */
|
||||
#define SHF_RD 0x0001
|
||||
#define SHF_WR 0x0002
|
||||
#define SHF_RDWR (SHF_RD|SHF_WR)
|
||||
#define SHF_ACCMODE 0x0003 /* mask */
|
||||
#define SHF_GETFL 0x0004 /* use fcntl() to figure RD/WR flags */
|
||||
#define SHF_UNBUF 0x0008 /* unbuffered I/O */
|
||||
#define SHF_CLEXEC 0x0010 /* set close on exec flag */
|
||||
#define SHF_MAPHI 0x0020 /* make fd > FDBASE (and close orig)
|
||||
* (shf_open() only) */
|
||||
#define SHF_DYNAMIC 0x0040 /* string: increase buffer as needed */
|
||||
#define SHF_INTERRUPT 0x0080 /* EINTR in read/write causes error */
|
||||
/* Flags used internally */
|
||||
#define SHF_STRING 0x0100 /* a string, not a file */
|
||||
#define SHF_ALLOCS 0x0200 /* shf and shf->buf were alloc()ed */
|
||||
#define SHF_ALLOCB 0x0400 /* shf->buf was alloc()ed */
|
||||
#define SHF_ERROR 0x0800 /* read()/write() error */
|
||||
#define SHF_EOF 0x1000 /* read eof (sticky) */
|
||||
#define SHF_READING 0x2000 /* currently reading: rnleft,rp valid */
|
||||
#define SHF_WRITING 0x4000 /* currently writing: wnleft,wp valid */
|
||||
|
||||
|
||||
struct shf {
|
||||
int flags; /* see SHF_* */
|
||||
unsigned char *rp; /* read: current position in buffer */
|
||||
int rbsize; /* size of buffer (1 if SHF_UNBUF) */
|
||||
int rnleft; /* read: how much data left in buffer */
|
||||
unsigned char *wp; /* write: current position in buffer */
|
||||
int wbsize; /* size of buffer (0 if SHF_UNBUF) */
|
||||
int wnleft; /* write: how much space left in buffer */
|
||||
unsigned char *buf; /* buffer */
|
||||
int fd; /* file descriptor */
|
||||
int errno_; /* saved value of errno after error */
|
||||
int bsize; /* actual size of buf */
|
||||
Area *areap; /* area shf/buf were allocated in */
|
||||
};
|
||||
|
||||
extern struct shf shf_iob[];
|
||||
|
||||
struct shf *shf_open ARGS((const char *name, int oflags, int mode,
|
||||
int sflags));
|
||||
struct shf *shf_fdopen ARGS((int fd, int sflags, struct shf *shf));
|
||||
struct shf *shf_reopen ARGS((int fd, int sflags, struct shf *shf));
|
||||
struct shf *shf_sopen ARGS((char *buf, int bsize, int sflags,
|
||||
struct shf *shf));
|
||||
int shf_close ARGS((struct shf *shf));
|
||||
int shf_fdclose ARGS((struct shf *shf));
|
||||
char *shf_sclose ARGS((struct shf *shf));
|
||||
int shf_finish ARGS((struct shf *shf));
|
||||
int shf_flush ARGS((struct shf *shf));
|
||||
int shf_seek ARGS((struct shf *shf, off_t where, int from));
|
||||
int shf_read ARGS((char *buf, int bsize, struct shf *shf));
|
||||
char *shf_getse ARGS((char *buf, int bsize, struct shf *shf));
|
||||
int shf_getchar ARGS((struct shf *shf));
|
||||
int shf_ungetc ARGS((int c, struct shf *shf));
|
||||
int shf_putchar ARGS((int c, struct shf *shf));
|
||||
int shf_puts ARGS((const char *s, struct shf *shf));
|
||||
int shf_write ARGS((const char *buf, int nbytes, struct shf *shf));
|
||||
int shf_fprintf ARGS((struct shf *shf, const char *fmt, ...));
|
||||
int shf_snprintf ARGS((char *buf, int bsize, const char *fmt, ...));
|
||||
char *shf_smprintf ARGS((const char *fmt, ...));
|
||||
int shf_vfprintf ARGS((struct shf *, const char *fmt, va_list args));
|
||||
|
||||
#endif /* SHF_H */
|
486
bin/ksh/sigact.c
Normal file
486
bin/ksh/sigact.c
Normal file
|
@ -0,0 +1,486 @@
|
|||
/* $NetBSD: sigact.c,v 1.4 2003/06/23 11:39:03 agc Exp $ */
|
||||
|
||||
/* NAME:
|
||||
* sigact.c - fake sigaction(2)
|
||||
*
|
||||
* SYNOPSIS:
|
||||
* #include "sigact.h"
|
||||
*
|
||||
* int sigaction(int sig, struct sigaction *act,
|
||||
* struct sigaction *oact);
|
||||
* int sigaddset(sigset_t *mask, int sig);
|
||||
* int sigdelset(sigset_t *mask, int sig);
|
||||
* int sigemptyset(sigset_t *mask);
|
||||
* int sigfillset(sigset_t *mask);
|
||||
* int sigismember(sigset_t *mask, int sig);
|
||||
* int sigpending(sigset_t *set);
|
||||
* int sigprocmask(int how, sigset_t *set, sigset_t *oset);
|
||||
* int sigsuspend(sigset_t *mask);
|
||||
*
|
||||
* RETSIGTYPE (*Signal(int sig, RETSIGTYPE (*disp)(int)))(int);
|
||||
*
|
||||
* DESCRIPTION:
|
||||
* This is a fake sigaction implementation. It uses
|
||||
* sigsetmask(2) et al or sigset(2) and friends if
|
||||
* available, otherwise it just uses signal(2). If it
|
||||
* thinks sigaction(2) really exists it compiles to "almost"
|
||||
* nothing.
|
||||
*
|
||||
* In any case it provides a Signal() function that is
|
||||
* implemented in terms of sigaction().
|
||||
* If not using signal(2) as part of the underlying
|
||||
* implementation (USE_SIGNAL or USE_SIGMASK), and
|
||||
* NO_SIGNAL is not defined, it also provides a signal()
|
||||
* function that calls Signal().
|
||||
*
|
||||
* The need for all this mucking about is the problems
|
||||
* caused by mixing various signal handling mechanisms in
|
||||
* the one process. This module allows for a consistent
|
||||
* POSIX compliant interface to whatever is actually
|
||||
* available.
|
||||
*
|
||||
* sigaction() allows the caller to examine and/or set the
|
||||
* action to be associated with a given signal. "act" and
|
||||
* "oact" are pointers to 'sigaction structs':
|
||||
*.nf
|
||||
*
|
||||
* struct sigaction
|
||||
* {
|
||||
* RETSIGTYPE (*sa_handler)();
|
||||
* sigset_t sa_mask;
|
||||
* int sa_flags;
|
||||
* };
|
||||
*.fi
|
||||
*
|
||||
* RETSIGTYPE is normally 'void' in the POSIX implementation
|
||||
* and for most current systems. On some older UNIX
|
||||
* systems, signal handlers do not return 'void', so
|
||||
* this implementation keeps 'sa_handler' inline with the
|
||||
* hosts normal signal handling conventions.
|
||||
* 'sa_mask' controls which signals will be blocked while
|
||||
* the selected signal handler is active. It is not used
|
||||
* in this implementation.
|
||||
* 'sa_flags' controls various semantics such as whether
|
||||
* system calls should be automagically restarted
|
||||
* (SA_RESTART) etc. It is not used in this
|
||||
* implementation.
|
||||
* Either "act" or "oact" may be NULL in which case the
|
||||
* appropriate operation is skipped.
|
||||
*
|
||||
* sigaddset() adds "sig" to the sigset_t pointed to by "mask".
|
||||
*
|
||||
* sigdelset() removes "sig" from the sigset_t pointed to
|
||||
* by "mask".
|
||||
*
|
||||
* sigemptyset() makes the sigset_t pointed to by "mask" empty.
|
||||
*
|
||||
* sigfillset() makes the sigset_t pointed to by "mask"
|
||||
* full ie. match all signals.
|
||||
*
|
||||
* sigismember() returns true if "sig" is found in "*mask".
|
||||
*
|
||||
* sigpending() is supposed to return "set" loaded with the
|
||||
* set of signals that are blocked and pending for the
|
||||
* calling process. It does nothing in this impementation.
|
||||
*
|
||||
* sigprocmask() is used to examine and/or change the
|
||||
* signal mask for the calling process. Either "set" or
|
||||
* "oset" may be NULL in which case the appropriate
|
||||
* operation is skipped. "how" may be one of SIG_BLOCK,
|
||||
* SIG_UNBLOCK or SIG_SETMASK. If this package is built
|
||||
* with USE_SIGNAL, then this routine achieves nothing.
|
||||
*
|
||||
* sigsuspend() sets the signal mask to "*mask" and waits
|
||||
* for a signal to be delivered after which the previous
|
||||
* mask is restored.
|
||||
*
|
||||
*
|
||||
* RETURN VALUE:
|
||||
* 0==success, -1==failure
|
||||
*
|
||||
* BUGS:
|
||||
* Since we fake most of this, don't expect fancy usage to
|
||||
* work.
|
||||
*
|
||||
* AUTHOR:
|
||||
* Simon J. Gerraty <sjg@zen.void.oz.au>
|
||||
*/
|
||||
/* COPYRIGHT:
|
||||
* @(#)Copyright (c) 1992 Simon J. Gerraty
|
||||
*
|
||||
* This is free software. It comes with NO WARRANTY.
|
||||
* Permission to use, modify and distribute this source code
|
||||
* is granted subject to the following conditions.
|
||||
* 1/ that that the above copyright notice and this notice
|
||||
* are preserved in all copies and that due credit be given
|
||||
* to the author.
|
||||
* 2/ that any changes to this code are clearly commented
|
||||
* as such so that the author does get blamed for bugs
|
||||
* other than his own.
|
||||
*
|
||||
* Please send copies of changes and bug-fixes to:
|
||||
* sjg@zen.void.oz.au
|
||||
*
|
||||
*/
|
||||
/* Changes to sigact.c for pdksh, Michael Rendell <michael@cs.mun.ca>:
|
||||
* - sigsuspend(): pass *mask to bsd4.2 sigpause instead of mask.
|
||||
* - changed SIG_HDLR to RETSIGTYPE for use with GNU autoconf
|
||||
* - added and used RETSIGVAL
|
||||
* - include sh.h instead of signal.h (to get *_SIGNALS macros)
|
||||
* - changed if !SA_NOCLDSTOP ... to USE_FAKE_SIGACT to avoid confusion
|
||||
* - set the USE_* defines using the *_SIGNALS defines from autoconf
|
||||
* - sigaction(): if using BSD signals, use sigvec() (used to use
|
||||
* signal()) and set the SV_INTERRUPT flag (POSIX says syscalls
|
||||
* are interrupted and pdksh needs this behaviour).
|
||||
* - define IS_KSH before including anything; ifdef out routines
|
||||
* not used in ksh if IS_KSH is defined (same in sigact.h).
|
||||
* - use ARGS() instead of __P()
|
||||
* - sigaction(),sigsuspend(),Signal(),signal(): use handler_t typedef
|
||||
* instead of explicit type.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: sigact.c,v 1.4 2003/06/23 11:39:03 agc Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
#include <signal.h>
|
||||
*/
|
||||
#define IS_KSH
|
||||
#include "sh.h"
|
||||
|
||||
/*
|
||||
#ifndef __P
|
||||
# define __P(p) p
|
||||
#endif
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* some systems have a faulty sigaction() implementation!
|
||||
* Allow us to bypass it.
|
||||
* Or they may have installed sigact.h as signal.h which is why
|
||||
* we have SA_NOCLDSTOP defined.
|
||||
*/
|
||||
#ifdef USE_FAKE_SIGACT /* let autoconf decide.. */
|
||||
/* #if !defined(SA_NOCLDSTOP) || defined(_SIGACT_H) || defined(USE_SIGNAL) || defined(USE_SIGSET) || defined(USE_SIGMASK) */
|
||||
|
||||
/* Let autoconf decide which to use */
|
||||
#ifdef BSD42_SIGNALS
|
||||
# define USE_SIGMASK
|
||||
#else
|
||||
# ifdef BSD41_SIGNALS
|
||||
# define USE_SIGSET
|
||||
# else
|
||||
# define USE_SIGNAL
|
||||
# endif
|
||||
#endif /* BSD42_SIGNALS */
|
||||
|
||||
/*
|
||||
* if we haven't been told,
|
||||
* try and guess what we should implement with.
|
||||
*/
|
||||
#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL)
|
||||
# if defined(sigmask) || defined(BSD) || defined(_BSD) && !defined(BSD41)
|
||||
# define USE_SIGMASK
|
||||
# else
|
||||
# ifndef NO_SIGSET
|
||||
# define USE_SIGSET
|
||||
# else
|
||||
# define USE_SIGNAL
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
/*
|
||||
* if we still don't know, we're in trouble
|
||||
*/
|
||||
#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL)
|
||||
error must know what to implement with
|
||||
#endif
|
||||
|
||||
#include "sigact.h"
|
||||
|
||||
/*
|
||||
* in case signal() has been mapped to our Signal().
|
||||
*/
|
||||
#undef signal
|
||||
|
||||
|
||||
int
|
||||
sigaction(sig, act, oact)
|
||||
int sig;
|
||||
struct sigaction *act, *oact;
|
||||
{
|
||||
handler_t oldh;
|
||||
|
||||
if (act)
|
||||
{
|
||||
#ifdef USE_SIGSET
|
||||
oldh = sigset(sig, act->sa_handler);
|
||||
#else
|
||||
# ifdef USE_SIGMASK
|
||||
struct sigvec nsv,osv;
|
||||
|
||||
nsv.sv_handler = act->sa_handler;
|
||||
nsv.sv_mask = 0; /* punt */
|
||||
nsv.sv_flags = SV_INTERRUPT; /* punt */
|
||||
sigvec(sig, &nsv, &osv);
|
||||
oldh = osv.sv_handler;
|
||||
# else /* USE_SIGMASK */
|
||||
oldh = signal(sig, act->sa_handler);
|
||||
# endif /* USE_SIGMASK */
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (oact)
|
||||
{
|
||||
#ifdef USE_SIGSET
|
||||
oldh = sigset(sig, SIG_IGN);
|
||||
#else
|
||||
oldh = signal(sig, SIG_IGN);
|
||||
#endif
|
||||
if (oldh != SIG_IGN && oldh != SIG_ERR)
|
||||
{
|
||||
#ifdef USE_SIGSET
|
||||
(void) sigset(sig, oldh);
|
||||
#else
|
||||
(void) signal(sig, oldh);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oact)
|
||||
{
|
||||
oact->sa_handler = oldh;
|
||||
}
|
||||
return 0; /* hey we're faking it */
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sigaddset(mask, sig)
|
||||
sigset_t *mask;
|
||||
int sig;
|
||||
{
|
||||
*mask |= sigmask(sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifndef IS_KSH
|
||||
int
|
||||
sigdelset(mask, sig)
|
||||
sigset_t *mask;
|
||||
int sig;
|
||||
{
|
||||
*mask &= ~(sigmask(sig));
|
||||
return 0;
|
||||
}
|
||||
#endif /* IS_KSH */
|
||||
|
||||
|
||||
int
|
||||
sigemptyset(mask)
|
||||
sigset_t *mask;
|
||||
{
|
||||
*mask = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifndef IS_KSH
|
||||
int
|
||||
sigfillset(mask)
|
||||
sigset_t *mask;
|
||||
{
|
||||
*mask = ~0;
|
||||
return 0;
|
||||
}
|
||||
#endif /* IS_KSH */
|
||||
|
||||
|
||||
#ifndef IS_KSH
|
||||
int
|
||||
sigismember(mask, sig)
|
||||
sigset_t *mask;
|
||||
int sig;
|
||||
{
|
||||
return ((*mask) & sigmask(sig));
|
||||
}
|
||||
#endif /* IS_KSH */
|
||||
|
||||
|
||||
#ifndef IS_KSH
|
||||
int
|
||||
sigpending(set)
|
||||
sigset_t *set;
|
||||
{
|
||||
return 0; /* faking it! */
|
||||
}
|
||||
#endif /* IS_KSH */
|
||||
|
||||
|
||||
int
|
||||
sigprocmask(how, set, oset)
|
||||
int how;
|
||||
sigset_t *set, *oset;
|
||||
{
|
||||
#ifdef USE_SIGSET
|
||||
register int i;
|
||||
#endif
|
||||
static sigset_t sm;
|
||||
static int once = 0;
|
||||
|
||||
if (!once)
|
||||
{
|
||||
/*
|
||||
* initally we clear sm,
|
||||
* there after, it represents the last
|
||||
* thing we did.
|
||||
*/
|
||||
once++;
|
||||
#ifdef USE_SIGMASK
|
||||
sm = sigblock(0);
|
||||
#else
|
||||
sm = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (oset)
|
||||
*oset = sm;
|
||||
if (set)
|
||||
{
|
||||
switch (how)
|
||||
{
|
||||
case SIG_BLOCK:
|
||||
sm |= *set;
|
||||
break;
|
||||
case SIG_UNBLOCK:
|
||||
sm &= ~(*set);
|
||||
break;
|
||||
case SIG_SETMASK:
|
||||
sm = *set;
|
||||
break;
|
||||
}
|
||||
#ifdef USE_SIGMASK
|
||||
(void) sigsetmask(sm);
|
||||
#else
|
||||
# ifdef USE_SIGSET
|
||||
for (i = 1; i < NSIG; i++)
|
||||
{
|
||||
if (how == SIG_UNBLOCK)
|
||||
{
|
||||
if (*set & sigmask(i))
|
||||
sigrelse(i);
|
||||
}
|
||||
else
|
||||
if (sm & sigmask(i))
|
||||
{
|
||||
sighold(i);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sigsuspend(mask)
|
||||
sigset_t *mask;
|
||||
{
|
||||
#ifdef USE_SIGMASK
|
||||
sigpause(*mask);
|
||||
#else
|
||||
register int i;
|
||||
|
||||
# ifdef USE_SIGSET
|
||||
|
||||
for (i = 1; i < NSIG; i++)
|
||||
{
|
||||
if (*mask & sigmask(i))
|
||||
{
|
||||
/* not the same sigpause() as above! */
|
||||
sigpause(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
# else /* signal(2) only */
|
||||
handler_t oldh;
|
||||
|
||||
/*
|
||||
* make sure that signals in mask will not
|
||||
* be ignored.
|
||||
*/
|
||||
for (i = 1; i < NSIG; i++)
|
||||
{
|
||||
if (*mask & sigmask(i))
|
||||
{
|
||||
if ((oldh = signal(i, SIG_DFL)) != SIG_ERR &&
|
||||
oldh != SIG_IGN &&
|
||||
oldh != SIG_DFL)
|
||||
(void) signal(i, oldh); /* restore handler */
|
||||
}
|
||||
}
|
||||
pause(); /* wait for a signal */
|
||||
# endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* USE_FAKE_SIGACT (was ! SA_NOCLDSTOP) */
|
||||
|
||||
#if !defined(RETSIGTYPE)
|
||||
# define RETSIGTYPE void
|
||||
# define RETSIGVAL
|
||||
#endif
|
||||
#if !defined(SIG_ERR)
|
||||
# define SIG_ERR (RETSIGTYPE (*)())-1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* an implementation of signal() using sigaction().
|
||||
*/
|
||||
|
||||
#ifndef IS_KSH
|
||||
handler_t Signal(sig, handler)
|
||||
int sig;
|
||||
handler_t handler;
|
||||
{
|
||||
struct sigaction act, oact;
|
||||
|
||||
act.sa_handler = handler;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(sig, &act, &oact) < 0)
|
||||
return (SIG_ERR);
|
||||
return (oact.sa_handler);
|
||||
}
|
||||
#endif /* IS_KSH */
|
||||
|
||||
#ifndef IS_KSH
|
||||
#if !defined(USE_SIGNAL) && !defined(USE_SIGMASK) && !defined(NO_SIGNAL)
|
||||
/*
|
||||
* ensure we avoid signal mayhem
|
||||
*/
|
||||
|
||||
handler_t signal(sig, handler)
|
||||
int sig;
|
||||
handler_t handler;
|
||||
{
|
||||
return (Signal(sig, handler));
|
||||
}
|
||||
#endif
|
||||
#endif /* IS_KSH */
|
||||
|
||||
/* This lot (for GNU-Emacs) goes at the end of the file. */
|
||||
/*
|
||||
* Local Variables:
|
||||
* version-control:t
|
||||
* comment-column:40
|
||||
* End:
|
||||
*/
|
125
bin/ksh/sigact.h
Normal file
125
bin/ksh/sigact.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/* $NetBSD: sigact.h,v 1.3 2002/05/25 23:29:17 wiz Exp $ */
|
||||
|
||||
/* NAME:
|
||||
* sigact.h - sigaction et al
|
||||
*
|
||||
* SYNOPSIS:
|
||||
* #include "sigact.h"
|
||||
*
|
||||
* DESCRIPTION:
|
||||
* This header is the interface to a fake sigaction(2)
|
||||
* implementation. It provides a POSIX compliant interface
|
||||
* to whatever signal handling mechanisms are available.
|
||||
* It also provides a Signal() function that is implemented
|
||||
* in terms of sigaction().
|
||||
* If not using signal(2) as part of the underlying
|
||||
* implementation (USE_SIGNAL or USE_SIGMASK), and
|
||||
* NO_SIGNAL is not defined, it also provides a signal()
|
||||
* function that calls Signal().
|
||||
*
|
||||
* SEE ALSO:
|
||||
* sigact.c
|
||||
*/
|
||||
/*
|
||||
* RCSid:
|
||||
* $NetBSD: sigact.h,v 1.3 2002/05/25 23:29:17 wiz Exp $
|
||||
*/
|
||||
/* Changes to sigact.h for pdksh, Michael Rendell <michael@cs.mun.ca>:
|
||||
* - changed SIG_HDLR to RETSIGTYPE for use with GNU autoconf
|
||||
* - added RETSIGVAL
|
||||
* - ifdef'd out ARGS(), volatile and const initializations
|
||||
* - ifdef'd out sigset_t definition - let autoconf handle it
|
||||
* - ifdef out routines not used in ksh if IS_KSH is defined
|
||||
* (same in sigact.c).
|
||||
*/
|
||||
#ifndef _SIGACT_H
|
||||
#define _SIGACT_H
|
||||
|
||||
/*
|
||||
* most modern systems use void for signal handlers but
|
||||
* not all.
|
||||
*/
|
||||
#ifndef RETSIGTYPE
|
||||
# define RETSIGTYPE void
|
||||
# define RETSIGVAL
|
||||
#endif
|
||||
|
||||
#if 0 /* ARGS(), volatile and const are already set up in config*.h -mhr */
|
||||
#undef ARGS
|
||||
#define ARGS(p) p
|
||||
#endif
|
||||
|
||||
#ifndef IS_KSH
|
||||
handler_t Signal ARGS((int sig, handler_t disp));
|
||||
#endif /* IS_KSH */
|
||||
|
||||
/*
|
||||
* if you want to install this header as signal.h,
|
||||
* modify this to pick up the original signal.h
|
||||
*/
|
||||
#ifndef SIGKILL
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
#ifndef SIG_ERR
|
||||
# define SIG_ERR ((handler_t) -1)
|
||||
#endif
|
||||
#ifndef BADSIG
|
||||
# define BADSIG SIG_ERR
|
||||
#endif
|
||||
|
||||
#ifndef SA_NOCLDSTOP
|
||||
/* we assume we need the fake sigaction */
|
||||
/* sa_flags */
|
||||
#define SA_NOCLDSTOP 1 /* don't send SIGCHLD on child stop */
|
||||
#define SA_RESTART 2 /* re-start I/O */
|
||||
|
||||
/* sigprocmask flags */
|
||||
#define SIG_BLOCK 1
|
||||
#define SIG_UNBLOCK 2
|
||||
#define SIG_SETMASK 4
|
||||
|
||||
#if 0 /* autoconf will define sigset_t if it isn't available */
|
||||
/*
|
||||
* this is a bit untidy
|
||||
*/
|
||||
#if !defined(__sys_stdtypes_h)
|
||||
typedef unsigned int sigset_t;
|
||||
#endif
|
||||
#endif /* 0 */
|
||||
|
||||
/*
|
||||
* POSIX sa_handler should return void, but since we are
|
||||
* implementing in terms of something else, it may
|
||||
* be appropriate to use the normal RETSIGTYPE return type
|
||||
*/
|
||||
struct sigaction
|
||||
{
|
||||
handler_t sa_handler;
|
||||
sigset_t sa_mask;
|
||||
int sa_flags;
|
||||
};
|
||||
|
||||
|
||||
int sigaction ARGS(( int sig, struct sigaction *act, struct sigaction *oact ));
|
||||
int sigaddset ARGS(( sigset_t *mask, int sig ));
|
||||
#ifndef IS_KSH
|
||||
int sigdelset ARGS(( sigset_t *mask, int sig ));
|
||||
#endif /* IS_KSH */
|
||||
int sigemptyset ARGS(( sigset_t *mask ));
|
||||
#ifndef IS_KSH
|
||||
int sigfillset ARGS(( sigset_t *mask ));
|
||||
int sigismember ARGS(( sigset_t *mask, int sig ));
|
||||
int sigpending ARGS(( sigset_t *set ));
|
||||
#endif /* IS_KSH */
|
||||
int sigprocmask ARGS(( int how, sigset_t *set, sigset_t *oset ));
|
||||
int sigsuspend ARGS(( sigset_t *mask ));
|
||||
|
||||
#ifndef sigmask
|
||||
# define sigmask(s) (1<<((s)-1)) /* convert SIGnum to mask */
|
||||
#endif
|
||||
#if !defined(NSIG) && defined(_NSIG)
|
||||
# define NSIG _NSIG
|
||||
#endif
|
||||
#endif /* ! SA_NOCLDSTOP */
|
||||
#endif /* _SIGACT_H */
|
56
bin/ksh/siglist.in
Normal file
56
bin/ksh/siglist.in
Normal file
|
@ -0,0 +1,56 @@
|
|||
# $NetBSD: siglist.in,v 1.2 1997/01/12 19:12:17 tls Exp $
|
||||
#
|
||||
# List of signals used to initialize ksh's signal table (see trap.c
|
||||
# and siglist.sh).
|
||||
#
|
||||
# Note that if a system has multiple defines for the same signal
|
||||
# (eg, SIGABRT vs SIGIOT, SIGCHLD vs SIGCLD), only the first one
|
||||
# will be seen, so the order in this list is important.
|
||||
#
|
||||
HUP Hangup
|
||||
INT Interrupt
|
||||
QUIT Quit
|
||||
ILL Illegal instruction
|
||||
TRAP Trace trap
|
||||
# before IOT (ABRT is posix and ABRT is sometimes the same as IOT)
|
||||
ABRT Abort
|
||||
IOT IOT instruction
|
||||
EMT EMT trap
|
||||
FPE Floating point exception
|
||||
KILL Killed
|
||||
# before BUS (linux doesn't really have a BUS, but defines it to UNUSED)
|
||||
UNUSED Unused
|
||||
BUS Bus error
|
||||
SEGV Memory fault
|
||||
SYS Bad system call
|
||||
PIPE Broken pipe
|
||||
ALRM Alarm clock
|
||||
TERM Terminated
|
||||
STKFLT Stack fault
|
||||
IO I/O possible
|
||||
XCPU CPU time limit exceeded
|
||||
XFSZ File size limit exceeded
|
||||
VTALRM Virtual timer expired
|
||||
PROF Profiling timer expired
|
||||
WINCH Window size change
|
||||
LOST File lock lost
|
||||
USR1 User defined signal 1
|
||||
USR2 User defined signal 2
|
||||
PWR Power-fail/Restart
|
||||
POLL Pollable event occurred
|
||||
STOP Stopped (signal)
|
||||
TSTP Stopped
|
||||
CONT Continued
|
||||
# before CLD (CHLD is posix and CHLD is sometimes the same as CLD)
|
||||
CHLD Child exited
|
||||
CLD Child exited
|
||||
TTIN Stopped (tty input)
|
||||
TTOU Stopped (tty output)
|
||||
INFO Information request
|
||||
URG Urgent I/O condition
|
||||
# Solaris (svr4?) signals
|
||||
WAITING No runnable LWPs
|
||||
LWP Inter-LWP signal
|
||||
FREEZE Checkpoint freeze
|
||||
THAW Checkpoint thaw
|
||||
CANCEL Thread cancellation
|
44
bin/ksh/siglist.sh
Executable file
44
bin/ksh/siglist.sh
Executable file
|
@ -0,0 +1,44 @@
|
|||
#!/bin/sh
|
||||
# $NetBSD: siglist.sh,v 1.9 2011/01/23 17:11:55 hauke Exp $
|
||||
#
|
||||
# Script to generate a sorted, complete list of signals, suitable
|
||||
# for inclusion in trap.c as array initializer.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
: ${AWK:=awk}
|
||||
: ${SED:=sed}
|
||||
|
||||
in=tmpi$$.c
|
||||
out=tmpo$$.c
|
||||
ecode=1
|
||||
trapsigs='0 1 2 13 15'
|
||||
trap 'rm -f $in $out; trap 0; exit $ecode' $trapsigs
|
||||
|
||||
CPP="${1-cc -E}"
|
||||
|
||||
# The trap here to make up for a bug in bash (1.14.3(1)) that calls the trap
|
||||
(trap $trapsigs;
|
||||
echo '#include "sh.h"';
|
||||
echo ' { QwErTy SIGNALS , "DUMMY" , "hook for number of signals" },';
|
||||
${SED} -e '/^[ ]*#/d' -e 's/^[ ]*\([^ ][^ ]*\)[ ][ ]*\(.*[^ ]\)[ ]*$/#ifdef SIG\1\
|
||||
{ QwErTy .signal = SIG\1 , .name = "\1", .mess = "\2" },\
|
||||
#endif/') > $in
|
||||
$CPP $in > $out
|
||||
${SED} -n 's/{ QwErTy/{/p' < $out | ${AWK} '{print NR, $0}' | sort -k 5n -k 1n |
|
||||
${SED} 's/^[0-9]* //' |
|
||||
${AWK} 'BEGIN { last=0; nsigs=0; }
|
||||
{
|
||||
if ($4 ~ /^[0-9][0-9]*$/ && $5 == ",") {
|
||||
n = $4;
|
||||
if (n > 0 && n != last) {
|
||||
while (++last < n) {
|
||||
printf "\t{ .signal = %d , .name = NULL, .mess = `Signal %d` } ,\n", last, last;
|
||||
}
|
||||
print;
|
||||
}
|
||||
}
|
||||
}' |
|
||||
tr '`' '"' | grep -v '"DUMMY"'
|
||||
ecode=0
|
958
bin/ksh/syn.c
Normal file
958
bin/ksh/syn.c
Normal file
|
@ -0,0 +1,958 @@
|
|||
/* $NetBSD: syn.c,v 1.9 2006/10/16 00:07:32 christos Exp $ */
|
||||
|
||||
/*
|
||||
* shell parser (C version)
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: syn.c,v 1.9 2006/10/16 00:07:32 christos Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
#include "c_test.h"
|
||||
#include <assert.h>
|
||||
|
||||
struct nesting_state {
|
||||
int start_token; /* token than began nesting (eg, FOR) */
|
||||
int start_line; /* line nesting began on */
|
||||
};
|
||||
|
||||
static void yyparse ARGS((void));
|
||||
static struct op *pipeline ARGS((int cf));
|
||||
static struct op *andor ARGS((void));
|
||||
static struct op *c_list ARGS((int multi));
|
||||
static struct ioword *synio ARGS((int cf));
|
||||
static void musthave ARGS((int c, int cf));
|
||||
static struct op *nested ARGS((int type, int smark, int emark));
|
||||
static struct op *get_command ARGS((int cf));
|
||||
static struct op *dogroup ARGS((void));
|
||||
static struct op *thenpart ARGS((void));
|
||||
static struct op *elsepart ARGS((void));
|
||||
static struct op *caselist ARGS((void));
|
||||
static struct op *casepart ARGS((int endtok));
|
||||
static struct op *function_body ARGS((char *name, int ksh_func));
|
||||
static char ** wordlist ARGS((void));
|
||||
static struct op *block ARGS((int type, struct op *t1, struct op *t2,
|
||||
char **wp));
|
||||
static struct op *newtp ARGS((int type));
|
||||
static void syntaxerr ARGS((const char *what))
|
||||
GCC_FUNC_ATTR(noreturn);
|
||||
static void nesting_push ARGS((struct nesting_state *save, int tok));
|
||||
static void nesting_pop ARGS((struct nesting_state *saved));
|
||||
static int assign_command ARGS((char *s));
|
||||
static int inalias ARGS((struct source *s));
|
||||
#ifdef KSH
|
||||
static int dbtestp_isa ARGS((Test_env *te, Test_meta meta));
|
||||
static const char *dbtestp_getopnd ARGS((Test_env *te, Test_op op,
|
||||
int do_eval));
|
||||
static int dbtestp_eval ARGS((Test_env *te, Test_op op, const char *opnd1,
|
||||
const char *opnd2, int do_eval));
|
||||
static void dbtestp_error ARGS((Test_env *te, int offset, const char *msg));
|
||||
#endif /* KSH */
|
||||
|
||||
static struct op *outtree; /* yyparse output */
|
||||
|
||||
static struct nesting_state nesting; /* \n changed to ; */
|
||||
|
||||
static int reject; /* token(cf) gets symbol again */
|
||||
static int symbol; /* yylex value */
|
||||
|
||||
#define REJECT (reject = 1)
|
||||
#define ACCEPT (reject = 0)
|
||||
#define token(cf) \
|
||||
((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
|
||||
#define tpeek(cf) \
|
||||
((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
|
||||
|
||||
static void
|
||||
yyparse()
|
||||
{
|
||||
int c;
|
||||
|
||||
ACCEPT;
|
||||
|
||||
outtree = c_list(source->type == SSTRING);
|
||||
c = tpeek(0);
|
||||
if (c == 0 && !outtree)
|
||||
outtree = newtp(TEOF);
|
||||
else if (c != '\n' && c != 0)
|
||||
syntaxerr((char *) 0);
|
||||
}
|
||||
|
||||
static struct op *
|
||||
pipeline(cf)
|
||||
int cf;
|
||||
{
|
||||
register struct op *t, *p, *tl = NULL;
|
||||
|
||||
t = get_command(cf);
|
||||
if (t != NULL) {
|
||||
while (token(0) == '|') {
|
||||
if ((p = get_command(CONTIN)) == NULL)
|
||||
syntaxerr((char *) 0);
|
||||
if (tl == NULL)
|
||||
t = tl = block(TPIPE, t, p, NOWORDS);
|
||||
else
|
||||
tl = tl->right = block(TPIPE, tl->right, p, NOWORDS);
|
||||
}
|
||||
REJECT;
|
||||
}
|
||||
return (t);
|
||||
}
|
||||
|
||||
static struct op *
|
||||
andor()
|
||||
{
|
||||
register struct op *t, *p;
|
||||
register int c;
|
||||
|
||||
t = pipeline(0);
|
||||
if (t != NULL) {
|
||||
while ((c = token(0)) == LOGAND || c == LOGOR) {
|
||||
if ((p = pipeline(CONTIN)) == NULL)
|
||||
syntaxerr((char *) 0);
|
||||
t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
|
||||
}
|
||||
REJECT;
|
||||
}
|
||||
return (t);
|
||||
}
|
||||
|
||||
static struct op *
|
||||
c_list(multi)
|
||||
int multi;
|
||||
{
|
||||
register struct op *t = NULL, *p, *tl = NULL;
|
||||
register int c;
|
||||
int have_sep;
|
||||
|
||||
while (1) {
|
||||
p = andor();
|
||||
/* Token has always been read/rejected at this point, so
|
||||
* we don't worry about what flags to pass token()
|
||||
*/
|
||||
c = token(0);
|
||||
have_sep = 1;
|
||||
if (c == '\n' && (multi || inalias(source))) {
|
||||
if (!p) /* ignore blank lines */
|
||||
continue;
|
||||
} else if (!p)
|
||||
break;
|
||||
else if (c == '&' || c == COPROC)
|
||||
p = block(c == '&' ? TASYNC : TCOPROC,
|
||||
p, NOBLOCK, NOWORDS);
|
||||
else if (c != ';')
|
||||
have_sep = 0;
|
||||
if (!t)
|
||||
t = p;
|
||||
else if (!tl)
|
||||
t = tl = block(TLIST, t, p, NOWORDS);
|
||||
else
|
||||
tl = tl->right = block(TLIST, tl->right, p, NOWORDS);
|
||||
if (!have_sep)
|
||||
break;
|
||||
}
|
||||
REJECT;
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct ioword *
|
||||
synio(cf)
|
||||
int cf;
|
||||
{
|
||||
register struct ioword *iop;
|
||||
int ishere;
|
||||
|
||||
if (tpeek(cf) != REDIR)
|
||||
return NULL;
|
||||
ACCEPT;
|
||||
iop = yylval.iop;
|
||||
ishere = (iop->flag&IOTYPE) == IOHERE;
|
||||
musthave(LWORD, ishere ? HEREDELIM : 0);
|
||||
if (ishere) {
|
||||
iop->delim = yylval.cp;
|
||||
if (*ident != 0) /* unquoted */
|
||||
iop->flag |= IOEVAL;
|
||||
if (herep >= &heres[HERES])
|
||||
yyerror("too many <<'s\n");
|
||||
*herep++ = iop;
|
||||
} else
|
||||
iop->name = yylval.cp;
|
||||
return iop;
|
||||
}
|
||||
|
||||
static void
|
||||
musthave(c, cf)
|
||||
int c, cf;
|
||||
{
|
||||
if ((token(cf)) != c)
|
||||
syntaxerr((char *) 0);
|
||||
}
|
||||
|
||||
static struct op *
|
||||
nested(type, smark, emark)
|
||||
int type, smark, emark;
|
||||
{
|
||||
register struct op *t;
|
||||
struct nesting_state old_nesting;
|
||||
|
||||
nesting_push(&old_nesting, smark);
|
||||
t = c_list(TRUE);
|
||||
musthave(emark, KEYWORD|ALIAS);
|
||||
nesting_pop(&old_nesting);
|
||||
return (block(type, t, NOBLOCK, NOWORDS));
|
||||
}
|
||||
|
||||
static struct op *
|
||||
get_command(cf)
|
||||
int cf;
|
||||
{
|
||||
register struct op *t;
|
||||
register int c, iopn = 0, syniocf;
|
||||
struct ioword *iop, **iops;
|
||||
XPtrV args, vars;
|
||||
struct nesting_state old_nesting;
|
||||
|
||||
iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1),
|
||||
ATEMP);
|
||||
XPinit(args, 16);
|
||||
XPinit(vars, 16);
|
||||
|
||||
syniocf = KEYWORD|ALIAS;
|
||||
switch (c = token(cf|KEYWORD|ALIAS|VARASN)) {
|
||||
default:
|
||||
REJECT;
|
||||
afree((void*) iops, ATEMP);
|
||||
XPfree(args);
|
||||
XPfree(vars);
|
||||
return NULL; /* empty line */
|
||||
|
||||
case LWORD:
|
||||
case REDIR:
|
||||
REJECT;
|
||||
syniocf &= ~(KEYWORD|ALIAS);
|
||||
t = newtp(TCOM);
|
||||
t->lineno = source->line;
|
||||
while (1) {
|
||||
cf = (t->u.evalflags ? ARRAYVAR : 0)
|
||||
| (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);
|
||||
switch (tpeek(cf)) {
|
||||
case REDIR:
|
||||
if (iopn >= NUFILE)
|
||||
yyerror("too many redirections\n");
|
||||
iops[iopn++] = synio(cf);
|
||||
break;
|
||||
|
||||
case LWORD:
|
||||
ACCEPT;
|
||||
/* the iopn == 0 and XPsize(vars) == 0 are
|
||||
* dubious but at&t ksh acts this way
|
||||
*/
|
||||
if (iopn == 0 && XPsize(vars) == 0
|
||||
&& XPsize(args) == 0
|
||||
&& assign_command(ident))
|
||||
t->u.evalflags = DOVACHECK;
|
||||
if ((XPsize(args) == 0 || Flag(FKEYWORD))
|
||||
&& is_wdvarassign(yylval.cp))
|
||||
XPput(vars, yylval.cp);
|
||||
else
|
||||
XPput(args, yylval.cp);
|
||||
break;
|
||||
|
||||
case '(':
|
||||
/* Check for "> foo (echo hi)", which at&t ksh
|
||||
* allows (not POSIX, but not disallowed)
|
||||
*/
|
||||
afree(t, ATEMP);
|
||||
if (XPsize(args) == 0 && XPsize(vars) == 0) {
|
||||
ACCEPT;
|
||||
goto Subshell;
|
||||
}
|
||||
/* Must be a function */
|
||||
if (iopn != 0 || XPsize(args) != 1
|
||||
|| XPsize(vars) != 0)
|
||||
syntaxerr((char *) 0);
|
||||
ACCEPT;
|
||||
/*(*/
|
||||
musthave(')', 0);
|
||||
t = function_body(XPptrv(args)[0], FALSE);
|
||||
goto Leave;
|
||||
|
||||
default:
|
||||
goto Leave;
|
||||
}
|
||||
}
|
||||
Leave:
|
||||
break;
|
||||
|
||||
Subshell:
|
||||
case '(':
|
||||
t = nested(TPAREN, '(', ')');
|
||||
break;
|
||||
|
||||
case '{': /*}*/
|
||||
t = nested(TBRACE, '{', '}');
|
||||
break;
|
||||
|
||||
#ifdef KSH
|
||||
case MDPAREN:
|
||||
{
|
||||
static const char let_cmd[] = { CHAR, 'l', CHAR, 'e',
|
||||
CHAR, 't', EOS };
|
||||
/* Leave KEYWORD in syniocf (allow if (( 1 )) then ...) */
|
||||
t = newtp(TCOM);
|
||||
t->lineno = source->line;
|
||||
ACCEPT;
|
||||
XPput(args, wdcopy(let_cmd, ATEMP));
|
||||
musthave(LWORD,LETEXPR);
|
||||
XPput(args, yylval.cp);
|
||||
break;
|
||||
}
|
||||
#endif /* KSH */
|
||||
|
||||
#ifdef KSH
|
||||
case DBRACKET: /* [[ .. ]] */
|
||||
/* Leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */
|
||||
t = newtp(TDBRACKET);
|
||||
ACCEPT;
|
||||
{
|
||||
Test_env te;
|
||||
|
||||
te.flags = TEF_DBRACKET;
|
||||
te.pos.av = &args;
|
||||
te.isa = dbtestp_isa;
|
||||
te.getopnd = dbtestp_getopnd;
|
||||
te.eval = dbtestp_eval;
|
||||
te.error = dbtestp_error;
|
||||
|
||||
test_parse(&te);
|
||||
}
|
||||
break;
|
||||
#endif /* KSH */
|
||||
|
||||
case FOR:
|
||||
case SELECT:
|
||||
t = newtp((c == FOR) ? TFOR : TSELECT);
|
||||
musthave(LWORD, ARRAYVAR);
|
||||
if (!is_wdvarname(yylval.cp, TRUE))
|
||||
yyerror("%s: bad identifier\n",
|
||||
c == FOR ? "for" : "select");
|
||||
t->str = str_save(ident, ATEMP);
|
||||
nesting_push(&old_nesting, c);
|
||||
t->vars = wordlist();
|
||||
t->left = dogroup();
|
||||
nesting_pop(&old_nesting);
|
||||
break;
|
||||
|
||||
case WHILE:
|
||||
case UNTIL:
|
||||
nesting_push(&old_nesting, c);
|
||||
t = newtp((c == WHILE) ? TWHILE : TUNTIL);
|
||||
t->left = c_list(TRUE);
|
||||
t->right = dogroup();
|
||||
nesting_pop(&old_nesting);
|
||||
break;
|
||||
|
||||
case CASE:
|
||||
t = newtp(TCASE);
|
||||
musthave(LWORD, 0);
|
||||
t->str = yylval.cp;
|
||||
nesting_push(&old_nesting, c);
|
||||
t->left = caselist();
|
||||
nesting_pop(&old_nesting);
|
||||
break;
|
||||
|
||||
case IF:
|
||||
nesting_push(&old_nesting, c);
|
||||
t = newtp(TIF);
|
||||
t->left = c_list(TRUE);
|
||||
t->right = thenpart();
|
||||
musthave(FI, KEYWORD|ALIAS);
|
||||
nesting_pop(&old_nesting);
|
||||
break;
|
||||
|
||||
case BANG:
|
||||
syniocf &= ~(KEYWORD|ALIAS);
|
||||
t = pipeline(0);
|
||||
if (t == (struct op *) 0)
|
||||
syntaxerr((char *) 0);
|
||||
t = block(TBANG, NOBLOCK, t, NOWORDS);
|
||||
break;
|
||||
|
||||
case TIME:
|
||||
syniocf &= ~(KEYWORD|ALIAS);
|
||||
t = pipeline(0);
|
||||
t = block(TTIME, t, NOBLOCK, NOWORDS);
|
||||
break;
|
||||
|
||||
case FUNCTION:
|
||||
musthave(LWORD, 0);
|
||||
t = function_body(yylval.cp, TRUE);
|
||||
break;
|
||||
}
|
||||
|
||||
while ((iop = synio(syniocf)) != NULL) {
|
||||
if (iopn >= NUFILE)
|
||||
yyerror("too many redirections\n");
|
||||
iops[iopn++] = iop;
|
||||
}
|
||||
|
||||
if (iopn == 0) {
|
||||
afree((void*) iops, ATEMP);
|
||||
t->ioact = NULL;
|
||||
} else {
|
||||
iops[iopn++] = NULL;
|
||||
iops = (struct ioword **) aresize((void*) iops,
|
||||
sizeofN(struct ioword *, iopn), ATEMP);
|
||||
t->ioact = iops;
|
||||
}
|
||||
|
||||
if (t->type == TCOM || t->type == TDBRACKET) {
|
||||
XPput(args, NULL);
|
||||
t->args = (char **) XPclose(args);
|
||||
XPput(vars, NULL);
|
||||
t->vars = (char **) XPclose(vars);
|
||||
} else {
|
||||
XPfree(args);
|
||||
XPfree(vars);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static struct op *
|
||||
dogroup()
|
||||
{
|
||||
register int c;
|
||||
register struct op *list;
|
||||
|
||||
c = token(CONTIN|KEYWORD|ALIAS);
|
||||
/* A {...} can be used instead of do...done for for/select loops
|
||||
* but not for while/until loops - we don't need to check if it
|
||||
* is a while loop because it would have been parsed as part of
|
||||
* the conditional command list...
|
||||
*/
|
||||
if (c == DO)
|
||||
c = DONE;
|
||||
else if (c == '{')
|
||||
c = '}';
|
||||
else
|
||||
syntaxerr((char *) 0);
|
||||
list = c_list(TRUE);
|
||||
musthave(c, KEYWORD|ALIAS);
|
||||
return list;
|
||||
}
|
||||
|
||||
static struct op *
|
||||
thenpart()
|
||||
{
|
||||
register struct op *t;
|
||||
|
||||
musthave(THEN, KEYWORD|ALIAS);
|
||||
t = newtp(0);
|
||||
t->left = c_list(TRUE);
|
||||
if (t->left == NULL)
|
||||
syntaxerr((char *) 0);
|
||||
t->right = elsepart();
|
||||
return (t);
|
||||
}
|
||||
|
||||
static struct op *
|
||||
elsepart()
|
||||
{
|
||||
register struct op *t;
|
||||
|
||||
switch (token(KEYWORD|ALIAS|VARASN)) {
|
||||
case ELSE:
|
||||
if ((t = c_list(TRUE)) == NULL)
|
||||
syntaxerr((char *) 0);
|
||||
return (t);
|
||||
|
||||
case ELIF:
|
||||
t = newtp(TELIF);
|
||||
t->left = c_list(TRUE);
|
||||
t->right = thenpart();
|
||||
return (t);
|
||||
|
||||
default:
|
||||
REJECT;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct op *
|
||||
caselist()
|
||||
{
|
||||
register struct op *t, *tl;
|
||||
int c;
|
||||
|
||||
c = token(CONTIN|KEYWORD|ALIAS);
|
||||
/* A {...} can be used instead of in...esac for case statements */
|
||||
if (c == IN)
|
||||
c = ESAC;
|
||||
else if (c == '{')
|
||||
c = '}';
|
||||
else
|
||||
syntaxerr((char *) 0);
|
||||
t = tl = NULL;
|
||||
while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */
|
||||
struct op *tc = casepart(c);
|
||||
if (tl == NULL)
|
||||
t = tl = tc, tl->right = NULL;
|
||||
else
|
||||
tl->right = tc, tl = tc;
|
||||
}
|
||||
musthave(c, KEYWORD|ALIAS);
|
||||
return (t);
|
||||
}
|
||||
|
||||
static struct op *
|
||||
casepart(endtok)
|
||||
int endtok;
|
||||
{
|
||||
register struct op *t;
|
||||
register int c;
|
||||
XPtrV ptns;
|
||||
|
||||
XPinit(ptns, 16);
|
||||
t = newtp(TPAT);
|
||||
c = token(CONTIN|KEYWORD); /* no ALIAS here */
|
||||
if (c != '(')
|
||||
REJECT;
|
||||
do {
|
||||
musthave(LWORD, 0);
|
||||
XPput(ptns, yylval.cp);
|
||||
} while ((c = token(0)) == '|');
|
||||
REJECT;
|
||||
XPput(ptns, NULL);
|
||||
t->vars = (char **) XPclose(ptns);
|
||||
musthave(')', 0);
|
||||
|
||||
t->left = c_list(TRUE);
|
||||
/* Note: Posix requires the ;; */
|
||||
if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)
|
||||
musthave(BREAK, CONTIN|KEYWORD|ALIAS);
|
||||
return (t);
|
||||
}
|
||||
|
||||
static struct op *
|
||||
function_body(name, ksh_func)
|
||||
char *name;
|
||||
int ksh_func; /* function foo { ... } vs foo() { .. } */
|
||||
{
|
||||
char *sname, *p;
|
||||
struct op *t;
|
||||
int old_func_parse;
|
||||
|
||||
sname = wdstrip(name);
|
||||
/* Check for valid characters in name. posix and ksh93 say only
|
||||
* allow [a-zA-Z_0-9] but this allows more as old pdksh's have
|
||||
* allowed more (the following were never allowed:
|
||||
* nul space nl tab $ ' " \ ` ( ) & | ; = < >
|
||||
* C_QUOTE covers all but = and adds # [ ? *)
|
||||
*/
|
||||
for (p = sname; *p; p++)
|
||||
if (ctype(*p, C_QUOTE) || *p == '=')
|
||||
yyerror("%s: invalid function name\n", sname);
|
||||
|
||||
t = newtp(TFUNCT);
|
||||
t->str = sname;
|
||||
t->u.ksh_func = ksh_func;
|
||||
t->lineno = source->line;
|
||||
|
||||
/* Note that POSIX allows only compound statements after foo(), sh and
|
||||
* at&t ksh allow any command, go with the later since it shouldn't
|
||||
* break anything. However, for function foo, at&t ksh only accepts
|
||||
* an open-brace.
|
||||
*/
|
||||
if (ksh_func) {
|
||||
musthave('{', CONTIN|KEYWORD|ALIAS); /* } */
|
||||
REJECT;
|
||||
}
|
||||
|
||||
old_func_parse = e->flags & EF_FUNC_PARSE;
|
||||
e->flags |= EF_FUNC_PARSE;
|
||||
if ((t->left = get_command(CONTIN)) == (struct op *) 0) {
|
||||
/*
|
||||
* Probably something like foo() followed by eof or ;.
|
||||
* This is accepted by sh and ksh88.
|
||||
* To make "typeset -f foo" work reliably (so its output can
|
||||
* be used as input), we pretend there is a colon here.
|
||||
*/
|
||||
t->left = newtp(TCOM);
|
||||
t->left->args = (char **) alloc(sizeof(char *) * 2, ATEMP);
|
||||
t->left->args[0] = alloc(sizeof(char) * 3, ATEMP);
|
||||
t->left->args[0][0] = CHAR;
|
||||
t->left->args[0][1] = ':';
|
||||
t->left->args[0][2] = EOS;
|
||||
t->left->args[1] = (char *) 0;
|
||||
t->left->vars = (char **) alloc(sizeof(char *), ATEMP);
|
||||
t->left->vars[0] = (char *) 0;
|
||||
t->left->lineno = 1;
|
||||
}
|
||||
if (!old_func_parse)
|
||||
e->flags &= ~EF_FUNC_PARSE;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static char **
|
||||
wordlist()
|
||||
{
|
||||
register int c;
|
||||
XPtrV args;
|
||||
|
||||
XPinit(args, 16);
|
||||
/* Posix does not do alias expansion here... */
|
||||
if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) {
|
||||
if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */
|
||||
REJECT;
|
||||
return NULL;
|
||||
}
|
||||
while ((c = token(0)) == LWORD)
|
||||
XPput(args, yylval.cp);
|
||||
if (c != '\n' && c != ';')
|
||||
syntaxerr((char *) 0);
|
||||
if (XPsize(args) == 0) {
|
||||
XPfree(args);
|
||||
return NULL;
|
||||
} else {
|
||||
XPput(args, NULL);
|
||||
return (char **) XPclose(args);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* supporting functions
|
||||
*/
|
||||
|
||||
static struct op *
|
||||
block(type, t1, t2, wp)
|
||||
int type;
|
||||
struct op *t1, *t2;
|
||||
char **wp;
|
||||
{
|
||||
register struct op *t;
|
||||
|
||||
t = newtp(type);
|
||||
t->left = t1;
|
||||
t->right = t2;
|
||||
t->vars = wp;
|
||||
return (t);
|
||||
}
|
||||
|
||||
const struct tokeninfo {
|
||||
const char *name;
|
||||
short val;
|
||||
short reserved;
|
||||
} tokentab[] = {
|
||||
/* Reserved words */
|
||||
{ "if", IF, TRUE },
|
||||
{ "then", THEN, TRUE },
|
||||
{ "else", ELSE, TRUE },
|
||||
{ "elif", ELIF, TRUE },
|
||||
{ "fi", FI, TRUE },
|
||||
{ "case", CASE, TRUE },
|
||||
{ "esac", ESAC, TRUE },
|
||||
{ "for", FOR, TRUE },
|
||||
#ifdef KSH
|
||||
{ "select", SELECT, TRUE },
|
||||
#endif /* KSH */
|
||||
{ "while", WHILE, TRUE },
|
||||
{ "until", UNTIL, TRUE },
|
||||
{ "do", DO, TRUE },
|
||||
{ "done", DONE, TRUE },
|
||||
{ "in", IN, TRUE },
|
||||
{ "function", FUNCTION, TRUE },
|
||||
{ "time", TIME, TRUE },
|
||||
{ "{", '{', TRUE },
|
||||
{ "}", '}', TRUE },
|
||||
{ "!", BANG, TRUE },
|
||||
#ifdef KSH
|
||||
{ "[[", DBRACKET, TRUE },
|
||||
#endif /* KSH */
|
||||
/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
|
||||
{ "&&", LOGAND, FALSE },
|
||||
{ "||", LOGOR, FALSE },
|
||||
{ ";;", BREAK, FALSE },
|
||||
#ifdef KSH
|
||||
{ "((", MDPAREN, FALSE },
|
||||
{ "|&", COPROC, FALSE },
|
||||
#endif /* KSH */
|
||||
/* and some special cases... */
|
||||
{ "newline", '\n', FALSE },
|
||||
{ .name = NULL }
|
||||
};
|
||||
|
||||
void
|
||||
initkeywords()
|
||||
{
|
||||
register struct tokeninfo const *tt;
|
||||
register struct tbl *p;
|
||||
|
||||
tinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */
|
||||
for (tt = tokentab; tt->name; tt++) {
|
||||
if (tt->reserved) {
|
||||
p = tenter(&keywords, tt->name, hash(tt->name));
|
||||
p->flag |= DEFINED|ISSET;
|
||||
p->type = CKEYWD;
|
||||
p->val.i = tt->val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
syntaxerr(what)
|
||||
const char *what;
|
||||
{
|
||||
char redir[6]; /* 2<<- is the longest redirection, I think */
|
||||
const char *s;
|
||||
struct tokeninfo const *tt;
|
||||
int c;
|
||||
|
||||
if (!what)
|
||||
what = "unexpected";
|
||||
REJECT;
|
||||
c = token(0);
|
||||
Again:
|
||||
switch (c) {
|
||||
case 0:
|
||||
if (nesting.start_token) {
|
||||
c = nesting.start_token;
|
||||
source->errline = nesting.start_line;
|
||||
what = "unmatched";
|
||||
goto Again;
|
||||
}
|
||||
/* don't quote the EOF */
|
||||
yyerror("syntax error: unexpected EOF\n");
|
||||
/*NOTREACHED*/
|
||||
|
||||
case LWORD:
|
||||
s = snptreef((char *) 0, 32, "%S", yylval.cp);
|
||||
break;
|
||||
|
||||
case REDIR:
|
||||
s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
|
||||
break;
|
||||
|
||||
default:
|
||||
for (tt = tokentab; tt->name; tt++)
|
||||
if (tt->val == c)
|
||||
break;
|
||||
if (tt->name)
|
||||
s = tt->name;
|
||||
else {
|
||||
if (c > 0 && c < 256) {
|
||||
redir[0] = c;
|
||||
redir[1] = '\0';
|
||||
} else
|
||||
shf_snprintf(redir, sizeof(redir),
|
||||
"?%d", c);
|
||||
s = redir;
|
||||
}
|
||||
}
|
||||
yyerror("syntax error: `%s' %s\n", s, what);
|
||||
}
|
||||
|
||||
static void
|
||||
nesting_push(save, tok)
|
||||
struct nesting_state *save;
|
||||
int tok;
|
||||
{
|
||||
*save = nesting;
|
||||
nesting.start_token = tok;
|
||||
nesting.start_line = source->line;
|
||||
}
|
||||
|
||||
static void
|
||||
nesting_pop(saved)
|
||||
struct nesting_state *saved;
|
||||
{
|
||||
nesting = *saved;
|
||||
}
|
||||
|
||||
static struct op *
|
||||
newtp(type)
|
||||
int type;
|
||||
{
|
||||
register struct op *t;
|
||||
|
||||
t = (struct op *) alloc(sizeof(*t), ATEMP);
|
||||
t->type = type;
|
||||
t->u.evalflags = 0;
|
||||
t->args = t->vars = NULL;
|
||||
t->ioact = NULL;
|
||||
t->left = t->right = NULL;
|
||||
t->str = NULL;
|
||||
return (t);
|
||||
}
|
||||
|
||||
struct op *
|
||||
compile(s)
|
||||
Source *s;
|
||||
{
|
||||
nesting.start_token = 0;
|
||||
nesting.start_line = 0;
|
||||
herep = heres;
|
||||
source = s;
|
||||
yyparse();
|
||||
return outtree;
|
||||
}
|
||||
|
||||
/* This kludge exists to take care of sh/at&t ksh oddity in which
|
||||
* the arguments of alias/export/readonly/typeset have no field
|
||||
* splitting, file globbing, or (normal) tilde expansion done.
|
||||
* at&t ksh seems to do something similar to this since
|
||||
* $ touch a=a; typeset a=[ab]; echo "$a"
|
||||
* a=[ab]
|
||||
* $ x=typeset; $x a=[ab]; echo "$a"
|
||||
* a=a
|
||||
* $
|
||||
*/
|
||||
static int
|
||||
assign_command(s)
|
||||
char *s;
|
||||
{
|
||||
char c = *s;
|
||||
|
||||
if (Flag(FPOSIX) || !*s)
|
||||
return 0;
|
||||
return (c == 'a' && strcmp(s, "alias") == 0)
|
||||
|| (c == 'e' && strcmp(s, "export") == 0)
|
||||
|| (c == 'r' && strcmp(s, "readonly") == 0)
|
||||
|| (c == 't' && strcmp(s, "typeset") == 0);
|
||||
}
|
||||
|
||||
/* Check if we are in the middle of reading an alias */
|
||||
static int
|
||||
inalias(s)
|
||||
struct source *s;
|
||||
{
|
||||
for (; s && s->type == SALIAS; s = s->next)
|
||||
if (!(s->flags & SF_ALIASEND))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef KSH
|
||||
/* Order important - indexed by Test_meta values
|
||||
* Note that ||, &&, ( and ) can't appear in as unquoted strings
|
||||
* in normal shell input, so these can be interpreted unambiguously
|
||||
* in the evaluation pass.
|
||||
*/
|
||||
static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };
|
||||
static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };
|
||||
static const char dbtest_not[] = { CHAR, '!', EOS };
|
||||
static const char dbtest_oparen[] = { CHAR, '(', EOS };
|
||||
static const char dbtest_cparen[] = { CHAR, ')', EOS };
|
||||
const char *const dbtest_tokens[] = {
|
||||
dbtest_or, dbtest_and, dbtest_not,
|
||||
dbtest_oparen, dbtest_cparen
|
||||
};
|
||||
const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
|
||||
const char db_lthan[] = { CHAR, '<', EOS };
|
||||
const char db_gthan[] = { CHAR, '>', EOS };
|
||||
|
||||
/* Test if the current token is a whatever. Accepts the current token if
|
||||
* it is. Returns 0 if it is not, non-zero if it is (in the case of
|
||||
* TM_UNOP and TM_BINOP, the returned value is a Test_op).
|
||||
*/
|
||||
static int
|
||||
dbtestp_isa(te, meta)
|
||||
Test_env *te;
|
||||
Test_meta meta;
|
||||
{
|
||||
int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
|
||||
int uqword = 0;
|
||||
char *save = (char *) 0;
|
||||
int ret = 0;
|
||||
|
||||
/* unquoted word? */
|
||||
uqword = c == LWORD && *ident;
|
||||
|
||||
if (meta == TM_OR)
|
||||
ret = c == LOGOR;
|
||||
else if (meta == TM_AND)
|
||||
ret = c == LOGAND;
|
||||
else if (meta == TM_NOT)
|
||||
ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0;
|
||||
else if (meta == TM_OPAREN)
|
||||
ret = c == '(' /*)*/;
|
||||
else if (meta == TM_CPAREN)
|
||||
ret = c == /*(*/ ')';
|
||||
else if (meta == TM_UNOP || meta == TM_BINOP) {
|
||||
if (meta == TM_BINOP && c == REDIR
|
||||
&& (yylval.iop->flag == IOREAD
|
||||
|| yylval.iop->flag == IOWRITE))
|
||||
{
|
||||
ret = 1;
|
||||
save = wdcopy(yylval.iop->flag == IOREAD ?
|
||||
db_lthan : db_gthan, ATEMP);
|
||||
} else if (uqword && (ret = (int) test_isop(te, meta, ident)))
|
||||
save = yylval.cp;
|
||||
} else /* meta == TM_END */
|
||||
ret = uqword && strcmp(yylval.cp, db_close) == 0;
|
||||
if (ret) {
|
||||
ACCEPT;
|
||||
if (meta != TM_END) {
|
||||
if (!save) {
|
||||
assert(/* meta >= 0 && */
|
||||
meta < sizeof(dbtest_tokens) /
|
||||
sizeof(dbtest_tokens[0]));
|
||||
save = wdcopy(dbtest_tokens[(int) meta], ATEMP);
|
||||
}
|
||||
XPput(*te->pos.av, save);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *
|
||||
dbtestp_getopnd(te, op, do_eval)
|
||||
Test_env *te;
|
||||
Test_op op;
|
||||
int do_eval;
|
||||
{
|
||||
int c = tpeek(ARRAYVAR);
|
||||
|
||||
if (c != LWORD)
|
||||
return (const char *) 0;
|
||||
|
||||
ACCEPT;
|
||||
XPput(*te->pos.av, yylval.cp);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static int
|
||||
dbtestp_eval(te, op, opnd1, opnd2, do_eval)
|
||||
Test_env *te;
|
||||
Test_op op;
|
||||
const char *opnd1;
|
||||
const char *opnd2;
|
||||
int do_eval;
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
dbtestp_error(te, offset, msg)
|
||||
Test_env *te;
|
||||
int offset;
|
||||
const char *msg;
|
||||
{
|
||||
te->flags |= TEF_ERROR;
|
||||
|
||||
if (offset < 0) {
|
||||
REJECT;
|
||||
/* Kludgy to say the least... */
|
||||
symbol = LWORD;
|
||||
yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av)
|
||||
+ offset);
|
||||
}
|
||||
syntaxerr(msg);
|
||||
}
|
||||
#endif /* KSH */
|
247
bin/ksh/table.c
Normal file
247
bin/ksh/table.c
Normal file
|
@ -0,0 +1,247 @@
|
|||
/* $NetBSD: table.c,v 1.4 2003/06/23 11:39:04 agc Exp $ */
|
||||
|
||||
/*
|
||||
* dynamic hashed associative table for commands and variables
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: table.c,v 1.4 2003/06/23 11:39:04 agc Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
#define INIT_TBLS 8 /* initial table size (power of 2) */
|
||||
|
||||
static void texpand ARGS((struct table *tp, int nsize));
|
||||
static int tnamecmp ARGS((void *p1, void *p2));
|
||||
|
||||
|
||||
unsigned int
|
||||
hash(n)
|
||||
register const char * n;
|
||||
{
|
||||
register unsigned int h = 0;
|
||||
|
||||
while (*n != '\0')
|
||||
h = 2*h + *n++;
|
||||
return h * 32821; /* scatter bits */
|
||||
}
|
||||
|
||||
void
|
||||
tinit(tp, ap, tsize)
|
||||
register struct table *tp;
|
||||
register Area *ap;
|
||||
int tsize;
|
||||
{
|
||||
tp->areap = ap;
|
||||
tp->tbls = NULL;
|
||||
tp->size = tp->nfree = 0;
|
||||
if (tsize)
|
||||
texpand(tp, tsize);
|
||||
}
|
||||
|
||||
static void
|
||||
texpand(tp, nsize)
|
||||
register struct table *tp;
|
||||
int nsize;
|
||||
{
|
||||
register int i;
|
||||
register struct tbl *tblp, **p;
|
||||
register struct tbl **ntblp, **otblp = tp->tbls;
|
||||
int osize = tp->size;
|
||||
|
||||
ntblp = (struct tbl**) alloc(sizeofN(struct tbl *, nsize), tp->areap);
|
||||
for (i = 0; i < nsize; i++)
|
||||
ntblp[i] = NULL;
|
||||
tp->size = nsize;
|
||||
tp->nfree = 8*nsize/10; /* table can get 80% full */
|
||||
tp->tbls = ntblp;
|
||||
if (otblp == NULL)
|
||||
return;
|
||||
for (i = 0; i < osize; i++) {
|
||||
if ((tblp = otblp[i]) != NULL) {
|
||||
if ((tblp->flag&DEFINED)) {
|
||||
for (p = &ntblp[hash(tblp->name)
|
||||
& (tp->size-1)];
|
||||
*p != NULL; p--)
|
||||
if (p == ntblp) /* wrap */
|
||||
p += tp->size;
|
||||
*p = tblp;
|
||||
tp->nfree--;
|
||||
} else if (!(tblp->flag & FINUSE)) {
|
||||
afree((void*)tblp, tp->areap);
|
||||
}
|
||||
}
|
||||
}
|
||||
afree((void*)otblp, tp->areap);
|
||||
}
|
||||
|
||||
struct tbl *
|
||||
tsearch(tp, n, h)
|
||||
register struct table *tp; /* table */
|
||||
register const char *n; /* name to enter */
|
||||
unsigned int h; /* hash(n) */
|
||||
{
|
||||
register struct tbl **pp, *p;
|
||||
|
||||
if (tp->size == 0)
|
||||
return NULL;
|
||||
|
||||
/* search for name in hashed table */
|
||||
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
|
||||
if (*p->name == *n && strcmp(p->name, n) == 0
|
||||
&& (p->flag&DEFINED))
|
||||
return p;
|
||||
if (pp == tp->tbls) /* wrap */
|
||||
pp += tp->size;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct tbl *
|
||||
tenter(tp, n, h)
|
||||
register struct table *tp; /* table */
|
||||
register const char *n; /* name to enter */
|
||||
unsigned int h; /* hash(n) */
|
||||
{
|
||||
register struct tbl **pp, *p;
|
||||
register int len;
|
||||
|
||||
if (tp->size == 0)
|
||||
texpand(tp, INIT_TBLS);
|
||||
Search:
|
||||
/* search for name in hashed table */
|
||||
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
|
||||
if (*p->name == *n && strcmp(p->name, n) == 0)
|
||||
return p; /* found */
|
||||
if (pp == tp->tbls) /* wrap */
|
||||
pp += tp->size;
|
||||
}
|
||||
|
||||
if (tp->nfree <= 0) { /* too full */
|
||||
texpand(tp, 2*tp->size);
|
||||
goto Search;
|
||||
}
|
||||
|
||||
/* create new tbl entry */
|
||||
len = strlen(n) + 1;
|
||||
p = (struct tbl *) alloc(offsetof(struct tbl, name[0]) + len,
|
||||
tp->areap);
|
||||
p->flag = 0;
|
||||
p->type = 0;
|
||||
p->areap = tp->areap;
|
||||
p->u2.field = 0;
|
||||
p->u.array = (struct tbl *)0;
|
||||
memcpy(p->name, n, len);
|
||||
|
||||
/* enter in tp->tbls */
|
||||
tp->nfree--;
|
||||
*pp = p;
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
tdelete(p)
|
||||
register struct tbl *p;
|
||||
{
|
||||
p->flag = 0;
|
||||
}
|
||||
|
||||
void
|
||||
twalk(ts, tp)
|
||||
struct tstate *ts;
|
||||
struct table *tp;
|
||||
{
|
||||
ts->left = tp->size;
|
||||
ts->next = tp->tbls;
|
||||
}
|
||||
|
||||
struct tbl *
|
||||
tnext(ts)
|
||||
struct tstate *ts;
|
||||
{
|
||||
while (--ts->left >= 0) {
|
||||
struct tbl *p = *ts->next++;
|
||||
if (p != NULL && (p->flag&DEFINED))
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
tnamecmp(p1, p2)
|
||||
void *p1, *p2;
|
||||
{
|
||||
return strcmp(((struct tbl *)p1)->name, ((struct tbl *)p2)->name);
|
||||
}
|
||||
|
||||
struct tbl **
|
||||
tsort(tp)
|
||||
register struct table *tp;
|
||||
{
|
||||
register int i;
|
||||
register struct tbl **p, **sp, **dp;
|
||||
|
||||
p = (struct tbl **)alloc(sizeofN(struct tbl *, tp->size+1), ATEMP);
|
||||
sp = tp->tbls; /* source */
|
||||
dp = p; /* dest */
|
||||
for (i = 0; i < tp->size; i++)
|
||||
if ((*dp = *sp++) != NULL && (((*dp)->flag&DEFINED) ||
|
||||
((*dp)->flag&ARRAY)))
|
||||
dp++;
|
||||
i = dp - p;
|
||||
qsortp((void**)p, (size_t)i, tnamecmp);
|
||||
p[i] = NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef PERF_DEBUG /* performance debugging */
|
||||
|
||||
void tprintinfo ARGS((struct table *tp));
|
||||
|
||||
void
|
||||
tprintinfo(tp)
|
||||
struct table *tp;
|
||||
{
|
||||
struct tbl *te;
|
||||
char *n;
|
||||
unsigned int h;
|
||||
int ncmp;
|
||||
int totncmp = 0, maxncmp = 0;
|
||||
int nentries = 0;
|
||||
struct tstate ts;
|
||||
|
||||
shellf("table size %d, nfree %d\n", tp->size, tp->nfree);
|
||||
shellf(" Ncmp name\n");
|
||||
twalk(&ts, tp);
|
||||
while ((te = tnext(&ts))) {
|
||||
register struct tbl **pp, *p;
|
||||
|
||||
h = hash(n = te->name);
|
||||
ncmp = 0;
|
||||
|
||||
/* taken from tsearch() and added counter */
|
||||
for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) {
|
||||
ncmp++;
|
||||
if (*p->name == *n && strcmp(p->name, n) == 0
|
||||
&& (p->flag&DEFINED))
|
||||
break; /* return p; */
|
||||
if (pp == tp->tbls) /* wrap */
|
||||
pp += tp->size;
|
||||
}
|
||||
shellf(" %4d %s\n", ncmp, n);
|
||||
totncmp += ncmp;
|
||||
nentries++;
|
||||
if (ncmp > maxncmp)
|
||||
maxncmp = ncmp;
|
||||
}
|
||||
if (nentries)
|
||||
shellf(" %d entries, worst ncmp %d, avg ncmp %d.%02d\n",
|
||||
nentries, maxncmp,
|
||||
totncmp / nentries,
|
||||
(totncmp % nentries) * 100 / nentries);
|
||||
}
|
||||
#endif /* PERF_DEBUG */
|
181
bin/ksh/table.h
Normal file
181
bin/ksh/table.h
Normal file
|
@ -0,0 +1,181 @@
|
|||
/* $NetBSD: table.h,v 1.3 1999/10/20 15:10:00 hubertf Exp $ */
|
||||
|
||||
/*
|
||||
* generic hashed associative table for commands and variables.
|
||||
*/
|
||||
|
||||
struct table {
|
||||
Area *areap; /* area to allocate entries */
|
||||
short size, nfree; /* hash size (always 2^^n), free entries */
|
||||
struct tbl **tbls; /* hashed table items */
|
||||
};
|
||||
|
||||
struct tbl { /* table item */
|
||||
Tflag flag; /* flags */
|
||||
int type; /* command type (see below), base (if INTEGER),
|
||||
* or offset from val.s of value (if EXPORT) */
|
||||
Area *areap; /* area to allocate from */
|
||||
union {
|
||||
char *s; /* string */
|
||||
long i; /* integer */
|
||||
int (*f) ARGS((char **)); /* int function */
|
||||
struct op *t; /* "function" tree */
|
||||
} val; /* value */
|
||||
int index; /* index for an array */
|
||||
union {
|
||||
int field; /* field with for -L/-R/-Z */
|
||||
int errno_; /* CEXEC/CTALIAS */
|
||||
} u2;
|
||||
union {
|
||||
struct tbl *array; /* array values */
|
||||
char *fpath; /* temporary path to undef function */
|
||||
} u;
|
||||
char name[4]; /* name -- variable length */
|
||||
};
|
||||
|
||||
/* common flag bits */
|
||||
#define ALLOC BIT(0) /* val.s has been allocated */
|
||||
#define DEFINED BIT(1) /* is defined in block */
|
||||
#define ISSET BIT(2) /* has value, vp->val.[si] */
|
||||
#define EXPORT BIT(3) /* exported variable/function */
|
||||
#define TRACE BIT(4) /* var: user flagged, func: execution tracing */
|
||||
/* (start non-common flags at 8) */
|
||||
/* flag bits used for variables */
|
||||
#define SPECIAL BIT(8) /* PATH, IFS, SECONDS, etc */
|
||||
#define INTEGER BIT(9) /* val.i contains integer value */
|
||||
#define RDONLY BIT(10) /* read-only variable */
|
||||
#define LOCAL BIT(11) /* for local typeset() */
|
||||
#define ARRAY BIT(13) /* array */
|
||||
#define LJUST BIT(14) /* left justify */
|
||||
#define RJUST BIT(15) /* right justify */
|
||||
#define ZEROFIL BIT(16) /* 0 filled if RJUSTIFY, strip 0s if LJUSTIFY */
|
||||
#define LCASEV BIT(17) /* convert to lower case */
|
||||
#define UCASEV_AL BIT(18)/* convert to upper case / autoload function */
|
||||
#define INT_U BIT(19) /* unsigned integer */
|
||||
#define INT_L BIT(20) /* long integer (no-op) */
|
||||
#define IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */
|
||||
#define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */
|
||||
#define EXPRINEVAL BIT(23) /* contents currently being evaluated */
|
||||
#define EXPRLVALUE BIT(24) /* useable as lvalue (temp flag) */
|
||||
/* flag bits used for taliases/builtins/aliases/keywords/functions */
|
||||
#define KEEPASN BIT(8) /* keep command assignments (eg, var=x cmd) */
|
||||
#define FINUSE BIT(9) /* function being executed */
|
||||
#define FDELETE BIT(10) /* function deleted while it was executing */
|
||||
#define FKSH BIT(11) /* function defined with function x (vs x()) */
|
||||
#define SPEC_BI BIT(12) /* a POSIX special builtin */
|
||||
#define REG_BI BIT(13) /* a POSIX regular builtin */
|
||||
/* Attributes that can be set by the user (used to decide if an unset param
|
||||
* should be repoted by set/typeset). Does not include ARRAY or LOCAL.
|
||||
*/
|
||||
#define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL\
|
||||
|LCASEV|UCASEV_AL|INT_U|INT_L)
|
||||
|
||||
/* command types */
|
||||
#define CNONE 0 /* undefined */
|
||||
#define CSHELL 1 /* built-in */
|
||||
#define CFUNC 2 /* function */
|
||||
#define CEXEC 4 /* executable command */
|
||||
#define CALIAS 5 /* alias */
|
||||
#define CKEYWD 6 /* keyword */
|
||||
#define CTALIAS 7 /* tracked alias */
|
||||
|
||||
/* Flags for findcom()/comexec() */
|
||||
#define FC_SPECBI BIT(0) /* special builtin */
|
||||
#define FC_FUNC BIT(1) /* function builtin */
|
||||
#define FC_REGBI BIT(2) /* regular builtin */
|
||||
#define FC_UNREGBI BIT(3) /* un-regular builtin (!special,!regular) */
|
||||
#define FC_BI (FC_SPECBI|FC_REGBI|FC_UNREGBI)
|
||||
#define FC_PATH BIT(4) /* do path search */
|
||||
#define FC_DEFPATH BIT(5) /* use default path in path search */
|
||||
|
||||
|
||||
#define AF_ARGV_ALLOC 0x1 /* argv[] array allocated */
|
||||
#define AF_ARGS_ALLOCED 0x2 /* argument strings allocated */
|
||||
#define AI_ARGV(a, i) ((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
|
||||
#define AI_ARGC(a) ((a).argc_ - (a).skip)
|
||||
|
||||
/* Argument info. Used for $#, $* for shell, functions, includes, etc. */
|
||||
struct arg_info {
|
||||
int flags; /* AF_* */
|
||||
char **argv;
|
||||
int argc_;
|
||||
int skip; /* first arg is argv[0], second is argv[1 + skip] */
|
||||
};
|
||||
|
||||
/*
|
||||
* activation record for function blocks
|
||||
*/
|
||||
struct block {
|
||||
Area area; /* area to allocate things */
|
||||
/*struct arg_info argi;*/
|
||||
char **argv;
|
||||
int argc;
|
||||
int flags; /* see BF_* */
|
||||
struct table vars; /* local variables */
|
||||
struct table funs; /* local functions */
|
||||
Getopt getopts_state;
|
||||
#if 1
|
||||
char * error; /* error handler */
|
||||
char * exit; /* exit handler */
|
||||
#else
|
||||
Trap error, exit;
|
||||
#endif
|
||||
struct block *next; /* enclosing block */
|
||||
};
|
||||
|
||||
/* Values for struct block.flags */
|
||||
#define BF_DOGETOPTS BIT(0) /* save/restore getopts state */
|
||||
|
||||
/*
|
||||
* Used by twalk() and tnext() routines.
|
||||
*/
|
||||
struct tstate {
|
||||
int left;
|
||||
struct tbl **next;
|
||||
};
|
||||
|
||||
|
||||
EXTERN struct table taliases; /* tracked aliases */
|
||||
EXTERN struct table builtins; /* built-in commands */
|
||||
EXTERN struct table aliases; /* aliases */
|
||||
EXTERN struct table keywords; /* keywords */
|
||||
EXTERN struct table homedirs; /* homedir() cache */
|
||||
|
||||
struct builtin {
|
||||
const char *name;
|
||||
int (*func) ARGS((char **));
|
||||
};
|
||||
|
||||
/* these really are externs! Look in table.c for them */
|
||||
extern const struct builtin shbuiltins [], kshbuiltins [];
|
||||
|
||||
/* var spec values */
|
||||
#define V_NONE 0
|
||||
#define V_PATH 1
|
||||
#define V_IFS 2
|
||||
#define V_SECONDS 3
|
||||
#define V_OPTIND 4
|
||||
#define V_MAIL 5
|
||||
#define V_MAILPATH 6
|
||||
#define V_MAILCHECK 7
|
||||
#define V_RANDOM 8
|
||||
#define V_HISTSIZE 9
|
||||
#define V_HISTFILE 10
|
||||
#define V_VISUAL 11
|
||||
#define V_EDITOR 12
|
||||
#define V_COLUMNS 13
|
||||
#define V_POSIXLY_CORRECT 14
|
||||
#define V_TMOUT 15
|
||||
#define V_TMPDIR 16
|
||||
#define V_LINENO 17
|
||||
|
||||
/* values for set_prompt() */
|
||||
#define PS1 0 /* command */
|
||||
#define PS2 1 /* command continuation */
|
||||
|
||||
EXTERN char *path; /* copy of either PATH or def_path */
|
||||
EXTERN const char *def_path; /* path to use if PATH not set */
|
||||
EXTERN char *tmpdir; /* TMPDIR value */
|
||||
EXTERN const char *prompt;
|
||||
EXTERN int cur_prompt; /* PS1 or PS2 */
|
||||
EXTERN int current_lineno; /* LINENO value */
|
460
bin/ksh/trap.c
Normal file
460
bin/ksh/trap.c
Normal file
|
@ -0,0 +1,460 @@
|
|||
/* $NetBSD: trap.c,v 1.8 2006/10/16 00:07:32 christos Exp $ */
|
||||
|
||||
/*
|
||||
* signal handling
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: trap.c,v 1.8 2006/10/16 00:07:32 christos Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
/* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
|
||||
#define FROM_TRAP_C
|
||||
#include "sh.h"
|
||||
|
||||
/* Table is indexed by signal number
|
||||
*
|
||||
* The script siglist.sh generates siglist.out, which is a sorted, complete
|
||||
* list of signals
|
||||
*/
|
||||
Trap sigtraps[SIGNALS+1] = {
|
||||
{ .signal = SIGEXIT_, .name = "EXIT", .mess = "Signal 0" },
|
||||
#include "siglist.out" /* generated by siglist.sh */
|
||||
{ .signal = SIGERR_, .name = "ERR", .mess = "Error handler" },
|
||||
};
|
||||
|
||||
static struct sigaction Sigact_ign, Sigact_trap;
|
||||
|
||||
void
|
||||
inittraps()
|
||||
{
|
||||
#ifdef HAVE_SYS_SIGLIST
|
||||
# ifndef SYS_SIGLIST_DECLARED
|
||||
extern char *sys_siglist[];
|
||||
# endif
|
||||
int i;
|
||||
|
||||
/* Use system description, if available, for unknown signals... */
|
||||
for (i = 0; i < NSIG; i++)
|
||||
if (!sigtraps[i].name && sys_siglist[i] && sys_siglist[i][0])
|
||||
sigtraps[i].mess = sys_siglist[i];
|
||||
#endif /* HAVE_SYS_SIGLIST */
|
||||
|
||||
sigemptyset(&Sigact_ign.sa_mask);
|
||||
Sigact_ign.sa_flags = KSH_SA_FLAGS;
|
||||
Sigact_ign.sa_handler = SIG_IGN;
|
||||
Sigact_trap = Sigact_ign;
|
||||
Sigact_trap.sa_handler = trapsig;
|
||||
|
||||
sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
|
||||
sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
|
||||
sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
|
||||
sigtraps[SIGHUP].flags |= TF_FATAL;
|
||||
sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
|
||||
|
||||
/* these are always caught so we can clean up any temporary files. */
|
||||
setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
|
||||
setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
|
||||
setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
|
||||
setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
|
||||
}
|
||||
|
||||
#ifdef KSH
|
||||
static RETSIGTYPE alarm_catcher ARGS((int sig));
|
||||
|
||||
void
|
||||
alarm_init()
|
||||
{
|
||||
sigtraps[SIGALRM].flags |= TF_SHELL_USES;
|
||||
setsig(&sigtraps[SIGALRM], alarm_catcher,
|
||||
SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
|
||||
}
|
||||
|
||||
static RETSIGTYPE
|
||||
alarm_catcher(sig)
|
||||
int sig;
|
||||
{
|
||||
int errno_ = errno;
|
||||
|
||||
if (ksh_tmout_state == TMOUT_READING) {
|
||||
int left = alarm(0);
|
||||
|
||||
if (left == 0) {
|
||||
ksh_tmout_state = TMOUT_LEAVING;
|
||||
intrsig = 1;
|
||||
} else
|
||||
alarm(left);
|
||||
}
|
||||
errno = errno_;
|
||||
return RETSIGVAL;
|
||||
}
|
||||
#endif /* KSH */
|
||||
|
||||
Trap *
|
||||
gettrap(name, igncase)
|
||||
const char *name;
|
||||
int igncase;
|
||||
{
|
||||
int i;
|
||||
register Trap *p;
|
||||
|
||||
if (digit(*name)) {
|
||||
int n;
|
||||
|
||||
if (getn(name, &n) && 0 <= n && n < SIGNALS)
|
||||
return &sigtraps[n];
|
||||
return NULL;
|
||||
}
|
||||
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
||||
if (p->name) {
|
||||
if (igncase) {
|
||||
if (p->name && (!strcasecmp(p->name, name) ||
|
||||
(strlen(name) > 3 && !strncasecmp("SIG",
|
||||
p->name, 3) &&
|
||||
!strcasecmp(p->name, name + 3))))
|
||||
return p;
|
||||
} else {
|
||||
if (p->name && (!strcmp(p->name, name) ||
|
||||
(strlen(name) > 3 && !strncmp("SIG",
|
||||
p->name, 3) && !strcmp(p->name, name + 3))))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* trap signal handler
|
||||
*/
|
||||
RETSIGTYPE
|
||||
trapsig(i)
|
||||
int i;
|
||||
{
|
||||
Trap *p = &sigtraps[i];
|
||||
int errno_ = errno;
|
||||
|
||||
trap = p->set = 1;
|
||||
if (p->flags & TF_DFL_INTR)
|
||||
intrsig = 1;
|
||||
if ((p->flags & TF_FATAL) && !p->trap) {
|
||||
fatal_trap = 1;
|
||||
intrsig = 1;
|
||||
}
|
||||
if (p->shtrap)
|
||||
(*p->shtrap)(i);
|
||||
#ifdef V7_SIGNALS
|
||||
if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */
|
||||
sigaction(i, &Sigact_trap, (struct sigaction *) 0);
|
||||
#endif /* V7_SIGNALS */
|
||||
errno = errno_;
|
||||
return RETSIGVAL;
|
||||
}
|
||||
|
||||
/* called when we want to allow the user to ^C out of something - won't
|
||||
* work if user has trapped SIGINT.
|
||||
*/
|
||||
void
|
||||
intrcheck()
|
||||
{
|
||||
if (intrsig)
|
||||
runtraps(TF_DFL_INTR|TF_FATAL);
|
||||
}
|
||||
|
||||
/* called after EINTR to check if a signal with normally causes process
|
||||
* termination has been received.
|
||||
*/
|
||||
int
|
||||
fatal_trap_check()
|
||||
{
|
||||
int i;
|
||||
Trap *p;
|
||||
|
||||
/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
|
||||
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
||||
if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
|
||||
/* return value is used as an exit code */
|
||||
return 128 + p->signal;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the signal number of any pending traps: ie, a signal which has
|
||||
* occurred for which a trap has been set or for which the TF_DFL_INTR flag
|
||||
* is set.
|
||||
*/
|
||||
int
|
||||
trap_pending()
|
||||
{
|
||||
int i;
|
||||
Trap *p;
|
||||
|
||||
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
||||
if (p->set && ((p->trap && p->trap[0])
|
||||
|| ((p->flags & (TF_DFL_INTR|TF_FATAL))
|
||||
&& !p->trap)))
|
||||
return p->signal;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* run any pending traps. If intr is set, only run traps that
|
||||
* can interrupt commands.
|
||||
*/
|
||||
void
|
||||
runtraps(flag)
|
||||
int flag;
|
||||
{
|
||||
int i;
|
||||
register Trap *p;
|
||||
|
||||
#ifdef KSH
|
||||
if (ksh_tmout_state == TMOUT_LEAVING) {
|
||||
ksh_tmout_state = TMOUT_EXECUTING;
|
||||
warningf(FALSE, "timed out waiting for input");
|
||||
unwind(LEXIT);
|
||||
} else
|
||||
/* XXX: this means the alarm will have no effect if a trap
|
||||
* is caught after the alarm() was started...not good.
|
||||
*/
|
||||
ksh_tmout_state = TMOUT_EXECUTING;
|
||||
#endif /* KSH */
|
||||
if (!flag)
|
||||
trap = 0;
|
||||
if (flag & TF_DFL_INTR)
|
||||
intrsig = 0;
|
||||
if (flag & TF_FATAL)
|
||||
fatal_trap = 0;
|
||||
for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
|
||||
if (p->set && (!flag
|
||||
|| ((p->flags & flag) && p->trap == (char *) 0)))
|
||||
runtrap(p);
|
||||
}
|
||||
|
||||
void
|
||||
runtrap(p)
|
||||
Trap *p;
|
||||
{
|
||||
int i = p->signal;
|
||||
char *trapstr = p->trap;
|
||||
int oexstat;
|
||||
int UNINITIALIZED(old_changed);
|
||||
|
||||
p->set = 0;
|
||||
if (trapstr == (char *) 0) { /* SIG_DFL */
|
||||
if (p->flags & TF_FATAL) {
|
||||
/* eg, SIGHUP */
|
||||
exstat = 128 + i;
|
||||
unwind(LLEAVE);
|
||||
}
|
||||
if (p->flags & TF_DFL_INTR) {
|
||||
/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
|
||||
exstat = 128 + i;
|
||||
unwind(LINTR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (trapstr[0] == '\0') /* SIG_IGN */
|
||||
return;
|
||||
if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
|
||||
old_changed = p->flags & TF_CHANGED;
|
||||
p->flags &= ~TF_CHANGED;
|
||||
p->trap = (char *) 0;
|
||||
}
|
||||
oexstat = exstat;
|
||||
/* Note: trapstr is fully parsed before anything is executed, thus
|
||||
* no problem with afree(p->trap) in settrap() while still in use.
|
||||
*/
|
||||
command(trapstr);
|
||||
exstat = oexstat;
|
||||
if (i == SIGEXIT_ || i == SIGERR_) {
|
||||
if (p->flags & TF_CHANGED)
|
||||
/* don't clear TF_CHANGED */
|
||||
afree(trapstr, APERM);
|
||||
else
|
||||
p->trap = trapstr;
|
||||
p->flags |= old_changed;
|
||||
}
|
||||
}
|
||||
|
||||
/* clear pending traps and reset user's trap handlers; used after fork(2) */
|
||||
void
|
||||
cleartraps()
|
||||
{
|
||||
int i;
|
||||
Trap *p;
|
||||
|
||||
trap = 0;
|
||||
intrsig = 0;
|
||||
fatal_trap = 0;
|
||||
for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) {
|
||||
p->set = 0;
|
||||
if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
|
||||
settrap(p, (char *) 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* restore signals just before an exec(2) */
|
||||
void
|
||||
restoresigs()
|
||||
{
|
||||
int i;
|
||||
Trap *p;
|
||||
|
||||
for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++)
|
||||
if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
|
||||
setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
|
||||
SS_RESTORE_CURR|SS_FORCE);
|
||||
}
|
||||
|
||||
void
|
||||
settrap(p, s)
|
||||
Trap *p;
|
||||
char *s;
|
||||
{
|
||||
handler_t f;
|
||||
|
||||
if (p->trap)
|
||||
afree(p->trap, APERM);
|
||||
p->flags |= TF_CHANGED|TF_USER_SET;
|
||||
if (s) {
|
||||
p->trap = str_save(s, APERM);
|
||||
f = s[0] ? trapsig : SIG_IGN;
|
||||
} else {
|
||||
p->trap = NULL;
|
||||
f = SIG_DFL;
|
||||
}
|
||||
if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
|
||||
f = trapsig;
|
||||
else if (p->flags & TF_SHELL_USES) {
|
||||
if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
|
||||
/* do what user wants at exec time */
|
||||
p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
|
||||
if (f == SIG_IGN)
|
||||
p->flags |= TF_EXEC_IGN;
|
||||
else
|
||||
p->flags |= TF_EXEC_DFL;
|
||||
}
|
||||
/* assumes handler already set to what shell wants it
|
||||
* (normally trapsig, but could be j_sigchld() or SIG_IGN)
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* todo: should we let user know signal is ignored? how? */
|
||||
setsig(p, f, SS_RESTORE_CURR|SS_USER);
|
||||
}
|
||||
|
||||
/* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
|
||||
* kill shell (unless user catches it and exits)
|
||||
*/
|
||||
int
|
||||
block_pipe()
|
||||
{
|
||||
int restore_dfl = 0;
|
||||
Trap *p = &sigtraps[SIGPIPE];
|
||||
|
||||
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
|
||||
setsig(p, SIG_IGN, SS_RESTORE_CURR);
|
||||
if (p->flags & TF_ORIG_DFL)
|
||||
restore_dfl = 1;
|
||||
} else if (p->cursig == SIG_DFL) {
|
||||
setsig(p, SIG_IGN, SS_RESTORE_CURR);
|
||||
restore_dfl = 1; /* restore to SIG_DFL */
|
||||
}
|
||||
return restore_dfl;
|
||||
}
|
||||
|
||||
/* Called by c_print() to undo whatever block_pipe() did */
|
||||
void
|
||||
restore_pipe(restore_dfl)
|
||||
int restore_dfl;
|
||||
{
|
||||
if (restore_dfl)
|
||||
setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
|
||||
}
|
||||
|
||||
/* Set action for a signal. Action may not be set if original
|
||||
* action was SIG_IGN, depending on the value of flags and
|
||||
* FTALKING.
|
||||
*/
|
||||
int
|
||||
setsig(p, f, flags)
|
||||
Trap *p;
|
||||
handler_t f;
|
||||
int flags;
|
||||
{
|
||||
struct sigaction sigact;
|
||||
|
||||
if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
|
||||
return 1;
|
||||
|
||||
/* First time setting this signal? If so, get and note the current
|
||||
* setting.
|
||||
*/
|
||||
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
|
||||
sigaction(p->signal, &Sigact_ign, &sigact);
|
||||
p->flags |= sigact.sa_handler == SIG_IGN ?
|
||||
TF_ORIG_IGN : TF_ORIG_DFL;
|
||||
p->cursig = SIG_IGN;
|
||||
}
|
||||
|
||||
/* Generally, an ignored signal stays ignored, except if
|
||||
* - the user of an interactive shell wants to change it
|
||||
* - the shell wants for force a change
|
||||
*/
|
||||
if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE)
|
||||
&& (!(flags & SS_USER) || !Flag(FTALKING)))
|
||||
return 0;
|
||||
|
||||
setexecsig(p, flags & SS_RESTORE_MASK);
|
||||
|
||||
/* This is here 'cause there should be a way of clearing shtraps, but
|
||||
* don't know if this is a sane way of doing it. At the moment,
|
||||
* all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
|
||||
*/
|
||||
if (!(flags & SS_USER))
|
||||
p->shtrap = (handler_t) 0;
|
||||
if (flags & SS_SHTRAP) {
|
||||
p->shtrap = f;
|
||||
f = trapsig;
|
||||
}
|
||||
|
||||
if (p->cursig != f) {
|
||||
p->cursig = f;
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = KSH_SA_FLAGS;
|
||||
sigact.sa_handler = f;
|
||||
sigaction(p->signal, &sigact, (struct sigaction *) 0);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* control what signal is set to before an exec() */
|
||||
void
|
||||
setexecsig(p, restore)
|
||||
Trap *p;
|
||||
int restore;
|
||||
{
|
||||
/* XXX debugging */
|
||||
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
|
||||
internal_errorf(1, "setexecsig: unset signal %d(%s)",
|
||||
p->signal, p->name);
|
||||
|
||||
/* restore original value for exec'd kids */
|
||||
p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
|
||||
switch (restore & SS_RESTORE_MASK) {
|
||||
case SS_RESTORE_CURR: /* leave things as they currently are */
|
||||
break;
|
||||
case SS_RESTORE_ORIG:
|
||||
p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
|
||||
break;
|
||||
case SS_RESTORE_DFL:
|
||||
p->flags |= TF_EXEC_DFL;
|
||||
break;
|
||||
case SS_RESTORE_IGN:
|
||||
p->flags |= TF_EXEC_IGN;
|
||||
break;
|
||||
}
|
||||
}
|
766
bin/ksh/tree.c
Normal file
766
bin/ksh/tree.c
Normal file
|
@ -0,0 +1,766 @@
|
|||
/* $NetBSD: tree.c,v 1.6 2005/06/26 19:09:00 christos Exp $ */
|
||||
|
||||
/*
|
||||
* command tree climbing
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: tree.c,v 1.6 2005/06/26 19:09:00 christos Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
#define INDENT 4
|
||||
|
||||
#define tputc(c, shf) shf_putchar(c, shf);
|
||||
static void ptree ARGS((struct op *t, int indent, struct shf *f));
|
||||
static void pioact ARGS((struct shf *f, int indent, struct ioword *iop));
|
||||
static void tputC ARGS((int c, struct shf *shf));
|
||||
static void tputS ARGS((char *wp, struct shf *shf));
|
||||
static void vfptreef ARGS((struct shf *shf, int indent, const char *fmt, va_list va));
|
||||
static struct ioword **iocopy ARGS((struct ioword **iow, Area *ap));
|
||||
static void iofree ARGS((struct ioword **iow, Area *ap));
|
||||
|
||||
/*
|
||||
* print a command tree
|
||||
*/
|
||||
|
||||
static void
|
||||
ptree(t, indent, shf)
|
||||
register struct op *t;
|
||||
int indent;
|
||||
register struct shf *shf;
|
||||
{
|
||||
register char **w;
|
||||
struct ioword **ioact;
|
||||
struct op *t1;
|
||||
|
||||
Chain:
|
||||
if (t == NULL)
|
||||
return;
|
||||
switch (t->type) {
|
||||
case TCOM:
|
||||
if (t->vars)
|
||||
for (w = t->vars; *w != NULL; )
|
||||
fptreef(shf, indent, "%S ", *w++);
|
||||
else
|
||||
fptreef(shf, indent, "#no-vars# ");
|
||||
if (t->args)
|
||||
for (w = t->args; *w != NULL; )
|
||||
fptreef(shf, indent, "%S ", *w++);
|
||||
else
|
||||
fptreef(shf, indent, "#no-args# ");
|
||||
break;
|
||||
case TEXEC:
|
||||
#if 0 /* ?not useful - can't be called? */
|
||||
/* Print original vars */
|
||||
if (t->left->vars)
|
||||
for (w = t->left->vars; *w != NULL; )
|
||||
fptreef(shf, indent, "%S ", *w++);
|
||||
else
|
||||
fptreef(shf, indent, "#no-vars# ");
|
||||
/* Print expanded vars */
|
||||
if (t->args)
|
||||
for (w = t->args; *w != NULL; )
|
||||
fptreef(shf, indent, "%s ", *w++);
|
||||
else
|
||||
fptreef(shf, indent, "#no-args# ");
|
||||
/* Print original io */
|
||||
t = t->left;
|
||||
#else
|
||||
t = t->left;
|
||||
goto Chain;
|
||||
#endif
|
||||
case TPAREN:
|
||||
fptreef(shf, indent + 2, "( %T) ", t->left);
|
||||
break;
|
||||
case TPIPE:
|
||||
fptreef(shf, indent, "%T| ", t->left);
|
||||
t = t->right;
|
||||
goto Chain;
|
||||
case TLIST:
|
||||
fptreef(shf, indent, "%T%;", t->left);
|
||||
t = t->right;
|
||||
goto Chain;
|
||||
case TOR:
|
||||
case TAND:
|
||||
fptreef(shf, indent, "%T%s %T",
|
||||
t->left, (t->type==TOR) ? "||" : "&&", t->right);
|
||||
break;
|
||||
case TBANG:
|
||||
fptreef(shf, indent, "! ");
|
||||
t = t->right;
|
||||
goto Chain;
|
||||
case TDBRACKET:
|
||||
{
|
||||
int i;
|
||||
|
||||
fptreef(shf, indent, "[[");
|
||||
for (i = 0; t->args[i]; i++)
|
||||
fptreef(shf, indent, " %S", t->args[i]);
|
||||
fptreef(shf, indent, " ]] ");
|
||||
break;
|
||||
}
|
||||
#ifdef KSH
|
||||
case TSELECT:
|
||||
fptreef(shf, indent, "select %s ", t->str);
|
||||
/* fall through */
|
||||
#endif /* KSH */
|
||||
case TFOR:
|
||||
if (t->type == TFOR)
|
||||
fptreef(shf, indent, "for %s ", t->str);
|
||||
if (t->vars != NULL) {
|
||||
fptreef(shf, indent, "in ");
|
||||
for (w = t->vars; *w; )
|
||||
fptreef(shf, indent, "%S ", *w++);
|
||||
fptreef(shf, indent, "%;");
|
||||
}
|
||||
fptreef(shf, indent + INDENT, "do%N%T", t->left);
|
||||
fptreef(shf, indent, "%;done ");
|
||||
break;
|
||||
case TCASE:
|
||||
fptreef(shf, indent, "case %S in", t->str);
|
||||
for (t1 = t->left; t1 != NULL; t1 = t1->right) {
|
||||
fptreef(shf, indent, "%N(");
|
||||
for (w = t1->vars; *w != NULL; w++)
|
||||
fptreef(shf, indent, "%S%c", *w,
|
||||
(w[1] != NULL) ? '|' : ')');
|
||||
fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left);
|
||||
}
|
||||
fptreef(shf, indent, "%Nesac ");
|
||||
break;
|
||||
case TIF:
|
||||
case TELIF:
|
||||
/* 3 == strlen("if ") */
|
||||
fptreef(shf, indent + 3, "if %T", t->left);
|
||||
for (;;) {
|
||||
t = t->right;
|
||||
if (t->left != NULL) {
|
||||
fptreef(shf, indent, "%;");
|
||||
fptreef(shf, indent + INDENT, "then%N%T",
|
||||
t->left);
|
||||
}
|
||||
if (t->right == NULL || t->right->type != TELIF)
|
||||
break;
|
||||
t = t->right;
|
||||
fptreef(shf, indent, "%;");
|
||||
/* 5 == strlen("elif ") */
|
||||
fptreef(shf, indent + 5, "elif %T", t->left);
|
||||
}
|
||||
if (t->right != NULL) {
|
||||
fptreef(shf, indent, "%;");
|
||||
fptreef(shf, indent + INDENT, "else%;%T", t->right);
|
||||
}
|
||||
fptreef(shf, indent, "%;fi ");
|
||||
break;
|
||||
case TWHILE:
|
||||
case TUNTIL:
|
||||
/* 6 == strlen("while"/"until") */
|
||||
fptreef(shf, indent + 6, "%s %T",
|
||||
(t->type==TWHILE) ? "while" : "until",
|
||||
t->left);
|
||||
fptreef(shf, indent, "%;do");
|
||||
fptreef(shf, indent + INDENT, "%;%T", t->right);
|
||||
fptreef(shf, indent, "%;done ");
|
||||
break;
|
||||
case TBRACE:
|
||||
fptreef(shf, indent + INDENT, "{%;%T", t->left);
|
||||
fptreef(shf, indent, "%;} ");
|
||||
break;
|
||||
case TCOPROC:
|
||||
fptreef(shf, indent, "%T|& ", t->left);
|
||||
break;
|
||||
case TASYNC:
|
||||
fptreef(shf, indent, "%T& ", t->left);
|
||||
break;
|
||||
case TFUNCT:
|
||||
fptreef(shf, indent,
|
||||
t->u.ksh_func ? "function %s %T" : "%s() %T",
|
||||
t->str, t->left);
|
||||
break;
|
||||
case TTIME:
|
||||
fptreef(shf, indent, "time %T", t->left);
|
||||
break;
|
||||
default:
|
||||
fptreef(shf, indent, "<botch>");
|
||||
break;
|
||||
}
|
||||
if ((ioact = t->ioact) != NULL) {
|
||||
int need_nl = 0;
|
||||
|
||||
while (*ioact != NULL)
|
||||
pioact(shf, indent, *ioact++);
|
||||
/* Print here documents after everything else... */
|
||||
for (ioact = t->ioact; *ioact != NULL; ) {
|
||||
struct ioword *iop = *ioact++;
|
||||
|
||||
/* heredoc is 0 when tracing (set -x) */
|
||||
if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc) {
|
||||
tputc('\n', shf);
|
||||
shf_puts(iop->heredoc, shf);
|
||||
fptreef(shf, indent, "%s",
|
||||
evalstr(iop->delim, 0));
|
||||
need_nl = 1;
|
||||
}
|
||||
}
|
||||
/* Last delimiter must be followed by a newline (this often
|
||||
* leads to an extra blank line, but its not worth worrying
|
||||
* about)
|
||||
*/
|
||||
if (need_nl)
|
||||
tputc('\n', shf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pioact(shf, indent, iop)
|
||||
register struct shf *shf;
|
||||
int indent;
|
||||
register struct ioword *iop;
|
||||
{
|
||||
int flag = iop->flag;
|
||||
int type = flag & IOTYPE;
|
||||
int expected;
|
||||
|
||||
expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0
|
||||
: (type == IOCAT || type == IOWRITE) ? 1
|
||||
: (type == IODUP && (iop->unit == !(flag & IORDUP))) ?
|
||||
iop->unit
|
||||
: iop->unit + 1;
|
||||
if (iop->unit != expected)
|
||||
tputc('0' + iop->unit, shf);
|
||||
|
||||
switch (type) {
|
||||
case IOREAD:
|
||||
fptreef(shf, indent, "< ");
|
||||
break;
|
||||
case IOHERE:
|
||||
if (flag&IOSKIP)
|
||||
fptreef(shf, indent, "<<- ");
|
||||
else
|
||||
fptreef(shf, indent, "<< ");
|
||||
break;
|
||||
case IOCAT:
|
||||
fptreef(shf, indent, ">> ");
|
||||
break;
|
||||
case IOWRITE:
|
||||
if (flag&IOCLOB)
|
||||
fptreef(shf, indent, ">| ");
|
||||
else
|
||||
fptreef(shf, indent, "> ");
|
||||
break;
|
||||
case IORDWR:
|
||||
fptreef(shf, indent, "<> ");
|
||||
break;
|
||||
case IODUP:
|
||||
if (flag & IORDUP)
|
||||
fptreef(shf, indent, "<&");
|
||||
else
|
||||
fptreef(shf, indent, ">&");
|
||||
break;
|
||||
}
|
||||
/* name/delim are 0 when printing syntax errors */
|
||||
if (type == IOHERE) {
|
||||
if (iop->delim)
|
||||
fptreef(shf, indent, "%S ", iop->delim);
|
||||
} else if (iop->name)
|
||||
fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
|
||||
iop->name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* variants of fputc, fputs for ptreef and snptreef
|
||||
*/
|
||||
|
||||
static void
|
||||
tputC(c, shf)
|
||||
register int c;
|
||||
register struct shf *shf;
|
||||
{
|
||||
if ((c&0x60) == 0) { /* C0|C1 */
|
||||
tputc((c&0x80) ? '$' : '^', shf);
|
||||
tputc(((c&0x7F)|0x40), shf);
|
||||
} else if ((c&0x7F) == 0x7F) { /* DEL */
|
||||
tputc((c&0x80) ? '$' : '^', shf);
|
||||
tputc('?', shf);
|
||||
} else
|
||||
tputc(c, shf);
|
||||
}
|
||||
|
||||
static void
|
||||
tputS(wp, shf)
|
||||
register char *wp;
|
||||
register struct shf *shf;
|
||||
{
|
||||
register int c, quoted=0;
|
||||
|
||||
/* problems:
|
||||
* `...` -> $(...)
|
||||
* 'foo' -> "foo"
|
||||
* could change encoding to:
|
||||
* OQUOTE ["'] ... CQUOTE ["']
|
||||
* COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case)
|
||||
*/
|
||||
while (1)
|
||||
switch ((c = *wp++)) {
|
||||
case EOS:
|
||||
return;
|
||||
case CHAR:
|
||||
tputC(*wp++, shf);
|
||||
break;
|
||||
case QCHAR:
|
||||
c = *wp++;
|
||||
if (!quoted || (c == '"' || c == '`' || c == '$'))
|
||||
tputc('\\', shf);
|
||||
tputC(c, shf);
|
||||
break;
|
||||
case COMSUB:
|
||||
tputc('$', shf);
|
||||
tputc('(', shf);
|
||||
while (*wp != 0)
|
||||
tputC(*wp++, shf);
|
||||
tputc(')', shf);
|
||||
wp++;
|
||||
break;
|
||||
case EXPRSUB:
|
||||
tputc('$', shf);
|
||||
tputc('(', shf);
|
||||
tputc('(', shf);
|
||||
while (*wp != 0)
|
||||
tputC(*wp++, shf);
|
||||
tputc(')', shf);
|
||||
tputc(')', shf);
|
||||
wp++;
|
||||
break;
|
||||
case OQUOTE:
|
||||
quoted = 1;
|
||||
tputc('"', shf);
|
||||
break;
|
||||
case CQUOTE:
|
||||
quoted = 0;
|
||||
tputc('"', shf);
|
||||
break;
|
||||
case OSUBST:
|
||||
tputc('$', shf);
|
||||
if (*wp++ == '{')
|
||||
tputc('{', shf);
|
||||
while ((c = *wp++) != 0)
|
||||
tputC(c, shf);
|
||||
break;
|
||||
case CSUBST:
|
||||
if (*wp++ == '}')
|
||||
tputc('}', shf);
|
||||
break;
|
||||
#ifdef KSH
|
||||
case OPAT:
|
||||
tputc(*wp++, shf);
|
||||
tputc('(', shf);
|
||||
break;
|
||||
case SPAT:
|
||||
tputc('|', shf);
|
||||
break;
|
||||
case CPAT:
|
||||
tputc(')', shf);
|
||||
break;
|
||||
#endif /* KSH */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* this is the _only_ way to reliably handle
|
||||
* variable args with an ANSI compiler
|
||||
*/
|
||||
/* VARARGS */
|
||||
int
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
fptreef(struct shf *shf, int indent, const char *fmt, ...)
|
||||
#else
|
||||
fptreef(shf, indent, fmt, va_alist)
|
||||
struct shf *shf;
|
||||
int indent;
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list va;
|
||||
|
||||
SH_VA_START(va, fmt);
|
||||
|
||||
vfptreef(shf, indent, fmt, va);
|
||||
va_end(va);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* VARARGS */
|
||||
char *
|
||||
#ifdef HAVE_PROTOTYPES
|
||||
snptreef(char *s, int n, const char *fmt, ...)
|
||||
#else
|
||||
snptreef(s, n, fmt, va_alist)
|
||||
char *s;
|
||||
int n;
|
||||
const char *fmt;
|
||||
va_dcl
|
||||
#endif
|
||||
{
|
||||
va_list va;
|
||||
struct shf shf;
|
||||
|
||||
shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
|
||||
|
||||
SH_VA_START(va, fmt);
|
||||
vfptreef(&shf, 0, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
return shf_sclose(&shf); /* null terminates */
|
||||
}
|
||||
|
||||
static void
|
||||
vfptreef(shf, indent, fmt, va)
|
||||
register struct shf *shf;
|
||||
int indent;
|
||||
const char *fmt;
|
||||
register va_list va;
|
||||
{
|
||||
register int c;
|
||||
|
||||
while ((c = *fmt++))
|
||||
if (c == '%') {
|
||||
register long n;
|
||||
register char *p;
|
||||
int neg;
|
||||
|
||||
switch ((c = *fmt++)) {
|
||||
case 'c':
|
||||
tputc(va_arg(va, int), shf);
|
||||
break;
|
||||
case 's':
|
||||
p = va_arg(va, char *);
|
||||
while (*p)
|
||||
tputc(*p++, shf);
|
||||
break;
|
||||
case 'S': /* word */
|
||||
p = va_arg(va, char *);
|
||||
tputS(p, shf);
|
||||
break;
|
||||
case 'd': case 'u': /* decimal */
|
||||
n = (c == 'd') ? va_arg(va, int)
|
||||
: va_arg(va, unsigned int);
|
||||
neg = c=='d' && n<0;
|
||||
p = ulton((neg) ? -n : n, 10);
|
||||
if (neg)
|
||||
*--p = '-';
|
||||
while (*p)
|
||||
tputc(*p++, shf);
|
||||
break;
|
||||
case 'T': /* format tree */
|
||||
ptree(va_arg(va, struct op *), indent, shf);
|
||||
break;
|
||||
case ';': /* newline or ; */
|
||||
case 'N': /* newline or space */
|
||||
if (shf->flags & SHF_STRING) {
|
||||
if (c == ';')
|
||||
tputc(';', shf);
|
||||
tputc(' ', shf);
|
||||
} else {
|
||||
int i;
|
||||
|
||||
tputc('\n', shf);
|
||||
for (i = indent; i >= 8; i -= 8)
|
||||
tputc('\t', shf);
|
||||
for (; i > 0; --i)
|
||||
tputc(' ', shf);
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
pioact(shf, indent, va_arg(va, struct ioword *));
|
||||
break;
|
||||
default:
|
||||
tputc(c, shf);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
tputc(c, shf);
|
||||
}
|
||||
|
||||
/*
|
||||
* copy tree (for function definition)
|
||||
*/
|
||||
|
||||
struct op *
|
||||
tcopy(t, ap)
|
||||
register struct op *t;
|
||||
Area *ap;
|
||||
{
|
||||
register struct op *r;
|
||||
register char **tw, **rw;
|
||||
|
||||
if (t == NULL)
|
||||
return NULL;
|
||||
|
||||
r = (struct op *) alloc(sizeof(struct op), ap);
|
||||
|
||||
r->type = t->type;
|
||||
r->u.evalflags = t->u.evalflags;
|
||||
|
||||
r->str = t->type == TCASE ? wdcopy(t->str, ap) : str_save(t->str, ap);
|
||||
|
||||
if (t->vars == NULL)
|
||||
r->vars = NULL;
|
||||
else {
|
||||
for (tw = t->vars; *tw++ != NULL; )
|
||||
;
|
||||
rw = r->vars = (char **)
|
||||
alloc((tw - t->vars + 1) * sizeof(*tw), ap);
|
||||
for (tw = t->vars; *tw != NULL; )
|
||||
*rw++ = wdcopy(*tw++, ap);
|
||||
*rw = NULL;
|
||||
}
|
||||
|
||||
if (t->args == NULL)
|
||||
r->args = NULL;
|
||||
else {
|
||||
for (tw = t->args; *tw++ != NULL; )
|
||||
;
|
||||
rw = r->args = (char **)
|
||||
alloc((tw - t->args + 1) * sizeof(*tw), ap);
|
||||
for (tw = t->args; *tw != NULL; )
|
||||
*rw++ = wdcopy(*tw++, ap);
|
||||
*rw = NULL;
|
||||
}
|
||||
|
||||
r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
|
||||
|
||||
r->left = tcopy(t->left, ap);
|
||||
r->right = tcopy(t->right, ap);
|
||||
r->lineno = t->lineno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char *
|
||||
wdcopy(wp, ap)
|
||||
const char *wp;
|
||||
Area *ap;
|
||||
{
|
||||
size_t len = wdscan(wp, EOS) - wp;
|
||||
return memcpy(alloc(len, ap), wp, len);
|
||||
}
|
||||
|
||||
/* return the position of prefix c in wp plus 1 */
|
||||
char *
|
||||
wdscan(wp, c)
|
||||
register const char *wp;
|
||||
register int c;
|
||||
{
|
||||
register int nest = 0;
|
||||
|
||||
while (1)
|
||||
switch (*wp++) {
|
||||
case EOS:
|
||||
return (char *) __UNCONST(wp);
|
||||
case CHAR:
|
||||
case QCHAR:
|
||||
wp++;
|
||||
break;
|
||||
case COMSUB:
|
||||
case EXPRSUB:
|
||||
while (*wp++ != 0)
|
||||
;
|
||||
break;
|
||||
case OQUOTE:
|
||||
case CQUOTE:
|
||||
break;
|
||||
case OSUBST:
|
||||
nest++;
|
||||
while (*wp++ != '\0')
|
||||
;
|
||||
break;
|
||||
case CSUBST:
|
||||
wp++;
|
||||
if (c == CSUBST && nest == 0)
|
||||
return (char *) __UNCONST(wp);
|
||||
nest--;
|
||||
break;
|
||||
#ifdef KSH
|
||||
case OPAT:
|
||||
nest++;
|
||||
wp++;
|
||||
break;
|
||||
case SPAT:
|
||||
case CPAT:
|
||||
if (c == wp[-1] && nest == 0)
|
||||
return (char *) __UNCONST(wp);
|
||||
if (wp[-1] == CPAT)
|
||||
nest--;
|
||||
break;
|
||||
#endif /* KSH */
|
||||
default:
|
||||
internal_errorf(0,
|
||||
"wdscan: unknown char 0x%x (carrying on)",
|
||||
wp[-1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* return a copy of wp without any of the mark up characters and
|
||||
* with quote characters (" ' \) stripped.
|
||||
* (string is allocated from ATEMP)
|
||||
*/
|
||||
char *
|
||||
wdstrip(wp)
|
||||
const char *wp;
|
||||
{
|
||||
struct shf shf;
|
||||
int c;
|
||||
|
||||
shf_sopen((char *) 0, 32, SHF_WR | SHF_DYNAMIC, &shf);
|
||||
|
||||
/* problems:
|
||||
* `...` -> $(...)
|
||||
* x${foo:-"hi"} -> x${foo:-hi}
|
||||
* x${foo:-'hi'} -> x${foo:-hi}
|
||||
*/
|
||||
while (1)
|
||||
switch ((c = *wp++)) {
|
||||
case EOS:
|
||||
return shf_sclose(&shf); /* null terminates */
|
||||
case CHAR:
|
||||
case QCHAR:
|
||||
shf_putchar(*wp++, &shf);
|
||||
break;
|
||||
case COMSUB:
|
||||
shf_putchar('$', &shf);
|
||||
shf_putchar('(', &shf);
|
||||
while (*wp != 0)
|
||||
shf_putchar(*wp++, &shf);
|
||||
shf_putchar(')', &shf);
|
||||
break;
|
||||
case EXPRSUB:
|
||||
shf_putchar('$', &shf);
|
||||
shf_putchar('(', &shf);
|
||||
shf_putchar('(', &shf);
|
||||
while (*wp != 0)
|
||||
shf_putchar(*wp++, &shf);
|
||||
shf_putchar(')', &shf);
|
||||
shf_putchar(')', &shf);
|
||||
break;
|
||||
case OQUOTE:
|
||||
break;
|
||||
case CQUOTE:
|
||||
break;
|
||||
case OSUBST:
|
||||
shf_putchar('$', &shf);
|
||||
if (*wp++ == '{')
|
||||
shf_putchar('{', &shf);
|
||||
while ((c = *wp++) != 0)
|
||||
shf_putchar(c, &shf);
|
||||
break;
|
||||
case CSUBST:
|
||||
if (*wp++ == '}')
|
||||
shf_putchar('}', &shf);
|
||||
break;
|
||||
#ifdef KSH
|
||||
case OPAT:
|
||||
shf_putchar(*wp++, &shf);
|
||||
shf_putchar('(', &shf);
|
||||
break;
|
||||
case SPAT:
|
||||
shf_putchar('|', &shf);
|
||||
break;
|
||||
case CPAT:
|
||||
shf_putchar(')', &shf);
|
||||
break;
|
||||
#endif /* KSH */
|
||||
}
|
||||
}
|
||||
|
||||
static struct ioword **
|
||||
iocopy(iow, ap)
|
||||
register struct ioword **iow;
|
||||
Area *ap;
|
||||
{
|
||||
register struct ioword **ior;
|
||||
register int i;
|
||||
|
||||
for (ior = iow; *ior++ != NULL; )
|
||||
;
|
||||
ior = (struct ioword **) alloc((ior - iow + 1) * sizeof(*ior), ap);
|
||||
|
||||
for (i = 0; iow[i] != NULL; i++) {
|
||||
register struct ioword *p, *q;
|
||||
|
||||
p = iow[i];
|
||||
q = (struct ioword *) alloc(sizeof(*p), ap);
|
||||
ior[i] = q;
|
||||
*q = *p;
|
||||
if (p->name != (char *) 0)
|
||||
q->name = wdcopy(p->name, ap);
|
||||
if (p->delim != (char *) 0)
|
||||
q->delim = wdcopy(p->delim, ap);
|
||||
if (p->heredoc != (char *) 0)
|
||||
q->heredoc = str_save(p->heredoc, ap);
|
||||
}
|
||||
ior[i] = NULL;
|
||||
|
||||
return ior;
|
||||
}
|
||||
|
||||
/*
|
||||
* free tree (for function definition)
|
||||
*/
|
||||
|
||||
void
|
||||
tfree(t, ap)
|
||||
register struct op *t;
|
||||
Area *ap;
|
||||
{
|
||||
register char **w;
|
||||
|
||||
if (t == NULL)
|
||||
return;
|
||||
|
||||
if (t->str != NULL)
|
||||
afree((void*)t->str, ap);
|
||||
|
||||
if (t->vars != NULL) {
|
||||
for (w = t->vars; *w != NULL; w++)
|
||||
afree((void*)*w, ap);
|
||||
afree((void*)t->vars, ap);
|
||||
}
|
||||
|
||||
if (t->args != NULL) {
|
||||
for (w = t->args; *w != NULL; w++)
|
||||
afree((void*)*w, ap);
|
||||
afree((void*)t->args, ap);
|
||||
}
|
||||
|
||||
if (t->ioact != NULL)
|
||||
iofree(t->ioact, ap);
|
||||
|
||||
tfree(t->left, ap);
|
||||
tfree(t->right, ap);
|
||||
|
||||
afree((void*)t, ap);
|
||||
}
|
||||
|
||||
static void
|
||||
iofree(iow, ap)
|
||||
struct ioword **iow;
|
||||
Area *ap;
|
||||
{
|
||||
register struct ioword **iop;
|
||||
register struct ioword *p;
|
||||
|
||||
for (iop = iow; (p = *iop++) != NULL; ) {
|
||||
if (p->name != NULL)
|
||||
afree((void*)p->name, ap);
|
||||
if (p->delim != NULL)
|
||||
afree((void*)p->delim, ap);
|
||||
if (p->heredoc != NULL)
|
||||
afree((void*)p->heredoc, ap);
|
||||
afree((void*)p, ap);
|
||||
}
|
||||
}
|
142
bin/ksh/tree.h
Normal file
142
bin/ksh/tree.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/* $NetBSD: tree.h,v 1.4 2004/07/07 19:20:09 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* command trees for compile/execute
|
||||
*/
|
||||
|
||||
/* $Id: tree.h,v 1.4 2004/07/07 19:20:09 mycroft Exp $ */
|
||||
|
||||
#define NOBLOCK ((struct op *)NULL)
|
||||
#define NOWORD ((char *)NULL)
|
||||
#define NOWORDS ((char **)NULL)
|
||||
|
||||
/*
|
||||
* Description of a command or an operation on commands.
|
||||
*/
|
||||
struct op {
|
||||
short type; /* operation type, see below */
|
||||
union { /* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */
|
||||
short evalflags; /* TCOM: arg expansion eval() flags */
|
||||
short ksh_func; /* TFUNC: function x (vs x()) */
|
||||
} u;
|
||||
char **args; /* arguments to a command */
|
||||
char **vars; /* variable assignments */
|
||||
struct ioword **ioact; /* IO actions (eg, < > >>) */
|
||||
struct op *left, *right; /* descendents */
|
||||
char *str; /* word for case; identifier for for,
|
||||
* select, and functions;
|
||||
* path to execute for TEXEC;
|
||||
* time hook for TCOM.
|
||||
*/
|
||||
int lineno; /* TCOM/TFUNC: LINENO for this */
|
||||
};
|
||||
|
||||
/* Tree.type values */
|
||||
#define TEOF 0
|
||||
#define TCOM 1 /* command */
|
||||
#define TPAREN 2 /* (c-list) */
|
||||
#define TPIPE 3 /* a | b */
|
||||
#define TLIST 4 /* a ; b */
|
||||
#define TOR 5 /* || */
|
||||
#define TAND 6 /* && */
|
||||
#define TBANG 7 /* ! */
|
||||
#define TDBRACKET 8 /* [[ .. ]] */
|
||||
#define TFOR 9
|
||||
#define TSELECT 10
|
||||
#define TCASE 11
|
||||
#define TIF 12
|
||||
#define TWHILE 13
|
||||
#define TUNTIL 14
|
||||
#define TELIF 15
|
||||
#define TPAT 16 /* pattern in case */
|
||||
#define TBRACE 17 /* {c-list} */
|
||||
#define TASYNC 18 /* c & */
|
||||
#define TFUNCT 19 /* function name { command; } */
|
||||
#define TTIME 20 /* time pipeline */
|
||||
#define TEXEC 21 /* fork/exec eval'd TCOM */
|
||||
#define TCOPROC 22 /* coprocess |& */
|
||||
|
||||
/*
|
||||
* prefix codes for words in command tree
|
||||
*/
|
||||
#define EOS 0 /* end of string */
|
||||
#define CHAR 1 /* unquoted character */
|
||||
#define QCHAR 2 /* quoted character */
|
||||
#define COMSUB 3 /* $() substitution (0 terminated) */
|
||||
#define EXPRSUB 4 /* $(()) substitution (0 terminated) */
|
||||
#define OQUOTE 5 /* opening " or ' */
|
||||
#define CQUOTE 6 /* closing " or ' */
|
||||
#define OSUBST 7 /* opening ${ subst (followed by { or X) */
|
||||
#define CSUBST 8 /* closing } of above (followed by } or X) */
|
||||
#define OPAT 9 /* open pattern: *(, @(, etc. */
|
||||
#define SPAT 10 /* separate pattern: | */
|
||||
#define CPAT 11 /* close pattern: ) */
|
||||
|
||||
/*
|
||||
* IO redirection
|
||||
*/
|
||||
struct ioword {
|
||||
int unit; /* unit affected */
|
||||
int flag; /* action (below) */
|
||||
char *name; /* file name (unused if heredoc) */
|
||||
char *delim; /* delimiter for <<,<<- */
|
||||
char *heredoc;/* content of heredoc */
|
||||
};
|
||||
|
||||
/* ioword.flag - type of redirection */
|
||||
#define IOTYPE 0xF /* type: bits 0:3 */
|
||||
#define IOREAD 0x1 /* < */
|
||||
#define IOWRITE 0x2 /* > */
|
||||
#define IORDWR 0x3 /* <>: todo */
|
||||
#define IOHERE 0x4 /* << (here file) */
|
||||
#define IOCAT 0x5 /* >> */
|
||||
#define IODUP 0x6 /* <&/>& */
|
||||
#define IOEVAL BIT(4) /* expand in << */
|
||||
#define IOSKIP BIT(5) /* <<-, skip ^\t* */
|
||||
#define IOCLOB BIT(6) /* >|, override -o noclobber */
|
||||
#define IORDUP BIT(7) /* x<&y (as opposed to x>&y) */
|
||||
#define IONAMEXP BIT(8) /* name has been expanded */
|
||||
|
||||
/* execute/exchild flags */
|
||||
#define XEXEC BIT(0) /* execute without forking */
|
||||
#define XFORK BIT(1) /* fork before executing */
|
||||
#define XBGND BIT(2) /* command & */
|
||||
#define XPIPEI BIT(3) /* input is pipe */
|
||||
#define XPIPEO BIT(4) /* output is pipe */
|
||||
#define XPIPE (XPIPEI|XPIPEO) /* member of pipe */
|
||||
#define XXCOM BIT(5) /* `...` command */
|
||||
#define XPCLOSE BIT(6) /* exchild: close close_fd in parent */
|
||||
#define XCCLOSE BIT(7) /* exchild: close close_fd in child */
|
||||
#define XERROK BIT(8) /* non-zero exit ok (for set -e) */
|
||||
#define XCOPROC BIT(9) /* starting a co-process */
|
||||
#define XTIME BIT(10) /* timing TCOM command */
|
||||
#define XINTACT BIT(11) /* OS2: proc started from interactive session */
|
||||
|
||||
/*
|
||||
* flags to control expansion of words (assumed by t->evalflags to fit
|
||||
* in a short)
|
||||
*/
|
||||
#define DOBLANK BIT(0) /* perform blank interpretation */
|
||||
#define DOGLOB BIT(1) /* expand [?* */
|
||||
#define DOPAT BIT(2) /* quote *?[ */
|
||||
#define DOTILDE BIT(3) /* normal ~ expansion (first char) */
|
||||
#define DONTRUNCOMMAND BIT(4) /* do not run $(command) things */
|
||||
#define DOASNTILDE BIT(5) /* assignment ~ expansion (after =, :) */
|
||||
#define DOBRACE_ BIT(6) /* used by expand(): do brace expansion */
|
||||
#define DOMAGIC_ BIT(7) /* used by expand(): string contains MAGIC */
|
||||
#define DOTEMP_ BIT(8) /* ditto : in word part of ${..[%#=?]..} */
|
||||
#define DOVACHECK BIT(9) /* var assign check (for typeset, set, etc) */
|
||||
#define DOMARKDIRS BIT(10) /* force markdirs behaviour */
|
||||
|
||||
/*
|
||||
* The arguments of [[ .. ]] expressions are kept in t->args[] and flags
|
||||
* indicating how the arguments have been munged are kept in t->vars[].
|
||||
* The contents of t->vars[] are stuffed strings (so they can be treated
|
||||
* like all other t->vars[]) in which the second character is the one that
|
||||
* is examined. The DB_* defines are the values for these second characters.
|
||||
*/
|
||||
#define DB_NORM 1 /* normal argument */
|
||||
#define DB_OR 2 /* || -> -o conversion */
|
||||
#define DB_AND 3 /* && -> -a conversion */
|
||||
#define DB_BE 4 /* an inserted -BE */
|
||||
#define DB_PAT 5 /* a pattern argument */
|
187
bin/ksh/tty.c
Normal file
187
bin/ksh/tty.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
/* $NetBSD: tty.c,v 1.4 2003/06/23 11:39:06 agc Exp $ */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: tty.c,v 1.4 2003/06/23 11:39:06 agc Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
#include "ksh_stat.h"
|
||||
#define EXTERN
|
||||
#include "tty.h"
|
||||
#undef EXTERN
|
||||
|
||||
int
|
||||
get_tty(fd, ts)
|
||||
int fd;
|
||||
TTY_state *ts;
|
||||
{
|
||||
int ret;
|
||||
|
||||
# ifdef HAVE_TERMIOS_H
|
||||
ret = tcgetattr(fd, ts);
|
||||
# else /* HAVE_TERIOS_H */
|
||||
# ifdef HAVE_TERMIO_H
|
||||
ret = ioctl(fd, TCGETA, ts);
|
||||
# else /* HAVE_TERMIO_H */
|
||||
ret = ioctl(fd, TIOCGETP, &ts->sgttyb);
|
||||
# ifdef TIOCGATC
|
||||
if (ioctl(fd, TIOCGATC, &ts->lchars) < 0)
|
||||
ret = -1;
|
||||
# else
|
||||
if (ioctl(fd, TIOCGETC, &ts->tchars) < 0)
|
||||
ret = -1;
|
||||
# ifdef TIOCGLTC
|
||||
if (ioctl(fd, TIOCGLTC, &ts->ltchars) < 0)
|
||||
ret = -1;
|
||||
# endif /* TIOCGLTC */
|
||||
# endif /* TIOCGATC */
|
||||
# endif /* HAVE_TERMIO_H */
|
||||
# endif /* HAVE_TERIOS_H */
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
set_tty(fd, ts, flags)
|
||||
int fd;
|
||||
TTY_state *ts;
|
||||
int flags;
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
# ifdef HAVE_TERMIOS_H
|
||||
ret = tcsetattr(fd, TCSADRAIN, ts);
|
||||
# else /* HAVE_TERIOS_H */
|
||||
# ifdef HAVE_TERMIO_H
|
||||
# ifndef TCSETAW /* e.g. Cray-2 */
|
||||
/* first wait for output to drain */
|
||||
# ifdef TCSBRK
|
||||
if (ioctl(tty_fd, TCSBRK, 1) < 0)
|
||||
ret = -1;
|
||||
# else /* the following kludge is minimally intrusive, but sometimes fails */
|
||||
if (flags & TF_WAIT)
|
||||
sleep((unsigned)1); /* fake it */
|
||||
# endif
|
||||
# endif /* !TCSETAW */
|
||||
# if defined(_BSD_SYSV) || !defined(TCSETAW)
|
||||
/* _BSD_SYSV must force TIOCSETN instead of TIOCSETP (preserve type-ahead) */
|
||||
if (ioctl(tty_fd, TCSETA, ts) < 0)
|
||||
ret = -1;
|
||||
# else
|
||||
if (ioctl(tty_fd, TCSETAW, ts) < 0)
|
||||
ret = -1;
|
||||
# endif
|
||||
# else /* HAVE_TERMIO_H */
|
||||
# if defined(__mips) && (defined(_SYSTYPE_BSD43) || defined(__SYSTYPE_BSD43))
|
||||
/* Under RISC/os 5.00, bsd43 environment, after a tty driver
|
||||
* generated interrupt (eg, INTR, TSTP), all output to tty is
|
||||
* lost until a SETP is done (there must be a better way of
|
||||
* doing this...).
|
||||
*/
|
||||
if (flags & TF_MIPSKLUDGE)
|
||||
ret = ioctl(fd, TIOCSETP, &ts->sgttyb);
|
||||
else
|
||||
# endif /* _SYSTYPE_BSD43 */
|
||||
ret = ioctl(fd, TIOCSETN, &ts->sgttyb);
|
||||
# ifdef TIOCGATC
|
||||
if (ioctl(fd, TIOCSATC, &ts->lchars) < 0)
|
||||
ret = -1;
|
||||
# else
|
||||
if (ioctl(fd, TIOCSETC, &ts->tchars) < 0)
|
||||
ret = -1;
|
||||
# ifdef TIOCGLTC
|
||||
if (ioctl(fd, TIOCSLTC, &ts->ltchars) < 0)
|
||||
ret = -1;
|
||||
# endif /* TIOCGLTC */
|
||||
# endif /* TIOCGATC */
|
||||
# endif /* HAVE_TERMIO_H */
|
||||
# endif /* HAVE_TERIOS_H */
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize tty_fd. Used for saving/reseting tty modes upon
|
||||
* foreground job completion and for setting up tty process group.
|
||||
*/
|
||||
void
|
||||
tty_init(init_ttystate)
|
||||
int init_ttystate;
|
||||
{
|
||||
int do_close = 1;
|
||||
int tfd;
|
||||
const char *devtty = _PATH_TTY;
|
||||
|
||||
if (tty_fd >= 0) {
|
||||
close(tty_fd);
|
||||
tty_fd = -1;
|
||||
}
|
||||
tty_devtty = 1;
|
||||
|
||||
/* SCO can't job control on /dev/tty, so don't try... */
|
||||
#if !defined(__SCO__)
|
||||
if ((tfd = open(devtty, O_RDWR, 0)) < 0) {
|
||||
#ifdef __NeXT
|
||||
/* rlogin on NeXT boxes does not set up the controlling tty,
|
||||
* so force it to be done here...
|
||||
*/
|
||||
{
|
||||
extern char *ttyname ARGS((int));
|
||||
char *s = ttyname(isatty(2) ? 2 : 0);
|
||||
int fd;
|
||||
|
||||
if (s && (fd = open(s, O_RDWR, 0)) >= 0) {
|
||||
close(fd);
|
||||
tfd = open(devtty, O_RDWR, 0);
|
||||
}
|
||||
}
|
||||
#endif /* __NeXT */
|
||||
|
||||
/* X11R5 xterm on mips doesn't set controlling tty properly - temporary hack */
|
||||
# if !defined(__mips) || !(defined(_SYSTYPE_BSD43) || defined(__SYSTYPE_BSD43))
|
||||
if (tfd < 0) {
|
||||
tty_devtty = 0;
|
||||
warningf(FALSE,
|
||||
"No controlling tty (open %s: %s)",
|
||||
devtty, strerror(errno));
|
||||
}
|
||||
# endif /* __mips */
|
||||
}
|
||||
#else /* !__SCO__ */
|
||||
tfd = -1;
|
||||
#endif /* __SCO__ */
|
||||
|
||||
if (tfd < 0) {
|
||||
do_close = 0;
|
||||
if (isatty(0))
|
||||
tfd = 0;
|
||||
else if (isatty(2))
|
||||
tfd = 2;
|
||||
else {
|
||||
warningf(FALSE, "Can't find tty file descriptor");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((tty_fd = ksh_dupbase(tfd, FDBASE)) < 0) {
|
||||
warningf(FALSE, "j_ttyinit: dup of tty fd failed: %s",
|
||||
strerror(errno));
|
||||
} else if (fd_clexec(tty_fd) < 0) {
|
||||
warningf(FALSE, "j_ttyinit: can't set close-on-exec flag: %s",
|
||||
strerror(errno));
|
||||
close(tty_fd);
|
||||
tty_fd = -1;
|
||||
} else if (init_ttystate)
|
||||
get_tty(tty_fd, &tty_state);
|
||||
if (do_close)
|
||||
close(tfd);
|
||||
}
|
||||
|
||||
void
|
||||
tty_close()
|
||||
{
|
||||
if (tty_fd >= 0) {
|
||||
close(tty_fd);
|
||||
tty_fd = -1;
|
||||
}
|
||||
}
|
110
bin/ksh/tty.h
Normal file
110
bin/ksh/tty.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/* $NetBSD: tty.h,v 1.2 1997/01/12 19:12:25 tls Exp $ */
|
||||
|
||||
/*
|
||||
tty.h -- centralized definitions for a variety of terminal interfaces
|
||||
|
||||
created by DPK, Oct. 1986
|
||||
|
||||
Rearranged to work with autoconf, added TTY_state, get_tty/set_tty
|
||||
Michael Rendell, May '94
|
||||
|
||||
last edit: 30-Jul-1987 D A Gwyn
|
||||
*/
|
||||
/* $NetBSD: tty.h,v 1.2 1997/01/12 19:12:25 tls Exp $ */
|
||||
|
||||
/* some useful #defines */
|
||||
#ifdef EXTERN
|
||||
# define I__(i) = i
|
||||
#else
|
||||
# define I__(i)
|
||||
# define EXTERN extern
|
||||
# define EXTERN_DEFINED
|
||||
#endif
|
||||
|
||||
/* Don't know of a system on which including sys/ioctl.h with termios.h
|
||||
* causes problems. If there is one, these lines need to be deleted and
|
||||
* aclocal.m4 needs to have stuff un-commented.
|
||||
*/
|
||||
#ifdef SYS_IOCTL_WITH_TERMIOS
|
||||
# define SYS_IOCTL_WITH_TERMIOS
|
||||
#endif /* SYS_IOCTL_WITH_TERMIOS */
|
||||
#ifdef SYS_IOCTL_WITH_TERMIO
|
||||
# define SYS_IOCTL_WITH_TERMIO
|
||||
#endif /* SYS_IOCTL_WITH_TERMIO */
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
# include <termios.h>
|
||||
# ifdef SYS_IOCTL_WITH_TERMIOS
|
||||
# if !(defined(sun) && !defined(__svr4__)) /* too many warnings on sunos */
|
||||
/* Need to include sys/ioctl.h on some systems to get the TIOCGWINSZ
|
||||
* stuff (eg, digital unix).
|
||||
*/
|
||||
# include <sys/ioctl.h>
|
||||
# endif /* !(sun && !__svr4__) */
|
||||
# endif /* SYS_IOCTL_WITH_TERMIOS */
|
||||
typedef struct termios TTY_state;
|
||||
#else
|
||||
# ifdef HAVE_TERMIO_H
|
||||
# include <termio.h>
|
||||
# ifdef SYS_IOCTL_WITH_TERMIO
|
||||
# include <sys/ioctl.h> /* see comment above in termios stuff */
|
||||
# endif /* SYS_IOCTL_WITH_TERMIO */
|
||||
# if _BSD_SYSV /* BRL UNIX System V emulation */
|
||||
# ifndef NTTYDISC
|
||||
# define TIOCGETD _IOR( 't', 0, int )
|
||||
# define TIOCSETD _IOW( 't', 1, int )
|
||||
# define NTTYDISC 2
|
||||
# endif
|
||||
# ifndef TIOCSTI
|
||||
# define TIOCSTI _IOW( 't', 114, char )
|
||||
# endif
|
||||
# ifndef TIOCSPGRP
|
||||
# define TIOCSPGRP _IOW( 't', 118, int )
|
||||
# endif
|
||||
# endif /* _BSD_SYSV */
|
||||
typedef struct termio TTY_state;
|
||||
# else /* HAVE_TERMIO_H */
|
||||
/* Assume BSD tty stuff. Uses TIOCGETP, TIOCSETN; uses TIOCGATC/TIOCSATC if
|
||||
* available, otherwise it uses TIOCGETC/TIOCSETC (also uses TIOCGLTC/TIOCSLTC
|
||||
* if available)
|
||||
*/
|
||||
# ifdef _MINIX
|
||||
# include <sgtty.h>
|
||||
# define TIOCSETN TIOCSETP
|
||||
# else
|
||||
# include <sys/ioctl.h>
|
||||
# endif
|
||||
typedef struct {
|
||||
struct sgttyb sgttyb;
|
||||
# ifdef TIOCGATC
|
||||
struct lchars lchars;
|
||||
# else /* TIOCGATC */
|
||||
struct tchars tchars;
|
||||
# ifdef TIOCGLTC
|
||||
struct ltchars ltchars;
|
||||
# endif /* TIOCGLTC */
|
||||
# endif /* TIOCGATC */
|
||||
} TTY_state;
|
||||
# endif /* HAVE_TERMIO_H */
|
||||
#endif /* HAVE_TERMIOS_H */
|
||||
|
||||
/* Flags for set_tty() */
|
||||
#define TF_NONE 0x00
|
||||
#define TF_WAIT 0x01 /* drain output, even it requires sleep() */
|
||||
#define TF_MIPSKLUDGE 0x02 /* kludge to unwedge RISC/os 5.0 tty driver */
|
||||
|
||||
EXTERN int tty_fd I__(-1); /* dup'd tty file descriptor */
|
||||
EXTERN int tty_devtty; /* true if tty_fd is from /dev/tty */
|
||||
EXTERN TTY_state tty_state; /* saved tty state */
|
||||
|
||||
extern int get_tty ARGS((int fd, TTY_state *ts));
|
||||
extern int set_tty ARGS((int fd, TTY_state *ts, int flags));
|
||||
extern void tty_init ARGS((int init_ttystate));
|
||||
extern void tty_close ARGS((void));
|
||||
|
||||
/* be sure not to interfere with anyone else's idea about EXTERN */
|
||||
#ifdef EXTERN_DEFINED
|
||||
# undef EXTERN_DEFINED
|
||||
# undef EXTERN
|
||||
#endif
|
||||
#undef I__
|
1260
bin/ksh/var.c
Normal file
1260
bin/ksh/var.c
Normal file
File diff suppressed because it is too large
Load diff
16
bin/ksh/version.c
Normal file
16
bin/ksh/version.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* $NetBSD: version.c,v 1.5 2005/06/26 19:09:00 christos Exp $ */
|
||||
|
||||
/*
|
||||
* value of $KSH_VERSION (or $SH_VERSION)
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: version.c,v 1.5 2005/06/26 19:09:00 christos Exp $");
|
||||
#endif
|
||||
|
||||
|
||||
#include "sh.h"
|
||||
|
||||
char ksh_version [] =
|
||||
"@(#)PD KSH v5.2.14 99/07/13.2";
|
2203
bin/ksh/vi.c
Normal file
2203
bin/ksh/vi.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,7 @@
|
|||
./bin/halt minix-sys
|
||||
./bin/intr minix-sys
|
||||
./bin/kill minix-sys
|
||||
./bin/ksh minix-sys
|
||||
./bin/ln minix-sys
|
||||
./bin/loadkeys minix-sys
|
||||
./bin/ls minix-sys
|
||||
|
@ -1287,6 +1288,7 @@
|
|||
./usr/man/man1/jobs.1 minix-sys
|
||||
./usr/man/man1/join.1 minix-sys
|
||||
./usr/man/man1/kill.1 minix-sys
|
||||
./usr/man/man1/ksh.1 minix-sys
|
||||
./usr/man/man1/last.1 minix-sys
|
||||
./usr/man/man1/ldd.1 minix-sys
|
||||
./usr/man/man1/ld.elf_so.1 minix-sys
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
20130312:
|
||||
/bin/ksh was imported and needs to be added to your /etc/shells.
|
||||
Run this command to add it:
|
||||
|
||||
# echo "/bin/ksh" >> /etc/shells
|
||||
|
||||
20130306:
|
||||
For people building ARM images, the procedure has changed
|
||||
a bit. You need a full FS to boot now. In short, you need
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
/bin/sh
|
||||
/bin/ksh
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
2012/01/16 18:47:57,bin/ed
|
||||
2012/10/17 12:00:00,bin/expr
|
||||
2012/10/17 12:00:00,bin/kill
|
||||
2013/03/12 12:00:00,bin/ksh
|
||||
2012/10/17 12:00:00,bin/ln
|
||||
2012/10/17 12:00:00,bin/ls
|
||||
2012/10/17 12:00:00,bin/Makefile
|
||||
|
|
Loading…
Reference in a new issue