--- qfile.c.orig 2006-07-09 16:48:57.000000000 +0200 +++ qfile.c 2006-07-09 17:26:14.000000000 +0200 @@ -21,42 +21,30 @@ static char qfile_rcsid[] = "$Id: qfile.c,v 1.29 2006/05/14 21:37:11 vapier Exp $"; #define qfile_usage(ret) usage(ret, QFILE_FLAGS, qfile_long_opts, qfile_opts_help, lookup_applet_idx("qfile")) -void qfile(char *path, char *fullname); -void qfile(char *path, char *fullname) +void qfile(char *path, char *base_name, char *dir_name, char *real_dir_name); +void qfile(char *path, char *base_name, char *dir_name, char *real_dir_name) { FILE *fp; DIR *dir; struct dirent *dentry; char *p; - size_t flen; - int base = 0; - char fname[_Q_PATH_MAX]; + char *q; + size_t bnlen; + size_t dnlen = 0; + size_t rdnlen = 0; char buf[1024]; char pkg[126]; depend_atom *atom; - strncpy(fname, fullname, sizeof(fname)); - - if ((fname[0] == '.') && ((p = getenv("PWD")) != NULL)) { - char tmp[_Q_PATH_MAX]; - snprintf(tmp, sizeof(fname), "%s/%s", p, fullname); - /* Don't check the return value here as it is ok if - * the function fails. Think of the case where fname - * is '...somefile', we don't want to abort then as - * the value in fname will be unchanged. */ - realpath(tmp, fname); - } - - flen = strlen(fname); + bnlen = strlen(base_name); + if (dir_name != NULL) + dnlen = strlen(dir_name); + if (real_dir_name != NULL) + rdnlen = strlen(real_dir_name); if (chdir(path) != 0 || (dir = opendir(".")) == NULL) return; - if (!strchr(fname, '/')) - base = 1; - else - base = 0; - while ((dentry = readdir(dir))) { if (dentry->d_name[0] == '.') continue; @@ -70,20 +58,61 @@ snprintf(pkg, sizeof(pkg), "%s/%s", basename(path), dentry->d_name); while ((fgets(buf, sizeof(buf), fp)) != NULL) { contents_entry *e; + int path_ok = 0; + if (dir_name == NULL && real_dir_name == NULL) + path_ok = 1; e = contents_parse_line(buf); if (!e) continue; p = xstrdup(e->name); - if (strncmp(base ? basename(p) : p, fname, flen) != 0 - || strlen(base ? basename(p) : p) != flen) { + q = basename(p); + if (strncmp(q, base_name, bnlen) != 0 + || strlen(q) != bnlen) { free(p); continue; } + free(p); + + if (!path_ok) { + // check the full filepath... + p = xstrdup(e->name); + q = xstrdup(dirname(p)); + free(p); + if (dnlen == strlen(q) + && strncmp(q, dir_name, dnlen) == 0) + // dir_name == dirname(CONTENTS) + path_ok = 1; + else if (rdnlen == strlen(q) + && strncmp(q, real_dir_name, rdnlen) == 0) + // real_dir_name == dirname(CONTENTS) + path_ok = 1; + else { + char rpath[_Q_PATH_MAX]; + errno = 0; + realpath(q, rpath); + if (errno != 0) { + if (verbose) { + warn("Could not read real path of \"%s\": %s", q, strerror(errno)); + warn("We'll never know whether it was a result for your query..."); + } + } else if (dnlen == strlen(rpath) + && strncmp(rpath, dir_name, dnlen) == 0) + // dir_name == realpath(dirname(CONTENTS)) + path_ok = 1; + else if (rdnlen == strlen(rpath) + && strncmp(rpath, real_dir_name, rdnlen) == 0) + // real_dir_name == realpath(dirname(CONTENTS)) + path_ok = 1; + } + free(q); + } + if (!path_ok) + continue; + if ((atom = atom_explode(pkg)) == NULL) { warn("invalid atom %s", pkg); - free(p); continue; } printf("%s%s/%s%s%s", BOLD, atom->CATEGORY, BLUE, @@ -92,10 +121,9 @@ if (quiet) puts(""); else - printf(" (%s)\n", p); + printf(" (%s)\n", e->name); atom_implode(atom); - free(p); found++; } fclose(fp); @@ -110,6 +138,10 @@ struct dirent *dentry; int i; char *p; + char ** basenames; + char ** dirnames; + char ** realdirnames; + char * pwd; DBG("argc=%d argv[0]=%s argv[1]=%s", argc, argv[0], argc > 1 ? argv[1] : "NULL?"); @@ -130,21 +162,90 @@ if (chdir(portvdb) != 0 || (dir = opendir(".")) == NULL) return EXIT_FAILURE; - /* CONTENTS stores dir names w/out trailing / so clean up input */ - for (i = optind; i < argc; ++i) { - p = argv[i] + strlen(argv[i]) - 1; - if (*p == '/') - *p = '\0'; + // For each argument, we store its basename, its dirname, + // and the realpath of its dirname. + basenames = malloc((argc-optind) * sizeof(char*)); + dirnames = malloc((argc-optind) * sizeof(char*)); + realdirnames = malloc((argc-optind) * sizeof(char*)); + if ((pwd = getenv("PWD")) != NULL) { + int pwdlen = strlen(pwd); + if ((pwdlen > 0) && (pwd[pwdlen-1] == '/')) + pwd[pwdlen-1] = '\0'; + } + for (i = 0; i < (argc-optind); ++i) { + char tmppath[_Q_PATH_MAX]; + char abspath[_Q_PATH_MAX]; + + basenames[i] = NULL; + dirnames[i] = NULL; + realdirnames[i] = NULL; + + // Record basename, but if it is "." or ".." + strncpy(tmppath, basename(argv[i+optind]), _Q_PATH_MAX); + if ((strlen(tmppath) > 2) || strncmp(tmppath, "..", strlen(tmppath))) { + basenames[i] = xstrdup(tmppath); + // If there is no "/" in the argument, then it's over. + // (we are searching a simple file name) + if (strchr(argv[i+optind], '/') == NULL) + continue; + } + + // Make sure we have an absolute path available + if (argv[i+optind][0] == '/') + strncpy(abspath, argv[i+optind], _Q_PATH_MAX); + else if (pwd != NULL) + snprintf(abspath, _Q_PATH_MAX, "%s/%s", pwd, argv[i+optind]); + else { + err("$PWD not found in environment."); + err("Skipping query item \"%s\".", argv[i+optind]); + continue; + } + + if (basenames[i] != NULL) { + // Get both the dirname and its realpath + dirnames[i] = xstrdup(dirname(abspath)); + errno = 0; + realpath(dirnames[i], tmppath); + if (errno != 0) { + if (verbose) { + warn("Could not read real path of \"%s\": %s", dirnames[i], strerror(errno)); + warn("Results for query item \"%s\" may not be accurate.", argv[i+optind]); + } + } else if (strcmp(dirnames[i], tmppath)) + realdirnames[i] = xstrdup(tmppath); + } else { + // No basename means we are looking for something like "/foo/bar/.." + // Dirname is meaningless here, we can only get realpath of the full + // path and then split it. + errno = 0; + realpath(tmppath, abspath); + if (errno != 0) { + err("Could not read real path of \"%s\": %s", tmppath, strerror(errno)); + err("Skipping query item \"%s\".", argv[i+optind]); + continue; + } + basenames[i] = xstrdup(basename(tmppath)); + realdirnames[i] = xstrdup(dirname(tmppath)); + } } /* open /var/db/pkg */ while ((dentry = q_vdb_get_next_dir(dir))) { xasprintf(&p, "%s%s/%s", portroot, portvdb, dentry->d_name); - for (i = optind; i < argc; ++i) - qfile(p, argv[i]); + for (i = 0; i < (argc-optind); ++i) { + if (basenames[i] != NULL) + qfile(p, basenames[i], dirnames[i], realdirnames[i]); + } free(p); } + for (i = 0; i < (argc-optind); ++i) { + if (basenames[i] != NULL) free(basenames[i]); + if (dirnames[i] != NULL) free(dirnames[i]); + if (realdirnames[i] != NULL) free(realdirnames[i]); + } + free(basenames); free(dirnames); free(realdirnames); + return (found ? EXIT_SUCCESS : EXIT_FAILURE); }