2 * Copyright 1998,2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
10 * Copyright (c) 1985 Regents of the University of California.
11 * All rights reserved. The Berkeley software License Agreement
12 * specifies the terms and conditions for redistribution.
25 #define round(a, b) ((((a) + (b) - 1) / (b)) * (b))
28 * Things to handle interruptions.
32 static char *nextarg
= NULL
;
34 static int dontexpand
; /* co-routine state set in getnext, used in expandarg */
37 static void getcmd(char *, char *, size_t, char *, size_t, struct arglist
*);
38 static void expandarg(char *, struct arglist
*);
39 static void printlist(char *, ino_t
, char *, int);
40 static void formatf(struct arglist
*);
41 static char *copynext(char *, char *, size_t);
42 static int fcmp(struct afile
*, struct afile
*);
43 static char *fmtentry(struct afile
*);
44 static void setpagercmd(void);
45 static uint_t
setpagerargs(char **);
48 static void expandarg();
49 static void printlist();
50 static void formatf();
51 static char *copynext();
53 static char *fmtentry();
54 static void setpagercmd();
55 static uint_t
setpagerargs();
59 * Read and execute commands from the terminal.
70 static struct arglist alist
= { 0, 0, 0, 0, 0 };
71 char curdir
[MAXCOMPLEXLEN
];
72 char name
[MAXCOMPLEXLEN
];
79 canon("/", curdir
, sizeof (curdir
));
81 if (setjmp(reset
) != 0) {
82 for (; alist
.head
< alist
.last
; alist
.head
++)
83 freename(alist
.head
->fname
);
86 goto loop
; /* make sure jmpbuf is up-to-date */
89 getcmd(curdir
, cmd
, sizeof (cmd
), name
, sizeof (name
), &alist
);
92 * Using strncmp() to catch unique prefixes.
96 * Add elements to the extraction list.
99 if (strncmp(cmd
, "add", strlen(cmd
)) != 0)
103 ino
= dirlookup(name
);
108 treescan(name
, ino
, addfile
);
111 * Change working directory.
114 if (strncmp(cmd
, "cd", strlen(cmd
)) != 0)
118 ino
= dirlookup(name
);
121 if (inodetype(ino
) == LEAF
) {
122 (void) fprintf(stderr
,
123 gettext("%s: not a directory\n"), name
);
127 /* No need to canon(name), getcmd() did it for us */
128 (void) strncpy(curdir
, name
, sizeof (curdir
));
129 curdir
[sizeof (curdir
) - 1] = '\0';
132 * Delete elements from the extraction list.
135 if (strncmp(cmd
, "delete", strlen(cmd
)) != 0)
139 np
= lookupname(name
);
140 if (np
== NIL
|| (np
->e_flags
& NEW
) == 0) {
141 (void) fprintf(stderr
,
142 gettext("%s: not on extraction list\n"), name
);
145 treescan(name
, np
->e_ino
, deletefile
);
148 * Extract the requested list.
151 if (strncmp(cmd
, "extract", strlen(cmd
)) != 0)
153 attrscan(0, addfile
);
162 * List available commands.
165 if (strncmp(cmd
, "help", strlen(cmd
)) != 0)
169 /* ANSI string catenation, to shut cstyle up */
170 (void) fprintf(stderr
, "%s",
171 gettext("Available commands are:\n"
172 "\tls [arg] - list directory\n"
173 "\tmarked [arg] - list items marked for extraction from directory\n"
174 "\tcd arg - change directory\n"
175 "\tpwd - print current directory\n"
176 "\tadd [arg] - add `arg' to list of files to be extracted\n"
177 "\tdelete [arg] - delete `arg' from list of files to be extracted\n"
178 "\textract - extract requested files\n"
179 "\tsetmodes - set modes of requested directories\n"
180 "\tquit - immediately exit program\n"
181 "\twhat - list dump header information\n"
182 "\tverbose - toggle verbose flag (useful with ``ls'')\n"
183 "\tpaginate - toggle pagination flag (affects ``ls'' and ``marked'')\n"
184 "\tsetpager - set pagination command and arguments\n"
185 "\thelp or `?' - print this list\n"
186 "If no `arg' is supplied, the current directory is used\n"));
193 if ((strncmp(cmd
, "ls", strlen(cmd
)) != 0) &&
194 (strncmp(cmd
, "marked", strlen(cmd
)) != 0))
198 ino
= dirlookup(name
);
201 printlist(name
, ino
, curdir
, *cmd
== 'm');
204 * Print current directory or enable pagination.
209 if (strncmp(cmd
, "pwd", strlen(cmd
)) == 0) {
210 if (curdir
[1] == '\0') {
211 (void) fprintf(stderr
, "/\n");
213 (void) fprintf(stderr
, "%s\n", &curdir
[1]);
215 } else if (strncmp(cmd
, "paginate", strlen(cmd
)) == 0) {
217 (void) fprintf(stderr
,
218 gettext("paging disabled\n"));
223 (void) fprintf(stderr
,
224 gettext("paging enabled (%s)\n"),
227 (void) fprintf(stderr
,
228 gettext("paging enabled\n"));
233 while (index
< pager_len
) {
234 (void) fprintf(stderr
,
235 ">>>pager_vector[%d] = `%s'\n",
237 pager_vector
[index
] ?
238 pager_vector
[index
] : "(null)");
251 if (strncmp(cmd
, "quit", strlen(cmd
)) != 0)
256 if (strncmp(cmd
, "xit", strlen(cmd
)) != 0)
261 * Toggle verbose mode.
264 if (strncmp(cmd
, "verbose", strlen(cmd
)) != 0)
267 (void) fprintf(stderr
, gettext("verbose mode off\n"));
271 (void) fprintf(stderr
, gettext("verbose mode on\n"));
275 * Just restore requested directory modes, or set pagination command.
280 if (strncmp(cmd
, "setmodes", strlen(cmd
)) == 0) {
282 } else if (strncmp(cmd
, "setpager", strlen(cmd
)) == 0) {
289 * Print out dump header information.
292 if (strncmp(cmd
, "what", strlen(cmd
)) != 0)
300 if (strncmp(cmd
, "Debug", strlen(cmd
)) != 0)
303 (void) fprintf(stderr
, gettext("debugging mode off\n"));
307 (void) fprintf(stderr
, gettext("debugging mode on\n"));
315 (void) fprintf(stderr
,
316 gettext("%s: unknown command; type ? for help\n"), cmd
);
319 (void) fprintf(stderr
,
320 gettext("%s: ambiguous command; type ? for help\n"), cmd
);
326 static char input
[MAXCOMPLEXLEN
]; /* shared by getcmd() and setpagercmd() */
327 #define rawname input /* save space by reusing input buffer */
330 * Read and parse an interactive command.
331 * The first word on the line is assigned to "cmd". If
332 * there are no arguments on the command line, then "curdir"
333 * is returned as the argument. If there are arguments
334 * on the line they are returned one at a time on each
335 * successive call to getcmd. Each argument is first assigned
336 * to "name". If it does not start with "/" the pathname in
337 * "curdir" is prepended to it. Finally "canon" is called to
338 * eliminate any embedded ".." components.
342 getcmd(curdir
, cmd
, cmdsiz
, name
, namesiz
, ap
)
343 char *curdir
, *cmd
, *name
;
344 size_t cmdsiz
, namesiz
;
348 char output
[MAXCOMPLEXLEN
];
351 * Check to see if still processing arguments.
353 if (ap
->head
!= ap
->last
) {
354 (void) strncpy(name
, ap
->head
->fname
, namesiz
);
355 name
[namesiz
- 1] = '\0';
356 /* double null terminate string */
357 if ((strlen(name
) + 2) > namesiz
) {
358 fprintf(stderr
, gettext("name is too long, ignoring"));
359 memset(name
, 0, namesiz
);
361 name
[strlen(name
) + 1] = '\0';
363 freename(ap
->head
->fname
);
370 * Read a command line and trim off trailing white space.
374 (void) fprintf(stderr
, "%s > ", progname
);
375 (void) fflush(stderr
);
376 (void) fgets(input
, sizeof (input
), terminal
);
377 } while (!feof(terminal
) && input
[0] == '\n');
378 if (feof(terminal
)) {
379 (void) strncpy(cmd
, "quit", cmdsiz
);
382 /* trim off trailing white space and newline */
383 for (cp
= &input
[strlen(input
) - 2];
384 cp
>= &input
[0] && isspace((uchar_t
)*cp
);
387 /*LINTED [empty loop body]*/
390 if ((strlen(input
) + 2) > MAXCOMPLEXLEN
) {
391 fprintf(stderr
, gettext("command is too long\n"));
394 /* double null terminate string */
402 * Copy the command into "cmd".
404 cp
= copynext(input
, cmd
, cmdsiz
);
407 * If no argument, use curdir as the default.
410 (void) strncpy(name
, curdir
, namesiz
);
411 name
[namesiz
- 1] = '\0';
412 /* double null terminate string */
413 if ((strlen(name
) + 2) > namesiz
) {
414 fprintf(stderr
, gettext("name is too long, ignoring"));
415 memset(name
, 0, namesiz
);
417 name
[strlen(name
) + 1] = '\0';
423 * Find the next argument.
426 cp
= copynext(nextarg
, rawname
, sizeof (rawname
));
432 * If it an absolute pathname, canonicalize it and return it.
434 if (rawname
[0] == '/') {
435 canon(rawname
, name
, namesiz
);
438 * For relative pathnames, prepend the current directory to
439 * it then canonicalize and return it.
441 (void) snprintf(output
, sizeof (output
), "%s/%s",
443 canon(output
, name
, namesiz
);
447 * ap->head->fname guaranteed to be double null-terminated and
448 * no more than MAXCOMPLEXLEN characters long.
450 assert(namesiz
>= (MAXCOMPLEXLEN
));
451 (void) strcpy(name
, ap
->head
->fname
);
452 /* double null terminate string */
453 name
[strlen(name
) + 1] = '\0';
454 freename(ap
->head
->fname
);
460 * Strip off the next token of the input.
463 copynext(input
, output
, outsize
)
464 char *input
, *output
;
467 char *cp
, *bp
, *limit
;
471 /* skip to argument */
472 for (cp
= input
; *cp
!= '\0' && isspace((uchar_t
)*cp
); cp
++) {
474 /*LINTED [empty loop body]*/
477 limit
= output
+ outsize
- 1; /* -1 for the trailing \0 */
478 while (!isspace((uchar_t
)*cp
) && *cp
!= '\0' && bp
< limit
) {
480 * Handle back slashes.
484 (void) fprintf(stderr
, gettext(
485 "command lines cannot be continued\n"));
492 * The usual unquoted case.
494 if (*cp
!= '\'' && *cp
!= '"') {
499 * Handle single and double quotes.
503 while (*cp
!= quote
&& *cp
!= '\0' && bp
< limit
)
506 (void) fprintf(stderr
,
507 gettext("missing %c\n"), (uchar_t
)quote
);
513 if ((strlen(output
) + 2) > outsize
) {
514 fprintf(stderr
, gettext(
515 "name is too long, ignoring"));
516 memset(output
, 0, outsize
);
518 /* double null terminate string */
525 * Canonicalize file names to always start with ``./'' and
526 * remove any imbedded "." and ".." components.
528 * The pathname "canonname" is returned double null terminated.
531 canon(rawname
, canonname
, limit
)
532 char *rawname
, *canonname
;
535 char *cp
, *np
, *prefix
;
539 if (strcmp(rawname
, ".") == 0 || strncmp(rawname
, "./", 2) == 0)
541 else if (rawname
[0] == '/')
545 (void) snprintf(canonname
, limit
, "%s%s", prefix
, rawname
);
547 * Eliminate multiple and trailing '/'s
549 for (cp
= np
= canonname
; *np
!= '\0'; cp
++) {
551 while (*cp
== '/' && *np
== '/')
555 if ((strlen(canonname
) + 2) > limit
) {
557 gettext("canonical name is too long, ignoring name\n"));
558 memset(canonname
, 0, limit
);
560 /* double null terminate string */
567 * Eliminate extraneous "." and ".." from pathnames. Uses
568 * memmove(), as strcpy() might do the wrong thing for these
572 while (*np
!= '\0') {
575 while (*np
!= '/' && *np
!= '\0')
577 if (np
- cp
== 1 && *cp
== '.') {
580 (void) memmove(cp
, np
, len
);
582 /* double null terminate string */
583 *(cp
+ len
+ 1) = '\0';
586 if (np
- cp
== 2 && strncmp(cp
, "..", 2) == 0) {
588 /* find beginning of name */
589 while (cp
> &canonname
[1] && *--cp
!= '/') {
591 /*LINTED [empty loop body]*/
594 (void) memmove(cp
, np
, len
);
596 /* double null terminate string */
597 *(cp
+ len
+ 1) = '\0';
604 * globals (file name generation)
606 * "*" in params matches r.e ".*"
607 * "?" in params matches r.e. "."
608 * "[...]" in params matches character class
609 * "[...a-z...]" in params matches a through z.
616 static struct afile single
;
619 ap
->head
= ap
->last
= (struct afile
*)0;
623 size
= expand(arg
, 0, ap
);
627 ep
= lookupname(arg
);
628 single
.fnum
= ep
? ep
->e_ino
: 0;
629 single
.fname
= savename(arg
);
631 ap
->last
= ap
->head
+ 1;
634 if ((ap
->last
- ap
->head
) > ULONG_MAX
) {
635 (void) fprintf(stderr
,
636 gettext("Argument expansion too large to sort\n"));
638 /* LINTED pointer arith just range-checked */
639 qsort((char *)ap
->head
, (size_t)(ap
->last
- ap
->head
),
641 (int (*)(const void *, const void *)) fcmp
);
646 * Do an "ls" style listing of a directory
649 printlist(name
, ino
, basename
, marked_only
)
657 static struct arglist alist
= { 0, 0, 0, 0, "ls" };
663 if ((dirp
= rst_opendir(name
)) == NULL
) {
665 if (strncmp(name
, basename
, strlen(basename
)) == 0)
666 single
.fname
= savename(name
+ strlen(basename
) + 1);
668 single
.fname
= savename(name
);
669 alist
.head
= &single
;
670 alist
.last
= alist
.head
+ 1;
671 if (alist
.base
!= NULL
) {
676 alist
.head
= (struct afile
*)0;
677 (void) fprintf(stderr
, "%s:\n", name
);
678 while (dp
= rst_readdir(dirp
)) {
679 if (dp
== NULL
|| dp
->d_ino
== 0) {
684 if (!dflag
&& BIT(dp
->d_ino
, dumpmap
) == 0)
687 (strcmp(dp
->d_name
, ".") == 0 ||
688 strcmp(dp
->d_name
, "..") == 0))
692 np
= lookupino(dp
->d_ino
);
693 if ((np
== NIL
) || ((np
->e_flags
& NEW
) == 0))
697 if (!mkentry(dp
->d_name
, dp
->d_ino
, &alist
)) {
704 if (alist
.head
!= 0) {
705 if ((alist
.last
- alist
.head
) > ULONG_MAX
) {
706 (void) fprintf(stderr
,
707 gettext("Directory too large to sort\n"));
709 qsort((char *)alist
.head
,
710 /* LINTED range-checked */
711 (size_t)(alist
.last
- alist
.head
),
712 sizeof (*alist
.head
),
713 (int (*)(const void *, const void *)) fcmp
);
716 for (fp
= alist
.head
; fp
< alist
.last
; fp
++)
720 * Don't free alist.base, as we'll probably be called
721 * again, and might as well re-use what we've got.
725 (void) fprintf(stderr
, "\n");
731 * Print out a pretty listing of a directory
739 /* LINTED: result fits into an int */
740 int nentry
= (int)(ap
->last
- ap
->head
);
742 uint_t len
, w
, width
= 0, columns
, lines
;
744 FILE *output
= stderr
;
746 if (ap
->head
== ap
->last
)
753 perror(gettext("could not create pipe"));
759 perror(gettext("could not fork"));
763 * Make sure final output still ends up in
766 (void) dup2(fileno(stderr
), fileno(stdout
));
767 (void) close(fds
[0]);
768 (void) dup2(fds
[1], fileno(stdin
));
769 execvp(pager_vector
[0], pager_vector
);
770 perror(gettext("execvp of pager failed"));
774 (void) close(fds
[1]);
775 output
= fdopen(fds
[0], "w");
776 if (output
!= (FILE *)NULL
) {
779 perror(gettext("could not open pipe to pager"));
782 (void) fprintf(stderr
,
783 gettext("pagination disabled\n"));
788 for (fp
= ap
->head
; fp
< ap
->last
; fp
++) {
789 fp
->ftype
= inodetype(fp
->fnum
);
790 np
= lookupino(fp
->fnum
);
792 fp
->fflags
= np
->e_flags
;
795 len
= strlen(fmtentry(fp
));
800 columns
= 80 / width
;
803 lines
= (nentry
+ columns
- 1) / columns
;
804 for (i
= 0; i
< lines
&& !ferror(output
); i
++) {
805 for (j
= 0; j
< columns
&& !ferror(output
); j
++) {
806 fp
= ap
->head
+ j
* lines
+ i
;
808 (void) fprintf(output
, "%s", cp
);
809 if (fp
+ lines
>= ap
->last
) {
810 (void) fprintf(output
, "\n");
816 if (fprintf(output
, " ") < 0)
823 (void) fclose(output
);
824 (void) wait((int *)NULL
);
829 * Comparison routine for qsort.
833 struct afile
*f1
, *f2
;
836 return (strcoll(f1
->fname
, f2
->fname
));
840 * Format a directory entry.
846 static char fmtres
[MAXCOMPLEXLEN
];
847 static int precision
= 0;
849 char *cp
, *dp
, *limit
;
852 /* MAXCOMPLEXLEN assumed to be >= 1 */
855 if (precision
== 0) {
856 for (i
= maxino
; i
!= 0; i
/= 10)
858 if (sizeof (fmtres
) < (unsigned)(precision
+ 2)) {
859 (void) fprintf(stderr
, gettext(
860 "\nInternal check failed, minimum width %d exceeds available size %d\n"),
861 (precision
+ 2), sizeof (fmtres
));
865 (void) snprintf(fmtres
, sizeof (fmtres
), "%*ld ",
866 precision
, fp
->fnum
);
868 dp
= &fmtres
[strlen(fmtres
)];
869 limit
= fmtres
+ sizeof (fmtres
) - 1;
870 if (dflag
&& BIT(fp
->fnum
, dumpmap
) == 0)
872 else if ((fp
->fflags
& NEW
) != 0)
876 for (cp
= fp
->fname
; *cp
&& dp
< limit
; cp
++)
877 /* LINTED: precedence ok, can't fix system macro */
878 if (!vflag
&& (!ISPRINT(*cp
, wp
)))
882 if (fp
->ftype
== NODE
&& dp
< limit
)
889 * respond to interrupts
898 if (command
== 'i' && reset_OK
)
901 (void) snprintf(buf
, sizeof (buf
),
902 gettext("%s interrupted, continue"), progname
);
903 if (reply(buf
) == FAIL
)
907 * Set up pager_catenated and pager_vector.
918 cp
= getenv("PAGER");
920 pager_catenated
= strdup(cp
);
921 if ((pager_catenated
== NULL
) || (*pager_catenated
== '\0')) {
922 if (pager_catenated
!= NULL
)
923 free(pager_catenated
);
924 pager_catenated
= strdup(DEF_PAGER
);
926 if (pager_catenated
== NULL
) {
927 (void) fprintf(stderr
, gettext("out of memory\n"));
931 pager_vector
= (char **)malloc(sizeof (char *));
932 if (pager_vector
== NULL
) {
933 (void) fprintf(stderr
, gettext("out of memory\n"));
938 cp
= pager_catenated
;
939 (void) setpagerargs(&cp
);
944 * Resets pager_catenated and pager_vector from user input.
953 uint_t catenate_length
;
957 * We'll get called immediately after setting a pager, due to
958 * our interaction with getcmd()'s internal state. Don't do
959 * anything when that happens.
965 for (index
= 0; pager_vector
[index
] != (char *)NULL
; index
+= 1)
966 free(pager_vector
[index
]);
968 free(pager_catenated
);
971 pager_vector
= (char **)malloc(2 * sizeof (char *));
972 if (pager_vector
== NULL
) {
973 (void) fprintf(stderr
, gettext("out of memory\n"));
978 pager_vector
[0] = strdup(input
);
979 if (pager_vector
[0] == NULL
) {
980 (void) fprintf(stderr
, gettext("out of memory\n"));
984 (void) fprintf(stderr
, gettext("got command `%s'\n"), input
);
985 catenate_length
= setpagerargs(&nextarg
) + strlen(pager_vector
[0]) + 1;
986 pager_catenated
= (char *)malloc(catenate_length
*
987 (size_t)sizeof (char));
988 if (pager_catenated
== (char *)NULL
) {
989 (void) fprintf(stderr
, gettext("out of memory\n"));
992 for (index
= 0; pager_vector
[index
] != (char *)NULL
; index
+= 1) {
994 (void) strcat(pager_catenated
, " ");
995 (void) strcat(pager_catenated
, pager_vector
[index
]);
1001 * Extract arguments for the pager command from getcmd()'s input buffer.
1004 setpagerargs(source
)
1007 char word
[MAXCOMPLEXLEN
];
1011 while ((cp
!= (char *)NULL
) && (*cp
!= '\0')) {
1012 cp
= copynext(cp
, word
, sizeof (word
));
1014 fprintf(stderr
, gettext("got word `%s'\n"), word
);
1015 pager_vector
= (char **)realloc(pager_vector
,
1016 (size_t)sizeof (char *) * (pager_len
+ 1));
1017 if (pager_vector
== (char **)NULL
) {
1018 (void) fprintf(stderr
, gettext("out of memory\n"));
1021 pager_vector
[pager_len
- 1] = strdup(word
);
1022 if (pager_vector
[pager_len
- 1] == (char *)NULL
) {
1023 (void) fprintf(stderr
, gettext("out of memory\n"));
1026 length
+= strlen(word
) + 1;
1029 pager_vector
[pager_len
- 1] = (char *)NULL
;