#include #include #include #include #include #include #include #include #include "xmalloc.h" #include "findfile.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", "gunzip -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) di_error ("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(const char *fnam, char *dir, int recdepth, char **suf) { FILE *fp = NULL; DIR *d; struct dirent *de; char *ff, *fdir, *p, **sp; const char *q; 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) { struct stat statbuf; 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) { 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); if (stat(pathname, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) continue; /* 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(const 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; }