160786Sps/* 2240121Sdelphij * Copyright (C) 1984-2012 Mark Nudelman 360786Sps * 460786Sps * You may distribute under the terms of either the GNU General Public 560786Sps * License or the Less License, as specified in the README file. 660786Sps * 7240121Sdelphij * For more information, see the README file. 860786Sps */ 960786Sps 1060786Sps 1160786Sps/* 1260786Sps * Routines to mess around with filenames (and files). 1360786Sps * Much of this is very OS dependent. 1460786Sps */ 1560786Sps 1660786Sps#include "less.h" 1760786Sps#include "lglob.h" 1860786Sps#if MSDOS_COMPILER 1960786Sps#include <dos.h> 2060786Sps#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER) 2160786Sps#include <dir.h> 2260786Sps#endif 2360786Sps#if MSDOS_COMPILER==DJGPPC 2460786Sps#include <glob.h> 2560786Sps#include <dir.h> 2660786Sps#define _MAX_PATH PATH_MAX 2760786Sps#endif 2860786Sps#endif 2960786Sps#ifdef _OSK 3060786Sps#include <rbf.h> 3160786Sps#ifndef _OSK_MWC32 3260786Sps#include <modes.h> 3360786Sps#endif 3460786Sps#endif 3589019Sps#if OS2 3689019Sps#include <signal.h> 3789019Sps#endif 3860786Sps 3960786Sps#if HAVE_STAT 4060786Sps#include <sys/stat.h> 4160786Sps#ifndef S_ISDIR 4260786Sps#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 4360786Sps#endif 4460786Sps#ifndef S_ISREG 4560786Sps#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 4660786Sps#endif 4760786Sps#endif 4860786Sps 4960786Sps 5060786Spsextern int force_open; 5160786Spsextern int secure; 52128345Stjrextern int use_lessopen; 53170256Sdelphijextern int ctldisp; 54191930Sdelphijextern int utf_mode; 5560786Spsextern IFILE curr_ifile; 5660786Spsextern IFILE old_ifile; 5760786Sps#if SPACES_IN_FILENAMES 5860786Spsextern char openquote; 5960786Spsextern char closequote; 6060786Sps#endif 6160786Sps 6260786Sps/* 6360786Sps * Remove quotes around a filename. 6460786Sps */ 6560786Sps public char * 66128345Stjrshell_unquote(str) 6760786Sps char *str; 6860786Sps{ 6960786Sps char *name; 7060786Sps char *p; 7160786Sps 72128345Stjr name = p = (char *) ecalloc(strlen(str)+1, sizeof(char)); 73128345Stjr if (*str == openquote) 74128345Stjr { 75128345Stjr str++; 76128345Stjr while (*str != '\0') 77128345Stjr { 78128345Stjr if (*str == closequote) 79128345Stjr { 80128345Stjr if (str[1] != closequote) 81128345Stjr break; 82128345Stjr str++; 83128345Stjr } 84128345Stjr *p++ = *str++; 85128345Stjr } 86128345Stjr } else 87128345Stjr { 88128345Stjr char *esc = get_meta_escape(); 89128345Stjr int esclen = strlen(esc); 90128345Stjr while (*str != '\0') 91128345Stjr { 92128345Stjr if (esclen > 0 && strncmp(str, esc, esclen) == 0) 93128345Stjr str += esclen; 94128345Stjr *p++ = *str++; 95128345Stjr } 96128345Stjr } 97128345Stjr *p = '\0'; 9860786Sps return (name); 9960786Sps} 10060786Sps 10160786Sps/* 102128345Stjr * Get the shell's escape character. 103128345Stjr */ 104128345Stjr public char * 105128345Stjrget_meta_escape() 106128345Stjr{ 107128345Stjr char *s; 108128345Stjr 109128345Stjr s = lgetenv("LESSMETAESCAPE"); 110128345Stjr if (s == NULL) 111128345Stjr s = DEF_METAESCAPE; 112128345Stjr return (s); 113128345Stjr} 114128345Stjr 115128345Stjr/* 116128345Stjr * Get the characters which the shell considers to be "metacharacters". 117128345Stjr */ 118128345Stjr static char * 119128345Stjrmetachars() 120128345Stjr{ 121128345Stjr static char *mchars = NULL; 122128345Stjr 123128345Stjr if (mchars == NULL) 124128345Stjr { 125128345Stjr mchars = lgetenv("LESSMETACHARS"); 126128345Stjr if (mchars == NULL) 127128345Stjr mchars = DEF_METACHARS; 128128345Stjr } 129128345Stjr return (mchars); 130128345Stjr} 131128345Stjr 132128345Stjr/* 133128345Stjr * Is this a shell metacharacter? 134128345Stjr */ 135128345Stjr static int 136128345Stjrmetachar(c) 137128345Stjr char c; 138128345Stjr{ 139128345Stjr return (strchr(metachars(), c) != NULL); 140128345Stjr} 141128345Stjr 142128345Stjr/* 143128345Stjr * Insert a backslash before each metacharacter in a string. 144128345Stjr */ 145128345Stjr public char * 146128345Stjrshell_quote(s) 147128345Stjr char *s; 148128345Stjr{ 149128345Stjr char *p; 150128345Stjr char *newstr; 151128345Stjr int len; 152128345Stjr char *esc = get_meta_escape(); 153128345Stjr int esclen = strlen(esc); 154128345Stjr int use_quotes = 0; 155128345Stjr int have_quotes = 0; 156128345Stjr 157128345Stjr /* 158128345Stjr * Determine how big a string we need to allocate. 159128345Stjr */ 160128345Stjr len = 1; /* Trailing null byte */ 161128345Stjr for (p = s; *p != '\0'; p++) 162128345Stjr { 163128345Stjr len++; 164128345Stjr if (*p == openquote || *p == closequote) 165128345Stjr have_quotes = 1; 166128345Stjr if (metachar(*p)) 167128345Stjr { 168128345Stjr if (esclen == 0) 169128345Stjr { 170128345Stjr /* 171128345Stjr * We've got a metachar, but this shell 172128345Stjr * doesn't support escape chars. Use quotes. 173128345Stjr */ 174128345Stjr use_quotes = 1; 175128345Stjr } else 176128345Stjr { 177128345Stjr /* 178128345Stjr * Allow space for the escape char. 179128345Stjr */ 180128345Stjr len += esclen; 181128345Stjr } 182128345Stjr } 183128345Stjr } 184128345Stjr if (use_quotes) 185128345Stjr { 186128345Stjr if (have_quotes) 187128345Stjr /* 188128345Stjr * We can't quote a string that contains quotes. 189128345Stjr */ 190128345Stjr return (NULL); 191128345Stjr len = strlen(s) + 3; 192128345Stjr } 193128345Stjr /* 194128345Stjr * Allocate and construct the new string. 195128345Stjr */ 196128345Stjr newstr = p = (char *) ecalloc(len, sizeof(char)); 197128345Stjr if (use_quotes) 198128345Stjr { 199161475Sdelphij SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote); 200128345Stjr } else 201128345Stjr { 202128345Stjr while (*s != '\0') 203128345Stjr { 204128345Stjr if (metachar(*s)) 205128345Stjr { 206128345Stjr /* 207128345Stjr * Add the escape char. 208128345Stjr */ 209128345Stjr strcpy(p, esc); 210128345Stjr p += esclen; 211128345Stjr } 212128345Stjr *p++ = *s++; 213128345Stjr } 214128345Stjr *p = '\0'; 215128345Stjr } 216128345Stjr return (newstr); 217128345Stjr} 218128345Stjr 219128345Stjr/* 22060786Sps * Return a pathname that points to a specified file in a specified directory. 22160786Sps * Return NULL if the file does not exist in the directory. 22260786Sps */ 22360786Sps static char * 22460786Spsdirfile(dirname, filename) 22560786Sps char *dirname; 22660786Sps char *filename; 22760786Sps{ 22860786Sps char *pathname; 22960786Sps char *qpathname; 230161475Sdelphij int len; 23160786Sps int f; 23260786Sps 23360786Sps if (dirname == NULL || *dirname == '\0') 23460786Sps return (NULL); 23560786Sps /* 23660786Sps * Construct the full pathname. 23760786Sps */ 238161475Sdelphij len= strlen(dirname) + strlen(filename) + 2; 239161475Sdelphij pathname = (char *) calloc(len, sizeof(char)); 24060786Sps if (pathname == NULL) 24160786Sps return (NULL); 242161475Sdelphij SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename); 24360786Sps /* 24460786Sps * Make sure the file exists. 24560786Sps */ 246128345Stjr qpathname = shell_unquote(pathname); 24760786Sps f = open(qpathname, OPEN_READ); 24860786Sps if (f < 0) 24960786Sps { 25060786Sps free(pathname); 25160786Sps pathname = NULL; 25260786Sps } else 25360786Sps { 25460786Sps close(f); 25560786Sps } 25660786Sps free(qpathname); 25760786Sps return (pathname); 25860786Sps} 25960786Sps 26060786Sps/* 26160786Sps * Return the full pathname of the given file in the "home directory". 26260786Sps */ 26360786Sps public char * 26460786Spshomefile(filename) 26560786Sps char *filename; 26660786Sps{ 26760786Sps register char *pathname; 26860786Sps 26960786Sps /* 27060786Sps * Try $HOME/filename. 27160786Sps */ 27260786Sps pathname = dirfile(lgetenv("HOME"), filename); 27360786Sps if (pathname != NULL) 27460786Sps return (pathname); 27560786Sps#if OS2 27660786Sps /* 27760786Sps * Try $INIT/filename. 27860786Sps */ 27960786Sps pathname = dirfile(lgetenv("INIT"), filename); 28060786Sps if (pathname != NULL) 28160786Sps return (pathname); 28260786Sps#endif 28360786Sps#if MSDOS_COMPILER || OS2 28460786Sps /* 28560786Sps * Look for the file anywhere on search path. 28660786Sps */ 28760786Sps pathname = (char *) calloc(_MAX_PATH, sizeof(char)); 28860786Sps#if MSDOS_COMPILER==DJGPPC 28960786Sps { 29060786Sps char *res = searchpath(filename); 29160786Sps if (res == 0) 29260786Sps *pathname = '\0'; 29360786Sps else 29460786Sps strcpy(pathname, res); 29560786Sps } 29660786Sps#else 29760786Sps _searchenv(filename, "PATH", pathname); 29860786Sps#endif 29960786Sps if (*pathname != '\0') 30060786Sps return (pathname); 30160786Sps free(pathname); 30260786Sps#endif 30360786Sps return (NULL); 30460786Sps} 30560786Sps 30660786Sps/* 30760786Sps * Expand a string, substituting any "%" with the current filename, 30860786Sps * and any "#" with the previous filename. 30960786Sps * But a string of N "%"s is just replaced with N-1 "%"s. 31060786Sps * Likewise for a string of N "#"s. 31160786Sps * {{ This is a lot of work just to support % and #. }} 31260786Sps */ 31360786Sps public char * 31460786Spsfexpand(s) 31560786Sps char *s; 31660786Sps{ 31760786Sps register char *fr, *to; 31860786Sps register int n; 31960786Sps register char *e; 32060786Sps IFILE ifile; 32160786Sps 32260786Sps#define fchar_ifile(c) \ 32360786Sps ((c) == '%' ? curr_ifile : \ 32460786Sps (c) == '#' ? old_ifile : NULL_IFILE) 32560786Sps 32660786Sps /* 32760786Sps * Make one pass to see how big a buffer we 32860786Sps * need to allocate for the expanded string. 32960786Sps */ 33060786Sps n = 0; 33160786Sps for (fr = s; *fr != '\0'; fr++) 33260786Sps { 33360786Sps switch (*fr) 33460786Sps { 33560786Sps case '%': 33660786Sps case '#': 33760786Sps if (fr > s && fr[-1] == *fr) 33860786Sps { 33960786Sps /* 34060786Sps * Second (or later) char in a string 34160786Sps * of identical chars. Treat as normal. 34260786Sps */ 34360786Sps n++; 34460786Sps } else if (fr[1] != *fr) 34560786Sps { 34660786Sps /* 34760786Sps * Single char (not repeated). Treat specially. 34860786Sps */ 34960786Sps ifile = fchar_ifile(*fr); 35060786Sps if (ifile == NULL_IFILE) 35160786Sps n++; 35260786Sps else 35360786Sps n += strlen(get_filename(ifile)); 35460786Sps } 35560786Sps /* 35660786Sps * Else it is the first char in a string of 35760786Sps * identical chars. Just discard it. 35860786Sps */ 35960786Sps break; 36060786Sps default: 36160786Sps n++; 36260786Sps break; 36360786Sps } 36460786Sps } 36560786Sps 36660786Sps e = (char *) ecalloc(n+1, sizeof(char)); 36760786Sps 36860786Sps /* 36960786Sps * Now copy the string, expanding any "%" or "#". 37060786Sps */ 37160786Sps to = e; 37260786Sps for (fr = s; *fr != '\0'; fr++) 37360786Sps { 37460786Sps switch (*fr) 37560786Sps { 37660786Sps case '%': 37760786Sps case '#': 37860786Sps if (fr > s && fr[-1] == *fr) 37960786Sps { 38060786Sps *to++ = *fr; 38160786Sps } else if (fr[1] != *fr) 38260786Sps { 38360786Sps ifile = fchar_ifile(*fr); 38460786Sps if (ifile == NULL_IFILE) 38560786Sps *to++ = *fr; 38660786Sps else 38760786Sps { 38860786Sps strcpy(to, get_filename(ifile)); 38960786Sps to += strlen(to); 39060786Sps } 39160786Sps } 39260786Sps break; 39360786Sps default: 39460786Sps *to++ = *fr; 39560786Sps break; 39660786Sps } 39760786Sps } 39860786Sps *to = '\0'; 39960786Sps return (e); 40060786Sps} 40160786Sps 402221715Sdelphij 40360786Sps#if TAB_COMPLETE_FILENAME 40460786Sps 40560786Sps/* 40660786Sps * Return a blank-separated list of filenames which "complete" 40760786Sps * the given string. 40860786Sps */ 40960786Sps public char * 41060786Spsfcomplete(s) 41160786Sps char *s; 41260786Sps{ 41360786Sps char *fpat; 414128345Stjr char *qs; 41560786Sps 41660786Sps if (secure) 41760786Sps return (NULL); 41860786Sps /* 41960786Sps * Complete the filename "s" by globbing "s*". 42060786Sps */ 42160786Sps#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC) 42260786Sps /* 42360786Sps * But in DOS, we have to glob "s*.*". 42460786Sps * But if the final component of the filename already has 42560786Sps * a dot in it, just do "s*". 42660786Sps * (Thus, "FILE" is globbed as "FILE*.*", 42760786Sps * but "FILE.A" is globbed as "FILE.A*"). 42860786Sps */ 42960786Sps { 43060786Sps char *slash; 431161475Sdelphij int len; 43260786Sps for (slash = s+strlen(s)-1; slash > s; slash--) 43360786Sps if (*slash == *PATHNAME_SEP || *slash == '/') 43460786Sps break; 435161475Sdelphij len = strlen(s) + 4; 436161475Sdelphij fpat = (char *) ecalloc(len, sizeof(char)); 43760786Sps if (strchr(slash, '.') == NULL) 438161475Sdelphij SNPRINTF1(fpat, len, "%s*.*", s); 43960786Sps else 440161475Sdelphij SNPRINTF1(fpat, len, "%s*", s); 44160786Sps } 44260786Sps#else 443161475Sdelphij { 444161475Sdelphij int len = strlen(s) + 2; 445161475Sdelphij fpat = (char *) ecalloc(len, sizeof(char)); 446161475Sdelphij SNPRINTF1(fpat, len, "%s*", s); 447161475Sdelphij } 44860786Sps#endif 449128345Stjr qs = lglob(fpat); 450128345Stjr s = shell_unquote(qs); 45160786Sps if (strcmp(s,fpat) == 0) 45260786Sps { 45360786Sps /* 45460786Sps * The filename didn't expand. 45560786Sps */ 456128345Stjr free(qs); 457128345Stjr qs = NULL; 45860786Sps } 459128345Stjr free(s); 46060786Sps free(fpat); 461128345Stjr return (qs); 46260786Sps} 46360786Sps#endif 46460786Sps 46560786Sps/* 46660786Sps * Try to determine if a file is "binary". 46760786Sps * This is just a guess, and we need not try too hard to make it accurate. 46860786Sps */ 46960786Sps public int 47060786Spsbin_file(f) 47160786Sps int f; 47260786Sps{ 47360786Sps int n; 474170256Sdelphij int bin_count = 0; 475191930Sdelphij char data[256]; 476191930Sdelphij char* p; 477191930Sdelphij char* pend; 47860786Sps 47960786Sps if (!seekable(f)) 48060786Sps return (0); 481173682Sdelphij if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK) 48260786Sps return (0); 48360786Sps n = read(f, data, sizeof(data)); 484191930Sdelphij pend = &data[n]; 485191930Sdelphij for (p = data; p < pend; ) 486170256Sdelphij { 487191930Sdelphij LWCHAR c = step_char(&p, +1, pend); 488172468Sdelphij if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) 489170256Sdelphij { 490191930Sdelphij do { 491191930Sdelphij c = step_char(&p, +1, pend); 492191930Sdelphij } while (p < pend && is_ansi_middle(c)); 493170256Sdelphij } else if (binary_char(c)) 494170256Sdelphij bin_count++; 495170256Sdelphij } 496170256Sdelphij /* 497170256Sdelphij * Call it a binary file if there are more than 5 binary characters 498170256Sdelphij * in the first 256 bytes of the file. 499170256Sdelphij */ 500170256Sdelphij return (bin_count > 5); 50160786Sps} 50260786Sps 50360786Sps/* 50460786Sps * Try to determine the size of a file by seeking to the end. 50560786Sps */ 50660786Sps static POSITION 50760786Spsseek_filesize(f) 50860786Sps int f; 50960786Sps{ 51060786Sps off_t spos; 51160786Sps 512173682Sdelphij spos = lseek(f, (off_t)0, SEEK_END); 51360786Sps if (spos == BAD_LSEEK) 51460786Sps return (NULL_POSITION); 51560786Sps return ((POSITION) spos); 51660786Sps} 51760786Sps 51860786Sps/* 51960786Sps * Read a string from a file. 52060786Sps * Return a pointer to the string in memory. 52160786Sps */ 52260786Sps static char * 52360786Spsreadfd(fd) 52460786Sps FILE *fd; 52560786Sps{ 52660786Sps int len; 52760786Sps int ch; 52860786Sps char *buf; 52960786Sps char *p; 53060786Sps 53160786Sps /* 53260786Sps * Make a guess about how many chars in the string 53360786Sps * and allocate a buffer to hold it. 53460786Sps */ 53560786Sps len = 100; 53660786Sps buf = (char *) ecalloc(len, sizeof(char)); 53760786Sps for (p = buf; ; p++) 53860786Sps { 53960786Sps if ((ch = getc(fd)) == '\n' || ch == EOF) 54060786Sps break; 54160786Sps if (p - buf >= len-1) 54260786Sps { 54360786Sps /* 54460786Sps * The string is too big to fit in the buffer we have. 54560786Sps * Allocate a new buffer, twice as big. 54660786Sps */ 54760786Sps len *= 2; 54860786Sps *p = '\0'; 54960786Sps p = (char *) ecalloc(len, sizeof(char)); 55060786Sps strcpy(p, buf); 55160786Sps free(buf); 55260786Sps buf = p; 55360786Sps p = buf + strlen(buf); 55460786Sps } 55560786Sps *p = ch; 55660786Sps } 55760786Sps *p = '\0'; 55860786Sps return (buf); 55960786Sps} 56060786Sps 56160786Sps 56260786Sps 56360786Sps#if HAVE_POPEN 56460786Sps 56560786SpsFILE *popen(); 56660786Sps 56760786Sps/* 56860786Sps * Execute a shell command. 56960786Sps * Return a pointer to a pipe connected to the shell command's standard output. 57060786Sps */ 57160786Sps static FILE * 57260786Spsshellcmd(cmd) 57360786Sps char *cmd; 57460786Sps{ 57560786Sps FILE *fd; 57660786Sps 57760786Sps#if HAVE_SHELL 57860786Sps char *shell; 57960786Sps 58060786Sps shell = lgetenv("SHELL"); 58160786Sps if (shell != NULL && *shell != '\0') 58260786Sps { 58360786Sps char *scmd; 58460786Sps char *esccmd; 58560786Sps 58660786Sps /* 587128345Stjr * Read the output of <$SHELL -c cmd>. 588128345Stjr * Escape any metacharacters in the command. 58960786Sps */ 590128345Stjr esccmd = shell_quote(cmd); 591128345Stjr if (esccmd == NULL) 59260786Sps { 593128345Stjr fd = popen(cmd, "r"); 59460786Sps } else 59560786Sps { 596161475Sdelphij int len = strlen(shell) + strlen(esccmd) + 5; 597161475Sdelphij scmd = (char *) ecalloc(len, sizeof(char)); 598161475Sdelphij SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd); 59960786Sps free(esccmd); 600128345Stjr fd = popen(scmd, "r"); 601128345Stjr free(scmd); 60260786Sps } 60360786Sps } else 60460786Sps#endif 60560786Sps { 60660786Sps fd = popen(cmd, "r"); 60760786Sps } 608128345Stjr /* 609128345Stjr * Redirection in `popen' might have messed with the 610128345Stjr * standard devices. Restore binary input mode. 611128345Stjr */ 612128345Stjr SET_BINARY(0); 61360786Sps return (fd); 61460786Sps} 61560786Sps 61660786Sps#endif /* HAVE_POPEN */ 61760786Sps 61860786Sps 61960786Sps/* 62060786Sps * Expand a filename, doing any system-specific metacharacter substitutions. 62160786Sps */ 62260786Sps public char * 62360786Spslglob(filename) 62460786Sps char *filename; 62560786Sps{ 62660786Sps char *gfilename; 62760786Sps char *ofilename; 62860786Sps 62960786Sps ofilename = fexpand(filename); 63060786Sps if (secure) 63160786Sps return (ofilename); 632128345Stjr filename = shell_unquote(ofilename); 63360786Sps 63460786Sps#ifdef DECL_GLOB_LIST 63560786Sps{ 63660786Sps /* 63760786Sps * The globbing function returns a list of names. 63860786Sps */ 63960786Sps int length; 64060786Sps char *p; 641128345Stjr char *qfilename; 64260786Sps DECL_GLOB_LIST(list) 64360786Sps 64460786Sps GLOB_LIST(filename, list); 64560786Sps if (GLOB_LIST_FAILED(list)) 64660786Sps { 64760786Sps free(filename); 64860786Sps return (ofilename); 64960786Sps } 65060786Sps length = 1; /* Room for trailing null byte */ 65160786Sps for (SCAN_GLOB_LIST(list, p)) 65260786Sps { 65360786Sps INIT_GLOB_LIST(list, p); 654128345Stjr qfilename = shell_quote(p); 655128345Stjr if (qfilename != NULL) 656128345Stjr { 657128345Stjr length += strlen(qfilename) + 1; 658128345Stjr free(qfilename); 659128345Stjr } 66060786Sps } 66160786Sps gfilename = (char *) ecalloc(length, sizeof(char)); 66260786Sps for (SCAN_GLOB_LIST(list, p)) 66360786Sps { 66460786Sps INIT_GLOB_LIST(list, p); 665128345Stjr qfilename = shell_quote(p); 666128345Stjr if (qfilename != NULL) 667128345Stjr { 668128345Stjr sprintf(gfilename + strlen(gfilename), "%s ", qfilename); 669128345Stjr free(qfilename); 670128345Stjr } 67160786Sps } 67260786Sps /* 67360786Sps * Overwrite the final trailing space with a null terminator. 67460786Sps */ 67560786Sps *--p = '\0'; 67660786Sps GLOB_LIST_DONE(list); 67760786Sps} 67860786Sps#else 67960786Sps#ifdef DECL_GLOB_NAME 68060786Sps{ 68160786Sps /* 68260786Sps * The globbing function returns a single name, and 68360786Sps * is called multiple times to walk thru all names. 68460786Sps */ 68560786Sps register char *p; 68660786Sps register int len; 68760786Sps register int n; 688128345Stjr char *pathname; 689128345Stjr char *qpathname; 69060786Sps DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) 69160786Sps 69260786Sps GLOB_FIRST_NAME(filename, &fnd, handle); 69360786Sps if (GLOB_FIRST_FAILED(handle)) 69460786Sps { 69560786Sps free(filename); 69660786Sps return (ofilename); 69760786Sps } 69860786Sps 69960786Sps _splitpath(filename, drive, dir, fname, ext); 70060786Sps len = 100; 70160786Sps gfilename = (char *) ecalloc(len, sizeof(char)); 70260786Sps p = gfilename; 70360786Sps do { 70460786Sps n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1; 705128345Stjr pathname = (char *) ecalloc(n, sizeof(char)); 706161475Sdelphij SNPRINTF3(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME); 707128345Stjr qpathname = shell_quote(pathname); 708128345Stjr free(pathname); 709128345Stjr if (qpathname != NULL) 71060786Sps { 711128345Stjr n = strlen(qpathname); 712128345Stjr while (p - gfilename + n + 2 >= len) 713128345Stjr { 714128345Stjr /* 715128345Stjr * No room in current buffer. 716128345Stjr * Allocate a bigger one. 717128345Stjr */ 718128345Stjr len *= 2; 719128345Stjr *p = '\0'; 720128345Stjr p = (char *) ecalloc(len, sizeof(char)); 721128345Stjr strcpy(p, gfilename); 722128345Stjr free(gfilename); 723128345Stjr gfilename = p; 724128345Stjr p = gfilename + strlen(gfilename); 725128345Stjr } 726128345Stjr strcpy(p, qpathname); 727128345Stjr free(qpathname); 728128345Stjr p += n; 729128345Stjr *p++ = ' '; 73060786Sps } 73160786Sps } while (GLOB_NEXT_NAME(handle, &fnd) == 0); 73260786Sps 73360786Sps /* 73460786Sps * Overwrite the final trailing space with a null terminator. 73560786Sps */ 73660786Sps *--p = '\0'; 73760786Sps GLOB_NAME_DONE(handle); 73860786Sps} 73960786Sps#else 74060786Sps#if HAVE_POPEN 74160786Sps{ 74260786Sps /* 74360786Sps * We get the shell to glob the filename for us by passing 74460786Sps * an "echo" command to the shell and reading its output. 74560786Sps */ 74660786Sps FILE *fd; 74760786Sps char *s; 74860786Sps char *lessecho; 74960786Sps char *cmd; 750128345Stjr char *esc; 751161475Sdelphij int len; 75260786Sps 753128345Stjr esc = get_meta_escape(); 754128345Stjr if (strlen(esc) == 0) 755128345Stjr esc = "-"; 756128345Stjr esc = shell_quote(esc); 757128345Stjr if (esc == NULL) 75860786Sps { 75960786Sps free(filename); 76060786Sps return (ofilename); 76160786Sps } 762128345Stjr lessecho = lgetenv("LESSECHO"); 763128345Stjr if (lessecho == NULL || *lessecho == '\0') 764128345Stjr lessecho = "lessecho"; 76560786Sps /* 76660786Sps * Invoke lessecho, and read its output (a globbed list of filenames). 76760786Sps */ 768161475Sdelphij len = strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24; 769161475Sdelphij cmd = (char *) ecalloc(len, sizeof(char)); 770161475Sdelphij SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc); 771128345Stjr free(esc); 772128345Stjr for (s = metachars(); *s != '\0'; s++) 773128345Stjr sprintf(cmd + strlen(cmd), "-n0x%x ", *s); 774128345Stjr sprintf(cmd + strlen(cmd), "-- %s", ofilename); 77560786Sps fd = shellcmd(cmd); 77660786Sps free(cmd); 77760786Sps if (fd == NULL) 77860786Sps { 77960786Sps /* 78060786Sps * Cannot create the pipe. 78160786Sps * Just return the original (fexpanded) filename. 78260786Sps */ 78360786Sps free(filename); 78460786Sps return (ofilename); 78560786Sps } 78660786Sps gfilename = readfd(fd); 78760786Sps pclose(fd); 78860786Sps if (*gfilename == '\0') 78960786Sps { 79060786Sps free(gfilename); 79160786Sps free(filename); 79260786Sps return (ofilename); 79360786Sps } 79460786Sps} 79560786Sps#else 79660786Sps /* 79760786Sps * No globbing functions at all. Just use the fexpanded filename. 79860786Sps */ 79960786Sps gfilename = save(filename); 80060786Sps#endif 80160786Sps#endif 80260786Sps#endif 80360786Sps free(filename); 80460786Sps free(ofilename); 80560786Sps return (gfilename); 80660786Sps} 80760786Sps 80860786Sps/* 809240121Sdelphij * Return number of %s escapes in a string. 810240121Sdelphij * Return a large number if there are any other % escapes besides %s. 811240121Sdelphij */ 812240121Sdelphij static int 813240121Sdelphijnum_pct_s(lessopen) 814240121Sdelphij char *lessopen; 815240121Sdelphij{ 816240121Sdelphij int num; 817240121Sdelphij 818240121Sdelphij for (num = 0;; num++) 819240121Sdelphij { 820240121Sdelphij lessopen = strchr(lessopen, '%'); 821240121Sdelphij if (lessopen == NULL) 822240121Sdelphij break; 823240121Sdelphij if (*++lessopen != 's') 824240121Sdelphij return (999); 825240121Sdelphij } 826240121Sdelphij return (num); 827240121Sdelphij} 828240121Sdelphij 829240121Sdelphij/* 83060786Sps * See if we should open a "replacement file" 83160786Sps * instead of the file we're about to open. 83260786Sps */ 83360786Sps public char * 83460786Spsopen_altfile(filename, pf, pfd) 83560786Sps char *filename; 83660786Sps int *pf; 83760786Sps void **pfd; 83860786Sps{ 83960786Sps#if !HAVE_POPEN 84060786Sps return (NULL); 84160786Sps#else 84260786Sps char *lessopen; 84360786Sps char *cmd; 844161475Sdelphij int len; 84560786Sps FILE *fd; 84660786Sps#if HAVE_FILENO 84760786Sps int returnfd = 0; 84860786Sps#endif 84960786Sps 850128345Stjr if (!use_lessopen || secure) 85160786Sps return (NULL); 85260786Sps ch_ungetchar(-1); 85360786Sps if ((lessopen = lgetenv("LESSOPEN")) == NULL) 85460786Sps return (NULL); 855240121Sdelphij while (*lessopen == '|') 85660786Sps { 85760786Sps /* 85860786Sps * If LESSOPEN starts with a |, it indicates 85960786Sps * a "pipe preprocessor". 86060786Sps */ 861191930Sdelphij#if !HAVE_FILENO 862191930Sdelphij error("LESSOPEN pipe is not supported", NULL_PARG); 863191930Sdelphij return (NULL); 864191930Sdelphij#else 86560786Sps lessopen++; 866240121Sdelphij returnfd++; 86760786Sps#endif 86860786Sps } 869195941Sdelphij if (*lessopen == '-') { 870195941Sdelphij /* 871195941Sdelphij * Lessopen preprocessor will accept "-" as a filename. 872195941Sdelphij */ 873195941Sdelphij lessopen++; 874195941Sdelphij } else { 875195941Sdelphij if (strcmp(filename, "-") == 0) 876195941Sdelphij return (NULL); 877195941Sdelphij } 878240121Sdelphij if (num_pct_s(lessopen) > 1) 879240121Sdelphij { 880240121Sdelphij error("Invalid LESSOPEN variable", NULL_PARG); 881240121Sdelphij return (NULL); 882240121Sdelphij } 88360786Sps 884161475Sdelphij len = strlen(lessopen) + strlen(filename) + 2; 885161475Sdelphij cmd = (char *) ecalloc(len, sizeof(char)); 886161475Sdelphij SNPRINTF1(cmd, len, lessopen, filename); 88760786Sps fd = shellcmd(cmd); 88860786Sps free(cmd); 88960786Sps if (fd == NULL) 89060786Sps { 89160786Sps /* 89260786Sps * Cannot create the pipe. 89360786Sps */ 89460786Sps return (NULL); 89560786Sps } 89660786Sps#if HAVE_FILENO 89760786Sps if (returnfd) 89860786Sps { 89960786Sps int f; 90060786Sps char c; 90160786Sps 90260786Sps /* 90360786Sps * Read one char to see if the pipe will produce any data. 90460786Sps * If it does, push the char back on the pipe. 90560786Sps */ 90660786Sps f = fileno(fd); 90760786Sps SET_BINARY(f); 90860786Sps if (read(f, &c, 1) != 1) 90960786Sps { 91060786Sps /* 911240121Sdelphij * Pipe is empty. 912240121Sdelphij * If more than 1 pipe char was specified, 913240121Sdelphij * the exit status tells whether the file itself 914240121Sdelphij * is empty, or if there is no alt file. 915240121Sdelphij * If only one pipe char, just assume no alt file. 91660786Sps */ 917240121Sdelphij int status = pclose(fd); 918240121Sdelphij if (returnfd > 1 && status == 0) { 919240121Sdelphij *pfd = NULL; 920240121Sdelphij *pf = -1; 921240121Sdelphij return (save(FAKE_EMPTYFILE)); 922240121Sdelphij } 92360786Sps return (NULL); 92460786Sps } 92560786Sps ch_ungetchar(c); 92660786Sps *pfd = (void *) fd; 92760786Sps *pf = f; 92860786Sps return (save("-")); 92960786Sps } 93060786Sps#endif 931128345Stjr cmd = readfd(fd); 93260786Sps pclose(fd); 933128345Stjr if (*cmd == '\0') 93460786Sps /* 93560786Sps * Pipe is empty. This means there is no alt file. 93660786Sps */ 93760786Sps return (NULL); 938128345Stjr return (cmd); 93960786Sps#endif /* HAVE_POPEN */ 94060786Sps} 94160786Sps 94260786Sps/* 94360786Sps * Close a replacement file. 94460786Sps */ 94560786Sps public void 94660786Spsclose_altfile(altfilename, filename, pipefd) 94760786Sps char *altfilename; 94860786Sps char *filename; 94960786Sps void *pipefd; 95060786Sps{ 95160786Sps#if HAVE_POPEN 95260786Sps char *lessclose; 95360786Sps FILE *fd; 95460786Sps char *cmd; 955161475Sdelphij int len; 95660786Sps 95760786Sps if (secure) 95860786Sps return; 95960786Sps if (pipefd != NULL) 96089019Sps { 96189019Sps#if OS2 96289019Sps /* 96389019Sps * The pclose function of OS/2 emx sometimes fails. 96489019Sps * Send SIGINT to the piped process before closing it. 96589019Sps */ 96689019Sps kill(((FILE*)pipefd)->_pid, SIGINT); 96789019Sps#endif 96860786Sps pclose((FILE*) pipefd); 96989019Sps } 97060786Sps if ((lessclose = lgetenv("LESSCLOSE")) == NULL) 97160786Sps return; 972240121Sdelphij if (num_pct_s(lessclose) > 2) 973240121Sdelphij { 974240121Sdelphij error("Invalid LESSCLOSE variable"); 975240121Sdelphij return; 976240121Sdelphij } 977161475Sdelphij len = strlen(lessclose) + strlen(filename) + strlen(altfilename) + 2; 978161475Sdelphij cmd = (char *) ecalloc(len, sizeof(char)); 979161475Sdelphij SNPRINTF2(cmd, len, lessclose, filename, altfilename); 98060786Sps fd = shellcmd(cmd); 98160786Sps free(cmd); 98260786Sps if (fd != NULL) 98360786Sps pclose(fd); 98460786Sps#endif 98560786Sps} 98660786Sps 98760786Sps/* 98860786Sps * Is the specified file a directory? 98960786Sps */ 99060786Sps public int 99160786Spsis_dir(filename) 99260786Sps char *filename; 99360786Sps{ 99460786Sps int isdir = 0; 99560786Sps 996128345Stjr filename = shell_unquote(filename); 99760786Sps#if HAVE_STAT 99860786Sps{ 99960786Sps int r; 100060786Sps struct stat statbuf; 100160786Sps 100260786Sps r = stat(filename, &statbuf); 100360786Sps isdir = (r >= 0 && S_ISDIR(statbuf.st_mode)); 100460786Sps} 100560786Sps#else 100660786Sps#ifdef _OSK 100760786Sps{ 100860786Sps register int f; 100960786Sps 101060786Sps f = open(filename, S_IREAD | S_IFDIR); 101160786Sps if (f >= 0) 101260786Sps close(f); 101360786Sps isdir = (f >= 0); 101460786Sps} 101560786Sps#endif 101660786Sps#endif 101760786Sps free(filename); 101860786Sps return (isdir); 101960786Sps} 102060786Sps 102160786Sps/* 102260786Sps * Returns NULL if the file can be opened and 102360786Sps * is an ordinary file, otherwise an error message 102460786Sps * (if it cannot be opened or is a directory, etc.) 102560786Sps */ 102660786Sps public char * 102760786Spsbad_file(filename) 102860786Sps char *filename; 102960786Sps{ 103060786Sps register char *m = NULL; 103160786Sps 1032128345Stjr filename = shell_unquote(filename); 1033170256Sdelphij if (!force_open && is_dir(filename)) 103460786Sps { 1035128345Stjr static char is_a_dir[] = " is a directory"; 103660786Sps 1037128345Stjr m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir), 103860786Sps sizeof(char)); 103960786Sps strcpy(m, filename); 1040128345Stjr strcat(m, is_a_dir); 104160786Sps } else 104260786Sps { 104360786Sps#if HAVE_STAT 104460786Sps int r; 104560786Sps struct stat statbuf; 104660786Sps 104760786Sps r = stat(filename, &statbuf); 104860786Sps if (r < 0) 104960786Sps { 105060786Sps m = errno_message(filename); 105160786Sps } else if (force_open) 105260786Sps { 105360786Sps m = NULL; 105460786Sps } else if (!S_ISREG(statbuf.st_mode)) 105560786Sps { 105660786Sps static char not_reg[] = " is not a regular file (use -f to see it)"; 105760786Sps m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 105860786Sps sizeof(char)); 105960786Sps strcpy(m, filename); 106060786Sps strcat(m, not_reg); 106160786Sps } 106260786Sps#endif 106360786Sps } 106460786Sps free(filename); 106560786Sps return (m); 106660786Sps} 106760786Sps 106860786Sps/* 106960786Sps * Return the size of a file, as cheaply as possible. 107060786Sps * In Unix, we can stat the file. 107160786Sps */ 107260786Sps public POSITION 107360786Spsfilesize(f) 107460786Sps int f; 107560786Sps{ 107660786Sps#if HAVE_STAT 107760786Sps struct stat statbuf; 107860786Sps 107960786Sps if (fstat(f, &statbuf) >= 0) 108060786Sps return ((POSITION) statbuf.st_size); 108160786Sps#else 108260786Sps#ifdef _OSK 108360786Sps long size; 108460786Sps 108560786Sps if ((size = (long) _gs_size(f)) >= 0) 108660786Sps return ((POSITION) size); 108760786Sps#endif 108860786Sps#endif 108960786Sps return (seek_filesize(f)); 109060786Sps} 109160786Sps 1092128345Stjr/* 1093128345Stjr * 1094128345Stjr */ 1095128345Stjr public char * 1096128345Stjrshell_coption() 1097128345Stjr{ 1098128345Stjr return ("-c"); 1099128345Stjr} 1100221715Sdelphij 1101221715Sdelphij/* 1102221715Sdelphij * Return last component of a pathname. 1103221715Sdelphij */ 1104221715Sdelphij public char * 1105221715Sdelphijlast_component(name) 1106221715Sdelphij char *name; 1107221715Sdelphij{ 1108221715Sdelphij char *slash; 1109221715Sdelphij 1110221715Sdelphij for (slash = name + strlen(name); slash > name; ) 1111221715Sdelphij { 1112221715Sdelphij --slash; 1113221715Sdelphij if (*slash == *PATHNAME_SEP || *slash == '/') 1114221715Sdelphij return (slash + 1); 1115221715Sdelphij } 1116221715Sdelphij return (name); 1117221715Sdelphij} 1118221715Sdelphij 1119