tmisu

Notification to stdout daemon
git clone https://git.inz.fi/tmisu/
Log | Files | Refs | README | LICENSE

commit 048f4a417cb321922ddd3377fc3e17c0706d305f
parent 5d8f9c1f832f856f3ca78bb27c82dd34dacc4e5d
Author: Santtu Lakkala <inz@inz.fi>
Date:   Wed,  2 Mar 2022 16:06:31 +0200

Some rewrite

Diffstat:
Aarg.h | 37+++++++++++++++++++++++++++++++++++++
Moutput.c | 305++++++++++++++++++++++++++++++++-----------------------------------------------
Moutput.h | 8++++++--
Mtmisu.c | 112+++++++++++++++++++++++++++++++++++++++++--------------------------------------
4 files changed, 223 insertions(+), 239 deletions(-)

diff --git a/arg.h b/arg.h @@ -0,0 +1,37 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H +#define ARG_H + +extern char *argv0; + +/* int main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, *argv ? (argc--, argv++) : ((void *)0); \ + *argv && (*argv)[0] == '-' && (*argv)[1]; argc--, argv++) { \ + int i_, argused_; \ + if ((*argv)[1] == '-' && !(*argv)[2]) { \ + argc--, argv++; \ + break; \ + } \ + for (i_ = 1, argused_ = 0; (*argv)[i_]; i_++) { \ + switch((*argv)[i_]) +#define ARGEND if (argused_) { \ + if ((*argv)[i_ + 1]) { \ + break; \ + } else { \ + argc--, argv++; \ + break; \ + } \ + } \ + } \ + } +#define ARGC() ((*argv)[i_]) +#define ARGF_(x) (((*argv)[i_ + 1]) ? (argused_ = 1, &((*argv)[i_ + 1])) : \ + (*(argv + 1)) ? (argused_ = 1, *(argv + 1)) : (x)) +#define EARGF(x) ARGF_(((x), exit(1), (char *)0)) +#define ARGF() ARGF_((char *)0) + +#endif diff --git a/output.c b/output.c @@ -6,227 +6,166 @@ #include "output.h" #include "tmisu.h" -static void print_sanitized(const char *string, const char *escape) { +static void escputs(const char *string, const char *escape) +{ + static const char *special = "\b\f\n\r\t"; + static const char *sp_rep = "bfnrt"; + char esc[2] = "\\"; + while (*string) { size_t len = strcspn(string, escape); - printf("%.*s", (int)len, string); + fwrite(string, 1, len, stdout); string += len; + while (*string && strchr(escape, *string)) { - if (*string == '\n') - printf("\\n"); + const char *sp; + if ((sp = strchr(special, *string))) + esc[1] = sp_rep[sp - special]; else - printf("\\%c", *string); + esc[1] = *string; string++; + fwrite(esc, 1, 2, stdout); } } } -static void hints_output_iterator(DBusMessageIter *hints, const char *key_prefix, const char *key_suffix, const char *str_prefix, const char *str_suffix, const char *escape, const char *delimiter, int join) +void esc_printf(const char *spec, const char *fmt, ...) { - const char *d = ""; - for (; dbus_message_iter_get_arg_type(hints) != DBUS_TYPE_INVALID; dbus_message_iter_next(hints), d = delimiter) { - DBusMessageIter dictentry; - DBusMessageIter variant; - DBusBasicValue value; - const char *key; - - dbus_message_iter_recurse(hints, &dictentry); - dbus_message_iter_get_basic(&dictentry, &key); - dbus_message_iter_next(&dictentry); - dbus_message_iter_recurse(&dictentry, &variant); - - printf("%s%s", d, key_prefix); - print_sanitized(key, escape); - printf("%s", key_suffix); - - if (!dbus_type_is_basic(dbus_message_iter_get_arg_type(&variant))) { - printf("null"); - continue; - } - - dbus_message_iter_get_basic(&variant, &value); - - switch (dbus_message_iter_get_arg_type(&variant)) { - case DBUS_TYPE_BYTE: - printf("%" PRIu8, value.byt); + const char *arg; + DBusBasicValue value; + va_list a; + va_start(a, fmt); + + while (*fmt) { + size_t n = strcspn(fmt, "%"); + fwrite(fmt, 1, n, stdout); + if (!fmt[n]) break; - case DBUS_TYPE_BOOLEAN: - printf("%s", value.bool_val ? "true" : "false"); + switch (fmt[n + 1]) { + case 's': + arg = va_arg(a, const char *); + fputs(arg, stdout); break; - case DBUS_TYPE_INT16: - printf("%" PRId16, value.i16); + case 'z': + arg = va_arg(a, const char *); + escputs(arg, spec); break; - case DBUS_TYPE_UINT16: - printf("%" PRIu16, value.u16); + case 'D': + DBusMessageIter *i = va_arg(a, DBusMessageIter *); + int type = dbus_message_iter_get_arg_type(i); + + if (type == DBUS_TYPE_ARRAY || + type == DBUS_TYPE_STRUCT) { + fputs("null", stdout); + break; + } + + dbus_message_iter_get_basic(i, &value); + switch (type) { + case DBUS_TYPE_BYTE: + printf("%" PRIu8, value.byt); + break; + case DBUS_TYPE_BOOLEAN: + printf("%s", value.bool_val ? "true" : "false"); + break; + case DBUS_TYPE_INT16: + printf("%" PRId16, value.i16); + break; + case DBUS_TYPE_UINT16: + printf("%" PRIu16, value.u16); + break; + case DBUS_TYPE_INT32: + printf("%" PRId32, value.i32); + break; + case DBUS_TYPE_UINT32: + printf("%" PRIu32, value.u32); + break; + case DBUS_TYPE_INT64: + printf("%" PRId64, value.i64); + break; + case DBUS_TYPE_UINT64: + printf("%" PRIu64, value.u64); + break; + case DBUS_TYPE_DOUBLE: + printf("%lf", value.dbl); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + fwrite("\"", 1, 1, stdout); + escputs(value.str, spec); + fwrite("\"", 1, 1, stdout); + break; + } break; - case DBUS_TYPE_INT32: - printf("%" PRId32, value.i32); + case '%': + fputs("%", stdout); break; - case DBUS_TYPE_UINT32: - printf("%" PRIu32, value.u32); - break; - case DBUS_TYPE_INT64: - printf("%" PRId64, value.i64); - break; - case DBUS_TYPE_UINT64: - printf("%" PRIu64, value.u64); - break; - case DBUS_TYPE_DOUBLE: - printf("%lf", value.dbl); - break; - case DBUS_TYPE_STRING: - case DBUS_TYPE_OBJECT_PATH: - dbus_message_iter_get_basic(&variant, &value); - printf("%s", str_prefix); - print_sanitized(value.str, escape); - printf("%s", str_suffix); - continue; - break; - default: - printf("null%s", d); - continue; } + fmt += n + 2; } - - if (!join && d == delimiter) - printf("%s", delimiter); } -static void default_output(const char *app_name, const char *app_icon, dbus_uint32_t id, dbus_uint32_t replaces_id, - dbus_int32_t timeout, DBusMessageIter *hints, DBusMessageIter *actions, const char *summary, - const char *body, const char *delimiter) +static void json_output(uint32_t id, struct notification_data *d, + const char *delimiter) { - (void)id; - printf("app_name: "); - print_sanitized(app_name, "\n\\"); - printf("%sapp_icon: ", delimiter); - print_sanitized(app_icon, "\n\\"); - printf("%sreplaces_id: %" PRIu32 "%stimeout: %" PRId32 "%s", delimiter, - replaces_id, delimiter, - timeout, delimiter); - - printf("hints:%s", delimiter); - hints_output_iterator(hints, "\t", ": ", "", "", "\n\\", delimiter, 0); - - printf("actions:%s", delimiter); - while (dbus_message_iter_get_arg_type(actions) != DBUS_TYPE_INVALID) { - if (!dbus_message_iter_has_next(actions)) - break; - const char *key; - const char *value; - - dbus_message_iter_get_basic(actions, &key); - dbus_message_iter_next(actions); - dbus_message_iter_get_basic(actions, &value); - dbus_message_iter_next(actions); - - printf("\t"); - print_sanitized(key, "\n\\"); - printf(": "); - print_sanitized(value, "\n\\"); - printf("%s", delimiter); - } - - printf("body: "); - print_sanitized(body, "\n\\"); - printf("%ssummary: ", delimiter); - print_sanitized(summary, "\n\\"); - printf("%s", delimiter); -} - -static void json_output(const char *app_name, const char *app_icon, dbus_uint32_t id, dbus_uint32_t replaces_id, - dbus_int32_t timeout, DBusMessageIter *hints, DBusMessageIter *actions, const char *summary, - const char *body, const char *delimiter) { + DBusMessageIter i; const char *sep = ""; + const char *json_spec = "\\\n\r\b\f\""; printf("{" - "\"id\": %" PRIu32 ", " - "\"app_name\": \"", id); - print_sanitized(app_name, "\n\\\""); - printf("\", " - "\"app_icon\": \""); - print_sanitized(app_icon, "\n\\\""); - printf("\", " - "\"replaces_id\": %" PRIu32 ", " + "\"id\": %" PRIu32 ", ", id); + if (d->app_name) + esc_printf(json_spec, "\"app_name\": \"%s\", ", d->app_name); + if (d->icon) + esc_printf(json_spec, "\"app_icon\": \"%s\", ", d->icon); + printf("\"replaces_id\": %" PRIu32 ", " "\"timeout\": %" PRId32 ", ", - replaces_id, timeout); + d->replaces, d->expiry_ms); printf("\"hints\": {"); - hints_output_iterator(hints, "\"", "\": ", "\"", "\"", "\n\\\"", ", ", 1); + for (i = d->hints; + dbus_message_iter_get_arg_type(&i) != DBUS_TYPE_INVALID; + dbus_message_iter_next(&i)) { + DBusMessageIter di; + const char *key; + + dbus_message_iter_recurse(&i, &di); + dbus_message_iter_get_basic(&di, &key); + dbus_message_iter_next(&di); + dbus_message_iter_recurse(&di, &di); + + esc_printf(json_spec, "%s\"%z\": %D", sep, key, &di); + sep = ", "; + } printf("}, \"actions\": {"); - while (dbus_message_iter_get_arg_type(actions) != DBUS_TYPE_INVALID) { - if (!dbus_message_iter_has_next(actions)) - break; + sep = ""; + for (i = d->actions; + dbus_message_iter_get_arg_type(&i) == DBUS_TYPE_STRING; + dbus_message_iter_next(&i)) { const char *key; const char *value; - dbus_message_iter_get_basic(actions, &key); - dbus_message_iter_next(actions); - dbus_message_iter_get_basic(actions, &value); - dbus_message_iter_next(actions); + if (!dbus_message_iter_has_next(&i)) + break; - printf("\""); - print_sanitized(key, "\n\\\""); - printf("\": \""); - print_sanitized(value, "\n\\\""); - printf("\"%s", sep); + dbus_message_iter_get_basic(&i, &key); + dbus_message_iter_next(&i); + dbus_message_iter_get_basic(&i, &value); + + esc_printf(json_spec, "%s\"%z\": \"%z\"", sep, key, value); sep = ", "; } - printf("}, "); - printf("\"summary\": \""); - print_sanitized(summary, "\n\\\""); - printf("\", " - "\"body\": \""); - print_sanitized(body, "\n\\\""); - printf("\"}%s", delimiter); + esc_printf(json_spec, + "}, " + "\"summary\": \"%z\", " + "\"body\": \"%z\"" + "}", d->summary, d->body, delimiter); } -void output_notification(DBusMessage *message, dbus_uint32_t id, enum output_format fmt, const char *delimiter) +void output_notification(uint32_t id, struct notification_data *notif) { - DBusMessageIter i; - DBusMessageIter actions; - DBusMessageIter hints; - const char *app_name; - dbus_uint32_t replaces_id; - dbus_int32_t timeout; - const char *app_icon; - const char *summary; - const char *body; - - dbus_message_iter_init(message, &i); - dbus_message_iter_get_basic(&i, &app_name); - dbus_message_iter_next(&i); - dbus_message_iter_get_basic(&i, &replaces_id); - dbus_message_iter_next(&i); - dbus_message_iter_get_basic(&i, &app_icon); - dbus_message_iter_next(&i); - dbus_message_iter_get_basic(&i, &summary); - dbus_message_iter_next(&i); - dbus_message_iter_get_basic(&i, &body); - - dbus_message_iter_next(&i); - dbus_message_iter_recurse(&i, &actions); - - dbus_message_iter_next(&i); - dbus_message_iter_recurse(&i, &hints); - - dbus_message_iter_next(&i); - dbus_message_iter_get_basic(&i, &timeout); - - switch (fmt) { - case FORMAT_JSON: - json_output(app_name, app_icon, id, replaces_id, - timeout, &hints, &actions, summary, body, - delimiter); - break; - case FORMAT_TEXT: - default: - default_output(app_name, app_icon, id, replaces_id, - timeout, &hints, &actions, summary, body, - delimiter); - } - + json_output(id, notif, ", "); fflush(stdout); } diff --git a/output.h b/output.h @@ -1,10 +1,14 @@ -#pragma once +#ifndef OUTPUT_H +#define OUTPUT_H #include <dbus/dbus.h> +#include "tmisu.h" enum output_format { FORMAT_TEXT, FORMAT_JSON }; -void output_notification(DBusMessage *message, dbus_uint32_t id, enum output_format fmt, const char *delimiter); +void output_notification(dbus_uint32_t id, struct notification_data *notif); + +#endif diff --git a/tmisu.c b/tmisu.c @@ -5,7 +5,6 @@ #include <stdarg.h> #include <unistd.h> #include <stdlib.h> -#include <getopt.h> #include <time.h> #include <sys/poll.h> #include <sys/wait.h> @@ -31,11 +30,10 @@ struct conf { struct notification { struct notification *next; + struct notification_data d; DBusMessage *msg; dbus_uint32_t id; time_t expiry; - const char *title; - const char *body; pid_t child; }; @@ -207,47 +205,47 @@ time_t timeout_next_expiry(void) static void notif_spawn(struct notification *n) { - if (cmd) { - n->child = fork(); - switch (n->child) { - case -1: - groan("fork() failed:"); - n->child = 0; - break; - case 0: - close(STDIN_FILENO); - open("/dev/null", O_RDONLY); - close(STDOUT_FILENO); - open("/dev/null", O_WRONLY); - execlp(cmd, cmd, n->title, n->body, NULL); - exit(1); - break; - default: - break; - } + if (!cmd) { + n->child = 0; + return; + } + + n->child = fork(); + switch (n->child) { + case -1: + groan("fork() failed:"); + n->child = 0; + break; + case 0: + close(STDIN_FILENO); + open("/dev/null", O_RDONLY); + close(STDOUT_FILENO); + open("/dev/null", O_WRONLY); + execlp(cmd, cmd, n->d.summary, n->d.body, NULL); + exit(1); + break; + default: + break; } } const struct notification *notif_update(DBusMessage *msg, - dbus_uint32_t id, - const char *title, - const char *body, + struct notification_data *d, time_t expiry) { struct notification *i; - for (i = notifications; i && i->id != id; i = i->next); + for (i = notifications; i && i->id != d->replaces; i = i->next); if (!i) return NULL; - changed |= use_json || strcmp(title, i->title); + changed |= use_json || strcmp(d->summary, i->d.summary); dbus_message_unref(i->msg); if (cmd && i->child) kill(i->child, SIGTERM); - i->title = title; - i->body = body; + memcpy(&i->d, d, sizeof(i->d)); i->expiry = expiry; i->msg = dbus_message_ref(msg); @@ -257,8 +255,7 @@ const struct notification *notif_update(DBusMessage *msg, } const struct notification *notif_add(DBusMessage *msg, - const char *title, - const char *body, + struct notification_data *d, time_t expiry) { static dbus_uint32_t notification_id = 0; @@ -268,8 +265,7 @@ const struct notification *notif_add(DBusMessage *msg, if (!(nn->id = ++notification_id)) nn->id = ++notification_id; nn->msg = dbus_message_ref(msg); - nn->title = title; - nn->body = body; + memcpy(&nn->d, d, sizeof(nn->d)); nn->expiry = expiry; nn->next = NULL; @@ -375,28 +371,39 @@ void notif_check_expiry(void) } } -static const char *esc(const char *string, const char *escape) { +static void writ(const char *part, size_t plen, void *data) +{ + fwrite(part, 1, plen, data); +} + +static void esc(const char *string, const char *escape, + void (*cb)(const char *part, size_t plen, void *data), + void *data) { static char buffer[1024]; static const char *special = "\b\f\n\r\t"; static const char *sp_rep = "bfnrt"; size_t p = 0; while (*string) { - size_t len = strcspn(string, escape); - memcpy(buffer + p, string, len); - string += len; - p += len; - while (*string && strchr(escape, *string)) { - const char *sp; - buffer[p++] = '\\'; - if ((sp = strchr(special, *string))) - buffer[p++] = sp_rep[sp - special]; - else - buffer[p++] = *string; - string++; + while (*string && p < sizeof(buffer)) { + size_t len = strcspn(string, escape); + if (len > sizeof(buffer) - p) + len = sizeof(buffer) - p; + memcpy(buffer + p, string, len); + string += len; + p += len; + while (*string && strchr(escape, *string)) { + const char *sp; + buffer[p++] = '\\'; + if ((sp = strchr(special, *string))) + buffer[p++] = sp_rep[sp - special]; + else + buffer[p++] = *string; + string++; + } } + cb(buffer, p, data); + p = 0; } - buffer[p] = '\0'; - return buffer; } void notif_dump(void) @@ -409,9 +416,9 @@ void notif_dump(void) for (i = notifications; i; i = i->next) { printf("%s", sep); if (use_json) - output_notification(i->msg, i->id, FORMAT_JSON, ""); + output_notification(i->id, &i->d); else - printf("%s", esc(i->title, "\\\r\n\t\b\f")); + esc(i->d.summary, "\\\r\n\t\b\f", writ, stdout); sep = delimiter; } if (use_json) @@ -506,14 +513,11 @@ DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *messag if (d.replaces) n = notif_update(message, - d.replaces, - d.summary, - d.body, + &d, expiry); if (!n) n = notif_add(message, - d.summary, - d.body, + &d, expiry); reply = dbus_message_new_method_return(message);