[BACK]Return to filename.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/filename.c, Revision 1.1.1.4

1.1       etheisen    1: /*
1.1.1.4 ! shadchin    2:  * Copyright (C) 1984-2012  Mark Nudelman
1.1       etheisen    3:  *
1.1.1.2   millert     4:  * You may distribute under the terms of either the GNU General Public
                      5:  * License or the Less License, as specified in the README file.
1.1       etheisen    6:  *
1.1.1.4 ! shadchin    7:  * For more information, see the README file.
1.1       etheisen    8:  */
                      9:
                     10:
                     11: /*
                     12:  * Routines to mess around with filenames (and files).
                     13:  * Much of this is very OS dependent.
                     14:  */
                     15:
                     16: #include "less.h"
1.1.1.2   millert    17: #include "lglob.h"
                     18: #if MSDOS_COMPILER
1.1       etheisen   19: #include <dos.h>
1.1.1.2   millert    20: #if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER)
                     21: #include <dir.h>
                     22: #endif
                     23: #if MSDOS_COMPILER==DJGPPC
                     24: #include <glob.h>
                     25: #include <dir.h>
                     26: #define _MAX_PATH      PATH_MAX
                     27: #endif
                     28: #endif
                     29: #ifdef _OSK
                     30: #include <rbf.h>
                     31: #ifndef _OSK_MWC32
                     32: #include <modes.h>
                     33: #endif
                     34: #endif
                     35: #if OS2
                     36: #include <signal.h>
1.1       etheisen   37: #endif
                     38:
1.1.1.2   millert    39: #if HAVE_STAT
                     40: #include <sys/stat.h>
                     41: #ifndef S_ISDIR
                     42: #define        S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
                     43: #endif
                     44: #ifndef S_ISREG
                     45: #define        S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
                     46: #endif
                     47: #endif
                     48:
                     49:
1.1       etheisen   50: extern int force_open;
1.1.1.2   millert    51: extern int secure;
                     52: extern int use_lessopen;
1.1.1.3   shadchin   53: extern int ctldisp;
                     54: extern int utf_mode;
1.1       etheisen   55: extern IFILE curr_ifile;
                     56: extern IFILE old_ifile;
1.1.1.2   millert    57: #if SPACES_IN_FILENAMES
                     58: extern char openquote;
                     59: extern char closequote;
                     60: #endif
                     61:
                     62: /*
                     63:  * Remove quotes around a filename.
                     64:  */
                     65:        public char *
                     66: shell_unquote(str)
                     67:        char *str;
                     68: {
                     69:        char *name;
                     70:        char *p;
                     71:
                     72:        name = p = (char *) ecalloc(strlen(str)+1, sizeof(char));
                     73:        if (*str == openquote)
                     74:        {
                     75:                str++;
                     76:                while (*str != '\0')
                     77:                {
                     78:                        if (*str == closequote)
                     79:                        {
                     80:                                if (str[1] != closequote)
                     81:                                        break;
                     82:                                str++;
                     83:                        }
                     84:                        *p++ = *str++;
                     85:                }
                     86:        } else
                     87:        {
                     88:                char *esc = get_meta_escape();
                     89:                int esclen = strlen(esc);
                     90:                while (*str != '\0')
                     91:                {
                     92:                        if (esclen > 0 && strncmp(str, esc, esclen) == 0)
                     93:                                str += esclen;
                     94:                        *p++ = *str++;
                     95:                }
                     96:        }
                     97:        *p = '\0';
                     98:        return (name);
                     99: }
                    100:
                    101: /*
                    102:  * Get the shell's escape character.
                    103:  */
                    104:        public char *
                    105: get_meta_escape()
                    106: {
                    107:        char *s;
                    108:
                    109:        s = lgetenv("LESSMETAESCAPE");
                    110:        if (s == NULL)
                    111:                s = DEF_METAESCAPE;
                    112:        return (s);
                    113: }
                    114:
                    115: /*
                    116:  * Get the characters which the shell considers to be "metacharacters".
                    117:  */
                    118:        static char *
                    119: metachars()
                    120: {
                    121:        static char *mchars = NULL;
                    122:
                    123:        if (mchars == NULL)
                    124:        {
                    125:                mchars = lgetenv("LESSMETACHARS");
                    126:                if (mchars == NULL)
                    127:                        mchars = DEF_METACHARS;
                    128:        }
                    129:        return (mchars);
                    130: }
                    131:
                    132: /*
                    133:  * Is this a shell metacharacter?
                    134:  */
                    135:        static int
                    136: metachar(c)
                    137:        char c;
                    138: {
                    139:        return (strchr(metachars(), c) != NULL);
                    140: }
                    141:
                    142: /*
                    143:  * Insert a backslash before each metacharacter in a string.
                    144:  */
                    145:        public char *
                    146: shell_quote(s)
                    147:        char *s;
                    148: {
                    149:        char *p;
                    150:        char *newstr;
                    151:        int len;
                    152:        char *esc = get_meta_escape();
                    153:        int esclen = strlen(esc);
                    154:        int use_quotes = 0;
                    155:        int have_quotes = 0;
                    156:
                    157:        /*
                    158:         * Determine how big a string we need to allocate.
                    159:         */
                    160:        len = 1; /* Trailing null byte */
                    161:        for (p = s;  *p != '\0';  p++)
                    162:        {
                    163:                len++;
                    164:                if (*p == openquote || *p == closequote)
                    165:                        have_quotes = 1;
                    166:                if (metachar(*p))
                    167:                {
                    168:                        if (esclen == 0)
                    169:                        {
                    170:                                /*
                    171:                                 * We've got a metachar, but this shell
                    172:                                 * doesn't support escape chars.  Use quotes.
                    173:                                 */
                    174:                                use_quotes = 1;
                    175:                        } else
                    176:                        {
                    177:                                /*
                    178:                                 * Allow space for the escape char.
                    179:                                 */
                    180:                                len += esclen;
                    181:                        }
                    182:                }
                    183:        }
                    184:        if (use_quotes)
                    185:        {
                    186:                if (have_quotes)
                    187:                        /*
                    188:                         * We can't quote a string that contains quotes.
                    189:                         */
                    190:                        return (NULL);
                    191:                len = strlen(s) + 3;
                    192:        }
                    193:        /*
                    194:         * Allocate and construct the new string.
                    195:         */
                    196:        newstr = p = (char *) ecalloc(len, sizeof(char));
                    197:        if (use_quotes)
                    198:        {
1.1.1.3   shadchin  199:                SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote);
1.1.1.2   millert   200:        } else
                    201:        {
                    202:                while (*s != '\0')
                    203:                {
                    204:                        if (metachar(*s))
                    205:                        {
                    206:                                /*
                    207:                                 * Add the escape char.
                    208:                                 */
1.1.1.4 ! shadchin  209:                                strlcpy(p, esc, newstr + len - p);
1.1.1.2   millert   210:                                p += esclen;
                    211:                        }
                    212:                        *p++ = *s++;
                    213:                }
                    214:                *p = '\0';
                    215:        }
                    216:        return (newstr);
                    217: }
