Annotation of 43BSD/bin/csh/sh.file.c, revision 1.1.1.1

1.1       root        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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.