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. 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.25 2002/07/19 14:57:39 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 (const char *text
, int start
, int end
);
87 static char *command_generator(const char *text
, int state
);
88 static char *filename_generator(const 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.
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
= (int (*)(const char *, void *))glob_stat
;
149 arglist
.glob
.gl_stat
= (int (*)(const char *, void *))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)
214 setdirmodes(oflag
? FORCE
: 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)
325 if (strncmp(cmd
, "Debug", strlen(cmd
)) != 0)
328 fprintf(stderr
, "debugging mode off\n");
332 fprintf(stderr
, "debugging mode on\n");
340 fprintf(stderr
, "%s: unknown command; type ? for help\n", cmd
);
347 * Read and parse an interactive command.
348 * The first word on the line is assigned to "cmd". If
349 * there are no arguments on the command line, then "curdir"
350 * is returned as the argument. If there are arguments
351 * on the line they are returned one at a time on each
352 * successive call to getcmd. Each argument is first assigned
353 * to "name". If it does not start with "/" the pathname in
354 * "curdir" is prepended to it. Finally "canon" is called to
355 * eliminate any embedded ".." components.
358 getcmd(char *curdir
, char *cmd
, char *name
, int size
, struct arglist
*ap
)
361 static char input
[BUFSIZ
];
363 # define rawname input /* save space by reusing input buffer */
366 * Check to see if still processing arguments.
373 * Read a command line and trim off trailing white space.
376 snprintf(input
, BUFSIZ
, "%s\n", rl_gets(curdir
));
380 fprintf(stderr
, "%s:%s:%s > ",
383 curdir
[1] ? &curdir
[1] : "/");
385 fprintf(stderr
, "%s > ", __progname
);
386 (void) fflush(stderr
);
387 (void) fgets(input
, BUFSIZ
, terminal
);
388 } while (!feof(terminal
) && input
[0] == '\n');
389 if (feof(terminal
)) {
390 (void) strcpy(cmd
, "quit");
394 for (cp
= &input
[strlen(input
) - 2]; *cp
== ' ' || *cp
== '\t'; cp
--)
395 /* trim off trailing white space and newline */;
398 * Copy the command into "cmd".
400 cp
= copynext(input
, cmd
);
403 * If no argument, use curdir as the default.
406 (void) strncpy(name
, curdir
, size
);
407 name
[size
- 1] = '\0';
412 * Find the next argument.
415 cp
= copynext(nextarg
, rawname
);
421 * If it is an absolute pathname, canonicalize it and return it.
423 if (rawname
[0] == '/') {
424 canon(rawname
, name
, size
);
427 * For relative pathnames, prepend the current directory to
428 * it then canonicalize and return it.
430 snprintf(output
, sizeof(output
), "%s/%s", curdir
, rawname
);
431 canon(output
, name
, size
);
433 if (glob(name
, GLOB_ALTDIRFUNC
, NULL
, &ap
->glob
) < 0)
434 fprintf(stderr
, "%s: out of memory\n", ap
->cmd
);
435 if (ap
->glob
.gl_pathc
== 0)
438 ap
->argcnt
= ap
->glob
.gl_pathc
;
441 strncpy(name
, ap
->glob
.gl_pathv
[ap
->glob
.gl_pathc
- ap
->argcnt
], size
);
442 name
[size
- 1] = '\0';
443 if (--ap
->argcnt
== 0) {
451 * Strip off the next token of the input.
454 copynext(char *input
, char *output
)
459 for (cp
= input
; *cp
== ' ' || *cp
== '\t'; cp
++)
460 /* skip to argument */;
462 while (*cp
!= ' ' && *cp
!= '\t' && *cp
!= '\0') {
464 * Handle back slashes.
469 "command lines cannot be continued\n");
476 * The usual unquoted case.
478 if (*cp
!= '\'' && *cp
!= '"') {
483 * Handle single and double quotes.
486 while (*cp
!= quote
&& *cp
!= '\0')
489 fprintf(stderr
, "missing %c\n", quote
);
499 * Canonicalize file names to always start with ``./'' and
500 * remove any embedded "." and ".." components.
503 canon(char *rawname
, char *canonname
, int len
)
507 if (strcmp(rawname
, ".") == 0 || strncmp(rawname
, "./", 2) == 0)
508 (void) strcpy(canonname
, "");
509 else if (rawname
[0] == '/')
510 (void) strcpy(canonname
, ".");
512 (void) strcpy(canonname
, "./");
513 if (strlen(canonname
) + strlen(rawname
) >= (unsigned)len
)
514 errx(1, "canonname: not enough buffer space");
516 (void) strcat(canonname
, rawname
);
518 * Eliminate multiple and trailing '/'s
520 for (cp
= np
= canonname
; *np
!= '\0'; cp
++) {
522 while (*cp
== '/' && *np
== '/')
529 * Eliminate extraneous "." and ".." from pathnames.
531 for (np
= canonname
; *np
!= '\0'; ) {
534 while (*np
!= '/' && *np
!= '\0')
536 if (np
- cp
== 1 && *cp
== '.') {
538 (void) strcpy(cp
, np
);
541 if (np
- cp
== 2 && strncmp(cp
, "..", 2) == 0) {
543 while (cp
> &canonname
[1] && *--cp
!= '/')
544 /* find beginning of name */;
545 (void) strcpy(cp
, np
);
552 * Do an "ls" style listing of a directory
555 printlist(char *name
, char *basename
)
557 struct afile
*fp
, *list
, *listp
= NULL
;
561 int entries
, len
, namelen
;
562 char locname
[MAXPATHLEN
+ 1];
564 dp
= pathsearch(name
);
565 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
566 (!vflag
&& dp
->d_ino
== WINO
))
568 if ((dirp
= rst_opendir(name
)) == NULL
) {
571 mkentry(name
, dp
, list
);
572 len
= strlen(basename
) + 1;
573 if (strlen(name
) - len
> (unsigned)single
.len
) {
574 freename(single
.fname
);
575 single
.fname
= savename(&name
[len
]);
576 single
.len
= strlen(single
.fname
);
580 while ((dp
= rst_readdir(dirp
)))
583 list
= (struct afile
*)malloc(entries
* sizeof(struct afile
));
585 fprintf(stderr
, "ls: out of memory\n");
588 if ((dirp
= rst_opendir(name
)) == NULL
)
589 panic("directory reopen failed\n");
590 fprintf(stderr
, "%s:\n", name
);
593 namelen
= snprintf(locname
, sizeof(locname
), "%s/", name
);
594 if (namelen
>= (int)sizeof(locname
))
595 namelen
= sizeof(locname
) - 1;
596 while ((dp
= rst_readdir(dirp
))) {
599 if (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0)
601 if (!vflag
&& (dp
->d_ino
== WINO
||
602 strcmp(dp
->d_name
, ".") == 0 ||
603 strcmp(dp
->d_name
, "..") == 0))
605 locname
[namelen
] = '\0';
606 if (namelen
+ strlen(dp
->d_name
) >= MAXPATHLEN
) {
607 fprintf(stderr
, "%s%s: name exceeds %d char\n",
608 locname
, dp
->d_name
, MAXPATHLEN
);
610 (void) strncat(locname
, dp
->d_name
,
611 (int)strlen(dp
->d_name
));
612 mkentry(locname
, dp
, listp
++);
618 fprintf(stderr
, "\n");
622 qsort((char *)list
, entries
, sizeof(struct afile
), fcmp
);
624 formatf(list
, entries
);
626 for (fp
= listp
- 1; fp
>= list
; fp
--)
628 fprintf(stderr
, "\n");
634 * Read the contents of a directory.
637 mkentry(char *name
, struct direct
*dp
, struct afile
*fp
)
642 fp
->fnum
= dp
->d_ino
;
643 fp
->fname
= savename(dp
->d_name
);
644 for (cp
= fp
->fname
; *cp
; cp
++)
645 if (!vflag
&& (*cp
< ' ' || *cp
>= 0177))
647 fp
->len
= cp
- fp
->fname
;
648 if (dflag
&& TSTINO(fp
->fnum
, dumpmap
) == 0)
650 else if ((np
= lookupname(name
)) != NULL
&& (np
->e_flags
& NEW
))
657 fprintf(stderr
, "Warning: undefined file type %d\n",
679 /* no need for this */
687 if (inodetype(dp
->d_ino
) == NODE
)
697 * Print out a pretty listing of a directory
700 formatf(struct afile
*list
, int nentry
)
702 struct afile
*fp
, *endlist
;
703 int width
, bigino
, haveprefix
, havepostfix
;
704 int i
, j
, w
, precision
= 0, columns
, lines
;
710 endlist
= &list
[nentry
];
711 for (fp
= &list
[0]; fp
< endlist
; fp
++) {
712 if (bigino
< (int)fp
->fnum
)
716 if (fp
->prefix
!= ' ')
718 if (fp
->postfix
!= ' ')
726 for (precision
= 0, i
= bigino
; i
> 0; i
/= 10)
728 width
+= precision
+ 1;
731 columns
= 81 / width
;
734 lines
= (nentry
+ columns
- 1) / columns
;
735 for (i
= 0; i
< lines
; i
++) {
736 for (j
= 0; j
< columns
; j
++) {
737 fp
= &list
[j
* lines
+ i
];
739 fprintf(stderr
, "%*ld ", precision
, (long)fp
->fnum
);
740 fp
->len
+= precision
+ 1;
743 putc(fp
->prefix
, stderr
);
746 fprintf(stderr
, "%s", fp
->fname
);
748 putc(fp
->postfix
, stderr
);
751 if (fp
+ lines
>= endlist
) {
752 fprintf(stderr
, "\n");
755 for (w
= fp
->len
; w
< width
; w
++)
762 * Skip over directory entries that are not on the tape
764 * First have to get definition of a dirent.
766 * For Linux the dirent struct is now included from bsdcompat.h
772 #endif /* ! __linux__ */
775 glob_readdir(RST_DIR
*dirp
)
778 static struct dirent adirent
;
780 while ((dp
= rst_readdir(dirp
)) != NULL
) {
781 if (!vflag
&& dp
->d_ino
== WINO
)
783 if (dflag
|| TSTINO(dp
->d_ino
, dumpmap
))
788 adirent
.d_fileno
= dp
->d_ino
;
789 memmove(adirent
.d_name
, dp
->d_name
, dp
->d_namlen
+ 1);
794 * Return st_mode information in response to stat or lstat calls
797 glob_stat(const char *name
, struct stat
*stp
)
800 dp
= pathsearch(name
);
801 if (dp
== NULL
|| (!dflag
&& TSTINO(dp
->d_ino
, dumpmap
) == 0) ||
802 (!vflag
&& dp
->d_ino
== WINO
))
804 if (inodetype(dp
->d_ino
) == NODE
)
805 stp
->st_mode
= IFDIR
;
807 stp
->st_mode
= IFREG
;
812 * Comparison routine for qsort.
815 fcmp(const void *f1
, const void *f2
)
817 return (strcmp(((struct afile
*)f1
)->fname
,
818 ((struct afile
*)f2
)->fname
));
822 * respond to interrupts
825 onintr(UNUSED(int signo
))
827 int save_errno
= errno
;
829 if (command
== 'i' && runshell
)
831 if (reply("restore interrupted, continue") == FAIL
)
839 #if !HAVE_READLINE_RLCM
840 #define rl_completion_matches completion_matches
843 /* A static variable for holding the line. */
844 static char *line_read
= NULL
;
846 static char completion_curdir
[MAXPATHLEN
];
848 static char *commands
[] = {
849 "add ", "cd ", "delete ", "extract ", "help ",
850 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
851 "verbose ", "setmodes ", "what ", "Debug ",
854 static char *files
= NULL
;
862 snprintf(completion_curdir
, MAXPATHLEN
, "%s", dir
);
863 completion_curdir
[MAXPATHLEN
- 1] = '\0';
866 sz
= 6 + strlen(__progname
) + strlen(spcl
.c_filesys
) + strlen((completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
867 prompt
= (char *)malloc(sz
);
870 snprintf(prompt
, sz
, "%s:%s:%s > ",
873 (completion_curdir
+ 1 ? completion_curdir
+ 1 : "/"));
876 sz
= 4 + strlen(__progname
);
877 prompt
= (char *)malloc(sz
);
880 snprintf(prompt
, sz
, "%s > ", __progname
);
882 prompt
[sz
- 1] = '\0';
886 line_read
= (char *)NULL
;
890 line_read
= readline (prompt
);
891 } while (line_read
&& !*line_read
);
897 return strdup("quit");
900 add_history (line_read
);
906 command_generator(const char *text
, int state
)
908 static int list_index
, len
;
916 while ( (name
= commands
[list_index
]) != NULL
) {
920 if (strncmp(name
, text
, len
) == 0)
928 filename_generator(const char *text
, int state
)
930 static int list_index
;
935 char pname
[MAXPATHLEN
];
936 char fname
[MAXPATHLEN
];
938 char ppname
[MAXPATHLEN
];
948 if ((slash
= strrchr(text
, '/')) != NULL
) {
949 int idx
= slash
- text
;
950 if (idx
> MAXPATHLEN
- 2)
951 idx
= MAXPATHLEN
- 2;
952 strncpy(ppname
, text
, MAXPATHLEN
);
953 ppname
[MAXPATHLEN
- 1] = '\0';
956 snprintf(pname
, MAXPATHLEN
, ".%s", ppname
);
958 snprintf(pname
, MAXPATHLEN
, "%s/%s", completion_curdir
, ppname
);
959 strncpy(fname
, ppname
+ idx
+ 1, MAXPATHLEN
);
961 ppname
[idx
+ 1] = '\0';
964 strncpy(pname
, completion_curdir
, MAXPATHLEN
);
965 strncpy(fname
, text
, MAXPATHLEN
);
968 pname
[MAXPATHLEN
- 1] = '\0';
969 fname
[MAXPATHLEN
- 1] = '\0';
970 if ((dirp
= rst_opendir(pname
)) == NULL
)
973 while ((dp
= rst_readdir(dirp
)))
976 files
= (char *)malloc(entries
* MAXPATHLEN
);
978 fprintf(stderr
, "Out of memory\n");
982 if ((dirp
= rst_opendir(pname
)) == NULL
)
983 panic("directory reopen failed\n");
985 while ((dp
= rst_readdir(dirp
))) {
986 if (TSTINO(dp
->d_ino
, dumpmap
) == 0)
988 if (strcmp(dp
->d_name
, ".") == 0 ||
989 strcmp(dp
->d_name
, "..") == 0)
991 if (strncmp(dp
->d_name
, fname
, strlen(fname
)) == 0) {
992 if (inodetype(dp
->d_ino
) == NODE
)
993 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s/", ppname
, dp
->d_name
);
995 snprintf(files
+ entries
* MAXPATHLEN
, MAXPATHLEN
, "%s%s ", ppname
, dp
->d_name
);
996 *(files
+ (entries
+ 1) * MAXPATHLEN
- 1) = '\0';
1003 if (list_index
>= entries
)
1006 name
= strdup(files
+ list_index
* MAXPATHLEN
);
1013 restore_completion (const char *text
, int start
, UNUSED(int end
))
1018 matches
= rl_completion_matches (text
, command_generator
);
1020 matches
= rl_completion_matches (text
, filename_generator
);
1026 initialize_readline(void)
1028 rl_readline_name
= "dump";
1029 rl_attempted_completion_function
= restore_completion
;
1030 rl_completion_entry_function
= NULL
;
1031 #if HAVE_READLINE_CAC /* compile with readline 2.0 */
1032 rl_completion_append_character
= '\0';
1034 rl_instream
= terminal
;
1037 #endif /* HAVE_READLINE */