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@cybercable.fr>, 1999-2000
9 * Copyright (c) 1985, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 static const char rcsid
[] =
43 "$Id: interactive.c,v 1.12 2000/06/11 17:26:04 stelian Exp $";
46 #include <sys/param.h>
50 #include <linux/ext2_fs.h>
51 #include <bsdcompat.h>
53 #include <ufs/ufs/dinode.h>
54 #include <ufs/ufs/dir.h>
55 #endif /* __linux__ */
56 #include <protocols/dumprestore.h>
59 #include <compaterr.h>
61 #include <compatglob.h>
67 #include <ext2fs/ext2fs.h>
68 extern char * __progname
;
75 #include <readline/readline.h>
76 #include <readline/history.h>
78 static char *rl_gets (char *prompt
);
79 static void initialize_readline(void);
80 static char **restore_completion (char *text
, int start
, int end
);
81 static char *command_generator(char *text
, int state
);
82 static char *filename_generator(char *text
, int state
);
85 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
88 * Things to handle interruptions.
92 static char *nextarg
= NULL
;
93 static int pflag
= 0; /* prompt mode */
95 * Structure and routines associated with listing directories.
98 ino_t fnum
; /* inode number of file */
99 char *fname
; /* file name */
100 short len
; /* name length */
101 char prefix
; /* prefix character */
102 char postfix
; /* postfix character */
105 int freeglob
; /* glob structure needs to be freed */
106 int argcnt
; /* next globbed argument to return */
107 glob_t glob
; /* globbing information */
108 char *cmd
; /* the current command */
111 static char *copynext
__P((char *, char *));
112 static int fcmp
__P((const void *, const void *));
113 static void formatf
__P((struct afile
*, int));
114 static void getcmd
__P((char *, char *, char *, int, struct arglist
*));
115 struct dirent
*glob_readdir
__P((RST_DIR
*dirp
));
116 static int glob_stat
__P((const char *, struct stat
*));
117 static void mkentry
__P((char *, struct direct
*, struct afile
*));
118 static void printlist
__P((char *, char *));
121 * Read and execute commands from the terminal.
126 register struct entry
*np
;
128 struct arglist arglist
;
129 char curdir
[MAXPATHLEN
];
130 char name
[MAXPATHLEN
];
134 initialize_readline();
136 arglist
.freeglob
= 0;
138 arglist
.glob
.gl_flags
= GLOB_ALTDIRFUNC
;
139 arglist
.glob
.gl_opendir
= (void *)rst_opendir
;
140 arglist
.glob
.gl_readdir
= (void *)glob_readdir
;
141 arglist
.glob
.gl_closedir
= (void *)rst_closedir
;
142 arglist
.glob
.gl_lstat
= glob_stat
;
143 arglist
.glob
.gl_stat
= glob_stat
;
144 canon("/", curdir
, sizeof(curdir
));
146 if (setjmp(reset
) != 0) {
147 if (arglist
.freeglob
!= 0) {
148 arglist
.freeglob
= 0;
150 globfree(&arglist
.glob
);
156 getcmd(curdir
, cmd
, name
, sizeof(name
), &arglist
);
159 * Add elements to the extraction list.
162 if (strncmp(cmd
, "add", strlen(cmd
)) != 0)
164 ino
= dirlookup(name
);
169 treescan(name
, ino
, addfile
);
172 * Change working directory.
175 if (strncmp(cmd
, "cd", strlen(cmd
)) != 0)
177 ino
= dirlookup(name
);
180 if (inodetype(ino
) == LEAF
) {
181 fprintf(stderr
, "%s: not a directory\n", name
);
184 (void) strncpy(curdir
, name
, sizeof(curdir
));
185 curdir
[sizeof(curdir
) - 1] = '\0';
188 * Delete elements from the extraction list.
191 if (strncmp(cmd
, "delete", strlen(cmd
)) != 0)
193 np
= lookupname(name
);
194 if (np
== NULL
|| (np
->e_flags
& NEW
) == 0) {
195 fprintf(stderr
, "%s: not on extraction list\n", name
);
198 treescan(name
, np
->e_ino
, deletefile
);
201 * Extract the requested list.
204 if (strncmp(cmd
, "extract", strlen(cmd
)) != 0)
214 * List available commands.
217 if (strncmp(cmd
, "help", strlen(cmd
)) != 0)
220 fprintf(stderr
, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
221 "Available commands are:\n",
222 "\tls [arg] - list directory\n",
223 "\tcd arg - change directory\n",
224 "\tpwd - print current directory\n",
225 "\tadd [arg] - add `arg' to list of",
226 " files to be extracted\n",
227 "\tdelete [arg] - delete `arg' from",
228 " list of files to be extracted\n",
229 "\textract - extract requested files\n",
230 "\tsetmodes - set modes of requested directories\n",
231 "\tquit - immediately exit program\n",
232 "\twhat - list dump header information\n",
233 "\tverbose - toggle verbose flag",
234 " (useful with ``ls'')\n",
235 "\tprompt - toggle the prompt display\n",
236 "\thelp or `?' - print this list\n",
237 "If no `arg' is supplied, the current",
238 " directory is used\n");
244 if (strncmp(cmd
, "ls", strlen(cmd
)) != 0)
246 printlist(name
, curdir
);
249 * Print current directory.
252 if (strncmp(cmd
, "pwd", strlen(cmd
)) == 0) {
253 if (curdir
[1] == '\0')
254 fprintf(stderr
, "/\n");
256 fprintf(stderr
, "%s\n", &curdir
[1]);
259 * Toggle prompt mode.
261 else if (strncmp(cmd
, "prompt", strlen(cmd
)) == 0) {
263 fprintf(stderr
, "prompt mode off\n");
267 fprintf(stderr
, "prompt mode on\n");
277 if (strncmp(cmd
, "quit", strlen(cmd
)) != 0)
281 if (strncmp(cmd
, "xit", strlen(cmd
)) != 0)
285 * Toggle verbose mode.
288 if (strncmp(cmd
, "verbose", strlen(cmd
)) != 0)
291 fprintf(stderr
, "verbose mode off\n");
295 fprintf(stderr
, "verbose mode on\n");
299 * Just restore requested directory modes.
302 if (strncmp(cmd
, "setmodes", strlen(cmd
)) != 0)
307 * Print out dump header information.
310 if (strncmp(cmd
, "what", strlen(cmd
)) != 0)
318 if (strncmp(cmd
, "Debug", strlen(cmd
)) != 0)
321 fprintf(stderr
, "debugging mode off\n");
325 fprintf(stderr
, "debugging mode on\n");
333 fprintf(stderr
, "%s: unknown command; type ? for help\n", cmd
);
340 * Read and parse an interactive command.
341 * The first word on the line is assigned to "cmd". If
342 * there are no arguments on the command line, then "curdir"
343 * is returned as the argument. If there are arguments
344 * on the line they are returned one at a time on each
345 * successive call to getcmd. Each argument is first assigned
346 * to "name". If it does not start with "/" the pathname in
347 * "curdir" is prepended to it. Finally "canon" is called to
348 * eliminate any embedded ".." components.
351 getcmd(char *curdir
, char *cmd
, char *name
, int size
, struct arglist
*ap
)
354 static char input
[BUFSIZ
];
356 # define rawname input /* save space by reusing input buffer */
359 * Check to see if still processing arguments.
366 * Read a command line and trim off trailing white space.
369 snprintf(input
, BUFSIZ
, "%s\n", rl_gets(curdir
));
373 fprintf(stderr
, "%s:%s:%s > ",
376 curdir
[1] ? &curdir
[1] : "/");
378 fprintf(stderr
, "%s > ", __progname
);
379 (void) fflush(stderr
);
380 (void) fgets(input
, BUFSIZ
, terminal
);
381 } while (!feof(terminal
) && input
[0] == '\n');
382 if (feof(terminal
)) {
383 (void) strcpy(cmd
, "quit");
387 for (cp
= &input
[strlen(input
) - 2]; *cp
== ' ' || *cp
== '\t'; cp
--)
388 /* trim off trailing white space and newline */;
391 * Copy the command into "cmd".
393 cp
= copynext(input
, cmd
);
396 * If no argument, use curdir as the default.
399 (void) strncpy(name
, curdir
, size
);
400 name
[size
- 1] = '\0';
405 * Find the next argument.
408 cp
= copynext(nextarg
, rawname
);
414 * If it is an absolute pathname, canonicalize it and return it.
416 if (rawname
[0] == '/') {
417 canon(rawname
, name
, size
);
420 * For relative pathnames, prepend the current directory to
421 * it then canonicalize and return it.
423 snprintf(output
, sizeof(output
), "%s/%s", curdir
, rawname
);
424 canon(output
, name
, size
);
426 if (glob(name
, GLOB_ALTDIRFUNC
, NULL
, &ap
->glob
) < 0)
427 fprintf(stderr
, "%s: out of memory\n", ap
->cmd
);
428 if (ap
->glob
.gl_pathc
== 0)
431 ap
->argcnt
= ap
->glob
.gl_pathc
;
434 strncpy(name
, ap
->glob
.gl_pathv
[ap
->glob
.gl_pathc
- ap
->argcnt
], size
);
435 name
[size
- 1] = '\0';
436 if (--ap
->argcnt
== 0) {
444 * Strip off the next token of the input.
447 copynext(char *input
, char *output
)
449 register char *cp
, *bp
;
452 for (cp
= input
; *cp
== ' ' || *cp
== '\t'; cp
++)
453 /* skip to argument */;
455 while (*cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0') {
457 * Handle back slashes.
462 "command lines cannot be continued\n");
469 * The usual unquoted case.
471 if (*cp
!= '\'' && *cp
!= '"') {
476 * Handle single and double quotes.
479 while (*cp
!= quote
&& *cp
!= '\0')
482 fprintf(stderr
, "missing %c\n", quote
);
492 * Canonicalize file names to always start with ``./'' and
493 * remove any embedded "." and ".." components.
496 canon(char *rawname
, char *canonname
, int len
)
498 register char *cp
, *np
;
500 if (strcmp(rawname
, ".") == 0 || strncmp(rawname
, "./", 2) == 0)
501 (void) strcpy(canonname
, "");
502 else if (rawname
[0] == '/')
503 (void) strcpy(canonname
, ".");
505 (void) strcpy(canonname
, "./");
506 if (strlen(canonname
) + strlen(rawname
) >= len
)
507 errx(1, "canonname: not enough buffer space");
509 (void) strcat(canonname
, rawname
);
511 * Eliminate multiple and trailing '/'s
513 for (cp
= np
= canonname
; *np
!= '\0'; cp
++) {
515 while (*cp
== '/' && *np
== '/')
522 * Eliminate extraneous "." and ".." from pathnames.
524 for (np
= canonname
; *np
!= '\0'; ) {
527 while (*np
!= '/' && *np
!= '\0')
529 if (np
- cp
== 1 && *cp
== '.') {
531 (void) strcpy(cp
, np
);
534 if (np
- cp
== 2 && strncmp(cp
, "..", 2) == 0) {
536 while (cp
> &canonname
[1] && *--cp
!= '/')
537 /* find beginning of name */;
538 (void) strcpy(cp
, np
);
545 * Do an "ls" style listing of a directory
548 printlist(char *name
, char *basename
)
550 register struct afile
*fp
, *list
, *listp
= NULL
;
551 register struct direct
*dp
;
554 int entries
, len
, namelen
;
555 char locname
[MAXPATHLEN
+ 1];
557 dp
= pathsearch(name
);
558 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
559 (!vflag
&& dp
->d_ino
== WINO
))
561 if ((dirp
= rst_opendir(name
)) == NULL
) {
564 mkentry(name
, dp
, list
);
565 len
= strlen(basename
) + 1;
566 if (strlen(name
) - len
> single
.len
) {
567 freename(single
.fname
);
568 single
.fname
= savename(&name
[len
]);
569 single
.len
= strlen(single
.fname
);
573 while ((dp
= rst_readdir(dirp
)))
576 list
= (struct afile
*)malloc(entries
* sizeof(struct afile
));
578 fprintf(stderr
, "ls: out of memory\n");
581 if ((dirp
= rst_opendir(name
)) == NULL
)
582 panic("directory reopen failed\n");
583 fprintf(stderr
, "%s:\n", name
);
586 namelen
= snprintf(locname
, sizeof(locname
), "%s/", name
);
587 if (namelen
>= sizeof(locname
))
588 namelen
= sizeof(locname
) - 1;
589 while ((dp
= rst_readdir(dirp
))) {
592 if (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0)
594 if (!vflag
&& (dp
->d_ino
== WINO
||
595 strcmp(dp
->d_name
, ".") == 0 ||
596 strcmp(dp
->d_name
, "..") == 0))
598 locname
[namelen
] = '\0';
599 if (namelen
+ strlen(dp
->d_name
) >= MAXPATHLEN
) {
600 fprintf(stderr
, "%s%s: name exceeds %d char\n",
601 locname
, dp
->d_name
, MAXPATHLEN
);
603 (void) strncat(locname
, dp
->d_name
,
604 (int)strlen(dp
->d_name
));
605 mkentry(locname
, dp
, listp
++);
611 fprintf(stderr
, "\n");
615 qsort((char *)list
, entries
, sizeof(struct afile
), fcmp
);
617 formatf(list
, entries
);
619 for (fp
= listp
- 1; fp
>= list
; fp
--)
621 fprintf(stderr
, "\n");
627 * Read the contents of a directory.
630 mkentry(char *name
, struct direct
*dp
, struct afile
*fp
)
635 fp
->fnum
= dp
->d_ino
;
636 fp
->fname
= savename(dp
->d_name
);
637 for (cp
= fp
->fname
; *cp
; cp
++)
638 if (!vflag
&& (*cp
< ' ' || *cp
>= 0177))
640 fp
->len
= cp
- fp
->fname
;
641 if (dflag
&& TSTINO(fp
->fnum
, dumpmap
) == 0)
643 else if ((np
= lookupname(name
)) != NULL
&& (np
->e_flags
& NEW
))
650 fprintf(stderr
, "Warning: undefined file type %d\n",
672 /* no need for this */
680 if (inodetype(dp
->d_ino
) == NODE
)
690 * Print out a pretty listing of a directory
693 formatf(struct afile
*list
, int nentry
)
695 register struct afile
*fp
, *endlist
;
696 int width
, bigino
, haveprefix
, havepostfix
;
697 int i
, j
, w
, precision
= 0, columns
, lines
;
703 endlist
= &list
[nentry
];
704 for (fp
= &list
[0]; fp
< endlist
; fp
++) {
705 if (bigino
< fp
->fnum
)
709 if (fp
->prefix
!= ' ')
711 if (fp
->postfix
!= ' ')
719 for (precision
= 0, i
= bigino
; i
> 0; i
/= 10)
721 width
+= precision
+ 1;
724 columns
= 81 / width
;
727 lines
= (nentry
+ columns
- 1) / columns
;
728 for (i
= 0; i
< lines
; i
++) {
729 for (j
= 0; j
< columns
; j
++) {
730 fp
= &list
[j
* lines
+ i
];
732 fprintf(stderr
, "%*ld ", precision
, (long)fp
->fnum
);
733 fp
->len
+= precision
+ 1;
736 putc(fp
->prefix
, stderr
);
739 fprintf(stderr
, "%s", fp
->fname
);
741 putc(fp
->postfix
, stderr
);
744 if (fp
+ lines
>= endlist
) {
745 fprintf(stderr
, "\n");
748 for (w
= fp
->len
; w
< width
; w
++)
755 * Skip over directory entries that are not on the tape
757 * First have to get definition of a dirent.
759 * For Linux the dirent struct is now included from bsdcompat.h
765 #endif /* ! __linux__ */
768 glob_readdir(RST_DIR
*dirp
)
771 static struct dirent adirent
;
773 while ((dp
= rst_readdir(dirp
)) != NULL
) {
774 if (!vflag
&& dp
->d_ino
== WINO
)
776 if (dflag
|| TSTINO(dp
->d_ino
, dumpmap
))
781 adirent
.d_fileno
= dp
->d_ino
;
782 memmove(adirent
.d_name
, dp
->d_name
, dp
->d_namlen
+ 1);
787 * Return st_mode information in response to stat or lstat calls
790 glob_stat(const char *name
, struct stat
*stp
)
792 register struct direct
*dp
;
793 dp
= pathsearch(name
);
794 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
795 (!vflag
&& dp
->d_ino
== WINO
))
797 if (inodetype(dp
->d_ino
) == NODE
)
798 stp
->st_mode
= IFDIR
;
800 stp
->st_mode
= IFREG
;
805 * Comparison routine for qsort.
808 fcmp(const void *f1
, const void *f2
)
810 return (strcmp(((struct afile
*)f1
)->fname
,
811 ((struct afile
*)f2
)->fname
));
815 * respond to interrupts
820 int save_errno
= errno
;
822 if (command
== 'i' && runshell
)
824 if (reply("restore interrupted, continue") == FAIL
)
832 /* A static variable for holding the line. */
833 static char *line_read
= NULL
;
835 static char completion_curdir
[MAXPATHLEN
];
837 static char *commands
[] = {
838 "add ", "cd ", "delete ", "extract ", "help ",
839 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
840 "verbose ", "setmodes ", "what ", "Debug ",
843 static char *files
= NULL
;
851 snprintf(completion_curdir
, MAXPATHLEN
, "%s", dir
);
852 completion_curdir
[MAXPATHLEN
- 1] = '\0';
855 sz
= 6 + strlen(__progname
) + strlen(spcl
.c_filesys
) + strlen((completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
856 prompt
= (char *)malloc(sz
);
859 snprintf(prompt
, sz
, "%s:%s:%s > ",
862 (completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
865 sz
= 4 + strlen(__progname
);
866 prompt
= (char *)malloc(sz
);
869 snprintf(prompt
, sz
, "%s > ", __progname
);
871 prompt
[sz
- 1] = '\0';
875 line_read
= (char *)NULL
;
879 line_read
= readline (prompt
);
880 } while (line_read
&& !*line_read
);
886 return strdup("quit");
889 add_history (line_read
);
895 initialize_readline(void)
897 rl_attempted_completion_function
= restore_completion
;
898 rl_completion_entry_function
= (Function
*)NULL
;
899 rl_completion_append_character
= '\0';
900 rl_instream
= terminal
;
904 restore_completion (char *text
, int start
, int end
)
909 matches
= completion_matches (text
, command_generator
);
911 matches
= completion_matches (text
, filename_generator
);
917 command_generator(char *text
, int state
)
919 static int list_index
, len
;
927 while ( (name
= commands
[list_index
]) != NULL
) {
931 if (strncmp(name
, text
, len
) == 0)
939 filename_generator(char *text
, int state
)
941 static int list_index
;
946 char pname
[MAXPATHLEN
];
947 char fname
[MAXPATHLEN
];
949 char ppname
[MAXPATHLEN
];
959 if ((slash
= strrchr(text
, '/')) != NULL
) {
960 int idx
= slash
- text
;
961 if (idx
> MAXPATHLEN
- 2)
962 idx
= MAXPATHLEN
- 2;
963 strncpy(ppname
, text
, MAXPATHLEN
);
964 ppname
[MAXPATHLEN
- 1] = '\0';
967 snprintf(pname
, MAXPATHLEN
, ".%s", ppname
);
969 snprintf(pname
, MAXPATHLEN
, "%s/%s", completion_curdir
, ppname
);
970 strncpy(fname
, ppname
+ idx
+ 1, MAXPATHLEN
);
972 ppname
[idx
+ 1] = '\0';
975 strncpy(pname
, completion_curdir
, MAXPATHLEN
);
976 strncpy(fname
, text
, MAXPATHLEN
);
979 pname
[MAXPATHLEN
- 1] = '\0';
980 fname
[MAXPATHLEN
- 1] = '\0';
981 if ((dirp
= rst_opendir(pname
)) == NULL
)
984 while ((dp
= rst_readdir(dirp
)))
987 files
= (char *)malloc(entries
* MAXPATHLEN
);
989 fprintf(stderr
, "Out of memory\n");
993 if ((dirp
= rst_opendir(pname
)) == NULL
)
994 panic("directory reopen failed\n");
996 while ((dp
= rst_readdir(dirp
))) {
997 if (TSTINO(dp
->d_ino
, dumpmap
) == 0)
999 if (strcmp(dp
->d_name
, ".") == 0 ||
1000 strcmp(dp
->d_name
, "..") == 0)
1002 if (strncmp(dp
->d_name
, fname
, strlen(fname
)) == 0) {
1003 if (inodetype(dp
->d_ino
) == NODE
)
1004 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s/", ppname
, dp
->d_name
);
1006 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s ", ppname
, dp
->d_name
);
1007 *(files
+ (entries
+ 1) * MAXPATHLEN
- 1) = '\0';
1014 if (list_index
>= entries
)
1017 name
= strdup(files
+ list_index
* MAXPATHLEN
);
1022 #endif /* HAVE_READLINE */