1 /* 2 * sh.file.c: File completion for csh. This file is not used in tcsh. 3 */ 4 /*- 5 * Copyright (c) 1980, 1991 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 #include "sh.h" 33 #include "ed.h" 34 35 #if defined(FILEC) && defined(TIOCSTI) 36 37 /* 38 * Tenex style file name recognition, .. and more. 39 * History: 40 * Author: Ken Greer, Sept. 1975, CMU. 41 * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 42 */ 43 44 #define ON 1 45 #define OFF 0 46 #ifndef TRUE 47 #define TRUE 1 48 #endif 49 #ifndef FALSE 50 #define FALSE 0 51 #endif 52 53 #define ESC CTL_ESC('\033') 54 55 typedef enum { 56 LIST, RECOGNIZE 57 } COMMAND; 58 59 static void setup_tty (int); 60 static void back_to_col_1 (void); 61 static void pushback (const Char *); 62 static void print_by_column (const Char *, Char *[], size_t); 63 static Char *tilde (const Char *); 64 static void retype (void); 65 static void beep (void); 66 static void print_recognized_stuff (const Char *); 67 static void extract_dir_and_name (const Char *, Char **, const Char **); 68 static Char *getitem (DIR *, int); 69 static size_t tsearch (Char *, COMMAND, size_t); 70 static int compare (const void *, const void *); 71 static int recognize (Char **, Char *, size_t, size_t); 72 static int is_prefix (const Char *, const Char *); 73 static int is_suffix (const Char *, const Char *); 74 static int ignored (const Char *); 75 76 77 /* 78 * Put this here so the binary can be patched with adb to enable file 79 * completion by default. Filec controls completion, nobeep controls 80 * ringing the terminal bell on incomplete expansions. 81 */ 82 int filec = 0; 83 84 static void 85 setup_tty(int on) 86 { 87 #ifdef TERMIO 88 # ifdef POSIX 89 struct termios tchars; 90 # else 91 struct termio tchars; 92 # endif /* POSIX */ 93 94 # ifdef POSIX 95 (void) tcgetattr(SHIN, &tchars); 96 # else 97 (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars); 98 # endif /* POSIX */ 99 if (on) { 100 tchars.c_cc[VEOL] = ESC; 101 if (tchars.c_lflag & ICANON) 102 # ifdef POSIX 103 on = TCSADRAIN; 104 # else 105 on = TCSETA; 106 # endif /* POSIX */ 107 else { 108 # ifdef POSIX 109 on = TCSAFLUSH; 110 # else 111 on = TCSETAF; 112 # endif /* POSIX */ 113 tchars.c_lflag |= ICANON; 114 115 } 116 } 117 else { 118 tchars.c_cc[VEOL] = _POSIX_VDISABLE; 119 # ifdef POSIX 120 on = TCSADRAIN; 121 # else 122 on = TCSETA; 123 # endif /* POSIX */ 124 } 125 # ifdef POSIX 126 (void) xtcsetattr(SHIN, on, &tchars); 127 # else 128 (void) ioctl(SHIN, on, (ioctl_t) &tchars); 129 # endif /* POSIX */ 130 #else 131 struct sgttyb sgtty; 132 static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */ 133 134 if (on) { 135 (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars); 136 tchars.t_brkc = ESC; 137 (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 138 /* 139 * This must be done after every command: if the tty gets into raw or 140 * cbreak mode the user can't even type 'reset'. 141 */ 142 (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty); 143 if (sgtty.sg_flags & (RAW | CBREAK)) { 144 sgtty.sg_flags &= ~(RAW | CBREAK); 145 (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty); 146 } 147 } 148 else { 149 tchars.t_brkc = -1; 150 (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 151 } 152 #endif /* TERMIO */ 153 } 154 155 /* 156 * Move back to beginning of current line 157 */ 158 static void 159 back_to_col_1(void) 160 { 161 #ifdef TERMIO 162 # ifdef POSIX 163 struct termios tty, tty_normal; 164 # else 165 struct termio tty, tty_normal; 166 # endif /* POSIX */ 167 #else 168 struct sgttyb tty, tty_normal; 169 #endif /* TERMIO */ 170 171 pintr_disabled++; 172 cleanup_push(&pintr_disabled, disabled_cleanup); 173 174 #ifdef TERMIO 175 # ifdef POSIX 176 (void) tcgetattr(SHOUT, &tty); 177 # else 178 (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal); 179 # endif /* POSIX */ 180 tty_normal = tty; 181 tty.c_iflag &= ~INLCR; 182 tty.c_oflag &= ~ONLCR; 183 # ifdef POSIX 184 (void) xtcsetattr(SHOUT, TCSANOW, &tty); 185 # else 186 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 187 # endif /* POSIX */ 188 (void) xwrite(SHOUT, "\r", 1); 189 # ifdef POSIX 190 (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal); 191 # else 192 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 193 # endif /* POSIX */ 194 #else 195 (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty); 196 tty_normal = tty; 197 tty.sg_flags &= ~CRMOD; 198 (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty); 199 (void) xwrite(SHOUT, "\r", 1); 200 (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal); 201 #endif /* TERMIO */ 202 203 cleanup_until(&pintr_disabled); 204 } 205 206 /* 207 * Push string contents back into tty queue 208 */ 209 static void 210 pushback(const Char *string) 211 { 212 const Char *p; 213 #ifdef TERMIO 214 # ifdef POSIX 215 struct termios tty, tty_normal; 216 # else 217 struct termio tty, tty_normal; 218 # endif /* POSIX */ 219 #else 220 struct sgttyb tty, tty_normal; 221 #endif /* TERMIO */ 222 223 pintr_disabled++; 224 cleanup_push(&pintr_disabled, disabled_cleanup); 225 226 #ifdef TERMIO 227 # ifdef POSIX 228 (void) tcgetattr(SHOUT, &tty); 229 # else 230 (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 231 # endif /* POSIX */ 232 tty_normal = tty; 233 tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | 234 #ifndef __QNXNTO__ 235 ECHOPRT | 236 #endif 237 ECHOCTL); 238 # ifdef POSIX 239 (void) xtcsetattr(SHOUT, TCSANOW, &tty); 240 # else 241 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 242 # endif /* POSIX */ 243 244 for (p = string; *p != '\0'; p++) { 245 char buf[MB_LEN_MAX]; 246 size_t i, len; 247 248 len = one_wctomb(buf, *p); 249 for (i = 0; i < len; i++) 250 (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) &buf[i]); 251 } 252 # ifdef POSIX 253 (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal); 254 # else 255 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 256 # endif /* POSIX */ 257 #else 258 (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty); 259 tty_normal = tty; 260 tty.sg_flags &= ~ECHO; 261 (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty); 262 263 for (p = string; c = *p; p++) 264 (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 265 (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal); 266 #endif /* TERMIO */ 267 268 cleanup_until(&pintr_disabled); 269 } 270 271 static int 272 filetype(const Char *dir, const Char *file) 273 { 274 Char *path; 275 char *spath; 276 struct stat statb; 277 278 path = Strspl(dir, file); 279 spath = short2str(path); 280 xfree(path); 281 if (lstat(spath, &statb) == 0) { 282 switch (statb.st_mode & S_IFMT) { 283 case S_IFDIR: 284 return ('/'); 285 286 case S_IFLNK: 287 if (stat(spath, &statb) == 0 && /* follow it out */ 288 S_ISDIR(statb.st_mode)) 289 return ('>'); 290 else 291 return ('@'); 292 293 case S_IFSOCK: 294 return ('='); 295 296 default: 297 if (statb.st_mode & 0111) 298 return ('*'); 299 } 300 } 301 return (' '); 302 } 303 304 /* 305 * Print sorted down columns 306 */ 307 static void 308 print_by_column(const Char *dir, Char *items[], size_t count) 309 { 310 struct winsize win; 311 size_t i; 312 int rows, r, c, maxwidth = 0, columns; 313 314 if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 315 win.ws_col = 80; 316 for (i = 0; i < count; i++) 317 maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 318 maxwidth += 2; /* for the file tag and space */ 319 columns = win.ws_col / maxwidth; 320 if (columns == 0) 321 columns = 1; 322 rows = (count + (columns - 1)) / columns; 323 for (r = 0; r < rows; r++) { 324 for (c = 0; c < columns; c++) { 325 i = c * rows + r; 326 if (i < count) { 327 int w; 328 329 xprintf("%" TCSH_S, items[i]); 330 xputchar(dir ? filetype(dir, items[i]) : ' '); 331 if (c < columns - 1) { /* last column? */ 332 w = Strlen(items[i]) + 1; 333 for (; w < maxwidth; w++) 334 xputchar(' '); 335 } 336 } 337 } 338 xputchar('\r'); 339 xputchar('\n'); 340 } 341 } 342 343 /* 344 * Expand file name with possible tilde usage 345 * ~person/mumble 346 * expands to 347 * home_directory_of_person/mumble 348 */ 349 static Char * 350 tilde(const Char *old) 351 { 352 const Char *o, *home; 353 struct passwd *pw; 354 355 if (old[0] != '~') 356 return (Strsave(old)); 357 old++; 358 359 for (o = old; *o != '\0' && *o != '/'; o++) 360 ; 361 if (o == old) 362 home = varval(STRhome); 363 else { 364 Char *person; 365 366 person = Strnsave(old, o - old); 367 pw = xgetpwnam(short2str(person)); 368 xfree(person); 369 if (pw == NULL) 370 return (NULL); 371 home = str2short(pw->pw_dir); 372 } 373 return Strspl(home, o); 374 } 375 376 /* 377 * Cause pending line to be printed 378 */ 379 static void 380 retype(void) 381 { 382 #ifdef TERMIO 383 # ifdef POSIX 384 struct termios tty; 385 386 (void) tcgetattr(SHOUT, &tty); 387 # else 388 struct termio tty; 389 390 (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 391 # endif /* POSIX */ 392 393 #ifndef __QNXNTO__ 394 tty.c_lflag |= PENDIN; 395 #endif 396 397 # ifdef POSIX 398 (void) xtcsetattr(SHOUT, TCSANOW, &tty); 399 # else 400 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 401 # endif /* POSIX */ 402 #else 403 int pending_input = LPENDIN; 404 405 (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input); 406 #endif /* TERMIO */ 407 } 408 409 static void 410 beep(void) 411 { 412 if (adrof(STRnobeep) == 0) 413 #ifdef IS_ASCII 414 (void) xwrite(SHOUT, "\007", 1); 415 #else 416 { 417 unsigned char beep_ch = CTL_ESC('\007'); 418 (void) xwrite(SHOUT, &beep_ch, 1); 419 } 420 #endif 421 } 422 423 /* 424 * Erase that silly ^[ and 425 * print the recognized part of the string 426 */ 427 static void 428 print_recognized_stuff(const Char *recognized_part) 429 { 430 /* An optimized erasing of that silly ^[ */ 431 (void) putraw('\b'); 432 (void) putraw('\b'); 433 switch (Strlen(recognized_part)) { 434 435 case 0: /* erase two Characters: ^[ */ 436 (void) putraw(' '); 437 (void) putraw(' '); 438 (void) putraw('\b'); 439 (void) putraw('\b'); 440 break; 441 442 case 1: /* overstrike the ^, erase the [ */ 443 xprintf("%" TCSH_S, recognized_part); 444 (void) putraw(' '); 445 (void) putraw('\b'); 446 break; 447 448 default: /* overstrike both Characters ^[ */ 449 xprintf("%" TCSH_S, recognized_part); 450 break; 451 } 452 flush(); 453 } 454 455 /* 456 * Parse full path in file into 2 parts: directory and file names 457 * Should leave final slash (/) at end of dir. 458 */ 459 static void 460 extract_dir_and_name(const Char *path, Char **dir, const Char **name) 461 { 462 const Char *p; 463 464 p = Strrchr(path, '/'); 465 if (p == NULL) 466 p = path; 467 else 468 p++; 469 *name = p; 470 *dir = Strnsave(path, p - path); 471 } 472 473 static Char * 474 getitem(DIR *dir_fd, int looking_for_lognames) 475 { 476 struct passwd *pw; 477 struct dirent *dirp; 478 479 if (looking_for_lognames) { 480 #ifndef HAVE_GETPWENT 481 return (NULL); 482 #else 483 if ((pw = getpwent()) == NULL) 484 return (NULL); 485 return (str2short(pw->pw_name)); 486 #endif /* atp vmsposix */ 487 } 488 if ((dirp = readdir(dir_fd)) != NULL) 489 return (str2short(dirp->d_name)); 490 return (NULL); 491 } 492 493 /* 494 * Perform a RECOGNIZE or LIST command on string "word". 495 */ 496 static size_t 497 tsearch(Char *word, COMMAND command, size_t max_word_length) 498 { 499 DIR *dir_fd; 500 int ignoring = TRUE, nignored = 0; 501 int looking_for_lognames; 502 Char *tilded_dir = NULL, *dir = NULL; 503 Char *extended_name = NULL; 504 const Char *name; 505 Char *item; 506 struct blk_buf items = BLK_BUF_INIT; 507 size_t name_length; 508 509 looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 510 if (looking_for_lognames) { 511 #ifdef HAVE_GETPWENT 512 (void) setpwent(); 513 #endif 514 name = word + 1; /* name sans ~ */ 515 dir_fd = NULL; 516 cleanup_push(dir, xfree); 517 } 518 else { 519 extract_dir_and_name(word, &dir, &name); 520 cleanup_push(dir, xfree); 521 tilded_dir = tilde(dir); 522 if (tilded_dir == NULL) 523 goto end; 524 cleanup_push(tilded_dir, xfree); 525 dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 526 if (dir_fd == NULL) 527 goto end; 528 } 529 530 name_length = Strlen(name); 531 cleanup_push(&extended_name, xfree_indirect); 532 cleanup_push(&items, bb_cleanup); 533 again: /* search for matches */ 534 while ((item = getitem(dir_fd, looking_for_lognames)) != NULL) { 535 if (!is_prefix(name, item)) 536 continue; 537 /* Don't match . files on null prefix match */ 538 if (name_length == 0 && item[0] == '.' && 539 !looking_for_lognames) 540 continue; 541 if (command == LIST) 542 bb_append(&items, Strsave(item)); 543 else { /* RECOGNIZE command */ 544 if (ignoring && ignored(item)) 545 nignored++; 546 else if (recognize(&extended_name, item, name_length, ++items.len)) 547 break; 548 } 549 } 550 if (ignoring && items.len == 0 && nignored > 0) { 551 ignoring = FALSE; 552 nignored = 0; 553 if (looking_for_lognames) { 554 #ifdef HAVE_GETPWENT 555 (void) setpwent(); 556 #endif /* atp vmsposix */ 557 } else 558 rewinddir(dir_fd); 559 goto again; 560 } 561 562 if (looking_for_lognames) { 563 #ifdef HAVE_GETPWENT 564 (void) endpwent(); 565 #endif 566 } else 567 xclosedir(dir_fd); 568 if (items.len != 0) { 569 if (command == RECOGNIZE) { 570 if (looking_for_lognames) 571 copyn(word, STRtilde, 2);/*FIXBUF, sort of */ 572 else 573 /* put back dir part */ 574 copyn(word, dir, max_word_length);/*FIXBUF*/ 575 /* add extended name */ 576 catn(word, extended_name, max_word_length);/*FIXBUF*/ 577 } 578 else { /* LIST */ 579 qsort(items.vec, items.len, sizeof(items.vec[0]), compare); 580 print_by_column(looking_for_lognames ? NULL : tilded_dir, 581 items.vec, items.len); 582 } 583 } 584 end: 585 cleanup_until(dir); 586 return items.len; 587 } 588 589 590 static int 591 compare(const void *p, const void *q) 592 { 593 #if defined (WIDE_STRINGS) && !defined (UTF16_STRING) 594 errno = 0; 595 596 return (wcscoll(*(Char *const *) p, *(Char *const *) q)); 597 #else 598 char *p1, *q1; 599 int res; 600 601 p1 = strsave(short2str(*(Char *const *) p)); 602 q1 = strsave(short2str(*(Char *const *) q)); 603 # if defined(NLS) && defined(HAVE_STRCOLL) 604 res = strcoll(p1, q1); 605 # else 606 res = strcmp(p1, q1); 607 # endif /* NLS && HAVE_STRCOLL */ 608 xfree (p1); 609 xfree (q1); 610 return res; 611 #endif /* not WIDE_STRINGS */ 612 } 613 614 /* 615 * Object: extend what user typed up to an ambiguity. 616 * Algorithm: 617 * On first match, copy full item (assume it'll be the only match) 618 * On subsequent matches, shorten extended_name to the first 619 * Character mismatch between extended_name and item. 620 * If we shorten it back to the prefix length, stop searching. 621 */ 622 static int 623 recognize(Char **extended_name, Char *item, size_t name_length, 624 size_t numitems) 625 { 626 if (numitems == 1) /* 1st match */ 627 *extended_name = Strsave(item); 628 else { /* 2nd & subsequent matches */ 629 Char *x, *ent; 630 size_t len = 0; 631 632 x = *extended_name; 633 for (ent = item; *x && *x == *ent++; x++, len++); 634 *x = '\0'; /* Shorten at 1st Char diff */ 635 if (len == name_length) /* Ambiguous to prefix? */ 636 return (-1); /* So stop now and save time */ 637 } 638 return (0); 639 } 640 641 /* 642 * Return true if check matches initial Chars in template. 643 * This differs from PWB imatch in that if check is null 644 * it matches anything. 645 */ 646 static int 647 is_prefix(const Char *check, const Char *template) 648 { 649 do 650 if (*check == 0) 651 return (TRUE); 652 while (*check++ == *template++); 653 return (FALSE); 654 } 655 656 /* 657 * Return true if the Chars in template appear at the 658 * end of check, I.e., are it's suffix. 659 */ 660 static int 661 is_suffix(const Char *check, const Char *template) 662 { 663 const Char *c, *t; 664 665 for (c = check; *c++;); 666 for (t = template; *t++;); 667 for (;;) { 668 if (t == template) 669 return 1; 670 if (c == check || *--t != *--c) 671 return 0; 672 } 673 } 674 675 static void 676 setup_tty_cleanup(void *dummy) 677 { 678 USE(dummy); 679 setup_tty(OFF); 680 } 681 682 size_t 683 tenex(Char *inputline, size_t inputline_size) 684 { 685 size_t numitems; 686 ssize_t num_read; 687 char tinputline[BUFSIZE + 1];/*FIXBUF*/ 688 689 setup_tty(ON); 690 cleanup_push(&num_read, setup_tty_cleanup); /* num_read is only a marker */ 691 692 while ((num_read = xread(SHIN, tinputline, BUFSIZE)) > 0) {/*FIXBUF*/ 693 static const Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 694 '>', '(', ')', '|', '^', '%', '\0'}; 695 Char *str_end, *word_start, last_Char, should_retype; 696 size_t space_left; 697 COMMAND command; 698 699 tinputline[num_read] = 0; 700 Strcpy(inputline, str2short(tinputline));/*FIXBUF*/ 701 num_read = Strlen(inputline); 702 last_Char = CTL_ESC(ASC(inputline[num_read - 1]) & ASCII); 703 704 if (last_Char == '\n' || (size_t)num_read == inputline_size) 705 break; 706 command = (last_Char == ESC) ? RECOGNIZE : LIST; 707 if (command == LIST) 708 xputchar('\n'); 709 str_end = &inputline[num_read]; 710 if (last_Char == ESC) 711 --str_end; /* wipeout trailing cmd Char */ 712 *str_end = '\0'; 713 /* 714 * Find LAST occurence of a delimiter in the inputline. The word start 715 * is one Character past it. 716 */ 717 for (word_start = str_end; word_start > inputline; --word_start) 718 if (Strchr(delims, word_start[-1])) 719 break; 720 space_left = inputline_size - (word_start - inputline) - 1; 721 numitems = tsearch(word_start, command, space_left); 722 723 if (command == RECOGNIZE) { 724 /* print from str_end on */ 725 print_recognized_stuff(str_end); 726 if (numitems != 1) /* Beep = No match/ambiguous */ 727 beep(); 728 } 729 730 /* 731 * Tabs in the input line cause trouble after a pushback. tty driver 732 * won't backspace over them because column positions are now 733 * incorrect. This is solved by retyping over current line. 734 */ 735 should_retype = FALSE; 736 if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 737 back_to_col_1(); 738 should_retype = TRUE; 739 } 740 if (command == LIST) /* Always retype after a LIST */ 741 should_retype = TRUE; 742 if (should_retype) 743 printprompt(0, NULL); 744 pushback(inputline); 745 if (should_retype) 746 retype(); 747 } 748 cleanup_until(&num_read); 749 return (num_read); 750 } 751 752 static int 753 ignored(const Char *item) 754 { 755 struct varent *vp; 756 Char **cp; 757 758 if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 759 return (FALSE); 760 for (; *cp != NULL; cp++) 761 if (is_suffix(item, *cp)) 762 return (TRUE); 763 return (FALSE); 764 } 765 #endif /* FILEC && TIOCSTI */ 766