1: /*
   2:  * Copyright (c) 1980 Regents of the University of California.
   3:  * All rights reserved.  The Berkeley Software License Agreement
   4:  * specifies the terms and conditions for redistribution.
   5:  */
   6: 
   7: #ifndef lint
   8: static char *sccsid = "@(#)sh.file.c	5.6 (Berkeley) 5/18/86";
   9: #endif
  10: 
  11: #ifdef FILEC
  12: /*
  13:  * Tenex style file name recognition, .. and more.
  14:  * History:
  15:  *	Author: Ken Greer, Sept. 1975, CMU.
  16:  *	Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
  17:  */
  18: 
  19: #include "sh.h"
  20: #include <sgtty.h>
  21: #include <sys/dir.h>
  22: #include <pwd.h>
  23: 
  24: #define TRUE    1
  25: #define FALSE   0
  26: #define ON  1
  27: #define OFF 0
  28: 
  29: #define ESC '\033'
  30: 
  31: typedef enum {LIST, RECOGNIZE} COMMAND;
  32: 
  33: int sortscmp();         /* defined in sh.glob.c */
  34: 
  35: /*
  36:  * Put this here so the binary can be patched with adb to enable file
  37:  * completion by default.  Filec controls completion, nobeep controls
  38:  * ringing the terminal bell on incomplete expansions.
  39:  */
  40: bool filec = 0;
  41: 
  42: static
  43: setup_tty(on)
  44:     int on;
  45: {
  46:     struct sgttyb sgtty;
  47:     static struct tchars tchars;    /* INT, QUIT, XON, XOFF, EOF, BRK */
  48: 
  49:     if (on) {
  50:         (void) ioctl(SHIN, TIOCGETC, (char *)&tchars);
  51:         tchars.t_brkc = ESC;
  52:         (void) ioctl(SHIN, TIOCSETC, (char *)&tchars);
  53:         /*
  54: 		 * This must be done after every command: if
  55: 		 * the tty gets into raw or cbreak mode the user
  56: 		 * can't even type 'reset'.
  57: 		 */
  58:         (void) ioctl(SHIN, TIOCGETP, (char *)&sgtty);
  59:         if (sgtty.sg_flags & (RAW|CBREAK)) {
  60:              sgtty.sg_flags &= ~(RAW|CBREAK);
  61:              (void) ioctl(SHIN, TIOCSETP, (char *)&sgtty);
  62:         }
  63:     } else {
  64:         tchars.t_brkc = -1;
  65:         (void) ioctl(SHIN, TIOCSETC, (char *)&tchars);
  66:     }
  67: }
  68: 
  69: /*
  70:  * Move back to beginning of current line
  71:  */
  72: static
  73: back_to_col_1()
  74: {
  75:     struct sgttyb tty, tty_normal;
  76:     int omask;
  77: 
  78:     omask = sigblock(sigmask(SIGINT));
  79:     (void) ioctl(SHIN, TIOCGETP, (char *)&tty);
  80:     tty_normal = tty;
  81:     tty.sg_flags &= ~CRMOD;
  82:     (void) ioctl(SHIN, TIOCSETN, (char *)&tty);
  83:     (void) write(SHOUT, "\r", 1);
  84:     (void) ioctl(SHIN, TIOCSETN, (char *)&tty_normal);
  85:     (void) sigsetmask(omask);
  86: }
  87: 
  88: /*
  89:  * Push string contents back into tty queue
  90:  */
  91: static
  92: pushback(string)
  93:     char *string;
  94: {
  95:     register char *p;
  96:     struct sgttyb tty, tty_normal;
  97:     int omask;
  98: 
  99:     omask = sigblock(sigmask(SIGINT));
 100:     (void) ioctl(SHOUT, TIOCGETP, (char *)&tty);
 101:     tty_normal = tty;
 102:     tty.sg_flags &= ~ECHO;
 103:     (void) ioctl(SHOUT, TIOCSETN, (char *)&tty);
 104: 
 105:     for (p = string; *p; p++)
 106:         (void) ioctl(SHOUT, TIOCSTI, p);
 107:     (void) ioctl(SHOUT, TIOCSETN, (char *)&tty_normal);
 108:     (void) sigsetmask(omask);
 109: }
 110: 
 111: /*
 112:  * Concatenate src onto tail of des.
 113:  * Des is a string whose maximum length is count.
 114:  * Always null terminate.
 115:  */
 116: static
 117: catn(des, src, count)
 118:     register char *des, *src;
 119:     register count;
 120: {
 121: 
 122:     while (--count >= 0 && *des)
 123:         des++;
 124:     while (--count >= 0)
 125:         if ((*des++ = *src++) == 0)
 126:              return;
 127:     *des = '\0';
 128: }
 129: 
 130: /*
 131:  * Like strncpy but always leave room for trailing \0
 132:  * and always null terminate.
 133:  */
 134: static
 135: copyn(des, src, count)
 136:     register char *des, *src;
 137:     register count;
 138: {
 139: 
 140:     while (--count >= 0)
 141:         if ((*des++ = *src++) == 0)
 142:             return;
 143:     *des = '\0';
 144: }
 145: 
 146: static char
 147: filetype(dir, file)
 148:     char *dir, *file;
 149: {
 150:     char path[MAXPATHLEN];
 151:     struct stat statb;
 152: 
 153:     catn(strcpy(path, dir), file, sizeof path);
 154:     if (lstat(path, &statb) == 0) {
 155:         switch(statb.st_mode & S_IFMT) {
 156:             case S_IFDIR:
 157:             return ('/');
 158: 
 159:             case S_IFLNK:
 160:             if (stat(path, &statb) == 0 &&      /* follow it out */
 161:                (statb.st_mode & S_IFMT) == S_IFDIR)
 162:                 return ('>');
 163:             else
 164:                 return ('@');
 165: 
 166:             case S_IFSOCK:
 167:             return ('=');
 168: 
 169:             default:
 170:             if (statb.st_mode & 0111)
 171:                 return ('*');
 172:         }
 173:     }
 174:     return (' ');
 175: }
 176: 
 177: static struct winsize win;
 178: 
 179: /*
 180:  * Print sorted down columns
 181:  */
 182: static
 183: print_by_column(dir, items, count)
 184:     char *dir, *items[];
 185: {
 186:     register int i, rows, r, c, maxwidth = 0, columns;
 187: 
 188:     if (ioctl(SHOUT, TIOCGWINSZ, (char *)&win) < 0 || win.ws_col == 0)
 189:         win.ws_col = 80;
 190:     for (i = 0; i < count; i++)
 191:         maxwidth = maxwidth > (r = strlen(items[i])) ? maxwidth : r;
 192:     maxwidth += 2;          /* for the file tag and space */
 193:     columns = win.ws_col / maxwidth;
 194:     if (columns == 0)
 195:         columns = 1;
 196:     rows = (count + (columns - 1)) / columns;
 197:     for (r = 0; r < rows; r++) {
 198:         for (c = 0; c < columns; c++) {
 199:             i = c * rows + r;
 200:             if (i < count) {
 201:                 register int w;
 202: 
 203:                 printf("%s", items[i]);
 204:                 putchar(dir ? filetype(dir, items[i]) : ' ');
 205:                 if (c < columns - 1) {  /* last column? */
 206:                     w = strlen(items[i]) + 1;
 207:                     for (; w < maxwidth; w++)
 208:                         putchar(' ');
 209:                 }
 210:             }
 211:         }
 212:         putchar('\n');
 213:     }
 214: }
 215: 
 216: /*
 217:  * Expand file name with possible tilde usage
 218:  *	~person/mumble
 219:  * expands to
 220:  *	home_directory_of_person/mumble
 221:  */
 222: static char *
 223: tilde(new, old)
 224:     char *new, *old;
 225: {
 226:     register char *o, *p;
 227:     register struct passwd *pw;
 228:     static char person[40];
 229: 
 230:     if (old[0] != '~')
 231:         return (strcpy(new, old));
 232: 
 233:     for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
 234:         ;
 235:     *p = '\0';
 236:     if (person[0] == '\0')
 237:         (void) strcpy(new, value("home"));
 238:     else {
 239:         pw = getpwnam(person);
 240:         if (pw == NULL)
 241:             return (NULL);
 242:         (void) strcpy(new, pw->pw_dir);
 243:     }
 244:     (void) strcat(new, o);
 245:     return (new);
 246: }
 247: 
 248: /*
 249:  * Cause pending line to be printed
 250:  */
 251: static
 252: retype()
 253: {
 254:     int pending_input = LPENDIN;
 255: 
 256:     (void) ioctl(SHOUT, TIOCLBIS, (char *)&pending_input);
 257: }
 258: 
 259: static
 260: beep()
 261: {
 262: 
 263:     if (adrof("nobeep") == 0)
 264:         (void) write(SHOUT, "\007", 1);
 265: }
 266: 
 267: /*
 268:  * Erase that silly ^[ and
 269:  * print the recognized part of the string
 270:  */
 271: static
 272: print_recognized_stuff(recognized_part)
 273:     char *recognized_part;
 274: {
 275: 
 276:     /* An optimized erasing of that silly ^[ */
 277:     switch (strlen(recognized_part)) {
 278: 
 279:     case 0:             /* erase two characters: ^[ */
 280:         printf("\210\210  \210\210");
 281:         break;
 282: 
 283:     case 1:             /* overstrike the ^, erase the [ */
 284:         printf("\210\210%s \210", recognized_part);
 285:         break;
 286: 
 287:     default:            /* overstrike both characters ^[ */
 288:         printf("\210\210%s", recognized_part);
 289:         break;
 290:     }
 291:     flush();
 292: }
 293: 
 294: /*
 295:  * Parse full path in file into 2 parts: directory and file names
 296:  * Should leave final slash (/) at end of dir.
 297:  */
 298: static
 299: extract_dir_and_name(path, dir, name)
 300:     char *path, *dir, *name;
 301: {
 302:     register char  *p;
 303: 
 304:     p = rindex(path, '/');
 305:     if (p == NULL) {
 306:         copyn(name, path, MAXNAMLEN);
 307:         dir[0] = '\0';
 308:     } else {
 309:         copyn(name, ++p, MAXNAMLEN);
 310:         copyn(dir, path, p - path);
 311:     }
 312: }
 313: 
 314: static char *
 315: getentry(dir_fd, looking_for_lognames)
 316:     DIR *dir_fd;
 317: {
 318:     register struct passwd *pw;
 319:     register struct direct *dirp;
 320: 
 321:     if (looking_for_lognames) {
 322:         if ((pw = getpwent()) == NULL)
 323:             return (NULL);
 324:         return (pw->pw_name);
 325:     }
 326:     if (dirp = readdir(dir_fd))
 327:         return (dirp->d_name);
 328:     return (NULL);
 329: }
 330: 
 331: static
 332: free_items(items)
 333:     register char **items;
 334: {
 335:     register int i;
 336: 
 337:     for (i = 0; items[i]; i++)
 338:         free(items[i]);
 339:     free((char *)items);
 340: }
 341: 
 342: #define FREE_ITEMS(items) { \
 343:     int omask;\
 344: \
 345:     omask = sigblock(sigmask(SIGINT));\
 346:     free_items(items);\
 347:     items = NULL;\
 348:     (void) sigsetmask(omask);\
 349: }
 350: 
 351: /*
 352:  * Perform a RECOGNIZE or LIST command on string "word".
 353:  */
 354: static
 355: search(word, command, max_word_length)
 356:     char *word;
 357:     COMMAND command;
 358: {
 359:     static char **items = NULL;
 360:     register DIR *dir_fd;
 361:     register numitems = 0, ignoring = TRUE, nignored = 0;
 362:     register name_length, looking_for_lognames;
 363:     char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
 364:     char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1];
 365:     char *entry;
 366: #define MAXITEMS 1024
 367: 
 368:     if (items != NULL)
 369:         FREE_ITEMS(items);
 370: 
 371:     looking_for_lognames = (*word == '~') && (index(word, '/') == NULL);
 372:     if (looking_for_lognames) {
 373:         (void) setpwent();
 374:         copyn(name, &word[1], MAXNAMLEN);   /* name sans ~ */
 375:     } else {
 376:         extract_dir_and_name(word, dir, name);
 377:         if (tilde(tilded_dir, dir) == 0)
 378:             return (0);
 379:         dir_fd = opendir(*tilded_dir ? tilded_dir : ".");
 380:         if (dir_fd == NULL)
 381:             return (0);
 382:     }
 383: 
 384: again:  /* search for matches */
 385:     name_length = strlen(name);
 386:     for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) {
 387:         if (!is_prefix(name, entry))
 388:             continue;
 389:         /* Don't match . files on null prefix match */
 390:         if (name_length == 0 && entry[0] == '.' &&
 391:             !looking_for_lognames)
 392:             continue;
 393:         if (command == LIST) {
 394:             if (numitems >= MAXITEMS) {
 395:                 printf ("\nYikes!! Too many %s!!\n",
 396:                     looking_for_lognames ?
 397:                     "names in password file":"files");
 398:                 break;
 399:             }
 400:             if (items == NULL)
 401:                 items = (char **) calloc(sizeof (items[1]),
 402:                     MAXITEMS);
 403:             items[numitems] = xalloc((unsigned)strlen(entry) + 1);
 404:             copyn(items[numitems], entry, MAXNAMLEN);
 405:             numitems++;
 406:         } else {            /* RECOGNIZE command */
 407:             if (ignoring && ignored(entry))
 408:                 nignored++;
 409:             else if (recognize(extended_name,
 410:                 entry, name_length, ++numitems))
 411:                 break;
 412:         }
 413:     }
 414:     if (ignoring && numitems == 0 && nignored > 0) {
 415:         ignoring = FALSE;
 416:         nignored = 0;
 417:         if (looking_for_lognames)
 418:             (void) setpwent();
 419:         else
 420:             rewinddir(dir_fd);
 421:         goto again;
 422:     }
 423: 
 424:     if (looking_for_lognames)
 425:         (void) endpwent();
 426:     else
 427:         closedir(dir_fd);
 428:     if (numitems == 0)
 429:         return (0);
 430:     if (command == RECOGNIZE) {
 431:         if (looking_for_lognames)
 432:              copyn(word, "~", 1);
 433:         else
 434:             /* put back dir part */
 435:             copyn(word, dir, max_word_length);
 436:         /* add extended name */
 437:         catn(word, extended_name, max_word_length);
 438:         return (numitems);
 439:     }
 440:     else {              /* LIST */
 441:         qsort((char *)items, numitems, sizeof(items[1]), sortscmp);
 442:         print_by_column(looking_for_lognames ? NULL : tilded_dir,
 443:             items, numitems);
 444:         if (items != NULL)
 445:             FREE_ITEMS(items);
 446:     }
 447:     return (0);
 448: }
 449: 
 450: /*
 451:  * Object: extend what user typed up to an ambiguity.
 452:  * Algorithm:
 453:  * On first match, copy full entry (assume it'll be the only match)
 454:  * On subsequent matches, shorten extended_name to the first
 455:  * character mismatch between extended_name and entry.
 456:  * If we shorten it back to the prefix length, stop searching.
 457:  */
 458: static
 459: recognize(extended_name, entry, name_length, numitems)
 460:     char *extended_name, *entry;
 461: {
 462: 
 463:     if (numitems == 1)          /* 1st match */
 464:         copyn(extended_name, entry, MAXNAMLEN);
 465:     else {                  /* 2nd & subsequent matches */
 466:         register char *x, *ent;
 467:         register int len = 0;
 468: 
 469:         x = extended_name;
 470:         for (ent = entry; *x && *x == *ent++; x++, len++)
 471:             ;
 472:         *x = '\0';          /* Shorten at 1st char diff */
 473:         if (len == name_length)     /* Ambiguous to prefix? */
 474:             return (-1);        /* So stop now and save time */
 475:     }
 476:     return (0);
 477: }
 478: 
 479: /*
 480:  * Return true if check matches initial chars in template.
 481:  * This differs from PWB imatch in that if check is null
 482:  * it matches anything.
 483:  */
 484: static
 485: is_prefix(check, template)
 486:     register char *check, *template;
 487: {
 488: 
 489:     do
 490:         if (*check == 0)
 491:             return (TRUE);
 492:     while (*check++ == *template++);
 493:     return (FALSE);
 494: }
 495: 
 496: /*
 497:  *  Return true if the chars in template appear at the
 498:  *  end of check, I.e., are it's suffix.
 499:  */
 500: static
 501: is_suffix(check, template)
 502:     char *check, *template;
 503: {
 504:     register char *c, *t;
 505: 
 506:     for (c = check; *c++;)
 507:         ;
 508:     for (t = template; *t++;)
 509:         ;
 510:     for (;;) {
 511:         if (t == template)
 512:             return 1;
 513:         if (c == check || *--t != *--c)
 514:             return 0;
 515:     }
 516: }
 517: 
 518: tenex(inputline, inputline_size)
 519:     char *inputline;
 520:     int inputline_size;
 521: {
 522:     register int numitems, num_read;
 523: 
 524:     setup_tty(ON);
 525:     while ((num_read = read(SHIN, inputline, inputline_size)) > 0) {
 526:         static char *delims = " '\"\t;&<>()|^%";
 527:         register char *str_end, *word_start, last_char, should_retype;
 528:         register int space_left;
 529:         COMMAND command;
 530: 
 531:         last_char = inputline[num_read - 1] & 0177;
 532: 
 533:         if (last_char == '\n' || num_read == inputline_size)
 534:             break;
 535:         command = (last_char == ESC) ? RECOGNIZE : LIST;
 536:         if (command == LIST)
 537:             putchar('\n');
 538:         str_end = &inputline[num_read];
 539:         if (last_char == ESC)
 540:             --str_end;      /* wipeout trailing cmd char */
 541:         *str_end = '\0';
 542:         /*
 543: 		 * Find LAST occurence of a delimiter in the inputline.
 544: 		 * The word start is one character past it.
 545: 		 */
 546:         for (word_start = str_end; word_start > inputline; --word_start)
 547:             if (index(delims, word_start[-1]))
 548:                 break;
 549:         space_left = inputline_size - (word_start - inputline) - 1;
 550:         numitems = search(word_start, command, space_left);
 551: 
 552:         if (command == RECOGNIZE) {
 553:             /* print from str_end on */
 554:             print_recognized_stuff(str_end);
 555:             if (numitems != 1)  /* Beep = No match/ambiguous */
 556:                 beep();
 557:         }
 558: 
 559:         /*
 560: 		 * Tabs in the input line cause trouble after a pushback.
 561: 		 * tty driver won't backspace over them because column
 562: 		 * positions are now incorrect. This is solved by retyping
 563: 		 * over current line.
 564: 		 */
 565:         should_retype = FALSE;
 566:         if (index(inputline, '\t')) {   /* tab char in input line? */
 567:             back_to_col_1();
 568:             should_retype = TRUE;
 569:         }
 570:         if (command == LIST)        /* Always retype after a LIST */
 571:             should_retype = TRUE;
 572:         if (should_retype)
 573:             printprompt();
 574:         pushback(inputline);
 575:         if (should_retype)
 576:             retype();
 577:     }
 578:     setup_tty(OFF);
 579:     return (num_read);
 580: }
 581: 
 582: static
 583: ignored(entry)
 584:     register char *entry;
 585: {
 586:     struct varent *vp;
 587:     register char **cp;
 588: 
 589:     if ((vp = adrof("fignore")) == NULL || (cp = vp->vec) == NULL)
 590:         return (FALSE);
 591:     for (; *cp != NULL; cp++)
 592:         if (is_suffix(entry, *cp))
 593:             return (TRUE);
 594:     return (FALSE);
 595: }
 596: #endif FILEC

