home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / less_332.lzh / less_332 / filename.c < prev    next >
Text File  |  1998-03-03  |  19KB  |  1,029 lines

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