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.17 2001/06/18 10:58:28 stelian Exp $";
48 #include <sys/param.h>
52 #ifdef HAVE_EXT2FS_EXT2_FS_H
53 #include <ext2fs/ext2_fs.h>
55 #include <linux/ext2_fs.h>
57 #include <bsdcompat.h>
59 #include <ufs/ufs/dinode.h>
60 #include <ufs/ufs/dir.h>
61 #endif /* __linux__ */
62 #include <protocols/dumprestore.h>
65 #include <compaterr.h>
67 #include <compatglob.h>
73 #include <ext2fs/ext2fs.h>
74 extern char * __progname
;
81 #include <readline/readline.h>
82 #include <readline/history.h>
84 static char *rl_gets (char *prompt
);
85 static void initialize_readline(void);
86 static char **restore_completion (char *text
, int start
, int end
);
87 static char *command_generator(char *text
, int state
);
88 static char *filename_generator(char *text
, int state
);
91 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
94 * Things to handle interruptions.
98 static char *nextarg
= NULL
;
99 static int pflag
= 0; /* prompt mode */
101 * Structure and routines associated with listing directories.
104 dump_ino_t fnum
; /* inode number of file */
105 char *fname
; /* file name */
106 short len
; /* name length */
107 char prefix
; /* prefix character */
108 char postfix
; /* postfix character */
111 int freeglob
; /* glob structure needs to be freed */
112 int argcnt
; /* next globbed argument to return */
113 glob_t glob
; /* globbing information */
114 char *cmd
; /* the current command */
117 static char *copynext
__P((char *, char *));
118 static int fcmp
__P((const void *, const void *));
119 static void formatf
__P((struct afile
*, int));
120 static void getcmd
__P((char *, char *, char *, int, struct arglist
*));
121 struct dirent
*glob_readdir
__P((RST_DIR
*dirp
));
122 static int glob_stat
__P((const char *, struct stat
*));
123 static void mkentry
__P((char *, struct direct
*, struct afile
*));
124 static void printlist
__P((char *, char *));
127 * Read and execute commands from the terminal.
132 register struct entry
*np
;
134 struct arglist arglist
;
135 char curdir
[MAXPATHLEN
];
136 char name
[MAXPATHLEN
];
140 initialize_readline();
142 arglist
.freeglob
= 0;
144 arglist
.glob
.gl_flags
= GLOB_ALTDIRFUNC
;
145 arglist
.glob
.gl_opendir
= (void *)rst_opendir
;
146 arglist
.glob
.gl_readdir
= (void *)glob_readdir
;
147 arglist
.glob
.gl_closedir
= (void *)rst_closedir
;
148 arglist
.glob
.gl_lstat
= glob_stat
;
149 arglist
.glob
.gl_stat
= glob_stat
;
150 canon("/", curdir
, sizeof(curdir
));
152 if (setjmp(reset
) != 0) {
153 if (arglist
.freeglob
!= 0) {
154 arglist
.freeglob
= 0;
156 globfree(&arglist
.glob
);
162 getcmd(curdir
, cmd
, name
, sizeof(name
), &arglist
);
165 * Add elements to the extraction list.
168 if (strncmp(cmd
, "add", strlen(cmd
)) != 0)
170 ino
= dirlookup(name
);
175 treescan(name
, ino
, addfile
);
178 * Change working directory.
181 if (strncmp(cmd
, "cd", strlen(cmd
)) != 0)
183 ino
= dirlookup(name
);
186 if (inodetype(ino
) == LEAF
) {
187 fprintf(stderr
, "%s: not a directory\n", name
);
190 (void) strncpy(curdir
, name
, sizeof(curdir
));
191 curdir
[sizeof(curdir
) - 1] = '\0';
194 * Delete elements from the extraction list.
197 if (strncmp(cmd
, "delete", strlen(cmd
)) != 0)
199 np
= lookupname(name
);
200 if (np
== NULL
|| (np
->e_flags
& NEW
) == 0) {
201 fprintf(stderr
, "%s: not on extraction list\n", name
);
204 treescan(name
, np
->e_ino
, deletefile
);
207 * Extract the requested list.
210 if (strncmp(cmd
, "extract", strlen(cmd
)) != 0)
220 * List available commands.
223 if (strncmp(cmd
, "help", strlen(cmd
)) != 0)
226 fprintf(stderr
, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
227 "Available commands are:\n",
228 "\tls [arg] - list directory\n",
229 "\tcd arg - change directory\n",
230 "\tpwd - print current directory\n",
231 "\tadd [arg] - add `arg' to list of",
232 " files to be extracted\n",
233 "\tdelete [arg] - delete `arg' from",
234 " list of files to be extracted\n",
235 "\textract - extract requested files\n",
236 "\tsetmodes - set modes of requested directories\n",
237 "\tquit - immediately exit program\n",
238 "\twhat - list dump header information\n",
239 "\tverbose - toggle verbose flag",
240 " (useful with ``ls'')\n",
241 "\tprompt - toggle the prompt display\n",
242 "\thelp or `?' - print this list\n",
243 "If no `arg' is supplied, the current",
244 " directory is used\n");
250 if (strncmp(cmd
, "ls", strlen(cmd
)) != 0)
252 printlist(name
, curdir
);
255 * Print current directory.
258 if (strncmp(cmd
, "pwd", strlen(cmd
)) == 0) {
259 if (curdir
[1] == '\0')
260 fprintf(stderr
, "/\n");
262 fprintf(stderr
, "%s\n", &curdir
[1]);
265 * Toggle prompt mode.
267 else if (strncmp(cmd
, "prompt", strlen(cmd
)) == 0) {
269 fprintf(stderr
, "prompt mode off\n");
273 fprintf(stderr
, "prompt mode on\n");
283 if (strncmp(cmd
, "quit", strlen(cmd
)) != 0)
287 if (strncmp(cmd
, "xit", strlen(cmd
)) != 0)
291 * Toggle verbose mode.
294 if (strncmp(cmd
, "verbose", strlen(cmd
)) != 0)
297 fprintf(stderr
, "verbose mode off\n");
301 fprintf(stderr
, "verbose mode on\n");
305 * Just restore requested directory modes.
308 if (strncmp(cmd
, "setmodes", strlen(cmd
)) != 0)
313 * Print out dump header information.
316 if (strncmp(cmd
, "what", strlen(cmd
)) != 0)
324 if (strncmp(cmd
, "Debug", strlen(cmd
)) != 0)
327 fprintf(stderr
, "debugging mode off\n");
331 fprintf(stderr
, "debugging mode on\n");
339 fprintf(stderr
, "%s: unknown command; type ? for help\n", cmd
);
346 * Read and parse an interactive command.
347 * The first word on the line is assigned to "cmd". If
348 * there are no arguments on the command line, then "curdir"
349 * is returned as the argument. If there are arguments
350 * on the line they are returned one at a time on each
351 * successive call to getcmd. Each argument is first assigned
352 * to "name". If it does not start with "/" the pathname in
353 * "curdir" is prepended to it. Finally "canon" is called to
354 * eliminate any embedded ".." components.
357 getcmd(char *curdir
, char *cmd
, char *name
, int size
, struct arglist
*ap
)
360 static char input
[BUFSIZ
];
362 # define rawname input /* save space by reusing input buffer */
365 * Check to see if still processing arguments.
372 * Read a command line and trim off trailing white space.
375 snprintf(input
, BUFSIZ
, "%s\n", rl_gets(curdir
));
379 fprintf(stderr
, "%s:%s:%s > ",
382 curdir
[1] ? &curdir
[1] : "/");
384 fprintf(stderr
, "%s > ", __progname
);
385 (void) fflush(stderr
);
386 (void) fgets(input
, BUFSIZ
, terminal
);
387 } while (!feof(terminal
) && input
[0] == '\n');
388 if (feof(terminal
)) {
389 (void) strcpy(cmd
, "quit");
393 for (cp
= &input
[strlen(input
) - 2]; *cp
== ' ' || *cp
== '\t'; cp
--)
394 /* trim off trailing white space and newline */;
397 * Copy the command into "cmd".
399 cp
= copynext(input
, cmd
);
402 * If no argument, use curdir as the default.
405 (void) strncpy(name
, curdir
, size
);
406 name
[size
- 1] = '\0';
411 * Find the next argument.
414 cp
= copynext(nextarg
, rawname
);
420 * If it is an absolute pathname, canonicalize it and return it.
422 if (rawname
[0] == '/') {
423 canon(rawname
, name
, size
);
426 * For relative pathnames, prepend the current directory to
427 * it then canonicalize and return it.
429 snprintf(output
, sizeof(output
), "%s/%s", curdir
, rawname
);
430 canon(output
, name
, size
);
432 if (glob(name
, GLOB_ALTDIRFUNC
, NULL
, &ap
->glob
) < 0)
433 fprintf(stderr
, "%s: out of memory\n", ap
->cmd
);
434 if (ap
->glob
.gl_pathc
== 0)
437 ap
->argcnt
= ap
->glob
.gl_pathc
;
440 strncpy(name
, ap
->glob
.gl_pathv
[ap
->glob
.gl_pathc
- ap
->argcnt
], size
);
441 name
[size
- 1] = '\0';
442 if (--ap
->argcnt
== 0) {
450 * Strip off the next token of the input.
453 copynext(char *input
, char *output
)
455 register char *cp
, *bp
;
458 for (cp
= input
; *cp
== ' ' || *cp
== '\t'; cp
++)
459 /* skip to argument */;
461 while (*cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0') {
463 * Handle back slashes.
468 "command lines cannot be continued\n");
475 * The usual unquoted case.
477 if (*cp
!= '\'' && *cp
!= '"') {
482 * Handle single and double quotes.
485 while (*cp
!= quote
&& *cp
!= '\0')
488 fprintf(stderr
, "missing %c\n", quote
);
498 * Canonicalize file names to always start with ``./'' and
499 * remove any embedded "." and ".." components.
502 canon(char *rawname
, char *canonname
, int len
)
504 register char *cp
, *np
;
506 if (strcmp(rawname
, ".") == 0 || strncmp(rawname
, "./", 2) == 0)
507 (void) strcpy(canonname
, "");
508 else if (rawname
[0] == '/')
509 (void) strcpy(canonname
, ".");
511 (void) strcpy(canonname
, "./");
512 if (strlen(canonname
) + strlen(rawname
) >= len
)
513 errx(1, "canonname: not enough buffer space");
515 (void) strcat(canonname
, rawname
);
517 * Eliminate multiple and trailing '/'s
519 for (cp
= np
= canonname
; *np
!= '\0'; cp
++) {
521 while (*cp
== '/' && *np
== '/')
528 * Eliminate extraneous "." and ".." from pathnames.
530 for (np
= canonname
; *np
!= '\0'; ) {
533 while (*np
!= '/' && *np
!= '\0')
535 if (np
- cp
== 1 && *cp
== '.') {
537 (void) strcpy(cp
, np
);
540 if (np
- cp
== 2 && strncmp(cp
, "..", 2) == 0) {
542 while (cp
> &canonname
[1] && *--cp
!= '/')
543 /* find beginning of name */;
544 (void) strcpy(cp
, np
);
551 * Do an "ls" style listing of a directory
554 printlist(char *name
, char *basename
)
556 register struct afile
*fp
, *list
, *listp
= NULL
;
557 register struct direct
*dp
;
560 int entries
, len
, namelen
;
561 char locname
[MAXPATHLEN
+ 1];
563 dp
= pathsearch(name
);
564 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
565 (!vflag
&& dp
->d_ino
== WINO
))
567 if ((dirp
= rst_opendir(name
)) == NULL
) {
570 mkentry(name
, dp
, list
);
571 len
= strlen(basename
) + 1;
572 if (strlen(name
) - len
> single
.len
) {
573 freename(single
.fname
);
574 single
.fname
= savename(&name
[len
]);
575 single
.len
= strlen(single
.fname
);
579 while ((dp
= rst_readdir(dirp
)))
582 list
= (struct afile
*)malloc(entries
* sizeof(struct afile
));
584 fprintf(stderr
, "ls: out of memory\n");
587 if ((dirp
= rst_opendir(name
)) == NULL
)
588 panic("directory reopen failed\n");
589 fprintf(stderr
, "%s:\n", name
);
592 namelen
= snprintf(locname
, sizeof(locname
), "%s/", name
);
593 if (namelen
>= sizeof(locname
))
594 namelen
= sizeof(locname
) - 1;
595 while ((dp
= rst_readdir(dirp
))) {
598 if (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0)
600 if (!vflag
&& (dp
->d_ino
== WINO
||
601 strcmp(dp
->d_name
, ".") == 0 ||
602 strcmp(dp
->d_name
, "..") == 0))
604 locname
[namelen
] = '\0';
605 if (namelen
+ strlen(dp
->d_name
) >= MAXPATHLEN
) {
606 fprintf(stderr
, "%s%s: name exceeds %d char\n",
607 locname
, dp
->d_name
, MAXPATHLEN
);
609 (void) strncat(locname
, dp
->d_name
,
610 (int)strlen(dp
->d_name
));
611 mkentry(locname
, dp
, listp
++);
617 fprintf(stderr
, "\n");
621 qsort((char *)list
, entries
, sizeof(struct afile
), fcmp
);
623 formatf(list
, entries
);
625 for (fp
= listp
- 1; fp
>= list
; fp
--)
627 fprintf(stderr
, "\n");
633 * Read the contents of a directory.
636 mkentry(char *name
, struct direct
*dp
, struct afile
*fp
)
641 fp
->fnum
= dp
->d_ino
;
642 fp
->fname
= savename(dp
->d_name
);
643 for (cp
= fp
->fname
; *cp
; cp
++)
644 if (!vflag
&& (*cp
< ' ' || *cp
>= 0177))
646 fp
->len
= cp
- fp
->fname
;
647 if (dflag
&& TSTINO(fp
->fnum
, dumpmap
) == 0)
649 else if ((np
= lookupname(name
)) != NULL
&& (np
->e_flags
& NEW
))
656 fprintf(stderr
, "Warning: undefined file type %d\n",
678 /* no need for this */
686 if (inodetype(dp
->d_ino
) == NODE
)
696 * Print out a pretty listing of a directory
699 formatf(struct afile
*list
, int nentry
)
701 register struct afile
*fp
, *endlist
;
702 int width
, bigino
, haveprefix
, havepostfix
;
703 int i
, j
, w
, precision
= 0, columns
, lines
;
709 endlist
= &list
[nentry
];
710 for (fp
= &list
[0]; fp
< endlist
; fp
++) {
711 if (bigino
< fp
->fnum
)
715 if (fp
->prefix
!= ' ')
717 if (fp
->postfix
!= ' ')
725 for (precision
= 0, i
= bigino
; i
> 0; i
/= 10)
727 width
+= precision
+ 1;
730 columns
= 81 / width
;
733 lines
= (nentry
+ columns
- 1) / columns
;
734 for (i
= 0; i
< lines
; i
++) {
735 for (j
= 0; j
< columns
; j
++) {
736 fp
= &list
[j
* lines
+ i
];
738 fprintf(stderr
, "%*ld ", precision
, (long)fp
->fnum
);
739 fp
->len
+= precision
+ 1;
742 putc(fp
->prefix
, stderr
);
745 fprintf(stderr
, "%s", fp
->fname
);
747 putc(fp
->postfix
, stderr
);
750 if (fp
+ lines
>= endlist
) {
751 fprintf(stderr
, "\n");
754 for (w
= fp
->len
; w
< width
; w
++)
761 * Skip over directory entries that are not on the tape
763 * First have to get definition of a dirent.
765 * For Linux the dirent struct is now included from bsdcompat.h
771 #endif /* ! __linux__ */
774 glob_readdir(RST_DIR
*dirp
)
777 static struct dirent adirent
;
779 while ((dp
= rst_readdir(dirp
)) != NULL
) {
780 if (!vflag
&& dp
->d_ino
== WINO
)
782 if (dflag
|| TSTINO(dp
->d_ino
, dumpmap
))
787 adirent
.d_fileno
= dp
->d_ino
;
788 memmove(adirent
.d_name
, dp
->d_name
, dp
->d_namlen
+ 1);
793 * Return st_mode information in response to stat or lstat calls
796 glob_stat(const char *name
, struct stat
*stp
)
798 register struct direct
*dp
;
799 dp
= pathsearch(name
);
800 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
801 (!vflag
&& dp
->d_ino
== WINO
))
803 if (inodetype(dp
->d_ino
) == NODE
)
804 stp
->st_mode
= IFDIR
;
806 stp
->st_mode
= IFREG
;
811 * Comparison routine for qsort.
814 fcmp(const void *f1
, const void *f2
)
816 return (strcmp(((struct afile
*)f1
)->fname
,
817 ((struct afile
*)f2
)->fname
));
821 * respond to interrupts
826 int save_errno
= errno
;
828 if (command
== 'i' && runshell
)
830 if (reply("restore interrupted, continue") == FAIL
)
838 /* A static variable for holding the line. */
839 static char *line_read
= NULL
;
841 static char completion_curdir
[MAXPATHLEN
];
843 static char *commands
[] = {
844 "add ", "cd ", "delete ", "extract ", "help ",
845 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
846 "verbose ", "setmodes ", "what ", "Debug ",
849 static char *files
= NULL
;
857 snprintf(completion_curdir
, MAXPATHLEN
, "%s", dir
);
858 completion_curdir
[MAXPATHLEN
- 1] = '\0';
861 sz
= 6 + strlen(__progname
) + strlen(spcl
.c_filesys
) + strlen((completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
862 prompt
= (char *)malloc(sz
);
865 snprintf(prompt
, sz
, "%s:%s:%s > ",
868 (completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
871 sz
= 4 + strlen(__progname
);
872 prompt
= (char *)malloc(sz
);
875 snprintf(prompt
, sz
, "%s > ", __progname
);
877 prompt
[sz
- 1] = '\0';
881 line_read
= (char *)NULL
;
885 line_read
= readline (prompt
);
886 } while (line_read
&& !*line_read
);
892 return strdup("quit");
895 add_history (line_read
);
901 initialize_readline(void)
903 rl_attempted_completion_function
= restore_completion
;
904 rl_completion_entry_function
= (Function
*)NULL
;
905 rl_completion_append_character
= '\0';
906 rl_instream
= terminal
;
910 restore_completion (char *text
, int start
, int end
)
915 matches
= completion_matches (text
, command_generator
);
917 matches
= completion_matches (text
, filename_generator
);
923 command_generator(char *text
, int state
)
925 static int list_index
, len
;
933 while ( (name
= commands
[list_index
]) != NULL
) {
937 if (strncmp(name
, text
, len
) == 0)
945 filename_generator(char *text
, int state
)
947 static int list_index
;
952 char pname
[MAXPATHLEN
];
953 char fname
[MAXPATHLEN
];
955 char ppname
[MAXPATHLEN
];
965 if ((slash
= strrchr(text
, '/')) != NULL
) {
966 int idx
= slash
- text
;
967 if (idx
> MAXPATHLEN
- 2)
968 idx
= MAXPATHLEN
- 2;
969 strncpy(ppname
, text
, MAXPATHLEN
);
970 ppname
[MAXPATHLEN
- 1] = '\0';
973 snprintf(pname
, MAXPATHLEN
, ".%s", ppname
);
975 snprintf(pname
, MAXPATHLEN
, "%s/%s", completion_curdir
, ppname
);
976 strncpy(fname
, ppname
+ idx
+ 1, MAXPATHLEN
);
978 ppname
[idx
+ 1] = '\0';
981 strncpy(pname
, completion_curdir
, MAXPATHLEN
);
982 strncpy(fname
, text
, MAXPATHLEN
);
985 pname
[MAXPATHLEN
- 1] = '\0';
986 fname
[MAXPATHLEN
- 1] = '\0';
987 if ((dirp
= rst_opendir(pname
)) == NULL
)
990 while ((dp
= rst_readdir(dirp
)))
993 files
= (char *)malloc(entries
* MAXPATHLEN
);
995 fprintf(stderr
, "Out of memory\n");
999 if ((dirp
= rst_opendir(pname
)) == NULL
)
1000 panic("directory reopen failed\n");
1002 while ((dp
= rst_readdir(dirp
))) {
1003 if (TSTINO(dp
->d_ino
, dumpmap
) == 0)
1005 if (strcmp(dp
->d_name
, ".") == 0 ||
1006 strcmp(dp
->d_name
, "..") == 0)
1008 if (strncmp(dp
->d_name
, fname
, strlen(fname
)) == 0) {
1009 if (inodetype(dp
->d_ino
) == NODE
)
1010 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s/", ppname
, dp
->d_name
);
1012 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s ", ppname
, dp
->d_name
);
1013 *(files
+ (entries
+ 1) * MAXPATHLEN
- 1) = '\0';
1020 if (list_index
>= entries
)
1023 name
= strdup(files
+ list_index
* MAXPATHLEN
);
1028 #endif /* HAVE_READLINE */