1 /*
2 * Copyright 1998,2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
8
9 /*
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.
13 */
14
15 #pragma ident "%Z%%M% %I% %E% SMI"
16
17 #include <setjmp.h>
18 #include <euc.h>
19 #include <widec.h>
20 #include "restore.h"
21 #include <ctype.h>
22 #include <limits.h>
23 #include <sys/wait.h>
24
25 extern eucwidth_t wp;
26
27 #define round(a, b) ((((a) + (b) - 1) / (b)) * (b))
28
29 /*
30 * Things to handle interruptions.
31 */
32 static jmp_buf reset;
33 static int reset_OK;
34 static char *nextarg = NULL;
35
36 static int dontexpand; /* co-routine state set in getnext, used in expandarg */
37
38 #ifdef __STDC__
39 static void getcmd(char *, char *, size_t, char *, size_t, struct arglist *);
40 static void expandarg(char *, struct arglist *);
41 static void printlist(char *, ino_t, char *, int);
42 static void formatf(struct arglist *);
43 static char *copynext(char *, char *, size_t);
44 static int fcmp(struct afile *, struct afile *);
45 static char *fmtentry(struct afile *);
46 static void setpagercmd(void);
47 static uint_t setpagerargs(char **);
48 #else
49 static void getcmd();
50 static void expandarg();
51 static void printlist();
52 static void formatf();
53 static char *copynext();
54 static int fcmp();
55 static char *fmtentry();
56 static void setpagercmd();
57 static uint_t setpagerargs();
58 #endif
59
60 /*
61 * Read and execute commands from the terminal.
62 */
63 void
64 #ifdef __STDC__
runcmdshell(void)65 runcmdshell(void)
66 #else
67 runcmdshell()
68 #endif
69 {
70 struct entry *np;
71 ino_t ino;
72 static struct arglist alist = { 0, 0, 0, 0, 0 };
73 char curdir[MAXCOMPLEXLEN];
74 char name[MAXCOMPLEXLEN];
75 char cmd[BUFSIZ];
76
77 #ifdef lint
78 curdir[0] = '\0';
79 #endif /* lint */
80
81 canon("/", curdir, sizeof (curdir));
82 loop:
83 if (setjmp(reset) != 0) {
84 for (; alist.head < alist.last; alist.head++)
85 freename(alist.head->fname);
86 nextarg = NULL;
87 volno = 0;
88 goto loop; /* make sure jmpbuf is up-to-date */
89 }
90 reset_OK = 1;
91 getcmd(curdir, cmd, sizeof (cmd), name, sizeof (name), &alist);
92
93 /*
94 * Using strncmp() to catch unique prefixes.
95 */
96 switch (cmd[0]) {
97 /*
98 * Add elements to the extraction list.
99 */
100 case 'a':
101 if (strncmp(cmd, "add", strlen(cmd)) != 0)
102 goto bad;
103 if (name[0] == '\0')
104 break;
105 ino = dirlookup(name);
106 if (ino == 0)
107 break;
108 if (mflag)
109 pathcheck(name);
110 treescan(name, ino, addfile);
111 break;
112 /*
113 * Change working directory.
114 */
115 case 'c':
116 if (strncmp(cmd, "cd", strlen(cmd)) != 0)
117 goto bad;
118 if (name[0] == '\0')
119 break;
120 ino = dirlookup(name);
121 if (ino == 0)
122 break;
123 if (inodetype(ino) == LEAF) {
124 (void) fprintf(stderr,
125 gettext("%s: not a directory\n"), name);
126 break;
127 }
128
129 /* No need to canon(name), getcmd() did it for us */
130 (void) strncpy(curdir, name, sizeof (curdir));
131 curdir[sizeof (curdir) - 1] = '\0';
132 break;
133 /*
134 * Delete elements from the extraction list.
135 */
136 case 'd':
137 if (strncmp(cmd, "delete", strlen(cmd)) != 0)
138 goto bad;
139 if (name[0] == '\0')
140 break;
141 np = lookupname(name);
142 if (np == NIL || (np->e_flags & NEW) == 0) {
143 (void) fprintf(stderr,
144 gettext("%s: not on extraction list\n"), name);
145 break;
146 }
147 treescan(name, np->e_ino, deletefile);
148 break;
149 /*
150 * Extract the requested list.
151 */
152 case 'e':
153 if (strncmp(cmd, "extract", strlen(cmd)) != 0)
154 goto bad;
155 attrscan(0, addfile);
156 createfiles();
157 createlinks();
158 setdirmodes();
159 if (dflag)
160 checkrestore();
161 volno = 0;
162 break;
163 /*
164 * List available commands.
165 */
166 case 'h':
167 if (strncmp(cmd, "help", strlen(cmd)) != 0)
168 goto bad;
169 /*FALLTHROUGH*/
170 case '?':
171 /* ANSI string catenation, to shut cstyle up */
172 (void) fprintf(stderr, "%s",
173 gettext("Available commands are:\n"
174 "\tls [arg] - list directory\n"
175 "\tmarked [arg] - list items marked for extraction from directory\n"
176 "\tcd arg - change directory\n"
177 "\tpwd - print current directory\n"
178 "\tadd [arg] - add `arg' to list of files to be extracted\n"
179 "\tdelete [arg] - delete `arg' from list of files to be extracted\n"
180 "\textract - extract requested files\n"
181 "\tsetmodes - set modes of requested directories\n"
182 "\tquit - immediately exit program\n"
183 "\twhat - list dump header information\n"
184 "\tverbose - toggle verbose flag (useful with ``ls'')\n"
185 "\tpaginate - toggle pagination flag (affects ``ls'' and ``marked'')\n"
186 "\tsetpager - set pagination command and arguments\n"
187 "\thelp or `?' - print this list\n"
188 "If no `arg' is supplied, the current directory is used\n"));
189 break;
190 /*
191 * List a directory.
192 */
193 case 'l':
194 case 'm':
195 if ((strncmp(cmd, "ls", strlen(cmd)) != 0) &&
196 (strncmp(cmd, "marked", strlen(cmd)) != 0))
197 goto bad;
198 if (name[0] == '\0')
199 break;
200 ino = dirlookup(name);
201 if (ino == 0)
202 break;
203 printlist(name, ino, curdir, *cmd == 'm');
204 break;
205 /*
206 * Print current directory or enable pagination.
207 */
208 case 'p':
209 if (strlen(cmd) < 2)
210 goto ambiguous;
211 if (strncmp(cmd, "pwd", strlen(cmd)) == 0) {
212 if (curdir[1] == '\0') {
213 (void) fprintf(stderr, "/\n");
214 } else {
215 (void) fprintf(stderr, "%s\n", &curdir[1]);
216 }
217 } else if (strncmp(cmd, "paginate", strlen(cmd)) == 0) {
218 if (paginating) {
219 (void) fprintf(stderr,
220 gettext("paging disabled\n"));
221 paginating = 0;
222 break;
223 }
224 if (vflag) {
225 (void) fprintf(stderr,
226 gettext("paging enabled (%s)\n"),
227 pager_catenated);
228 } else {
229 (void) fprintf(stderr,
230 gettext("paging enabled\n"));
231 }
232 if (dflag) {
233 int index = 0;
234
235 while (index < pager_len) {
236 (void) fprintf(stderr,
237 ">>>pager_vector[%d] = `%s'\n",
238 index,
239 pager_vector[index] ?
240 pager_vector[index] : "(null)");
241 index += 1;
242 }
243 }
244 paginating = 1;
245 } else {
246 goto bad;
247 }
248 break;
249 /*
250 * Quit.
251 */
252 case 'q':
253 if (strncmp(cmd, "quit", strlen(cmd)) != 0)
254 goto bad;
255 reset_OK = 0;
256 return;
257 case 'x':
258 if (strncmp(cmd, "xit", strlen(cmd)) != 0)
259 goto bad;
260 reset_OK = 0;
261 return;
262 /*
263 * Toggle verbose mode.
264 */
265 case 'v':
266 if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
267 goto bad;
268 if (vflag) {
269 (void) fprintf(stderr, gettext("verbose mode off\n"));
270 vflag = 0;
271 break;
272 }
273 (void) fprintf(stderr, gettext("verbose mode on\n"));
274 vflag = 1;
275 break;
276 /*
277 * Just restore requested directory modes, or set pagination command.
278 */
279 case 's':
280 if (strlen(cmd) < 4)
281 goto ambiguous;
282 if (strncmp(cmd, "setmodes", strlen(cmd)) == 0) {
283 setdirmodes();
284 } else if (strncmp(cmd, "setpager", strlen(cmd)) == 0) {
285 setpagercmd();
286 } else {
287 goto bad;
288 }
289 break;
290 /*
291 * Print out dump header information.
292 */
293 case 'w':
294 if (strncmp(cmd, "what", strlen(cmd)) != 0)
295 goto bad;
296 printdumpinfo();
297 break;
298 /*
299 * Turn on debugging.
300 */
301 case 'D':
302 if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
303 goto bad;
304 if (dflag) {
305 (void) fprintf(stderr, gettext("debugging mode off\n"));
306 dflag = 0;
307 break;
308 }
309 (void) fprintf(stderr, gettext("debugging mode on\n"));
310 dflag++;
311 break;
312 /*
313 * Unknown command.
314 */
315 default:
316 bad:
317 (void) fprintf(stderr,
318 gettext("%s: unknown command; type ? for help\n"), cmd);
319 break;
320 ambiguous:
321 (void) fprintf(stderr,
322 gettext("%s: ambiguous command; type ? for help\n"), cmd);
323 break;
324 }
325 goto loop;
326 }
327
328 static char input[MAXCOMPLEXLEN]; /* shared by getcmd() and setpagercmd() */
329 #define rawname input /* save space by reusing input buffer */
330
331 /*
332 * Read and parse an interactive command.
333 * The first word on the line is assigned to "cmd". If
334 * there are no arguments on the command line, then "curdir"
335 * is returned as the argument. If there are arguments
336 * on the line they are returned one at a time on each
337 * successive call to getcmd. Each argument is first assigned
338 * to "name". If it does not start with "/" the pathname in
339 * "curdir" is prepended to it. Finally "canon" is called to
340 * eliminate any embedded ".." components.
341 */
342 /* ARGSUSED */
343 static void
getcmd(curdir,cmd,cmdsiz,name,namesiz,ap)344 getcmd(curdir, cmd, cmdsiz, name, namesiz, ap)
345 char *curdir, *cmd, *name;
346 size_t cmdsiz, namesiz;
347 struct arglist *ap;
348 {
349 char *cp;
350 char output[MAXCOMPLEXLEN];
351
352 /*
353 * Check to see if still processing arguments.
354 */
355 if (ap->head != ap->last) {
356 (void) strncpy(name, ap->head->fname, namesiz);
357 name[namesiz - 1] = '\0';
358 /* double null terminate string */
359 if ((strlen(name) + 2) > namesiz) {
360 fprintf(stderr, gettext("name is too long, ignoring"));
361 memset(name, 0, namesiz);
362 } else {
363 name[strlen(name) + 1] = '\0';
364 }
365 freename(ap->head->fname);
366 ap->head++;
367 return;
368 }
369 if (nextarg != NULL)
370 goto getnext;
371 /*
372 * Read a command line and trim off trailing white space.
373 */
374 readagain:
375 do {
376 (void) fprintf(stderr, "%s > ", progname);
377 (void) fflush(stderr);
378 (void) fgets(input, sizeof (input), terminal);
379 } while (!feof(terminal) && input[0] == '\n');
380 if (feof(terminal)) {
381 (void) strncpy(cmd, "quit", cmdsiz);
382 return;
383 }
384 /* trim off trailing white space and newline */
385 for (cp = &input[strlen(input) - 2];
386 cp >= &input[0] && isspace((uchar_t)*cp);
387 cp--) {
388 continue;
389 /*LINTED [empty loop body]*/
390 }
391 *++cp = '\0';
392 if ((strlen(input) + 2) > MAXCOMPLEXLEN) {
393 fprintf(stderr, gettext("command is too long\n"));
394 goto readagain;
395 } else {
396 /* double null terminate string */
397 *(cp + 1) = '\0';
398 }
399
400 if (cp == &input[0])
401 goto readagain;
402
403 /*
404 * Copy the command into "cmd".
405 */
406 cp = copynext(input, cmd, cmdsiz);
407 ap->cmd = cmd;
408 /*
409 * If no argument, use curdir as the default.
410 */
411 if (*cp == '\0') {
412 (void) strncpy(name, curdir, namesiz);
413 name[namesiz - 1] = '\0';
414 /* double null terminate string */
415 if ((strlen(name) + 2) > namesiz) {
416 fprintf(stderr, gettext("name is too long, ignoring"));
417 memset(name, 0, namesiz);
418 } else {
419 name[strlen(name) + 1] = '\0';
420 }
421 return;
422 }
423 nextarg = cp;
424 /*
425 * Find the next argument.
426 */
427 getnext:
428 cp = copynext(nextarg, rawname, sizeof (rawname));
429 if (*cp == '\0')
430 nextarg = NULL;
431 else
432 nextarg = cp;
433 /*
434 * If it an absolute pathname, canonicalize it and return it.
435 */
436 if (rawname[0] == '/') {
437 canon(rawname, name, namesiz);
438 } else {
439 /*
440 * For relative pathnames, prepend the current directory to
441 * it then canonicalize and return it.
442 */
443 (void) snprintf(output, sizeof (output), "%s/%s",
444 curdir, rawname);
445 canon(output, name, namesiz);
446 }
447 expandarg(name, ap);
448 /*
449 * ap->head->fname guaranteed to be double null-terminated and
450 * no more than MAXCOMPLEXLEN characters long.
451 */
452 assert(namesiz >= (MAXCOMPLEXLEN));
453 (void) strcpy(name, ap->head->fname);
454 /* double null terminate string */
455 name[strlen(name) + 1] = '\0';
456 freename(ap->head->fname);
457 ap->head++;
458 #undef rawname
459 }
460
461 /*
462 * Strip off the next token of the input.
463 */
464 static char *
copynext(input,output,outsize)465 copynext(input, output, outsize)
466 char *input, *output;
467 size_t outsize;
468 {
469 char *cp, *bp, *limit;
470 char quote;
471
472 dontexpand = 0;
473 /* skip to argument */
474 for (cp = input; *cp != '\0' && isspace((uchar_t)*cp); cp++) {
475 continue;
476 /*LINTED [empty loop body]*/
477 }
478 bp = output;
479 limit = output + outsize - 1; /* -1 for the trailing \0 */
480 while (!isspace((uchar_t)*cp) && *cp != '\0' && bp < limit) {
481 /*
482 * Handle back slashes.
483 */
484 if (*cp == '\\') {
485 if (*++cp == '\0') {
486 (void) fprintf(stderr, gettext(
487 "command lines cannot be continued\n"));
488 continue;
489 }
490 *bp++ = *cp++;
491 continue;
492 }
493 /*
494 * The usual unquoted case.
495 */
496 if (*cp != '\'' && *cp != '"') {
497 *bp++ = *cp++;
498 continue;
499 }
500 /*
501 * Handle single and double quotes.
502 */
503 quote = *cp++;
504 dontexpand = 1;
505 while (*cp != quote && *cp != '\0' && bp < limit)
506 *bp++ = *cp++;
507 if (*cp++ == '\0') {
508 (void) fprintf(stderr,
509 gettext("missing %c\n"), (uchar_t)quote);
510 cp--;
511 continue;
512 }
513 }
514 *bp = '\0';
515 if ((strlen(output) + 2) > outsize) {
516 fprintf(stderr, gettext(
517 "name is too long, ignoring"));
518 memset(output, 0, outsize);
519 } else {
520 /* double null terminate string */
521 *(bp + 1) = '\0';
522 }
523 return (cp);
524 }
525
526 /*
527 * Canonicalize file names to always start with ``./'' and
528 * remove any imbedded "." and ".." components.
529 *
530 * The pathname "canonname" is returned double null terminated.
531 */
532 void
canon(rawname,canonname,limit)533 canon(rawname, canonname, limit)
534 char *rawname, *canonname;
535 size_t limit;
536 {
537 char *cp, *np, *prefix;
538 uint_t len;
539
540 assert(limit > 3);
541 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
542 prefix = "";
543 else if (rawname[0] == '/')
544 prefix = ".";
545 else
546 prefix = "./";
547 (void) snprintf(canonname, limit, "%s%s", prefix, rawname);
548 /*
549 * Eliminate multiple and trailing '/'s
550 */
551 for (cp = np = canonname; *np != '\0'; cp++) {
552 *cp = *np++;
553 while (*cp == '/' && *np == '/')
554 np++;
555 }
556 *cp = '\0';
557 if ((strlen(canonname) + 2) > limit) {
558 fprintf(stderr,
559 gettext("canonical name is too long, ignoring name\n"));
560 memset(canonname, 0, limit);
561 } else {
562 /* double null terminate string */
563 *(cp + 1) = '\0';
564 }
565
566 if (*--cp == '/')
567 *cp = '\0';
568 /*
569 * Eliminate extraneous "." and ".." from pathnames. Uses
570 * memmove(), as strcpy() might do the wrong thing for these
571 * small overlaps.
572 */
573 np = canonname;
574 while (*np != '\0') {
575 np++;
576 cp = np;
577 while (*np != '/' && *np != '\0')
578 np++;
579 if (np - cp == 1 && *cp == '.') {
580 cp--;
581 len = strlen(np);
582 (void) memmove(cp, np, len);
583 *(cp + len) = '\0';
584 /* double null terminate string */
585 *(cp + len + 1) = '\0';
586 np = cp;
587 }
588 if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
589 cp--;
590 /* find beginning of name */
591 while (cp > &canonname[1] && *--cp != '/') {
592 continue;
593 /*LINTED [empty loop body]*/
594 }
595 len = strlen(np);
596 (void) memmove(cp, np, len);
597 *(cp + len) = '\0';
598 /* double null terminate string */
599 *(cp + len + 1) = '\0';
600 np = cp;
601 }
602 }
603 }
604
605 /*
606 * globals (file name generation)
607 *
608 * "*" in params matches r.e ".*"
609 * "?" in params matches r.e. "."
610 * "[...]" in params matches character class
611 * "[...a-z...]" in params matches a through z.
612 */
613 static void
expandarg(arg,ap)614 expandarg(arg, ap)
615 char *arg;
616 struct arglist *ap;
617 {
618 static struct afile single;
619 int size;
620
621 ap->head = ap->last = (struct afile *)0;
622 if (dontexpand)
623 size = 0;
624 else
625 size = expand(arg, 0, ap);
626 if (size == 0) {
627 struct entry *ep;
628
629 ep = lookupname(arg);
630 single.fnum = ep ? ep->e_ino : 0;
631 single.fname = savename(arg);
632 ap->head = &single;
633 ap->last = ap->head + 1;
634 return;
635 }
636 if ((ap->last - ap->head) > ULONG_MAX) {
637 (void) fprintf(stderr,
638 gettext("Argument expansion too large to sort\n"));
639 } else {
640 /* LINTED pointer arith just range-checked */
641 qsort((char *)ap->head, (size_t)(ap->last - ap->head),
642 sizeof (*ap->head),
643 (int (*)(const void *, const void *)) fcmp);
644 }
645 }
646
647 /*
648 * Do an "ls" style listing of a directory
649 */
650 static void
printlist(name,ino,basename,marked_only)651 printlist(name, ino, basename, marked_only)
652 char *name;
653 ino_t ino;
654 char *basename;
655 int marked_only;
656 {
657 struct afile *fp;
658 struct direct *dp;
659 static struct arglist alist = { 0, 0, 0, 0, "ls" };
660 struct afile single;
661 struct entry *np;
662 RST_DIR *dirp;
663 int list_entry;
664
665 if ((dirp = rst_opendir(name)) == NULL) {
666 single.fnum = ino;
667 if (strncmp(name, basename, strlen(basename)) == 0)
668 single.fname = savename(name + strlen(basename) + 1);
669 else
670 single.fname = savename(name);
671 alist.head = &single;
672 alist.last = alist.head + 1;
673 if (alist.base != NULL) {
674 free(alist.base);
675 alist.base = NULL;
676 }
677 } else {
678 alist.head = (struct afile *)0;
679 (void) fprintf(stderr, "%s:\n", name);
680 while (dp = rst_readdir(dirp)) {
681 if (dp == NULL || dp->d_ino == 0) {
682 rst_closedir(dirp);
683 dirp = NULL;
684 break;
685 }
686 if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
687 continue;
688 if (vflag == 0 &&
689 (strcmp(dp->d_name, ".") == 0 ||
690 strcmp(dp->d_name, "..") == 0))
691 continue;
692 list_entry = 1;
693 if (marked_only) {
694 np = lookupino(dp->d_ino);
695 if ((np == NIL) || ((np->e_flags & NEW) == 0))
696 list_entry = 0;
697 }
698 if (list_entry) {
699 if (!mkentry(dp->d_name, dp->d_ino, &alist)) {
700 rst_closedir(dirp);
701 return;
702 }
703 }
704 }
705 }
706 if (alist.head != 0) {
707 if ((alist.last - alist.head) > ULONG_MAX) {
708 (void) fprintf(stderr,
709 gettext("Directory too large to sort\n"));
710 } else {
711 qsort((char *)alist.head,
712 /* LINTED range-checked */
713 (size_t)(alist.last - alist.head),
714 sizeof (*alist.head),
715 (int (*)(const void *, const void *)) fcmp);
716 }
717 formatf(&alist);
718 for (fp = alist.head; fp < alist.last; fp++)
719 freename(fp->fname);
720 alist.head = NULL;
721 /*
722 * Don't free alist.base, as we'll probably be called
723 * again, and might as well re-use what we've got.
724 */
725 }
726 if (dirp != NULL) {
727 (void) fprintf(stderr, "\n");
728 rst_closedir(dirp);
729 }
730 }
731
732 /*
733 * Print out a pretty listing of a directory
734 */
735 static void
formatf(ap)736 formatf(ap)
737 struct arglist *ap;
738 {
739 struct afile *fp;
740 struct entry *np;
741 /* LINTED: result fits into an int */
742 int nentry = (int)(ap->last - ap->head);
743 int i, j;
744 uint_t len, w, width = 0, columns, lines;
745 char *cp;
746 FILE *output = stderr;
747
748 if (ap->head == ap->last)
749 return;
750
751 if (paginating) {
752 int fds[2];
753
754 if (pipe(fds) < 0) {
755 perror(gettext("could not create pipe"));
756 goto no_page;
757 }
758
759 switch (fork()) {
760 case -1:
761 perror(gettext("could not fork"));
762 goto no_page;
763 case 0:
764 /*
765 * Make sure final output still ends up in
766 * the same place.
767 */
768 (void) dup2(fileno(stderr), fileno(stdout));
769 (void) close(fds[0]);
770 (void) dup2(fds[1], fileno(stdin));
771 execvp(pager_vector[0], pager_vector);
772 perror(gettext("execvp of pager failed"));
773 exit(1);
774 /*NOTREACHED*/
775 default:
776 (void) close(fds[1]);
777 output = fdopen(fds[0], "w");
778 if (output != (FILE *)NULL) {
779 break;
780 }
781 perror(gettext("could not open pipe to pager"));
782 output = stderr;
783 no_page:
784 (void) fprintf(stderr,
785 gettext("pagination disabled\n"));
786 paginating = 0;
787 }
788 }
789
790 for (fp = ap->head; fp < ap->last; fp++) {
791 fp->ftype = inodetype(fp->fnum);
792 np = lookupino(fp->fnum);
793 if (np != NIL)
794 fp->fflags = np->e_flags;
795 else
796 fp->fflags = 0;
797 len = strlen(fmtentry(fp));
798 if (len > width)
799 width = len;
800 }
801 width += 2;
802 columns = 80 / width;
803 if (columns == 0)
804 columns = 1;
805 lines = (nentry + columns - 1) / columns;
806 for (i = 0; i < lines && !ferror(output); i++) {
807 for (j = 0; j < columns && !ferror(output); j++) {
808 fp = ap->head + j * lines + i;
809 cp = fmtentry(fp);
810 (void) fprintf(output, "%s", cp);
811 if (fp + lines >= ap->last) {
812 (void) fprintf(output, "\n");
813 break;
814 }
815 w = strlen(cp);
816 while (w < width) {
817 w++;
818 if (fprintf(output, " ") < 0)
819 break;
820 }
821 }
822 }
823
824 if (paginating) {
825 (void) fclose(output);
826 (void) wait((int *)NULL);
827 }
828 }
829
830 /*
831 * Comparison routine for qsort.
832 */
833 static int
fcmp(f1,f2)834 fcmp(f1, f2)
835 struct afile *f1, *f2;
836 {
837
838 return (strcoll(f1->fname, f2->fname));
839 }
840
841 /*
842 * Format a directory entry.
843 */
844 static char *
fmtentry(fp)845 fmtentry(fp)
846 struct afile *fp;
847 {
848 static char fmtres[MAXCOMPLEXLEN];
849 static int precision = 0;
850 ino_t i;
851 char *cp, *dp, *limit;
852
853 if (!vflag) {
854 /* MAXCOMPLEXLEN assumed to be >= 1 */
855 fmtres[0] = '\0';
856 } else {
857 if (precision == 0) {
858 for (i = maxino; i != 0; i /= 10)
859 precision++;
860 if (sizeof (fmtres) < (unsigned)(precision + 2)) {
861 (void) fprintf(stderr, gettext(
862 "\nInternal check failed, minimum width %d exceeds available size %d\n"),
863 (precision + 2), sizeof (fmtres));
864 done(1);
865 }
866 }
867 (void) snprintf(fmtres, sizeof (fmtres), "%*ld ",
868 precision, fp->fnum);
869 }
870 dp = &fmtres[strlen(fmtres)];
871 limit = fmtres + sizeof (fmtres) - 1;
872 if (dflag && BIT(fp->fnum, dumpmap) == 0)
873 *dp++ = '^';
874 else if ((fp->fflags & NEW) != 0)
875 *dp++ = '*';
876 else
877 *dp++ = ' ';
878 for (cp = fp->fname; *cp && dp < limit; cp++)
879 /* LINTED: precedence ok, can't fix system macro */
880 if (!vflag && (!ISPRINT(*cp, wp)))
881 *dp++ = '?';
882 else
883 *dp++ = *cp;
884 if (fp->ftype == NODE && dp < limit)
885 *dp++ = '/';
886 *dp++ = 0;
887 return (fmtres);
888 }
889
890 /*
891 * respond to interrupts
892 */
893 /* ARGSUSED */
894 void
onintr(sig)895 onintr(sig)
896 int sig;
897 {
898 char buf[300];
899
900 if (command == 'i' && reset_OK)
901 longjmp(reset, 1);
902
903 (void) snprintf(buf, sizeof (buf),
904 gettext("%s interrupted, continue"), progname);
905 if (reply(buf) == FAIL)
906 done(1);
907 }
908 /*
909 * Set up pager_catenated and pager_vector.
910 */
911 void
912 #ifdef __STDC__
initpagercmd(void)913 initpagercmd(void)
914 #else
915 initpagercmd()
916 #endif
917 {
918 char *cp;
919
920 cp = getenv("PAGER");
921 if (cp != NULL)
922 pager_catenated = strdup(cp);
923 if ((pager_catenated == NULL) || (*pager_catenated == '\0')) {
924 if (pager_catenated != NULL)
925 free(pager_catenated);
926 pager_catenated = strdup(DEF_PAGER);
927 }
928 if (pager_catenated == NULL) {
929 (void) fprintf(stderr, gettext("out of memory\n"));
930 done(1);
931 }
932
933 pager_vector = (char **)malloc(sizeof (char *));
934 if (pager_vector == NULL) {
935 (void) fprintf(stderr, gettext("out of memory\n"));
936 done(1);
937 }
938
939 pager_len = 1;
940 cp = pager_catenated;
941 (void) setpagerargs(&cp);
942 }
943
944
945 /*
946 * Resets pager_catenated and pager_vector from user input.
947 */
948 void
949 #ifdef __STDC__
setpagercmd(void)950 setpagercmd(void)
951 #else
952 setpagercmd()
953 #endif
954 {
955 uint_t catenate_length;
956 int index;
957
958 /*
959 * We'll get called immediately after setting a pager, due to
960 * our interaction with getcmd()'s internal state. Don't do
961 * anything when that happens.
962 */
963 if (*input == '\0')
964 return;
965
966 if (pager_len > 0) {
967 for (index = 0; pager_vector[index] != (char *)NULL; index += 1)
968 free(pager_vector[index]);
969 free(pager_vector);
970 free(pager_catenated);
971 }
972
973 pager_vector = (char **)malloc(2 * sizeof (char *));
974 if (pager_vector == NULL) {
975 (void) fprintf(stderr, gettext("out of memory\n"));
976 done(1);
977 }
978
979 pager_len = 2;
980 pager_vector[0] = strdup(input);
981 if (pager_vector[0] == NULL) {
982 (void) fprintf(stderr, gettext("out of memory\n"));
983 done(1);
984 }
985 if (dflag)
986 (void) fprintf(stderr, gettext("got command `%s'\n"), input);
987 catenate_length = setpagerargs(&nextarg) + strlen(pager_vector[0]) + 1;
988 pager_catenated = (char *)malloc(catenate_length *
989 (size_t)sizeof (char));
990 if (pager_catenated == (char *)NULL) {
991 (void) fprintf(stderr, gettext("out of memory\n"));
992 done(1);
993 }
994 for (index = 0; pager_vector[index] != (char *)NULL; index += 1) {
995 if (index > 0)
996 (void) strcat(pager_catenated, " ");
997 (void) strcat(pager_catenated, pager_vector[index]);
998 }
999 }
1000
1001
1002 /*
1003 * Extract arguments for the pager command from getcmd()'s input buffer.
1004 */
1005 static uint_t
setpagerargs(source)1006 setpagerargs(source)
1007 char **source;
1008 {
1009 char word[MAXCOMPLEXLEN];
1010 char *cp = *source;
1011 uint_t length = 0;
1012
1013 while ((cp != (char *)NULL) && (*cp != '\0')) {
1014 cp = copynext(cp, word, sizeof (word));
1015 if (dflag)
1016 fprintf(stderr, gettext("got word `%s'\n"), word);
1017 pager_vector = (char **)realloc(pager_vector,
1018 (size_t)sizeof (char *) * (pager_len + 1));
1019 if (pager_vector == (char **)NULL) {
1020 (void) fprintf(stderr, gettext("out of memory\n"));
1021 done(1);
1022 }
1023 pager_vector[pager_len - 1] = strdup(word);
1024 if (pager_vector[pager_len - 1] == (char *)NULL) {
1025 (void) fprintf(stderr, gettext("out of memory\n"));
1026 done(1);
1027 }
1028 length += strlen(word) + 1;
1029 pager_len += 1;
1030 }
1031 pager_vector[pager_len - 1] = (char *)NULL;
1032 *source = cp;
1033 return (length);
1034 }
1035