home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
misc
/
volume18
/
mush
/
part10
< prev
next >
Wrap
Internet Message Format
|
1991-04-22
|
51KB
From: argv@zipcode.com (Dan Heller)
Newsgroups: comp.sources.misc
Subject: v18i067: mush - Mail User's Shell, Part10/22
Message-ID: <1991Apr22.000314.18736@sparky.IMD.Sterling.COM>
Date: 22 Apr 91 00:03:14 GMT
Approved: kent@sparky.imd.sterling.com
X-Checksum-Snefru: d75d5d29 b8aec007 9f0e321b 2ecdbf05
Submitted-by: Dan Heller <argv@zipcode.com>
Posting-number: Volume 18, Issue 67
Archive-name: mush/part10
Supersedes: mush: Volume 12, Issue 28-47
#!/bin/sh
# do not concatenate these parts, unpack them in order with /bin/sh
# file hdrs.c continued
#
if test ! -r _shar_seq_.tmp; then
echo 'Please unpack part 1 first!'
exit 1
fi
(read Scheck
if test "$Scheck" != 10; then
echo Please unpack part "$Scheck" next!
exit 1
else
exit 0
fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
echo 'x - still skipping hdrs.c'
else
echo 'x - continuing file hdrs.c'
sed 's/^X//' << 'SHAR_EOF' >> 'hdrs.c' &&
X *b++ = ',', *b++ = ' ';
X p[lim] = '\0'; /* prevent overflow */
X (void) strcpy(b, p);
X }
X }
X fix_up_addr(buf);
X /* p2 used to save boolean value of $metoo */
X if (!(p2 = do_set(set_options, "metoo"))) {
X /* Save the original name/addr in case it is the only one */
X (void) get_name_n_addr(buf, name, addr);
X take_me_off(buf);
X }
X for (p = buf; *p == ',' || isspace(*p); p++)
X ;
X if (!*p)
X if (p2) /* take_me_off() was not done */
X (void) strcpy(buf, login);
X else
X (void) sprintf(buf, "%s <%s>", name, addr);
X return buf;
}
X
char *
subject_to(n, buf)
register char *buf;
{
X register char *p;
X buf[0] = 0; /* make sure it's already null terminated */
X if (!(p = header_field(n, "subject")))
X return NULL;
X if (lcase_strncmp(p, "Re:", 3))
X (void) strcpy(buf, "Re: ");
X return strcat(buf, p);
}
X
char *
cc_to(n, buf)
register char *buf;
{
X register char *p;
X buf[0] = 0; /* make sure it's already null terminated */
X if (!(p = header_field(n, "cc")))
X return NULL;
X fix_up_addr(p);
X if (!do_set(set_options, "metoo"))
X take_me_off(p);
X return strcpy(buf, p);
}
SHAR_EOF
echo 'File hdrs.c is complete' &&
chmod 0644 hdrs.c ||
echo 'restore of hdrs.c failed'
Wc_c="`wc -c < 'hdrs.c'`"
test 22622 -eq "$Wc_c" ||
echo 'hdrs.c: original size 22622, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= init.c ==============
if test -f 'init.c' -a X"$1" != X"-c"; then
echo 'x - skipping init.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting init.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'init.c' &&
/* init.c (c) copyright 1986 (Dan Heller) */
X
/* init.c -- functions and whatnot that initialize everything */
#include "mush.h"
#include <pwd.h>
X
#ifdef BSD
#include <netdb.h>
#endif /* BSD */
X
#if defined(SYSV) && !defined(HPUX)
#include <sys/utsname.h>
#endif /* SYSV && !HPUX */
X
void
init()
{
X char *home, *realname, *argv[4];
X extern char *getlogin();
X char buf[MAXPATHLEN];
#if defined(SYSV) && !defined(HPUX)
X extern struct passwd *getpwuid(); /* sys-v forgot this in pwd.h! */
X struct utsname ourhost;
#else
X char ourhost[128];
#endif /* SYSV && !HPUX */
X register char *p;
X struct passwd *entry;
X int cnt;
#if defined(BSD) || defined(HPUX)
X struct hostent *hp;
#endif /* BSD || HPUX */
X
X home = getenv("HOME");
X if (realname = getenv("NAME")) {
X (void) strcpy(buf, realname);
X }
X argv[1] = "=";
X argv[3] = NULL;
X
X if (!(entry = getpwuid(getuid())))
X if (p = getlogin())
X strdup(login, p);
X else {
X login = "unknown";
X print("I don't know you, but that's ok.\n");
X }
X else {
X strdup(login, entry->pw_name);
X if (!home || !*home)
X home = entry->pw_dir;
X if (!realname && (realname = entry->pw_gecos)) {
X if (p = index(realname, ','))
X *p = 0;
X for (p = buf; *realname; realname++)
X if (*realname == '&')
X *p++ = upper(*login), p += Strcpy(p, login+1);
X else
X *p++ = *realname;
X *p = 0;
X }
X endpwent();
X }
X if (!home || !*home || Access(home, W_OK)) {
X if (home && *home)
X error(home);
X else
X print("No home!? ");
X print_more("Using \"%s\" as home.\n", ALTERNATE_HOME);
X } else {
X argv[0] = "home";
X argv[2] = home;
X (void) add_option(&set_options, argv);
X }
X if (realname && *realname) {
X /* realname has already been copied to buf */
X argv[0] = "realname";
X argv[2] = buf;
X (void) add_option(&set_options, argv);
X }
X crt = 24;
X screen = 18;
X wrapcolumn = 0; /* Default is no wrap */
X escape = DEF_ESCAPE;
X prompt = DEF_PROMPT;
X
#if defined(BSD) || defined(HPUX)
X (void) gethostname(ourhost, sizeof ourhost);
X if (!(hp = gethostbyname(ourhost))) {
X if (ourname = (char **)calloc((unsigned)2, sizeof (char *)))
X strdup(ourname[0], ourhost);
X } else {
X int n = 0;
X cnt = 2; /* 1 for ourhost and 1 for NULL terminator */
X for (p = hp->h_name; p && *p; p = hp->h_aliases[n++])
X if (strcmp(ourhost, p)) /* if host name is different */
X cnt++;
X if (ourname = (char **)calloc((unsigned)cnt, sizeof (char *))) {
X ourname[--cnt] = NULL;
X for (p = hp->h_name; p && *p && n >= 0; p = hp->h_aliases[--n])
X if (strcmp(ourhost, p)) /* if host name is different */
X ourname[--cnt] = savestr(p);
X strdup(ourname[0], ourhost); /* cnt better be 0! */
X }
X }
#endif /* BSD || HPUX */
#if defined(SYSV) && !defined(HPUX)
X if (ourname = (char **)calloc((unsigned)2, sizeof (char *))) {
X if ((uname (&ourhost) >= 0) && (*ourhost.nodename))
X ourname[0] = savestr(ourhost.nodename);
X else {
X /* Try to use uuname -l to get host's name if uname didn't work */
X char buff[50];
X char *p;
X FILE *F;
X
X if (F = popen("exec uuname -l", "r")) {
X if ((fgets(buff, sizeof buff, F) == buff) &&
X (p = strchr(buff, '\n'))) {
X *p = '\0'; /* eliminate newline */
X ourname[0] = savestr (buff);
X }
X (void)pclose(F);
X }
X }
X }
#endif /* SYSV && !HPUX */
X if (ourname && ourname[0]) {
X for (p = buf, cnt = 0; ourname[cnt]; cnt++) {
X if (cnt)
X *p++ = ' ';
X p += Strcpy(p, ourname[cnt]);
X }
X argv[0] = "hostname";
X argv[2] = buf;
X (void) add_option(&set_options, argv);
X }
X
X init_bindings();
}
X
/*
X * Source a file, or just the default file. Since sourcing files
X * means reading possible aliases, don't expand the ! as history
X * by setting the IGN_BANG flag. Since a command in the sourced file
X * may call source on another file, this routine may be called from
X * within itself. Continue to ignore ! chars by setting save_bang (local).
X *
X * Try opening the file passed to us. If not given, check for the correct
X * .rc file which is found in the user's home dir.
X *
X * return -1 for filesystem errors, -2 for attempting to read a directory.
X */
source(argc, argv)
char **argv;
{
X register char *p;
X FILE *fp;
X char file[MAXPATHLEN];
X u_long save_bang = ison(glob_flags, IGN_BANG);
X int line_no = 0;
X
X if (argc && *++argv && !strcmp(*argv, "-?"))
X return help(0, "source", cmd_help);
X if (argc && *argv)
X (void) strcpy(file, *argv);
X else if ((p = getenv("MUSHRC")) && *p || (p = getenv("MAILRC")) && *p)
X (void) strcpy(file, p);
X else {
X char *home = do_set(set_options, "home");
X if (!home || !*home)
X home = ALTERNATE_HOME;
X if (Access(sprintf(file, "%s/%s", home, MAILRC), R_OK)
X && Access(sprintf(file, "%s/%s", home, ALTERNATE_RC), R_OK))
X if (argc || argv)
X (void) strcpy(file, DEFAULT_RC);
X else
X return -1;
X }
X
X argc = 0; /* don't ignore ENOENT */
X p = getpath(file, &argc);
X /* Try the ALT_DEF_RC if DEFAULT_RC fails */
X if (argc && !strcmp(file, DEFAULT_RC)) {
X argc = 0; /* don't ignore ENOENT */
X (void) strcpy(file, ALT_DEF_RC);
X p = getpath(file, &argc);
X }
X if (argc) {
X /* Don't print error messages for missing default files */
X if (strcmp(file, ALT_DEF_RC))
X if (argc == -1) {
X print("%s: %s\n", file, p);
X return -1;
X } else {
X print("%s is a directory.\n", file);
X return -2;
X }
X return -1;
X }
X if (!(fp = fopen(p, "r"))) {
X if (errno != ENOENT)
X error("Can't open %s", p);
X return -1;
X }
X turnon(glob_flags, IGN_BANG); /* ignore ! when reading record files */
X (void) strcpy(file, p);
X (void) src_parse(file, fp, 0, 0, &line_no);
X /* if we entered the routine ignoring !, leave it that way. */
X if (!save_bang)
X turnoff(glob_flags, IGN_BANG);
X /* Sourcing might change things, so abort pipes/macros */
X return 0 - (in_pipe() || in_macro());
}
X
/*
X * Do the actual file parsing for source(). The first argument should
X * be the name of the file referenced by the second argument. The third
X * argument is used for handling nested if_else_endif expressions. The
X * fourth argument is used to keep track of the recursion depth, and the
X * last argument keeps track of the line number in the current file.
X *
X * This function calls itself recursively. It also calls do_command(),
X * which may in turn call source() recursively.
X *
X * If-then-else nesting algorithm:
X * On any "if" (whether parsing or not), increment if_else
X * On true "if" when parsing, evaluate by recursion
X * On false "if" when parsing, set find_else equal to if_else
X * On any "if" when not parsing, set find_endif equal to if_else
X * On "else", invert parsing only when find_else equals if_else
X * When "if" was false and there is nesting, recur for "else"
X * Skip nested "if...endif" when find_else or find_endif true
X * On "endif" or when recursion returns, decrement if_else
X * On "endif", test both find_endif and find_else against if_else:
X * when either matches, reset that one;
X * when the lesser (less nested) matches, resume parsing
X * On "endif", when if_else hits 0, continue (depth 0) or return
X */
src_parse(file, fp, if_else, depth, line_no)
char *file;
FILE *fp;
int if_else, depth, *line_no;
{
X register char *p, *p2, **newargv;
X static int exited;
X int parsing = 1, cont_line = 0;
X int find_else = 0, find_endif = 0;
X char line[BUFSIZ];
X int argc;
X
X exited = 0;
X
X while (p = fgets(&line[cont_line], BUFSIZ - cont_line, fp)) {
X (*line_no)++;
X if (*(p2 = no_newln(p)) == '\\') {
X *p2++ = ' ';
X cont_line = p2 - line;
X continue;
X } else
X cont_line = 0;
X /* don't consider comments (#) in lines. check if # is within quotes */
X if (p = any(line, "\"'#\\")) {
X register int balanced = 1;
X do {
X if (*p == '\\' && p[1])
X p = any(p+2, "\"'#\\");
X else if (*p != '#') {
X /* first find matching quote */
X register char *quote = index(p+1, *p);
X if (!quote) {
X print("%s: line %d: unbalanced %c.\n",
X file, *line_no, *p);
X balanced = 0;
X } else
X p = any(quote+1, "\"'#\\");
X }
X } while (p && *p != '#' && balanced);
X if (!balanced)
X continue;
X if (p && *p == '#')
X *p = 0; /* found a Comment: null terminate line at comment */
X }
X if (!*line || !parsing && !(newargv = mk_argv(line, &argc, 0))
X || parsing && !(newargv = make_command(line, TRPL_NULL, &argc))) {
X if (!strncmp(line, "if", 2))
X find_else = ++if_else, parsing = FALSE;
X continue;
X }
X if (!strcmp(newargv[0], "endif")) {
X if (!if_else)
X print("%s: line %d: endif with no \"if\".\n", file, *line_no);
X else {
X /* If looking for an else or endif, reset parsing */
X if (find_endif && find_endif == if_else) {
X if (find_endif <= find_else || !find_else)
X parsing = 1, find_else = 0;
X find_endif = 0;
X }
X /* Note: find_else never < find_endif */
X if (find_else && find_else == if_else)
X parsing = !parsing, find_else = 0;
X /* Decrement if_else and check depth */
X if (--if_else == 0)
X /* Resume parsing if at the top */
X if (depth == 0)
X parsing = 1;
X /* Return if not at the top */
X else
X return 1;
X }
X goto bad;
X } else if (!strcmp(newargv[0], "else")) {
X if (!if_else)
X print("%s: line %d: if-less \"else\".\n", file, *line_no);
X /* If inside an else, ignore nested else;
X * otherwise, recur when if_else > 1 */
X else if (!find_else && !find_endif && !parsing) {
X parsing = src_parse(file, fp, 1, depth + 1, line_no);
X --if_else;
X } else if (find_else == if_else || if_else == 1) {
X find_else = 0;
X parsing = !parsing;
X if (!parsing)
X find_endif = if_else;
X }
X goto bad;
X } else if (!strcmp(newargv[0], "if")) {
X /* if statements are of the form:
X * if expr
X * if !expr or if ! expr
X * if expr == expr or if expr != expr
X */
X int equals = TRUE, pattern = FALSE;
X register char *lhs = newargv[1], *rhs = NULL;
X
X if_else++;
X /* If parsing, set parsing to 0 until
X * evaluating the "if" proves otherwise.
X * If not parsing, skip to the "endif".
X */
X if (parsing)
X parsing = 0;
X else {
X if (!find_endif)
X find_endif = if_else;
X goto bad;
X }
X if (!lhs || !*lhs) {
X print("%s: line %d: if what?\n", file, *line_no);
X goto bad;
X }
X /* "lhs" is the left hand side of the equation
X * In this instance, we're doing case 2 above (check for negation).
X */
X if (*lhs == '!') {
X if (!*++lhs && !(lhs = newargv[2])) {
X print("%s: line %d: syntax error: \"if ! <what?>\"\n",
X file, *line_no);
X goto bad;
X }
X equals = FALSE;
X }
X if (*lhs == '-' && (lhs[1] == 'e' || lhs[1] == 'z') && !lhs[2]) {
X char *path;
X int n = 1; /* ignore ENOENT, I'll handle it here */
X struct stat statb;
X
X /* check for existence or zero-length folders/files */
X if (argc > 3 + (!equals)) {
X print("%s: line %d: if %s \"filename\"\n",
X file, *line_no, lhs);
X goto bad;
X }
X path = getpath(newargv[argc-1], &n);
X parsing = !equals ^ (n == -1 || n == 1 && lhs[1] == 'e' ||
X !stat(path, &statb) && (lhs[1] == 'e' || !statb.st_size));
X } else {
X if (equals && argc > 2) {
X if (argc != 4) {
X print("%s: %d: argument count error: %d args.\n",
X file, *line_no, argc);
X goto bad;
X }
X /* now check newargv[2] for == or != or =~ or !~ */
X if (!strcmp(newargv[2], "!=") ||
X (pattern = !strcmp(newargv[2], "!~")))
X equals = !equals;
X else if (!strcmp(newargv[2], "=~"))
X pattern = TRUE;
X else if (strcmp(newargv[2], "==")) {
X print("%s: %d: use `==' or `!=' only.\n",
X file, *line_no);
X goto bad;
X }
X rhs = newargv[3];
X }
X if (rhs) {
X /* Some fun tricks with booleans here.
X * Extra ! ops make sure all == are on 0 or 1;
X * aside from that, we want (glob == equals)
X * or (!strcmp == equals). Make sense?
X */
X if (pattern && !glob(lhs,rhs) == !equals)
X parsing = 1;
X else if (!pattern && !strcmp(lhs, rhs) == !!equals)
X parsing = 1;
X } else if (isdigit(*lhs))
X parsing = !!(atoi(lhs) ? equals : !equals);
X else if (!strcmp(lhs, "redirect") && (!isatty(0) != !equals)
X /* (ison(glob_flags, REDIRECT) && equals ||
X isoff(glob_flags, REDIRECT) && !equals) */
X || !strcmp(lhs, "is_shell") && (!is_shell == !equals)
X || !strcmp(lhs, "is_sending") &&
X (ison(glob_flags, IS_SENDING) && equals ||
X isoff(glob_flags, IS_SENDING) && !equals)
X || !strcmp(lhs, "hdrs_only") &&
X (hdrs_only && equals || !hdrs_only && !equals)
X || !strcmp(lhs, "istool") &&
X (istool && equals || !istool && !equals)
X || !strcmp(lhs, "iscurses") &&
X ((iscurses || ison(glob_flags, PRE_CURSES)) && equals
X || (isoff(glob_flags, PRE_CURSES) &&
X !iscurses && !equals)))
X parsing = 1;
X }
X if (parsing) {
X parsing = src_parse(file, fp, 1, depth + 1, line_no);
X --if_else;
X }
X else
X find_else = if_else; /* Look for a matching else */
bad:
X free_vec(newargv);
X continue;
X }
X if (parsing && argc > 0)
X if (!strcmp(newargv[0], "exit")) {
X if_else = find_else = find_endif = 0;
X exited = 1;
X break;
X } else {
X (void) do_command(argc, newargv, msg_list);
X exited = 0;
X }
X else
X free_vec(newargv);
X }
X if (if_else && !exited)
X print("%s: missing endif\n", file);
X if (depth == 0)
X (void) fclose(fp);
X else
X (void) fseek(fp, 0L, 2); /* Skip ahead to the end */
X return 0;
}
SHAR_EOF
chmod 0644 init.c ||
echo 'restore of init.c failed'
Wc_c="`wc -c < 'init.c'`"
test 13558 -eq "$Wc_c" ||
echo 'init.c: original size 13558, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= lock.c ==============
if test -f 'lock.c' -a X"$1" != X"-c"; then
echo 'x - skipping lock.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting lock.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'lock.c' &&
/*
X * lock.c -- deal with file locking on various architectures and UNIXs.
X * dot_lock() creates a file with the same name as the parameter passed
X * with the appendage ".lock" -- this is to be compatible with certain
X * systems that don't use flock or lockf or whatever they have available
X * that they don't use.
X */
X
#ifdef USG
#include <unistd.h>
#endif /* USG */
#include "mush.h"
#if defined(SYSV) && !defined(USG)
#include <sys/locking.h>
#endif /* SYSV && !USG */
X
#ifdef DOT_LOCK
extern int sgid;
#ifdef BSD
extern int rgid;
#endif /* BSD */
X
dot_lock(filename)
char *filename;
{
X char buf[MAXPATHLEN];
X int lockfd, cnt = 0;
X SIGRET (*oldint)(), (*oldquit)();
X
#ifdef SYSV
X /* Only the spoolfile needs to be dot_locked -- other files are
X * handled by lock_fopen, below. To avoid collisions with 14-char
X * file name limits, we allow dot_locking ONLY of the spoolfile.
X */
X if (strcmp(spoolfile, filename) != 0)
X return 0;
#endif
#ifdef BSD
X setregid(rgid, sgid);
#else /* BSD */
X setgid(sgid);
#endif /* BSD */
#ifdef M_XENIX
X (void) sprintf(buf, "/tmp/%.10s.mlk", login);
#else /* M_XENIX */
X (void) sprintf(buf, "%s.lock", filename);
#endif /* M_XENIX */
X on_intr();
X while ((lockfd = open(buf, O_CREAT|O_WRONLY|O_EXCL, 0444)) == -1) {
X if (errno != EEXIST) {
X error("unable to lock %s", filename);
X break;
X }
X if (cnt++ == 0)
X print("%s already locked, waiting", filename);
X else
X print_more(".");
X sleep(1);
X if (ison(glob_flags, WAS_INTR)) {
X print_more("\nAborted.\n");
X break;
X }
X }
X off_intr();
X if (lockfd != -1) {
X if (cnt)
X print("done.\n");
X (void) close(lockfd);
X }
#ifdef BSD
X setregid(sgid, rgid);
#else
X setgid(getgid());
#endif /* BSD */
X return lockfd == -1? -1 : 0;
}
#endif /* DOT_LOCK */
X
#ifdef SYSV
X
/*
X * Define some BSD names for the SYSV world
X */
#ifdef USG
#define LOCK_SH F_RDLCK
#define LOCK_EX F_WRLCK
#define LOCK_UN F_UNLCK
#else /* USG */
#define LOCK_SH LK_LOCK
#define LOCK_EX LK_LOCK
#define LOCK_UN LK_UNLCK
#endif /* USG */
#define LOCK_NB 0 /* Always non-blocking in this case */
X
#ifdef EWOULDBLOCK
#undef EWOULDBLOCK
#endif /* EWOULDBLOCK */
#ifdef M_UNIX
#define EWOULDBLOCK EACCESS /* SCO bug that may eventually be fixed */
#else /* !M_UNIX */
#define EWOULDBLOCK EAGAIN
#endif /* M_UNIX */
X
#ifndef F_SETLKW
#define F_SETLKW F_SETLK
#endif /* F_SETLKW */
X
flock(fd, op)
int fd, op;
{
#ifndef USG
X (void) locking(fd, op, 0); /* old xenix (sys III) */
X return 0;
#else
X struct flock l;
X
X l.l_len = 0L;
X l.l_start = 0L;
X l.l_whence = 1;
X l.l_type = op;
X
X return fcntl(fd, F_SETLKW, &l);
#endif /* USG */
}
X
#endif /* SYSV */
X
static struct options *exclude_list;
X
/* Quick'n'dirty test to avoid opening the same file multiple times.
X * Fails if we aren't passed full paths or if the file is known by
X * more than one name, but you can't have everything.
X */
static FILE *
exclusive_fopen(filename, mode)
char *filename, *mode;
{
X struct options *tmp;
X FILE *fp;
X
X for (tmp = exclude_list; tmp; tmp = tmp->next)
X if (strcmp(tmp->option, filename) == 0) {
X errno = EWOULDBLOCK;
X return NULL_FILE;
X }
X if (!(fp = mask_fopen(filename, mode)))
X return NULL_FILE;
X if (tmp = (struct options *)malloc(sizeof(struct options))) {
X tmp->option = savestr(filename);
X tmp->value = (char *)fp;
X /*
X * NOTE: The LCKDFLDIR code below depends on this stackwise
X * insertion to be able to close/reopen the file pointer.
X * These routines therefore cannot cleanly be used outside
X * of lock_fopen() and close_lock(), which handle LCKDFLDIR.
X */
X tmp->next = exclude_list;
X exclude_list = tmp;
X return fp;
X } else
X (void) fclose(fp);
X return NULL_FILE;
}
X
static int
exclusive_fclose(fileptr)
FILE *fileptr;
{
X struct options *tmp1, *tmp2;
X int n = 0;
X
X for (tmp1 = tmp2 = exclude_list; tmp1; tmp2 = tmp1, tmp1 = tmp1->next)
X if ((FILE *)(tmp1->value) == fileptr) {
X if (tmp1 == tmp2)
X exclude_list = tmp1->next;
X else
X tmp2->next = tmp1->next;
X xfree(tmp1->option);
#ifndef LCKDFLDIR
X /* LCKDFLDIR needs lk_fclose(), so let caller do it */
X n = fclose(fileptr);
#endif /* !LCKDFLDIR */
X xfree(tmp1);
X break;
X }
X return n;
}
X
FILE *
lock_fopen(filename, mode)
char *filename;
char *mode;
{
X FILE *mail_fp = NULL_FILE;
X struct options exclude;
X int fd, lk;
X int cnt = 0;
X SIGRET (*oldint)(), (*oldquit)();
#ifdef LCKDFLDIR
X extern FILE *lk_fopen();
#endif /* !LCKDFLDIR */
X
X if (debug && do_set(set_options, "deadlock")) {
X (void) un_set(&set_options, "deadlock");
X return NULL_FILE;
X }
X
#ifdef DOT_LOCK
X if (dot_lock(filename) == 0)
#endif /* DOT_LOCK */
X mail_fp = exclusive_fopen(filename, mode);
X if (!mail_fp)
X return NULL_FILE;
X fd = fileno(mail_fp);
X
X if (mode[0] != 'r' || mode[1] == '+')
X lk = LOCK_EX | LOCK_NB;
X else
X lk = LOCK_SH | LOCK_NB;
X
X on_intr();
#ifdef LCKDFLDIR
X (void) fclose(mail_fp);
X while (isoff(glob_flags, WAS_INTR))
X if (mail_fp = lk_fopen(filename, mode, NULL, NULL, 0)) {
X /* See note in exclusive_fopen() above */
X exclude_list->value = (char *)mail_fp;
X break;
X } else /* uses the open brace below the #endif LCKDFLDIR */
#else /* !LCKDFLDIR */
X while (isoff(glob_flags, WAS_INTR) && flock(fd, lk))
#endif /* LCKDFLDIR */
X {
#ifdef LCKDFLDIR
X if (Access(filename, any(mode, "aw+") ? W_OK : R_OK) == 0)
#else /* !LCKDFLDIR */
X if (errno == EWOULDBLOCK)
#endif /* LCKDFLDIR */
X {
X if (isoff(glob_flags, REDIRECT))
X if (!cnt++)
X print("\nwaiting to lock");
X else
X print(".");
X } else {
X error("Unable to lock \"%s\"", filename);
X exclusive_fclose(mail_fp);
X off_intr();
X return NULL_FILE;
X }
X (void) fflush(stdout);
X sleep(1);
X }
X if (cnt)
X print("\n");
X cnt = (ison(glob_flags, WAS_INTR) != 0);
X off_intr();
X if (cnt) {
X exclusive_fclose(mail_fp);
X return NULL_FILE;
X }
X return mail_fp;
}
X
/*ARGSUSED*/
close_lock(filename, fp)
char *filename;
FILE *fp;
#ifdef LCKDFLDIR
{
X (void) exclusive_fclose(fp); /* Only removes the list elem */
X return lk_fclose(fp, filename, NULL, NULL);
}
#else /* !LCKDFLDIR */
{
#ifdef DOT_LOCK
X char buf[MAXPATHLEN];
#endif /* DOT_LOCK */
X
X fflush(fp);
#ifdef DOT_LOCK
#ifdef BSD
X setregid(rgid, sgid);
#else
X setgid(sgid);
#endif /* BSD */
#ifdef SYSV
X if (strcmp(spoolfile, filename) == 0)
#endif /* SYSV */
#ifdef M_XENIX
X (void) unlink(sprintf(buf, "/tmp/%.10s.mlk", login));
#else /* M_XENIX */
X {
X /* If the file was locked through open_file(), we may not have
X * a complete pathname to work with here. Expand it and test
X * whether we need to unlink at all. This should really be
X * handled by having open_file() return the name it used, but
X * that breaks too many other things at the moment.
X */
X int isdir = 0;
X char *p = getpath(sprintf(buf, "%s.lock", filename), &isdir);
X if (isdir == 0)
X (void) unlink(p);
X }
#endif /* M_XENIX */
#ifdef BSD
X setregid(sgid, rgid);
#else
X setgid(getgid());
#endif /* BSD */
#endif /* DOT_LOCK */
X
X (void) flock(fileno(fp), LOCK_UN);
X return exclusive_fclose(fp);
}
#endif /* LCKDFLDIR */
SHAR_EOF
chmod 0644 lock.c ||
echo 'restore of lock.c failed'
Wc_c="`wc -c < 'lock.c'`"
test 7206 -eq "$Wc_c" ||
echo 'lock.c: original size 7206, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= loop.c ==============
if test -f 'loop.c' -a X"$1" != X"-c"; then
echo 'x - skipping loop.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting loop.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'loop.c' &&
/* loop.c (c) copyright 1986 (Dan Heller) */
X
/*
X * Here is where the main loop for text mode exists. Also, all the
X * history is kept here and all the command parsing and execution
X * and alias expansion in or out of text/graphics mode is done here.
X */
X
#include "mush.h"
#include "version.h"
X
#ifdef BSD
#include <sys/wait.h>
#else
#ifndef SYSV
#include <wait.h>
#endif /* SYSV */
#endif /* BSD */
X
#define ever (;;)
#define MAXARGS 100
#define isdelimeter(c) (index(" \t;|", c))
X
char *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
char *calloc();
X
struct history {
X int histno;
X char **argv;
X struct history *prev;
X struct history *next;
};
static struct history *hist_head, *hist_tail;
#define malloc(n) (struct history *)calloc((unsigned)1,(unsigned)(n))
#define NULL_HIST (struct history *)0
X
static char *last_aliased;
static int hist_size, print_only;
X
do_loop()
{
X register char *p, **argv;
X char **last_argv = DUBL_NULL, line[256];
X int argc, c = (iscurses - 1);
#ifdef CURSES
X int save_echo_flg = FALSE;
#endif /* CURSES */
X
X /* catch the right signals -- see main.c for other signal catching */
X (void) signal(SIGINT, catch);
X (void) signal(SIGQUIT, catch);
X (void) signal(SIGHUP, catch);
X (void) signal(SIGTERM, catch);
X (void) signal(SIGCHLD,
#ifndef SYSV
X sigchldcatcher
#else /* SYSV */
X SIG_DFL
#endif /* SYSV */
X );
X
X turnoff(glob_flags, IGN_SIGS);
X if (hist_size == 0) /* if user didn't set history in .rc file */
X hist_size = 1;
X
X for ever {
X if (setjmp(jmpbuf)) {
X Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
#ifdef CURSES
X if (c > 0) { /* don't pass last command back to curses_command() */
X iscurses = TRUE;
X c = hit_return();
X }
#endif /* CURSES */
X }
#ifdef CURSES
X if (iscurses || c > -1) {
X /* if !iscurses, we know that we returned from a curses-based
X * call and we really ARE still in curses. Reset tty modes!
X */
X if (ison(glob_flags, ECHO_FLAG)) {
X turnoff(glob_flags, ECHO_FLAG);
X echo_off();
X save_echo_flg = TRUE;
X }
X if (!iscurses) {
X iscurses = TRUE;
X c = hit_return();
X }
X if (c < 0)
X c = 0;
X if ((c = curses_command(c)) == -1 && save_echo_flg) {
X echo_on();
X turnon(glob_flags, ECHO_FLAG);
X save_echo_flg = FALSE;
X }
X continue;
X }
#endif /* CURSES */
X clear_msg_list(msg_list);
X (void) check_new_mail();
X
X /* print a prompt according to printf like format:
X * (current message, deleted, unread, etc) are found in mail_status.
X */
X mail_status(1);
X if (Getstr(line, sizeof(line), 0) > -1)
X p = line;
X else {
X if (isatty(0) && (p = do_set(set_options, "ignoreeof"))) {
X if (!*p)
X continue;
X else
X p = strcpy(line, p); /* so processing won't destroy var */
X } else {
X putchar('\n');
X (void) mush_quit(0, DUBL_NULL);
X continue; /* quit may return if new mail arrives */
X }
X }
X
X skipspaces(0);
X if (!*p && !(p = do_set(set_options, "newline"))) {
X (void) readmsg(0, DUBL_NULL, msg_list);
X continue;
X }
X if (!*p) /* if newline is set, but no value, then continue */
X continue;
X
X /* upon error, argc = -1 -- still save in history so user can
X * modify syntax error. if !argv, error is too severe. We pass
X * the last command typed in last_argv for history reference, and
X * get back the current command _as typed_ (unexpanded by aliases
X * or history) in last_argv.
X */
X if (!(argv = make_command(p, &last_argv, &argc)))
X continue;
X /* now save the old argv in a newly created history structure */
X (void) add_history(0, last_argv); /* argc is currently ignored */
X
X if (print_only) {
X print_only = 0;
X free_vec(argv);
X } else if (argc > -1)
X (void) do_command(argc, argv, msg_list);
X }
}
X
/* Add a command to the history list
X */
/*ARGSUSED*/
add_history(un_used, argv)
char **argv;
{
X struct history *new;
X
X if (!(new = malloc(sizeof (struct history))))
X error("can't increment history");
X else {
X new->histno = ++hist_no;
X new->argv = argv; /* this is the command _as typed_ */
X new->next = NULL_HIST;
X new->prev = hist_head;
X /* if first command, the tail of the list is "new" because
X * nothing is in the list. If not the first command, the
X * head of the list's "next" pointer points to the new command.
X */
X if (hist_head)
X hist_head->next = new;
X else
X hist_tail = new;
X hist_head = new;
X }
X /*
X * truncate the history list to the size of the history.
X * Free the outdated command (argv) and move the tail closer to front.
X * use a while loop in case the last command reset histsize to "small"
X */
X while (hist_head->histno - hist_tail->histno >= hist_size) {
X hist_tail = hist_tail->next;
X free_vec(hist_tail->prev->argv);
X xfree((char *) (hist_tail->prev));
X hist_tail->prev = NULL_HIST;
X }
}
X
/* make a command from "buf".
X * first, expand history references. make an argv from that and save
X * in last_argv (to be passed back and stored in history). After that,
X * THEN expand aliases. return that argv to be executed as a command.
X */
char **
make_command(start, last_argv, argc)
register char *start, ***last_argv;
int *argc;
{
X register char *p, **tmp;
X char buf[BUFSIZ];
X
X if (!last_argv)
X tmp = DUBL_NULL;
X else
X tmp = *last_argv;
X /* first expand history -- (here's where argc gets set)
X * pass the buffer, the history list to reference if \!* (or whatever)
X * result in static buffer (pointed to by p) -- even if history parsing is
X * ignored, do this to remove \'s behind !'s and verifying matching quotes
X */
X if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > sizeof buf)
X return DUBL_NULL;
X /* if history was referenced in the command, echo new command */
X if (*argc)
X puts(buf);
X
X /* argc may == -1; ignore this error for now but catch it later */
X if (!(tmp = mk_argv(buf, argc, 0)))
X return DUBL_NULL;
X
X /* save this as the command typed */
X if (last_argv)
X *last_argv = tmp;
X
X /* expand all aliases (recursively)
X * pass _this_ command (as typed and without aliases) to let aliases
X * with "!*" be able to reference the command line just typed.
X */
X if (alias_stuff(buf, *argc, tmp) == -1)
X return DUBL_NULL;
X
X if (!last_argv)
X free_vec(tmp);
X
X /* with everything expanded, build final argv from new buffer
X * Note that backslashes and quotes still exist. Those are removed
X * because argument final is 1.
X */
X tmp = mk_argv(buf, argc, 1);
X return tmp;
}
X
/* Return values from commands, see check_internal() */
static int last_status; /* Changes after every command */
static char last_output[MAXMSGS]; /* Changes after SUCCESSFUL command */
X
/*
X * do the command specified by the argument vector, argv.
X * First check to see if argc < 0. If so, someone called this
X * command and they should not have! make_command() will return
X * an argv but it will set argc to -1 if there's a syntax error.
X */
do_command(argc, argv, list)
char **argv, list[];
{
X register char *p;
X char **tmp = argv, *next_cmd = NULL;
X int i, status = 0;
X long do_pipe = ison(glob_flags, DO_PIPE);
X
X if (argc <= 0) {
X turnoff(glob_flags, DO_PIPE);
X return -1;
X }
X
X clear_msg_list(list);
X
X for (i = 0; do_pipe >= 0 && argc; argc--) {
X p = argv[i];
X /* mk_argv inserts a boolean in argv[i][2] for separators */
X if ((!strcmp(p, "|") || !strcmp(p, ";")) && p[2]) {
X if (do_pipe = (*p == '|'))
X turnon(glob_flags, DO_PIPE);
X else if (next_cmd = argv[i+1])
X argv[i+1] = NULL, argc--;
X argv[i] = NULL;
X if ((status = exec_argv(i, argv, list)) <= -1)
X mac_flush();
X else
X list_to_str(list, last_output);
X turnon(glob_flags, IGN_SIGS); /* prevent longjmp */
X /* if piping, then don't call next command if this one failed. */
X if (status <= -1 && do_pipe) {
X print("Broken pipe.\n");
X do_pipe = -1, turnoff(glob_flags, DO_PIPE);
X }
X last_status = status;
X /* if command failed and piping, or command worked and not piping */
X if (do_pipe <= 0)
X status = 0, clear_msg_list(list);
X /* else command worked and piping: set is_pipe */
X else if (!status)
X turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
X argv[i] = p;
X argv += (i+1);
X i = 0;
X turnoff(glob_flags, IGN_SIGS);
X } else
X i++;
X }
X if (*argv && do_pipe >= 0) {
X status = exec_argv(i, argv, list);
X turnon(glob_flags, IGN_SIGS);
X if (status < 0) {
X mac_flush();
X } else
X list_to_str(list, last_output);
X last_status = status;
X }
X Debug("freeing: "), print_argv(tmp);
X free_vec(tmp);
X turnoff(glob_flags, DO_PIPE), turnoff(glob_flags, IS_PIPE);
X if (next_cmd) {
X if (tmp = mk_argv(next_cmd, &argc, 1)) {
X turnoff(glob_flags, IGN_SIGS);
X status = do_command(argc, tmp, list);
X turnon(glob_flags, IGN_SIGS);
X } else
X status = argc;
X xfree(next_cmd);
X }
X turnoff(glob_flags, IGN_SIGS);
X return status;
}
X
exec_argv(argc, argv, list)
register char **argv, list[];
{
X register int n;
X
X if (!argv || !*argv || argv[0][0] == '\\' && !argv[0][1]) {
X if (ison(glob_flags, IS_PIPE))
X print("Invalid null command.\n");
X else if (ison(glob_flags, DO_PIPE)) {
X set_msg_bit(list, current_msg);
X return 0;
X }
X return -1;
X } else if (argv[0][0] == '\\') {
X /* Can't change *argv (breaks free_vec),
X * so shift to remove the backslash
X */
X for (n = 0; argv[0][n]; n++)
X argv[0][n] = argv[0][n+1];
X }
X Debug("executing: "), print_argv(argv);
X
X /* if interrupted during execution of a command, return -1 */
X if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
X Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
X return -1;
X }
X
X /* standard commands */
X for (n = 0; cmds[n].command; n++)
X if (!strcmp(argv[0], cmds[n].command))
X return (*cmds[n].func)(argc, argv, list);
X
X /* ucb-Mail compatible commands */
X for (n = 0; ucb_cmds[n].command; n++)
X if (!strcmp(argv[0], ucb_cmds[n].command))
X return (*ucb_cmds[n].func)(argc, argv, list);
X
X /* for hidden, undocumented commands */
X for (n = 0; hidden_cmds[n].command; n++)
X if (!strcmp(argv[0], hidden_cmds[n].command))
X return (*hidden_cmds[n].func)(argc, argv, list);
X
X n = -1; /* default to failure */
X if ((isdigit(**argv) || index("^.*$-`{}", **argv))
X && (n = get_msg_list(argv, list)) != 0) {
X if (n < 0)
X return -1;
X else if (isoff(glob_flags, DO_PIPE))
X for (n = 0; n < msg_cnt; n++)
X if (msg_bit(list, n)) {
X display_msg((current_msg = n), (long)0);
X unset_msg_bit(list, n);
X }
X return 0;
X } else {
X /* get_msg_list will set the current message bit if nothing parsed */
X if (n == 0)
X unset_msg_bit(list, current_msg);
X if (strlen(*argv) == 1 && index("$^.", **argv)) {
X if (!msg_cnt) {
X print("No messages.");
X return -1;
X } else {
X if (**argv != '.')
X current_msg = (**argv == '$') ? msg_cnt-1 : 0;
X set_msg_bit(list, current_msg);
X display_msg(current_msg, (long)0);
X }
X return 0;
X }
X }
X
X if (!istool && do_set(set_options, "unix")) {
X if (ison(glob_flags, IS_PIPE)) {
X return pipe_msg(argc, argv, list);
X } else
X execute(argv); /* try to execute a unix command */
X return -1; /* doesn't affect messages! */
X }
X
X print("%s: command not found.\n", *argv);
X if (!istool)
X print("type '?' for valid commands, or type `help'\n");
X return -1;
}
X
/* recursively look for aliases on a command line. aliases may
X * reference other aliases.
X */
alias_stuff(b, argc, Argv)
register char *b, **Argv;
{
X register char *p, **argv = DUBL_NULL;
X register int n = 0, i = 0, Argc;
X static int loops;
X int dummy;
X
X if (++loops == 20) {
X print("Alias loop.\n");
X return -1;
X }
X for (Argc = 0; Argc < argc; Argc++) {
X register char *h = Argv[n + ++i];
X register char *p2 = "";
X int sep;
X
X /* we've hit a command separator or the end of the line */
X if (h && strcmp(h, ";") && strcmp(h, "|"))
X continue;
X
X /* create a new argv containing this (possible subset) of argv */
X if (!(argv = (char **)calloc((unsigned)(i+1), sizeof (char *))))
X continue;
X sep = n + i;
X while (i--)
X strdup(argv[i], Argv[n+i]);
X
X if ((!last_aliased || strcmp(last_aliased, argv[0]))
X && (p = alias_expand(argv[0]))) {
X /* if history was referenced, ignore the rest of argv
X * else copy all of argv onto the end of the buffer.
X */
X if (!(p2 = hist_expand(p, argv, &dummy)))
X break;
X if (!dummy)
X (void) argv_to_string(p2+strlen(p2), argv+1);
X if (Strcpy(b, p2) > BUFSIZ) {
X print("Not enough buffer space.\n");
X break;
X }
X /* release old argv and build a new one based on new string */
X free_vec(argv);
X if (!(argv = mk_argv(b, &dummy, 0)))
X break;
X if (alias_stuff(b, dummy, argv) == -1)
X break;
X } else
X b = argv_to_string(b, argv);
X xfree(last_aliased), last_aliased = NULL;
X free_vec(argv);
X b += strlen(b);
X if (h) {
X b += strlen(sprintf(b, " %s ", h));
X while (++Argc < argc && (h = Argv[Argc]))
X if (Argc > sep && strcmp(h, ";"))
X break;
X n = Argc--;
X }
X i = 0;
X }
X xfree(last_aliased), last_aliased = NULL;
X --loops;
X if (Argc < argc) {
X free_vec(argv);
X return -1;
X }
X return 0;
}
X
char *
alias_expand(cmd)
register char *cmd;
{
X register char *p;
X register int x;
X
X if (!(p = do_set(functions, cmd)))
X return NULL;
X last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
X if (isoff(glob_flags, WARNING))
X return p;
X for (x = 0; cmds[x].command; x++)
X if (!strcmp(cmd, cmds[x].command)) {
X wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
X return p;
X }
X for (x = 0; ucb_cmds[x].command; x++)
X if (!strcmp(cmd, ucb_cmds[x].command)) {
X wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
X return p;
X }
X return p;
}
X
static int nonobang;
X
/* expand history references and separate message lists from other tokens */
char *
hist_expand(str, argv, hist_was_referenced)
register char *str, **argv;
register int *hist_was_referenced;
{
X static char buf[BUFSIZ];
X register int b = 0, inquotes = 0;
X int first_space = 0, ignore_bang;
X
X ignore_bang = (ison(glob_flags, IGN_BANG) ||
X do_set(set_options, "ignore_bang"));
X nonobang = !!do_set(set_options, "nonobang");
X
X if (hist_was_referenced)
X *hist_was_referenced = 0;
X while (*str) {
X while (!inquotes && isspace(*str))
X str++;
X do {
X if (!*str)
X break;
X if (b >= sizeof(buf)-1) {
X print("argument list too long.\n");
X return NULL;
X }
X if ((buf[b] = *str++) == '\'') {
X /* make sure there's a match! */
X inquotes = !inquotes;
X }
X if (!first_space && !inquotes && index("0123456789{}*$^.", buf[b])
X && b && !index("0123456789{}-^. \t", buf[b-1])) {
X buf[b+1] = buf[b];
X buf[b++] = ' ';
X while ((buf[++b] = *str++) && index("0123456789-,${}.", buf[b]))
X ;
X if (!buf[b])
X str--;
X first_space++;
X }
X /* check for (;) (|) or any other delimiter and separate it from
X * other tokens.
X */
X if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b]) &&
X (b < 0 || buf[b-1] != '\\')) {
X if (!isspace(buf[b]))
X first_space = -1; /* resume msg-list separation */
X if (b && !isspace(buf[b-1]))
X buf[b+1] = buf[b], buf[b++] = ' ';
X b++;
X break;
X }
X /*
X * If double-quotes, just copy byte by byte, char by char,
X * but do remove backslashes from in front of !s
X */
X if (!inquotes && buf[b] == '"') {
X int B = b;
X while ((buf[++B] = *str++) && buf[B] != '"')
X if (*str == '!' && buf[B] == '\\')
X buf[B] = '!', str++;
X if (buf[B])
X b = B;
X else
X str--;
X b++;
X continue;
X }
X if (buf[b] == '\\') {
X first_space = 1; /* don't split escaped words */
X if ((buf[++b] = *str) == '!')
X buf[--b] = '!';
X ++str;
X } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
X && !ignore_bang) {
X char word[BUFSIZ], *s;
X if (!(s = reference_hist(str, word, argv))) {
X if (!nonobang)
X return NULL;
X } else {
X str = s;
X if (hist_was_referenced)
X *hist_was_referenced = 1;
X if (strlen(word) + b >= sizeof buf) {
X print("argument list too long.\n");
X return NULL;
X }
X b += Strcpy(&buf[b], word) - 1;
X }
X }
X b++;
X } while (*str && (!isdelimeter(*str) || str[-1] == '\\'));
X if (!inquotes)
X first_space++, buf[b++] = ' ';
X }
X buf[b] = 0;
X return buf;
}
X
/*
X * expand references to internal variables. This allows such things
X * as $iscurses, $hdrs_only, etc. to work correctly.
X */
char *
check_internal(str)
register char *str;
{
X int ret_val = -1;
X static char version[80], get_status[4];
X
X if (!strcmp(str, "iscurses"))
X ret_val = (iscurses || ison(glob_flags, PRE_CURSES));
X else if (!strcmp(str, "istool"))
X ret_val = istool;
X else if (!strcmp(str, "hdrs_only"))
X ret_val = (hdrs_only && *hdrs_only);
X else if (!strcmp(str, "is_shell"))
X ret_val = is_shell;
X else if (!strcmp(str, "is_sending"))
X ret_val = (ison(glob_flags, IS_SENDING) != 0);
X else if (!strcmp(str, "redirect"))
X ret_val = (isatty(0) != 0);
X else if (!strcmp(str, "thisfolder"))
X return (mailfile && *mailfile) ? mailfile : NULL;
X else if (!strcmp(str, "status"))
X return sprintf(get_status, "%d", last_status);
X else if (!strcmp(str, "output"))
X return last_output;
X else if (!strcmp(str, "version")) {
X /* Create the version string ONCE, then re-use it. */
X if (!*version)
X (void) sprintf(version, "%s (%d.%s.%d %s)",
X MUSHNAME, RELEASE, REVISION, PATCHLEVEL, RELEASE_DATE);
X return version;
X }
X
X return ret_val > 0 ? "1" : ret_val == 0? "0" : NULL;
}
X
/*
X * Parse and expand a single variable reference. Variable references
X * begin with a '$' and thereafter look like any of:
X * $ $$ is the pid of the current process
X * [%x] $[%x] expands %x as a hdr_format character ($%x is same)
X * (%x) $(%x) expands %x as a prompt format character
X * name Value of variable "name" (error if not set)
X * v:x Modified expansion; v is any of above, x is any of
X * h head of a file pathname
X * t tail of a file pathname
X * l value converted to lowercase
X * u value converted to uppercase
X * q quote against further expansion (not yet)
X * <num> select the <num>th space-separated field
X * ?name Set/unset truth value of "name"
X * {v} Separate v (any of above) from surrounding text
X * A variable name may include alphabetics, numbers, or underscores but
X * must begin with an alphabetic or underscore.
X */
varexp(ref)
struct expand *ref;
{
X char *str = ref->orig, c, *p, *var, *end = NULL, *op = NULL;
X int do_bool, do_fmt = 0, expanded = 0;
X
X if (*str == '$') {
X /* Allow a $ all by itself to stand */
X if (!*++str || isspace(*str)) {
X ref->exp = savestr("$");
X ref->rest = str;
X return 1;
X }
X /* Handle $?{name} for backwards compatibility */
X if (do_bool = (*str == '?'))
X str++;
X if (*str == '{')
X if (p = index(str + 1, '}')) {
X var = str + 1;
X end = p;
X } else
X goto bad_var;
X else
X var = str;
X /* Handle $?name and ${?name} (normal cases) */
X if (*var == '?') {
X if (do_bool) /* backwards compatibility clash */
X goto bad_var;
X ++var, do_bool = 1;
X }
X switch (*var) {
X case '$':
X if (str[0] == '{' && str[2] != '}')
X goto bad_var;
X else {
X char buf[16];
X (void) sprintf(buf, "%d", getpid());
X ref->exp = savestr(buf);
X ref->rest = (end ? end : var) + 1;
X return 1;
X }
X when '%':
X for (p = var + 1; *p && !index(" \t\n;|\"'$", *p); p++)
X if (*p == ':') {
X if (!do_bool && !op) {
X op = p;
X do_fmt = p - var;
X } else
X break;
X }
X if (!do_fmt)
X do_fmt = p - var;
X end = p;
X when '[': case '(': /*)*/
X p = any(var, *var == '(' ? ") \t\n" : "] \t\n");
X if (!p || isspace(*p))
X goto bad_var;
X if (end && p > end)
X goto bad_var;
X else {
X var++;
X do_fmt = p - var;
X if (*++p == ':')
X op = p;
X else
X end = p;
X }
X /* fall through */
X default:
X if (!do_fmt && !isalpha(*var) && *var != '_')
X goto bad_var;
X if (!end)
X end = var + strlen(var);
X for (p = (op ? op : var + do_fmt) + 1; p < end; p++)
X if (!do_bool && !op && *p == ':') {
X op = p;
X } else if (!isalnum(*p) && *p != '_') {
X if (*str == '{') /*}*/
X goto bad_var;
X end = p;
X break;
X }
X if (op && op > end)
X op = NULL;
X }
X /* replace the end of "var" (end) with a nul,
X * and save char in `c'. Similarly chop at op.
X */
X c = *end, *end = 0;
X if (op)
X *op++ = 0;
X
X if (!do_fmt && debug > 3)
X printf("expanding (%s) ", var);
X
X /* get the value of the variable. */
X if (do_fmt) {
X char c1 = var[do_fmt];
X var[do_fmt] = 0;
X if (debug > 3)
X printf("expanding (%s) ", var);
X if (/*(*/ ')' == c1)
X p = format_prompt(current_msg, var);
X else
X p = format_hdr(current_msg, var, FALSE) + 9;
X var[do_fmt] = c1;
X } else if (!(p = check_internal(var)))
X p = do_set(set_options, var);
X if (do_bool) {
X ref->exp = savestr((p && (*p || !do_fmt)) ? "1" : "0");
X expanded = 1;
X if (debug > 3)
X printf("--> (%s)\n", p);
X } else if (p) {
X if (debug > 3)
X printf("--> (%s)", p);
X if (op && isdigit(*op)) {
X int varc, ix = atoi(op) - 1;
X char **varv = mk_argv(p, &varc, FALSE);
X /* Ignore non-fatal errors like unmatched quotes */
X if (varv && varc < 0)
X for (varc = 0; varv[varc]; varc++)
X ;
X if (ix < 0 || varc <= ix || !varv)
X ref->exp = savestr("");
X else
X ref->exp = savestr(varv[ix]);
X expanded = 1;
X free_vec(varv);
X } else if (op) {
X char *p2 = rindex(p, '/');
X expanded = (*op == 'h' || *op == 't');
X if (*op == 't' && p2)
X p = p2 + 1;
X else if (*op == 'h' && p2)
X *p2 = 0;
X ref->exp = savestr(p);
X if (*op == 'h' && p2)
X *p2 = '/';
X else if (*op == 'l' || *op == 'u') {
X expanded = 1;
X for (p = ref->exp; *p; p++)
X if (*op == 'u')
X Upper(*p);
X else
X Lower(*p);
X }
X if (!expanded) {
X print("Unknown colon modifier :%c.\n", *op);
X xfree(ref->exp);
X } else
X if (debug > 3)
X printf("--> (%s)\n", p);
X } else {
X ref->exp = savestr(p);
X expanded = 1;
X if (debug > 3)
X printf("\n");
X }
X } else {
X print("%s: undefined variable\n", var);
X expanded = 0;
X }
X *end = c; /* replace the null with the old character */
X if (op)
X *--op = ':'; /* Put back the colon */
X ref->rest = end + (*str == '{'); /* } */
X }
X return expanded;
bad_var:
X print("Illegal variable name.\n");
X return 0;
}
X
/*
X * find mush variable references and expand them to their values.
X * variables are preceded by a '$' and cannot be within single
X * quotes. Only if expansion has been made do we copy buf back into str.
X * We expand only as far as the first unprotected `;' separator in str,
X * to get the right behavior when multiple commands are on one line.
X * RETURN 0 on failure, 1 on success.
X */
variable_expand(str)
register char *str;
{
X register int b = 0, inquotes = 0;
X char buf[BUFSIZ], *start = str;
X int expanded = 0;
X
X while (*str && b < sizeof buf - 1) {
X if (*str == '~' && (str == start || isspace(*(str-1)))) {
X register char *p = any(str, " \t"), *tmp;
X int x = 1;
X if (p)
X *p = 0;
X tmp = getpath(str, &x);
X /* if error, print message and return 0 */
X if (x == -1) {
X wprint("%s: %s\n", str, tmp);
X return 0;
X }
X b += Strcpy(buf+b, tmp);
X if (p)
X *p = ' ', str = p;
X else
X str += strlen(str);
X expanded = 1;
X }
X /* if single-quotes, just copy byte by byte, char by char ... */
X if ((buf[b] = *str++) == '\'' && !inquotes) {
X while ((buf[++b] = *str++) && buf[b] != '\'')
X ;
X if (!buf[b])
X str--;
X } else if (!inquotes && buf[b] == '\\' && *str) {
X buf[++b] = *str++;
X b++;
X continue;
X } else if (buf[b] == '"')
X inquotes = !inquotes;
X /* If $ is eol, continue. Variables must start with a `$'
X * and continue with {, _, a-z, A-Z or it is not a variable. }
X */
X if (buf[b] == '$' && *str) {
X struct expand expansion;
X expansion.orig = str - 1;
X if (varexp(&expansion)) {
X b += Strcpy(&buf[b], expansion.exp);
X xfree(expansion.exp);
X str = expansion.rest;
X expanded = 1;
X } else
X return 0;
X } else if (!inquotes && buf[b] == ';') {
X while (buf[++b] = *str++)
X ;
X b++;
X break;
X } else
X b++;
X }
X buf[b] = 0;
X if (expanded) /* if any expansions were done, copy back into orig buf */
X (void) strcpy(start, buf);
X if (debug > 3)
X printf("expanded to: %s\n", start);
X return 1;
}
X
/* make an argv of space delimited character strings out of string "str".
X * place in "argc" the number of args made. If final is true, then expand
X * variables and file names and remove quotes and backslants according to
X * standard.
X */
char **
mk_argv(str, argc, final)
register char *str;
int *argc;
{
X register char *s = NULL, *p;
X register int tmp, err = 0, unq_sep = 0;
X char *newargv[MAXARGS], **argv, *p2, c, buf[BUFSIZ];
X
X if (debug > 3)
X (void) printf("Working on: %s\n",str);
SHAR_EOF
true || echo 'restore of loop.c failed'
fi
echo 'End of part 10'
echo 'File loop.c is continued in part 11'
echo 11 > _shar_seq_.tmp
exit 0
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.