# 2.11BSD#431 - /usr/src/bin/csh/sh.file.c

##### Functions

    tenex             X

##### Variables

    filec             X

##### Macros

    ESC               X
    FALSE             X
    FREE_ITEMS        X
    MAXITEMS          X
    OFF               X
    ON                X
    TRUE              X

       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: #if !defined(lint) && defined(DOSCCS)
       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:     long 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:     long 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:     long 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

- in line 567

beep defined in line 259; used 1 times

- in line 556

catn defined in line 116; used 2 times

- in line 153, 437

copyn defined in line 134; used 8 times

- in line 306-310(3), 374, 404, 432-435(2), 464

extract_dir_and_name defined in line 298; used 1 times

- in line 376

filetype defined in line 146; used 1 times

- in line 204

free_items defined in line 331; used 1 times

- in line 346

getentry defined in line 314; used 1 times

- in line 386

ignored defined in line 582; used 1 times

- in line 407

is_prefix defined in line 484; used 1 times

- in line 387

is_suffix defined in line 500; used 1 times

- in line 592

print_by_column defined in line 182; used 1 times

- in line 442

print_recognized_stuff defined in line 271; used 1 times

- in line 554

pushback defined in line 91; used 1 times

- in line 574

recognize defined in line 458; used 1 times

- in line 409

retype defined in line 251; used 1 times

- in line 576

search defined in line 354; used 1 times

- in line 550

setup_tty defined in line 42; used 2 times

- in line 524, 578

tenex defined in line 518; used 1 times

- in /usr/src/bin/csh/sh.lex.c line 1225

tilde defined in line 222; used 1 times

- in line 377

#### Defined variables

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

- in line 188-193(4)

#### Defined macros

ESC defined in line 29; used 3 times

- in line 51, 535-539(2)

FALSE defined in line 25; used 5 times

- in line 415, 493, 565, 590-594(2)

FREE_ITEMS defined in line 342; used 2 times

- in line 369, 445

MAXITEMS defined in line 366; used 2 times

- in line 394, 402

OFF defined in line 27; used 1 times

- in line 578

ON defined in line 26; used 1 times

- in line 524

TRUE defined in line 24; used 5 times

- in line 361, 491, 568-571(2), 593

Last modified: 1991-08-31 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 2253 |