tag 433290 + patch
thanks
On Mon, 16 Jul 2007, Raphael Hertzog wrote:
> For symlinks we could have:
> $ dpkg -S /usr/bin/man
> non-packaged symlink to /usr/lib/man-db/man: /usr/bin/man
> man-db: /usr/lib/man-db/man
>
> It should do symlink resolution one by one. Which means that for
> alternatives we should have:
> $ dpkg -S /usr/bin/editor
> non-packaged symlink to /etc/alternatives/editor: /usr/bin/editor
> non-packaged symlink to /usr/bin/vim: /etc/alternatives/editor
> non-packaged symlink to /etc/alternatives/vim: /usr/bin/vim
> non-packaged symlink to /usr/bin/vim.full: /etc/alternatives/vim
> vim-full: /usr/bin/vim.full
I just implemented this. Please find the patch attached.
As this is the first time I really dig through the C code of dpkg, a
proper review is in order. I believe it's sane but comments are welcome.
Cheers,
--
Raphaël Hertzog
Premier livre français sur Debian GNU/Linux :
http://www.ouaza.com/livre/admin-debian/
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 7fd423b..cbe0ca2 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -13,6 +13,7 @@ noinst_LIBRARIES = libdpkg.a
libdpkg_a_SOURCES = \
dpkg.h \
dpkg-db.h \
+ canonpath.c \
compat.c \
compression.c \
database.c \
diff --git a/lib/canonpath.c b/lib/canonpath.c
new file mode 100644
index 0000000..0d52f3e
--- /dev/null
+++ b/lib/canonpath.c
@@ -0,0 +1,107 @@
+/* canonpath.c - function to canonicalize file paths
+ *
+ * Adapted by Raphael Hertzog from pathcanon.c in bash-2.05a.
+ *
+ * Copyright (C) 1993 Free Software Foundation, Inc.
+ * Copyright (C) 2007 Raphael Hertzog.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Canonicalize PATH, and return a new path. The new path differs from PATH
+ * in that:
+ * Multiple `/'s are collapsed to a single `/'.
+ * Leading `./'s and trailing `/.'s are removed.
+ * Trailing `/'s are removed.
+ * Non-leading `../'s and trailing `..'s are handled by removing
+ * portions of the path.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#define ABSPATH(x) ((x)[0] == '/')
+#define RELPATH(x) ((x)[0] != '/')
+#define DIRSEP '/'
+#define ISDIRSEP(c) ((c) == '/')
+#define PATHSEP(c) (ISDIRSEP(c) || (c) == 0)
+
+char *canonpath (char* path) {
+ char stub_char;
+ char *result, *p, *q, *base, *dotdot;
+ int rooted;
+
+ assert(path != NULL);
+
+ /* The result cannot be larger than the input PATH. */
+ result = path;
+
+ if ((rooted = ABSPATH(path))) {
+ stub_char = DIRSEP;
+ base = result + 1;
+ } else {
+ stub_char = '.';
+ base = result;
+ }
+
+ /*
+ * invariants:
+ * base points to the portion of the path we want to modify
+ * p points at beginning of path element we're considering.
+ * q points just past the last path element we wrote (no slash).
+ * dotdot points just past the point where .. cannot backtrack
+ * any further (no slash).
+ */
+ p = q = dotdot = base;
+
+ while (*p) {
+ if (ISDIRSEP(p[0])) { /* null element */
+ p++;
+ } else if(p[0] == '.' && PATHSEP(p[1])) { /* . and ./ */
+ p += 1; /* don't count the separator in case it is nul */
+ } else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) { /* .. and ../ */
+ p += 2; /* skip `..' */
+ if (q > dotdot) { /* can backtrack */
+ while (--q > dotdot && ISDIRSEP(*q) == 0)
+ ;
+ } else if (rooted == 0) {
+ /* /.. is / but ./../ is .. */
+ if (q != base)
+ *q++ = DIRSEP;
+ *q++ = '.';
+ *q++ = '.';
+ dotdot = q;
+ }
+ } else { /* real path element */
+ /* add separator if not at start of work portion of result */
+ if (q != base)
+ *q++ = DIRSEP;
+ while (*p && (ISDIRSEP(*p) == 0))
+ *q++ = *p++;
+ }
+ }
+
+ /* Empty string is really ``.'' or `/', depending on what we started with. */
+ if (q == result)
+ *q++ = stub_char;
+ *q = '\0';
+
+ return (result);
+}
diff --git a/lib/dpkg.h b/lib/dpkg.h
index ff3ac9a..2030bd7 100644
--- a/lib/dpkg.h
+++ b/lib/dpkg.h
@@ -384,6 +384,9 @@ enum compress_type {
void decompress_cat(enum compress_type type, int fd_in, int fd_out, char *desc, ...) NONRETURNING;
void compress_cat(enum compress_type type, int fd_in, int fd_out, const char *compression, char *desc, ...) NONRETURNING;
+/*** from canonpath.c ***/
+char *canonpath(char *path);
+
/*** from compat.c ***/
#ifndef HAVE_STRERROR
diff --git a/src/query.c b/src/query.c
index 1addb08..88c9bd9 100644
--- a/src/query.c
+++ b/src/query.c
@@ -35,6 +35,9 @@
#include <sys/ioctl.h>
#include <sys/termios.h>
#include <fcntl.h>
+#include <libgen.h>
+#include <errno.h>
+#include <sys/param.h>
#include <dpkg.h>
#include <dpkg-db.h>
@@ -281,6 +284,8 @@ void searchfiles(const char *const *argv) {
while ((thisarg= *argv++) != 0) {
found= 0;
if (!strchr("*[?/",*thisarg)) {
+ /* If thisarg doesn't start with a character from "*[?/", then
+ * change it in "*<thisarg>*". */
varbufreset(&vb);
varbufaddc(&vb,'*');
varbufaddstr(&vb,thisarg);
@@ -289,9 +294,44 @@ void searchfiles(const char *const *argv) {
thisarg= vb.buf;
}
if (strcspn(thisarg,"*[?\\") == strlen(thisarg)) {
- namenode= findnamenode(thisarg, 0);
- found += searchoutput(namenode);
+ /* No special character at the beginning, it should be an
+ * absolute filename. */
+ struct stat stab;
+ int more;
+ do {
+ more= 0;
+ namenode= findnamenode(thisarg, 0);
+ found += searchoutput(namenode);
+ if (!found && !lstat(thisarg, &stab) && S_ISLNK(stab.st_mode)) {
+ char link_buf[MAXPATHLEN], *temp, *symlink, *link_dir;
+ ssize_t s = readlink(thisarg, link_buf, MAXPATHLEN - 1);
+ if (s == -1) {
+ fprintf(stderr, _("Failed to read link %s: %s\n"), thisarg, strerror(errno));
+ more= 0;
+ break;
+ }
+ link_buf[s] = '\0';
+ symlink= strdup(thisarg);
+ varbufreset(&vb);
+ if (link_buf[0] == '/') { /* Absolute link */
+ varbufaddstr(&vb, link_buf);
+ } else { /* Relative link */
+ temp= strdup(thisarg);
+ link_dir= dirname(temp);
+ varbufaddstr(&vb, link_dir);
+ free(temp);
+ varbufaddc(&vb, '/');
+ varbufaddbuf(&vb, link_buf, s);
+ }
+ varbufaddc(&vb, '\0');
+ thisarg= canonpath(vb.buf);
+ printf(_("non-packaged symlink to %s: %s\n"), thisarg, symlink);
+ free(symlink);
+ more= 1;
+ }
+ } while (more);
} else {
+ /* Check all files looking for those matching the pattern */
it= iterfilestart();
while ((namenode= iterfilenext(it)) != 0) {
if (fnmatch(thisarg,namenode->name,0)) continue;
@@ -307,6 +347,7 @@ void searchfiles(const char *const *argv) {
if (ferror(stdout)) werr("stdout");
}
}
+ varbuffree(&vb);
modstatdb_shutdown();
}