1 /***************************************************************/
2 /*                                                             */
3 /*  FUNCS.C                                                    */
4 /*                                                             */
5 /*  This file contains the built-in functions used in          */
6 /*  expressions.                                               */
7 /*                                                             */
8 /*  This file is part of REMIND.                               */
9 /*  Copyright (C) 1992-2021 by Dianne Skoll                    */
10 /*                                                             */
11 /***************************************************************/
12 
13 #include "version.h"
14 #include "config.h"
15 
16 #include <stdio.h>
17 
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <math.h>
22 
23 #include <unistd.h>
24 
25 #ifdef HAVE_SYS_FILE_H
26 #include <sys/file.h>
27 #endif
28 
29 #include <sys/types.h>
30 
31 #include <sys/stat.h>
32 
33 #ifdef TM_IN_SYS_TIME
34 #include <sys/time.h>
35 #else
36 #include <time.h>
37 #endif
38 
39 #ifndef R_OK
40 #define R_OK 4
41 #define W_OK 2
42 #define X_OK 1
43 #endif
44 
45 #include "types.h"
46 #include "globals.h"
47 #include "protos.h"
48 #include "err.h"
49 #include "expr.h"
50 
51 /* Defines that used to be static variables */
52 #define Nargs (info->nargs)
53 #define RetVal (info->retval)
54 
55 /* Function prototypes */
56 static int FADawn          (func_info *);
57 static int FADusk          (func_info *);
58 static int FAbs            (func_info *);
59 static int FAccess         (func_info *);
60 static int FAmpm           (func_info *);
61 static int FIsAny          (func_info *);
62 static int FArgs           (func_info *);
63 static int FAsc            (func_info *);
64 static int FBaseyr         (func_info *);
65 static int FChar           (func_info *);
66 static int FChoose         (func_info *);
67 static int FCoerce         (func_info *);
68 static int FCurrent        (func_info *);
69 static int FDate           (func_info *);
70 static int FDateTime       (func_info *);
71 static int FDatepart       (func_info *);
72 static int FDawn           (func_info *);
73 static int FDay            (func_info *);
74 static int FDaysinmon      (func_info *);
75 static int FDefined        (func_info *);
76 static int FDosubst        (func_info *);
77 static int FDusk           (func_info *);
78 static int FEasterdate     (func_info *);
79 static int FEvalTrig       (func_info *);
80 static int FFiledate       (func_info *);
81 static int FFiledatetime   (func_info *);
82 static int FFiledir        (func_info *);
83 static int FFilename       (func_info *);
84 static int FGetenv         (func_info *);
85 static int FHebdate        (func_info *);
86 static int FHebday         (func_info *);
87 static int FHebmon         (func_info *);
88 static int FHebyear        (func_info *);
89 static int FHour           (func_info *);
90 static int FIif            (func_info *);
91 static int FIndex          (func_info *);
92 static int FIsdst          (func_info *);
93 static int FIsleap         (func_info *);
94 static int FIsomitted      (func_info *);
95 static int FLanguage       (func_info *);
96 static int FLower          (func_info *);
97 static int FMax            (func_info *);
98 static int FMin            (func_info *);
99 static int FMinsfromutc    (func_info *);
100 static int FMinute         (func_info *);
101 static int FMon            (func_info *);
102 static int FMonnum         (func_info *);
103 static int FMoondate       (func_info *);
104 static int FMoondatetime   (func_info *);
105 static int FMoonphase      (func_info *);
106 static int FMoontime       (func_info *);
107 static int FNDawn          (func_info *);
108 static int FNDusk          (func_info *);
109 static int FNonomitted     (func_info *);
110 static int FNow            (func_info *);
111 static int FOrd            (func_info *);
112 static int FOstype         (func_info *);
113 static int FPlural         (func_info *);
114 static int FPsmoon         (func_info *);
115 static int FPsshade        (func_info *);
116 static int FRealCurrent    (func_info *);
117 static int FRealnow        (func_info *);
118 static int FRealtoday      (func_info *);
119 static int FSgn            (func_info *);
120 static int FShell          (func_info *);
121 static int FSlide          (func_info *);
122 static int FStrlen         (func_info *);
123 static int FSubstr         (func_info *);
124 static int FSunrise        (func_info *);
125 static int FSunset         (func_info *);
126 static int FTime           (func_info *);
127 static int FTimepart       (func_info *);
128 static int FToday          (func_info *);
129 static int FTrigback       (func_info *);
130 static int FTrigdate       (func_info *);
131 static int FTrigdatetime   (func_info *);
132 static int FTrigdelta      (func_info *);
133 static int FTrigduration   (func_info *);
134 static int FTrigeventduration(func_info *);
135 static int FTrigeventstart (func_info *);
136 static int FTrigfrom       (func_info *);
137 static int FTrigger        (func_info *);
138 static int FTrigpriority   (func_info *);
139 static int FTrigrep        (func_info *);
140 static int FTrigscanfrom   (func_info *);
141 static int FTrigtime       (func_info *);
142 static int FTrigtimedelta  (func_info *);
143 static int FTrigtimerep    (func_info *);
144 static int FTriguntil      (func_info *);
145 static int FTrigvalid      (func_info *);
146 static int FTypeof         (func_info *);
147 static int FTzconvert      (func_info *);
148 static int FUpper          (func_info *);
149 static int FValue          (func_info *);
150 static int FVersion        (func_info *);
151 static int FWeekno         (func_info *);
152 static int FWkday          (func_info *);
153 static int FWkdaynum       (func_info *);
154 static int FYear           (func_info *);
155 static int FShellescape    (func_info *);
156 
157 static int CleanUpAfterFunc (func_info *);
158 static int CheckArgs       (BuiltinFunc *f, int nargs);
159 static int SunStuff        (int rise, double cosz, int jul);
160 
161 /* "Overload" the struct Operator definition */
162 #define NO_MAX 127
163 
164 /* Caches for extracting months, days, years from dates - may
165    improve performance slightly. */
166 static int CacheJul = -1;
167 static int CacheYear, CacheMon, CacheDay;
168 
169 static int CacheHebJul = -1;
170 static int CacheHebYear, CacheHebMon, CacheHebDay;
171 
172 /* We need access to the value stack */
173 extern Value ValStack[];
174 extern int ValStackPtr;
175 
176 /* Macro for accessing arguments from the value stack - args are numbered
177    from 0 to (Nargs - 1) */
178 #define ARG(x) (ValStack[ValStackPtr - Nargs + (x)])
179 
180 #define ARGV(x) ARG(x).v.val
181 #define ARGSTR(x) ARG(x).v.str
182 
183 #define ASSERT_TYPE(x, t) if (ARG(x).type != t) return E_BAD_TYPE
184 
185 /* Macro for getting date part of a date or datetime value */
186 #define DATEPART(x) ((x).type == DATE_TYPE ? (x).v.val : ((x).v.val / MINUTES_PER_DAY))
187 
188 /* Macro for getting time part of a time or datetime value */
189 #define TIMEPART(x) ((x).type == TIME_TYPE ? (x).v.val : ((x).v.val % MINUTES_PER_DAY))
190 
191 #define HASDATE(x) ((x).type == DATE_TYPE || (x).type == DATETIME_TYPE)
192 #define HASTIME(x) ((x).type == TIME_TYPE || (x).type == DATETIME_TYPE)
193 
194 /* Macro for copying a value while destroying original copy */
195 #define DCOPYVAL(x, y) ( (x) = (y), (y).type = ERR_TYPE )
196 
197 /* Get at RetVal.v.val easily */
198 #define RETVAL info->retval.v.val
199 
200 /* Convenience macros */
201 #define UPPER(c) (islower(c) ? toupper(c) : c)
202 #define LOWER(c) (isupper(c) ? tolower(c) : c)
203 
204 /* The array holding the built-in functions. */
205 BuiltinFunc Func[] = {
206 /*	Name		minargs maxargs	is_constant func   */
207 
208     {   "abs",          1,      1,      1,          FAbs },
209     {   "access",       2,      2,      0,          FAccess },
210     {   "adawn",        0,      1,      0,          FADawn},
211     {   "adusk",        0,      1,      0,          FADusk},
212     {   "ampm",         1,      3,      1,          FAmpm   },
213     {   "args",         1,      1,      0,          FArgs   },
214     {   "asc",          1,      1,      1,          FAsc    },
215     {   "baseyr",       0,      0,      1,          FBaseyr },
216     {   "char",         1,      NO_MAX, 1,          FChar   },
217     {   "choose",       2,      NO_MAX, 1,          FChoose },
218     {   "coerce",       2,      2,      1,          FCoerce },
219     {   "current",      0,      0,      0,          FCurrent },
220     {   "date",         3,      3,      1,          FDate   },
221     {   "datepart",     1,      1,      1,          FDatepart },
222     {   "datetime",     2,      5,      1,          FDateTime },
223     {   "dawn",         0,      1,      0,          FDawn},
224     {   "day",          1,      1,      1,          FDay    },
225     {   "daysinmon",    2,      2,      1,          FDaysinmon },
226     {   "defined",      1,      1,      0,          FDefined },
227     {   "dosubst",      1,      3,      0,          FDosubst },
228     {   "dusk",         0,      1,      0,          FDusk },
229     {   "easterdate",   1,      1,      0,          FEasterdate },
230     {   "evaltrig",     1,      2,      0,          FEvalTrig },
231     {   "filedate",     1,      1,      0,          FFiledate },
232     {   "filedatetime", 1,      1,      0,          FFiledatetime },
233     {   "filedir",      0,      0,      0,          FFiledir },
234     {   "filename",     0,      0,      0,          FFilename },
235     {   "getenv",       1,      1,      0,          FGetenv },
236     {   "hebdate",      2,      5,      0,          FHebdate },
237     {   "hebday",       1,      1,      0,          FHebday },
238     {   "hebmon",       1,      1,      0,          FHebmon },
239     {   "hebyear",      1,      1,      0,          FHebyear },
240     {   "hour",         1,      1,      1,          FHour   },
241     {   "iif",          1,      NO_MAX, 1,          FIif    },
242     {   "index",        2,      3,      1,          FIndex  },
243     {   "isany",        1,      NO_MAX, 1,          FIsAny  },
244     {   "isdst",        0,      2,      0,          FIsdst },
245     {   "isleap",       1,      1,      1,          FIsleap },
246     {   "isomitted",    1,      1,      0,          FIsomitted },
247     {   "language",     0,      0,      1,          FLanguage },
248     {   "lower",        1,      1,      1,          FLower  },
249     {   "max",          1,      NO_MAX, 1,          FMax    },
250     {   "min",          1,      NO_MAX, 1,          FMin    },
251     {   "minsfromutc",  0,      2,      0,          FMinsfromutc },
252     {   "minute",       1,      1,      1,          FMinute },
253     {   "mon",          1,      1,      1,          FMon    },
254     {   "monnum",       1,      1,      1,          FMonnum },
255     {   "moondate",     1,      3,      0,          FMoondate },
256     {   "moondatetime", 1,      3,      0,          FMoondatetime },
257     {   "moonphase",    0,      2,      0,          FMoonphase },
258     {   "moontime",     1,      3,      0,          FMoontime },
259     {   "ndawn",        0,      1,      0,          FNDawn},
260     {   "ndusk",        0,      1,      0,          FNDusk},
261     {   "nonomitted",   2,      NO_MAX, 0,          FNonomitted },
262     {   "now",          0,      0,      0,          FNow    },
263     {   "ord",          1,      1,      1,          FOrd    },
264     {   "ostype",       0,      0,      1,          FOstype },
265     {   "plural",       1,      3,      1,          FPlural },
266     {   "psmoon",       1,      4,      1,          FPsmoon},
267     {   "psshade",      1,      3,      1,          FPsshade},
268     {   "realcurrent",  0,      0,      0,          FRealCurrent},
269     {   "realnow",      0,      0,      0,          FRealnow},
270     {   "realtoday",    0,      0,      0,          FRealtoday },
271     {   "sgn",          1,      1,      1,          FSgn    },
272     {   "shell",        1,      2,      0,          FShell  },
273     {   "shellescape",  1,      1,      1,          FShellescape },
274     {   "slide",        2,      NO_MAX, 0,          FSlide  },
275     {   "strlen",       1,      1,      1,          FStrlen },
276     {   "substr",       2,      3,      1,          FSubstr },
277     {   "sunrise",      0,      1,      0,          FSunrise},
278     {   "sunset",       0,      1,      0,          FSunset },
279     {   "time",         2,      2,      1,          FTime   },
280     {   "timepart",     1,      1,      1,          FTimepart },
281     {   "today",        0,      0,      0,          FToday  },
282     {   "trigback",     0,      0,      0,          FTrigback },
283     {   "trigdate",     0,      0,      0,          FTrigdate },
284     {   "trigdatetime", 0,      0,      0,          FTrigdatetime },
285     {   "trigdelta",    0,      0,      0,          FTrigdelta },
286     {   "trigduration", 0,      0,      0,          FTrigduration },
287     {   "trigeventduration", 0, 0,      0,          FTrigeventduration },
288     {   "trigeventstart", 0,    0,      0,          FTrigeventstart },
289     {   "trigfrom",     0,      0,      0,          FTrigfrom },
290     {   "trigger",      1,      3,      0,          FTrigger },
291     {   "trigpriority", 0,      0,      0,          FTrigpriority },
292     {   "trigrep",      0,      0,      0,          FTrigrep },
293     {   "trigscanfrom", 0,      0,      0,          FTrigscanfrom },
294     {   "trigtime",     0,      0,      0,          FTrigtime },
295     {   "trigtimedelta",0,      0,      0,          FTrigtimedelta },
296     {   "trigtimerep",  0,      0,      0,          FTrigtimerep },
297     {   "triguntil",    0,      0,      0,          FTriguntil },
298     {   "trigvalid",    0,      0,      0,          FTrigvalid },
299     {   "typeof",       1,      1,      1,          FTypeof },
300     {   "tzconvert",    2,      3,      0,          FTzconvert },
301     {   "upper",        1,      1,      1,          FUpper  },
302     {   "value",        1,      2,      0,          FValue  },
303     {   "version",      0,      0,      1,          FVersion },
304     {   "weekno",       0,      3,      1,          FWeekno },
305     {   "wkday",        1,      1,      1,          FWkday  },
306     {   "wkdaynum",     1,      1,      1,          FWkdaynum },
307     {   "year",         1,      1,      1,          FYear   }
308 };
309 
310 /* Need a variable here - Func[] array not really visible to outside. */
311 int NumFuncs = sizeof(Func) / sizeof(Operator) ;
312 
313 /***************************************************************/
314 /*                                                             */
315 /*  CallFunc                                                   */
316 /*                                                             */
317 /*  Call a function given a pointer to it, and the number      */
318 /*  of arguments supplied.                                     */
319 /*                                                             */
320 /***************************************************************/
CallFunc(BuiltinFunc * f,int nargs)321 int CallFunc(BuiltinFunc *f, int nargs)
322 {
323     register int r = CheckArgs(f, nargs);
324     int i;
325 
326     func_info info_obj;
327     func_info *info = &info_obj;
328 
329     Nargs = nargs;
330     RetVal.type = ERR_TYPE;
331 
332     if (DebugFlag & DB_PRTEXPR) {
333 	fprintf(ErrFp, "%s(", f->name);
334 	for (i=0; i<nargs; i++) {
335 	    PrintValue(&ARG(i), ErrFp);
336 	    if (i<nargs-1) fprintf(ErrFp, ", ");
337 	}
338 	fprintf(ErrFp, ") => ");
339 	if (r) {
340 	    fprintf(ErrFp, "%s\n", ErrMsg[r]);
341 	    return r;
342 	}
343     }
344     if (r) {
345 	Eprint("%s(): %s", f->name, ErrMsg[r]);
346 	return r;
347     }
348 
349     r = (*(f->func))(info);
350     if (r) {
351 	DestroyValue(RetVal);
352 	if (DebugFlag & DB_PRTEXPR)
353 	    fprintf(ErrFp, "%s\n", ErrMsg[r]);
354 	else
355 	    Eprint("%s(): %s", f->name, ErrMsg[r]);
356 	return r;
357     }
358     if (DebugFlag & DB_PRTEXPR) {
359 	PrintValue(&RetVal, ErrFp);
360 	fprintf(ErrFp, "\n");
361     }
362     r = CleanUpAfterFunc(info);
363     return r;
364 }
365 
366 /***************************************************************/
367 /*                                                             */
368 /*  CheckArgs                                                  */
369 /*                                                             */
370 /*  Check that the right number of args have been supplied     */
371 /*  for a function.                                            */
372 /*                                                             */
373 /***************************************************************/
CheckArgs(BuiltinFunc * f,int nargs)374 static int CheckArgs(BuiltinFunc *f, int nargs)
375 {
376     if (nargs < f->minargs) return E_2FEW_ARGS;
377     if (nargs > f->maxargs && f->maxargs != NO_MAX) return E_2MANY_ARGS;
378     return OK;
379 }
380 /***************************************************************/
381 /*                                                             */
382 /*  CleanUpAfterFunc                                           */
383 /*                                                             */
384 /*  Clean up the stack after a function call - remove          */
385 /*  args and push the new value.                               */
386 /*                                                             */
387 /***************************************************************/
CleanUpAfterFunc(func_info * info)388 static int CleanUpAfterFunc(func_info *info)
389 {
390     Value v;
391     int i;
392 
393     for (i=0; i<Nargs; i++) {
394 	PopValStack(v);
395 	DestroyValue(v);
396     }
397     PushValStack(RetVal);
398     return OK;
399 }
400 
401 /***************************************************************/
402 /*                                                             */
403 /*  RetStrVal                                                  */
404 /*                                                             */
405 /*  Return a string value from a function.                     */
406 /*                                                             */
407 /***************************************************************/
RetStrVal(char const * s,func_info * info)408 static int RetStrVal(char const *s, func_info *info)
409 {
410     RetVal.type = STR_TYPE;
411     if (!s) {
412 	RetVal.v.str = malloc(1);
413 	if (RetVal.v.str) *RetVal.v.str = 0;
414     } else {
415 	RetVal.v.str = StrDup(s);
416     }
417 
418     if (!RetVal.v.str) {
419 	RetVal.type = ERR_TYPE;
420 	return E_NO_MEM;
421     }
422     return OK;
423 }
424 
425 
426 /***************************************************************/
427 /*                                                             */
428 /*  FStrlen - string length                                    */
429 /*                                                             */
430 /***************************************************************/
FStrlen(func_info * info)431 static int FStrlen(func_info *info)
432 {
433     Value *v = &ARG(0);
434     if (v->type != STR_TYPE) return E_BAD_TYPE;
435     RetVal.type = INT_TYPE;
436     RETVAL = strlen(v->v.str);
437     return OK;
438 }
439 
440 /***************************************************************/
441 /*                                                             */
442 /*  FBaseyr - system base year                                 */
443 /*                                                             */
444 /***************************************************************/
FBaseyr(func_info * info)445 static int FBaseyr(func_info *info)
446 {
447     RetVal.type = INT_TYPE;
448     RETVAL = BASE;
449     return OK;
450 }
451 
452 /***************************************************************/
453 /*                                                             */
454 /*  FDate - make a date from year, month, day.                 */
455 /*                                                             */
456 /***************************************************************/
FDate(func_info * info)457 static int FDate(func_info *info)
458 {
459     int y, m, d;
460     int ytemp, mtemp, dtemp;
461 
462     /* Any arg can be a date (in which case we use the corresponding
463        component) or an integer */
464     if (HASDATE(ARG(0))) {
465 	FromJulian(DATEPART(ARG(0)), &ytemp, &mtemp, &dtemp);
466 	y = ytemp;
467     } else {
468 	ASSERT_TYPE(0, INT_TYPE);
469 	y = ARGV(0);
470     }
471 
472     if (HASDATE(ARG(1))) {
473 	FromJulian(DATEPART(ARG(1)), &ytemp, &mtemp, &dtemp);
474 	m = mtemp;
475     } else {
476 	ASSERT_TYPE(1, INT_TYPE);
477 	m = ARGV(1) - 1;
478     }
479 
480     if (HASDATE(ARG(2))) {
481 	FromJulian(DATEPART(ARG(2)), &ytemp, &mtemp, &dtemp);
482 	d = dtemp;
483     } else {
484 	ASSERT_TYPE(2, INT_TYPE);
485 	d = ARGV(2);
486     }
487 
488     if (!DateOK(y, m, d)) {
489 	return E_BAD_DATE;
490     }
491     RetVal.type = DATE_TYPE;
492     RETVAL = Julian(y, m, d);
493     return OK;
494 }
495 
496 /***************************************************************/
497 /*                                                             */
498 /*  FDateTime - make a datetime from one of these combos:      */
499 /*  DATE, TIME                                                 */
500 /*  DATE, HOUR, MINUTE                                         */
501 /*  YEAR, MONTH, DAY, TIME                                     */
502 /*  YEAR, MONTH, DAY, HOUR, MINUTE                             */
503 /*                                                             */
504 /***************************************************************/
FDateTime(func_info * info)505 static int FDateTime(func_info *info)
506 {
507     int y, m, d;
508 
509     RetVal.type = DATETIME_TYPE;
510 
511     switch(Nargs) {
512     case 2:
513 	if (ARG(0).type != DATE_TYPE ||
514 	    ARG(1).type != TIME_TYPE) return E_BAD_TYPE;
515 	RETVAL = (MINUTES_PER_DAY * ARGV(0)) + ARGV(1);
516 	return OK;
517     case 3:
518 	if (ARG(0).type != DATE_TYPE ||
519 	    ARG(1).type != INT_TYPE ||
520 	    ARG(2).type != INT_TYPE) return E_BAD_TYPE;
521 	if (ARGV(1) < 0 || ARGV(2) < 0) return E_2LOW;
522 	if (ARGV(1) > 23 || ARGV(2) > 59) return E_2HIGH;
523 	RETVAL = (MINUTES_PER_DAY * ARGV(0)) + 60 * ARGV(1) + ARGV(2);
524 	return OK;
525     case 4:
526 	if (ARG(0).type != INT_TYPE ||
527 	    ARG(1).type != INT_TYPE ||
528 	    ARG(2).type != INT_TYPE ||
529 	    ARG(3).type != TIME_TYPE) return E_BAD_TYPE;
530 	y = ARGV(0);
531 	m = ARGV(1) - 1;
532 	d = ARGV(2);
533 
534 	if (!DateOK(y, m, d)) return E_BAD_DATE;
535 	RETVAL = Julian(y, m, d) * MINUTES_PER_DAY + ARGV(3);
536 	return OK;
537     case 5:
538 	if (ARG(0).type != INT_TYPE ||
539 	    ARG(1).type != INT_TYPE ||
540 	    ARG(2).type != INT_TYPE ||
541 	    ARG(3).type != INT_TYPE ||
542 	    ARG(4).type != INT_TYPE) return E_BAD_TYPE;
543 
544 	y = ARGV(0);
545 	m = ARGV(1) - 1;
546 	d = ARGV(2);
547 	if (!DateOK(y, m, d)) return E_BAD_DATE;
548 
549 	if (ARGV(3) < 0 || ARGV(4) < 0) return E_2LOW;
550 	if (ARGV(3) > 23 || ARGV(4) > 59) return E_2HIGH;
551 	RETVAL = Julian(y, m, d) * MINUTES_PER_DAY + ARGV(3) * 60 + ARGV(4);
552 	return OK;
553 
554     default:
555 	return E_2MANY_ARGS;
556     }
557 }
558 
559 /***************************************************************/
560 /*                                                             */
561 /*  FCoerce - type coercion function.                          */
562 /*                                                             */
563 /***************************************************************/
FCoerce(func_info * info)564 static int FCoerce(func_info *info)
565 {
566     char const *s;
567 
568     ASSERT_TYPE(0, STR_TYPE);
569     s = ARGSTR(0);
570 
571     /* Copy the value of ARG(1) into RetVal, and make ARG(1) invalid so
572        it won't be destroyed */
573     DCOPYVAL(RetVal, ARG(1));
574 
575     if (! StrCmpi(s, "int")) return DoCoerce(INT_TYPE, &RetVal);
576     else if (! StrCmpi(s, "date")) return DoCoerce(DATE_TYPE, &RetVal);
577     else if (! StrCmpi(s, "time")) return DoCoerce(TIME_TYPE, &RetVal);
578     else if (! StrCmpi(s, "string")) return DoCoerce(STR_TYPE, &RetVal);
579     else if (! StrCmpi(s, "datetime")) return DoCoerce(DATETIME_TYPE, &RetVal);
580     else return E_CANT_COERCE;
581 }
582 
583 /***************************************************************/
584 /*                                                             */
585 /*  FMax - select maximum from a list of args.                 */
586 /*                                                             */
587 /***************************************************************/
FMax(func_info * info)588 static int FMax(func_info *info)
589 {
590     Value *maxptr;
591     int i;
592     char type;
593 
594     maxptr = &ARG(0);
595     type = maxptr->type;
596 
597     for (i=1; i<Nargs; i++) {
598 	if (ARG(i).type != type) return E_BAD_TYPE;
599 	if (type != STR_TYPE) {
600 	    if (ARG(i).v.val > maxptr->v.val) maxptr = &ARG(i);
601 	} else {
602 	    if (strcmp(ARG(i).v.str, maxptr->v.str) > 0) maxptr = &ARG(i);
603 	}
604     }
605     DCOPYVAL(RetVal, *maxptr);
606     return OK;
607 }
608 
609 /***************************************************************/
610 /*                                                             */
611 /*  FMin - select minimum from a list of args.                 */
612 /*                                                             */
613 /***************************************************************/
FMin(func_info * info)614 static int FMin(func_info *info)
615 {
616     Value *minptr;
617     int i;
618     char type;
619 
620     minptr = &ARG(0);
621     type = minptr->type;
622 
623     for (i=1; i<Nargs; i++) {
624 	if (ARG(i).type != type) return E_BAD_TYPE;
625 	if (type != STR_TYPE) {
626 	    if (ARG(i).v.val < minptr->v.val) minptr = &ARG(i);
627 	} else {
628 	    if (strcmp(ARG(i).v.str, minptr->v.str) < 0) minptr = &ARG(i);
629 	}
630     }
631     DCOPYVAL(RetVal, *minptr);
632     return OK;
633 }
634 
635 /***************************************************************/
636 /*                                                             */
637 /*  FAsc - ASCII value of first char of string                 */
638 /*                                                             */
639 /***************************************************************/
FAsc(func_info * info)640 static int FAsc(func_info *info)
641 {
642     ASSERT_TYPE(0, STR_TYPE);
643     RetVal.type = INT_TYPE;
644     RETVAL = *(ARGSTR(0));
645     return OK;
646 }
647 
648 /***************************************************************/
649 /*                                                             */
650 /*  FChar - build a string from ASCII values                   */
651 /*                                                             */
652 /***************************************************************/
FChar(func_info * info)653 static int FChar(func_info *info)
654 {
655 
656     int i, len;
657 
658 /* Special case of one arg - if given ascii value 0, create empty string */
659     if (Nargs == 1) {
660 	ASSERT_TYPE(0, INT_TYPE);
661 	if (ARGV(0) < -128) return E_2LOW;
662 	if (ARGV(0) > 255) return E_2HIGH;
663 	len = ARGV(0) ? 2 : 1;
664 	RetVal.v.str = malloc(len);
665 	if (!RetVal.v.str) return E_NO_MEM;
666 	RetVal.type = STR_TYPE;
667 	*(RetVal.v.str) = ARGV(0);
668 	if (len>1) *(RetVal.v.str + 1) = 0;
669 	return OK;
670     }
671 
672     RetVal.v.str = malloc(Nargs + 1);
673     if (!RetVal.v.str) return E_NO_MEM;
674     RetVal.type = STR_TYPE;
675     for (i=0; i<Nargs; i++) {
676 	if (ARG(i).type != INT_TYPE) {
677 	    free(RetVal.v.str);
678 	    RetVal.type = ERR_TYPE;
679 	    return E_BAD_TYPE;
680 	}
681 	if (ARG(i).v.val < -128 || ARG(i).v.val == 0) {
682 	    free(RetVal.v.str);
683 	    RetVal.type = ERR_TYPE;
684 	    return E_2LOW;
685 	}
686 	if (ARG(i).v.val > 255) {
687 	    free(RetVal.v.str);
688 	    RetVal.type = ERR_TYPE;
689 	    return E_2HIGH;
690 	}
691 	*(RetVal.v.str + i) = ARG(i).v.val;
692     }
693     *(RetVal.v.str + Nargs) = 0;
694     return OK;
695 }
696 /***************************************************************/
697 /*                                                             */
698 /*  Functions for extracting the components of a date.         */
699 /*                                                             */
700 /*  FDay - get day of month                                    */
701 /*  FMonnum - get month (1-12)                                 */
702 /*  FYear - get year                                           */
703 /*  FWkdaynum - get weekday num (0 = Sun)                      */
704 /*  FWkday - get weekday (string)                              */
705 /*  FMon - get month (string)                                  */
706 /*                                                             */
707 /***************************************************************/
FDay(func_info * info)708 static int FDay(func_info *info)
709 {
710     int y, m, d, v;
711     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
712     v = DATEPART(ARG(0));
713 
714     if (v == CacheJul)
715 	d = CacheDay;
716     else {
717 	FromJulian(v, &y, &m, &d);
718 	CacheJul = v;
719 	CacheYear = y;
720 	CacheMon = m;
721 	CacheDay = d;
722     }
723     RetVal.type = INT_TYPE;
724     RETVAL = d;
725     return OK;
726 }
727 
FMonnum(func_info * info)728 static int FMonnum(func_info *info)
729 {
730     int y, m, d, v;
731     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
732     v = DATEPART(ARG(0));
733 
734     if (v == CacheJul)
735 	m = CacheMon;
736     else {
737 	FromJulian(v, &y, &m, &d);
738 	CacheJul = v;
739 	CacheYear = y;
740 	CacheMon = m;
741 	CacheDay = d;
742     }
743     RetVal.type = INT_TYPE;
744     RETVAL = m+1;
745     return OK;
746 }
747 
FYear(func_info * info)748 static int FYear(func_info *info)
749 {
750     int y, m, d, v;
751     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
752     v = DATEPART(ARG(0));
753 
754     if (v == CacheJul)
755 	y = CacheYear;
756     else {
757 	FromJulian(v, &y, &m, &d);
758 	CacheJul = v;
759 	CacheYear = y;
760 	CacheMon = m;
761 	CacheDay = d;
762     }
763     RetVal.type = INT_TYPE;
764     RETVAL = y;
765     return OK;
766 }
767 
FWkdaynum(func_info * info)768 static int FWkdaynum(func_info *info)
769 {
770     int v;
771     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
772     v = DATEPART(ARG(0));
773 
774     RetVal.type = INT_TYPE;
775 
776     /* Correct so that 0 = Sunday */
777     RETVAL = (v+1) % 7;
778     return OK;
779 }
780 
FWkday(func_info * info)781 static int FWkday(func_info *info)
782 {
783     char const *s;
784 
785     if (!HASDATE(ARG(0)) && ARG(0).type != INT_TYPE) return E_BAD_TYPE;
786     if (ARG(0).type == INT_TYPE) {
787 	if (ARGV(0) < 0) return E_2LOW;
788 	if (ARGV(0) > 6) return E_2HIGH;
789 	/* Convert 0=Sun to 0=Mon */
790 	ARGV(0)--;
791 	if (ARGV(0) < 0) ARGV(0) = 6;
792 	s = DayName[ARGV(0)];
793     } else s = DayName[DATEPART(ARG(0)) % 7];
794     return RetStrVal(s, info);
795 }
796 
FMon(func_info * info)797 static int FMon(func_info *info)
798 {
799     char const *s;
800     int y, m, d, v;
801 
802     if (!HASDATE(ARG(0)) && ARG(0).type != INT_TYPE) return E_BAD_TYPE;
803 
804     if (ARG(0).type == INT_TYPE) {
805 	m = ARGV(0) - 1;
806 	if (m < 0) return E_2LOW;
807 	if (m > 11) return E_2HIGH;
808     } else {
809 	v = DATEPART(ARG(0));
810 	if (v == CacheJul)
811 	    m = CacheMon;
812 	else {
813 	    FromJulian(v, &y, &m, &d);
814 	    CacheJul = v;
815 	    CacheYear = y;
816 	    CacheMon = m;
817 	    CacheDay = d;
818 	}
819     }
820     s = MonthName[m];
821     return RetStrVal(s, info);
822 }
823 
824 /***************************************************************/
825 /*                                                             */
826 /*  FHour - extract hour from a time                           */
827 /*  FMinute - extract minute from a time                       */
828 /*  FTime - create a time from hour and minute                 */
829 /*                                                             */
830 /***************************************************************/
FHour(func_info * info)831 static int FHour(func_info *info)
832 {
833     int v;
834     if (!HASTIME(ARG(0))) return E_BAD_TYPE;
835     v = TIMEPART(ARG(0));
836     RetVal.type = INT_TYPE;
837     RETVAL = v / 60;
838     return OK;
839 }
840 
FMinute(func_info * info)841 static int FMinute(func_info *info)
842 {
843     int v;
844     if (!HASTIME(ARG(0))) return E_BAD_TYPE;
845     v = TIMEPART(ARG(0));
846     RetVal.type = INT_TYPE;
847     RETVAL = v % 60;
848     return OK;
849 }
850 
FTime(func_info * info)851 static int FTime(func_info *info)
852 {
853     int h, m;
854 
855     if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;
856 
857     h = ARGV(0);
858     m = ARGV(1);
859     if (h<0 || m<0) return E_2LOW;
860     if (h>23 || m>59) return E_2HIGH;
861     RetVal.type = TIME_TYPE;
862     RETVAL = h*60 + m;
863     return OK;
864 }
865 
866 /***************************************************************/
867 /*                                                             */
868 /*  FAbs - absolute value                                      */
869 /*  FSgn - signum function                                     */
870 /*                                                             */
871 /***************************************************************/
FAbs(func_info * info)872 static int FAbs(func_info *info)
873 {
874     volatile int v;
875 
876     ASSERT_TYPE(0, INT_TYPE);
877     v = ARGV(0);
878     RetVal.type = INT_TYPE;
879     RETVAL = (v < 0) ? (-v) : v;
880     v = RETVAL;
881     if (v < 0) return E_2HIGH;
882     return OK;
883 }
884 
FSgn(func_info * info)885 static int FSgn(func_info *info)
886 {
887     int v;
888 
889     ASSERT_TYPE(0, INT_TYPE);
890     v = ARGV(0);
891     RetVal.type = INT_TYPE;
892     if (v>0) RETVAL = 1;
893     else if (v<0) RETVAL = -1;
894     else RETVAL = 0;
895     return OK;
896 }
897 
898 /***************************************************************/
899 /*                                                             */
900 /*  FAmpm - return a time as a string with "AM" or "PM" suffix */
901 /*                                                             */
902 /***************************************************************/
FAmpm(func_info * info)903 static int FAmpm(func_info *info)
904 {
905     int h, m;
906     int yr=0, mo=0, da=0;
907 
908     char const *am = "AM";
909     char const *pm = "PM";
910     char const *ampm = NULL;
911 
912     char outbuf[128];
913 
914     if (ARG(0).type != DATETIME_TYPE && ARG(0).type != TIME_TYPE) {
915 	return E_BAD_TYPE;
916     }
917     if (HASDATE(ARG(0))) {
918 	FromJulian(DATEPART(ARG(0)), &yr, &mo, &da);
919     }
920     if (Nargs >= 2) {
921 	ASSERT_TYPE(1, STR_TYPE);
922 	am = ARGSTR(1);
923 	if (Nargs >= 3) {
924 	    ASSERT_TYPE(2, STR_TYPE);
925 	    pm = ARGSTR(2);
926 	}
927     }
928     h = TIMEPART(ARG(0)) / 60;
929     m = TIMEPART(ARG(0)) % 60;
930     if (h <= 11) {
931 	/* AM */
932 	if (h == 0) {
933 	    if (ARG(0).type == DATETIME_TYPE) {
934 		snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c12%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, TimeSep, m);
935 	    } else {
936 		snprintf(outbuf, sizeof(outbuf), "12%c%02d", TimeSep, m);
937 	    }
938 	} else {
939 	    if (ARG(0).type == DATETIME_TYPE) {
940 		snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m);
941 	    } else {
942 		snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m);
943 	    }
944 	}
945 	ampm = am;
946     } else {
947 	if (h > 12) {
948 	    h -= 12;
949 	}
950 	if (ARG(0).type == DATETIME_TYPE) {
951 	    snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m);
952 	} else {
953 	    snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m);
954 	}
955 	ampm = pm;
956     }
957     RetVal.type = STR_TYPE;
958     RetVal.v.str = malloc(strlen(outbuf) + strlen(ampm) + 1);
959     if (!RetVal.v.str) {
960 	RetVal.type = ERR_TYPE;
961 	return E_NO_MEM;
962     }
963     strcpy(RetVal.v.str, outbuf);
964     strcat(RetVal.v.str, ampm);
965     return OK;
966 }
967 
968 /***************************************************************/
969 /*                                                             */
970 /*  FOrd - returns a string containing ordinal number.         */
971 /*                                                             */
972 /*  EG - ord(2) == "2nd", etc.                                 */
973 /*                                                             */
974 /***************************************************************/
FOrd(func_info * info)975 static int FOrd(func_info *info)
976 {
977     int t, u, v;
978     char const *s;
979 
980     char buf[32];
981 
982     ASSERT_TYPE(0, INT_TYPE);
983 
984     v = ARGV(0);
985     t = v % 100;
986     if (t < 0) t = -t;
987     u = t % 10;
988     s = "th";
989     if (u == 1 && t != 11) s = "st";
990     if (u == 2 && t != 12) s = "nd";
991     if (u == 3 && t != 13) s = "rd";
992     sprintf(buf, "%d%s", v, s);
993     return RetStrVal(buf, info);
994 }
995 
996 /***************************************************************/
997 /*                                                             */
998 /*  FPlural - pluralization function                           */
999 /*                                                             */
1000 /*  plural(n) -->  "" or "s"                                   */
1001 /*  plural(n, str) --> "str" or "strs"                         */
1002 /*  plural(n, str1, str2) --> "str1" or "str2"                 */
1003 /*                                                             */
1004 /***************************************************************/
FPlural(func_info * info)1005 static int FPlural(func_info *info)
1006 {
1007     ASSERT_TYPE(0, INT_TYPE);
1008 
1009     switch(Nargs) {
1010     case 1:
1011 	if (ARGV(0) == 1) return RetStrVal("", info);
1012 	else return RetStrVal("s", info);
1013 
1014     case 2:
1015 	ASSERT_TYPE(1, STR_TYPE);
1016 	if (ARGV(0) == 1) {
1017 	    DCOPYVAL(RetVal, ARG(1));
1018 	    return OK;
1019 	}
1020 	RetVal.type = STR_TYPE;
1021 	RetVal.v.str = malloc(strlen(ARGSTR(1))+2);
1022 	if (!RetVal.v.str) {
1023 	    RetVal.type = ERR_TYPE;
1024 	    return E_NO_MEM;
1025 	}
1026 	strcpy(RetVal.v.str, ARGSTR(1));
1027 	strcat(RetVal.v.str, "s");
1028 	return OK;
1029 
1030     default:
1031 	if (ARG(1).type != STR_TYPE || ARG(2).type != STR_TYPE)
1032 	    return E_BAD_TYPE;
1033 	if (ARGV(0) == 1) DCOPYVAL(RetVal, ARG(1));
1034 	else DCOPYVAL(RetVal, ARG(2));
1035 	return OK;
1036     }
1037 }
1038 
1039 /***************************************************************/
1040 /*                                                             */
1041 /*  FIsAny                                                     */
1042 /*  Return 1 if the first arg equals any subsequent arg, 0     */
1043 /*  otherwise.                                                 */
1044 /*                                                             */
1045 /***************************************************************/
FIsAny(func_info * info)1046 static int FIsAny(func_info *info)
1047 {
1048     int i;
1049     RetVal.type = INT_TYPE;
1050     RETVAL = 0;
1051     for (i=1; i<Nargs; i++) {
1052         if (ARG(0).type == ARG(i).type) {
1053             if (ARG(0).type == STR_TYPE) {
1054                 if (!strcmp(ARGSTR(0), ARGSTR(i))) {
1055                     RETVAL = 1;
1056                     return OK;
1057                 }
1058             } else {
1059                 if (ARGV(0) == ARGV(i)) {
1060                     RETVAL = 1;
1061                     return OK;
1062                 }
1063             }
1064         }
1065     }
1066     return OK;
1067 }
1068 
1069 /***************************************************************/
1070 /*                                                             */
1071 /*  FChoose                                                    */
1072 /*  Choose the nth value from a list of value.  If n<1, choose */
1073 /*  first.  If n>N, choose Nth value.  Indexes always start    */
1074 /*  from 1.                                                    */
1075 /*                                                             */
1076 /***************************************************************/
FChoose(func_info * info)1077 static int FChoose(func_info *info)
1078 {
1079     int v;
1080 
1081     ASSERT_TYPE(0, INT_TYPE);
1082     v = ARGV(0);
1083     if (v < 1) v = 1;
1084     if (v > Nargs-1) v = Nargs-1;
1085     DCOPYVAL(RetVal, ARG(v));
1086     return OK;
1087 }
1088 
1089 /***************************************************************/
1090 /*                                                             */
1091 /*  FVersion - version of Remind                               */
1092 /*                                                             */
1093 /***************************************************************/
FVersion(func_info * info)1094 static int FVersion(func_info *info)
1095 {
1096     return RetStrVal(VERSION, info);
1097 }
1098 
1099 /***************************************************************/
1100 /*                                                             */
1101 /*  FOstype - the type of operating system                     */
1102 /*  (UNIX, OS/2, or MSDOS)                                     */
1103 /*                                                             */
1104 /***************************************************************/
FOstype(func_info * info)1105 static int FOstype(func_info *info)
1106 {
1107     return RetStrVal("UNIX", info);
1108 }
1109 
1110 /***************************************************************/
1111 /*                                                             */
1112 /*  FShellescape - escape shell meta-characters                */
1113 /*                                                             */
1114 /***************************************************************/
FShellescape(func_info * info)1115 static int FShellescape(func_info *info)
1116 {
1117     DynamicBuffer buf;
1118     int r;
1119 
1120     ASSERT_TYPE(0, STR_TYPE);
1121     DBufInit (&buf);
1122     if (ShellEscape(ARG(0).v.str, &buf) != OK) {
1123         DBufFree(&buf);
1124         return E_NO_MEM;
1125     }
1126 
1127     r = RetStrVal(DBufValue(&buf), info);
1128     DBufFree(&buf);
1129     return r;
1130 }
1131 
1132 /***************************************************************/
1133 /*                                                             */
1134 /*  FUpper - convert string to upper-case                      */
1135 /*  FLower - convert string to lower-case                      */
1136 /*                                                             */
1137 /***************************************************************/
FUpper(func_info * info)1138 static int FUpper(func_info *info)
1139 {
1140     char *s;
1141 
1142     ASSERT_TYPE(0, STR_TYPE);
1143     DCOPYVAL(RetVal, ARG(0));
1144     s = RetVal.v.str;
1145     while (*s) {
1146 	*s = UPPER(*s);
1147 	s++;
1148     }
1149     return OK;
1150 }
1151 
FLower(func_info * info)1152 static int FLower(func_info *info)
1153 {
1154     char *s;
1155 
1156     ASSERT_TYPE(0, STR_TYPE);
1157     DCOPYVAL(RetVal, ARG(0));
1158     s = RetVal.v.str;
1159     while (*s) {
1160 	*s = LOWER(*s);
1161 	s++;
1162     }
1163     return OK;
1164 }
1165 
1166 /***************************************************************/
1167 /*                                                             */
1168 /*  FToday - return the system's notion of "today"             */
1169 /*  Frealtoday - return today's date as read from OS.          */
1170 /*  FNow - return the system time (or time on cmd line.)       */
1171 /*  FRealnow - return the true system time                     */
1172 /*                                                             */
1173 /***************************************************************/
FToday(func_info * info)1174 static int FToday(func_info *info)
1175 {
1176     RetVal.type = DATE_TYPE;
1177     RETVAL = JulianToday;
1178     return OK;
1179 }
1180 
FRealtoday(func_info * info)1181 static int FRealtoday(func_info *info)
1182 {
1183     RetVal.type = DATE_TYPE;
1184     RETVAL = RealToday;
1185     return OK;
1186 }
1187 
FNow(func_info * info)1188 static int FNow(func_info *info)
1189 {
1190     RetVal.type = TIME_TYPE;
1191     RETVAL = (int) ( SystemTime(0) / 60L );
1192     return OK;
1193 }
1194 
FRealnow(func_info * info)1195 static int FRealnow(func_info *info)
1196 {
1197     RetVal.type = TIME_TYPE;
1198     RETVAL = (int) ( SystemTime(1) / 60L );
1199     return OK;
1200 }
1201 
FCurrent(func_info * info)1202 static int FCurrent(func_info *info)
1203 {
1204     RetVal.type = DATETIME_TYPE;
1205     RETVAL = JulianToday * MINUTES_PER_DAY + (SystemTime(0) / 60);
1206     return OK;
1207 }
1208 
FRealCurrent(func_info * info)1209 static int FRealCurrent(func_info *info)
1210 {
1211     RetVal.type = DATETIME_TYPE;
1212     RETVAL = RealToday * MINUTES_PER_DAY + (SystemTime(1) / 60);
1213     return OK;
1214 }
1215 
1216 /***************************************************************/
1217 /*                                                             */
1218 /*  FGetenv - get the value of an environment variable.        */
1219 /*                                                             */
1220 /***************************************************************/
FGetenv(func_info * info)1221 static int FGetenv(func_info *info)
1222 {
1223     ASSERT_TYPE(0, STR_TYPE);
1224     return RetStrVal(getenv(ARGSTR(0)), info);
1225 }
1226 
1227 /***************************************************************/
1228 /*                                                             */
1229 /*  FValue                                                     */
1230 /*                                                             */
1231 /*  Get the value of a variable.  If a second arg is supplied, */
1232 /*  it is returned if variable is undefined.                   */
1233 /*                                                             */
1234 /***************************************************************/
FValue(func_info * info)1235 static int FValue(func_info *info)
1236 {
1237     Var *v;
1238 
1239     ASSERT_TYPE(0, STR_TYPE);
1240     switch(Nargs) {
1241     case 1:
1242 	return GetVarValue(ARGSTR(0), &RetVal, NULL, NULL);
1243 
1244     case 2:
1245 	v = FindVar(ARGSTR(0), 0);
1246 	if (!v) {
1247 	    DCOPYVAL(RetVal, ARG(1));
1248 	    return OK;
1249 	} else {
1250 	    return CopyValue(&RetVal, &v->v);
1251 	}
1252     }
1253     return OK;
1254 }
1255 
1256 /***************************************************************/
1257 /*                                                             */
1258 /*  FDefined                                                   */
1259 /*                                                             */
1260 /*  Return 1 if a variable is defined, 0 if it is not.         */
1261 /*                                                             */
1262 /***************************************************************/
FDefined(func_info * info)1263 static int FDefined(func_info *info)
1264 {
1265     ASSERT_TYPE(0, STR_TYPE);
1266 
1267     RetVal.type = INT_TYPE;
1268 
1269     if (FindVar(ARGSTR(0), 0))
1270 	RETVAL = 1;
1271     else
1272 	RETVAL = 0;
1273     return OK;
1274 }
1275 
1276 /***************************************************************/
1277 /*                                                             */
1278 /*  FTrigdate and FTrigtime                                    */
1279 /*                                                             */
1280 /*  Date and time of last trigger.  These are stored in global */
1281 /*  vars.                                                      */
1282 /*                                                             */
1283 /***************************************************************/
FTrigdate(func_info * info)1284 static int FTrigdate(func_info *info)
1285 {
1286     if (LastTrigValid) {
1287 	RetVal.type = DATE_TYPE;
1288 	RETVAL = LastTriggerDate;
1289     } else {
1290 	RetVal.type = INT_TYPE;
1291 	RETVAL = 0;
1292     }
1293     return OK;
1294 }
1295 
FTrigback(func_info * info)1296 static int FTrigback(func_info *info)
1297 {
1298     RetVal.type = INT_TYPE;
1299     RETVAL = LastTrigger.back;
1300     return OK;
1301 }
1302 
FTrigdelta(func_info * info)1303 static int FTrigdelta(func_info *info)
1304 {
1305     RetVal.type = INT_TYPE;
1306     RETVAL = LastTrigger.delta;
1307     return OK;
1308 }
1309 
FTrigtimedelta(func_info * info)1310 static int FTrigtimedelta(func_info *info)
1311 {
1312     RetVal.type = INT_TYPE;
1313     RETVAL = LastTimeTrig.delta;
1314     return OK;
1315 }
1316 
FTrigtimerep(func_info * info)1317 static int FTrigtimerep(func_info *info)
1318 {
1319     RetVal.type = INT_TYPE;
1320     RETVAL = LastTimeTrig.rep;
1321     return OK;
1322 }
1323 
FTrigeventduration(func_info * info)1324 static int FTrigeventduration(func_info *info)
1325 {
1326     if (LastTrigger.eventduration == NO_TIME) {
1327 	RetVal.type = INT_TYPE;
1328 	RETVAL = -1;
1329     } else {
1330 	RetVal.type = TIME_TYPE;
1331 	RETVAL = LastTrigger.eventduration;
1332     }
1333     return OK;
1334 }
1335 
FTrigeventstart(func_info * info)1336 static int FTrigeventstart(func_info *info)
1337 {
1338     if (LastTrigger.eventstart == NO_TIME) {
1339 	RetVal.type = INT_TYPE;
1340 	RETVAL = -1;
1341     } else {
1342 	RetVal.type = DATETIME_TYPE;
1343 	RETVAL = LastTrigger.eventstart;
1344     }
1345     return OK;
1346 }
1347 
FTrigduration(func_info * info)1348 static int FTrigduration(func_info *info)
1349 {
1350     if (LastTimeTrig.duration == NO_TIME) {
1351 	RetVal.type = INT_TYPE;
1352 	RETVAL = -1;
1353     } else {
1354 	RetVal.type = TIME_TYPE;
1355 	RETVAL = LastTimeTrig.duration;
1356     }
1357     return OK;
1358 }
1359 
FTrigrep(func_info * info)1360 static int FTrigrep(func_info *info)
1361 {
1362     RetVal.type = INT_TYPE;
1363     RETVAL = LastTrigger.rep;
1364     return OK;
1365 }
1366 
FTrigpriority(func_info * info)1367 static int FTrigpriority(func_info *info)
1368 {
1369     RetVal.type = INT_TYPE;
1370     RETVAL = LastTrigger.priority;
1371     return OK;
1372 }
1373 
FTriguntil(func_info * info)1374 static int FTriguntil(func_info *info)
1375 {
1376     if (LastTrigger.until == NO_UNTIL) {
1377 	RetVal.type = INT_TYPE;
1378 	RETVAL = -1;
1379     } else {
1380 	RetVal.type = DATE_TYPE;
1381 	RETVAL = LastTrigger.until;
1382     }
1383     return OK;
1384 }
1385 
FTrigscanfrom(func_info * info)1386 static int FTrigscanfrom(func_info *info)
1387 {
1388     if (LastTrigger.scanfrom == NO_DATE) {
1389 	RetVal.type = INT_TYPE;
1390 	RETVAL = -1;
1391     } else {
1392 	RetVal.type = DATE_TYPE;
1393 	RETVAL = LastTrigger.scanfrom;
1394     }
1395     return OK;
1396 }
1397 
FTrigfrom(func_info * info)1398 static int FTrigfrom(func_info *info)
1399 {
1400     if (LastTrigger.from == NO_DATE) {
1401 	RetVal.type = INT_TYPE;
1402 	RETVAL = -1;
1403     } else {
1404 	RetVal.type = DATE_TYPE;
1405 	RETVAL = LastTrigger.from;
1406     }
1407     return OK;
1408 }
1409 
FTrigvalid(func_info * info)1410 static int FTrigvalid(func_info *info)
1411 {
1412     RetVal.type = INT_TYPE;
1413     RETVAL = LastTrigValid;
1414     return OK;
1415 }
1416 
FTrigtime(func_info * info)1417 static int FTrigtime(func_info *info)
1418 {
1419     if (LastTriggerTime != NO_TIME) {
1420 	RetVal.type = TIME_TYPE;
1421 	RETVAL = LastTriggerTime;
1422     } else {
1423 	RetVal.type = INT_TYPE;
1424 	RETVAL = 0;
1425     }
1426     return OK;
1427 }
1428 
FTrigdatetime(func_info * info)1429 static int FTrigdatetime(func_info *info)
1430 {
1431     if (!LastTrigValid) {
1432 	RetVal.type = INT_TYPE;
1433 	RETVAL = 0;
1434     } else if (LastTriggerTime != NO_TIME) {
1435 	RetVal.type = DATETIME_TYPE;
1436 	RETVAL = LastTriggerDate * MINUTES_PER_DAY + LastTriggerTime;
1437     } else {
1438 	RetVal.type = DATE_TYPE;
1439 	RETVAL = LastTriggerDate;
1440     }
1441     return OK;
1442 }
1443 
1444 /***************************************************************/
1445 /*                                                             */
1446 /*  FDaysinmon                                                 */
1447 /*                                                             */
1448 /*  Returns the number of days in mon,yr                       */
1449 /*                                                             */
1450 /***************************************************************/
FDaysinmon(func_info * info)1451 static int FDaysinmon(func_info *info)
1452 {
1453     if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;
1454 
1455     if (ARGV(0) > 12 || ARGV(0) < 1 ||
1456 	ARGV(1) < BASE || ARGV(1) > BASE+YR_RANGE)
1457 	return E_DOMAIN_ERR;
1458 
1459     RetVal.type = INT_TYPE;
1460     RETVAL = DaysInMonth(ARGV(0)-1, ARGV(1));
1461     return OK;
1462 }
1463 
1464 /***************************************************************/
1465 /*                                                             */
1466 /*  FIsleap                                                    */
1467 /*                                                             */
1468 /*  Return 1 if year is a leap year, zero otherwise.           */
1469 /*                                                             */
1470 /***************************************************************/
FIsleap(func_info * info)1471 static int FIsleap(func_info *info)
1472 {
1473     int y, m, d;
1474 
1475     if (ARG(0).type != INT_TYPE && !HASDATE(ARG(0))) return E_BAD_TYPE;
1476 
1477     /* If it's a date, extract the year */
1478     if (HASDATE(ARG(0)))
1479 	FromJulian(DATEPART(ARG(0)), &y, &m, &d);
1480     else
1481 	y = ARGV(0);
1482 
1483     RetVal.type = INT_TYPE;
1484     RETVAL = IsLeapYear(y);
1485     return OK;
1486 }
1487 
1488 /***************************************************************/
1489 /*                                                             */
1490 /*  FTrigger                                                   */
1491 /*                                                             */
1492 /*  Put out a date in a format suitable for triggering.        */
1493 /*                                                             */
1494 /***************************************************************/
FTrigger(func_info * info)1495 static int FTrigger(func_info *info)
1496 {
1497     int y, m, d;
1498     int date, tim;
1499     char buf[128];
1500 
1501     tim = NO_TIME;
1502     if (ARG(0).type != DATE_TYPE &&
1503 	ARG(0).type != DATETIME_TYPE) return E_BAD_TYPE;
1504 
1505     if (ARG(0).type == DATE_TYPE) {
1506 	date = ARGV(0);
1507     } else {
1508 	date = ARGV(0) / MINUTES_PER_DAY;
1509 	tim = ARGV(0) % MINUTES_PER_DAY;
1510     }
1511 
1512     if (ARG(0).type == DATE_TYPE) {
1513 	if (Nargs > 2) {
1514 	    /* Date Time UTCFlag */
1515 	    if (ARG(0).type == DATETIME_TYPE) return E_BAD_TYPE;
1516 	    ASSERT_TYPE(2, INT_TYPE);
1517 	    ASSERT_TYPE(1, TIME_TYPE);
1518 	    tim = ARGV(1);
1519 	    if (ARGV(2)) {
1520 		UTCToLocal(date, tim, &date, &tim);
1521 	    }
1522 	} else if (Nargs > 1) {
1523 	    /* Date Time */
1524 	    ASSERT_TYPE(1, TIME_TYPE);
1525 	    tim = ARGV(1);
1526 	}
1527     } else {
1528 	if (Nargs > 2) {
1529 	    return E_2MANY_ARGS;
1530 	} else if (Nargs > 1) {
1531 	    /* DateTime UTCFlag */
1532 	    ASSERT_TYPE(1, INT_TYPE);
1533 	    if (ARGV(1)) {
1534 		UTCToLocal(date, tim, &date, &tim);
1535 	    }
1536 	}
1537     }
1538 
1539     FromJulian(date, &y, &m, &d);
1540     if (tim != NO_TIME) {
1541 	sprintf(buf, "%d %s %d AT %02d:%02d", d, EnglishMonthName[m], y,
1542 		tim/60, tim%60);
1543     } else {
1544 	sprintf(buf, "%d %s %d", d, EnglishMonthName[m], y);
1545     }
1546     return RetStrVal(buf, info);
1547 }
1548 
1549 /***************************************************************/
1550 /*                                                             */
1551 /*  FShell                                                     */
1552 /*                                                             */
1553 /*  The shell function.                                        */
1554 /*                                                             */
1555 /*  If run is disabled, will not be executed.                  */
1556 /*                                                             */
1557 /***************************************************************/
FShell(func_info * info)1558 static int FShell(func_info *info)
1559 {
1560     DynamicBuffer buf;
1561     int ch, r;
1562     FILE *fp;
1563 
1564     /* For compatibility with previous versions of Remind, which
1565        used a static buffer for reading results from shell() command */
1566     int maxlen = 511;
1567 
1568     DBufInit(&buf);
1569     if (RunDisabled) return E_RUN_DISABLED;
1570     ASSERT_TYPE(0, STR_TYPE);
1571     if (Nargs >= 2) {
1572 	ASSERT_TYPE(1, INT_TYPE);
1573 	maxlen = ARGV(1);
1574     }
1575     fp = popen(ARGSTR(0), "r");
1576     if (!fp) return E_IO_ERR;
1577     while (1) {
1578 	ch = getc(fp);
1579 	if (ch == EOF) {
1580 	    break;
1581 	}
1582 	if (isspace(ch)) ch = ' ';
1583 	if (DBufPutc(&buf, (char) ch) != OK) {
1584 	    pclose(fp);
1585 	    DBufFree(&buf);
1586 	    return E_NO_MEM;
1587 	}
1588 	if (maxlen > 0 && DBufLen(&buf) >= maxlen) {
1589 	    break;
1590 	}
1591     }
1592 
1593     /* Delete trailing newline (converted to space) */
1594     if (DBufLen(&buf) && DBufValue(&buf)[DBufLen(&buf)-1] == ' ') {
1595 	DBufValue(&buf)[DBufLen(&buf)-1] = 0;
1596     }
1597 
1598     /* XXX Should we consume remaining output from cmd? */
1599 
1600     pclose(fp);
1601     r = RetStrVal(DBufValue(&buf), info);
1602     DBufFree(&buf);
1603     return r;
1604 }
1605 
1606 /***************************************************************/
1607 /*                                                             */
1608 /*  FIsomitted                                                 */
1609 /*                                                             */
1610 /*  Is a date omitted?                                         */
1611 /*                                                             */
1612 /***************************************************************/
FIsomitted(func_info * info)1613 static int FIsomitted(func_info *info)
1614 {
1615     int r;
1616     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
1617 
1618     RetVal.type = INT_TYPE;
1619     r = IsOmitted(DATEPART(ARG(0)), 0, NULL, &RETVAL);
1620     return r;
1621 }
1622 
1623 /***************************************************************/
1624 /*                                                             */
1625 /*  FSubstr                                                    */
1626 /*                                                             */
1627 /*  The substr function.  We destroy the value on the stack.   */
1628 /*                                                             */
1629 /***************************************************************/
FSubstr(func_info * info)1630 static int FSubstr(func_info *info)
1631 {
1632     char *s;
1633     char const *t;
1634     int start, end;
1635 
1636     if (ARG(0).type != STR_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;
1637     if (Nargs == 3 && ARG(2).type != INT_TYPE) return E_BAD_TYPE;
1638 
1639     s = ARGSTR(0);
1640     start = 1;
1641     while (start < ARGV(1)) {
1642 	if (!*s) break;
1643 	s++;
1644 	start++;
1645     }
1646     if (Nargs == 2 || !*s) return RetStrVal(s, info);
1647     end = start;
1648     t = s;
1649     while (end <= ARGV(2)) {
1650 	if (!*s) break;
1651 	s++;
1652 	end++;
1653     }
1654     *s = 0;
1655     return RetStrVal(t, info);
1656 }
1657 
1658 /***************************************************************/
1659 /*                                                             */
1660 /*  FIndex                                                     */
1661 /*                                                             */
1662 /*  The index of one string embedded in another.               */
1663 /*                                                             */
1664 /***************************************************************/
FIndex(func_info * info)1665 static int FIndex(func_info *info)
1666 {
1667     char const *s;
1668     int start;
1669 
1670     if (ARG(0).type != STR_TYPE || ARG(1).type != STR_TYPE ||
1671 	(Nargs == 3 && ARG(2).type != INT_TYPE)) return E_BAD_TYPE;
1672 
1673     s = ARGSTR(0);
1674 
1675 /* If 3 args, bump up the start */
1676     if (Nargs == 3) {
1677 	start = 1;
1678 	while (start < ARGV(2)) {
1679 	    if (!*s) break;
1680 	    s++;
1681 	    start++;
1682 	}
1683     }
1684 
1685 /* Find the string */
1686     s = strstr(s, ARGSTR(1));
1687     RetVal.type = INT_TYPE;
1688     if (!s) {
1689 	RETVAL = 0;
1690 	return OK;
1691     }
1692     RETVAL = (s - ARGSTR(0)) + 1;
1693     return OK;
1694 }
1695 
1696 /***************************************************************/
1697 /*                                                             */
1698 /*  FIif                                                       */
1699 /*                                                             */
1700 /*  The IIF function.                                          */
1701 /*                                                             */
1702 /***************************************************************/
FIif(func_info * info)1703 static int FIif(func_info *info)
1704 {
1705     int istrue;
1706     int arg;
1707 
1708     if (!(Nargs % 2)) return E_IIF_ODD;
1709 
1710     for (arg=0; arg<Nargs-1; arg += 2) {
1711 	if (ARG(arg).type != STR_TYPE && ARG(arg).type != INT_TYPE)
1712 	    return E_BAD_TYPE;
1713 
1714 	if (ARG(arg).type == INT_TYPE)
1715 	    istrue = ARG(arg).v.val;
1716 	else
1717 	    istrue = *(ARG(arg).v.str);
1718 
1719 	if (istrue) {
1720 	    DCOPYVAL(RetVal, ARG(arg+1));
1721 	    return OK;
1722 	}
1723     }
1724 
1725     DCOPYVAL(RetVal, ARG(Nargs-1));
1726     return OK;
1727 }
1728 
1729 /***************************************************************/
1730 /*                                                             */
1731 /*  FFilename                                                  */
1732 /*                                                             */
1733 /*  Return name of current file                                */
1734 /*                                                             */
1735 /***************************************************************/
FFilename(func_info * info)1736 static int FFilename(func_info *info)
1737 {
1738     return RetStrVal(FileName, info);
1739 }
1740 
1741 /***************************************************************/
1742 /*                                                             */
1743 /*  FFiledir                                                   */
1744 /*                                                             */
1745 /*  Return directory of current file                           */
1746 /*                                                             */
1747 /***************************************************************/
FFiledir(func_info * info)1748 static int FFiledir(func_info *info)
1749 {
1750     char *s;
1751     DynamicBuffer buf;
1752     int r;
1753 
1754     DBufInit(&buf);
1755 
1756     if (DBufPuts(&buf, FileName) != OK) return E_NO_MEM;
1757     if (DBufLen(&buf) == 0) {
1758 	DBufFree(&buf);
1759 	return RetStrVal(".", info);
1760     }
1761 
1762     s = DBufValue(&buf) + DBufLen(&buf) - 1;
1763     while (s > DBufValue(&buf) && *s != '/') s--;
1764     if (*s == '/') {
1765 	*s = 0;
1766 	r = RetStrVal(DBufValue(&buf), info);
1767     } else r = RetStrVal(".", info);
1768     DBufFree(&buf);
1769     return r;
1770 }
1771 /***************************************************************/
1772 /*                                                             */
1773 /*  FAccess                                                    */
1774 /*                                                             */
1775 /*  The UNIX access() system call.                             */
1776 /*                                                             */
1777 /***************************************************************/
FAccess(func_info * info)1778 static int FAccess(func_info *info)
1779 {
1780     int amode;
1781     char const *s;
1782 
1783     if (ARG(0).type != STR_TYPE ||
1784 	(ARG(1).type != INT_TYPE && ARG(1).type != STR_TYPE)) return E_BAD_TYPE;
1785 
1786     if (ARG(1).type == INT_TYPE) amode = ARGV(1);
1787     else {
1788 	amode = 0;
1789 	s = ARGSTR(1);
1790 	while (*s) {
1791 	    switch(*s++) {
1792 	    case 'r':
1793 	    case 'R': amode |= R_OK; break;
1794 	    case 'w':
1795 	    case 'W': amode |= W_OK; break;
1796 	    case 'x':
1797 	    case 'X': amode |= X_OK; break;
1798 	    }
1799 	}
1800     }
1801     RetVal.type = INT_TYPE;
1802     RETVAL = access(ARGSTR(0), amode);
1803     return OK;
1804 }
1805 
1806 /***************************************************************/
1807 /*                                                             */
1808 /*  FTypeof                                                    */
1809 /*                                                             */
1810 /*  Implement the typeof() function.                           */
1811 /*                                                             */
1812 /***************************************************************/
FTypeof(func_info * info)1813 static int FTypeof(func_info *info)
1814 {
1815     switch(ARG(0).type) {
1816     case INT_TYPE:  return RetStrVal("INT", info);
1817     case DATE_TYPE: return RetStrVal("DATE", info);
1818     case TIME_TYPE:  return RetStrVal("TIME", info);
1819     case STR_TYPE:  return RetStrVal("STRING", info);
1820     case DATETIME_TYPE: return RetStrVal("DATETIME", info);
1821     default:        return RetStrVal("ERR", info);
1822     }
1823 }
1824 
1825 /***************************************************************/
1826 /*                                                             */
1827 /*  FLanguage                                                  */
1828 /*                                                             */
1829 /*  Implement the language() function.                         */
1830 /*                                                             */
1831 /***************************************************************/
FLanguage(func_info * info)1832 static int FLanguage(func_info *info)
1833 {
1834     return RetStrVal(L_LANGNAME, info);
1835 }
1836 
1837 /***************************************************************/
1838 /*                                                             */
1839 /*  FArgs                                                      */
1840 /*                                                             */
1841 /*  Implement the args() function.                             */
1842 /*                                                             */
1843 /***************************************************************/
FArgs(func_info * info)1844 static int FArgs(func_info *info)
1845 {
1846     ASSERT_TYPE(0, STR_TYPE);
1847     RetVal.type = INT_TYPE;
1848     RETVAL = UserFuncExists(ARGSTR(0));
1849     return OK;
1850 }
1851 
1852 /***************************************************************/
1853 /*                                                             */
1854 /*  FDosubst                                                   */
1855 /*                                                             */
1856 /*  Implement the dosubst() function.                          */
1857 /*                                                             */
1858 /***************************************************************/
FDosubst(func_info * info)1859 static int FDosubst(func_info *info)
1860 {
1861     int jul, tim, r;
1862     DynamicBuffer buf;
1863 
1864     DBufInit(&buf);
1865 
1866     jul = NO_DATE;
1867     tim = NO_TIME;
1868     ASSERT_TYPE(0, STR_TYPE);
1869     if (Nargs >= 2) {
1870 	if (ARG(1).type == DATETIME_TYPE) {
1871 	    jul = DATEPART(ARG(1));
1872 	    tim = TIMEPART(ARG(1));
1873 	} else {
1874 	    ASSERT_TYPE(1, DATE_TYPE);
1875 	    jul = ARGV(1);
1876 	}
1877 	if (Nargs >= 3) {
1878 	    if (ARG(1).type == DATETIME_TYPE) {
1879 		return E_2MANY_ARGS;
1880 	    }
1881 	    ASSERT_TYPE(2, TIME_TYPE);
1882 	    tim = ARGV(2);
1883 	}
1884     }
1885 
1886     if ((r=DoSubstFromString(ARGSTR(0), &buf, jul, tim))) return r;
1887     r = RetStrVal(DBufValue(&buf), info);
1888     DBufFree(&buf);
1889     return r;
1890 }
1891 
1892 /***************************************************************/
1893 /*                                                             */
1894 /*  FHebdate                                                   */
1895 /*  FHebday						       */
1896 /*  FHebmon						       */
1897 /*  FHebyear                                                   */
1898 /*                                                             */
1899 /*  Hebrew calendar support functions                          */
1900 /*                                                             */
1901 /***************************************************************/
FHebdate(func_info * info)1902 static int FHebdate(func_info *info)
1903 {
1904     int year, day, mon, jahr;
1905     int mout, dout;
1906     int ans, r;
1907     int adarbehave;
1908 
1909     if (ARG(0).type != INT_TYPE || ARG(1).type != STR_TYPE) return E_BAD_TYPE;
1910     day = ARGV(0);
1911     mon = HebNameToNum(ARGSTR(1));
1912     if (mon < 0) return E_BAD_HEBDATE;
1913     if (Nargs == 2) {
1914 	r = GetNextHebrewDate(JulianToday, mon, day, 0, 0, &ans);
1915 	if (r) return r;
1916 	RetVal.type = DATE_TYPE;
1917 	RETVAL = ans;
1918 	return OK;
1919     }
1920     if (Nargs == 5) {
1921 	ASSERT_TYPE(4, INT_TYPE);
1922 	adarbehave = ARGV(4);
1923 	if (adarbehave < 0) return E_2LOW;
1924 	if (adarbehave > 2) return E_2HIGH;
1925     } else adarbehave = 0;
1926 
1927     if (Nargs >= 4) {
1928 	ASSERT_TYPE(3, INT_TYPE);
1929 	jahr = ARGV(3);
1930 	if (jahr < 0) return E_2LOW;
1931 	if (jahr > 2) {
1932 	    r = ComputeJahr(jahr, mon, day, &jahr);
1933 	    if (r) return r;
1934 	}
1935     } else jahr = 0;
1936 
1937 
1938     if (ARG(2).type == INT_TYPE) {
1939 	year = ARGV(2);
1940 	r = GetValidHebDate(year, mon, day, 0, &mout, &dout, jahr);
1941 	if (r) return r;
1942 	r = HebToJul(year, mout, dout);
1943 	if (r<0) return E_DATE_OVER;
1944 	RETVAL = r;
1945 	RetVal.type = DATE_TYPE;
1946 	return OK;
1947     } else if (HASDATE(ARG(2))) {
1948 	r = GetNextHebrewDate(DATEPART(ARG(2)), mon, day, jahr, adarbehave, &ans);
1949 	if (r) return r;
1950 	RETVAL = ans;
1951 	RetVal.type = DATE_TYPE;
1952 	return OK;
1953     } else return E_BAD_TYPE;
1954 }
1955 
FHebday(func_info * info)1956 static int FHebday(func_info *info)
1957 {
1958     int y, m, d, v;
1959 
1960     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
1961     v = DATEPART(ARG(0));
1962     if (v == CacheHebJul)
1963 	d = CacheHebDay;
1964     else {
1965 	JulToHeb(v, &y, &m, &d);
1966 	CacheHebJul = v;
1967 	CacheHebYear = y;
1968 	CacheHebMon = m;
1969 	CacheHebDay = d;
1970     }
1971     RetVal.type = INT_TYPE;
1972     RETVAL = d;
1973     return OK;
1974 }
1975 
FHebmon(func_info * info)1976 static int FHebmon(func_info *info)
1977 {
1978     int y, m, d, v;
1979 
1980     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
1981     v = DATEPART(ARG(0));
1982 
1983     if (v == CacheHebJul) {
1984 	m = CacheHebMon;
1985 	y = CacheHebYear;
1986     } else {
1987 	JulToHeb(v, &y, &m, &d);
1988 	CacheHebJul = v;
1989 	CacheHebYear = y;
1990 	CacheHebMon = m;
1991 	CacheHebDay = d;
1992     }
1993     return RetStrVal(HebMonthName(m, y), info);
1994 }
1995 
FHebyear(func_info * info)1996 static int FHebyear(func_info *info)
1997 {
1998     int y, m, d, v;
1999 
2000     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
2001     v = DATEPART(ARG(0));
2002 
2003     if (v == CacheHebJul)
2004 	y = CacheHebYear;
2005     else {
2006 	JulToHeb(v, &y, &m, &d);
2007 	CacheHebJul = v;
2008 	CacheHebYear = y;
2009 	CacheHebMon = m;
2010 	CacheHebDay = d;
2011     }
2012     RetVal.type = INT_TYPE;
2013     RETVAL = y;
2014     return OK;
2015 }
2016 /****************************************************************/
2017 /*                                                              */
2018 /*  FEasterdate - calc. easter Sunday from a year.              */
2019 /*                                                              */
2020 /*    from The Art of Computer Programming Vol 1.               */
2021 /*            Fundamental Algorithms                            */
2022 /*    by Donald Knuth.                                          */
2023 /*                                                              */
2024 /* Donated by Michael Salmon - thanks!                          */
2025 /*                                                              */
2026 /* I haven't examined this in detail, but I *think* int         */
2027 /* arithmetic is fine, even on 16-bit machines.                 */
2028 /*                                                              */
2029 /****************************************************************/
FEasterdate(func_info * info)2030 static int FEasterdate(func_info *info)
2031 {
2032     int y, m, d;
2033     int g, c, x, z, e, n;
2034     if (ARG(0).type == INT_TYPE) {
2035 	y = ARGV(0);
2036 	if (y < BASE) return E_2LOW;
2037 	else if (y > BASE+YR_RANGE) return E_2HIGH;
2038     } else if (HASDATE(ARG(0))) {
2039 	FromJulian(DATEPART(ARG(0)), &y, &m, &d);  /* We just want the year */
2040     } else return E_BAD_TYPE;
2041 
2042     do {
2043 	g = (y % 19) + 1;  /* golden number */
2044 	c = (y / 100) + 1; /* century */
2045 	x = (3 * c)/4 - 12;        /* correction for non-leap year centuries */
2046 	z = (8 * c + 5)/25 - 5;    /* special constant for moon sync */
2047 	d = (5 * y)/4 - x - 10;    /* find sunday */
2048 	e = (11 * g + 20 + z - x) % 30;    /* calc epact */
2049 	if ( e < 0 ) e += 30;
2050 	if ( e == 24 || (e == 25 && g > 11)) e++;
2051 	n = 44 - e;                        /* find full moon */
2052 	if ( n < 21 ) n += 30;     /* after 21st */
2053 	d = n + 7 - (d + n)%7;     /* calc sunday after */
2054 	if (d <= 31) m = 2;
2055 	else
2056 	{
2057 	    d = d - 31;
2058 	    m = 3;
2059 	}
2060 
2061 	RetVal.type = DATE_TYPE;
2062 	RETVAL = Julian(y, m, d);
2063 	y++; } while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0)));
2064 
2065     return OK;
2066 }
2067 /***************************************************************/
2068 /*                                                             */
2069 /*  FIsdst and FMinsfromutc                                    */
2070 /*                                                             */
2071 /*  Check whether daylight saving time is in effect, and      */
2072 /*  get minutes from UTC.                                      */
2073 /*                                                             */
2074 /***************************************************************/
2075 static int FTimeStuff (int wantmins, func_info *info);
FIsdst(func_info * info)2076 static int FIsdst(func_info *info)
2077 {
2078     return FTimeStuff(0, info);
2079 }
2080 
FMinsfromutc(func_info * info)2081 static int FMinsfromutc(func_info *info)
2082 {
2083     return FTimeStuff(1, info);
2084 }
2085 
FTimeStuff(int wantmins,func_info * info)2086 static int FTimeStuff(int wantmins, func_info *info)
2087 {
2088     int jul, tim;
2089     int mins, dst;
2090 
2091     jul = JulianToday;
2092     tim = 0;
2093 
2094     if (Nargs >= 1) {
2095 	if (!HASDATE(ARG(0))) return E_BAD_TYPE;
2096 	jul = DATEPART(ARG(0));
2097 	if (HASTIME(ARG(0))) {
2098 	    tim = TIMEPART(ARG(0));
2099 	}
2100 	if (Nargs >= 2) {
2101 	    if (HASTIME(ARG(0))) return E_2MANY_ARGS;
2102 	    ASSERT_TYPE(1, TIME_TYPE);
2103 	    tim = ARGV(1);
2104 	}
2105     }
2106 
2107     if (CalcMinsFromUTC(jul, tim, &mins, &dst)) return E_MKTIME_PROBLEM;
2108     RetVal.type = INT_TYPE;
2109     if (wantmins) RETVAL = mins; else RETVAL = dst;
2110 
2111     return OK;
2112 }
2113 
2114 /***************************************************************/
2115 /*                                                             */
2116 /*  Sunrise and sunset functions.                              */
2117 /*                                                             */
2118 /*  Algorithm from "Almanac for computers for the year 1978"   */
2119 /*  by L. E. Doggett, Nautical Almanac Office, USNO.           */
2120 /*                                                             */
2121 /*  This code also uses some ideas found in programs written   */
2122 /*  by Michael Schwartz and Marc T. Kaufman.                   */
2123 /*                                                             */
2124 /***************************************************************/
2125 #ifdef PI
2126 #undef PI
2127 #endif
2128 #define PI 3.14159265358979323846
2129 #define DEGRAD (PI/180.0)
2130 #define RADDEG (180.0/PI)
2131 
SunStuff(int rise,double cosz,int jul)2132 static int SunStuff(int rise, double cosz, int jul)
2133 {
2134     int mins, hours;
2135     int year, mon, day;
2136 
2137     double M, L, sinDelta, cosDelta, a, a_hr, cosH, t, H, T;
2138     double latitude, longdeg, UT, local;
2139 
2140 /* Get offset from UTC */
2141     if (CalculateUTC) {
2142 	if (CalcMinsFromUTC(jul, 12*60, &mins, NULL)) {
2143 	    Eprint(ErrMsg[E_MKTIME_PROBLEM]);
2144 	    return NO_TIME;
2145 	}
2146     } else mins = MinsFromUTC;
2147 
2148 /* Get latitude and longitude */
2149     longdeg = -Longitude;
2150     latitude = DEGRAD * Latitude;
2151 
2152     FromJulian(jul, &year, &mon, &day);
2153 
2154 /* Following formula on page B6 exactly... */
2155     t = (double) jul;
2156     if (rise) {
2157 	t += (6.0 + longdeg/15.0) / 24.0;
2158     } else {
2159 	t += (18.0 + longdeg/15.0) / 24.0;
2160     }
2161 
2162 /* Mean anomaly of sun starting from 1 Jan 1990 */
2163 /* NOTE: This assumes that BASE = 1990!!! */
2164 #if BASE != 1990
2165 #error Sun calculations assume a BASE of 1990!
2166 #endif
2167     t = 0.9856002585 * t;
2168     M = t + 357.828757; /* In degrees */
2169 
2170     /* Make sure M is in the range [0, 360) */
2171     M -= (floor(M/360.0) * 360.0);
2172 
2173 /* Sun's true longitude */
2174     L = M + 1.916*sin(DEGRAD*M) + 0.02*sin(2*DEGRAD*M) + 283.07080214;
2175     if (L > 360.0) L -= 360.0;
2176 
2177 /* Tan of sun's right ascension */
2178     a = RADDEG * atan2(0.91746*sin(DEGRAD*L), cos(DEGRAD*L));
2179     if (a<0) {
2180 	a += 360.0;
2181     }
2182 
2183     a_hr = a / 15.0;
2184 
2185 /* Sine of sun's declination */
2186     sinDelta = 0.39782 * sin(DEGRAD*L);
2187     cosDelta = sqrt(1 - sinDelta*sinDelta);
2188 
2189 /* Cosine of sun's local hour angle */
2190     cosH = (cosz - sinDelta * sin(latitude)) / (cosDelta * cos(latitude));
2191 
2192     if (cosH < -1.0) { /* Summer -- permanent daylight */
2193 	if (rise) return NO_TIME;
2194 	else      return -NO_TIME;
2195     }
2196     if (cosH > 1.0) { /* Winter -- permanent darkness */
2197 	if (rise) return -NO_TIME;
2198 	else      return NO_TIME;
2199     }
2200 
2201     H = RADDEG * acos(cosH);
2202     if (rise) H = 360.0 - H;
2203 
2204     t -= 360.0*floor(t/360.0);
2205     T = (H-t) / 15.0 + a_hr - 6.726637276;
2206 
2207     if (T >= 24.0) T -= 24.0;
2208     else if (T < 0.0) T+= 24.0;
2209 
2210     UT = T + longdeg / 15.0;
2211 
2212 
2213     local = UT + (double) mins / 60.0;
2214     if (local < 0.0) local += 24.0;
2215     else if (local >= 24.0) local -= 24.0;
2216 
2217     /* Round off local time to nearest minute */
2218     local = floor(local * 60.0 + 0.5) / 60.0;
2219 
2220     hours = (int) local;
2221     mins = (int) ((local - hours) * 60.0);
2222 
2223     /* Sometimes, we get roundoff error.  Check for "reasonableness" of
2224        answer. */
2225     if (rise) {
2226 	/* Sunrise so close to midnight it wrapped around -- permament light */
2227 	if (hours >= 23) return NO_TIME;
2228     } else {
2229 	/* Sunset so close to midnight it wrapped around -- permament light */
2230 	if (hours <= 1) return -NO_TIME;
2231     }
2232     return hours*60 + mins;
2233 }
2234 
2235 /***************************************************************/
2236 /*                                                             */
2237 /*  Sunrise and Sunset functions.                              */
2238 /*                                                             */
2239 /***************************************************************/
FSun(int rise,func_info * info)2240 static int FSun(int rise, func_info *info)
2241 {
2242     int jul = JulianToday;
2243     /* Assignment below is not necessary, but it silences
2244        a GCC warning about a possibly-uninitialized variable */
2245     double cosz = 0.0;
2246     int r;
2247 
2248     if (rise == 0 || rise == 1) {
2249     /* Sunrise and sunset : cos(90 degrees + 50 arcminutes) */
2250     cosz = -0.01454389765158243;
2251     } else if (rise == 2 || rise == 3) {
2252     /* Civil twilight: cos(96 degrees) */
2253 	cosz = -0.10452846326765333;
2254     } else if (rise == 4 || rise == 5) {
2255     /* Nautical twilight: cos(102 degrees) */
2256 	cosz = -0.20791169081775912;
2257     } else if (rise == 6 || rise == 7) {
2258     /* Astronomical twilight: cos(108 degrees) */
2259 	cosz = -0.30901699437494734;
2260     }
2261     if (Nargs >= 1) {
2262 	if (!HASDATE(ARG(0))) return E_BAD_TYPE;
2263 	jul = DATEPART(ARG(0));
2264     }
2265 
2266     r = SunStuff(rise % 2, cosz, jul);
2267     if (r == NO_TIME) {
2268 	RETVAL = 0;
2269 	RetVal.type = INT_TYPE;
2270     } else if (r == -NO_TIME) {
2271 	RETVAL = MINUTES_PER_DAY;
2272 	RetVal.type = INT_TYPE;
2273     } else {
2274 	RETVAL = r;
2275 	RetVal.type = TIME_TYPE;
2276     }
2277     return OK;
2278 }
2279 
FSunrise(func_info * info)2280 static int FSunrise(func_info *info)
2281 {
2282     return FSun(1, info);
2283 }
FSunset(func_info * info)2284 static int FSunset(func_info *info)
2285 {
2286     return FSun(0, info);
2287 }
2288 
FDawn(func_info * info)2289 static int FDawn(func_info *info)
2290 {
2291     return FSun(3, info);
2292 }
FDusk(func_info * info)2293 static int FDusk(func_info *info)
2294 {
2295     return FSun(2, info);
2296 }
2297 
FNDawn(func_info * info)2298 static int FNDawn(func_info *info)
2299 {
2300     return FSun(5, info);
2301 }
FNDusk(func_info * info)2302 static int FNDusk(func_info *info)
2303 {
2304     return FSun(4, info);
2305 }
2306 
FADawn(func_info * info)2307 static int FADawn(func_info *info)
2308 {
2309     return FSun(7, info);
2310 }
FADusk(func_info * info)2311 static int FADusk(func_info *info)
2312 {
2313     return FSun(6, info);
2314 }
2315 
2316 /***************************************************************/
2317 /*                                                             */
2318 /*  FFiledate                                                  */
2319 /*                                                             */
2320 /*  Return modification date of a file                         */
2321 /*                                                             */
2322 /***************************************************************/
FFiledate(func_info * info)2323 static int FFiledate(func_info *info)
2324 {
2325     struct stat statbuf;
2326     struct tm *t1;
2327 
2328     RetVal.type = DATE_TYPE;
2329 
2330     ASSERT_TYPE(0, STR_TYPE);
2331 
2332     if (stat(ARGSTR(0), &statbuf)) {
2333 	RETVAL = 0;
2334 	return OK;
2335     }
2336 
2337     t1 = localtime(&(statbuf.st_mtime));
2338 
2339     if (t1->tm_year + 1900 < BASE)
2340 	RETVAL=0;
2341     else
2342 	RETVAL=Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday);
2343 
2344     return OK;
2345 }
2346 
2347 /***************************************************************/
2348 /*                                                             */
2349 /*  FFiledatetime                                              */
2350 /*                                                             */
2351 /*  Return modification datetime of a file                     */
2352 /*                                                             */
2353 /***************************************************************/
FFiledatetime(func_info * info)2354 static int FFiledatetime(func_info *info)
2355 {
2356     struct stat statbuf;
2357     struct tm *t1;
2358 
2359     RetVal.type = DATETIME_TYPE;
2360 
2361     ASSERT_TYPE(0, STR_TYPE);
2362 
2363     if (stat(ARGSTR(0), &statbuf)) {
2364 	RETVAL = 0;
2365 	return OK;
2366     }
2367 
2368     t1 = localtime(&(statbuf.st_mtime));
2369 
2370     if (t1->tm_year + 1900 < BASE)
2371 	RETVAL=0;
2372     else
2373 	RETVAL = MINUTES_PER_DAY * Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday) + t1->tm_hour * 60 + t1->tm_min;
2374 
2375     return OK;
2376 }
2377 
2378 /***************************************************************/
2379 /*                                                             */
2380 /*  FPsshade                                                   */
2381 /*                                                             */
2382 /*  Canned PostScript code for shading a calendar square       */
2383 /*                                                             */
2384 /***************************************************************/
2385 static int psshade_warned = 0;
FPsshade(func_info * info)2386 static int FPsshade(func_info *info)
2387 {
2388     char psbuff[256];
2389     char *s = psbuff;
2390     int i;
2391 
2392     /* 1 or 3 args */
2393     if (Nargs != 1 && Nargs != 3) return E_2MANY_ARGS;
2394 
2395     for (i=0; i<Nargs; i++) {
2396 	if (ARG(i).type != INT_TYPE) return E_BAD_TYPE;
2397 	if (ARG(i).v.val < 0) return E_2LOW;
2398 	if (ARG(i).v.val > 100) return E_2HIGH;
2399     }
2400 
2401     if (!psshade_warned) {
2402 	psshade_warned = 1;
2403 	Eprint("psshade() is deprecated; use SPECIAL SHADE instead.");
2404         FreshLine = 1;
2405     }
2406 
2407     sprintf(s, "/_A LineWidth 2 div def ");
2408     s += strlen(s);
2409     sprintf(s, "_A _A moveto ");
2410     s += strlen(s);
2411     sprintf(s, "BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto ");
2412     s += strlen(s);
2413     if (Nargs == 1) {
2414 	sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARGV(0));
2415     } else {
2416 	sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div %d 100 div %d 100 div setrgbcolor fill 0.0 setgray", ARGV(0), ARGV(1), ARGV(2));
2417     }
2418     return RetStrVal(psbuff, info);
2419 }
2420 
2421 /***************************************************************/
2422 /*                                                             */
2423 /*  FPsmoon                                                    */
2424 /*                                                             */
2425 /*  Canned PostScript code for generating moon phases          */
2426 /*                                                             */
2427 /***************************************************************/
2428 static int psmoon_warned = 0;
2429 
FPsmoon(func_info * info)2430 static int FPsmoon(func_info *info)
2431 {
2432     char psbuff[512];
2433     char sizebuf[30];
2434     char fontsizebuf[30];
2435     char *s = psbuff;
2436     char const *extra = NULL;
2437     int size = -1;
2438     int fontsize = -1;
2439 
2440     ASSERT_TYPE(0, INT_TYPE);
2441     if (ARGV(0) < 0) return E_2LOW;
2442     if (ARGV(0) > 3) return E_2HIGH;
2443     if (Nargs > 1) {
2444 	ASSERT_TYPE(1, INT_TYPE);
2445 	if (ARGV(1) < -1) return E_2LOW;
2446 	size = ARGV(1);
2447 	if (Nargs > 2) {
2448 	    ASSERT_TYPE(2, STR_TYPE);
2449 	    extra = ARGSTR(2);
2450 	    if (Nargs > 3) {
2451 		ASSERT_TYPE(3, INT_TYPE);
2452 		if (ARGV(3) <= 0) return E_2LOW;
2453 		fontsize = ARGV(3);
2454 	    }
2455 	}
2456     }
2457     if (!psmoon_warned) {
2458 	psmoon_warned = 1;
2459 	Eprint("psmoon() is deprecated; use SPECIAL MOON instead.");
2460         FreshLine = 1;
2461     }
2462     if (size > 0) {
2463 	sprintf(sizebuf, "%d", size);
2464     } else {
2465 	strcpy(sizebuf, "DaySize 2 div");
2466     }
2467 
2468     if (fontsize > 0) {
2469 	sprintf(fontsizebuf, "%d", fontsize);
2470     } else {
2471 	strcpy(fontsizebuf, "EntrySize");
2472     }
2473 
2474     sprintf(s, "gsave 0 setgray newpath Border %s add BoxHeight Border sub %s sub",
2475 	    sizebuf, sizebuf);
2476     s += strlen(s);
2477     sprintf(s, " %s 0 360 arc closepath", sizebuf);
2478     s += strlen(s);
2479     switch(ARGV(0)) {
2480     case 0:
2481 	sprintf(s, " fill");
2482 	s += strlen(s);
2483 	break;
2484 
2485     case 2:
2486 	sprintf(s, " stroke");
2487 	s += strlen(s);
2488 	break;
2489 
2490     case 1:
2491 	sprintf(s, " stroke");
2492 	s += strlen(s);
2493 	sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub",
2494 		sizebuf, sizebuf);
2495 	s += strlen(s);
2496 	sprintf(s, " %s 90 270 arc closepath fill", sizebuf);
2497 	s += strlen(s);
2498 	break;
2499 
2500     default:
2501 	sprintf(s, " stroke");
2502 	s += strlen(s);
2503 	sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub",
2504 		sizebuf, sizebuf);
2505 	s += strlen(s);
2506 	sprintf(s, " %s 270 90 arc closepath fill", sizebuf);
2507 	s += strlen(s);
2508 	break;
2509     }
2510     if (extra) {
2511 	sprintf(s, " Border %s add %s add Border add BoxHeight border sub %s sub %s sub moveto /EntryFont findfont %s scalefont setfont (%s) show",
2512 		sizebuf, sizebuf, sizebuf, sizebuf, fontsizebuf, extra);
2513 	s += strlen(s);
2514     }
2515 
2516     sprintf(s, " grestore");
2517     return RetStrVal(psbuff, info);
2518 }
2519 
2520 /***************************************************************/
2521 /*                                                             */
2522 /*  FMoonphase                                                 */
2523 /*                                                             */
2524 /*  Phase of moon for specified date/time.                     */
2525 /*                                                             */
2526 /***************************************************************/
FMoonphase(func_info * info)2527 static int FMoonphase(func_info *info)
2528 {
2529     int date, time;
2530 
2531     switch(Nargs) {
2532     case 0:
2533 	date = JulianToday;
2534 	time = 0;
2535 	break;
2536     case 1:
2537 	if (!HASDATE(ARG(0))) return E_BAD_TYPE;
2538 	date = DATEPART(ARG(0));
2539 	if (HASTIME(ARG(0))) {
2540 	    time = TIMEPART(ARG(0));
2541 	} else {
2542 	    time = 0;
2543 	}
2544 	break;
2545     case 2:
2546 	if (ARG(0).type == DATETIME_TYPE) return E_2MANY_ARGS;
2547 	if (ARG(0).type != DATE_TYPE && ARG(1).type != TIME_TYPE) return E_BAD_TYPE;
2548 	date = ARGV(0);
2549 	time = ARGV(1);
2550 	break;
2551 
2552     default: return E_SWERR;
2553     }
2554 
2555     RetVal.type = INT_TYPE;
2556     RETVAL = MoonPhase(date, time);
2557     return OK;
2558 }
2559 
2560 /***************************************************************/
2561 /*                                                             */
2562 /*  FMoondate                                                  */
2563 /*                                                             */
2564 /*  Hunt for next occurrence of specified moon phase           */
2565 /*                                                             */
2566 /***************************************************************/
2567 static int MoonStuff (int want_time, func_info *info);
FMoondate(func_info * info)2568 static int FMoondate(func_info *info)
2569 {
2570     return MoonStuff(DATE_TYPE, info);
2571 }
2572 
FMoontime(func_info * info)2573 static int FMoontime(func_info *info)
2574 {
2575     return MoonStuff(TIME_TYPE, info);
2576 }
2577 
FMoondatetime(func_info * info)2578 static int FMoondatetime(func_info *info)
2579 {
2580     return MoonStuff(DATETIME_TYPE, info);
2581 }
2582 
MoonStuff(int type_wanted,func_info * info)2583 static int MoonStuff(int type_wanted, func_info *info)
2584 {
2585     int startdate, starttim;
2586     int d, t;
2587 
2588     startdate = JulianToday;
2589     starttim = 0;
2590 
2591     ASSERT_TYPE(0, INT_TYPE);
2592     if (ARGV(0) < 0) return E_2LOW;
2593     if (ARGV(0) > 3) return E_2HIGH;
2594     if (Nargs >= 2) {
2595 	if (!HASDATE(ARG(1))) return E_BAD_TYPE;
2596 	startdate = DATEPART(ARG(1));
2597 	if (HASTIME(ARG(1))) {
2598 		starttim = TIMEPART(ARG(1));
2599 	}
2600 
2601 	if (Nargs >= 3) {
2602 	    if (HASTIME(ARG(1))) return E_2MANY_ARGS;
2603 	    ASSERT_TYPE(2, TIME_TYPE);
2604 	    starttim = ARGV(2);
2605 	}
2606     }
2607 
2608     HuntPhase(startdate, starttim, ARGV(0), &d, &t);
2609     RetVal.type = type_wanted;
2610     switch(type_wanted) {
2611     case TIME_TYPE:
2612 	RETVAL = t;
2613 	break;
2614     case DATE_TYPE:
2615 	RETVAL = d;
2616 	break;
2617     case DATETIME_TYPE:
2618 	RETVAL = d * MINUTES_PER_DAY + t;
2619 	break;
2620     default:
2621 	return E_BAD_TYPE;
2622     }
2623     return OK;
2624 }
2625 
FTimepart(func_info * info)2626 static int FTimepart(func_info *info)
2627 {
2628     if (!HASTIME(ARG(0))) return E_BAD_TYPE;
2629     RetVal.type = TIME_TYPE;
2630     RETVAL = TIMEPART(ARG(0));
2631     return OK;
2632 }
2633 
FDatepart(func_info * info)2634 static int FDatepart(func_info *info)
2635 {
2636     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
2637     RetVal.type = DATE_TYPE;
2638     RETVAL = DATEPART(ARG(0));
2639     return OK;
2640 }
2641 
2642 #ifndef HAVE_SETENV
2643 /* This is NOT a general-purpose replacement for setenv.  It's only
2644  * used for the timezone stuff! */
setenv(char const * varname,char const * val,int overwrite)2645 static int setenv(char const *varname, char const *val, int overwrite)
2646 {
2647     static char tzbuf[256];
2648     if (strcmp(varname, "TZ")) {
2649 	fprintf(stderr, "built-in setenv can only be used with TZ\n");
2650 	abort();
2651     }
2652     if (!overwrite) {
2653 	fprintf(stderr, "built-in setenv must have overwrite=1\n");
2654 	abort();
2655     }
2656 
2657     if (strlen(val) > 250) {
2658 	return -1;
2659     }
2660     sprintf(tzbuf, "%s=%s", varname, val);
2661     return(putenv(tzbuf));
2662 }
2663 #endif
2664 #ifndef HAVE_UNSETENV
2665 /* This is NOT a general-purpose replacement for unsetenv.  It's only
2666  * used for the timezone stuff! */
unsetenv(char const * varname)2667 static void unsetenv(char const *varname)
2668 {
2669     static char tzbuf[8];
2670     if (strcmp(varname, "TZ")) {
2671 	fprintf(stderr, "built-in unsetenv can only be used with TZ\n");
2672 	abort();
2673     }
2674     sprintf(tzbuf, "%s", varname);
2675     putenv(tzbuf);
2676 }
2677 #endif
2678 
2679 /***************************************************************/
2680 /*                                                             */
2681 /*  FTz                                                        */
2682 /*                                                             */
2683 /*  Conversion between different timezones.                    */
2684 /*                                                             */
2685 /***************************************************************/
tz_set_tz(char const * tz)2686 static int tz_set_tz(char const *tz)
2687 {
2688     int r;
2689     if (tz == NULL) {
2690        unsetenv("TZ");
2691        r = 0;
2692     } else {
2693 	r = setenv("TZ", tz, 1);
2694     }
2695     tzset();
2696     return r;
2697 }
2698 
tz_convert(int year,int month,int day,int hour,int minute,char const * src_tz,char const * tgt_tz,struct tm * tm)2699 static int tz_convert(int year, int month, int day,
2700 		      int hour, int minute,
2701 		      char const *src_tz, char const *tgt_tz,
2702 		      struct tm *tm)
2703 {
2704     int r;
2705     time_t t;
2706     struct tm *res;
2707     char const *old_tz;
2708 
2709     /* init tm struct */
2710     tm->tm_sec = 0;
2711     tm->tm_min = minute;
2712     tm->tm_hour = hour;
2713     tm->tm_mday = day;
2714     tm->tm_mon = month;
2715     tm->tm_year = year - 1900;
2716     tm->tm_wday = 0; /* ignored by mktime */
2717     tm->tm_yday = 0; /* ignored by mktime */
2718     tm->tm_isdst = -1;  /* information not available */
2719 
2720     /* backup old TZ env var */
2721     old_tz = getenv("TZ");
2722     if (tgt_tz == NULL) {
2723 	tgt_tz = old_tz;
2724     }
2725 
2726     /* set source TZ */
2727     r = tz_set_tz(src_tz);
2728     if (r == -1) {
2729 	return -1;
2730     }
2731 
2732     /* create timestamp in UTC */
2733     t = mktime(tm);
2734 
2735     if (t == (time_t) -1) {
2736 	tz_set_tz(old_tz);
2737 	return -1;
2738     }
2739 
2740     /* set target TZ */
2741     r = tz_set_tz(tgt_tz);
2742     if (r == -1) {
2743 	tz_set_tz(old_tz);
2744 	return -1;
2745     }
2746 
2747     /* convert to target TZ */
2748     res = localtime_r(&t, tm);
2749 
2750     /* restore old TZ */
2751     tz_set_tz(old_tz);
2752 
2753     /* return result */
2754     if (res == NULL) {
2755 	return -1;
2756     } else {
2757 	return 1;
2758     }
2759 }
2760 
FTzconvert(func_info * info)2761 static int FTzconvert(func_info *info)
2762 {
2763     int year, month, day, hour, minute, r;
2764     int jul, tim;
2765     struct tm tm;
2766 
2767     if (ARG(0).type != DATETIME_TYPE ||
2768 	ARG(1).type != STR_TYPE) return E_BAD_TYPE;
2769     if (Nargs == 3 && ARG(2).type != STR_TYPE) return E_BAD_TYPE;
2770 
2771     FromJulian(DATEPART(ARG(0)), &year, &month, &day);
2772 
2773     r = TIMEPART(ARG(0));
2774     hour = r / 60;
2775     minute = r % 60;
2776 
2777     if (Nargs == 2) {
2778 	r = tz_convert(year, month, day, hour, minute,
2779 		       ARGSTR(1), NULL, &tm);
2780     } else {
2781 	r = tz_convert(year, month, day, hour, minute,
2782 		       ARGSTR(1), ARGSTR(2), &tm);
2783     }
2784 
2785     if (r == -1) return E_CANT_CONVERT_TZ;
2786 
2787     jul = Julian(tm.tm_year + 1900, tm.tm_mon, tm.tm_mday);
2788     tim = tm.tm_hour * 60 + tm.tm_min;
2789     RetVal.type = DATETIME_TYPE;
2790     RETVAL = jul * MINUTES_PER_DAY + tim;
2791     return OK;
2792 }
2793 
2794 static int
FSlide(func_info * info)2795 FSlide(func_info *info)
2796 {
2797     int r, omit, d, i, localomit, amt;
2798     Token tok;
2799 
2800     if (!HASDATE(ARG(0))) return E_BAD_TYPE;
2801     ASSERT_TYPE(1, INT_TYPE);
2802 
2803     d = DATEPART(ARG(0));
2804     amt = ARGV(1);
2805     if (amt > 1000000) return E_2HIGH;
2806     if (amt < -1000000) return E_2LOW;
2807 
2808     localomit = 0;
2809     for (i=2; i<Nargs; i++) {
2810 	if (ARG(i).type != STR_TYPE) return E_BAD_TYPE;
2811 	FindToken(ARG(i).v.str, &tok);
2812 	if (tok.type != T_WkDay) return E_UNKNOWN_TOKEN;
2813 	localomit |= (1 << tok.val);
2814     }
2815 
2816     /* If ALL weekdays are omitted... barf! */
2817     if (localomit == 127 && amt != 0) return E_2MANY_LOCALOMIT;
2818     if (amt > 0) {
2819 	while(amt) {
2820 	    d++;
2821 	    r = IsOmitted(d, localomit, NULL,&omit);
2822 	    if (r) return r;
2823 	    if (!omit) amt--;
2824 	}
2825     } else {
2826 	while(amt) {
2827 	    d--;
2828 	    if (d < 0) return E_DATE_OVER;
2829 	    r = IsOmitted(d, localomit, NULL,&omit);
2830 	    if (r) return r;
2831 	    if (!omit) amt++;
2832 	}
2833     }
2834     RetVal.type = DATE_TYPE;
2835     RETVAL = d;
2836     return OK;
2837 }
2838 
2839 static int
FNonomitted(func_info * info)2840 FNonomitted(func_info *info)
2841 {
2842     int d1, d2, ans, localomit, i;
2843     int omit, r;
2844     Token tok;
2845 
2846     if (!HASDATE(ARG(0)) ||
2847 	!HASDATE(ARG(1))) {
2848 	return E_BAD_TYPE;
2849     }
2850     d1 = DATEPART(ARG(0));
2851     d2 = DATEPART(ARG(1));
2852     if (d2 < d1) return E_2LOW;
2853 
2854     localomit = 0;
2855     for (i=2; i<Nargs; i++) {
2856 	if (ARG(i).type != STR_TYPE) return E_BAD_TYPE;
2857 	FindToken(ARG(i).v.str, &tok);
2858 	if (tok.type != T_WkDay) return E_UNKNOWN_TOKEN;
2859 	localomit |= (1 << tok.val);
2860     }
2861 
2862     ans = 0;
2863     while (d1 < d2) {
2864 	r = IsOmitted(d1++, localomit, NULL, &omit);
2865 	if (r) return r;
2866 	if (!omit) {
2867 	    ans++;
2868 	}
2869     }
2870     RetVal.type = INT_TYPE;
2871     RETVAL = ans;
2872     return OK;
2873 }
2874 
2875 static int
FWeekno(func_info * info)2876 FWeekno(func_info *info)
2877 {
2878     int jul = JulianToday;
2879     int wkstart = 0; /* Week start on Monday */
2880     int daystart = 29; /* First week starts on wkstart on or after Dec. 29 */
2881     int monstart;
2882     int candidate;
2883 
2884     int y, m, d;
2885 
2886     if (Nargs >= 1) {
2887 	if (!HASDATE(ARG(0))) return E_BAD_TYPE;
2888 	jul = DATEPART(ARG(0));
2889     }
2890     if (Nargs >= 2) {
2891 	ASSERT_TYPE(1, INT_TYPE);
2892 	if (ARGV(1) < 0) return E_2LOW;
2893 	if (ARGV(1) > 6) return E_2HIGH;
2894 	wkstart = ARGV(1);
2895 	/* Convert 0=Sun to 0=Mon */
2896 	wkstart--;
2897 	if (wkstart < 0) wkstart = 6;
2898 	if (Nargs >= 3) {
2899 	    ASSERT_TYPE(2, INT_TYPE);
2900 	    if (ARGV(2) < 1) return E_2LOW;
2901 	    if (ARGV(2) > 31) return E_2HIGH;
2902 	    daystart = ARGV(2);
2903 	}
2904     }
2905 
2906     RetVal.type = INT_TYPE;
2907     /* If start day is 7, first week starts after Jan,
2908        otherwise after Dec. */
2909     if (daystart <= 7) {
2910 	monstart = 0;
2911     } else {
2912 	monstart = 11;
2913     }
2914 
2915     FromJulian(jul, &y, &m, &d);
2916 
2917     /* Try this year */
2918     candidate = Julian(y, monstart, daystart);
2919     while((candidate % 7) != wkstart) candidate++;
2920 
2921     if (candidate <= jul) {
2922 	RETVAL = ((jul - candidate) / 7) + 1;
2923 	return OK;
2924     }
2925 
2926     if (y-1 < BASE) return E_DATE_OVER;
2927     /* Must be last year */
2928     candidate = Julian(y-1, monstart, daystart);
2929     while((candidate % 7) != wkstart) candidate++;
2930     if (candidate <= jul) {
2931 	RETVAL = ((jul - candidate) / 7) + 1;
2932 	return OK;
2933     }
2934 
2935     if (y-2 < BASE) return E_DATE_OVER;
2936     /* Holy cow! */
2937     candidate = Julian(y-2, monstart, daystart);
2938     while((candidate % 7) != wkstart) candidate++;
2939     RETVAL = ((jul - candidate) / 7) + 1;
2940     return OK;
2941 }
2942 
2943 static int
FEvalTrig(func_info * info)2944 FEvalTrig(func_info *info)
2945 {
2946     Parser p;
2947     Trigger trig;
2948     TimeTrig tim;
2949     int jul, scanfrom;
2950     int r;
2951 
2952     ASSERT_TYPE(0, STR_TYPE);
2953     if (Nargs >= 2) {
2954 	if (!HASDATE(ARG(1))) return E_BAD_TYPE;
2955 	scanfrom = DATEPART(ARG(1));
2956     } else {
2957 	scanfrom = NO_DATE;
2958     }
2959 
2960     CreateParser(ARGSTR(0), &p);
2961     p.allownested = 0;
2962     r = ParseRem(&p, &trig, &tim, 0);
2963     if (r) return r;
2964     if (trig.typ != NO_TYPE) {
2965 	FreeTrig(&trig);
2966 	return E_PARSE_ERR;
2967     }
2968     if (scanfrom == NO_DATE) {
2969 	jul = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0);
2970     } else {
2971 	/* Hokey... */
2972 	if (trig.scanfrom != JulianToday) {
2973 	    Eprint("Warning: SCANFROM is ignored in two-argument form of evaltrig()");
2974 	}
2975 	jul = ComputeTrigger(scanfrom, &trig, &tim, &r, 0);
2976     }
2977     if (r == E_CANT_TRIG && trig.maybe_uncomputable) {
2978         r = 0;
2979         jul = -1;
2980     }
2981     FreeTrig(&trig);
2982     if (r) return r;
2983     if (jul < 0) {
2984 	RetVal.type = INT_TYPE;
2985 	RETVAL = jul;
2986     } else if (tim.ttime == NO_TIME) {
2987 	RetVal.type = DATE_TYPE;
2988 	RETVAL = jul;
2989     } else {
2990 	RetVal.type = DATETIME_TYPE;
2991 	RETVAL = (MINUTES_PER_DAY * jul) + tim.ttime;
2992     }
2993     return OK;
2994 }
2995