1238730Sdelphij/* 2330571Sdelphij * Copyright (C) 1984-2017 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 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 3560786Sps 3660786Sps#if HAVE_STAT 3760786Sps#include <sys/stat.h> 3860786Sps#ifndef S_ISDIR 3960786Sps#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 4060786Sps#endif 4160786Sps#ifndef S_ISREG 4260786Sps#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 4360786Sps#endif 4460786Sps#endif 4560786Sps 4660786Spsextern int force_open; 4760786Spsextern int secure; 48128345Stjrextern int use_lessopen; 49170256Sdelphijextern int ctldisp; 50191930Sdelphijextern int utf_mode; 5160786Spsextern IFILE curr_ifile; 5260786Spsextern IFILE old_ifile; 5360786Sps#if SPACES_IN_FILENAMES 5460786Spsextern char openquote; 5560786Spsextern char closequote; 5660786Sps#endif 5760786Sps 5860786Sps/* 5960786Sps * Remove quotes around a filename. 6060786Sps */ 6160786Sps public char * 62128345Stjrshell_unquote(str) 6360786Sps char *str; 6460786Sps{ 6560786Sps char *name; 6660786Sps char *p; 6760786Sps 68128345Stjr name = p = (char *) ecalloc(strlen(str)+1, sizeof(char)); 69128345Stjr if (*str == openquote) 70128345Stjr { 71128345Stjr str++; 72128345Stjr while (*str != '\0') 73128345Stjr { 74128345Stjr if (*str == closequote) 75128345Stjr { 76128345Stjr if (str[1] != closequote) 77128345Stjr break; 78128345Stjr str++; 79128345Stjr } 80128345Stjr *p++ = *str++; 81128345Stjr } 82128345Stjr } else 83128345Stjr { 84128345Stjr char *esc = get_meta_escape(); 85294286Sdelphij int esclen = (int) strlen(esc); 86128345Stjr while (*str != '\0') 87128345Stjr { 88128345Stjr if (esclen > 0 && strncmp(str, esc, esclen) == 0) 89128345Stjr str += esclen; 90128345Stjr *p++ = *str++; 91128345Stjr } 92128345Stjr } 93128345Stjr *p = '\0'; 9460786Sps return (name); 9560786Sps} 9660786Sps 9760786Sps/* 98128345Stjr * Get the shell's escape character. 99128345Stjr */ 100128345Stjr public char * 101128345Stjrget_meta_escape() 102128345Stjr{ 103128345Stjr char *s; 104128345Stjr 105128345Stjr s = lgetenv("LESSMETAESCAPE"); 106128345Stjr if (s == NULL) 107128345Stjr s = DEF_METAESCAPE; 108128345Stjr return (s); 109128345Stjr} 110128345Stjr 111128345Stjr/* 112128345Stjr * Get the characters which the shell considers to be "metacharacters". 113128345Stjr */ 114128345Stjr static char * 115128345Stjrmetachars() 116128345Stjr{ 117128345Stjr static char *mchars = NULL; 118128345Stjr 119128345Stjr if (mchars == NULL) 120128345Stjr { 121128345Stjr mchars = lgetenv("LESSMETACHARS"); 122128345Stjr if (mchars == NULL) 123128345Stjr mchars = DEF_METACHARS; 124128345Stjr } 125128345Stjr return (mchars); 126128345Stjr} 127128345Stjr 128128345Stjr/* 129128345Stjr * Is this a shell metacharacter? 130128345Stjr */ 131128345Stjr static int 132128345Stjrmetachar(c) 133128345Stjr char c; 134128345Stjr{ 135128345Stjr return (strchr(metachars(), c) != NULL); 136128345Stjr} 137128345Stjr 138128345Stjr/* 139128345Stjr * Insert a backslash before each metacharacter in a string. 140128345Stjr */ 141128345Stjr public char * 142128345Stjrshell_quote(s) 143128345Stjr char *s; 144128345Stjr{ 145128345Stjr char *p; 146128345Stjr char *newstr; 147128345Stjr int len; 148128345Stjr char *esc = get_meta_escape(); 149294286Sdelphij int esclen = (int) strlen(esc); 150128345Stjr int use_quotes = 0; 151128345Stjr int have_quotes = 0; 152128345Stjr 153128345Stjr /* 154128345Stjr * Determine how big a string we need to allocate. 155128345Stjr */ 156128345Stjr len = 1; /* Trailing null byte */ 157128345Stjr for (p = s; *p != '\0'; p++) 158128345Stjr { 159128345Stjr len++; 160128345Stjr if (*p == openquote || *p == closequote) 161128345Stjr have_quotes = 1; 162128345Stjr if (metachar(*p)) 163128345Stjr { 164128345Stjr if (esclen == 0) 165128345Stjr { 166128345Stjr /* 167128345Stjr * We've got a metachar, but this shell 168128345Stjr * doesn't support escape chars. Use quotes. 169128345Stjr */ 170128345Stjr use_quotes = 1; 171128345Stjr } else 172128345Stjr { 173128345Stjr /* 174128345Stjr * Allow space for the escape char. 175128345Stjr */ 176128345Stjr len += esclen; 177128345Stjr } 178128345Stjr } 179128345Stjr } 180128345Stjr if (use_quotes) 181128345Stjr { 182128345Stjr if (have_quotes) 183128345Stjr /* 184128345Stjr * We can't quote a string that contains quotes. 185128345Stjr */ 186128345Stjr return (NULL); 187294286Sdelphij len = (int) strlen(s) + 3; 188128345Stjr } 189128345Stjr /* 190128345Stjr * Allocate and construct the new string. 191128345Stjr */ 192128345Stjr newstr = p = (char *) ecalloc(len, sizeof(char)); 193128345Stjr if (use_quotes) 194128345Stjr { 195161475Sdelphij SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote); 196128345Stjr } else 197128345Stjr { 198128345Stjr while (*s != '\0') 199128345Stjr { 200128345Stjr if (metachar(*s)) 201128345Stjr { 202128345Stjr /* 203128345Stjr * Add the escape char. 204128345Stjr */ 205128345Stjr strcpy(p, esc); 206128345Stjr p += esclen; 207128345Stjr } 208128345Stjr *p++ = *s++; 209128345Stjr } 210128345Stjr *p = '\0'; 211128345Stjr } 212128345Stjr return (newstr); 213128345Stjr} 214128345Stjr 215128345Stjr/* 21660786Sps * Return a pathname that points to a specified file in a specified directory. 21760786Sps * Return NULL if the file does not exist in the directory. 21860786Sps */ 21960786Sps static char * 22060786Spsdirfile(dirname, filename) 22160786Sps char *dirname; 22260786Sps char *filename; 22360786Sps{ 22460786Sps char *pathname; 225161475Sdelphij int len; 22660786Sps int f; 22760786Sps 22860786Sps if (dirname == NULL || *dirname == '\0') 22960786Sps return (NULL); 23060786Sps /* 23160786Sps * Construct the full pathname. 23260786Sps */ 233294286Sdelphij len = (int) (strlen(dirname) + strlen(filename) + 2); 234161475Sdelphij pathname = (char *) calloc(len, sizeof(char)); 23560786Sps if (pathname == NULL) 23660786Sps return (NULL); 237161475Sdelphij SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename); 23860786Sps /* 23960786Sps * Make sure the file exists. 24060786Sps */ 241330571Sdelphij f = open(pathname, OPEN_READ); 24260786Sps if (f < 0) 24360786Sps { 24460786Sps free(pathname); 24560786Sps pathname = NULL; 24660786Sps } else 24760786Sps { 24860786Sps close(f); 24960786Sps } 25060786Sps return (pathname); 25160786Sps} 25260786Sps 25360786Sps/* 25460786Sps * Return the full pathname of the given file in the "home directory". 25560786Sps */ 25660786Sps public char * 25760786Spshomefile(filename) 25860786Sps char *filename; 25960786Sps{ 260330571Sdelphij char *pathname; 26160786Sps 26260786Sps /* 26360786Sps * Try $HOME/filename. 26460786Sps */ 26560786Sps pathname = dirfile(lgetenv("HOME"), filename); 26660786Sps if (pathname != NULL) 26760786Sps return (pathname); 26860786Sps#if OS2 26960786Sps /* 27060786Sps * Try $INIT/filename. 27160786Sps */ 27260786Sps pathname = dirfile(lgetenv("INIT"), filename); 27360786Sps if (pathname != NULL) 27460786Sps return (pathname); 27560786Sps#endif 27660786Sps#if MSDOS_COMPILER || OS2 27760786Sps /* 27860786Sps * Look for the file anywhere on search path. 27960786Sps */ 28060786Sps pathname = (char *) calloc(_MAX_PATH, sizeof(char)); 28160786Sps#if MSDOS_COMPILER==DJGPPC 28260786Sps { 28360786Sps char *res = searchpath(filename); 28460786Sps if (res == 0) 28560786Sps *pathname = '\0'; 28660786Sps else 28760786Sps strcpy(pathname, res); 28860786Sps } 28960786Sps#else 29060786Sps _searchenv(filename, "PATH", pathname); 29160786Sps#endif 29260786Sps if (*pathname != '\0') 29360786Sps return (pathname); 29460786Sps free(pathname); 29560786Sps#endif 29660786Sps return (NULL); 29760786Sps} 29860786Sps 29960786Sps/* 30060786Sps * Expand a string, substituting any "%" with the current filename, 30160786Sps * and any "#" with the previous filename. 30260786Sps * But a string of N "%"s is just replaced with N-1 "%"s. 30360786Sps * Likewise for a string of N "#"s. 30460786Sps * {{ This is a lot of work just to support % and #. }} 30560786Sps */ 30660786Sps public char * 30760786Spsfexpand(s) 30860786Sps char *s; 30960786Sps{ 310330571Sdelphij char *fr, *to; 311330571Sdelphij int n; 312330571Sdelphij char *e; 31360786Sps IFILE ifile; 31460786Sps 31560786Sps#define fchar_ifile(c) \ 31660786Sps ((c) == '%' ? curr_ifile : \ 31760786Sps (c) == '#' ? old_ifile : NULL_IFILE) 31860786Sps 31960786Sps /* 32060786Sps * Make one pass to see how big a buffer we 32160786Sps * need to allocate for the expanded string. 32260786Sps */ 32360786Sps n = 0; 32460786Sps for (fr = s; *fr != '\0'; fr++) 32560786Sps { 32660786Sps switch (*fr) 32760786Sps { 32860786Sps case '%': 32960786Sps case '#': 33060786Sps if (fr > s && fr[-1] == *fr) 33160786Sps { 33260786Sps /* 33360786Sps * Second (or later) char in a string 33460786Sps * of identical chars. Treat as normal. 33560786Sps */ 33660786Sps n++; 33760786Sps } else if (fr[1] != *fr) 33860786Sps { 33960786Sps /* 34060786Sps * Single char (not repeated). Treat specially. 34160786Sps */ 34260786Sps ifile = fchar_ifile(*fr); 34360786Sps if (ifile == NULL_IFILE) 34460786Sps n++; 34560786Sps else 346294286Sdelphij n += (int) strlen(get_filename(ifile)); 34760786Sps } 34860786Sps /* 34960786Sps * Else it is the first char in a string of 35060786Sps * identical chars. Just discard it. 35160786Sps */ 35260786Sps break; 35360786Sps default: 35460786Sps n++; 35560786Sps break; 35660786Sps } 35760786Sps } 35860786Sps 35960786Sps e = (char *) ecalloc(n+1, sizeof(char)); 36060786Sps 36160786Sps /* 36260786Sps * Now copy the string, expanding any "%" or "#". 36360786Sps */ 36460786Sps to = e; 36560786Sps for (fr = s; *fr != '\0'; fr++) 36660786Sps { 36760786Sps switch (*fr) 36860786Sps { 36960786Sps case '%': 37060786Sps case '#': 37160786Sps if (fr > s && fr[-1] == *fr) 37260786Sps { 37360786Sps *to++ = *fr; 37460786Sps } else if (fr[1] != *fr) 37560786Sps { 37660786Sps ifile = fchar_ifile(*fr); 37760786Sps if (ifile == NULL_IFILE) 37860786Sps *to++ = *fr; 37960786Sps else 38060786Sps { 38160786Sps strcpy(to, get_filename(ifile)); 38260786Sps to += strlen(to); 38360786Sps } 38460786Sps } 38560786Sps break; 38660786Sps default: 38760786Sps *to++ = *fr; 38860786Sps break; 38960786Sps } 39060786Sps } 39160786Sps *to = '\0'; 39260786Sps return (e); 39360786Sps} 39460786Sps 395221715Sdelphij 39660786Sps#if TAB_COMPLETE_FILENAME 39760786Sps 39860786Sps/* 39960786Sps * Return a blank-separated list of filenames which "complete" 40060786Sps * the given string. 40160786Sps */ 40260786Sps public char * 40360786Spsfcomplete(s) 40460786Sps char *s; 40560786Sps{ 40660786Sps char *fpat; 407128345Stjr char *qs; 40860786Sps 40960786Sps if (secure) 41060786Sps return (NULL); 41160786Sps /* 41260786Sps * Complete the filename "s" by globbing "s*". 41360786Sps */ 41460786Sps#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC) 41560786Sps /* 41660786Sps * But in DOS, we have to glob "s*.*". 41760786Sps * But if the final component of the filename already has 41860786Sps * a dot in it, just do "s*". 41960786Sps * (Thus, "FILE" is globbed as "FILE*.*", 42060786Sps * but "FILE.A" is globbed as "FILE.A*"). 42160786Sps */ 42260786Sps { 42360786Sps char *slash; 424161475Sdelphij int len; 42560786Sps for (slash = s+strlen(s)-1; slash > s; slash--) 42660786Sps if (*slash == *PATHNAME_SEP || *slash == '/') 42760786Sps break; 428294286Sdelphij len = (int) strlen(s) + 4; 429161475Sdelphij fpat = (char *) ecalloc(len, sizeof(char)); 43060786Sps if (strchr(slash, '.') == NULL) 431161475Sdelphij SNPRINTF1(fpat, len, "%s*.*", s); 43260786Sps else 433161475Sdelphij SNPRINTF1(fpat, len, "%s*", s); 43460786Sps } 43560786Sps#else 436161475Sdelphij { 437294286Sdelphij int len = (int) strlen(s) + 2; 438161475Sdelphij fpat = (char *) ecalloc(len, sizeof(char)); 439161475Sdelphij SNPRINTF1(fpat, len, "%s*", s); 440161475Sdelphij } 44160786Sps#endif 442128345Stjr qs = lglob(fpat); 443128345Stjr s = shell_unquote(qs); 44460786Sps if (strcmp(s,fpat) == 0) 44560786Sps { 44660786Sps /* 44760786Sps * The filename didn't expand. 44860786Sps */ 449128345Stjr free(qs); 450128345Stjr qs = NULL; 45160786Sps } 452128345Stjr free(s); 45360786Sps free(fpat); 454128345Stjr return (qs); 45560786Sps} 45660786Sps#endif 45760786Sps 45860786Sps/* 45960786Sps * Try to determine if a file is "binary". 46060786Sps * This is just a guess, and we need not try too hard to make it accurate. 46160786Sps */ 46260786Sps public int 46360786Spsbin_file(f) 46460786Sps int f; 46560786Sps{ 46660786Sps int n; 467170256Sdelphij int bin_count = 0; 468191930Sdelphij char data[256]; 469191930Sdelphij char* p; 470330571Sdelphij char* edata; 47160786Sps 47260786Sps if (!seekable(f)) 47360786Sps return (0); 474173682Sdelphij if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK) 47560786Sps return (0); 47660786Sps n = read(f, data, sizeof(data)); 477294286Sdelphij if (n <= 0) 478294286Sdelphij return (0); 479330571Sdelphij edata = &data[n]; 480330571Sdelphij for (p = data; p < edata; ) 481170256Sdelphij { 482330571Sdelphij if (utf_mode && !is_utf8_well_formed(p, edata-data)) 483170256Sdelphij { 484330571Sdelphij bin_count++; 485330571Sdelphij utf_skip_to_lead(&p, edata); 486330571Sdelphij } else 487330571Sdelphij { 488330571Sdelphij LWCHAR c = step_char(&p, +1, edata); 489294286Sdelphij if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) 490330571Sdelphij skip_ansi(&p, edata); 491330571Sdelphij else if (binary_char(c)) 492294286Sdelphij bin_count++; 493294286Sdelphij } 494170256Sdelphij } 495170256Sdelphij /* 496170256Sdelphij * Call it a binary file if there are more than 5 binary characters 497170256Sdelphij * in the first 256 bytes of the file. 498170256Sdelphij */ 499170256Sdelphij return (bin_count > 5); 50060786Sps} 50160786Sps 50260786Sps/* 50360786Sps * Try to determine the size of a file by seeking to the end. 50460786Sps */ 50560786Sps static POSITION 50660786Spsseek_filesize(f) 50760786Sps int f; 50860786Sps{ 50960786Sps off_t spos; 51060786Sps 511173682Sdelphij spos = lseek(f, (off_t)0, SEEK_END); 51260786Sps if (spos == BAD_LSEEK) 51360786Sps return (NULL_POSITION); 51460786Sps return ((POSITION) spos); 51560786Sps} 51660786Sps 51760786Sps/* 51860786Sps * Read a string from a file. 51960786Sps * Return a pointer to the string in memory. 52060786Sps */ 52160786Sps static char * 52260786Spsreadfd(fd) 52360786Sps FILE *fd; 52460786Sps{ 52560786Sps int len; 52660786Sps int ch; 52760786Sps char *buf; 52860786Sps char *p; 52960786Sps 53060786Sps /* 53160786Sps * Make a guess about how many chars in the string 53260786Sps * and allocate a buffer to hold it. 53360786Sps */ 53460786Sps len = 100; 53560786Sps buf = (char *) ecalloc(len, sizeof(char)); 53660786Sps for (p = buf; ; p++) 53760786Sps { 53860786Sps if ((ch = getc(fd)) == '\n' || ch == EOF) 53960786Sps break; 54060786Sps if (p - buf >= len-1) 54160786Sps { 54260786Sps /* 54360786Sps * The string is too big to fit in the buffer we have. 54460786Sps * Allocate a new buffer, twice as big. 54560786Sps */ 54660786Sps len *= 2; 54760786Sps *p = '\0'; 54860786Sps p = (char *) ecalloc(len, sizeof(char)); 54960786Sps strcpy(p, buf); 55060786Sps free(buf); 55160786Sps buf = p; 55260786Sps p = buf + strlen(buf); 55360786Sps } 55460786Sps *p = ch; 55560786Sps } 55660786Sps *p = '\0'; 55760786Sps return (buf); 55860786Sps} 55960786Sps 56060786Sps 56160786Sps 56260786Sps#if HAVE_POPEN 56360786Sps 56460786SpsFILE *popen(); 56560786Sps 56660786Sps/* 56760786Sps * Execute a shell command. 56860786Sps * Return a pointer to a pipe connected to the shell command's standard output. 56960786Sps */ 57060786Sps static FILE * 57160786Spsshellcmd(cmd) 57260786Sps char *cmd; 57360786Sps{ 57460786Sps FILE *fd; 57560786Sps 57660786Sps#if HAVE_SHELL 57760786Sps char *shell; 57860786Sps 57960786Sps shell = lgetenv("SHELL"); 58060786Sps if (shell != NULL && *shell != '\0') 58160786Sps { 58260786Sps char *scmd; 58360786Sps char *esccmd; 58460786Sps 58560786Sps /* 586128345Stjr * Read the output of <$SHELL -c cmd>. 587128345Stjr * Escape any metacharacters in the command. 58860786Sps */ 589128345Stjr esccmd = shell_quote(cmd); 590128345Stjr if (esccmd == NULL) 59160786Sps { 592128345Stjr fd = popen(cmd, "r"); 59360786Sps } else 59460786Sps { 595294286Sdelphij int len = (int) (strlen(shell) + strlen(esccmd) + 5); 596161475Sdelphij scmd = (char *) ecalloc(len, sizeof(char)); 597161475Sdelphij SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd); 59860786Sps free(esccmd); 599128345Stjr fd = popen(scmd, "r"); 600128345Stjr free(scmd); 60160786Sps } 60260786Sps } else 60360786Sps#endif 60460786Sps { 60560786Sps fd = popen(cmd, "r"); 60660786Sps } 607128345Stjr /* 608128345Stjr * Redirection in `popen' might have messed with the 609128345Stjr * standard devices. Restore binary input mode. 610128345Stjr */ 611128345Stjr SET_BINARY(0); 61260786Sps return (fd); 61360786Sps} 61460786Sps 61560786Sps#endif /* HAVE_POPEN */ 61660786Sps 61760786Sps 61860786Sps/* 61960786Sps * Expand a filename, doing any system-specific metacharacter substitutions. 62060786Sps */ 62160786Sps public char * 62260786Spslglob(filename) 62360786Sps char *filename; 62460786Sps{ 62560786Sps char *gfilename; 62660786Sps 627330571Sdelphij filename = fexpand(filename); 62860786Sps if (secure) 629330571Sdelphij return (filename); 63060786Sps 63160786Sps#ifdef DECL_GLOB_LIST 63260786Sps{ 63360786Sps /* 63460786Sps * The globbing function returns a list of names. 63560786Sps */ 63660786Sps int length; 63760786Sps char *p; 638128345Stjr char *qfilename; 63960786Sps DECL_GLOB_LIST(list) 64060786Sps 64160786Sps GLOB_LIST(filename, list); 64260786Sps if (GLOB_LIST_FAILED(list)) 64360786Sps { 644330571Sdelphij return (filename); 64560786Sps } 64660786Sps length = 1; /* Room for trailing null byte */ 64760786Sps for (SCAN_GLOB_LIST(list, p)) 64860786Sps { 64960786Sps INIT_GLOB_LIST(list, p); 650128345Stjr qfilename = shell_quote(p); 651128345Stjr if (qfilename != NULL) 652128345Stjr { 653128345Stjr length += strlen(qfilename) + 1; 654128345Stjr free(qfilename); 655128345Stjr } 65660786Sps } 65760786Sps gfilename = (char *) ecalloc(length, sizeof(char)); 65860786Sps for (SCAN_GLOB_LIST(list, p)) 65960786Sps { 66060786Sps INIT_GLOB_LIST(list, p); 661128345Stjr qfilename = shell_quote(p); 662128345Stjr if (qfilename != NULL) 663128345Stjr { 664128345Stjr sprintf(gfilename + strlen(gfilename), "%s ", qfilename); 665128345Stjr free(qfilename); 666128345Stjr } 66760786Sps } 66860786Sps /* 66960786Sps * Overwrite the final trailing space with a null terminator. 67060786Sps */ 67160786Sps *--p = '\0'; 67260786Sps GLOB_LIST_DONE(list); 67360786Sps} 67460786Sps#else 67560786Sps#ifdef DECL_GLOB_NAME 67660786Sps{ 67760786Sps /* 67860786Sps * The globbing function returns a single name, and 67960786Sps * is called multiple times to walk thru all names. 68060786Sps */ 681330571Sdelphij char *p; 682330571Sdelphij int len; 683330571Sdelphij int n; 684330571Sdelphij char *pfilename; 685330571Sdelphij char *qfilename; 68660786Sps DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) 68760786Sps 68860786Sps GLOB_FIRST_NAME(filename, &fnd, handle); 68960786Sps if (GLOB_FIRST_FAILED(handle)) 69060786Sps { 691330571Sdelphij return (filename); 69260786Sps } 69360786Sps 69460786Sps _splitpath(filename, drive, dir, fname, ext); 69560786Sps len = 100; 69660786Sps gfilename = (char *) ecalloc(len, sizeof(char)); 69760786Sps p = gfilename; 69860786Sps do { 699294286Sdelphij n = (int) (strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1); 700330571Sdelphij pfilename = (char *) ecalloc(n, sizeof(char)); 701330571Sdelphij SNPRINTF3(pfilename, n, "%s%s%s", drive, dir, fnd.GLOB_NAME); 702330571Sdelphij qfilename = shell_quote(pfilename); 703330571Sdelphij free(pfilename); 704330571Sdelphij if (qfilename != NULL) 70560786Sps { 706330571Sdelphij n = (int) strlen(qfilename); 707128345Stjr while (p - gfilename + n + 2 >= len) 708128345Stjr { 709128345Stjr /* 710128345Stjr * No room in current buffer. 711128345Stjr * Allocate a bigger one. 712128345Stjr */ 713128345Stjr len *= 2; 714128345Stjr *p = '\0'; 715128345Stjr p = (char *) ecalloc(len, sizeof(char)); 716128345Stjr strcpy(p, gfilename); 717128345Stjr free(gfilename); 718128345Stjr gfilename = p; 719128345Stjr p = gfilename + strlen(gfilename); 720128345Stjr } 721330571Sdelphij strcpy(p, qfilename); 722330571Sdelphij free(qfilename); 723128345Stjr p += n; 724128345Stjr *p++ = ' '; 72560786Sps } 72660786Sps } while (GLOB_NEXT_NAME(handle, &fnd) == 0); 72760786Sps 72860786Sps /* 72960786Sps * Overwrite the final trailing space with a null terminator. 73060786Sps */ 73160786Sps *--p = '\0'; 73260786Sps GLOB_NAME_DONE(handle); 73360786Sps} 73460786Sps#else 73560786Sps#if HAVE_POPEN 73660786Sps{ 73760786Sps /* 73860786Sps * We get the shell to glob the filename for us by passing 73960786Sps * an "echo" command to the shell and reading its output. 74060786Sps */ 74160786Sps FILE *fd; 74260786Sps char *s; 74360786Sps char *lessecho; 74460786Sps char *cmd; 745128345Stjr char *esc; 746161475Sdelphij int len; 74760786Sps 748128345Stjr esc = get_meta_escape(); 749128345Stjr if (strlen(esc) == 0) 750128345Stjr esc = "-"; 751128345Stjr esc = shell_quote(esc); 752128345Stjr if (esc == NULL) 75360786Sps { 754330571Sdelphij return (filename); 75560786Sps } 756128345Stjr lessecho = lgetenv("LESSECHO"); 757128345Stjr if (lessecho == NULL || *lessecho == '\0') 758128345Stjr lessecho = "lessecho"; 75960786Sps /* 76060786Sps * Invoke lessecho, and read its output (a globbed list of filenames). 76160786Sps */ 762330571Sdelphij len = (int) (strlen(lessecho) + strlen(filename) + (7*strlen(metachars())) + 24); 763161475Sdelphij cmd = (char *) ecalloc(len, sizeof(char)); 764161475Sdelphij SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc); 765128345Stjr free(esc); 766128345Stjr for (s = metachars(); *s != '\0'; s++) 767128345Stjr sprintf(cmd + strlen(cmd), "-n0x%x ", *s); 768330571Sdelphij sprintf(cmd + strlen(cmd), "-- %s", filename); 76960786Sps fd = shellcmd(cmd); 77060786Sps free(cmd); 77160786Sps if (fd == NULL) 77260786Sps { 77360786Sps /* 77460786Sps * Cannot create the pipe. 77560786Sps * Just return the original (fexpanded) filename. 77660786Sps */ 777330571Sdelphij return (filename); 77860786Sps } 77960786Sps gfilename = readfd(fd); 78060786Sps pclose(fd); 78160786Sps if (*gfilename == '\0') 78260786Sps { 78360786Sps free(gfilename); 784330571Sdelphij return (save(filename)); 78560786Sps } 78660786Sps} 78760786Sps#else 78860786Sps /* 78960786Sps * No globbing functions at all. Just use the fexpanded filename. 79060786Sps */ 79160786Sps gfilename = save(filename); 79260786Sps#endif 79360786Sps#endif 79460786Sps#endif 79560786Sps free(filename); 79660786Sps return (gfilename); 79760786Sps} 79860786Sps 79960786Sps/* 800237613Sdelphij * Return number of %s escapes in a string. 801237613Sdelphij * Return a large number if there are any other % escapes besides %s. 802237613Sdelphij */ 803237613Sdelphij static int 804237613Sdelphijnum_pct_s(lessopen) 805237613Sdelphij char *lessopen; 806237613Sdelphij{ 807294286Sdelphij int num = 0; 808237613Sdelphij 809294286Sdelphij while (*lessopen != '\0') 810237613Sdelphij { 811294286Sdelphij if (*lessopen == '%') 812294286Sdelphij { 813294286Sdelphij if (lessopen[1] == '%') 814294286Sdelphij ++lessopen; 815294286Sdelphij else if (lessopen[1] == 's') 816294286Sdelphij ++num; 817294286Sdelphij else 818294286Sdelphij return (999); 819294286Sdelphij } 820294286Sdelphij ++lessopen; 821237613Sdelphij } 822237613Sdelphij return (num); 823237613Sdelphij} 824237613Sdelphij 825237613Sdelphij/* 82660786Sps * See if we should open a "replacement file" 82760786Sps * instead of the file we're about to open. 82860786Sps */ 82960786Sps public char * 83060786Spsopen_altfile(filename, pf, pfd) 83160786Sps char *filename; 83260786Sps int *pf; 83360786Sps void **pfd; 83460786Sps{ 83560786Sps#if !HAVE_POPEN 83660786Sps return (NULL); 83760786Sps#else 83860786Sps char *lessopen; 839330571Sdelphij char *qfilename; 84060786Sps char *cmd; 841161475Sdelphij int len; 84260786Sps FILE *fd; 84360786Sps#if HAVE_FILENO 84460786Sps int returnfd = 0; 84560786Sps#endif 84660786Sps 847128345Stjr if (!use_lessopen || secure) 84860786Sps return (NULL); 84960786Sps ch_ungetchar(-1); 85060786Sps if ((lessopen = lgetenv("LESSOPEN")) == NULL) 85160786Sps return (NULL); 852237613Sdelphij while (*lessopen == '|') 85360786Sps { 85460786Sps /* 85560786Sps * If LESSOPEN starts with a |, it indicates 85660786Sps * a "pipe preprocessor". 85760786Sps */ 858191930Sdelphij#if !HAVE_FILENO 859191930Sdelphij error("LESSOPEN pipe is not supported", NULL_PARG); 860191930Sdelphij return (NULL); 861191930Sdelphij#else 86260786Sps lessopen++; 863237613Sdelphij returnfd++; 86460786Sps#endif 86560786Sps } 866330571Sdelphij if (*lessopen == '-') 867330571Sdelphij { 868195941Sdelphij /* 869195941Sdelphij * Lessopen preprocessor will accept "-" as a filename. 870195941Sdelphij */ 871195941Sdelphij lessopen++; 872330571Sdelphij } else 873330571Sdelphij { 874195941Sdelphij if (strcmp(filename, "-") == 0) 875195941Sdelphij return (NULL); 876195941Sdelphij } 877330571Sdelphij if (num_pct_s(lessopen) != 1) 878237613Sdelphij { 879330571Sdelphij error("LESSOPEN ignored: must contain exactly one %%s", NULL_PARG); 880237613Sdelphij return (NULL); 881237613Sdelphij } 88260786Sps 883330571Sdelphij qfilename = shell_quote(filename); 884330571Sdelphij len = (int) (strlen(lessopen) + strlen(qfilename) + 2); 885161475Sdelphij cmd = (char *) ecalloc(len, sizeof(char)); 886330571Sdelphij SNPRINTF1(cmd, len, lessopen, qfilename); 887330571Sdelphij free(qfilename); 88860786Sps fd = shellcmd(cmd); 88960786Sps free(cmd); 89060786Sps if (fd == NULL) 89160786Sps { 89260786Sps /* 89360786Sps * Cannot create the pipe. 89460786Sps */ 89560786Sps return (NULL); 89660786Sps } 89760786Sps#if HAVE_FILENO 89860786Sps if (returnfd) 89960786Sps { 900330571Sdelphij char c; 90160786Sps int f; 90260786Sps 90360786Sps /* 904330571Sdelphij * The first time we open the file, read one char 905330571Sdelphij * to see if the pipe will produce any data. 90660786Sps * If it does, push the char back on the pipe. 90760786Sps */ 90860786Sps f = fileno(fd); 90960786Sps SET_BINARY(f); 91060786Sps if (read(f, &c, 1) != 1) 91160786Sps { 91260786Sps /* 913237613Sdelphij * Pipe is empty. 914237613Sdelphij * If more than 1 pipe char was specified, 915237613Sdelphij * the exit status tells whether the file itself 916237613Sdelphij * is empty, or if there is no alt file. 917237613Sdelphij * If only one pipe char, just assume no alt file. 91860786Sps */ 919237613Sdelphij int status = pclose(fd); 920237613Sdelphij if (returnfd > 1 && status == 0) { 921237613Sdelphij *pfd = NULL; 922237613Sdelphij *pf = -1; 923237613Sdelphij return (save(FAKE_EMPTYFILE)); 924237613Sdelphij } 92560786Sps return (NULL); 92660786Sps } 92760786Sps ch_ungetchar(c); 92860786Sps *pfd = (void *) fd; 92960786Sps *pf = f; 93060786Sps return (save("-")); 93160786Sps } 93260786Sps#endif 933128345Stjr cmd = readfd(fd); 93460786Sps pclose(fd); 935128345Stjr if (*cmd == '\0') 93660786Sps /* 93760786Sps * Pipe is empty. This means there is no alt file. 93860786Sps */ 93960786Sps return (NULL); 940128345Stjr return (cmd); 94160786Sps#endif /* HAVE_POPEN */ 94260786Sps} 94360786Sps 94460786Sps/* 94560786Sps * Close a replacement file. 94660786Sps */ 94760786Sps public void 948330571Sdelphijclose_altfile(altfilename, filename) 94960786Sps char *altfilename; 95060786Sps char *filename; 95160786Sps{ 95260786Sps#if HAVE_POPEN 95360786Sps char *lessclose; 95460786Sps FILE *fd; 95560786Sps char *cmd; 956161475Sdelphij int len; 95760786Sps 95860786Sps if (secure) 95960786Sps return; 960330571Sdelphij ch_ungetchar(-1); 96160786Sps if ((lessclose = lgetenv("LESSCLOSE")) == NULL) 96260786Sps return; 963237613Sdelphij if (num_pct_s(lessclose) > 2) 964237613Sdelphij { 965330571Sdelphij error("LESSCLOSE ignored; must contain no more than 2 %%s", NULL_PARG); 966237613Sdelphij return; 967237613Sdelphij } 968294286Sdelphij len = (int) (strlen(lessclose) + strlen(filename) + strlen(altfilename) + 2); 969161475Sdelphij cmd = (char *) ecalloc(len, sizeof(char)); 970161475Sdelphij SNPRINTF2(cmd, len, lessclose, filename, altfilename); 97160786Sps fd = shellcmd(cmd); 97260786Sps free(cmd); 97360786Sps if (fd != NULL) 97460786Sps pclose(fd); 97560786Sps#endif 97660786Sps} 97760786Sps 97860786Sps/* 97960786Sps * Is the specified file a directory? 98060786Sps */ 98160786Sps public int 98260786Spsis_dir(filename) 98360786Sps char *filename; 98460786Sps{ 98560786Sps int isdir = 0; 98660786Sps 98760786Sps#if HAVE_STAT 98860786Sps{ 98960786Sps int r; 99060786Sps struct stat statbuf; 99160786Sps 99260786Sps r = stat(filename, &statbuf); 99360786Sps isdir = (r >= 0 && S_ISDIR(statbuf.st_mode)); 99460786Sps} 99560786Sps#else 99660786Sps#ifdef _OSK 99760786Sps{ 998330571Sdelphij int f; 99960786Sps 100060786Sps f = open(filename, S_IREAD | S_IFDIR); 100160786Sps if (f >= 0) 100260786Sps close(f); 100360786Sps isdir = (f >= 0); 100460786Sps} 100560786Sps#endif 100660786Sps#endif 100760786Sps return (isdir); 100860786Sps} 100960786Sps 101060786Sps/* 101160786Sps * Returns NULL if the file can be opened and 101260786Sps * is an ordinary file, otherwise an error message 101360786Sps * (if it cannot be opened or is a directory, etc.) 101460786Sps */ 101560786Sps public char * 101660786Spsbad_file(filename) 101760786Sps char *filename; 101860786Sps{ 1019330571Sdelphij char *m = NULL; 102060786Sps 1021170256Sdelphij if (!force_open && is_dir(filename)) 102260786Sps { 1023128345Stjr static char is_a_dir[] = " is a directory"; 102460786Sps 1025128345Stjr m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir), 102660786Sps sizeof(char)); 102760786Sps strcpy(m, filename); 1028128345Stjr strcat(m, is_a_dir); 102960786Sps } else 103060786Sps { 103160786Sps#if HAVE_STAT 103260786Sps int r; 103360786Sps struct stat statbuf; 103460786Sps 103560786Sps r = stat(filename, &statbuf); 103660786Sps if (r < 0) 103760786Sps { 103860786Sps m = errno_message(filename); 103960786Sps } else if (force_open) 104060786Sps { 104160786Sps m = NULL; 104260786Sps } else if (!S_ISREG(statbuf.st_mode)) 104360786Sps { 104460786Sps static char not_reg[] = " is not a regular file (use -f to see it)"; 104560786Sps m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 104660786Sps sizeof(char)); 104760786Sps strcpy(m, filename); 104860786Sps strcat(m, not_reg); 104960786Sps } 105060786Sps#endif 105160786Sps } 105260786Sps return (m); 105360786Sps} 105460786Sps 105560786Sps/* 105660786Sps * Return the size of a file, as cheaply as possible. 105760786Sps * In Unix, we can stat the file. 105860786Sps */ 105960786Sps public POSITION 106060786Spsfilesize(f) 106160786Sps int f; 106260786Sps{ 106360786Sps#if HAVE_STAT 106460786Sps struct stat statbuf; 106560786Sps 106660786Sps if (fstat(f, &statbuf) >= 0) 106760786Sps return ((POSITION) statbuf.st_size); 106860786Sps#else 106960786Sps#ifdef _OSK 107060786Sps long size; 107160786Sps 107260786Sps if ((size = (long) _gs_size(f)) >= 0) 107360786Sps return ((POSITION) size); 107460786Sps#endif 107560786Sps#endif 107660786Sps return (seek_filesize(f)); 107760786Sps} 107860786Sps 1079128345Stjr/* 1080128345Stjr * 1081128345Stjr */ 1082128345Stjr public char * 1083128345Stjrshell_coption() 1084128345Stjr{ 1085128345Stjr return ("-c"); 1086128345Stjr} 1087221715Sdelphij 1088221715Sdelphij/* 1089221715Sdelphij * Return last component of a pathname. 1090221715Sdelphij */ 1091221715Sdelphij public char * 1092221715Sdelphijlast_component(name) 1093221715Sdelphij char *name; 1094221715Sdelphij{ 1095221715Sdelphij char *slash; 1096221715Sdelphij 1097221715Sdelphij for (slash = name + strlen(name); slash > name; ) 1098221715Sdelphij { 1099221715Sdelphij --slash; 1100221715Sdelphij if (*slash == *PATHNAME_SEP || *slash == '/') 1101221715Sdelphij return (slash + 1); 1102221715Sdelphij } 1103221715Sdelphij return (name); 1104221715Sdelphij} 1105221715Sdelphij 1106