1.1       etheisen  218:
                    219: /*
                    220:  * Return a pathname that points to a specified file in a specified directory.
                    221:  * Return NULL if the file does not exist in the directory.
                    222:  */
                    223:        static char *
                    224: dirfile(dirname, filename)
                    225:        char *dirname;
                    226:        char *filename;
                    227: {
                    228:        char *pathname;
1.1.1.2   millert   229:        char *qpathname;
1.1.1.4 ! shadchin  230:        size_t len;
1.1       etheisen  231:        int f;
                    232:
                    233:        if (dirname == NULL || *dirname == '\0')
                    234:                return (NULL);
                    235:        /*
                    236:         * Construct the full pathname.
                    237:         */
1.1.1.3   shadchin  238:        len= strlen(dirname) + strlen(filename) + 2;
                    239:        pathname = (char *) calloc(len, sizeof(char));
1.1       etheisen  240:        if (pathname == NULL)
                    241:                return (NULL);
1.1.1.3   shadchin  242:        SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename);
1.1       etheisen  243:        /*
                    244:         * Make sure the file exists.
                    245:         */
1.1.1.2   millert   246:        qpathname = shell_unquote(pathname);
                    247:        f = open(qpathname, OPEN_READ);
1.1       etheisen  248:        if (f < 0)
                    249:        {
                    250:                free(pathname);
                    251:                pathname = NULL;
                    252:        } else
                    253:        {
1.1.1.2   millert   254:                close(f);
1.1       etheisen  255:        }
1.1.1.2   millert   256:        free(qpathname);
1.1       etheisen  257:        return (pathname);
                    258: }
                    259:
1.1.1.4 ! shadchin  260: #if USERFILE
1.1       etheisen  261: /*
                    262:  * Return the full pathname of the given file in the "home directory".
                    263:  */
                    264:        public char *
                    265: homefile(filename)
                    266:        char *filename;
                    267: {
                    268:        register char *pathname;
                    269:
                    270:        /*
                    271:         * Try $HOME/filename.
                    272:         */
1.1.1.2   millert   273:        pathname = dirfile(lgetenv("HOME"), filename);
1.1       etheisen  274:        if (pathname != NULL)
                    275:                return (pathname);
                    276: #if OS2
                    277:        /*
                    278:         * Try $INIT/filename.
                    279:         */
1.1.1.2   millert   280:        pathname = dirfile(lgetenv("INIT"), filename);
1.1       etheisen  281:        if (pathname != NULL)
                    282:                return (pathname);
                    283: #endif
1.1.1.2   millert   284: #if MSDOS_COMPILER || OS2
1.1       etheisen  285:        /*
                    286:         * Look for the file anywhere on search path.
                    287:         */
                    288:        pathname = (char *) calloc(_MAX_PATH, sizeof(char));
1.1.1.2   millert   289: #if MSDOS_COMPILER==DJGPPC
                    290:        {
                    291:                char *res = searchpath(filename);
                    292:                if (res == 0)
                    293:                        *pathname = '\0';
                    294:                else
1.1.1.4 ! shadchin  295:                        strlcpy(pathname, res, _MAX_PATH);
1.1.1.2   millert   296:        }
                    297: #else
1.1       etheisen  298:        _searchenv(filename, "PATH", pathname);
1.1.1.2   millert   299: #endif
1.1       etheisen  300:        if (*pathname != '\0')
                    301:                return (pathname);
                    302:        free(pathname);
                    303: #endif
                    304:        return (NULL);
                    305: }