Defined functions

back_to_col_1 defined in line 72; used 1 times
beep defined in line 259; used 1 times
catn defined in line 116; used 2 times
copyn defined in line 134; used 8 times
extract_dir_and_name defined in line 298; used 1 times
filetype defined in line 146; used 1 times
free_items defined in line 331; used 1 times
getentry defined in line 314; used 1 times
ignored defined in line 582; used 1 times
is_prefix defined in line 484; used 1 times
is_suffix defined in line 500; used 1 times
print_by_column defined in line 182; used 1 times
print_recognized_stuff defined in line 271; used 1 times
pushback defined in line 91; used 1 times
recognize defined in line 458; used 1 times
retype defined in line 251; used 1 times
search defined in line 354; used 1 times
setup_tty defined in line 42; used 2 times
tenex defined in line 518; used 1 times
tilde defined in line 222; used 1 times

Defined variables

filec defined in line 40; never used
sccsid defined in line 8; never used
win defined in line 177; used 4 times

Defined macros

ESC defined in line 29; used 3 times
FALSE defined in line 25; used 5 times
FREE_ITEMS defined in line 342; used 2 times
MAXITEMS defined in line 366; used 2 times
OFF defined in line 27; used 1 times
ON defined in line 26; used 1 times
TRUE defined in line 24; used 5 times
Last modified: 1986-05-19
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1600
Valid CSS Valid XHTML 1.0 Strict