blob: ffed0045bebf1e67416047a3e4edeeca7c22c7ac [file] [log] [blame]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include "xmalloc.h"
#include "findfile.h"
#include "nls.h"
char pathname[1024];
static int ispipe;
void fpclose(FILE *fp) {
if (ispipe)
pclose(fp);
else
fclose(fp);
}
#define SIZE(a) (sizeof(a)/sizeof(a[0]))
static struct decompressor {
char *ext; /* starts with `.', has no other dots */
char *cmd;
} decompressors[] = {
{ ".gz", "gzip -d -c" },
{ ".bz2", "bzip2 -d -c" },
{ 0, 0 }
};
static FILE *
pipe_open(struct decompressor *dc) {
char *pipe_cmd;
FILE *fp;
ispipe = 1;
pipe_cmd = xmalloc(strlen(dc->cmd) + strlen(pathname) + 2);
sprintf(pipe_cmd, "%s %s", dc->cmd, pathname);
fp = popen(pipe_cmd, "r");
if (fp == NULL)
fprintf(stderr, _("error executing %s\n"), pipe_cmd);
return fp;
}
/* If a file PATHNAME exists, then open it.
If is has a `compressed' extension, then open a pipe reading it */
static FILE *
maybe_pipe_open(void) {
FILE *fp;
char *t;
struct decompressor *dc;
if ((fp = fopen(pathname, "r")) != NULL) {
t = rindex(pathname, '.');
if (t) {
for (dc = &decompressors[0]; dc->cmd; dc++)
if (strcmp(t, dc->ext) == 0) {
fclose(fp);
return pipe_open(dc);
}
}
}
return fp;
}
static FILE *
findfile_in_dir(char *fnam, char *dir, int recdepth, char **suf) {
FILE *fp = NULL;
DIR *d;
struct dirent *de;
char *ff, *fdir, *p, *q, **sp;
struct decompressor *dc;
int secondpass = 0;
ispipe = 0;
ff = index(fnam, '/');
if (ff) {
fdir = xstrdup(fnam);
fdir[ff-fnam] = 0; /* caller guarantees fdir != "" */
} else
fdir = 0; /* just to please gcc */
/* Scan the directory twice: first for files, then
for subdirectories, so that we do never search
a subdirectory when the directory itself already
contains the file we are looking for. */
StartScan:
d = opendir(dir);
if (d == NULL)
return NULL;
while ((de = readdir(d)) != NULL) {
int okdir;
if (strcmp(de->d_name, ".") == 0 ||
strcmp(de->d_name, "..") == 0)
continue;
if (strlen(dir) + strlen(de->d_name) + 2 > sizeof(pathname))
continue;
okdir = (ff && strcmp(de->d_name, fdir) == 0);
if ((secondpass && recdepth) || okdir) {
struct stat statbuf;
char *a;
a = xmalloc(strlen(dir) + strlen(de->d_name) + 2);
sprintf(a, "%s/%s", dir, de->d_name);
if (stat(a, &statbuf) == 0 &&
S_ISDIR(statbuf.st_mode)) {
if (okdir)
fp = findfile_in_dir(ff+1, a, 0, suf);
if (!fp && recdepth)
fp = findfile_in_dir(fnam, a, recdepth-1, suf);
if (fp)
return fp;
}
free(a);
}
if (secondpass)
continue;
/* Should we be in a subdirectory? */
if (ff)
continue;
/* Does d_name start right? */
p = &de->d_name[0];
q = fnam;
while (*p && *p == *q) p++,q++;
if (*q)
continue;
sprintf(pathname, "%s/%s", dir, de->d_name);
/* Does tail consist of a known suffix and possibly
a compression suffix? */
for(sp = suf; *sp; sp++) {
int l;
if (!strcmp(p, *sp))
return maybe_pipe_open();
l = strlen(*sp);
if (strncmp(p,*sp,l) == 0) {
for (dc = &decompressors[0]; dc->cmd; dc++)
if (strcmp(p+l, dc->ext) == 0)
return pipe_open(dc);
}
}
}
closedir(d);
if (recdepth > 0 && !secondpass) {
secondpass = 1;
goto StartScan;
}
return NULL;
}
/* find input file; leave name in pathname[] */
FILE *findfile(char *fnam, char **dirpath, char **suffixes) {
char **dp, *dir, **sp;
FILE *fp;
int dl, recdepth;
struct decompressor *dc;
if (strlen(fnam) >= sizeof(pathname))
return NULL;
/* Try explicitly given name first */
strcpy(pathname, fnam);
fp = maybe_pipe_open();
if (fp)
return fp;
/* Test for full pathname - opening it failed, so need suffix */
/* (This is just nonsense, for backwards compatibility.) */
if (*fnam == '/') {
for (sp = suffixes; *sp; sp++) {
if (strlen(fnam) + strlen(*sp) + 1 > sizeof(pathname))
continue;
if (*sp == 0)
continue; /* we tried it already */
sprintf(pathname, "%s%s", fnam, *sp);
if((fp = fopen(pathname, "r")) != NULL)
return fp;
}
for (sp = suffixes; *sp; sp++) {
for (dc = &decompressors[0]; dc->cmd; dc++) {
if (strlen(fnam) + strlen(*sp)
+ strlen(dc->ext) + 1 > sizeof(pathname))
continue;
sprintf(pathname, "%s%s%s", fnam, *sp, dc->ext);
if ((fp = fopen(pathname, "r")) != NULL) {
fclose(fp);
return pipe_open(dc);
}
}
}
return NULL;
}
/* Search a list of directories and directory hierarchies */
for (dp = dirpath; *dp; dp++) {
/* delete trailing slashes; trailing stars denote recursion */
dir = xstrdup(*dp);
dl = strlen(dir);
recdepth = 0;
while (dl && dir[dl-1] == '*') {
dir[--dl] = 0;
recdepth++;
}
if (dl == 0) {
dir = ".";
} else if (dl > 1 && dir[dl-1] == '/') {
dir[dl-1] = 0;
}
fp = findfile_in_dir(fnam, dir, recdepth, suffixes);
if (fp)
return fp;
}
return NULL;
}