2 * Ported to Linux's Second Extended File System as part of the
3 * dump and restore backup suit
4 * Remy Card <card@Linux.EU.Org>, 1994-1997
5 * Stelian Pop <stelian@popies.net>, 1999-2000
6 * Stelian Pop <stelian@popies.net> - AlcĂ´ve <www.alcove.com>, 2000-2002
10 * Copyright (c) 1985, 1993
11 * The Regents of the University of California. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 static const char rcsid[] =
40 "$Id: interactive.c,v 1.27 2003/10/26 16:05:48 stelian Exp $";
44 #include <sys/types.h>
45 #include <sys/param.h>
49 #ifdef HAVE_EXT2FS_EXT2_FS_H
50 #include <ext2fs/ext2_fs.h>
52 #include <linux/ext2_fs.h>
54 #include <bsdcompat.h>
57 #include <sys/fcntl.h>
58 #include <bsdcompat.h>
60 #include <ufs/ufs/dinode.h>
61 #include <ufs/ufs/dir.h>
63 #endif /* __linux__ */
64 #include <protocols/dumprestore.h>
67 #include <compaterr.h>
69 #include <compatglob.h>
75 #include <ext2fs/ext2fs.h>
78 #if defined(__linux__) || defined(sunos)
79 extern char * __progname;
86 #include <readline/readline.h>
87 #include <readline/history.h>
89 static char *rl_gets (char *prompt);
90 static void initialize_readline(void);
91 static char **restore_completion (const char *text, int start, int end);
92 static char *command_generator(const char *text, int state);
93 static char *filename_generator(const char *text, int state);
96 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
99 * Things to handle interruptions.
102 static jmp_buf reset;
103 static char *nextarg = NULL;
104 static int pflag = 0; /* prompt mode */
106 * Structure and routines associated with listing directories.
109 dump_ino_t fnum; /* inode number of file */
110 char *fname; /* file name */
111 short len; /* name length */
112 char prefix; /* prefix character */
113 char postfix; /* postfix character */
116 int freeglob; /* glob structure needs to be freed */
117 int argcnt; /* next globbed argument to return */
118 glob_t glob; /* globbing information */
119 char *cmd; /* the current command */
122 static char *copynext __P((char *, char *));
123 static int fcmp __P((const void *, const void *));
124 static void formatf __P((struct afile *, int));
125 static void getcmd __P((char *, char *, char *, int, struct arglist *));
126 struct dirent *glob_readdir __P((RST_DIR *dirp));
127 static int glob_stat __P((const char *, struct stat *));
128 static void mkentry __P((char *, struct direct *, struct afile *));
129 static void printlist __P((char *, char *));
132 * Read and execute commands from the terminal.
139 struct arglist arglist;
140 char curdir[MAXPATHLEN];
141 char name[MAXPATHLEN];
145 initialize_readline();
147 arglist.freeglob = 0;
149 arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
150 arglist.glob.gl_opendir = (void *)rst_opendir;
151 arglist.glob.gl_readdir = (void *)glob_readdir;
152 arglist.glob.gl_closedir = (void *)rst_closedir;
153 arglist.glob.gl_lstat = (int (*)(const char *, void *))glob_stat;
154 arglist.glob.gl_stat = (int (*)(const char *, void *))glob_stat;
155 canon("/", curdir, sizeof(curdir));
157 if (setjmp(reset) != 0) {
158 if (arglist.freeglob != 0) {
159 arglist.freeglob = 0;
161 globfree(&arglist.glob);
167 getcmd(curdir, cmd, name, sizeof(name), &arglist);
170 * Add elements to the extraction list.
173 if (strncmp(cmd, "add", strlen(cmd)) != 0)
175 ino = dirlookup(name);
180 treescan(name, ino, addfile);
183 * Change working directory.
186 if (strncmp(cmd, "cd", strlen(cmd)) != 0)
188 ino = dirlookup(name);
191 if (inodetype(ino) == LEAF) {
192 fprintf(stderr, "%s: not a directory\n", name);
195 (void) strncpy(curdir, name, sizeof(curdir));
196 curdir[sizeof(curdir) - 1] = '\0';
199 * Delete elements from the extraction list.
202 if (strncmp(cmd, "delete", strlen(cmd)) != 0)
204 np = lookupname(name);
205 if (np == NULL || (np->e_flags & NEW) == 0) {
206 fprintf(stderr, "%s: not on extraction list\n", name);
209 treescan(name, np->e_ino, deletefile);
212 * Extract the requested list.
215 if (strncmp(cmd, "extract", strlen(cmd)) != 0)
219 setdirmodes(oflag ? FORCE : 0);
225 * List available commands.
228 if (strncmp(cmd, "help", strlen(cmd)) != 0)
231 fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
232 "Available commands are:\n",
233 "\tls [arg] - list directory\n",
234 "\tcd arg - change directory\n",
235 "\tpwd - print current directory\n",
236 "\tadd [arg] - add `arg' to list of",
237 " files to be extracted\n",
238 "\tdelete [arg] - delete `arg' from",
239 " list of files to be extracted\n",
240 "\textract - extract requested files\n",
241 "\tsetmodes - set modes of requested directories\n",
242 "\tquit - immediately exit program\n",
243 "\twhat - list dump header information\n",
244 "\tverbose - toggle verbose flag",
245 " (useful with ``ls'')\n",
246 "\tprompt - toggle the prompt display\n",
247 "\thelp or `?' - print this list\n",
248 "If no `arg' is supplied, the current",
249 " directory is used\n");
255 if (strncmp(cmd, "ls", strlen(cmd)) != 0)
257 printlist(name, curdir);
260 * Print current directory.
263 if (strncmp(cmd, "pwd", strlen(cmd)) == 0) {
264 if (curdir[1] == '\0')
265 fprintf(stderr, "/\n");
267 fprintf(stderr, "%s\n", &curdir[1]);
270 * Toggle prompt mode.
272 else if (strncmp(cmd, "prompt", strlen(cmd)) == 0) {
274 fprintf(stderr, "prompt mode off\n");
278 fprintf(stderr, "prompt mode on\n");
288 if (strncmp(cmd, "quit", strlen(cmd)) != 0)
292 if (strncmp(cmd, "xit", strlen(cmd)) != 0)
296 * Toggle verbose mode.
299 if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
302 fprintf(stderr, "verbose mode off\n");
306 fprintf(stderr, "verbose mode on\n");
310 * Just restore requested directory modes.
313 if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
318 * Print out dump header information.
321 if (strncmp(cmd, "what", strlen(cmd)) != 0)
330 if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
333 fprintf(stderr, "debugging mode off\n");
337 fprintf(stderr, "debugging mode on\n");
345 fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
352 * Read and parse an interactive command.
353 * The first word on the line is assigned to "cmd". If
354 * there are no arguments on the command line, then "curdir"
355 * is returned as the argument. If there are arguments
356 * on the line they are returned one at a time on each
357 * successive call to getcmd. Each argument is first assigned
358 * to "name". If it does not start with "/" the pathname in
359 * "curdir" is prepended to it. Finally "canon" is called to
360 * eliminate any embedded ".." components.
363 getcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
366 static char input[BUFSIZ];
368 # define rawname input /* save space by reusing input buffer */
371 * Check to see if still processing arguments.
378 * Read a command line and trim off trailing white space.
381 snprintf(input, BUFSIZ, "%s\n", rl_gets(curdir));
385 fprintf(stderr, "%s:%s:%s > ",
388 curdir[1] ? &curdir[1] : "/");
390 fprintf(stderr, "%s > ", __progname);
391 (void) fflush(stderr);
392 (void) fgets(input, BUFSIZ, terminal);
393 } while (!feof(terminal) && input[0] == '\n');
394 if (feof(terminal)) {
395 (void) strcpy(cmd, "quit");
399 for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
400 /* trim off trailing white space and newline */;
403 * Copy the command into "cmd".
405 cp = copynext(input, cmd);
408 * If no argument, use curdir as the default.
411 (void) strncpy(name, curdir, size);
412 name[size - 1] = '\0';
417 * Find the next argument.
420 cp = copynext(nextarg, rawname);
426 * If it is an absolute pathname, canonicalize it and return it.
428 if (rawname[0] == '/') {
429 canon(rawname, name, size);
432 * For relative pathnames, prepend the current directory to
433 * it then canonicalize and return it.
435 snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
436 canon(output, name, size);
438 if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
439 fprintf(stderr, "%s: out of memory\n", ap->cmd);
440 if (ap->glob.gl_pathc == 0)
443 ap->argcnt = ap->glob.gl_pathc;
446 strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
447 name[size - 1] = '\0';
448 if (--ap->argcnt == 0) {
456 * Strip off the next token of the input.
459 copynext(char *input, char *output)
464 for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
465 /* skip to argument */;
467 while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
469 * Handle back slashes.
474 "command lines cannot be continued\n");
481 * The usual unquoted case.
483 if (*cp != '\'' && *cp != '"') {
488 * Handle single and double quotes.
491 while (*cp != quote && *cp != '\0')
494 fprintf(stderr, "missing %c\n", quote);
504 * Canonicalize file names to always start with ``./'' and
505 * remove any embedded "." and ".." components.
508 canon(char *rawname, char *canonname, int len)
512 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
513 (void) strcpy(canonname, "");
514 else if (rawname[0] == '/')
515 (void) strcpy(canonname, ".");
517 (void) strcpy(canonname, "./");
518 if (strlen(canonname) + strlen(rawname) >= (unsigned)len)
519 errx(1, "canonname: not enough buffer space");
521 (void) strcat(canonname, rawname);
523 * Eliminate multiple and trailing '/'s
525 for (cp = np = canonname; *np != '\0'; cp++) {
527 while (*cp == '/' && *np == '/')
534 * Eliminate extraneous "." and ".." from pathnames.
536 for (np = canonname; *np != '\0'; ) {
539 while (*np != '/' && *np != '\0')
541 if (np - cp == 1 && *cp == '.') {
543 (void) strcpy(cp, np);
546 if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
548 while (cp > &canonname[1] && *--cp != '/')
549 /* find beginning of name */;
550 (void) strcpy(cp, np);
557 * Do an "ls" style listing of a directory
560 printlist(char *name, char *basename)
562 struct afile *fp, *list, *listp = NULL;
566 int entries, len, namelen;
567 char locname[MAXPATHLEN + 1];
569 dp = pathsearch(name);
570 if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
571 (!vflag && dp->d_ino == WINO))
573 if ((dirp = rst_opendir(name)) == NULL) {
576 mkentry(name, dp, list);
577 len = strlen(basename) + 1;
578 if (strlen(name) - len > (unsigned)single.len) {
579 freename(single.fname);
580 single.fname = savename(&name[len]);
581 single.len = strlen(single.fname);
585 while ((dp = rst_readdir(dirp)))
588 list = (struct afile *)malloc(entries * sizeof(struct afile));
590 fprintf(stderr, "ls: out of memory\n");
593 if ((dirp = rst_opendir(name)) == NULL)
594 panic("directory reopen failed\n");
595 fprintf(stderr, "%s:\n", name);
598 namelen = snprintf(locname, sizeof(locname), "%s/", name);
599 if (namelen >= (int)sizeof(locname))
600 namelen = sizeof(locname) - 1;
601 while ((dp = rst_readdir(dirp))) {
604 if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
606 if (!vflag && (dp->d_ino == WINO ||
607 strcmp(dp->d_name, ".") == 0 ||
608 strcmp(dp->d_name, "..") == 0))
610 locname[namelen] = '\0';
611 if (namelen + strlen(dp->d_name) >= MAXPATHLEN) {
612 fprintf(stderr, "%s%s: name exceeds %d char\n",
613 locname, dp->d_name, MAXPATHLEN);
615 (void) strncat(locname, dp->d_name,
616 (int)strlen(dp->d_name));
617 mkentry(locname, dp, listp++);
623 fprintf(stderr, "\n");
627 qsort((char *)list, entries, sizeof(struct afile), fcmp);
629 formatf(list, entries);
631 for (fp = listp - 1; fp >= list; fp--)
633 fprintf(stderr, "\n");
639 * Read the contents of a directory.
642 mkentry(char *name, struct direct *dp, struct afile *fp)
647 fp->fnum = dp->d_ino;
648 fp->fname = savename(dp->d_name);
649 for (cp = fp->fname; *cp; cp++)
650 if (!vflag && (*cp < ' ' || *cp >= 0177))
652 fp->len = cp - fp->fname;
653 if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
655 else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
662 fprintf(stderr, "Warning: undefined file type %d\n",
684 /* no need for this */
692 if (inodetype(dp->d_ino) == NODE)
702 * Print out a pretty listing of a directory
705 formatf(struct afile *list, int nentry)
707 struct afile *fp, *endlist;
708 int width, bigino, haveprefix, havepostfix;
709 int i, j, w, precision = 0, columns, lines;
715 endlist = &list[nentry];
716 for (fp = &list[0]; fp < endlist; fp++) {
717 if (bigino < (int)fp->fnum)
721 if (fp->prefix != ' ')
723 if (fp->postfix != ' ')
731 for (precision = 0, i = bigino; i > 0; i /= 10)
733 width += precision + 1;
736 columns = 81 / width;
739 lines = (nentry + columns - 1) / columns;
740 for (i = 0; i < lines; i++) {
741 for (j = 0; j < columns; j++) {
742 fp = &list[j * lines + i];
744 fprintf(stderr, "%*ld ", precision, (long)fp->fnum);
745 fp->len += precision + 1;
748 putc(fp->prefix, stderr);
751 fprintf(stderr, "%s", fp->fname);
753 putc(fp->postfix, stderr);
756 if (fp + lines >= endlist) {
757 fprintf(stderr, "\n");
760 for (w = fp->len; w < width; w++)
767 * Skip over directory entries that are not on the tape
769 * First have to get definition of a dirent.
771 * For Linux the dirent struct is now included from bsdcompat.h
777 #endif /* ! __linux__ */
780 glob_readdir(RST_DIR *dirp)
783 static struct dirent adirent;
785 while ((dp = rst_readdir(dirp)) != NULL) {
786 if (!vflag && dp->d_ino == WINO)
788 if (dflag || TSTINO(dp->d_ino, dumpmap))
793 adirent.d_fileno = dp->d_ino;
794 memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
799 * Return st_mode information in response to stat or lstat calls
802 glob_stat(const char *name, struct stat *stp)
805 dp = pathsearch(name);
806 if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
807 (!vflag && dp->d_ino == WINO))
809 if (inodetype(dp->d_ino) == NODE)
810 stp->st_mode = IFDIR;
812 stp->st_mode = IFREG;
817 * Comparison routine for qsort.
820 fcmp(const void *f1, const void *f2)
822 return (strcmp(((struct afile *)f1)->fname,
823 ((struct afile *)f2)->fname));
827 * respond to interrupts
830 onintr(UNUSED(int signo))
832 int save_errno = errno;
834 if (command == 'i' && runshell)
836 if (reply("restore interrupted, continue") == FAIL)
844 #if !HAVE_READLINE_RLCM
845 #define rl_completion_matches completion_matches
848 /* A static variable for holding the line. */
849 static char *line_read = NULL;
851 static char completion_curdir[MAXPATHLEN];
853 static char *commands[] = {
854 "add ", "cd ", "delete ", "extract ", "help ",
855 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
856 "verbose ", "setmodes ", "what ", "Debug ",
859 static char *files = NULL;
867 snprintf(completion_curdir, MAXPATHLEN, "%s", dir);
868 completion_curdir[MAXPATHLEN - 1] = '\0';
871 sz = 6 + strlen(__progname) + strlen(spcl.c_filesys) + strlen((completion_curdir + 1 ? completion_curdir + 1 : "/"));
872 prompt = (char *)malloc(sz);
875 snprintf(prompt, sz, "%s:%s:%s > ",
878 (completion_curdir + 1 ? completion_curdir + 1 : "/"));
881 sz = 4 + strlen(__progname);
882 prompt = (char *)malloc(sz);
885 snprintf(prompt, sz, "%s > ", __progname);
887 prompt[sz - 1] = '\0';
891 line_read = (char *)NULL;
895 line_read = readline (prompt);
896 } while (line_read && !*line_read);
902 return strdup("quit");
905 add_history (line_read);
911 command_generator(const char *text, int state)
913 static int list_index, len;
921 while ( (name = commands[list_index]) != NULL) {
925 if (strncmp(name, text, len) == 0)
933 filename_generator(const char *text, int state)
935 static int list_index;
940 char pname[MAXPATHLEN];
941 char fname[MAXPATHLEN];
943 char ppname[MAXPATHLEN];
953 if ((slash = strrchr(text, '/')) != NULL) {
954 int idx = slash - text;
955 if (idx > MAXPATHLEN - 2)
956 idx = MAXPATHLEN - 2;
957 strncpy(ppname, text, MAXPATHLEN);
958 ppname[MAXPATHLEN - 1] = '\0';
961 snprintf(pname, MAXPATHLEN, ".%s", ppname);
963 snprintf(pname, MAXPATHLEN, "%s/%s", completion_curdir, ppname);
964 strncpy(fname, ppname + idx + 1, MAXPATHLEN);
966 ppname[idx + 1] = '\0';
969 strncpy(pname, completion_curdir, MAXPATHLEN);
970 strncpy(fname, text, MAXPATHLEN);
973 pname[MAXPATHLEN - 1] = '\0';
974 fname[MAXPATHLEN - 1] = '\0';
975 if ((dirp = rst_opendir(pname)) == NULL)
978 while ((dp = rst_readdir(dirp)))
981 files = (char *)malloc(entries * MAXPATHLEN);
983 fprintf(stderr, "Out of memory\n");
987 if ((dirp = rst_opendir(pname)) == NULL)
988 panic("directory reopen failed\n");
990 while ((dp = rst_readdir(dirp))) {
991 if (TSTINO(dp->d_ino, dumpmap) == 0)
993 if (strcmp(dp->d_name, ".") == 0 ||
994 strcmp(dp->d_name, "..") == 0)
996 if (strncmp(dp->d_name, fname, strlen(fname)) == 0) {
997 if (inodetype(dp->d_ino) == NODE)
998 snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s/", ppname, dp->d_name);
1000 snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s ", ppname, dp->d_name);
1001 *(files + (entries + 1) * MAXPATHLEN - 1) = '\0';
1008 if (list_index >= entries)
1011 name = strdup(files + list_index * MAXPATHLEN);
1018 restore_completion (const char *text, int start, UNUSED(int end))
1023 matches = rl_completion_matches (text, command_generator);
1025 matches = rl_completion_matches (text, filename_generator);
1031 initialize_readline(void)
1033 rl_readline_name = "dump";
1034 rl_attempted_completion_function = restore_completion;
1035 rl_completion_entry_function = NULL;
1036 #if HAVE_READLINE_CAC /* compile with readline 2.0 */
1037 rl_completion_append_character = '\0';
1039 rl_instream = terminal;
1042 #endif /* HAVE_READLINE */