| /* |
| * libdpkg - Debian packaging suite library routines |
| * pkg-format.c - customizable package formatting |
| * |
| * Copyright © 2001 Wichert Akkerman <wakkerma@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 <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <config.h> |
| #include <compat.h> |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #include <dpkg/i18n.h> |
| #include <dpkg/dpkg.h> |
| #include <dpkg/dpkg-db.h> |
| #include <dpkg/parsedump.h> |
| #include <dpkg/pkg-format.h> |
| |
| enum pkg_format_type { |
| invalid, |
| string, |
| field, |
| }; |
| |
| struct pkg_format_node { |
| struct pkg_format_node *next; |
| enum pkg_format_type type; |
| size_t width; |
| int pad; |
| char *data; |
| }; |
| |
| |
| static struct pkg_format_node * |
| pkg_format_node_new(void) |
| { |
| struct pkg_format_node *buf; |
| |
| buf = m_malloc(sizeof(*buf)); |
| buf->type = invalid; |
| buf->next = NULL; |
| buf->data = NULL; |
| buf->width = 0; |
| buf->pad = 0; |
| |
| return buf; |
| } |
| |
| static bool |
| parsefield(struct pkg_format_node *cur, const char *fmt, const char *fmtend) |
| { |
| int len; |
| const char *ws; |
| |
| len = fmtend - fmt + 1; |
| |
| ws = memchr(fmt, ';', len); |
| if (ws) { |
| char *endptr; |
| long w; |
| |
| w = strtol(ws + 1, &endptr, 0); |
| if (endptr[0] != '}') { |
| fprintf(stderr, |
| _("invalid character `%c' in field width\n"), |
| *endptr); |
| return false; |
| } |
| |
| if (w < 0) { |
| cur->pad = 1; |
| cur->width = (size_t)-w; |
| } else |
| cur->width = (size_t)w; |
| |
| len = ws - fmt; |
| } |
| |
| cur->type = field; |
| cur->data = m_malloc(len + 1); |
| memcpy(cur->data, fmt, len); |
| cur->data[len] = '\0'; |
| |
| return true; |
| } |
| |
| static bool |
| parsestring(struct pkg_format_node *cur, const char *fmt, const char *fmtend) |
| { |
| int len; |
| char *write; |
| |
| len = fmtend - fmt + 1; |
| |
| cur->type = string; |
| write = cur->data = 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 *next; |
| |
| while (head) { |
| next = head->next; |
| free(head->data); |
| free(head); |
| head = next; |
| } |
| } |
| |
| struct pkg_format_node * |
| pkg_format_parse(const char *fmt) |
| { |
| struct pkg_format_node *head; |
| struct pkg_format_node *cur; |
| const char *fmtend; |
| |
| head = cur = NULL; |
| |
| while (*fmt) { |
| if (cur) |
| cur = cur->next = pkg_format_node_new(); |
| else |
| head = cur = pkg_format_node_new(); |
| |
| if (fmt[0] == '$' && fmt[1] == '{') { |
| fmtend = strchr(fmt, '}'); |
| if (!fmtend) { |
| fprintf(stderr, |
| _("Closing brace missing in format\n")); |
| pkg_format_free(head); |
| return NULL; |
| } |
| |
| if (!parsefield(cur, fmt + 2, fmtend - 1)) { |
| pkg_format_free(head); |
| return NULL; |
| } |
| fmt = fmtend + 1; |
| } else { |
| fmtend = fmt; |
| do { |
| fmtend += 1; |
| fmtend = strchr(fmtend, '$'); |
| } while (fmtend && fmtend[1] != '{'); |
| |
| if (!fmtend) |
| fmtend = fmt + strlen(fmt); |
| |
| if (!parsestring(cur, fmt, fmtend - 1)) { |
| pkg_format_free(head); |
| return NULL; |
| } |
| fmt = fmtend; |
| } |
| } |
| |
| return head; |
| } |
| |
| void |
| pkg_format_show(const struct pkg_format_node *head, |
| struct pkginfo *pkg, struct pkgbin *pif) |
| { |
| struct varbuf vb = VARBUF_INIT, fb = VARBUF_INIT, wb = VARBUF_INIT; |
| |
| while (head) { |
| bool ok; |
| char fmt[16]; |
| |
| ok = false; |
| |
| if (head->width > 0) |
| snprintf(fmt, 16, "%%%s%zus", |
| ((head->pad) ? "-" : ""), head->width); |
| else |
| strcpy(fmt, "%s"); |
| |
| if (head->type == string) { |
| varbuf_printf(&fb, fmt, head->data); |
| ok = true; |
| } else if (head->type == field) { |
| const struct fieldinfo *fip; |
| |
| for (fip = fieldinfos; fip->name; fip++) |
| if (strcasecmp(head->data, fip->name) == 0) { |
| fip->wcall(&wb, pkg, pif, 0, fip); |
| |
| varbuf_end_str(&wb); |
| varbuf_printf(&fb, fmt, wb.buf); |
| varbuf_reset(&wb); |
| ok = true; |
| break; |
| } |
| |
| if (!fip->name) { |
| const struct arbitraryfield *afp; |
| |
| for (afp = pif->arbs; afp; afp = afp->next) |
| if (strcasecmp(head->data, afp->name) == 0) { |
| varbuf_printf(&fb, fmt, afp->value); |
| ok = true; |
| break; |
| } |
| } |
| } |
| |
| if (ok) { |
| size_t len = strlen(fb.buf); |
| if ((head->width > 0) && (len > head->width)) |
| len = head->width; |
| varbuf_add_buf(&vb, fb.buf, len); |
| } |
| |
| varbuf_reset(&fb); |
| head = head->next; |
| } |
| |
| if (vb.buf) { |
| varbuf_end_str(&vb); |
| fputs(vb.buf, stdout); |
| } |
| |
| varbuf_destroy(&wb); |
| varbuf_destroy(&fb); |
| varbuf_destroy(&vb); |
| } |