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
];
76 canon("/", curdir
, sizeof (curdir
));
78 if (setjmp(reset
) != 0) {
79 for (; alist
.head
< alist
.last
; alist
.head
++)
80 freename(alist
.head
->fname
);
83 goto loop
; /* make sure jmpbuf is up-to-date */
86 getcmd(curdir
, cmd
, sizeof (cmd
), name
, sizeof (name
), &alist
);
89 * Using strncmp() to catch unique prefixes.
93 * Add elements to the extraction list.
96 if (strncmp(cmd
, "add", strlen(cmd
)) != 0)
100 ino
= dirlookup(name
);
105 treescan(name
, ino
, addfile
);
108 * Change working directory.
111 if (strncmp(cmd
, "cd", strlen(cmd
)) != 0)
115 ino
= dirlookup(name
);
118 if (inodetype(ino
) == LEAF
) {
119 (void) fprintf(stderr
,
120 gettext("%s: not a directory\n"), name
);
124 /* No need to canon(name), getcmd() did it for us */
125 (void) strncpy(curdir
, name
, sizeof (curdir
));
126 curdir
[sizeof (curdir
) - 1] = '\0';
129 * Delete elements from the extraction list.
132 if (strncmp(cmd
, "delete", strlen(cmd
)) != 0)
136 np
= lookupname(name
);
137 if (np
== NIL
|| (np
->e_flags
& NEW
) == 0) {
138 (void) fprintf(stderr
,
139 gettext("%s: not on extraction list\n"), name
);
142 treescan(name
, np
->e_ino
, deletefile
);
145 * Extract the requested list.
148 if (strncmp(cmd
, "extract", strlen(cmd
)) != 0)
150 attrscan(0, addfile
);
159 * List available commands.
162 if (strncmp(cmd
, "help", strlen(cmd
)) != 0)
166 /* ANSI string catenation, to shut cstyle up */
167 (void) fprintf(stderr
, "%s",
168 gettext("Available commands are:\n"
169 "\tls [arg] - list directory\n"
170 "\tmarked [arg] - list items marked for extraction from directory\n"
171 "\tcd arg - change directory\n"
172 "\tpwd - print current directory\n"
173 "\tadd [arg] - add `arg' to list of files to be extracted\n"
174 "\tdelete [arg] - delete `arg' from list of files to be extracted\n"
175 "\textract - extract requested files\n"
176 "\tsetmodes - set modes of requested directories\n"
177 "\tquit - immediately exit program\n"
178 "\twhat - list dump header information\n"
179 "\tverbose - toggle verbose flag (useful with ``ls'')\n"
180 "\tpaginate - toggle pagination flag (affects ``ls'' and ``marked'')\n"
181 "\tsetpager - set pagination command and arguments\n"
182 "\thelp or `?' - print this list\n"
183 "If no `arg' is supplied, the current directory is used\n"));
190 if ((strncmp(cmd
, "ls", strlen(cmd
)) != 0) &&
191 (strncmp(cmd
, "marked", strlen(cmd
)) != 0))
195 ino
= dirlookup(name
);
198 printlist(name
, ino
, curdir
, *cmd
== 'm');
201 * Print current directory or enable pagination.
206 if (strncmp(cmd
, "pwd", strlen(cmd
)) == 0) {
207 if (curdir
[1] == '\0') {
208 (void) fprintf(stderr
, "/\n");
210 (void) fprintf(stderr
, "%s\n", &curdir
[1]);
212 } else if (strncmp(cmd
, "paginate", strlen(cmd
)) == 0) {
214 (void) fprintf(stderr
,
215 gettext("paging disabled\n"));
220 (void) fprintf(stderr
,
221 gettext("paging enabled (%s)\n"),
224 (void) fprintf(stderr
,
225 gettext("paging enabled\n"));
230 while (index
< pager_len
) {
231 (void) fprintf(stderr
,
232 ">>>pager_vector[%d] = `%s'\n",
234 pager_vector
[index
] ?
235 pager_vector
[index
] : "(null)");
248 if (strncmp(cmd
, "quit", strlen(cmd
)) != 0)
253 if (strncmp(cmd
, "xit", strlen(cmd
)) != 0)
258 * Toggle verbose mode.
261 if (strncmp(cmd
, "verbose", strlen(cmd
)) != 0)
264 (void) fprintf(stderr
, gettext("verbose mode off\n"));
268 (void) fprintf(stderr
, gettext("verbose mode on\n"));
272 * Just restore requested directory modes, or set pagination command.
277 if (strncmp(cmd
, "setmodes", strlen(cmd
)) == 0) {
279 } else if (strncmp(cmd
, "setpager", strlen(cmd
)) == 0) {
286 * Print out dump header information.
289 if (strncmp(cmd
, "what", strlen(cmd
)) != 0)
297 if (strncmp(cmd
, "Debug", strlen(cmd
)) != 0)
300 (void) fprintf(stderr
, gettext("debugging mode off\n"));
304 (void) fprintf(stderr
, gettext("debugging mode on\n"));
312 (void) fprintf(stderr
,
313 gettext("%s: unknown command; type ? for help\n"), cmd
);
316 (void) fprintf(stderr
,
317 gettext("%s: ambiguous command; type ? for help\n"), cmd
);
323 static char input
[MAXCOMPLEXLEN
]; /* shared by getcmd() and setpagercmd() */
324 #define rawname input /* save space by reusing input buffer */
327 * Read and parse an interactive command.
328 * The first word on the line is assigned to "cmd". If
329 * there are no arguments on the command line, then "curdir"
330 * is returned as the argument. If there are arguments
331 * on the line they are returned one at a time on each
332 * successive call to getcmd. Each argument is first assigned
333 * to "name". If it does not start with "/" the pathname in
334 * "curdir" is prepended to it. Finally "canon" is called to
335 * eliminate any embedded ".." components.
339 getcmd(curdir
, cmd
, cmdsiz
, name
, namesiz
, ap
)
340 char *curdir
, *cmd
, *name
;
341 size_t cmdsiz
, namesiz
;
345 char output
[MAXCOMPLEXLEN
];
348 * Check to see if still processing arguments.
350 if (ap
->head
!= ap
->last
) {
351 (void) strncpy(name
, ap
->head
->fname
, namesiz
);
352 name
[namesiz
- 1] = '\0';
353 /* double null terminate string */
354 if ((strlen(name
) + 2) > namesiz
) {
355 fprintf(stderr
, gettext("name is too long, ignoring"));
356 memset(name
, 0, namesiz
);
358 name
[strlen(name
) + 1] = '\0';
360 freename(ap
->head
->fname
);
367 * Read a command line and trim off trailing white space.
371 (void) fprintf(stderr
, "%s > ", progname
);
372 (void) fflush(stderr
);
373 (void) fgets(input
, sizeof (input
), terminal
);
374 } while (!feof(terminal
) && input
[0] == '\n');
375 if (feof(terminal
)) {
376 (void) strncpy(cmd
, "quit", cmdsiz
);
379 /* trim off trailing white space and newline */
380 for (cp
= &input
[strlen(input
) - 2];
381 cp
>= &input
[0] && isspace((uchar_t
)*cp
);
384 /*LINTED [empty loop body]*/
387 if ((strlen(input
) + 2) > MAXCOMPLEXLEN
) {
388 fprintf(stderr
, gettext("command is too long\n"));
391 /* double null terminate string */
399 * Copy the command into "cmd".
401 cp
= copynext(input
, cmd
, cmdsiz
);
404 * If no argument, use curdir as the default.
407 (void) strncpy(name
, curdir
, namesiz
);
408 name
[namesiz
- 1] = '\0';
409 /* double null terminate string */
410 if ((strlen(name
) + 2) > namesiz
) {
411 fprintf(stderr
, gettext("name is too long, ignoring"));
412 memset(name
, 0, namesiz
);
414 name
[strlen(name
) + 1] = '\0';
420 * Find the next argument.
423 cp
= copynext(nextarg
, rawname
, sizeof (rawname
));
429 * If it an absolute pathname, canonicalize it and return it.
431 if (rawname
[0] == '/') {
432 canon(rawname
, name
, namesiz
);
435 * For relative pathnames, prepend the current directory to
436 * it then canonicalize and return it.
438 (void) snprintf(output
, sizeof (output
), "%s/%s",
440 canon(output
, name
, namesiz
);
444 * ap->head->fname guaranteed to be double null-terminated and
445 * no more than MAXCOMPLEXLEN characters long.
447 assert(namesiz
>= (MAXCOMPLEXLEN
));
448 (void) strcpy(name
, ap
->head
->fname
);
449 /* double null terminate string */
450 name
[strlen(name
) + 1] = '\0';
451 freename(ap
->head
->fname
);
457 * Strip off the next token of the input.
460 copynext(input
, output
, outsize
)
461 char *input
, *output
;
464 char *cp
, *bp
, *limit
;
468 /* skip to argument */
469 for (cp
= input
; *cp
!= '\0' && isspace((uchar_t
)*cp
); cp
++) {
471 /*LINTED [empty loop body]*/
474 limit
= output
+ outsize
- 1; /* -1 for the trailing \0 */
475 while (!isspace((uchar_t
)*cp
) && *cp
!= '\0' && bp
< limit
) {
477 * Handle back slashes.
481 (void) fprintf(stderr
, gettext(
482 "command lines cannot be continued\n"));
489 * The usual unquoted case.
491 if (*cp
!= '\'' && *cp
!= '"') {
496 * Handle single and double quotes.
500 while (*cp
!= quote
&& *cp
!= '\0' && bp
< limit
)
503 (void) fprintf(stderr
,
504 gettext("missing %c\n"), (uchar_t
)quote
);
510 if ((strlen(output
) + 2) > outsize
) {
511 fprintf(stderr
, gettext(
512 "name is too long, ignoring"));
513 memset(output
, 0, outsize
);
515 /* double null terminate string */
522 * Canonicalize file names to always start with ``./'' and
523 * remove any imbedded "." and ".." components.
525 * The pathname "canonname" is returned double null terminated.
528 canon(rawname
, canonname
, limit
)
529 char *rawname
, *canonname
;
532 char *cp
, *np
, *prefix
;
536 if (strcmp(rawname
, ".") == 0 || strncmp(rawname
, "./", 2) == 0)
538 else if (rawname
[0] == '/')
542 (void) snprintf(canonname
, limit
, "%s%s", prefix
, rawname
);
544 * Eliminate multiple and trailing '/'s
546 for (cp
= np
= canonname
; *np
!= '\0'; cp
++) {
548 while (*cp
== '/' && *np
== '/')
552 if ((strlen(canonname
) + 2) > limit
) {
554 gettext("canonical name is too long, ignoring name\n"));
555 memset(canonname
, 0, limit
);
557 /* double null terminate string */
564 * Eliminate extraneous "." and ".." from pathnames. Uses
565 * memmove(), as strcpy() might do the wrong thing for these
569 while (*np
!= '\0') {
572 while (*np
!= '/' && *np
!= '\0')
574 if (np
- cp
== 1 && *cp
== '.') {
577 (void) memmove(cp
, np
, len
);
579 /* double null terminate string */
580 *(cp
+ len
+ 1) = '\0';
583 if (np
- cp
== 2 && strncmp(cp
, "..", 2) == 0) {
585 /* find beginning of name */
586 while (cp
> &canonname
[1] && *--cp
!= '/') {
588 /*LINTED [empty loop body]*/
591 (void) memmove(cp
, np
, len
);
593 /* double null terminate string */
594 *(cp
+ len
+ 1) = '\0';
601 * globals (file name generation)
603 * "*" in params matches r.e ".*"
604 * "?" in params matches r.e. "."
605 * "[...]" in params matches character class
606 * "[...a-z...]" in params matches a through z.
613 static struct afile single
;
616 ap
->head
= ap
->last
= NULL
;
620 size
= expand(arg
, 0, ap
);
624 ep
= lookupname(arg
);
625 single
.fnum
= ep
? ep
->e_ino
: 0;
626 single
.fname
= savename(arg
);
628 ap
->last
= ap
->head
+ 1;
631 if ((ap
->last
- ap
->head
) > ULONG_MAX
) {
632 (void) fprintf(stderr
,
633 gettext("Argument expansion too large to sort\n"));
635 /* LINTED pointer arith just range-checked */
636 qsort((char *)ap
->head
, (size_t)(ap
->last
- ap
->head
),
638 (int (*)(const void *, const void *)) fcmp
);
643 * Do an "ls" style listing of a directory
646 printlist(name
, ino
, basename
, marked_only
)
654 static struct arglist alist
= { 0, 0, 0, 0, "ls" };
660 if ((dirp
= rst_opendir(name
)) == NULL
) {
662 if (strncmp(name
, basename
, strlen(basename
)) == 0)
663 single
.fname
= savename(name
+ strlen(basename
) + 1);
665 single
.fname
= savename(name
);
666 alist
.head
= &single
;
667 alist
.last
= alist
.head
+ 1;
668 if (alist
.base
!= NULL
) {
674 (void) fprintf(stderr
, "%s:\n", name
);
675 while (dp
= rst_readdir(dirp
)) {
676 if (dp
== NULL
|| dp
->d_ino
== 0) {
681 if (!dflag
&& BIT(dp
->d_ino
, dumpmap
) == 0)
684 (strcmp(dp
->d_name
, ".") == 0 ||
685 strcmp(dp
->d_name
, "..") == 0))
689 np
= lookupino(dp
->d_ino
);
690 if ((np
== NIL
) || ((np
->e_flags
& NEW
) == 0))
694 if (!mkentry(dp
->d_name
, dp
->d_ino
, &alist
)) {
701 if (alist
.head
!= 0) {
702 if ((alist
.last
- alist
.head
) > ULONG_MAX
) {
703 (void) fprintf(stderr
,
704 gettext("Directory too large to sort\n"));
706 qsort((char *)alist
.head
,
707 /* LINTED range-checked */
708 (size_t)(alist
.last
- alist
.head
),
709 sizeof (*alist
.head
),
710 (int (*)(const void *, const void *)) fcmp
);
713 for (fp
= alist
.head
; fp
< alist
.last
; fp
++)
717 * Don't free alist.base, as we'll probably be called
718 * again, and might as well re-use what we've got.
722 (void) fprintf(stderr
, "\n");
728 * Print out a pretty listing of a directory
736 /* LINTED: result fits into an int */
737 int nentry
= (int)(ap
->last
- ap
->head
);
739 uint_t len
, w
, width
= 0, columns
, lines
;
741 FILE *output
= stderr
;
743 if (ap
->head
== ap
->last
)
750 perror(gettext("could not create pipe"));
756 perror(gettext("could not fork"));
760 * Make sure final output still ends up in
763 (void) dup2(fileno(stderr
), fileno(stdout
));
764 (void) close(fds
[0]);
765 (void) dup2(fds
[1], fileno(stdin
));
766 execvp(pager_vector
[0], pager_vector
);
767 perror(gettext("execvp of pager failed"));
771 (void) close(fds
[1]);
772 output
= fdopen(fds
[0], "w");
773 if (output
!= NULL
) {
776 perror(gettext("could not open pipe to pager"));
779 (void) fprintf(stderr
,
780 gettext("pagination disabled\n"));
785 for (fp
= ap
->head
; fp
< ap
->last
; fp
++) {
786 fp
->ftype
= inodetype(fp
->fnum
);
787 np
= lookupino(fp
->fnum
);
789 fp
->fflags
= np
->e_flags
;
792 len
= strlen(fmtentry(fp
));
797 columns
= 80 / width
;
800 lines
= (nentry
+ columns
- 1) / columns
;
801 for (i
= 0; i
< lines
&& !ferror(output
); i
++) {
802 for (j
= 0; j
< columns
&& !ferror(output
); j
++) {
803 fp
= ap
->head
+ j
* lines
+ i
;
805 (void) fprintf(output
, "%s", cp
);
806 if (fp
+ lines
>= ap
->last
) {
807 (void) fprintf(output
, "\n");
813 if (fprintf(output
, " ") < 0)
820 (void) fclose(output
);
826 * Comparison routine for qsort.
830 struct afile
*f1
, *f2
;
833 return (strcoll(f1
->fname
, f2
->fname
));
837 * Format a directory entry.
843 static char fmtres
[MAXCOMPLEXLEN
];
844 static int precision
= 0;
846 char *cp
, *dp
, *limit
;
849 /* MAXCOMPLEXLEN assumed to be >= 1 */
852 if (precision
== 0) {
853 for (i
= maxino
; i
!= 0; i
/= 10)
855 if (sizeof (fmtres
) < (unsigned)(precision
+ 2)) {
856 (void) fprintf(stderr
, gettext(
857 "\nInternal check failed, minimum width %d exceeds available size %d\n"),
858 (precision
+ 2), sizeof (fmtres
));
862 (void) snprintf(fmtres
, sizeof (fmtres
), "%*ld ",
863 precision
, fp
->fnum
);
865 dp
= &fmtres
[strlen(fmtres
)];
866 limit
= fmtres
+ sizeof (fmtres
) - 1;
867 if (dflag
&& BIT(fp
->fnum
, dumpmap
) == 0)
869 else if ((fp
->fflags
& NEW
) != 0)
873 for (cp
= fp
->fname
; *cp
&& dp
< limit
; cp
++)
874 /* LINTED: precedence ok, can't fix system macro */
875 if (!vflag
&& (!ISPRINT(*cp
, wp
)))
879 if (fp
->ftype
== NODE
&& dp
< limit
)
886 * respond to interrupts
895 if (command
== 'i' && reset_OK
)
898 (void) snprintf(buf
, sizeof (buf
),
899 gettext("%s interrupted, continue"), progname
);
900 if (reply(buf
) == FAIL
)
904 * Set up pager_catenated and pager_vector.
915 cp
= getenv("PAGER");
917 pager_catenated
= strdup(cp
);
918 if ((pager_catenated
== NULL
) || (*pager_catenated
== '\0')) {
919 free(pager_catenated
);
920 pager_catenated
= strdup(DEF_PAGER
);
922 if (pager_catenated
== NULL
) {
923 (void) fprintf(stderr
, gettext("out of memory\n"));
927 pager_vector
= (char **)malloc(sizeof (char *));
928 if (pager_vector
== NULL
) {
929 (void) fprintf(stderr
, gettext("out of memory\n"));
934 cp
= pager_catenated
;
935 (void) setpagerargs(&cp
);
940 * Resets pager_catenated and pager_vector from user input.
949 uint_t catenate_length
;
953 * We'll get called immediately after setting a pager, due to
954 * our interaction with getcmd()'s internal state. Don't do
955 * anything when that happens.
961 for (index
= 0; pager_vector
[index
] != NULL
; index
+= 1)
962 free(pager_vector
[index
]);
964 free(pager_catenated
);
967 pager_vector
= (char **)malloc(2 * sizeof (char *));
968 if (pager_vector
== NULL
) {
969 (void) fprintf(stderr
, gettext("out of memory\n"));
974 pager_vector
[0] = strdup(input
);
975 if (pager_vector
[0] == NULL
) {
976 (void) fprintf(stderr
, gettext("out of memory\n"));
980 (void) fprintf(stderr
, gettext("got command `%s'\n"), input
);
981 catenate_length
= setpagerargs(&nextarg
) + strlen(pager_vector
[0]) + 1;
982 pager_catenated
= (char *)malloc(catenate_length
*
983 (size_t)sizeof (char));
984 if (pager_catenated
== NULL
) {
985 (void) fprintf(stderr
, gettext("out of memory\n"));
988 for (index
= 0; pager_vector
[index
] != NULL
; index
+= 1) {
990 (void) strcat(pager_catenated
, " ");
991 (void) strcat(pager_catenated
, pager_vector
[index
]);
997 * Extract arguments for the pager command from getcmd()'s input buffer.
1000 setpagerargs(source
)
1003 char word
[MAXCOMPLEXLEN
];
1007 while ((cp
!= NULL
) && (*cp
!= '\0')) {
1008 cp
= copynext(cp
, word
, sizeof (word
));
1010 fprintf(stderr
, gettext("got word `%s'\n"), word
);
1011 pager_vector
= reallocarray(pager_vector
,
1012 pager_len
+ 1, sizeof (char *));
1013 if (pager_vector
== (char **)NULL
) {
1014 (void) fprintf(stderr
, gettext("out of memory\n"));
1017 pager_vector
[pager_len
- 1] = strdup(word
);
1018 if (pager_vector
[pager_len
- 1] == NULL
) {
1019 (void) fprintf(stderr
, gettext("out of memory\n"));
1022 length
+= strlen(word
) + 1;
1025 pager_vector
[pager_len
- 1] = NULL
;