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:
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);