Merge branch 'andrew/pformat' into 'master'

Draft: WIP: add python/rust style format strings

See merge request pacman/pacman!41
This commit is contained in:
Andrew Gregory 2025-06-22 00:58:18 +00:00
commit b2fa9f466c
8 changed files with 500 additions and 0 deletions

View file

@ -2987,6 +2987,9 @@ int alpm_sandbox_setup_child(alpm_handle_t *handle, const char *sandboxuser, con
/* End of libalpm_api */ /* 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 #ifdef __cplusplus
} }
#endif #endif

226
lib/libalpm/info.c Normal file
View file

@ -0,0 +1,226 @@
#include <errno.h>
#include <string.h>
#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: */

View file

@ -31,5 +31,7 @@ libalpm_sources = files('''
sync.h sync.c sync.h sync.c
trans.h trans.c trans.h trans.c
util.h util.c util.h util.c
info.c
mfmt.c mfmt.h
version.c version.c
'''.split()) '''.split())

193
lib/libalpm/mfmt.c Normal file
View file

@ -0,0 +1,193 @@
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#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);
}

66
lib/libalpm/mfmt.h Normal file
View file

@ -0,0 +1,66 @@
#include <stdio.h>
#include <stddef.h>
#include <inttypes.h>
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);

View file

@ -60,6 +60,7 @@ typedef struct __config_t {
unsigned short disable_dl_timeout; unsigned short disable_dl_timeout;
unsigned short disable_sandbox; unsigned short disable_sandbox;
char *print_format; char *print_format;
char *pformat;
/* unfortunately, we have to keep track of paths both here and in the library /* 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 * because they can come from both the command line or config file, and we
* need to ensure we get the order of preference right. */ * need to ensure we get the order of preference right. */
@ -175,6 +176,7 @@ enum {
OP_ASEXPLICIT, OP_ASEXPLICIT,
OP_ARCH, OP_ARCH,
OP_PRINTFORMAT, OP_PRINTFORMAT,
OP_PFORMAT,
OP_GPGDIR, OP_GPGDIR,
OP_DBONLY, OP_DBONLY,
OP_FORCE, OP_FORCE,

View file

@ -684,6 +684,9 @@ static int parsearg_trans(int opt)
free(config->print_format); free(config->print_format);
config->print_format = strdup(optarg); config->print_format = strdup(optarg);
break; break;
case OP_PFORMAT:
config->pformat = strdup(optarg);
break;
case OP_ASSUMEINSTALLED: case OP_ASSUMEINSTALLED:
parsearg_util_addlist(&(config->assumeinstalled)); parsearg_util_addlist(&(config->assumeinstalled));
break; break;
@ -977,6 +980,7 @@ static int parseargs(int argc, char *argv[])
{"asexplicit", no_argument, 0, OP_ASEXPLICIT}, {"asexplicit", no_argument, 0, OP_ASEXPLICIT},
{"arch", required_argument, 0, OP_ARCH}, {"arch", required_argument, 0, OP_ARCH},
{"print-format", required_argument, 0, OP_PRINTFORMAT}, {"print-format", required_argument, 0, OP_PRINTFORMAT},
{"pformat" , required_argument, 0, OP_PFORMAT},
{"gpgdir", required_argument, 0, OP_GPGDIR}, {"gpgdir", required_argument, 0, OP_GPGDIR},
{"dbonly", no_argument, 0, OP_DBONLY}, {"dbonly", no_argument, 0, OP_DBONLY},
{"color", required_argument, 0, OP_COLOR}, {"color", required_argument, 0, OP_COLOR},

View file

@ -1201,6 +1201,10 @@ double humanize_size(off_t bytes, const char target_unit, int precision,
void print_packages(const alpm_list_t *packages) void print_packages(const alpm_list_t *packages)
{ {
const alpm_list_t *i; const alpm_list_t *i;
if(config->pformat) {
alpm_info_print_pkgs(config->pformat, (alpm_list_t*) packages);
return;
}
if(!config->print_format) { if(!config->print_format) {
config->print_format = strdup("%l"); config->print_format = strdup("%l");
} }