1 /* $Header: /p/tcsh/cvsroot/tcsh/sh.file.c,v 3.37 2010/02/09 20:21:49 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.37 2010/02/09 20:21:49 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 | 238 #ifndef __QNXNTO__ 239 ECHOPRT | 240 #endif 241 ECHOCTL); 242 # ifdef POSIX 243 (void) xtcsetattr(SHOUT, TCSANOW, &tty); 244 # else 245 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 246 # endif /* POSIX */ 247 248 for (p = string; *p != '\0'; p++) { 249 char buf[MB_LEN_MAX]; 250 size_t i, len; 251 252 len = one_wctomb(buf, *p & CHAR); 253 for (i = 0; i < len; i++) 254 (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) &buf[i]); 255 } 256 # ifdef POSIX 257 (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal); 258 # else 259 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 260 # endif /* POSIX */ 261 #else 262 (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty); 263 tty_normal = tty; 264 tty.sg_flags &= ~ECHO; 265 (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty); 266 267 for (p = string; c = *p; p++) 268 (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 269 (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal); 270 #endif /* TERMIO */ 271 272 cleanup_until(&pintr_disabled); 273 } 274 275 static int 276 filetype(const Char *dir, const Char *file) 277 { 278 Char *path; 279 char *spath; 280 struct stat statb; 281 282 path = Strspl(dir, file); 283 spath = short2str(path); 284 xfree(path); 285 if (lstat(spath, &statb) == 0) { 286 switch (statb.st_mode & S_IFMT) { 287 case S_IFDIR: 288 return ('/'); 289 290 case S_IFLNK: 291 if (stat(spath, &statb) == 0 && /* follow it out */ 292 S_ISDIR(statb.st_mode)) 293 return ('>'); 294 else 295 return ('@'); 296 297 case S_IFSOCK: 298 return ('='); 299 300 default: 301 if (statb.st_mode & 0111) 302 return ('*'); 303 } 304 } 305 return (' '); 306 } 307 308 /* 309 * Print sorted down columns 310 */ 311 static void 312 print_by_column(const Char *dir, Char *items[], size_t count) 313 { 314 struct winsize win; 315 size_t i; 316 int rows, r, c, maxwidth = 0, columns; 317 318 if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 319 win.ws_col = 80; 320 for (i = 0; i < count; i++) 321 maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 322 maxwidth += 2; /* for the file tag and space */ 323 columns = win.ws_col / maxwidth; 324 if (columns == 0) 325 columns = 1; 326 rows = (count + (columns - 1)) / columns; 327 for (r = 0; r < rows; r++) { 328 for (c = 0; c < columns; c++) { 329 i = c * rows + r; 330 if (i < count) { 331 int w; 332 333 xprintf("%S", items[i]); 334 xputchar(dir ? filetype(dir, items[i]) : ' '); 335 if (c < columns - 1) { /* last column? */ 336 w = Strlen(items[i]) + 1; 337 for (; w < maxwidth; w++) 338 xputchar(' '); 339 } 340 } 341 } 342 xputchar('\r'); 343 xputchar('\n'); 344 } 345 } 346 347 /* 348 * Expand file name with possible tilde usage 349 * ~person/mumble 350 * expands to 351 * home_directory_of_person/mumble 352 */ 353 static Char * 354 tilde(const Char *old) 355 { 356 const Char *o, *home; 357 struct passwd *pw; 358 359 if (old[0] != '~') 360 return (Strsave(old)); 361 old++; 362 363 for (o = old; *o != '\0' && *o != '/'; o++) 364 ; 365 if (o == old) 366 home = varval(STRhome); 367 else { 368 Char *person; 369 370 person = Strnsave(old, o - old); 371 pw = xgetpwnam(short2str(person)); 372 xfree(person); 373 if (pw == NULL) 374 return (NULL); 375 home = str2short(pw->pw_dir); 376 } 377 return Strspl(home, o); 378 } 379 380 /* 381 * Cause pending line to be printed 382 */ 383 static void 384 retype(void) 385 { 386 #ifdef TERMIO 387 # ifdef POSIX 388 struct termios tty; 389 390 (void) tcgetattr(SHOUT, &tty); 391 # else 392 struct termio tty; 393 394 (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 395 # endif /* POSIX */ 396 397 #ifndef __QNXNTO__ 398 tty.c_lflag |= PENDIN; 399 #endif 400 401 # ifdef POSIX 402 (void) xtcsetattr(SHOUT, TCSANOW, &tty); 403 # else 404 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 405 # endif /* POSIX */ 406 #else 407 int pending_input = LPENDIN; 408 409 (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input); 410 #endif /* TERMIO */ 411 } 412 413 static void 414 beep(void) 415 { 416 if (adrof(STRnobeep) == 0) 417 #ifdef IS_ASCII 418 (void) xwrite(SHOUT, "\007", 1); 419 #else 420 { 421 unsigned char beep_ch = CTL_ESC('\007'); 422 (void) xwrite(SHOUT, &beep_ch, 1); 423 } 424 #endif 425 } 426 427 /* 428 * Erase that silly ^[ and 429 * print the recognized part of the string 430 */ 431 static void 432 print_recognized_stuff(const Char *recognized_part) 433 { 434 /* An optimized erasing of that silly ^[ */ 435 (void) putraw('\b'); 436 (void) putraw('\b'); 437 switch (Strlen(recognized_part)) { 438 439 case 0: /* erase two Characters: ^[ */ 440 (void) putraw(' '); 441 (void) putraw(' '); 442 (void) putraw('\b'); 443 (void) putraw('\b'); 444 break; 445 446 case 1: /* overstrike the ^, erase the [ */ 447 xprintf("%S", recognized_part); 448 (void) putraw(' '); 449 (void) putraw('\b'); 450 break; 451 452 default: /* overstrike both Characters ^[ */ 453 xprintf("%S", recognized_part); 454 break; 455 } 456 flush(); 457 } 458 459 /* 460 * Parse full path in file into 2 parts: directory and file names 461 * Should leave final slash (/) at end of dir. 462 */ 463 static void 464 extract_dir_and_name(const Char *path, Char **dir, const Char **name) 465 { 466 const Char *p; 467 468 p = Strrchr(path, '/'); 469 if (p == NULL) 470 p = path; 471 else 472 p++; 473 *name = p; 474 *dir = Strnsave(path, p - path); 475 } 476 477 static Char * 478 getitem(DIR *dir_fd, int looking_for_lognames) 479 { 480 struct passwd *pw; 481 struct dirent *dirp; 482 483 if (looking_for_lognames) { 484 #ifndef HAVE_GETPWENT 485 return (NULL); 486 #else 487 if ((pw = getpwent()) == NULL) 488 return (NULL); 489 return (str2short(pw->pw_name)); 490 #endif /* atp vmsposix */ 491 } 492 if ((dirp = readdir(dir_fd)) != NULL) 493 return (str2short(dirp->d_name)); 494 return (NULL); 495 } 496 497 /* 498 * Perform a RECOGNIZE or LIST command on string "word". 499 */ 500 static size_t 501 tsearch(Char *word, COMMAND command, size_t max_word_length) 502 { 503 DIR *dir_fd; 504 int ignoring = TRUE, nignored = 0; 505 int looking_for_lognames; 506 Char *tilded_dir = NULL, *dir = NULL; 507 Char *extended_name = NULL; 508 const Char *name; 509 Char *item; 510 struct blk_buf items = BLK_BUF_INIT; 511 size_t name_length; 512 513 looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 514 if (looking_for_lognames) { 515 #ifdef HAVE_GETPWENT 516 (void) setpwent(); 517 #endif 518 name = word + 1; /* name sans ~ */ 519 dir_fd = NULL; 520 cleanup_push(dir, xfree); 521 } 522 else { 523 extract_dir_and_name(word, &dir, &name); 524 cleanup_push(dir, xfree); 525 tilded_dir = tilde(dir); 526 if (tilded_dir == NULL) 527 goto end; 528 cleanup_push(tilded_dir, xfree); 529 dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 530 if (dir_fd == NULL) 531 goto end; 532 } 533 534 name_length = Strlen(name); 535 cleanup_push(&extended_name, xfree_indirect); 536 cleanup_push(&items, bb_cleanup); 537 again: /* search for matches */ 538 while ((item = getitem(dir_fd, looking_for_lognames)) != NULL) { 539 if (!is_prefix(name, item)) 540 continue; 541 /* Don't match . files on null prefix match */ 542 if (name_length == 0 && item[0] == '.' && 543 !looking_for_lognames) 544 continue; 545 if (command == LIST) 546 bb_append(&items, Strsave(item)); 547 else { /* RECOGNIZE command */ 548 if (ignoring && ignored(item)) 549 nignored++; 550 else if (recognize(&extended_name, item, name_length, ++items.len)) 551 break; 552 } 553 } 554 if (ignoring && items.len == 0 && nignored > 0) { 555 ignoring = FALSE; 556 nignored = 0; 557 if (looking_for_lognames) { 558 #ifdef HAVE_GETPWENT 559 (void) setpwent(); 560 #endif /* atp vmsposix */ 561 } else 562 rewinddir(dir_fd); 563 goto again; 564 } 565 566 if (looking_for_lognames) { 567 #ifndef HAVE_GETPWENT 568 (void) endpwent(); 569 #endif 570 } else 571 xclosedir(dir_fd); 572 if (items.len != 0) { 573 if (command == RECOGNIZE) { 574 if (looking_for_lognames) 575 copyn(word, STRtilde, 2);/*FIXBUF, sort of */ 576 else 577 /* put back dir part */ 578 copyn(word, dir, max_word_length);/*FIXBUF*/ 579 /* add extended name */ 580 catn(word, extended_name, max_word_length);/*FIXBUF*/ 581 } 582 else { /* LIST */ 583 qsort(items.vec, items.len, sizeof(items.vec[0]), compare); 584 print_by_column(looking_for_lognames ? NULL : tilded_dir, 585 items.vec, items.len); 586 } 587 } 588 end: 589 cleanup_until(dir); 590 return items.len; 591 } 592 593 594 static int 595 compare(const void *p, const void *q) 596 { 597 #if defined (WIDE_STRINGS) && !defined (UTF16_STRING) 598 errno = 0; 599 600 return (wcscoll(*(Char *const *) p, *(Char *const *) q)); 601 #else 602 char *p1, *q1; 603 int res; 604 605 p1 = strsave(short2str(*(Char *const *) p)); 606 q1 = strsave(short2str(*(Char *const *) q)); 607 # if defined(NLS) && defined(HAVE_STRCOLL) 608 res = strcoll(p1, q1); 609 # else 610 res = strcmp(p1, q1); 611 # endif /* NLS && HAVE_STRCOLL */ 612 xfree (p1); 613 xfree (q1); 614 return res; 615 #endif /* not WIDE_STRINGS */ 616 } 617 618 /* 619 * Object: extend what user typed up to an ambiguity. 620 * Algorithm: 621 * On first match, copy full item (assume it'll be the only match) 622 * On subsequent matches, shorten extended_name to the first 623 * Character mismatch between extended_name and item. 624 * If we shorten it back to the prefix length, stop searching. 625 */ 626 static int 627 recognize(Char **extended_name, Char *item, size_t name_length, 628 size_t numitems) 629 { 630 if (numitems == 1) /* 1st match */ 631 *extended_name = Strsave(item); 632 else { /* 2nd & subsequent matches */ 633 Char *x, *ent; 634 size_t len = 0; 635 636 x = *extended_name; 637 for (ent = item; *x && *x == *ent++; x++, len++); 638 *x = '\0'; /* Shorten at 1st Char diff */ 639 if (len == name_length) /* Ambiguous to prefix? */ 640 return (-1); /* So stop now and save time */ 641 } 642 return (0); 643 } 644 645 /* 646 * Return true if check matches initial Chars in template. 647 * This differs from PWB imatch in that if check is null 648 * it matches anything. 649 */ 650 static int 651 is_prefix(const Char *check, const Char *template) 652 { 653 do 654 if (*check == 0) 655 return (TRUE); 656 while (*check++ == *template++); 657 return (FALSE); 658 } 659 660 /* 661 * Return true if the Chars in template appear at the 662 * end of check, I.e., are it's suffix. 663 */ 664 static int 665 is_suffix(const Char *check, const Char *template) 666 { 667 const Char *c, *t; 668 669 for (c = check; *c++;); 670 for (t = template; *t++;); 671 for (;;) { 672 if (t == template) 673 return 1; 674 if (c == check || *--t != *--c) 675 return 0; 676 } 677 } 678 679 static void 680 setup_tty_cleanup(void *dummy) 681 { 682 USE(dummy); 683 setup_tty(OFF); 684 } 685 686 size_t 687 tenex(Char *inputline, size_t inputline_size) 688 { 689 size_t numitems; 690 ssize_t num_read; 691 char tinputline[BUFSIZE + 1];/*FIXBUF*/ 692 693 setup_tty(ON); 694 cleanup_push(&num_read, setup_tty_cleanup); /* num_read is only a marker */ 695 696 while ((num_read = xread(SHIN, tinputline, BUFSIZE)) > 0) {/*FIXBUF*/ 697 static const Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 698 '>', '(', ')', '|', '^', '%', '\0'}; 699 Char *str_end, *word_start, last_Char, should_retype; 700 size_t space_left; 701 COMMAND command; 702 703 tinputline[num_read] = 0; 704 Strcpy(inputline, str2short(tinputline));/*FIXBUF*/ 705 num_read = Strlen(inputline); 706 last_Char = CTL_ESC(ASC(inputline[num_read - 1]) & ASCII); 707 708 if (last_Char == '\n' || (size_t)num_read == inputline_size) 709 break; 710 command = (last_Char == ESC) ? RECOGNIZE : LIST; 711 if (command == LIST) 712 xputchar('\n'); 713 str_end = &inputline[num_read]; 714 if (last_Char == ESC) 715 --str_end; /* wipeout trailing cmd Char */ 716 *str_end = '\0'; 717 /* 718 * Find LAST occurence of a delimiter in the inputline. The word start 719 * is one Character past it. 720 */ 721 for (word_start = str_end; word_start > inputline; --word_start) 722 if (Strchr(delims, word_start[-1])) 723 break; 724 space_left = inputline_size - (word_start - inputline) - 1; 725 numitems = tsearch(word_start, command, space_left); 726 727 if (command == RECOGNIZE) { 728 /* print from str_end on */ 729 print_recognized_stuff(str_end); 730 if (numitems != 1) /* Beep = No match/ambiguous */ 731 beep(); 732 } 733 734 /* 735 * Tabs in the input line cause trouble after a pushback. tty driver 736 * won't backspace over them because column positions are now 737 * incorrect. This is solved by retyping over current line. 738 */ 739 should_retype = FALSE; 740 if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 741 back_to_col_1(); 742 should_retype = TRUE; 743 } 744 if (command == LIST) /* Always retype after a LIST */ 745 should_retype = TRUE; 746 if (should_retype) 747 printprompt(0, NULL); 748 pushback(inputline); 749 if (should_retype) 750 retype(); 751 } 752 cleanup_until(&num_read); 753 return (num_read); 754 } 755 756 static int 757 ignored(const Char *item) 758 { 759 struct varent *vp; 760 Char **cp; 761 762 if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 763 return (FALSE); 764 for (; *cp != NULL; cp++) 765 if (is_suffix(item, *cp)) 766 return (TRUE); 767 return (FALSE); 768 } 769 #endif /* FILEC && TIOCSTI */ 770