svn commit: r185452 - in head/usr.bin/cpio: . test

Tim Kientzle kientzle at FreeBSD.org
Sat Nov 29 12:22:03 PST 2008


Author: kientzle
Date: Sat Nov 29 20:22:02 2008
New Revision: 185452
URL: http://svn.freebsd.org/changeset/base/185452

Log:
  Format the output of -itv for real.  In particular:
   * Lookup uname/gname if not provided by the archive (I copied the
     uname/gname lookup cache from bsdtar)
   * Format device number instead of size for device nodes
   * Format date.
  
  There's still a few improvements that I could copy from
  bsdtar, especially the locale-aware safe_fprintf() code
  and the locale-aware setup for day_first date formatting.
  (And, of course, I need to think through a clean way to
  push this stuff down into libarchive.)
  
  Thanks to Peter Wemm for reminding me of this overlooked TODO item.

Modified:
  head/usr.bin/cpio/cpio.c
  head/usr.bin/cpio/cpio.h
  head/usr.bin/cpio/test/test_option_tv.stdout.uu

Modified: head/usr.bin/cpio/cpio.c
==============================================================================
--- head/usr.bin/cpio/cpio.c	Sat Nov 29 20:16:53 2008	(r185451)
+++ head/usr.bin/cpio/cpio.c	Sat Nov 29 20:22:02 2008	(r185452)
@@ -41,6 +41,12 @@ __FBSDID("$FreeBSD$");
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
 #ifdef HAVE_STDARG_H
 #include <stdarg.h>
 #endif
@@ -58,11 +64,32 @@ __FBSDID("$FreeBSD$");
 #include "cpio.h"
 #include "matching.h"
 
+/* Fixed size of uname/gname caches. */
+#define	name_cache_size 101
+
+struct name_cache {
+	int	probes;
+	int	hits;
+	size_t	size;
+	struct {
+		id_t id;
+		char *name;
+	} cache[name_cache_size];
+};
+
 static int	copy_data(struct archive *, struct archive *);
 static const char *cpio_rename(const char *name);
 static int	entry_to_archive(struct cpio *, struct archive_entry *);
 static int	file_to_archive(struct cpio *, const char *);
+static void	free_cache(struct name_cache *cache);
+static void	list_item_verbose(struct cpio *, struct archive_entry *);
 static void	long_help(void);
+static const char *lookup_gname(struct cpio *, gid_t gid);
+static int	lookup_gname_helper(struct cpio *,
+		    const char **name, id_t gid);
+static const char *lookup_uname(struct cpio *, uid_t uid);
+static int	lookup_uname_helper(struct cpio *,
+		    const char **name, id_t uid);
 static void	mode_in(struct cpio *);
 static void	mode_list(struct cpio *);
 static void	mode_out(struct cpio *);
@@ -271,6 +298,8 @@ main(int argc, char *argv[])
 		    "Must specify at least one of -i, -o, or -p");
 	}
 
+	free_cache(cpio->gname_cache);
+	free_cache(cpio->uname_cache);
 	return (0);
 }
 
@@ -805,18 +834,9 @@ mode_list(struct cpio *cpio)
 		}
 		if (excluded(cpio, archive_entry_pathname(entry)))
 			continue;
-		if (cpio->verbose) {
-			/* TODO: uname/gname lookups */
-			/* TODO: Clean this up. */
-			fprintf(stdout,
-			    "%s%3d %8s%8s " CPIO_FILESIZE_PRINTF " %s\n",
-			    archive_entry_strmode(entry),
-			    archive_entry_nlink(entry),
-			    archive_entry_uname(entry),
-			    archive_entry_gname(entry),
-			    (CPIO_FILESIZE_TYPE)archive_entry_size(entry),
-			    archive_entry_pathname(entry));
-		} else
+		if (cpio->verbose)
+			list_item_verbose(cpio, entry);
+		else
 			fprintf(stdout, "%s\n", archive_entry_pathname(entry));
 	}
 	r = archive_read_close(a);
@@ -832,6 +852,73 @@ mode_list(struct cpio *cpio)
 	exit(0);
 }
 
