filename.c revision 191930
1130803Smarcel/* 2130803Smarcel * Copyright (C) 1984-2008 Mark Nudelman 3130803Smarcel * 4130803Smarcel * You may distribute under the terms of either the GNU General Public 5130803Smarcel * License or the Less License, as specified in the README file. 6130803Smarcel * 7130803Smarcel * For more information about less, or for information on how to 8130803Smarcel * contact the author, see the README file. 9130803Smarcel */ 10130803Smarcel 11130803Smarcel 12130803Smarcel/* 13130803Smarcel * Routines to mess around with filenames (and files). 14130803Smarcel * Much of this is very OS dependent. 15130803Smarcel */ 16130803Smarcel 17130803Smarcel#include "less.h" 18130803Smarcel#include "lglob.h" 19130803Smarcel#if MSDOS_COMPILER 20130803Smarcel#include <dos.h> 21130803Smarcel#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER) 22130803Smarcel#include <dir.h> 23130803Smarcel#endif 24130803Smarcel#if MSDOS_COMPILER==DJGPPC 25130803Smarcel#include <glob.h> 26130803Smarcel#include <dir.h> 27130803Smarcel#define _MAX_PATH PATH_MAX 28130803Smarcel#endif 29130803Smarcel#endif 30130803Smarcel#ifdef _OSK 31130803Smarcel#include <rbf.h> 32130803Smarcel#ifndef _OSK_MWC32 33130803Smarcel#include <modes.h> 34130803Smarcel#endif 35130803Smarcel#endif 36130803Smarcel#if OS2 37130803Smarcel#include <signal.h> 38130803Smarcel#endif 39130803Smarcel 40130803Smarcel#if HAVE_STAT 41130803Smarcel#include <sys/stat.h> 42130803Smarcel#ifndef S_ISDIR 43130803Smarcel#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 44130803Smarcel#endif 45130803Smarcel#ifndef S_ISREG 46130803Smarcel#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 47130803Smarcel#endif 48130803Smarcel#endif 49130803Smarcel 50130803Smarcel 51130803Smarcelextern int force_open; 52130803Smarcelextern int secure; 53130803Smarcelextern int use_lessopen; 54130803Smarcelextern int ctldisp; 55130803Smarcelextern int utf_mode; 56130803Smarcelextern IFILE curr_ifile; 57130803Smarcelextern IFILE old_ifile; 58130803Smarcel#if SPACES_IN_FILENAMES 59130803Smarcelextern char openquote; 60130803Smarcelextern char closequote; 61130803Smarcel#endif 62130803Smarcel 63130803Smarcel/* 64130803Smarcel * Remove quotes around a filename. 65130803Smarcel */ 66130803Smarcel public char * 67130803Smarcelshell_unquote(str) 68130803Smarcel char *str; 69130803Smarcel{ 70130803Smarcel char *name; 71130803Smarcel char *p; 72130803Smarcel 73130803Smarcel name = p = (char *) ecalloc(strlen(str)+1, sizeof(char)); 74130803Smarcel if (*str == openquote) 75130803Smarcel { 76130803Smarcel str++; 77130803Smarcel while (*str != '\0') 78130803Smarcel { 79130803Smarcel if (*str == closequote) 80130803Smarcel { 81130803Smarcel if (str[1] != closequote) 82130803Smarcel break; 83130803Smarcel str++; 84130803Smarcel } 85130803Smarcel *p++ = *str++; 86130803Smarcel } 87130803Smarcel } else 88130803Smarcel { 89130803Smarcel char *esc = get_meta_escape(); 90130803Smarcel int esclen = strlen(esc); 91130803Smarcel while (*str != '\0') 92130803Smarcel { 93130803Smarcel if (esclen > 0 && strncmp(str, esc, esclen) == 0) 94130803Smarcel str += esclen; 95130803Smarcel *p++ = *str++; 96130803Smarcel } 97130803Smarcel } 98130803Smarcel *p = '\0'; 99130803Smarcel return (name); 100130803Smarcel} 101130803Smarcel 102130803Smarcel/* 103130803Smarcel * Get the shell's escape character. 104130803Smarcel */ 105130803Smarcel public char * 106130803Smarcelget_meta_escape() 107130803Smarcel{ 108130803Smarcel char *s; 109130803Smarcel 110130803Smarcel s = lgetenv("LESSMETAESCAPE"); 111130803Smarcel if (s == NULL) 112130803Smarcel s = DEF_METAESCAPE; 113130803Smarcel return (s); 114130803Smarcel} 115130803Smarcel 116130803Smarcel/* 117130803Smarcel * Get the characters which the shell considers to be "metacharacters". 118130803Smarcel */ 119130803Smarcel static char * 120130803Smarcelmetachars() 121130803Smarcel{ 122130803Smarcel static char *mchars = NULL; 123130803Smarcel 124130803Smarcel if (mchars == NULL) 125130803Smarcel { 126130803Smarcel mchars = lgetenv("LESSMETACHARS"); 127130803Smarcel if (mchars == NULL) 128130803Smarcel mchars = DEF_METACHARS; 129130803Smarcel } 130130803Smarcel return (mchars); 131130803Smarcel} 132130803Smarcel 133130803Smarcel/* 134130803Smarcel * Is this a shell metacharacter? 135130803Smarcel */ 136130803Smarcel static int 137130803Smarcelmetachar(c) 138130803Smarcel char c; 139130803Smarcel{ 140130803Smarcel return (strchr(metachars(), c) != NULL); 141130803Smarcel} 142130803Smarcel 143130803Smarcel/* 144130803Smarcel * Insert a backslash before each metacharacter in a string. 145130803Smarcel */ 146130803Smarcel public char * 147130803Smarcelshell_quote(s) 148130803Smarcel char *s; 149130803Smarcel{ 150130803Smarcel char *p; 151130803Smarcel char *newstr; 152130803Smarcel int len; 153130803Smarcel char *esc = get_meta_escape(); 154130803Smarcel int esclen = strlen(esc); 155130803Smarcel int use_quotes = 0; 156130803Smarcel int have_quotes = 0; 157130803Smarcel 158130803Smarcel /* 159130803Smarcel * Determine how big a string we need to allocate. 160130803Smarcel */ 161130803Smarcel len = 1; /* Trailing null byte */ 162130803Smarcel for (p = s; *p != '\0'; p++) 163130803Smarcel { 164130803Smarcel len++; 165130803Smarcel if (*p == openquote || *p == closequote) 166130803Smarcel have_quotes = 1; 167130803Smarcel if (metachar(*p)) 168130803Smarcel { 169130803Smarcel if (esclen == 0) 170130803Smarcel { 171130803Smarcel /* 172130803Smarcel * We've got a metachar, but this shell 173130803Smarcel * doesn't support escape chars. Use quotes. 174130803Smarcel */ 175130803Smarcel use_quotes = 1; 176130803Smarcel } else 177130803Smarcel { 178130803Smarcel /* 179130803Smarcel * Allow space for the escape char. 180130803Smarcel */ 181130803Smarcel len += esclen; 182130803Smarcel } 183130803Smarcel } 184130803Smarcel } 185130803Smarcel if (use_quotes) 186130803Smarcel { 187130803Smarcel if (have_quotes) 188130803Smarcel /* 189130803Smarcel * We can't quote a string that contains quotes. 190130803Smarcel */ 191130803Smarcel return (NULL); 192130803Smarcel len = strlen(s) + 3; 193130803Smarcel } 194130803Smarcel /* 195130803Smarcel * Allocate and construct the new string. 196130803Smarcel */ 197130803Smarcel newstr = p = (char *) ecalloc(len, sizeof(char)); 198130803Smarcel if (use_quotes) 199130803Smarcel { 200130803Smarcel SNPRINTF3(newstr, len, "%c%s%c", openquote, s, closequote); 201130803Smarcel } else 202130803Smarcel { 203130803Smarcel while (*s != '\0') 204130803Smarcel { 205130803Smarcel if (metachar(*s)) 206130803Smarcel { 207130803Smarcel /* 208130803Smarcel * Add the escape char. 209130803Smarcel */ 210130803Smarcel strcpy(p, esc); 211130803Smarcel p += esclen; 212130803Smarcel } 213130803Smarcel *p++ = *s++; 214130803Smarcel } 215130803Smarcel *p = '\0'; 216130803Smarcel } 217130803Smarcel return (newstr); 218130803Smarcel} 219130803Smarcel 220130803Smarcel/* 221130803Smarcel * Return a pathname that points to a specified file in a specified directory. 222130803Smarcel * Return NULL if the file does not exist in the directory. 223130803Smarcel */ 224130803Smarcel static char * 225130803Smarceldirfile(dirname, filename) 226130803Smarcel char *dirname; 227130803Smarcel char *filename; 228130803Smarcel{ 229130803Smarcel char *pathname; 230130803Smarcel char *qpathname; 231130803Smarcel int len; 232130803Smarcel int f; 233130803Smarcel 234130803Smarcel if (dirname == NULL || *dirname == '\0') 235130803Smarcel return (NULL); 236130803Smarcel /* 237130803Smarcel * Construct the full pathname. 238130803Smarcel */ 239130803Smarcel len= strlen(dirname) + strlen(filename) + 2; 240130803Smarcel pathname = (char *) calloc(len, sizeof(char)); 241130803Smarcel if (pathname == NULL) 242130803Smarcel return (NULL); 243130803Smarcel SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename); 244130803Smarcel /* 245130803Smarcel * Make sure the file exists. 246130803Smarcel */ 247130803Smarcel qpathname = shell_unquote(pathname); 248130803Smarcel f = open(qpathname, OPEN_READ); 249130803Smarcel if (f < 0) 250130803Smarcel { 251130803Smarcel free(pathname); 252130803Smarcel pathname = NULL; 253130803Smarcel } else 254130803Smarcel { 255130803Smarcel close(f); 256130803Smarcel } 257130803Smarcel free(qpathname); 258130803Smarcel return (pathname); 259130803Smarcel} 260130803Smarcel 261130803Smarcel/* 262130803Smarcel * Return the full pathname of the given file in the "home directory". 263130803Smarcel */ 264130803Smarcel public char * 265130803Smarcelhomefile(filename) 266130803Smarcel char *filename; 267130803Smarcel{ 268130803Smarcel register char *pathname; 269130803Smarcel 270130803Smarcel /* 271130803Smarcel * Try $HOME/filename. 272130803Smarcel */ 273130803Smarcel pathname = dirfile(lgetenv("HOME"), filename); 274130803Smarcel if (pathname != NULL) 275130803Smarcel return (pathname); 276130803Smarcel#if OS2 277130803Smarcel /* 278130803Smarcel * Try $INIT/filename. 279130803Smarcel */ 280130803Smarcel pathname = dirfile(lgetenv("INIT"), filename); 281130803Smarcel if (pathname != NULL) 282130803Smarcel return (pathname); 283130803Smarcel#endif 284130803Smarcel#if MSDOS_COMPILER || OS2 285130803Smarcel /* 286130803Smarcel * Look for the file anywhere on search path. 287130803Smarcel */ 288130803Smarcel pathname = (char *) calloc(_MAX_PATH, sizeof(char)); 289130803Smarcel#if MSDOS_COMPILER==DJGPPC 290130803Smarcel { 291130803Smarcel char *res = searchpath(filename); 292130803Smarcel if (res == 0) 293130803Smarcel *pathname = '\0'; 294130803Smarcel else 295130803Smarcel strcpy(pathname, res); 296130803Smarcel } 297130803Smarcel#else 298130803Smarcel _searchenv(filename, "PATH", pathname); 299130803Smarcel#endif 300130803Smarcel if (*pathname != '\0') 301130803Smarcel return (pathname); 302130803Smarcel free(pathname); 303130803Smarcel#endif 304130803Smarcel return (NULL); 305130803Smarcel} 306130803Smarcel 307130803Smarcel/* 308130803Smarcel * Expand a string, substituting any "%" with the current filename, 309130803Smarcel * and any "#" with the previous filename. 310130803Smarcel * But a string of N "%"s is just replaced with N-1 "%"s. 311130803Smarcel * Likewise for a string of N "#"s. 312130803Smarcel * {{ This is a lot of work just to support % and #. }} 313130803Smarcel */ 314130803Smarcel public char * 315130803Smarcelfexpand(s) 316130803Smarcel char *s; 317130803Smarcel{ 318130803Smarcel register char *fr, *to; 319130803Smarcel register int n; 320130803Smarcel register char *e; 321130803Smarcel IFILE ifile; 322130803Smarcel 323130803Smarcel#define fchar_ifile(c) \ 324130803Smarcel ((c) == '%' ? curr_ifile : \ 325130803Smarcel (c) == '#' ? old_ifile : NULL_IFILE) 326130803Smarcel 327130803Smarcel /* 328130803Smarcel * Make one pass to see how big a buffer we 329130803Smarcel * need to allocate for the expanded string. 330130803Smarcel */ 331130803Smarcel n = 0; 332130803Smarcel for (fr = s; *fr != '\0'; fr++) 333130803Smarcel { 334130803Smarcel switch (*fr) 335130803Smarcel { 336130803Smarcel case '%': 337130803Smarcel case '#': 338130803Smarcel if (fr > s && fr[-1] == *fr) 339130803Smarcel { 340130803Smarcel /* 341130803Smarcel * Second (or later) char in a string 342130803Smarcel * of identical chars. Treat as normal. 343130803Smarcel */ 344130803Smarcel n++; 345130803Smarcel } else if (fr[1] != *fr) 346130803Smarcel { 347130803Smarcel /* 348130803Smarcel * Single char (not repeated). Treat specially. 349130803Smarcel */ 350130803Smarcel ifile = fchar_ifile(*fr); 351130803Smarcel if (ifile == NULL_IFILE) 352130803Smarcel n++; 353130803Smarcel else 354130803Smarcel n += strlen(get_filename(ifile)); 355130803Smarcel } 356130803Smarcel /* 357130803Smarcel * Else it is the first char in a string of 358130803Smarcel * identical chars. Just discard it. 359130803Smarcel */ 360130803Smarcel break; 361130803Smarcel default: 362130803Smarcel n++; 363130803Smarcel break; 364130803Smarcel } 365130803Smarcel } 366130803Smarcel 367130803Smarcel e = (char *) ecalloc(n+1, sizeof(char)); 368130803Smarcel 369130803Smarcel /* 370130803Smarcel * Now copy the string, expanding any "%" or "#". 371130803Smarcel */ 372130803Smarcel to = e; 373130803Smarcel for (fr = s; *fr != '\0'; fr++) 374130803Smarcel { 375130803Smarcel switch (*fr) 376130803Smarcel { 377130803Smarcel case '%': 378130803Smarcel case '#': 379130803Smarcel if (fr > s && fr[-1] == *fr) 380130803Smarcel { 381130803Smarcel *to++ = *fr; 382130803Smarcel } else if (fr[1] != *fr) 383130803Smarcel { 384130803Smarcel ifile = fchar_ifile(*fr); 385130803Smarcel if (ifile == NULL_IFILE) 386130803Smarcel *to++ = *fr; 387130803Smarcel else 388130803Smarcel { 389130803Smarcel strcpy(to, get_filename(ifile)); 390130803Smarcel to += strlen(to); 391130803Smarcel } 392130803Smarcel } 393130803Smarcel break; 394130803Smarcel default: 395130803Smarcel *to++ = *fr; 396130803Smarcel break; 397130803Smarcel } 398130803Smarcel } 399130803Smarcel *to = '\0'; 400130803Smarcel return (e); 401130803Smarcel} 402130803Smarcel 403130803Smarcel#if TAB_COMPLETE_FILENAME 404130803Smarcel 405130803Smarcel/* 406130803Smarcel * Return a blank-separated list of filenames which "complete" 407130803Smarcel * the given string. 408130803Smarcel */ 409130803Smarcel public char * 410130803Smarcelfcomplete(s) 411130803Smarcel char *s; 412130803Smarcel{ 413130803Smarcel char *fpat; 414130803Smarcel char *qs; 415130803Smarcel 416130803Smarcel if (secure) 417130803Smarcel return (NULL); 418130803Smarcel /* 419130803Smarcel * Complete the filename "s" by globbing "s*". 420130803Smarcel */ 421130803Smarcel#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC) 422130803Smarcel /* 423130803Smarcel * But in DOS, we have to glob "s*.*". 424130803Smarcel * But if the final component of the filename already has 425130803Smarcel * a dot in it, just do "s*". 426130803Smarcel * (Thus, "FILE" is globbed as "FILE*.*", 427130803Smarcel * but "FILE.A" is globbed as "FILE.A*"). 428130803Smarcel */ 429130803Smarcel { 430130803Smarcel char *slash; 431130803Smarcel int len; 432130803Smarcel for (slash = s+strlen(s)-1; slash > s; slash--) 433130803Smarcel if (*slash == *PATHNAME_SEP || *slash == '/') 434130803Smarcel break; 435130803Smarcel len = strlen(s) + 4; 436130803Smarcel fpat = (char *) ecalloc(len, sizeof(char)); 437130803Smarcel if (strchr(slash, '.') == NULL) 438130803Smarcel SNPRINTF1(fpat, len, "%s*.*", s); 439130803Smarcel else 440130803Smarcel SNPRINTF1(fpat, len, "%s*", s); 441130803Smarcel } 442130803Smarcel#else 443130803Smarcel { 444130803Smarcel int len = strlen(s) + 2; 445130803Smarcel fpat = (char *) ecalloc(len, sizeof(char)); 446130803Smarcel SNPRINTF1(fpat, len, "%s*", s); 447130803Smarcel } 448130803Smarcel#endif 449130803Smarcel qs = lglob(fpat); 450130803Smarcel s = shell_unquote(qs); 451130803Smarcel if (strcmp(s,fpat) == 0) 452130803Smarcel { 453130803Smarcel /* 454130803Smarcel * The filename didn't expand. 455130803Smarcel */ 456130803Smarcel free(qs); 457130803Smarcel qs = NULL; 458130803Smarcel } 459130803Smarcel free(s); 460130803Smarcel free(fpat); 461130803Smarcel return (qs); 462130803Smarcel} 463130803Smarcel#endif 464130803Smarcel 465130803Smarcel/* 466130803Smarcel * Try to determine if a file is "binary". 467130803Smarcel * This is just a guess, and we need not try too hard to make it accurate. 468130803Smarcel */ 469130803Smarcel public int 470130803Smarcelbin_file(f) 471130803Smarcel int f; 472130803Smarcel{ 473130803Smarcel int n; 474130803Smarcel int bin_count = 0; 475130803Smarcel char data[256]; 476130803Smarcel char* p; 477130803Smarcel char* pend; 478130803Smarcel 479130803Smarcel if (!seekable(f)) 480130803Smarcel return (0); 481130803Smarcel if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK) 482130803Smarcel return (0); 483130803Smarcel n = read(f, data, sizeof(data)); 484130803Smarcel pend = &data[n]; 485130803Smarcel for (p = data; p < pend; ) 486130803Smarcel { 487130803Smarcel LWCHAR c = step_char(&p, +1, pend); 488130803Smarcel if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) 489130803Smarcel { 490130803Smarcel do { 491130803Smarcel c = step_char(&p, +1, pend); 492130803Smarcel } while (p < pend && is_ansi_middle(c)); 493130803Smarcel } else if (binary_char(c)) 494130803Smarcel bin_count++; 495130803Smarcel } 496130803Smarcel /* 497130803Smarcel * Call it a binary file if there are more than 5 binary characters 498130803Smarcel * in the first 256 bytes of the file. 499130803Smarcel */ 500130803Smarcel return (bin_count > 5); 501130803Smarcel} 502130803Smarcel 503130803Smarcel/* 504130803Smarcel * Try to determine the size of a file by seeking to the end. 505130803Smarcel */ 506130803Smarcel static POSITION 507130803Smarcelseek_filesize(f) 508130803Smarcel int f; 509130803Smarcel{ 510130803Smarcel off_t spos; 511130803Smarcel 512130803Smarcel spos = lseek(f, (off_t)0, SEEK_END); 513130803Smarcel if (spos == BAD_LSEEK) 514130803Smarcel return (NULL_POSITION); 515130803Smarcel return ((POSITION) spos); 516130803Smarcel} 517130803Smarcel 518130803Smarcel/* 519130803Smarcel * Read a string from a file. 520130803Smarcel * Return a pointer to the string in memory. 521130803Smarcel */ 522130803Smarcel static char * 523130803Smarcelreadfd(fd) 524130803Smarcel FILE *fd; 525130803Smarcel{ 526130803Smarcel int len; 527130803Smarcel int ch; 528130803Smarcel char *buf; 529130803Smarcel char *p; 530130803Smarcel 531130803Smarcel /* 532130803Smarcel * Make a guess about how many chars in the string 533130803Smarcel * and allocate a buffer to hold it. 534130803Smarcel */ 535130803Smarcel len = 100; 536130803Smarcel buf = (char *) ecalloc(len, sizeof(char)); 537130803Smarcel for (p = buf; ; p++) 538130803Smarcel { 539130803Smarcel if ((ch = getc(fd)) == '\n' || ch == EOF) 540130803Smarcel break; 541130803Smarcel if (p - buf >= len-1) 542130803Smarcel { 543130803Smarcel /* 544130803Smarcel * The string is too big to fit in the buffer we have. 545130803Smarcel * Allocate a new buffer, twice as big. 546130803Smarcel */ 547130803Smarcel len *= 2; 548130803Smarcel *p = '\0'; 549130803Smarcel p = (char *) ecalloc(len, sizeof(char)); 550130803Smarcel strcpy(p, buf); 551130803Smarcel free(buf); 552130803Smarcel buf = p; 553130803Smarcel p = buf + strlen(buf); 554130803Smarcel } 555130803Smarcel *p = ch; 556130803Smarcel } 557130803Smarcel *p = '\0'; 558130803Smarcel return (buf); 559130803Smarcel} 560130803Smarcel 561130803Smarcel 562130803Smarcel 563130803Smarcel#if HAVE_POPEN 564130803Smarcel 565130803SmarcelFILE *popen(); 566130803Smarcel 567130803Smarcel/* 568130803Smarcel * Execute a shell command. 569130803Smarcel * Return a pointer to a pipe connected to the shell command's standard output. 570130803Smarcel */ 571130803Smarcel static FILE * 572130803Smarcelshellcmd(cmd) 573130803Smarcel char *cmd; 574130803Smarcel{ 575130803Smarcel FILE *fd; 576130803Smarcel 577130803Smarcel#if HAVE_SHELL 578130803Smarcel char *shell; 579130803Smarcel 580130803Smarcel shell = lgetenv("SHELL"); 581130803Smarcel if (shell != NULL && *shell != '\0') 582130803Smarcel { 583130803Smarcel char *scmd; 584130803Smarcel char *esccmd; 585130803Smarcel 586130803Smarcel /* 587130803Smarcel * Read the output of <$SHELL -c cmd>. 588130803Smarcel * Escape any metacharacters in the command. 589130803Smarcel */ 590130803Smarcel esccmd = shell_quote(cmd); 591130803Smarcel if (esccmd == NULL) 592130803Smarcel { 593130803Smarcel fd = popen(cmd, "r"); 594130803Smarcel } else 595130803Smarcel { 596130803Smarcel int len = strlen(shell) + strlen(esccmd) + 5; 597130803Smarcel scmd = (char *) ecalloc(len, sizeof(char)); 598130803Smarcel SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd); 599130803Smarcel free(esccmd); 600130803Smarcel fd = popen(scmd, "r"); 601130803Smarcel free(scmd); 602130803Smarcel } 603130803Smarcel } else 604130803Smarcel#endif 605130803Smarcel { 606130803Smarcel fd = popen(cmd, "r"); 607130803Smarcel } 608130803Smarcel /* 609130803Smarcel * Redirection in `popen' might have messed with the 610130803Smarcel * standard devices. Restore binary input mode. 611130803Smarcel */ 612130803Smarcel SET_BINARY(0); 613130803Smarcel return (fd); 614130803Smarcel} 615130803Smarcel 616130803Smarcel#endif /* HAVE_POPEN */ 617130803Smarcel 618130803Smarcel 619130803Smarcel/* 620130803Smarcel * Expand a filename, doing any system-specific metacharacter substitutions. 621130803Smarcel */ 622130803Smarcel public char * 623130803Smarcellglob(filename) 624130803Smarcel char *filename; 625130803Smarcel{ 626130803Smarcel char *gfilename; 627130803Smarcel char *ofilename; 628130803Smarcel 629130803Smarcel ofilename = fexpand(filename); 630130803Smarcel if (secure) 631130803Smarcel return (ofilename); 632130803Smarcel filename = shell_unquote(ofilename); 633130803Smarcel 634130803Smarcel#ifdef DECL_GLOB_LIST 635130803Smarcel{ 636130803Smarcel /* 637130803Smarcel * The globbing function returns a list of names. 638130803Smarcel */ 639130803Smarcel int length; 640130803Smarcel char *p; 641130803Smarcel char *qfilename; 642130803Smarcel DECL_GLOB_LIST(list) 643130803Smarcel 644130803Smarcel GLOB_LIST(filename, list); 645130803Smarcel if (GLOB_LIST_FAILED(list)) 646130803Smarcel { 647130803Smarcel free(filename); 648130803Smarcel return (ofilename); 649130803Smarcel } 650130803Smarcel length = 1; /* Room for trailing null byte */ 651130803Smarcel for (SCAN_GLOB_LIST(list, p)) 652130803Smarcel { 653130803Smarcel INIT_GLOB_LIST(list, p); 654130803Smarcel qfilename = shell_quote(p); 655130803Smarcel if (qfilename != NULL) 656130803Smarcel { 657130803Smarcel length += strlen(qfilename) + 1; 658130803Smarcel free(qfilename); 659130803Smarcel } 660130803Smarcel } 661130803Smarcel gfilename = (char *) ecalloc(length, sizeof(char)); 662130803Smarcel for (SCAN_GLOB_LIST(list, p)) 663130803Smarcel { 664130803Smarcel INIT_GLOB_LIST(list, p); 665130803Smarcel qfilename = shell_quote(p); 666130803Smarcel if (qfilename != NULL) 667130803Smarcel { 668130803Smarcel sprintf(gfilename + strlen(gfilename), "%s ", qfilename); 669130803Smarcel free(qfilename); 670130803Smarcel } 671130803Smarcel } 672130803Smarcel /* 673130803Smarcel * Overwrite the final trailing space with a null terminator. 674130803Smarcel */ 675130803Smarcel *--p = '\0'; 676130803Smarcel GLOB_LIST_DONE(list); 677130803Smarcel} 678130803Smarcel#else 679130803Smarcel#ifdef DECL_GLOB_NAME 680130803Smarcel{ 681130803Smarcel /* 682130803Smarcel * The globbing function returns a single name, and 683130803Smarcel * is called multiple times to walk thru all names. 684130803Smarcel */ 685130803Smarcel register char *p; 686130803Smarcel register int len; 687130803Smarcel register int n; 688130803Smarcel char *pathname; 689130803Smarcel char *qpathname; 690130803Smarcel DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle) 691130803Smarcel 692130803Smarcel GLOB_FIRST_NAME(filename, &fnd, handle); 693130803Smarcel if (GLOB_FIRST_FAILED(handle)) 694130803Smarcel { 695130803Smarcel free(filename); 696130803Smarcel return (ofilename); 697130803Smarcel } 698130803Smarcel 699130803Smarcel _splitpath(filename, drive, dir, fname, ext); 700130803Smarcel len = 100; 701130803Smarcel gfilename = (char *) ecalloc(len, sizeof(char)); 702130803Smarcel p = gfilename; 703130803Smarcel do { 704130803Smarcel n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1; 705130803Smarcel pathname = (char *) ecalloc(n, sizeof(char)); 706130803Smarcel SNPRINTF3(pathname, n, "%s%s%s", drive, dir, fnd.GLOB_NAME); 707130803Smarcel qpathname = shell_quote(pathname); 708130803Smarcel free(pathname); 709 if (qpathname != NULL) 710 { 711 n = strlen(qpathname); 712 while (p - gfilename + n + 2 >= len) 713 { 714 /* 715 * No room in current buffer. 716 * Allocate a bigger one. 717 */ 718 len *= 2; 719 *p = '\0'; 720 p = (char *) ecalloc(len, sizeof(char)); 721 strcpy(p, gfilename); 722 free(gfilename); 723 gfilename = p; 724 p = gfilename + strlen(gfilename); 725 } 726 strcpy(p, qpathname); 727 free(qpathname); 728 p += n; 729 *p++ = ' '; 730 } 731 } while (GLOB_NEXT_NAME(handle, &fnd) == 0); 732 733 /* 734 * Overwrite the final trailing space with a null terminator. 735 */ 736 *--p = '\0'; 737 GLOB_NAME_DONE(handle); 738} 739#else 740#if HAVE_POPEN 741{ 742 /* 743 * We get the shell to glob the filename for us by passing 744 * an "echo" command to the shell and reading its output. 745 */ 746 FILE *fd; 747 char *s; 748 char *lessecho; 749 char *cmd; 750 char *esc; 751 int len; 752 753 esc = get_meta_escape(); 754 if (strlen(esc) == 0) 755 esc = "-"; 756 esc = shell_quote(esc); 757 if (esc == NULL) 758 { 759 free(filename); 760 return (ofilename); 761 } 762 lessecho = lgetenv("LESSECHO"); 763 if (lessecho == NULL || *lessecho == '\0') 764 lessecho = "lessecho"; 765 /* 766 * Invoke lessecho, and read its output (a globbed list of filenames). 767 */ 768 len = strlen(lessecho) + strlen(ofilename) + (7*strlen(metachars())) + 24; 769 cmd = (char *) ecalloc(len, sizeof(char)); 770 SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho, openquote, closequote, esc); 771 free(esc); 772 for (s = metachars(); *s != '\0'; s++) 773 sprintf(cmd + strlen(cmd), "-n0x%x ", *s); 774 sprintf(cmd + strlen(cmd), "-- %s", ofilename); 775 fd = shellcmd(cmd); 776 free(cmd); 777 if (fd == NULL) 778 { 779 /* 780 * Cannot create the pipe. 781 * Just return the original (fexpanded) filename. 782 */ 783 free(filename); 784 return (ofilename); 785 } 786 gfilename = readfd(fd); 787 pclose(fd); 788 if (*gfilename == '\0') 789 { 790 free(gfilename); 791 free(filename); 792 return (ofilename); 793 } 794} 795#else 796 /* 797 * No globbing functions at all. Just use the fexpanded filename. 798 */ 799 gfilename = save(filename); 800#endif 801#endif 802#endif 803 free(filename); 804 free(ofilename); 805 return (gfilename); 806} 807 808/* 809 * See if we should open a "replacement file" 810 * instead of the file we're about to open. 811 */ 812 public char * 813open_altfile(filename, pf, pfd) 814 char *filename; 815 int *pf; 816 void **pfd; 817{ 818#if !HAVE_POPEN 819 return (NULL); 820#else 821 char *lessopen; 822 char *cmd; 823 int len; 824 FILE *fd; 825#if HAVE_FILENO 826 int returnfd = 0; 827#endif 828 829 if (!use_lessopen || secure) 830 return (NULL); 831 ch_ungetchar(-1); 832 if ((lessopen = lgetenv("LESSOPEN")) == NULL) 833 return (NULL); 834 if (*lessopen == '|') 835 { 836 /* 837 * If LESSOPEN starts with a |, it indicates 838 * a "pipe preprocessor". 839 */ 840#if !HAVE_FILENO 841 error("LESSOPEN pipe is not supported", NULL_PARG); 842 return (NULL); 843#else 844 lessopen++; 845 returnfd = 1; 846 if (*lessopen == '-') { 847 /* 848 * Lessopen preprocessor will accept "-" as a filename. 849 */ 850 lessopen++; 851 } else { 852 if (strcmp(filename, "-") == 0) 853 return (NULL); 854 } 855#endif 856 } 857 858 len = strlen(lessopen) + strlen(filename) + 2; 859 cmd = (char *) ecalloc(len, sizeof(char)); 860 SNPRINTF1(cmd, len, lessopen, filename); 861 fd = shellcmd(cmd); 862 free(cmd); 863 if (fd == NULL) 864 { 865 /* 866 * Cannot create the pipe. 867 */ 868 return (NULL); 869 } 870#if HAVE_FILENO 871 if (returnfd) 872 { 873 int f; 874 char c; 875 876 /* 877 * Read one char to see if the pipe will produce any data. 878 * If it does, push the char back on the pipe. 879 */ 880 f = fileno(fd); 881 SET_BINARY(f); 882 if (read(f, &c, 1) != 1) 883 { 884 /* 885 * Pipe is empty. This means there is no alt file. 886 */ 887 pclose(fd); 888 return (NULL); 889 } 890 ch_ungetchar(c); 891 *pfd = (void *) fd; 892 *pf = f; 893 return (save("-")); 894 } 895#endif 896 cmd = readfd(fd); 897 pclose(fd); 898 if (*cmd == '\0') 899 /* 900 * Pipe is empty. This means there is no alt file. 901 */ 902 return (NULL); 903 return (cmd); 904#endif /* HAVE_POPEN */ 905} 906 907/* 908 * Close a replacement file. 909 */ 910 public void 911close_altfile(altfilename, filename, pipefd) 912 char *altfilename; 913 char *filename; 914 void *pipefd; 915{ 916#if HAVE_POPEN 917 char *lessclose; 918 FILE *fd; 919 char *cmd; 920 int len; 921 922 if (secure) 923 return; 924 if (pipefd != NULL) 925 { 926#if OS2 927 /* 928 * The pclose function of OS/2 emx sometimes fails. 929 * Send SIGINT to the piped process before closing it. 930 */ 931 kill(((FILE*)pipefd)->_pid, SIGINT); 932#endif 933 pclose((FILE*) pipefd); 934 } 935 if ((lessclose = lgetenv("LESSCLOSE")) == NULL) 936 return; 937 len = strlen(lessclose) + strlen(filename) + strlen(altfilename) + 2; 938 cmd = (char *) ecalloc(len, sizeof(char)); 939 SNPRINTF2(cmd, len, lessclose, filename, altfilename); 940 fd = shellcmd(cmd); 941 free(cmd); 942 if (fd != NULL) 943 pclose(fd); 944#endif 945} 946 947/* 948 * Is the specified file a directory? 949 */ 950 public int 951is_dir(filename) 952 char *filename; 953{ 954 int isdir = 0; 955 956 filename = shell_unquote(filename); 957#if HAVE_STAT 958{ 959 int r; 960 struct stat statbuf; 961 962 r = stat(filename, &statbuf); 963 isdir = (r >= 0 && S_ISDIR(statbuf.st_mode)); 964} 965#else 966#ifdef _OSK 967{ 968 register int f; 969 970 f = open(filename, S_IREAD | S_IFDIR); 971 if (f >= 0) 972 close(f); 973 isdir = (f >= 0); 974} 975#endif 976#endif 977 free(filename); 978 return (isdir); 979} 980 981/* 982 * Returns NULL if the file can be opened and 983 * is an ordinary file, otherwise an error message 984 * (if it cannot be opened or is a directory, etc.) 985 */ 986 public char * 987bad_file(filename) 988 char *filename; 989{ 990 register char *m = NULL; 991 992 filename = shell_unquote(filename); 993 if (!force_open && is_dir(filename)) 994 { 995 static char is_a_dir[] = " is a directory"; 996 997 m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir), 998 sizeof(char)); 999 strcpy(m, filename); 1000 strcat(m, is_a_dir); 1001 } else 1002 { 1003#if HAVE_STAT 1004 int r; 1005 struct stat statbuf; 1006 1007 r = stat(filename, &statbuf); 1008 if (r < 0) 1009 { 1010 m = errno_message(filename); 1011 } else if (force_open) 1012 { 1013 m = NULL; 1014 } else if (!S_ISREG(statbuf.st_mode)) 1015 { 1016 static char not_reg[] = " is not a regular file (use -f to see it)"; 1017 m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 1018 sizeof(char)); 1019 strcpy(m, filename); 1020 strcat(m, not_reg); 1021 } 1022#endif 1023 } 1024 free(filename); 1025 return (m); 1026} 1027 1028/* 1029 * Return the size of a file, as cheaply as possible. 1030 * In Unix, we can stat the file. 1031 */ 1032 public POSITION 1033filesize(f) 1034 int f; 1035{ 1036#if HAVE_STAT 1037 struct stat statbuf; 1038 1039 if (fstat(f, &statbuf) >= 0) 1040 return ((POSITION) statbuf.st_size); 1041#else 1042#ifdef _OSK 1043 long size; 1044 1045 if ((size = (long) _gs_size(f)) >= 0) 1046 return ((POSITION) size); 1047#endif 1048#endif 1049 return (seek_filesize(f)); 1050} 1051 1052/* 1053 * 1054 */ 1055 public char * 1056shell_coption() 1057{ 1058 return ("-c"); 1059} 1060