1.1.1.4 ! shadchin  306: #endif /* USERFILE */
1.1       etheisen  307:
                    308: /*
                    309:  * Expand a string, substituting any "%" with the current filename,
                    310:  * and any "#" with the previous filename.
1.1.1.2   millert   311:  * But a string of N "%"s is just replaced with N-1 "%"s.
                    312:  * Likewise for a string of N "#"s.
1.1       etheisen  313:  * {{ This is a lot of work just to support % and #. }}
                    314:  */
                    315:        public char *
                    316: fexpand(s)
                    317:        char *s;
                    318: {
                    319:        register char *fr, *to;
                    320:        register int n;
                    321:        register char *e;
1.1.1.2   millert   322:        IFILE ifile;
                    323:
                    324: #define        fchar_ifile(c) \
                    325:        ((c) == '%' ? curr_ifile : \
                    326:         (c) == '#' ? old_ifile : NULL_IFILE)
1.1       etheisen  327:
                    328:        /*
                    329:         * Make one pass to see how big a buffer we
                    330:         * need to allocate for the expanded string.
                    331:         */
                    332:        n = 0;
                    333:        for (fr = s;  *fr != '\0';  fr++)
                    334:        {
                    335:                switch (*fr)
                    336:                {
                    337:                case '%':
                    338:                case '#':
1.1.1.2   millert   339:                        if (fr > s && fr[-1] == *fr)
                    340:                        {
                    341:                                /*
                    342:                                 * Second (or later) char in a string
                    343:                                 * of identical chars.  Treat as normal.
                    344:                                 */
                    345:                                n++;
                    346:                        } else if (fr[1] != *fr)
1.1       etheisen  347:                        {
1.1.1.2   millert   348:                                /*
                    349:                                 * Single char (not repeated).  Treat specially.
                    350:                                 */
                    351:                                ifile = fchar_ifile(*fr);
                    352:                                if (ifile == NULL_IFILE)
                    353:                                        n++;
                    354:                                else
                    355:                                        n += strlen(get_filename(ifile));
1.1       etheisen  356:                        }
1.1.1.2   millert   357:                        /*
                    358:                         * Else it is the first char in a string of
                    359:                         * identical chars.  Just discard it.
                    360:                         */
1.1       etheisen  361:                        break;
                    362:                default:
                    363:                        n++;
                    364:                        break;
                    365:                }
                    366:        }
                    367:
                    368:        e = (char *) ecalloc(n+1, sizeof(char));
                    369:
                    370:        /*
                    371:         * Now copy the string, expanding any "%" or "#".
                    372:         */
                    373:        to = e;
                    374:        for (fr = s;  *fr != '\0';  fr++)
                    375:        {
                    376:                switch (*fr)
                    377:                {
                    378:                case '%':
                    379:                case '#':
1.1.1.2   millert   380:                        if (fr > s && fr[-1] == *fr)
                    381:                        {
                    382:                                *to++ = *fr;
                    383:                        } else if (fr[1] != *fr)
                    384:                        {
                    385:                                ifile = fchar_ifile(*fr);
                    386:                                if (ifile == NULL_IFILE)
                    387:                                        *to++ = *fr;
                    388:                                else
                    389:                                {
1.1.1.4 ! shadchin  390:                                        strlcpy(to, get_filename(ifile),
        !           391:                                            e + n + 1 - to);
1.1.1.2   millert   392:                                        to += strlen(to);
                    393:                                }
                    394:                        }
1.1       etheisen  395:                        break;
                    396:                default:
                    397:                        *to++ = *fr;
                    398:                        break;
                    399:                }
                    400:        }
                    401:        *to = '\0';
                    402:        return (e);
                    403: }
                    404:
1.1.1.3   shadchin  405:
1.1       etheisen  406: #if TAB_COMPLETE_FILENAME
                    407:
                    408: /*
                    409:  * Return a blank-separated list of filenames which "complete"
                    410:  * the given string.
                    411:  */
                    412:        public char *
                    413: fcomplete(s)
                    414:        char *s;
                    415: {
                    416:        char *fpat;
1.1.1.2   millert   417:        char *qs;
1.1.1.4 ! shadchin  418:        size_t len;
1.1.1.2   millert   419:
                    420:        if (secure)
                    421:                return (NULL);
1.1       etheisen  422:        /*
                    423:         * Complete the filename "s" by globbing "s*".
                    424:         */
1.1.1.2   millert   425: #if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC)
1.1       etheisen  426:        /*
                    427:         * But in DOS, we have to glob "s*.*".
                    428:         * But if the final component of the filename already has
                    429:         * a dot in it, just do "s*".
                    430:         * (Thus, "FILE" is globbed as "FILE*.*",
                    431:         *  but "FILE.A" is globbed as "FILE.A*").
                    432:         */
1.1.1.2   millert   433:        {
                    434:                char *slash;
                    435:                for (slash = s+strlen(s)-1;  slash > s;  slash--)
                    436:                        if (*slash == *PATHNAME_SEP || *slash == '/')
                    437:                                break;
1.1.1.3   shadchin  438:                len = strlen(s) + 4;
                    439:                fpat = (char *) ecalloc(len, sizeof(char));
1.1.1.2   millert   440:                if (strchr(slash, '.') == NULL)
1.1.1.3   shadchin  441:                        SNPRINTF1(fpat, len, "%s*.*", s);
1.1.1.2   millert   442:                else
1.1.1.3   shadchin  443:                        SNPRINTF1(fpat, len, "%s*", s);
1.1.1.2   millert   444:        }
1.1       etheisen  445: #else
1.1.1.3   shadchin  446:        {
1.1.1.4 ! shadchin  447:        len = strlen(s) + 2;
1.1.1.3   shadchin  448:        fpat = (char *) ecalloc(len, sizeof(char));
                    449:        SNPRINTF1(fpat, len, "%s*", s);
                    450:        }
1.1       etheisen  451: #endif
1.1.1.2   millert   452:        qs = lglob(fpat);
                    453:        s = shell_unquote(qs);
1.1       etheisen  454:        if (strcmp(s,fpat) == 0)
                    455:        {
                    456:                /*
                    457:                 * The filename didn't expand.
                    458:                 */
1.1.1.2   millert   459:                free(qs);
                    460:                qs = NULL;
1.1       etheisen  461:        }
1.1.1.2   millert   462:        free(s);
1.1       etheisen  463:        free(fpat);
1.1.1.2   millert   464:        return (qs);
1.1       etheisen  465: }
                    466: #endif
                    467:
                    468: /*
                    469:  * Try to determine if a file is "binary".
                    470:  * This is just a guess, and we need not try too hard to make it accurate.
                    471:  */
                    472:        public int
                    473: bin_file(f)
                    474:        int f;
                    475: {
                    476:        int n;
1.1.1.3   shadchin  477:        int bin_count = 0;
                    478:        char data[256];
                    479:        char* p;
                    480:        char* pend;
1.1       etheisen  481:
                    482:        if (!seekable(f))
                    483:                return (0);
1.1.1.3   shadchin  484:        if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK)
1.1       etheisen  485:                return (0);
                    486:        n = read(f, data, sizeof(data));
