/*
- * Copyright (c) 1985 The Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that: (1) source distributions retain this entire copyright
- * notice and comment, and (2) distributions including binaries display
- * the following acknowledgement: ``This product includes software
- * developed by the University of California, Berkeley and its contributors''
- * in the documentation or other materials provided with the distribution
- * and in all advertising materials mentioning features or use of this
- * software. Neither the name of the University nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*/
#ifndef lint
-static char sccsid[] = "@(#)interactive.c 5.9 (Berkeley) 6/1/90";
+static char sccsid[] = "@(#)interactive.c 8.1 (Berkeley) 6/5/93";
#endif /* not lint */
-#include "restore.h"
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
#include <protocols/dumprestore.h>
+
#include <setjmp.h>
-#include <ufs/dir.h>
+#include <glob.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "restore.h"
+#include "extern.h"
#define round(a, b) (((a) + (b) - 1) / (b) * (b))
/*
* Things to handle interruptions.
*/
+static int runshell;
static jmp_buf reset;
static char *nextarg = NULL;
struct afile {
ino_t fnum; /* inode number of file */
char *fname; /* file name */
- short fflags; /* extraction flags, if any */
- char ftype; /* file type, e.g. LEAF or NODE */
+ short len; /* name length */
+ char prefix; /* prefix character */
+ char postfix; /* postfix character */
};
struct arglist {
- struct afile *head; /* start of argument list */
- struct afile *last; /* end of argument list */
- struct afile *base; /* current list arena */
- int nent; /* maximum size of list */
- char *cmd; /* the current command */
+ int freeglob; /* glob structure needs to be freed */
+ int argcnt; /* next globbed argument to return */
+ glob_t glob; /* globbing information */
+ char *cmd; /* the current command */
};
-extern int fcmp();
-extern char *fmtentry();
-char *copynext();
+
+static char *copynext __P((char *, char *));
+static int fcmp __P((const void *, const void *));
+static void formatf __P((struct afile *, int));
+static void getcmd __P((char *, char *, char *, struct arglist *));
+struct dirent *glob_readdir __P((RST_DIR *dirp));
+static int glob_stat __P((const char *, struct stat *));
+static void mkentry __P((struct direct *, struct afile *));
+static void printlist __P((char *, char *));
/*
* Read and execute commands from the terminal.
*/
+void
runcmdshell()
{
register struct entry *np;
ino_t ino;
- static struct arglist alist = { 0, 0, 0, 0, 0 };
+ struct arglist arglist;
char curdir[MAXPATHLEN];
char name[MAXPATHLEN];
char cmd[BUFSIZ];
+ arglist.freeglob = 0;
+ arglist.argcnt = 0;
+ arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
+ arglist.glob.gl_opendir = (void *)rst_opendir;
+ arglist.glob.gl_readdir = (void *)glob_readdir;
+ arglist.glob.gl_closedir = (void *)rst_closedir;
+ arglist.glob.gl_lstat = glob_stat;
+ arglist.glob.gl_stat = glob_stat;
canon("/", curdir);
loop:
if (setjmp(reset) != 0) {
- for (; alist.head < alist.last; alist.head++)
- freename(alist.head->fname);
+ if (arglist.freeglob != 0) {
+ arglist.freeglob = 0;
+ arglist.argcnt = 0;
+ globfree(&arglist.glob);
+ }
nextarg = NULL;
volno = 0;
}
- getcmd(curdir, cmd, name, &alist);
+ runshell = 1;
+ getcmd(curdir, cmd, name, &arglist);
switch (cmd[0]) {
/*
* Add elements to the extraction list.
if (strncmp(cmd, "delete", strlen(cmd)) != 0)
goto bad;
np = lookupname(name);
- if (np == NIL || (np->e_flags & NEW) == 0) {
+ if (np == NULL || (np->e_flags & NEW) == 0) {
fprintf(stderr, "%s: not on extraction list\n", name);
break;
}
goto bad;
createfiles();
createlinks();
- setdirmodes();
+ setdirmodes(0);
if (dflag)
checkrestore();
volno = 0;
case 'l':
if (strncmp(cmd, "ls", strlen(cmd)) != 0)
goto bad;
- ino = dirlookup(name);
- if (ino == 0)
- break;
- printlist(name, ino, curdir);
+ printlist(name, curdir);
break;
/*
* Print current directory.
case 's':
if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
goto bad;
- setdirmodes();
+ setdirmodes(FORCE);
break;
/*
* Print out dump header information.
* "curdir" is prepended to it. Finally "canon" is called to
* eliminate any embedded ".." components.
*/
+static void
getcmd(curdir, cmd, name, ap)
char *curdir, *cmd, *name;
struct arglist *ap;
/*
* Check to see if still processing arguments.
*/
- if (ap->head != ap->last) {
- strcpy(name, ap->head->fname);
- freename(ap->head->fname);
- ap->head++;
- return;
- }
+ if (ap->argcnt > 0)
+ goto retnext;
if (nextarg != NULL)
goto getnext;
/*
else
nextarg = cp;
/*
- * If it an absolute pathname, canonicalize it and return it.
+ * If it is an absolute pathname, canonicalize it and return it.
*/
if (rawname[0] == '/') {
canon(rawname, name);
(void) strcat(output, rawname);
canon(output, name);
}
- expandarg(name, ap);
- strcpy(name, ap->head->fname);
- freename(ap->head->fname);
- ap->head++;
+ if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
+ fprintf(stderr, "%s: out of memory\n", ap->cmd);
+ if (ap->glob.gl_pathc == 0)
+ return;
+ ap->freeglob = 1;
+ ap->argcnt = ap->glob.gl_pathc;
+
+retnext:
+ strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]);
+ if (--ap->argcnt == 0) {
+ ap->freeglob = 0;
+ globfree(&ap->glob);
+ }
# undef rawname
}
/*
* Strip off the next token of the input.
*/
-char *
+static char *
copynext(input, output)
char *input, *output;
{
* Canonicalize file names to always start with ``./'' and
* remove any imbedded "." and ".." components.
*/
+void
canon(rawname, canonname)
char *rawname, *canonname;
{
register char *cp, *np;
- int len;
if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
(void) strcpy(canonname, "");
}
}
-/*
- * globals (file name generation)
- *
- * "*" in params matches r.e ".*"
- * "?" in params matches r.e. "."
- * "[...]" in params matches character class
- * "[...a-z...]" in params matches a through z.
- */
-expandarg(arg, ap)
- char *arg;
- register struct arglist *ap;
-{
- static struct afile single;
- struct entry *ep;
- int size;
-
- ap->head = ap->last = (struct afile *)0;
- size = expand(arg, 0, ap);
- if (size == 0) {
- ep = lookupname(arg);
- single.fnum = ep ? ep->e_ino : 0;
- single.fname = savename(arg);
- ap->head = &single;
- ap->last = ap->head + 1;
- return;
- }
- qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
-}
-
-/*
- * Expand a file name
- */
-expand(as, rflg, ap)
- char *as;
- int rflg;
- register struct arglist *ap;
-{
- int count, size;
- char dir = 0;
- char *rescan = 0;
- DIR *dirp;
- register char *s, *cs;
- int sindex, rindex, lindex;
- struct direct *dp;
- register char slash;
- register char *rs;
- register char c;
-
- /*
- * check for meta chars
- */
- s = cs = as;
- slash = 0;
- while (*cs != '*' && *cs != '?' && *cs != '[') {
- if (*cs++ == 0) {
- if (rflg && slash)
- break;
- else
- return (0) ;
- } else if (*cs == '/') {
- slash++;
- }
- }
- for (;;) {
- if (cs == s) {
- s = "";
- break;
- } else if (*--cs == '/') {
- *cs = 0;
- if (s == cs)
- s = "/";
- break;
- }
- }
- if ((dirp = rst_opendir(s)) != NULL)
- dir++;
- count = 0;
- if (*cs == 0)
- *cs++ = 0200;
- if (dir) {
- /*
- * check for rescan
- */
- rs = cs;
- do {
- if (*rs == '/') {
- rescan = rs;
- *rs = 0;
- }
- } while (*rs++);
- sindex = ap->last - ap->head;
- while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
- if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
- continue;
- if ((*dp->d_name == '.' && *cs != '.'))
- continue;
- if (gmatch(dp->d_name, cs)) {
- if (addg(dp, s, rescan, ap) < 0)
- return (-1);
- count++;
- }
- }
- if (rescan) {
- rindex = sindex;
- lindex = ap->last - ap->head;
- if (count) {
- count = 0;
- while (rindex < lindex) {
- size = expand(ap->head[rindex].fname,
- 1, ap);
- if (size < 0)
- return (size);
- count += size;
- rindex++;
- }
- }
- bcopy((char *)&ap->head[lindex],
- (char *)&ap->head[sindex],
- (ap->last - &ap->head[rindex]) * sizeof *ap->head);
- ap->last -= lindex - sindex;
- *rescan = '/';
- }
- }
- s = as;
- while (c = *s)
- *s++ = (c&0177 ? c : '/');
- return (count);
-}
-
-/*
- * Check for a name match
- */
-gmatch(s, p)
- register char *s, *p;
-{
- register int scc;
- char c;
- char ok;
- int lc;
-
- if (scc = *s++)
- if ((scc &= 0177) == 0)
- scc = 0200;
- switch (c = *p++) {
-
- case '[':
- ok = 0;
- lc = 077777;
- while (c = *p++) {
- if (c == ']') {
- return (ok ? gmatch(s, p) : 0);
- } else if (c == '-') {
- if (lc <= scc && scc <= (*p++))
- ok++ ;
- } else {
- if (scc == (lc = (c&0177)))
- ok++ ;
- }
- }
- return (0);
-
- default:
- if ((c&0177) != scc)
- return (0) ;
- /* falls through */
-
- case '?':
- return (scc ? gmatch(s, p) : 0);
-
- case '*':
- if (*p == 0)
- return (1) ;
- s--;
- while (*s) {
- if (gmatch(s++, p))
- return (1);
- }
- return (0);
-
- case 0:
- return (scc == 0);
- }
-}
-
-/*
- * Construct a matched name.
- */
-addg(dp, as1, as3, ap)
- struct direct *dp;
- char *as1, *as3;
- struct arglist *ap;
-{
- register char *s1, *s2;
- register int c;
- char buf[BUFSIZ];
-
- s2 = buf;
- s1 = as1;
- while (c = *s1++) {
- if ((c &= 0177) == 0) {
- *s2++ = '/';
- break;
- }
- *s2++ = c;
- }
- s1 = dp->d_name;
- while (*s2 = *s1++)
- s2++;
- if (s1 = as3) {
- *s2++ = '/';
- while (*s2++ = *++s1)
- /* void */;
- }
- if (mkentry(buf, dp->d_ino, ap) == FAIL)
- return (-1);
-}
-
/*
* Do an "ls" style listing of a directory
*/
-printlist(name, ino, basename)
+static void
+printlist(name, basename)
char *name;
- ino_t ino;
char *basename;
{
- register struct afile *fp;
+ register struct afile *fp, *list, *listp;
register struct direct *dp;
- static struct arglist alist = { 0, 0, 0, 0, "ls" };
struct afile single;
- DIR *dirp;
+ RST_DIR *dirp;
+ int entries, len;
+ dp = pathsearch(name);
+ if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0))
+ return;
if ((dirp = rst_opendir(name)) == NULL) {
- single.fnum = ino;
- single.fname = savename(name + strlen(basename) + 1);
- alist.head = &single;
- alist.last = alist.head + 1;
+ entries = 1;
+ list = &single;
+ mkentry(dp, list);
+ len = strlen(basename) + 1;
+ if (strlen(name) - len > single.len) {
+ freename(single.fname);
+ single.fname = savename(&name[len]);
+ single.len = strlen(single.fname);
+ }
} else {
- alist.head = (struct afile *)0;
+ entries = 0;
+ while (dp = rst_readdir(dirp))
+ entries++;
+ rst_closedir(dirp);
+ list = (struct afile *)malloc(entries * sizeof(struct afile));
+ if (list == NULL) {
+ fprintf(stderr, "ls: out of memory\n");
+ return;
+ }
+ if ((dirp = rst_opendir(name)) == NULL)
+ panic("directory reopen failed\n");
fprintf(stderr, "%s:\n", name);
+ entries = 0;
+ listp = list;
while (dp = rst_readdir(dirp)) {
if (dp == NULL || dp->d_ino == 0)
break;
- if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
+ if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
continue;
if (vflag == 0 &&
(strcmp(dp->d_name, ".") == 0 ||
strcmp(dp->d_name, "..") == 0))
continue;
- if (!mkentry(dp->d_name, dp->d_ino, &alist))
- return;
+ mkentry(dp, listp++);
+ entries++;
}
+ rst_closedir(dirp);
+ if (entries == 0) {
+ fprintf(stderr, "\n");
+ free(list);
+ return;
+ }
+ qsort((char *)list, entries, sizeof(struct afile), fcmp);
}
- if (alist.head != 0) {
- qsort((char *)alist.head, alist.last - alist.head,
- sizeof *alist.head, fcmp);
- formatf(&alist);
- for (fp = alist.head; fp < alist.last; fp++)
+ formatf(list, entries);
+ if (dirp != NULL) {
+ for (fp = listp - 1; fp >= list; fp--)
freename(fp->fname);
- }
- if (dirp != NULL)
fprintf(stderr, "\n");
+ free(list);
+ }
}
/*
* Read the contents of a directory.
*/
-mkentry(name, ino, ap)
- char *name;
- ino_t ino;
- register struct arglist *ap;
-{
+static void
+mkentry(dp, fp)
+ struct direct *dp;
register struct afile *fp;
+{
+ char *cp;
+ struct entry *np;
- if (ap->base == NULL) {
- ap->nent = 20;
- ap->base = (struct afile *)calloc((unsigned)ap->nent,
- sizeof (struct afile));
- if (ap->base == NULL) {
- fprintf(stderr, "%s: out of memory\n", ap->cmd);
- return (FAIL);
- }
- }
- if (ap->head == 0)
- ap->head = ap->last = ap->base;
- fp = ap->last;
- fp->fnum = ino;
- fp->fname = savename(name);
- fp++;
- if (fp == ap->head + ap->nent) {
- ap->base = (struct afile *)realloc((char *)ap->base,
- (unsigned)(2 * ap->nent * sizeof (struct afile)));
- if (ap->base == 0) {
- fprintf(stderr, "%s: out of memory\n", ap->cmd);
- return (FAIL);
- }
- ap->head = ap->base;
- fp = ap->head + ap->nent;
- ap->nent *= 2;
+ fp->fnum = dp->d_ino;
+ fp->fname = savename(dp->d_name);
+ for (cp = fp->fname; *cp; cp++)
+ if (!vflag && (*cp < ' ' || *cp >= 0177))
+ *cp = '?';
+ fp->len = cp - fp->fname;
+ if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
+ fp->prefix = '^';
+ else if ((np = lookupino(fp->fnum)) != NULL && (np->e_flags & NEW))
+ fp->prefix = '*';
+ else
+ fp->prefix = ' ';
+ switch(dp->d_type) {
+
+ default:
+ fprintf(stderr, "Warning: undefined file type %d\n",
+ dp->d_type);
+ /* fall through */
+ case DT_REG:
+ fp->postfix = ' ';
+ break;
+
+ case DT_LNK:
+ fp->postfix = '@';
+ break;
+
+ case DT_FIFO:
+ case DT_SOCK:
+ fp->postfix = '=';
+ break;
+
+ case DT_CHR:
+ case DT_BLK:
+ fp->postfix = '#';
+ break;
+
+ case DT_UNKNOWN:
+ case DT_DIR:
+ if (inodetype(dp->d_ino) == NODE)
+ fp->postfix = '/';
+ else
+ fp->postfix = ' ';
+ break;
}
- ap->last = fp;
- return (GOOD);
+ return;
}
/*
* Print out a pretty listing of a directory
*/
-formatf(ap)
- register struct arglist *ap;
+static void
+formatf(list, nentry)
+ register struct afile *list;
+ int nentry;
{
- register struct afile *fp;
- struct entry *np;
- int width = 0, w, nentry = ap->last - ap->head;
- int i, j, len, columns, lines;
- char *cp;
-
- if (ap->head == ap->last)
- return;
- for (fp = ap->head; fp < ap->last; fp++) {
- fp->ftype = inodetype(fp->fnum);
- np = lookupino(fp->fnum);
- if (np != NIL)
- fp->fflags = np->e_flags;
- else
- fp->fflags = 0;
- len = strlen(fmtentry(fp));
- if (len > width)
- width = len;
+ register struct afile *fp, *endlist;
+ int width, bigino, haveprefix, havepostfix;
+ int i, j, w, precision, columns, lines;
+
+ width = 0;
+ haveprefix = 0;
+ havepostfix = 0;
+ bigino = ROOTINO;
+ endlist = &list[nentry];
+ for (fp = &list[0]; fp < endlist; fp++) {
+ if (bigino < fp->fnum)
+ bigino = fp->fnum;
+ if (width < fp->len)
+ width = fp->len;
+ if (fp->prefix != ' ')
+ haveprefix = 1;
+ if (fp->postfix != ' ')
+ havepostfix = 1;
}
- width += 2;
- columns = 80 / width;
+ if (haveprefix)
+ width++;
+ if (havepostfix)
+ width++;
+ if (vflag) {
+ for (precision = 0, i = bigino; i > 0; i /= 10)
+ precision++;
+ width += precision + 1;
+ }
+ width++;
+ columns = 81 / width;
if (columns == 0)
columns = 1;
lines = (nentry + columns - 1) / columns;
for (i = 0; i < lines; i++) {
for (j = 0; j < columns; j++) {
- fp = ap->head + j * lines + i;
- cp = fmtentry(fp);
- fprintf(stderr, "%s", cp);
- if (fp + lines >= ap->last) {
+ fp = &list[j * lines + i];
+ if (vflag) {
+ fprintf(stderr, "%*d ", precision, fp->fnum);
+ fp->len += precision + 1;
+ }
+ if (haveprefix) {
+ putc(fp->prefix, stderr);
+ fp->len++;
+ }
+ fprintf(stderr, "%s", fp->fname);
+ if (havepostfix) {
+ putc(fp->postfix, stderr);
+ fp->len++;
+ }
+ if (fp + lines >= endlist) {
fprintf(stderr, "\n");
break;
}
- w = strlen(cp);
- while (w < width) {
- w++;
- fprintf(stderr, " ");
- }
+ for (w = fp->len; w < width; w++)
+ putc(' ', stderr);
}
}
}
/*
- * Comparison routine for qsort.
+ * Skip over directory entries that are not on the tape
+ *
+ * First have to get definition of a dirent.
*/
-fcmp(f1, f2)
- register struct afile *f1, *f2;
+#undef DIRBLKSIZ
+#include <dirent.h>
+#undef d_ino
+
+struct dirent *
+glob_readdir(dirp)
+ RST_DIR *dirp;
{
+ struct direct *dp;
+ static struct dirent adirent;
- return (strcmp(f1->fname, f2->fname));
+ while ((dp = rst_readdir(dirp)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (dflag || TSTINO(dp->d_ino, dumpmap))
+ break;
+ }
+ if (dp == NULL)
+ return (NULL);
+ adirent.d_fileno = dp->d_ino;
+ adirent.d_namlen = dp->d_namlen;
+ bcopy(dp->d_name, adirent.d_name, dp->d_namlen + 1);
+ return (&adirent);
}
/*
- * Format a directory entry.
+ * Return st_mode information in response to stat or lstat calls
*/
-char *
-fmtentry(fp)
- register struct afile *fp;
+static int
+glob_stat(name, stp)
+ const char *name;
+ struct stat *stp;
{
- static char fmtres[BUFSIZ];
- static int precision = 0;
- int i;
- register char *cp, *dp;
+ register struct direct *dp;
- if (!vflag) {
- fmtres[0] = '\0';
- } else {
- if (precision == 0)
- for (i = maxino; i > 0; i /= 10)
- precision++;
- (void) sprintf(fmtres, "%*d ", precision, fp->fnum);
- }
- dp = &fmtres[strlen(fmtres)];
- if (dflag && BIT(fp->fnum, dumpmap) == 0)
- *dp++ = '^';
- else if ((fp->fflags & NEW) != 0)
- *dp++ = '*';
+ dp = pathsearch(name);
+ if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0))
+ return (-1);
+ if (inodetype(dp->d_ino) == NODE)
+ stp->st_mode = IFDIR;
else
- *dp++ = ' ';
- for (cp = fp->fname; *cp; cp++)
- if (!vflag && (*cp < ' ' || *cp >= 0177))
- *dp++ = '?';
- else
- *dp++ = *cp;
- if (fp->ftype == NODE)
- *dp++ = '/';
- *dp++ = 0;
- return (fmtres);
+ stp->st_mode = IFREG;
+ return (0);
+}
+
+/*
+ * Comparison routine for qsort.
+ */
+static int
+fcmp(f1, f2)
+ register const void *f1, *f2;
+{
+ return (strcmp(((struct afile *)f1)->fname,
+ ((struct afile *)f2)->fname));
}
/*
* respond to interrupts
*/
void
-onintr()
+onintr(signo)
+ int signo;
{
- if (command == 'i')
+ if (command == 'i' && runshell)
longjmp(reset, 1);
if (reply("restore interrupted, continue") == FAIL)
done(1);