diff --git a/src/calendar.c b/src/calendar.c index 46e036b4..c63c898a 100644 --- a/src/calendar.c +++ b/src/calendar.c @@ -61,6 +61,7 @@ typedef struct cal_entry { TimeTrig tt; int nonconst_expr; int if_depth; + TrigInfo *infos; } CalEntry; /* Line-drawing sequences */ @@ -1504,6 +1505,7 @@ static int WriteOneColLine(int col) free(e->raw_text); free(e->filename); if (e->wc_text) free(e->wc_text); + FreeTrigInfoChain(e->infos); free(e); return 1; } @@ -1590,6 +1592,7 @@ static int WriteOneColLine(int col) free(e->raw_text); free(e->filename); if (e->wc_text) free(e->wc_text); + FreeTrigInfoChain(e->infos); free(e); } else { e->wc_pos = ws; @@ -1611,6 +1614,7 @@ static int WriteOneColLine(int col) if (e->wc_text) free(e->wc_text); #endif free(e->raw_text); + FreeTrigInfoChain(e->infos); free(e); return 1; } @@ -1673,6 +1677,7 @@ static int WriteOneColLine(int col) if (e->wc_text) free(e->wc_text); #endif free(e->raw_text); + FreeTrigInfoChain(e->infos); free(e); } else { e->pos = s; @@ -2232,6 +2237,7 @@ static int DoCalRem(ParsePtr p, int col) FreeTrig(&trig); return E_NO_MEM; } + e->infos = NULL; e->nonconst_expr = nonconst_expr; e->if_depth = NumIfs; e->trig = trig; @@ -2252,6 +2258,7 @@ static int DoCalRem(ParsePtr p, int col) if (!e->text || !e->raw_text) { if (e->text) free(e->text); if (e->raw_text) free(e->raw_text); + FreeTrigInfoChain(e->infos); free(e); FreeTrig(&trig); return E_NO_MEM; @@ -2265,12 +2272,17 @@ static int DoCalRem(ParsePtr p, int col) AppendTag(&(e->tags), SynthesizeTag()); } + /* Take over any TrigInfo! */ + e->infos = trig.infos; + trig.infos = NULL; + /* Don't need tags any more */ FreeTrig(&trig); e->duration = tim.duration; e->priority = trig.priority; e->filename = StrDup(FileName); if(!e->filename) { + FreeTrigInfoChain(e->infos); if (e->text) free(e->text); if (e->raw_text) free(e->raw_text); #ifdef REM_USE_WCHAR @@ -2433,6 +2445,20 @@ void WriteJSONTrigger(Trigger const *t, int include_tags, int today) PrintJSONKeyPairInt("addomit", 1); } if (include_tags) { + if (t->infos) { + TrigInfo *ti = t->infos; + printf("\"info\":["); + while (ti) { + printf("\""); + PrintJSONString(ti->info); + printf("\""); + if (ti->next) { + printf(","); + } + ti = ti->next; + } + printf("],"); + } PrintJSONKeyPairString("tags", DBufValue(&(t->tags))); } } @@ -2446,6 +2472,20 @@ static void WriteSimpleEntryProtocol2(CalEntry *e, int today) } PrintJSONKeyPairString("passthru", e->passthru); PrintJSONKeyPairString("tags", DBufValue(&(e->tags))); + if (e->infos) { + TrigInfo *ti = e->infos; + printf("\"info\":["); + while (ti) { + printf("\""); + PrintJSONString(ti->info); + printf("\""); + if (ti->next) { + printf(","); + } + ti = ti->next; + } + printf("],"); + } if (e->duration != NO_TIME) { PrintJSONKeyPairInt("duration", e->duration); } @@ -2576,6 +2616,7 @@ static void WriteSimpleEntries(int col, int dse) free(e->text); free(e->raw_text); free(e->filename); + FreeTrigInfoChain(e->infos); #ifdef REM_USE_WCHAR if (e->wc_text) free(e->wc_text); #endif diff --git a/src/dorem.c b/src/dorem.c index 2dfacb61..c855f732 100644 --- a/src/dorem.c +++ b/src/dorem.c @@ -408,6 +408,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim) tim->duration = NO_TIME; trig->need_wkday = 0; trig->adj_for_last = 0; + trig->infos = NULL; int parsing = 1; while(parsing) { @@ -649,6 +650,15 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim) DBufFree(&buf); break; + case T_Info: + r = ParseQuotedString(s, &buf); + if (r != OK) { + return r; + } + r = AppendTrigInfo(trig, DBufValue(&buf)); + DBufFree(&buf); + if (r) return r; + break; case T_Tag: r = ParseToken(s, &buf); if (r) return r; diff --git a/src/expr.c b/src/expr.c index fec822a8..884a2291 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1877,8 +1877,8 @@ static expr_node * parse_function_call(char const **e, int *r, Var *locals, int } } } + ptr = *e; if (TOKEN_IS(")")) { - ptr = *e; *r = GET_TOKEN(); if (*r != OK) { return free_expr_tree(node); diff --git a/src/main.c b/src/main.c index d0112d20..1a1c9a7d 100644 --- a/src/main.c +++ b/src/main.c @@ -1915,6 +1915,8 @@ void FreeTrig(Trigger *t) { DBufFree(&(t->tags)); + FreeTrigInfoChain(t->infos); + t->infos = NULL; } void @@ -1941,7 +1943,7 @@ ClearLastTriggers(void) LastTrigger.omitfunc[0] = 0; LastTrigger.passthru[0] = 0; DBufFree(&(LastTrigger.tags)); - + LastTrigger.infos = NULL; LastTimeTrig.ttime = NO_TIME; LastTimeTrig.delta = NO_DELTA; LastTimeTrig.rep = NO_REP; diff --git a/src/omit.c b/src/omit.c index ce2755fc..2f1ba0e8 100644 --- a/src/omit.c +++ b/src/omit.c @@ -388,6 +388,7 @@ int DoOmit(ParsePtr p) case T_RemType: case T_Priority: case T_Tag: + case T_Info: case T_Duration: DBufFree(&buf); parsing = 0; @@ -415,7 +416,7 @@ int DoOmit(ParsePtr p) return E_2MANY_LOCALOMIT; } WeekdayOmits |= wd; - if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM; + if (tok.type == T_Tag || tok.type == T_Info || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM; return OK; } @@ -500,7 +501,7 @@ int DoOmit(ParsePtr p) } } - if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM; + if (tok.type == T_Tag || tok.type == T_Info || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM; return OK; } diff --git a/src/protos.h b/src/protos.h index fc2825e0..d59569f6 100644 --- a/src/protos.h +++ b/src/protos.h @@ -276,3 +276,7 @@ void print_escaped_string(FILE *fp, char const *s); void print_escaped_string_helper(FILE *fp, char const *s, int esc_for_remind); void GenerateSysvarTranslationTemplates(void); void TranslationTemplate(char const *msg); +TrigInfo *NewTrigInfo(char const *i); +void FreeTrigInfo(TrigInfo *ti); +void FreeTrigInfoChain(TrigInfo *ti); +int AppendTrigInfo(Trigger *t, char const *info); diff --git a/src/queue.c b/src/queue.c index be46b0ff..0c2dd097 100644 --- a/src/queue.c +++ b/src/queue.c @@ -163,6 +163,7 @@ static void del_reminder(QueuedRem *qid) if (q == qid) { QueueHead = q->next; if (q->text) free((void *) q->text); + FreeTrig(&(q->t)); free(q); return; } @@ -171,6 +172,7 @@ static void del_reminder(QueuedRem *qid) if (q->next == qid) { q->next = q->next->next; if (next->text) free((void *) next->text); + FreeTrig(&(next->t)); free(next); return; } @@ -227,6 +229,10 @@ int QueueReminder(ParsePtr p, Trigger *trig, strcpy(qelem->passthru, trig->passthru); qelem->tt = *tim; qelem->t = *trig; + + /* Take over infos */ + trig->infos = NULL; + DBufInit(&(qelem->t.tags)); DBufPuts(&(qelem->t.tags), DBufValue(&(trig->tags))); if (SynthesizeTags) { @@ -490,6 +496,7 @@ void HandleQueuedReminders(void) DefaultColorB = q->blue; /* Make a COPY of q->t because TriggerReminder can change q->t.typ */ Trigger tcopy = q->t; + if (DaemonJSON) { DynamicBuffer out; DBufInit(&out); diff --git a/src/token.c b/src/token.c index e881580c..889af482 100644 --- a/src/token.c +++ b/src/token.c @@ -71,6 +71,7 @@ Token TokArray[] = { { "in", 2, T_In, 0 }, { "include", 3, T_Include, 0 }, { "includecmd", 10, T_IncludeCmd, 0 }, + { "info", 4, T_Info, 0 }, { "january", 3, T_Month, 0 }, { "july", 3, T_Month, 6 }, { "june", 3, T_Month, 5 }, diff --git a/src/trigger.c b/src/trigger.c index af829bbc..b097331c 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -12,8 +12,9 @@ #include "config.h" #include - #include +#include + #include "types.h" #include "protos.h" #include "globals.h" @@ -668,3 +669,82 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim, return -1; } +/***************************************************************/ +/* */ +/* NewTrigInfo */ +/* */ +/* Create a new TrigInfo object with the specified contents. */ +/* Returns NULL if memory allocation fails. */ +/* */ +/***************************************************************/ +TrigInfo * +NewTrigInfo(char const *i) +{ + TrigInfo *ti = malloc(sizeof(TrigInfo)); + + if (!ti) { + return NULL; + } + ti->next = NULL; + ti->info = StrDup(i); + if (!ti->info) { + free(ti); + return NULL; + } + return ti; +} + +/***************************************************************/ +/* */ +/* FreeTrigInfo */ +/* */ +/* Free a TrigInfo objects. */ +/* */ +/***************************************************************/ +void +FreeTrigInfo(TrigInfo *ti) +{ + if (ti->info) { + free( (void *) ti->info); + } + free(ti); +} + +void +FreeTrigInfoChain(TrigInfo *ti) +{ + TrigInfo *next; + + while(ti) { + next = ti->next; + FreeTrigInfo(ti); + ti = next; + } +} + +/***************************************************************/ +/* */ +/* AppendTrigInfo */ +/* */ +/* Append an info item to a trigger. */ +/* */ +/***************************************************************/ +int +AppendTrigInfo(Trigger *t, char const *info) +{ + TrigInfo *ti = NewTrigInfo(info); + TrigInfo *last = t->infos; + if (!ti) { + return E_NO_MEM; + } + if (!last) { + t->infos = ti; + return OK; + } + while (last->next) { + last = last->next; + } + last->next = ti; + return OK; +} + diff --git a/src/types.h b/src/types.h index 37148e8a..08699166 100644 --- a/src/types.h +++ b/src/types.h @@ -107,6 +107,11 @@ typedef struct var { Value v; } Var; +typedef struct triginfo { + struct triginfo *next; + char const *info; +} TrigInfo; + /* A trigger */ typedef struct { int expired; @@ -138,6 +143,7 @@ typedef struct { char omitfunc[VAR_NAME_LEN+1]; /* OMITFUNC function */ DynamicBuffer tags; char passthru[PASSTHRU_LEN+1]; + TrigInfo *infos; } Trigger; /* A time trigger */ @@ -217,9 +223,9 @@ enum TokTypes T_Date, T_DateTime, T_Day, T_Debug, T_Delta, T_Dumpvars, T_Duration, T_Else, T_Empty, T_EndIf, T_ErrMsg, T_Exit, T_Expr, T_Flush, T_Frename, T_Fset, T_Funset, T_If, T_IfTrig, T_In, - T_Include, T_IncludeCmd, T_IncludeR, T_IncludeSys, T_LastBack, T_LongTime, - T_MaybeUncomputable, T_Month, T_NoQueue, T_Number, T_Omit, T_OmitFunc, - T_Once, T_Ordinal, T_Pop, T_Preserve, T_Priority, T_Push,T_Rem, + T_Include, T_IncludeCmd, T_IncludeR, T_IncludeSys, T_Info, T_LastBack, + T_LongTime, T_MaybeUncomputable, T_Month, T_NoQueue, T_Number, T_Omit, + T_OmitFunc, T_Once, T_Ordinal, T_Pop, T_Preserve, T_Priority, T_Push,T_Rem, T_RemType, T_Rep, T_Scanfrom, T_Sched, T_Set, T_Skip, T_Tag, T_Through, T_Time, T_Translate, T_UnSet, T_Until, T_Warn, T_WkDay, T_Year }; diff --git a/tests/queue1.rem b/tests/queue1.rem index 71242623..b4268c98 100644 --- a/tests/queue1.rem +++ b/tests/queue1.rem @@ -1,6 +1,6 @@ FSET msgprefix(x) "Priority: " + x + "; Filename: " + filename() + ": " REM at 23:56 MSG foo -REM PRIORITY 42 at 23:57 MSG bar -REM PRIORITY 999 at 23:58 MSG quux +REM PRIORITY 42 at 23:57 INFO "A piece of info" MSG bar +REM PRIORITY 999 at 23:58 INFO "info1" INFO "info2" MSG quux DO queue2.rem diff --git a/tests/test-rem b/tests/test-rem index 17cee07c..530dcb69 100644 --- a/tests/test-rem +++ b/tests/test-rem @@ -645,6 +645,11 @@ REM WED AT 11:00 MSG wookie REM WED AT 13:00 MSG blah 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 +EOF + # Languages for i in ../include/lang/??.rem ; do ../src/remind -r -q "-ii=\"$i\"" ../tests/tstlang.rem 1 Feb 2024 13:34 >> ../tests/test.out 2>&1 diff --git a/tests/test.cmp b/tests/test.cmp index f8c54941..99c8fbc9 100644 --- a/tests/test.cmp +++ b/tests/test.cmp @@ -23205,12 +23205,12 @@ Enabling test mode: This is meant for the acceptance test. Do not use --test in production. In test mode, the system time is fixed at 2025-01-06@19:00 NOTE JSONQUEUE -[{"priority":2,"eventstart":"2025-01-06T23:59","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"2025-01-06T23:58","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"2025-01-06T23:57","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"2025-01-06T23:56","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}] +[{"priority":2,"eventstart":"2025-01-06T23:59","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"2025-01-06T23:58","info":["info1","info2"],"time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"2025-01-06T23:57","info":["A piece of info"],"time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"2025-01-06T23:56","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}] NOTE ENDJSONQUEUE Enabling test mode: This is meant for the acceptance test. Do not use --test in production. In test mode, the system time is fixed at 2025-01-06@19:00 -{"response":"queue","queue":[{"priority":2,"eventstart":"2025-01-06T23:59","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"2025-01-06T23:58","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"2025-01-06T23:57","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"2025-01-06T23:56","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}],"command":"QUEUE"} +{"response":"queue","queue":[{"priority":2,"eventstart":"2025-01-06T23:59","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"2025-01-06T23:58","info":["info1","info2"],"time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"2025-01-06T23:57","info":["A piece of info"],"time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"2025-01-06T23:56","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}],"command":"QUEUE"} BANNER % REM 29 MSG One -(2): Trig = Thursday, 29 February, 2024 @@ -23981,6 +23981,7 @@ in inc include includecmd +info last lastday lastworkday @@ -24647,7 +24648,19 @@ TRANSLATE "remaining call frames omitted" "" | | | |qoejkpqw‎ | | | | | | | |blah‎ | | | | +----------+----------+----------+----------+----------+----------+----------+ - Agenda pel dijous, 1 de febrer de 2024: + # translations +{"LANGID":"en"} +# rem2ps2 begin +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"} +# rem2ps2 end +Agenda pel dijous, 1 de febrer de 2024: Language: ca