1a5f0fb15SPaul Saab /* 2*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 3a5f0fb15SPaul Saab * 4a5f0fb15SPaul Saab * You may distribute under the terms of either the GNU General Public 5a5f0fb15SPaul Saab * License or the Less License, as specified in the README file. 6a5f0fb15SPaul Saab * 796e55cc7SXin LI * For more information, see the README file. 8a5f0fb15SPaul Saab */ 9a5f0fb15SPaul Saab 10a5f0fb15SPaul Saab 11a5f0fb15SPaul Saab /* 12a5f0fb15SPaul Saab * Routines to mess around with filenames (and files). 13a5f0fb15SPaul Saab * Much of this is very OS dependent. 14a5f0fb15SPaul Saab */ 15a5f0fb15SPaul Saab 16a5f0fb15SPaul Saab #include "less.h" 17a5f0fb15SPaul Saab #include "lglob.h" 18a5f0fb15SPaul Saab #if MSDOS_COMPILER 19a5f0fb15SPaul Saab #include <dos.h> 20a5f0fb15SPaul Saab #if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER) 21a5f0fb15SPaul Saab #include <dir.h> 22a5f0fb15SPaul Saab #endif 23a5f0fb15SPaul Saab #if MSDOS_COMPILER==DJGPPC 24a5f0fb15SPaul Saab #include <glob.h> 25a5f0fb15SPaul Saab #include <dir.h> 26a5f0fb15SPaul Saab #define _MAX_PATH PATH_MAX 27a5f0fb15SPaul Saab #endif 28a5f0fb15SPaul Saab #endif 29a5f0fb15SPaul Saab #ifdef _OSK 30a5f0fb15SPaul Saab #include <rbf.h> 31a5f0fb15SPaul Saab #ifndef _OSK_MWC32 32a5f0fb15SPaul Saab #include <modes.h> 33a5f0fb15SPaul Saab #endif 34a5f0fb15SPaul Saab #endif 35a5f0fb15SPaul Saab 36a5f0fb15SPaul Saab #if HAVE_STAT 37a5f0fb15SPaul Saab #include <sys/stat.h> 38a5f0fb15SPaul Saab #ifndef S_ISDIR 39a5f0fb15SPaul Saab #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 40a5f0fb15SPaul Saab #endif 41a5f0fb15SPaul Saab #ifndef S_ISREG 42a5f0fb15SPaul Saab #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 43a5f0fb15SPaul Saab #endif 44a5f0fb15SPaul Saab #endif 45a5f0fb15SPaul Saab 46a5f0fb15SPaul Saab extern int force_open; 47000ba3e8STim J. Robbins extern int use_lessopen; 487f074f9cSXin LI extern int ctldisp; 497374caaaSXin LI extern int utf_mode; 50a5f0fb15SPaul Saab extern IFILE curr_ifile; 51a5f0fb15SPaul Saab extern IFILE old_ifile; 52a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES 53a5f0fb15SPaul Saab extern char openquote; 54a5f0fb15SPaul Saab extern char closequote; 55a5f0fb15SPaul Saab #endif 56d713e089SXin LI #if HAVE_STAT_INO 57d713e089SXin LI extern ino_t curr_ino; 58d713e089SXin LI extern dev_t curr_dev; 59d713e089SXin LI #endif 60a5f0fb15SPaul Saab 61a5f0fb15SPaul Saab /* 62a5f0fb15SPaul Saab * Remove quotes around a filename. 63a5f0fb15SPaul Saab */ 64*c77c4889SXin LI public char * shell_unquote(constant char *str) 65a5f0fb15SPaul Saab { 66a5f0fb15SPaul Saab char *name; 67a5f0fb15SPaul Saab char *p; 68a5f0fb15SPaul Saab 69000ba3e8STim J. Robbins name = p = (char *) ecalloc(strlen(str)+1, sizeof(char)); 70000ba3e8STim J. Robbins if (*str == openquote) 71000ba3e8STim J. Robbins { 72000ba3e8STim J. Robbins str++; 73000ba3e8STim J. Robbins while (*str != '\0') 74000ba3e8STim J. Robbins { 75000ba3e8STim J. Robbins if (*str == closequote) 76000ba3e8STim J. Robbins { 77000ba3e8STim J. Robbins if (str[1] != closequote) 78000ba3e8STim J. Robbins break; 79000ba3e8STim J. Robbins str++; 80000ba3e8STim J. Robbins } 81000ba3e8STim J. Robbins *p++ = *str++; 82000ba3e8STim J. Robbins } 83000ba3e8STim J. Robbins } else 84000ba3e8STim J. Robbins { 85*c77c4889SXin LI constant char *esc = get_meta_escape(); 86*c77c4889SXin LI size_t esclen = strlen(esc); 87000ba3e8STim J. Robbins while (*str != '\0') 88000ba3e8STim J. Robbins { 89000ba3e8STim J. Robbins if (esclen > 0 && strncmp(str, esc, esclen) == 0) 90000ba3e8STim J. Robbins str += esclen; 91000ba3e8STim J. Robbins *p++ = *str++; 92000ba3e8STim J. Robbins } 93000ba3e8STim J. Robbins } 94a5f0fb15SPaul Saab *p = '\0'; 95a5f0fb15SPaul Saab return (name); 96000ba3e8STim J. Robbins } 97000ba3e8STim J. Robbins 98000ba3e8STim J. Robbins /* 99000ba3e8STim J. Robbins * Get the shell's escape character. 100000ba3e8STim J. Robbins */ 101*c77c4889SXin LI public constant char * get_meta_escape(void) 102000ba3e8STim J. Robbins { 103*c77c4889SXin LI constant char *s; 104000ba3e8STim J. Robbins 105000ba3e8STim J. Robbins s = lgetenv("LESSMETAESCAPE"); 106000ba3e8STim J. Robbins if (s == NULL) 107000ba3e8STim J. Robbins s = DEF_METAESCAPE; 108000ba3e8STim J. Robbins return (s); 109000ba3e8STim J. Robbins } 110000ba3e8STim J. Robbins 111000ba3e8STim J. Robbins /* 112000ba3e8STim J. Robbins * Get the characters which the shell considers to be "metacharacters". 113000ba3e8STim J. Robbins */ 114*c77c4889SXin LI static constant char * metachars(void) 115000ba3e8STim J. Robbins { 116*c77c4889SXin LI static constant char *mchars = NULL; 117000ba3e8STim J. Robbins 118000ba3e8STim J. Robbins if (mchars == NULL) 119000ba3e8STim J. Robbins { 120000ba3e8STim J. Robbins mchars = lgetenv("LESSMETACHARS"); 121000ba3e8STim J. Robbins if (mchars == NULL) 122000ba3e8STim J. Robbins mchars = DEF_METACHARS; 123000ba3e8STim J. Robbins } 124000ba3e8STim J. Robbins return (mchars); 125000ba3e8STim J. Robbins } 126000ba3e8STim J. Robbins 127000ba3e8STim J. Robbins /* 128000ba3e8STim J. Robbins * Is this a shell metacharacter? 129000ba3e8STim J. Robbins */ 130*c77c4889SXin LI static lbool metachar(char c) 131000ba3e8STim J. Robbins { 132000ba3e8STim J. Robbins return (strchr(metachars(), c) != NULL); 133000ba3e8STim J. Robbins } 134000ba3e8STim J. Robbins 135000ba3e8STim J. Robbins /* 136*c77c4889SXin LI * Must use quotes rather than escape char for this metachar? 137*c77c4889SXin LI */ 138*c77c4889SXin LI static lbool must_quote(char c) 139*c77c4889SXin LI { 140*c77c4889SXin LI /* {{ Maybe the set of must_quote chars should be configurable? }} */ 141*c77c4889SXin LI return (c == '\n'); 142*c77c4889SXin LI } 143*c77c4889SXin LI 144*c77c4889SXin LI /* 145000ba3e8STim J. Robbins * Insert a backslash before each metacharacter in a string. 146000ba3e8STim J. Robbins */ 147*c77c4889SXin LI public char * shell_quoten(constant char *s, size_t slen) 148000ba3e8STim J. Robbins { 149*c77c4889SXin LI constant char *p; 150*c77c4889SXin LI char *np; 151000ba3e8STim J. Robbins char *newstr; 152*c77c4889SXin LI size_t len; 153*c77c4889SXin LI constant char *esc = get_meta_escape(); 154*c77c4889SXin LI size_t esclen = strlen(esc); 155*c77c4889SXin LI lbool use_quotes = FALSE; 156*c77c4889SXin LI lbool have_quotes = FALSE; 157000ba3e8STim J. Robbins 158000ba3e8STim J. Robbins /* 159000ba3e8STim J. Robbins * Determine how big a string we need to allocate. 160000ba3e8STim J. Robbins */ 161000ba3e8STim J. Robbins len = 1; /* Trailing null byte */ 162*c77c4889SXin LI for (p = s; p < s + slen; p++) 163000ba3e8STim J. Robbins { 164000ba3e8STim J. Robbins len++; 165000ba3e8STim J. Robbins if (*p == openquote || *p == closequote) 166*c77c4889SXin LI have_quotes = TRUE; 167000ba3e8STim J. Robbins if (metachar(*p)) 168000ba3e8STim J. Robbins { 169000ba3e8STim J. Robbins if (esclen == 0) 170000ba3e8STim J. Robbins { 171000ba3e8STim J. Robbins /* 172000ba3e8STim J. Robbins * We've got a metachar, but this shell 173000ba3e8STim J. Robbins * doesn't support escape chars. Use quotes. 174000ba3e8STim J. Robbins */ 175*c77c4889SXin LI use_quotes = TRUE; 176*c77c4889SXin LI } else if (must_quote(*p)) 177*c77c4889SXin LI { 178*c77c4889SXin LI len += 3; /* open quote + char + close quote */ 179000ba3e8STim J. Robbins } else 180000ba3e8STim J. Robbins { 181000ba3e8STim J. Robbins /* 182000ba3e8STim J. Robbins * Allow space for the escape char. 183000ba3e8STim J. Robbins */ 184000ba3e8STim J. Robbins len += esclen; 185000ba3e8STim J. Robbins } 186000ba3e8STim J. Robbins } 187000ba3e8STim J. Robbins } 188000ba3e8STim J. Robbins if (use_quotes) 189000ba3e8STim J. Robbins { 190000ba3e8STim J. Robbins if (have_quotes) 191000ba3e8STim J. Robbins /* 192000ba3e8STim J. Robbins * We can't quote a string that contains quotes. 193000ba3e8STim J. Robbins */ 194000ba3e8STim J. Robbins return (NULL); 195*c77c4889SXin LI len = slen + 3; 196000ba3e8STim J. Robbins } 197000ba3e8STim J. Robbins /* 198000ba3e8STim J. Robbins * Allocate and construct the new string. 199000ba3e8STim J. Robbins */ 200*c77c4889SXin LI newstr = np = (char *) ecalloc(len, sizeof(char)); 201000ba3e8STim J. Robbins if (use_quotes) 202000ba3e8STim J. Robbins { 203*c77c4889SXin LI SNPRINTF4(newstr, len, "%c%.*s%c", openquote, (int) slen, s, closequote); 204000ba3e8STim J. Robbins } else 205000ba3e8STim J. Robbins { 206*c77c4889SXin LI constant char *es = s + slen; 207*c77c4889SXin LI while (s < es) 208000ba3e8STim J. Robbins { 209*c77c4889SXin LI if (!metachar(*s)) 210000ba3e8STim J. Robbins { 211*c77c4889SXin LI *np++ = *s++; 212*c77c4889SXin LI } else if (must_quote(*s)) 213*c77c4889SXin LI { 214*c77c4889SXin LI /* Surround the char with quotes. */ 215*c77c4889SXin LI *np++ = openquote; 216*c77c4889SXin LI *np++ = *s++; 217*c77c4889SXin LI *np++ = closequote; 218*c77c4889SXin LI } else 219*c77c4889SXin LI { 220*c77c4889SXin LI /* Insert an escape char before the char. */ 221*c77c4889SXin LI strcpy(np, esc); 222*c77c4889SXin LI np += esclen; 223*c77c4889SXin LI *np++ = *s++; 224000ba3e8STim J. Robbins } 225000ba3e8STim J. Robbins } 226*c77c4889SXin LI *np = '\0'; 227000ba3e8STim J. Robbins } 228000ba3e8STim J. Robbins return (newstr); 229a5f0fb15SPaul Saab } 230a5f0fb15SPaul Saab 231*c77c4889SXin LI public char * shell_quote(constant char *s) 232*c77c4889SXin LI { 233*c77c4889SXin LI return shell_quoten(s, strlen(s)); 234*c77c4889SXin LI } 235*c77c4889SXin LI 236a5f0fb15SPaul Saab /* 237a5f0fb15SPaul Saab * Return a pathname that points to a specified file in a specified directory. 238a5f0fb15SPaul Saab * Return NULL if the file does not exist in the directory. 239a5f0fb15SPaul Saab */ 240*c77c4889SXin LI public char * dirfile(constant char *dirname, constant char *filename, int must_exist) 241a5f0fb15SPaul Saab { 242a5f0fb15SPaul Saab char *pathname; 243*c77c4889SXin LI size_t len; 244a5f0fb15SPaul Saab int f; 245a5f0fb15SPaul Saab 246a5f0fb15SPaul Saab if (dirname == NULL || *dirname == '\0') 247a5f0fb15SPaul Saab return (NULL); 248a5f0fb15SPaul Saab /* 249a5f0fb15SPaul Saab * Construct the full pathname. 250a5f0fb15SPaul Saab */ 251*c77c4889SXin LI len = strlen(dirname) + strlen(filename) + 2; 2526dcb072bSXin LI pathname = (char *) calloc(len, sizeof(char)); 253a5f0fb15SPaul Saab if (pathname == NULL) 254a5f0fb15SPaul Saab return (NULL); 2556dcb072bSXin LI SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename); 25630a1828cSXin LI if (must_exist) 25730a1828cSXin LI { 258a5f0fb15SPaul Saab /* 259a5f0fb15SPaul Saab * Make sure the file exists. 260a5f0fb15SPaul Saab */ 261b2ea2440SXin LI f = open(pathname, OPEN_READ); 262a5f0fb15SPaul Saab if (f < 0) 263a5f0fb15SPaul Saab { 264a5f0fb15SPaul Saab free(pathname); 265a5f0fb15SPaul Saab pathname = NULL; 266a5f0fb15SPaul Saab } else 267a5f0fb15SPaul Saab { 268a5f0fb15SPaul Saab close(f); 269a5f0fb15SPaul Saab } 27030a1828cSXin LI } 271a5f0fb15SPaul Saab return (pathname); 272a5f0fb15SPaul Saab } 273a5f0fb15SPaul Saab 274a5f0fb15SPaul Saab /* 275a5f0fb15SPaul Saab * Return the full pathname of the given file in the "home directory". 276a5f0fb15SPaul Saab */ 277*c77c4889SXin LI public char * homefile(constant char *filename) 278a5f0fb15SPaul Saab { 2791ea31627SRobert Watson char *pathname; 280a5f0fb15SPaul Saab 28130a1828cSXin LI /* Try $HOME/filename. */ 28230a1828cSXin LI pathname = dirfile(lgetenv("HOME"), filename, 1); 283a5f0fb15SPaul Saab if (pathname != NULL) 284a5f0fb15SPaul Saab return (pathname); 285a5f0fb15SPaul Saab #if OS2 28630a1828cSXin LI /* Try $INIT/filename. */ 28730a1828cSXin LI pathname = dirfile(lgetenv("INIT"), filename, 1); 288a5f0fb15SPaul Saab if (pathname != NULL) 289a5f0fb15SPaul Saab return (pathname); 290a5f0fb15SPaul Saab #endif 291a5f0fb15SPaul Saab #if MSDOS_COMPILER || OS2 29230a1828cSXin LI /* Look for the file anywhere on search path. */ 29330a1828cSXin LI pathname = (char *) ecalloc(_MAX_PATH, sizeof(char)); 294a5f0fb15SPaul Saab #if MSDOS_COMPILER==DJGPPC 295a5f0fb15SPaul Saab { 296a5f0fb15SPaul Saab char *res = searchpath(filename); 297a5f0fb15SPaul Saab if (res == 0) 298a5f0fb15SPaul Saab *pathname = '\0'; 299a5f0fb15SPaul Saab else 300a5f0fb15SPaul Saab strcpy(pathname, res); 301a5f0fb15SPaul Saab } 302a5f0fb15SPaul Saab #else 303a5f0fb15SPaul Saab _searchenv(filename, "PATH", pathname); 304a5f0fb15SPaul Saab #endif 305a5f0fb15SPaul Saab if (*pathname != '\0') 306a5f0fb15SPaul Saab return (pathname); 307a5f0fb15SPaul Saab free(pathname); 308a5f0fb15SPaul Saab #endif 309a5f0fb15SPaul Saab return (NULL); 310a5f0fb15SPaul Saab } 311a5f0fb15SPaul Saab 312*c77c4889SXin LI typedef struct xcpy { char *dest; size_t copied; } xcpy; 313*c77c4889SXin LI 314*c77c4889SXin LI static void xcpy_char(xcpy *xp, char ch) 315*c77c4889SXin LI { 316*c77c4889SXin LI if (xp->dest != NULL) *(xp->dest)++ = ch; 317*c77c4889SXin LI xp->copied++; 318*c77c4889SXin LI } 319*c77c4889SXin LI 320*c77c4889SXin LI static void xcpy_filename(xcpy *xp, constant char *str) 321*c77c4889SXin LI { 322*c77c4889SXin LI /* If filename contains spaces, quote it 323*c77c4889SXin LI * to prevent edit_list from splitting it. */ 324*c77c4889SXin LI lbool quote = (strchr(str, ' ') != NULL); 325*c77c4889SXin LI if (quote) 326*c77c4889SXin LI xcpy_char(xp, openquote); 327*c77c4889SXin LI for (; *str != '\0'; str++) 328*c77c4889SXin LI xcpy_char(xp, *str); 329*c77c4889SXin LI if (quote) 330*c77c4889SXin LI xcpy_char(xp, closequote); 331*c77c4889SXin LI } 332*c77c4889SXin LI 333*c77c4889SXin LI static size_t fexpand_copy(constant char *fr, char *to) 334*c77c4889SXin LI { 335*c77c4889SXin LI xcpy xp; 336*c77c4889SXin LI xp.copied = 0; 337*c77c4889SXin LI xp.dest = to; 338*c77c4889SXin LI 339*c77c4889SXin LI for (; *fr != '\0'; fr++) 340*c77c4889SXin LI { 341*c77c4889SXin LI lbool expand = FALSE; 342*c77c4889SXin LI switch (*fr) 343*c77c4889SXin LI { 344*c77c4889SXin LI case '%': 345*c77c4889SXin LI case '#': 346*c77c4889SXin LI if (fr[1] == *fr) 347*c77c4889SXin LI { 348*c77c4889SXin LI /* Two identical chars. Output just one. */ 349*c77c4889SXin LI fr += 1; 350*c77c4889SXin LI } else 351*c77c4889SXin LI { 352*c77c4889SXin LI /* Single char. Expand to a (quoted) file name. */ 353*c77c4889SXin LI expand = TRUE; 354*c77c4889SXin LI } 355*c77c4889SXin LI break; 356*c77c4889SXin LI default: 357*c77c4889SXin LI break; 358*c77c4889SXin LI } 359*c77c4889SXin LI if (expand) 360*c77c4889SXin LI { 361*c77c4889SXin LI IFILE ifile = (*fr == '%') ? curr_ifile : (*fr == '#') ? old_ifile : NULL_IFILE; 362*c77c4889SXin LI if (ifile == NULL_IFILE) 363*c77c4889SXin LI xcpy_char(&xp, *fr); 364*c77c4889SXin LI else 365*c77c4889SXin LI xcpy_filename(&xp, get_filename(ifile)); 366*c77c4889SXin LI } else 367*c77c4889SXin LI { 368*c77c4889SXin LI xcpy_char(&xp, *fr); 369*c77c4889SXin LI } 370*c77c4889SXin LI } 371*c77c4889SXin LI xcpy_char(&xp, '\0'); 372*c77c4889SXin LI return xp.copied; 373*c77c4889SXin LI } 374*c77c4889SXin LI 375a5f0fb15SPaul Saab /* 376a5f0fb15SPaul Saab * Expand a string, substituting any "%" with the current filename, 377a5f0fb15SPaul Saab * and any "#" with the previous filename. 378a5f0fb15SPaul Saab * But a string of N "%"s is just replaced with N-1 "%"s. 379a5f0fb15SPaul Saab * Likewise for a string of N "#"s. 380a5f0fb15SPaul Saab * {{ This is a lot of work just to support % and #. }} 381a5f0fb15SPaul Saab */ 382*c77c4889SXin LI public char * fexpand(constant char *s) 383a5f0fb15SPaul Saab { 384*c77c4889SXin LI size_t n; 3851ea31627SRobert Watson char *e; 386a5f0fb15SPaul Saab 387a5f0fb15SPaul Saab /* 388a5f0fb15SPaul Saab * Make one pass to see how big a buffer we 389a5f0fb15SPaul Saab * need to allocate for the expanded string. 390a5f0fb15SPaul Saab */ 391*c77c4889SXin LI n = fexpand_copy(s, NULL); 392*c77c4889SXin LI e = (char *) ecalloc(n, sizeof(char)); 393a5f0fb15SPaul Saab 394a5f0fb15SPaul Saab /* 395a5f0fb15SPaul Saab * Now copy the string, expanding any "%" or "#". 396a5f0fb15SPaul Saab */ 397*c77c4889SXin LI fexpand_copy(s, e); 398a5f0fb15SPaul Saab return (e); 399a5f0fb15SPaul Saab } 400a5f0fb15SPaul Saab 40133096f16SXin LI 402a5f0fb15SPaul Saab #if TAB_COMPLETE_FILENAME 403a5f0fb15SPaul Saab 404a5f0fb15SPaul Saab /* 405a5f0fb15SPaul Saab * Return a blank-separated list of filenames which "complete" 406a5f0fb15SPaul Saab * the given string. 407a5f0fb15SPaul Saab */ 408*c77c4889SXin LI public char * fcomplete(constant char *s) 409a5f0fb15SPaul Saab { 410a5f0fb15SPaul Saab char *fpat; 411000ba3e8STim J. Robbins char *qs; 412*c77c4889SXin LI char *uqs; 413a5f0fb15SPaul Saab 414*c77c4889SXin LI /* {{ Is this needed? lglob calls secure_allow. }} */ 415*c77c4889SXin LI if (!secure_allow(SF_GLOB)) 416a5f0fb15SPaul Saab return (NULL); 417a5f0fb15SPaul Saab /* 418a5f0fb15SPaul Saab * Complete the filename "s" by globbing "s*". 419a5f0fb15SPaul Saab */ 420a5f0fb15SPaul Saab #if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC) 421a5f0fb15SPaul Saab /* 422a5f0fb15SPaul Saab * But in DOS, we have to glob "s*.*". 423a5f0fb15SPaul Saab * But if the final component of the filename already has 424a5f0fb15SPaul Saab * a dot in it, just do "s*". 425a5f0fb15SPaul Saab * (Thus, "FILE" is globbed as "FILE*.*", 426a5f0fb15SPaul Saab * but "FILE.A" is globbed as "FILE.A*"). 427a5f0fb15SPaul Saab */ 428a5f0fb15SPaul Saab { 429*c77c4889SXin LI constant char *slash; 430*c77c4889SXin LI size_t len; 431a5f0fb15SPaul Saab for (slash = s+strlen(s)-1; slash > s; slash--) 432a5f0fb15SPaul Saab if (*slash == *PATHNAME_SEP || *slash == '/') 433a5f0fb15SPaul Saab break; 434*c77c4889SXin LI len = strlen(s) + 4; 4356dcb072bSXin LI fpat = (char *) ecalloc(len, sizeof(char)); 436a5f0fb15SPaul Saab if (strchr(slash, '.') == NULL) 4376dcb072bSXin LI SNPRINTF1(fpat, len, "%s*.*", s); 438a5f0fb15SPaul Saab else 4396dcb072bSXin LI SNPRINTF1(fpat, len, "%s*", s); 440a5f0fb15SPaul Saab } 441a5f0fb15SPaul Saab #else 4426dcb072bSXin LI { 443*c77c4889SXin LI size_t len = strlen(s) + 2; 4446dcb072bSXin LI fpat = (char *) ecalloc(len, sizeof(char)); 4456dcb072bSXin LI SNPRINTF1(fpat, len, "%s*", s); 4466dcb072bSXin LI } 447a5f0fb15SPaul Saab #endif 448000ba3e8STim J. Robbins qs = lglob(fpat); 449*c77c4889SXin LI uqs = shell_unquote(qs); 450*c77c4889SXin LI if (strcmp(uqs, fpat) == 0) 451a5f0fb15SPaul Saab { 452a5f0fb15SPaul Saab /* 453a5f0fb15SPaul Saab * The filename didn't expand. 454a5f0fb15SPaul Saab */ 455000ba3e8STim J. Robbins free(qs); 456000ba3e8STim J. Robbins qs = NULL; 457a5f0fb15SPaul Saab } 458*c77c4889SXin LI free(uqs); 459a5f0fb15SPaul Saab free(fpat); 460000ba3e8STim J. Robbins return (qs); 461a5f0fb15SPaul Saab } 462a5f0fb15SPaul Saab #endif 463a5f0fb15SPaul Saab 464a5f0fb15SPaul Saab /* 465a5f0fb15SPaul Saab * Try to determine if a file is "binary". 466a5f0fb15SPaul Saab * This is just a guess, and we need not try too hard to make it accurate. 467*c77c4889SXin LI * 468*c77c4889SXin LI * The number of bytes read is returned to the caller, because it will 469*c77c4889SXin LI * be used later to compare to st_size from stat(2) to see if the file 470*c77c4889SXin LI * is lying about its size. 471a5f0fb15SPaul Saab */ 472*c77c4889SXin LI public int bin_file(int f, ssize_t *n) 473a5f0fb15SPaul Saab { 4747f074f9cSXin LI int bin_count = 0; 4757374caaaSXin LI char data[256]; 476*c77c4889SXin LI constant char* p; 477*c77c4889SXin LI constant char* edata; 478a5f0fb15SPaul Saab 479a5f0fb15SPaul Saab if (!seekable(f)) 480a5f0fb15SPaul Saab return (0); 481*c77c4889SXin LI if (less_lseek(f, (less_off_t)0, SEEK_SET) == BAD_LSEEK) 482a5f0fb15SPaul Saab return (0); 483*c77c4889SXin LI *n = read(f, data, sizeof(data)); 484*c77c4889SXin LI if (*n <= 0) 485a15691bfSXin LI return (0); 486*c77c4889SXin LI edata = &data[*n]; 487b2ea2440SXin LI for (p = data; p < edata; ) 488a15691bfSXin LI { 489*c77c4889SXin LI if (utf_mode && !is_utf8_well_formed(p, (int) ptr_diff(edata,p))) 490b2ea2440SXin LI { 491b2ea2440SXin LI bin_count++; 492b2ea2440SXin LI utf_skip_to_lead(&p, edata); 493a15691bfSXin LI } else 494a15691bfSXin LI { 495*c77c4889SXin LI LWCHAR c = step_charc(&p, +1, edata); 4962235c7feSXin LI struct ansi_state *pansi; 4972235c7feSXin LI if (ctldisp == OPT_ONPLUS && (pansi = ansi_start(c)) != NULL) 4982235c7feSXin LI { 4992235c7feSXin LI skip_ansi(pansi, &p, edata); 5002235c7feSXin LI ansi_done(pansi); 5012235c7feSXin LI } else if (binary_char(c)) 5027f074f9cSXin LI bin_count++; 5037f074f9cSXin LI } 504a15691bfSXin LI } 5057f074f9cSXin LI /* 5067f074f9cSXin LI * Call it a binary file if there are more than 5 binary characters 5077f074f9cSXin LI * in the first 256 bytes of the file. 5087f074f9cSXin LI */ 5097f074f9cSXin LI return (bin_count > 5); 510a5f0fb15SPaul Saab } 511a5f0fb15SPaul Saab 512a5f0fb15SPaul Saab /* 513a5f0fb15SPaul Saab * Try to determine the size of a file by seeking to the end. 514a5f0fb15SPaul Saab */ 515d713e089SXin LI static POSITION seek_filesize(int f) 516a5f0fb15SPaul Saab { 517*c77c4889SXin LI less_off_t spos; 518a5f0fb15SPaul Saab 519*c77c4889SXin LI spos = less_lseek(f, (less_off_t)0, SEEK_END); 520a5f0fb15SPaul Saab if (spos == BAD_LSEEK) 521a5f0fb15SPaul Saab return (NULL_POSITION); 522a5f0fb15SPaul Saab return ((POSITION) spos); 523a5f0fb15SPaul Saab } 524a5f0fb15SPaul Saab 52530a1828cSXin LI #if HAVE_POPEN 526a5f0fb15SPaul Saab /* 527a5f0fb15SPaul Saab * Read a string from a file. 528a5f0fb15SPaul Saab * Return a pointer to the string in memory. 529a5f0fb15SPaul Saab */ 530*c77c4889SXin LI public char * readfd(FILE *fd) 531a5f0fb15SPaul Saab { 532*c77c4889SXin LI struct xbuffer xbuf; 533*c77c4889SXin LI xbuf_init(&xbuf); 534*c77c4889SXin LI for (;;) 535*c77c4889SXin LI { 536a5f0fb15SPaul Saab int ch; 537a5f0fb15SPaul Saab if ((ch = getc(fd)) == '\n' || ch == EOF) 538a5f0fb15SPaul Saab break; 539*c77c4889SXin LI xbuf_add_char(&xbuf, (char) ch); 540a5f0fb15SPaul Saab } 541*c77c4889SXin LI xbuf_add_char(&xbuf, '\0'); 542*c77c4889SXin LI return (char *) xbuf.data; 543a5f0fb15SPaul Saab } 544a5f0fb15SPaul Saab 545a5f0fb15SPaul Saab /* 546a5f0fb15SPaul Saab * Execute a shell command. 547a5f0fb15SPaul Saab * Return a pointer to a pipe connected to the shell command's standard output. 548a5f0fb15SPaul Saab */ 549*c77c4889SXin LI static FILE * shellcmd(constant char *cmd) 550a5f0fb15SPaul Saab { 551a5f0fb15SPaul Saab FILE *fd; 552a5f0fb15SPaul Saab 553a5f0fb15SPaul Saab #if HAVE_SHELL 554*c77c4889SXin LI constant char *shell; 555a5f0fb15SPaul Saab 556a5f0fb15SPaul Saab shell = lgetenv("SHELL"); 557b7780dbeSXin LI if (!isnullenv(shell)) 558a5f0fb15SPaul Saab { 559a5f0fb15SPaul Saab char *scmd; 560a5f0fb15SPaul Saab char *esccmd; 561a5f0fb15SPaul Saab 562a5f0fb15SPaul Saab /* 563000ba3e8STim J. Robbins * Read the output of <$SHELL -c cmd>. 564000ba3e8STim J. Robbins * Escape any metacharacters in the command. 565a5f0fb15SPaul Saab */ 566000ba3e8STim J. Robbins esccmd = shell_quote(cmd); 567000ba3e8STim J. Robbins if (esccmd == NULL) 568a5f0fb15SPaul Saab { 569000ba3e8STim J. Robbins fd = popen(cmd, "r"); 570a5f0fb15SPaul Saab } else 571a5f0fb15SPaul Saab { 572*c77c4889SXin LI size_t len = strlen(shell) + strlen(esccmd) + 5; 5736dcb072bSXin LI scmd = (char *) ecalloc(len, sizeof(char)); 5746dcb072bSXin LI SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd); 575a5f0fb15SPaul Saab free(esccmd); 576a5f0fb15SPaul Saab fd = popen(scmd, "r"); 577a5f0fb15SPaul Saab free(scmd); 578000ba3e8STim J. Robbins } 579a5f0fb15SPaul Saab } else 580a5f0fb15SPaul Saab #endif 581a5f0fb15SPaul Saab { 582a5f0fb15SPaul Saab fd = popen(cmd, "r"); 583000ba3e8STim J. Robbins } 584a5f0fb15SPaul Saab /* 585a5f0fb15SPaul Saab * Redirection in `popen' might have messed with the 586a5f0fb15SPaul Saab * standard devices. Restore binary input mode. 587a5f0fb15SPaul Saab */ 588a5f0fb15SPaul Saab SET_BINARY(0); 589a5f0fb15SPaul Saab return (fd); 590a5f0fb15SPaul Saab } 591a5f0fb15SPaul Saab 592a5f0fb15SPaul Saab #endif /* HAVE_POPEN */ 593a5f0fb15SPaul Saab 594a5f0fb15SPaul Saab 595a5f0fb15SPaul Saab /* 596a5f0fb15SPaul Saab * Expand a filename, doing any system-specific metacharacter substitutions. 597a5f0fb15SPaul Saab */ 598*c77c4889SXin LI public char * lglob(constant char *afilename) 599a5f0fb15SPaul Saab { 600a5f0fb15SPaul Saab char *gfilename; 601*c77c4889SXin LI char *filename = fexpand(afilename); 602a5f0fb15SPaul Saab 603*c77c4889SXin LI if (!secure_allow(SF_GLOB)) 604b2ea2440SXin LI return (filename); 605a5f0fb15SPaul Saab 606a5f0fb15SPaul Saab #ifdef DECL_GLOB_LIST 607a5f0fb15SPaul Saab { 608a5f0fb15SPaul Saab /* 609a5f0fb15SPaul Saab * The globbing function returns a list of names. 610a5f0fb15SPaul Saab */ 611*c77c4889SXin LI size_t length; 612a5f0fb15SPaul Saab char *p; 613000ba3e8STim J. Robbins char *qfilename; 614a5f0fb15SPaul Saab DECL_GLOB_LIST(list) 615a5f0fb15SPaul Saab 616a5f0fb15SPaul Saab GLOB_LIST(filename, list); 617a5f0fb15SPaul Saab if (GLOB_LIST_FAILED(list)) 618a5f0fb15SPaul Saab { 619b2ea2440SXin LI return (filename); 620a5f0fb15SPaul Saab } 621a5f0fb15SPaul Saab length = 1; /* Room for trailing null byte */ 622a5f0fb15SPaul Saab for (SCAN_GLOB_LIST(list, p)) 623a5f0fb15SPaul Saab { 624a5f0fb15SPaul Saab INIT_GLOB_LIST(list, p); 625000ba3e8STim J. Robbins qfilename = shell_quote(p); 626000ba3e8STim J. Robbins if (qfilename != NULL) 627000ba3e8STim J. Robbins { 628000ba3e8STim J. Robbins length += strlen(qfilename) + 1; 629000ba3e8STim J. Robbins free(qfilename); 630000ba3e8STim J. Robbins } 631a5f0fb15SPaul Saab } 632a5f0fb15SPaul Saab gfilename = (char *) ecalloc(length, sizeof(char)); 633a5f0fb15SPaul Saab for (SCAN_GLOB_LIST(list, p)) 634a5f0fb15SPaul Saab { 635a5f0fb15SPaul Saab INIT_GLOB_LIST(list, p); 636000ba3e8STim J. Robbins qfilename = shell_quote(p); 637000ba3e8STim J. Robbins if (qfilename != NULL) 638000ba3e8STim J. Robbins { 639000ba3e8STim J. Robbins sprintf(gfilename + strlen(gfilename), "%s ", qfilename); 640000ba3e8STim J. Robbins free(qfilename); 641000ba3e8STim J. Robbins } 642a5f0fb15SPaul Saab } 643a5f0fb15SPaul Saab /* 644a5f0fb15SPaul Saab * Overwrite the final trailing space with a null terminator. 645a5f0fb15SPaul Saab */ 646a5f0fb15SPaul Saab *--p = '\0'; 647a5f0fb15SPaul Saab GLOB_LIST_DONE(list); 648a5f0fb15SPaul Saab } 649a5f0fb15SPaul Saab #else 650a5f0fb15SPaul Saab #ifdef DECL_GLOB_NAME 651a5f0fb15SPaul Saab { 652a5f0fb15SPaul Saab /* 653a5f0fb15SPaul Saab * The globbing function returns a single name, and 654a5f0fb15SPaul Saab * is called multiple times to walk thru all names. 655a5f0fb15SPaul Saab */ 6561ea31627SRobert Watson char *p; 657*c77c4889SXin LI size_t len; 658*c77c4889SXin LI size_t n; 659b2ea2440SXin LI char *pfilename; 660b2ea2440SXin LI char *qfilename; 661a5f0fb15SPaul Saab DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) 662a5f0fb15SPaul Saab 663a5f0fb15SPaul Saab GLOB_FIRST_NAME(filename, &fnd, handle); 664a5f0fb15SPaul Saab if (GLOB_FIRST_FAILED(handle)) 665a5f0fb15SPaul Saab { 666b2ea2440SXin LI return (filename); 667a5f0fb15SPaul Saab } 668a5f0fb15SPaul Saab 669a5f0fb15SPaul Saab _splitpath(filename, drive, dir, fname, ext); 670a5f0fb15SPaul Saab len = 100; 671a5f0fb15SPaul Saab gfilename = (char *) ecalloc(len, sizeof(char)); 672a5f0fb15SPaul Saab p = gfilename; 673a5f0fb15SPaul Saab do { 674*c77c4889SXin LI n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1; 675b2ea2440SXin LI pfilename = (char *) ecalloc(n, sizeof(char)); 676b2ea2440SXin LI SNPRINTF3(pfilename, n, "%s%s%s", drive, dir, fnd.GLOB_NAME); 677b2ea2440SXin LI qfilename = shell_quote(pfilename); 678b2ea2440SXin LI free(pfilename); 679b2ea2440SXin LI if (qfilename != NULL) 680a5f0fb15SPaul Saab { 681*c77c4889SXin LI n = strlen(qfilename); 682a5f0fb15SPaul Saab while (p - gfilename + n + 2 >= len) 683a5f0fb15SPaul Saab { 684a5f0fb15SPaul Saab /* 685000ba3e8STim J. Robbins * No room in current buffer. 686000ba3e8STim J. Robbins * Allocate a bigger one. 687a5f0fb15SPaul Saab */ 688a5f0fb15SPaul Saab len *= 2; 689a5f0fb15SPaul Saab *p = '\0'; 690a5f0fb15SPaul Saab p = (char *) ecalloc(len, sizeof(char)); 691a5f0fb15SPaul Saab strcpy(p, gfilename); 692a5f0fb15SPaul Saab free(gfilename); 693a5f0fb15SPaul Saab gfilename = p; 694a5f0fb15SPaul Saab p = gfilename + strlen(gfilename); 695a5f0fb15SPaul Saab } 696b2ea2440SXin LI strcpy(p, qfilename); 697b2ea2440SXin LI free(qfilename); 698a5f0fb15SPaul Saab p += n; 699000ba3e8STim J. Robbins *p++ = ' '; 700000ba3e8STim J. Robbins } 701a5f0fb15SPaul Saab } while (GLOB_NEXT_NAME(handle, &fnd) == 0); 702a5f0fb15SPaul Saab 703a5f0fb15SPaul Saab /* 704a5f0fb15SPaul Saab * Overwrite the final trailing space with a null terminator. 705a5f0fb15SPaul Saab */ 706a5f0fb15SPaul Saab *--p = '\0'; 707a5f0fb15SPaul Saab GLOB_NAME_DONE(handle); 708a5f0fb15SPaul Saab } 709a5f0fb15SPaul Saab #else 710a5f0fb15SPaul Saab #if HAVE_POPEN 711a5f0fb15SPaul Saab { 712a5f0fb15SPaul Saab /* 713a5f0fb15SPaul Saab * We get the shell to glob the filename for us by passing 714a5f0fb15SPaul Saab * an "echo" command to the shell and reading its output. 715a5f0fb15SPaul Saab */ 716a5f0fb15SPaul Saab FILE *fd; 717*c77c4889SXin LI constant char *s; 718*c77c4889SXin LI constant char *lessecho; 719a5f0fb15SPaul Saab char *cmd; 720*c77c4889SXin LI constant char *esc; 721*c77c4889SXin LI char *qesc; 722*c77c4889SXin LI size_t len; 723a5f0fb15SPaul Saab 724000ba3e8STim J. Robbins esc = get_meta_escape(); 725000ba3e8STim J. Robbins if (strlen(esc) == 0) 726000ba3e8STim J. Robbins esc = "-"; 727*c77c4889SXin LI qesc = shell_quote(esc); 728*c77c4889SXin LI if (qesc == NULL) 729a5f0fb15SPaul Saab { 730b2ea2440SXin LI return (filename); 731a5f0fb15SPaul Saab } 732000ba3e8STim J. Robbins lessecho = lgetenv("LESSECHO"); 733b7780dbeSXin LI if (isnullenv(lessecho)) 734000ba3e8STim J. Robbins lessecho = "lessecho"; 735a5f0fb15SPaul Saab /* 736a5f0fb15SPaul Saab * Invoke lessecho, and read its output (a globbed list of filenames). 737a5f0fb15SPaul Saab */ 738*c77c4889SXin LI len = strlen(lessecho) + strlen(filename) + (7*strlen(metachars())) + 24; 7396dcb072bSXin LI cmd = (char *) ecalloc(len, sizeof(char)); 74095270f73SXin LI SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, 741*c77c4889SXin LI (unsigned char) openquote, (unsigned char) closequote, qesc); 742*c77c4889SXin LI free(qesc); 743000ba3e8STim J. Robbins for (s = metachars(); *s != '\0'; s++) 74495270f73SXin LI sprintf(cmd + strlen(cmd), "-n0x%x ", (unsigned char) *s); 745b2ea2440SXin LI sprintf(cmd + strlen(cmd), "-- %s", filename); 746a5f0fb15SPaul Saab fd = shellcmd(cmd); 747a5f0fb15SPaul Saab free(cmd); 748a5f0fb15SPaul Saab if (fd == NULL) 749a5f0fb15SPaul Saab { 750a5f0fb15SPaul Saab /* 751a5f0fb15SPaul Saab * Cannot create the pipe. 752a5f0fb15SPaul Saab * Just return the original (fexpanded) filename. 753a5f0fb15SPaul Saab */ 754b2ea2440SXin LI return (filename); 755a5f0fb15SPaul Saab } 756a5f0fb15SPaul Saab gfilename = readfd(fd); 757a5f0fb15SPaul Saab pclose(fd); 758a5f0fb15SPaul Saab if (*gfilename == '\0') 759a5f0fb15SPaul Saab { 760a5f0fb15SPaul Saab free(gfilename); 761b7780dbeSXin LI return (filename); 762a5f0fb15SPaul Saab } 763a5f0fb15SPaul Saab } 764a5f0fb15SPaul Saab #else 765a5f0fb15SPaul Saab /* 766a5f0fb15SPaul Saab * No globbing functions at all. Just use the fexpanded filename. 767a5f0fb15SPaul Saab */ 768a5f0fb15SPaul Saab gfilename = save(filename); 769a5f0fb15SPaul Saab #endif 770a5f0fb15SPaul Saab #endif 771a5f0fb15SPaul Saab #endif 772a5f0fb15SPaul Saab free(filename); 773a5f0fb15SPaul Saab return (gfilename); 774a5f0fb15SPaul Saab } 775a5f0fb15SPaul Saab 776a5f0fb15SPaul Saab /* 777d713e089SXin LI * Does path not represent something in the file system? 778d713e089SXin LI */ 779*c77c4889SXin LI public lbool is_fake_pathname(constant char *path) 780d713e089SXin LI { 781d713e089SXin LI return (strcmp(path, "-") == 0 || 782d713e089SXin LI strcmp(path, FAKE_HELPFILE) == 0 || strcmp(path, FAKE_EMPTYFILE) == 0); 783d713e089SXin LI } 784d713e089SXin LI 785d713e089SXin LI /* 7866f26c71dSXin LI * Return canonical pathname. 787b7780dbeSXin LI */ 788*c77c4889SXin LI public char * lrealpath(constant char *path) 789d713e089SXin LI { 790d713e089SXin LI if (!is_fake_pathname(path)) 791b7780dbeSXin LI { 792b7780dbeSXin LI #if HAVE_REALPATH 793*c77c4889SXin LI /* 794*c77c4889SXin LI * Not all systems support the POSIX.1-2008 realpath() behavior 795*c77c4889SXin LI * of allocating when passing a NULL argument. And PATH_MAX is 796*c77c4889SXin LI * not required to be defined, or might contain an exceedingly 797*c77c4889SXin LI * big value. We assume that if it is not defined (such as on 798*c77c4889SXin LI * GNU/Hurd), then realpath() accepts NULL. 799*c77c4889SXin LI */ 800*c77c4889SXin LI #ifndef PATH_MAX 801*c77c4889SXin LI char *rpath; 802*c77c4889SXin LI 803*c77c4889SXin LI rpath = realpath(path, NULL); 804*c77c4889SXin LI if (rpath != NULL) 805*c77c4889SXin LI return (rpath); 806*c77c4889SXin LI #else 807b7780dbeSXin LI char rpath[PATH_MAX]; 808b7780dbeSXin LI if (realpath(path, rpath) != NULL) 809b7780dbeSXin LI return (save(rpath)); 810b7780dbeSXin LI #endif 811*c77c4889SXin LI #endif 812d713e089SXin LI } 813b7780dbeSXin LI return (save(path)); 814b7780dbeSXin LI } 815b7780dbeSXin LI 81630a1828cSXin LI #if HAVE_POPEN 817b7780dbeSXin LI /* 81896e55cc7SXin LI * Return number of %s escapes in a string. 81996e55cc7SXin LI * Return a large number if there are any other % escapes besides %s. 82096e55cc7SXin LI */ 821*c77c4889SXin LI static int num_pct_s(constant char *lessopen) 82296e55cc7SXin LI { 823a15691bfSXin LI int num = 0; 82496e55cc7SXin LI 825a15691bfSXin LI while (*lessopen != '\0') 82696e55cc7SXin LI { 827a15691bfSXin LI if (*lessopen == '%') 828a15691bfSXin LI { 829a15691bfSXin LI if (lessopen[1] == '%') 830a15691bfSXin LI ++lessopen; 831a15691bfSXin LI else if (lessopen[1] == 's') 832a15691bfSXin LI ++num; 833a15691bfSXin LI else 83496e55cc7SXin LI return (999); 83596e55cc7SXin LI } 836a15691bfSXin LI ++lessopen; 837a15691bfSXin LI } 83896e55cc7SXin LI return (num); 83996e55cc7SXin LI } 84030a1828cSXin LI #endif 84196e55cc7SXin LI 84296e55cc7SXin LI /* 843a5f0fb15SPaul Saab * See if we should open a "replacement file" 844a5f0fb15SPaul Saab * instead of the file we're about to open. 845a5f0fb15SPaul Saab */ 846*c77c4889SXin LI public char * open_altfile(constant char *filename, int *pf, void **pfd) 847a5f0fb15SPaul Saab { 848a5f0fb15SPaul Saab #if !HAVE_POPEN 849a5f0fb15SPaul Saab return (NULL); 850a5f0fb15SPaul Saab #else 851*c77c4889SXin LI constant char *lessopen; 852b2ea2440SXin LI char *qfilename; 853a5f0fb15SPaul Saab char *cmd; 854*c77c4889SXin LI size_t len; 855a5f0fb15SPaul Saab FILE *fd; 856a5f0fb15SPaul Saab #if HAVE_FILENO 857a5f0fb15SPaul Saab int returnfd = 0; 858a5f0fb15SPaul Saab #endif 859a5f0fb15SPaul Saab 860*c77c4889SXin LI if (!secure_allow(SF_LESSOPEN)) 861*c77c4889SXin LI return (NULL); 862*c77c4889SXin LI if (!use_lessopen) 863a5f0fb15SPaul Saab return (NULL); 864a5f0fb15SPaul Saab ch_ungetchar(-1); 865a5f0fb15SPaul Saab if ((lessopen = lgetenv("LESSOPEN")) == NULL) 866a5f0fb15SPaul Saab return (NULL); 86796e55cc7SXin LI while (*lessopen == '|') 868a5f0fb15SPaul Saab { 869a5f0fb15SPaul Saab /* 870a5f0fb15SPaul Saab * If LESSOPEN starts with a |, it indicates 871a5f0fb15SPaul Saab * a "pipe preprocessor". 872a5f0fb15SPaul Saab */ 8737374caaaSXin LI #if !HAVE_FILENO 874a5f0fb15SPaul Saab error("LESSOPEN pipe is not supported", NULL_PARG); 875a5f0fb15SPaul Saab return (NULL); 8767374caaaSXin LI #else 8777374caaaSXin LI lessopen++; 87896e55cc7SXin LI returnfd++; 879f0be0a1fSXin LI #endif 880f0be0a1fSXin LI } 881b2ea2440SXin LI if (*lessopen == '-') 882b2ea2440SXin LI { 8837374caaaSXin LI /* 8847374caaaSXin LI * Lessopen preprocessor will accept "-" as a filename. 8857374caaaSXin LI */ 8867374caaaSXin LI lessopen++; 887b2ea2440SXin LI } else 888b2ea2440SXin LI { 8897374caaaSXin LI if (strcmp(filename, "-") == 0) 8907374caaaSXin LI return (NULL); 8917374caaaSXin LI } 892b2ea2440SXin LI if (num_pct_s(lessopen) != 1) 89396e55cc7SXin LI { 894b2ea2440SXin LI error("LESSOPEN ignored: must contain exactly one %%s", NULL_PARG); 89596e55cc7SXin LI return (NULL); 89696e55cc7SXin LI } 897a5f0fb15SPaul Saab 898b2ea2440SXin LI qfilename = shell_quote(filename); 899*c77c4889SXin LI len = strlen(lessopen) + strlen(qfilename) + 2; 9006dcb072bSXin LI cmd = (char *) ecalloc(len, sizeof(char)); 901b2ea2440SXin LI SNPRINTF1(cmd, len, lessopen, qfilename); 902b2ea2440SXin LI free(qfilename); 903a5f0fb15SPaul Saab fd = shellcmd(cmd); 904a5f0fb15SPaul Saab free(cmd); 905a5f0fb15SPaul Saab if (fd == NULL) 906a5f0fb15SPaul Saab { 907a5f0fb15SPaul Saab /* 908a5f0fb15SPaul Saab * Cannot create the pipe. 909a5f0fb15SPaul Saab */ 910a5f0fb15SPaul Saab return (NULL); 911a5f0fb15SPaul Saab } 912a5f0fb15SPaul Saab #if HAVE_FILENO 913a5f0fb15SPaul Saab if (returnfd) 914a5f0fb15SPaul Saab { 915*c77c4889SXin LI unsigned char c; 916b2ea2440SXin LI int f; 917a5f0fb15SPaul Saab 918a5f0fb15SPaul Saab /* 9192235c7feSXin LI * The alt file is a pipe. Read one char 920b2ea2440SXin LI * to see if the pipe will produce any data. 921a5f0fb15SPaul Saab * If it does, push the char back on the pipe. 922a5f0fb15SPaul Saab */ 923a5f0fb15SPaul Saab f = fileno(fd); 924a5f0fb15SPaul Saab SET_BINARY(f); 925a5f0fb15SPaul Saab if (read(f, &c, 1) != 1) 926a5f0fb15SPaul Saab { 927a5f0fb15SPaul Saab /* 92896e55cc7SXin LI * Pipe is empty. 92996e55cc7SXin LI * If more than 1 pipe char was specified, 93096e55cc7SXin LI * the exit status tells whether the file itself 93196e55cc7SXin LI * is empty, or if there is no alt file. 93296e55cc7SXin LI * If only one pipe char, just assume no alt file. 933a5f0fb15SPaul Saab */ 93496e55cc7SXin LI int status = pclose(fd); 93596e55cc7SXin LI if (returnfd > 1 && status == 0) { 9362235c7feSXin LI /* File is empty. */ 93796e55cc7SXin LI *pfd = NULL; 93896e55cc7SXin LI *pf = -1; 93996e55cc7SXin LI return (save(FAKE_EMPTYFILE)); 94096e55cc7SXin LI } 9412235c7feSXin LI /* No alt file. */ 942a5f0fb15SPaul Saab return (NULL); 943a5f0fb15SPaul Saab } 9442235c7feSXin LI /* Alt pipe contains data, so use it. */ 945a5f0fb15SPaul Saab ch_ungetchar(c); 946a5f0fb15SPaul Saab *pfd = (void *) fd; 947a5f0fb15SPaul Saab *pf = f; 948a5f0fb15SPaul Saab return (save("-")); 949a5f0fb15SPaul Saab } 950a5f0fb15SPaul Saab #endif 9512235c7feSXin LI /* The alt file is a regular file. Read its name from LESSOPEN. */ 952000ba3e8STim J. Robbins cmd = readfd(fd); 953a5f0fb15SPaul Saab pclose(fd); 954000ba3e8STim J. Robbins if (*cmd == '\0') 95530a1828cSXin LI { 956a5f0fb15SPaul Saab /* 957a5f0fb15SPaul Saab * Pipe is empty. This means there is no alt file. 958a5f0fb15SPaul Saab */ 95930a1828cSXin LI free(cmd); 960a5f0fb15SPaul Saab return (NULL); 96130a1828cSXin LI } 962000ba3e8STim J. Robbins return (cmd); 963a5f0fb15SPaul Saab #endif /* HAVE_POPEN */ 964a5f0fb15SPaul Saab } 965a5f0fb15SPaul Saab 966a5f0fb15SPaul Saab /* 967a5f0fb15SPaul Saab * Close a replacement file. 968a5f0fb15SPaul Saab */ 969*c77c4889SXin LI public void close_altfile(constant char *altfilename, constant char *filename) 970a5f0fb15SPaul Saab { 971a5f0fb15SPaul Saab #if HAVE_POPEN 972*c77c4889SXin LI constant char *lessclose; 97395270f73SXin LI char *qfilename; 97495270f73SXin LI char *qaltfilename; 975a5f0fb15SPaul Saab FILE *fd; 976a5f0fb15SPaul Saab char *cmd; 977*c77c4889SXin LI size_t len; 978a5f0fb15SPaul Saab 979*c77c4889SXin LI if (!secure_allow(SF_LESSOPEN)) 980a5f0fb15SPaul Saab return; 981a5f0fb15SPaul Saab if ((lessclose = lgetenv("LESSCLOSE")) == NULL) 982a5f0fb15SPaul Saab return; 98396e55cc7SXin LI if (num_pct_s(lessclose) > 2) 98496e55cc7SXin LI { 985b2ea2440SXin LI error("LESSCLOSE ignored; must contain no more than 2 %%s", NULL_PARG); 98696e55cc7SXin LI return; 98796e55cc7SXin LI } 98895270f73SXin LI qfilename = shell_quote(filename); 98995270f73SXin LI qaltfilename = shell_quote(altfilename); 990*c77c4889SXin LI len = strlen(lessclose) + strlen(qfilename) + strlen(qaltfilename) + 2; 9916dcb072bSXin LI cmd = (char *) ecalloc(len, sizeof(char)); 99295270f73SXin LI SNPRINTF2(cmd, len, lessclose, qfilename, qaltfilename); 99395270f73SXin LI free(qaltfilename); 99495270f73SXin LI free(qfilename); 995a5f0fb15SPaul Saab fd = shellcmd(cmd); 996a5f0fb15SPaul Saab free(cmd); 997a5f0fb15SPaul Saab if (fd != NULL) 998a5f0fb15SPaul Saab pclose(fd); 999a5f0fb15SPaul Saab #endif 1000a5f0fb15SPaul Saab } 1001a5f0fb15SPaul Saab 1002a5f0fb15SPaul Saab /* 1003a5f0fb15SPaul Saab * Is the specified file a directory? 1004a5f0fb15SPaul Saab */ 1005*c77c4889SXin LI public lbool is_dir(constant char *filename) 1006a5f0fb15SPaul Saab { 1007*c77c4889SXin LI lbool isdir = FALSE; 1008a5f0fb15SPaul Saab 1009a5f0fb15SPaul Saab #if HAVE_STAT 1010a5f0fb15SPaul Saab { 1011a5f0fb15SPaul Saab int r; 1012*c77c4889SXin LI less_stat_t statbuf; 1013a5f0fb15SPaul Saab 1014*c77c4889SXin LI r = less_stat(filename, &statbuf); 1015a5f0fb15SPaul Saab isdir = (r >= 0 && S_ISDIR(statbuf.st_mode)); 1016a5f0fb15SPaul Saab } 1017a5f0fb15SPaul Saab #else 1018a5f0fb15SPaul Saab #ifdef _OSK 1019a5f0fb15SPaul Saab { 10201ea31627SRobert Watson int f; 1021a5f0fb15SPaul Saab 1022a5f0fb15SPaul Saab f = open(filename, S_IREAD | S_IFDIR); 1023a5f0fb15SPaul Saab if (f >= 0) 1024a5f0fb15SPaul Saab close(f); 1025a5f0fb15SPaul Saab isdir = (f >= 0); 1026a5f0fb15SPaul Saab } 1027a5f0fb15SPaul Saab #endif 1028a5f0fb15SPaul Saab #endif 1029a5f0fb15SPaul Saab return (isdir); 1030a5f0fb15SPaul Saab } 1031a5f0fb15SPaul Saab 1032a5f0fb15SPaul Saab /* 1033a5f0fb15SPaul Saab * Returns NULL if the file can be opened and 1034a5f0fb15SPaul Saab * is an ordinary file, otherwise an error message 1035a5f0fb15SPaul Saab * (if it cannot be opened or is a directory, etc.) 1036a5f0fb15SPaul Saab */ 1037*c77c4889SXin LI public char * bad_file(constant char *filename) 1038a5f0fb15SPaul Saab { 10391ea31627SRobert Watson char *m = NULL; 1040a5f0fb15SPaul Saab 10417f074f9cSXin LI if (!force_open && is_dir(filename)) 1042a5f0fb15SPaul Saab { 1043000ba3e8STim J. Robbins static char is_a_dir[] = " is a directory"; 1044a5f0fb15SPaul Saab 1045000ba3e8STim J. Robbins m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir), 1046a5f0fb15SPaul Saab sizeof(char)); 1047a5f0fb15SPaul Saab strcpy(m, filename); 1048000ba3e8STim J. Robbins strcat(m, is_a_dir); 1049a5f0fb15SPaul Saab } else 1050a5f0fb15SPaul Saab { 1051a5f0fb15SPaul Saab #if HAVE_STAT 1052a5f0fb15SPaul Saab int r; 1053*c77c4889SXin LI less_stat_t statbuf; 1054a5f0fb15SPaul Saab 1055*c77c4889SXin LI r = less_stat(filename, &statbuf); 1056a5f0fb15SPaul Saab if (r < 0) 1057a5f0fb15SPaul Saab { 1058a5f0fb15SPaul Saab m = errno_message(filename); 1059a5f0fb15SPaul Saab } else if (force_open) 1060a5f0fb15SPaul Saab { 1061a5f0fb15SPaul Saab m = NULL; 1062a5f0fb15SPaul Saab } else if (!S_ISREG(statbuf.st_mode)) 1063a5f0fb15SPaul Saab { 1064a5f0fb15SPaul Saab static char not_reg[] = " is not a regular file (use -f to see it)"; 1065a5f0fb15SPaul Saab m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 1066a5f0fb15SPaul Saab sizeof(char)); 1067a5f0fb15SPaul Saab strcpy(m, filename); 1068a5f0fb15SPaul Saab strcat(m, not_reg); 1069a5f0fb15SPaul Saab } 1070a5f0fb15SPaul Saab #endif 1071a5f0fb15SPaul Saab } 1072a5f0fb15SPaul Saab return (m); 1073a5f0fb15SPaul Saab } 1074a5f0fb15SPaul Saab 1075a5f0fb15SPaul Saab /* 1076a5f0fb15SPaul Saab * Return the size of a file, as cheaply as possible. 1077a5f0fb15SPaul Saab * In Unix, we can stat the file. 1078a5f0fb15SPaul Saab */ 1079d713e089SXin LI public POSITION filesize(int f) 1080a5f0fb15SPaul Saab { 1081a5f0fb15SPaul Saab #if HAVE_STAT 1082*c77c4889SXin LI less_stat_t statbuf; 1083a5f0fb15SPaul Saab 1084*c77c4889SXin LI if (less_fstat(f, &statbuf) >= 0) 1085a5f0fb15SPaul Saab return ((POSITION) statbuf.st_size); 1086a5f0fb15SPaul Saab #else 1087a5f0fb15SPaul Saab #ifdef _OSK 1088a5f0fb15SPaul Saab long size; 1089a5f0fb15SPaul Saab 1090a5f0fb15SPaul Saab if ((size = (long) _gs_size(f)) >= 0) 1091a5f0fb15SPaul Saab return ((POSITION) size); 1092a5f0fb15SPaul Saab #endif 1093a5f0fb15SPaul Saab #endif 1094a5f0fb15SPaul Saab return (seek_filesize(f)); 1095a5f0fb15SPaul Saab } 1096a5f0fb15SPaul Saab 1097*c77c4889SXin LI public lbool curr_ifile_changed(void) 1098d713e089SXin LI { 1099d713e089SXin LI #if HAVE_STAT_INO 1100d713e089SXin LI /* 1101d713e089SXin LI * If the file's i-number or device has changed, 1102d713e089SXin LI * or if the file is smaller than it previously was, 1103d713e089SXin LI * the file must be different. 1104d713e089SXin LI */ 1105d713e089SXin LI struct stat st; 1106d713e089SXin LI POSITION curr_pos = ch_tell(); 1107d713e089SXin LI int r = stat(get_filename(curr_ifile), &st); 1108d713e089SXin LI if (r == 0 && (st.st_ino != curr_ino || 1109d713e089SXin LI st.st_dev != curr_dev || 1110d713e089SXin LI (curr_pos != NULL_POSITION && st.st_size < curr_pos))) 1111d713e089SXin LI return (TRUE); 1112d713e089SXin LI #endif 1113d713e089SXin LI return (FALSE); 1114d713e089SXin LI } 1115d713e089SXin LI 1116000ba3e8STim J. Robbins /* 1117000ba3e8STim J. Robbins * 1118000ba3e8STim J. Robbins */ 1119*c77c4889SXin LI public constant char * shell_coption(void) 1120000ba3e8STim J. Robbins { 1121000ba3e8STim J. Robbins return ("-c"); 1122000ba3e8STim J. Robbins } 112333096f16SXin LI 112433096f16SXin LI /* 112533096f16SXin LI * Return last component of a pathname. 112633096f16SXin LI */ 1127*c77c4889SXin LI public constant char * last_component(constant char *name) 112833096f16SXin LI { 1129*c77c4889SXin LI constant char *slash; 113033096f16SXin LI 113133096f16SXin LI for (slash = name + strlen(name); slash > name; ) 113233096f16SXin LI { 113333096f16SXin LI --slash; 113433096f16SXin LI if (*slash == *PATHNAME_SEP || *slash == '/') 113533096f16SXin LI return (slash + 1); 113633096f16SXin LI } 113733096f16SXin LI return (name); 113833096f16SXin LI } 1139