argv@zipcode.com (Dan Heller) (04/22/91)
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.