mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-16 06:18:47 +02:00
Completely revamp expression engine.
This commit is contained in:
@@ -166,6 +166,10 @@
|
||||
|
||||
#define PASSTHRU_LEN 32
|
||||
|
||||
#define MAX_RECURSION_LEVEL 1000
|
||||
|
||||
#define MAX_FUNC_ARGS 64
|
||||
|
||||
#define PSBEGIN "# rem2ps begin"
|
||||
#define PSEND "# rem2ps end"
|
||||
|
||||
|
||||
@@ -166,6 +166,10 @@
|
||||
|
||||
#define PASSTHRU_LEN 32
|
||||
|
||||
#define MAX_RECURSION_LEVEL 1000
|
||||
|
||||
#define MAX_FUNC_ARGS 64
|
||||
|
||||
#define PSBEGIN "# rem2ps begin"
|
||||
#define PSEND "# rem2ps end"
|
||||
|
||||
|
||||
33
src/dorem.c
33
src/dorem.c
@@ -1268,20 +1268,28 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
|
||||
{
|
||||
int iter, dse, r, start;
|
||||
Value v;
|
||||
char const *s;
|
||||
char const *t;
|
||||
expr_node *sat_node;
|
||||
int nonconst = 0;
|
||||
|
||||
t = p->pos;
|
||||
sat_node = ParseExpr(p, &r);
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
if (!sat_node) {
|
||||
return E_SWERR;
|
||||
}
|
||||
iter = 0;
|
||||
start = trig->scanfrom;
|
||||
while (iter++ < MaxSatIter) {
|
||||
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0);
|
||||
if (r) {
|
||||
free_expr_tree(sat_node);
|
||||
if (r == E_CANT_TRIG) return OK; else return r;
|
||||
}
|
||||
if (dse != start && trig->duration_days) {
|
||||
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days);
|
||||
if (r) {
|
||||
free_expr_tree(sat_node);
|
||||
if (r == E_CANT_TRIG) return OK; else return r;
|
||||
}
|
||||
} else if (dse == start) {
|
||||
@@ -1294,13 +1302,18 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
|
||||
SaveAllTriggerInfo(trig, tt, dse, tt->ttime, 1);
|
||||
}
|
||||
if (dse == -1) {
|
||||
free_expr_tree(sat_node);
|
||||
return E_EXPIRED;
|
||||
}
|
||||
s = p->pos;
|
||||
r = EvaluateExpr(p, &v);
|
||||
t = p->pos;
|
||||
if (r) return r;
|
||||
if (v.type != INT_TYPE && v.type != STR_TYPE) return E_BAD_TYPE;
|
||||
r = evaluate_expr_node(sat_node, NULL, &v, &nonconst);
|
||||
if (r) {
|
||||
free_expr_tree(sat_node);
|
||||
return r;
|
||||
}
|
||||
if (v.type != INT_TYPE && v.type != STR_TYPE) {
|
||||
free_expr_tree(sat_node);
|
||||
return E_BAD_TYPE;
|
||||
}
|
||||
if ((v.type == INT_TYPE && v.v.val) ||
|
||||
(v.type == STR_TYPE && *v.v.str)) {
|
||||
AdjustTriggerForDuration(trig->scanfrom, dse, trig, tt, 1);
|
||||
@@ -1325,17 +1338,17 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
|
||||
}
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
free_expr_tree(sat_node);
|
||||
return OK;
|
||||
}
|
||||
p->pos = s;
|
||||
if (dse+trig->duration_days < start) {
|
||||
start++;
|
||||
} else {
|
||||
start = dse+trig->duration_days+1;
|
||||
}
|
||||
}
|
||||
p->pos = t;
|
||||
LastTrigValid = 0;
|
||||
free_expr_tree(sat_node);
|
||||
return E_CANT_TRIG;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
/***************************************************************/
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "expr.h"
|
||||
#define L_IN_DOSUBST
|
||||
#include <stdio.h>
|
||||
@@ -20,7 +21,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "globals.h"
|
||||
#include "err.h"
|
||||
#include "protos.h"
|
||||
|
||||
@@ -124,7 +124,9 @@ int DBufPuts(DynamicBuffer *dbuf, char const *str)
|
||||
**********************************************************************/
|
||||
void DBufFree(DynamicBuffer *dbuf)
|
||||
{
|
||||
if (dbuf->buffer != dbuf->staticBuf) free(dbuf->buffer);
|
||||
if (dbuf->buffer != NULL && dbuf->buffer != dbuf->staticBuf) {
|
||||
free(dbuf->buffer);
|
||||
}
|
||||
DBufInit(dbuf);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ EXTERN char *ErrMsg[]
|
||||
"RUN disabled",
|
||||
"Domain error",
|
||||
"Invalid identifier",
|
||||
"Recursive function call detected",
|
||||
"Too many recursive function calls",
|
||||
"",
|
||||
"Cannot modify system variable",
|
||||
"C library function can't represent date/time",
|
||||
|
||||
3140
src/expr.c
3140
src/expr.c
File diff suppressed because it is too large
Load Diff
46
src/expr.h
46
src/expr.h
@@ -1,15 +1,19 @@
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* EXPR.H */
|
||||
/* EXPR_NEW.H */
|
||||
/* */
|
||||
/* Contains a few definitions used by expression evaluator. */
|
||||
/* Contains a few definitions used by expression pareser and */
|
||||
/* evaluator. */
|
||||
/* */
|
||||
/* This file is part of REMIND. */
|
||||
/* Copyright (C) 1992-2024 by Dianne Skoll */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (C) 2022 by Dianne Skoll */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
typedef struct udf_struct UserFunc;
|
||||
|
||||
expr_node *parse_expression(char const **e, int *r, Var *locals);
|
||||
|
||||
/* Define the types of values */
|
||||
#define ERR_TYPE 0
|
||||
#define INT_TYPE 1
|
||||
@@ -20,43 +24,10 @@
|
||||
#define SPECIAL_TYPE 6 /* Only for system variables */
|
||||
#define CONST_INT_TYPE 7 /* Only for system variables */
|
||||
|
||||
/* Define stuff for parsing expressions */
|
||||
#define BEG_OF_EXPR '['
|
||||
#define END_OF_EXPR ']'
|
||||
#define COMMA ','
|
||||
|
||||
#define UN_OP 0 /* Unary operator */
|
||||
#define BIN_OP 1 /* Binary Operator */
|
||||
#define FUNC 2 /* Function */
|
||||
|
||||
/* Make the pushing and popping of values and operators in-line code
|
||||
for speed. BEWARE: These macros invoke return if an error happens ! */
|
||||
|
||||
#define PushOpStack(op) \
|
||||
do { if (OpStackPtr >= OP_STACK_SIZE) return E_OP_STK_OVER; \
|
||||
else { OpStack[OpStackPtr++] = (op); if (OpStackPtr > OpStackHiWater) OpStackHiWater = OpStackPtr; } } while(0)
|
||||
|
||||
#define PopOpStack(op) \
|
||||
if (OpStackPtr <= 0) \
|
||||
return E_OP_STK_UNDER; \
|
||||
else \
|
||||
(op) = OpStack[--OpStackPtr]
|
||||
|
||||
#define PushValStack(val) \
|
||||
do { if (ValStackPtr >= VAL_STACK_SIZE) { \
|
||||
DestroyValue(val); \
|
||||
return E_VA_STK_OVER; \
|
||||
} else { \
|
||||
ValStack[ValStackPtr++] = (val); \
|
||||
if (ValStackPtr > ValStackHiWater) ValStackHiWater = ValStackPtr; \
|
||||
} } while (0);
|
||||
|
||||
#define PopValStack(val) \
|
||||
if (ValStackPtr <= 0) \
|
||||
return E_VA_STK_UNDER; \
|
||||
else \
|
||||
(val) = ValStack[--ValStackPtr]
|
||||
|
||||
/* These functions are in utils.c and are used to detect overflow
|
||||
in various arithmetic operators. They have to be in separate
|
||||
functions with extern linkage to defeat compiler optimizations
|
||||
@@ -64,4 +35,3 @@ else \
|
||||
extern int _private_mul_overflow(int a, int b);
|
||||
extern int _private_add_overflow(int a, int b);
|
||||
extern int _private_sub_overflow(int a, int b);
|
||||
|
||||
|
||||
559
src/funcs.c
559
src/funcs.c
@@ -60,6 +60,8 @@
|
||||
#define Nargs (info->nargs)
|
||||
#define RetVal (info->retval)
|
||||
|
||||
#define DBG(x) do { if (DebugFlag & DB_PRTEXPR) { x; } } while(0)
|
||||
|
||||
static int
|
||||
solstice_equinox_for_year(int y, int which);
|
||||
|
||||
@@ -74,7 +76,7 @@ static int FArgs (func_info *);
|
||||
static int FAsc (func_info *);
|
||||
static int FBaseyr (func_info *);
|
||||
static int FChar (func_info *);
|
||||
static int FChoose (func_info *);
|
||||
static int FChoose (expr_node *, Value *, Value *, int *);
|
||||
static int FCoerce (func_info *);
|
||||
static int FColumns (func_info *);
|
||||
static int FCurrent (func_info *);
|
||||
@@ -101,7 +103,7 @@ static int FHebyear (func_info *);
|
||||
static int FHour (func_info *);
|
||||
static int FHtmlEscape (func_info *);
|
||||
static int FHtmlStriptags (func_info *);
|
||||
static int FIif (func_info *);
|
||||
static int FIif (expr_node *, Value *, Value *, int *);
|
||||
static int FIndex (func_info *);
|
||||
static int FIsAny (func_info *);
|
||||
static int FIsdst (func_info *);
|
||||
@@ -180,14 +182,9 @@ static int FWkday (func_info *);
|
||||
static int FWkdaynum (func_info *);
|
||||
static int FYear (func_info *);
|
||||
|
||||
static int CleanUpAfterFunc (func_info *);
|
||||
static int CheckArgs (BuiltinFunc *f, int nargs);
|
||||
static int SunStuff (int rise, double cosz, int dse);
|
||||
static int tz_set_tz (char const *tz);
|
||||
|
||||
/* "Overload" the struct Operator definition */
|
||||
#define NO_MAX 127
|
||||
|
||||
/* Caches for extracting months, days, years from dates - may
|
||||
improve performance slightly. */
|
||||
static int CacheDse = -1;
|
||||
@@ -196,14 +193,9 @@ static int CacheYear, CacheMon, CacheDay;
|
||||
static int CacheHebDse = -1;
|
||||
static int CacheHebYear, CacheHebMon, CacheHebDay;
|
||||
|
||||
/* We need access to the value stack */
|
||||
extern Value ValStack[];
|
||||
extern int ValStackPtr;
|
||||
extern int ValStackHiWater;
|
||||
|
||||
/* Macro for accessing arguments from the value stack - args are numbered
|
||||
from 0 to (Nargs - 1) */
|
||||
#define ARG(x) (ValStack[ValStackPtr - Nargs + (x)])
|
||||
#define ARG(x) (info->args[x])
|
||||
|
||||
#define ARGV(x) ARG(x).v.val
|
||||
#define ARGSTR(x) ARG(x).v.str
|
||||
@@ -231,215 +223,127 @@ extern int ValStackHiWater;
|
||||
|
||||
/* The array holding the built-in functions. */
|
||||
BuiltinFunc Func[] = {
|
||||
/* Name minargs maxargs is_constant func */
|
||||
/* Name minargs maxargs is_constant func newfunc*/
|
||||
|
||||
{ "abs", 1, 1, 1, FAbs },
|
||||
{ "access", 2, 2, 0, FAccess },
|
||||
{ "adawn", 0, 1, 0, FADawn},
|
||||
{ "adusk", 0, 1, 0, FADusk},
|
||||
{ "ampm", 1, 3, 1, FAmpm },
|
||||
{ "ansicolor", 1, 5, 1, FAnsicolor },
|
||||
{ "args", 1, 1, 0, FArgs },
|
||||
{ "asc", 1, 1, 1, FAsc },
|
||||
{ "baseyr", 0, 0, 1, FBaseyr },
|
||||
{ "char", 1, NO_MAX, 1, FChar },
|
||||
{ "choose", 2, NO_MAX, 1, FChoose },
|
||||
{ "coerce", 2, 2, 1, FCoerce },
|
||||
{ "columns", 0, 1, 0, FColumns },
|
||||
{ "current", 0, 0, 0, FCurrent },
|
||||
{ "date", 3, 3, 1, FDate },
|
||||
{ "datepart", 1, 1, 1, FDatepart },
|
||||
{ "datetime", 2, 5, 1, FDateTime },
|
||||
{ "dawn", 0, 1, 0, FDawn},
|
||||
{ "day", 1, 1, 1, FDay },
|
||||
{ "daysinmon", 2, 2, 1, FDaysinmon },
|
||||
{ "defined", 1, 1, 0, FDefined },
|
||||
{ "dosubst", 1, 3, 0, FDosubst },
|
||||
{ "dusk", 0, 1, 0, FDusk },
|
||||
{ "easterdate", 0, 1, 0, FEasterdate },
|
||||
{ "evaltrig", 1, 2, 0, FEvalTrig },
|
||||
{ "filedate", 1, 1, 0, FFiledate },
|
||||
{ "filedatetime", 1, 1, 0, FFiledatetime },
|
||||
{ "filedir", 0, 0, 0, FFiledir },
|
||||
{ "filename", 0, 0, 0, FFilename },
|
||||
{ "getenv", 1, 1, 0, FGetenv },
|
||||
{ "hebdate", 2, 5, 0, FHebdate },
|
||||
{ "hebday", 1, 1, 0, FHebday },
|
||||
{ "hebmon", 1, 1, 0, FHebmon },
|
||||
{ "hebyear", 1, 1, 0, FHebyear },
|
||||
{ "hour", 1, 1, 1, FHour },
|
||||
{ "htmlescape", 1, 1, 1, FHtmlEscape },
|
||||
{ "htmlstriptags",1, 1, 1, FHtmlStriptags },
|
||||
{ "iif", 1, NO_MAX, 1, FIif },
|
||||
{ "index", 2, 3, 1, FIndex },
|
||||
{ "isany", 1, NO_MAX, 1, FIsAny },
|
||||
{ "isdst", 0, 2, 0, FIsdst },
|
||||
{ "isleap", 1, 1, 1, FIsleap },
|
||||
{ "isomitted", 1, 1, 0, FIsomitted },
|
||||
{ "language", 0, 0, 1, FLanguage },
|
||||
{ "localtoutc", 1, 1, 1, FLocalToUTC },
|
||||
{ "lower", 1, 1, 1, FLower },
|
||||
{ "max", 1, NO_MAX, 1, FMax },
|
||||
{ "min", 1, NO_MAX, 1, FMin },
|
||||
{ "minsfromutc", 0, 2, 0, FMinsfromutc },
|
||||
{ "minute", 1, 1, 1, FMinute },
|
||||
{ "mon", 1, 1, 1, FMon },
|
||||
{ "monnum", 1, 1, 1, FMonnum },
|
||||
{ "moondate", 1, 3, 0, FMoondate },
|
||||
{ "moondatetime", 1, 3, 0, FMoondatetime },
|
||||
{ "moonphase", 0, 2, 0, FMoonphase },
|
||||
{ "moontime", 1, 3, 0, FMoontime },
|
||||
{ "multitrig", 1, NO_MAX, 0, FMultiTrig },
|
||||
{ "ndawn", 0, 1, 0, FNDawn},
|
||||
{ "ndusk", 0, 1, 0, FNDusk},
|
||||
{ "nonomitted", 2, NO_MAX, 0, FNonomitted },
|
||||
{ "now", 0, 0, 0, FNow },
|
||||
{ "ord", 1, 1, 1, FOrd },
|
||||
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster },
|
||||
{ "ostype", 0, 0, 1, FOstype },
|
||||
{ "pad", 3, 4, 1, FPad },
|
||||
{ "plural", 1, 3, 1, FPlural },
|
||||
{ "psmoon", 1, 4, 1, FPsmoon},
|
||||
{ "psshade", 1, 3, 1, FPsshade},
|
||||
{ "realcurrent", 0, 0, 0, FRealCurrent},
|
||||
{ "realnow", 0, 0, 0, FRealnow},
|
||||
{ "realtoday", 0, 0, 0, FRealtoday },
|
||||
{ "rows", 0, 0, 0, FRows },
|
||||
{ "sgn", 1, 1, 1, FSgn },
|
||||
{ "shell", 1, 2, 0, FShell },
|
||||
{ "shellescape", 1, 1, 1, FShellescape },
|
||||
{ "slide", 2, NO_MAX, 0, FSlide },
|
||||
{ "soleq", 1, 2, 0, FSoleq },
|
||||
{ "stdout", 0, 0, 1, FStdout },
|
||||
{ "strlen", 1, 1, 1, FStrlen },
|
||||
{ "substr", 2, 3, 1, FSubstr },
|
||||
{ "sunrise", 0, 1, 0, FSunrise},
|
||||
{ "sunset", 0, 1, 0, FSunset },
|
||||
{ "time", 2, 2, 1, FTime },
|
||||
{ "timepart", 1, 1, 1, FTimepart },
|
||||
{ "timezone", 0, 1, 1, FTimezone },
|
||||
{ "today", 0, 0, 0, FToday },
|
||||
{ "trig", 0, NO_MAX, 0, FTrig },
|
||||
{ "trigback", 0, 0, 0, FTrigback },
|
||||
{ "trigdate", 0, 0, 0, FTrigdate },
|
||||
{ "trigdatetime", 0, 0, 0, FTrigdatetime },
|
||||
{ "trigdelta", 0, 0, 0, FTrigdelta },
|
||||
{ "trigduration", 0, 0, 0, FTrigduration },
|
||||
{ "trigeventduration", 0, 0, 0, FTrigeventduration },
|
||||
{ "trigeventstart", 0, 0, 0, FTrigeventstart },
|
||||
{ "trigfrom", 0, 0, 0, FTrigfrom },
|
||||
{ "trigger", 1, 3, 0, FTrigger },
|
||||
{ "trigpriority", 0, 0, 0, FTrigpriority },
|
||||
{ "trigrep", 0, 0, 0, FTrigrep },
|
||||
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom },
|
||||
{ "trigtags", 0, 0, 0, FTrigtags },
|
||||
{ "trigtime", 0, 0, 0, FTrigtime },
|
||||
{ "trigtimedelta",0, 0, 0, FTrigtimedelta },
|
||||
{ "trigtimerep", 0, 0, 0, FTrigtimerep },
|
||||
{ "triguntil", 0, 0, 0, FTriguntil },
|
||||
{ "trigvalid", 0, 0, 0, FTrigvalid },
|
||||
{ "typeof", 1, 1, 1, FTypeof },
|
||||
{ "tzconvert", 2, 3, 0, FTzconvert },
|
||||
{ "upper", 1, 1, 1, FUpper },
|
||||
{ "utctolocal", 1, 1, 1, FUTCToLocal },
|
||||
{ "value", 1, 2, 0, FValue },
|
||||
{ "version", 0, 0, 1, FVersion },
|
||||
{ "weekno", 0, 3, 1, FWeekno },
|
||||
{ "wkday", 1, 1, 1, FWkday },
|
||||
{ "wkdaynum", 1, 1, 1, FWkdaynum },
|
||||
{ "year", 1, 1, 1, FYear }
|
||||
{ "abs", 1, 1, 1, FAbs, NULL },
|
||||
{ "access", 2, 2, 0, FAccess, NULL },
|
||||
{ "adawn", 0, 1, 0, FADawn, NULL},
|
||||
{ "adusk", 0, 1, 0, FADusk, NULL},
|
||||
{ "ampm", 1, 3, 1, FAmpm, NULL },
|
||||
{ "ansicolor", 1, 5, 1, FAnsicolor, NULL },
|
||||
{ "args", 1, 1, 0, FArgs, NULL },
|
||||
{ "asc", 1, 1, 1, FAsc, NULL },
|
||||
{ "baseyr", 0, 0, 1, FBaseyr, NULL },
|
||||
{ "char", 1, NO_MAX, 1, FChar, NULL },
|
||||
{ "choose", 2, NO_MAX, 1, NULL, FChoose }, /*NEW-STYLE*/
|
||||
{ "coerce", 2, 2, 1, FCoerce, NULL },
|
||||
{ "columns", 0, 1, 0, FColumns, NULL },
|
||||
{ "current", 0, 0, 0, FCurrent, NULL },
|
||||
{ "date", 3, 3, 1, FDate, NULL },
|
||||
{ "datepart", 1, 1, 1, FDatepart, NULL },
|
||||
{ "datetime", 2, 5, 1, FDateTime, NULL },
|
||||
{ "dawn", 0, 1, 0, FDawn, NULL },
|
||||
{ "day", 1, 1, 1, FDay, NULL },
|
||||
{ "daysinmon", 2, 2, 1, FDaysinmon, NULL },
|
||||
{ "defined", 1, 1, 0, FDefined, NULL },
|
||||
{ "dosubst", 1, 3, 0, FDosubst, NULL },
|
||||
{ "dusk", 0, 1, 0, FDusk, NULL },
|
||||
{ "easterdate", 0, 1, 0, FEasterdate, NULL },
|
||||
{ "evaltrig", 1, 2, 0, FEvalTrig, NULL },
|
||||
{ "filedate", 1, 1, 0, FFiledate, NULL },
|
||||
{ "filedatetime", 1, 1, 0, FFiledatetime, NULL },
|
||||
{ "filedir", 0, 0, 0, FFiledir, NULL },
|
||||
{ "filename", 0, 0, 0, FFilename, NULL },
|
||||
{ "getenv", 1, 1, 0, FGetenv, NULL },
|
||||
{ "hebdate", 2, 5, 0, FHebdate, NULL },
|
||||
{ "hebday", 1, 1, 0, FHebday, NULL },
|
||||
{ "hebmon", 1, 1, 0, FHebmon, NULL },
|
||||
{ "hebyear", 1, 1, 0, FHebyear, NULL },
|
||||
{ "hour", 1, 1, 1, FHour, NULL },
|
||||
{ "htmlescape", 1, 1, 1, FHtmlEscape, NULL },
|
||||
{ "htmlstriptags",1, 1, 1, FHtmlStriptags, NULL },
|
||||
{ "iif", 1, NO_MAX, 1, NULL, FIif }, /*NEW-STYLE*/
|
||||
{ "index", 2, 3, 1, FIndex, NULL },
|
||||
{ "isany", 1, NO_MAX, 1, FIsAny, NULL },
|
||||
{ "isdst", 0, 2, 0, FIsdst, NULL },
|
||||
{ "isleap", 1, 1, 1, FIsleap, NULL },
|
||||
{ "isomitted", 1, 1, 0, FIsomitted, NULL },
|
||||
{ "language", 0, 0, 1, FLanguage, NULL },
|
||||
{ "localtoutc", 1, 1, 1, FLocalToUTC, NULL },
|
||||
{ "lower", 1, 1, 1, FLower, NULL },
|
||||
{ "max", 1, NO_MAX, 1, FMax, NULL },
|
||||
{ "min", 1, NO_MAX, 1, FMin, NULL },
|
||||
{ "minsfromutc", 0, 2, 0, FMinsfromutc, NULL },
|
||||
{ "minute", 1, 1, 1, FMinute, NULL },
|
||||
{ "mon", 1, 1, 1, FMon, NULL },
|
||||
{ "monnum", 1, 1, 1, FMonnum, NULL },
|
||||
{ "moondate", 1, 3, 0, FMoondate, NULL },
|
||||
{ "moondatetime", 1, 3, 0, FMoondatetime, NULL },
|
||||
{ "moonphase", 0, 2, 0, FMoonphase, NULL },
|
||||
{ "moontime", 1, 3, 0, FMoontime, NULL },
|
||||
{ "multitrig", 1, NO_MAX, 0, FMultiTrig, NULL },
|
||||
{ "ndawn", 0, 1, 0, FNDawn, NULL },
|
||||
{ "ndusk", 0, 1, 0, FNDusk, NULL },
|
||||
{ "nonomitted", 2, NO_MAX, 0, FNonomitted, NULL },
|
||||
{ "now", 0, 0, 0, FNow, NULL },
|
||||
{ "ord", 1, 1, 1, FOrd, NULL },
|
||||
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster, NULL },
|
||||
{ "ostype", 0, 0, 1, FOstype, NULL },
|
||||
{ "pad", 3, 4, 1, FPad, NULL },
|
||||
{ "plural", 1, 3, 1, FPlural, NULL },
|
||||
{ "psmoon", 1, 4, 1, FPsmoon, NULL },
|
||||
{ "psshade", 1, 3, 1, FPsshade, NULL },
|
||||
{ "realcurrent", 0, 0, 0, FRealCurrent, NULL },
|
||||
{ "realnow", 0, 0, 0, FRealnow, NULL },
|
||||
{ "realtoday", 0, 0, 0, FRealtoday, NULL },
|
||||
{ "rows", 0, 0, 0, FRows, NULL },
|
||||
{ "sgn", 1, 1, 1, FSgn, NULL },
|
||||
{ "shell", 1, 2, 0, FShell, NULL },
|
||||
{ "shellescape", 1, 1, 1, FShellescape, NULL },
|
||||
{ "slide", 2, NO_MAX, 0, FSlide, NULL },
|
||||
{ "soleq", 1, 2, 0, FSoleq, NULL },
|
||||
{ "stdout", 0, 0, 1, FStdout, NULL },
|
||||
{ "strlen", 1, 1, 1, FStrlen, NULL },
|
||||
{ "substr", 2, 3, 1, FSubstr, NULL },
|
||||
{ "sunrise", 0, 1, 0, FSunrise, NULL },
|
||||
{ "sunset", 0, 1, 0, FSunset, NULL },
|
||||
{ "time", 2, 2, 1, FTime, NULL },
|
||||
{ "timepart", 1, 1, 1, FTimepart, NULL },
|
||||
{ "timezone", 0, 1, 1, FTimezone, NULL },
|
||||
{ "today", 0, 0, 0, FToday, NULL },
|
||||
{ "trig", 0, NO_MAX, 0, FTrig, NULL },
|
||||
{ "trigback", 0, 0, 0, FTrigback, NULL },
|
||||
{ "trigdate", 0, 0, 0, FTrigdate, NULL },
|
||||
{ "trigdatetime", 0, 0, 0, FTrigdatetime, NULL },
|
||||
{ "trigdelta", 0, 0, 0, FTrigdelta, NULL },
|
||||
{ "trigduration", 0, 0, 0, FTrigduration, NULL },
|
||||
{ "trigeventduration", 0, 0, 0, FTrigeventduration, NULL },
|
||||
{ "trigeventstart", 0, 0, 0, FTrigeventstart, NULL },
|
||||
{ "trigfrom", 0, 0, 0, FTrigfrom, NULL },
|
||||
{ "trigger", 1, 3, 0, FTrigger, NULL },
|
||||
{ "trigpriority", 0, 0, 0, FTrigpriority, NULL },
|
||||
{ "trigrep", 0, 0, 0, FTrigrep, NULL },
|
||||
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom, NULL },
|
||||
{ "trigtags", 0, 0, 0, FTrigtags, NULL },
|
||||
{ "trigtime", 0, 0, 0, FTrigtime, NULL },
|
||||
{ "trigtimedelta",0, 0, 0, FTrigtimedelta, NULL },
|
||||
{ "trigtimerep", 0, 0, 0, FTrigtimerep, NULL },
|
||||
{ "triguntil", 0, 0, 0, FTriguntil, NULL },
|
||||
{ "trigvalid", 0, 0, 0, FTrigvalid, NULL },
|
||||
{ "typeof", 1, 1, 1, FTypeof, NULL },
|
||||
{ "tzconvert", 2, 3, 0, FTzconvert, NULL },
|
||||
{ "upper", 1, 1, 1, FUpper, NULL },
|
||||
{ "utctolocal", 1, 1, 1, FUTCToLocal, NULL },
|
||||
{ "value", 1, 2, 0, FValue, NULL },
|
||||
{ "version", 0, 0, 1, FVersion, NULL },
|
||||
{ "weekno", 0, 3, 1, FWeekno, NULL },
|
||||
{ "wkday", 1, 1, 1, FWkday, NULL },
|
||||
{ "wkdaynum", 1, 1, 1, FWkdaynum, NULL },
|
||||
{ "year", 1, 1, 1, FYear, NULL }
|
||||
};
|
||||
|
||||
/* Need a variable here - Func[] array not really visible to outside. */
|
||||
int NumFuncs = sizeof(Func) / sizeof(Operator) ;
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* CallFunc */
|
||||
/* */
|
||||
/* Call a function given a pointer to it, and the number */
|
||||
/* of arguments supplied. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
int CallFunc(BuiltinFunc *f, int nargs)
|
||||
{
|
||||
register int r = CheckArgs(f, nargs);
|
||||
int i;
|
||||
|
||||
func_info info_obj;
|
||||
func_info *info = &info_obj;
|
||||
|
||||
Nargs = nargs;
|
||||
RetVal.type = ERR_TYPE;
|
||||
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
fprintf(ErrFp, "%s(", f->name);
|
||||
for (i=0; i<nargs; i++) {
|
||||
PrintValue(&ARG(i), ErrFp);
|
||||
if (i<nargs-1) fprintf(ErrFp, ", ");
|
||||
}
|
||||
fprintf(ErrFp, ") => ");
|
||||
if (r) {
|
||||
fprintf(ErrFp, "%s\n", ErrMsg[r]);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if (r) {
|
||||
Eprint("%s(): %s", f->name, ErrMsg[r]);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = (*(f->func))(info);
|
||||
if (r) {
|
||||
DestroyValue(RetVal);
|
||||
if (DebugFlag & DB_PRTEXPR)
|
||||
fprintf(ErrFp, "%s\n", ErrMsg[r]);
|
||||
else
|
||||
Eprint("%s(): %s", f->name, ErrMsg[r]);
|
||||
return r;
|
||||
}
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
PrintValue(&RetVal, ErrFp);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
r = CleanUpAfterFunc(info);
|
||||
return r;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* CheckArgs */
|
||||
/* */
|
||||
/* Check that the right number of args have been supplied */
|
||||
/* for a function. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int CheckArgs(BuiltinFunc *f, int nargs)
|
||||
{
|
||||
if (nargs < f->minargs) return E_2FEW_ARGS;
|
||||
if (nargs > f->maxargs && f->maxargs != NO_MAX) return E_2MANY_ARGS;
|
||||
return OK;
|
||||
}
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* CleanUpAfterFunc */
|
||||
/* */
|
||||
/* Clean up the stack after a function call - remove */
|
||||
/* args and push the new value. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int CleanUpAfterFunc(func_info *info)
|
||||
{
|
||||
Value v;
|
||||
int i;
|
||||
|
||||
for (i=0; i<Nargs; i++) {
|
||||
PopValStack(v);
|
||||
DestroyValue(v);
|
||||
}
|
||||
PushValStack(RetVal);
|
||||
return OK;
|
||||
}
|
||||
int NumFuncs = sizeof(Func) / sizeof(BuiltinFunc) ;
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
@@ -1279,6 +1183,9 @@ static int FIsAny(func_info *info)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Debugging helpers for "choose()" and "iif() */
|
||||
#define PUT(x) DBufPuts(&DebugBuf, x)
|
||||
#define OUT() do { fprintf(ErrFp, "%s\n", DBufValue(&DebugBuf)); DBufFree(&DebugBuf); } while(0)
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FChoose */
|
||||
@@ -1287,15 +1194,63 @@ static int FIsAny(func_info *info)
|
||||
/* from 1. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int FChoose(func_info *info)
|
||||
static int FChoose(expr_node *node, Value *locals, Value *ans, int *nonconst)
|
||||
{
|
||||
int v;
|
||||
DynamicBuffer DebugBuf;
|
||||
expr_node *cur;
|
||||
int r;
|
||||
int n;
|
||||
int nargs = node->num_kids;
|
||||
Value(v);
|
||||
DBG(DBufInit(&DebugBuf));
|
||||
PUT("choose(");
|
||||
|
||||
ASSERT_TYPE(0, INT_TYPE);
|
||||
v = ARGV(0);
|
||||
if (v < 1) v = 1;
|
||||
if (v > Nargs-1) v = Nargs-1;
|
||||
DCOPYVAL(RetVal, ARG(v));
|
||||
cur = node->child;
|
||||
r = evaluate_expr_node(cur, locals, &v, nonconst);
|
||||
if (r != OK) {
|
||||
DBG(DBufFree(&DebugBuf));
|
||||
return r;
|
||||
}
|
||||
DBG(PUT(PrintValue(&v, NULL)));
|
||||
if (v.type != INT_TYPE) {
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
cur = cur->sibling;
|
||||
while(cur) {
|
||||
PUT(", ?");
|
||||
cur = cur->sibling;
|
||||
}
|
||||
PUT(") => ");
|
||||
PUT(ErrMsg[E_BAD_TYPE]);
|
||||
OUT();
|
||||
}
|
||||
return E_BAD_TYPE;
|
||||
}
|
||||
n = v.v.val;
|
||||
if (n < 1) n = 1;
|
||||
if (n > nargs-1) n = nargs-1;
|
||||
|
||||
while(n--) {
|
||||
cur = cur->sibling;
|
||||
DBG(if (n) { PUT(", ?"); });
|
||||
if (!cur) return E_SWERR; /* Should not happen! */
|
||||
}
|
||||
r = evaluate_expr_node(cur, locals, ans, nonconst);
|
||||
if (r != OK) {
|
||||
DBG(DBufFree(&DebugBuf));
|
||||
return r;
|
||||
}
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
PUT(", ");
|
||||
PUT(PrintValue(ans, NULL));
|
||||
cur = cur->sibling;
|
||||
while(cur) {
|
||||
PUT(", ?");
|
||||
cur = cur->sibling;
|
||||
}
|
||||
PUT(") => ");
|
||||
PUT(PrintValue(ans, NULL));
|
||||
OUT();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -1481,7 +1436,7 @@ static int FValue(func_info *info)
|
||||
ASSERT_TYPE(0, STR_TYPE);
|
||||
switch(Nargs) {
|
||||
case 1:
|
||||
return GetVarValue(ARGSTR(0), &RetVal, NULL, NULL);
|
||||
return GetVarValue(ARGSTR(0), &RetVal);
|
||||
|
||||
case 2:
|
||||
v = FindVar(ARGSTR(0), 0);
|
||||
@@ -1953,33 +1908,101 @@ static int FIndex(func_info *info)
|
||||
/* */
|
||||
/* FIif */
|
||||
/* */
|
||||
/* The IIF function. */
|
||||
/* The IIF function. Uses new-style evaluation */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int FIif(func_info *info)
|
||||
static int FIif(expr_node *node, Value *locals, Value *ans, int *nonconst)
|
||||
{
|
||||
int istrue;
|
||||
int arg;
|
||||
int r;
|
||||
int done;
|
||||
Value v;
|
||||
expr_node *cur;
|
||||
DynamicBuffer DebugBuf;
|
||||
|
||||
if (!(Nargs % 2)) return E_IIF_ODD;
|
||||
DBG(DBufInit(&DebugBuf));
|
||||
DBG(PUT("iif("));
|
||||
cur = node->child;
|
||||
|
||||
for (arg=0; arg<Nargs-1; arg += 2) {
|
||||
if (ARG(arg).type != STR_TYPE && ARG(arg).type != INT_TYPE)
|
||||
return E_BAD_TYPE;
|
||||
|
||||
if (ARG(arg).type == INT_TYPE)
|
||||
istrue = ARG(arg).v.val;
|
||||
else
|
||||
istrue = *(ARG(arg).v.str);
|
||||
|
||||
if (istrue) {
|
||||
DCOPYVAL(RetVal, ARG(arg+1));
|
||||
return OK;
|
||||
}
|
||||
if (!(node->num_kids % 2)) {
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
r = 0;
|
||||
while(cur) {
|
||||
if (r) PUT(", ");
|
||||
r=1;
|
||||
PUT("?");
|
||||
cur = cur->sibling;
|
||||
}
|
||||
PUT(") => ");
|
||||
PUT(ErrMsg[E_IIF_ODD]);
|
||||
OUT();
|
||||
}
|
||||
return E_IIF_ODD;
|
||||
}
|
||||
|
||||
DCOPYVAL(RetVal, ARG(Nargs-1));
|
||||
return OK;
|
||||
|
||||
done = 0;
|
||||
while(cur->sibling) {
|
||||
r = evaluate_expr_node(cur, locals, &v, nonconst);
|
||||
if (r != OK) {
|
||||
DBG(DBufFree(&DebugBuf));
|
||||
return r;
|
||||
}
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
if (done) PUT(", ");
|
||||
done = 1;
|
||||
PUT(PrintValue(&v, NULL));
|
||||
}
|
||||
if (v.type != STR_TYPE && v.type != INT_TYPE) {
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
cur = cur->sibling;
|
||||
while(cur) {
|
||||
PUT(", ?");
|
||||
cur = cur->sibling;
|
||||
}
|
||||
PUT(") => ");
|
||||
PUT(ErrMsg[E_BAD_TYPE]);
|
||||
OUT();
|
||||
}
|
||||
return E_BAD_TYPE;
|
||||
}
|
||||
|
||||
if (v.type == INT_TYPE) {
|
||||
istrue = v.v.val;
|
||||
} else {
|
||||
istrue = *(v.v.str);
|
||||
}
|
||||
if (istrue) {
|
||||
r = evaluate_expr_node(cur->sibling, locals, ans, nonconst);
|
||||
if (r == OK && (DebugFlag & DB_PRTEXPR)) {
|
||||
PUT(", ");
|
||||
PUT(PrintValue(ans, NULL));
|
||||
cur = cur->sibling->sibling;
|
||||
while(cur) {
|
||||
PUT(", ?");
|
||||
cur = cur->sibling;
|
||||
}
|
||||
PUT(") => ");
|
||||
PUT(PrintValue(ans, NULL));
|
||||
OUT();
|
||||
}
|
||||
DBG(DBufFree(&DebugBuf));
|
||||
return r;
|
||||
}
|
||||
DBG(PUT(", ?"));
|
||||
cur = cur->sibling->sibling;
|
||||
}
|
||||
|
||||
/* Return the last arg */
|
||||
r = evaluate_expr_node(cur, locals, ans, nonconst);
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
if (done) PUT(", ");
|
||||
PUT(PrintValue(ans, NULL));
|
||||
PUT(") => ");
|
||||
PUT(PrintValue(ans, NULL));
|
||||
OUT();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
@@ -2373,14 +2396,18 @@ static int FEasterdate(func_info *info)
|
||||
{
|
||||
int y, m, d;
|
||||
int g, c, x, z, e, n;
|
||||
int base;
|
||||
if (Nargs == 0) {
|
||||
base = DSEToday;
|
||||
FromDSE(DSEToday, &y, &m, &d);
|
||||
} else {
|
||||
if (ARG(0).type == INT_TYPE) {
|
||||
base = -1;
|
||||
y = ARGV(0);
|
||||
if (y < BASE) return E_2LOW;
|
||||
else if (y > BASE+YR_RANGE) return E_2HIGH;
|
||||
} else if (HASDATE(ARG(0))) {
|
||||
base = DATEPART(ARG(0));
|
||||
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
|
||||
} else return E_BAD_TYPE;
|
||||
}
|
||||
@@ -2406,7 +2433,7 @@ static int FEasterdate(func_info *info)
|
||||
|
||||
RetVal.type = DATE_TYPE;
|
||||
RETVAL = DSE(y, m, d);
|
||||
y++; } while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0)));
|
||||
y++; } while (base > -1 && RETVAL < base);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -2422,7 +2449,9 @@ static int FOrthodoxeaster(func_info *info)
|
||||
{
|
||||
int y, m, d;
|
||||
int a, b, c, dd, e, f, dse;
|
||||
int base = -1;
|
||||
if (Nargs == 0) {
|
||||
base = DSEToday;
|
||||
FromDSE(DSEToday, &y, &m, &d);
|
||||
} else {
|
||||
if (ARG(0).type == INT_TYPE) {
|
||||
@@ -2430,6 +2459,7 @@ static int FOrthodoxeaster(func_info *info)
|
||||
if (y < BASE) return E_2LOW;
|
||||
else if (y > BASE+YR_RANGE) return E_2HIGH;
|
||||
} else if (HASDATE(ARG(0))) {
|
||||
base = DATEPART(ARG(0));
|
||||
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
|
||||
} else return E_BAD_TYPE;
|
||||
}
|
||||
@@ -2449,7 +2479,7 @@ static int FOrthodoxeaster(func_info *info)
|
||||
RetVal.type = DATE_TYPE;
|
||||
RETVAL = dse;
|
||||
y++;
|
||||
} while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0)));
|
||||
} while (base > -1 && RETVAL < base);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -3896,3 +3926,38 @@ FSoleq(func_info *info)
|
||||
RETVAL = ret;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Compare two strings case-insensitively, where we KNOW
|
||||
that the second string is definitely lower-case */
|
||||
static int strcmp_lcfirst(char const *s1, char const *s2)
|
||||
{
|
||||
int r;
|
||||
while (*s1 && *s2) {
|
||||
r = tolower(*s1) - *s2;
|
||||
if (r) return r;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return tolower(*s1) - *s2;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* FindBuiltinFunc */
|
||||
/* */
|
||||
/* Find a built-in function. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
BuiltinFunc *FindBuiltinFunc(char const *name)
|
||||
{
|
||||
int top=NumFuncs-1, bot=0;
|
||||
int mid, r;
|
||||
while (top >= bot) {
|
||||
mid = (top + bot) / 2;
|
||||
r = strcmp_lcfirst(name, Func[mid].name);
|
||||
if (!r) return &Func[mid];
|
||||
else if (r > 0) bot = mid+1;
|
||||
else top = mid-1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -159,6 +159,9 @@ EXTERN DynamicBuffer Banner;
|
||||
EXTERN DynamicBuffer LineBuffer;
|
||||
EXTERN DynamicBuffer ExprBuf;
|
||||
|
||||
/* User-func recursion level */
|
||||
EXTERN INIT( unsigned int FuncRecursionLevel, 0);
|
||||
|
||||
extern int NumFullOmits, NumPartialOmits;
|
||||
|
||||
/* List of months */
|
||||
|
||||
@@ -605,7 +605,7 @@ void InitRemind(int argc, char const *argv[])
|
||||
case 'D':
|
||||
while (*arg) {
|
||||
switch(*arg++) {
|
||||
case 's': case 'S': DebugFlag |= DB_EXPR_STACKS; break;
|
||||
case 's': case 'S': DebugFlag |= DB_PARSE_EXPR; break;
|
||||
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
|
||||
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
|
||||
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
|
||||
|
||||
@@ -246,7 +246,9 @@ EXTERN char *ErrMsg[] =
|
||||
"String too long",
|
||||
"Time specified twice",
|
||||
"Cannot specify DURATION without specifying AT",
|
||||
"Odotettu viikonpäivän nimi"
|
||||
"Odotettu viikonpäivän nimi",
|
||||
"Päällekkäinen argumentin nimi"
|
||||
|
||||
};
|
||||
#endif /* MK_GLOBALS */
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@ EXTERN char *ErrMsg[] =
|
||||
"Time specified twice",
|
||||
"Cannot specify DURATION without specifying AT",
|
||||
"Nom du jour de la semaine attendu",
|
||||
"Nom de l'argument en double",
|
||||
};
|
||||
#endif /* MK_GLOBALS */
|
||||
|
||||
|
||||
@@ -236,7 +236,8 @@ EXTERN char *ErrMsg[] =
|
||||
"String too long",
|
||||
"Time specified twice",
|
||||
"Cannot specify DURATION without specifying AT",
|
||||
"Oczekiwana nazwa dnia tygodnia"
|
||||
"Oczekiwana nazwa dnia tygodnia",
|
||||
"Zduplikowana nazwa argumentu",
|
||||
};
|
||||
#endif /* MK_GLOBALS */
|
||||
|
||||
|
||||
@@ -246,6 +246,7 @@ EXTERN char *ErrMsg[] =
|
||||
"Time specified twice",
|
||||
"Cannot specify DURATION without specifying AT",
|
||||
"Esperando nome do dia da semana",
|
||||
"Nome de argumento duplicado"
|
||||
};
|
||||
#endif /* MK_GLOBALS */
|
||||
|
||||
|
||||
113
src/main.c
113
src/main.c
@@ -55,6 +55,15 @@ static void DoReminders(void);
|
||||
/* Macro for simplifying common block so as not to litter code */
|
||||
#define OUTPUT(c) do { if (output) { DBufPutc(output, c); } else { putchar(c); } } while(0)
|
||||
|
||||
void
|
||||
exitfunc(void)
|
||||
{
|
||||
if (DebugFlag & DB_PARSE_EXPR) {
|
||||
UnsetAllUserFuncs();
|
||||
print_expr_nodes_stats();
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/***************************************************************/
|
||||
/** **/
|
||||
@@ -81,7 +90,7 @@ int main(int argc, char *argv[])
|
||||
DBufInit(&(LastTrigger.tags));
|
||||
ClearLastTriggers();
|
||||
|
||||
atexit(DebugExitFunc);
|
||||
atexit(exitfunc);
|
||||
|
||||
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
|
||||
ProduceCalendar();
|
||||
@@ -289,6 +298,7 @@ static void DoReminders(void)
|
||||
case T_RemType: if (tok.val == RUN_TYPE) {
|
||||
r=DoRun(&p);
|
||||
} else {
|
||||
DestroyParser(&p);
|
||||
CreateParser(CurLine, &p);
|
||||
r=DoRem(&p);
|
||||
purge_handled = 1;
|
||||
@@ -297,10 +307,8 @@ static void DoReminders(void)
|
||||
|
||||
|
||||
/* If we don't recognize the command, do a REM by default */
|
||||
/* Note: Since the parser hasn't been used yet, we don't */
|
||||
/* need to destroy it here. */
|
||||
|
||||
default: CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break;
|
||||
default: DestroyParser(&p); CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break;
|
||||
|
||||
}
|
||||
if (r && (!Hush || r != E_RUN_DISABLED)) {
|
||||
@@ -582,6 +590,53 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* ParseExpr */
|
||||
/* */
|
||||
/* We are expecting an expression here. Parse it and return */
|
||||
/* the value node tree. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
expr_node * ParseExpr(ParsePtr p, int *r)
|
||||
{
|
||||
|
||||
int bracketed = 0;
|
||||
expr_node *node;
|
||||
|
||||
if (p->isnested) {
|
||||
*r = E_PARSE_ERR; /* Can't nest expressions */
|
||||
return NULL;
|
||||
}
|
||||
if (!p->pos) {
|
||||
*r = E_PARSE_ERR; /* Missing expression */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (isempty(*p->pos)) (p->pos)++;
|
||||
if (!*(p->pos)) {
|
||||
*r = E_EOLN;
|
||||
return NULL;
|
||||
}
|
||||
if (*p->pos == BEG_OF_EXPR) {
|
||||
(p->pos)++;
|
||||
bracketed = 1;
|
||||
}
|
||||
node = parse_expression(&(p->pos), r, NULL);
|
||||
if (*r) {
|
||||
return free_expr_tree(node);
|
||||
}
|
||||
|
||||
if (bracketed) {
|
||||
if (*p->pos != END_OF_EXPR) {
|
||||
*r = E_MISS_END;
|
||||
return free_expr_tree(node);
|
||||
}
|
||||
(p->pos)++;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* EvaluateExpr */
|
||||
@@ -593,21 +648,22 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
|
||||
int EvaluateExpr(ParsePtr p, Value *v)
|
||||
{
|
||||
|
||||
int bracketed = 0;
|
||||
int r;
|
||||
int nonconst = 0;
|
||||
expr_node *node = ParseExpr(p, &r);
|
||||
|
||||
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */
|
||||
if (!p->pos) return E_PARSE_ERR; /* Missing expression */
|
||||
while (isempty(*p->pos)) (p->pos)++;
|
||||
if (*p->pos == BEG_OF_EXPR) {
|
||||
(p->pos)++;
|
||||
bracketed = 1;
|
||||
if (r != OK) {
|
||||
return r;
|
||||
}
|
||||
r = EvalExpr(&(p->pos), v, p);
|
||||
if (!node) {
|
||||
return E_SWERR;
|
||||
}
|
||||
|
||||
r = evaluate_expr_node(node, NULL, v, &nonconst);
|
||||
free_expr_tree(node);
|
||||
if (r) return r;
|
||||
if (bracketed) {
|
||||
if (*p->pos != END_OF_EXPR) return E_MISS_END;
|
||||
(p->pos)++;
|
||||
if (nonconst) {
|
||||
p->nonconst_expr = 1;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
@@ -651,27 +707,34 @@ void Eprint(char const *fmt, ...)
|
||||
FreshLine = 0;
|
||||
if (strcmp(FileName, "-")) {
|
||||
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
|
||||
va_start(argptr, fmt);
|
||||
(void) vfprintf(ErrFp, fmt, argptr);
|
||||
(void) fputc('\n', ErrFp);
|
||||
va_end(argptr);
|
||||
if (print_callstack(ErrFp)) {
|
||||
(void) fprintf(ErrFp, ": ");
|
||||
(void) fprintf(ErrFp, "\n");
|
||||
}
|
||||
} else {
|
||||
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
|
||||
va_start(argptr, fmt);
|
||||
(void) vfprintf(ErrFp, fmt, argptr);
|
||||
(void) fputc('\n', ErrFp);
|
||||
va_end(argptr);
|
||||
if (print_callstack(ErrFp)) {
|
||||
(void) fprintf(ErrFp, ": ");
|
||||
(void) fprintf(ErrFp, "\n");
|
||||
}
|
||||
}
|
||||
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
|
||||
} else if (FileName) {
|
||||
fprintf(ErrFp, " ");
|
||||
va_start(argptr, fmt);
|
||||
(void) vfprintf(ErrFp, fmt, argptr);
|
||||
(void) fputc('\n', ErrFp);
|
||||
va_end(argptr);
|
||||
if (print_callstack(ErrFp)) {
|
||||
(void) fprintf(ErrFp, ": ");
|
||||
(void) fprintf(ErrFp, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
va_start(argptr, fmt);
|
||||
(void) vfprintf(ErrFp, fmt, argptr);
|
||||
(void) fputc('\n', ErrFp);
|
||||
va_end(argptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1025,8 +1088,8 @@ int DoDebug(ParsePtr p)
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
if (val) DebugFlag |= DB_EXPR_STACKS;
|
||||
else DebugFlag &= ~DB_EXPR_STACKS;
|
||||
if (val) DebugFlag |= DB_PARSE_EXPR;
|
||||
else DebugFlag &= ~DB_PARSE_EXPR;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
|
||||
16
src/protos.h
16
src/protos.h
@@ -43,6 +43,7 @@
|
||||
int CallUserFunc (char const *name, int nargs, ParsePtr p);
|
||||
int DoFset (ParsePtr p);
|
||||
int DoFunset (ParsePtr p);
|
||||
void UnsetAllUserFuncs(void);
|
||||
void ProduceCalendar (void);
|
||||
char const *SimpleTime (int tim);
|
||||
char const *CalendarTime (int tim, int duration);
|
||||
@@ -56,9 +57,12 @@ int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
|
||||
int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim);
|
||||
int ParseLiteralDate (char const **s, int *dse, int *tim);
|
||||
int ParseLiteralTime (char const **s, int *tim);
|
||||
int evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst);
|
||||
void print_expr_tree(expr_node *node, FILE *fp);
|
||||
expr_node *free_expr_tree(expr_node *node);
|
||||
int EvalExpr (char const **e, Value *v, ParsePtr p);
|
||||
int DoCoerce (char type, Value *v);
|
||||
void PrintValue (Value *v, FILE *fp);
|
||||
char const *PrintValue (Value *v, FILE *fp);
|
||||
int CopyValue (Value *dest, const Value *src);
|
||||
int ReadLine (void);
|
||||
int OpenFile (char const *fname);
|
||||
@@ -77,8 +81,9 @@ int JulianToGregorianOffset(int y, int m);
|
||||
int ParseChar (ParsePtr p, int *err, int peek);
|
||||
int ParseToken (ParsePtr p, DynamicBuffer *dbuf);
|
||||
int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf);
|
||||
expr_node * ParseExpr(ParsePtr p, int *r);
|
||||
void print_expr_nodes_stats(void);
|
||||
int EvaluateExpr (ParsePtr p, Value *v);
|
||||
int Evaluate (char const **s, Var *locals, ParsePtr p);
|
||||
int FnPopValStack (Value *val);
|
||||
void Eprint (char const *fmt, ...);
|
||||
void Wprint (char const *fmt, ...);
|
||||
@@ -129,9 +134,10 @@ int StrCmpi (char const *s1, char const *s2);
|
||||
#endif
|
||||
|
||||
Var *FindVar (char const *str, int create);
|
||||
SysVar *FindSysVar (char const *name);
|
||||
int DeleteVar (char const *str);
|
||||
int SetVar (char const *str, Value const *val);
|
||||
int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p);
|
||||
int GetVarValue (char const *str, Value *val);
|
||||
int DoSet (Parser *p);
|
||||
int DoUnset (Parser *p);
|
||||
int DoDump (ParsePtr p);
|
||||
@@ -145,9 +151,10 @@ int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
|
||||
unsigned int HashVal (char const *str);
|
||||
int DateOK (int y, int m, int d);
|
||||
Operator *FindOperator (char const *name, Operator where[], int num);
|
||||
BuiltinFunc *FindFunc (char const *name, BuiltinFunc where[], int num);
|
||||
BuiltinFunc *FindBuiltinFunc (char const *name);
|
||||
int InsertIntoSortBuffer (int dse, int tim, char const *body, int typ, int prio);
|
||||
void IssueSortedReminders (void);
|
||||
UserFunc *FindUserFunc(char const *name);
|
||||
int UserFuncExists (char const *fn);
|
||||
void DSEToHeb (int dse, int *hy, int *hm, int *hd);
|
||||
int HebNameToNum (char const *mname);
|
||||
@@ -207,6 +214,7 @@ void set_cloexec(FILE *fp);
|
||||
int push_call(char const *filename, char const *func, int lineno);
|
||||
void clear_callstack(void);
|
||||
int print_callstack(FILE *fp);
|
||||
int have_callstack(void);
|
||||
void pop_call(void);
|
||||
void FixSpecialType(Trigger *trig);
|
||||
void WriteJSONTrigger(Trigger const *t, int include_tags, int today);
|
||||
|
||||
68
src/types.h
68
src/types.h
@@ -30,21 +30,62 @@ typedef struct {
|
||||
int (*func)(void);
|
||||
} Operator;
|
||||
|
||||
/* New-style expr_node structure and constants */
|
||||
enum expr_node_type
|
||||
{
|
||||
N_FREE,
|
||||
N_ERROR,
|
||||
N_CONSTANT,
|
||||
N_LOCAL_VAR,
|
||||
N_SHORT_VAR,
|
||||
N_VARIABLE,
|
||||
N_SHORT_SYSVAR,
|
||||
N_SYSVAR,
|
||||
N_BUILTIN_FUNC,
|
||||
N_SHORT_USER_FUNC,
|
||||
N_USER_FUNC,
|
||||
N_BINARY_OPERATOR,
|
||||
N_UNARY_OPERATOR,
|
||||
};
|
||||
|
||||
/* Structure for passing in Nargs and out RetVal from functions */
|
||||
typedef struct {
|
||||
int nargs;
|
||||
Value *args;
|
||||
Value retval;
|
||||
} func_info;
|
||||
|
||||
/* Forward reference */
|
||||
typedef struct expr_node_struct expr_node;
|
||||
|
||||
/* Define the type of user-functions */
|
||||
typedef struct {
|
||||
char const *name;
|
||||
char minargs;
|
||||
char maxargs;
|
||||
char is_constant;
|
||||
/* Old-style function calling convention */
|
||||
int (*func)(func_info *);
|
||||
|
||||
/* New-style function calling convention */
|
||||
int (*newfunc)(expr_node *node, Value *locals, Value *ans, int *nonconst);
|
||||
} BuiltinFunc;
|
||||
|
||||
#define SHORT_NAME_BUF 16
|
||||
typedef struct expr_node_struct {
|
||||
struct expr_node_struct *child;
|
||||
struct expr_node_struct *sibling;
|
||||
enum expr_node_type type;
|
||||
int num_kids;
|
||||
union {
|
||||
Value value;
|
||||
int arg;
|
||||
BuiltinFunc *builtin_func;
|
||||
char name[SHORT_NAME_BUF];
|
||||
int (*operator_func) (struct expr_node_struct *node, Value *locals, Value *ans, int *nonconst);
|
||||
} u;
|
||||
} expr_node;
|
||||
|
||||
/* Define the structure of a variable */
|
||||
typedef struct var {
|
||||
struct var *next;
|
||||
@@ -142,6 +183,8 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
|
||||
#define MSF_TYPE 7
|
||||
#define PASSTHRU_TYPE 8
|
||||
|
||||
/* For function arguments */
|
||||
#define NO_MAX 127
|
||||
|
||||
/* DEFINES for debugging flags */
|
||||
#define DB_PRTLINE 1
|
||||
@@ -150,7 +193,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
|
||||
#define DB_DUMP_VARS 8
|
||||
#define DB_ECHO_LINE 16
|
||||
#define DB_TRACE_FILES 32
|
||||
#define DB_EXPR_STACKS 64
|
||||
#define DB_PARSE_EXPR 64
|
||||
|
||||
/* Enumeration of the tokens */
|
||||
enum TokTypes
|
||||
@@ -244,3 +287,26 @@ typedef struct {
|
||||
#define TERMINAL_BACKGROUND_UNKNOWN -1
|
||||
#define TERMINAL_BACKGROUND_DARK 0
|
||||
#define TERMINAL_BACKGROUND_LIGHT 1
|
||||
|
||||
typedef int (*SysVarFunc)(int, Value *);
|
||||
/* The structure of a system variable */
|
||||
typedef struct {
|
||||
char const *name;
|
||||
char modifiable;
|
||||
int type;
|
||||
void *value;
|
||||
int min; /* Or const-value */
|
||||
int max;
|
||||
} SysVar;
|
||||
|
||||
/* Define the data structure used to hold a user-defined function */
|
||||
typedef struct udf_struct {
|
||||
struct udf_struct *next;
|
||||
char name[VAR_NAME_LEN+1];
|
||||
expr_node *node;
|
||||
char **args;
|
||||
int nargs;
|
||||
char const *filename;
|
||||
int lineno;
|
||||
} UserFunc;
|
||||
|
||||
|
||||
280
src/userfns.c
280
src/userfns.c
@@ -30,34 +30,12 @@
|
||||
|
||||
#define FUNC_HASH_SIZE 32 /* Size of User-defined function hash table */
|
||||
|
||||
/* Define the data structure used to hold a user-defined function */
|
||||
typedef struct udf_struct {
|
||||
struct udf_struct *next;
|
||||
char name[VAR_NAME_LEN+1];
|
||||
char const *text;
|
||||
Var *locals;
|
||||
char IsActive;
|
||||
int nargs;
|
||||
char const *filename;
|
||||
int lineno;
|
||||
} UserFunc;
|
||||
|
||||
/* The hash table */
|
||||
static UserFunc *FuncHash[FUNC_HASH_SIZE];
|
||||
|
||||
/* Access to built-in functions */
|
||||
extern int NumFuncs;
|
||||
extern BuiltinFunc Func[];
|
||||
|
||||
/* We need access to the expression evaluation stack */
|
||||
extern Value ValStack[];
|
||||
extern int ValStackPtr;
|
||||
|
||||
static void DestroyUserFunc (UserFunc *f);
|
||||
static void FUnset (char const *name);
|
||||
static void FSet (UserFunc *f);
|
||||
static int SetUpLocalVars (UserFunc *f);
|
||||
static void DestroyLocalVals (UserFunc *f);
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
@@ -98,9 +76,11 @@ int DoFset(ParsePtr p)
|
||||
{
|
||||
int r;
|
||||
int c;
|
||||
int i;
|
||||
UserFunc *func;
|
||||
Var *v;
|
||||
Var *local;
|
||||
UserFunc *existing;
|
||||
Var *locals = NULL;
|
||||
Var local_array[MAX_FUNC_ARGS];
|
||||
int orig_namelen;
|
||||
|
||||
DynamicBuffer buf;
|
||||
@@ -125,6 +105,18 @@ int DoFset(ParsePtr p)
|
||||
return E_PARSE_ERR;
|
||||
}
|
||||
|
||||
/* If the function exists and was defined at the same line of the same
|
||||
file, do nothing */
|
||||
existing = FindUserFunc(DBufValue(&buf));
|
||||
if (existing) {
|
||||
if (!strcmp(existing->filename, FileName) &&
|
||||
strcmp(existing->filename, "[cmdline]") &&
|
||||
existing->lineno == LineNo) {
|
||||
DBufFree(&buf);
|
||||
/* We already have it! Our work here is done. */
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
func = NEW(UserFunc);
|
||||
if (!func) {
|
||||
DBufFree(&buf);
|
||||
@@ -143,25 +135,22 @@ int DoFset(ParsePtr p)
|
||||
StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN);
|
||||
DBufFree(&buf);
|
||||
if (!Hush) {
|
||||
if (FindFunc(func->name, Func, NumFuncs)) {
|
||||
if (FindBuiltinFunc(func->name)) {
|
||||
Eprint("%s: `%s'", ErrMsg[E_REDEF_FUNC], func->name);
|
||||
}
|
||||
}
|
||||
func->locals = NULL;
|
||||
func->text = NULL;
|
||||
func->IsActive = 0;
|
||||
func->node = NULL;
|
||||
func->nargs = 0;
|
||||
func->args = NULL;
|
||||
|
||||
/* Get the local variables - we insert the local variables in REVERSE
|
||||
order, but that's OK, because we pop them off the stack in reverse
|
||||
order, too, so everything works out just fine. */
|
||||
/* Get the local variables */
|
||||
|
||||
c=ParseNonSpaceChar(p, &r, 1);
|
||||
if (r) return r;
|
||||
if (c == ')') {
|
||||
(void) ParseNonSpaceChar(p, &r, 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
locals = local_array;
|
||||
while(1) {
|
||||
if ( (r=ParseIdentifier(p, &buf)) ) return r;
|
||||
if (*DBufValue(&buf) == '$') {
|
||||
@@ -170,27 +159,24 @@ int DoFset(ParsePtr p)
|
||||
return E_BAD_ID;
|
||||
}
|
||||
/* If we've already seen this local variable, error */
|
||||
local = func->locals;
|
||||
while(local) {
|
||||
if (!StrinCmp(DBufValue(&buf), local->name, VAR_NAME_LEN)) {
|
||||
for (i=0; i<func->nargs; i++) {
|
||||
if (!StrinCmp(DBufValue(&buf), local_array[i].name, VAR_NAME_LEN)) {
|
||||
DBufFree(&buf);
|
||||
DestroyUserFunc(func);
|
||||
return E_REPEATED_ARG;
|
||||
}
|
||||
local = local->next;
|
||||
}
|
||||
v = NEW(Var);
|
||||
if (!v) {
|
||||
DBufFree(&buf);
|
||||
DestroyUserFunc(func);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
i = func->nargs;
|
||||
if (i >= MAX_FUNC_ARGS-1) {
|
||||
DBufFree(&buf);
|
||||
DestroyUserFunc(func);
|
||||
return E_2MANY_ARGS;
|
||||
}
|
||||
local_array[i].v.type = ERR_TYPE;
|
||||
StrnCpy(local_array[i].name, DBufValue(&buf), VAR_NAME_LEN);
|
||||
local_array[i].next = &(local_array[i+1]);
|
||||
local_array[i+1].next = NULL;
|
||||
func->nargs++;
|
||||
v->v.type = ERR_TYPE;
|
||||
StrnCpy(v->name, DBufValue(&buf), VAR_NAME_LEN);
|
||||
DBufFree(&buf);
|
||||
v->next = func->locals;
|
||||
func->locals = v;
|
||||
c = ParseNonSpaceChar(p, &r, 0);
|
||||
if (c == ')') break;
|
||||
else if (c != ',') {
|
||||
@@ -205,7 +191,6 @@ int DoFset(ParsePtr p)
|
||||
if (c == '=') {
|
||||
(void) ParseNonSpaceChar(p, &r, 0);
|
||||
}
|
||||
/* Copy the text over */
|
||||
if (p->isnested) {
|
||||
Eprint("%s", ErrMsg[E_CANTNEST_FDEF]);
|
||||
DestroyUserFunc(func);
|
||||
@@ -219,10 +204,23 @@ int DoFset(ParsePtr p)
|
||||
DestroyUserFunc(func);
|
||||
return E_EOLN;
|
||||
}
|
||||
func->text = StrDup(p->pos);
|
||||
if (!func->text) {
|
||||
DestroyUserFunc(func);
|
||||
return E_NO_MEM;
|
||||
/* Parse the expression */
|
||||
func->node = parse_expression(&(p->pos), &r, locals);
|
||||
if (!func->node) {
|
||||
DestroyUserFunc(func);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Save the argument names */
|
||||
if (func->nargs) {
|
||||
func->args = calloc(sizeof(char *), func->nargs);
|
||||
for (i=0; i<func->nargs; i++) {
|
||||
func->args[i] = StrDup(local_array[i].name);
|
||||
if (!func->args[i]) {
|
||||
DestroyUserFunc(func);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If an old definition of this function exists, destroy it */
|
||||
@@ -246,23 +244,22 @@ int DoFset(ParsePtr p)
|
||||
/***************************************************************/
|
||||
static void DestroyUserFunc(UserFunc *f)
|
||||
{
|
||||
Var *v, *prev;
|
||||
|
||||
/* Free the local variables first */
|
||||
v = f->locals;
|
||||
while(v) {
|
||||
DestroyValue(v->v);
|
||||
prev = v;
|
||||
v = v->next;
|
||||
free(prev);
|
||||
}
|
||||
int i;
|
||||
|
||||
/* Free the function definition */
|
||||
if (f->text) free( (char *) f->text);
|
||||
if (f->node) free_expr_tree(f->node);
|
||||
|
||||
/* Free the filename */
|
||||
if (f->filename) free( (char *) f->filename);
|
||||
|
||||
/* Free arg names */
|
||||
if (f->args) {
|
||||
for (i=0; i<f->nargs; i++) {
|
||||
if (f->args[i]) free(f->args[i]);
|
||||
}
|
||||
free(f->args);
|
||||
}
|
||||
|
||||
/* Free the data structure itself */
|
||||
free(f);
|
||||
}
|
||||
@@ -308,129 +305,17 @@ static void FSet(UserFunc *f)
|
||||
FuncHash[h] = f;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* CallUserFunc */
|
||||
/* */
|
||||
/* Call a user-defined function. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
int CallUserFunc(char const *name, int nargs, ParsePtr p)
|
||||
UserFunc *FindUserFunc(char const *name)
|
||||
{
|
||||
UserFunc *f;
|
||||
int h = HashVal(name) % FUNC_HASH_SIZE;
|
||||
int i;
|
||||
char const *s;
|
||||
UserFunc *f;
|
||||
int h = HashVal(name) % FUNC_HASH_SIZE;
|
||||
|
||||
/* Search for the function */
|
||||
f = FuncHash[h];
|
||||
while (f && StrinCmp(name, f->name, VAR_NAME_LEN)) f = f->next;
|
||||
if (!f) {
|
||||
Eprint("%s: `%s'", ErrMsg[E_UNDEF_FUNC], name);
|
||||
return E_UNDEF_FUNC;
|
||||
}
|
||||
/* Debugging stuff */
|
||||
if (DebugFlag & DB_PRTEXPR) {
|
||||
fprintf(ErrFp, "%s %s(", ErrMsg[E_ENTER_FUN], f->name);
|
||||
for (i=0; i<nargs; i++) {
|
||||
PrintValue(&ValStack[ValStackPtr - nargs + i], ErrFp);
|
||||
if (i<nargs-1) fprintf(ErrFp, ", ");
|
||||
}
|
||||
fprintf(ErrFp, ")\n");
|
||||
}
|
||||
/* Detect illegal recursive call */
|
||||
if (f->IsActive) {
|
||||
if (DebugFlag &DB_PRTEXPR) {
|
||||
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
|
||||
fprintf(ErrFp, "%s\n", ErrMsg[E_RECURSIVE]);
|
||||
}
|
||||
return E_RECURSIVE;
|
||||
}
|
||||
|
||||
/* Check number of args */
|
||||
if (nargs != f->nargs) {
|
||||
if (DebugFlag &DB_PRTEXPR) {
|
||||
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
|
||||
fprintf(ErrFp, "%s\n",
|
||||
ErrMsg[(nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS]);
|
||||
}
|
||||
return (nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS;
|
||||
}
|
||||
/* Found the function - set up a local variable frame */
|
||||
h = SetUpLocalVars(f);
|
||||
if (h) {
|
||||
if (DebugFlag &DB_PRTEXPR) {
|
||||
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
|
||||
fprintf(ErrFp, "%s\n", ErrMsg[h]);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Evaluate the expression */
|
||||
f->IsActive = 1;
|
||||
s = f->text;
|
||||
|
||||
/* Skip the opening bracket, if there's one */
|
||||
while (isempty(*s)) s++;
|
||||
if (*s == BEG_OF_EXPR) {
|
||||
s++;
|
||||
}
|
||||
push_call(f->filename, f->name, f->lineno);
|
||||
h = Evaluate(&s, f->locals, p);
|
||||
if (h == OK) {
|
||||
pop_call();
|
||||
}
|
||||
f->IsActive = 0;
|
||||
DestroyLocalVals(f);
|
||||
if (DebugFlag &DB_PRTEXPR) {
|
||||
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
|
||||
if (h) fprintf(ErrFp, "%s\n", ErrMsg[h]);
|
||||
else {
|
||||
PrintValue(&ValStack[ValStackPtr-1], ErrFp);
|
||||
fprintf(ErrFp, "\n");
|
||||
}
|
||||
}
|
||||
return h;
|
||||
/* Search for the function */
|
||||
f = FuncHash[h];
|
||||
while (f && StrinCmp(name, f->name, VAR_NAME_LEN)) f = f->next;
|
||||
return f;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* SetUpLocalVars */
|
||||
/* */
|
||||
/* Set up the local variables from the stack frame. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static int SetUpLocalVars(UserFunc *f)
|
||||
{
|
||||
int i, r;
|
||||
Var *var;
|
||||
|
||||
for (i=0, var=f->locals; var && i<f->nargs; var=var->next, i++) {
|
||||
if ( (r=FnPopValStack(&(var->v))) ) {
|
||||
DestroyLocalVals(f);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* DestroyLocalVals */
|
||||
/* */
|
||||
/* Destroy the values of all local variables after evaluating */
|
||||
/* the function. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static void DestroyLocalVals(UserFunc *f)
|
||||
{
|
||||
Var *v = f->locals;
|
||||
|
||||
while(v) {
|
||||
DestroyValue(v->v);
|
||||
v = v->next;
|
||||
}
|
||||
}
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* UserFuncExists */
|
||||
@@ -441,12 +326,33 @@ static void DestroyLocalVals(UserFunc *f)
|
||||
/***************************************************************/
|
||||
int UserFuncExists(char const *fn)
|
||||
{
|
||||
UserFunc *f;
|
||||
int h = HashVal(fn) % FUNC_HASH_SIZE;
|
||||
UserFunc *f = FindUserFunc(fn);
|
||||
|
||||
f = FuncHash[h];
|
||||
while (f && StrinCmp(fn, f->name, VAR_NAME_LEN)) f = f->next;
|
||||
if (!f) return -1;
|
||||
else return f->nargs;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
/* */
|
||||
/* UnsetAllUserFuncs */
|
||||
/* */
|
||||
/* Call FUNSET on all user funcs. Used with -ds flag to */
|
||||
/* ensure no expr_node memory leaks. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
void
|
||||
UnsetAllUserFuncs(void)
|
||||
{
|
||||
UserFunc *f;
|
||||
UserFunc *next;
|
||||
int i;
|
||||
for (i=0; i<FUNC_HASH_SIZE; i++) {
|
||||
f = FuncHash[i];
|
||||
while(f) {
|
||||
next = f->next;
|
||||
DestroyUserFunc(f);
|
||||
f = next;
|
||||
}
|
||||
FuncHash[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
54
src/utils.c
54
src/utils.c
@@ -187,31 +187,33 @@ typedef struct cs_s {
|
||||
} cs;
|
||||
|
||||
static cs *callstack = NULL;
|
||||
static cs *freecs = NULL;
|
||||
|
||||
static void
|
||||
destroy_cs(cs *entry)
|
||||
{
|
||||
if (entry->filename) free( (void *) entry->filename);
|
||||
if (entry->func) free( (void *) entry->func);
|
||||
free( (void *) entry);
|
||||
entry->next = freecs;
|
||||
freecs = entry;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
push_call(char const *filename, char const *func, int lineno)
|
||||
{
|
||||
cs *entry = NEW(cs);
|
||||
if (!entry) {
|
||||
return E_NO_MEM;
|
||||
cs *entry;
|
||||
if (freecs) {
|
||||
entry = freecs;
|
||||
freecs = freecs->next;
|
||||
} else {
|
||||
entry = NEW(cs);
|
||||
if (!entry) {
|
||||
return E_NO_MEM;
|
||||
}
|
||||
}
|
||||
entry->next = NULL;
|
||||
entry->filename = StrDup(filename);
|
||||
entry->func = StrDup(func);
|
||||
entry->filename = filename;
|
||||
entry->func = func;
|
||||
entry->lineno = lineno;
|
||||
if (!entry->filename || !entry->func) {
|
||||
destroy_cs(entry);
|
||||
return E_NO_MEM;
|
||||
}
|
||||
entry->next = callstack;
|
||||
callstack = entry;
|
||||
return OK;
|
||||
@@ -233,11 +235,31 @@ clear_callstack(void)
|
||||
static void
|
||||
print_callstack_aux(FILE *fp, cs *entry)
|
||||
{
|
||||
if (entry) {
|
||||
print_callstack_aux(fp, entry->next);
|
||||
fprintf(fp, "\n");
|
||||
(void) fprintf(fp, "%s(%d): In function `%s'", entry->filename, entry->lineno, entry->func);
|
||||
int i = 0;
|
||||
char const *in = "In";
|
||||
cs *prev = NULL;
|
||||
while(entry) {
|
||||
if (prev) {
|
||||
fprintf(fp, "\n");
|
||||
in = "Called from";
|
||||
}
|
||||
(void) fprintf(fp, " %s(%d): [#%d] %s function `%s'", entry->filename, entry->lineno, i, in, entry->func);
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
i++;
|
||||
if (i > 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (entry) {
|
||||
(void) fprintf(fp, "\n [remaining call frames omitted]");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
have_callstack(void)
|
||||
{
|
||||
return (callstack != NULL);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
27
src/var.c
27
src/var.c
@@ -39,8 +39,6 @@ static int IntMax = INT_MAX;
|
||||
|
||||
static Var *VHashTbl[VAR_HASH_SIZE];
|
||||
|
||||
typedef int (*SysVarFunc)(int, Value *);
|
||||
|
||||
static double
|
||||
strtod_in_c_locale(char const *str, char **endptr)
|
||||
{
|
||||
@@ -511,20 +509,10 @@ int SetVar(char const *str, Value const *val)
|
||||
/* Get a copy of the value of the variable. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
int GetVarValue(char const *str, Value *val, Var *locals, ParsePtr p)
|
||||
int GetVarValue(char const *str, Value *val)
|
||||
{
|
||||
Var *v;
|
||||
|
||||
/* Try searching local variables first */
|
||||
v = locals;
|
||||
while (v) {
|
||||
if (! StrinCmp(str, v->name, VAR_NAME_LEN))
|
||||
return CopyValue(val, &v->v);
|
||||
v = v->next;
|
||||
}
|
||||
|
||||
/* Global variable... mark expression as non-constant */
|
||||
if (p) p->nonconst_expr = 1;
|
||||
v=FindVar(str, 0);
|
||||
|
||||
if (!v) {
|
||||
@@ -785,16 +773,6 @@ int DoPreserve (Parser *p)
|
||||
/* */
|
||||
/***************************************************************/
|
||||
|
||||
/* The structure of a system variable */
|
||||
typedef struct {
|
||||
char const *name;
|
||||
char modifiable;
|
||||
int type;
|
||||
void *value;
|
||||
int min; /* Or const-value */
|
||||
int max;
|
||||
} SysVar;
|
||||
|
||||
/* Macro to access "min" but as a constval. Just to make source more
|
||||
readable */
|
||||
#define constval min
|
||||
@@ -919,7 +897,6 @@ static SysVar SysVarArr[] = {
|
||||
};
|
||||
|
||||
#define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) )
|
||||
static SysVar *FindSysVar (char const *name);
|
||||
static void DumpSysVar (char const *name, const SysVar *v);
|
||||
/***************************************************************/
|
||||
/* */
|
||||
@@ -1016,7 +993,7 @@ int GetSysVar(char const *name, Value *val)
|
||||
/* Find a system var with specified name. */
|
||||
/* */
|
||||
/***************************************************************/
|
||||
static SysVar *FindSysVar(char const *name)
|
||||
SysVar *FindSysVar(char const *name)
|
||||
{
|
||||
int top=NUMSYSVARS-1, bottom=0;
|
||||
int mid=(top + bottom) / 2;
|
||||
|
||||
@@ -35,8 +35,8 @@ set a ansicolor(-1, 0, 0)
|
||||
set a ansicolor(42, 42, 256)
|
||||
set a ansicolor("foo")
|
||||
set a ansicolor("1 1")
|
||||
set a ansicolor("-1 -1 0");
|
||||
set a ansicolor("256 1 1");
|
||||
set a ansicolor("-1 -1 0")
|
||||
set a ansicolor("256 1 1")
|
||||
set a ansicolor(128, 128, 128, 2)
|
||||
set a ansicolor(128, 128, 128, -1)
|
||||
set a ansicolor(128, 128, 128, 0, 2)
|
||||
|
||||
Reference in New Issue
Block a user