1.1.1.3   shadchin  487:        pend = &data[n];
                    488:        for (p = data;  p < pend;  )
                    489:        {
                    490:                LWCHAR c = step_char(&p, +1, pend);
                    491:                if (ctldisp == OPT_ONPLUS && IS_CSI_START(c))
                    492:                {
                    493:                        do {
                    494:                                c = step_char(&p, +1, pend);
                    495:                        } while (p < pend && is_ansi_middle(c));
                    496:                } else if (binary_char(c))
                    497:                        bin_count++;
                    498:        }
                    499:        /*
                    500:         * Call it a binary file if there are more than 5 binary characters
                    501:         * in the first 256 bytes of the file.
                    502:         */
                    503:        return (bin_count > 5);
1.1       etheisen  504: }
                    505:
                    506: /*
                    507:  * Try to determine the size of a file by seeking to the end.
                    508:  */
                    509:        static POSITION
                    510: seek_filesize(f)
                    511:        int f;
                    512: {
                    513:        off_t spos;
                    514:
1.1.1.3   shadchin  515:        spos = lseek(f, (off_t)0, SEEK_END);
1.1       etheisen  516:        if (spos == BAD_LSEEK)
                    517:                return (NULL_POSITION);
                    518:        return ((POSITION) spos);
                    519: }
                    520:
                    521: /*
                    522:  * Read a string from a file.
                    523:  * Return a pointer to the string in memory.
                    524:  */
                    525:        static char *
                    526: readfd(fd)
                    527:        FILE *fd;
                    528: {
                    529:        int len;
                    530:        int ch;
                    531:        char *buf;
                    532:        char *p;
                    533:
                    534:        /*
                    535:         * Make a guess about how many chars in the string
                    536:         * and allocate a buffer to hold it.
                    537:         */
                    538:        len = 100;
                    539:        buf = (char *) ecalloc(len, sizeof(char));
                    540:        for (p = buf;  ;  p++)
                    541:        {
                    542:                if ((ch = getc(fd)) == '\n' || ch == EOF)
                    543:                        break;
                    544:                if (p - buf >= len-1)
                    545:                {
                    546:                        /*
                    547:                         * The string is too big to fit in the buffer we have.
                    548:                         * Allocate a new buffer, twice as big.
                    549:                         */
                    550:                        len *= 2;
                    551:                        *p = '\0';
                    552:                        p = (char *) ecalloc(len, sizeof(char));
1.1.1.4 ! shadchin  553:                        strlcpy(p, buf, len);
1.1       etheisen  554:                        free(buf);
                    555:                        buf = p;
                    556:                        p = buf + strlen(buf);
                    557:                }
                    558:                *p = ch;
                    559:        }
                    560:        *p = '\0';
                    561:        return (buf);
                    562: }
                    563:
1.1.1.2   millert   564:
                    565:
                    566: #if HAVE_POPEN
                    567:
                    568: FILE *popen();
                    569:
1.1       etheisen  570: /*
                    571:  * Execute a shell command.
                    572:  * Return a pointer to a pipe connected to the shell command's standard output.
                    573:  */
                    574:        static FILE *
1.1.1.2   millert   575: shellcmd(cmd)
1.1       etheisen  576:        char *cmd;
                    577: {
                    578:        FILE *fd;
1.1.1.2   millert   579:
1.1       etheisen  580: #if HAVE_SHELL
1.1.1.2   millert   581:        char *shell;
                    582:
                    583:        shell = lgetenv("SHELL");
1.1       etheisen  584:        if (shell != NULL && *shell != '\0')
                    585:        {
1.1.1.2   millert   586:                char *scmd;
                    587:                char *esccmd;
                    588:
1.1       etheisen  589:                /*
1.1.1.2   millert   590:                 * Read the output of <$SHELL -c cmd>.
                    591:                 * Escape any metacharacters in the command.
1.1       etheisen  592:                 */
1.1.1.2   millert   593:                esccmd = shell_quote(cmd);
                    594:                if (esccmd == NULL)
                    595:                {
                    596:                        fd = popen(cmd, "r");
                    597:                } else
                    598:                {
1.1.1.4 ! shadchin  599:                        size_t len = strlen(shell) + strlen(esccmd) + 5;
1.1.1.3   shadchin  600:                        scmd = (char *) ecalloc(len, sizeof(char));
                    601:                        SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd);
1.1.1.2   millert   602:                        free(esccmd);
                    603:                        fd = popen(scmd, "r");
                    604:                        free(scmd);
                    605:                }
                    606:        } else
