mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-16 14:28:40 +02:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
143f1d6144 | ||
|
|
358f6c9497 | ||
|
|
ca26544be8 | ||
|
|
5ceffddd5b | ||
|
|
8e3ddb96b3 | ||
|
|
377de36b35 | ||
|
|
4395e2f7ed | ||
|
|
1d0cc31b10 | ||
|
|
4b4b2ddcd4 | ||
|
|
3c9b5b786e | ||
|
|
08f1bea6ce | ||
|
|
a2cc5943e0 | ||
|
|
895ac6f0f7 | ||
|
|
759ca0253e | ||
|
|
0ca368c8d9 | ||
|
|
a467cc1b84 | ||
|
|
c65fd826a5 | ||
|
|
bd6f4e1b43 | ||
|
|
169520914f | ||
|
|
a163a0c446 | ||
|
|
295aeb0ed8 | ||
|
|
9b2fdad56c | ||
|
|
7a1184d3c5 | ||
|
|
b036244316 | ||
|
|
5ad5366e8a |
2
configure
vendored
2
configure
vendored
@@ -3991,7 +3991,7 @@ _ACEOF
|
||||
fi
|
||||
done
|
||||
|
||||
VERSION=03.03.07
|
||||
VERSION=03.03.08
|
||||
|
||||
ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h"
|
||||
|
||||
|
||||
@@ -75,6 +75,6 @@ if test "$GCC" = yes; then
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale)
|
||||
VERSION=03.03.07
|
||||
VERSION=03.03.08
|
||||
AC_SUBST(VERSION)
|
||||
AC_OUTPUT(src/Makefile www/Makefile src/version.h)
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
CHANGES TO REMIND
|
||||
|
||||
* VERSION 3.3 Patch 8 - 2021-09-13
|
||||
|
||||
- NEW FEATURE: remind: Add INCLUDECMD command
|
||||
|
||||
- NEW FEATURE: remind: Add shellescape() built-in function
|
||||
|
||||
- BUG FIX: tkremind: TkRemind would sometimes fill in incorrect initial
|
||||
values for the reminder-editing form if you clicked on a TkRemind-created
|
||||
reminder to edit it. This has been fixed.
|
||||
|
||||
- BUG FIX: tkremind: Get back better error messages from Remind if you
|
||||
try to create a reminder with an invalid date specification.
|
||||
|
||||
- BUG FIX: remind: Catch integer overflow if we try to evaluate $IntMin * -1
|
||||
|
||||
- DOC UPDATES: remind: Minor man page fixes
|
||||
|
||||
* VERSION 3.3 Patch 7 - 2021-05-10
|
||||
|
||||
- MINOR FIX: Refuse to run "make test" as root --- it would fail
|
||||
|
||||
65
man/remind.1
65
man/remind.1
@@ -31,7 +31,8 @@ ignore them for now and skip to the section "REMINDER FILES".
|
||||
.B \-n
|
||||
The \fB\-n\fR option causes \fBRemind\fR to print the \fBnext\fR occurrence
|
||||
of each reminder in a simple calendar format. You can sort this by
|
||||
date by piping the output through \fBsort(1)\fR.
|
||||
date by piping the output through \fBsort(1)\fR. Note that the \fB\-n\fR
|
||||
option causes any \fB\-g\fR option to be \fIignored\fR.
|
||||
.TP
|
||||
.B \-j\fR[\fIn\fR]
|
||||
Runs \fBRemind\fR in "purge" mode to get rid of expired reminders.
|
||||
@@ -250,6 +251,7 @@ The optional \fBa\fR and \fBd\fR characters specify the sort order
|
||||
(ascending or descending) for the date, time and priority fields. See
|
||||
the section "SORTING REMINDERS" for more information.
|
||||
|
||||
Note that \fB\-g\fR is \fIignored\fR if you use the \fB\-n\fR option.
|
||||
.TP
|
||||
\fB\-b\fR[\fIn\fR]
|
||||
Set the time format for the calendar and simple-calendar outputs. \fIN\fR
|
||||
@@ -349,7 +351,7 @@ it as YYYY-MM-DD or YYYY/MM/DD. You can even supply a date and
|
||||
time on the command line as one argument: YYYY-MM-DD@HH:MM.
|
||||
.PP
|
||||
In addition, you can supply a \fIrepeat\fR parameter, which has the
|
||||
form *\fInum\fR. This causes \fBRemind\fR to be run \fInum\fR times,
|
||||
form *\fIrep\fR. This causes \fBRemind\fR to be run \fIrep\fR times,
|
||||
with the date incrementing on each iteration. You may have to enclose
|
||||
the parameter in quotes to avoid shell expansion. See the subsection
|
||||
"Repeated Execution" in the section "CALENDAR MODE" for more
|
||||
@@ -1578,7 +1580,49 @@ features. It will not read a file that is group- or world-writable.
|
||||
It will not run set-uid. If it reads a file you don't own, it will
|
||||
disable RUN and the shell() function. And if it is run as \fIroot\fR,
|
||||
it will only read files owned by \fIroot\fR.
|
||||
|
||||
.SH THE INCLUDECMD COMMAND
|
||||
.PP
|
||||
\fBRemind\fR allows you to execute a shell command and evaluate the
|
||||
output of that command as if it were an included file. For example,
|
||||
you could have scripts that extract reminders out of a database and print
|
||||
them on stdout as REM commands. Here is an example:
|
||||
.PP
|
||||
.nf
|
||||
INCLUDECMD extract_reminders_for dfs
|
||||
.fi
|
||||
.PP
|
||||
We assume that the command "extract_reminders_for" extracts reminders out
|
||||
of a central database for the named user. Another use-case of INCLUDECMD
|
||||
is if you have your reminders stored in a file in some non-Remind format;
|
||||
you can write a command that transforms them to Remind format and then
|
||||
Remind can "include" the file with an appropriate INCLUDECMD command.
|
||||
.PP
|
||||
Note that if RUN is disabled, then INCLUDECMD will fail with the error
|
||||
message "RUN disabled"
|
||||
.PP
|
||||
INCLUDECMD passes the rest of the line to \fBpopen\fR(3), meaning that
|
||||
the command is executed by the shell. As such, shell metacharacters
|
||||
may need escaping or arguments quoting, depending on what you're trying
|
||||
to do. Remind itself does not perform any modification of the command
|
||||
line (apart from the normal [expr] expression-pasting mechanism).
|
||||
.PP
|
||||
If the command passed to INCLUDECMD begins with an exclamation mark "!",
|
||||
then Remind disables \fBRUN\fR for the output of the command. If you are
|
||||
running a command whose output you don't quite trust, you should
|
||||
prefix it with "!" so that any RUN commands it emits fail.
|
||||
.PP
|
||||
An \fBINCLUDECMD\fR command counts towards the INCLUDE nesting depth.
|
||||
For any given Remind run, a given INCLUDECMD command is only executed
|
||||
once and the results are cached. For example, if you generate a
|
||||
calendar, each unique INCLUDECMD command is run just once, not once
|
||||
for each day of the produced calendar. "Uniqueness" is determined by
|
||||
looking at the command that will be passed to the shell, so if (for example)
|
||||
your INCLUDECMD uses expression-pasting that results in differences depending
|
||||
on the value of \fBtoday()\fR, then each \fIunique\fR version of the
|
||||
command will be executed once.
|
||||
.PP
|
||||
|
||||
.SH THE BANNER COMMAND
|
||||
.PP
|
||||
When \fBRemind\fR first issues a reminder, it prints a message like this:
|
||||
@@ -2851,6 +2895,23 @@ If \fImaxlen\fR is specified, then \fBshell()\fR returns the first
|
||||
\fImaxlen\fR is specified as a negative number, then \fIall\fR the
|
||||
output from \fIcmd\fR is returned.
|
||||
.RE
|
||||
.TP
|
||||
.B shellescape(s_str)
|
||||
Returns \fIstr\fR with all shell metacharacters such as " ", "*", etc
|
||||
escaped with a backslash. For example:
|
||||
.PP
|
||||
.nf
|
||||
SET a shellescape("a b*? c&d$e")
|
||||
.fi
|
||||
.RS
|
||||
.PP
|
||||
will set \fBa\fR to:
|
||||
.RE
|
||||
.PP
|
||||
.nf
|
||||
"a\\ b\\*\\?\\ c\\&d\\$e"
|
||||
.fi
|
||||
|
||||
.TP
|
||||
.B slide(d_start, i_amt [,s_wkday...])
|
||||
This function is the inverse of \fBnonomitted\fR. It adds \fIamt\fR
|
||||
|
||||
@@ -1005,8 +1005,8 @@ proc FillCalWindow {} {
|
||||
ConfigureCalWindow $monthName $year $firstWkday $daysInMonth
|
||||
set offset [CalEntryOffset $firstWkday]
|
||||
|
||||
set fntag "x"
|
||||
while { [gets $file line] >= 0 } {
|
||||
set fntag "x"
|
||||
# Ignore unless begins with left brace
|
||||
if { ! [string match "\{*" $line]} {
|
||||
continue
|
||||
@@ -1017,7 +1017,11 @@ proc FillCalWindow {} {
|
||||
}
|
||||
|
||||
if {[dict exists $obj filename]} {
|
||||
set fntag [string cat "FILE_" [dict get $obj lineno] "_" [dict get $obj filename]]
|
||||
set fname [dict get $obj filename]
|
||||
# Don't make INCLUDECMD output editable
|
||||
if {![string match "*|" $fname]} {
|
||||
set fntag [string cat "FILE_" [dict get $obj lineno] "_" $fname]
|
||||
}
|
||||
}
|
||||
|
||||
set date [dict get $obj date]
|
||||
@@ -1087,13 +1091,17 @@ proc FillCalWindow {} {
|
||||
continue
|
||||
}
|
||||
.cal.t$n configure -state normal
|
||||
if {[regexp {TKTAG([0-9]+)} $tag all tagno]} {
|
||||
if {[regexp {TKTAG([0-9]+)} $tag all tagno] && "$fntag" != "x"} {
|
||||
.cal.t$n insert end [string trim $stuff] [list REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag]
|
||||
.cal.t$n tag bind "TKTAG$tagno" <Enter> "TaggedEnter .cal.t$n"
|
||||
.cal.t$n tag bind "TKTAG$tagno" <Leave> "TaggedLeave .cal.t$n"
|
||||
set TagToObj(TKTAG$tagno) $obj
|
||||
} else {
|
||||
.cal.t$n insert end [string trim $stuff] [list REM $extratags $fntag]
|
||||
if {"$fntag" == "x" } {
|
||||
.cal.t$n insert end [string trim $stuff] [list REM $extratags]
|
||||
} else {
|
||||
.cal.t$n insert end [string trim $stuff] [list REM $extratags $fntag]
|
||||
}
|
||||
}
|
||||
.cal.t$n insert end "\n"
|
||||
.cal.t$n configure -state disabled
|
||||
@@ -1837,6 +1845,7 @@ proc ModifyDay {d firstDay} {
|
||||
set oldFocus [focus]
|
||||
while {1} {
|
||||
grab .mod
|
||||
raise .mod
|
||||
focus .mod.entry
|
||||
set ModifyDialogResult -1
|
||||
tkwait variable ModifyDialogResult
|
||||
@@ -1983,7 +1992,7 @@ proc CreateReminder {w} {
|
||||
|
||||
# Check it out!
|
||||
global Remind
|
||||
set f [open "|$Remind -arq -e -" r+]
|
||||
set f [open "|$Remind -arq -e - 2>&1" r+]
|
||||
puts $f "BANNER %"
|
||||
puts $f "$rem MSG %"
|
||||
puts $f "MSG %_%_%_%_"
|
||||
@@ -2001,13 +2010,17 @@ proc CreateReminder {w} {
|
||||
return $rem
|
||||
}
|
||||
|
||||
# We used to return YYYY-MM-DD, but reverted to
|
||||
# day monthname year because this lets Remind produce
|
||||
# much better error messages.
|
||||
proc consolidate {y m d} {
|
||||
global MonthNames
|
||||
if {![regexp {^[0-9]+$} $m]} {
|
||||
set m [lsearch -exact $MonthNames $m]
|
||||
incr m
|
||||
}
|
||||
return [format "%04d-%02d-%02d" $y $m $d]
|
||||
set mname [lindex $MonthNames [expr $m-1]]
|
||||
return "$d $mname $y"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -2804,7 +2817,7 @@ proc ReadTaggedOptions { tag date } {
|
||||
lappend ans -text-day2 $d
|
||||
}
|
||||
if {[dict exists $obj m]} {
|
||||
set mm [dict get $obj m]
|
||||
set m [dict get $obj m]
|
||||
set mm [string trimleft $m 0]
|
||||
lappend ans -text-mon1 [lindex $MonthNames [expr $mm -1]]
|
||||
lappend ans -text-mon2 [lindex $MonthNames [expr $mm -1]]
|
||||
@@ -3194,6 +3207,7 @@ proc EditTaggedReminder { w } {
|
||||
tkwait visibility .mod
|
||||
set oldFocus [focus]
|
||||
while {1} {
|
||||
raise .mod
|
||||
grab .mod
|
||||
focus .mod.entry
|
||||
set ModifyDialogResult -1
|
||||
|
||||
@@ -1239,6 +1239,7 @@ static void GenerateCalEntries(int col)
|
||||
case T_Else: r=DoElse(&p); break;
|
||||
case T_EndIf: r=DoEndif(&p); break;
|
||||
case T_Include: r=DoInclude(&p); break;
|
||||
case T_IncludeCmd: r=DoIncludeCmd(&p); break;
|
||||
case T_Exit: DoExit(&p); break;
|
||||
case T_Set: r=DoSet(&p); break;
|
||||
case T_Fset: r=DoFset(&p); break;
|
||||
|
||||
22
src/dorem.c
22
src/dorem.c
@@ -24,10 +24,6 @@
|
||||
#include "protos.h"
|
||||
#include "expr.h"
|
||||
|
||||
/* Define the shell characters not to escape */
|
||||
static char const DontEscapeMe[] =
|
||||
"1234567890_-=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@.,";
|
||||
|
||||
static int ParseTimeTrig (ParsePtr s, TimeTrig *tim, int save_in_globals);
|
||||
static int ParseLocalOmit (ParsePtr s, Trigger *t);
|
||||
static int ParseScanFrom (ParsePtr s, Trigger *t, int type);
|
||||
@@ -1230,24 +1226,16 @@ int DoMsgCommand(char const *cmd, char const *msg)
|
||||
DynamicBuffer execBuffer;
|
||||
|
||||
DynamicBuffer buf;
|
||||
char const *s;
|
||||
|
||||
DBufInit(&buf);
|
||||
DBufInit(&execBuffer);
|
||||
|
||||
/* Escape shell characters in msg INCLUDING WHITESPACE! */
|
||||
for (s=msg; *s; s++) {
|
||||
if (isspace(*s) || !strchr(DontEscapeMe, *s)) {
|
||||
if (DBufPutc(&buf, '\\') != OK) {
|
||||
r = E_NO_MEM;
|
||||
goto finished;
|
||||
}
|
||||
}
|
||||
if (DBufPutc(&buf, *s) != OK) {
|
||||
r = E_NO_MEM;
|
||||
goto finished;
|
||||
}
|
||||
/* Escape shell characters in msg */
|
||||
if (ShellEscape(msg, &buf) != OK) {
|
||||
r = E_NO_MEM;
|
||||
goto finished;
|
||||
}
|
||||
|
||||
msg = DBufValue(&buf);
|
||||
|
||||
/* Do "%s" substitution */
|
||||
|
||||
@@ -940,6 +940,11 @@ static int Multiply(void)
|
||||
}
|
||||
|
||||
if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
|
||||
/* Prevent floating-point exception */
|
||||
if ((v2.v.val == -1 && v1.v.val == INT_MIN) ||
|
||||
(v1.v.val == -1 && v2.v.val == INT_MIN)) {
|
||||
return E_2HIGH;
|
||||
}
|
||||
int old = v1.v.val;
|
||||
v1.v.val *= v2.v.val;
|
||||
if (v2.v.val != 0) {
|
||||
|
||||
263
src/files.c
263
src/files.c
@@ -40,8 +40,9 @@
|
||||
#include "err.h"
|
||||
|
||||
|
||||
/* Convenient macro for closing files */
|
||||
/* Convenient macros for closing files */
|
||||
#define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
|
||||
#define PCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL))
|
||||
|
||||
/* Define the structures needed by the file caching system */
|
||||
typedef struct cache {
|
||||
@@ -91,12 +92,12 @@ static FILE *fp;
|
||||
static IncludeStruct IStack[INCLUDE_NEST];
|
||||
static int IStackPtr = 0;
|
||||
|
||||
static int ReadLineFromFile (void);
|
||||
static int CacheFile (char const *fname);
|
||||
static int ReadLineFromFile (int use_pclose);
|
||||
static int CacheFile (char const *fname, int use_pclose);
|
||||
static void DestroyCache (CachedFile *cf);
|
||||
static int CheckSafety (void);
|
||||
static int PopFile (void);
|
||||
|
||||
static int IncludeCmd(char const *);
|
||||
static void OpenPurgeFile(char const *fname, char const *mode)
|
||||
{
|
||||
DynamicBuffer fname_buf;
|
||||
@@ -167,7 +168,7 @@ int ReadLine(void)
|
||||
}
|
||||
|
||||
/* Not cached. Read from the file. */
|
||||
return ReadLineFromFile();
|
||||
return ReadLineFromFile(0);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
@@ -177,7 +178,7 @@ int ReadLine(void)
|
||||
/* Read a line from the file pointed to by fp. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int ReadLineFromFile(void)
|
||||
static int ReadLineFromFile(int use_pclose)
|
||||
{
|
||||
int l;
|
||||
char copy_buffer[4096];
|
||||
@@ -200,7 +201,11 @@ static int ReadLineFromFile(void)
|
||||
return E_IO_ERR;
|
||||
}
|
||||
if (feof(fp)) {
|
||||
FCLOSE(fp);
|
||||
if (use_pclose) {
|
||||
PCLOSE(fp);
|
||||
} else {
|
||||
FCLOSE(fp);
|
||||
}
|
||||
if ((DBufLen(&buf) == 0) &&
|
||||
(DBufLen(&LineBuffer) == 0) && PurgeMode) {
|
||||
if (PurgeFP != NULL && PurgeFP != stdout) fclose(PurgeFP);
|
||||
@@ -248,7 +253,11 @@ static int ReadLineFromFile(void)
|
||||
if (PurgeFP != stdout) fclose(PurgeFP);
|
||||
PurgeFP = NULL;
|
||||
}
|
||||
FCLOSE(fp);
|
||||
if (use_pclose) {
|
||||
PCLOSE(fp);
|
||||
} else {
|
||||
FCLOSE(fp);
|
||||
}
|
||||
DBufFree(&LineBuffer);
|
||||
CurLine = DBufValue(&LineBuffer);
|
||||
}
|
||||
@@ -282,9 +291,6 @@ int OpenFile(char const *fname)
|
||||
PurgeFP = NULL;
|
||||
}
|
||||
|
||||
/* Assume we own the file for now */
|
||||
RunDisabled &= ~RUN_NOTOWNER;
|
||||
|
||||
/* If it's in the cache, get it from there. */
|
||||
|
||||
while (h) {
|
||||
@@ -297,7 +303,9 @@ int OpenFile(char const *fname)
|
||||
LineNo = 0;
|
||||
if (!h->ownedByMe) {
|
||||
RunDisabled |= RUN_NOTOWNER;
|
||||
}
|
||||
} else {
|
||||
RunDisabled &= ~RUN_NOTOWNER;
|
||||
}
|
||||
if (FileName) return OK; else return E_NO_MEM;
|
||||
}
|
||||
h = h->next;
|
||||
@@ -306,6 +314,7 @@ int OpenFile(char const *fname)
|
||||
/* If it's a dash, then it's stdin */
|
||||
if (!strcmp(fname, "-")) {
|
||||
fp = stdin;
|
||||
RunDisabled &= ~RUN_NOTOWNER;
|
||||
if (PurgeMode) {
|
||||
PurgeFP = stdout;
|
||||
}
|
||||
@@ -325,7 +334,7 @@ int OpenFile(char const *fname)
|
||||
CLine = NULL;
|
||||
if (ShouldCache) {
|
||||
LineNo = 0;
|
||||
r = CacheFile(fname);
|
||||
r = CacheFile(fname, 0);
|
||||
if (r == OK) {
|
||||
fp = NULL;
|
||||
CLine = CachedFiles->cache;
|
||||
@@ -353,7 +362,7 @@ int OpenFile(char const *fname)
|
||||
/* Returns an indication of success or failure. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int CacheFile(char const *fname)
|
||||
static int CacheFile(char const *fname, int use_pclose)
|
||||
{
|
||||
int r;
|
||||
CachedFile *cf;
|
||||
@@ -368,14 +377,22 @@ static int CacheFile(char const *fname)
|
||||
cf = NEW(CachedFile);
|
||||
if (!cf) {
|
||||
ShouldCache = 0;
|
||||
FCLOSE(fp);
|
||||
if (use_pclose) {
|
||||
PCLOSE(fp);
|
||||
} else {
|
||||
FCLOSE(fp);
|
||||
}
|
||||
return E_NO_MEM;
|
||||
}
|
||||
cf->cache = NULL;
|
||||
cf->filename = StrDup(fname);
|
||||
if (!cf->filename) {
|
||||
ShouldCache = 0;
|
||||
FCLOSE(fp);
|
||||
if (use_pclose) {
|
||||
PCLOSE(fp);
|
||||
} else {
|
||||
FCLOSE(fp);
|
||||
}
|
||||
free(cf);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
@@ -388,11 +405,15 @@ static int CacheFile(char const *fname)
|
||||
|
||||
/* Read the file */
|
||||
while(fp) {
|
||||
r = ReadLineFromFile();
|
||||
r = ReadLineFromFile(use_pclose);
|
||||
if (r) {
|
||||
DestroyCache(cf);
|
||||
ShouldCache = 0;
|
||||
FCLOSE(fp);
|
||||
if (use_pclose) {
|
||||
PCLOSE(fp);
|
||||
} else {
|
||||
FCLOSE(fp);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
/* Skip blank chars */
|
||||
@@ -406,7 +427,11 @@ static int CacheFile(char const *fname)
|
||||
DBufFree(&LineBuffer);
|
||||
DestroyCache(cf);
|
||||
ShouldCache = 0;
|
||||
FCLOSE(fp);
|
||||
if (use_pclose) {
|
||||
PCLOSE(fp);
|
||||
} else {
|
||||
FCLOSE(fp);
|
||||
}
|
||||
return E_NO_MEM;
|
||||
}
|
||||
cl = cf->cache;
|
||||
@@ -416,7 +441,11 @@ static int CacheFile(char const *fname)
|
||||
DBufFree(&LineBuffer);
|
||||
DestroyCache(cf);
|
||||
ShouldCache = 0;
|
||||
FCLOSE(fp);
|
||||
if (use_pclose) {
|
||||
PCLOSE(fp);
|
||||
} else {
|
||||
FCLOSE(fp);
|
||||
}
|
||||
return E_NO_MEM;
|
||||
}
|
||||
cl = cl->next;
|
||||
@@ -428,7 +457,11 @@ static int CacheFile(char const *fname)
|
||||
if (!cl->text) {
|
||||
DestroyCache(cf);
|
||||
ShouldCache = 0;
|
||||
FCLOSE(fp);
|
||||
if (use_pclose) {
|
||||
PCLOSE(fp);
|
||||
} else {
|
||||
FCLOSE(fp);
|
||||
}
|
||||
return E_NO_MEM;
|
||||
}
|
||||
}
|
||||
@@ -471,9 +504,6 @@ static int PopFile(void)
|
||||
{
|
||||
IncludeStruct *i;
|
||||
|
||||
/* Assume we own the file for now */
|
||||
RunDisabled &= ~RUN_NOTOWNER;
|
||||
|
||||
if (!Hush && NumIfs) Eprint("%s", ErrMsg[E_MISS_ENDIF]);
|
||||
if (!IStackPtr) return E_EOF;
|
||||
i = &IStack[IStackPtr-1];
|
||||
@@ -500,8 +530,10 @@ static int PopFile(void)
|
||||
STRSET(FileName, i->filename);
|
||||
if (!i->ownedByMe) {
|
||||
RunDisabled |= RUN_NOTOWNER;
|
||||
} else {
|
||||
RunDisabled &= ~RUN_NOTOWNER;
|
||||
}
|
||||
if (!CLine && (i->offset != -1L)) {
|
||||
if (!CLine && (i->offset != -1L || !strcmp(i->filename, "-"))) {
|
||||
/* We must open the file, then seek to specified position */
|
||||
if (strcmp(i->filename, "-")) {
|
||||
fp = fopen(i->filename, "r");
|
||||
@@ -544,6 +576,64 @@ int DoInclude(ParsePtr p)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* DoIncludeCmd */
|
||||
/* */
|
||||
/* The INCLUDECMD command. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
int DoIncludeCmd(ParsePtr p)
|
||||
{
|
||||
DynamicBuffer buf;
|
||||
int r;
|
||||
int ch;
|
||||
char append_buf[2];
|
||||
int seen_nonspace = 0;
|
||||
|
||||
append_buf[1] = 0;
|
||||
|
||||
DBufInit(&buf);
|
||||
|
||||
while(1) {
|
||||
ch = ParseChar(p, &r, 0);
|
||||
if (r) {
|
||||
DBufFree(&buf);
|
||||
return r;
|
||||
}
|
||||
if (!ch) {
|
||||
break;
|
||||
}
|
||||
if (isspace(ch) && !seen_nonspace) {
|
||||
continue;
|
||||
}
|
||||
seen_nonspace = 1;
|
||||
/* Convert \n to ' ' to better handle line continuation */
|
||||
if (ch == '\n') {
|
||||
ch = ' ';
|
||||
}
|
||||
append_buf[0] = (char) ch;
|
||||
if (DBufPuts(&buf, append_buf) != OK) {
|
||||
DBufFree(&buf);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (RunDisabled) {
|
||||
DBufFree(&buf);
|
||||
return E_RUN_DISABLED;
|
||||
}
|
||||
|
||||
if ( (r=IncludeCmd(DBufValue(&buf))) ) {
|
||||
DBufFree(&buf);
|
||||
return r;
|
||||
}
|
||||
DBufFree(&buf);
|
||||
NumIfs = 0;
|
||||
IfFlags = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef HAVE_GLOB
|
||||
static int SetupGlobChain(char const *dirname, IncludeStruct *i)
|
||||
{
|
||||
@@ -664,6 +754,127 @@ static int SetupGlobChain(char const *dirname, IncludeStruct *i)
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* IncludeCmd */
|
||||
/* */
|
||||
/* Process the INCLUDECMD command - actually do the command */
|
||||
/* inclusion. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int IncludeCmd(char const *cmd)
|
||||
{
|
||||
IncludeStruct *i;
|
||||
DynamicBuffer buf;
|
||||
FILE *fp2;
|
||||
int r;
|
||||
CachedFile *h;
|
||||
char const *fname;
|
||||
int old_flag;
|
||||
|
||||
FreshLine = 1;
|
||||
if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE;
|
||||
i = &IStack[IStackPtr];
|
||||
|
||||
/* Use "cmd|" as the filename */
|
||||
DBufInit(&buf);
|
||||
if (DBufPuts(&buf, cmd) != OK) {
|
||||
DBufFree(&buf);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
if (DBufPuts(&buf, "|") != OK) {
|
||||
DBufFree(&buf);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
fname = DBufValue(&buf);
|
||||
|
||||
if (FileName) {
|
||||
i->filename = StrDup(FileName);
|
||||
if (!i->filename) {
|
||||
DBufFree(&buf);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
} else {
|
||||
i->filename = NULL;
|
||||
}
|
||||
i->ownedByMe = 1;
|
||||
i->LineNo = LineNo;
|
||||
i->NumIfs = NumIfs;
|
||||
i->IfFlags = IfFlags;
|
||||
i->CLine = CLine;
|
||||
i->offset = -1L;
|
||||
i->chain = NULL;
|
||||
if (fp) {
|
||||
i->offset = ftell(fp);
|
||||
FCLOSE(fp);
|
||||
}
|
||||
IStackPtr++;
|
||||
|
||||
/* If the file is cached, use it */
|
||||
h = CachedFiles;
|
||||
while(h) {
|
||||
if (!strcmp(fname, h->filename)) {
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Reading command `%s': Found in cache\n", fname);
|
||||
}
|
||||
CLine = h->cache;
|
||||
STRSET(FileName, fname);
|
||||
DBufFree(&buf);
|
||||
LineNo = 0;
|
||||
if (!h->ownedByMe) {
|
||||
RunDisabled |= RUN_NOTOWNER;
|
||||
} else {
|
||||
RunDisabled &= ~RUN_NOTOWNER;
|
||||
}
|
||||
if (FileName) return OK; else return E_NO_MEM;
|
||||
}
|
||||
h = h->next;
|
||||
}
|
||||
|
||||
if (DebugFlag & DB_TRACE_FILES) {
|
||||
fprintf(ErrFp, "Executing `%s' for INCLUDECMD and caching as `%s'\n",
|
||||
cmd, fname);
|
||||
}
|
||||
|
||||
/* Not found in cache */
|
||||
|
||||
/* If cmd starts with !, then disable RUN within the cmd output */
|
||||
if (cmd[0] == '!') {
|
||||
fp2 = popen(cmd+1, "r");
|
||||
} else {
|
||||
fp2 = popen(cmd, "r");
|
||||
}
|
||||
if (!fp2) {
|
||||
DBufFree(&buf);
|
||||
return E_CANT_OPEN;
|
||||
}
|
||||
fp = fp2;
|
||||
LineNo = 0;
|
||||
|
||||
/* Temporarily turn of file tracing */
|
||||
old_flag = DebugFlag;
|
||||
DebugFlag &= (~DB_TRACE_FILES);
|
||||
|
||||
if (cmd[0] == '!') {
|
||||
RunDisabled |= RUN_NOTOWNER;
|
||||
}
|
||||
r = CacheFile(fname, 1);
|
||||
|
||||
DebugFlag = old_flag;
|
||||
if (r == OK) {
|
||||
fp = NULL;
|
||||
CLine = CachedFiles->cache;
|
||||
LineNo = 0;
|
||||
STRSET(FileName, fname);
|
||||
DBufFree(&buf);
|
||||
return OK;
|
||||
}
|
||||
DBufFree(&buf);
|
||||
/* We failed */
|
||||
PopFile();
|
||||
return E_CANT_OPEN;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* IncludeFile */
|
||||
@@ -869,6 +1080,8 @@ static int CheckSafety(void)
|
||||
/* If file is not owned by me, disable RUN command */
|
||||
if (statbuf.st_uid != geteuid()) {
|
||||
RunDisabled |= RUN_NOTOWNER;
|
||||
} else {
|
||||
RunDisabled &= ~RUN_NOTOWNER;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
24
src/funcs.c
24
src/funcs.c
@@ -151,6 +151,7 @@ static int FWeekno (func_info *);
|
||||
static int FWkday (func_info *);
|
||||
static int FWkdaynum (func_info *);
|
||||
static int FYear (func_info *);
|
||||
static int FShellescape (func_info *);
|
||||
|
||||
static int CleanUpAfterFunc (func_info *);
|
||||
static int CheckArgs (BuiltinFunc *f, int nargs);
|
||||
@@ -267,6 +268,7 @@ BuiltinFunc Func[] = {
|
||||
{ "realtoday", 0, 0, 0, FRealtoday },
|
||||
{ "sgn", 1, 1, 1, FSgn },
|
||||
{ "shell", 1, 2, 0, FShell },
|
||||
{ "shellescape", 1, 1, 1, FShellescape },
|
||||
{ "slide", 2, NO_MAX, 0, FSlide },
|
||||
{ "strlen", 1, 1, 1, FStrlen },
|
||||
{ "substr", 2, 3, 1, FSubstr },
|
||||
@@ -1072,6 +1074,28 @@ static int FOstype(func_info *info)
|
||||
return RetStrVal("UNIX", info);
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FShellescape - escape shell meta-characters */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int FShellescape(func_info *info)
|
||||
{
|
||||
DynamicBuffer buf;
|
||||
int r;
|
||||
|
||||
ASSERT_TYPE(0, STR_TYPE);
|
||||
DBufInit (&buf);
|
||||
if (ShellEscape(ARG(0).v.str, &buf) != OK) {
|
||||
DBufFree(&buf);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
|
||||
r = RetStrVal(DBufValue(&buf), info);
|
||||
DBufFree(&buf);
|
||||
return r;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FUpper - convert string to upper-case */
|
||||
|
||||
@@ -243,6 +243,15 @@ static void DoReminders(void)
|
||||
r=DoInclude(&p);
|
||||
purge_handled = 1;
|
||||
break;
|
||||
case T_IncludeCmd:
|
||||
/* In purge mode, include closes file, so we
|
||||
need to echo it here! */
|
||||
if (PurgeMode) {
|
||||
PurgeEchoLine("%s\n", CurLine);
|
||||
}
|
||||
r=DoIncludeCmd(&p);
|
||||
purge_handled = 1;
|
||||
break;
|
||||
case T_Exit: DoExit(&p); break;
|
||||
case T_Flush: r=DoFlush(&p); break;
|
||||
case T_Set: r=DoSet(&p); break;
|
||||
|
||||
@@ -45,6 +45,7 @@ int CopyValue (Value *dest, const Value *src);
|
||||
int ReadLine (void);
|
||||
int OpenFile (char const *fname);
|
||||
int DoInclude (ParsePtr p);
|
||||
int DoIncludeCmd (ParsePtr p);
|
||||
int IncludeFile (char const *fname);
|
||||
int GetAccessDate (char const *file);
|
||||
int SetAccessDate (char const *fname, int jul);
|
||||
@@ -161,3 +162,4 @@ void PrintJSONKeyPairDate(char const *name, int jul);
|
||||
void PrintJSONKeyPairDateTime(char const *name, int dt);
|
||||
void PrintJSONKeyPairTime(char const *name, int t);
|
||||
void System(char const *cmd);
|
||||
int ShellEscape(char const *in, DynamicBuffer *out);
|
||||
|
||||
@@ -62,6 +62,7 @@ Token TokArray[] = {
|
||||
{ "if", 2, T_If, 0 },
|
||||
{ "iftrig", 6, T_IfTrig, 0 },
|
||||
{ "include", 3, T_Include, 0 },
|
||||
{ "includecmd", 10, T_IncludeCmd, 0 },
|
||||
{ "january", 3, T_Month, 0 },
|
||||
{ "july", 3, T_Month, 6 },
|
||||
{ "june", 3, T_Month, 5 },
|
||||
|
||||
@@ -150,7 +150,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
|
||||
enum TokTypes
|
||||
{ T_Illegal,
|
||||
/* Commands first */
|
||||
T_Rem, T_Push, T_Pop, T_Preserve, T_Include, T_If, T_Else, T_EndIf,
|
||||
T_Rem, T_Push, T_Pop, T_Preserve, T_Include, T_IncludeCmd, T_If, T_Else, T_EndIf,
|
||||
T_IfTrig, T_ErrMsg,
|
||||
T_Set, T_UnSet, T_Fset, T_Omit, T_Banner, T_Exit,
|
||||
T_WkDay,
|
||||
|
||||
16
src/utils.c
16
src/utils.c
@@ -9,7 +9,11 @@
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
static char const DontEscapeMe[] =
|
||||
"1234567890_-=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@.,/";
|
||||
|
||||
#include "config.h"
|
||||
#include "err.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@@ -146,3 +150,15 @@ int _private_unminus_overflow(int a, int b)
|
||||
if (a < 0 && b < 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ShellEscape(char const *in, DynamicBuffer *out)
|
||||
{
|
||||
while(*in) {
|
||||
if (!strchr(DontEscapeMe, *in)) {
|
||||
if (DBufPutc(out, '\\') != OK) return E_NO_MEM;
|
||||
}
|
||||
if (DBufPutc(out, *in++) != OK) return E_NO_MEM;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
cd /home/dfs/Software/Remind.git || exit 1
|
||||
|
||||
rm -f .git/COMMIT_EDITMSG .git/*~
|
||||
|
||||
git update-server-info && cd .git && rsync --exclude HEADER.html --archive --verbose --progress --delete ./ dianne.skoll.ca:web/projects/remind/git/Remind.git/
|
||||
exit $?
|
||||
|
||||
@@ -307,14 +307,21 @@ rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
|
||||
EOF
|
||||
|
||||
# If we're already in a utf-8 locale, do
|
||||
# nothing
|
||||
|
||||
if ! echo $LC_ALL | grep -i utf-8 > /dev/null 2>&1 ; then
|
||||
export LC_ALL=en_US.utf-8
|
||||
# nothing; otherwise, set LC_ALL
|
||||
OK=0
|
||||
if echo $LC_ALL | grep -i utf-8 > /dev/null 2>&1 ; then
|
||||
OK=1
|
||||
fi
|
||||
|
||||
if ! echo $LANG | grep -i utf-8 > /dev/null 2>&1 ; then
|
||||
export LANG=en_US.utf-8
|
||||
if test -z "$LC_ALL" ; then
|
||||
if echo $LANG | grep -i utf-8 > /dev/null 2>&1 ; then
|
||||
export LC_ALL="$LANG"
|
||||
OK=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$OK" = 0 ; then
|
||||
export LC_ALL=en_US.utf-8
|
||||
fi
|
||||
|
||||
../src/remind -w128 -c ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out
|
||||
|
||||
1231
tests/test.cmp
1231
tests/test.cmp
File diff suppressed because it is too large
Load Diff
@@ -15,9 +15,40 @@ REM Wed UNTIL 15 Feb 1991 SATISFY [trigdate() > '1990-01-01'] MSG wookie
|
||||
# bad AT
|
||||
REM AT 0:00 0:01 0:02 MSG foo
|
||||
|
||||
# Includecmd
|
||||
INCLUDECMD echo REM 16 Feb 1991 MSG Blork
|
||||
INCLUDECMD echo REM 18 Feb 1991 MSG Blork
|
||||
|
||||
# Includecmd with continuation line
|
||||
INCLUDECMD echo REM 18 Feb 1991 MSG This line is \
|
||||
continued so there
|
||||
|
||||
# This should work
|
||||
INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee
|
||||
|
||||
# This should fail
|
||||
INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo INCLUDECMD echo MSG Yippee
|
||||
REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())]
|
||||
fset _h(x, y) trigger(hebdate(x,y))
|
||||
|
||||
# Test case from Remind mailing list
|
||||
set mltest "a b"
|
||||
INCLUDECMD printf 'REM %s\n' [mltest]
|
||||
|
||||
# Disabling RUN in an !includecmd
|
||||
INCLUDECMD !echo MSG foo
|
||||
INCLUDECMD !echo MSG foo
|
||||
INCLUDECMD !echo INCLUDECMD echo MSG foo
|
||||
INCLUDECMD !echo INCLUDECMD echo MSG foo
|
||||
INCLUDECMD !echo MSG foo
|
||||
INCLUDECMD !echo MSG foo
|
||||
|
||||
# INCLUDECMD with RUN disabled
|
||||
RUN OFF
|
||||
INCLUDECMD echo MSG foo
|
||||
RUN ON
|
||||
INCLUDECMD echo MSG foo
|
||||
|
||||
[_h(1, "Tishrey")] MSG Rosh Hashana 1
|
||||
[_h(2, "Tishrey")] MSG Rosh Hashana 2
|
||||
[_h(3, "Tishrey")] MSG Tzom Gedalia
|
||||
@@ -551,8 +582,15 @@ set a $IntMin * 2
|
||||
set a $IntMin * $IntMin
|
||||
set a $IntMin * $IntMax
|
||||
set a $IntMin / (-1)
|
||||
set a $IntMin * (-1)
|
||||
set a (-1) * $IntMin
|
||||
set a abs($IntMin)
|
||||
|
||||
# Shellescape
|
||||
set a shellescape(" !\"#$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
|
||||
|
||||
msg [a]
|
||||
|
||||
# Don't want Remind to queue reminders
|
||||
EXIT
|
||||
|
||||
|
||||
Reference in New Issue
Block a user