diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 96e5e643..cfee19e0 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -2987,6 +2987,9 @@ int alpm_sandbox_setup_child(alpm_handle_t *handle, const char *sandboxuser, con /* End of libalpm_api */ /** @} */ +size_t alpm_info_print_pkg(const char *format, alpm_pkg_t *pkg); +size_t alpm_info_print_pkgs(const char *format, alpm_list_t *pkgs); + #ifdef __cplusplus } #endif diff --git a/lib/libalpm/info.c b/lib/libalpm/info.c new file mode 100644 index 00000000..097f6db1 --- /dev/null +++ b/lib/libalpm/info.c @@ -0,0 +1,226 @@ +#include +#include + +#include "alpm.h" +#include "util.h" + +#include "mfmt.h" + +typedef enum field_t { + FILENAME, + NAME, + BASE, + DESCRIPTION, + VERSION, + ORIGIN, + REASON, + LICENSE, + GROUP, + + DEPENDS, + OPTDEPENDS, + CONFLICTS, + PROVIDES, + REPLACES, + REQUIREDBY, + + DELTAS, + FILES, + BACKUP, + DB, + VALIDATION, + URL, + BUILDDATE, + INSTALLDATE, + PACKAGER, + MD5SUM, + SHA256SUM, + ARCH, + SIZE, + ISIZE, + BASE64SIG, + + UNKNOWN, +} field_t; + +static struct field_map_t { + const char *input; + field_t field; +} field_map[] = { + {"filename", FILENAME}, + {"name", NAME}, + {"base", BASE}, + {"description", DESCRIPTION}, + {"version", VERSION}, + + {"license", LICENSE}, + {"group", GROUP}, + {"groups", GROUP}, + + {"depends", DEPENDS}, + {"optdepends", OPTDEPENDS}, + {"conflicts", CONFLICTS}, + {"provides", PROVIDES}, + {"replaces", REPLACES}, + {"requiredby", REQUIREDBY}, + + {"url", URL}, + {"builddate", BUILDDATE}, + {"installdate", INSTALLDATE}, + {"packager", PACKAGER}, + {"md5sum", MD5SUM}, + {"sha256sum", SHA256SUM}, + {"arch", ARCH}, + {"size", SIZE}, + {"isize", ISIZE}, + {"base64sig", BASE64SIG}, + + {NULL, 0} +}; + +static char *_alpm_hr_size(off_t bytes, char *dest) +{ + static const char *suff[] = {"B", "K", "M", "G", "T", "P", "E", NULL}; + float hrsize; + int s = 0; + while((bytes >= 1000000 || bytes <= -1000000) && suff[s + 1]) { + bytes /= 1024; + ++s; + } + hrsize = bytes; + if((hrsize >= 1000 || hrsize <= -1000) && suff[s + 1]) { + hrsize /= 1024; + ++s; + } + sprintf(dest, "%.2f %s", hrsize, suff[s]); + return dest; +} + +static field_t _alpm_info_lookup_field(const char *name) { + struct field_map_t *m; + for(m = field_map; m->input; m++) { + if(strcmp(name, m->input) == 0) { return m->field; } + } + return UNKNOWN; +} + +static size_t _alpm_info_print_str(mfmt_token_callback_t *t, const char *str, FILE *f) { + return mfmt_render_str(t, str ? str : "NULL", f); +} + +static size_t _alpm_info_print_size(mfmt_token_callback_t *t, const off_t s, FILE *f) { + if(s) { + char hrsize[50]; + if(t->conversion == 'd') { + snprintf(hrsize, 50, "%lld", (long long)s); + } else { + _alpm_hr_size(s, hrsize); + } + return mfmt_render_str(t, hrsize, f); + } else { + return mfmt_render_str(t, "NULL", f); + } +} + +static size_t _alpm_info_print_strlist(mfmt_token_callback_t *t, alpm_list_t *l, FILE *f) { + if(l) { + size_t len = 0; + while(l) { + len += mfmt_render_str(t, l->data, f) + 1; + fputc('\n', f); + l = l->next; + } + return len; + } else { + return mfmt_render_str(t, "NULL", f); + } +} + +static size_t _alpm_info_print_deplist(mfmt_token_callback_t *t, alpm_list_t *l, FILE *f) { + if(l) { + size_t len = 0; + while(l) { + char *s = alpm_dep_compute_string(l->data); + len += mfmt_render_str(t, s, f) + 1; + fputc('\n', f); + l = l->next; + free(s); + } + return len; + } else { + return mfmt_render_str(t, "NULL", f); + } +} + +static size_t _alpm_info_print_timestamp(mfmt_token_callback_t *t, const alpm_time_t s, FILE *f) { + if(s) { + char datestr[50] = ""; + if(strftime(datestr, 50, " %c", localtime(&s)) == 0) { return 0; } + return mfmt_render_str(t, datestr + 1, f); + } else { + return mfmt_render_str(t, "NULL", f); + } +} + +static size_t _alpm_info_process_token(FILE *f, mfmt_token_callback_t *t, void *ctx, void *arg) { + alpm_pkg_t *p = arg; + (void)ctx; + switch(_alpm_info_lookup_field(t->name)) { + case NAME: return _alpm_info_print_str(t, alpm_pkg_get_name(p), f); + case DESCRIPTION: return _alpm_info_print_str(t, alpm_pkg_get_desc(p), f); + case PACKAGER: return _alpm_info_print_str(t, alpm_pkg_get_packager(p), f); + case MD5SUM: return _alpm_info_print_str(t, alpm_pkg_get_md5sum(p), f); + case FILENAME: return _alpm_info_print_str(t, alpm_pkg_get_filename(p), f); + case BASE: return _alpm_info_print_str(t, alpm_pkg_get_base(p), f); + case VERSION: return _alpm_info_print_str(t, alpm_pkg_get_version(p), f); + case URL: return _alpm_info_print_str(t, alpm_pkg_get_url(p), f); + case SHA256SUM: return _alpm_info_print_str(t, alpm_pkg_get_sha256sum(p), f); + case ARCH: return _alpm_info_print_str(t, alpm_pkg_get_arch(p), f); + case BASE64SIG: return _alpm_info_print_str(t, alpm_pkg_get_base64_sig(p), f); + + case SIZE: return _alpm_info_print_size(t, alpm_pkg_get_size(p), f); + case ISIZE: return _alpm_info_print_size(t, alpm_pkg_get_isize(p), f); + + case BUILDDATE: return _alpm_info_print_timestamp(t, alpm_pkg_get_builddate(p), f); + case INSTALLDATE: return _alpm_info_print_timestamp(t, alpm_pkg_get_installdate(p), f); + + case DEPENDS: return _alpm_info_print_deplist(t, alpm_pkg_get_depends(p), f); + case OPTDEPENDS: return _alpm_info_print_deplist(t, alpm_pkg_get_optdepends(p), f); + case CONFLICTS: return _alpm_info_print_deplist(t, alpm_pkg_get_conflicts(p), f); + case PROVIDES: return _alpm_info_print_deplist(t, alpm_pkg_get_provides(p), f); + case REPLACES: return _alpm_info_print_deplist(t, alpm_pkg_get_replaces(p), f); + case REQUIREDBY: { + alpm_list_t *rb = alpm_pkg_compute_requiredby(p); + size_t len = _alpm_info_print_strlist(t, rb, f); + FREELIST(rb); + return len; + } + + default: errno = EINVAL; return 0; + } +} + +size_t SYMEXPORT alpm_info_print_pkg(const char *format, alpm_pkg_t *pkg) { + alpm_list_t l = { + .data = pkg, + .next = NULL, + }; + l.prev = &l; + return alpm_info_print_pkgs(format, &l); +} + +size_t SYMEXPORT alpm_info_print_pkgs(const char *format, alpm_list_t *pkgs) { + mfmt_t *mfmt = mfmt_parse(format, _alpm_info_process_token, NULL); + size_t len = 0; + if(mfmt == NULL) { + return 0; + } + for(alpm_list_t *i = pkgs; i; i = i->next) { + size_t plen = mfmt_printf(mfmt, i->data, stdout); + if(plen == 0) { return 0; } + len += plen; + } + return len; +} + +/* vim: set ts=2 sw=2 et: */ diff --git a/lib/libalpm/meson.build b/lib/libalpm/meson.build index c2c605f9..b0845e96 100644 --- a/lib/libalpm/meson.build +++ b/lib/libalpm/meson.build @@ -31,5 +31,7 @@ libalpm_sources = files(''' sync.h sync.c trans.h trans.c util.h util.c + info.c + mfmt.c mfmt.h version.c '''.split()) diff --git a/lib/libalpm/mfmt.c b/lib/libalpm/mfmt.c new file mode 100644 index 00000000..c55d3fd0 --- /dev/null +++ b/lib/libalpm/mfmt.c @@ -0,0 +1,193 @@ +#define _GNU_SOURCE + +#include +#include +#include + +#include "mfmt.h" + +char *_mfmt_find_unescaped_char(char *haystack, char needle) { + while(1) { + haystack = strchrnul(haystack, needle); + if(*haystack && *(haystack + 1) == needle) { haystack += 2; continue; } + else { break; } + } + return haystack; +} + +void _mfmt_brace_dedup(char *str) { + char *c = str, *end = str + strlen(str); + while((c = strchr(c, '{'))) { + memmove(c, c + 1, end - c); + c++; + } + + c = str; + while((c = strchr(c, '}'))) { + memmove(c, c + 1, end - c); + c++; + } +} + +mfmt_t *mfmt_parse(const char *tmpl, mfmt_callback_t *cb, void *ctx) { + mfmt_t *mfmt; + char *c; + + mfmt = calloc(sizeof(mfmt_t), 1); + if(mfmt == NULL) { return NULL; } + + mfmt->cb = cb; + mfmt->ctx = ctx; + + for(c = (char*) tmpl; c && *c; ) { + mfmt->token_count++; + if(*c == '{' && *(c + 1) != '{') { + /* replacement */ + if(!*(c = _mfmt_find_unescaped_char(c + 1, '}'))) { + errno = EINVAL; + free(mfmt); + return NULL; + } else { + c++; + } + } else { + /* literal */ + c = _mfmt_find_unescaped_char(c, '{'); + } + } + + if((mfmt->tokens = calloc(sizeof(mfmt_token_t), mfmt->token_count)) == NULL) { + free(mfmt); + return NULL; + } + + size_t i; + for(c = (char*) tmpl, i = 0; c && *c; i++) { + if(*c == '{' && *(c + 1) != '{') { + /* replacement */ + mfmt_token_callback_t *t = &mfmt->tokens[i].callback; + char *end = _mfmt_find_unescaped_char(c + 1, '}'); + t->type = MFMT_TOKEN_CALLBACK; + t->name = strndup(c + 1, end - c - 1); + c = end + 1; + } else { + /* literal */ + char *end = _mfmt_find_unescaped_char(c, '{'); + mfmt_token_literal_t *t = &mfmt->tokens[i].literal; + t->type = MFMT_TOKEN_LITERAL; + t->string = strndup(c, end - c); + _mfmt_brace_dedup(t->string); + c = end; + } + } + + return mfmt; +} + +size_t mfmt_printf(mfmt_t *mfmt, void *args, FILE *f) { + size_t len = 0; + size_t i; + for(i = 0; i < mfmt->token_count; i++) { + mfmt_token_t *t = &mfmt->tokens[i]; + switch(t->base.type) { + case MFMT_TOKEN_LITERAL: + len += fputs(t->literal.string, f); + break; + case MFMT_TOKEN_CALLBACK: + len += mfmt->cb(f, &t->callback, mfmt->ctx, args); + break; + default: + errno = EINVAL; + return 0; + } + } + return len; +} + +static size_t _mfmt_printf_close(mfmt_t *mfmt, void *args, FILE *f) { + if(f) { + size_t len = mfmt_printf(mfmt, args, f); + fclose(f); + return len; + } + return -1; +} + +size_t mfmt_printd(mfmt_t *mfmt, void *args, int fd) { + return _mfmt_printf_close(mfmt, args, fdopen(fd, "w")); +} + +size_t mfmt_printb(mfmt_t *mfmt, void *args, char *buf, size_t buflen) { + return _mfmt_printf_close(mfmt, args, fmemopen(buf, buflen, "w")); +} + +size_t mfmt_prints(mfmt_t *mfmt, void *args, char **buf, size_t *buflen) { + return _mfmt_printf_close(mfmt, args, open_memstream(buf, buflen)); +} + +size_t mfmt_fmt(const char *tmpl, mfmt_val_t *args, FILE *f) { + mfmt_t *mfmt = mfmt_parse(tmpl, NULL, NULL); + size_t len; + for(size_t i = 0; i < mfmt->token_count; i++) { + mfmt_token_t *t = &mfmt->tokens[i]; + switch(t->base.type) { + case MFMT_TOKEN_LITERAL: + len += fputs(t->literal.string, f); + break; + case MFMT_TOKEN_CALLBACK: + /* fprintf(stderr, "token: %s\n", t->callback.name); */ + if(t->callback.name[0]) { + for(mfmt_val_t *v = args; v; v++) { + /* fprintf(stderr, "val: %s\n", v->name); */ + if(strcmp(v->name, t->callback.name) == 0) { + len += fputs(v->string, f); + break; + } + } + } else { + len += fputs(args->string, f); + args++; + } + break; + } + } + return len; +} + +size_t mfmt_mfmt(mfmt_t *mfmt, mfmt_val_t *args, FILE *f) { + size_t len; + for(size_t i = 0; i < mfmt->token_count; i++) { + mfmt_token_t *t = &mfmt->tokens[i]; + switch(t->base.type) { + case MFMT_TOKEN_LITERAL: + len += fputs(t->literal.string, f); + break; + case MFMT_TOKEN_CALLBACK: + /* fprintf(stderr, "token: %s\n", t->callback.name); */ + if(t->callback.name[0]) { + for(mfmt_val_t *v = args; v; v++) { + /* fprintf(stderr, "val: %s\n", v->name); */ + if(strcmp(v->name, t->callback.name) == 0) { + len += fputs(v->string, f); + break; + } + } + } else { + len += fputs(args->string, f); + args++; + } + break; + } + } + return len; +} + +size_t mfmt_render_int(mfmt_token_callback_t *t, const intmax_t i, FILE *f) { + (void)t; + return fprintf(f, "%jd", i); +} + +size_t mfmt_render_str(mfmt_token_callback_t *t, const char *str, FILE *f) { + (void)t; + return fputs(str, f); +} diff --git a/lib/libalpm/mfmt.h b/lib/libalpm/mfmt.h new file mode 100644 index 00000000..2f90cdb7 --- /dev/null +++ b/lib/libalpm/mfmt.h @@ -0,0 +1,66 @@ +#include +#include +#include + +typedef enum mfmt_token_type_t { + MFMT_TOKEN_LITERAL, + MFMT_TOKEN_CALLBACK, +} mfmt_token_type_t; + +typedef struct mfmt_token_literal_t { + mfmt_token_type_t type; + char *string; +} mfmt_token_literal_t; + +typedef struct mfmt_token_base_t { + mfmt_token_type_t type; +} mfmt_token_base_t; + +typedef struct mfmt_token_callback_t { + mfmt_token_type_t type; + + size_t position; + char *name; + size_t width; + size_t precision; + char align; + char fill; + char conversion; + int sign; +} mfmt_token_callback_t; + +typedef union mfmt_token_t { + mfmt_token_base_t base; + mfmt_token_literal_t literal; + mfmt_token_callback_t callback; +} mfmt_token_t; + +typedef size_t (mfmt_callback_t)(FILE *f, mfmt_token_callback_t *token, void *ctx, void *args); + +typedef struct mfmt_t { + mfmt_callback_t *cb; + void *ctx; + size_t token_count; + mfmt_token_t *tokens; +} mfmt_t; + +typedef struct mfmt_val_t { + const char *name; + const char *string; +} mfmt_val_t; + +mfmt_t *mfmt_parse(const char *tmpl, mfmt_callback_t *cb, void *ctx); +size_t mfmt_printf(mfmt_t *mfmt, void *args, FILE *f); +size_t mfmt_printd(mfmt_t *mfmt, void *args, int fd); +size_t mfmt_printb(mfmt_t *mfmt, void *args, char *buf, size_t buflen); +size_t mfmt_prints(mfmt_t *mfmt, void *args, char **buf, size_t *buflen); +void mfmt_free(mfmt_t *mfmt); + +size_t mfmt_render_int(mfmt_token_callback_t *token, intmax_t i, FILE *f); +size_t mfmt_render_uint(mfmt_token_callback_t *token, uintmax_t i, FILE *f); +size_t mfmt_render_str(mfmt_token_callback_t *token, const char *str, FILE *f); + +size_t mfmt_formatf(const char *tmpl, mfmt_callback_t *cb, void *ctx, FILE *f); +size_t mfmt_formatd(const char *tmpl, mfmt_callback_t *cb, void *ctx, int fd); +size_t mfmt_formatb(const char *tmpl, mfmt_callback_t *cb, void *ctx, char *buf, size_t buflen); +size_t mfmt_formats(const char *tmpl, mfmt_callback_t *cb, void *ctx, char **buf); diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 2c4fddf0..7f7d14a5 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -60,6 +60,7 @@ typedef struct __config_t { unsigned short disable_dl_timeout; unsigned short disable_sandbox; char *print_format; + char *pformat; /* unfortunately, we have to keep track of paths both here and in the library * because they can come from both the command line or config file, and we * need to ensure we get the order of preference right. */ @@ -175,6 +176,7 @@ enum { OP_ASEXPLICIT, OP_ARCH, OP_PRINTFORMAT, + OP_PFORMAT, OP_GPGDIR, OP_DBONLY, OP_FORCE, diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 2866fc98..59d73e7f 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -684,6 +684,9 @@ static int parsearg_trans(int opt) free(config->print_format); config->print_format = strdup(optarg); break; + case OP_PFORMAT: + config->pformat = strdup(optarg); + break; case OP_ASSUMEINSTALLED: parsearg_util_addlist(&(config->assumeinstalled)); break; @@ -977,6 +980,7 @@ static int parseargs(int argc, char *argv[]) {"asexplicit", no_argument, 0, OP_ASEXPLICIT}, {"arch", required_argument, 0, OP_ARCH}, {"print-format", required_argument, 0, OP_PRINTFORMAT}, + {"pformat" , required_argument, 0, OP_PFORMAT}, {"gpgdir", required_argument, 0, OP_GPGDIR}, {"dbonly", no_argument, 0, OP_DBONLY}, {"color", required_argument, 0, OP_COLOR}, diff --git a/src/pacman/util.c b/src/pacman/util.c index 3b96568d..13c66040 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1201,6 +1201,10 @@ double humanize_size(off_t bytes, const char target_unit, int precision, void print_packages(const alpm_list_t *packages) { const alpm_list_t *i; + if(config->pformat) { + alpm_info_print_pkgs(config->pformat, (alpm_list_t*) packages); + return; + } if(!config->print_format) { config->print_format = strdup("%l"); }