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 <pop@noos.fr>, 1999-2000
6 * Stelian Pop <pop@noos.fr> - AlcĂ´ve <www.alcove.fr>, 2000
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. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 static const char rcsid
[] =
44 "$Id: interactive.c,v 1.16 2001/03/20 10:02:48 stelian Exp $";
48 #include <sys/param.h>
52 #include <linux/ext2_fs.h>
53 #include <bsdcompat.h>
55 #include <ufs/ufs/dinode.h>
56 #include <ufs/ufs/dir.h>
57 #endif /* __linux__ */
58 #include <protocols/dumprestore.h>
61 #include <compaterr.h>
63 #include <compatglob.h>
69 #include <ext2fs/ext2fs.h>
70 extern char * __progname
;
77 #include <readline/readline.h>
78 #include <readline/history.h>
80 static char *rl_gets (char *prompt
);
81 static void initialize_readline(void);
82 static char **restore_completion (char *text
, int start
, int end
);
83 static char *command_generator(char *text
, int state
);
84 static char *filename_generator(char *text
, int state
);
87 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
90 * Things to handle interruptions.
94 static char *nextarg
= NULL
;
95 static int pflag
= 0; /* prompt mode */
97 * Structure and routines associated with listing directories.
100 dump_ino_t fnum
; /* inode number of file */
101 char *fname
; /* file name */
102 short len
; /* name length */
103 char prefix
; /* prefix character */
104 char postfix
; /* postfix character */
107 int freeglob
; /* glob structure needs to be freed */
108 int argcnt
; /* next globbed argument to return */
109 glob_t glob
; /* globbing information */
110 char *cmd
; /* the current command */
113 static char *copynext
__P((char *, char *));
114 static int fcmp
__P((const void *, const void *));
115 static void formatf
__P((struct afile
*, int));
116 static void getcmd
__P((char *, char *, char *, int, struct arglist
*));
117 struct dirent
*glob_readdir
__P((RST_DIR
*dirp
));
118 static int glob_stat
__P((const char *, struct stat
*));
119 static void mkentry
__P((char *, struct direct
*, struct afile
*));
120 static void printlist
__P((char *, char *));
123 * Read and execute commands from the terminal.
128 register struct entry
*np
;
130 struct arglist arglist
;
131 char curdir
[MAXPATHLEN
];
132 char name
[MAXPATHLEN
];
136 initialize_readline();
138 arglist
.freeglob
= 0;
140 arglist
.glob
.gl_flags
= GLOB_ALTDIRFUNC
;
141 arglist
.glob
.gl_opendir
= (void *)rst_opendir
;
142 arglist
.glob
.gl_readdir
= (void *)glob_readdir
;
143 arglist
.glob
.gl_closedir
= (void *)rst_closedir
;
144 arglist
.glob
.gl_lstat
= glob_stat
;
145 arglist
.glob
.gl_stat
= glob_stat
;
146 canon("/", curdir
, sizeof(curdir
));
148 if (setjmp(reset
) != 0) {
149 if (arglist
.freeglob
!= 0) {
150 arglist
.freeglob
= 0;
152 globfree(&arglist
.glob
);
158 getcmd(curdir
, cmd
, name
, sizeof(name
), &arglist
);
161 * Add elements to the extraction list.
164 if (strncmp(cmd
, "add", strlen(cmd
)) != 0)
166 ino
= dirlookup(name
);
171 treescan(name
, ino
, addfile
);
174 * Change working directory.
177 if (strncmp(cmd
, "cd", strlen(cmd
)) != 0)
179 ino
= dirlookup(name
);
182 if (inodetype(ino
) == LEAF
) {
183 fprintf(stderr
, "%s: not a directory\n", name
);
186 (void) strncpy(curdir
, name
, sizeof(curdir
));
187 curdir
[sizeof(curdir
) - 1] = '\0';
190 * Delete elements from the extraction list.
193 if (strncmp(cmd
, "delete", strlen(cmd
)) != 0)
195 np
= lookupname(name
);
196 if (np
== NULL
|| (np
->e_flags
& NEW
) == 0) {
197 fprintf(stderr
, "%s: not on extraction list\n", name
);
200 treescan(name
, np
->e_ino
, deletefile
);
203 * Extract the requested list.
206 if (strncmp(cmd
, "extract", strlen(cmd
)) != 0)
216 * List available commands.
219 if (strncmp(cmd
, "help", strlen(cmd
)) != 0)
222 fprintf(stderr
, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
223 "Available commands are:\n",
224 "\tls [arg] - list directory\n",
225 "\tcd arg - change directory\n",
226 "\tpwd - print current directory\n",
227 "\tadd [arg] - add `arg' to list of",
228 " files to be extracted\n",
229 "\tdelete [arg] - delete `arg' from",
230 " list of files to be extracted\n",
231 "\textract - extract requested files\n",
232 "\tsetmodes - set modes of requested directories\n",
233 "\tquit - immediately exit program\n",
234 "\twhat - list dump header information\n",
235 "\tverbose - toggle verbose flag",
236 " (useful with ``ls'')\n",
237 "\tprompt - toggle the prompt display\n",
238 "\thelp or `?' - print this list\n",
239 "If no `arg' is supplied, the current",
240 " directory is used\n");
246 if (strncmp(cmd
, "ls", strlen(cmd
)) != 0)
248 printlist(name
, curdir
);
251 * Print current directory.
254 if (strncmp(cmd
, "pwd", strlen(cmd
)) == 0) {
255 if (curdir
[1] == '\0')
256 fprintf(stderr
, "/\n");
258 fprintf(stderr
, "%s\n", &curdir
[1]);
261 * Toggle prompt mode.
263 else if (strncmp(cmd
, "prompt", strlen(cmd
)) == 0) {
265 fprintf(stderr
, "prompt mode off\n");
269 fprintf(stderr
, "prompt mode on\n");
279 if (strncmp(cmd
, "quit", strlen(cmd
)) != 0)
283 if (strncmp(cmd
, "xit", strlen(cmd
)) != 0)
287 * Toggle verbose mode.
290 if (strncmp(cmd
, "verbose", strlen(cmd
)) != 0)
293 fprintf(stderr
, "verbose mode off\n");
297 fprintf(stderr
, "verbose mode on\n");
301 * Just restore requested directory modes.
304 if (strncmp(cmd
, "setmodes", strlen(cmd
)) != 0)
309 * Print out dump header information.
312 if (strncmp(cmd
, "what", strlen(cmd
)) != 0)
320 if (strncmp(cmd
, "Debug", strlen(cmd
)) != 0)
323 fprintf(stderr
, "debugging mode off\n");
327 fprintf(stderr
, "debugging mode on\n");
335 fprintf(stderr
, "%s: unknown command; type ? for help\n", cmd
);
342 * Read and parse an interactive command.
343 * The first word on the line is assigned to "cmd". If
344 * there are no arguments on the command line, then "curdir"
345 * is returned as the argument. If there are arguments
346 * on the line they are returned one at a time on each
347 * successive call to getcmd. Each argument is first assigned
348 * to "name". If it does not start with "/" the pathname in
349 * "curdir" is prepended to it. Finally "canon" is called to
350 * eliminate any embedded ".." components.
353 getcmd(char *curdir
, char *cmd
, char *name
, int size
, struct arglist
*ap
)
356 static char input
[BUFSIZ
];
358 # define rawname input /* save space by reusing input buffer */
361 * Check to see if still processing arguments.
368 * Read a command line and trim off trailing white space.
371 snprintf(input
, BUFSIZ
, "%s\n", rl_gets(curdir
));
375 fprintf(stderr
, "%s:%s:%s > ",
378 curdir
[1] ? &curdir
[1] : "/");
380 fprintf(stderr
, "%s > ", __progname
);
381 (void) fflush(stderr
);
382 (void) fgets(input
, BUFSIZ
, terminal
);
383 } while (!feof(terminal
) && input
[0] == '\n');
384 if (feof(terminal
)) {
385 (void) strcpy(cmd
, "quit");
389 for (cp
= &input
[strlen(input
) - 2]; *cp
== ' ' || *cp
== '\t'; cp
--)
390 /* trim off trailing white space and newline */;
393 * Copy the command into "cmd".
395 cp
= copynext(input
, cmd
);
398 * If no argument, use curdir as the default.
401 (void) strncpy(name
, curdir
, size
);
402 name
[size
- 1] = '\0';
407 * Find the next argument.
410 cp
= copynext(nextarg
, rawname
);
416 * If it is an absolute pathname, canonicalize it and return it.
418 if (rawname
[0] == '/') {
419 canon(rawname
, name
, size
);
422 * For relative pathnames, prepend the current directory to
423 * it then canonicalize and return it.
425 snprintf(output
, sizeof(output
), "%s/%s", curdir
, rawname
);
426 canon(output
, name
, size
);
428 if (glob(name
, GLOB_ALTDIRFUNC
, NULL
, &ap
->glob
) < 0)
429 fprintf(stderr
, "%s: out of memory\n", ap
->cmd
);
430 if (ap
->glob
.gl_pathc
== 0)
433 ap
->argcnt
= ap
->glob
.gl_pathc
;
436 strncpy(name
, ap
->glob
.gl_pathv
[ap
->glob
.gl_pathc
- ap
->argcnt
], size
);
437 name
[size
- 1] = '\0';
438 if (--ap
->argcnt
== 0) {
446 * Strip off the next token of the input.
449 copynext(char *input
, char *output
)
451 register char *cp
, *bp
;
454 for (cp
= input
; *cp
== ' ' || *cp
== '\t'; cp
++)
455 /* skip to argument */;
457 while (*cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0') {
459 * Handle back slashes.
464 "command lines cannot be continued\n");
471 * The usual unquoted case.
473 if (*cp
!= '\'' && *cp
!= '"') {
478 * Handle single and double quotes.
481 while (*cp
!= quote
&& *cp
!= '\0')
484 fprintf(stderr
, "missing %c\n", quote
);
494 * Canonicalize file names to always start with ``./'' and
495 * remove any embedded "." and ".." components.
498 canon(char *rawname
, char *canonname
, int len
)
500 register char *cp
, *np
;
502 if (strcmp(rawname
, ".") == 0 || strncmp(rawname
, "./", 2) == 0)
503 (void) strcpy(canonname
, "");
504 else if (rawname
[0] == '/')
505 (void) strcpy(canonname
, ".");
507 (void) strcpy(canonname
, "./");
508 if (strlen(canonname
) + strlen(rawname
) >= len
)
509 errx(1, "canonname: not enough buffer space");
511 (void) strcat(canonname
, rawname
);
513 * Eliminate multiple and trailing '/'s
515 for (cp
= np
= canonname
; *np
!= '\0'; cp
++) {
517 while (*cp
== '/' && *np
== '/')
524 * Eliminate extraneous "." and ".." from pathnames.
526 for (np
= canonname
; *np
!= '\0'; ) {
529 while (*np
!= '/' && *np
!= '\0')
531 if (np
- cp
== 1 && *cp
== '.') {
533 (void) strcpy(cp
, np
);
536 if (np
- cp
== 2 && strncmp(cp
, "..", 2) == 0) {
538 while (cp
> &canonname
[1] && *--cp
!= '/')
539 /* find beginning of name */;
540 (void) strcpy(cp
, np
);
547 * Do an "ls" style listing of a directory
550 printlist(char *name
, char *basename
)
552 register struct afile
*fp
, *list
, *listp
= NULL
;
553 register struct direct
*dp
;
556 int entries
, len
, namelen
;
557 char locname
[MAXPATHLEN
+ 1];
559 dp
= pathsearch(name
);
560 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
561 (!vflag
&& dp
->d_ino
== WINO
))
563 if ((dirp
= rst_opendir(name
)) == NULL
) {
566 mkentry(name
, dp
, list
);
567 len
= strlen(basename
) + 1;
568 if (strlen(name
) - len
> single
.len
) {
569 freename(single
.fname
);
570 single
.fname
= savename(&name
[len
]);
571 single
.len
= strlen(single
.fname
);
575 while ((dp
= rst_readdir(dirp
)))
578 list
= (struct afile
*)malloc(entries
* sizeof(struct afile
));
580 fprintf(stderr
, "ls: out of memory\n");
583 if ((dirp
= rst_opendir(name
)) == NULL
)
584 panic("directory reopen failed\n");
585 fprintf(stderr
, "%s:\n", name
);
588 namelen
= snprintf(locname
, sizeof(locname
), "%s/", name
);
589 if (namelen
>= sizeof(locname
))
590 namelen
= sizeof(locname
) - 1;
591 while ((dp
= rst_readdir(dirp
))) {
594 if (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0)
596 if (!vflag
&& (dp
->d_ino
== WINO
||
597 strcmp(dp
->d_name
, ".") == 0 ||
598 strcmp(dp
->d_name
, "..") == 0))
600 locname
[namelen
] = '\0';
601 if (namelen
+ strlen(dp
->d_name
) >= MAXPATHLEN
) {
602 fprintf(stderr
, "%s%s: name exceeds %d char\n",
603 locname
, dp
->d_name
, MAXPATHLEN
);
605 (void) strncat(locname
, dp
->d_name
,
606 (int)strlen(dp
->d_name
));
607 mkentry(locname
, dp
, listp
++);
613 fprintf(stderr
, "\n");
617 qsort((char *)list
, entries
, sizeof(struct afile
), fcmp
);
619 formatf(list
, entries
);
621 for (fp
= listp
- 1; fp
>= list
; fp
--)
623 fprintf(stderr
, "\n");
629 * Read the contents of a directory.
632 mkentry(char *name
, struct direct
*dp
, struct afile
*fp
)
637 fp
->fnum
= dp
->d_ino
;
638 fp
->fname
= savename(dp
->d_name
);
639 for (cp
= fp
->fname
; *cp
; cp
++)
640 if (!vflag
&& (*cp
< ' ' || *cp
>= 0177))
642 fp
->len
= cp
- fp
->fname
;
643 if (dflag
&& TSTINO(fp
->fnum
, dumpmap
) == 0)
645 else if ((np
= lookupname(name
)) != NULL
&& (np
->e_flags
& NEW
))
652 fprintf(stderr
, "Warning: undefined file type %d\n",
674 /* no need for this */
682 if (inodetype(dp
->d_ino
) == NODE
)
692 * Print out a pretty listing of a directory
695 formatf(struct afile
*list
, int nentry
)
697 register struct afile
*fp
, *endlist
;
698 int width
, bigino
, haveprefix
, havepostfix
;
699 int i
, j
, w
, precision
= 0, columns
, lines
;
705 endlist
= &list
[nentry
];
706 for (fp
= &list
[0]; fp
< endlist
; fp
++) {
707 if (bigino
< fp
->fnum
)
711 if (fp
->prefix
!= ' ')
713 if (fp
->postfix
!= ' ')
721 for (precision
= 0, i
= bigino
; i
> 0; i
/= 10)
723 width
+= precision
+ 1;
726 columns
= 81 / width
;
729 lines
= (nentry
+ columns
- 1) / columns
;
730 for (i
= 0; i
< lines
; i
++) {
731 for (j
= 0; j
< columns
; j
++) {
732 fp
= &list
[j
* lines
+ i
];
734 fprintf(stderr
, "%*ld ", precision
, (long)fp
->fnum
);
735 fp
->len
+= precision
+ 1;
738 putc(fp
->prefix
, stderr
);
741 fprintf(stderr
, "%s", fp
->fname
);
743 putc(fp
->postfix
, stderr
);
746 if (fp
+ lines
>= endlist
) {
747 fprintf(stderr
, "\n");
750 for (w
= fp
->len
; w
< width
; w
++)
757 * Skip over directory entries that are not on the tape
759 * First have to get definition of a dirent.
761 * For Linux the dirent struct is now included from bsdcompat.h
767 #endif /* ! __linux__ */
770 glob_readdir(RST_DIR
*dirp
)
773 static struct dirent adirent
;
775 while ((dp
= rst_readdir(dirp
)) != NULL
) {
776 if (!vflag
&& dp
->d_ino
== WINO
)
778 if (dflag
|| TSTINO(dp
->d_ino
, dumpmap
))
783 adirent
.d_fileno
= dp
->d_ino
;
784 memmove(adirent
.d_name
, dp
->d_name
, dp
->d_namlen
+ 1);
789 * Return st_mode information in response to stat or lstat calls
792 glob_stat(const char *name
, struct stat
*stp
)
794 register struct direct
*dp
;
795 dp
= pathsearch(name
);
796 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
797 (!vflag
&& dp
->d_ino
== WINO
))
799 if (inodetype(dp
->d_ino
) == NODE
)
800 stp
->st_mode
= IFDIR
;
802 stp
->st_mode
= IFREG
;
807 * Comparison routine for qsort.
810 fcmp(const void *f1
, const void *f2
)
812 return (strcmp(((struct afile
*)f1
)->fname
,
813 ((struct afile
*)f2
)->fname
));
817 * respond to interrupts
822 int save_errno
= errno
;
824 if (command
== 'i' && runshell
)
826 if (reply("restore interrupted, continue") == FAIL
)
834 /* A static variable for holding the line. */
835 static char *line_read
= NULL
;
837 static char completion_curdir
[MAXPATHLEN
];
839 static char *commands
[] = {
840 "add ", "cd ", "delete ", "extract ", "help ",
841 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
842 "verbose ", "setmodes ", "what ", "Debug ",
845 static char *files
= NULL
;
853 snprintf(completion_curdir
, MAXPATHLEN
, "%s", dir
);
854 completion_curdir
[MAXPATHLEN
- 1] = '\0';
857 sz
= 6 + strlen(__progname
) + strlen(spcl
.c_filesys
) + strlen((completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
858 prompt
= (char *)malloc(sz
);
861 snprintf(prompt
, sz
, "%s:%s:%s > ",
864 (completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
867 sz
= 4 + strlen(__progname
);
868 prompt
= (char *)malloc(sz
);
871 snprintf(prompt
, sz
, "%s > ", __progname
);
873 prompt
[sz
- 1] = '\0';
877 line_read
= (char *)NULL
;
881 line_read
= readline (prompt
);
882 } while (line_read
&& !*line_read
);
888 return strdup("quit");
891 add_history (line_read
);
897 initialize_readline(void)
899 rl_attempted_completion_function
= restore_completion
;
900 rl_completion_entry_function
= (Function
*)NULL
;
901 rl_completion_append_character
= '\0';
902 rl_instream
= terminal
;
906 restore_completion (char *text
, int start
, int end
)
911 matches
= completion_matches (text
, command_generator
);
913 matches
= completion_matches (text
, filename_generator
);
919 command_generator(char *text
, int state
)
921 static int list_index
, len
;
929 while ( (name
= commands
[list_index
]) != NULL
) {
933 if (strncmp(name
, text
, len
) == 0)
941 filename_generator(char *text
, int state
)
943 static int list_index
;
948 char pname
[MAXPATHLEN
];
949 char fname
[MAXPATHLEN
];
951 char ppname
[MAXPATHLEN
];
961 if ((slash
= strrchr(text
, '/')) != NULL
) {
962 int idx
= slash
- text
;
963 if (idx
> MAXPATHLEN
- 2)
964 idx
= MAXPATHLEN
- 2;
965 strncpy(ppname
, text
, MAXPATHLEN
);
966 ppname
[MAXPATHLEN
- 1] = '\0';
969 snprintf(pname
, MAXPATHLEN
, ".%s", ppname
);
971 snprintf(pname
, MAXPATHLEN
, "%s/%s", completion_curdir
, ppname
);
972 strncpy(fname
, ppname
+ idx
+ 1, MAXPATHLEN
);
974 ppname
[idx
+ 1] = '\0';
977 strncpy(pname
, completion_curdir
, MAXPATHLEN
);
978 strncpy(fname
, text
, MAXPATHLEN
);
981 pname
[MAXPATHLEN
- 1] = '\0';
982 fname
[MAXPATHLEN
- 1] = '\0';
983 if ((dirp
= rst_opendir(pname
)) == NULL
)
986 while ((dp
= rst_readdir(dirp
)))
989 files
= (char *)malloc(entries
* MAXPATHLEN
);
991 fprintf(stderr
, "Out of memory\n");
995 if ((dirp
= rst_opendir(pname
)) == NULL
)
996 panic("directory reopen failed\n");
998 while ((dp
= rst_readdir(dirp
))) {
999 if (TSTINO(dp
->d_ino
, dumpmap
) == 0)
1001 if (strcmp(dp
->d_name
, ".") == 0 ||
1002 strcmp(dp
->d_name
, "..") == 0)
1004 if (strncmp(dp
->d_name
, fname
, strlen(fname
)) == 0) {
1005 if (inodetype(dp
->d_ino
) == NODE
)
1006 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s/", ppname
, dp
->d_name
);
1008 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s ", ppname
, dp
->d_name
);
1009 *(files
+ (entries
+ 1) * MAXPATHLEN
- 1) = '\0';
1016 if (list_index
>= entries
)
1019 name
= strdup(files
+ list_index
* MAXPATHLEN
);
1024 #endif /* HAVE_READLINE */