argv@zipcode.com (Dan Heller) (04/21/91)
Submitted-by: Dan Heller <argv@zipcode.com> Posting-number: Volume 18, Issue 65 Archive-name: mush/part08 Supersedes: mush: Volume 12, Issue 28-47 #!/bin/sh # do not concatenate these parts, unpack them in order with /bin/sh # file expr.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 8; 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 expr.c' else echo 'x - continuing file expr.c' sed 's/^X//' << 'SHAR_EOF' >> 'expr.c' && X */ char * eval_expr(p, new_list) register char *p, new_list[]; { X register char *p2, **argv; X int argc; X u_long save_flags = glob_flags; X X if (!(p2 = index(++p, '`'))) { X print("unmatched backquote (`)\n"); X return NULL; X } X *p2 = 0; X X skipspaces(0); X if (!*p) { X print("Invalid null command\n"); X return NULL; X } X turnon(glob_flags, DO_PIPE); X /* ignore sigs only because if user interrupts the do_command, X * the longjmp will corrupt the stack and the program is hosed. X * fix is to have layers of jmp_bufs to return to different levels. X */ X turnon(glob_flags, IGN_SIGS); X if (*p && (argv = make_command(p, TRPL_NULL, &argc))) X (void) do_command(argc, argv, new_list); X glob_flags = save_flags; X *p2 = '`'; X return p2+1; } SHAR_EOF echo 'File expr.c is complete' && chmod 0644 expr.c || echo 'restore of expr.c failed' Wc_c="`wc -c < 'expr.c'`" test 4685 -eq "$Wc_c" || echo 'expr.c: original size 4685, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= file.c ============== if test -f 'file.c' -a X"$1" != X"-c"; then echo 'x - skipping file.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting file.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'file.c' && /* file.c -- Copyright (1988) Dan Heller */ X #include "mush.h" #include <pwd.h> X /* takes string 'p' and address of int (isdir). If p uses the ~ to reference X * a home directory of some sort, then expand it. find out what sort of X * file final path is. set isdir to 1 if a directory, 0 if not, -1 on error X * return final path. If an error occurs, return string indicating error. X * if isdir has a value of 1 when passed, it ignores "No such file or directory" X */ char * getpath(p, isdir) register char *p; int *isdir; { X static char buf[MAXPATHLEN]; X struct stat stat_buf; X X if (p != buf) { /* Just in case */ X if (!p || !*p || !strcmp(p, "~")) { X char *home = do_set(set_options, "home"); X if (!home || !*home) X home = ALTERNATE_HOME; X (void) strcpy(buf, home); /* no arg means home */ X } else if (*p == '~') { X if (p[1] != '/') { X /* not our home, but someone else's X * look for ~user or ~user/subpath X * if '/' exists, separate into tmp="user" p="subpath" X */ X struct passwd *ent, *getpwnam(); X char *p2 = p+1; X if (p = index(p2, '/')) X *p++ = 0; X if (!(ent = getpwnam(p2))) { X *isdir = -1; X return sprintf(buf, "no such user: %s", p2); X } X /* append subpath to pathname */ X if (p && *p) X (void) sprintf(buf, "%s/%s", ent->pw_dir, p); X /* if *p == NULL, pathname is done (buf), set isdir = 1 */ X else { X *isdir = 1; X return strcpy(buf, ent->pw_dir); X } X } else { X char *home = do_set(set_options, "home"); X if (!home || !*home) X home = ALTERNATE_HOME; X (void) sprintf(buf, "%s/%s", home, p+2); X } X } else if (*p == '%') { X /* if %user, append user name... else, it's just us */ X if (!*++p || *p == ' ' || *p == '\t') X (void) strcpy(buf, spoolfile); X else #ifndef HOMEMAIL X (void) sprintf(buf, "%s/%s", MAILDIR, p); #else /* HOMEMAIL */ X { X /* If it's NOT us, recur to get the path for ~user/MAILFILE */ X int t_isdir = *isdir; X char *t, tmp[MAXPATHLEN]; X (void) sprintf(tmp, "~%s/%s", p, MAILFILE); X t = getpath(tmp, &t_isdir); X if (t_isdir == -1) { X *isdir = -1; X return t; X } X /* strcpy(buf, t); --buf already has info because it's static */ X } #endif /* HOMEMAIL */ X } else if (*p == '+') { X register char *p2 = do_set(set_options, "folder"); X if (!p2 || !*p2) X p2 = DEF_FOLDER; X if (*++p) X (void) sprintf(buf, "%s/%s", p2, p); X else X (void) strcpy(buf, p2); X if (*buf != '/') { X int t_isdir = *isdir; X char *t, tmp[MAXPATHLEN]; X if (*buf != '~') X (void) sprintf(tmp, "~/%s", buf); X else X (void) strcpy(tmp, buf); X t = getpath(tmp, &t_isdir); X if (t_isdir == -1) { X *isdir = -1; X return t; X } X /* strcpy(buf, t); --buf already has info because it's static */ X } X } else { /* allow \ to escape the special chars, +, %, ~ */ X if (*p == '\\') X p++; X (void) strcpy(buf, p); X } X } X if (stat(buf, &stat_buf)) { X (void) access(buf, F_OK); /* set errno to the "real" reason */ X if (errno == ENOENT && *isdir == 1) { X *isdir = 0; /* say it's a regular file even tho it doesn't exist */ X return buf; /* it may be wanted for creating */ X } X *isdir = -1; X return sys_errlist[errno]; X } X *isdir = ((stat_buf.st_mode & S_IFMT) == S_IFDIR); X return buf; } X /* X * Given a (possibly NULL or empty) string, return the name of a a valid X * directory. The string may contain the usual filename metachars (see X * above). Returns the current user's home directory if the input string X * does not refer to a directory, the ALTERNATE_HOME if the user's home X * directory cannot be found, or NULL if none of the above are accessible. X * X * NOTE: Returns the getpath() static buffer, so the same caveats apply. X */ char * getdir(path) char *path; { X int isdir = 0; X X /* getpath() already handles the NULL and empty cases */ X if (!(path = getpath(path, &isdir)) || isdir != 1) { X isdir = 0; X path = getpath(ALTERNATE_HOME, &isdir); X if (isdir != 1) X path = NULL; X } X return path; } X /* X * Given a filename[pointer] (p), a file pointer, and a mode, file_to_fp X * opens the file with the mode. X * If the mode is "r" then we read the file into the file pointer at the X * end (fseek(fp, 2, 0)). If the file is opened for writing, then read X * from the beginning of fp and write it into the file. X * This is usually called to read .signatures into messages (thus, X * opening .signature with "r" and writing to the end of fp which is probably X * the sendmail process or the message file pointer) or to write fortunes into X * the message buffer: reading fp (the popened fortune) and writing into file. X */ file_to_fp(p, fp, mode) register char *p; register FILE *fp; char *mode; { X int x = 1; X char *file, buf[BUFSIZ]; X FILE *tmp_fp; X X if (!p || !*p) { X print("specify filename"); X return -1; X } X /* Special case for IS_SENDING && !IS_GETTING should eventually go away */ X if (ison(glob_flags, IS_SENDING) && isoff(glob_flags, IS_GETTING) && X strcmp(p, "-") == 0) { X file = p; X if (*mode == 'r') X tmp_fp = stdin; X else X tmp_fp = stdout; X } else { X file = getpath(p, &x); X if (x == -1) { /* on error, file contains error message */ X wprint(file); X return -1; X } X wprint("%s: ", file); X if (x) { X /* if x == 1, then path is a directory */ X wprint("is a directory.\n"); X return -1; X } else if (!(tmp_fp = fopen(file, mode))) { X wprint("%s\n", sys_errlist[errno]); X return -1; X } X } X if (*mode != 'r') { X rewind(fp); X for(x = 0; fgets(buf, BUFSIZ, fp); x++) X (void) fputs(buf, tmp_fp); X } else { X for(x = 0; fgets(buf, BUFSIZ, tmp_fp); x++) X (void) fputs(buf, fp); X (void) fflush(fp); X } X wprint("%s%d line%s\n", (*mode == 'a')? "added ": "", X x, (x == 1)? "": "s"); X if (file != p || strcmp(file, "-") != 0) X (void) fclose(tmp_fp); X return 0; } X /* clear all contents of the file. Careful that the file is opened for X * _writing_ --tempfile is opened for reading, so don't try to empty it X * if you're using ftruncate. Return -1 on error, 0 on success. X */ emptyfile(fp, fname) register FILE **fp; register char *fname; { X Debug("Emptying \"%s\"\n", fname); #ifndef SYSV X return ftruncate(fileno(*fp), 0L); #else X { X int omask = umask(077), ret; X (void) fclose(*fp); X if (!(*fp = fopen(fname, "w"))) X ret = -1; X else X ret = 0; X (void) umask(omask); X return ret; X } #endif /* SYSV */ } X /* X * Finds out how many file descriptors are opened. Useful for making sure X * no files got opened in subprocedures which were not subsequently closed. X * If argc is 0, returns the number of available fds. X */ nopenfiles(argc) { #ifdef MAXFILES X register int size = MAXFILES; #else X register int size = getdtablesize(); #endif /* MAXFILES */ X register int nfiles = 0, totalfiles = size; X X if (argc > 1) X return -1; X X if (argc == 1) X wprint("open file descriptors:"); X while (--size >= 0) X if (fcntl(size, F_GETFL, 0) != -1) { X if (argc == 1) X wprint(" %d", size); X ++nfiles; X } X if (argc == 1) { X wprint("\n"); X return 0; X } X return totalfiles - nfiles; } X /* X * Close all "extraneous" file descriptors; return the number closed X */ closefileds (n) { X register int nfiles = 0; #ifdef MAXFILES X register int size = MAXFILES; #else X register int size = getdtablesize(); #endif /* MAXFILES */ X X while (--size >= n) X if (fcntl(size, F_GETFL, 0) != -1) { X (void) close(size); X ++nfiles; X } X return nfiles; } X /* X * Open a path for writing or appending -- return a FILE pointer. X * If program is TRUE, then use popen, not fopen and don't check X * to see if the file is writable. If program is FALSE and lockit X * is TRUE, then lock on open. X */ FILE * open_file(p, program, lockit) register char *p; { X register FILE *newfile = NULL_FILE; X register char *tmp; X int x = 1; X X if (program) X tmp = p, x = 0; X else X tmp = getpath(p, &x); X if (x == 1) X print("%s is a directory.\n", tmp); X else if (x == -1) X print("%s: %s\n", p, tmp); X else { X register char *mode = NULL; X /* if it doesn't exist open for "w" */ X if (program || Access(tmp, F_OK)) X mode = "w"; X /* if we can't write to it, forget it */ X else if (Access(tmp, W_OK)) X error(tmp); X else X mode = "a"; X if (mode) X if (program) { X if (!(newfile = popen(tmp, mode))) X error("Can't execute %s\n", tmp); X } else if (lockit) { X /* Lock on open */ X if (!(newfile = lock_fopen(tmp, mode))) X error("Can't write to %s", tmp); X } else { X /* Ordinary open */ X if (!(newfile = mask_fopen(tmp, mode))) X error("Can't write to %s", tmp); X } X if (newfile != NULL_FILE) X Debug("Successfully opened %s\n", tmp); X } X return newfile; } X /* X * Open each file in the vector names[] and place the corresponding X * file descriptor in files[]. If the file is really a program (pipe), X * delete the name after opening; otherwise lock the file. X * Tokens beginning with a "/, ~, or + are files; tokens beginning X * with a | are programs. X */ open_list(names, files, size) char *names[]; FILE *files[]; { X register int total = 0, prog; X register char *fpath; X X Debug("opening "), print_argv(names); X for (total = 0; size && total < size; ) { X fpath = names[total] + (prog = (names[total][0] == '|')); X /* open_file() locks the file here only if prog is false */ X if ((files[total] = open_file(fpath, prog, TRUE))) { X if (prog) { X xfree(names[total]); X names[total++] = NULL; X } else { X /* Seek to end of file AFTER locking */ X (void) fseek(files[total++], 0L, 2); X } X } else { X Debug("Failed to open %s\n", names[total]); X /* Swap the failed file with the last in the list */ X if (size--) { X xfree(names[total]); X names[total] = names[size]; X names[size] = NULL; X } X } X } X return size; } X /* X * find_files gets a set of addresses and an array of X * char pointers and the maximum size that array can be. X * The object is to find the files or programs listed in "s". If the X * size is 0, then just extract the file names and give error messages X * for each one since they will not be opened. Return the number of X * files found and delete all files from the list in * "s". X * The string "s" is modified to be a list of address -- all names AND X * files are stripped out of the list. X * The force parameter causes names to be interpreted as files even if X * they would normally appear to be addresses. X */ find_files(s, names, size, force) register char *s; char *names[]; { X register int total = 0; X char file[MAXPATHLEN], buf[HDRSIZ], *start = s, c; X register char *p, *b = buf, *fpath; X X do { X if (!(p = get_name_n_addr(s, NULL, file))) X break; X c = *p, *p = 0; X /* See if it's a file. This doesn't get written back X * onto "buf" since it is supposed to be extracted anyway. X * The check for '@' in names beginning with '/' is to X * avoid mis-identifying X.400 addresses as file names. X */ X if (force || *file == '+' || *file == '~' || X *file == '|' || *file == '/' && !index(file, '@')) { X int isdir; X /* open either "file" or &file[1] */ X if (*file == '|') { X isdir = 0; X fpath = file; X } else { X isdir = 1; X /* if successful, getpath will reset isdir to 0 */ X fpath = getpath(file, &isdir); X } X if (!isdir) { X if (size && total < size) X names[total++] = savestr(fpath); X else X print("No open space for %s\n", file); X } else if (isdir == 1) X print("%s: is a directory\n", file); X else X print("%s: %s\n", file, fpath); X } else { X b += Strcpy(b, s); X *b++ = ',', *b++ = ' '; X } X for (*p = c, s = p; *s == ',' || isspace(*s); s++) X ; X } while (*s); X for (*b-- = 0; b > buf && (*b == ',' || isspace(*b)); b--) X *b = 0; X (void) strcpy(start, buf); X names[total] = NULL; /* for free_vec() */ X return total; } X /* X * access(2) has an undocumented feature which ignores suid. If you are X * su'ed and try to read your mail, you will be unable to because access() X * will give the illusion that you cannot read/write to your mbox. Solve X * the problem by using stat() instead. X */ Access(file, mode) register char *file; { X struct stat buf; X X if (stat(file, &buf) == -1) X return -1; X if (mode == R_OK) X return (buf.st_mode & 0400)? 0 : -1; X if (mode == W_OK) X return (buf.st_mode & 0200)? 0 : -1; X return 0; } X /* X * Open a file for read/write/whatever but make sure umask is rw by user only. X */ FILE * mask_fopen(file, mode) char *file, *mode; { X int omask = umask(077); X FILE *fp = fopen(file, mode); X (void) umask(omask); X return fp; } X /* X * Shorten a file name, replacing its full path name with one using an X * accepted mush abbreviation: X * ~ home directory X * + folder directory X * For files in the current directory, the path is simply skipped. X * Returns a pointer into a static buffer holding the trimmed path. X */ char * trim_filename(name) char *name; { X static char buf[MAXPATHLEN]; X char *fldr = do_set(set_options, "folder"), X *home = do_set(set_options, "home"); X int len; X X /* Handling $folder is tough, because if it is not set then we should X * trim DEF_FOLDER; but DEF_FOLDER may not be a full path, and we can't X * call getpath() because the "name" parameter may point to gepath()'s X * static buffer. So we handle the special case of DEF_FOLDER starting X * with a tilde ($home), and forget about it otherwise. Yuck. X */ X if ((!fldr || !*fldr) && (fldr = DEF_FOLDER) && *fldr == '~' && home) { X (void) sprintf(buf, "%s%s", home, fldr + 1); X fldr = buf; /* buf will get overwritten again below */ X } X /* One more special case: if $folder and $home are the same, then we X * trim as $home, otherwise we trim as $folder. This prevents strange X * contractions like "+.cshrc" for "~/.cshrc". X */ X if ((!home || strcmp(home, fldr)) && (len = strlen(fldr)) && X !strncmp(fldr, name, len) && (name[len] == '/' || !name[len])) { X buf[0] = '+'; X if (name[len] && name[len + 1]) X (void) strcpy(buf + 1, name + len + 1); X else X buf[1] = 0; X return buf; X } else if (home && (len = strlen(home)) && !strncmp(home, name, len) && X (name[len] == '/' || !name[len])) { X buf[0] = '~'; X (void) strcpy(buf + 1, name + len); X return buf; X } else if ((fldr = do_set(set_options, "cwd")) && X (len = strlen(fldr)) && !strncmp(fldr, name, len) && X name[len] == '/') X return strcpy(buf, name + len + 1); X return strcpy(buf, name); } SHAR_EOF chmod 0644 file.c || echo 'restore of file.c failed' Wc_c="`wc -c < 'file.c'`" test 14503 -eq "$Wc_c" || echo 'file.c: original size 14503, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= fkeys.c ============== if test -f 'fkeys.c' -a X"$1" != X"-c"; then echo 'x - skipping fkeys.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting fkeys.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'fkeys.c' && /* @(#)fkeys.c (c) copyright 10/18/86 (Dan Heller) */ X #include "mush.h" X #define L(n) KEY_LEFTFIRST+(n)-1 #define R(n) KEY_RIGHTFIRST+(n)-1 #define F(n) KEY_TOPFIRST+(n)-1 #define BREAK_KEY KEY_TOPLAST X static int func_key(); X Notify_value fkey_interposer(client, event, arg, type) Frame client; Event *event; Notify_arg arg; Notify_event_type type; { X if ((event_is_key_left(event) || event_is_key_right(event) || X event_is_key_top(event)) && X event_is_down(event) && func_key(event_id(event))) X return NOTIFY_DONE; X X return notify_next_event_func(client, event, arg, type); } X /* X * Execute commands defined by a function key. X * Left keys: X * L1 = (null) can't be set X * L2 ... L10 X * Top function keys X * F1 ... F9, BREAK/backspace (key not definable) X * Right function keys X * R1 ... R15 X * Usually, the last Function key displays the others' settings. X */ static int func_key(key) register int key; { X register char **argv, *p; X char buf[256]; X int n; X X if (key >= KEY_LEFTFIRST && key <= KEY_LEFTLAST) X buf[0] = 'L', n = key - KEY_LEFTFIRST; X else if (key >= KEY_TOPFIRST && key <= KEY_TOPLAST) X buf[0] = 'F', n = key - KEY_TOPFIRST; X else if (key >= KEY_RIGHTFIRST && key <= KEY_RIGHTLAST) X buf[0] = 'R', n = key - KEY_RIGHTFIRST; X (void) sprintf(buf+1, "%d", n+1); X X if (!(p = do_set(fkeys, buf))) { X if (!chk_option("quiet", "fkey")) X wprint("Function key \"%s\" not set.\n", buf); X return FALSE; X } X /* make_command will screw up "p", so copy it first */ X (void) strcpy(buf, p); X Debug("(%s) \"%s\": ", key, p), turnon(glob_flags, CONT_PRNT); X if (argv = make_command(buf, TRPL_NULL, &n)) X (void) do_command(n, argv, msg_list); X return TRUE; } SHAR_EOF chmod 0644 fkeys.c || echo 'restore of fkeys.c failed' Wc_c="`wc -c < 'fkeys.c'`" test 1717 -eq "$Wc_c" || echo 'fkeys.c: original size 1717, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= folders.c ============== if test -f 'folders.c' -a X"$1" != X"-c"; then echo 'x - skipping folders.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting folders.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'folders.c' && /* @(#)folders.c (c) copyright 10/18/86 (Dan Heller) */ X #include "mush.h" X static char oldfolder[MAXPATHLEN]; X /* folder %[user] --new mailfile is the spool/mail/login file [user]. X * folder # --new mailfile is the folder previous to the current folder X * folder & --new mailfile is ~/mbox (or whatever "mbox" is set to) X * folder +file --new mailfile is in the directory "folder"; name is 'file' X * folder "path" --full path name or the one in current working directory. X * X * in all cases, changes are updated unless a '!' is specified after the X * folder command (e.g. "f!", "folder !" "fo!" .. all permutations) X * as usual, if new mail has arrived before the file is copied back, then X * user will be notified beforehand. X * X * RETURN -1 on error -- else return 0. All bits in msg_list are set to true. X */ folder(argc, argv, list) register char **argv; char list[]; { X int n, updating = !strcmp(*argv, "update"), do_read_only = 0, no_hdrs = 0; X char *tmp, *newfolder = NULL, buf[MAXPATHLEN]; X struct stat statbuf; X extern long last_spool_size; X X if (ison(glob_flags, IS_PIPE)) { X print("You can't pipe to the %s command.\n", *argv); X return -1; X } else if (ison(glob_flags, IS_SENDING)) { X print("You can't use the %s command when sending.\n", *argv); X return -1; X } else if (!tempfile || !*tempfile) { X print("You can't use the %s command in init files.\n", *argv); X return -1; X } X while (*++argv && (**argv == '-' || **argv == '!')) X if (!strcmp(*argv, "-N")) X no_hdrs = !iscurses; X else if (!updating && !strcmp(*argv, "-n")) X turnoff(glob_flags, DO_UPDATE); X else if (!strcmp(*argv, "-r")) X do_read_only = 1; X else if (!strcmp(*argv, "!")) { X if (updating) X turnon(glob_flags, DO_UPDATE); /* useful? */ X else X turnoff(glob_flags, DO_UPDATE); X } else X return help(0, "folder", cmd_help); X X if (updating) { X (void) strcpy(buf, mailfile); X if (ison(glob_flags, READ_ONLY)) X do_read_only = 1; X } else { X if (!*argv) { X mail_status(0); X return 0; X } X if (!strcmp(*argv, "#")) X if (!*oldfolder) { X print("No previous folder\n"); X return -1; X } else X newfolder = oldfolder; X else if (!strcmp(*argv, "&")) { X if (!(newfolder = do_set(set_options, "mbox")) || !*newfolder) X newfolder = DEF_MBOX; X } else X newfolder = *argv; X n = 0; X tmp = getpath(newfolder, &n); X if (n == -1) { X print("%s: %s\n", newfolder, tmp); X return -1; X } else if (n == 1) { X print("%s: is a directory\n", tmp); X return -1; X } X /* strcpy so copyback() below (which calls getpath) doesn't change X * the data that tmp intended to point to. Get the cwd if necessary. X */ X n = 0; X if (*tmp != '/') { X if (!GetCwd(buf, sizeof buf)) { X error("getcwd: %s",buf); X return -1; X } X n = strlen(buf); X buf[n++] = '/'; X } X (void) strcpy(&buf[n], tmp); X } #ifdef SUNTOOL X if (istool > 1) X timeout_cursors(TRUE); #endif /* SUNTOOL */ X if (stat(buf, &statbuf) == -1 || !(statbuf.st_mode & 0400)) { X error("Unable to read %s", buf); #ifdef SUNTOOL X if (istool > 1) X timeout_cursors(FALSE); #endif /* SUNTOOL */ X return -1; X } X /* If the file can't be opened for writing, autoset READ_ONLY */ X if (!(statbuf.st_mode & 0200)) X do_read_only = 1; X X if (!(n=copyback(updating?"Update folder?":"Change anyway?",!updating))) { #ifdef SUNTOOL X if (istool > 1) X timeout_cursors(FALSE); #endif /* SUNTOOL */ X /* an error occured updating the folder */ X return -1; X } X turnoff(glob_flags, CORRUPTED); /* copyback() was successful */ X /* Assure that both oldfolder and mailfile are full paths */ X if (strcmp(mailfile, buf) || !*oldfolder) { X n = 1; /* force load of new folder */ X if (!updating) X (void) strcpy(oldfolder, *oldfolder? mailfile : buf); X strdup(mailfile, buf); X } X do_read_only? turnon(glob_flags,READ_ONLY) : turnoff(glob_flags,READ_ONLY); X last_size = spool_size = 0L; X while (msg_cnt--) { X xfree(msg[msg_cnt].m_date_recv); X xfree(msg[msg_cnt].m_date_sent); X msg[msg_cnt].m_date_recv = msg[msg_cnt].m_date_sent = NO_STRING; X } X msg_cnt = 0, msg[0].m_offset = 0L; X turnoff(glob_flags, CONT_PRNT); X X turnon(glob_flags, IGN_SIGS); X /* clear the tempfile */ X if (tmpf) X (void) fclose(tmpf); X if (!do_read_only) { X if (!(tmpf = mask_fopen(tempfile, "w"))) { X error("error truncating %s", tempfile); X turnoff(glob_flags, IGN_SIGS); #ifdef SUNTOOL X if (istool > 1) X timeout_cursors(FALSE); #endif /* SUNTOOL */ X return -1; X } X } X /* Don't reload the folder if it was removed */ X if (n > 0) { X if (load_folder(mailfile, TRUE, NULL) < 1) { X last_msg_cnt = 0; X last_size = statbuf.st_size; /* Disable check_new_mail() */ X turnoff(glob_flags, IGN_SIGS); #ifdef SUNTOOL X if (istool > 1) X timeout_cursors(FALSE); #endif /* SUNTOOL */ X return -1; X } X if (do_read_only && !(tmpf = fopen(mailfile, "r"))) { X error(mailfile); X turnoff(glob_flags, IGN_SIGS); #ifdef SUNTOOL X if (istool > 1) X timeout_cursors(FALSE); #endif /* SUNTOOL */ X return -1; X } X } X last_msg_cnt = msg_cnt; /* for check_new_mail */ X /* Prevent both bogus "new mail" messages and missed new mail */ X last_size = msg[msg_cnt].m_offset; X if (!strcmp(mailfile, spoolfile)) X spool_size = last_spool_size = last_size; #ifdef SUNTOOL X if (istool) { X extern Panel_item folder_text_item; X Rect *rect = (Rect *)window_get(hdr_sw, WIN_RECT); X (void) pw_rop(hdr_win, 0,0, rect->r_width, rect->r_height, PIX_CLR, X (struct pixrect *) 0,0,0); X panel_set_value(folder_text_item, mailfile); X } #endif /* SUNTOOL */ X X if (!updating || current_msg >= msg_cnt) X current_msg = (msg_cnt? 0 : -1); X turnoff(glob_flags, IGN_SIGS); X X /* now sort messages according a user-defined default */ X if (!updating && msg_cnt > 1 && !strcmp(mailfile, spoolfile) && X (tmp = do_set(set_options, "sort"))) { X (void) sprintf(buf, "sort %s", tmp); X if ((argv = mk_argv(buf, &argc, TRUE)) && argc > 0) { X /* msg_list can't be null for do_command and since we're not X * interested in the result, call sort directly X */ X (void) sort(argc, argv, NULL); X free_vec(argv); X if (!updating) X current_msg = 0; /* Sort may move the current message */ X } X } X turnoff(glob_flags, DO_UPDATE); X X /* go to first NEW message */ X for (n = 0; n < msg_cnt && ison(msg[n].m_flags, OLD); n++) X ; X if (n == msg_cnt) { X turnoff(glob_flags, NEW_MAIL); X if (!updating) { X /* no new message found -- try first unread message */ X for (n = 0; n < msg_cnt && isoff(msg[n].m_flags, UNREAD); n++) X ; X } X } else { X turnon(glob_flags, NEW_MAIL); X /* default for toolmode is true */ X if (istool && !chk_option("quiet", "tool")) X bell(); X } X if (msg_cnt && (!updating || current_msg < 0)) X current_msg = (n == msg_cnt ? 0 : n); X X if ((!istool || istool && !msg_cnt) && !iscurses) X mail_status(0); X /* be quiet if we're piping */ X if (!istool && !updating && !no_hdrs && msg_cnt X && isoff(glob_flags, DO_PIPE)) X (void) cmd_line(sprintf(buf, "headers %d", current_msg+1), msg_list); #ifdef SUNTOOL X if (istool > 1) { X if (!msg_cnt) X print("No Mail in %s\n", mailfile); X if (msg_cnt) { X display_msg(current_msg, (long)0); X do_hdrs(0, DUBL_NULL, NULL); X /* Automatic display should not "touch" this message */ X turnoff(msg[current_msg].m_flags, DO_UPDATE); X /* don't update folder just because a message is displayed */ X turnoff(glob_flags, DO_UPDATE); X } X timeout_cursors(FALSE); X } #endif /* SUNTOOL */ X if (list) { X clear_msg_list(list); X bitput(list, list, msg_cnt, =~); /* macro */ X } X return 0; } X folders(argc, argv) register char **argv; { X register char *p; X char buf[128], unused[MAXMSGS_BITS]; X X if (argv && argv[1] && !strcmp(argv[1], "-?")) X return help(0, "folders", cmd_help); X X if (!(p = do_set(set_options, "folder")) || !*p) X p = DEF_FOLDER; X (void) sprintf(buf, "ls -FR %s", p); X if (argv = make_command(buf, TRPL_NULL, &argc)) X return do_command(argc, argv, unused); X return -1; } X /* X * Determine whether a file could be a folder. If prompt is non-NULL, X * ask the user whether we should treat the file as a folder anyway. X */ test_folder(name, prompt) char *name, *prompt; { X char line[BUFSIZ], *p; X FILE *fp = fopen(name, "r"); X int retval = FALSE; X X if (!fp) X return 0; X if (fgets(line, sizeof line - 1, fp)) { #ifndef MSG_SEPARATOR X if (p = any(line, " \t")) { X skipspaces(1); X p = any(p, " \t"); X } X if (p && !strncmp(line, "From ", 5) && (p = parse_date(p + 1))) #else /* MSG_SEPARATOR */ X if (!strncmp(line, MSG_SEPARATOR, strlen(MSG_SEPARATOR))) #endif /* MSG_SEPARATOR */ X retval = TRUE; X } else X retval = TRUE; /* Empty files are legitimate folders */ X (void) fclose(fp); X if (prompt && !retval) { X char buf[BUFSIZ]; #ifdef SUNTOOL X if (istool) { X (void) sprintf(buf, "\"%s\": %s", name, prompt); X return ask(buf); X } #endif /* SUNTOOL */ X print("\"%s\": %s [n] ", name, prompt); X buf[0] = 0; X retval = (Getstr(buf, sizeof (buf), 0) && lower(*buf) == 'y'); X } X return retval; } X /* merge_folders filename -- concatenate the folder specified by filename X * to the current folder. X * X * RETURN -1 on error -- else return 0. A bit in msg_list is set to true X * for each of the "new" messages read in to the current folder. X */ merge_folders(n, argv, list) register char **argv, list[]; { X int no_hdrs = 0, newest_msg; X long orig_offset; X char *tmp, *newfolder = NULL, buf[MAXPATHLEN]; X X if (ison(glob_flags, IS_PIPE)) { X print("You can't pipe to the %s command.\n", *argv); X return -1; X } else if (ison(glob_flags, IS_SENDING)) { X print("You can't use the %s command while sending.\n", *argv); X return -1; X } X X while (*++argv && **argv == '-') X if (!strcmp(*argv, "-?")) X return help(0, "merge", cmd_help); X else if (!strcmp(*argv, "-N")) X no_hdrs = !(iscurses || ison(glob_flags, PRE_CURSES)); X X if (!*argv) X return 0; X X if (ison(glob_flags, READ_ONLY)) { X print("Folder is read-only.\n"); X return -1; X } X X if (!strcmp(*argv, "#")) X if (!*oldfolder) { X print("No previous folder\n"); X return -1; X } else X newfolder = oldfolder; X else if (!strcmp(*argv, "&")) { X if (!(newfolder = do_set(set_options, "mbox")) || !*newfolder) X newfolder = DEF_MBOX; X } else X newfolder = *argv; X n = 0; X tmp = getpath(newfolder, &n); X if (n == -1) { X print("%s: %s\n", newfolder, tmp); X return -1; X } else if (n == 1) { X print("%s: is a directory\n", tmp); X return -1; X } X X turnon(glob_flags, IGN_SIGS); X orig_offset = msg[msg_cnt].m_offset; X (void) load_folder(tmp, 2, list); X msg[msg_cnt].m_offset = orig_offset; X newest_msg = last_msg_cnt; X Debug("newest_msg = %d\n", newest_msg); X last_msg_cnt = msg_cnt; /* for check_new_mail */ X Debug("msg_cnt = %d\n", msg_cnt); X if (current_msg < 0) X current_msg = 0; X (void) mail_size(); X turnoff(glob_flags, IGN_SIGS); X X if ((!istool || istool && !msg_cnt) X && !iscurses && !ison(glob_flags, PRE_CURSES)) X mail_status(0); X /* be quiet if we're piping or if told not to show headers */ X if ((istool || !no_hdrs) && isoff(glob_flags, DO_PIPE) X && newest_msg < msg_cnt) X (void) cmd_line(sprintf(buf, "headers %d", newest_msg + 1), NULL); X return 0; } X /* X * Default digest article separator X */ #define ARTICLE_SEP "--------" X /* X * Undigestify messages. If a message is in digest-format, there are many X * messages within this message which are to be extracted. Kinda like a X * folder within a folder. By default, this routine will create a new X * folder that contains the new messages. -m option will merge the new X * messages into the current folder. X */ do_undigest(n, argv, list) char *argv[], list[]; { X int r, articles = 0, merge = 0, appending = 0; X char buf[MAXPATHLEN], cmdbuf[MAXPATHLEN], newlist[MAXMSGS_BITS], *dir; X char *art_sep = ARTICLE_SEP, *mktemp(); X FILE *fp; X X while (argv && *++argv && **argv == '-') { X switch(argv[0][1]) { X case 'm': X if (ison(glob_flags, READ_ONLY)) { X print("Folder is read only.\n"); X return -1; X } X merge++; X when 'p': X if (*++argv) X art_sep = *argv; X else { X print("Specify separator pattern with -p.\n"); X return -1; X } X otherwise: return help(0, "undigest", cmd_help); X } X } X X if ((n = get_msg_list(argv, list)) == -1) X return -1; X X argv += n; X X if (*argv) { X int isdir = 1; /* Ignore file nonexistance errors */ X (void) strcpy(buf, getpath(*argv, &isdir)); X if (isdir < 0) { X print("%s: %s\n", *argv, buf); X return -1; X } else if (isdir == 1) { X print("%s: is a directory\n", buf); X return -1; X } X } else { X register char *p, *p2; X if (Access(dir = ".", W_OK) == 0 || X (dir = do_set(set_options, "folder")) || X (dir = do_set(set_options, "tmpdir"))) X dir = getdir(dir); /* expand metachars */ X if (!dir) alted: X dir = ALTERNATE_HOME; X for (n = 0; n < msg_cnt; n++) X if (msg_bit(list, n)) X break; X X if (!(p = header_field(n, "subject"))) X (void) mktemp(sprintf(buf, "%s/digestXXXXX", dir)); X else { X if (!lcase_strncmp(p, "re: ", 4)) X p += 4; X for (p2 = p; *p2; p2++) X if (!isalnum(*p2) && *p2 != '-' && *p2 != '.') { X *p2 = 0; X break; X } X p2 = buf + Strcpy(buf, dir); X *p2++ = '/'; X (void) strcpy(p2, p); X } X } X X if (!Access(buf, W_OK)) X appending = ((fp = mask_fopen(buf, "a")) != NULL_FILE); X else X fp = mask_fopen(buf, "w"); X if (!fp) { X if (!*argv && strcmp(dir, ALTERNATE_HOME)) X goto alted; X error("can't create %s", buf); X return -1; X } X X for (n = 0; n < msg_cnt; n++) { X if (!msg_bit(list, n)) X continue; X X print("undigesting message %d\n", n+1); X /* copy message into file making sure all headers exist. */ X r = undigest(n, fp, art_sep); X if (r <= 0) X break; X articles += r; X } X (void) fclose(fp); X if (r <= 0) { X if (!appending) X (void) unlink(buf); X return -1; X } X if (merge) { X (void) cmd_line(sprintf(cmdbuf, "\\merge -N %s", buf), newlist); X (void) unlink(buf); X print("Merged in %d messages.\n", articles); X } else X print("Added %d messages to \"%s\".\n", articles, buf); X clear_msg_list(list); X for (n = 0; n < msg_cnt; n++) X if (msg_bit(newlist, n)) X set_msg_bit(list, n); X return 0; } X /* X * split digest-message 'n' to file "fp" using article separator "sep". X * return number of articles copied or -1 if system error on fputs. X * A digest is a folder-in-a-message in a special, semi-standard form. X */ undigest(n, fp, sep) int n; FILE *fp; char *sep; { X int art_cnt = 0, on_hdr = -1; /* on_hdr is -1 if hdr not yet found */ X int sep_len = (sep ? strlen(sep) : strlen(sep = ARTICLE_SEP)); X long get_hdr = 0L; X char from[HDRSIZ], line[HDRSIZ], last_sep[HDRSIZ]; X char from_hdr[256], afrom[256], adate[64]; X char *fdate = "Xxx Xxx 00 00:00:00 0000"; /* Dummy date in ctime form */ X SIGRET (*oldint)(), (*oldquit)(); X X if (!msg_get(n, from, sizeof from)) { X error("Unable to find msg %d", n+1); X return -1; X } #ifndef MSG_SEPARATOR X else { X char *p = from + 5; X skipspaces(0); X p = index(p, ' '); X if (p) { X skipspaces(0); X fdate = p; X } X if (fputs(from, fp) == EOF) X return -1; X } #endif /* !MSG_SEPARATOR */ X X on_intr(); X *afrom = *adate = *last_sep = '\0'; X while (ftell(tmpf) < msg[n].m_offset + msg[n].m_size && X fgets(line, sizeof (line), tmpf)) { X if (ison(glob_flags, WAS_INTR)) X goto handle_error; X if (*line == '\n' && on_hdr > 0) /* blank line -- end of header */ X on_hdr = 0; X X /* Check for the beginning of a digest article */ X if (!strncmp(line, sep, sep_len)) { X if (get_hdr) { X if (do_set(set_options, "warning")) X print("Article with no header? (added to article #%d)\n", X art_cnt); X /* Don't start a new message for whatever this is, X * just fseek back and keep appending to the last one. X */ X if (fseek(tmpf, get_hdr, L_SET) < 0 || X fputs(last_sep, fp) == EOF) { X art_cnt = -1; X goto handle_error; X } X get_hdr = 0L; X on_hdr = 0; X } else { X (void) strcpy(last_sep, line); X get_hdr = ftell(tmpf); X *afrom = *adate = '\0'; X on_hdr = -1; /* Haven't found the new header yet */ X } X continue; X } X X if (get_hdr) { X char *p = *line == '>' ? line + 1 : line; X if (*line == '\n') { X if (*afrom || *adate) { X (void) fseek(tmpf, get_hdr, L_SET); X /* Terminate the previous article */ X art_cnt++; #ifdef MSG_SEPARATOR #ifdef END_MSG_SEP X if (fputs(END_MSG_SEP, fp) == EOF) { X art_cnt = -1; X goto handle_error; X } #endif /* END_MSG_SEP */ #ifdef MMDF X /* MMDF has a newline in MSG_SEPARATOR */ X if (fputs(MSG_SEPARATOR, fp) == EOF) #else /* !MMDF */ X /* Other MSG_SEPARATORs need a newline */ X if (fputs(MSG_SEPARATOR, fp) == EOF || X fputc('\n', fp) == EOF) #endif /* MMDF */ #else /* !MSG_SEPARATOR */ X /* Everybody else needs a From_ line */ X if (fprintf(fp, "From %s %s", *afrom ? afrom : "unknown", X *adate ? date_to_ctime(adate) : fdate) == EOF) #endif /* MSG_SEPARATOR */ X { X art_cnt = -1; X goto handle_error; X } X /* Make sure there is a From: without a leading > */ X if (*afrom && *from_hdr && fputs(from_hdr, fp) == EOF) { X art_cnt = -1; X goto handle_error; X } X get_hdr = 0L; X } else if (on_hdr < 0) X /* Skip blanks between "--------" and the hdr */ X get_hdr = ftell(tmpf); X } else if (on_hdr < 0) X on_hdr = 1; X if (on_hdr > 0 && !strncmp(p, "From: ", 6)) { X (void) get_name_n_addr(p + 6, NULL, afrom); X (void) no_newln(afrom); X /* Get the From: minus the leading > */ X if (p != line) X (void) strcpy(from_hdr, p); X else /* We don't need From: twice! */ X *from_hdr = '\0'; X } else if (on_hdr > 0 && !strncmp(line, "Date: ", 6)) { X if (p = parse_date(line+6)) X (void) strcpy(adate, p); X } else if (on_hdr > 0 && !lcase_strncmp(line, "end", 3)) { X if (!*afrom && !*adate) X break; X } X } else if (fputs(line, fp) == EOF) { X /* Pipe broken, out of file space, etc */ X art_cnt = -1; X goto handle_error; X } X } X ++art_cnt; #ifdef END_MSG_SEP X if (art_cnt > 0 && fputs(END_MSG_SEP, fp) == EOF) { X art_cnt = -1; X goto handle_error; X } #endif /* END_MSG_SEP */ X /* If we're still looking for a header, there is some stuff left X * at the end of the digest. Create an extra article for it. X */ X if (get_hdr) { X char *p; X (void) fseek(tmpf, get_hdr, L_SET); X if (ftell(tmpf) >= msg[n].m_offset + msg[n].m_size) X goto handle_error; #ifdef MSG_SEPARATOR #ifdef MMDF X if (fputs(MSG_SEPARATOR, fp) == EOF) #else /* !MMDF */ X if (fputs(MSG_SEPARATOR, fp) == EOF || X fputc('\n', fp) == EOF) #endif /* MMDF */ #else /* !MSG_SEPARATOR */ X if (fputs(from, fp) == EOF) #endif /* MSG_SEPARATOR */ X art_cnt = -1; X if (!(p = header_field(n, "from"))) X p = "Mush-Undigest (Real author unknown)"; X if (fprintf(fp, "From: %s\n", p) == EOF) X art_cnt = -1; X if (!(p = header_field(n, "date"))) X p = fdate, (void) no_newln(p); X if (fprintf(fp, "Date: %s\n", p) == EOF) X art_cnt = -1; X if (!(p = header_field(n, "subject"))) X p = "Digest"; X if (fprintf(fp, "Subject: Trailing part of %s\n\n", p) == EOF) X art_cnt = -1; X /* header_field() moves the pointer, so seek again */ X (void) fseek(tmpf, get_hdr, L_SET); X while (art_cnt > 0 && ftell(tmpf) < msg[n].m_offset + msg[n].m_size X && fgets(line, sizeof (line), tmpf)) { X if (fputs(line, fp) == EOF) X art_cnt = -1; #ifdef END_MSG_SEP X if (!strncmp(line, END_MSG_SEP, strlen(END_MSG_SEP))) X break; #endif /* END_MSG_SEP */ X } X /* The END_MSG_SEP, if any, of the digest will have been output X * by the while loop above, so we don't need to add one here. X */ X ++art_cnt; X } handle_error: X if (art_cnt == -1) X error("cannot completely undigest"); X else if (ison(glob_flags, WAS_INTR)) X art_cnt = -1; X off_intr(); X return art_cnt; } SHAR_EOF chmod 0644 folders.c || echo 'restore of folders.c failed' Wc_c="`wc -c < 'folders.c'`" test 20079 -eq "$Wc_c" || echo 'folders.c: original size 20079, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= glob.c ============== if test -f 'glob.c' -a X"$1" != X"-c"; then echo 'x - skipping glob.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting glob.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'glob.c' && #include "mush.h" #include "glob.h" X /* X * Buried somewhere in here is the skeleton of a pattern matcher posted X * by koblas@mips.COM (David Koblas). It has been hacked almost beyond X * recognition to handle more complex patterns, and directory search has X * been added (patterns are split at '/' characters when file globbing). X */ X #ifdef TEST /* Define TEST to build a stand-alone file globbing program */ X extern char *malloc(), *realloc(); X #define getpath(x,y) (*(y) = 0, (x)) #define Access access #define Strcpy(x,y) (strcpy(x,y), strlen(x)) #define savestr(x) (strcpy(malloc(strlen(x)+1),x)) #ifndef max #define max(x,y) ((x) > (y) ? (x) : (y)) #endif /* max */ #ifndef min #define min(x,y) ((x) > (y) ? (y) : (x)) #endif /* min */ #define xfree free #undef wprint #define wprint printf #define debug 0 #undef sprintf X #define TESTGLOB(str1,str2) \ X printf("%s %s = %s\n",str1,str2,glob(str1,str2)?"TRUE":"FALSE") X main(argc, argv) int argc; char **argv; { X char **e; X int f; X X if (argc > 1) X while (*++argv) { X (void) printf("%s -->\n", *argv); X if (f = filexp(*argv, &e)) { X columnate(f, e, 0); X } X } #ifdef TEST2 /* Define TEST2 to automatically run these test cases */ X TESTGLOB("abcdefg", "abcdefg"); X TESTGLOB("abcdefg", "a?cd?fg"); X TESTGLOB("abcdefg", "ab[cde]defg"); X TESTGLOB("abcdefg", "ab[a-z]defg"); X TESTGLOB("abcdefg", "ab[a-z]defg"); X TESTGLOB("ab]defg", "ab[a]c]defg"); X TESTGLOB("ab]defg", "ab[a\\]c]defg"); X TESTGLOB("abcdefg", "ab*fg"); X TESTGLOB("./bc/def/gh/ij", "*de*"); X TESTGLOB("./der/den/deq/der/", "*deq*"); X TESTGLOB("./bc/def/gh/ij", "*ij"); X TESTGLOB("./ij", ".?ij"); X TESTGLOB("./bc/def/gh/ij", "./*"); X TESTGLOB("abcdef", "*def"); X TESTGLOB("abcdef", "*abcdef"); X TESTGLOB("abcdef", "abc*"); X TESTGLOB("abcdef", "abcdef*"); X TESTGLOB("abcdef", "*?*{xxx,,yy}"); X TESTGLOB("abcdef", "abcde{f}"); X TESTGLOB("abcdef", "abcdef{xxx,,yyy}"); X TESTGLOB("abcdef", "abc{def,qwrx}"); X TESTGLOB("abcdef", "abc{ab,def,qwrx}"); X TESTGLOB("abcdef", "{naqrwer,fuwnwer,as,abc,a}{ab,def,qwrx}"); X TESTGLOB("abcdef", "{naqrwer,*,as,abc,a}{ab,def,qwrx}"); X TESTGLOB("abcdef", "{{a*,b*},as,a}{ab,def,qwrx}"); X TESTGLOB("abcdef", "{{c*,b*},as,a}{ab,def,qwrx}"); X TESTGLOB("abcdef", "{{c*,?b*},as,a}{ab,def,qwrx}"); X TESTGLOB("abcdef", "{naqrwer,fuwnwer,as,abc,a}{ab,d*f,qwrx}"); #endif /* TEST2 */ } X char * any(s1, s2) register char *s1, *s2; { X register char *p; X if (!s1 || !*s1 || !s2 || !*s2) X return 0; X for( ; *s1; s1++) { X for(p = s2; *p; p++) X if (*p == *s1) X return s1; X } X return 0; } X #endif /* TEST */ X /* X * Make a string into a one-element vector X */ char ** unitv(s) char *s; { X char **v; X X if (v = (char **)malloc((unsigned)(2 * sizeof(char *)))) { X v[0] = savestr(s); X v[1] = NULL; X } X return v; } X /* X * Append one vector to another X */ catv(s1, v1, s2, v2) int s1, s2; char ***v1, **v2; { X int i; X X if (s1 < 0 || !v1) X return -1; X if (s2 < 0 || !v2) X return s1; X X /* realloc(NULL, size) should be legal, but Sun doesn't support it. */ X if (*v1) X *v1 = (char **)realloc(*v1,(unsigned)((s1+s2+1) * sizeof(char **))); X else X *v1 = (char **)malloc((unsigned)((s1+s2+1) * sizeof(char **))); X X if (*v1) { X for (i = 0; i < s2 && v2[i]; i++) X (*v1)[s1 + i] = v2[i]; X (*v1)[s1 + i] = NULL; X xfree(v2); X return s1 + i; X } X return -1; } X /* X * A duplicate-eliminating comparison for sorting. It treats an empty X * string as greater than any other string, and forces empty one of any X * pair of of equal strings. Two passes are sufficient to move the empty X * strings to the end where they can be deleted by the calling function. X * X * This is NOT compatible with the ANSI C qsort(), which requires that the X * comparison function will not modify its arguments! X */ uniqcmp(p1, p2) char **p1, **p2; { X int cmp; X X if (**p1 && !**p2) X return -1; X if (**p2 && !**p1) X return 1; X if (cmp = strcmp(*p1, *p2)) X return cmp; X **p2 = 0; X return -1; } X /* X * Expand a pattern into a list of file names. Returns the number of X * matches. As in csh, names generated from pattern sets are returned X * even if there are no actual matches. X */ filexp(pat, exp) char *pat, ***exp; { X char **t1, **t2; X int n, new, cnt; X X if (!exp) X return -1; X if (!pat || !*pat) X return 0; X X if ((n = sxp(pat, &t1)) > 0) X cnt = 0; X else X return n; X *exp = DUBL_NULL; X while (n--) X if ((new = fxp(t1[n], &t2)) > 0 || new++ == 0 && t2) X cnt = catv(cnt, exp, new, t2); X if (cnt > 1) { X /* Two sort passes to eliminate duplicates -- see uniqcmp() */ X qsort((char *)*exp, cnt, sizeof(char *), uniqcmp); X qsort((char *)*exp, cnt, sizeof(char *), uniqcmp); X while (!(*exp)[cnt - 1][0]) { X xfree((*exp)[--cnt]); X (*exp)[cnt] = NULL; X } X } X return cnt; } X /* X * Expand a filename with globbing chars into a list of matching filenames. X * Pattern set notatation which crosses directories is not handled, e.g. X * "fi{le/exp,nger/h}and" will NOT expand to "file/expand finger/hand". X * Such patterns must be pre-expanded by sxp() before calling fxp(). X * X * The list of expansions is placed in *exp, and the number of matches X * is returned, or -1 on an error. X */ fxp(name, exp) char *name, ***exp; { X char *p; X int isdir; X X if (!exp) X return -1; X X isdir = 1; /* ignore no such file */ X p = getpath(name, &isdir); X if (isdir < 0) X return -1; X else if (isdir) X return ((*exp = unitv(p)) ? 1 : -1); X return pglob(p, 0, exp); } X /* X * Match all globbings in a path. Mutually recursive with dglob(), below. X * The first "skip" characters of the path are not globbed, see dglob(). X * X * Returns the number of matches, or -1 on an error. *exp is set to the X * list of matches. X * X * If the path has no metachars, it is returned in *exp whether it matches X * a real file or not. This allows patterns built by sxp() to be recognized X * and returned even when there are no matches (ala csh generation of names X * from pattern sets). pglob() still returns zero in this case. X */ pglob(path, skip, exp) char *path, ***exp; int skip; { X char *t, *t2; X int ret = 0; X X if (!path || !exp || skip < 0) X return -1; X *exp = DUBL_NULL; /* Must be null in case of zero matches and no sets */ X X for (t = t2 = path + skip; (t2 = any(t2, META)) && *t2 == '/'; t = t2++) X ; X if (!t2) { X ret = ((*exp = unitv(path)) ? 1 : -1); X if (ret > 0 && Access(path, F_OK) < 0) X ret = 0; X } else { X if (t2 = index(t + 1, '/')) X *t2++ = 0; X if (*t == '/') { X *t++ = 0; X if (!*path) X ret = dglob("/", t, t2, exp); X else X ret = dglob(path, t, t2, exp); X } else { X ret = dglob("", t, t2, exp); X } X } X return ret; } X /* X * Search a directory (possibly recursively) for glob matches. X * Argument pat1 is a pattern to be matched in this directory, X * and pat2 is a pattern to be matched in matched subdirectories. X * X * Matches are returned through *exp. X */ dglob(dir, pat1, pat2, exp) char *dir, *pat1, *pat2, ***exp; { X DIR *dirp; X struct dirent *dp; X char *b, *d, buf[MAXPATHLEN], **tmp; X int n, ret = 0, skip; X X if (!dir || !exp) X return -1; X d = (*dir ? dir : "."); X if (!(dirp = opendir(d))) X return -1; X b = buf + Strcpy(buf, dir); X if (b > buf && *(b - 1) != '/') X *b++ = '/'; X skip = b - buf; /* We know this much matches, don't glob it again */ X while (ret >= 0 && (dp = readdir(dirp))) { X if (fglob(dp->d_name, pat1)) { X if (pat2) { X (void) sprintf(b, "%s/%s", dp->d_name, pat2); X n = pglob(buf, skip, &tmp); X ret = catv(ret, exp, n, tmp); X } else { X (void) strcpy(b, dp->d_name); X ret = catv(ret, exp, 1, unitv(buf)); X } X } X } X closedir(dirp); X return ret; } X /* X * Match file names. This means that metachars do not match leading ".". X */ fglob(str, pat) char *str, *pat; { X if (!str || !pat || *str == '.' && *pat != '.') X return FALSE; X else X return glob(str, pat); } X /* X * Match two concatenated patterns. Mainly for use by sglob(). X */ static glob2(str, pat1, pat2) char *str, *pat1, *pat2; { X char buf[MAXPATHLEN]; X X if (!str || !pat1 && !pat2) X return FALSE; X (void) sprintf(buf, "%s%s", pat1? pat1 : "", pat2? pat2 : ""); X return glob(str, buf); } X /* X * The basic globbing matcher. X * X * "*" = match 0 or more occurances of anything X * "[abc]" = match any of "abc" (ranges supported) X * "{xx,yy,...}" = match any of "xx", "yy", ... where X * "xx", "yy" can be any pattern or empty X * "?" = match any character X */ glob(str, pat) char *str, *pat; { X int done = FALSE, ret = FALSE; X X if (!str || !pat) X return FALSE; X X while (*pat && !done && (*str || (*pat == '{' || *pat == '*'))) /*}*/ { X /* X * First look for a literal match, stepping over backslashes X * in the pattern to match against the "protected" character. X * Ordering and precendence are important in this expression! X */ X if (*pat == '\\' && *str == *++pat || *str == *pat) { X str++; X pat++; X } else switch (*pat++) { X case '*': /* Match any string */ X if (!*pat) { X while (*str) X str++; X break; X } X /* X * Try the rest of the glob against every X * possible suffix of the string. A bit X * inefficient in cases that eventually fail. X */ X while (*str && !(ret = glob(str++, pat))) X ; X return ret; X break; X case '[': /* Match a set */ X repeat: X /* If we've hit the end of the set, give up. */ SHAR_EOF true || echo 'restore of glob.c failed' fi echo 'End of part 8' echo 'File glob.c is continued in part 9' echo 9 > _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.