1.1       etheisen  607: #endif
1.1.1.2   millert   608:        {
                    609:                fd = popen(cmd, "r");
                    610:        }
                    611:        /*
                    612:         * Redirection in `popen' might have messed with the
                    613:         * standard devices.  Restore binary input mode.
                    614:         */
                    615:        SET_BINARY(0);
1.1       etheisen  616:        return (fd);
                    617: }
                    618:
1.1.1.2   millert   619: #endif /* HAVE_POPEN */
                    620:
                    621:
1.1.1.4 ! shadchin  622: #if !SMALL
1.1       etheisen  623: /*
1.1.1.2   millert   624:  * Expand a filename, doing any system-specific metacharacter substitutions.
1.1       etheisen  625:  */
                    626:        public char *
1.1.1.2   millert   627: lglob(filename)
1.1       etheisen  628:        char *filename;
                    629: {
                    630:        char *gfilename;
1.1.1.2   millert   631:        char *ofilename;
1.1       etheisen  632:
1.1.1.2   millert   633:        ofilename = fexpand(filename);
                    634:        if (secure)
                    635:                return (ofilename);
                    636:        filename = shell_unquote(ofilename);
                    637:
                    638: #ifdef DECL_GLOB_LIST
1.1       etheisen  639: {
1.1.1.2   millert   640:        /*
                    641:         * The globbing function returns a list of names.
                    642:         */
1.1       etheisen  643:        int length;
1.1.1.2   millert   644:        char *p;
                    645:        char *qfilename;
                    646:        DECL_GLOB_LIST(list)
1.1       etheisen  647:
1.1.1.2   millert   648:        GLOB_LIST(filename, list);
                    649:        if (GLOB_LIST_FAILED(list))
                    650:        {
                    651:                free(filename);
                    652:                return (ofilename);
                    653:        }
                    654:        length = 1; /* Room for trailing null byte */
                    655:        for (SCAN_GLOB_LIST(list, p))
                    656:        {
                    657:                INIT_GLOB_LIST(list, p);
                    658:                qfilename = shell_quote(p);
                    659:                if (qfilename != NULL)
                    660:                {
                    661:                        length += strlen(qfilename) + 1;
                    662:                        free(qfilename);
                    663:                }
                    664:        }
1.1       etheisen  665:        gfilename = (char *) ecalloc(length, sizeof(char));
1.1.1.2   millert   666:        for (SCAN_GLOB_LIST(list, p))
1.1       etheisen  667:        {
1.1.1.2   millert   668:                INIT_GLOB_LIST(list, p);
                    669:                qfilename = shell_quote(p);
                    670:                if (qfilename != NULL)
                    671:                {
1.1.1.4 ! shadchin  672:                        snprintf(gfilename + strlen(gfilename),
        !           673:                            length - strlen(gfilename), "%s ", qfilename);
1.1.1.2   millert   674:                        free(qfilename);
                    675:                }
1.1       etheisen  676:        }
1.1.1.2   millert   677:        /*
                    678:         * Overwrite the final trailing space with a null terminator.
                    679:         */
1.1.1.4 ! shadchin  680:        if (gfilename[0] != '\0' && gfilename[strlen(gfilename) - 1] == ' ')
        !           681:                gfilename[strlen(gfilename) - 1] = '\0';
1.1.1.2   millert   682:        GLOB_LIST_DONE(list);
1.1       etheisen  683: }
                    684: #else
1.1.1.2   millert   685: #ifdef DECL_GLOB_NAME
1.1       etheisen  686: {
1.1.1.2   millert   687:        /*
                    688:         * The globbing function returns a single name, and
                    689:         * is called multiple times to walk thru all names.
                    690:         */
                    691:        register char *p;
                    692:        register int len;
                    693:        register int n;
                    694:        char *pathname;
                    695:        char *qpathname;
                    696:        DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
                    697:
                    698:        GLOB_FIRST_NAME(filename, &fnd, handle);
                    699:        if (GLOB_FIRST_FAILED(handle))
                    700:        {
                    701:                free(filename);
                    702:                return (ofilename);
                    703:        }
                    704:
                    705:        _splitpath(filename, drive, dir, fname, ext);
                    706:        len = 100;
                    707:        gfilename = (char *) ecalloc(len, sizeof(char));
                    708:        p = gfilename;
                    709:        do {
                    710:                n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
                    711:                pathname = (char *) ecalloc(n, sizeof(char));
1.1.1.3   shadchin  712:                SNPRINTF3(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME);
1.1.1.2   millert   713:                qpathname = shell_quote(pathname);
                    714:                free(pathname);
                    715:                if (qpathname != NULL)
                    716:                {
                    717:                        n = strlen(qpathname);
                    718:                        while (p - gfilename + n + 2 >= len)
                    719:                        {
                    720:                                /*
                    721:                                 * No room in current buffer.
                    722:                                 * Allocate a bigger one.
                    723:                                 */
                    724:                                len *= 2;
                    725:                                *p = '\0';
                    726:                                p = (char *) ecalloc(len, sizeof(char));
1.1.1.4 ! shadchin  727:                                strlcpy(p, gfilename, len);
1.1.1.2   millert   728:                                free(gfilename);
                    729:                                gfilename = p;
                    730:                                p = gfilename + strlen(gfilename);
                    731:                        }
1.1.1.4 ! shadchin  732:                        strlcpy(p, qpathname, gfilename + len - p);
1.1.1.2   millert   733:                        free(qpathname);
                    734:                        p += n;
                    735:                        *p++ = ' ';
                    736:                }
                    737:        } while (GLOB_NEXT_NAME(handle, &fnd) == 0);
1.1       etheisen  738:
                    739:        /*
1.1.1.2   millert   740:         * Overwrite the final trailing space with a null terminator.
                    741:         */
                    742:        *--p = '\0';
                    743:        GLOB_NAME_DONE(handle);
                    744: }
                    745: #else
                    746: #if HAVE_POPEN
                    747: {
                    748:        /*
                    749:         * We get the shell to glob the filename for us by passing
1.1       etheisen  750:         * an "echo" command to the shell and reading its output.
                    751:         */
1.1.1.2   millert   752:        FILE *fd;
                    753:        char *s;
                    754:        char *lessecho;
                    755:        char *cmd;
                    756:        char *esc;
1.1.1.4 ! shadchin  757:        size_t len;
1.1.1.2   millert   758:
                    759:        esc = get_meta_escape();
                    760:        if (strlen(esc) == 0)
                    761:                esc = "-";
                    762:        esc = shell_quote(esc);
                    763:        if (esc == NULL)
                    764:        {
                    765:                free(filename);
                    766:                return (ofilename);
                    767:        }
                    768:        lessecho = lgetenv("LESSECHO");
                    769:        if (lessecho == NULL || *lessecho == '\0')
                    770:                lessecho = "lessecho";
                    771:        /*
                    772:         * Invoke lessecho, and read its output (a globbed list of filenames).
                    773:         */
1.1.1.3   shadchin  774:        len = strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24;
                    775:        cmd = (char *) ecalloc(len, sizeof(char));
                    776:        SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc);
1.1.1.2   millert   777:        free(esc);
                    778:        for (s = metachars();  *s != '\0';  s++)
1.1.1.4 ! shadchin  779:                snprintf(cmd + strlen(cmd), len - strlen(cmd), "-n0x%x ", *s);
        !           780:        snprintf(cmd + strlen(cmd), len - strlen(cmd), "-- %s", ofilename);
1.1.1.2   millert   781:        fd = shellcmd(cmd);
                    782:        free(cmd);
1.1       etheisen  783:        if (fd == NULL)
                    784:        {
                    785:                /*
                    786:                 * Cannot create the pipe.
                    787:                 * Just return the original (fexpanded) filename.
                    788:                 */
1.1.1.2   millert   789:                free(filename);
                    790:                return (ofilename);
1.1       etheisen  791:        }
                    792:        gfilename = readfd(fd);
                    793:        pclose(fd);
                    794:        if (*gfilename == '\0')
                    795:        {
                    796:                free(gfilename);
1.1.1.2   millert   797:                free(filename);
                    798:                return (ofilename);
1.1       etheisen  799:        }
                    800: }
