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.
This commit is contained in:
parent
090a6bb003
commit
bb05e1a003
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user