diff --git a/contrib/remind-conf-mode/remind-conf-mode.el b/contrib/remind-conf-mode/remind-conf-mode.el index 1e471b21..3ce74a19 100644 --- a/contrib/remind-conf-mode/remind-conf-mode.el +++ b/contrib/remind-conf-mode/remind-conf-mode.el @@ -177,7 +177,7 @@ "slide" "soleq" "stdout" "strlen" "substr" "sunrise" "sunset" "time" "timepart" "timezone" "today" "trig" "trigback" "trigdate" "trigdatetime" "trigdelta" "trigduration" "trigeventduration" - "trigeventstart" "trigfrom" "trigger" "trigpriority" "trigrep" + "trigeventstart" "trigfrom" "trigger" "triginfo" "trigpriority" "trigrep" "trigscanfrom" "trigtags" "trigtime" "trigtimedelta" "trigtimerep" "triguntil" "trigvalid" "typeof" "tzconvert" "upper" "utctolocal" "value" "version" "weekno" "wkday" "wkdaynum" "year") diff --git a/man/remind.1.in b/man/remind.1.in index 2fcd6af0..fad57a76 100644 --- a/man/remind.1.in +++ b/man/remind.1.in @@ -1661,6 +1661,11 @@ is replaced with "\fIyy\fR", the last two digits of the year. is replaced with the lookup of \fIany_text\fR in the translation table. It is the equivalent of [_("any_text")] but is more convenient to type. .TP +.B %<\fIany_text\fR\fB> +is replaced with the INFO value associated with the header \fIany_text\fR +or the empty string if no such INFO value exists. It is the +equivalent of [triginfo("any_text")] but is more convenient to type. +.TP .B %_ (percent-underscore) is replaced with a newline. You can use this to achieve multi-line reminders. Note that calendar back-ends vary in @@ -4292,7 +4297,23 @@ whose value is the maximum of "yyyy-mm-dd" and today. .B trigfrom() Returns (as a \fBDATE\fR type) the \fBFROM\fR parameter of the last \fBREM\fR or \fBIFTRIG\fR command. If there was no \fBFROM\fR parameter, returns the integer -1. - +.TP +.B triginfo(s_header) +Returns a \fBSTRING\fR that is the INFO item associated with the header +\fIheader\fR. The header should \fInot\fR contain a colon. Header name +comparisons are case-insensitive. +.RS +.PP +For example, the following will assign "At home" to the variable a and +the empty string to variable b: +.PP +.nf + REM INFO "Location: At home" MSG test + SET a triginfo("location") + SET b triginfo("no_such_header") +.fi +.PP +.RE .TP .B trigger(d_date [,t_time [,i_utcflag]]) \fRor\fB trigger(q_datetime [,i_utcflag]) Returns a string suitable for use in a \fBREM\fR command or a diff --git a/src/dosubst.c b/src/dosubst.c index 758dfae2..a6018d9c 100644 --- a/src/dosubst.c +++ b/src/dosubst.c @@ -215,6 +215,33 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, if (!c) { break; } + if (c == '<') { + DynamicBuffer header; + char const *val; + DBufInit(&header); + + while(1) { + c = ParseChar(p, &err, 0); + if (err) { + DBufFree(&header); + return err; + } + if (!c || c == '>') { + break; + } + DBufPutc(&header, c); + } + if (!c) { + Wprint(tr("Warning: Unterminated %%<...> substitution sequence")); + } + err = OK; + val = FindTrigInfo(t, DBufValue(&header)); + DBufFree(&header); + if (val) { + SHIP_OUT(val); + } + continue; + } if (c == '(') { DynamicBuffer orig; DynamicBuffer translated; diff --git a/src/funcs.c b/src/funcs.c index c6989879..a5c68aff 100644 --- a/src/funcs.c +++ b/src/funcs.c @@ -162,6 +162,7 @@ static int FTrigdate (func_info *); static int FTrigdatetime (func_info *); static int FTrigdelta (func_info *); static int FTrigduration (func_info *); +static int FTriginfo (func_info *); static int FTrigeventduration(func_info *); static int FTrigeventstart (func_info *); static int FTrigfrom (func_info *); @@ -326,6 +327,7 @@ BuiltinFunc Func[] = { { "trigeventstart", 0, 0, 0, FTrigeventstart, NULL }, { "trigfrom", 0, 0, 0, FTrigfrom, NULL }, { "trigger", 1, 3, 0, FTrigger, NULL }, + { "triginfo", 1, 1, 1, FTriginfo, NULL }, { "trigpriority", 0, 0, 0, FTrigpriority, NULL }, { "trigrep", 0, 0, 0, FTrigrep, NULL }, { "trigscanfrom", 0, 0, 0, FTrigscanfrom, NULL }, @@ -1620,6 +1622,17 @@ static int FTrigeventduration(func_info *info) return OK; } +static int FTriginfo(func_info *info) +{ + char const *s; + ASSERT_TYPE(0, STR_TYPE); + s = FindTrigInfo(&LastTrigger, ARGSTR(0)); + if (!s) { + return RetStrVal("", info); + } + return RetStrVal(s, info); +} + static int FTrigeventstart(func_info *info) { if (LastTrigger.eventstart == NO_TIME) { diff --git a/src/protos.h b/src/protos.h index 84824db9..dbeb09e5 100644 --- a/src/protos.h +++ b/src/protos.h @@ -282,3 +282,4 @@ void FreeTrigInfoChain(TrigInfo *ti); int AppendTrigInfo(Trigger *t, char const *info); int TrigInfoHeadersAreTheSame(char const *i1, char const *i2); int TrigInfoIsValid(char const *info); +char const *FindTrigInfo(Trigger *t, char const *header); diff --git a/src/trigger.c b/src/trigger.c index 5a0c6841..e7a9e4d4 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -794,3 +794,26 @@ TrigInfoIsValid(char const *info) } return 1; } + +char const * +FindTrigInfo(Trigger *t, char const *header) +{ + TrigInfo *ti; + size_t len; + char const *s; + + if (!t || !header || !*header) return NULL; + + ti = t->infos; + len = strlen(header); + while(ti) { + if (!strncasecmp(ti->info, header, len) && + ti->info[len] == ':') { + s = ti->info + len + 1; + while(isspace(*s)) s++; + return s; + } + ti = ti->next; + } + return NULL; +} diff --git a/tests/test-rem b/tests/test-rem index ee80b1c3..953c8580 100644 --- a/tests/test-rem +++ b/tests/test-rem @@ -647,7 +647,7 @@ EOF # The INFO keyword ../src/remind -pp - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1 -REM Wed INFO "Location: here" INFO "Summary: Nope" MSG Meeting +REM Wed INFO "Location: here" INFO "Summary: Nope" MSG Meeting [triginfo("location")] % % [triginfo("cabbage")] EOF # Invalid info strings diff --git a/tests/test.cmp b/tests/test.cmp index 360f146d..97c2b92b 100644 --- a/tests/test.cmp +++ b/tests/test.cmp @@ -24164,6 +24164,7 @@ trigeventduration trigeventstart trigfrom trigger +triginfo trigpriority trigrep trigscanfrom @@ -24541,6 +24542,7 @@ TRANSLATE "Warning: UNTIL/THROUGH date earlier than SCANFROM date" "" TRANSLATE "Warning: UNTIL/THROUGH date earlier than start date" "" TRANSLATE "Warning: Unable to save ONCE timestamp to %s: %s" "" TRANSLATE "Warning: Unterminated %%(...) substitution sequence" "" +TRANSLATE "Warning: Unterminated %%<...> substitution sequence" "" TRANSLATE "Warning: Unterminated %%{...} substitution sequence" "" TRANSLATE "Warning: Useless use of UNTIL with fully-specified date and no *rep" "" TRANSLATE "Warning: Variable name `%.*s...' truncated to `%.*s'" "" @@ -24659,10 +24661,10 @@ February 2024 29 4 0 Sunday Monday Tuesday Wednesday Thursday Friday Saturday January 31 March 31 -{"date":"2024-02-07","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"body":"Meeting"} -{"date":"2024-02-14","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"body":"Meeting"} -{"date":"2024-02-21","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"body":"Meeting"} -{"date":"2024-02-28","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"body":"Meeting"} +{"date":"2024-02-07","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"rawbody":"Meeting [triginfo(\"location\")] % % [triginfo(\"cabbage\")]","body":"Meeting here Nope "} +{"date":"2024-02-14","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"rawbody":"Meeting [triginfo(\"location\")] % % [triginfo(\"cabbage\")]","body":"Meeting here Nope "} +{"date":"2024-02-21","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"rawbody":"Meeting [triginfo(\"location\")] % % [triginfo(\"cabbage\")]","body":"Meeting here Nope "} +{"date":"2024-02-28","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"rawbody":"Meeting [triginfo(\"location\")] % % [triginfo(\"cabbage\")]","body":"Meeting here Nope "} # rem2ps2 end -stdin-(1): Invalid INFO string: Must be of the form "Header: Value" -stdin-(2): Invalid INFO string: Must be of the form "Header: Value"