hirc

IRC client
Log | Files | Refs

commit dfe5514401e74cfc6c9ab3ed7550ec3b522c20eb
parent f2f54d06c485e5c0e9e5f42f67dd0ec7faba5fb3
Author: hhvn <dev@hhvn.uk>
Date:   Fri, 25 Feb 2022 22:25:33 +0000

Implement %{rdate:s} for formatting relative dates

Diffstat:
Mhirc.1.header | 5++++-
Msrc/config.c | 17+++++++++++++++++
Msrc/hirc.h | 1+
Msrc/main.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ui.c | 21++++++++++++++++++++-
5 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/hirc.1.header b/hirc.1.header @@ -78,6 +78,10 @@ Split string s using character c, and print nth element. .It %{time:f,s} If string s is a unix timestamp, format it by passing f to .Xr strftime 3 "." +.It %{rtime:s} +Get relative date from unix timestamp s. +This behaviour is dictated by rdate.* namespaced settings. +For a better understanding, see strrdate() in src/main.c. .El Certain variables may also be used in formats: @@ -108,7 +112,6 @@ This selects all parameters following n. .It ${time} Unix timestamp of a message in string form. Should most likely be used with the %{time:f,s} styling. -.Xr strftime 3 "." .El For example, the message: diff --git a/src/config.c b/src/config.c @@ -190,6 +190,23 @@ struct Config config[] = { .strhandle = config_redraws, .description = { "String to be used as divider", NULL}}, + {"rdate.short", 1, Val_bool, + .num = 0, + .numhandle = config_redrawl, + .description = { + "Show short units of time (eg, 1d 2h) vs", + "long (eg, 1 day 2 hours) units for %{rdate:...}"}}, + {"rdate.averages", 1, Val_bool, + .num = 1, + .numhandle = config_redrawl, + .description = { + "Months and years are calculated with averages.", + "Disabling this setting will only use absolute units."}}, /* heh, not intentional */ + {"rdate.verbose", 1, Val_bool, + .num = 0, + .numhandle = config_redrawl, + .description = { + "Show all units for %{rdate:...}"}}, {"timestamp.toggle", 1, Val_bool, .num = 1, .numhandle = config_redrawl, diff --git a/src/hirc.h b/src/hirc.h @@ -53,6 +53,7 @@ char chrcmp(char c, char *s); char * struntil(char *str, char until); int strisnum(char *str); char * strntok(char *str, char *sep, int n); +char * strrdate(time_t secs); /* chan.c */ void chan_free(struct Channel *channel); diff --git a/src/main.c b/src/main.c @@ -365,6 +365,72 @@ strntok(char *str, char *sep, int n) { return NULL; } +#define S_YEAR 31557600 /* 60*60*24*365.25 */ +#define S_MONTH 2629800 /* 60*60*24*(365.25 / 12) */ +#define S_WEEK 604800 /* 60*60*24*7 */ +#define S_DAY 86400 /* 60*60*24 */ +#define S_HOUR 3600 /* 60*60 */ +#define S_MIN 60 + +char * +strrdate(time_t secs) { + static char ret[1024]; + size_t rc = 0; + long shrt = config_getl("rdate.short"); + long avg = config_getl("rdate.averages"); + long verb = config_getl("rdate.verbose"); + int years = 0, months = 0, weeks = 0, + days = 0, hours = 0, mins = 0; + + if (avg) { + years = secs / S_YEAR; + secs -= years * S_YEAR; + + months = secs / S_MONTH; + secs -= months * S_MONTH; + } + + weeks = secs / S_WEEK; + secs -= weeks * S_WEEK; + + days = secs / S_DAY; + secs -= days * S_DAY; + + hours = secs / S_HOUR; + secs -= hours * S_HOUR; + + mins = secs / S_MIN; + secs -= mins * S_MIN; + + if (years || (verb && avg)) + rc += snprintf(&ret[rc], sizeof(ret) - rc, "%d%s%s, ", years, + shrt ? "y" : " year", !shrt && years != 1 ? "s" : ""); + if (months || (verb && avg)) + rc += snprintf(&ret[rc], sizeof(ret) - rc, "%d%s%s, ", months, + shrt ? "mo" : " month", !shrt && months != 1 ? "s" : ""); + if (weeks || verb) + rc += snprintf(&ret[rc], sizeof(ret) - rc, "%d%s%s, ", weeks, + shrt ? "w" : " week", !shrt && weeks != 1 ? "s" : ""); + if (days || verb) + rc += snprintf(&ret[rc], sizeof(ret) - rc, "%d%s%s, ", days, + shrt ? "d" : " day", !shrt && days != 1 ? "s" : ""); + if (hours || verb) + rc += snprintf(&ret[rc], sizeof(ret) - rc, "%d%s%s, ", hours, + shrt ? "h" : " hour", !shrt && hours != 1 ? "s" : ""); + if (mins || verb) + rc += snprintf(&ret[rc], sizeof(ret) - rc, "%d%s%s, ", mins, + shrt ? "m" : " min", !shrt && mins != 1 ? "s" : ""); + if (secs || verb) + rc += snprintf(&ret[rc], sizeof(ret) - rc, "%d%s%s, ", secs, + shrt ? "s" : " sec", !shrt && secs != 1 ? "s" : ""); + if (rc >= 2) + ret[rc - 2] = '\0'; + else + ret[rc] = '\0'; + + return ret; +} + void sighandler(int signal) { return; diff --git a/src/ui.c b/src/ui.c @@ -1451,7 +1451,10 @@ ui_format(struct Window *window, char *format, struct History *hist) { break; } - /* pad, nick, split and time, must then continue as they modify content */ + /* pad, nick, split, rdate and time, must then continue as they modify content + * + * These styling formatters are quite ugly and repetitive. + * %{nick:...} was implemented first, and has the most (all of them :)) comments */ if (strncmp(content, "pad:", strlen("pad:")) == 0 && strchr(content, ',')) { pn = strtol(content + strlen("pad:"), NULL, 10); content = estrdup(ui_format_get_content(strchr(format+2+strlen("pad:"), ',') + 1, 1)); @@ -1469,6 +1472,22 @@ ui_format(struct Window *window, char *format, struct History *hist) { continue; } + if (strncmp(content, "rdate:", strlen("rdate:")) == 0) { + content = estrdup(ui_format_get_content(format+2+strlen("rdate:"), 1)); + save = estrdup(ret); + recursive = 1; + p = estrdup(ui_format(NULL, content, hist)); + recursive = 0; + memcpy(ret, save, rc); + pn = strtoll(p, NULL, 10); + rc += snprintf(&ret[rc], sizeof(ret) - rc, "%s", strrdate((time_t)pn)); + format += 3 + strlen("rdate:") + strlen(content); + + free(content); + free(save); + free(p); + } + if (strncmp(content, "time:", strlen("time:")) == 0 && strchr(content, ',')) { content = estrdup(ui_format_get_content(strchr(format+2+strlen("time:"), ',') + 1, 1)); save = estrdup(ret);