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