+/*
+ * Display information about the current file.
+ *
+ * The format here roughly duplicates the output of 'ls -l'.
+ * This is based on SUSv2, where 'tar tv' is documented as
+ * listing additional information in an "unspecified format,"
+ * and 'pax -l' is documented as using the same format as 'ls -l'.
+ */
+static void
+list_item_verbose(struct cpio *cpio, struct archive_entry *entry)
+{
+	char			 size[32];
+	char			 date[32];
+	const char 		*uname, *gname;
+	FILE			*out = stdout;
+	const struct stat	*st;
+	const char		*fmt;
+	time_t			 tim;
+	static time_t		 now;
+
+	st = archive_entry_stat(entry);
+
+	if (!now)
+		time(&now);
+
+	/* Use uname if it's present, else uid. */
+	uname = archive_entry_uname(entry);
+	if (uname == NULL)
+		uname = lookup_uname(cpio, archive_entry_uid(entry));
+
+	/* Use gname if it's present, else gid. */
+	gname = archive_entry_gname(entry);
+	if (gname == NULL)
+		gname = lookup_gname(cpio, archive_entry_gid(entry));
+
+	/* Print device number or file size. */
+	if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
+		snprintf(size, sizeof(size), "%lu,%lu",
+		    (unsigned long)major(st->st_rdev),
+		    (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */
+	} else {
+		snprintf(size, sizeof(size), CPIO_FILESIZE_PRINTF,
+		    (CPIO_FILESIZE_TYPE)st->st_size);
+	}
+
+	/* Format the time using 'ls -l' conventions. */
+	tim = (time_t)st->st_mtime;
+	if (abs(tim - now) > (365/2)*86400)
+		fmt = cpio->day_first ? "%e %b  %Y" : "%b %e  %Y";
+	else
+		fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M";
+	strftime(date, sizeof(date), fmt, localtime(&tim));
+
+	fprintf(out, "%s%3d %-8s %-8s %8s %12s %s",
+	    archive_entry_strmode(entry),
+	    archive_entry_nlink(entry),
+	    uname, gname, size, date,
+	    archive_entry_pathname(entry));
+
+	/* Extra information for links. */
+	if (archive_entry_hardlink(entry)) /* Hard link */
+		fprintf(out, " link to %s", archive_entry_hardlink(entry));
+	else if (archive_entry_symlink(entry)) /* Symbolic link */
+		fprintf(out, " -> %s", archive_entry_symlink(entry));
+	fprintf(out, "\n");
+}
+
 static void
 mode_pass(struct cpio *cpio, const char *destdir)
 {
@@ -1040,3 +1127,123 @@ process_lines_free(struct line_reader *l
 	free(lr->pathname);
 	free(lr);
 }
+
+static void
+free_cache(struct name_cache *cache)
+{
+	size_t i;
+
+	if (cache != NULL) {
+		for (i = 0; i < cache->size; i++)
+			free(cache->cache[i].name);
+		free(cache);
+	}
+}
+
+/*
+ * Lookup uname/gname from uid/gid, return NULL if no match.
+ */
+static const char *
+lookup_name(struct cpio *cpio, struct name_cache **name_cache_variable,
+    int (*lookup_fn)(struct cpio *, const char **, id_t), id_t id)
+{
+	char asnum[16];
+	struct name_cache	*cache;
+	const char *name;
+	int slot;
+
+
+	if (*name_cache_variable == NULL) {
+		*name_cache_variable = malloc(sizeof(struct name_cache));
+		if (*name_cache_variable == NULL)
+			cpio_errc(1, ENOMEM, "No more memory");
+		memset(*name_cache_variable, 0, sizeof(struct name_cache));
+		(*name_cache_variable)->size = name_cache_size;
+	}
+
+	cache = *name_cache_variable;
+	cache->probes++;
+
+	slot = id % cache->size;
+	if (cache->cache[slot].name != NULL) {
+		if (cache->cache[slot].id == id) {
+			cache->hits++;
+			return (cache->cache[slot].name);
+		}
+		free(cache->cache[slot].name);
+		cache->cache[slot].name = NULL;
+	}
+
+	if (lookup_fn(cpio, &name, id) == 0) {
+		if (name == NULL || name[0] == '\0') {
+			/* If lookup failed, format it as a number. */
+			snprintf(asnum, sizeof(asnum), "%u", (unsigned)id);
+			name = asnum;
+		}
+		cache->cache[slot].name = strdup(name);
+		if (cache->cache[slot].name != NULL) {
+			cache->cache[slot].id = id;
+			return (cache->cache[slot].name);
+		}
+		/*
+		 * Conveniently, NULL marks an empty slot, so
+		 * if the strdup() fails, we've just failed to
+		 * cache it.  No recovery necessary.
+		 */
+	}
+	return (NULL);
+}
+
+static const char *
+lookup_uname(struct cpio *cpio, uid_t uid)
+{
+	return (lookup_name(cpio, &cpio->uname_cache,
+		    &lookup_uname_helper, (id_t)uid));
+}
+
+static int
+lookup_uname_helper(struct cpio *cpio, const char **name, id_t id)
+{
+	struct passwd	*pwent;
+
+	(void)cpio; /* UNUSED */
+
+	errno = 0;
+	pwent = getpwuid((uid_t)id);
+	if (pwent == NULL) {
+		*name = NULL;
+		if (errno != 0)
+			cpio_warnc(errno, "getpwuid(%d) failed", id);
+		return (errno);
+	}
+
+	*name = pwent->pw_name;
+	return (0);
+}
+
+static const char *
+lookup_gname(struct cpio *cpio, gid_t gid)
+{
+	return (lookup_name(cpio, &cpio->gname_cache,
+		    &lookup_gname_helper, (id_t)gid));
+}
+
+static int
+lookup_gname_helper(struct cpio *cpio, const char **name, id_t id)
+{
+	struct group	*grent;
+
+	(void)cpio; /* UNUSED */
+
+	errno = 0;
+	grent = getgrgid((gid_t)id);
+	if (grent == NULL) {
+		*name = NULL;
+		if (errno != 0)
+			cpio_warnc(errno, "getgrgid(%d) failed", id);
+		return (errno);
+	}
+
+	*name = grent->gr_name;
+	return (0);
+}

Modified: head/usr.bin/cpio/cpio.h
==============================================================================
--- head/usr.bin/cpio/cpio.h	Sat Nov 29 20:16:53 2008	(r185451)
+++ head/usr.bin/cpio/cpio.h	Sat Nov 29 20:22:02 2008	(r185452)
@@ -65,6 +65,7 @@ struct cpio {
 	char		 *pass_destpath;
 	int		  uid_override;
 	int		  gid_override;
+	int		  day_first; /* true if locale prefers day/mon */
 
 	/* If >= 0, then close this when done. */
 	int		  fd;
@@ -76,6 +77,9 @@ struct cpio {
 	int		  return_value; /* Value returned by main() */
 	struct archive_entry_linkresolver *linkresolver;
 
+	struct name_cache *uname_cache;
+	struct name_cache *gname_cache;
+
 	/* Work data. */
 	struct matching  *matching;
 	char		 *buff;

Modified: head/usr.bin/cpio/test/test_option_tv.stdout.uu
==============================================================================
--- head/usr.bin/cpio/test/test_option_tv.stdout.uu	Sat Nov 29 20:16:53 2008	(r185451)
+++ head/usr.bin/cpio/test/test_option_tv.stdout.uu	Sat Nov 29 20:22:02 2008	(r185452)
@@ -1,5 +1,6 @@
 $FreeBSD$
 begin 644 test_option_tv.stdout
-G+7)W+7(M+7(M+2`@(#$@("`H;G5L;"D@("AN=6QL*2`P(&9I;&4*
+M+7)W+7(M+7(M+2`@(#$@=&EM("`@("`@=&EM("`@("`@("`@("`@(#`@1&5C
+/(#,Q("`Q.38Y(&9I;&4*
 `
 end


More information about the svn-src-all mailing list