1 0 stevel /* 2 356 muffin * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 0 stevel * Use is subject to license terms. 4 0 stevel */ 5 0 stevel 6 0 stevel /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 0 stevel /* All Rights Reserved */ 8 0 stevel 9 0 stevel /* 10 0 stevel * Copyright (c) 1980 Regents of the University of California. 11 0 stevel * All rights reserved. The Berkeley Software License Agreement 12 0 stevel * specifies the terms and conditions for redistribution. 13 0 stevel */ 14 0 stevel 15 0 stevel #pragma ident "%Z%%M% %I% %E% SMI" 16 0 stevel 17 0 stevel #ifdef FILEC 18 0 stevel /* 19 0 stevel * Tenex style file name recognition, .. and more. 20 0 stevel * History: 21 0 stevel * Author: Ken Greer, Sept. 1975, CMU. 22 0 stevel * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 23 0 stevel */ 24 0 stevel 25 0 stevel #include "sh.h" 26 0 stevel #include <sys/types.h> 27 0 stevel #include <dirent.h> 28 0 stevel #include <pwd.h> 29 0 stevel #include "sh.tconst.h" 30 0 stevel 31 559 nakanon #define TRUE 1 32 559 nakanon #define FALSE 0 33 559 nakanon #define ON 1 34 559 nakanon #define OFF 0 35 0 stevel 36 559 nakanon #define ESC '\033' 37 0 stevel 38 356 muffin extern DIR *opendir_(tchar *); 39 0 stevel 40 0 stevel static char *BELL = "\07"; 41 0 stevel static char *CTRLR = "^R\n"; 42 0 stevel 43 0 stevel typedef enum {LIST, RECOGNIZE} COMMAND; 44 0 stevel 45 0 stevel static jmp_buf osetexit; /* saved setexit() state */ 46 0 stevel static struct termios tty_save; /* saved terminal state */ 47 0 stevel static struct termios tty_new; /* new terminal state */ 48 0 stevel 49 356 muffin static int is_prefix(tchar *, tchar *); 50 356 muffin static int is_suffix(tchar *, tchar *); 51 356 muffin static int ignored(tchar *); 52 356 muffin 53 0 stevel /* 54 0 stevel * Put this here so the binary can be patched with adb to enable file 55 0 stevel * completion by default. Filec controls completion, nobeep controls 56 0 stevel * ringing the terminal bell on incomplete expansions. 57 0 stevel */ 58 0 stevel bool filec = 0; 59 0 stevel 60 356 muffin static void 61 356 muffin setup_tty(int on) 62 0 stevel { 63 0 stevel int omask; 64 0 stevel #ifdef TRACE 65 0 stevel tprintf("TRACE- setup_tty()\n"); 66 0 stevel #endif 67 0 stevel 68 0 stevel omask = sigblock(sigmask(SIGINT)); 69 0 stevel if (on) { 70 0 stevel /* 71 0 stevel * The shell makes sure that the tty is not in some weird state 72 0 stevel * and fixes it if it is. But it should be noted that the 73 0 stevel * tenex routine will not work correctly in CBREAK or RAW mode 74 0 stevel * so this code below is, therefore, mandatory. 75 0 stevel * 76 0 stevel * Also, in order to recognize the ESC (filename-completion) 77 0 stevel * character, set EOL to ESC. This way, ESC will terminate 78 0 stevel * the line, but still be in the input stream. 79 0 stevel * EOT (filename list) will also terminate the line, 80 0 stevel * but will not appear in the input stream. 81 0 stevel * 82 0 stevel * The getexit/setexit contortions ensure that the 83 0 stevel * tty state will be restored if the user types ^C. 84 0 stevel */ 85 0 stevel (void) ioctl(SHIN, TCGETS, (char *)&tty_save); 86 0 stevel getexit(osetexit); 87 0 stevel if (setjmp(reslab)) { 88 0 stevel (void) ioctl(SHIN, TCSETSW, (char *)&tty_save); 89 0 stevel resexit(osetexit); 90 0 stevel reset(); 91 0 stevel } 92 0 stevel tty_new = tty_save; 93 0 stevel tty_new.c_cc[VEOL] = ESC; 94 0 stevel tty_new.c_iflag |= IMAXBEL | BRKINT | IGNPAR; 95 0 stevel tty_new.c_lflag |= ICANON; 96 0 stevel tty_new.c_lflag |= ECHOCTL; 97 0 stevel tty_new.c_oflag &= ~OCRNL; 98 0 stevel (void) ioctl(SHIN, TCSETSW, (char *)&tty_new); 99 0 stevel } else { 100 0 stevel /* 101 0 stevel * Reset terminal state to what user had when invoked 102 0 stevel */ 103 0 stevel (void) ioctl(SHIN, TCSETSW, (char *)&tty_save); 104 0 stevel resexit(osetexit); 105 0 stevel } 106 0 stevel (void) sigsetmask(omask); 107 0 stevel } 108 0 stevel 109 356 muffin static void 110 356 muffin termchars(void) 111 0 stevel { 112 0 stevel extern char *tgetstr(); 113 356 muffin char bp[1024]; 114 0 stevel static char area[256]; 115 0 stevel static int been_here = 0; 116 356 muffin char *ap = area; 117 356 muffin char *s; 118 356 muffin char *term; 119 0 stevel 120 0 stevel #ifdef TRACE 121 0 stevel tprintf("TRACE- termchars()\n"); 122 0 stevel #endif 123 0 stevel if (been_here) 124 0 stevel return; 125 0 stevel been_here = TRUE; 126 0 stevel 127 0 stevel if ((term = getenv("TERM")) == NULL) 128 0 stevel return; 129 0 stevel if (tgetent(bp, term) != 1) 130 0 stevel return; 131 0 stevel if (s = tgetstr("vb", &ap)) /* Visible Bell */ 132 0 stevel BELL = s; 133 0 stevel } 134 0 stevel 135 0 stevel /* 136 0 stevel * Move back to beginning of current line 137 0 stevel */ 138 356 muffin static void 139 356 muffin back_to_col_1(void) 140 0 stevel { 141 0 stevel int omask; 142 0 stevel 143 0 stevel #ifdef TRACE 144 0 stevel tprintf("TRACE- back_to_col_1()\n"); 145 0 stevel #endif 146 0 stevel omask = sigblock(sigmask(SIGINT)); 147 0 stevel (void) write(SHOUT, "\r", 1); 148 0 stevel (void) sigsetmask(omask); 149 0 stevel } 150 0 stevel 151 0 stevel /* 152 0 stevel * Push string contents back into tty queue 153 0 stevel */ 154 356 muffin static void 155 356 muffin pushback(tchar *string, int echoflag) 156 0 stevel { 157 356 muffin tchar *p; 158 0 stevel struct termios tty; 159 559 nakanon int omask, retry = 0; 160 0 stevel 161 0 stevel #ifdef TRACE 162 0 stevel tprintf("TRACE- pushback()\n"); 163 0 stevel #endif 164 0 stevel omask = sigblock(sigmask(SIGINT)); 165 0 stevel tty = tty_new; 166 0 stevel if (!echoflag) 167 0 stevel tty.c_lflag &= ~ECHO; 168 559 nakanon 169 559 nakanon again: 170 0 stevel (void) ioctl(SHIN, TCSETSF, (char *)&tty); 171 0 stevel 172 559 nakanon for (p = string; *p; p++) { 173 0 stevel char mbc[MB_LEN_MAX]; 174 0 stevel int i, j = wctomb(mbc, (wchar_t)*p); 175 559 nakanon 176 0 stevel if (j < 0) { 177 0 stevel /* Error! But else what can we do? */ 178 0 stevel continue; 179 0 stevel } 180 0 stevel for (i = 0; i < j; ++i) { 181 559 nakanon if (ioctl(SHIN, TIOCSTI, mbc + i) != 0 && 182 559 nakanon errno == EAGAIN) { 183 559 nakanon if (retry++ < 5) 184 559 nakanon goto again; 185 559 nakanon /* probably no worth retrying any more */ 186 559 nakanon } 187 0 stevel } 188 0 stevel } 189 0 stevel 190 0 stevel if (tty.c_lflag != tty_new.c_lflag) 191 0 stevel (void) ioctl(SHIN, TCSETS, (char *)&tty_new); 192 0 stevel (void) sigsetmask(omask); 193 0 stevel } 194 0 stevel 195 0 stevel /* 196 0 stevel * Concatenate src onto tail of des. 197 0 stevel * Des is a string whose maximum length is count. 198 0 stevel * Always null terminate. 199 0 stevel */ 200 356 muffin void 201 356 muffin catn(tchar *des, tchar *src, int count) 202 0 stevel { 203 0 stevel #ifdef TRACE 204 0 stevel tprintf("TRACE- catn()\n"); 205 0 stevel #endif 206 0 stevel 207 0 stevel while (--count >= 0 && *des) 208 0 stevel des++; 209 0 stevel while (--count >= 0) 210 0 stevel if ((*des++ = *src++) == '\0') 211 0 stevel return; 212 0 stevel *des = '\0'; 213 0 stevel } 214 0 stevel 215 356 muffin static int 216 0 stevel max(a, b) 217 0 stevel { 218 0 stevel 219 0 stevel return (a > b ? a : b); 220 0 stevel } 221 0 stevel 222 0 stevel /* 223 0 stevel * Like strncpy but always leave room for trailing \0 224 0 stevel * and always null terminate. 225 0 stevel */ 226 356 muffin void 227 356 muffin copyn(tchar *des, tchar *src, int count) 228 0 stevel { 229 0 stevel 230 0 stevel #ifdef TRACE 231 0 stevel tprintf("TRACE- copyn()\n"); 232 0 stevel #endif 233 0 stevel while (--count >= 0) 234 0 stevel if ((*des++ = *src++) == '\0') 235 0 stevel return; 236 0 stevel *des = '\0'; 237 0 stevel } 238 0 stevel 239 0 stevel /* 240 0 stevel * For qsort() 241 0 stevel */ 242 356 muffin static int 243 356 muffin fcompare(tchar **file1, tchar **file2) 244 0 stevel { 245 0 stevel 246 0 stevel #ifdef TRACE 247 0 stevel tprintf("TRACE- fcompare()\n"); 248 0 stevel #endif 249 0 stevel return (strcoll_(*file1, *file2)); 250 0 stevel } 251 0 stevel 252 0 stevel static char 253 356 muffin filetype(tchar *dir, tchar *file, int nosym) 254 0 stevel { 255 0 stevel tchar path[MAXPATHLEN + 1]; 256 0 stevel struct stat statb; 257 0 stevel 258 0 stevel #ifdef TRACE 259 0 stevel tprintf("TRACE- filetype()\n"); 260 0 stevel #endif 261 0 stevel if (dir) { 262 0 stevel catn(strcpy_(path, dir), file, MAXPATHLEN); 263 0 stevel if (nosym) { 264 0 stevel if (stat_(path, &statb) < 0) 265 0 stevel return (' '); 266 0 stevel } else { 267 0 stevel if (lstat_(path, &statb) < 0) 268 0 stevel return (' '); 269 0 stevel } 270 0 stevel if ((statb.st_mode & S_IFMT) == S_IFLNK) 271 0 stevel return ('@'); 272 0 stevel if ((statb.st_mode & S_IFMT) == S_IFDIR) 273 0 stevel return ('/'); 274 0 stevel if (((statb.st_mode & S_IFMT) == S_IFREG) && 275 0 stevel (statb.st_mode & 011)) 276 0 stevel return ('*'); 277 0 stevel } 278 0 stevel return (' '); 279 0 stevel } 280 0 stevel 281 0 stevel /* 282 0 stevel * Print sorted down columns 283 0 stevel */ 284 356 muffin static void 285 356 muffin print_by_column(tchar *dir, tchar *items[], int count, int looking_for_command) 286 0 stevel { 287 356 muffin int i, rows, r, c, maxwidth = 0, columns; 288 0 stevel 289 0 stevel #ifdef TRACE 290 0 stevel tprintf("TRACE- print_by_column()\n"); 291 0 stevel #endif 292 0 stevel for (i = 0; i < count; i++) 293 0 stevel maxwidth = max(maxwidth, tswidth(items[i])); 294 0 stevel 295 0 stevel /* for the file tag and space */ 296 0 stevel maxwidth += looking_for_command ? 1 : 2; 297 0 stevel columns = max(78 / maxwidth, 1); 298 0 stevel rows = (count + (columns - 1)) / columns; 299 0 stevel 300 0 stevel for (r = 0; r < rows; r++) { 301 0 stevel for (c = 0; c < columns; c++) { 302 0 stevel i = c * rows + r; 303 0 stevel if (i < count) { 304 356 muffin int w; 305 0 stevel 306 0 stevel /* 307 0 stevel * Print filename followed by 308 0 stevel * '@' or '/' or '*' or ' ' 309 0 stevel */ 310 0 stevel printf("%t", items[i]); 311 0 stevel w = tswidth(items[i]); 312 0 stevel if (!looking_for_command) { 313 0 stevel printf("%c", 314 0 stevel (tchar) filetype(dir, items[i], 0)); 315 0 stevel w++; 316 0 stevel } 317 0 stevel if (c < columns - 1) /* last column? */ 318 0 stevel for (; w < maxwidth; w++) 319 0 stevel printf(" "); 320 0 stevel } 321 0 stevel } 322 0 stevel printf("\n"); 323 0 stevel } 324 0 stevel } 325 0 stevel 326 0 stevel /* 327 0 stevel * Expand file name with possible tilde usage 328 0 stevel * ~person/mumble 329 0 stevel * expands to 330 0 stevel * home_directory_of_person/mumble 331 0 stevel */ 332 0 stevel tchar * 333 356 muffin tilde(tchar *new, tchar *old) 334 0 stevel { 335 356 muffin tchar *o, *p; 336 356 muffin struct passwd *pw; 337 0 stevel static tchar person[40]; 338 0 stevel char person_[40]; /* work */ 339 0 stevel tchar *pw_dir; /* work */ 340 0 stevel 341 0 stevel #ifdef TRACE 342 0 stevel tprintf("TRACE- tilde()\n"); 343 0 stevel #endif 344 0 stevel if (old[0] != '~') 345 0 stevel return (strcpy_(new, old)); 346 0 stevel 347 0 stevel for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) 348 0 stevel ; 349 0 stevel *p = '\0'; 350 0 stevel if (person[0] == '\0') 351 559 nakanon (void) strcpy_(new, value(S_home /* "home" */)); 352 0 stevel else { 353 559 nakanon pw = getpwnam(tstostr(person_, person)); 354 0 stevel if (pw == NULL) 355 0 stevel return (NULL); 356 0 stevel pw_dir = strtots((tchar *)NULL, pw->pw_dir); /* allocate */ 357 0 stevel (void) strcpy_(new, pw_dir); 358 0 stevel xfree(pw_dir); /* free it */ 359 0 stevel } 360 0 stevel (void) strcat_(new, o); 361 0 stevel return (new); 362 0 stevel } 363 0 stevel 364 0 stevel /* 365 0 stevel * Cause pending line to be printed 366 0 stevel */ 367 356 muffin static void 368 356 muffin sim_retype(void) 369 0 stevel { 370 0 stevel #ifdef notdef 371 0 stevel struct termios tty_pending; 372 559 nakanon 373 0 stevel #ifdef TRACE 374 0 stevel tprintf("TRACE- sim_retypr()\n"); 375 0 stevel #endif 376 0 stevel tty_pending = tty_new; 377 0 stevel tty_pending.c_lflag |= PENDIN; 378 0 stevel 379 0 stevel (void) ioctl(SHIN, TCSETS, (char *)&tty_pending); 380 0 stevel #else 381 0 stevel #ifdef TRACE 382 0 stevel tprintf("TRACE- sim_retype()\n"); 383 0 stevel #endif 384 0 stevel (void) write(SHOUT, CTRLR, strlen(CTRLR)); 385 0 stevel printprompt(); 386 0 stevel #endif 387 0 stevel } 388 0 stevel 389 356 muffin static int 390 356 muffin beep_outc(int c) 391 356 muffin { 392 0 stevel char buf[1]; 393 0 stevel 394 0 stevel buf[0] = c; 395 0 stevel 396 559 nakanon (void) write(SHOUT, buf, 1); 397 0 stevel 398 0 stevel return 0; 399 0 stevel } 400 0 stevel 401 356 muffin static void 402 356 muffin beep(void) 403 0 stevel { 404 0 stevel 405 0 stevel #ifdef TRACE 406 0 stevel tprintf("TRACE- beep()\n"); 407 0 stevel #endif 408 559 nakanon if (adrof(S_nobeep /* "nobeep" */) == 0) 409 559 nakanon (void) tputs(BELL, 0, beep_outc); 410 0 stevel } 411 0 stevel 412 0 stevel /* 413 0 stevel * Erase that silly ^[ and print the recognized part of the string. 414 0 stevel */ 415 356 muffin static void 416 356 muffin print_recognized_stuff(tchar *recognized_part) 417 0 stevel { 418 0 stevel int unit = didfds ? 1 : SHOUT; 419 0 stevel 420 0 stevel #ifdef TRACE 421 0 stevel tprintf("TRACE- print_recognized_stuff()\n"); 422 0 stevel #endif 423 0 stevel 424 0 stevel /* 425 0 stevel * An optimized erasing of that silly ^[ 426 0 stevel * 427 0 stevel * One would think that line speeds have become fast enough that this 428 0 stevel * isn't necessary, but it turns out that the visual difference is 429 0 stevel * quite noticeable. 430 0 stevel */ 431 559 nakanon flush(); 432 0 stevel switch (tswidth(recognized_part)) { 433 0 stevel case 0: 434 0 stevel /* erase two characters: ^[ */ 435 0 stevel write(unit, "\b\b \b\b", sizeof "\b\b \b\b" - 1); 436 0 stevel break; 437 0 stevel 438 0 stevel case 1: 439 0 stevel /* overstrike the ^, erase the [ */ 440 0 stevel write(unit, "\b\b", 2); 441 0 stevel printf("%t", recognized_part); 442 0 stevel write(unit, " \b\b", 4); 443 0 stevel break; 444 0 stevel 445 0 stevel default: 446 0 stevel /* overstrike both characters ^[ */ 447 0 stevel write(unit, "\b\b", 2); 448 0 stevel printf("%t", recognized_part); 449 0 stevel break; 450 0 stevel } 451 0 stevel flush(); 452 0 stevel } 453 0 stevel 454 0 stevel /* 455 0 stevel * Parse full path in file into 2 parts: directory and file names 456 0 stevel * Should leave final slash (/) at end of dir. 457 0 stevel */ 458 356 muffin static void 459 356 muffin extract_dir_and_name(tchar *path, tchar *dir, tchar *name) 460 0 stevel { 461 356 muffin tchar *p; 462 0 stevel 463 0 stevel #ifdef TRACE 464 0 stevel tprintf("TRACE- extract_dir_and_name()\n"); 465 0 stevel #endif 466 0 stevel p = rindex_(path, '/'); 467 0 stevel if (p == NOSTR) { 468 0 stevel copyn(name, path, MAXNAMLEN); 469 0 stevel dir[0] = '\0'; 470 0 stevel } else { 471 0 stevel copyn(name, ++p, MAXNAMLEN); 472 0 stevel copyn(dir, path, p - path); 473 0 stevel } 474 0 stevel } 475 0 stevel 476 0 stevel tchar * 477 356 muffin getentry(DIR *dir_fd, int looking_for_lognames) 478 0 stevel { 479 356 muffin struct passwd *pw; 480 356 muffin struct dirent *dirp; 481 0 stevel /* 482 0 stevel * For char * -> tchar * Conversion 483 0 stevel */ 484 0 stevel static tchar strbuf[MAXNAMLEN+1]; 485 0 stevel 486 0 stevel #ifdef TRACE 487 0 stevel tprintf("TRACE- getentry()\n"); 488 0 stevel #endif 489 0 stevel if (looking_for_lognames) { 490 559 nakanon if ((pw = getpwent()) == NULL) 491 0 stevel return (NULL); 492 559 nakanon return (strtots(strbuf, pw->pw_name)); 493 0 stevel } 494 0 stevel if (dirp = readdir(dir_fd)) 495 559 nakanon return (strtots(strbuf, dirp->d_name)); 496 0 stevel return (NULL); 497 0 stevel } 498 0 stevel 499 356 muffin static void 500 356 muffin free_items(tchar **items) 501 0 stevel { 502 356 muffin int i; 503 0 stevel 504 0 stevel #ifdef TRACE 505 0 stevel tprintf("TRACE- free_items()\n"); 506 0 stevel #endif 507 0 stevel for (i = 0; items[i]; i++) 508 559 nakanon xfree(items[i]); 509 559 nakanon xfree((char *)items); 510 0 stevel } 511 0 stevel 512 559 nakanon #define FREE_ITEMS(items) { \ 513 0 stevel int omask;\ 514 0 stevel \ 515 0 stevel omask = sigblock(sigmask(SIGINT));\ 516 0 stevel free_items(items);\ 517 0 stevel items = NULL;\ 518 0 stevel (void) sigsetmask(omask);\ 519 0 stevel } 520 0 stevel 521 0 stevel /* 522 0 stevel * Perform a RECOGNIZE or LIST command on string "word". 523 0 stevel */ 524 356 muffin static int 525 356 muffin search2(tchar *word, COMMAND command, int max_word_length) 526 0 stevel { 527 0 stevel static tchar **items = NULL; 528 356 muffin DIR *dir_fd; 529 356 muffin int numitems = 0, ignoring = TRUE, nignored = 0; 530 356 muffin int name_length, looking_for_lognames; 531 0 stevel tchar tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; 532 0 stevel tchar name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1]; 533 0 stevel tchar *entry; 534 559 nakanon #define MAXITEMS 1024 535 0 stevel #ifdef TRACE 536 0 stevel tprintf("TRACE- search2()\n"); 537 0 stevel #endif 538 0 stevel 539 0 stevel if (items != NULL) 540 0 stevel FREE_ITEMS(items); 541 0 stevel 542 0 stevel looking_for_lognames = (*word == '~') && (index_(word, '/') == NULL); 543 0 stevel if (looking_for_lognames) { 544 0 stevel (void) setpwent(); 545 0 stevel copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 546 0 stevel } else { 547 0 stevel extract_dir_and_name(word, dir, name); 548 0 stevel if (tilde(tilded_dir, dir) == 0) 549 0 stevel return (0); 550 559 nakanon dir_fd = opendir_(*tilded_dir ? tilded_dir : S_DOT /* "." */); 551 0 stevel if (dir_fd == NULL) 552 0 stevel return (0); 553 0 stevel } 554 0 stevel 555 0 stevel again: /* search for matches */ 556 0 stevel name_length = strlen_(name); 557 0 stevel for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) { 558 0 stevel if (!is_prefix(name, entry)) 559 0 stevel continue; 560 0 stevel /* Don't match . files on null prefix match */ 561 0 stevel if (name_length == 0 && entry[0] == '.' && 562 0 stevel !looking_for_lognames) 563 0 stevel continue; 564 0 stevel if (command == LIST) { 565 0 stevel if (numitems >= MAXITEMS) { 566 559 nakanon printf("\nYikes!! Too many %s!!\n", 567 0 stevel looking_for_lognames ? 568 0 stevel "names in password file":"files"); 569 0 stevel break; 570 0 stevel } 571 0 stevel if (items == NULL) 572 559 nakanon items = (tchar **)xcalloc(sizeof (items[1]), 573 0 stevel MAXITEMS+1); 574 559 nakanon items[numitems] = (tchar *)xalloc((unsigned)(strlen_(entry) + 1) * sizeof (tchar)); 575 0 stevel copyn(items[numitems], entry, MAXNAMLEN); 576 0 stevel numitems++; 577 0 stevel } else { /* RECOGNIZE command */ 578 0 stevel if (ignoring && ignored(entry)) 579 0 stevel nignored++; 580 0 stevel else if (recognize(extended_name, 581 0 stevel entry, name_length, ++numitems)) 582 0 stevel break; 583 0 stevel } 584 0 stevel } 585 0 stevel if (ignoring && numitems == 0 && nignored > 0) { 586 0 stevel ignoring = FALSE; 587 0 stevel nignored = 0; 588 0 stevel if (looking_for_lognames) 589 559 nakanon (void) setpwent(); 590 0 stevel else 591 0 stevel rewinddir(dir_fd); 592 0 stevel goto again; 593 0 stevel } 594 0 stevel 595 0 stevel if (looking_for_lognames) 596 0 stevel (void) endpwent(); 597 0 stevel else { 598 0 stevel unsetfd(dir_fd->dd_fd); 599 0 stevel closedir_(dir_fd); 600 0 stevel } 601 0 stevel if (command == RECOGNIZE && numitems > 0) { 602 0 stevel if (looking_for_lognames) 603 559 nakanon copyn(word, S_TIL /* "~" */, 1); 604 0 stevel else 605 0 stevel /* put back dir part */ 606 0 stevel copyn(word, dir, max_word_length); 607 0 stevel /* add extended name */ 608 0 stevel catn(word, extended_name, max_word_length); 609 0 stevel return (numitems); 610 0 stevel } 611 0 stevel if (command == LIST) { 612 559 nakanon qsort((char *)items, numitems, sizeof (items[1]), 613 559 nakanon (int (*)(const void *, const void *))fcompare); 614 0 stevel /* 615 0 stevel * Never looking for commands in this version, so final 616 0 stevel * argument forced to 0. If command name completion is 617 0 stevel * reinstated, this must change. 618 0 stevel */ 619 0 stevel print_by_column(looking_for_lognames ? NULL : tilded_dir, 620 0 stevel items, numitems, 0); 621 0 stevel if (items != NULL) 622 0 stevel FREE_ITEMS(items); 623 0 stevel } 624 0 stevel return (0); 625 0 stevel } 626 0 stevel 627 0 stevel /* 628 0 stevel * Object: extend what user typed up to an ambiguity. 629 0 stevel * Algorithm: 630 559 nakanon * On first match, copy full entry (assume it'll be the only match) 631 0 stevel * On subsequent matches, shorten extended_name to the first 632 0 stevel * character mismatch between extended_name and entry. 633 0 stevel * If we shorten it back to the prefix length, stop searching. 634 0 stevel */ 635 356 muffin int 636 356 muffin recognize(tchar *extended_name, tchar *entry, int name_length, int numitems) 637 0 stevel { 638 0 stevel 639 0 stevel #ifdef TRACE 640 0 stevel tprintf("TRACE- recognize()\n"); 641 0 stevel #endif 642 0 stevel if (numitems == 1) /* 1st match */ 643 0 stevel copyn(extended_name, entry, MAXNAMLEN); 644 0 stevel else { /* 2nd and subsequent matches */ 645 356 muffin tchar *x, *ent; 646 356 muffin int len = 0; 647 0 stevel 648 0 stevel x = extended_name; 649 0 stevel for (ent = entry; *x && *x == *ent++; x++, len++) 650 0 stevel ; 651 0 stevel *x = '\0'; /* Shorten at 1st char diff */ 652 0 stevel if (len == name_length) /* Ambiguous to prefix? */ 653 0 stevel return (-1); /* So stop now and save time */ 654 0 stevel } 655 0 stevel return (0); 656 0 stevel } 657 0 stevel 658 0 stevel /* 659 0 stevel * Return true if check items initial chars in template 660 0 stevel * This differs from PWB imatch in that if check is null 661 0 stevel * it items anything 662 0 stevel */ 663 356 muffin static int 664 356 muffin is_prefix(tchar *check, tchar *template) 665 0 stevel { 666 0 stevel #ifdef TRACE 667 0 stevel tprintf("TRACE- is_prefix()\n"); 668 0 stevel #endif 669 0 stevel 670 0 stevel do 671 0 stevel if (*check == 0) 672 0 stevel return (TRUE); 673 0 stevel while (*check++ == *template++); 674 0 stevel return (FALSE); 675 0 stevel } 676 0 stevel 677 0 stevel /* 678 0 stevel * Return true if the chars in template appear at the 679 0 stevel * end of check, i.e., are its suffix. 680 0 stevel */ 681 356 muffin static int 682 356 muffin is_suffix(tchar *check, tchar *template) 683 0 stevel { 684 356 muffin tchar *c, *t; 685 0 stevel 686 0 stevel #ifdef TRACE 687 0 stevel tprintf("TRACE- is_suffix()\n"); 688 0 stevel #endif 689 559 nakanon for (c = check; *c++; ) 690 0 stevel ; 691 559 nakanon for (t = template; *t++; ) 692 0 stevel ; 693 0 stevel for (;;) { 694 0 stevel if (t == template) 695 0 stevel return (TRUE); 696 0 stevel if (c == check || *--t != *--c) 697 0 stevel return (FALSE); 698 0 stevel } 699 0 stevel } 700 0 stevel 701 356 muffin int 702 356 muffin tenex(tchar *inputline, int inputline_size) 703 0 stevel { 704 356 muffin int numitems, num_read, should_retype; 705 0 stevel int i; 706 0 stevel 707 0 stevel #ifdef TRACE 708 0 stevel tprintf("TRACE- tenex()\n"); 709 0 stevel #endif 710 0 stevel setup_tty(ON); 711 0 stevel termchars(); 712 0 stevel num_read = 0; 713 0 stevel should_retype = FALSE; 714 0 stevel while ((i = read_(SHIN, inputline+num_read, inputline_size-num_read)) 715 0 stevel > 0) { 716 559 nakanon static tchar *delims = S_DELIM /* " '\"\t;&<>()|`" */; 717 356 muffin tchar *str_end, *word_start, last_char; 718 356 muffin int space_left; 719 0 stevel struct termios tty; 720 0 stevel COMMAND command; 721 0 stevel 722 0 stevel num_read += i; 723 0 stevel inputline[num_read] = '\0'; 724 0 stevel last_char = inputline[num_read - 1] & TRIM; 725 0 stevel 726 559 nakanon /* 727 559 nakanon * read_() can return more than requested size if there 728 559 nakanon * is multibyte character at the end. 729 559 nakanon */ 730 559 nakanon if ((num_read >= inputline_size) || (last_char == '\n')) 731 0 stevel break; 732 0 stevel 733 0 stevel str_end = &inputline[num_read]; 734 0 stevel if (last_char == ESC) { 735 0 stevel command = RECOGNIZE; 736 0 stevel *--str_end = '\0'; /* wipe out trailing ESC */ 737 0 stevel } else 738 0 stevel command = LIST; 739 0 stevel 740 0 stevel tty = tty_new; 741 0 stevel tty.c_lflag &= ~ECHO; 742 0 stevel (void) ioctl(SHIN, TCSETSF, (char *)&tty); 743 0 stevel 744 0 stevel if (command == LIST) 745 0 stevel printf("\n"); 746 0 stevel /* 747 0 stevel * Find LAST occurence of a delimiter in the inputline. 748 0 stevel * The word start is one character past it. 749 0 stevel */ 750 0 stevel for (word_start = str_end; word_start > inputline; 751 0 stevel --word_start) { 752 0 stevel if (index_(delims, word_start[-1]) || 753 0 stevel isauxsp(word_start[-1])) 754 0 stevel break; 755 0 stevel } 756 0 stevel space_left = inputline_size - (word_start - inputline) - 1; 757 0 stevel numitems = search2(word_start, command, space_left); 758 0 stevel 759 0 stevel /* 760 0 stevel * Tabs in the input line cause trouble after a pushback. 761 0 stevel * tty driver won't backspace over them because column 762 0 stevel * positions are now incorrect. This is solved by retyping 763 0 stevel * over current line. 764 0 stevel */ 765 0 stevel if (index_(inputline, '\t')) { /* tab tchar in input line? */ 766 0 stevel back_to_col_1(); 767 0 stevel should_retype = TRUE; 768 0 stevel } 769 0 stevel if (command == LIST) /* Always retype after a LIST */ 770 0 stevel should_retype = TRUE; 771 0 stevel if (should_retype) 772 0 stevel printprompt(); 773 0 stevel pushback(inputline, should_retype); 774 0 stevel num_read = 0; /* chars will be reread */ 775 0 stevel should_retype = FALSE; 776 0 stevel 777 0 stevel /* 778 0 stevel * Avoid a race condition by echoing what we're recognized 779 0 stevel * _after_ pushing back the command line. This way, if the 780 0 stevel * user waits until seeing this output before typing more 781 0 stevel * stuff, the resulting keystrokes won't race with the STIed 782 0 stevel * input we've pushed back. (Of course, if the user types 783 0 stevel * ahead, the race still exists and it's quite possible that 784 0 stevel * the pushed back input line will interleave with the 785 0 stevel * keystrokes in unexpected ways.) 786 0 stevel */ 787 0 stevel if (command == RECOGNIZE) { 788 0 stevel /* print from str_end on */ 789 0 stevel print_recognized_stuff(str_end); 790 0 stevel if (numitems != 1) /* Beep = No match/ambiguous */ 791 0 stevel beep(); 792 0 stevel } 793 0 stevel } 794 0 stevel setup_tty(OFF); 795 0 stevel return (num_read); 796 0 stevel } 797 0 stevel 798 356 muffin static int 799 356 muffin ignored(tchar *entry) 800 0 stevel { 801 0 stevel struct varent *vp; 802 356 muffin tchar **cp; 803 0 stevel 804 0 stevel #ifdef TRACE 805 0 stevel tprintf("TRACE- ignored()\n"); 806 0 stevel #endif 807 559 nakanon if ((vp = adrof(S_fignore /* "fignore" */)) == NULL || 808 0 stevel (cp = vp->vec) == NULL) 809 0 stevel return (FALSE); 810 0 stevel for (; *cp != NULL; cp++) 811 0 stevel if (is_suffix(entry, *cp)) 812 0 stevel return (TRUE); 813 0 stevel return (FALSE); 814 0 stevel } 815 356 muffin #endif /* FILEC */ 816