diff options
Diffstat (limited to 'lib/dpkg/pkg-format.c')
-rw-r--r-- | lib/dpkg/pkg-format.c | 475 |
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); +} |