#include <sys/stat.h>
#include <glob.h>
#include <stdarg.h>
#include "less.h"
extern int force_open;
extern int secure;
extern int ctldisp;
extern int utf_mode;
extern IFILE curr_ifile;
extern IFILE old_ifile;
extern char openquote;
extern char closequote;
char *
shell_unquote(char *str)
{
char *name;
char *p;
name = p = 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 {
char *esc = get_meta_escape();
int esclen = strlen(esc);
while (*str != '\0') {
if (esclen > 0 && strncmp(str, esc, esclen) == 0)
str += esclen;
*p++ = *str++;
}
}
*p = '\0';
return (name);
}
char *
get_meta_escape(void)
{
char *s;
s = lgetenv("LESSMETAESCAPE");
if (s == NULL)
s = "\\";
return (s);
}
static char *
metachars(void)
{
static char *mchars = NULL;
if (mchars == NULL) {
mchars = lgetenv("LESSMETACHARS");
if (mchars == NULL)
mchars = DEF_METACHARS;
}
return (mchars);
}
static int
metachar(char c)
{
return (strchr(metachars(), c) != NULL);
}
static int
must_quote(char c)
{
return (c == '\n');
}
char *
shell_quote(const char *s)
{
const char *p;
char *r;
char *newstr;
int len;
char *esc = get_meta_escape();
int esclen = strlen(esc);
int use_quotes = 0;
int have_quotes = 0;
len = 1;
for (p = s; *p != '\0'; p++) {
len++;
if (*p == openquote || *p == closequote)
have_quotes = 1;
if (metachar(*p)) {
if (esclen == 0) {
use_quotes = 1;
} else if (must_quote(*p)) {
len += 3;
} else {
len += esclen;
}
}
}
if (use_quotes) {
if (have_quotes)
return (NULL);
newstr = easprintf("%c%s%c", openquote, s, closequote);
} else {
newstr = r = ecalloc(len, sizeof (char));
while (*s != '\0') {
if (!metachar(*s)) {
*r++ = *s++;
} else if (must_quote(*s)) {
*r++ = openquote;
*r++ = *s++;
*r++ = closequote;
} else {
(void) strlcpy(r, esc, newstr + len - p);
r += esclen;
*r++ = *s++;
}
}
*r = '\0';
}
return (newstr);
}
static char *
dirfile(const char *dirname, const char *filename)
{
char *pathname;
char *qpathname;
int f;
if (dirname == NULL || *dirname == '\0')
return (NULL);
pathname = easprintf("%s/%s", dirname, filename);
qpathname = shell_unquote(pathname);
f = open(qpathname, O_RDONLY);
if (f == -1) {
free(pathname);
pathname = NULL;
} else {
(void) close(f);
}
free(qpathname);
return (pathname);
}
char *
homefile(char *filename)
{
return (dirfile(lgetenv("HOME"), filename));
}
char *
fexpand(char *s)
{
char *fr, *to;
int n;
char *e;
IFILE ifile;
#define fchar_ifile(c) \
((c) == '%' ? curr_ifile : (c) == '#' ? old_ifile : NULL)
n = 0;
for (fr = s; *fr != '\0'; fr++) {
switch (*fr) {
case '%':
case '#':
if (fr > s && fr[-1] == *fr) {
n++;
} else if (fr[1] != *fr) {
ifile = fchar_ifile(*fr);
if (ifile == NULL)
n++;
else
n += strlen(get_filename(ifile));
}
break;
default:
n++;
break;
}
}
e = ecalloc(n+1, sizeof (char));
to = e;
for (fr = s; *fr != '\0'; fr++) {
switch (*fr) {
case '%':
case '#':
if (fr > s && fr[-1] == *fr) {
*to++ = *fr;
} else if (fr[1] != *fr) {
ifile = fchar_ifile(*fr);
if (ifile == NULL) {
*to++ = *fr;
} else {
(void) strlcpy(to, get_filename(ifile),
e + n + 1 - to);
to += strlen(to);
}
}
break;
default:
*to++ = *fr;
break;
}
}
*to = '\0';
return (e);
}
char *
fcomplete(char *s)
{
char *fpat;
char *qs;
if (secure)
return (NULL);
fpat = easprintf("%s*", s);
qs = lglob(fpat);
s = shell_unquote(qs);
if (strcmp(s, fpat) == 0) {
free(qs);
qs = NULL;
}
free(s);
free(fpat);
return (qs);
}
int
bin_file(int f)
{
char data[256];
ssize_t i, n;
wchar_t ch;
int bin_count, len;
if (!seekable(f))
return (0);
if (lseek(f, (off_t)0, SEEK_SET) == (off_t)-1)
return (0);
n = read(f, data, sizeof (data));
bin_count = 0;
for (i = 0; i < n; i += len) {
len = mbtowc(&ch, data + i, n - i);
if (len <= 0) {
bin_count++;
len = 1;
} else if (iswprint(ch) == 0 && iswspace(ch) == 0 &&
data[i] != '\b' &&
(ctldisp != OPT_ONPLUS || data[i] != ESC))
bin_count++;
}
return (bin_count > 5);
}
char *
lglob(char *filename)
{
char *gfilename;
char *ofilename;
glob_t list;
int i;
int length;
char *p;
char *qfilename;
ofilename = fexpand(filename);
if (secure)
return (ofilename);
filename = shell_unquote(ofilename);
#ifndef GLOB_TILDE
#define GLOB_TILDE 0
#endif
#ifndef GLOB_LIMIT
#define GLOB_LIMIT 0
#endif
if (glob(filename, GLOB_TILDE | GLOB_LIMIT, NULL, &list) != 0) {
free(filename);
return (ofilename);
}
length = 1;
for (i = 0; i < list.gl_pathc; i++) {
p = list.gl_pathv[i];
qfilename = shell_quote(p);
if (qfilename != NULL) {
length += strlen(qfilename) + 1;
free(qfilename);
}
}
gfilename = ecalloc(length, sizeof (char));
for (i = 0; i < list.gl_pathc; i++) {
p = list.gl_pathv[i];
qfilename = shell_quote(p);
if (qfilename != NULL) {
if (i != 0) {
(void) strlcat(gfilename, " ", length);
}
(void) strlcat(gfilename, qfilename, length);
free(qfilename);
}
}
globfree(&list);
free(filename);
free(ofilename);
return (gfilename);
}
int
is_dir(char *filename)
{
int isdir = 0;
int r;
struct stat statbuf;
filename = shell_unquote(filename);
r = stat(filename, &statbuf);
isdir = (r >= 0 && S_ISDIR(statbuf.st_mode));
free(filename);
return (isdir);
}
char *
bad_file(char *filename)
{
char *m = NULL;
filename = shell_unquote(filename);
if (!force_open && is_dir(filename)) {
m = easprintf("%s is a directory", filename);
} else {
int r;
struct stat statbuf;
r = stat(filename, &statbuf);
if (r == -1) {
m = errno_message(filename);
} else if (force_open) {
m = NULL;
} else if (!S_ISREG(statbuf.st_mode)) {
m = easprintf("%s is not a regular file (use -f to "
"see it)", filename);
}
}
free(filename);
return (m);
}
off_t
filesize(int f)
{
struct stat statbuf;
if (fstat(f, &statbuf) >= 0)
return (statbuf.st_size);
return (-1);
}
char *
last_component(char *name)
{
char *slash;
for (slash = name + strlen(name); slash > name; ) {
--slash;
if (*slash == '/')
return (slash + 1);
}
return (name);
}