commit 4b027294335c0672311f021f2dc76edaab049b11 Author: Marcin Banasiak Date: Sun Dec 13 20:50:11 2009 +0100 Support for query format in ls command. Syntax of the queryfmt is almost the same as in rpm except query expressions which are not (yet) supported. To see supported tags type: ls --querytags diff --git a/cli/Makefile.am b/cli/Makefile.am index 53fcdfd..4a32e01 100644 --- a/cli/Makefile.am +++ b/cli/Makefile.am @@ -28,6 +28,7 @@ libpoclidek_la_SOURCES = \ op_split.c \ op_verify.c \ ls.c \ + ls_queryfmt.c ls_queryfmt.h \ install.c \ uninstall.c \ desc.c \ diff --git a/cli/ls_queryfmt.c b/cli/ls_queryfmt.c new file mode 100644 index 0000000..1a0c02f --- /dev/null +++ b/cli/ls_queryfmt.c @@ -0,0 +1,1096 @@ +/* + Copyright (C) 2009 Marcin Banasiak + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2 as + published by the Free Software Foundation (see file COPYING for details). + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* for asprintf() from stdio.h */ +#define _GNU_SOURCE + +/* FIXME: nbuf.h should include stdint.h */ +#include + +#include +#include +#include +#include +#include + +#include + +#include "capreq.h" +#include "i18n.h" +#include "log.h" +#include "ls_queryfmt.h" +#include "pkgu.h" +#include "pkgfl.h" + +#define n_strcase_eq(s, p) (strcasecmp(s, p) == 0) + +static const char *invalid_format = N_("invalid format:"); + +enum LsqfParseMode { + LSQF_PARSE_NORMAL = 0, + LSQF_PARSE_ARRAY +}; + +/* Tags have to be in alphabetical order. */ +enum { + LSQF_TAG_UNKNOWN = -1, + + LSQF_TAG_ARCH = 0, + LSQF_TAG_BASENAMES, + LSQF_TAG_BUILDHOST, + LSQF_TAG_BUILDTIME, + LSQF_TAG_CONFLICTFLAGS, + LSQF_TAG_CONFLICTS, + LSQF_TAG_CONFLICTVERSION, + LSQF_TAG_DESCRIPTION, + LSQF_TAG_DIRNAMES, + LSQF_TAG_EPOCH, + LSQF_TAG_FILELINKTOS, + LSQF_TAG_FILEMODES, + LSQF_TAG_FILENAMES, + LSQF_TAG_FILESIZES, + LSQF_TAG_GROUP, + LSQF_TAG_LICENSE, + LSQF_TAG_NAME, + LSQF_TAG_NVRA, + LSQF_TAG_OBSOLETEFLAGS, + LSQF_TAG_OBSOLETES, + LSQF_TAG_OBSOLETEVERSION, + LSQF_TAG_PACKAGECOLOR, + LSQF_TAG_PROVIDEFLAGS, + LSQF_TAG_PROVIDES, + LSQF_TAG_PROVIDEVERSION, + LSQF_TAG_RELEASE, + LSQF_TAG_REQUIREFLAGS, + LSQF_TAG_REQUIRES, + LSQF_TAG_REQUIREVERSION, + LSQF_TAG_SIZE, + LSQF_TAG_SOURCERPM, + LSQF_TAG_SUGGESTSFLAGS, + LSQF_TAG_SUGGESTS, + LSQF_TAG_SUGGESTSVERSION, + LSQF_TAG_SUMMARY, + LSQF_TAG_URL, + LSQF_TAG_VENDOR, + LSQF_TAG_VERSION, + + LSQF_N_TAGS +}; + +enum { + LSQF_TAG_OUTFMTFN_NONE = 0, + + LSQF_TAG_OUTFMTFN_DATE, + LSQF_TAG_OUTFMTFN_DAY, + LSQF_TAG_OUTFMTFN_DEPFLAGS +}; + +struct lsqf_tags { + int tagid; + int is_array : 1; + int need_uinf : 1; + int need_flist : 1; + const char *tagname[4]; +}; + +/* + * LSQF_TAG_* have to be added in the same sequence as in enumeration + * (LSQF_TAG_ value is an index in lsqf_tags[] array). + */ +static const struct lsqf_tags lsqf_tags[] = { + { LSQF_TAG_ARCH, 0, 0, 0, { "ARCH", NULL } }, + { LSQF_TAG_BASENAMES, 1, 0, 1, { "BASENAMES", NULL } }, + { LSQF_TAG_BUILDHOST, 0, 1, 0, { "BUILDHOST", NULL } }, + { LSQF_TAG_BUILDTIME, 0, 0, 0, { "BUILDTIME", NULL } }, + { LSQF_TAG_CONFLICTFLAGS, 1, 0, 0, { "CONFLICTFLAGS", NULL } }, + { LSQF_TAG_CONFLICTS, 1, 0, 0, { "C", "CONFLICTNAME", "CONFLICTS", NULL } }, + { LSQF_TAG_CONFLICTVERSION, 1, 0, 0, { "CONFLICTVERSION", NULL } }, + { LSQF_TAG_DESCRIPTION, 0, 1, 0, { "DESCRIPTION", NULL } }, + { LSQF_TAG_DIRNAMES, 1, 0, 1, { "DIRNAMES", NULL } }, + { LSQF_TAG_EPOCH, 0, 0, 0, { "E", "EPOCH", NULL } }, + { LSQF_TAG_FILELINKTOS, 1, 0, 1, { "FILELINKTOS", NULL } }, + { LSQF_TAG_FILEMODES, 1, 0, 1, { "FILEMODES", NULL } }, + { LSQF_TAG_FILENAMES, 1, 0, 1, { "FILENAMES", NULL } }, + { LSQF_TAG_FILESIZES, 1, 0, 1, { "FILESIZES", NULL } }, + { LSQF_TAG_GROUP, 0, 0, 0, { "GROUP", NULL } }, + { LSQF_TAG_LICENSE, 0, 1, 0, { "LICENSE", NULL } }, + { LSQF_TAG_NAME, 0, 0, 0, { "N", "NAME", NULL } }, + { LSQF_TAG_NVRA, 0, 0, 0, { "NVRA", NULL } }, + { LSQF_TAG_OBSOLETEFLAGS, 1, 0, 0, { "OBSOLETEFLAGS", NULL } }, + { LSQF_TAG_OBSOLETES, 1, 0, 0, { "O", "OBSOLETENAME", "OBSOLETES", NULL } }, + { LSQF_TAG_OBSOLETEVERSION, 1, 0, 0, { "OBSOLETEVERSION", NULL } }, + { LSQF_TAG_PACKAGECOLOR, 0, 0, 0, { "PACKAGECOLOR", NULL } }, + { LSQF_TAG_PROVIDEFLAGS, 1, 0, 0, { "PROVIDEFLAGS", NULL } }, + { LSQF_TAG_PROVIDES, 1, 0, 0, { "P", "PROVIDENAME", "PROVIDES", NULL } }, + { LSQF_TAG_PROVIDEVERSION, 1, 0, 0, { "PROVIDEVERSION", NULL } }, + { LSQF_TAG_RELEASE, 0, 0, 0, { "R", "RELEASE", NULL } }, + { LSQF_TAG_REQUIREFLAGS, 1, 0, 0, { "REQUIREFLAGS", NULL } }, + { LSQF_TAG_REQUIRES, 1, 0, 0, { "REQUIRENAME", "REQUIRES", NULL } }, + { LSQF_TAG_REQUIREVERSION, 1, 0, 0, { "REQUIREVERSION", NULL } }, + { LSQF_TAG_SIZE, 0, 0, 0, { "SIZE", NULL } }, + { LSQF_TAG_SOURCERPM, 0, 0, 0, { "SOURCERPM", NULL } }, + { LSQF_TAG_SUGGESTSFLAGS, 1, 0, 0, { "SUGGESTSFLAGS", NULL } }, + { LSQF_TAG_SUGGESTS, 1, 0, 0, { "SUGGESTS", "SUGGESTSNAME", NULL } }, + { LSQF_TAG_SUGGESTSVERSION, 1, 0, 0, { "SUGGESTSVERSION", NULL } }, + { LSQF_TAG_SUMMARY, 0, 1, 0, { "SUMMARY", NULL } }, + { LSQF_TAG_URL, 0, 1, 0, { "URL", NULL } }, + { LSQF_TAG_VENDOR, 0, 1, 0, { "VENDOR", NULL } }, + { LSQF_TAG_VERSION, 0, 0, 0, { "V", "VERSION", NULL } }, + { LSQF_N_TAGS, 0, 0, 0, { NULL } } +}; + +struct lsqf_pkgdata { + const struct pkg *pkg; + struct pkgflist *flist; + struct pkguinf *uinf; +}; + +static struct lsqf_pkgdata *lsqf_pkgdata_new(const struct pkg *pkg) +{ + struct lsqf_pkgdata *pkgdata = NULL; + + pkgdata = n_malloc(sizeof(struct lsqf_pkgdata)); + + if (pkgdata) { + pkgdata->pkg = pkg; + pkgdata->flist = NULL; + pkgdata->uinf = NULL; + } + + return pkgdata; +} + +static struct pkgflist *lsqf_pkgdata_flist(struct lsqf_pkgdata *pkgdata) +{ + if (pkgdata->flist == NULL) + pkgdata->flist = pkg_get_flist(pkgdata->pkg); + + return pkgdata->flist; +} + +static struct pkguinf *lsqf_pkgdata_uinf(struct lsqf_pkgdata *pkgdata) +{ + if (pkgdata->uinf == NULL) + pkgdata->uinf = pkg_uinf(pkgdata->pkg); + + return pkgdata->uinf; +} + +static void lsqf_pkgdata_free(struct lsqf_pkgdata *pkgdata) +{ + if (pkgdata) { + if (pkgdata->flist) + pkgflist_free(pkgdata->flist); + + if (pkgdata->uinf) + pkguinf_free(pkgdata->uinf); + + n_free(pkgdata); + } +} + +static int get_tagid_by_name(char *tag) +{ + if (tag) { + unsigned int i, j; + + for (i = 0; i < LSQF_N_TAGS; i++) { + for (j = 0; lsqf_tags[i].tagname[j]; j++) { + if (n_strcase_eq(tag, lsqf_tags[i].tagname[j])) { + return lsqf_tags[i].tagid; + } + } + } + } + + return LSQF_TAG_UNKNOWN; +} + +static int get_outfmtfnid_by_name(char *outfmtfn) +{ + if (outfmtfn && *outfmtfn) { + if (n_str_eq(outfmtfn, "date")) + return LSQF_TAG_OUTFMTFN_DATE; + if (n_str_eq(outfmtfn, "day")) + return LSQF_TAG_OUTFMTFN_DAY; + if (n_str_eq(outfmtfn, "depflags")) + return LSQF_TAG_OUTFMTFN_DEPFLAGS; + } + + return LSQF_TAG_OUTFMTFN_NONE; +} + +/* TODO: move to capreq.c */ +static int capreq_snprintf_evr(char *str, size_t size, const struct capreq *cr) +{ + int n = 0; + + n_assert(size > 0); + + if (capreq_has_epoch(cr)) + n += n_snprintf(&str[n], size - n, "%d:", capreq_epoch(cr)); + + if (capreq_has_ver(cr)) + n += n_snprintf(&str[n], size - n, "%s", capreq_ver(cr)); + + if (capreq_has_rel(cr)) { + n_assert(capreq_has_ver(cr)); + + n += n_snprintf(&str[n], size - n, "-%s", capreq_rel(cr)); + } + + return n; +} + +static char *format_date(int outfmtfnid, uint32_t time) +{ + char *buf = NULL, datestr[32];; + + if (outfmtfnid == LSQF_TAG_OUTFMTFN_DATE) { + strftime(datestr, sizeof(datestr), "%c", gmtime((time_t *)&time)); + asprintf(&buf, "%s", datestr); + } else if (outfmtfnid == LSQF_TAG_OUTFMTFN_DAY) { + strftime(datestr, sizeof(datestr), "%a %b %d %Y", gmtime((time_t *)&time)); + asprintf(&buf, "%s", datestr); + } else { + asprintf(&buf, "%u", time); + } + + return buf; +} + +static char *format_flags(int outfmtfnid, struct capreq *cr) +{ + char *buf = NULL; + + if (outfmtfnid == LSQF_TAG_OUTFMTFN_DEPFLAGS) { + char relstr[3], *p; + + p = relstr; + *p = '\0'; + + if (cr->cr_relflags & REL_LT) + *p++ = '<'; + else if (cr->cr_relflags & REL_GT) + *p++ = '>'; + + if (cr->cr_relflags & REL_EQ) + *p++ = '='; + + *p = '\0'; + + asprintf(&buf, " %s ", relstr); + } else { + asprintf(&buf, "%u", cr->cr_relflags); + } + + return buf; +} + +static char *get_str_by_tagid(const struct lsqf_ent *ent, struct lsqf_pkgdata *pkgdata, unsigned int num) +{ + const struct pkg *pkg = pkgdata->pkg; + struct capreq *c = NULL; + char *buf = NULL, evr[32]; + unsigned int i; + + if (lsqf_tags[ent->tag.id].need_uinf) { + struct pkguinf *pkgu = lsqf_pkgdata_uinf(pkgdata); + const char *str = NULL; + + switch (ent->tag.id) { + case LSQF_TAG_BUILDHOST: + str = pkguinf_get(pkgu, PKGUINF_BUILDHOST); + break; + + case LSQF_TAG_DESCRIPTION: + str = pkguinf_get(pkgu, PKGUINF_DESCRIPTION); + break; + + case LSQF_TAG_LICENSE: + str = pkguinf_get(pkgu, PKGUINF_LICENSE); + break; + + case LSQF_TAG_SUMMARY: + str = pkguinf_get(pkgu, PKGUINF_SUMMARY); + break; + + case LSQF_TAG_URL: + str = pkguinf_get(pkgu, PKGUINF_URL); + break; + + case LSQF_TAG_VENDOR: + str = pkguinf_get(pkgu, PKGUINF_VENDOR); + break; + + default: + n_assert(0); + } + + if (str) + buf = n_strdup(str); + else + buf = n_strdup("(none)"); + + } else if (lsqf_tags[ent->tag.id].need_flist) { + struct pkgflist *flist = lsqf_pkgdata_flist(pkgdata); + struct pkgfl_ent *flent; + + if (flist) { + switch (ent->tag.id) { + case LSQF_TAG_BASENAMES: + case LSQF_TAG_FILELINKTOS: + case LSQF_TAG_FILEMODES: + case LSQF_TAG_FILENAMES: + case LSQF_TAG_FILESIZES: + for (i = 0; i < n_tuple_size(flist->fl); i++) { + flent = n_tuple_nth(flist->fl, i); + + if (flent->items <= num) + num -= flent->items; + else + break; + } + + if (ent->tag.id == LSQF_TAG_BASENAMES) + buf = n_strdup(flent->files[num]->basename); + else if (ent->tag.id == LSQF_TAG_FILEMODES) + asprintf(&buf, "%u", flent->files[num]->mode); + else if (ent->tag.id == LSQF_TAG_FILENAMES) { + if (*flent->dirname == '/') + asprintf(&buf, "%s%s", flent->dirname, flent->files[num]->basename); + else + asprintf(&buf, "/%s%s%s", flent->dirname, + *flent->files[num]->basename ? "/" : "", + flent->files[num]->basename); + } else if (ent->tag.id == LSQF_TAG_FILESIZES) + asprintf(&buf, "%u", flent->files[num]->size); + else + if (S_ISLNK(flent->files[num]->mode)) + buf = n_strdup(flent->files[num]->basename + strlen(flent->files[num]->basename) + 1); + + break; + + case LSQF_TAG_DIRNAMES: + flent = n_tuple_nth(flist->fl, num); + + asprintf(&buf, "%s%s", *flent->dirname == '/' ? "" : "/", + flent->dirname); + break; + + default: + n_assert(0); + } + } + } else { + switch (ent->tag.id) { + case LSQF_TAG_ARCH: + buf = n_strdup(pkg_arch(pkg)); + break; + + case LSQF_TAG_BUILDTIME: + buf = format_date(ent->tag.outfmtfnid, pkg->btime); + break; + + case LSQF_TAG_CONFLICTFLAGS: + case LSQF_TAG_CONFLICTS: + case LSQF_TAG_CONFLICTVERSION: + case LSQF_TAG_OBSOLETEFLAGS: + case LSQF_TAG_OBSOLETES: + case LSQF_TAG_OBSOLETEVERSION: + { + unsigned int n = 0; + + for (i = 0; i < n_array_size(pkg->cnfls); i++) { + struct capreq *cr = n_array_nth(pkg->cnfls, i); + + if (ent->tag.id == LSQF_TAG_CONFLICTS || ent->tag.id == LSQF_TAG_CONFLICTFLAGS) { + if (!capreq_is_obsl(cr)) { + if (n == num) { + c = cr; + break; + } + + n++; + } + } else { + if (capreq_is_obsl(cr)) { + if (n == num) { + c = cr; + break; + } + + n++; + } + } + } + + if (c) { + if (ent->tag.id == LSQF_TAG_CONFLICTS || ent->tag.id == LSQF_TAG_OBSOLETES) + buf = n_strdup(capreq_name(c)); + else if (ent->tag.id == LSQF_TAG_CONFLICTFLAGS || ent->tag.id == LSQF_TAG_OBSOLETEFLAGS) + buf = format_flags(ent->tag.outfmtfnid, c); + else if (ent->tag.id == LSQF_TAG_CONFLICTVERSION || ent->tag.id == LSQF_TAG_OBSOLETEVERSION) + if (capreq_snprintf_evr(evr, sizeof(evr), c) > 0) + buf = n_strdup(evr); + + } + break; + } + + case LSQF_TAG_EPOCH: + asprintf(&buf, "%d", pkg->epoch); + break; + + case LSQF_TAG_GROUP: + buf = n_strdup(pkg_group(pkg)); + break; + + case LSQF_TAG_NAME: + buf = n_strdup(pkg->name); + break; + + case LSQF_TAG_NVRA: + buf = n_strdup(pkg_id(pkg)); + break; + + case LSQF_TAG_PACKAGECOLOR: + asprintf(&buf, "%d", pkg->color); + break; + + case LSQF_TAG_PROVIDEFLAGS: + buf = format_flags(ent->tag.outfmtfnid, n_array_nth(pkg->caps, num)); + break; + + case LSQF_TAG_PROVIDES: + c = n_array_nth(pkg->caps, num); + buf = n_strdup(capreq_name(c)); + break; + + case LSQF_TAG_PROVIDEVERSION: + c = n_array_nth(pkg->caps, num); + + if (capreq_snprintf_evr(evr, sizeof(evr), c) > 0) + buf = n_strdup(evr); + + break; + + case LSQF_TAG_RELEASE: + buf = n_strdup(pkg->rel); + break; + + case LSQF_TAG_REQUIREFLAGS: + buf = format_flags(ent->tag.outfmtfnid, n_array_nth(pkg->reqs, num)); + break; + + case LSQF_TAG_REQUIRES: + c = n_array_nth(pkg->reqs, num); + + if (capreq_is_rpmlib(c)) + asprintf(&buf, "rpmlib(%s)", capreq_name(c)); + else + buf = n_strdup(capreq_name(c)); + + break; + + case LSQF_TAG_REQUIREVERSION: + c = n_array_nth(pkg->reqs, num); + + if (capreq_snprintf_evr(evr, sizeof(evr), c) > 0) + buf = n_strdup(evr); + + break; + + case LSQF_TAG_VERSION: + buf = n_strdup(pkg->ver); + break; + + case LSQF_TAG_SIZE: + asprintf(&buf, "%u", pkg->size); + break; + + case LSQF_TAG_SOURCERPM: + buf = n_strdup(pkg_srcfilename_s(pkg)); + break; + + case LSQF_TAG_SUGGESTSFLAGS: + buf = format_flags(ent->tag.outfmtfnid, n_array_nth(pkg->sugs, num)); + break; + + case LSQF_TAG_SUGGESTS: + c = n_array_nth(pkg->sugs, num); + buf = n_strdup(capreq_name(c)); + break; + + case LSQF_TAG_SUGGESTSVERSION: + c = n_array_nth(pkg->sugs, num); + + if (capreq_snprintf_evr(evr, sizeof(evr), c) > 0) + buf = n_strdup(evr); + + break; + + default: + n_assert(0); + } + } + + return buf; +} + +static char get_escaped_char(char zn) +{ + switch (zn) { + case 'a': return '\a'; + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + default: return zn; + } +} + +static struct lsqf_ent *lsqf_ent_new(int type) +{ + struct lsqf_ent *ent = NULL; + + ent = n_malloc(sizeof(struct lsqf_ent)); + + if (ent) { + ent->type = type; + + switch (type) { + case LSQF_ENT_TYPE_TAG: + ent->tag.id = 0; + ent->tag.iterate = 0; + ent->tag.countArray = 0; + ent->tag.pad = 0; + break; + + case LSQF_ENT_TYPE_STRING: + ent->string = NULL; + break; + + case LSQF_ENT_TYPE_ARRAY: + ent->array = lsqf_ent_array_new(); + break; + + default: + n_assert(0); + } + } + + return ent; +} + +static void lsqf_ent_free(struct lsqf_ent *ent) +{ + if (ent) { + switch (ent->type) { + case LSQF_ENT_TYPE_TAG: + break; + + case LSQF_ENT_TYPE_STRING: + if (ent->string) + n_free(ent->string); + + break; + + case LSQF_ENT_TYPE_ARRAY: + lsqf_ent_array_free(ent->array); + break; + } + + n_free(ent); + } +} + +struct lsqf_ent_array *lsqf_ent_array_new(void) +{ + struct lsqf_ent_array *array = NULL; + + array = n_malloc(sizeof(struct lsqf_ent_array)); + + if (array) { + array->ents = NULL; + array->items = 0; + } + + return array; +} + +void lsqf_ent_array_free(struct lsqf_ent_array *array) +{ + unsigned int i; + + if (array) { + for (i = 0; i < array->items; i++) { + struct lsqf_ent *ent = array->ents[i]; + + lsqf_ent_free(ent); + } + + if (array->ents) + n_free(array->ents); + + n_free(array); + } +} + +static void lsqf_ent_array_add_ent(struct lsqf_ent_array *array, struct lsqf_ent *ent) +{ + array->ents = n_realloc(array->ents, (array->items + 1) * sizeof(struct lsqf_ent *)); + + if (array->ents) { + array->ents[array->items] = ent; + + array->items++; + } +} + +static void lsqf_ent_array_add_ent_string(struct lsqf_ent_array *array, tn_buf *nbuf) +{ + struct lsqf_ent *ent = NULL; + + if (n_buf_size(nbuf) > 0) { + n_buf_putc(nbuf, '\0'); + + if ((ent = lsqf_ent_new(LSQF_ENT_TYPE_STRING))) { + ent->string = n_strdup(n_buf_ptr(nbuf)); + + lsqf_ent_array_add_ent(array, ent); + + n_buf_clean(nbuf); + } + } +} + +/** + * do_parse: + * + * Returns: 1 on error. + **/ +static int do_parse(struct lsqf_ent_array *array, char *fmt, char **endfmt, enum LsqfParseMode mode) +{ + struct lsqf_ent *ent; + tn_buf *nbuf = NULL; + int done = 0, error = 0; + char *end; + + if (fmt == NULL) + return 1; + + nbuf = n_buf_new(8); + + while (*fmt && !done && !error) { + switch (*fmt) { + case '%': + { + char *p, *outfmtfn; + int tagid, pad, countArray = 0, iterate = 0; + + fmt++; + + /* catch %% */ + if (*fmt == '%') { + n_buf_putc(nbuf, '%'); + break; + } + + pad = strtoul(fmt, &p, 10); + + fmt = p; + + if (*fmt != '{') { + logn(LOGERR, _("%s missing { after %%"), invalid_format); + error = 1; + break; + } + + fmt++; + + if (*fmt == '#') { + countArray = 1; + fmt++; + } else if (*fmt == '=') { + iterate = 1; + fmt++; + } + + if ((p = strchr(fmt, '}')) == NULL) { + logn(LOGERR, _("%s missing } after %%{"), invalid_format); + error = 1; + break; + } + + *p = '\0'; + + if (*fmt == '\0') { + logn(LOGERR, _("%s empty tag name"), invalid_format); + error = 1; + break; + } + + /* check if another output format is requested */ + if ((outfmtfn = strchr(fmt, ':')) != NULL) { + *outfmtfn = '\0'; + outfmtfn++; + } + + if ((tagid = get_tagid_by_name(fmt)) == LSQF_TAG_UNKNOWN) { + logn(LOGERR, _("%s unknown tag: \'%s\'"), invalid_format, fmt); + error = 1; + break; + } + + /* create new ent with a string that is currently stored in nbuf */ + lsqf_ent_array_add_ent_string(array, nbuf); + + ent = lsqf_ent_new(LSQF_ENT_TYPE_TAG); + ent->tag.id = tagid; + ent->tag.pad = pad; + ent->tag.countArray = countArray; + ent->tag.iterate = iterate; + ent->tag.outfmtfnid = get_outfmtfnid_by_name(outfmtfn); + + lsqf_ent_array_add_ent(array, ent); + + fmt = p; + + break; + } + case '[': + fmt++; + + lsqf_ent_array_add_ent_string(array, nbuf); + + ent = lsqf_ent_new(LSQF_ENT_TYPE_ARRAY); + lsqf_ent_array_add_ent(array, ent); + + if (do_parse(ent->array, fmt, &end, LSQF_PARSE_ARRAY)) { + error = 1; + break; + } + + if (*end == '\0') { + logn(LOGERR, _("%s missing ] at end of array"), invalid_format); + error = 1; + break; + } + + fmt = end; + + break; + + case ']': + if (mode != LSQF_PARSE_ARRAY) { + logn(LOGERR, _("%s unexpected ]"), invalid_format); + error = 1; + break; + } + + /* found end of array -> stop parsing */ + done = 1; + + /* save address of the last character we parsed */ + *endfmt = fmt; + + break; + + case '}': + logn(LOGERR, _("%s unexpected }"), invalid_format); + error = 1; + break; + + default: + if (fmt[0] == '\\' && fmt[1] != '\0') { + fmt++; + + n_buf_putc(nbuf, get_escaped_char(*fmt)); + + } else { + n_buf_putc(nbuf, *fmt); + } + } + + fmt++; + } + + if (error) { + n_buf_free(nbuf); + return 1; + } + + lsqf_ent_array_add_ent_string(array, nbuf); + + if (!done && endfmt) + *endfmt = fmt; + + n_buf_free(nbuf); + + return 0; +} + +/** + * lsqf_parse: + * + * Returns: On success, pointer to structure which is a base to display requested information or NULL when parsing failed. + **/ +struct lsqf_ent_array *lsqf_parse(char *fmt) +{ + struct lsqf_ent_array *array = NULL; + + if ((array = lsqf_ent_array_new())) { + if (do_parse(array, fmt, NULL, LSQF_PARSE_NORMAL)) { + lsqf_ent_array_free(array); + array = NULL; + } + } + + return array; +} + +static int get_tag_array_size(const struct lsqf_ent *ent, struct lsqf_pkgdata *pkgdata) +{ + const struct pkg *pkg = pkgdata->pkg; + unsigned int i; + int size = 1; + + n_assert(ent->type == LSQF_ENT_TYPE_TAG); + + if (lsqf_tags[ent->tag.id].is_array) { + size = 0; + + if (lsqf_tags[ent->tag.id].need_flist) { + struct pkgflist *flist = lsqf_pkgdata_flist(pkgdata); + + if (flist) { + switch (ent->tag.id) { + case LSQF_TAG_BASENAMES: + case LSQF_TAG_FILELINKTOS: + case LSQF_TAG_FILEMODES: + case LSQF_TAG_FILENAMES: + case LSQF_TAG_FILESIZES: + for (i = 0; i < n_tuple_size(flist->fl); i++) { + struct pkgfl_ent *flent = n_tuple_nth(flist->fl, i); + + size += flent->items; + } + + break; + + case LSQF_TAG_DIRNAMES: + size = n_tuple_size(flist->fl); + break; + + default: + n_assert(0); + } + } + } else { + switch (ent->tag.id) { + case LSQF_TAG_CONFLICTFLAGS: + case LSQF_TAG_CONFLICTS: + case LSQF_TAG_CONFLICTVERSION: + case LSQF_TAG_OBSOLETEFLAGS: + case LSQF_TAG_OBSOLETES: + case LSQF_TAG_OBSOLETEVERSION: + if (pkg->cnfls) { + int nobsl = 0, ncnfls = 0; + + for (i = 0; i < n_array_size(pkg->cnfls); i++) { + struct capreq *cr = n_array_nth(pkg->cnfls, i); + + if (capreq_is_obsl(cr)) + nobsl++; + else + ncnfls++; + } + + if (ent->tag.id == LSQF_TAG_CONFLICTFLAGS || ent->tag.id == LSQF_TAG_CONFLICTS + || ent->tag.id == LSQF_TAG_CONFLICTVERSION) + size = ncnfls; + else + size = nobsl; + } + + break; + + case LSQF_TAG_PROVIDEFLAGS: + case LSQF_TAG_PROVIDES: + case LSQF_TAG_PROVIDEVERSION: + if (pkg->caps) + size = n_array_size(pkg->caps); + break; + + case LSQF_TAG_REQUIREFLAGS: + case LSQF_TAG_REQUIRES: + case LSQF_TAG_REQUIREVERSION: + if (pkg->reqs) + size = n_array_size(pkg->reqs); + break; + + case LSQF_TAG_SUGGESTSFLAGS: + case LSQF_TAG_SUGGESTS: + case LSQF_TAG_SUGGESTSVERSION: + if (pkg->sugs) + size = n_array_size(pkg->sugs); + break; + + default: + n_assert(0); + } + } + } + + return size; +} + +/** + * Returns 1 when arrays size differ. + **/ +static int check_size(const struct lsqf_ent_array *array, struct lsqf_pkgdata *pkgdata, unsigned int *s) +{ + unsigned int i; + int size = 1, prev_size = -1; + + for (i = 0; i < array->items; i++) { + struct lsqf_ent *ent = array->ents[i]; + + if (ent->type == LSQF_ENT_TYPE_TAG) { + if (lsqf_tags[ent->tag.id].is_array) { + size = get_tag_array_size(ent, pkgdata); + } else { + /* check whether we want to print this tag with every iteration */ + if (ent->tag.iterate) + continue; + + size = 1; + } + + if (prev_size < 0) + prev_size = size; + + if (prev_size != size) + return 1; + } + } + + *s = size; + + return 0; +} + +static void add_tagstr_to_nbuf(tn_buf *nbuf, const struct lsqf_ent *ent, struct lsqf_pkgdata *pkgdata, unsigned int num) +{ + char *str = NULL, fmt[16]; + + if (ent->tag.countArray) { + n_snprintf(fmt, sizeof(fmt), "%%%dd", ent->tag.pad); + n_buf_printf(nbuf, fmt, get_tag_array_size(ent, pkgdata)); + + } else if ((str = get_str_by_tagid(ent, pkgdata, num))) { + n_snprintf(fmt, sizeof(fmt), "%%%ds", ent->tag.pad); + n_buf_printf(nbuf, fmt, str); + + n_free(str); + } +} + +/** + * tags_size - number of items in tags. It's mostly used by tag-arrays (for example REQUIRES) + */ +static int ent_array_to_string(const struct lsqf_ent_array *array, + struct lsqf_pkgdata *pkgdata, + tn_buf *nbuf, int tags_size) +{ + unsigned int i, j, size = 0; + + for (j = 0; j < tags_size; j++) { + for (i = 0; i < array->items; i++) { + struct lsqf_ent *ent = array->ents[i]; + int ret = 1; + + switch (ent->type) { + case LSQF_ENT_TYPE_TAG: + add_tagstr_to_nbuf(nbuf, ent, pkgdata, j); + break; + + case LSQF_ENT_TYPE_STRING: + n_buf_puts_z(nbuf, ent->string); + break; + + case LSQF_ENT_TYPE_ARRAY: + if (check_size(ent->array, pkgdata, &size)) { + logn(LOGERR, _("%s array iterator used with different sized arrays"), invalid_format); + ret = 0; + } else { + ret = ent_array_to_string(ent->array, pkgdata, nbuf, size); + } + + break; + + default: + n_assert(0); + } + + /* break on error */ + if (ret == 0) + return 0; + } + } + + return 1; +} + +char *lsqf_to_string(const struct lsqf_ent_array *array, const struct pkg *pkg) +{ + struct lsqf_pkgdata *pkgdata = NULL; + tn_buf *nbuf = NULL; + char *buf = NULL; + + pkgdata = lsqf_pkgdata_new(pkg); + nbuf = n_buf_new(64); + + /* In the first array there can't be more than one item per tag, + * so force tags_size = 1 */ + if (ent_array_to_string(array, pkgdata, nbuf, 1)) { + buf = n_strdup(n_buf_ptr(nbuf)); + } + + n_buf_free(nbuf); + lsqf_pkgdata_free(pkgdata); + + return buf; +} + +/** + * lsqf_show_querytags: + * + * Print all supported tags. + */ +void lsqf_show_querytags(struct cmdctx *cmdctx) +{ + int i, j; + + for (i = 0; i < LSQF_N_TAGS; i++) { + for (j = 0; lsqf_tags[i].tagname[j]; j++) { + cmdctx_printf(cmdctx, "%s\n", lsqf_tags[i].tagname[j]); + } + } +} diff --git a/cli/ls_queryfmt.h b/cli/ls_queryfmt.h new file mode 100644 index 0000000..43d3858 --- /dev/null +++ b/cli/ls_queryfmt.h @@ -0,0 +1,47 @@ +#ifndef POCLIDEK_LS_QUERYFMT_H +#define POCLIDEK_LS_QUERYFMT_H + +#include "cmd.h" +#include "pkg.h" + +struct lsqf_ent; + +struct lsqf_ent_array { + struct lsqf_ent **ents; + unsigned int items; +}; + +struct lsqf_ent { + enum { + LSQF_ENT_TYPE_TAG = 1, + LSQF_ENT_TYPE_STRING, + LSQF_ENT_TYPE_ARRAY + } type; + + union { + struct { + int id; + + int iterate; + int countArray; + int pad; + int outfmtfnid; + } tag; + + char *string; + + struct lsqf_ent_array *array; + }; +}; + + + +struct lsqf_ent_array *lsqf_parse(char *fmt); +char *lsqf_to_string(const struct lsqf_ent_array *array, const struct pkg *pkg); + +struct lsqf_ent_array *lsqf_ent_array_new(void); +void lsqf_ent_array_free(struct lsqf_ent_array *array); + +void lsqf_show_querytags(struct cmdctx *cmdctx); + +#endif /* POCLIDEK_LS_QUERYFMT_H */ diff --git a/cli/ls.c b/cli/ls.c index 0fe548c..e956e40 100644 --- a/cli/ls.c +++ b/cli/ls.c @@ -24,6 +24,7 @@ #include "pkgu.h" #include "cli.h" #include "log.h" +#include "ls_queryfmt.h" static int ls(struct cmdctx *cmdctx); static @@ -51,6 +52,8 @@ #define OPT_LS_NAMES_ONLY (1 << 11) #define OPT_LS_SOURCERPM (1 << 12) +#define OPT_LS_QUERYFMT (1 << 13) +#define OPT_LS_QUERYTAGS (1 << 14) #define OPT_LS_ERR (1 << 16); @@ -69,7 +72,9 @@ { NULL, 'G', 0, 0, N_("Print package groups"), 1}, { NULL, 'O', 0, 0, N_("Print package summaries"), 1}, { "source-rpm", 's', 0, 0,N_("Print package source rpm"), 1}, -// { NULL, 'i', 0, OPTION_ALIAS, 0, 1 }, + { 0, 0, 0, 0, N_("Query format options:"), 2}, + { "qf", OPT_LS_QUERYFMT, "QUERYFMT", 0, N_("Use the following query format"), 2}, + { "querytags", OPT_LS_QUERYTAGS, 0, 0, N_("Show supported tags"), 2}, { 0, 0, 0, 0, 0, 0 }, }; @@ -152,7 +157,25 @@ case 'n': cmdctx->_flags |= OPT_LS_NAMES_ONLY; break; - + + case OPT_LS_QUERYFMT: + cmdctx->_flags |= OPT_LS_QUERYFMT; + + if (arg) { + struct lsqf_ent_array *array = NULL; + + if ((array = lsqf_parse(arg)) == NULL) + return EINVAL; + + cmdctx->_data = array; + } + + break; + + case OPT_LS_QUERYTAGS: + lsqf_show_querytags(cmdctx); + return EINVAL; + default: return ARGP_ERR_UNKNOWN; } @@ -368,6 +391,10 @@ l_end: + if (cmdctx->_flags & OPT_LS_QUERYFMT) { + lsqf_ent_array_free(cmdctx->_data); + cmdctx->_data = NULL; + } if (ls_ents) n_array_free(ls_ents); @@ -524,6 +524,16 @@ else if (flags & OPT_LS_SOURCERPM) { const char *srcrpm = pkg_srcfilename_s(pkg); cmdctx_printf(cmdctx, fmt_pkg, pkg_name, srcrpm ? srcrpm : "(unset)"); + + } else if (flags & OPT_LS_QUERYFMT) { + char *queryfmt = NULL; + + if ((queryfmt = lsqf_to_string(cmdctx->_data, pkg))) { + cmdctx_printf(cmdctx, "%s", queryfmt); + + n_free(queryfmt); + } + } else if ((flags & OPT_LS_LONG) == 0) { cmdctx_printf(cmdctx, "%s\n", pkg_name);