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.14 2000/12/04 15:43:17 stelian Exp $";
47 #include <sys/param.h>
51 #include <linux/ext2_fs.h>
52 #include <bsdcompat.h>
54 #include <ufs/ufs/dinode.h>
55 #include <ufs/ufs/dir.h>
56 #endif /* __linux__ */
57 #include <protocols/dumprestore.h>
60 #include <compaterr.h>
62 #include <compatglob.h>
68 #include <ext2fs/ext2fs.h>
69 extern char * __progname
;
76 #include <readline/readline.h>
77 #include <readline/history.h>
79 static char *rl_gets (char *prompt
);
80 static void initialize_readline(void);
81 static char **restore_completion (char *text
, int start
, int end
);
82 static char *command_generator(char *text
, int state
);
83 static char *filename_generator(char *text
, int state
);
86 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
89 * Things to handle interruptions.
93 static char *nextarg
= NULL
;
94 static int pflag
= 0; /* prompt mode */
96 * Structure and routines associated with listing directories.
99 ino_t fnum
; /* inode number of file */
100 char *fname
; /* file name */
101 short len
; /* name length */
102 char prefix
; /* prefix character */
103 char postfix
; /* postfix character */
106 int freeglob
; /* glob structure needs to be freed */
107 int argcnt
; /* next globbed argument to return */
108 glob_t glob
; /* globbing information */
109 char *cmd
; /* the current command */
112 static char *copynext
__P((char *, char *));
113 static int fcmp
__P((const void *, const void *));
114 static void formatf
__P((struct afile
*, int));
115 static void getcmd
__P((char *, char *, char *, int, struct arglist
*));
116 struct dirent
*glob_readdir
__P((RST_DIR
*dirp
));
117 static int glob_stat
__P((const char *, struct stat
*));
118 static void mkentry
__P((char *, struct direct
*, struct afile
*));
119 static void printlist
__P((char *, char *));
122 * Read and execute commands from the terminal.
127 register struct entry
*np
;
129 struct arglist arglist
;
130 char curdir
[MAXPATHLEN
];
131 char name
[MAXPATHLEN
];
135 initialize_readline();
137 arglist
.freeglob
= 0;
139 arglist
.glob
.gl_flags
= GLOB_ALTDIRFUNC
;
140 arglist
.glob
.gl_opendir
= (void *)rst_opendir
;
141 arglist
.glob
.gl_readdir
= (void *)glob_readdir
;
142 arglist
.glob
.gl_closedir
= (void *)rst_closedir
;
143 arglist
.glob
.gl_lstat
= glob_stat
;
144 arglist
.glob
.gl_stat
= glob_stat
;
145 canon("/", curdir
, sizeof(curdir
));
147 if (setjmp(reset
) != 0) {
148 if (arglist
.freeglob
!= 0) {
149 arglist
.freeglob
= 0;
151 globfree(&arglist
.glob
);
157 getcmd(curdir
, cmd
, name
, sizeof(name
), &arglist
);
160 * Add elements to the extraction list.
163 if (strncmp(cmd
, "add", strlen(cmd
)) != 0)
165 ino
= dirlookup(name
);
170 treescan(name
, ino
, addfile
);
173 * Change working directory.
176 if (strncmp(cmd
, "cd", strlen(cmd
)) != 0)
178 ino
= dirlookup(name
);
181 if (inodetype(ino
) == LEAF
) {
182 fprintf(stderr
, "%s: not a directory\n", name
);
185 (void) strncpy(curdir
, name
, sizeof(curdir
));
186 curdir
[sizeof(curdir
) - 1] = '\0';
189 * Delete elements from the extraction list.
192 if (strncmp(cmd
, "delete", strlen(cmd
)) != 0)
194 np
= lookupname(name
);
195 if (np
== NULL
|| (np
->e_flags
& NEW
) == 0) {
196 fprintf(stderr
, "%s: not on extraction list\n", name
);
199 treescan(name
, np
->e_ino
, deletefile
);
202 * Extract the requested list.
205 if (strncmp(cmd
, "extract", strlen(cmd
)) != 0)
215 * List available commands.
218 if (strncmp(cmd
, "help", strlen(cmd
)) != 0)
221 fprintf(stderr
, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
222 "Available commands are:\n",
223 "\tls [arg] - list directory\n",
224 "\tcd arg - change directory\n",
225 "\tpwd - print current directory\n",
226 "\tadd [arg] - add `arg' to list of",
227 " files to be extracted\n",
228 "\tdelete [arg] - delete `arg' from",
229 " list of files to be extracted\n",
230 "\textract - extract requested files\n",
231 "\tsetmodes - set modes of requested directories\n",
232 "\tquit - immediately exit program\n",
233 "\twhat - list dump header information\n",
234 "\tverbose - toggle verbose flag",
235 " (useful with ``ls'')\n",
236 "\tprompt - toggle the prompt display\n",
237 "\thelp or `?' - print this list\n",
238 "If no `arg' is supplied, the current",
239 " directory is used\n");
245 if (strncmp(cmd
, "ls", strlen(cmd
)) != 0)
247 printlist(name
, curdir
);
250 * Print current directory.
253 if (strncmp(cmd
, "pwd", strlen(cmd
)) == 0) {
254 if (curdir
[1] == '\0')
255 fprintf(stderr
, "/\n");
257 fprintf(stderr
, "%s\n", &curdir
[1]);
260 * Toggle prompt mode.
262 else if (strncmp(cmd
, "prompt", strlen(cmd
)) == 0) {
264 fprintf(stderr
, "prompt mode off\n");
268 fprintf(stderr
, "prompt mode on\n");
278 if (strncmp(cmd
, "quit", strlen(cmd
)) != 0)
282 if (strncmp(cmd
, "xit", strlen(cmd
)) != 0)
286 * Toggle verbose mode.
289 if (strncmp(cmd
, "verbose", strlen(cmd
)) != 0)
292 fprintf(stderr
, "verbose mode off\n");
296 fprintf(stderr
, "verbose mode on\n");
300 * Just restore requested directory modes.
303 if (strncmp(cmd
, "setmodes", strlen(cmd
)) != 0)
308 * Print out dump header information.
311 if (strncmp(cmd
, "what", strlen(cmd
)) != 0)
319 if (strncmp(cmd
, "Debug", strlen(cmd
)) != 0)
322 fprintf(stderr
, "debugging mode off\n");
326 fprintf(stderr
, "debugging mode on\n");
334 fprintf(stderr
, "%s: unknown command; type ? for help\n", cmd
);
341 * Read and parse an interactive command.
342 * The first word on the line is assigned to "cmd". If
343 * there are no arguments on the command line, then "curdir"
344 * is returned as the argument. If there are arguments
345 * on the line they are returned one at a time on each
346 * successive call to getcmd. Each argument is first assigned
347 * to "name". If it does not start with "/" the pathname in
348 * "curdir" is prepended to it. Finally "canon" is called to
349 * eliminate any embedded ".." components.
352 getcmd(char *curdir
, char *cmd
, char *name
, int size
, struct arglist
*ap
)
355 static char input
[BUFSIZ
];
357 # define rawname input /* save space by reusing input buffer */
360 * Check to see if still processing arguments.
367 * Read a command line and trim off trailing white space.
370 snprintf(input
, BUFSIZ
, "%s\n", rl_gets(curdir
));
374 fprintf(stderr
, "%s:%s:%s > ",
377 curdir
[1] ? &curdir
[1] : "/");
379 fprintf(stderr
, "%s > ", __progname
);
380 (void) fflush(stderr
);
381 (void) fgets(input
, BUFSIZ
, terminal
);
382 } while (!feof(terminal
) && input
[0] == '\n');
383 if (feof(terminal
)) {
384 (void) strcpy(cmd
, "quit");
388 for (cp
= &input
[strlen(input
) - 2]; *cp
== ' ' || *cp
== '\t'; cp
--)
389 /* trim off trailing white space and newline */;
392 * Copy the command into "cmd".
394 cp
= copynext(input
, cmd
);
397 * If no argument, use curdir as the default.
400 (void) strncpy(name
, curdir
, size
);
401 name
[size
- 1] = '\0';
406 * Find the next argument.
409 cp
= copynext(nextarg
, rawname
);
415 * If it is an absolute pathname, canonicalize it and return it.
417 if (rawname
[0] == '/') {
418 canon(rawname
, name
, size
);
421 * For relative pathnames, prepend the current directory to
422 * it then canonicalize and return it.
424 snprintf(output
, sizeof(output
), "%s/%s", curdir
, rawname
);
425 canon(output
, name
, size
);
427 if (glob(name
, GLOB_ALTDIRFUNC
, NULL
, &ap
->glob
) < 0)
428 fprintf(stderr
, "%s: out of memory\n", ap
->cmd
);
429 if (ap
->glob
.gl_pathc
== 0)
432 ap
->argcnt
= ap
->glob
.gl_pathc
;
435 strncpy(name
, ap
->glob
.gl_pathv
[ap
->glob
.gl_pathc
- ap
->argcnt
], size
);
436 name
[size
- 1] = '\0';
437 if (--ap
->argcnt
== 0) {
445 * Strip off the next token of the input.
448 copynext(char *input
, char *output
)
450 register char *cp
, *bp
;
453 for (cp
= input
; *cp
== ' ' || *cp
== '\t'; cp
++)
454 /* skip to argument */;
456 while (*cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0') {
458 * Handle back slashes.
463 "command lines cannot be continued\n");
470 * The usual unquoted case.
472 if (*cp
!= '\'' && *cp
!= '"') {
477 * Handle single and double quotes.
480 while (*cp
!= quote
&& *cp
!= '\0')
483 fprintf(stderr
, "missing %c\n", quote
);
493 * Canonicalize file names to always start with ``./'' and
494 * remove any embedded "." and ".." components.
497 canon(char *rawname
, char *canonname
, int len
)
499 register char *cp
, *np
;
501 if (strcmp(rawname
, ".") == 0 || strncmp(rawname
, "./", 2) == 0)
502 (void) strcpy(canonname
, "");
503 else if (rawname
[0] == '/')
504 (void) strcpy(canonname
, ".");
506 (void) strcpy(canonname
, "./");
507 if (strlen(canonname
) + strlen(rawname
) >= len
)
508 errx(1, "canonname: not enough buffer space");
510 (void) strcat(canonname
, rawname
);
512 * Eliminate multiple and trailing '/'s
514 for (cp
= np
= canonname
; *np
!= '\0'; cp
++) {
516 while (*cp
== '/' && *np
== '/')
523 * Eliminate extraneous "." and ".." from pathnames.
525 for (np
= canonname
; *np
!= '\0'; ) {
528 while (*np
!= '/' && *np
!= '\0')
530 if (np
- cp
== 1 && *cp
== '.') {
532 (void) strcpy(cp
, np
);
535 if (np
- cp
== 2 && strncmp(cp
, "..", 2) == 0) {
537 while (cp
> &canonname
[1] && *--cp
!= '/')
538 /* find beginning of name */;
539 (void) strcpy(cp
, np
);
546 * Do an "ls" style listing of a directory
549 printlist(char *name
, char *basename
)
551 register struct afile
*fp
, *list
, *listp
= NULL
;
552 register struct direct
*dp
;
555 int entries
, len
, namelen
;
556 char locname
[MAXPATHLEN
+ 1];
558 dp
= pathsearch(name
);
559 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
560 (!vflag
&& dp
->d_ino
== WINO
))
562 if ((dirp
= rst_opendir(name
)) == NULL
) {
565 mkentry(name
, dp
, list
);
566 len
= strlen(basename
) + 1;
567 if (strlen(name
) - len
> single
.len
) {
568 freename(single
.fname
);
569 single
.fname
= savename(&name
[len
]);
570 single
.len
= strlen(single
.fname
);
574 while ((dp
= rst_readdir(dirp
)))
577 list
= (struct afile
*)malloc(entries
* sizeof(struct afile
));
579 fprintf(stderr
, "ls: out of memory\n");
582 if ((dirp
= rst_opendir(name
)) == NULL
)
583 panic("directory reopen failed\n");
584 fprintf(stderr
, "%s:\n", name
);
587 namelen
= snprintf(locname
, sizeof(locname
), "%s/", name
);
588 if (namelen
>= sizeof(locname
))
589 namelen
= sizeof(locname
) - 1;
590 while ((dp
= rst_readdir(dirp
))) {
593 if (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0)
595 if (!vflag
&& (dp
->d_ino
== WINO
||
596 strcmp(dp
->d_name
, ".") == 0 ||
597 strcmp(dp
->d_name
, "..") == 0))
599 locname
[namelen
] = '\0';
600 if (namelen
+ strlen(dp
->d_name
) >= MAXPATHLEN
) {
601 fprintf(stderr
, "%s%s: name exceeds %d char\n",
602 locname
, dp
->d_name
, MAXPATHLEN
);
604 (void) strncat(locname
, dp
->d_name
,
605 (int)strlen(dp
->d_name
));
606 mkentry(locname
, dp
, listp
++);
612 fprintf(stderr
, "\n");
616 qsort((char *)list
, entries
, sizeof(struct afile
), fcmp
);
618 formatf(list
, entries
);
620 for (fp
= listp
- 1; fp
>= list
; fp
--)
622 fprintf(stderr
, "\n");
628 * Read the contents of a directory.
631 mkentry(char *name
, struct direct
*dp
, struct afile
*fp
)
636 fp
->fnum
= dp
->d_ino
;
637 fp
->fname
= savename(dp
->d_name
);
638 for (cp
= fp
->fname
; *cp
; cp
++)
639 if (!vflag
&& (*cp
< ' ' || *cp
>= 0177))
641 fp
->len
= cp
- fp
->fname
;
642 if (dflag
&& TSTINO(fp
->fnum
, dumpmap
) == 0)
644 else if ((np
= lookupname(name
)) != NULL
&& (np
->e_flags
& NEW
))
651 fprintf(stderr
, "Warning: undefined file type %d\n",
673 /* no need for this */
681 if (inodetype(dp
->d_ino
) == NODE
)
691 * Print out a pretty listing of a directory
694 formatf(struct afile
*list
, int nentry
)
696 register struct afile
*fp
, *endlist
;
697 int width
, bigino
, haveprefix
, havepostfix
;
698 int i
, j
, w
, precision
= 0, columns
, lines
;
704 endlist
= &list
[nentry
];
705 for (fp
= &list
[0]; fp
< endlist
; fp
++) {
706 if (bigino
< fp
->fnum
)
710 if (fp
->prefix
!= ' ')
712 if (fp
->postfix
!= ' ')
720 for (precision
= 0, i
= bigino
; i
> 0; i
/= 10)
722 width
+= precision
+ 1;
725 columns
= 81 / width
;
728 lines
= (nentry
+ columns
- 1) / columns
;
729 for (i
= 0; i
< lines
; i
++) {
730 for (j
= 0; j
< columns
; j
++) {
731 fp
= &list
[j
* lines
+ i
];
733 fprintf(stderr
, "%*ld ", precision
, (long)fp
->fnum
);
734 fp
->len
+= precision
+ 1;
737 putc(fp
->prefix
, stderr
);
740 fprintf(stderr
, "%s", fp
->fname
);
742 putc(fp
->postfix
, stderr
);
745 if (fp
+ lines
>= endlist
) {
746 fprintf(stderr
, "\n");
749 for (w
= fp
->len
; w
< width
; w
++)
756 * Skip over directory entries that are not on the tape
758 * First have to get definition of a dirent.
760 * For Linux the dirent struct is now included from bsdcompat.h
766 #endif /* ! __linux__ */
769 glob_readdir(RST_DIR
*dirp
)
772 static struct dirent adirent
;
774 while ((dp
= rst_readdir(dirp
)) != NULL
) {
775 if (!vflag
&& dp
->d_ino
== WINO
)
777 if (dflag
|| TSTINO(dp
->d_ino
, dumpmap
))
782 adirent
.d_fileno
= dp
->d_ino
;
783 memmove(adirent
.d_name
, dp
->d_name
, dp
->d_namlen
+ 1);
788 * Return st_mode information in response to stat or lstat calls
791 glob_stat(const char *name
, struct stat
*stp
)
793 register struct direct
*dp
;
794 dp
= pathsearch(name
);
795 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
796 (!vflag
&& dp
->d_ino
== WINO
))
798 if (inodetype(dp
->d_ino
) == NODE
)
799 stp
->st_mode
= IFDIR
;
801 stp
->st_mode
= IFREG
;
806 * Comparison routine for qsort.
809 fcmp(const void *f1
, const void *f2
)
811 return (strcmp(((struct afile
*)f1
)->fname
,
812 ((struct afile
*)f2
)->fname
));
816 * respond to interrupts
821 int save_errno
= errno
;
823 if (command
== 'i' && runshell
)
825 if (reply("restore interrupted, continue") == FAIL
)
833 /* A static variable for holding the line. */
834 static char *line_read
= NULL
;
836 static char completion_curdir
[MAXPATHLEN
];
838 static char *commands
[] = {
839 "add ", "cd ", "delete ", "extract ", "help ",
840 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
841 "verbose ", "setmodes ", "what ", "Debug ",
844 static char *files
= NULL
;
852 snprintf(completion_curdir
, MAXPATHLEN
, "%s", dir
);
853 completion_curdir
[MAXPATHLEN
- 1] = '\0';
856 sz
= 6 + strlen(__progname
) + strlen(spcl
.c_filesys
) + strlen((completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
857 prompt
= (char *)malloc(sz
);
860 snprintf(prompt
, sz
, "%s:%s:%s > ",
863 (completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
866 sz
= 4 + strlen(__progname
);
867 prompt
= (char *)malloc(sz
);
870 snprintf(prompt
, sz
, "%s > ", __progname
);
872 prompt
[sz
- 1] = '\0';
876 line_read
= (char *)NULL
;
880 line_read
= readline (prompt
);
881 } while (line_read
&& !*line_read
);
887 return strdup("quit");
890 add_history (line_read
);
896 initialize_readline(void)
898 rl_attempted_completion_function
= restore_completion
;
899 rl_completion_entry_function
= (Function
*)NULL
;
900 rl_completion_append_character
= '\0';
901 rl_instream
= terminal
;
905 restore_completion (char *text
, int start
, int end
)
910 matches
= completion_matches (text
, command_generator
);
912 matches
= completion_matches (text
, filename_generator
);
918 command_generator(char *text
, int state
)
920 static int list_index
, len
;
928 while ( (name
= commands
[list_index
]) != NULL
) {
932 if (strncmp(name
, text
, len
) == 0)
940 filename_generator(char *text
, int state
)
942 static int list_index
;
947 char pname
[MAXPATHLEN
];
948 char fname
[MAXPATHLEN
];
950 char ppname
[MAXPATHLEN
];
960 if ((slash
= strrchr(text
, '/')) != NULL
) {
961 int idx
= slash
- text
;
962 if (idx
> MAXPATHLEN
- 2)
963 idx
= MAXPATHLEN
- 2;
964 strncpy(ppname
, text
, MAXPATHLEN
);
965 ppname
[MAXPATHLEN
- 1] = '\0';
968 snprintf(pname
, MAXPATHLEN
, ".%s", ppname
);
970 snprintf(pname
, MAXPATHLEN
, "%s/%s", completion_curdir
, ppname
);
971 strncpy(fname
, ppname
+ idx
+ 1, MAXPATHLEN
);
973 ppname
[idx
+ 1] = '\0';
976 strncpy(pname
, completion_curdir
, MAXPATHLEN
);
977 strncpy(fname
, text
, MAXPATHLEN
);
980 pname
[MAXPATHLEN
- 1] = '\0';
981 fname
[MAXPATHLEN
- 1] = '\0';
982 if ((dirp
= rst_opendir(pname
)) == NULL
)
985 while ((dp
= rst_readdir(dirp
)))
988 files
= (char *)malloc(entries
* MAXPATHLEN
);
990 fprintf(stderr
, "Out of memory\n");
994 if ((dirp
= rst_opendir(pname
)) == NULL
)
995 panic("directory reopen failed\n");
997 while ((dp
= rst_readdir(dirp
))) {
998 if (TSTINO(dp
->d_ino
, dumpmap
) == 0)
1000 if (strcmp(dp
->d_name
, ".") == 0 ||
1001 strcmp(dp
->d_name
, "..") == 0)
1003 if (strncmp(dp
->d_name
, fname
, strlen(fname
)) == 0) {
1004 if (inodetype(dp
->d_ino
) == NODE
)
1005 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s/", ppname
, dp
->d_name
);
1007 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s ", ppname
, dp
->d_name
);
1008 *(files
+ (entries
+ 1) * MAXPATHLEN
- 1) = '\0';
1015 if (list_index
>= entries
)
1018 name
= strdup(files
+ list_index
* MAXPATHLEN
);
1023 #endif /* HAVE_READLINE */