summaryrefslogtreecommitdiffstats
path: root/usr.bin/cpio
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2008-11-29 20:22:02 +0000
committerkientzle <kientzle@FreeBSD.org>2008-11-29 20:22:02 +0000
commitbb05e1a003ef80629f9ca38084317dc934119ef3 (patch)
tree960fdddda5b7fc3b22f4cde27aff5e282ba07b52 /usr.bin/cpio
parent090a6bb003eec2932361f639e134808c8e998efa (diff)
downloadFreeBSD-src-bb05e1a003ef80629f9ca38084317dc934119ef3.zip
FreeBSD-src-bb05e1a003ef80629f9ca38084317dc934119ef3.tar.gz
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.
Diffstat (limited to 'usr.bin/cpio')
-rw-r--r--usr.bin/cpio/cpio.c231
-rw-r--r--usr.bin/cpio/cpio.h4
-rw-r--r--usr.bin/cpio/test/test_option_tv.stdout.uu3
3 files changed, 225 insertions, 13 deletions
diff --git a/usr.bin/cpio/cpio.c b/usr.bin/cpio/cpio.c
index a05c0e6..b2f9a95 100644
--- a/usr.bin/cpio/cpio.c
+++ b/usr.bin/cpio/cpio.c
@@ -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 *lr)
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);
+}
diff --git a/usr.bin/cpio/cpio.h b/usr.bin/cpio/cpio.h
index 1db7ec1..9af8a09 100644
--- a/usr.bin/cpio/cpio.h
+++ b/usr.bin/cpio/cpio.h
@@ -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;
diff --git a/usr.bin/cpio/test/test_option_tv.stdout.uu b/usr.bin/cpio/test/test_option_tv.stdout.uu
index baead3e..7f1879c 100644
--- a/usr.bin/cpio/test/test_option_tv.stdout.uu
+++ b/usr.bin/cpio/test/test_option_tv.stdout.uu
@@ -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
OpenPOWER on IntegriCloud