home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / less373.zip / filename.c < prev    next >
C/C++ Source or Header  |  2002-01-14  |  18KB  |  1,025 lines

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