1.1.1.2   millert   801: #else
                    802:        /*
                    803:         * No globbing functions at all.  Just use the fexpanded filename.
                    804:         */
                    805:        gfilename = save(filename);
                    806: #endif
                    807: #endif
1.1       etheisen  808: #endif
1.1.1.2   millert   809:        free(filename);
                    810:        free(ofilename);
1.1       etheisen  811:        return (gfilename);
                    812: }
1.1.1.4 ! shadchin  813: #endif /* !SMALL */
        !           814:
        !           815: /*
        !           816:  * Return number of %s escapes in a string.
        !           817:  * Return a large number if there are any other % escapes besides %s.
        !           818:  */
        !           819:        static int
        !           820: num_pct_s(lessopen)
        !           821:        char *lessopen;
        !           822: {
        !           823:        int num;
        !           824:
        !           825:        for (num = 0;; num++)
        !           826:        {
        !           827:                lessopen = strchr(lessopen, '%');
        !           828:                if (lessopen == NULL)
        !           829:                        break;
        !           830:                if (*++lessopen != 's')
        !           831:                        return (999);
        !           832:        }
        !           833:        return (num);
        !           834: }
1.1       etheisen  835:
                    836: /*
                    837:  * See if we should open a "replacement file"
                    838:  * instead of the file we're about to open.
                    839:  */
                    840:        public char *
                    841: open_altfile(filename, pf, pfd)
                    842:        char *filename;
                    843:        int *pf;
                    844:        void **pfd;
                    845: {
1.1.1.2   millert   846: #if !HAVE_POPEN
                    847:        return (NULL);
                    848: #else
1.1       etheisen  849:        char *lessopen;
1.1.1.2   millert   850:        char *cmd;
1.1.1.4 ! shadchin  851:        size_t len;
1.1       etheisen  852:        FILE *fd;
1.1.1.2   millert   853: #if HAVE_FILENO
                    854:        int returnfd = 0;
                    855: #endif
1.1       etheisen  856:
1.1.1.2   millert   857:        if (!use_lessopen || secure)
                    858:                return (NULL);
1.1       etheisen  859:        ch_ungetchar(-1);
1.1.1.2   millert   860:        if ((lessopen = lgetenv("LESSOPEN")) == NULL)
1.1       etheisen  861:                return (NULL);
1.1.1.4 ! shadchin  862:        while (*lessopen == '|')
1.1       etheisen  863:        {
                    864:                /*
                    865:                 * If LESSOPEN starts with a |, it indicates
                    866:                 * a "pipe preprocessor".
                    867:                 */
1.1.1.3   shadchin  868: #if !HAVE_FILENO
1.1.1.2   millert   869:                error("LESSOPEN pipe is not supported", NULL_PARG);
                    870:                return (NULL);
1.1.1.3   shadchin  871: #else
                    872:                lessopen++;
1.1.1.4 ! shadchin  873:                returnfd++;
1.1.1.2   millert   874: #endif
1.1       etheisen  875:        }
1.1.1.3   shadchin  876:        if (*lessopen == '-') {
                    877:                /*
                    878:                 * Lessopen preprocessor will accept "-" as a filename.
                    879:                 */
                    880:                lessopen++;
                    881:        } else {
                    882:                if (strcmp(filename, "-") == 0)
                    883:                        return (NULL);
                    884:        }
1.1.1.4 ! shadchin  885:        if (num_pct_s(lessopen) > 1)
        !           886:        {
        !           887:                error("Invalid LESSOPEN variable", NULL_PARG);
        !           888:                return (NULL);
        !           889:        }
1.1.1.2   millert   890:
1.1.1.3   shadchin  891:        len = strlen(lessopen) + strlen(filename) + 2;
                    892:        cmd = (char *) ecalloc(len, sizeof(char));
                    893:        SNPRINTF1(cmd, len, lessopen, filename);
1.1.1.2   millert   894:        fd = shellcmd(cmd);
                    895:        free(cmd);
1.1       etheisen  896:        if (fd == NULL)
                    897:        {
                    898:                /*
                    899:                 * Cannot create the pipe.
                    900:                 */
                    901:                return (NULL);
                    902:        }
1.1.1.2   millert   903: #if HAVE_FILENO
1.1       etheisen  904:        if (returnfd)
                    905:        {
                    906:                int f;
                    907:                char c;
                    908:
                    909:                /*
                    910:                 * Read one char to see if the pipe will produce any data.
                    911:                 * If it does, push the char back on the pipe.
                    912:                 */
                    913:                f = fileno(fd);
1.1.1.2   millert   914:                SET_BINARY(f);
1.1       etheisen  915:                if (read(f, &c, 1) != 1)
                    916:                {
                    917:                        /*
1.1.1.4 ! shadchin  918:                         * Pipe is empty.
        !           919:                         * If more than 1 pipe char was specified,
        !           920:                         * the exit status tells whether the file itself
        !           921:                         * is empty, or if there is no alt file.
        !           922:                         * If only one pipe char, just assume no alt file.
1.1       etheisen  923:                         */
1.1.1.4 ! shadchin  924:                        int status = pclose(fd);
        !           925:                        if (returnfd > 1 && status == 0) {
        !           926:                                *pfd = NULL;
        !           927:                                *pf = -1;
        !           928:                                return (save(FAKE_EMPTYFILE));
        !           929:                        }
1.1       etheisen  930:                        return (NULL);
                    931:                }
                    932:                ch_ungetchar(c);
                    933:                *pfd = (void *) fd;
                    934:                *pf = f;
                    935:                return (save("-"));
                    936:        }
1.1.1.2   millert   937: #endif
                    938:        cmd = readfd(fd);
1.1       etheisen  939:        pclose(fd);
1.1.1.2   millert   940:        if (*cmd == '\0')
1.1       etheisen  941:                /*
                    942:                 * Pipe is empty.  This means there is no alt file.
                    943:                 */
                    944:                return (NULL);
1.1.1.2   millert   945:        return (cmd);
                    946: #endif /* HAVE_POPEN */
1.1       etheisen  947: }
                    948:
                    949: /*
                    950:  * Close a replacement file.
                    951:  */
                    952:        public void
                    953: close_altfile(altfilename, filename, pipefd)
                    954:        char *altfilename;
                    955:        char *filename;
                    956:        void *pipefd;
                    957: {
1.1.1.2   millert   958: #if HAVE_POPEN
1.1       etheisen  959:        char *lessclose;
                    960:        FILE *fd;
1.1.1.2   millert   961:        char *cmd;
1.1.1.4 ! shadchin  962:        size_t len;
1.1       etheisen  963:
1.1.1.2   millert   964:        if (secure)
                    965:                return;
1.1       etheisen  966:        if (pipefd != NULL)
1.1.1.2   millert   967:        {
                    968: #if OS2
                    969:                /*
                    970:                 * The pclose function of OS/2 emx sometimes fails.
                    971:                 * Send SIGINT to the piped process before closing it.
                    972:                 */
                    973:                kill(((FILE*)pipefd)->_pid, SIGINT);
                    974: #endif
1.1       etheisen  975:                pclose((FILE*) pipefd);
1.1.1.2   millert   976:        }
                    977:        if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
1.1       etheisen  978:                return;
1.1.1.4 ! shadchin  979:        if (num_pct_s(lessclose) > 2)
        !           980:        {
        !           981:                error("Invalid LESSCLOSE variable");
        !           982:                return;
        !           983:        }
1.1.1.3   shadchin  984:        len = strlen(lessclose) + strlen(filename) + strlen(altfilename) + 2;
                    985:        cmd = (char *) ecalloc(len, sizeof(char));
                    986:        SNPRINTF2(cmd, len, lessclose, filename, altfilename);
1.1.1.2   millert   987:        fd = shellcmd(cmd);
                    988:        free(cmd);
                    989:        if (fd != NULL)
                    990:                pclose(fd);
                    991: #endif
1.1       etheisen  992: }
                    993:
