sh.file.c revision 296373
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 36RCSID("$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 58typedef enum { 59 LIST, RECOGNIZE 60} COMMAND; 61 62static void setup_tty (int); 63static void back_to_col_1 (void); 64static void pushback (const Char *); 65static int filetype (const Char *, const Char *); 66static void print_by_column (const Char *, Char *[], size_t); 67static Char *tilde (const Char *); 68static void retype (void); 69static void beep (void); 70static void print_recognized_stuff (const Char *); 71static void extract_dir_and_name (const Char *, Char **, const Char **); 72static Char *getitem (DIR *, int); 73static size_t tsearch (Char *, COMMAND, size_t); 74static int compare (const void *, const void *); 75static int recognize (Char **, Char *, size_t, size_t); 76static int is_prefix (const Char *, const Char *); 77static int is_suffix (const Char *, const Char *); 78static 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 */ 86int filec = 0; 87 88static void 89setup_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 */ 162static void 163back_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 */ 213static void 214pushback(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 275static int 276filetype(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 */ 311static void 312print_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 */ 353static Char * 354tilde(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 */ 383static void 384retype(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 413static void 414beep(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 */ 431static void 432print_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 */ 463static void 464extract_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 477static Char * 478getitem(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 */ 500static size_t 501tsearch(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); 537again: /* 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 594static int 595compare(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 */ 626static int 627recognize(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 */ 650static int 651is_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 */ 664static int 665is_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 679static void 680setup_tty_cleanup(void *dummy) 681{ 682 USE(dummy); 683 setup_tty(OFF); 684} 685 686size_t 687tenex(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 756static int 757ignored(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