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