#include "less.h"
#include "lglob.h"
#if MSDOS_COMPILER
#include <dos.h>
#if MSDOS_COMPILER==WIN32C && !defined(_MSC_VER)
#include <dir.h>
#endif
#if MSDOS_COMPILER==DJGPPC
#include <glob.h>
#include <dir.h>
#define _MAX_PATH PATH_MAX
#endif
#endif
#ifdef _OSK
#include <rbf.h>
#ifndef _OSK_MWC32
#include <modes.h>
#endif
#endif
#if HAVE_STAT
#include <sys/stat.h>
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#endif
extern int force_open;
extern int use_lessopen;
extern int ctldisp;
extern int utf_mode;
extern IFILE curr_ifile;
extern IFILE old_ifile;
#if SPACES_IN_FILENAMES
extern char openquote;
extern char closequote;
#endif
#if HAVE_STAT_INO
extern ino_t curr_ino;
extern dev_t curr_dev;
#endif
public char * shell_unquote(constant char *str)
{
char *name;
char *p;
name = p = (char *) ecalloc(strlen(str)+1, sizeof(char));
if (*str == openquote)
{
str++;
while (*str != '\0')
{
if (*str == closequote)
{
if (str[1] != closequote)
break;
str++;
}
*p++ = *str++;
}
} else
{
constant char *esc = get_meta_escape();
size_t esclen = strlen(esc);
while (*str != '\0')
{
if (esclen > 0 && strncmp(str, esc, esclen) == 0)
str += esclen;
*p++ = *str++;
}
}
*p = '\0';
return (name);
}
public constant char * get_meta_escape(void)
{
constant char *s;
s = lgetenv("LESSMETAESCAPE");
if (s == NULL)
s = DEF_METAESCAPE;
return (s);
}
static constant char * metachars(void)
{
static constant char *mchars = NULL;
if (mchars == NULL)
{
mchars = lgetenv("LESSMETACHARS");
if (mchars == NULL)
mchars = DEF_METACHARS;
}
return (mchars);
}
static lbool metachar(char c)
{
return (strchr(metachars(), c) != NULL);
}
static lbool must_quote(char c)
{
return (c == '\n');
}
public char * shell_quoten(constant char *s, size_t slen)
{
constant char *p;
char *np;
char *newstr;
size_t len;
constant char *esc = get_meta_escape();
size_t esclen = strlen(esc);
lbool use_quotes = FALSE;
lbool have_quotes = FALSE;
len = 1;
for (p = s; p < s + slen; p++)
{
len++;
if (*p == openquote || *p == closequote)
have_quotes = TRUE;
if (metachar(*p))
{
if (esclen == 0)
{
use_quotes = TRUE;
} else if (must_quote(*p))
{
len += 3;
} else
{
len += esclen;
}
}
}
if (use_quotes)
{
if (have_quotes)
return (NULL);
len = slen + 3;
}
newstr = np = (char *) ecalloc(len, sizeof(char));
if (use_quotes)
{
SNPRINTF4(newstr, len, "%c%.*s%c", openquote, (int) slen, s, closequote);
} else
{
constant char *es = s + slen;
while (s < es)
{
if (!metachar(*s))
{
*np++ = *s++;
} else if (must_quote(*s))
{
*np++ = openquote;
*np++ = *s++;
*np++ = closequote;
} else
{
strcpy(np, esc);
np += esclen;
*np++ = *s++;
}
}
*np = '\0';
}
return (newstr);
}
public char * shell_quote(constant char *s)
{
return shell_quoten(s, strlen(s));
}
public char * dirfile(constant char *dirname, constant char *filename, int must_exist)
{
char *pathname;
size_t len;
int f;
if (dirname == NULL || *dirname == '\0')
return (NULL);
len = strlen(dirname) + strlen(filename) + 2;
pathname = (char *) calloc(len, sizeof(char));
if (pathname == NULL)
return (NULL);
SNPRINTF3(pathname, len, "%s%s%s", dirname, PATHNAME_SEP, filename);
if (must_exist)
{
f = open(pathname, OPEN_READ);
if (f < 0)
{
free(pathname);
pathname = NULL;
} else
{
close(f);
}
}
return (pathname);
}
public char * homefile(constant char *filename)
{
char *pathname;
pathname = dirfile(lgetenv("HOME"), filename, 1);
if (pathname != NULL)
return (pathname);
#if OS2
pathname = dirfile(lgetenv("INIT"), filename, 1);
if (pathname != NULL)
return (pathname);
#endif
#if (MSDOS_COMPILER && MSDOS_COMPILER!=WIN32C) || OS2
pathname = (char *) ecalloc(_MAX_PATH, sizeof(char));
#if MSDOS_COMPILER==DJGPPC
{
char *res = searchpath(filename);
if (res == 0)
*pathname = '\0';
else
strcpy(pathname, res);
}
#else
_searchenv(filename, "PATH", pathname);
#endif
if (*pathname != '\0')
return (pathname);
free(pathname);
#endif
return (NULL);
}
typedef struct xcpy { char *dest; size_t copied; } xcpy;
static void xcpy_char(xcpy *xp, char ch)
{
if (xp->dest != NULL) *(xp->dest)++ = ch;
xp->copied++;
}
static void xcpy_filename(xcpy *xp, constant char *str)
{
lbool quote = (strchr(str, ' ') != NULL);
if (quote)
xcpy_char(xp, openquote);
for (; *str != '\0'; str++)
xcpy_char(xp, *str);
if (quote)
xcpy_char(xp, closequote);
}
static size_t fexpand_copy(constant char *fr, char *to)
{
xcpy xp;
xp.copied = 0;
xp.dest = to;
for (; *fr != '\0'; fr++)
{
lbool expand = FALSE;
switch (*fr)
{
case '%':
case '#':
if (fr[1] == *fr)
{
fr += 1;
} else
{
expand = TRUE;
}
break;
default:
break;
}
if (expand)
{
IFILE ifile = (*fr == '%') ? curr_ifile : (*fr == '#') ? old_ifile : NULL_IFILE;
if (ifile == NULL_IFILE)
xcpy_char(&xp, *fr);
else
xcpy_filename(&xp, get_filename(ifile));
} else
{
xcpy_char(&xp, *fr);
}
}
xcpy_char(&xp, '\0');
return xp.copied;
}
public char * fexpand(constant char *s)
{
size_t n;
char *e;
n = fexpand_copy(s, NULL);
e = (char *) ecalloc(n, sizeof(char));
fexpand_copy(s, e);
return (e);
}
#if TAB_COMPLETE_FILENAME
public char * fcomplete(constant char *s)
{
char *fpat;
char *qs;
char *uqs;
if (!secure_allow(SF_GLOB))
return (NULL);
#if MSDOS_COMPILER && (MSDOS_COMPILER == MSOFTC || MSDOS_COMPILER == BORLANDC)
{
constant char *slash;
size_t len;
for (slash = s+strlen(s)-1; slash > s; slash--)
if (*slash == *PATHNAME_SEP || *slash == '/')
break;
len = strlen(s) + 4;
fpat = (char *) ecalloc(len, sizeof(char));
if (strchr(slash, '.') == NULL)
SNPRINTF1(fpat, len, "%s*.*", s);
else
SNPRINTF1(fpat, len, "%s*", s);
}
#else
{
size_t len = strlen(s) + 2;
fpat = (char *) ecalloc(len, sizeof(char));
SNPRINTF1(fpat, len, "%s*", s);
}
#endif
qs = lglob(fpat);
uqs = shell_unquote(qs);
if (strcmp(uqs, fpat) == 0)
{
free(qs);
qs = NULL;
}
free(uqs);
free(fpat);
return (qs);
}
#endif
public int bin_file(int f, ssize_t *n)
{
int bin_count = 0;
char data[256];
constant char* p;
constant char* edata;
if (!seekable(f))
return (0);
if (less_lseek(f, (less_off_t)0, SEEK_SET) == BAD_LSEEK)
return (0);
*n = read(f, data, sizeof(data));
if (*n <= 0)
return (0);
edata = &data[*n];
for (p = data; p < edata; )
{
if (utf_mode && !is_utf8_well_formed(p, (int) ptr_diff(edata,p)))
{
bin_count++;
utf_skip_to_lead(&p, edata);
} else
{
LWCHAR c = step_charc(&p, +1, edata);
struct ansi_state *pansi;
if (ctldisp == OPT_ONPLUS && (pansi = ansi_start(c)) != NULL)
{
skip_ansi(pansi, c, &p, edata);
ansi_done(pansi);
} else if (binary_char(c))
bin_count++;
}
}
return (bin_count > 5);
}
static POSITION seek_filesize(int f)
{
less_off_t spos;
spos = less_lseek(f, (less_off_t)0, SEEK_END);
if (spos == BAD_LSEEK)
return (NULL_POSITION);
return ((POSITION) spos);
}
#if HAVE_POPEN
public char * readfd(FILE *fd)
{
struct xbuffer xbuf;
xbuf_init(&xbuf);
for (;;)
{
int ch;
if ((ch = getc(fd)) == '\n' || ch == EOF)
break;
xbuf_add_char(&xbuf, (char) ch);
}
xbuf_add_char(&xbuf, '\0');
return (char *) xbuf.data;
}
static FILE * shellcmd(constant char *cmd)
{
FILE *fd;
#if HAVE_SHELL
constant char *shell;
shell = lgetenv("SHELL");
if (!isnullenv(shell))
{
char *scmd;
char *esccmd;
esccmd = shell_quote(cmd);
if (esccmd == NULL)
{
fd = popen(cmd, "r");
} else
{
size_t len = strlen(shell) + strlen(esccmd) + 5;
scmd = (char *) ecalloc(len, sizeof(char));
SNPRINTF3(scmd, len, "%s %s %s", shell, shell_coption(), esccmd);
free(esccmd);
fd = popen(scmd, "r");
free(scmd);
}
} else
#endif
{
fd = popen(cmd, "r");
}
SET_BINARY(0);
return (fd);
}
#endif
public char * lglob(constant char *afilename)
{
char *gfilename;
char *filename = fexpand(afilename);
if (!secure_allow(SF_GLOB))
return (filename);
#ifdef DECL_GLOB_LIST
{
size_t length;
char *p;
char *qfilename;
DECL_GLOB_LIST(list)
GLOB_LIST(filename, list);
if (GLOB_LIST_FAILED(list))
{
return (filename);
}
length = 1;
for (SCAN_GLOB_LIST(list, p))
{
INIT_GLOB_LIST(list, p);
qfilename = shell_quote(p);
if (qfilename != NULL)
{
length += strlen(qfilename) + 1;
free(qfilename);
}
}
gfilename = (char *) ecalloc(length, sizeof(char));
for (SCAN_GLOB_LIST(list, p))
{
INIT_GLOB_LIST(list, p);
qfilename = shell_quote(p);
if (qfilename != NULL)
{
sprintf(gfilename + strlen(gfilename), "%s ", qfilename);
free(qfilename);
}
}
*--p = '\0';
GLOB_LIST_DONE(list);
}
#else
#ifdef DECL_GLOB_NAME
{
char *p;
size_t len;
size_t n;
char *pfilename;
char *qfilename;
DECL_GLOB_NAME(fnd,drive,dir,fname,ext,handle)
GLOB_FIRST_NAME(filename, &fnd, handle);
if (GLOB_FIRST_FAILED(handle))
{
return (filename);
}
_splitpath(filename, drive, dir, fname, ext);
len = 100;
gfilename = (char *) ecalloc(len, sizeof(char));
p = gfilename;
do {
n = strlen(drive) + strlen(dir) + strlen(fnd.GLOB_NAME) + 1;
pfilename = (char *) ecalloc(n, sizeof(char));
SNPRINTF3(pfilename, n, "%s%s%s", drive, dir, fnd.GLOB_NAME);
qfilename = shell_quote(pfilename);
free(pfilename);
if (qfilename != NULL)
{
n = strlen(qfilename);
while (p - gfilename + n + 2 >= len)
{
len *= 2;
*p = '\0';
p = (char *) ecalloc(len, sizeof(char));
strcpy(p, gfilename);
free(gfilename);
gfilename = p;
p = gfilename + strlen(gfilename);
}
strcpy(p, qfilename);
free(qfilename);
p += n;
*p++ = ' ';
}
} while (GLOB_NEXT_NAME(handle, &fnd) == 0);
*--p = '\0';
GLOB_NAME_DONE(handle);
}
#else
#if HAVE_POPEN
{
FILE *fd;
constant char *s;
constant char *lessecho;
char *cmd;
constant char *esc;
char *qesc;
size_t len;
esc = get_meta_escape();
if (strlen(esc) == 0)
esc = "-";
qesc = shell_quote(esc);
if (qesc == NULL)
{
return (filename);
}
lessecho = lgetenv("LESSECHO");
if (isnullenv(lessecho))
lessecho = "lessecho";
len = strlen(lessecho) + strlen(filename) + (7*strlen(metachars())) + 24;
cmd = (char *) ecalloc(len, sizeof(char));
SNPRINTF4(cmd, len, "%s -p0x%x -d0x%x -e%s ", lessecho,
(unsigned char) openquote, (unsigned char) closequote, qesc);
free(qesc);
for (s = metachars(); *s != '\0'; s++)
sprintf(cmd + strlen(cmd), "-n0x%x ", (unsigned char) *s);
sprintf(cmd + strlen(cmd), "-- %s", filename);
fd = shellcmd(cmd);
free(cmd);
if (fd == NULL)
{
return (filename);
}
gfilename = readfd(fd);
pclose(fd);
if (*gfilename == '\0')
{
free(gfilename);
return (filename);
}
}
#else
gfilename = save(filename);
#endif
#endif
#endif
free(filename);
return (gfilename);
}
public lbool is_fake_pathname(constant char *path)
{
return (strcmp(path, "-") == 0 ||
strcmp(path, FAKE_HELPFILE) == 0 || strcmp(path, FAKE_EMPTYFILE) == 0);
}
public char * lrealpath(constant char *path)
{
if (!is_fake_pathname(path))
{
#if HAVE_REALPATH
#ifndef PATH_MAX
char *rpath;
rpath = realpath(path, NULL);
if (rpath != NULL)
return (rpath);
#else
char rpath[PATH_MAX];
if (realpath(path, rpath) != NULL)
return (save(rpath));
#endif
#endif
}
return (save(path));
}
#if HAVE_POPEN
static int num_pct_s(constant char *lessopen)
{
int num = 0;
while (*lessopen != '\0')
{
if (*lessopen == '%')
{
if (lessopen[1] == '%')
++lessopen;
else if (lessopen[1] == 's')
++num;
else
return (999);
}
++lessopen;
}
return (num);
}
#endif
public char * open_altfile(constant char *filename, int *pf, void **pfd)
{
#if !HAVE_POPEN
return (NULL);
#else
constant char *lessopen;
char *qfilename;
char *cmd;
size_t len;
FILE *fd;
#if HAVE_FILENO
int returnfd = 0;
#endif
if (!secure_allow(SF_LESSOPEN))
return (NULL);
if (!use_lessopen)
return (NULL);
ch_ungetchar(-1);
if ((lessopen = lgetenv("LESSOPEN")) == NULL)
return (NULL);
while (*lessopen == '|')
{
#if !HAVE_FILENO
error("LESSOPEN pipe is not supported", NULL_PARG);
return (NULL);
#else
lessopen++;
returnfd++;
#endif
}
if (*lessopen == '-')
{
lessopen++;
} else
{
if (strcmp(filename, "-") == 0)
return (NULL);
}
if (num_pct_s(lessopen) != 1)
{
error("LESSOPEN ignored: must contain exactly one %%s", NULL_PARG);
return (NULL);
}
qfilename = shell_quote(filename);
len = strlen(lessopen) + strlen(qfilename) + 2;
cmd = (char *) ecalloc(len, sizeof(char));
SNPRINTF1(cmd, len, lessopen, qfilename);
free(qfilename);
fd = shellcmd(cmd);
free(cmd);
if (fd == NULL)
{
return (NULL);
}
#if HAVE_FILENO
if (returnfd)
{
unsigned char c;
int f;
f = fileno(fd);
SET_BINARY(f);
if (read(f, &c, 1) != 1)
{
int status = pclose(fd);
if (returnfd > 1 && status == 0) {
*pfd = NULL;
*pf = -1;
return (save(FAKE_EMPTYFILE));
}
return (NULL);
}
ch_ungetchar(c);
*pfd = (void *) fd;
*pf = f;
return (save("-"));
}
#endif
cmd = readfd(fd);
pclose(fd);
if (*cmd == '\0')
{
free(cmd);
return (NULL);
}
return (cmd);
#endif
}
public void close_altfile(constant char *altfilename, constant char *filename)
{
#if HAVE_POPEN
constant char *lessclose;
char *qfilename;
char *qaltfilename;
FILE *fd;
char *cmd;
size_t len;
if (!secure_allow(SF_LESSOPEN))
return;
if ((lessclose = lgetenv("LESSCLOSE")) == NULL)
return;
if (num_pct_s(lessclose) > 2)
{
error("LESSCLOSE ignored; must contain no more than 2 %%s", NULL_PARG);
return;
}
qfilename = shell_quote(filename);
qaltfilename = shell_quote(altfilename);
len = strlen(lessclose) + strlen(qfilename) + strlen(qaltfilename) + 2;
cmd = (char *) ecalloc(len, sizeof(char));
SNPRINTF2(cmd, len, lessclose, qfilename, qaltfilename);
free(qaltfilename);
free(qfilename);
fd = shellcmd(cmd);
free(cmd);
if (fd != NULL)
pclose(fd);
#endif
}
public lbool is_dir(constant char *filename)
{
lbool isdir = FALSE;
#if HAVE_STAT
{
int r;
less_stat_t statbuf;
r = less_stat(filename, &statbuf);
isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
}
#else
#ifdef _OSK
{
int f;
f = open(filename, S_IREAD | S_IFDIR);
if (f >= 0)
close(f);
isdir = (f >= 0);
}
#endif
#endif
return (isdir);
}
public char * bad_file(constant char *filename)
{
char *m = NULL;
if (!force_open && is_dir(filename))
{
static char is_a_dir[] = " is a directory";
m = (char *) ecalloc(strlen(filename) + sizeof(is_a_dir),
sizeof(char));
strcpy(m, filename);
strcat(m, is_a_dir);
} else
{
#if HAVE_STAT
int r;
less_stat_t statbuf;
r = less_stat(filename, &statbuf);
if (r < 0)
{
m = errno_message(filename);
} else if (force_open)
{
m = NULL;
} else if (!S_ISREG(statbuf.st_mode))
{
static char not_reg[] = " is not a regular file (use -f to see it)";
m = (char *) ecalloc(strlen(filename) + sizeof(not_reg),
sizeof(char));
strcpy(m, filename);
strcat(m, not_reg);
}
#endif
}
return (m);
}
public POSITION filesize(int f)
{
#if HAVE_STAT
less_stat_t statbuf;
if (less_fstat(f, &statbuf) >= 0)
return ((POSITION) statbuf.st_size);
#else
#ifdef _OSK
long size;
if ((size = (long) _gs_size(f)) >= 0)
return ((POSITION) size);
#endif
#endif
return (seek_filesize(f));
}
public lbool curr_ifile_changed(void)
{
#if HAVE_STAT_INO
struct stat st;
POSITION curr_pos = ch_tell();
int r = stat(get_filename(curr_ifile), &st);
if (r == 0 && (st.st_ino != curr_ino ||
st.st_dev != curr_dev ||
(curr_pos != NULL_POSITION && st.st_size < curr_pos)))
return (TRUE);
#endif
return (FALSE);
}
public constant char * shell_coption(void)
{
return ("-c");
}
public constant char * last_component(constant char *name)
{
constant char *slash;
for (slash = name + strlen(name); slash > name; )
{
--slash;
if (*slash == *PATHNAME_SEP || *slash == '/')
return (slash + 1);
}
return (name);
}