This patch likely needs some format polishing. I'll get there
eventually,
but I'm headed for rpmrepo *.rpm file digests, patterns, and lua
hooks next ...
Briefly, the patch adds a ":stat(foo,bar,baz)" format extension for
formatting
on-disk stat(2) fields, readlink, and eventually digests and xattrs and
whatever else seems usefully attached to a file path.
An example usage is easier:
$ rpm -q --qf '[%{FILENAMES:stat(mode,uname,gname)}\n]' popt
040755 | root | root
120777 | root | root
Some of the bugtures/featlets that need thought are:
1) unknown format extension parameters are ignored, breaking field
formatting.
The " | " separator is pretty feeble too.
2) one likely wants more control of output format than currently
provided,
like using strftime(3) instead of ctime(3), or perhaps prefixing like
"uname:root".
3) Lstat/Readlink failures are returned in-band on stdout. I have
always hated
that about rpm, but I'll be damned if I can think of sufficiently
better to justify a
change in rpm --query behavior.
Enjoy!
73 de Jeff
On May 10, 2008, at 7:12 AM, Jeff Johnson wrote:
> RPM Package Manager, CVS Repository
> http://rpm5.org/cvs/
>
> ______________________________________________________________________
> ______
>
> Server: rpm5.org Name: Jeff Johnson
> Root: /v/rpm/cvs Email: jbj@rpm5.org
> Module: rpm Date: 10-May-2008
> 13:12:20
> Branch: HEAD Handle: 2008051011122000
>
> Modified files:
> rpm CHANGES
> rpm/rpmdb hdrfmt.c
>
> Log:
> - jbj: replace "diskstat" tag with :stat format extension instead.
>
> Summary:
> Revision Changes Path
> 1.2333 +1 -0 rpm/CHANGES
> 1.85 +209 -26 rpm/rpmdb/hdrfmt.c
>
> ______________________________________________________________________
> ______
>
> patch -p0 <<'@@ .'
> Index: rpm/CHANGES
>
> ======================================================================
> ======
> $ cvs diff -u -r1.2332 -r1.2333 CHANGES
> --- rpm/CHANGES 10 May 2008 08:53:13 -0000 1.2332
> +++ rpm/CHANGES 10 May 2008 11:12:20 -0000 1.2333
> @@ -1,5 +1,6 @@
>
> 5.1.0 -> 5.2a0:
> + - jbj: replace "diskstat" tag with :stat format extension
> instead.
> - jbj: add :digest(foo) format. :fdigest(algo) for rpmrepo
> spew soon ...
> - jbj: generalize :utf8 to :iconv(UTF-8) iconv(3) conversions.
> - jbj: permit :foo(A,B,C) parameters to be passed to
> extension formats.
> @@ .
> patch -p0 <<'@@ .'
> Index: rpm/rpmdb/hdrfmt.c
>
> ======================================================================
> ======
> $ cvs diff -u -r1.84 -r1.85 hdrfmt.c
> --- rpm/rpmdb/hdrfmt.c 10 May 2008 08:53:14 -0000 1.84
> +++ rpm/rpmdb/hdrfmt.c 10 May 2008 11:12:20 -0000 1.85
> @@ -51,6 +51,7 @@
> #include "legacy.h"
> #include "argv.h"
> #include "misc.h"
> +#include "ugid.h"
>
> #include "debug.h"
>
> @@ -2009,16 +2010,6 @@
> return rc;
> }
>
> -static int diskstatTag(Header h, HE_t he)
> - /*@modifies he @*/
> -{
> - int rc;
> -
> - he->tag = RPMTAG_BASENAMES;
> - rc = _fnTag(h, he);
> - return rc;
> -}
> -
> static int PRCOSkip(rpmTag tag, rpmTagData N, rpmTagData EVR,
> rpmTagData F,
> uint32_t i)
> /*@*/
> @@ -2692,14 +2683,14 @@
> /*@=globstate@*/
> }
>
> -typedef struct _key {
> +typedef struct key_s {
> /*@observer@*/
> const char *name; /* key name */
> - uint32_t algo;
> + uint32_t value;
> } KEY;
>
> /*@unchecked@*/ /*@observer@*/
> -static KEY keylist[] = {
> +static KEY keyDigests[] = {
> { "adler32", PGPHASHALGO_ADLER32 },
> { "crc32", PGPHASHALGO_CRC32 },
> { "crc64", PGPHASHALGO_CRC64 },
> @@ -2721,33 +2712,94 @@
> { "sha512", PGPHASHALGO_SHA512 },
> { "tiger192", PGPHASHALGO_TIGER192 },
> };
> +/*@unchecked@*/
> +static size_t nkeyDigests = sizeof(keyDigests) / sizeof
> (keyDigests[0]);
> +
> +/**
> + * Bit field enum for stat(2) keys.
> + */
> +enum keyStat_e {
> + STAT_KEYS_NONE = 0,
> + STAT_KEYS_DEV = (1U << 0), /*!< st_dev */
> + STAT_KEYS_INO = (1U << 1), /*!< st_ino */
> + STAT_KEYS_MODE = (1U << 2), /*!< st_mode */
> + STAT_KEYS_NLINK = (1U << 3), /*!< st_nlink */
> + STAT_KEYS_UID = (1U << 4), /*!< st_uid */
> + STAT_KEYS_GID = (1U << 5), /*!< st_gid */
> + STAT_KEYS_RDEV = (1U << 6), /*!< st_rdev */
> + STAT_KEYS_SIZE = (1U << 7), /*!< st_size */
> + STAT_KEYS_BLKSIZE = (1U << 8), /*!< st_blksize */
> + STAT_KEYS_BLOCKS = (1U << 9), /*!< st_blocks */
> + STAT_KEYS_ATIME = (1U << 10), /*!< st_atime */
> + STAT_KEYS_CTIME = (1U << 11), /*!< st_ctime */
> + STAT_KEYS_MTIME = (1U << 12), /*!< st_mtime */
> +#ifdef NOTYET
> + STAT_KEYS_FLAGS = (1U << 13), /*!< st_flags */
> +#endif
> + STAT_KEYS_SLINK = (1U << 14), /*!< symlink */
> +#ifdef NOTYET
> + STAT_KEYS_DIGEST = (1U << 15), /*!< digest */
> + STAT_KEYS_FCONTEXT = (1U << 16), /*!< fcontext */
> +#endif
> + STAT_KEYS_UNAME = (1U << 17), /*!< user name */
> + STAT_KEYS_GNAME = (1U << 18), /*!< group name */
> +};
>
> +/*@unchecked@*/ /*@observer@*/
> +static KEY keyStat[] = {
> + { "atime", STAT_KEYS_ATIME },
> + { "ctime", STAT_KEYS_CTIME },
> + { "blksize", STAT_KEYS_BLKSIZE },
> + { "blocks", STAT_KEYS_BLOCKS },
> + { "dev", STAT_KEYS_DEV },
> +#ifdef NOTYET
> + { "digest", STAT_KEYS_DIGEST },
> + { "fcontext", STAT_KEYS_FCONTEXT },
> + { "flags", STAT_KEYS_FLAGS },
> +#endif
> + { "gid", STAT_KEYS_GID },
> + { "gname", STAT_KEYS_GNAME },
> + { "ino", STAT_KEYS_INO },
> + { "link", STAT_KEYS_SLINK },
> + { "mode", STAT_KEYS_MODE },
> + { "nlink", STAT_KEYS_NLINK },
> + { "rdev", STAT_KEYS_RDEV },
> + { "size", STAT_KEYS_SIZE },
> + { "mtime", STAT_KEYS_MTIME },
> + { "uid", STAT_KEYS_UID },
> + { "uname", STAT_KEYS_UNAME },
> +};
> +/*@unchecked@*/
> +static size_t nkeyStat = sizeof(keyStat) / sizeof(keyStat[0]);
> +
> +/**
> + */
> static int
> -keycompare(const void * a, const void * b)
> +keyCmp(const void * a, const void * b)
> /*@*/
> {
> return strcmp(((KEY *)a)->name, ((KEY *)b)->name);
> }
>
> +/**
> + */
> static uint32_t
> -digestname2algo(const char *name)
> +keyValue(KEY * keys, size_t nkeys, /*@null@*/ const char *name)
> /*@*/
> {
> - uint32_t algo = PGPHASHALGO_SHA1;
> + uint32_t keyval = 0;
>
> if (name && * name) {
> - KEY tmp = { .name = name };
> - KEY *k = (KEY *)bsearch(&tmp, keylist,
> - sizeof(keylist) / sizeof(keylist[0]),
> - sizeof(keylist[0]), keycompare);
> + KEY needle = { .name = name };
> + KEY *k = (KEY *)bsearch(&needle, keys, nkeys, sizeof(*keys),
> keyCmp);
> if (k)
> - algo = k->algo;
> + keyval = k->value;
> }
> - return algo;
> + return keyval;
> }
>
> /**
> - * Return digest of input.
> + * Return digest of tag data.
> * @param he tag container
> * @param av paramater list (NULL uses md5)
> * @return formatted string
> @@ -2776,7 +2828,8 @@
> break;
> }
>
> - { uint32_t algo = digestname2algo((av ? av[0] : NULL));
> + { uint32_t keyval = keyValue(keyDigests, nkeyDigests, (av ?
> av[0] : NULL));
> + uint32_t algo = (keyval ? keyval : PGPHASHALGO_SHA1);
> DIGEST_CTX ctx = rpmDigestInit(algo, 0);
> int xx = rpmDigestUpdate(ctx, he->p.ptr, ns);
> xx = rpmDigestFinal(ctx, &val, NULL, 1);
> @@ -2788,6 +2841,136 @@
> /*@=globstate@*/
> }
>
> +/**
> + * Return file info.
> + * @param he tag container
> + * @param av paramater list (NULL uses sha1)
> + * @return formatted string
> + */
> +static /*@only@*/ char * statFormat(HE_t he, /*@null@*/ const
> char ** av)
> + /*@*/
> +{
> + /*@unchecked@*/
> + static const char *avdefault[] = { "mode", NULL };
> + const char * fn = he->p.str;
> + struct stat sb, *st = &sb;
> + int ix = (he->ix > 0 ? he->ix : 0);
> + char * val = NULL;
> + int xx;
> + int i;
> +
> +assert(ix == 0);
> + switch(he->t) {
> + default:
> + val = xstrdup(_("(invalid type :stat)"));
> + goto exit;
> + /*@notreached@*/ break;
> + case RPM_STRING_TYPE:
> + if (Lstat(fn, st) == 0)
> + break;
> + val = rpmExpand("(Lstat:", strerror(errno), ")", NULL);
> + goto exit;
> + /*@notreached@*/ break;
> + }
> +
> + if (!(av && av[0] && *av[0]))
> + av = avdefault;
> + for (i = 0; av[i] != NULL; i++) {
> + char b[BUFSIZ];
> + size_t nb = sizeof(b);
> + char * nval;
> + uint32_t keyval = keyValue(keyStat, nkeyStat, av[i]);
> +
> + nval = NULL;
> + b[0] = '\0';
> + switch (keyval) {
> + default:
> + break;
> + case STAT_KEYS_NONE:
> + break;
> + case STAT_KEYS_DEV:
> + xx = snprintf(b, nb, "0x%lx", (unsigned long)st->st_dev);
> + break;
> + case STAT_KEYS_INO:
> + xx = snprintf(b, nb, "0x%lx", (unsigned long)st->st_ino);
> + break;
> + case STAT_KEYS_MODE:
> + xx = snprintf(b, nb, "%06o", (unsigned)st->st_mode);
> + break;
> + case STAT_KEYS_NLINK:
> + xx = snprintf(b, nb, "0x%ld", (unsigned long)st->st_nlink);
> + break;
> + case STAT_KEYS_UID:
> + xx = snprintf(b, nb, "%ld", (unsigned long)st->st_uid);
> + break;
> + case STAT_KEYS_GID:
> + xx = snprintf(b, nb, "%ld", (unsigned long)st->st_gid);
> + break;
> + case STAT_KEYS_RDEV:
> + xx = snprintf(b, nb, "0x%lx", (unsigned long)st->st_rdev);
> + break;
> + case STAT_KEYS_SIZE:
> + xx = snprintf(b, nb, "%ld", (unsigned long)st->st_size);
> + break;
> + case STAT_KEYS_BLKSIZE:
> + xx = snprintf(b, nb, "%ld", (unsigned long)st->st_blksize);
> + break;
> + case STAT_KEYS_BLOCKS:
> + xx = snprintf(b, nb, "%ld", (unsigned long)st->st_blocks);
> + break;
> + case STAT_KEYS_ATIME:
> + (void) stpcpy(b, ctime(&st->st_atime));
> + break;
> + case STAT_KEYS_CTIME:
> + (void) stpcpy(b, ctime(&st->st_ctime));
> + break;
> + case STAT_KEYS_MTIME:
> + (void) stpcpy(b, ctime(&st->st_mtime));
> + break;
> +#ifdef NOTYET
> + case STAT_KEYS_FLAGS:
> + break;
> +#endif
> + case STAT_KEYS_SLINK:
> + if (S_ISLNK(st->st_mode)) {
> + ssize_t size = Readlink(fn, b, nb);
> + if (size == -1) {
> + nval = rpmExpand("(Readlink:", strerror(errno), ")", NULL);
> + stpcpy(b, nval);
> + nval = _free(nval);
> + }
> + }
> + break;
> +#ifdef NOTYET
> + case STAT_KEYS_DIGEST:
> + break;
> +#endif
> + case STAT_KEYS_UNAME:
> + (void) stpcpy(b, uidToUname(st->st_uid));
> + break;
> + case STAT_KEYS_GNAME:
> + (void) stpcpy(b, gidToGname(st->st_gid));
> + break;
> + }
> + if (b[0] == '\0')
> + continue;
> + b[nb-1] = '\0';
> +
> + if (val == NULL)
> + val = xstrdup(b);
> + else {
> + nval = rpmExpand(val, " | ", b, NULL);
> + val = _free(val);
> + val = nval;
> + }
> + }
> +
> +exit:
> +/*@-globstate@*/
> + return val;
> +/*@=globstate@*/
> +}
> +
> /*@-type@*/ /* FIX: cast? */
> static struct headerSprintfExtension_s _headerCompoundFormats[] = {
> { HEADER_EXT_TAG, "RPMTAG_CHANGELOGNAME",
> @@ -2826,8 +3009,6 @@
> { .tagFunction = origpathsTag } },
> { HEADER_EXT_TAG, "RPMTAG_FILESTAT",
> { .tagFunction = filestatTag } },
> - { HEADER_EXT_TAG, "RPMTAG_STAT",
> - { .tagFunction = diskstatTag } },
> { HEADER_EXT_TAG, "RPMTAG_PROVIDEXMLENTRY",
> { .tagFunction = PxmlTag } },
> { HEADER_EXT_TAG, "RPMTAG_REQUIREXMLENTRY",
> @@ -2876,6 +3057,8 @@
> { .fmtFunction = pgpsigFormat } },
> { HEADER_EXT_FORMAT, "sqlescape",
> { .fmtFunction = sqlescapeFormat } },
> + { HEADER_EXT_FORMAT, "stat",
> + { .fmtFunction = statFormat } },
> { HEADER_EXT_FORMAT, "triggertype",
> { .fmtFunction = triggertypeFormat } },
> { HEADER_EXT_FORMAT, "utf8",
> @@ .
> ______________________________________________________________________
> RPM Package Manager http://rpm5.org
> CVS Sources Repository rpm-cvs@rpm5.org
Received on Sat May 10 13:32:00 2008