1 commit 4b027294335c0672311f021f2dc76edaab049b11
2 Author: Marcin Banasiak <marcin.banasiak@gmail.com>
3 Date: Sun Dec 13 20:50:11 2009 +0100
5 Support for query format in ls command.
7 Syntax of the queryfmt is almost the same as in rpm except query
8 expressions which are not (yet) supported.
10 To see supported tags type:
13 diff --git a/cli/Makefile.am b/cli/Makefile.am
14 index 53fcdfd..4a32e01 100644
17 @@ -28,6 +28,7 @@ libpoclidek_la_SOURCES = \
21 + ls_queryfmt.c ls_queryfmt.h \
25 diff --git a/cli/ls_queryfmt.c b/cli/ls_queryfmt.c
27 index 0000000..1a0c02f
29 +++ b/cli/ls_queryfmt.c
32 + Copyright (C) 2009 Marcin Banasiak <megabajt@pld-linux.org>
34 + This program is free software; you can redistribute it and/or modify
35 + it under the terms of the GNU General Public License, version 2 as
36 + published by the Free Software Foundation (see file COPYING for details).
38 + You should have received a copy of the GNU General Public License
39 + along with this program; if not, write to the Free Software
40 + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
47 +/* for asprintf() from stdio.h */
50 +/* FIXME: nbuf.h should include stdint.h */
53 +#include <sys/stat.h>
59 +#include <trurl/trurl.h>
64 +#include "ls_queryfmt.h"
68 +#define n_strcase_eq(s, p) (strcasecmp(s, p) == 0)
70 +static const char *invalid_format = N_("invalid format:");
73 + LSQF_PARSE_NORMAL = 0,
77 +/* Tags have to be in alphabetical order. */
79 + LSQF_TAG_UNKNOWN = -1,
85 + LSQF_TAG_CONFLICTFLAGS,
87 + LSQF_TAG_CONFLICTVERSION,
88 + LSQF_TAG_DESCRIPTION,
91 + LSQF_TAG_FILELINKTOS,
99 + LSQF_TAG_OBSOLETEFLAGS,
100 + LSQF_TAG_OBSOLETES,
101 + LSQF_TAG_OBSOLETEVERSION,
102 + LSQF_TAG_PACKAGECOLOR,
103 + LSQF_TAG_PROVIDEFLAGS,
105 + LSQF_TAG_PROVIDEVERSION,
107 + LSQF_TAG_REQUIREFLAGS,
109 + LSQF_TAG_REQUIREVERSION,
111 + LSQF_TAG_SOURCERPM,
112 + LSQF_TAG_SUGGESTSFLAGS,
114 + LSQF_TAG_SUGGESTSVERSION,
124 + LSQF_TAG_OUTFMTFN_NONE = 0,
126 + LSQF_TAG_OUTFMTFN_DATE,
127 + LSQF_TAG_OUTFMTFN_DAY,
128 + LSQF_TAG_OUTFMTFN_DEPFLAGS
135 + int need_flist : 1;
136 + const char *tagname[4];
140 + * LSQF_TAG_* have to be added in the same sequence as in enumeration
141 + * (LSQF_TAG_ value is an index in lsqf_tags[] array).
143 +static const struct lsqf_tags lsqf_tags[] = {
144 + { LSQF_TAG_ARCH, 0, 0, 0, { "ARCH", NULL } },
145 + { LSQF_TAG_BASENAMES, 1, 0, 1, { "BASENAMES", NULL } },
146 + { LSQF_TAG_BUILDHOST, 0, 1, 0, { "BUILDHOST", NULL } },
147 + { LSQF_TAG_BUILDTIME, 0, 0, 0, { "BUILDTIME", NULL } },
148 + { LSQF_TAG_CONFLICTFLAGS, 1, 0, 0, { "CONFLICTFLAGS", NULL } },
149 + { LSQF_TAG_CONFLICTS, 1, 0, 0, { "C", "CONFLICTNAME", "CONFLICTS", NULL } },
150 + { LSQF_TAG_CONFLICTVERSION, 1, 0, 0, { "CONFLICTVERSION", NULL } },
151 + { LSQF_TAG_DESCRIPTION, 0, 1, 0, { "DESCRIPTION", NULL } },
152 + { LSQF_TAG_DIRNAMES, 1, 0, 1, { "DIRNAMES", NULL } },
153 + { LSQF_TAG_EPOCH, 0, 0, 0, { "E", "EPOCH", NULL } },
154 + { LSQF_TAG_FILELINKTOS, 1, 0, 1, { "FILELINKTOS", NULL } },
155 + { LSQF_TAG_FILEMODES, 1, 0, 1, { "FILEMODES", NULL } },
156 + { LSQF_TAG_FILENAMES, 1, 0, 1, { "FILENAMES", NULL } },
157 + { LSQF_TAG_FILESIZES, 1, 0, 1, { "FILESIZES", NULL } },
158 + { LSQF_TAG_GROUP, 0, 0, 0, { "GROUP", NULL } },
159 + { LSQF_TAG_LICENSE, 0, 1, 0, { "LICENSE", NULL } },
160 + { LSQF_TAG_NAME, 0, 0, 0, { "N", "NAME", NULL } },
161 + { LSQF_TAG_NVRA, 0, 0, 0, { "NVRA", NULL } },
162 + { LSQF_TAG_OBSOLETEFLAGS, 1, 0, 0, { "OBSOLETEFLAGS", NULL } },
163 + { LSQF_TAG_OBSOLETES, 1, 0, 0, { "O", "OBSOLETENAME", "OBSOLETES", NULL } },
164 + { LSQF_TAG_OBSOLETEVERSION, 1, 0, 0, { "OBSOLETEVERSION", NULL } },
165 + { LSQF_TAG_PACKAGECOLOR, 0, 0, 0, { "PACKAGECOLOR", NULL } },
166 + { LSQF_TAG_PROVIDEFLAGS, 1, 0, 0, { "PROVIDEFLAGS", NULL } },
167 + { LSQF_TAG_PROVIDES, 1, 0, 0, { "P", "PROVIDENAME", "PROVIDES", NULL } },
168 + { LSQF_TAG_PROVIDEVERSION, 1, 0, 0, { "PROVIDEVERSION", NULL } },
169 + { LSQF_TAG_RELEASE, 0, 0, 0, { "R", "RELEASE", NULL } },
170 + { LSQF_TAG_REQUIREFLAGS, 1, 0, 0, { "REQUIREFLAGS", NULL } },
171 + { LSQF_TAG_REQUIRES, 1, 0, 0, { "REQUIRENAME", "REQUIRES", NULL } },
172 + { LSQF_TAG_REQUIREVERSION, 1, 0, 0, { "REQUIREVERSION", NULL } },
173 + { LSQF_TAG_SIZE, 0, 0, 0, { "SIZE", NULL } },
174 + { LSQF_TAG_SOURCERPM, 0, 0, 0, { "SOURCERPM", NULL } },
175 + { LSQF_TAG_SUGGESTSFLAGS, 1, 0, 0, { "SUGGESTSFLAGS", NULL } },
176 + { LSQF_TAG_SUGGESTS, 1, 0, 0, { "SUGGESTS", "SUGGESTSNAME", NULL } },
177 + { LSQF_TAG_SUGGESTSVERSION, 1, 0, 0, { "SUGGESTSVERSION", NULL } },
178 + { LSQF_TAG_SUMMARY, 0, 1, 0, { "SUMMARY", NULL } },
179 + { LSQF_TAG_URL, 0, 1, 0, { "URL", NULL } },
180 + { LSQF_TAG_VENDOR, 0, 1, 0, { "VENDOR", NULL } },
181 + { LSQF_TAG_VERSION, 0, 0, 0, { "V", "VERSION", NULL } },
182 + { LSQF_N_TAGS, 0, 0, 0, { NULL } }
185 +struct lsqf_pkgdata {
186 + const struct pkg *pkg;
187 + struct pkgflist *flist;
188 + struct pkguinf *uinf;
191 +static struct lsqf_pkgdata *lsqf_pkgdata_new(const struct pkg *pkg)
193 + struct lsqf_pkgdata *pkgdata = NULL;
195 + pkgdata = n_malloc(sizeof(struct lsqf_pkgdata));
198 + pkgdata->pkg = pkg;
199 + pkgdata->flist = NULL;
200 + pkgdata->uinf = NULL;
206 +static struct pkgflist *lsqf_pkgdata_flist(struct lsqf_pkgdata *pkgdata)
208 + if (pkgdata->flist == NULL)
209 + pkgdata->flist = pkg_get_flist(pkgdata->pkg);
211 + return pkgdata->flist;
214 +static struct pkguinf *lsqf_pkgdata_uinf(struct lsqf_pkgdata *pkgdata)
216 + if (pkgdata->uinf == NULL)
217 + pkgdata->uinf = pkg_uinf(pkgdata->pkg);
219 + return pkgdata->uinf;
222 +static void lsqf_pkgdata_free(struct lsqf_pkgdata *pkgdata)
225 + if (pkgdata->flist)
226 + pkgflist_free(pkgdata->flist);
229 + pkguinf_free(pkgdata->uinf);
235 +static int get_tagid_by_name(char *tag)
240 + for (i = 0; i < LSQF_N_TAGS; i++) {
241 + for (j = 0; lsqf_tags[i].tagname[j]; j++) {
242 + if (n_strcase_eq(tag, lsqf_tags[i].tagname[j])) {
243 + return lsqf_tags[i].tagid;
249 + return LSQF_TAG_UNKNOWN;
252 +static int get_outfmtfnid_by_name(char *outfmtfn)
254 + if (outfmtfn && *outfmtfn) {
255 + if (n_str_eq(outfmtfn, "date"))
256 + return LSQF_TAG_OUTFMTFN_DATE;
257 + if (n_str_eq(outfmtfn, "day"))
258 + return LSQF_TAG_OUTFMTFN_DAY;
259 + if (n_str_eq(outfmtfn, "depflags"))
260 + return LSQF_TAG_OUTFMTFN_DEPFLAGS;
263 + return LSQF_TAG_OUTFMTFN_NONE;
266 +/* TODO: move to capreq.c */
267 +static int capreq_snprintf_evr(char *str, size_t size, const struct capreq *cr)
271 + n_assert(size > 0);
273 + if (capreq_has_epoch(cr))
274 + n += n_snprintf(&str[n], size - n, "%d:", capreq_epoch(cr));
276 + if (capreq_has_ver(cr))
277 + n += n_snprintf(&str[n], size - n, "%s", capreq_ver(cr));
279 + if (capreq_has_rel(cr)) {
280 + n_assert(capreq_has_ver(cr));
282 + n += n_snprintf(&str[n], size - n, "-%s", capreq_rel(cr));
288 +static char *format_date(int outfmtfnid, uint32_t time)
290 + char *buf = NULL, datestr[32];;
292 + if (outfmtfnid == LSQF_TAG_OUTFMTFN_DATE) {
293 + strftime(datestr, sizeof(datestr), "%c", gmtime((time_t *)&time));
294 + asprintf(&buf, "%s", datestr);
295 + } else if (outfmtfnid == LSQF_TAG_OUTFMTFN_DAY) {
296 + strftime(datestr, sizeof(datestr), "%a %b %d %Y", gmtime((time_t *)&time));
297 + asprintf(&buf, "%s", datestr);
299 + asprintf(&buf, "%u", time);
305 +static char *format_flags(int outfmtfnid, struct capreq *cr)
309 + if (outfmtfnid == LSQF_TAG_OUTFMTFN_DEPFLAGS) {
310 + char relstr[3], *p;
315 + if (cr->cr_relflags & REL_LT)
317 + else if (cr->cr_relflags & REL_GT)
320 + if (cr->cr_relflags & REL_EQ)
325 + asprintf(&buf, " %s ", relstr);
327 + asprintf(&buf, "%u", cr->cr_relflags);
333 +static char *get_str_by_tagid(const struct lsqf_ent *ent, struct lsqf_pkgdata *pkgdata, unsigned int num)
335 + const struct pkg *pkg = pkgdata->pkg;
336 + struct capreq *c = NULL;
337 + char *buf = NULL, evr[32];
340 + if (lsqf_tags[ent->tag.id].need_uinf) {
341 + struct pkguinf *pkgu = lsqf_pkgdata_uinf(pkgdata);
342 + const char *str = NULL;
344 + switch (ent->tag.id) {
345 + case LSQF_TAG_BUILDHOST:
346 + str = pkguinf_get(pkgu, PKGUINF_BUILDHOST);
349 + case LSQF_TAG_DESCRIPTION:
350 + str = pkguinf_get(pkgu, PKGUINF_DESCRIPTION);
353 + case LSQF_TAG_LICENSE:
354 + str = pkguinf_get(pkgu, PKGUINF_LICENSE);
357 + case LSQF_TAG_SUMMARY:
358 + str = pkguinf_get(pkgu, PKGUINF_SUMMARY);
362 + str = pkguinf_get(pkgu, PKGUINF_URL);
365 + case LSQF_TAG_VENDOR:
366 + str = pkguinf_get(pkgu, PKGUINF_VENDOR);
374 + buf = n_strdup(str);
376 + buf = n_strdup("(none)");
378 + } else if (lsqf_tags[ent->tag.id].need_flist) {
379 + struct pkgflist *flist = lsqf_pkgdata_flist(pkgdata);
380 + struct pkgfl_ent *flent;
383 + switch (ent->tag.id) {
384 + case LSQF_TAG_BASENAMES:
385 + case LSQF_TAG_FILELINKTOS:
386 + case LSQF_TAG_FILEMODES:
387 + case LSQF_TAG_FILENAMES:
388 + case LSQF_TAG_FILESIZES:
389 + for (i = 0; i < n_tuple_size(flist->fl); i++) {
390 + flent = n_tuple_nth(flist->fl, i);
392 + if (flent->items <= num)
393 + num -= flent->items;
398 + if (ent->tag.id == LSQF_TAG_BASENAMES)
399 + buf = n_strdup(flent->files[num]->basename);
400 + else if (ent->tag.id == LSQF_TAG_FILEMODES)
401 + asprintf(&buf, "%u", flent->files[num]->mode);
402 + else if (ent->tag.id == LSQF_TAG_FILENAMES) {
403 + if (*flent->dirname == '/')
404 + asprintf(&buf, "%s%s", flent->dirname, flent->files[num]->basename);
406 + asprintf(&buf, "/%s%s%s", flent->dirname,
407 + *flent->files[num]->basename ? "/" : "",
408 + flent->files[num]->basename);
409 + } else if (ent->tag.id == LSQF_TAG_FILESIZES)
410 + asprintf(&buf, "%u", flent->files[num]->size);
412 + if (S_ISLNK(flent->files[num]->mode))
413 + buf = n_strdup(flent->files[num]->basename + strlen(flent->files[num]->basename) + 1);
417 + case LSQF_TAG_DIRNAMES:
418 + flent = n_tuple_nth(flist->fl, num);
420 + asprintf(&buf, "%s%s", *flent->dirname == '/' ? "" : "/",
429 + switch (ent->tag.id) {
430 + case LSQF_TAG_ARCH:
431 + buf = n_strdup(pkg_arch(pkg));
434 + case LSQF_TAG_BUILDTIME:
435 + buf = format_date(ent->tag.outfmtfnid, pkg->btime);
438 + case LSQF_TAG_CONFLICTFLAGS:
439 + case LSQF_TAG_CONFLICTS:
440 + case LSQF_TAG_CONFLICTVERSION:
441 + case LSQF_TAG_OBSOLETEFLAGS:
442 + case LSQF_TAG_OBSOLETES:
443 + case LSQF_TAG_OBSOLETEVERSION:
445 + unsigned int n = 0;
447 + for (i = 0; i < n_array_size(pkg->cnfls); i++) {
448 + struct capreq *cr = n_array_nth(pkg->cnfls, i);
450 + if (ent->tag.id == LSQF_TAG_CONFLICTS || ent->tag.id == LSQF_TAG_CONFLICTFLAGS) {
451 + if (!capreq_is_obsl(cr)) {
460 + if (capreq_is_obsl(cr)) {
472 + if (ent->tag.id == LSQF_TAG_CONFLICTS || ent->tag.id == LSQF_TAG_OBSOLETES)
473 + buf = n_strdup(capreq_name(c));
474 + else if (ent->tag.id == LSQF_TAG_CONFLICTFLAGS || ent->tag.id == LSQF_TAG_OBSOLETEFLAGS)
475 + buf = format_flags(ent->tag.outfmtfnid, c);
476 + else if (ent->tag.id == LSQF_TAG_CONFLICTVERSION || ent->tag.id == LSQF_TAG_OBSOLETEVERSION)
477 + if (capreq_snprintf_evr(evr, sizeof(evr), c) > 0)
478 + buf = n_strdup(evr);
484 + case LSQF_TAG_EPOCH:
485 + asprintf(&buf, "%d", pkg->epoch);
488 + case LSQF_TAG_GROUP:
489 + buf = n_strdup(pkg_group(pkg));
492 + case LSQF_TAG_NAME:
493 + buf = n_strdup(pkg->name);
496 + case LSQF_TAG_NVRA:
497 + buf = n_strdup(pkg_id(pkg));
500 + case LSQF_TAG_PACKAGECOLOR:
501 + asprintf(&buf, "%d", pkg->color);
504 + case LSQF_TAG_PROVIDEFLAGS:
505 + buf = format_flags(ent->tag.outfmtfnid, n_array_nth(pkg->caps, num));
508 + case LSQF_TAG_PROVIDES:
509 + c = n_array_nth(pkg->caps, num);
510 + buf = n_strdup(capreq_name(c));
513 + case LSQF_TAG_PROVIDEVERSION:
514 + c = n_array_nth(pkg->caps, num);
516 + if (capreq_snprintf_evr(evr, sizeof(evr), c) > 0)
517 + buf = n_strdup(evr);
521 + case LSQF_TAG_RELEASE:
522 + buf = n_strdup(pkg->rel);
525 + case LSQF_TAG_REQUIREFLAGS:
526 + buf = format_flags(ent->tag.outfmtfnid, n_array_nth(pkg->reqs, num));
529 + case LSQF_TAG_REQUIRES:
530 + c = n_array_nth(pkg->reqs, num);
532 + if (capreq_is_rpmlib(c))
533 + asprintf(&buf, "rpmlib(%s)", capreq_name(c));
535 + buf = n_strdup(capreq_name(c));
539 + case LSQF_TAG_REQUIREVERSION:
540 + c = n_array_nth(pkg->reqs, num);
542 + if (capreq_snprintf_evr(evr, sizeof(evr), c) > 0)
543 + buf = n_strdup(evr);
547 + case LSQF_TAG_VERSION:
548 + buf = n_strdup(pkg->ver);
551 + case LSQF_TAG_SIZE:
552 + asprintf(&buf, "%u", pkg->size);
555 + case LSQF_TAG_SOURCERPM:
556 + buf = n_strdup(pkg_srcfilename_s(pkg));
559 + case LSQF_TAG_SUGGESTSFLAGS:
560 + buf = format_flags(ent->tag.outfmtfnid, n_array_nth(pkg->sugs, num));
563 + case LSQF_TAG_SUGGESTS:
564 + c = n_array_nth(pkg->sugs, num);
565 + buf = n_strdup(capreq_name(c));
568 + case LSQF_TAG_SUGGESTSVERSION:
569 + c = n_array_nth(pkg->sugs, num);
571 + if (capreq_snprintf_evr(evr, sizeof(evr), c) > 0)
572 + buf = n_strdup(evr);
584 +static char get_escaped_char(char zn)
587 + case 'a': return '\a';
588 + case 'b': return '\b';
589 + case 'f': return '\f';
590 + case 'n': return '\n';
591 + case 'r': return '\r';
592 + case 't': return '\t';
593 + case 'v': return '\v';
594 + default: return zn;
598 +static struct lsqf_ent *lsqf_ent_new(int type)
600 + struct lsqf_ent *ent = NULL;
602 + ent = n_malloc(sizeof(struct lsqf_ent));
608 + case LSQF_ENT_TYPE_TAG:
610 + ent->tag.iterate = 0;
611 + ent->tag.countArray = 0;
615 + case LSQF_ENT_TYPE_STRING:
616 + ent->string = NULL;
619 + case LSQF_ENT_TYPE_ARRAY:
620 + ent->array = lsqf_ent_array_new();
631 +static void lsqf_ent_free(struct lsqf_ent *ent)
634 + switch (ent->type) {
635 + case LSQF_ENT_TYPE_TAG:
638 + case LSQF_ENT_TYPE_STRING:
640 + n_free(ent->string);
644 + case LSQF_ENT_TYPE_ARRAY:
645 + lsqf_ent_array_free(ent->array);
653 +struct lsqf_ent_array *lsqf_ent_array_new(void)
655 + struct lsqf_ent_array *array = NULL;
657 + array = n_malloc(sizeof(struct lsqf_ent_array));
660 + array->ents = NULL;
667 +void lsqf_ent_array_free(struct lsqf_ent_array *array)
672 + for (i = 0; i < array->items; i++) {
673 + struct lsqf_ent *ent = array->ents[i];
675 + lsqf_ent_free(ent);
679 + n_free(array->ents);
685 +static void lsqf_ent_array_add_ent(struct lsqf_ent_array *array, struct lsqf_ent *ent)
687 + array->ents = n_realloc(array->ents, (array->items + 1) * sizeof(struct lsqf_ent *));
690 + array->ents[array->items] = ent;
696 +static void lsqf_ent_array_add_ent_string(struct lsqf_ent_array *array, tn_buf *nbuf)
698 + struct lsqf_ent *ent = NULL;
700 + if (n_buf_size(nbuf) > 0) {
701 + n_buf_putc(nbuf, '\0');
703 + if ((ent = lsqf_ent_new(LSQF_ENT_TYPE_STRING))) {
704 + ent->string = n_strdup(n_buf_ptr(nbuf));
706 + lsqf_ent_array_add_ent(array, ent);
716 + * Returns: 1 on error.
718 +static int do_parse(struct lsqf_ent_array *array, char *fmt, char **endfmt, enum LsqfParseMode mode)
720 + struct lsqf_ent *ent;
721 + tn_buf *nbuf = NULL;
722 + int done = 0, error = 0;
728 + nbuf = n_buf_new(8);
730 + while (*fmt && !done && !error) {
734 + char *p, *outfmtfn;
735 + int tagid, pad, countArray = 0, iterate = 0;
741 + n_buf_putc(nbuf, '%');
745 + pad = strtoul(fmt, &p, 10);
750 + logn(LOGERR, _("%s missing { after %%"), invalid_format);
760 + } else if (*fmt == '=') {
765 + if ((p = strchr(fmt, '}')) == NULL) {
766 + logn(LOGERR, _("%s missing } after %%{"), invalid_format);
773 + if (*fmt == '\0') {
774 + logn(LOGERR, _("%s empty tag name"), invalid_format);
779 + /* check if another output format is requested */
780 + if ((outfmtfn = strchr(fmt, ':')) != NULL) {
785 + if ((tagid = get_tagid_by_name(fmt)) == LSQF_TAG_UNKNOWN) {
786 + logn(LOGERR, _("%s unknown tag: \'%s\'"), invalid_format, fmt);
791 + /* create new ent with a string that is currently stored in nbuf */
792 + lsqf_ent_array_add_ent_string(array, nbuf);
794 + ent = lsqf_ent_new(LSQF_ENT_TYPE_TAG);
795 + ent->tag.id = tagid;
796 + ent->tag.pad = pad;
797 + ent->tag.countArray = countArray;
798 + ent->tag.iterate = iterate;
799 + ent->tag.outfmtfnid = get_outfmtfnid_by_name(outfmtfn);
801 + lsqf_ent_array_add_ent(array, ent);
810 + lsqf_ent_array_add_ent_string(array, nbuf);
812 + ent = lsqf_ent_new(LSQF_ENT_TYPE_ARRAY);
813 + lsqf_ent_array_add_ent(array, ent);
815 + if (do_parse(ent->array, fmt, &end, LSQF_PARSE_ARRAY)) {
820 + if (*end == '\0') {
821 + logn(LOGERR, _("%s missing ] at end of array"), invalid_format);
831 + if (mode != LSQF_PARSE_ARRAY) {
832 + logn(LOGERR, _("%s unexpected ]"), invalid_format);
837 + /* found end of array -> stop parsing */
840 + /* save address of the last character we parsed */
846 + logn(LOGERR, _("%s unexpected }"), invalid_format);
851 + if (fmt[0] == '\\' && fmt[1] != '\0') {
854 + n_buf_putc(nbuf, get_escaped_char(*fmt));
857 + n_buf_putc(nbuf, *fmt);
869 + lsqf_ent_array_add_ent_string(array, nbuf);
871 + if (!done && endfmt)
882 + * Returns: On success, pointer to structure which is a base to display requested information or NULL when parsing failed.
884 +struct lsqf_ent_array *lsqf_parse(char *fmt)
886 + struct lsqf_ent_array *array = NULL;
888 + if ((array = lsqf_ent_array_new())) {
889 + if (do_parse(array, fmt, NULL, LSQF_PARSE_NORMAL)) {
890 + lsqf_ent_array_free(array);
898 +static int get_tag_array_size(const struct lsqf_ent *ent, struct lsqf_pkgdata *pkgdata)
900 + const struct pkg *pkg = pkgdata->pkg;
904 + n_assert(ent->type == LSQF_ENT_TYPE_TAG);
906 + if (lsqf_tags[ent->tag.id].is_array) {
909 + if (lsqf_tags[ent->tag.id].need_flist) {
910 + struct pkgflist *flist = lsqf_pkgdata_flist(pkgdata);
913 + switch (ent->tag.id) {
914 + case LSQF_TAG_BASENAMES:
915 + case LSQF_TAG_FILELINKTOS:
916 + case LSQF_TAG_FILEMODES:
917 + case LSQF_TAG_FILENAMES:
918 + case LSQF_TAG_FILESIZES:
919 + for (i = 0; i < n_tuple_size(flist->fl); i++) {
920 + struct pkgfl_ent *flent = n_tuple_nth(flist->fl, i);
922 + size += flent->items;
927 + case LSQF_TAG_DIRNAMES:
928 + size = n_tuple_size(flist->fl);
936 + switch (ent->tag.id) {
937 + case LSQF_TAG_CONFLICTFLAGS:
938 + case LSQF_TAG_CONFLICTS:
939 + case LSQF_TAG_CONFLICTVERSION:
940 + case LSQF_TAG_OBSOLETEFLAGS:
941 + case LSQF_TAG_OBSOLETES:
942 + case LSQF_TAG_OBSOLETEVERSION:
944 + int nobsl = 0, ncnfls = 0;
946 + for (i = 0; i < n_array_size(pkg->cnfls); i++) {
947 + struct capreq *cr = n_array_nth(pkg->cnfls, i);
949 + if (capreq_is_obsl(cr))
955 + if (ent->tag.id == LSQF_TAG_CONFLICTFLAGS || ent->tag.id == LSQF_TAG_CONFLICTS
956 + || ent->tag.id == LSQF_TAG_CONFLICTVERSION)
964 + case LSQF_TAG_PROVIDEFLAGS:
965 + case LSQF_TAG_PROVIDES:
966 + case LSQF_TAG_PROVIDEVERSION:
968 + size = n_array_size(pkg->caps);
971 + case LSQF_TAG_REQUIREFLAGS:
972 + case LSQF_TAG_REQUIRES:
973 + case LSQF_TAG_REQUIREVERSION:
975 + size = n_array_size(pkg->reqs);
978 + case LSQF_TAG_SUGGESTSFLAGS:
979 + case LSQF_TAG_SUGGESTS:
980 + case LSQF_TAG_SUGGESTSVERSION:
982 + size = n_array_size(pkg->sugs);
995 + * Returns 1 when arrays size differ.
997 +static int check_size(const struct lsqf_ent_array *array, struct lsqf_pkgdata *pkgdata, unsigned int *s)
1000 + int size = 1, prev_size = -1;
1002 + for (i = 0; i < array->items; i++) {
1003 + struct lsqf_ent *ent = array->ents[i];
1005 + if (ent->type == LSQF_ENT_TYPE_TAG) {
1006 + if (lsqf_tags[ent->tag.id].is_array) {
1007 + size = get_tag_array_size(ent, pkgdata);
1009 + /* check whether we want to print this tag with every iteration */
1010 + if (ent->tag.iterate)
1016 + if (prev_size < 0)
1019 + if (prev_size != size)
1029 +static void add_tagstr_to_nbuf(tn_buf *nbuf, const struct lsqf_ent *ent, struct lsqf_pkgdata *pkgdata, unsigned int num)
1031 + char *str = NULL, fmt[16];
1033 + if (ent->tag.countArray) {
1034 + n_snprintf(fmt, sizeof(fmt), "%%%dd", ent->tag.pad);
1035 + n_buf_printf(nbuf, fmt, get_tag_array_size(ent, pkgdata));
1037 + } else if ((str = get_str_by_tagid(ent, pkgdata, num))) {
1038 + n_snprintf(fmt, sizeof(fmt), "%%%ds", ent->tag.pad);
1039 + n_buf_printf(nbuf, fmt, str);
1046 + * tags_size - number of items in tags. It's mostly used by tag-arrays (for example REQUIRES)
1048 +static int ent_array_to_string(const struct lsqf_ent_array *array,
1049 + struct lsqf_pkgdata *pkgdata,
1050 + tn_buf *nbuf, int tags_size)
1052 + unsigned int i, j, size = 0;
1054 + for (j = 0; j < tags_size; j++) {
1055 + for (i = 0; i < array->items; i++) {
1056 + struct lsqf_ent *ent = array->ents[i];
1059 + switch (ent->type) {
1060 + case LSQF_ENT_TYPE_TAG:
1061 + add_tagstr_to_nbuf(nbuf, ent, pkgdata, j);
1064 + case LSQF_ENT_TYPE_STRING:
1065 + n_buf_puts_z(nbuf, ent->string);
1068 + case LSQF_ENT_TYPE_ARRAY:
1069 + if (check_size(ent->array, pkgdata, &size)) {
1070 + logn(LOGERR, _("%s array iterator used with different sized arrays"), invalid_format);
1073 + ret = ent_array_to_string(ent->array, pkgdata, nbuf, size);
1082 + /* break on error */
1091 +char *lsqf_to_string(const struct lsqf_ent_array *array, const struct pkg *pkg)
1093 + struct lsqf_pkgdata *pkgdata = NULL;
1094 + tn_buf *nbuf = NULL;
1097 + pkgdata = lsqf_pkgdata_new(pkg);
1098 + nbuf = n_buf_new(64);
1100 + /* In the first array there can't be more than one item per tag,
1101 + * so force tags_size = 1 */
1102 + if (ent_array_to_string(array, pkgdata, nbuf, 1)) {
1103 + buf = n_strdup(n_buf_ptr(nbuf));
1107 + lsqf_pkgdata_free(pkgdata);
1113 + * lsqf_show_querytags:
1115 + * Print all supported tags.
1117 +void lsqf_show_querytags(struct cmdctx *cmdctx)
1121 + for (i = 0; i < LSQF_N_TAGS; i++) {
1122 + for (j = 0; lsqf_tags[i].tagname[j]; j++) {
1123 + cmdctx_printf(cmdctx, "%s\n", lsqf_tags[i].tagname[j]);
1127 diff --git a/cli/ls_queryfmt.h b/cli/ls_queryfmt.h
1128 new file mode 100644
1129 index 0000000..43d3858
1131 +++ b/cli/ls_queryfmt.h
1133 +#ifndef POCLIDEK_LS_QUERYFMT_H
1134 +#define POCLIDEK_LS_QUERYFMT_H
1141 +struct lsqf_ent_array {
1142 + struct lsqf_ent **ents;
1143 + unsigned int items;
1148 + LSQF_ENT_TYPE_TAG = 1,
1149 + LSQF_ENT_TYPE_STRING,
1150 + LSQF_ENT_TYPE_ARRAY
1165 + struct lsqf_ent_array *array;
1171 +struct lsqf_ent_array *lsqf_parse(char *fmt);
1172 +char *lsqf_to_string(const struct lsqf_ent_array *array, const struct pkg *pkg);
1174 +struct lsqf_ent_array *lsqf_ent_array_new(void);
1175 +void lsqf_ent_array_free(struct lsqf_ent_array *array);
1177 +void lsqf_show_querytags(struct cmdctx *cmdctx);
1179 +#endif /* POCLIDEK_LS_QUERYFMT_H */
1180 diff --git a/cli/ls.c b/cli/ls.c
1181 index 0fe548c..e956e40 100644
1188 +#include "ls_queryfmt.h"
1190 static int ls(struct cmdctx *cmdctx);
1193 #define OPT_LS_NAMES_ONLY (1 << 11)
1194 #define OPT_LS_SOURCERPM (1 << 12)
1196 +#define OPT_LS_QUERYFMT (1 << 13)
1197 +#define OPT_LS_QUERYTAGS (1 << 14)
1199 #define OPT_LS_ERR (1 << 16);
1202 { NULL, 'G', 0, 0, N_("Print package groups"), 1},
1203 { NULL, 'O', 0, 0, N_("Print package summaries"), 1},
1204 { "source-rpm", 's', 0, 0,N_("Print package source rpm"), 1},
1205 -// { NULL, 'i', 0, OPTION_ALIAS, 0, 1 },
1206 + { 0, 0, 0, 0, N_("Query format options:"), 2},
1207 + { "qf", OPT_LS_QUERYFMT, "QUERYFMT", 0, N_("Use the following query format"), 2},
1208 + { "querytags", OPT_LS_QUERYTAGS, 0, 0, N_("Show supported tags"), 2},
1209 { 0, 0, 0, 0, 0, 0 },
1212 @@ -152,7 +157,25 @@
1214 cmdctx->_flags |= OPT_LS_NAMES_ONLY;
1218 + case OPT_LS_QUERYFMT:
1219 + cmdctx->_flags |= OPT_LS_QUERYFMT;
1222 + struct lsqf_ent_array *array = NULL;
1224 + if ((array = lsqf_parse(arg)) == NULL)
1227 + cmdctx->_data = array;
1232 + case OPT_LS_QUERYTAGS:
1233 + lsqf_show_querytags(cmdctx);
1237 return ARGP_ERR_UNKNOWN;
1239 @@ -368,6 +391,10 @@
1243 + if (cmdctx->_flags & OPT_LS_QUERYFMT) {
1244 + lsqf_ent_array_free(cmdctx->_data);
1245 + cmdctx->_data = NULL;
1249 n_array_free(ls_ents);
1250 @@ -524,6 +524,16 @@
1251 else if (flags & OPT_LS_SOURCERPM) {
1252 const char *srcrpm = pkg_srcfilename_s(pkg);
1253 cmdctx_printf(cmdctx, fmt_pkg, pkg_name, srcrpm ? srcrpm : "(unset)");
1255 + } else if (flags & OPT_LS_QUERYFMT) {
1256 + char *queryfmt = NULL;
1258 + if ((queryfmt = lsqf_to_string(cmdctx->_data, pkg))) {
1259 + cmdctx_printf(cmdctx, "%s", queryfmt);
1264 } else if ((flags & OPT_LS_LONG) == 0) {
1265 cmdctx_printf(cmdctx, "%s\n", pkg_name);