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