This is utils.c in view mode; [Download] [Up]
/*
* $Id: utils.c,v 2.61 1996/10/15 20:16:35 hzoli Exp $
*
* utils.c - miscellaneous utilities
*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 1992-1996 Paul Falstad
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and to distribute modified versions of this software for any
* purpose, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* In no event shall Paul Falstad or the Zsh Development Group be liable
* to any party for direct, indirect, special, incidental, or consequential
* damages arising out of the use of this software and its documentation,
* even if Paul Falstad and the Zsh Development Group have been advised of
* the possibility of such damage.
*
* Paul Falstad and the Zsh Development Group specifically disclaim any
* warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose. The software
* provided hereunder is on an "as is" basis, and Paul Falstad and the
* Zsh Development Group have no obligation to provide maintenance,
* support, updates, enhancements, or modifications.
*
*/
#include "zsh.h"
/* Print an error */
/**/
void
zwarnnam(char *cmd, char *fmt, char *str, int num)
{
int waserr;
waserr = errflag;
zerrnam(cmd, fmt, str, num);
errflag = waserr;
}
/**/
void
zerr(char *fmt, char *str, int num)
{
if (errflag || noerrs)
return;
errflag = 1;
trashzle();
/*
* scriptname is set when sourcing scripts, so that we get the
* correct name instead of the generic name of whatever
* program/script is running.
*/
nicezputs(isset(SHINSTDIN) ? "zsh" :
scriptname ? scriptname : argzero, stderr);
fputs(": ", stderr);
zerrnam(NULL, fmt, str, num);
}
/**/
void
zerrnam(char *cmd, char *fmt, char *str, int num)
{
if (cmd) {
if (errflag || noerrs)
return;
errflag = 1;
trashzle();
if(unset(SHINSTDIN)) {
nicezputs(scriptname ? scriptname : argzero, stderr);
fputs(": ", stderr);
}
nicezputs(cmd, stderr);
fputs(": ", stderr);
}
while (*fmt)
if (*fmt == '%') {
fmt++;
switch (*fmt++) {
case 's':
nicezputs(str, stderr);
break;
case 'l': {
char sav;
num = metalen(str, num);
sav = str[num];
str[num] = '\0';
nicezputs(str, stderr);
str[num] = sav;
break;
}
case 'd':
fprintf(stderr, "%d", num);
break;
case '%':
putc('%', stderr);
break;
case 'c':
fputs(nicechar(num), stderr);
break;
case 'e':
/* print the corresponding message for this errno */
if (num == EINTR) {
fputs("interrupt\n", stderr);
errflag = 1;
return;
}
/* If the message is not about I/O problems, it looks better *
* if we uncapitalize the first letter of the message */
if (num == EIO)
fputs(strerror(num), stderr);
else {
char *errmsg = strerror(num);
fputc(tulower(errmsg[0]), stderr);
fputs(errmsg + 1, stderr);
}
break;
}
} else {
putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, stderr);
fmt++;
}
if (unset(SHINSTDIN) && lineno)
fprintf(stderr, " [%ld]\n", lineno);
else
putc('\n', stderr);
fflush(stderr);
}
/* Output a single character, for the termcap routines. *
* This is used instead of putchar since it can be a macro. */
/**/
int
putraw(int c)
{
putc(c, stdout);
return 0;
}
/* Output a single character, for the termcap routines. */
/**/
int
putshout(int c)
{
putc(c, shout);
return 0;
}
/* Turn a character into a visible representation thereof. The visible *
* string is put together in a static buffer, and this function returns *
* a pointer to it. Printable characters stand for themselves, DEL is *
* represented as "^?", newline and tab are represented as "\n" and *
* "\t", and normal control characters are represented in "^C" form. *
* Characters with bit 7 set, if unprintable, are represented as "\M-" *
* followed by the visible representation of the character with bit 7 *
* stripped off. Tokens are interpreted, rather than being treated as *
* literal characters. */
/**/
char *
nicechar(int c)
{
static char buf[6];
char *s = buf;
c &= 0xff;
if (isprint(c))
goto done;
if (c & 0x80) {
*s++ = '\\';
*s++ = 'M';
*s++ = '-';
c &= 0x7f;
if(isprint(c))
goto done;
}
if (c == 0x7f) {
*s++ = '^';
c = '?';
} else if (c == '\n') {
*s++ = '\\';
c = 'n';
} else if (c == '\t') {
*s++ = '\\';
c = 't';
} else if (c < 0x20) {
*s++ = '^';
c += 0x40;
}
done:
*s++ = c;
*s = 0;
return buf;
}
#if 0
/* Output a string's visible representation. */
/**/
void
nicefputs(char *s, FILE *f)
{
for (; *s; s++)
fputs(nicechar(STOUC(*s)), f);
}
#endif
/* Return the length of the visible representation of a string. */
/**/
size_t
nicestrlen(char *s)
{
size_t l = 0;
for (; *s; s++)
l += strlen(nicechar(STOUC(*s)));
return l;
}
/* get a symlink-free pathname for s relative to PWD */
/**/
char *
findpwd(char *s)
{
char *t;
if (*s == '/')
return xsymlink(s);
s = tricat((pwd[1]) ? pwd : "", "/", s);
t = xsymlink(s);
zsfree(s);
return t;
}
/* Check whether a string contains the *
* name of the present directory. */
/**/
int
ispwd(char *s)
{
struct stat sbuf, tbuf;
if (stat(unmeta(s), &sbuf) == 0 && stat(".", &tbuf) == 0)
if (sbuf.st_dev == tbuf.st_dev && sbuf.st_ino == tbuf.st_ino)
return 1;
return 0;
}
static char xbuf[PATH_MAX*2];
/**/
char **
slashsplit(char *s)
{
char *t, **r, **q;
int t0;
if (!*s)
return (char **) zcalloc(sizeof(char **));
for (t = s, t0 = 0; *t; t++)
if (*t == '/')
t0++;
q = r = (char **) zalloc(sizeof(char **) * (t0 + 2));
while ((t = strchr(s, '/'))) {
*q++ = ztrduppfx(s, t - s);
while (*t == '/')
t++;
if (!*t) {
*q = NULL;
return r;
}
s = t;
}
*q++ = ztrdup(s);
*q = NULL;
return r;
}
/* expands symlinks and .. or . expressions */
/* if flag = 0, only expand .. and . expressions */
static int
xsymlinks(char *s, int flag)
{
char **pp, **opp;
char xbuf2[PATH_MAX*2], xbuf3[PATH_MAX*2];
int t0;
opp = pp = slashsplit(s);
for (; *pp; pp++) {
if (!strcmp(*pp, ".")) {
zsfree(*pp);
continue;
}
if (!strcmp(*pp, "..")) {
char *p;
zsfree(*pp);
if (!strcmp(xbuf, "/"))
continue;
p = xbuf + strlen(xbuf);
while (*--p != '/');
*p = '\0';
continue;
}
if (unset(CHASELINKS)) {
strcat(xbuf, "/");
strcat(xbuf, *pp);
zsfree(*pp);
continue;
}
sprintf(xbuf2, "%s/%s", xbuf, *pp);
t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
if (t0 == -1 || !flag) {
strcat(xbuf, "/");
strcat(xbuf, *pp);
zsfree(*pp);
} else {
metafy(xbuf3, t0, META_NOALLOC);
if (*xbuf3 == '/') {
strcpy(xbuf, "");
if (xsymlinks(xbuf3 + 1, flag))
return 1;
} else if (xsymlinks(xbuf3, flag))
return 1;
zsfree(*pp);
}
}
free(opp);
return 0;
}
/* expand symlinks in s, and remove other weird things */
/**/
char *
xsymlink(char *s)
{
if (unset(CHASELINKS))
return ztrdup(s);
if (*s != '/')
return NULL;
*xbuf = '\0';
if (xsymlinks(s + 1, 1))
return ztrdup(s);
if (!*xbuf)
return ztrdup("/");
return ztrdup(xbuf);
}
/* print a directory */
/**/
void
fprintdir(char *s, FILE *f)
{
Nameddir d = finddir(s);
if (!d)
fputs(unmeta(s), f);
else {
putc('~', f);
fputs(unmeta(d->nam), f);
fputs(unmeta(s + strlen(d->dir)), f);
}
}
/* Returns the current username. It caches the username *
* and uid to try to avoid requerying the password files *
* or NIS/NIS+ database. */
/**/
char *
get_username(void)
{
struct passwd *pswd;
uid_t current_uid;
current_uid = getuid();
if (current_uid != cached_uid) {
cached_uid = current_uid;
zsfree(cached_username);
if ((pswd = getpwuid(current_uid)))
cached_username = ztrdup(pswd->pw_name);
else
cached_username = ztrdup("");
}
return cached_username;
}
/* static variables needed by finddir(). */
static char finddir_full[PATH_MAX];
static Nameddir finddir_last;
static int finddir_best;
/* ScanFunc used by finddir(). */
static void finddir_scan _((HashNode, int));
static void
finddir_scan(HashNode hn, int flags)
{
Nameddir nd = (Nameddir) hn;
if(nd->diff > finddir_best && !dircmp(nd->dir, finddir_full)) {
finddir_last=nd;
finddir_best=nd->diff;
}
}
/* See if a path has a named directory as its prefix. *
* If passed a NULL argument, it will invalidate any *
* cached information. */
/**/
Nameddir
finddir(char *s)
{
static struct nameddir homenode = { NULL, "", 0, NULL, 0 };
/* Invalidate directory cache if argument is NULL. This is called *
* whenever a node is added to or removed from the hash table, and *
* whenever the value of $HOME changes. (On startup, too.) */
if (!s) {
homenode.dir = home;
homenode.diff = strlen(home);
if(homenode.diff==1 || homenode.diff>=PATH_MAX)
homenode.diff = 0;
finddir_full[0] = 0;
return finddir_last = NULL;
}
if (!strcmp(s, finddir_full))
return finddir_last;
strcpy(finddir_full, s);
finddir_best=0;
finddir_last=NULL;
finddir_scan((HashNode)&homenode, 0);
scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0);
return finddir_last;
}
/* add a named directory */
/**/
void
adduserdir(char *s, char *t, int flags, int always)
{
Nameddir nd;
/* We don't maintain a hash table in non-interactive shells. */
if (!interact)
return;
/* The ND_USERNAME flag means that this possible hash table *
* entry is derived from a passwd entry. Such entries are *
* subordinate to explicitly generated entries. */
if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s))
return;
/* Normal parameter assignments generate calls to this function, *
* with always==0. Unless the AUTO_NAME_DIRS option is set, we *
* don't let such assignments actually create directory names. *
* Instead, a reference to the parameter as a directory name can *
* cause the actual creation of the hash table entry. */
if (!always && unset(AUTONAMEDIRS) &&
!nameddirtab->getnode2(nameddirtab, s))
return;
if (!t || *t != '/' || strlen(t) >= PATH_MAX) {
/* We can't use this value as a directory, so simply remove *
* the corresponding entry in the hash table, if any. */
HashNode hn = nameddirtab->removenode(nameddirtab, s);
if(hn)
nameddirtab->freenode(hn);
return;
}
/* add the name */
nd = (Nameddir) zcalloc(sizeof *nd);
nd->flags = flags;
nd->dir = ztrdup(t);
nameddirtab->addnode(nameddirtab, ztrdup(s), nd);
}
/* Get a named directory: this function can cause a directory name *
* to be added to the hash table, if it isn't there already. */
/**/
char *
getnameddir(char *name)
{
Param pm;
char *str;
struct passwd *pw;
Nameddir nd;
/* Check if it is already in the named directory table */
if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)))
return dupstring(nd->dir);
/* Check if there is a scalar parameter with this name whose value *
* begins with a `/'. If there is, add it to the hash table and *
* return the new value. */
if ((pm = (Param) paramtab->getnode(paramtab, name)) &&
(PM_TYPE(pm->flags) == PM_SCALAR) &&
(str = getsparam(name)) && *str == '/') {
adduserdir(name, str, 0, 1);
return str;
}
/* Retrieve an entry from the password table/database for this user. */
if ((pw = getpwnam(name))) {
char *dir = xsymlink(pw->pw_dir);
adduserdir(name, dir, ND_USERNAME, 1);
str = dupstring(dir);
zsfree(dir);
return str;
}
/* There are no more possible sources of directory names, so give up. */
return NULL;
}
/**/
int
dircmp(char *s, char *t)
{
if (s) {
for (; *s == *t; s++, t++)
if (!*s)
return 0;
if (!*s && *t == '/')
return 0;
}
return 1;
}
/* do pre-prompt stuff */
/**/
void
preprompt(void)
{
List list;
struct schedcmd *sch, *schl;
int period = getiparam("PERIOD");
int mailcheck = getiparam("MAILCHECK");
in_vared = 0;
/* If NOTIFY is not set, then check for completed *
* jobs before we print the prompt. */
if (unset(NOTIFY))
scanjobs();
if (errflag)
return;
/* If a shell function named "precmd" exists, *
* then execute it. */
if ((list = getshfunc("precmd")))
doshfunc(list, NULL, 0, 1);
if (errflag)
return;
/* If 1) the parameter PERIOD exists, 2) the shell function *
* "periodic" exists, 3) it's been greater than PERIOD since we *
* executed "periodic", then execute it now. */
if (period && (time(NULL) > lastperiodic + period) &&
(list = getshfunc("periodic"))) {
doshfunc(list, NULL, 0, 1);
lastperiodic = time(NULL);
}
if (errflag)
return;
/* If WATCH is set, then check for the *
* specified login/logout events. */
if (watch) {
if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) {
dowatch();
lastwatch = time(NULL);
}
}
if (errflag)
return;
/* Check mail */
if (mailcheck && (int) difftime(time(NULL), lastmailcheck) > mailcheck) {
char *mailfile;
if (mailpath && *mailpath && **mailpath)
checkmailpath(mailpath);
else if ((mailfile = getsparam("MAIL")) && *mailfile) {
char *x[2];
x[0] = mailfile;
x[1] = NULL;
checkmailpath(x);
}
lastmailcheck = time(NULL);
}
/* Check scheduled commands */
for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds; sch;
sch = (schl = sch)->next) {
if (sch->time < time(NULL)) {
execstring(sch->cmd, 0, 0);
schl->next = sch->next;
zsfree(sch->cmd);
zfree(sch, sizeof(struct schedcmd));
sch = schl;
}
if (errflag)
return;
}
}
/**/
void
checkmailpath(char **s)
{
struct stat st;
char *v, *u, c;
while (*s) {
for (v = *s; *v && *v != '?'; v++);
c = *v;
*v = '\0';
if (c != '?')
u = NULL;
else
u = v + 1;
if (**s == 0) {
*v = c;
zerr("empty MAILPATH component: %s", *s, 0);
} else if (stat(unmeta(*s), &st) == -1) {
if (errno != ENOENT)
zerr("%e: %s", *s, errno);
} else if (S_ISDIR(st.st_mode)) {
LinkList l;
DIR *lock = opendir(unmeta(*s));
char buf[PATH_MAX * 2], **arr, **ap;
int ct = 1;
if (lock) {
char *fn;
HEAPALLOC {
pushheap();
l = newlinklist();
while ((fn = zreaddir(lock))) {
if (errflag)
break;
/* Ignore `.' and `..'. */
if (fn[0] == '.' &&
(fn[1] == '\0' ||
(fn[1] == '.' && fn[2] == '\0')))
continue;
if (u)
sprintf(buf, "%s/%s?%s", *s, fn, u);
else
sprintf(buf, "%s/%s", *s, fn);
addlinknode(l, dupstring(buf));
ct++;
}
closedir(lock);
ap = arr = (char **) alloc(ct * sizeof(char *));
while ((*ap++ = (char *)ugetnode(l)));
checkmailpath(arr);
popheap();
} LASTALLOC;
}
} else {
if (st.st_size && st.st_atime <= st.st_mtime &&
st.st_mtime > lastmailcheck)
if (!u) {
fprintf(stderr, "You have new mail.\n");
fflush(stderr);
} else {
char *usav = underscore;
underscore = *s;
HEAPALLOC {
u = dupstring(u);
if (! parsestr(u)) {
singsub(&u);
zputs(u, stderr);
fputc('\n', stderr);
fflush(stderr);
}
underscore = usav;
} LASTALLOC;
}
if (isset(MAILWARNING) && st.st_atime > st.st_mtime &&
st.st_atime > lastmailcheck && st.st_size) {
fprintf(stderr, "The mail in %s has been read.\n", unmeta(*s));
fflush(stderr);
}
}
*v = c;
s++;
}
}
/**/
void
freecompcond(void *a)
{
Compcond cc = (Compcond) a;
Compcond and, or, c;
int n;
for (c = cc; c; c = or) {
or = c->or;
for (; c; c = and) {
and = c->and;
if (c->type == CCT_POS ||
c->type == CCT_NUMWORDS) {
free(c->u.r.a);
free(c->u.r.b);
} else if (c->type == CCT_CURSUF ||
c->type == CCT_CURPRE) {
for (n = 0; n < c->n; n++)
if (c->u.s.s[n])
zsfree(c->u.s.s[n]);
free(c->u.s.s);
} else if (c->type == CCT_RANGESTR ||
c->type == CCT_RANGEPAT) {
for (n = 0; n < c->n; n++)
if (c->u.l.a[n])
zsfree(c->u.l.a[n]);
free(c->u.l.a);
for (n = 0; n < c->n; n++)
if (c->u.l.b[n])
zsfree(c->u.l.b[n]);
free(c->u.l.b);
} else {
for (n = 0; n < c->n; n++)
if (c->u.s.s[n])
zsfree(c->u.s.s[n]);
free(c->u.s.p);
free(c->u.s.s);
}
zfree(c, sizeof(struct compcond));
}
}
}
/**/
void
freestr(void *a)
{
zsfree(a);
}
/**/
void
gettyinfo(struct ttyinfo *ti)
{
if (SHTTY != -1) {
#ifdef HAVE_TERMIOS_H
# ifdef HAVE_TCGETATTR
if (tcgetattr(SHTTY, &ti->tio) == -1)
# else
if (ioctl(SHTTY, TCGETS, &ti->tio) == -1)
# endif
zerr("bad tcgets: %e", NULL, errno);
#else
# ifdef HAVE_TERMIO_H
ioctl(SHTTY, TCGETA, &ti->tio);
# else
ioctl(SHTTY, TIOCGETP, &ti->sgttyb);
ioctl(SHTTY, TIOCLGET, &ti->lmodes);
ioctl(SHTTY, TIOCGETC, &ti->tchars);
ioctl(SHTTY, TIOCGLTC, &ti->ltchars);
# endif
#endif
}
}
/**/
void
settyinfo(struct ttyinfo *ti)
{
if (SHTTY != -1) {
#ifdef HAVE_TERMIOS_H
# ifdef HAVE_TCGETATTR
# ifndef TCSADRAIN
# define TCSADRAIN 1 /* XXX Princeton's include files are screwed up */
# endif
tcsetattr(SHTTY, TCSADRAIN, &ti->tio);
/* if (tcsetattr(SHTTY, TCSADRAIN, &ti->tio) == -1) */
# else
ioctl(SHTTY, TCSETS, &ti->tio);
/* if (ioctl(SHTTY, TCSETS, &ti->tio) == -1) */
# endif
/* zerr("settyinfo: %e",NULL,errno)*/ ;
#else
# ifdef HAVE_TERMIO_H
ioctl(SHTTY, TCSETA, &ti->tio);
# else
ioctl(SHTTY, TIOCSETN, &ti->sgttyb);
ioctl(SHTTY, TIOCLSET, &ti->lmodes);
ioctl(SHTTY, TIOCSETC, &ti->tchars);
ioctl(SHTTY, TIOCSLTC, &ti->ltchars);
# endif
#endif
}
}
#ifdef TIOCGWINSZ
extern winchanged;
#endif
/* check the size of the window and adjust if necessary. *
* The value of from: *
* 0: called from update_job or setupvals *
* 1: called from the SIGWINCH handler *
* 2: the user have just changed LINES manually *
* 3: the user have just changed COLUMNS manually */
/**/
void
adjustwinsize(int from)
{
int oldcols = columns, oldrows = lines;
#ifdef TIOCGWINSZ
static int userlines, usercols;
if (SHTTY == -1)
return;
if (from == 2)
userlines = lines > 0;
if (from == 3)
usercols = columns > 0;
if (!ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize)) {
if (!userlines)
lines = shttyinfo.winsize.ws_row;
if (!usercols)
columns = shttyinfo.winsize.ws_col;
}
#endif /* TIOCGWINSZ */
if (lines <= 0)
lines = tclines > 0 ? tclines : 24;
if (columns <= 0)
columns = tccolumns > 0 ? tccolumns : 80;
if (lines > 2)
termflags &= ~TERM_SHORT;
else
termflags |= TERM_SHORT;
if (columns > 2)
termflags &= ~TERM_NARROW;
else
termflags |= TERM_NARROW;
#ifdef TIOCGWINSZ
if (from >= 2) {
shttyinfo.winsize.ws_row = lines;
shttyinfo.winsize.ws_col = columns;
ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize);
}
#endif
if (zleactive && (from >= 2 || oldcols != columns || oldrows != lines)) {
resetneeded = winchanged = 1;
refresh();
}
}
/* Move a fd to a place >= 10 and mark the new fd in fdtable. If the fd *
* is already >= 10, it is not moved. If it is invalid, -1 is returned. */
/**/
int
movefd(int fd)
{
if(fd != -1 && fd < 10) {
#ifdef F_DUPFD
int fe = fcntl(fd, F_DUPFD, 10);
#else
int fe = movefd(dup(fd));
#endif
zclose(fd);
fd = fe;
}
if(fd != -1) {
if (fd > max_zsh_fd) {
while (fd >= fdtable_size)
fdtable = zrealloc(fdtable, (fdtable_size *= 2));
max_zsh_fd = fd;
}
fdtable[fd] = 1;
}
return fd;
}
/* Move fd x to y. If x == -1, fd y is closed. */
/**/
void
redup(int x, int y)
{
if(x < 0)
zclose(y);
else if (x != y) {
while (y >= fdtable_size)
fdtable = zrealloc(fdtable, (fdtable_size *= 2));
dup2(x, y);
if ((fdtable[y] = fdtable[x]) && y > max_zsh_fd)
max_zsh_fd = y;
zclose(x);
}
}
/* Close the given fd, and clear it from fdtable. */
/**/
int
zclose(int fd)
{
if (fd >= 0) {
fdtable[fd] = 0;
while (max_zsh_fd > 0 && !fdtable[max_zsh_fd])
max_zsh_fd--;
if (fd == coprocin)
coprocin = -1;
if (fd == coprocout)
coprocout = -1;
}
return close(fd);
}
/* Get a file name relative to $TMPPREFIX which *
* is unique, for use as a temporary file. */
/**/
char *
gettempname(void)
{
char *s;
if (!(s = getsparam("TMPPREFIX")))
s = DEFAULT_TMPPREFIX;
return ((char *) mktemp(dyncat(unmeta(s), "XXXXXX")));
}
/* Check if a string contains a token */
/**/
int
has_token(const char *s)
{
while(*s)
if(itok(*s++))
return 1;
return 0;
}
/* Delete a character in a string */
/**/
void
chuck(char *str)
{
while ((str[0] = str[1]))
str++;
}
/**/
int
tulower(int c)
{
c &= 0xff;
return (isupper(c) ? tolower(c) : c);
}
/**/
int
tuupper(int c)
{
c &= 0xff;
return (islower(c) ? toupper(c) : c);
}
/* copy len chars from t into s, and null terminate */
/**/
void
ztrncpy(char *s, char *t, int len)
{
while (len--)
*s++ = *t++;
*s = '\0';
}
/* copy t into *s and update s */
/**/
void
strucpy(char **s, char *t)
{
char *u = *s;
while ((*u++ = *t++));
*s = u - 1;
}
/**/
void
struncpy(char **s, char *t, int n)
{
char *u = *s;
while (n--)
*u++ = *t++;
*s = u;
*u = '\0';
}
/* Return the number of elements in an array of pointers. *
* It doesn't count the NULL pointer at the end. */
/**/
int
arrlen(char **s)
{
int count;
for (count = 0; *s; s++, count++);
return count;
}
/* Skip over a balanced pair of parenthesis. */
/**/
int
skipparens(char inpar, char outpar, char **s)
{
int level;
if (**s != inpar)
return -1;
for (level = 1; *++*s && level;)
if (**s == inpar)
++level;
else if (**s == outpar)
--level;
return level;
}
/* Convert string to long. This function (without the z) *
* is contained in the ANSI standard C library, but a lot *
* of them seem to be broken. */
/**/
long
zstrtol(const char *s, char **t, int base)
{
long ret = 0;
int neg;
while (inblank(*s))
s++;
if ((neg = (*s == '-')))
s++;
else if (*s == '+')
s++;
if (!base)
if (*s != '0')
base = 10;
else if (*++s == 'x' || *s == 'X')
base = 16, s++;
else
base = 8;
if (base <= 10)
for (; *s >= '0' && *s < ('0' + base); s++)
ret = ret * base + *s - '0';
else
for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
|| (*s >= 'A' && *s < ('A' + base - 10)); s++)
ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
if (t)
*t = (char *)s;
return neg ? -ret : ret;
}
/* Convert string to quad_t. */
#if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED)
/**/
rlim_t
zstrtorlimit(const char *s, char **t, int base)
{
rlim_t ret = 0;
if (!base)
if (*s != '0')
base = 10;
else if (*++s == 'x' || *s == 'X')
base = 16, s++;
else
base = 8;
if (base <= 10)
for (; *s >= '0' && *s < ('0' + base); s++)
ret = ret * base + *s - '0';
else
for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
|| (*s >= 'A' && *s < ('A' + base - 10)); s++)
ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
if (t)
*t = (char *)s;
return ret;
}
#endif
/**/
int
checkrmall(char *s)
{
fflush(stdin);
fprintf(shout, "zsh: sure you want to delete all the files in ");
if (*s != '/') {
nicezputs(pwd[1] ? unmeta(pwd) : "", shout);
fputc('/', shout);
}
nicezputs(s, shout);
fputs(" [yn]? ", shout);
fflush(shout);
feep();
return (getquery("ny") == 'y');
}
/**/
int
setblock_stdin(void)
{
#ifdef O_NONBLOCK
struct stat st;
long mode;
if (!fstat(0, &st) && !S_ISREG(st.st_mode)) {
mode = fcntl(0, F_GETFL);
if (mode != -1 && (mode & O_NONBLOCK) &&
!fcntl(0, F_SETFL, mode & ~O_NONBLOCK))
return 1;
}
#endif
return 0;
}
/**/
int
getquery(char *valid_chars)
{
char c, d;
int isem = !strcmp(term, "emacs");
#ifdef FIONREAD
int val = 0;
#endif
attachtty(mypgrp);
if (!isem)
setcbreak();
#ifdef FIONREAD
ioctl(SHTTY, FIONREAD, (char *)&val);
if (val) {
if (!isem)
settyinfo(&shttyinfo);
write(SHTTY, "n\n", 2);
return 'n';
}
#endif
while (read(SHTTY, &c, 1) == 1) {
if (c == 'Y' || c == '\t')
c = 'y';
else if (c == 'N')
c = 'n';
if (!valid_chars)
break;
if (c == '\n') {
c = *valid_chars;
break;
}
if (strchr(valid_chars, c)) {
write(2, "\n", 1);
break;
}
feep();
if (icntrl(c))
write(2, "\b \b", 3);
write(2, "\b \b", 3);
}
if (isem) {
if (c != '\n')
while (read(SHTTY, &d, 1) == 1 && d != '\n');
} else {
settyinfo(&shttyinfo);
if (c != '\n' && !valid_chars)
write(2, "\n", 1);
}
return (int)c;
}
static int d;
static char *guess, *best;
/**/
void
spscan(HashNode hn, int scanflags)
{
int nd;
nd = spdist(hn->nam, guess, (int) strlen(guess) / 4 + 1);
if (nd <= d) {
best = hn->nam;
d = nd;
}
}
/* spellcheck a word */
/* fix s ; if hist is nonzero, fix the history list too */
/**/
void
spckword(char **s, int hist, int cmd, int ask)
{
char *t, *u;
int x;
char ic = '\0';
int ne;
int preflen = 0;
if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%')
return;
if (!strcmp(*s, "in"))
return;
if (!(*s)[0] || !(*s)[1])
return;
if (shfunctab->getnode(shfunctab, *s) ||
builtintab->getnode(builtintab, *s) ||
cmdnamtab->getnode(cmdnamtab, *s) ||
aliastab->getnode(aliastab, *s) ||
reswdtab->getnode(reswdtab, *s))
return;
else if (isset(HASHLISTALL)) {
cmdnamtab->filltable(cmdnamtab);
if (cmdnamtab->getnode(cmdnamtab, *s))
return;
}
t = *s;
if (*t == Tilde || *t == Equals || *t == String)
t++;
for (; *t; t++)
if (itok(*t))
return;
best = NULL;
for (t = *s; *t; t++)
if (*t == '/')
break;
if (**s == Tilde && !*t)
return;
if (**s == String && !*t) {
guess = *s + 1;
if (*t || !ialpha(*guess))
return;
ic = String;
d = 100;
scanhashtable(paramtab, 1, 0, 0, spscan, 0);
} else if (**s == Equals) {
if (*t)
return;
if (hashcmd(guess = *s + 1, pathchecked))
return;
d = 100;
ic = Equals;
scanhashtable(aliastab, 1, 0, 0, spscan, 0);
scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0);
} else {
guess = *s;
if (*guess == Tilde || *guess == String) {
ic = *guess;
if (!*++t)
return;
guess = dupstring(guess);
ne = noerrs;
noerrs = 1;
singsub(&guess);
noerrs = ne;
if (!guess)
return;
preflen = strlen(guess) - strlen(t);
}
if (access(unmeta(guess), F_OK) == 0)
return;
if ((u = spname(guess)) != guess)
best = u;
if (!*t && cmd) {
if (hashcmd(guess, pathchecked))
return;
d = 100;
scanhashtable(reswdtab, 1, 0, 0, spscan, 0);
scanhashtable(aliastab, 1, 0, 0, spscan, 0);
scanhashtable(shfunctab, 1, 0, 0, spscan, 0);
scanhashtable(builtintab, 1, 0, 0, spscan, 0);
scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0);
}
}
if (errflag)
return;
if (best && (int)strlen(best) > 1 && strcmp(best, guess)) {
if (ic) {
if (preflen) {
/* do not correct the result of an expansion */
if (strncmp(guess, best, preflen))
return;
/* replace the temporarily expanded prefix with the original */
u = (char *) ncalloc(t - *s + strlen(best + preflen) + 1);
strncpy(u, *s, t - *s);
strcpy(u + (t - *s), best + preflen);
} else {
u = (char *) ncalloc(strlen(best) + 2);
strcpy(u + 1, best);
}
best = u;
guess = *s;
*guess = *best = ztokens[ic - Pound];
}
if (ask) {
char *pptbuf;
int pptlen;
rstring = best;
Rstring = guess;
pptbuf = putprompt(sprompt, &pptlen, NULL, 1);
fwrite(pptbuf, pptlen, 1, stderr);
free(pptbuf);
fflush(stderr);
feep();
x = getquery("nyae ");
} else
x = 'y';
if (x == 'y' || x == ' ') {
*s = dupstring(best);
if (hist)
hwrep(best);
} else if (x == 'a') {
histdone |= HISTFLAG_NOEXEC;
} else if (x == 'e') {
histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
}
if (ic)
**s = ic;
}
}
/**/
int
ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm)
{
#ifndef HAVE_STRFTIME
static char *astr[] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static char *estr[] =
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
"Aug", "Sep", "Oct", "Nov", "Dec"};
#else
char *origbuf = buf;
#endif
static char *lstr[] =
{"12", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9",
"10", "11"};
char tmp[3];
tmp[0] = '%';
tmp[2] = '\0';
while (*fmt)
if (*fmt == '%') {
fmt++;
switch (*fmt++) {
case 'd':
*buf++ = '0' + tm->tm_mday / 10;
*buf++ = '0' + tm->tm_mday % 10;
break;
case 'e':
if (tm->tm_mday > 9)
*buf++ = '0' + tm->tm_mday / 10;
*buf++ = '0' + tm->tm_mday % 10;
break;
case 'k':
if (tm->tm_hour > 9)
*buf++ = '0' + tm->tm_hour / 10;
*buf++ = '0' + tm->tm_hour % 10;
break;
case 'l':
strucpy(&buf, lstr[tm->tm_hour % 12]);
break;
case 'm':
*buf++ = '0' + (tm->tm_mon + 1) / 10;
*buf++ = '0' + (tm->tm_mon + 1) % 10;
break;
case 'M':
*buf++ = '0' + tm->tm_min / 10;
*buf++ = '0' + tm->tm_min % 10;
break;
case 'S':
*buf++ = '0' + tm->tm_sec / 10;
*buf++ = '0' + tm->tm_sec % 10;
break;
case 'y':
*buf++ = '0' + tm->tm_year / 10;
*buf++ = '0' + tm->tm_year % 10;
break;
#ifndef HAVE_STRFTIME
case 'a':
strucpy(&buf, astr[tm->tm_wday]);
break;
case 'b':
strucpy(&buf, estr[tm->tm_mon]);
break;
case 'p':
*buf++ = (tm->tm_hour > 11) ? 'p' : 'a';
*buf++ = 'm';
break;
default:
*buf++ = '%';
if (fmt[-1] != '%')
*buf++ = fmt[-1];
#else
default:
*buf = '\0';
tmp[1] = fmt[-1];
strftime(buf, bufsize - strlen(origbuf), tmp, tm);
buf += strlen(buf);
#endif
break;
}
} else
*buf++ = *fmt++;
*buf = '\0';
return 0;
}
/**/
char *
zjoin(char **arr, int delim)
{
int len = 0;
char **s, *ret, *ptr;
for (s = arr; *s; s++)
len += strlen(*s) + 1;
if (!len)
return "";
ptr = ret = (char *) ncalloc(len);
for (s = arr; *s; s++) {
strucpy(&ptr, *s);
if (delim)
*ptr++ = delim;
}
ptr[-1] = '\0';
return ret;
}
/* Split a string containing a colon separated list *
* of items into an array of strings. */
/**/
char **
colonsplit(char *s, int uniq)
{
int ct;
char *t, **ret, **ptr, **p;
for (t = s, ct = 0; *t; t++) /* count number of colons */
if (*t == ':')
ct++;
ptr = ret = (char **) zalloc(sizeof(char **) * (ct + 2));
t = s;
do {
s = t;
/* move t to point at next colon */
for (; *t && *t != ':'; t++);
if (uniq)
for (p = ret; p < ptr; p++)
if (strlen(*p) == t - s && ! strncmp(*p, s, t - s))
goto cont;
*ptr = (char *) zalloc((t - s) + 1);
ztrncpy(*ptr++, s, t - s);
cont: ;
}
while (*t++);
*ptr = NULL;
return ret;
}
/**/
int
skipwsep(char **s)
{
char *t = *s;
int i = 0;
while (*t && iwsep(*t == Meta ? t[1] ^ 32 : *t)) {
if (*t == Meta)
t++;
t++;
i++;
}
*s = t;
return i;
}
/**/
char **
spacesplit(char *s, int allownull)
{
char *t, **ret, **ptr;
ptr = ret = (char **) ncalloc(sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1));
t = s;
skipwsep(&s);
if (*s && isep(*s == Meta ? s[1] ^ 32 : *s))
*ptr++ = dupstring(allownull ? "" : nulstring);
else if (!allownull && t != s)
*ptr++ = dupstring("");
while (*s) {
if (isep(*s == Meta ? s[1] ^ 32 : *s)) {
if (*s == Meta)
s++;
s++;
skipwsep(&s);
}
t = s;
findsep(&s, NULL);
if (s > t || allownull) {
*ptr = (char *) ncalloc((s - t) + 1);
ztrncpy(*ptr++, t, s - t);
} else
*ptr++ = dupstring(nulstring);
t = s;
skipwsep(&s);
}
if (!allownull && t != s)
*ptr++ = dupstring("");
*ptr = NULL;
return ret;
}
/**/
int
findsep(char **s, char *sep)
{
int i;
char *t, *tt;
if (!sep) {
for (t = *s; *t; t++) {
if (*t == Meta) {
if (isep(t[1] ^ 32))
break;
t++;
} else if (isep(*t))
break;
}
i = t - *s;
*s = t;
return i;
}
if (!sep[0]) {
return **s ? ++*s, 1 : -1;
}
for (i = 0; **s; i++) {
for (t = sep, tt = *s; *t && *tt && *t == *tt; t++, tt++);
if (!*t)
return i;
if (*(*s)++ == Meta) {
(*s)++;
#ifdef DEBUG
if (! **s)
fprintf(stderr, "BUG: unexpected end of string in findsep()\n");
#endif
}
}
return -1;
}
/**/
char *
findword(char **s, char *sep)
{
char *r, *t;
int sl;
if (!**s)
return NULL;
if (sep) {
sl = strlen(sep);
r = *s;
while (! findsep(s, sep)) {
r = *s += sl;
}
return r;
}
for (t = *s; *t; t++) {
if (*t == Meta) {
if (! isep(t[1] ^ 32))
break;
t++;
} else if (! isep(*t))
break;
}
*s = t;
findsep(s, sep);
return t;
}
/**/
int
wordcount(char *s, char *sep, int mul)
{
int r, sl, c;
if (sep) {
r = 1;
sl = strlen(sep);
for (; (c = findsep(&s, sep)) >= 0; s += sl)
if ((c && *(s + sl)) || mul)
r++;
} else {
char *t = s;
r = 0;
if (mul <= 0)
skipwsep(&s);
if ((*s && isep(*s == Meta ? s[1] ^ 32 : *s)) ||
(mul < 0 && t != s))
r++;
for (; *s; r++) {
if (isep(*s == Meta ? s[1] ^ 32 : *s)) {
if (*s == Meta)
s++;
s++;
if (mul <= 0)
skipwsep(&s);
}
findsep(&s, NULL);
t = s;
if (mul <= 0)
skipwsep(&s);
}
if (mul < 0 && t != s)
r++;
}
return r;
}
/**/
char *
sepjoin(char **s, char *sep)
{
char *r, *p, **t;
int l, sl, elide = 0;
char sepbuf[3];
if (!*s)
return "";
if (!sep) {
elide = 1;
sep = sepbuf;
sepbuf[0] = *ifs;
sepbuf[1] = *ifs == Meta ? ifs[1] ^ 32 : '\0';
sepbuf[2] = '\0';
}
sl = strlen(sep);
for (t = s, l = 1 - sl; *t; l += strlen(*t) + sl, t++);
r = p = (char *) ncalloc(l);
t = s;
while (*t) {
strucpy(&p, *t);
if (*++t)
strucpy(&p, sep);
}
*p = '\0';
return r;
}
/**/
char **
sepsplit(char *s, char *sep, int allownull)
{
int n, sl;
char *t, *tt, **r, **p;
if (!sep)
return spacesplit(s, allownull);
sl = strlen(sep);
n = wordcount(s, sep, 1);
r = p = (char **) ncalloc((n + 1) * sizeof(char *));
for (t = s; n--;) {
tt = t;
findsep(&t, sep);
*p = (char *) ncalloc(t - tt + 1);
strncpy(*p, tt, t - tt);
(*p)[t - tt] = '\0';
p++;
t += sl;
}
*p = NULL;
return r;
}
/* Get the definition of a shell function */
/**/
List
getshfunc(char *nam)
{
Shfunc shf;
List l;
if ((shf = (Shfunc) shfunctab->getnode(shfunctab, nam))) {
/* if autoloaded and currently undefined */
if (shf->flags & PM_UNDEFINED) {
if (!(l = getfpfunc(nam))) {
zerr("function not found: %s", nam, 0);
return NULL;
}
shf->flags &= ~PM_UNDEFINED;
PERMALLOC {
shf->funcdef = (List) dupstruct(l);
} LASTALLOC;
}
return shf->funcdef;
} else {
return NULL;
}
}
/* allocate a tree element */
static int sizetab[N_COUNT] =
{
sizeof(struct list),
sizeof(struct sublist),
sizeof(struct pline),
sizeof(struct cmd),
sizeof(struct redir),
sizeof(struct cond),
sizeof(struct forcmd),
sizeof(struct casecmd),
sizeof(struct ifcmd),
sizeof(struct whilecmd),
sizeof(struct varasg)};
static int flagtab[N_COUNT] =
{
NT_SET(N_LIST, 1, NT_NODE, NT_NODE, 0, 0),
NT_SET(N_SUBLIST, 2, NT_NODE, NT_NODE, 0, 0),
NT_SET(N_PLINE, 1, NT_NODE, NT_NODE, 0, 0),
NT_SET(N_CMD, 2, NT_STR | NT_LIST, NT_NODE, NT_NODE | NT_LIST, NT_NODE | NT_LIST),
NT_SET(N_REDIR, 3, NT_STR, 0, 0, 0),
NT_SET(N_COND, 1, NT_NODE, NT_NODE, 0, 0),
NT_SET(N_FOR, 1, NT_STR, NT_NODE, 0, 0),
NT_SET(N_CASE, 0, NT_STR | NT_ARR, NT_NODE | NT_ARR, 0, 0),
NT_SET(N_IF, 0, NT_NODE | NT_ARR, NT_NODE | NT_ARR, 0, 0),
NT_SET(N_WHILE, 1, NT_NODE, NT_NODE, 0, 0),
NT_SET(N_VARASG, 1, NT_STR, NT_STR, NT_STR | NT_LIST, 0)};
/**/
void *
allocnode(int type)
{
struct node *n;
n = (struct node *) alloc(sizetab[type]);
memset((void *) n, 0, sizetab[type]);
n->ntype = flagtab[type];
if (useheap)
n->ntype |= NT_HEAP;
return (void *) n;
}
/**/
void *
dupstruct(void *a)
{
struct node *n, *r;
n = (struct node *) a;
if (!a || ((List) a) == &dummy_list)
return (void *) a;
if ((n->ntype & NT_HEAP) && !useheap) {
HEAPALLOC {
n = (struct node *) dupstruct2((void *) n);
} LASTALLOC;
n = simplifystruct(n);
}
r = (struct node *)dupstruct2((void *) n);
if (!(n->ntype & NT_HEAP) && useheap)
r = expandstruct(r, N_LIST);
return (void *) r;
}
/**/
struct node *
simplifystruct(struct node *n)
{
if (!n || ((List) n) == &dummy_list)
return n;
switch (NT_TYPE(n->ntype)) {
case N_LIST:
{
List l = (List) n;
l->left = (Sublist) simplifystruct((struct node *)l->left);
if ((l->type & Z_SYNC) && !l->right)
return (struct node *)l->left;
}
break;
case N_SUBLIST:
{
Sublist sl = (Sublist) n;
sl->left = (Pline) simplifystruct((struct node *)sl->left);
if (sl->type == END && !sl->flags && !sl->right)
return (struct node *)sl->left;
}
break;
case N_PLINE:
{
Pline pl = (Pline) n;
pl->left = (Cmd) simplifystruct((struct node *)pl->left);
if (pl->type == END && !pl->right)
return (struct node *)pl->left;
}
break;
case N_CMD:
{
Cmd c = (Cmd) n;
int i = 0;
if (empty(c->args))
c->args = NULL, i++;
if (empty(c->redir))
c->redir = NULL, i++;
if (empty(c->vars))
c->vars = NULL, i++;
c->u.list = (List) simplifystruct((struct node *)c->u.list);
if (i == 3 && !c->flags &&
(c->type == CWHILE || c->type == CIF ||
c->type == COND))
return (struct node *)c->u.list;
}
break;
case N_FOR:
{
Forcmd f = (Forcmd) n;
f->list = (List) simplifystruct((struct node *)f->list);
}
break;
case N_CASE:
{
struct casecmd *c = (struct casecmd *)n;
List *l;
for (l = c->lists; *l; l++)
*l = (List) simplifystruct((struct node *)*l);
}
break;
case N_IF:
{
struct ifcmd *i = (struct ifcmd *)n;
List *l;
for (l = i->ifls; *l; l++)
*l = (List) simplifystruct((struct node *)*l);
for (l = i->thenls; *l; l++)
*l = (List) simplifystruct((struct node *)*l);
}
break;
case N_WHILE:
{
struct whilecmd *w = (struct whilecmd *)n;
w->cont = (List) simplifystruct((struct node *)w->cont);
w->loop = (List) simplifystruct((struct node *)w->loop);
}
}
return n;
}
/**/
struct node *
expandstruct(struct node *n, int exp)
{
struct node *m;
if (!n || ((List) n) == &dummy_list)
return n;
if (exp != N_COUNT && exp != NT_TYPE(n->ntype)) {
switch (exp) {
case N_LIST:
{
List l;
m = (struct node *) allocnode(N_LIST);
l = (List) m;
l->type = Z_SYNC;
l->left = (Sublist) expandstruct(n, N_SUBLIST);
return (struct node *)l;
}
case N_SUBLIST:
{
Sublist sl;
m = (struct node *) allocnode(N_SUBLIST);
sl = (Sublist) m;
sl->type = END;
sl->left = (Pline) expandstruct(n, N_PLINE);
return (struct node *)sl;
}
case N_PLINE:
{
Pline pl;
m = (struct node *) allocnode(N_PLINE);
pl = (Pline) m;
pl->type = END;
pl->left = (Cmd) expandstruct(n, N_CMD);
return (struct node *)pl;
}
case N_CMD:
{
Cmd c;
m = (struct node *) allocnode(N_CMD);
c = (Cmd) m;
switch (NT_TYPE(n->ntype)) {
case N_WHILE:
c->type = CWHILE;
break;
case N_IF:
c->type = CIF;
break;
case N_COND:
c->type = COND;
}
c->u.list = (List) expandstruct(n, NT_TYPE(n->ntype));
c->args = newlinklist();
c->vars = newlinklist();
c->redir = newlinklist();
return (struct node *)c;
}
}
} else
switch (NT_TYPE(n->ntype)) {
case N_LIST:
{
List l = (List) n;
l->left = (Sublist) expandstruct((struct node *)l->left,
N_SUBLIST);
l->right = (List) expandstruct((struct node *)l->right,
N_LIST);
}
break;
case N_SUBLIST:
{
Sublist sl = (Sublist) n;
sl->left = (Pline) expandstruct((struct node *)sl->left,
N_PLINE);
sl->right = (Sublist) expandstruct((struct node *)sl->right,
N_SUBLIST);
}
break;
case N_PLINE:
{
Pline pl = (Pline) n;
pl->left = (Cmd) expandstruct((struct node *)pl->left,
N_CMD);
pl->right = (Pline) expandstruct((struct node *)pl->right,
N_PLINE);
}
break;
case N_CMD:
{
Cmd c = (Cmd) n;
if (!c->args)
c->args = newlinklist();
if (!c->vars)
c->vars = newlinklist();
if (!c->redir)
c->redir = newlinklist();
switch (c->type) {
case CFOR:
case CSELECT:
c->u.list = (List) expandstruct((struct node *)c->u.list,
N_FOR);
break;
case CWHILE:
c->u.list = (List) expandstruct((struct node *)c->u.list,
N_WHILE);
break;
case CIF:
c->u.list = (List) expandstruct((struct node *)c->u.list,
N_IF);
break;
case CCASE:
c->u.list = (List) expandstruct((struct node *)c->u.list,
N_CASE);
break;
case COND:
c->u.list = (List) expandstruct((struct node *)c->u.list,
N_COND);
break;
case ZCTIME:
c->u.list = (List) expandstruct((struct node *)c->u.list,
N_SUBLIST);
break;
default:
c->u.list = (List) expandstruct((struct node *)c->u.list,
N_LIST);
}
}
break;
case N_FOR:
{
Forcmd f = (Forcmd) n;
f->list = (List) expandstruct((struct node *)f->list,
N_LIST);
}
break;
case N_CASE:
{
struct casecmd *c = (struct casecmd *)n;
List *l;
for (l = c->lists; *l; l++)
*l = (List) expandstruct((struct node *)*l, N_LIST);
}
break;
case N_IF:
{
struct ifcmd *i = (struct ifcmd *)n;
List *l;
for (l = i->ifls; *l; l++)
*l = (List) expandstruct((struct node *)*l, N_LIST);
for (l = i->thenls; *l; l++)
*l = (List) expandstruct((struct node *)*l, N_LIST);
}
break;
case N_WHILE:
{
struct whilecmd *w = (struct whilecmd *)n;
w->cont = (List) expandstruct((struct node *)w->cont,
N_LIST);
w->loop = (List) expandstruct((struct node *)w->loop,
N_LIST);
}
}
return n;
}
/* duplicate a syntax tree node of given type, argument number */
/**/
void *
dupnode(int type, void *a, int argnum)
{
if (!a)
return NULL;
switch (NT_N(type, argnum)) {
case NT_NODE:
return (void *) dupstruct2(a);
case NT_STR:
return (useheap) ? ((void *) dupstring(a)) :
((void *) ztrdup(a));
case NT_LIST | NT_NODE:
if (type & NT_HEAP)
if (useheap)
return (void *) duplist(a, (VFunc) dupstruct2);
else
return (void *) list2arr(a, (VFunc) dupstruct2);
else if (useheap)
return (void *) arr2list(a, (VFunc) dupstruct2);
else
return (void *) duparray(a, (VFunc) dupstruct2);
case NT_LIST | NT_STR:
if (type & NT_HEAP)
if (useheap)
return (void *) duplist(a, (VFunc) dupstring);
else
return (void *) list2arr(a, (VFunc) ztrdup);
else if (useheap)
return (void *) arr2list(a, (VFunc) dupstring);
else
return (void *) duparray(a, (VFunc) ztrdup);
case NT_NODE | NT_ARR:
return (void *) duparray(a, (VFunc) dupstruct2);
case NT_STR | NT_ARR:
return (void *) duparray(a, (VFunc) (useheap ? dupstring : ztrdup));
default:
abort();
}
}
/* Free a syntax tree node of given type, argument number */
/**/
void
freetreenode(int type, void *a, int argnum)
{
if (!a)
return;
switch (NT_N(type, argnum)) {
case NT_NODE:
freestruct(a);
break;
case NT_STR:
zsfree(a);
break;
case NT_LIST | NT_NODE:
case NT_NODE | NT_ARR:
{
char **p = (char **)a;
while (*p)
freestruct(*p++);
free(a);
}
break;
case NT_LIST | NT_STR:
case NT_STR | NT_ARR:
freearray(a);
break;
default:
abort();
}
}
/* duplicate a syntax tree */
/**/
void **
dupstruct2(void *a)
{
struct node *n = (struct node *)a, *m;
int type;
if (!n || ((List) n) == &dummy_list)
return a;
type = n->ntype;
m = (struct node *) alloc(sizetab[NT_TYPE(type)]);
m->ntype = (type & ~NT_HEAP);
if (useheap)
m->ntype |= NT_HEAP;
switch (NT_TYPE(type)) {
case N_LIST:
{
List nl = (List) n;
List ml = (List) m;
ml->type = nl->type;
ml->left = (Sublist) dupnode(type, nl->left, 0);
ml->right = (List) dupnode(type, nl->right, 1);
}
break;
case N_SUBLIST:
{
Sublist nsl = (Sublist) n;
Sublist msl = (Sublist) m;
msl->type = nsl->type;
msl->flags = nsl->flags;
msl->left = (Pline) dupnode(type, nsl->left, 0);
msl->right = (Sublist) dupnode(type, nsl->right, 1);
}
break;
case N_PLINE:
{
Pline npl = (Pline) n;
Pline mpl = (Pline) m;
mpl->type = npl->type;
mpl->left = (Cmd) dupnode(type, npl->left, 0);
mpl->right = (Pline) dupnode(type, npl->right, 1);
}
break;
case N_CMD:
{
Cmd nc = (Cmd) n;
Cmd mc = (Cmd) m;
mc->type = nc->type;
mc->flags = nc->flags;
mc->lineno = nc->lineno;
mc->args = (LinkList) dupnode(type, nc->args, 0);
mc->u.generic = (void *) dupnode(type, nc->u.generic, 1);
mc->redir = (LinkList) dupnode(type, nc->redir, 2);
mc->vars = (LinkList) dupnode(type, nc->vars, 3);
}
break;
case N_REDIR:
{
Redir nr = (Redir) n;
Redir mr = (Redir) m;
mr->type = nr->type;
mr->fd1 = nr->fd1;
mr->fd2 = nr->fd2;
mr->name = (char *)dupnode(type, nr->name, 0);
}
break;
case N_COND:
{
Cond nco = (Cond) n;
Cond mco = (Cond) m;
mco->type = nco->type;
mco->left = (void *) dupnode(type, nco->left, 0);
mco->right = (void *) dupnode(type, nco->right, 1);
}
break;
case N_FOR:
{
Forcmd nf = (Forcmd) n;
Forcmd mf = (Forcmd) m;
mf->inflag = nf->inflag;
mf->name = (char *)dupnode(type, nf->name, 0);
mf->list = (List) dupnode(type, nf->list, 1);
}
break;
case N_CASE:
{
struct casecmd *ncc = (struct casecmd *)n;
struct casecmd *mcc = (struct casecmd *)m;
mcc->pats = (char **)dupnode(type, ncc->pats, 0);
mcc->lists = (List *) dupnode(type, ncc->lists, 1);
}
break;
case N_IF:
{
struct ifcmd *nic = (struct ifcmd *)n;
struct ifcmd *mic = (struct ifcmd *)m;
mic->ifls = (List *) dupnode(type, nic->ifls, 0);
mic->thenls = (List *) dupnode(type, nic->thenls, 1);
}
break;
case N_WHILE:
{
struct whilecmd *nwc = (struct whilecmd *)n;
struct whilecmd *mwc = (struct whilecmd *)m;
mwc->cond = nwc->cond;
mwc->cont = (List) dupnode(type, nwc->cont, 0);
mwc->loop = (List) dupnode(type, nwc->loop, 1);
}
break;
case N_VARASG:
{
Varasg nva = (Varasg) n;
Varasg mva = (Varasg) m;
mva->type = nva->type;
mva->name = (char *)dupnode(type, nva->name, 0);
mva->str = (char *)dupnode(type, nva->str, 1);
mva->arr = (LinkList) dupnode(type, nva->arr, 2);
}
break;
}
return (void **) m;
}
/* free a syntax tree */
/**/
void
freestruct(void *a)
{
struct node *n = (struct node *)a;
int type;
if (!n || ((List) n) == &dummy_list)
return;
type = n->ntype;
switch (NT_TYPE(type)) {
case N_LIST:
{
List nl = (List) n;
freetreenode(type, nl->left, 0);
freetreenode(type, nl->right, 1);
}
break;
case N_SUBLIST:
{
Sublist nsl = (Sublist) n;
freetreenode(type, nsl->left, 0);
freetreenode(type, nsl->right, 1);
}
break;
case N_PLINE:
{
Pline npl = (Pline) n;
freetreenode(type, npl->left, 0);
freetreenode(type, npl->right, 1);
}
break;
case N_CMD:
{
Cmd nc = (Cmd) n;
freetreenode(type, nc->args, 0);
freetreenode(type, nc->u.generic, 1);
freetreenode(type, nc->redir, 2);
freetreenode(type, nc->vars, 3);
}
break;
case N_REDIR:
{
Redir nr = (Redir) n;
freetreenode(type, nr->name, 0);
}
break;
case N_COND:
{
Cond nco = (Cond) n;
freetreenode(type, nco->left, 0);
freetreenode(type, nco->right, 1);
}
break;
case N_FOR:
{
Forcmd nf = (Forcmd) n;
freetreenode(type, nf->name, 0);
freetreenode(type, nf->list, 1);
}
break;
case N_CASE:
{
struct casecmd *ncc = (struct casecmd *)n;
freetreenode(type, ncc->pats, 0);
freetreenode(type, ncc->lists, 1);
}
break;
case N_IF:
{
struct ifcmd *nic = (struct ifcmd *)n;
freetreenode(type, nic->ifls, 0);
freetreenode(type, nic->thenls, 1);
}
break;
case N_WHILE:
{
struct whilecmd *nwc = (struct whilecmd *)n;
freetreenode(type, nwc->cont, 0);
freetreenode(type, nwc->loop, 1);
}
break;
case N_VARASG:
{
Varasg nva = (Varasg) n;
freetreenode(type, nva->name, 0);
freetreenode(type, nva->str, 1);
freetreenode(type, nva->arr, 2);
}
break;
}
zfree(n, sizetab[NT_TYPE(type)]);
}
/**/
LinkList
duplist(LinkList l, VFunc func)
{
LinkList ret;
LinkNode node;
ret = newlinklist();
for (node = firstnode(l); node; incnode(node))
addlinknode(ret, func(getdata(node)));
return ret;
}
/**/
char **
duparray(char **arr, VFunc func)
{
char **ret, **rr;
ret = (char **) alloc((arrlen(arr) + 1) * sizeof(char *));
for (rr = ret; *arr;)
*rr++ = (char *)func(*arr++);
*rr = NULL;
return ret;
}
/**/
char **
list2arr(LinkList l, VFunc func)
{
char **arr, **r;
LinkNode n;
arr = r = (char **) alloc((countlinknodes(l) + 1) * sizeof(char *));
for (n = firstnode(l); n; incnode(n))
*r++ = (char *)func(getdata(n));
*r = NULL;
return arr;
}
/**/
LinkList
arr2list(char **arr, VFunc func)
{
LinkList l = newlinklist();
while (*arr)
addlinknode(l, func(*arr++));
return l;
}
/**/
char **
mkarray(char *s)
{
char **t = (char **) zalloc((s) ? (2 * sizeof s) : (sizeof s));
if ((*t = s))
t[1] = NULL;
return t;
}
/**/
void
feep(void)
{
if (isset(BEEP))
write(2, "\07", 1);
}
/**/
void
freearray(char **s)
{
char **t = s;
while (*s)
zsfree(*s++);
free(t);
}
/**/
int
equalsplit(char *s, char **t)
{
for (; *s && *s != '='; s++);
if (*s == '=') {
*s++ = '\0';
*t = s;
return 1;
}
return 0;
}
/* see if the right side of a list is trivial */
/**/
void
simplifyright(List l)
{
Cmd c;
if (l == &dummy_list || !l->right)
return;
if (l->right->right || l->right->left->right ||
l->right->left->flags || l->right->left->left->right ||
l->left->flags)
return;
c = l->left->left->left;
if (c->type != SIMPLE || nonempty(c->args) || nonempty(c->redir)
|| nonempty(c->vars))
return;
l->right = NULL;
return;
}
/* initialize the ztypes table */
/**/
void
inittyptab(void)
{
int t0;
char *s;
for (t0 = 0; t0 != 256; t0++)
typtab[t0] = 0;
for (t0 = 0; t0 != 32; t0++)
typtab[t0] = typtab[t0 + 128] = ICNTRL;
typtab[127] = ICNTRL;
for (t0 = '0'; t0 <= '9'; t0++)
typtab[t0] = IDIGIT | IALNUM | IWORD | IIDENT | IUSER;
for (t0 = 'a'; t0 <= 'z'; t0++)
typtab[t0] = typtab[t0 - 'a' + 'A'] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
for (t0 = 0240; t0 != 0400; t0++)
typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
typtab['_'] = IIDENT | IUSER;
typtab['-'] = IUSER;
typtab[' '] |= IBLANK | INBLANK;
typtab['\t'] |= IBLANK | INBLANK;
typtab['\n'] |= INBLANK;
typtab['\0'] |= IMETA;
typtab[STOUC(Meta) ] |= IMETA;
typtab[STOUC(Marker)] |= IMETA;
for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++)
typtab[t0] |= ITOK | IMETA;
for (s = ifs ? ifs : DEFAULT_IFS; *s; s++) {
if (inblank(*s))
if (s[1] == *s)
s++;
else
typtab[STOUC(*s)] |= IWSEP;
typtab[STOUC(*s == Meta ? *++s ^ 32 : *s)] |= ISEP;
}
for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++)
typtab[STOUC(*s == Meta ? *++s ^ 32 : *s)] |= IWORD;
for (s = SPECCHARS; *s; s++)
typtab[STOUC(*s)] |= ISPECIAL;
if (isset(BANGHIST) && bangchar && interact && isset(SHINSTDIN))
typtab[bangchar] |= ISPECIAL;
}
/**/
char **
arrdup(char **s)
{
char **x, **y;
y = x = (char **) ncalloc(sizeof(char *) * (arrlen(s) + 1));
while ((*x++ = dupstring(*s++)));
return y;
}
/**/
char *
spname(char *oldname)
{
char *p, spnameguess[PATH_MAX + 1], spnamebest[PATH_MAX + 1];
static char newname[PATH_MAX + 1];
char *new = newname, *old;
int bestdist = 200, thisdist;
old = oldname;
for (;;) {
while (*old == '/')
*new++ = *old++;
*new = '\0';
if (*old == '\0')
return newname;
p = spnameguess;
for (; *old != '/' && *old != '\0'; old++)
if (p < spnameguess + PATH_MAX)
*p++ = *old;
*p = '\0';
if ((thisdist = mindist(newname, spnameguess, spnamebest)) >= 3) {
if (bestdist < 3) {
strcpy(new, spnameguess);
strcat(new, old);
return newname;
} else
return NULL;
} else
bestdist = thisdist;
for (p = spnamebest; (*new = *p++);)
new++;
}
}
/**/
int
mindist(char *dir, char *mindistguess, char *mindistbest)
{
int mindistd, nd;
DIR *dd;
char *fn;
char buf[PATH_MAX];
if (dir[0] == '\0')
dir = ".";
mindistd = 100;
sprintf(buf, "%s/%s", dir, mindistguess);
if (access(unmeta(buf), F_OK) == 0) {
strcpy(mindistbest, mindistguess);
return 0;
}
if (!(dd = opendir(unmeta(dir))))
return mindistd;
while ((fn = zreaddir(dd))) {
nd = spdist(fn, mindistguess,
(int)strlen(mindistguess) / 4 + 1);
if (nd <= mindistd) {
strcpy(mindistbest, fn);
mindistd = nd;
if (mindistd == 0)
break;
}
}
closedir(dd);
return mindistd;
}
/**/
int
spdist(char *s, char *t, int thresh)
{
char *p, *q;
char *keymap =
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
\t1234567890-=\t\
\tqwertyuiop[]\t\
\tasdfghjkl;'\n\t\
\tzxcvbnm,./\t\t\t\
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
\t!@#$%^&*()_+\t\
\tQWERTYUIOP{}\t\
\tASDFGHJKL:\"\n\t\
\tZXCVBNM<>?\n\n\t\
\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
if (!strcmp(s, t))
return 0;
/* any number of upper/lower mistakes allowed (dist = 1) */
for (p = s, q = t; *p && tulower(*p) == tulower(*q); p++, q++);
if (!*p && !*q)
return 1;
if (!thresh)
return 200;
for (p = s, q = t; *p && *q; p++, q++)
if (*p == *q)
continue; /* don't consider "aa" transposed, ash */
else if (p[1] == q[0] && q[1] == p[0]) /* transpositions */
return spdist(p + 2, q + 2, thresh - 1) + 1;
else if (p[1] == q[0]) /* missing letter */
return spdist(p + 1, q + 0, thresh - 1) + 2;
else if (p[0] == q[1]) /* missing letter */
return spdist(p + 0, q + 1, thresh - 1) + 2;
else if (*p != *q)
break;
if ((!*p && strlen(q) == 1) || (!*q && strlen(p) == 1))
return 2;
for (p = s, q = t; *p && *q; p++, q++)
if (p[0] != q[0] && p[1] == q[1]) {
int t0;
char *z;
/* mistyped letter */
if (!(z = strchr(keymap, p[0])) || *z == '\n' || *z == '\t')
return spdist(p + 1, q + 1, thresh - 1) + 1;
t0 = z - keymap;
if (*q == keymap[t0 - 15] || *q == keymap[t0 - 14] ||
*q == keymap[t0 - 13] ||
*q == keymap[t0 - 1] || *q == keymap[t0 + 1] ||
*q == keymap[t0 + 13] || *q == keymap[t0 + 14] ||
*q == keymap[t0 + 15])
return spdist(p + 1, q + 1, thresh - 1) + 2;
return 200;
} else if (*p != *q)
break;
return 200;
}
/* set cbreak mode, or the equivalent */
/**/
void
setcbreak(void)
{
struct ttyinfo ti;
ti = shttyinfo;
#ifdef HAS_TIO
ti.tio.c_lflag &= ~ICANON;
ti.tio.c_cc[VMIN] = 1;
ti.tio.c_cc[VTIME] = 0;
#else
ti.sgttyb.sg_flags |= CBREAK;
#endif
settyinfo(&ti);
}
/* give the tty to some process */
/**/
void
attachtty(pid_t pgrp)
{
static int ep = 0;
if (jobbing) {
#ifdef HAVE_TCSETPGRP
if (SHTTY != -1 && tcsetpgrp(SHTTY, pgrp) == -1 && !ep)
#else
# if ardent
if (SHTTY != -1 && setpgrp() == -1 && !ep)
# else
int arg = pgrp;
if (SHTTY != -1 && ioctl(SHTTY, TIOCSPGRP, &arg) == -1 && !ep)
# endif
#endif
{
if (pgrp != mypgrp && kill(pgrp, 0) == -1)
attachtty(mypgrp);
else {
if (errno != ENOTTY)
{
zerr("can't set tty pgrp: %e", NULL, errno);
fflush(stderr);
}
opts[MONITOR] = 0;
ep = 1;
errflag = 0;
}
}
}
}
/* get the process group associated with the tty */
/**/
pid_t
gettygrp(void)
{
pid_t arg;
if (SHTTY == -1)
return -1;
#ifdef HAVE_TCSETPGRP
arg = tcgetpgrp(SHTTY);
#else
ioctl(SHTTY, TIOCGPGRP, &arg);
#endif
return arg;
}
/* Return the output baudrate */
#ifdef HAVE_SELECT
/**/
long
getbaudrate(struct ttyinfo *shttyinfo)
{
long speedcode;
#ifdef HAS_TIO
# if defined(HAVE_TCGETATTR) && defined(HAVE_TERMIOS_H)
speedcode = cfgetospeed(&shttyinfo->tio);
# else
speedcode = shttyinfo->tio.c_cflag & CBAUD;
# endif
#else
speedcode = shttyinfo->sgttyb.sg_ospeed;
#endif
switch (speedcode) {
case B0:
return (0L);
case B50:
return (50L);
case B75:
return (75L);
case B110:
return (110L);
case B134:
return (134L);
case B150:
return (150L);
case B200:
return (200L);
case B300:
return (300L);
case B600:
return (600L);
#ifdef _B900
case _B900:
return (900L);
#endif
case B1200:
return (1200L);
case B1800:
return (1800L);
case B2400:
return (2400L);
#ifdef _B3600
case _B3600:
return (3600L);
#endif
case B4800:
return (4800L);
#ifdef _B7200
case _B7200:
return (7200L);
#endif
case B9600:
return (9600L);
#ifdef B19200
case B19200:
return (19200L);
#else
# ifdef EXTA
case EXTA:
return (19200L);
# endif
#endif
#ifdef B38400
case B38400:
return (38400L);
#else
# ifdef EXTB
case EXTB:
return (38400L);
# endif
#endif
#ifdef B57600
case B57600:
return (57600L);
#endif
#ifdef B115200
case B115200:
return (115200L);
#endif
#ifdef B230400
case B230400:
return (230400L);
#endif
#ifdef B460800
case B460800:
return (460800L);
#endif
default:
if (speedcode >= 100)
return speedcode;
break;
}
return (0L);
}
#endif
/* Escape tokens and null characters. Buf is the string which should be *
* escaped. len is the length of the string. If len is -1, buf should *
* be null terminated. If len is non-zero and the third paramerer is not *
* META_DUP buf should point to an at least len+1 long memory area. The *
* return value points to the quoted string. If the given string does not *
* contain any special character which should be quoted and the third *
* parameter is not META_DUP, buf is returned unchanged (a terminating null *
* character is appended to buf if necessary). Otherwise the third `heap' *
* argument determines the method used to allocate space for the result. *
* It can have the following values: *
* META_REALLOC: use zrealloc on buf *
* META_USEHEAP: get memory from the heap *
* META_NOALLOC: buf points to a memory area which is long enough to hold *
* the quoted form, just quote it and return buf. *
* META_STATIC: store the quoted string in a static area. The original *
* sting should be at most PATH_MAX long. *
* META_ALLOC: allocate memory for the new string with zalloc(). *
* META_DUP: leave buf unchanged and allocate space for the return *
* value even if buf does not contains special characters *
* META_HEAPDUP: same as META_DUP, but uses the heap */
/**/
char *
metafy(char *buf, int len, int heap)
{
int meta = 0;
char *t, *p, *e;
static char mbuf[PATH_MAX*2+1];
if (len == -1) {
for (e = buf, len = 0; *e; len++)
if (imeta(*e++))
meta++;
} else
for (e = buf; e < buf + len;)
if (imeta(*e++))
meta++;
if (meta || heap == META_DUP || heap == META_HEAPDUP) {
switch (heap) {
case META_REALLOC:
buf = zrealloc(buf, len + meta + 1);
break;
case META_USEHEAP:
buf = hrealloc(buf, len, len + meta + 1);
break;
case META_ALLOC:
case META_DUP:
buf = memcpy(zalloc(len + meta + 1), buf, len);
break;
case META_HEAPDUP:
buf = memcpy(halloc(len + meta + 1), buf, len);
break;
case META_STATIC:
#ifdef DEBUG
if (len > PATH_MAX) {
fprintf(stderr, "BUG: len = %d > PATH_MAX in metafy\n", len);
fflush(stderr);
}
#endif
buf = memcpy(mbuf, buf, len);
break;
#ifdef DEBUG
case META_NOALLOC:
break;
default:
fprintf(stderr, "BUG: metafy called with invaild heap value\n");
fflush(stderr);
break;
#endif
}
p = buf + len;
e = t = buf + len + meta;
while (meta) {
if (imeta(*--t = *--p)) {
*t-- ^= 32;
*t = Meta;
meta--;
}
}
}
*e = '\0';
return buf;
}
/**/
char *
unmetafy(char *s, int *len)
{
char *p, *t;
for (p = s; *p && *p != Meta; p++);
for (t = p; (*t = *p++);)
if (*t++ == Meta)
t[-1] = *p++ ^ 32;
if (len)
*len = t - s;
return s;
}
/* Return the character length of a metafied substring, given the *
* unmetafied substring length. */
/**/
int
metalen(char *s, int len)
{
int mlen = len;
while (len--) {
if (*s++ == Meta) {
mlen++;
s++;
}
}
return mlen;
}
/* This function converts a zsh internal string to a form which can be *
* passed to a system call as a filename. The result is stored in a *
* single static area. NULL returned if the result is longer than *
* PATH_MAX. */
/**/
char *
unmeta(char *file_name)
{
static char fn[PATH_MAX];
char *p, *t;
for (t = file_name, p = fn; *t && p < fn + PATH_MAX - 1; p++)
if ((*p = *t++) == Meta)
*p = *t++ ^ 32;
if (*t)
return NULL;
if (p - fn == t - file_name)
return file_name;
*p = '\0';
return fn;
}
/* Unmetafy and compare two strings, with unsigned characters. *
* "a\0" sorts after "a". */
/**/
int
ztrcmp(unsigned char const *s1, unsigned char const *s2)
{
int c1, c2;
while(*s1 && *s1 == *s2) {
s1++;
s2++;
}
if(!(c1 = *s1))
c1 = -1;
else if(c1 == STOUC(Meta))
c1 = *++s1 ^ 32;
if(!(c2 = *s2))
c2 = -1;
else if(c2 == STOUC(Meta))
c2 = *++s2 ^ 32;
if(c1 == c2)
return 0;
else if(c1 < c2)
return -1;
else
return 1;
}
/* Return zero if the metafied string s and the non-metafied, *
* len-long string r are the same. Return -1 if r is a prefix *
* of s. Return 1 if r is the lowercase version of s. Return *
* 2 is r is the lowercase prefix of s and return 3 otherwise. */
/**/
int
metadiffer(char const *s, char const *r, int len)
{
int l = len;
while (l-- && *s && *r++ == (*s == Meta ? *++s ^ 32 : *s))
s++;
if (*s && l < 0)
return -1;
if (l < 0)
return 0;
if (!*s)
return 3;
s -= len - l - 1;
r -= len - l;
while (len-- && *s && *r++ == tulower(*s == Meta ? *++s ^ 32 : *s))
s++;
if (*s && len < 0)
return 2;
if (len < 0)
return 1;
return 3;
}
/* Return the unmetafied length of a metafied string. */
/**/
int
ztrlen(char const *s)
{
int l;
for (l = 0; *s; l++)
if (*s++ == Meta) {
#ifdef DEBUG
if (! *s)
fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n");
else
#endif
s++;
}
return l;
}
/* Subtract two pointers in a metafied string. */
/**/
int
ztrsub(char const *t, char const *s)
{
int l = t - s;
while (s != t)
if (*s++ == Meta) {
#ifdef DEBUG
if (! *s || s == t)
fprintf(stderr, "BUG: substring ends in the middle of a metachar in ztrsub()\n");
else
#endif
s++;
l--;
}
return l;
}
/**/
char *
zreaddir(DIR *dir)
{
struct dirent *de = readdir(dir);
return de ? metafy(de->d_name, -1, META_STATIC) : NULL;
}
/* Unmetafy and output a string. */
/**/
int
zputs(char const *s, FILE *stream)
{
int c;
while (*s) {
if (*s == Meta)
c = *++s ^ 32;
else
c = *s;
s++;
if (fputc(c, stream) < 0)
return EOF;
}
return 0;
}
/* Unmetafy and output a string, displaying special characters readably. */
/**/
int
nicezputs(char const *s, FILE *stream)
{
int c;
while ((c = *s++)) {
if (itok(c))
if (c <= Comma)
c = ztokens[c - Pound];
else
continue;
if (c == Meta)
c = *s++ ^ 32;
if(fputs(nicechar(c), stream) < 0)
return EOF;
}
return 0;
}
/* Return the length of the visible representation of a metafied string. */
/**/
size_t
niceztrlen(char const *s)
{
size_t l = 0;
int c;
while ((c = *s++)) {
if (itok(c))
if (c <= Comma)
c = ztokens[c - Pound];
else
continue;
if (c == Meta)
c = *s++ ^ 32;
l += strlen(nicechar(STOUC(c)));
}
return l;
}
/* check for special characters in the string */
/**/
int
hasspecial(char const *s)
{
for (; *s; s++)
if (ispecial(*s == Meta ? *++s ^ 32 : *s))
return 1;
return 0;
}
/* Unmetafy and output a string, quoted if it contains special characters. */
/**/
int
quotedzputs(char const *s, FILE *stream)
{
int inquote = 0, c;
/* check for empty string */
if(!*s)
return fputs("''", stream);
if (!hasspecial(s))
return zputs(s, stream);
if (isset(RCQUOTES)) {
/* use rc-style quotes-within-quotes for the whole string */
if(fputc('\'', stream) < 0)
return EOF;
while(*s) {
if (*s == Meta)
c = *++s ^ 32;
else
c = *s;
s++;
if (c == '\'') {
if(fputc('\'', stream) < 0)
return EOF;
} else if(c == '\n' && isset(CSHJUNKIEQUOTES)) {
if(fputc('\\', stream) < 0)
return EOF;
}
if(fputc(c, stream) < 0)
return EOF;
}
if(fputc('\'', stream) < 0)
return EOF;
} else {
/* use Bourne-style quoting, avoiding empty quoted strings */
while(*s) {
if (*s == Meta)
c = *++s ^ 32;
else
c = *s;
s++;
if (c == '\'') {
if(inquote) {
if(fputc('\'', stream) < 0)
return EOF;
inquote=0;
}
if(fputs("\\'", stream) < 0)
return EOF;
} else {
if (!inquote) {
if(fputc('\'', stream) < 0)
return EOF;
inquote=1;
}
if(c == '\n' && isset(CSHJUNKIEQUOTES)) {
if(fputc('\\', stream) < 0)
return EOF;
}
if(fputc(c, stream) < 0)
return EOF;
}
}
if (inquote) {
if(fputc('\'', stream) < 0)
return EOF;
}
}
return 0;
}
/* Identify an option name */
/**/
int
optlookup(char *s)
{
char *t;
int optno, starts_no;
t = s = dupstring(s);
/* exorcise underscores, and change to lowercase */
while (*t)
if (*t == '_')
chuck(t);
else {
*t = tulower(*t);
t++;
}
starts_no = (s[0] == 'n' && s[1] == 'o');
/* search for name in table */
for (optno = OPT_SIZE; --optno; ) {
if (!strcmp(optns[optno].name, s))
return optno;
if (starts_no && !strcmp(optns[optno].name, s+2))
return -optno;
}
return 0;
}
/* Identify an option letter */
/**/
int
optlookupc(char c)
{
int optno;
if(!c || (c & OPT_REV))
return 0;
/* search for letter in the table */
for (optno = OPT_SIZE; --optno; ) {
char id = optid(optns[optno]);
if (id == c)
return optno;
if (id == (char)(c | OPT_REV))
return -optno;
}
return 0;
}
/* Set or unset an option. The option number may be negative, indicating *
* that the sense is reversed from the usual meaning of the option. */
/**/
int
dosetopt(int optno, int value, int force)
{
if(!optno)
return -1;
if(optno < 0) {
optno = -optno;
value = !value;
}
if(!force && (optno == INTERACTIVE || optno == SHINSTDIN ||
optno == SINGLECOMMAND)) {
/* it is not permitted to change the value of these options */
return -1;
} else if(!force && optno == USEZLE && value) {
/* we require a terminal in order to use ZLE */
if(!interact || SHTTY == -1 || !shout)
return -1;
} else if(optno == PRIVILEGED && !value) {
/* unsetting PRIVILEGED causes the shell to make itself unprivileged */
#ifdef HAVE_SETUID
setuid(getuid());
setgid(getgid());
#endif /* HAVE_SETUID */
}
opts[optno] = value;
if (optno == BANGHIST || optno == SHINSTDIN)
inittyptab();
return 0;
}
/**/
char *
dupstrpfx(const char *s, int len)
{
char *r = ncalloc(len + 1);
memcpy(r, s, len);
r[len] = '\0';
return r;
}
/**/
char *
ztrduppfx(const char *s, int len)
{
char *r = zalloc(len + 1);
memcpy(r, s, len);
r[len] = '\0';
return r;
}
#ifdef DEBUG
/**/
void
dputs(char *message)
{
fprintf(stderr, "%s\n", message);
fflush(stderr);
}
#endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.