mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-16 06:18:47 +02:00
Make INFO require "Header: Value" strings; make the "info" element in the JSON output a hash instead of an array.
All checks were successful
Remind unit tests / tests (push) Successful in 36s
All checks were successful
Remind unit tests / tests (push) Successful in 36s
This commit is contained in:
@@ -516,10 +516,28 @@ MOON, etc.)
|
|||||||
If any TAG clauses are present, the \fBtags\fR key will be present and consist
|
If any TAG clauses are present, the \fBtags\fR key will be present and consist
|
||||||
of a comma-separated list of tags.
|
of a comma-separated list of tags.
|
||||||
.TP
|
.TP
|
||||||
.B info [\fIarray\fR]
|
.B info \fR{ \fIhash\fR }
|
||||||
If any INFO clauses are present, the \fBinfo\fR key will be present. Its
|
If any INFO clauses are present, the \fBinfo\fR key will be present. Its
|
||||||
value will be an array of info strings, one for each INFO clause and in
|
value will be a hash of info key-value pairs. Each key is the header
|
||||||
the same order as the INFO clauses.
|
from an INFO string, \fIconverted to all upper-case\fR. The value is the
|
||||||
|
valu from the INFO string.
|
||||||
|
.RS
|
||||||
|
.PP
|
||||||
|
For example, the following REM command:
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
REM INFO "Location: Boardroom" INFO "Summary: None" MSG whatever
|
||||||
|
.fi
|
||||||
|
.PP
|
||||||
|
will produce the following \fBinfo\fR hash:
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
"info" : {
|
||||||
|
"LOCATION" : "Boardroom",
|
||||||
|
"SUMMARY" : "None"
|
||||||
|
},
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.B time \fIt\fR
|
.B time \fIt\fR
|
||||||
If an AT clause was present, this key will contain the time of the AT clause
|
If an AT clause was present, this key will contain the time of the AT clause
|
||||||
|
|||||||
@@ -1365,11 +1365,20 @@ to each distinct REM command.
|
|||||||
.PP
|
.PP
|
||||||
The \fBINFO\fR keyword is similar to \fBTAG\fR but is intended to convey
|
The \fBINFO\fR keyword is similar to \fBTAG\fR but is intended to convey
|
||||||
metadata about an event, such as its location. Back-ends will have their
|
metadata about an event, such as its location. Back-ends will have their
|
||||||
own rules about the format of the \fIinfo_string\fRs and must ignore
|
own rules about which \fIinfo_string\fRs they recognize, and must ignore
|
||||||
\fIinfo_string\fRs they don't recognize. Note that \fBINFO\fR must
|
\fIinfo_string\fRs they don't recognize. Note that \fBINFO\fR must
|
||||||
be followed by a quoted string; you can include newlines in the string
|
be followed by a quoted string; you can include newlines in the string
|
||||||
by supplying them as "\\n".
|
by supplying them as "\\n".
|
||||||
.PP
|
.PP
|
||||||
|
An INFO string \fImust\fR be of the form "Header: Value". The header
|
||||||
|
can consist of any printable character, but cannot contain whitespace.
|
||||||
|
The value can consist of any characters you like. Space may not
|
||||||
|
appear before the colon, but can appear afterwards; such space is not
|
||||||
|
considered to be part of the value. If there is more than one INFO
|
||||||
|
string for a given reminder, there cannot be any duplicate headers.
|
||||||
|
Case is ignored when determining if a header is a duplicate of an
|
||||||
|
existing one.
|
||||||
|
.PP
|
||||||
For example, a hypothetical back-end might let you set the location
|
For example, a hypothetical back-end might let you set the location
|
||||||
and description of a reminder like this:
|
and description of a reminder like this:
|
||||||
.PP
|
.PP
|
||||||
|
|||||||
@@ -409,6 +409,23 @@ void PrintJSONString(char const *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrintJSONStringUC(char const *s)
|
||||||
|
{
|
||||||
|
while (*s) {
|
||||||
|
switch(*s) {
|
||||||
|
case '\b': printf("\\b"); break;
|
||||||
|
case '\f': printf("\\f"); break;
|
||||||
|
case '\n': printf("\\n"); break;
|
||||||
|
case '\r': printf("\\r"); break;
|
||||||
|
case '\t': printf("\\t"); break;
|
||||||
|
case '"': printf("\\\""); break;
|
||||||
|
case '\\': printf("\\\\"); break;
|
||||||
|
default: printf("%c", toupper(*s));
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PrintJSONKeyPairInt(char const *name, int val)
|
void PrintJSONKeyPairInt(char const *name, int val)
|
||||||
{
|
{
|
||||||
printf("\"");
|
printf("\"");
|
||||||
@@ -2354,6 +2371,41 @@ void WriteJSONTimeTrigger(TimeTrig const *tt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
WriteJSONInfoChain(TrigInfo *ti)
|
||||||
|
{
|
||||||
|
printf("\"info\":{");
|
||||||
|
while (ti) {
|
||||||
|
/* Skanky... */
|
||||||
|
char *colon = (char *) strchr(ti->info, ':');
|
||||||
|
char const *value;
|
||||||
|
if (!colon) {
|
||||||
|
/* Should be impossible... */
|
||||||
|
ti = ti->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Terminate the string at the colon */
|
||||||
|
*colon = 0;
|
||||||
|
|
||||||
|
value = colon+1;
|
||||||
|
while(*value && isspace(*value)) {
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
printf("\"");
|
||||||
|
PrintJSONStringUC(ti->info);
|
||||||
|
printf("\":\"");
|
||||||
|
PrintJSONString(value);
|
||||||
|
printf("\"");
|
||||||
|
|
||||||
|
/* Restore the value of the colon */
|
||||||
|
*colon = ':';
|
||||||
|
if (ti->next) {
|
||||||
|
printf(",");
|
||||||
|
}
|
||||||
|
ti = ti->next;
|
||||||
|
}
|
||||||
|
printf("},");
|
||||||
|
}
|
||||||
void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
|
void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
|
||||||
{
|
{
|
||||||
/* wd is an array of days from 0=monday to 6=sunday.
|
/* wd is an array of days from 0=monday to 6=sunday.
|
||||||
@@ -2446,18 +2498,7 @@ void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
|
|||||||
}
|
}
|
||||||
if (include_tags) {
|
if (include_tags) {
|
||||||
if (t->infos) {
|
if (t->infos) {
|
||||||
TrigInfo *ti = t->infos;
|
WriteJSONInfoChain(t->infos);
|
||||||
printf("\"info\":[");
|
|
||||||
while (ti) {
|
|
||||||
printf("\"");
|
|
||||||
PrintJSONString(ti->info);
|
|
||||||
printf("\"");
|
|
||||||
if (ti->next) {
|
|
||||||
printf(",");
|
|
||||||
}
|
|
||||||
ti = ti->next;
|
|
||||||
}
|
|
||||||
printf("],");
|
|
||||||
}
|
}
|
||||||
PrintJSONKeyPairString("tags", DBufValue(&(t->tags)));
|
PrintJSONKeyPairString("tags", DBufValue(&(t->tags)));
|
||||||
}
|
}
|
||||||
@@ -2473,18 +2514,7 @@ static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
|
|||||||
PrintJSONKeyPairString("passthru", e->passthru);
|
PrintJSONKeyPairString("passthru", e->passthru);
|
||||||
PrintJSONKeyPairString("tags", DBufValue(&(e->tags)));
|
PrintJSONKeyPairString("tags", DBufValue(&(e->tags)));
|
||||||
if (e->infos) {
|
if (e->infos) {
|
||||||
TrigInfo *ti = e->infos;
|
WriteJSONInfoChain(e->infos);
|
||||||
printf("\"info\":[");
|
|
||||||
while (ti) {
|
|
||||||
printf("\"");
|
|
||||||
PrintJSONString(ti->info);
|
|
||||||
printf("\"");
|
|
||||||
if (ti->next) {
|
|
||||||
printf(",");
|
|
||||||
}
|
|
||||||
ti = ti->next;
|
|
||||||
}
|
|
||||||
printf("],");
|
|
||||||
}
|
}
|
||||||
if (e->duration != NO_TIME) {
|
if (e->duration != NO_TIME) {
|
||||||
PrintJSONKeyPairInt("duration", e->duration);
|
PrintJSONKeyPairInt("duration", e->duration);
|
||||||
|
|||||||
@@ -280,3 +280,5 @@ TrigInfo *NewTrigInfo(char const *i);
|
|||||||
void FreeTrigInfo(TrigInfo *ti);
|
void FreeTrigInfo(TrigInfo *ti);
|
||||||
void FreeTrigInfoChain(TrigInfo *ti);
|
void FreeTrigInfoChain(TrigInfo *ti);
|
||||||
int AppendTrigInfo(Trigger *t, char const *info);
|
int AppendTrigInfo(Trigger *t, char const *info);
|
||||||
|
int TrigInfoHeadersAreTheSame(char const *i1, char const *i2);
|
||||||
|
int TrigInfoIsValid(char const *info);
|
||||||
|
|||||||
@@ -732,8 +732,16 @@ FreeTrigInfoChain(TrigInfo *ti)
|
|||||||
int
|
int
|
||||||
AppendTrigInfo(Trigger *t, char const *info)
|
AppendTrigInfo(Trigger *t, char const *info)
|
||||||
{
|
{
|
||||||
TrigInfo *ti = NewTrigInfo(info);
|
TrigInfo *ti;
|
||||||
TrigInfo *last = t->infos;
|
TrigInfo *last;
|
||||||
|
|
||||||
|
if (!TrigInfoIsValid(info)) {
|
||||||
|
Eprint("%s", tr("Invalid INFO string: Must be of the form \"Header: Value\""));
|
||||||
|
return E_PARSE_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ti = NewTrigInfo(info);
|
||||||
|
last = t->infos;
|
||||||
if (!ti) {
|
if (!ti) {
|
||||||
return E_NO_MEM;
|
return E_NO_MEM;
|
||||||
}
|
}
|
||||||
@@ -741,10 +749,46 @@ AppendTrigInfo(Trigger *t, char const *info)
|
|||||||
t->infos = ti;
|
t->infos = ti;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
if (TrigInfoHeadersAreTheSame(info, last->info)) {
|
||||||
|
Eprint("%s", tr("Duplicate INFO headers are not permitted"));
|
||||||
|
FreeTrigInfo(ti);
|
||||||
|
return E_PARSE_ERR;
|
||||||
|
}
|
||||||
while (last->next) {
|
while (last->next) {
|
||||||
last = last->next;
|
last = last->next;
|
||||||
|
if (TrigInfoHeadersAreTheSame(info, last->info)) {
|
||||||
|
Eprint("%s", tr("Duplicate INFO headers are not permitted"));
|
||||||
|
FreeTrigInfo(ti);
|
||||||
|
return E_PARSE_ERR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
last->next = ti;
|
last->next = ti;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TrigInfoHeadersAreTheSame(char const *i1, char const *i2)
|
||||||
|
{
|
||||||
|
char const *c1 = strchr(i1, ':');
|
||||||
|
char const *c2 = strchr(i2, ':');
|
||||||
|
if (!c1 || !c2) return 1;
|
||||||
|
if (c1 - i1 != c2 - i2) return 0;
|
||||||
|
if (!strncasecmp(i1, i2, (c1 - i1))) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
TrigInfoIsValid(char const *info)
|
||||||
|
{
|
||||||
|
char const *t;
|
||||||
|
char const *s = strchr(info, ':');
|
||||||
|
if (!s) return 0;
|
||||||
|
if (s == info) return 0;
|
||||||
|
|
||||||
|
t = info;
|
||||||
|
while (t < s) {
|
||||||
|
if (isspace(*t) || iscntrl(*t)) return 0;
|
||||||
|
t++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FSET msgprefix(x) "Priority: " + x + "; Filename: " + filename() + ": "
|
FSET msgprefix(x) "Priority: " + x + "; Filename: " + filename() + ": "
|
||||||
|
|
||||||
REM at 23:56 MSG foo
|
REM at 23:56 MSG foo
|
||||||
REM PRIORITY 42 at 23:57 INFO "A piece of info" MSG bar
|
REM PRIORITY 42 at 23:57 INFO "Info: yuppers" MSG bar
|
||||||
REM PRIORITY 999 at 23:58 INFO "info1" INFO "info2" MSG quux
|
REM PRIORITY 999 at 23:58 INFO "Info2: Nope" INFO "Info3: heh" MSG quux
|
||||||
DO queue2.rem
|
DO queue2.rem
|
||||||
|
|||||||
@@ -650,6 +650,16 @@ EOF
|
|||||||
REM Wed INFO "Location: here" INFO "Summary: Nope" MSG Meeting
|
REM Wed INFO "Location: here" INFO "Summary: Nope" MSG Meeting
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Invalid info strings
|
||||||
|
../src/remind - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
|
||||||
|
REM Thu INFO "Invalid" MSG wookie
|
||||||
|
REM Fri INFO ": foo" MSG blat
|
||||||
|
REM Sun INFO "foo bar baz : blork" MSG uua
|
||||||
|
|
||||||
|
# Duplicate info string
|
||||||
|
REM Sat INFO "Location: here" INFO "location: there" MSG blort
|
||||||
|
EOF
|
||||||
|
|
||||||
# Languages
|
# Languages
|
||||||
for i in ../include/lang/??.rem ; do
|
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
|
../src/remind -r -q "-ii=\"$i\"" ../tests/tstlang.rem 1 Feb 2024 13:34 >> ../tests/test.out 2>&1
|
||||||
|
|||||||
@@ -23205,12 +23205,12 @@ Enabling test mode: This is meant for the acceptance test.
|
|||||||
Do not use --test in production.
|
Do not use --test in production.
|
||||||
In test mode, the system time is fixed at 2025-01-06@19:00
|
In test mode, the system time is fixed at 2025-01-06@19:00
|
||||||
NOTE JSONQUEUE
|
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","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"}]
|
[{"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":{"INFO2":"Nope","INFO3":"heh"},"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":{"INFO":"yuppers"},"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
|
NOTE ENDJSONQUEUE
|
||||||
Enabling test mode: This is meant for the acceptance test.
|
Enabling test mode: This is meant for the acceptance test.
|
||||||
Do not use --test in production.
|
Do not use --test in production.
|
||||||
In test mode, the system time is fixed at 2025-01-06@19:00
|
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","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"}
|
{"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":{"INFO2":"Nope","INFO3":"heh"},"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":{"INFO":"yuppers"},"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 %
|
BANNER %
|
||||||
REM 29 MSG One
|
REM 29 MSG One
|
||||||
-(2): Trig = Thursday, 29 February, 2024
|
-(2): Trig = Thursday, 29 February, 2024
|
||||||
@@ -24503,6 +24503,7 @@ TRANSLATE "Cannot open `%s' for writing: %s" ""
|
|||||||
TRANSLATE "Cannot stat %s - not running as daemon!" ""
|
TRANSLATE "Cannot stat %s - not running as daemon!" ""
|
||||||
TRANSLATE "Cannot use AT clause in multitrig() function" ""
|
TRANSLATE "Cannot use AT clause in multitrig() function" ""
|
||||||
TRANSLATE "Do not use ["["]] around expression in SET command" ""
|
TRANSLATE "Do not use ["["]] around expression in SET command" ""
|
||||||
|
TRANSLATE "Duplicate INFO headers are not permitted" ""
|
||||||
TRANSLATE "Error: THROUGH date earlier than start date" ""
|
TRANSLATE "Error: THROUGH date earlier than start date" ""
|
||||||
TRANSLATE "Executing `%s' for INCLUDECMD and caching as `%s'" ""
|
TRANSLATE "Executing `%s' for INCLUDECMD and caching as `%s'" ""
|
||||||
TRANSLATE "Found cached directory listing for `%s'" ""
|
TRANSLATE "Found cached directory listing for `%s'" ""
|
||||||
@@ -24510,6 +24511,7 @@ TRANSLATE "Function `%s' defined at %s:%d should take %d argument%s, but actuall
|
|||||||
TRANSLATE "Function `%s' redefined (previously defined at %s:%d)" ""
|
TRANSLATE "Function `%s' redefined (previously defined at %s:%d)" ""
|
||||||
TRANSLATE "GetValidHebDate: Bad adarbehave value %d" ""
|
TRANSLATE "GetValidHebDate: Bad adarbehave value %d" ""
|
||||||
TRANSLATE "In" ""
|
TRANSLATE "In" ""
|
||||||
|
TRANSLATE "Invalid INFO string: Must be of the form \"Header: Value\"" ""
|
||||||
TRANSLATE "Invalid translation: Both original and translated must have the same printf-style formatting sequences in the same order." ""
|
TRANSLATE "Invalid translation: Both original and translated must have the same printf-style formatting sequences in the same order." ""
|
||||||
TRANSLATE "Missing REM type; assuming MSG" ""
|
TRANSLATE "Missing REM type; assuming MSG" ""
|
||||||
TRANSLATE "No Adar A in %d" ""
|
TRANSLATE "No Adar A in %d" ""
|
||||||
@@ -24655,11 +24657,16 @@ February 2024 29 4 0
|
|||||||
Sunday Monday Tuesday Wednesday Thursday Friday Saturday
|
Sunday Monday Tuesday Wednesday Thursday Friday Saturday
|
||||||
January 31
|
January 31
|
||||||
March 31
|
March 31
|
||||||
{"date":"2024-02-07","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,"body":"Meeting"}
|
||||||
{"date":"2024-02-14","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-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-28","filename":"-","lineno":1,"info":{"LOCATION":"here","SUMMARY":"Nope"},"wd":["Wednesday"],"priority":5000,"body":"Meeting"}
|
||||||
# rem2ps2 end
|
# 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"
|
||||||
|
-stdin-(3): Invalid INFO string: Must be of the form "Header: Value"
|
||||||
|
-stdin-(6): Duplicate INFO headers are not permitted
|
||||||
|
No reminders.
|
||||||
Agenda pel dijous, 1 de febrer de 2024:
|
Agenda pel dijous, 1 de febrer de 2024:
|
||||||
|
|
||||||
Language: ca
|
Language: ca
|
||||||
|
|||||||
Reference in New Issue
Block a user