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.26 2003/03/30 15:40:38 stelian Exp $";
44 #include <sys/param.h>
48 #ifdef HAVE_EXT2FS_EXT2_FS_H
49 #include <ext2fs/ext2_fs.h>
51 #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 (const char *text
, int start
, int end
);
83 static char *command_generator(const char *text
, int state
);
84 static char *filename_generator(const 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.
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
= (int (*)(const char *, void *))glob_stat
;
145 arglist
.glob
.gl_stat
= (int (*)(const char *, void *))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)
210 setdirmodes(oflag
? FORCE
: 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)
321 if (strncmp(cmd
, "Debug", strlen(cmd
)) != 0)
324 fprintf(stderr
, "debugging mode off\n");
328 fprintf(stderr
, "debugging mode on\n");
336 fprintf(stderr
, "%s: unknown command; type ? for help\n", cmd
);
343 * Read and parse an interactive command.
344 * The first word on the line is assigned to "cmd". If
345 * there are no arguments on the command line, then "curdir"
346 * is returned as the argument. If there are arguments
347 * on the line they are returned one at a time on each
348 * successive call to getcmd. Each argument is first assigned
349 * to "name". If it does not start with "/" the pathname in
350 * "curdir" is prepended to it. Finally "canon" is called to
351 * eliminate any embedded ".." components.
354 getcmd(char *curdir
, char *cmd
, char *name
, int size
, struct arglist
*ap
)
357 static char input
[BUFSIZ
];
359 # define rawname input /* save space by reusing input buffer */
362 * Check to see if still processing arguments.
369 * Read a command line and trim off trailing white space.
372 snprintf(input
, BUFSIZ
, "%s\n", rl_gets(curdir
));
376 fprintf(stderr
, "%s:%s:%s > ",
379 curdir
[1] ? &curdir
[1] : "/");
381 fprintf(stderr
, "%s > ", __progname
);
382 (void) fflush(stderr
);
383 (void) fgets(input
, BUFSIZ
, terminal
);
384 } while (!feof(terminal
) && input
[0] == '\n');
385 if (feof(terminal
)) {
386 (void) strcpy(cmd
, "quit");
390 for (cp
= &input
[strlen(input
) - 2]; *cp
== ' ' || *cp
== '\t'; cp
--)
391 /* trim off trailing white space and newline */;
394 * Copy the command into "cmd".
396 cp
= copynext(input
, cmd
);
399 * If no argument, use curdir as the default.
402 (void) strncpy(name
, curdir
, size
);
403 name
[size
- 1] = '\0';
408 * Find the next argument.
411 cp
= copynext(nextarg
, rawname
);
417 * If it is an absolute pathname, canonicalize it and return it.
419 if (rawname
[0] == '/') {
420 canon(rawname
, name
, size
);
423 * For relative pathnames, prepend the current directory to
424 * it then canonicalize and return it.
426 snprintf(output
, sizeof(output
), "%s/%s", curdir
, rawname
);
427 canon(output
, name
, size
);
429 if (glob(name
, GLOB_ALTDIRFUNC
, NULL
, &ap
->glob
) < 0)
430 fprintf(stderr
, "%s: out of memory\n", ap
->cmd
);
431 if (ap
->glob
.gl_pathc
== 0)
434 ap
->argcnt
= ap
->glob
.gl_pathc
;
437 strncpy(name
, ap
->glob
.gl_pathv
[ap
->glob
.gl_pathc
- ap
->argcnt
], size
);
438 name
[size
- 1] = '\0';
439 if (--ap
->argcnt
== 0) {
447 * Strip off the next token of the input.
450 copynext(char *input
, char *output
)
455 for (cp
= input
; *cp
== ' ' || *cp
== '\t'; cp
++)
456 /* skip to argument */;
458 while (*cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0') {
460 * Handle back slashes.
465 "command lines cannot be continued\n");
472 * The usual unquoted case.
474 if (*cp
!= '\'' && *cp
!= '"') {
479 * Handle single and double quotes.
482 while (*cp
!= quote
&& *cp
!= '\0')
485 fprintf(stderr
, "missing %c\n", quote
);
495 * Canonicalize file names to always start with ``./'' and
496 * remove any embedded "." and ".." components.
499 canon(char *rawname
, char *canonname
, int len
)
503 if (strcmp(rawname
, ".") == 0 || strncmp(rawname
, "./", 2) == 0)
504 (void) strcpy(canonname
, "");
505 else if (rawname
[0] == '/')
506 (void) strcpy(canonname
, ".");
508 (void) strcpy(canonname
, "./");
509 if (strlen(canonname
) + strlen(rawname
) >= (unsigned)len
)
510 errx(1, "canonname: not enough buffer space");
512 (void) strcat(canonname
, rawname
);
514 * Eliminate multiple and trailing '/'s
516 for (cp
= np
= canonname
; *np
!= '\0'; cp
++) {
518 while (*cp
== '/' && *np
== '/')
525 * Eliminate extraneous "." and ".." from pathnames.
527 for (np
= canonname
; *np
!= '\0'; ) {
530 while (*np
!= '/' && *np
!= '\0')
532 if (np
- cp
== 1 && *cp
== '.') {
534 (void) strcpy(cp
, np
);
537 if (np
- cp
== 2 && strncmp(cp
, "..", 2) == 0) {
539 while (cp
> &canonname
[1] && *--cp
!= '/')
540 /* find beginning of name */;
541 (void) strcpy(cp
, np
);
548 * Do an "ls" style listing of a directory
551 printlist(char *name
, char *basename
)
553 struct afile
*fp
, *list
, *listp
= NULL
;
557 int entries
, len
, namelen
;
558 char locname
[MAXPATHLEN
+ 1];
560 dp
= pathsearch(name
);
561 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
562 (!vflag
&& dp
->d_ino
== WINO
))
564 if ((dirp
= rst_opendir(name
)) == NULL
) {
567 mkentry(name
, dp
, list
);
568 len
= strlen(basename
) + 1;
569 if (strlen(name
) - len
> (unsigned)single
.len
) {
570 freename(single
.fname
);
571 single
.fname
= savename(&name
[len
]);
572 single
.len
= strlen(single
.fname
);
576 while ((dp
= rst_readdir(dirp
)))
579 list
= (struct afile
*)malloc(entries
* sizeof(struct afile
));
581 fprintf(stderr
, "ls: out of memory\n");
584 if ((dirp
= rst_opendir(name
)) == NULL
)
585 panic("directory reopen failed\n");
586 fprintf(stderr
, "%s:\n", name
);
589 namelen
= snprintf(locname
, sizeof(locname
), "%s/", name
);
590 if (namelen
>= (int)sizeof(locname
))
591 namelen
= sizeof(locname
) - 1;
592 while ((dp
= rst_readdir(dirp
))) {
595 if (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0)
597 if (!vflag
&& (dp
->d_ino
== WINO
||
598 strcmp(dp
->d_name
, ".") == 0 ||
599 strcmp(dp
->d_name
, "..") == 0))
601 locname
[namelen
] = '\0';
602 if (namelen
+ strlen(dp
->d_name
) >= MAXPATHLEN
) {
603 fprintf(stderr
, "%s%s: name exceeds %d char\n",
604 locname
, dp
->d_name
, MAXPATHLEN
);
606 (void) strncat(locname
, dp
->d_name
,
607 (int)strlen(dp
->d_name
));
608 mkentry(locname
, dp
, listp
++);
614 fprintf(stderr
, "\n");
618 qsort((char *)list
, entries
, sizeof(struct afile
), fcmp
);
620 formatf(list
, entries
);
622 for (fp
= listp
- 1; fp
>= list
; fp
--)
624 fprintf(stderr
, "\n");
630 * Read the contents of a directory.
633 mkentry(char *name
, struct direct
*dp
, struct afile
*fp
)
638 fp
->fnum
= dp
->d_ino
;
639 fp
->fname
= savename(dp
->d_name
);
640 for (cp
= fp
->fname
; *cp
; cp
++)
641 if (!vflag
&& (*cp
< ' ' || *cp
>= 0177))
643 fp
->len
= cp
- fp
->fname
;
644 if (dflag
&& TSTINO(fp
->fnum
, dumpmap
) == 0)
646 else if ((np
= lookupname(name
)) != NULL
&& (np
->e_flags
& NEW
))
653 fprintf(stderr
, "Warning: undefined file type %d\n",
675 /* no need for this */
683 if (inodetype(dp
->d_ino
) == NODE
)
693 * Print out a pretty listing of a directory
696 formatf(struct afile
*list
, int nentry
)
698 struct afile
*fp
, *endlist
;
699 int width
, bigino
, haveprefix
, havepostfix
;
700 int i
, j
, w
, precision
= 0, columns
, lines
;
706 endlist
= &list
[nentry
];
707 for (fp
= &list
[0]; fp
< endlist
; fp
++) {
708 if (bigino
< (int)fp
->fnum
)
712 if (fp
->prefix
!= ' ')
714 if (fp
->postfix
!= ' ')
722 for (precision
= 0, i
= bigino
; i
> 0; i
/= 10)
724 width
+= precision
+ 1;
727 columns
= 81 / width
;
730 lines
= (nentry
+ columns
- 1) / columns
;
731 for (i
= 0; i
< lines
; i
++) {
732 for (j
= 0; j
< columns
; j
++) {
733 fp
= &list
[j
* lines
+ i
];
735 fprintf(stderr
, "%*ld ", precision
, (long)fp
->fnum
);
736 fp
->len
+= precision
+ 1;
739 putc(fp
->prefix
, stderr
);
742 fprintf(stderr
, "%s", fp
->fname
);
744 putc(fp
->postfix
, stderr
);
747 if (fp
+ lines
>= endlist
) {
748 fprintf(stderr
, "\n");
751 for (w
= fp
->len
; w
< width
; w
++)
758 * Skip over directory entries that are not on the tape
760 * First have to get definition of a dirent.
762 * For Linux the dirent struct is now included from bsdcompat.h
768 #endif /* ! __linux__ */
771 glob_readdir(RST_DIR
*dirp
)
774 static struct dirent adirent
;
776 while ((dp
= rst_readdir(dirp
)) != NULL
) {
777 if (!vflag
&& dp
->d_ino
== WINO
)
779 if (dflag
|| TSTINO(dp
->d_ino
, dumpmap
))
784 adirent
.d_fileno
= dp
->d_ino
;
785 memmove(adirent
.d_name
, dp
->d_name
, dp
->d_namlen
+ 1);
790 * Return st_mode information in response to stat or lstat calls
793 glob_stat(const char *name
, struct stat
*stp
)
796 dp
= pathsearch(name
);
797 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
798 (!vflag
&& dp
->d_ino
== WINO
))
800 if (inodetype(dp
->d_ino
) == NODE
)
801 stp
->st_mode
= IFDIR
;
803 stp
->st_mode
= IFREG
;
808 * Comparison routine for qsort.
811 fcmp(const void *f1
, const void *f2
)
813 return (strcmp(((struct afile
*)f1
)->fname
,
814 ((struct afile
*)f2
)->fname
));
818 * respond to interrupts
821 onintr(UNUSED(int signo
))
823 int save_errno
= errno
;
825 if (command
== 'i' && runshell
)
827 if (reply("restore interrupted, continue") == FAIL
)
835 #if !HAVE_READLINE_RLCM
836 #define rl_completion_matches completion_matches
839 /* A static variable for holding the line. */
840 static char *line_read
= NULL
;
842 static char completion_curdir
[MAXPATHLEN
];
844 static char *commands
[] = {
845 "add ", "cd ", "delete ", "extract ", "help ",
846 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
847 "verbose ", "setmodes ", "what ", "Debug ",
850 static char *files
= NULL
;
858 snprintf(completion_curdir
, MAXPATHLEN
, "%s", dir
);
859 completion_curdir
[MAXPATHLEN
- 1] = '\0';
862 sz
= 6 + strlen(__progname
) + strlen(spcl
.c_filesys
) + strlen((completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
863 prompt
= (char *)malloc(sz
);
866 snprintf(prompt
, sz
, "%s:%s:%s > ",
869 (completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
872 sz
= 4 + strlen(__progname
);
873 prompt
= (char *)malloc(sz
);
876 snprintf(prompt
, sz
, "%s > ", __progname
);
878 prompt
[sz
- 1] = '\0';
882 line_read
= (char *)NULL
;
886 line_read
= readline (prompt
);
887 } while (line_read
&& !*line_read
);
893 return strdup("quit");
896 add_history (line_read
);
902 command_generator(const char *text
, int state
)
904 static int list_index
, len
;
912 while ( (name
= commands
[list_index
]) != NULL
) {
916 if (strncmp(name
, text
, len
) == 0)
924 filename_generator(const char *text
, int state
)
926 static int list_index
;
931 char pname
[MAXPATHLEN
];
932 char fname
[MAXPATHLEN
];
934 char ppname
[MAXPATHLEN
];
944 if ((slash
= strrchr(text
, '/')) != NULL
) {
945 int idx
= slash
- text
;
946 if (idx
> MAXPATHLEN
- 2)
947 idx
= MAXPATHLEN
- 2;
948 strncpy(ppname
, text
, MAXPATHLEN
);
949 ppname
[MAXPATHLEN
- 1] = '\0';
952 snprintf(pname
, MAXPATHLEN
, ".%s", ppname
);
954 snprintf(pname
, MAXPATHLEN
, "%s/%s", completion_curdir
, ppname
);
955 strncpy(fname
, ppname
+ idx
+ 1, MAXPATHLEN
);
957 ppname
[idx
+ 1] = '\0';
960 strncpy(pname
, completion_curdir
, MAXPATHLEN
);
961 strncpy(fname
, text
, MAXPATHLEN
);
964 pname
[MAXPATHLEN
- 1] = '\0';
965 fname
[MAXPATHLEN
- 1] = '\0';
966 if ((dirp
= rst_opendir(pname
)) == NULL
)
969 while ((dp
= rst_readdir(dirp
)))
972 files
= (char *)malloc(entries
* MAXPATHLEN
);
974 fprintf(stderr
, "Out of memory\n");
978 if ((dirp
= rst_opendir(pname
)) == NULL
)
979 panic("directory reopen failed\n");
981 while ((dp
= rst_readdir(dirp
))) {
982 if (TSTINO(dp
->d_ino
, dumpmap
) == 0)
984 if (strcmp(dp
->d_name
, ".") == 0 ||
985 strcmp(dp
->d_name
, "..") == 0)
987 if (strncmp(dp
->d_name
, fname
, strlen(fname
)) == 0) {
988 if (inodetype(dp
->d_ino
) == NODE
)
989 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s/", ppname
, dp
->d_name
);
991 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s ", ppname
, dp
->d_name
);
992 *(files
+ (entries
+ 1) * MAXPATHLEN
- 1) = '\0';
999 if (list_index
>= entries
)
1002 name
= strdup(files
+ list_index
* MAXPATHLEN
);
1009 restore_completion (const char *text
, int start
, UNUSED(int end
))
1014 matches
= rl_completion_matches (text
, command_generator
);
1016 matches
= rl_completion_matches (text
, filename_generator
);
1022 initialize_readline(void)
1024 rl_readline_name
= "dump";
1025 rl_attempted_completion_function
= restore_completion
;
1026 rl_completion_entry_function
= NULL
;
1027 #if HAVE_READLINE_CAC /* compile with readline 2.0 */
1028 rl_completion_append_character
= '\0';
1030 rl_instream
= terminal
;
1033 #endif /* HAVE_READLINE */