1.1.1.2   millert   994: /*
                    995:  * Is the specified file a directory?
                    996:  */
                    997:        public int
                    998: is_dir(filename)
1.1       etheisen  999:        char *filename;
                   1000: {
1.1.1.2   millert  1001:        int isdir = 0;
1.1       etheisen 1002:
1.1.1.2   millert  1003:        filename = shell_unquote(filename);
                   1004: #if HAVE_STAT
1.1       etheisen 1005: {
1.1.1.2   millert  1006:        int r;
                   1007:        struct stat statbuf;
1.1       etheisen 1008:
1.1.1.2   millert  1009:        r = stat(filename, &statbuf);
                   1010:        isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
1.1       etheisen 1011: }
1.1.1.2   millert  1012: #else
                   1013: #ifdef _OSK
1.1       etheisen 1014: {
1.1.1.2   millert  1015:        register int f;
1.1       etheisen 1016:
1.1.1.2   millert  1017:        f = open(filename, S_IREAD | S_IFDIR);
                   1018:        if (f >= 0)
                   1019:                close(f);
                   1020:        isdir = (f >= 0);
1.1       etheisen 1021: }
                   1022: #endif
                   1023: #endif
1.1.1.2   millert  1024:        free(filename);
                   1025:        return (isdir);
                   1026: }
