sh.file.c revision 59243
1/* $Header: /src/pub/tcsh/sh.file.c,v 3.15 1997/10/02 16:36:29 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. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37#include "sh.h" 38 39RCSID("$Id: sh.file.c,v 3.15 1997/10/02 16:36:29 christos Exp $") 40 41#ifdef FILEC 42 43/* 44 * Tenex style file name recognition, .. and more. 45 * History: 46 * Author: Ken Greer, Sept. 1975, CMU. 47 * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 48 */ 49 50#define ON 1 51#define OFF 0 52#ifndef TRUE 53#define TRUE 1 54#endif 55#ifndef FALSE 56#define FALSE 0 57#endif 58 59#define ESC CTL_ESC('\033') 60 61typedef enum { 62 LIST, RECOGNIZE 63} COMMAND; 64 65static void setup_tty __P((int)); 66static void back_to_col_1 __P((void)); 67static void pushback __P((Char *)); 68static void catn __P((Char *, Char *, int)); 69static void copyn __P((Char *, Char *, int)); 70static Char filetype __P((Char *, Char *)); 71static void print_by_column __P((Char *, Char *[], int)); 72static Char *tilde __P((Char *, Char *)); 73static void retype __P((void)); 74static void beep __P((void)); 75static void print_recognized_stuff __P((Char *)); 76static void extract_dir_and_name __P((Char *, Char *, Char *)); 77static Char *getitem __P((DIR *, int)); 78static void free_items __P((Char **)); 79static int tsearch __P((Char *, COMMAND, int)); 80static int recognize __P((Char *, Char *, int, int)); 81static int is_prefix __P((Char *, Char *)); 82static int is_suffix __P((Char *, Char *)); 83static int ignored __P((Char *)); 84 85 86/* 87 * Put this here so the binary can be patched with adb to enable file 88 * completion by default. Filec controls completion, nobeep controls 89 * ringing the terminal bell on incomplete expansions. 90 */ 91bool filec = 0; 92 93static void 94setup_tty(on) 95 int on; 96{ 97#ifdef TERMIO 98# ifdef POSIX 99 struct termios tchars; 100# else 101 struct termio tchars; 102# endif /* POSIX */ 103 104# ifdef POSIX 105 (void) tcgetattr(SHIN, &tchars); 106# else 107 (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars); 108# endif /* POSIX */ 109 if (on) { 110 tchars.c_cc[VEOL] = ESC; 111 if (tchars.c_lflag & ICANON) 112# ifdef POSIX 113 on = TCSADRAIN; 114# else 115 on = TCSETA; 116# endif /* POSIX */ 117 else { 118# ifdef POSIX 119 on = TCSAFLUSH; 120# else 121 on = TCSETAF; 122# endif /* POSIX */ 123 tchars.c_lflag |= ICANON; 124 125 } 126 } 127 else { 128 tchars.c_cc[VEOL] = _POSIX_VDISABLE; 129# ifdef POSIX 130 on = TCSADRAIN; 131# else 132 on = TCSETA; 133# endif /* POSIX */ 134 } 135# ifdef POSIX 136 (void) tcsetattr(SHIN, on, &tchars); 137# else 138 (void) ioctl(SHIN, on, (ioctl_t) &tchars); 139# endif /* POSIX */ 140#else 141 struct sgttyb sgtty; 142 static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */ 143 144 if (on) { 145 (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars); 146 tchars.t_brkc = ESC; 147 (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 148 /* 149 * This must be done after every command: if the tty gets into raw or 150 * cbreak mode the user can't even type 'reset'. 151 */ 152 (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty); 153 if (sgtty.sg_flags & (RAW | CBREAK)) { 154 sgtty.sg_flags &= ~(RAW | CBREAK); 155 (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty); 156 } 157 } 158 else { 159 tchars.t_brkc = -1; 160 (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 161 } 162#endif /* TERMIO */ 163} 164 165/* 166 * Move back to beginning of current line 167 */ 168static void 169back_to_col_1() 170{ 171#ifdef TERMIO 172# ifdef POSIX 173 struct termios tty, tty_normal; 174# else 175 struct termio tty, tty_normal; 176# endif /* POSIX */ 177#else 178 struct sgttyb tty, tty_normal; 179#endif /* TERMIO */ 180 181# ifdef BSDSIGS 182 sigmask_t omask = sigblock(sigmask(SIGINT)); 183# else 184 (void) sighold(SIGINT); 185# endif /* BSDSIGS */ 186 187#ifdef TERMIO 188# ifdef POSIX 189 (void) tcgetattr(SHOUT, &tty); 190# else 191 (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal); 192# endif /* POSIX */ 193 tty_normal = tty; 194 tty.c_iflag &= ~INLCR; 195 tty.c_oflag &= ~ONLCR; 196# ifdef POSIX 197 (void) tcsetattr(SHOUT, TCSANOW, &tty); 198# else 199 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 200# endif /* POSIX */ 201 (void) write(SHOUT, "\r", 1); 202# ifdef POSIX 203 (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); 204# else 205 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 206# endif /* POSIX */ 207#else 208 (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty); 209 tty_normal = tty; 210 tty.sg_flags &= ~CRMOD; 211 (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty); 212 (void) write(SHOUT, "\r", 1); 213 (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal); 214#endif /* TERMIO */ 215 216# ifdef BSDSIGS 217 (void) sigsetmask(omask); 218# else 219 (void) sigrelse(SIGINT); 220# endif /* BSDISGS */ 221} 222 223/* 224 * Push string contents back into tty queue 225 */ 226static void 227pushback(string) 228 Char *string; 229{ 230 register Char *p; 231 char c; 232#ifdef TERMIO 233# ifdef POSIX 234 struct termios tty, tty_normal; 235# else 236 struct termio tty, tty_normal; 237# endif /* POSIX */ 238#else 239 struct sgttyb tty, tty_normal; 240#endif /* TERMIO */ 241 242#ifdef BSDSIGS 243 sigmask_t omask = sigblock(sigmask(SIGINT)); 244#else 245 (void) sighold(SIGINT); 246#endif /* BSDSIGS */ 247 248#ifdef TERMIO 249# ifdef POSIX 250 (void) tcgetattr(SHOUT, &tty); 251# else 252 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 253# endif /* POSIX */ 254 tty_normal = tty; 255 tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL); 256# ifdef POSIX 257 (void) tcsetattr(SHOUT, TCSANOW, &tty); 258# else 259 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 260# endif /* POSIX */ 261 262 for (p = string; c = *p; p++) 263 (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 264# ifdef POSIX 265 (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); 266# else 267 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 268# endif /* POSIX */ 269 (void) sigsetmask(omask); 270#else 271 (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty); 272 tty_normal = tty; 273 tty.sg_flags &= ~ECHO; 274 (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty); 275 276 for (p = string; c = *p; p++) 277 (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 278 (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal); 279#endif /* TERMIO */ 280 281# ifdef BSDSIGS 282 (void) sigsetmask(omask); 283# else 284 (void) sigrelse(SIGINT); 285# endif /* BSDISGS */ 286} 287 288/* 289 * Concatenate src onto tail of des. 290 * Des is a string whose maximum length is count. 291 * Always null terminate. 292 */ 293static void 294catn(des, src, count) 295 register Char *des, *src; 296 register count; 297{ 298 while (--count >= 0 && *des) 299 des++; 300 while (--count >= 0) 301 if ((*des++ = *src++) == 0) 302 return; 303 *des = '\0'; 304} 305 306/* 307 * Like strncpy but always leave room for trailing \0 308 * and always null terminate. 309 */ 310static void 311copyn(des, src, count) 312 register Char *des, *src; 313 register count; 314{ 315 while (--count >= 0) 316 if ((*des++ = *src++) == 0) 317 return; 318 *des = '\0'; 319} 320 321static Char 322filetype(dir, file) 323 Char *dir, *file; 324{ 325 Char path[MAXPATHLEN]; 326 struct stat statb; 327 328 catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char)); 329 if (lstat(short2str(path), &statb) == 0) { 330 switch (statb.st_mode & S_IFMT) { 331 case S_IFDIR: 332 return ('/'); 333 334 case S_IFLNK: 335 if (stat(short2str(path), &statb) == 0 && /* follow it out */ 336 S_ISDIR(statb.st_mode)) 337 return ('>'); 338 else 339 return ('@'); 340 341 case S_IFSOCK: 342 return ('='); 343 344 default: 345 if (statb.st_mode & 0111) 346 return ('*'); 347 } 348 } 349 return (' '); 350} 351 352static struct winsize win; 353 354/* 355 * Print sorted down columns 356 */ 357static void 358print_by_column(dir, items, count) 359 Char *dir, *items[]; 360 int count; 361{ 362 register int i, rows, r, c, maxwidth = 0, columns; 363 364 if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 365 win.ws_col = 80; 366 for (i = 0; i < count; i++) 367 maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 368 maxwidth += 2; /* for the file tag and space */ 369 columns = win.ws_col / maxwidth; 370 if (columns == 0) 371 columns = 1; 372 rows = (count + (columns - 1)) / columns; 373 for (r = 0; r < rows; r++) { 374 for (c = 0; c < columns; c++) { 375 i = c * rows + r; 376 if (i < count) { 377 register int w; 378 379 xprintf("%S", items[i]); 380 xputchar(dir ? filetype(dir, items[i]) : ' '); 381 if (c < columns - 1) { /* last column? */ 382 w = Strlen(items[i]) + 1; 383 for (; w < maxwidth; w++) 384 xputchar(' '); 385 } 386 } 387 } 388 xputchar('\r'); 389 xputchar('\n'); 390 } 391} 392 393/* 394 * Expand file name with possible tilde usage 395 * ~person/mumble 396 * expands to 397 * home_directory_of_person/mumble 398 */ 399static Char * 400tilde(new, old) 401 Char *new, *old; 402{ 403 register Char *o, *p; 404 register struct passwd *pw; 405 static Char person[40]; 406 407 if (old[0] != '~') 408 return (Strcpy(new, old)); 409 410 for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++); 411 *p = '\0'; 412 if (person[0] == '\0') 413 (void) Strcpy(new, varval(STRhome)); 414 else { 415 pw = getpwnam(short2str(person)); 416 if (pw == NULL) 417 return (NULL); 418 (void) Strcpy(new, str2short(pw->pw_dir)); 419 } 420 (void) Strcat(new, o); 421 return (new); 422} 423 424/* 425 * Cause pending line to be printed 426 */ 427static void 428retype() 429{ 430#ifdef TERMIO 431# ifdef POSIX 432 struct termios tty; 433 434 (void) tcgetattr(SHOUT, &tty); 435# else 436 struct termio tty; 437 438 (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 439# endif /* POSIX */ 440 441 tty.c_lflag |= PENDIN; 442 443# ifdef POSIX 444 (void) tcsetattr(SHOUT, TCSANOW, &tty); 445# else 446 (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 447# endif /* POSIX */ 448#else 449 int pending_input = LPENDIN; 450 451 (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input); 452#endif /* TERMIO */ 453} 454 455static void 456beep() 457{ 458 if (adrof(STRnobeep) == 0) 459#ifndef _OSD_POSIX 460 (void) write(SHOUT, "\007", 1); 461#else /*_OSD_POSIX*/ 462 { 463 unsigned char beep_ch = CTL_ESC('\007'); 464 (void) write(SHOUT, &beep_ch, 1); 465 } 466#endif /*_OSD_POSIX*/ 467} 468 469/* 470 * Erase that silly ^[ and 471 * print the recognized part of the string 472 */ 473static void 474print_recognized_stuff(recognized_part) 475 Char *recognized_part; 476{ 477 /* An optimized erasing of that silly ^[ */ 478 (void) putraw('\b'); 479 (void) putraw('\b'); 480 switch (Strlen(recognized_part)) { 481 482 case 0: /* erase two Characters: ^[ */ 483 (void) putraw(' '); 484 (void) putraw(' '); 485 (void) putraw('\b'); 486 (void) putraw('\b'); 487 break; 488 489 case 1: /* overstrike the ^, erase the [ */ 490 xprintf("%S", recognized_part); 491 (void) putraw(' '); 492 (void) putraw('\b'); 493 break; 494 495 default: /* overstrike both Characters ^[ */ 496 xprintf("%S", recognized_part); 497 break; 498 } 499 flush(); 500} 501 502/* 503 * Parse full path in file into 2 parts: directory and file names 504 * Should leave final slash (/) at end of dir. 505 */ 506static void 507extract_dir_and_name(path, dir, name) 508 Char *path, *dir, *name; 509{ 510 register Char *p; 511 512 p = Strrchr(path, '/'); 513 if (p == NULL) { 514 copyn(name, path, MAXNAMLEN); 515 dir[0] = '\0'; 516 } 517 else { 518 copyn(name, ++p, MAXNAMLEN); 519 copyn(dir, path, p - path); 520 } 521} 522/* atp vmsposix - I need to remove all the setpwent 523 * getpwent endpwent stuff. VMS_POSIX has getpwnam getpwuid 524 * and getlogin. This needs fixing. (There is no access to 525 * pw->passwd in VMS - a secure system benefit :-| ) 526 */ 527static Char * 528getitem(dir_fd, looking_for_lognames) 529 DIR *dir_fd; 530 int looking_for_lognames; 531{ 532 register struct passwd *pw; 533 register struct dirent *dirp; 534 535 if (looking_for_lognames) { 536#ifdef _VMS_POSIX 537 return (NULL); 538#else 539 if ((pw = getpwent()) == NULL) 540 return (NULL); 541 return (str2short(pw->pw_name)); 542#endif /* atp vmsposix */ 543 } 544 if (dirp = readdir(dir_fd)) 545 return (str2short(dirp->d_name)); 546 return (NULL); 547} 548 549static void 550free_items(items) 551 register Char **items; 552{ 553 register int i; 554 555 for (i = 0; items[i]; i++) 556 xfree((ptr_t) items[i]); 557 xfree((ptr_t) items); 558} 559 560#ifdef BSDSIGS 561# define FREE_ITEMS(items) { \ 562 sigmask_t omask;\ 563\ 564 omask = sigblock(sigmask(SIGINT));\ 565 free_items(items);\ 566 items = NULL;\ 567 (void) sigsetmask(omask);\ 568} 569#else 570# define FREE_ITEMS(items) { \ 571 (void) sighold(SIGINT);\ 572 free_items(items);\ 573 items = NULL;\ 574 (void) sigrelse(SIGINT);\ 575} 576#endif /* BSDSIGS */ 577 578/* 579 * Perform a RECOGNIZE or LIST command on string "word". 580 */ 581static int 582tsearch(word, command, max_word_length) 583 Char *word; 584 int max_word_length; 585 COMMAND command; 586{ 587 static Char **items = NULL; 588 register DIR *dir_fd; 589 register numitems = 0, ignoring = TRUE, nignored = 0; 590 register name_length, looking_for_lognames; 591 Char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; 592 Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; 593 Char *item; 594 595#define MAXITEMS 1024 596 597 if (items != NULL) 598 FREE_ITEMS(items); 599 600 looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 601 if (looking_for_lognames) { 602#ifndef _VMS_POSIX 603 (void) setpwent(); 604#endif /*atp vmsposix */ 605 copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 606 dir_fd = NULL; 607 } 608 else { 609 extract_dir_and_name(word, dir, name); 610 if (tilde(tilded_dir, dir) == 0) 611 return (0); 612 dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 613 if (dir_fd == NULL) 614 return (0); 615 } 616 617again: /* search for matches */ 618 name_length = Strlen(name); 619 for (numitems = 0; item = getitem(dir_fd, looking_for_lognames);) { 620 if (!is_prefix(name, item)) 621 continue; 622 /* Don't match . files on null prefix match */ 623 if (name_length == 0 && item[0] == '.' && 624 !looking_for_lognames) 625 continue; 626 if (command == LIST) { 627 if (numitems >= MAXITEMS) { 628 xprintf(CGETS(14, 1, "\nYikes!! Too many %s!!\n"), 629 looking_for_lognames ? 630 CGETS(14, 2, "names in password file") : 631 CGETS(14, 3, "files"); 632 break; 633 } 634 /* 635 * From Beto Appleton (beto@aixwiz.austin.ibm.com) 636 * typing "./control-d" will cause the csh to core-dump. 637 * the problem can be reproduce as following: 638 * 1. set ignoreeof 639 * 2. set filec 640 * 3. create a directory with 1050 files 641 * 4. typing "./control-d" will cause the csh to core-dump 642 * Solution: Add + 1 to MAXITEMS 643 */ 644 if (items == NULL) 645 items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS + 1); 646 items[numitems] = (Char *) xmalloc((size_t) (Strlen(item) + 1) * 647 sizeof(Char)); 648 copyn(items[numitems], item, MAXNAMLEN); 649 numitems++; 650 } 651 else { /* RECOGNIZE command */ 652 if (ignoring && ignored(item)) 653 nignored++; 654 else if (recognize(extended_name, 655 item, name_length, ++numitems)) 656 break; 657 } 658 } 659 if (ignoring && numitems == 0 && nignored > 0) { 660 ignoring = FALSE; 661 nignored = 0; 662 if (looking_for_lognames) 663#ifndef _VMS_POSIX 664 (void) setpwent(); 665#endif /* atp vmsposix */ 666 else 667 rewinddir(dir_fd); 668 goto again; 669 } 670 671 if (looking_for_lognames) 672#ifndef _VMS_POSIX 673 (void) endpwent(); 674#endif /*atp vmsposix */ 675 else 676 (void) closedir(dir_fd); 677 if (numitems == 0) 678 return (0); 679 if (command == RECOGNIZE) { 680 if (looking_for_lognames) 681 copyn(word, STRtilde, 1); 682 else 683 /* put back dir part */ 684 copyn(word, dir, max_word_length); 685 /* add extended name */ 686 catn(word, extended_name, max_word_length); 687 return (numitems); 688 } 689 else { /* LIST */ 690 qsort((ptr_t) items, (size_t) numitems, sizeof(items[0]), sortscmp); 691 print_by_column(looking_for_lognames ? NULL : tilded_dir, 692 items, numitems); 693 if (items != NULL) 694 FREE_ITEMS(items); 695 } 696 return (0); 697} 698 699/* 700 * Object: extend what user typed up to an ambiguity. 701 * Algorithm: 702 * On first match, copy full item (assume it'll be the only match) 703 * On subsequent matches, shorten extended_name to the first 704 * Character mismatch between extended_name and item. 705 * If we shorten it back to the prefix length, stop searching. 706 */ 707static int 708recognize(extended_name, item, name_length, numitems) 709 Char *extended_name, *item; 710 int name_length, numitems; 711{ 712 if (numitems == 1) /* 1st match */ 713 copyn(extended_name, item, MAXNAMLEN); 714 else { /* 2nd & subsequent matches */ 715 register Char *x, *ent; 716 register int len = 0; 717 718 x = extended_name; 719 for (ent = item; *x && *x == *ent++; x++, len++); 720 *x = '\0'; /* Shorten at 1st Char diff */ 721 if (len == name_length) /* Ambiguous to prefix? */ 722 return (-1); /* So stop now and save time */ 723 } 724 return (0); 725} 726 727/* 728 * Return true if check matches initial Chars in template. 729 * This differs from PWB imatch in that if check is null 730 * it matches anything. 731 */ 732static int 733is_prefix(check, template) 734 register Char *check, *template; 735{ 736 do 737 if (*check == 0) 738 return (TRUE); 739 while (*check++ == *template++); 740 return (FALSE); 741} 742 743/* 744 * Return true if the Chars in template appear at the 745 * end of check, I.e., are it's suffix. 746 */ 747static int 748is_suffix(check, template) 749 Char *check, *template; 750{ 751 register Char *c, *t; 752 753 for (c = check; *c++;); 754 for (t = template; *t++;); 755 for (;;) { 756 if (t == template) 757 return 1; 758 if (c == check || *--t != *--c) 759 return 0; 760 } 761} 762 763int 764tenex(inputline, inputline_size) 765 Char *inputline; 766 int inputline_size; 767{ 768 register int numitems, num_read; 769 char tinputline[BUFSIZE]; 770 771 772 setup_tty(ON); 773 774 while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) { 775 int i; 776 static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 777 '>', '(', ')', '|', '^', '%', '\0'}; 778 register Char *str_end, *word_start, last_Char, should_retype; 779 register int space_left; 780 COMMAND command; 781 782 for (i = 0; i < num_read; i++) 783 inputline[i] = (unsigned char) tinputline[i]; 784 last_Char = inputline[num_read - 1] & ASCII; 785 786 if (last_Char == '\n' || num_read == inputline_size) 787 break; 788 command = (last_Char == ESC) ? RECOGNIZE : LIST; 789 if (command == LIST) 790 xputchar('\n'); 791 str_end = &inputline[num_read]; 792 if (last_Char == ESC) 793 --str_end; /* wipeout trailing cmd Char */ 794 *str_end = '\0'; 795 /* 796 * Find LAST occurence of a delimiter in the inputline. The word start 797 * is one Character past it. 798 */ 799 for (word_start = str_end; word_start > inputline; --word_start) 800 if (Strchr(delims, word_start[-1])) 801 break; 802 space_left = inputline_size - (word_start - inputline) - 1; 803 numitems = tsearch(word_start, command, space_left); 804 805 if (command == RECOGNIZE) { 806 /* print from str_end on */ 807 print_recognized_stuff(str_end); 808 if (numitems != 1) /* Beep = No match/ambiguous */ 809 beep(); 810 } 811 812 /* 813 * Tabs in the input line cause trouble after a pushback. tty driver 814 * won't backspace over them because column positions are now 815 * incorrect. This is solved by retyping over current line. 816 */ 817 should_retype = FALSE; 818 if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 819 back_to_col_1(); 820 should_retype = TRUE; 821 } 822 if (command == LIST) /* Always retype after a LIST */ 823 should_retype = TRUE; 824 if (should_retype) 825 printprompt(0, NULL); 826 pushback(inputline); 827 if (should_retype) 828 retype(); 829 } 830 setup_tty(OFF); 831 return (num_read); 832} 833 834static int 835ignored(item) 836 register Char *item; 837{ 838 struct varent *vp; 839 register Char **cp; 840 841 if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 842 return (FALSE); 843 for (; *cp != NULL; cp++) 844 if (is_suffix(item, *cp)) 845 return (TRUE); 846 return (FALSE); 847} 848#endif /* FILEC */ 849