1 /* $OpenBSD: file.c,v 1.39 2019/11/29 05:28:32 nayden Exp $ */ 2 /* $NetBSD: file.c,v 1.11 1996/11/08 19:34:37 christos Exp $ */ 3 4 /*- 5 * Copyright (c) 1980, 1991, 1993 6 * The Regents of the University of California. 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 33 #include <sys/ioctl.h> 34 #include <sys/stat.h> 35 #include <sys/types.h> 36 37 #include <dirent.h> 38 #include <errno.h> 39 #include <limits.h> 40 #include <pwd.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <termios.h> 44 #include <unistd.h> 45 46 #include "csh.h" 47 #include "extern.h" 48 49 /* 50 * Tenex style file name recognition, .. and more. 51 * History: 52 * Author: Ken Greer, Sept. 1975, CMU. 53 * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 54 */ 55 56 #ifndef TRUE 57 #define TRUE 1 58 #endif 59 #ifndef FALSE 60 #define FALSE 0 61 #endif 62 63 #define ESC '\033' 64 #define TABWIDTH 8 65 66 typedef enum { 67 LIST, 68 RECOGNIZE 69 } COMMAND; 70 71 struct cmdline { 72 int fdin; 73 int fdout; 74 int flags; 75 #define CL_ALTWERASE 0x1 76 #define CL_PROMPT 0x2 77 char *buf; 78 size_t len; 79 size_t size; 80 size_t cursor; 81 }; 82 83 /* Command line auxiliary functions. */ 84 static void cl_beep(struct cmdline *); 85 static void cl_flush(struct cmdline *); 86 static int cl_getc(struct cmdline *); 87 static Char *cl_lastw(struct cmdline *); 88 static void cl_putc(struct cmdline *, int); 89 static void cl_visc(struct cmdline *, int); 90 91 /* Command line editing functions. */ 92 static int cl_abort(struct cmdline *, int); 93 static int cl_erasec(struct cmdline *, int); 94 static int cl_erasew(struct cmdline *, int); 95 static int cl_insert(struct cmdline *, int); 96 static int cl_kill(struct cmdline *, int); 97 static int cl_list(struct cmdline *, int); 98 static int cl_literal(struct cmdline *, int); 99 static int cl_recognize(struct cmdline *, int); 100 static int cl_reprint(struct cmdline *, int); 101 static int cl_status(struct cmdline *, int); 102 103 static const struct termios *setup_tty(int); 104 105 static void catn(Char *, Char *, int); 106 static void copyn(Char *, Char *, int); 107 static Char filetype(Char *, Char *); 108 static void print_by_column(Char *, Char *[], int); 109 static Char *tilde(Char *, Char *); 110 static void extract_dir_and_name(Char *, Char *, Char *); 111 static Char *getentry(DIR *, int); 112 static void free_items(Char **, int); 113 static int tsearch(Char *, COMMAND, int); 114 static int recognize(Char *, Char *, int, int); 115 static int is_prefix(Char *, Char *); 116 static int is_suffix(Char *, Char *); 117 static int ignored(Char *); 118 119 /* 120 * Put this here so the binary can be patched with adb to enable file 121 * completion by default. Filec controls completion, nobeep controls 122 * ringing the terminal bell on incomplete expansions. 123 */ 124 bool filec = 0; 125 126 static void 127 cl_flush(struct cmdline *cl) 128 { 129 size_t i, len; 130 int c; 131 132 if (cl->flags & CL_PROMPT) { 133 cl->flags &= ~CL_PROMPT; 134 printprompt(); 135 } 136 137 if (cl->cursor < cl->len) { 138 for (; cl->cursor < cl->len; cl->cursor++) 139 cl_visc(cl, cl->buf[cl->cursor]); 140 } else if (cl->cursor > cl->len) { 141 len = cl->cursor - cl->len; 142 for (i = len; i > 0; i--) { 143 c = cl->buf[--cl->cursor]; 144 if (c == '\t') 145 len += TABWIDTH - 1; 146 else if (iscntrl(c)) 147 len++; /* account for leading ^ */ 148 } 149 for (i = 0; i < len; i++) 150 cl_putc(cl, '\b'); 151 for (i = 0; i < len; i++) 152 cl_putc(cl, ' '); 153 for (i = 0; i < len; i++) 154 cl_putc(cl, '\b'); 155 cl->cursor = cl->len; 156 } 157 } 158 159 static int 160 cl_getc(struct cmdline *cl) 161 { 162 ssize_t n; 163 unsigned char c; 164 165 for (;;) { 166 n = read(cl->fdin, &c, 1); 167 switch (n) { 168 case -1: 169 if (errno == EINTR) 170 continue; 171 /* FALLTHROUGH */ 172 case 0: 173 return 0; 174 default: 175 return c & 0x7F; 176 } 177 } 178 } 179 180 static Char * 181 cl_lastw(struct cmdline *cl) 182 { 183 static Char word[BUFSIZ]; 184 const unsigned char *delimiters = " '\"\t;&<>()|^%"; 185 Char *cp; 186 size_t i; 187 188 for (i = cl->len; i > 0; i--) 189 if (strchr(delimiters, cl->buf[i - 1]) != NULL) 190 break; 191 192 cp = word; 193 for (; i < cl->len; i++) 194 *cp++ = cl->buf[i]; 195 *cp = '\0'; 196 197 return word; 198 } 199 200 static void 201 cl_putc(struct cmdline *cl, int c) 202 { 203 unsigned char cc = c; 204 205 write(cl->fdout, &cc, 1); 206 } 207 208 static void 209 cl_visc(struct cmdline *cl, int c) 210 { 211 #define UNCNTRL(x) ((x) == 0x7F ? '?' : ((x) | 0x40)) 212 int i; 213 214 if (c == '\t') { 215 for (i = 0; i < TABWIDTH; i++) 216 cl_putc(cl, ' '); 217 } else if (c != '\n' && iscntrl(c)) { 218 cl_putc(cl, '^'); 219 cl_putc(cl, UNCNTRL(c)); 220 } else { 221 cl_putc(cl, c); 222 } 223 } 224 225 static int 226 cl_abort(struct cmdline *cl, int c) 227 { 228 cl_visc(cl, c); 229 230 /* Abort while/foreach loop prematurely. */ 231 if (whyles) { 232 setup_tty(0); 233 kill(getpid(), SIGINT); 234 } 235 236 cl_putc(cl, '\n'); 237 cl->len = cl->cursor = 0; 238 cl->flags |= CL_PROMPT; 239 240 return 0; 241 } 242 243 static int 244 cl_erasec(struct cmdline *cl, int c) 245 { 246 if (cl->len > 0) 247 cl->len--; 248 249 return 0; 250 } 251 252 static int 253 cl_erasew(struct cmdline *cl, int c) 254 { 255 const unsigned char *ws = " \t"; 256 257 for (; cl->len > 0; cl->len--) 258 if (strchr(ws, cl->buf[cl->len - 1]) == NULL && 259 ((cl->flags & CL_ALTWERASE) == 0 || 260 isalpha(cl->buf[cl->len - 1]))) 261 break; 262 for (; cl->len > 0; cl->len--) 263 if (strchr(ws, cl->buf[cl->len - 1]) != NULL || 264 ((cl->flags & CL_ALTWERASE) && 265 !isalpha(cl->buf[cl->len - 1]))) 266 break; 267 268 return 0; 269 } 270 271 static void 272 cl_beep(struct cmdline *cl) 273 { 274 if (adrof(STRnobeep) == 0) 275 cl_putc(cl, '\007'); 276 } 277 278 static int 279 cl_insert(struct cmdline *cl, int c) 280 { 281 if (cl->len == cl->size) 282 return 1; 283 284 cl->buf[cl->len++] = c; 285 286 if (c == '\n') 287 return 1; 288 289 return 0; 290 } 291 292 static int 293 cl_kill(struct cmdline *cl, int c) 294 { 295 cl->len = 0; 296 297 return 0; 298 } 299 300 static int 301 cl_list(struct cmdline *cl, int c) 302 { 303 Char *word; 304 size_t len; 305 306 if (adrof(STRignoreeof) || cl->len > 0) 307 cl_visc(cl, c); 308 309 if (cl->len == 0) 310 return 1; 311 312 cl_putc(cl, '\n'); 313 cl->cursor = 0; 314 cl->flags |= CL_PROMPT; 315 316 word = cl_lastw(cl); 317 len = Strlen(word); 318 tsearch(word, LIST, BUFSIZ - len - 1); /* NUL */ 319 320 return 0; 321 } 322 323 static int 324 cl_literal(struct cmdline *cl, int c) 325 { 326 int literal; 327 328 literal = cl_getc(cl); 329 if (literal == '\n') 330 literal = '\r'; 331 cl_insert(cl, literal); 332 333 return 0; 334 } 335 336 static int 337 cl_recognize(struct cmdline *cl, int c) 338 { 339 Char *word; 340 size_t len; 341 int nitems; 342 343 if (cl->len == 0) { 344 cl_beep(cl); 345 return 0; 346 } 347 348 word = cl_lastw(cl); 349 len = Strlen(word); 350 nitems = tsearch(word, RECOGNIZE, BUFSIZ - len - 1); /* NUL */ 351 for (word += len; *word != '\0'; word++) 352 cl_insert(cl, *word); 353 if (nitems != 1) 354 cl_beep(cl); 355 356 return 0; 357 } 358 359 static int 360 cl_reprint(struct cmdline *cl, int c) 361 { 362 cl_visc(cl, c); 363 cl_putc(cl, '\n'); 364 cl->cursor = 0; 365 366 return 0; 367 } 368 369 static int 370 cl_status(struct cmdline *cl, int c) 371 { 372 cl->cursor = 0; 373 ioctl(cl->fdin, TIOCSTAT); 374 375 return 0; 376 } 377 378 const struct termios * 379 setup_tty(int on) 380 { 381 static struct termios newtio, oldtio; 382 383 if (on) { 384 tcgetattr(SHIN, &oldtio); 385 386 newtio = oldtio; 387 newtio.c_lflag &= ~(ECHO | ICANON | ISIG); 388 newtio.c_cc[VEOL] = ESC; 389 newtio.c_cc[VLNEXT] = _POSIX_VDISABLE; 390 newtio.c_cc[VMIN] = 1; 391 newtio.c_cc[VTIME] = 0; 392 } else { 393 newtio = oldtio; 394 } 395 396 tcsetattr(SHIN, TCSADRAIN, &newtio); 397 398 /* 399 * Since VLNEXT is disabled, restore its previous value in order to make 400 * the key detectable. 401 */ 402 newtio.c_cc[VLNEXT] = oldtio.c_cc[VLNEXT]; 403 404 return &newtio; 405 } 406 407 /* 408 * Concatenate src onto tail of des. 409 * Des is a string whose maximum length is count. 410 * Always null terminate. 411 */ 412 static void 413 catn(Char *des, Char *src, int count) 414 { 415 while (--count >= 0 && *des) 416 des++; 417 while (--count >= 0) 418 if ((*des++ = *src++) == 0) 419 return; 420 *des = '\0'; 421 } 422 423 /* 424 * Places Char's like strlcpy, but no special return value. 425 */ 426 static void 427 copyn(Char *des, Char *src, int count) 428 { 429 while (--count >= 0) 430 if ((*des++ = *src++) == 0) 431 return; 432 *des = '\0'; 433 } 434 435 static Char 436 filetype(Char *dir, Char *file) 437 { 438 Char path[PATH_MAX]; 439 struct stat statb; 440 441 Strlcpy(path, dir, sizeof path/sizeof(Char)); 442 catn(path, file, sizeof(path) / sizeof(Char)); 443 if (lstat(short2str(path), &statb) == 0) { 444 switch (statb.st_mode & S_IFMT) { 445 case S_IFDIR: 446 return ('/'); 447 448 case S_IFLNK: 449 if (stat(short2str(path), &statb) == 0 && /* follow it out */ 450 S_ISDIR(statb.st_mode)) 451 return ('>'); 452 else 453 return ('@'); 454 455 case S_IFSOCK: 456 return ('='); 457 458 default: 459 if (statb.st_mode & 0111) 460 return ('*'); 461 } 462 } 463 return (' '); 464 } 465 466 /* 467 * Print sorted down columns 468 */ 469 static void 470 print_by_column(Char *dir, Char *items[], int count) 471 { 472 struct winsize win; 473 int i, rows, r, c, maxwidth = 0, columns; 474 475 if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) == -1 || win.ws_col == 0) 476 win.ws_col = 80; 477 for (i = 0; i < count; i++) 478 maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 479 maxwidth += 2; /* for the file tag and space */ 480 columns = win.ws_col / maxwidth; 481 if (columns == 0) 482 columns = 1; 483 rows = (count + (columns - 1)) / columns; 484 for (r = 0; r < rows; r++) { 485 for (c = 0; c < columns; c++) { 486 i = c * rows + r; 487 if (i < count) { 488 int w; 489 490 (void) fprintf(cshout, "%s", vis_str(items[i])); 491 (void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout); 492 if (c < columns - 1) { /* last column? */ 493 w = Strlen(items[i]) + 1; 494 for (; w < maxwidth; w++) 495 (void) fputc(' ', cshout); 496 } 497 } 498 } 499 (void) fputc('\r', cshout); 500 (void) fputc('\n', cshout); 501 } 502 } 503 504 /* 505 * Expand file name with possible tilde usage 506 * ~person/mumble 507 * expands to 508 * home_directory_of_person/mumble 509 */ 510 static Char * 511 tilde(Char *new, Char *old) 512 { 513 Char *o, *p; 514 struct passwd *pw; 515 static Char person[40]; 516 517 if (old[0] != '~') { 518 Strlcpy(new, old, PATH_MAX); 519 return new; 520 } 521 522 for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) 523 continue; 524 *p = '\0'; 525 if (person[0] == '\0') 526 (void) Strlcpy(new, value(STRhome), PATH_MAX); 527 else { 528 pw = getpwnam(short2str(person)); 529 if (pw == NULL) 530 return (NULL); 531 (void) Strlcpy(new, str2short(pw->pw_dir), PATH_MAX); 532 } 533 (void) Strlcat(new, o, PATH_MAX); 534 return (new); 535 } 536 537 /* 538 * Parse full path in file into 2 parts: directory and file names 539 * Should leave final slash (/) at end of dir. 540 */ 541 static void 542 extract_dir_and_name(Char *path, Char *dir, Char *name) 543 { 544 Char *p; 545 546 p = Strrchr(path, '/'); 547 if (p == NULL) { 548 copyn(name, path, MAXNAMLEN); 549 dir[0] = '\0'; 550 } 551 else { 552 copyn(name, ++p, MAXNAMLEN); 553 copyn(dir, path, p - path); 554 } 555 } 556 557 static Char * 558 getentry(DIR *dir_fd, int looking_for_lognames) 559 { 560 struct passwd *pw; 561 struct dirent *dirp; 562 563 if (looking_for_lognames) { 564 if ((pw = getpwent()) == NULL) 565 return (NULL); 566 return (str2short(pw->pw_name)); 567 } 568 if ((dirp = readdir(dir_fd)) != NULL) 569 return (str2short(dirp->d_name)); 570 return (NULL); 571 } 572 573 static void 574 free_items(Char **items, int numitems) 575 { 576 int i; 577 578 for (i = 0; i < numitems; i++) 579 free(items[i]); 580 free(items); 581 } 582 583 #define FREE_ITEMS(items) { \ 584 sigset_t sigset, osigset;\ 585 \ 586 sigemptyset(&sigset);\ 587 sigaddset(&sigset, SIGINT);\ 588 sigprocmask(SIG_BLOCK, &sigset, &osigset);\ 589 free_items(items, numitems);\ 590 sigprocmask(SIG_SETMASK, &osigset, NULL);\ 591 } 592 593 /* 594 * Perform a RECOGNIZE or LIST command on string "word". 595 */ 596 static int 597 tsearch(Char *word, COMMAND command, int max_word_length) 598 { 599 DIR *dir_fd; 600 int numitems = 0, ignoring = TRUE, nignored = 0; 601 int name_length, looking_for_lognames; 602 Char tilded_dir[PATH_MAX], dir[PATH_MAX]; 603 Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; 604 Char *entry; 605 Char **items = NULL; 606 size_t maxitems = 0; 607 608 looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 609 if (looking_for_lognames) { 610 (void) setpwent(); 611 copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 612 dir_fd = NULL; 613 } 614 else { 615 extract_dir_and_name(word, dir, name); 616 if (tilde(tilded_dir, dir) == 0) 617 return (0); 618 dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 619 if (dir_fd == NULL) 620 return (0); 621 } 622 623 again: /* search for matches */ 624 name_length = Strlen(name); 625 for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) { 626 if (!is_prefix(name, entry)) 627 continue; 628 /* Don't match . files on null prefix match */ 629 if (name_length == 0 && entry[0] == '.' && 630 !looking_for_lognames) 631 continue; 632 if (command == LIST) { 633 if (numitems >= maxitems) { 634 maxitems += 1024; 635 items = xreallocarray(items, maxitems, sizeof(*items)); 636 } 637 items[numitems] = xreallocarray(NULL, (Strlen(entry) + 1), sizeof(Char)); 638 copyn(items[numitems], entry, MAXNAMLEN); 639 numitems++; 640 } 641 else { /* RECOGNIZE command */ 642 if (ignoring && ignored(entry)) 643 nignored++; 644 else if (recognize(extended_name, 645 entry, name_length, ++numitems)) 646 break; 647 } 648 } 649 if (ignoring && numitems == 0 && nignored > 0) { 650 ignoring = FALSE; 651 nignored = 0; 652 if (looking_for_lognames) 653 (void) setpwent(); 654 else 655 rewinddir(dir_fd); 656 goto again; 657 } 658 659 if (looking_for_lognames) 660 (void) endpwent(); 661 else 662 (void) closedir(dir_fd); 663 if (numitems == 0) 664 return (0); 665 if (command == RECOGNIZE) { 666 if (looking_for_lognames) 667 copyn(word, STRtilde, 1); 668 else 669 /* put back dir part */ 670 copyn(word, dir, max_word_length); 671 /* add extended name */ 672 catn(word, extended_name, max_word_length); 673 return (numitems); 674 } 675 else { /* LIST */ 676 qsort(items, numitems, sizeof(*items), sortscmp); 677 print_by_column(looking_for_lognames ? NULL : tilded_dir, 678 items, numitems); 679 if (items != NULL) 680 FREE_ITEMS(items); 681 } 682 return (0); 683 } 684 685 /* 686 * Object: extend what user typed up to an ambiguity. 687 * Algorithm: 688 * On first match, copy full entry (assume it'll be the only match) 689 * On subsequent matches, shorten extended_name to the first 690 * Character mismatch between extended_name and entry. 691 * If we shorten it back to the prefix length, stop searching. 692 */ 693 static int 694 recognize(Char *extended_name, Char *entry, int name_length, int numitems) 695 { 696 if (numitems == 1) /* 1st match */ 697 copyn(extended_name, entry, MAXNAMLEN); 698 else { /* 2nd & subsequent matches */ 699 Char *x, *ent; 700 int len = 0; 701 702 x = extended_name; 703 for (ent = entry; *x && *x == *ent++; x++, len++) 704 continue; 705 *x = '\0'; /* Shorten at 1st Char diff */ 706 if (len == name_length) /* Ambiguous to prefix? */ 707 return (-1); /* So stop now and save time */ 708 } 709 return (0); 710 } 711 712 /* 713 * Return true if check matches initial Chars in template. 714 * This differs from PWB imatch in that if check is null 715 * it matches anything. 716 */ 717 static int 718 is_prefix(Char *check, Char *template) 719 { 720 do 721 if (*check == 0) 722 return (TRUE); 723 while (*check++ == *template++); 724 return (FALSE); 725 } 726 727 /* 728 * Return true if the Chars in template appear at the 729 * end of check, I.e., are its suffix. 730 */ 731 static int 732 is_suffix(Char *check, Char *template) 733 { 734 Char *c, *t; 735 736 for (c = check; *c++;) 737 continue; 738 for (t = template; *t++;) 739 continue; 740 for (;;) { 741 if (t == template) 742 return 1; 743 if (c == check || *--t != *--c) 744 return 0; 745 } 746 } 747 748 int 749 tenex(Char *inputline, int inputline_size) 750 { 751 static struct { 752 int (*fn)(struct cmdline *, int); 753 int idx; 754 } keys[] = { 755 { cl_abort, VINTR }, 756 { cl_erasec, VERASE }, 757 { cl_erasew, VWERASE }, 758 { cl_kill, VKILL }, 759 { cl_list, VEOF }, 760 { cl_literal, VLNEXT }, 761 { cl_recognize, VEOL }, 762 { cl_reprint, VREPRINT }, 763 { cl_status, VSTATUS }, 764 { cl_insert, -1 } 765 }; 766 unsigned char buf[BUFSIZ]; 767 const struct termios *tio; 768 struct cmdline cl; 769 size_t i; 770 int c, ret; 771 772 tio = setup_tty(1); 773 774 memset(&cl, 0, sizeof(cl)); 775 cl.fdin = SHIN; 776 cl.fdout = SHOUT; 777 cl.buf = buf; 778 cl.size = sizeof(buf); 779 if (inputline_size < cl.size) 780 cl.size = inputline_size; 781 if (tio->c_lflag & ALTWERASE) 782 cl.flags |= CL_ALTWERASE; 783 if (needprompt) { 784 needprompt = 0; 785 cl.flags |= CL_PROMPT; 786 cl_flush(&cl); 787 } 788 789 for (;;) { 790 if ((c = cl_getc(&cl)) == 0) 791 break; 792 793 for (i = 0; keys[i].idx >= 0; i++) 794 if (CCEQ(tio->c_cc[keys[i].idx], c)) 795 break; 796 ret = keys[i].fn(&cl, c); 797 cl_flush(&cl); 798 if (ret) 799 break; 800 } 801 802 setup_tty(0); 803 804 for (i = 0; i < cl.len; i++) 805 inputline[i] = cl.buf[i]; 806 /* 807 * NUL-terminating the buffer implies that it contains a complete 808 * command ready to be executed. Therefore, don't terminate if the 809 * buffer is full since more characters must be read in order to form a 810 * complete command. 811 */ 812 if (i < cl.size) 813 inputline[i] = '\0'; 814 815 return cl.len; 816 } 817 818 static int 819 ignored(Char *entry) 820 { 821 struct varent *vp; 822 Char **cp; 823 824 if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 825 return (FALSE); 826 for (; *cp != NULL; cp++) 827 if (is_suffix(entry, *cp)) 828 return (TRUE); 829 return (FALSE); 830 } 831