1.1       etheisen 1027:
                   1028: /*
                   1029:  * Returns NULL if the file can be opened and
                   1030:  * is an ordinary file, otherwise an error message
                   1031:  * (if it cannot be opened or is a directory, etc.)
                   1032:  */
                   1033:        public char *
                   1034: bad_file(filename)
                   1035:        char *filename;
                   1036: {
1.1.1.2   millert  1037:        register char *m = NULL;
1.1.1.4 ! shadchin 1038:        size_t len;
1.1       etheisen 1039:
1.1.1.2   millert  1040:        filename = shell_unquote(filename);
1.1.1.3   shadchin 1041:        if (!force_open && is_dir(filename))
1.1       etheisen 1042:        {
1.1.1.2   millert  1043:                static char is_a_dir[] = " is a directory";
                   1044:
1.1.1.4 ! shadchin 1045:                len = strlen(filename) + sizeof(is_a_dir);
        !          1046:                m = (char *) ecalloc(len, sizeof(char));
        !          1047:                strlcpy(m, filename, len);
        !          1048:                strlcat(m, is_a_dir, len);
1.1.1.2   millert  1049:        } else
1.1       etheisen 1050:        {
1.1.1.2   millert  1051: #if HAVE_STAT
                   1052:                int r;
                   1053:                struct stat statbuf;
1.1       etheisen 1054:
1.1.1.2   millert  1055:                r = stat(filename, &statbuf);
                   1056:                if (r < 0)
                   1057:                {
                   1058:                        m = errno_message(filename);
                   1059:                } else if (force_open)
                   1060:                {
                   1061:                        m = NULL;
                   1062:                } else if (!S_ISREG(statbuf.st_mode))
                   1063:                {
                   1064:                        static char not_reg[] = " is not a regular file (use -f to see it)";
1.1.1.4 ! shadchin 1065:                        len = strlen(filename) + sizeof(not_reg);
        !          1066:                        m = (char *) ecalloc(len, sizeof(char));
        !          1067:                        strlcpy(m, filename, len);
        !          1068:                        strlcat(m, not_reg, len);
1.1.1.2   millert  1069:                }
                   1070: #endif
                   1071:        }
                   1072:        free(filename);
                   1073:        return (m);
1.1       etheisen 1074: }
                   1075:
                   1076: /*
                   1077:  * Return the size of a file, as cheaply as possible.
                   1078:  * In Unix, we can stat the file.
                   1079:  */
                   1080:        public POSITION
                   1081: filesize(f)
                   1082:        int f;
                   1083: {
1.1.1.2   millert  1084: #if HAVE_STAT
1.1       etheisen 1085:        struct stat statbuf;
                   1086:
1.1.1.2   millert  1087:        if (fstat(f, &statbuf) >= 0)
                   1088:                return ((POSITION) statbuf.st_size);
1.1       etheisen 1089: #else
1.1.1.2   millert  1090: #ifdef _OSK
                   1091:        long size;
1.1       etheisen 1092:
1.1.1.2   millert  1093:        if ((size = (long) _gs_size(f)) >= 0)
                   1094:                return ((POSITION) size);
                   1095: #endif
                   1096: #endif
                   1097:        return (seek_filesize(f));
1.1       etheisen 1098: }
                   1099:
                   1100: /*
1.1.1.2   millert  1101:  *
1.1       etheisen 1102:  */
1.1.1.2   millert  1103:        public char *
                   1104: shell_coption()
1.1       etheisen 1105: {
1.1.1.2   millert  1106:        return ("-c");
1.1       etheisen 1107: }
1.1.1.3   shadchin 1108:
                   1109: /*
                   1110:  * Return last component of a pathname.
                   1111:  */
                   1112:        public char *
                   1113: last_component(name)
                   1114:        char *name;
                   1115: {
                   1116:        char *slash;
                   1117:
                   1118:        for (slash = name + strlen(name);  slash > name; )
                   1119:        {
                   1120:                --slash;
                   1121:                if (*slash == *PATHNAME_SEP || *slash == '/')
                   1122:                        return (slash + 1);
                   1123:        }
                   1124:        return (name);
                   1125: }
                   1126: