home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
misc
/
volume18
/
mush
/
part08
< prev
next >
Wrap
Internet Message Format
|
1991-04-22
|
51KB
From: argv@zipcode.com (Dan Heller)
Newsgroups: comp.sources.misc
Subject: v18i065: mush - Mail User's Shell, Part08/22
Message-ID: <1991Apr21.025028.11432@sparky.IMD.Sterling.COM>
Date: 21 Apr 91 02:50:28 GMT
Approved: kent@sparky.imd.sterling.com
X-Checksum-Snefru: 66a5696c f93bd72d c9b78bc9 6dad1564
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.