#include <stdio.h>
#include <libintl.h>
#include <stdlib.h>
#include <libgen.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <dirent.h>
#include "err.h"
#include "fn.h"
#include "glob.h"
static struct fn_list *glob_debrace(struct fn *fnp);
static struct fn_list *glob_reglob_list(struct fn_list *fnlp);
static boolean_t glob_magic(struct fn *fnp);
static struct fn_list *
glob_debrace(struct fn *fnp)
{
struct fn_list *ret = fn_list_new(NULL);
struct fn_list *newret;
char *sp = fn_s(fnp);
char *left;
char *right;
char *comma;
fn_list_adds(ret, "");
while (sp != NULL && (left = strchr(sp, '{')) != NULL)
if ((right = strchr(left, '}')) == NULL) {
err(EF_FILE|EF_JMP, "Missing }");
fn_list_free(ret);
return (NULL);
} else {
fn_list_appendrange(ret, sp, left);
sp = right + 1;
if (left + 1 == right)
continue;
left++;
newret = fn_list_new(NULL);
while ((comma = strchr(left, ',')) != NULL) {
struct fn_list *dup = fn_list_dup(ret);
fn_list_appendrange(dup, left, comma);
fn_list_addfn_list(newret, dup);
left = comma + 1;
}
fn_list_appendrange(ret, left, right);
fn_list_addfn_list(newret, ret);
ret = newret;
}
fn_list_appendrange(ret, sp, &sp[strlen(sp)]);
return (ret);
}
static boolean_t
glob_magic(struct fn *fnp)
{
char *s = fn_s(fnp);
for (; s != NULL && *s; s++)
if (*s == '*' ||
*s == '?' ||
*s == '[')
return (B_TRUE);
return (B_FALSE);
}
struct fn_list *
glob_glob(struct fn *fnp)
{
struct fn_list *tmplist = glob_debrace(fnp);
struct fn_list *ret;
struct fn *nextfnp;
struct fn *newfnp;
int magic = 0;
if (tmplist == NULL)
return (NULL);
fn_list_rewind(tmplist);
while ((nextfnp = fn_list_next(tmplist)) != NULL)
if (glob_magic(nextfnp)) {
magic = 1;
break;
}
if (!magic)
return (tmplist);
fn_list_rewind(tmplist);
ret = fn_list_new(NULL);
while ((nextfnp = fn_list_next(tmplist)) != NULL) {
newfnp = glob_to_reglob(nextfnp);
fn_list_addfn(ret, newfnp);
}
fn_list_free(tmplist);
tmplist = ret;
ret = glob_reglob_list(tmplist);
fn_list_free(tmplist);
return (ret);
}
struct fn_list *
glob_glob_list(struct fn_list *fnlp)
{
struct fn_list *ret = fn_list_new(NULL);
struct fn *fnp;
fn_list_rewind(fnlp);
while ((fnp = fn_list_next(fnlp)) != NULL)
fn_list_addfn_list(ret, glob_glob(fnp));
return (ret);
}
struct fn_list *
glob_reglob(struct fn *fnp)
{
struct fn_list *ret = fn_list_new(NULL);
struct fn_list *newret;
struct fn *nextfnp;
char *mys = STRDUP(fn_s(fnp));
char *sp = mys;
char *slash;
int skipdotfiles;
char *re;
char ret0[MAXPATHLEN];
if (*sp == '/') {
fn_list_adds(ret, "/");
while (*sp == '/')
sp++;
} else
fn_list_adds(ret, "./");
do {
if ((slash = strchr(sp, '/')) != NULL) {
*slash++ = '\0';
while (*slash == '/')
slash++;
}
if (sp[0] == '\\' && sp[1] == '.')
skipdotfiles = 0;
else
skipdotfiles = 1;
if ((re = regcmp("^", sp, "$", (char *)0)) == NULL)
err(EF_FILE|EF_JMP, "regcmp failed on <%s>", sp);
newret = fn_list_new(NULL);
fn_list_rewind(ret);
while ((nextfnp = fn_list_next(ret)) != NULL) {
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(fn_s(nextfnp))) == NULL)
continue;
while ((dp = readdir(dirp)) != NULL) {
if (skipdotfiles && dp->d_name[0] == '.')
continue;
*ret0 = '\0';
if (regex(re, dp->d_name, ret0)) {
struct fn *matchfnp = fn_dup(nextfnp);
struct stat stbuf;
int n;
fn_puts(matchfnp, dp->d_name);
if (stat(fn_s(matchfnp), &stbuf) < 0) {
fn_free(matchfnp);
continue;
}
if (slash &&
(stbuf.st_mode & S_IFMT) !=
S_IFDIR) {
fn_free(matchfnp);
continue;
}
if (*ret0)
n = atoi(ret0);
else
n = -1;
fn_setn(matchfnp, n);
fn_setstat(matchfnp, &stbuf);
if (slash)
fn_putc(matchfnp, '/');
fn_list_addfn(newret, matchfnp);
}
}
(void) closedir(dirp);
}
fn_list_free(ret);
ret = newret;
sp = slash;
} while (slash);
FREE(mys);
return (ret);
}
static struct fn_list *
glob_reglob_list(struct fn_list *fnlp)
{
struct fn_list *ret = fn_list_new(NULL);
struct fn *fnp;
fn_list_rewind(fnlp);
while ((fnp = fn_list_next(fnlp)) != NULL)
fn_list_addfn_list(ret, glob_reglob(fnp));
return (ret);
}
struct fn *
glob_to_reglob(struct fn *fnp)
{
int c;
struct fn *ret = fn_new(NULL);
fn_rewind(fnp);
while ((c = fn_getc(fnp)) != '\0')
switch (c) {
case '.':
case '(':
case ')':
case '^':
case '+':
case '{':
case '}':
case '$':
fn_putc(ret, '\\');
fn_putc(ret, c);
break;
case '?':
fn_putc(ret, '.');
break;
case '*':
fn_putc(ret, '.');
fn_putc(ret, '*');
break;
default:
fn_putc(ret, c);
}
return (ret);
}
#ifdef TESTMODULE
int
main(int argc, char *argv[])
{
int i;
int reglobs = 0;
struct fn *argfnp = fn_new(NULL);
struct fn *fnp;
struct fn_list *fnlp;
err_init(argv[0]);
setbuf(stdout, NULL);
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-r") == 0) {
reglobs = 1;
continue;
}
if (SETJMP) {
printf(" skipped due to errors\n");
continue;
} else {
printf("<%s>:\n", argv[i]);
fn_renew(argfnp, argv[i]);
if (reglobs)
fnlp = glob_reglob(argfnp);
else
fnlp = glob_glob(argfnp);
}
fn_list_rewind(fnlp);
while ((fnp = fn_list_next(fnlp)) != NULL)
printf(" <%s>\n", fn_s(fnp));
printf("total size: %lld\n", fn_list_totalsize(fnlp));
while ((fnp = fn_list_popoldest(fnlp)) != NULL) {
printf(" oldest <%s>\n", fn_s(fnp));
fn_free(fnp);
}
fn_list_free(fnlp);
}
fn_free(argfnp);
err_done(0);
return (0);
}
#endif