summaryrefslogtreecommitdiffstats
path: root/lib/dpkg/pkg-format.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dpkg/pkg-format.c')
-rw-r--r--lib/dpkg/pkg-format.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/lib/dpkg/pkg-format.c b/lib/dpkg/pkg-format.c
new file mode 100644
index 0000000..b984012
--- /dev/null
+++ b/lib/dpkg/pkg-format.c
@@ -0,0 +1,475 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * pkg-format.c - customizable package formatting
+ *
+ * Copyright © 2001 Wichert Akkerman <wakkerma@debian.org>
+ * Copyright © 2008-2015 Guillem Jover <guillem@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <dpkg/i18n.h>
+#include <dpkg/error.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/db-ctrl.h>
+#include <dpkg/db-fsys.h>
+#include <dpkg/parsedump.h>
+#include <dpkg/pkg-show.h>
+#include <dpkg/pkg-format.h>
+
+enum pkg_format_type {
+ PKG_FORMAT_INVALID,
+ PKG_FORMAT_STRING,
+ PKG_FORMAT_FIELD,
+};
+
+struct pkg_format_node {
+ struct pkg_format_node *next;
+ enum pkg_format_type type;
+ int width;
+ char *data;
+};
+
+
+static struct pkg_format_node *
+pkg_format_node_new(void)
+{
+ struct pkg_format_node *buf;
+
+ buf = m_malloc(sizeof(*buf));
+ buf->type = PKG_FORMAT_INVALID;
+ buf->next = NULL;
+ buf->data = NULL;
+ buf->width = 0;
+
+ return buf;
+}
+
+static bool
+parsefield(struct pkg_format_node *node, const char *fmt, const char *fmtend,
+ struct dpkg_error *err)
+{
+ int len;
+ const char *ws;
+
+ len = fmtend - fmt + 1;
+
+ ws = memchr(fmt, ';', len);
+ if (ws) {
+ char *endptr;
+ long w;
+
+ errno = 0;
+ w = strtol(ws + 1, &endptr, 0);
+ if (endptr[0] != '}') {
+ dpkg_put_error(err,
+ _("invalid character '%c' in field width"),
+ *endptr);
+ return false;
+ }
+ if (w < INT_MIN || w > INT_MAX || errno == ERANGE) {
+ dpkg_put_error(err, _("field width is out of range"));
+ return false;
+ }
+
+ node->width = w;
+
+ len = ws - fmt;
+ }
+
+ node->type = PKG_FORMAT_FIELD;
+ node->data = m_malloc(len + 1);
+ memcpy(node->data, fmt, len);
+ node->data[len] = '\0';
+
+ return true;
+}
+
+static bool
+parsestring(struct pkg_format_node *node, const char *fmt, const char *fmtend,
+ struct dpkg_error *err)
+{
+ int len;
+ char *write;
+
+ len = fmtend - fmt + 1;
+
+ node->type = PKG_FORMAT_STRING;
+ node->data = write = m_malloc(len + 1);
+ while (fmt <= fmtend) {
+ if (*fmt == '\\') {
+ fmt++;
+ switch (*fmt) {
+ case 'n':
+ *write = '\n';
+ break;
+ case 't':
+ *write = '\t';
+ break;
+ case 'r':
+ *write = '\r';
+ break;
+ case '\\':
+ default:
+ *write = *fmt;
+ break;
+ }
+ } else
+ *write = *fmt;
+ write++;
+ fmt++;
+ }
+ *write = '\0';
+
+ return true;
+}
+
+void
+pkg_format_free(struct pkg_format_node *head)
+{
+ struct pkg_format_node *node;
+
+ while (head) {
+ node = head;
+ head = node->next;
+
+ free(node->data);
+ free(node);
+ }
+}
+
+struct pkg_format_node *
+pkg_format_parse(const char *fmt, struct dpkg_error *err)
+{
+ struct pkg_format_node *head, *node;
+ const char *fmtend;
+
+ head = node = NULL;
+
+ while (*fmt) {
+ if (node)
+ node = node->next = pkg_format_node_new();
+ else
+ head = node = pkg_format_node_new();
+
+ if (fmt[0] == '$' && fmt[1] == '{') {
+ fmtend = strchr(fmt, '}');
+ if (!fmtend) {
+ dpkg_put_error(err, _("missing closing brace"));
+ pkg_format_free(head);
+ return NULL;
+ }
+
+ if (!parsefield(node, fmt + 2, fmtend - 1, err)) {
+ pkg_format_free(head);
+ return NULL;
+ }
+ fmt = fmtend + 1;
+ } else {
+ fmtend = fmt;
+ do {
+ fmtend += 1;
+ fmtend = strchrnul(fmtend, '$');
+ } while (fmtend[0] && fmtend[1] != '{');
+
+ if (!parsestring(node, fmt, fmtend - 1, err)) {
+ pkg_format_free(head);
+ return NULL;
+ }
+ fmt = fmtend;
+ }
+ }
+
+ if (!head)
+ dpkg_put_error(err, _("may not be empty string"));
+
+ return head;
+}
+
+static void
+virt_package(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ varbuf_add_pkgbin_name(vb, pkg, pkgbin, pnaw_nonambig);
+}
+
+static void
+virt_status_abbrev(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ if (pkgbin != &pkg->installed)
+ return;
+
+ varbuf_add_char(vb, pkg_abbrev_want(pkg));
+ varbuf_add_char(vb, pkg_abbrev_status(pkg));
+ varbuf_add_char(vb, pkg_abbrev_eflag(pkg));
+}
+
+static void
+virt_status_want(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ if (pkgbin != &pkg->installed)
+ return;
+
+ varbuf_add_str(vb, pkg_want_name(pkg));
+}
+
+static void
+virt_status_status(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ if (pkgbin != &pkg->installed)
+ return;
+
+ varbuf_add_str(vb, pkg_status_name(pkg));
+}
+
+static void
+virt_status_eflag(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ if (pkgbin != &pkg->installed)
+ return;
+
+ varbuf_add_str(vb, pkg_eflag_name(pkg));
+}
+
+static void
+virt_synopsis(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ const char *desc;
+ int len;
+
+ desc = pkgbin_synopsis(pkg, pkgbin, &len);
+
+ varbuf_add_buf(vb, desc, len);
+}
+
+static void
+virt_fsys_last_modified(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ const char *listfile;
+ struct stat st;
+ intmax_t mtime;
+
+ if (pkg->status == PKG_STAT_NOTINSTALLED)
+ return;
+
+ listfile = pkg_infodb_get_file(pkg, pkgbin, LISTFILE);
+
+ if (stat(listfile, &st) < 0) {
+ if (errno == ENOENT)
+ return;
+
+ ohshite(_("cannot get package %s filesystem last modification time"),
+ pkgbin_name_const(pkg, pkgbin, pnaw_nonambig));
+ }
+
+ mtime = st.st_mtime;
+ varbuf_printf(vb, "%jd", mtime);
+}
+
+/*
+ * This function requires the caller to have loaded the package fsys metadata,
+ * otherwise it will do nothing.
+ */
+static void
+virt_fsys_files(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ struct fsys_namenode_list *node;
+
+ if (!pkg->files_list_valid)
+ return;
+
+ for (node = pkg->files; node; node = node->next) {
+ varbuf_add_char(vb, ' ');
+ varbuf_add_str(vb, node->namenode->name);
+ varbuf_add_char(vb, '\n');
+ }
+}
+
+static void
+virt_source_package(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ const char *name;
+ size_t len;
+
+ name = pkgbin->source;
+ if (name == NULL)
+ name = pkg->set->name;
+
+ len = strcspn(name, " ");
+
+ varbuf_add_buf(vb, name, len);
+}
+
+static void
+virt_source_version(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ varbuf_add_source_version(vb, pkg, pkgbin);
+}
+
+static void
+virt_source_upstream_version(struct varbuf *vb,
+ const struct pkginfo *pkg, const struct pkgbin *pkgbin,
+ enum fwriteflags flags, const struct fieldinfo *fip)
+{
+ struct dpkg_version version;
+
+ pkg_source_version(&version, pkg, pkgbin);
+
+ if (version.version)
+ varbuf_add_str(vb, version.version);
+ varbuf_end_str(vb);
+}
+
+static const struct fieldinfo virtinfos[] = {
+ { FIELD("binary:Package"), NULL, virt_package },
+ { FIELD("binary:Synopsis"), NULL, virt_synopsis },
+ { FIELD("binary:Summary"), NULL, virt_synopsis },
+ { FIELD("db:Status-Abbrev"), NULL, virt_status_abbrev },
+ { FIELD("db:Status-Want"), NULL, virt_status_want },
+ { FIELD("db:Status-Status"), NULL, virt_status_status },
+ { FIELD("db:Status-Eflag"), NULL, virt_status_eflag },
+ { FIELD("db-fsys:Files"), NULL, virt_fsys_files },
+ { FIELD("db-fsys:Last-Modified"), NULL, virt_fsys_last_modified },
+ { FIELD("source:Package"), NULL, virt_source_package },
+ { FIELD("source:Version"), NULL, virt_source_version },
+ { FIELD("source:Upstream-Version"), NULL, virt_source_upstream_version },
+ { NULL },
+};
+
+bool
+pkg_format_needs_db_fsys(const struct pkg_format_node *head)
+{
+ const struct pkg_format_node *node;
+
+ for (node = head; node; node = node->next) {
+ if (node->type != PKG_FORMAT_FIELD)
+ continue;
+ if (strcmp(node->data, "db-fsys:Files") == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static void
+pkg_format_item(struct varbuf *vb,
+ const struct pkg_format_node *node, const char *str)
+{
+ if (node->width == 0)
+ varbuf_add_str(vb, str);
+ else
+ varbuf_printf(vb, "%*s", node->width, str);
+}
+
+void
+pkg_format_print(struct varbuf *vb, const struct pkg_format_node *head,
+ struct pkginfo *pkg, struct pkgbin *pkgbin)
+{
+ const struct pkg_format_node *node;
+ struct varbuf fb = VARBUF_INIT, wb = VARBUF_INIT;
+
+ for (node = head; node; node = node->next) {
+ bool ok = false;
+
+ if (node->type == PKG_FORMAT_STRING) {
+ pkg_format_item(&fb, node, node->data);
+ ok = true;
+ } else if (node->type == PKG_FORMAT_FIELD) {
+ const struct fieldinfo *fip;
+
+ fip = find_field_info(fieldinfos, node->data);
+ if (fip == NULL)
+ fip = find_field_info(virtinfos, node->data);
+
+ if (fip) {
+ fip->wcall(&wb, pkg, pkgbin, 0, fip);
+
+ varbuf_end_str(&wb);
+ pkg_format_item(&fb, node, wb.buf);
+ varbuf_reset(&wb);
+ ok = true;
+ } else {
+ const struct arbitraryfield *afp;
+
+ afp = find_arbfield_info(pkgbin->arbs, node->data);
+ if (afp) {
+ pkg_format_item(&fb, node, afp->value);
+ ok = true;
+ }
+ }
+ }
+
+ if (ok) {
+ size_t len = fb.used;
+ size_t width = abs(node->width);
+
+ if ((width != 0) && (len > width))
+ len = width;
+ varbuf_add_buf(vb, fb.buf, len);
+ varbuf_end_str(vb);
+ }
+
+ varbuf_reset(&fb);
+ }
+
+ varbuf_destroy(&wb);
+ varbuf_destroy(&fb);
+}
+
+void
+pkg_format_show(const struct pkg_format_node *head,
+ struct pkginfo *pkg, struct pkgbin *pkgbin)
+{
+ struct varbuf vb = VARBUF_INIT;
+
+ pkg_format_print(&vb, head, pkg, pkgbin);
+
+ if (vb.buf)
+ fputs(vb.buf, stdout);
+
+ varbuf_destroy(&vb);
+}