| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/param.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <dirent.h> |
| #include "nls.h" |
| #include "keymap/findfile.h" |
| |
| void |
| lk_fpclose(lkfile_t *fp) |
| { |
| if (!fp || !fp->fd) |
| return; |
| if (fp->pipe) |
| pclose(fp->fd); |
| else |
| fclose(fp->fd); |
| fp->fd = NULL; |
| } |
| |
| #define SIZE(a) (sizeof(a)/sizeof(a[0])) |
| |
| static struct decompressor { |
| const char *ext; /* starts with `.', has no other dots */ |
| const char *cmd; |
| } decompressors[] = { |
| { ".gz", "gzip -d -c" }, |
| { ".bz2", "bzip2 -d -c" }, |
| { 0, 0 } |
| }; |
| |
| static int |
| pipe_open(const struct decompressor *dc, lkfile_t *fp) |
| { |
| char *pipe_cmd; |
| |
| pipe_cmd = malloc(strlen(dc->cmd) + strlen(fp->pathname) + 2); |
| if (pipe_cmd == NULL) |
| return -1; |
| |
| sprintf(pipe_cmd, "%s %s", dc->cmd, fp->pathname); |
| |
| fp->fd = popen(pipe_cmd, "r"); |
| fp->pipe = 1; |
| |
| free(pipe_cmd); |
| |
| if (fp->fd == NULL) |
| return -1; |
| return 0; |
| } |
| |
| /* If a file PATHNAME exists, then open it. |
| If is has a `compressed' extension, then open a pipe reading it */ |
| static int |
| maybe_pipe_open(lkfile_t *fp) |
| { |
| char *t; |
| struct stat st; |
| struct decompressor *dc; |
| |
| if (stat(fp->pathname, &st) == -1 || !S_ISREG(st.st_mode) || |
| access(fp->pathname, R_OK) == -1) |
| return -1; |
| |
| t = strrchr(fp->pathname, '.'); |
| if (t) { |
| for (dc = &decompressors[0]; dc->cmd; dc++) { |
| if (strcmp(t, dc->ext) == 0) |
| return pipe_open(dc, fp); |
| } |
| } |
| fp->fd = fopen(fp->pathname, "r"); |
| fp->pipe = 0; |
| |
| if (fp->fd == NULL) |
| return -1; |
| |
| return 0; |
| } |
| |
| static int |
| findfile_by_fullname(const char *fnam, const char *const *suffixes, lkfile_t *fp) |
| { |
| int i; |
| struct stat st; |
| struct decompressor *dc; |
| size_t fnam_len, sp_len; |
| |
| fp->pipe = 0; |
| fnam_len = strlen(fnam); |
| |
| for (i = 0; suffixes[i]; i++) { |
| if (suffixes[i] == 0) |
| continue; /* we tried it already */ |
| |
| sp_len = strlen(suffixes[i]); |
| |
| if (fnam_len + sp_len + 1 > sizeof(fp->pathname)) |
| continue; |
| |
| sprintf(fp->pathname, "%s%s", fnam, suffixes[i]); |
| |
| if(stat(fp->pathname, &st) == 0 |
| && S_ISREG(st.st_mode) |
| && (fp->fd = fopen(fp->pathname, "r")) != NULL) |
| return 0; |
| |
| for (dc = &decompressors[0]; dc->cmd; dc++) { |
| if (fnam_len + sp_len + strlen(dc->ext) + 1 > sizeof(fp->pathname)) |
| continue; |
| |
| sprintf(fp->pathname, "%s%s%s", fnam, suffixes[i], dc->ext); |
| |
| if (stat(fp->pathname, &st) == 0 |
| && S_ISREG(st.st_mode) |
| && access(fp->pathname, R_OK) == 0) |
| return pipe_open(dc, fp); |
| } |
| } |
| |
| return -1; |
| } |
| |
| static int |
| findfile_in_dir(const char *fnam, const char *dir, const int recdepth, const char *const *suf, lkfile_t *fp) |
| { |
| DIR *d; |
| struct dirent *de; |
| char *ff, *fdir, *p; |
| const char *q; |
| struct decompressor *dc; |
| int i, rc = -1, secondpass = 0; |
| size_t dir_len; |
| |
| fp->fd = NULL; |
| fp->pipe = 0; |
| |
| if ((d = opendir(dir)) == NULL) |
| return -1; |
| |
| dir_len = strlen(dir); |
| |
| fdir = NULL; |
| if ((ff = strchr(fnam, '/')) != NULL) { |
| if ((fdir = strndup(fnam, ff - fnam)) == NULL) { |
| closedir(d); |
| return -1; |
| } |
| } |
| |
| /* 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: |
| while ((de = readdir(d)) != NULL) { |
| struct stat st; |
| int okdir; |
| size_t d_len; |
| |
| d_len = strlen(de->d_name); |
| if (d_len < 3) { |
| if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) |
| continue; |
| } |
| |
| if (dir_len + d_len + 2 > sizeof(fp->pathname)) |
| continue; |
| |
| okdir = (ff && strcmp(de->d_name, fdir) == 0); |
| |
| if ((secondpass && recdepth) || okdir) { |
| char *a; |
| |
| if ((a = malloc(dir_len + d_len + 2)) == NULL) |
| goto EndScan; |
| |
| sprintf(a, "%s/%s", dir, de->d_name); |
| |
| if (stat(a, &st) == 0 && S_ISDIR(st.st_mode)) { |
| if (okdir) |
| rc = findfile_in_dir(ff+1, a, 0, suf, fp); |
| |
| if (rc && recdepth) |
| rc = findfile_in_dir(fnam, a, recdepth-1, suf, fp); |
| |
| if (!rc) { |
| free(a); |
| goto EndScan; |
| } |
| } |
| 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(fp->pathname, "%s/%s", dir, de->d_name); |
| if (stat(fp->pathname, &st) != 0 || !S_ISREG(st.st_mode)) |
| continue; |
| |
| /* Does tail consist of a known suffix and possibly |
| a compression suffix? */ |
| for(i = 0; suf[i]; i++) { |
| size_t l; |
| |
| if (!strcmp(p, suf[i])) { |
| rc = maybe_pipe_open(fp); |
| goto EndScan; |
| } |
| |
| l = strlen(suf[i]); |
| if (!strncmp(p, suf[i], l)) { |
| for (dc = &decompressors[0]; dc->cmd; dc++) |
| if (strcmp(p+l, dc->ext) == 0) { |
| rc = pipe_open(dc, fp); |
| goto EndScan; |
| } |
| } |
| } |
| } |
| |
| if (recdepth > 0 && !secondpass) { |
| secondpass = 1; |
| seekdir(d, 0); |
| goto StartScan; |
| } |
| |
| EndScan: |
| if (fdir != NULL) |
| free(fdir); |
| closedir(d); |
| return rc; |
| } |
| |
| int |
| lk_findfile(const char *fnam, const char *const *dirpath, const char *const *suffixes, lkfile_t *fp) |
| { |
| char *dir; |
| int dl, recdepth, rc, i; |
| |
| fp->fd = NULL; |
| fp->pipe = 0; |
| |
| /* Try explicitly given name first */ |
| strcpy(fp->pathname, fnam); |
| |
| if (!maybe_pipe_open(fp)) |
| return 0; |
| |
| /* Test for full pathname - opening it failed, so need suffix */ |
| /* (This is just nonsense, for backwards compatibility.) */ |
| if (*fnam == '/' && |
| !findfile_by_fullname(fnam, suffixes, fp)) |
| return 0; |
| |
| /* Search a list of directories and directory hierarchies */ |
| for (i = 0; dirpath[i]; i++) { |
| recdepth = 0; |
| dl = strlen(dirpath[i]); |
| |
| /* trailing stars denote recursion */ |
| while (dl && dirpath[i][dl-1] == '*') |
| dl--, recdepth++; |
| |
| /* delete trailing slashes */ |
| while (dl && dirpath[i][dl-1] == '/') |
| dl--; |
| |
| if (dl) |
| dir = strndup(dirpath[i], dl); |
| else |
| dir = strdup("."); |
| |
| if (dir == NULL) |
| return 1; |
| |
| rc = findfile_in_dir(fnam, dir, recdepth, suffixes, fp); |
| free(dir); |
| |
| if (!rc) |
| return 0; |
| } |
| return 1; |
| } |