home *** CD-ROM | disk | FTP | other *** search
- 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.
-