1 /*!
2 * \file rc-utils.c
3 * \brief Pool of special functions necessary for managing
4 * the fixed dates.
5 */
6 /*
7 * Copyright (c) 1994, 95, 96, 1997, 2000, 2011 Thomas Esken
8 * Copyright (c) 2010, 2011, 2013 Free Software Foundation, Inc.
9 *
10 * This software doesn't claim completeness, correctness or usability.
11 * On principle I will not be liable for ANY damages or losses (implicit
12 * or explicit), which result from using or handling my software.
13 * If you use this software, you agree without any exception to this
14 * agreement, which binds you LEGALLY !!
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the `GNU General Public License' as published by
18 * the `Free Software Foundation'; either version 3, or (at your option)
19 * any later version.
20 *
21 * You should have received a copy of the `GNU General Public License'
22 * along with this program; if not, write to the:
23 *
24 */
25
26
27
28 /*
29 * Include definition header file to see whether USE_RC is defined there.
30 * Compile this module only if USE_RC is defined, otherwise skip it.
31 */
32 #include "tailor.h"
33
34
35 #if USE_RC
36
37
38 /*
39 * Include header files.
40 */
41 # if HAVE_CTYPE_H
42 # include <ctype.h>
43 # endif
44 # if HAVE_UNISTD_H
45 # include <unistd.h>
46 # endif
47 # if HAVE_MATH_H && HAVE_LIBM
48 # include <math.h>
49 # endif
50 # include "common.h"
51 # include "rc-defs.h"
52 # include "globals.h"
53 # include "file-io.h"
54 # include "hd-astro.h"
55 # include "hd-use.h"
56 # include "help.h"
57 # include "rc-astro.h"
58 # include "tty.h"
59 # include "utils.h"
60 # include "rc-utils.h"
61
62
63
64 /*
65 * static functions prototypes.
66 */
67 __BEGIN_DECLARATIONS
68 /*
69 ************************************************** Defined in `rc-utils.c'.
70 */
71 static void
72 var_warning __P_ ((const int exit_status,
73 const int var_name,
74 const char *line_buffer,
75 const char *filename, const long line_number));
76 __END_DECLARATIONS
77 /*
78 * Function implementations.
79 */
80
81
82 Bool
rc_valid_day(date_text,day,month,year)83 rc_valid_day (date_text, day, month, year)
84 const char *date_text;
85 const int day;
86 const int month;
87 const int year;
88 /*!
89 Checks the `date_text' for "%? special texts without argument", which
90 disables a fixed dates and which is stored in `date_text' without a
91 leading '%' character, and stores them into maps. The `date_text'
92 has to be proofed by the caller! If the date given in `day', `month'
93 and `year' is marked in the maps, we know that this date must be
94 excluded so this function returns FALSE, otherwise TRUE.
95 */
96 {
97 register int i;
98 register int wd = weekday_of_date (day, month, year);
99 auto const char *ptr_date_text = date_text;
100 auto Bool hd_found;
101 static Bool inclusive_weekday_map[DAY_MAX + 1];
102 static Bool exclusive_weekday_map[DAY_MAX + 1];
103
104
105 for (i = DAY_MIN; i <= DAY_MAX; i++)
106 inclusive_weekday_map[i] = !(exclusive_weekday_map[i] = TRUE);
107 *inclusive_weekday_map = *exclusive_weekday_map = FALSE;
108 while (*ptr_date_text)
109 {
110 hd_found = FALSE;
111 switch (*ptr_date_text)
112 {
113 case RC_EX_LHDY_CHAR:
114 case RC_EX_AHDY_CHAR:
115 /*
116 %exclude_legal_holiday special text or
117 %exclude_all_holiday special text found.
118 */
119 if (hd_ldays[((month - 1) * MONTH_LAST) + (day - 1)])
120 hd_found = TRUE;
121 if (hd_found || *ptr_date_text == RC_EX_LHDY_CHAR)
122 {
123 if (hd_found)
124 {
125 exclusive_weekday_map[wd] = FALSE;
126 *exclusive_weekday_map = TRUE;
127 }
128 break;
129 }
130 if (hd_mdays[((month - 1) * MONTH_LAST) + (day - 1)])
131 {
132 exclusive_weekday_map[wd] = FALSE;
133 *exclusive_weekday_map = TRUE;
134 }
135 break;
136 case RC_EX_NLHDY_CHAR:
137 case RC_EX_NAHDY_CHAR:
138 /*
139 %exclude_no_legal_holiday special text or
140 %exclude_no_all_holiday special text found.
141 */
142 if (hd_ldays[((month - 1) * MONTH_LAST) + (day - 1)])
143 {
144 hd_found = TRUE;
145 break;
146 }
147 if (hd_found || *ptr_date_text == RC_EX_NLHDY_CHAR)
148 {
149 if (hd_found)
150 inclusive_weekday_map[wd] = TRUE;
151 *inclusive_weekday_map = TRUE;
152 break;
153 }
154 if (hd_mdays[((month - 1) * MONTH_LAST) + (day - 1)])
155 {
156 hd_found = TRUE;
157 break;
158 }
159 if (hd_found)
160 inclusive_weekday_map[wd] = TRUE;
161 *inclusive_weekday_map = TRUE;
162 break;
163 case RC_EX_MON_CHAR:
164 /*
165 %exclude_monday special text found.
166 */
167 exclusive_weekday_map[DAY_MIN] = FALSE;
168 *exclusive_weekday_map = TRUE;
169 break;
170 case RC_EX_NMON_CHAR:
171 /*
172 %exclude_no_monday special text found.
173 */
174 *inclusive_weekday_map = inclusive_weekday_map[DAY_MIN] = TRUE;
175 break;
176 case RC_EX_TUE_CHAR:
177 /*
178 %exclude_tuesday special text found.
179 */
180 exclusive_weekday_map[2] = FALSE;
181 *exclusive_weekday_map = TRUE;
182 break;
183 case RC_EX_NTUE_CHAR:
184 /*
185 %exclude_no_tuesday special text found.
186 */
187 *inclusive_weekday_map = inclusive_weekday_map[2] = TRUE;
188 break;
189 case RC_EX_WED_CHAR:
190 /*
191 %exclude_wednesday special text found.
192 */
193 exclusive_weekday_map[3] = FALSE;
194 *exclusive_weekday_map = TRUE;
195 break;
196 case RC_EX_NWED_CHAR:
197 /*
198 %exclude_no_wednesday special text found.
199 */
200 *inclusive_weekday_map = inclusive_weekday_map[3] = TRUE;
201 break;
202 case RC_EX_THU_CHAR:
203 /*
204 %exclude_thursday special text found.
205 */
206 exclusive_weekday_map[4] = FALSE;
207 *exclusive_weekday_map = TRUE;
208 break;
209 case RC_EX_NTHU_CHAR:
210 /*
211 %exclude_no_thursday special text found.
212 */
213 *inclusive_weekday_map = inclusive_weekday_map[4] = TRUE;
214 break;
215 case RC_EX_FRI_CHAR:
216 /*
217 %exclude_friday special text found.
218 */
219 exclusive_weekday_map[5] = FALSE;
220 *exclusive_weekday_map = TRUE;
221 break;
222 case RC_EX_NFRI_CHAR:
223 /*
224 %exclude_no_friday special text found.
225 */
226 *inclusive_weekday_map = inclusive_weekday_map[5] = TRUE;
227 break;
228 case RC_EX_SAT_CHAR:
229 /*
230 %exclude_saturday special text found.
231 */
232 exclusive_weekday_map[6] = FALSE;
233 *exclusive_weekday_map = TRUE;
234 break;
235 case RC_EX_NSAT_CHAR:
236 /*
237 %exclude_no_saturday special text found.
238 */
239 *inclusive_weekday_map = inclusive_weekday_map[6] = TRUE;
240 break;
241 case RC_EX_SUN_CHAR:
242 /*
243 %exclude_sunday special text found.
244 */
245 exclusive_weekday_map[DAY_MAX] = FALSE;
246 *exclusive_weekday_map = TRUE;
247 break;
248 case RC_EX_NSUN_CHAR:
249 /*
250 %exclude_no_sunday special text found.
251 */
252 *inclusive_weekday_map = inclusive_weekday_map[DAY_MAX] = TRUE;
253 break;
254 case RC_EX_MON_2_THU_CHAR:
255 /*
256 %exclude_monday_to_thursday special text found.
257 */
258 for (i = DAY_MIN; i <= 4; i++)
259 exclusive_weekday_map[i] = FALSE;
260 *exclusive_weekday_map = TRUE;
261 break;
262 case RC_EX_NMON_2_THU_CHAR:
263 /*
264 %exclude_no_monday_to_thursday special text found.
265 */
266 for (i = 0; i <= 4; i++)
267 inclusive_weekday_map[i] = TRUE;
268 break;
269 case RC_EX_MON_2_FRI_CHAR:
270 /*
271 %exclude_monday_to_friday special text found.
272 */
273 for (i = DAY_MIN; i <= 5; i++)
274 exclusive_weekday_map[i] = FALSE;
275 *exclusive_weekday_map = TRUE;
276 break;
277 case RC_EX_NMON_2_FRI_CHAR:
278 /*
279 %exclude_no_monday_to_friday special text found.
280 */
281 for (i = 0; i <= 5; i++)
282 inclusive_weekday_map[i] = TRUE;
283 break;
284 default:
285 /*
286 This case MUST be an internal error!
287 */
288 abort ();
289 }
290 ptr_date_text++;
291 }
292 /*
293 Check whether a weekday to exclude is marked in the maps and
294 if so, avoid displaying the fixed date entry.
295 */
296 if (*inclusive_weekday_map || *exclusive_weekday_map)
297 {
298 if (*inclusive_weekday_map && *exclusive_weekday_map)
299 {
300 if (!inclusive_weekday_map[wd] || !exclusive_weekday_map[wd])
301 return (FALSE);
302 }
303 else if (*inclusive_weekday_map)
304 {
305 if (!inclusive_weekday_map[wd])
306 return (FALSE);
307 }
308 else if (!exclusive_weekday_map[wd])
309 return (FALSE);
310 }
311
312 return (TRUE);
313 }
314
315
316
317 Bool
rc_valid_period(date_text,d,m,y,incr_year,decr_year)318 rc_valid_period (date_text, d, m, y, incr_year, decr_year)
319 char *date_text;
320 const int d;
321 const int m;
322 const int y;
323 const int incr_year;
324 const int decr_year;
325 /*!
326 Checks the `date_text' for "%? special texts with date argument", which
327 disables a fixed dates and which is stored in `date_text' without a
328 leading '%' character, but separated by ',' colon characters, and stores
329 them into maps. The `date_text' has to be proofed by the caller!
330 If `the_day', `the_month' and `the_year' is marked there, we know
331 that this date must be excluded so this function returns FALSE,
332 otherwise TRUE.
333 */
334 {
335 auto Slint num;
336 register int i;
337 static Bool inclusive_date_map[DAY_LAST + 2];
338 static Bool exclusive_date_map[DAY_LAST + 2];
339 auto int len;
340 auto int rlen;
341 auto int dd;
342 auto int rdd;
343 auto int mm;
344 auto int rmm;
345 auto int yy;
346 auto int ryy;
347 auto int nn;
348 auto int rnn;
349 auto int hhn;
350 auto int rhn;
351 auto int hhwd;
352 auto int rhwd;
353 auto char *ptr_date_text = date_text;
354 auto char *ptr_char;
355 auto char special_text_char;
356 auto char ch;
357 auto char hhc;
358 auto char rhc;
359 auto Bool is_weekday_mode;
360 auto Bool ris_weekday_mode;
361 auto Bool is_range;
362 auto Bool dflt_yy_set;
363 auto Bool dflt_ryy_set;
364
365
366 /*
367 Initialize the tables.
368 */
369 for (i = DAY_MIN; i < DAY_LAST + 2; i++)
370 inclusive_date_map[i] = !(exclusive_date_map[i] = TRUE);
371 *inclusive_date_map = *exclusive_date_map = FALSE;
372 while (*ptr_date_text)
373 {
374 /*
375 Check if a range of dates is given.
376 */
377 rhc = '\0';
378 rlen = rdd = rmm = ryy = rnn = 0;
379 is_range = dflt_yy_set = dflt_ryy_set = FALSE;
380 special_text_char = *ptr_date_text++;
381 ptr_char = ptr_date_text;
382 while (*ptr_date_text
383 && (*ptr_date_text != *SPLIT_SEP)
384 && (*ptr_date_text != RC_DRANGE_CHAR))
385 ptr_date_text++;
386 if (*ptr_date_text == RC_DRANGE_CHAR)
387 is_range = TRUE;
388 ch = *ptr_date_text;
389 *ptr_date_text = '\0';
390 (void) rc_get_date (ptr_char, lptrs3, FALSE, &is_weekday_mode, &dd, &mm,
391 &yy, &nn, &len, &hhc, &hhn, &hhwd, _("Internal"),
392 -1L, date_text, FALSE);
393 /*
394 Error, invalid date encoded.
395 */
396 if (yy == SPECIAL_VALUE)
397 {
398 fprintf (stderr, _("%s: invalid date given -- %s\n%s\n%s\n"),
399 prgr_name, date_text, usage_msg (), lopt_msg ());
400 my_exit (ERR_INVALID_OPTION);
401 }
402 *ptr_date_text = ch;
403 if (is_range)
404 {
405 ptr_char = ++ptr_date_text;
406 while (*ptr_date_text && (*ptr_date_text != *SPLIT_SEP))
407 ptr_date_text++;
408 ch = *ptr_date_text;
409 *ptr_date_text = '\0';
410 (void) rc_get_date (ptr_char, lptrs3, FALSE, &ris_weekday_mode,
411 &rdd, &rmm, &ryy, &rnn, &rlen, &rhc, &rhn,
412 &rhwd, _("Internal"), -1L, date_text, FALSE);
413 if (ryy == SPECIAL_VALUE)
414 {
415 fprintf (stderr, _("%s: invalid date given -- %s\n%s\n%s\n"),
416 prgr_name, date_text, usage_msg (), lopt_msg ());
417 my_exit (ERR_INVALID_OPTION);
418 }
419 *ptr_date_text = ch;
420 }
421 if (ch)
422 ptr_date_text++;
423 if (!len)
424 dflt_yy_set = TRUE;
425 if (!rlen)
426 dflt_ryy_set = TRUE;
427 if (!yy)
428 {
429 if (dflt_yy_set)
430 yy = YEAR_MIN;
431 else
432 {
433 yy = year;
434 if (y && (fiscal_month > MONTH_MIN))
435 yy = y;
436 }
437 }
438 if (!ryy)
439 {
440 if (dflt_ryy_set)
441 ryy = YEAR_MAX;
442 else
443 {
444 ryy = year;
445 if (y && (fiscal_month > MONTH_MIN))
446 ryy = y;
447 }
448 }
449 /*
450 Respect possible fiscal year.
451 */
452 if (!dflt_yy_set && (yy != SPECIAL_VALUE))
453 {
454 yy -= incr_year;
455 yy += decr_year;
456 }
457 if (!dflt_ryy_set && (ryy != SPECIAL_VALUE))
458 {
459 ryy -= incr_year;
460 ryy += decr_year;
461 }
462 /*
463 If @... "date"-part is given, compute the according date.
464 */
465 switch (hhc)
466 {
467 case RC_EASTER_CHAR:
468 case RC_TODAY_CHAR:
469 if (!dflt_yy_set && (fiscal_month > MONTH_MIN))
470 {
471 if (!precomp_date (hhn, hhwd, &dd, &mm, yy + incr_year,
472 (hhc == RC_EASTER_CHAR) ? EAster : TOday))
473 yy = SPECIAL_VALUE;
474 }
475 else
476 if (!precomp_date (hhn, hhwd, &dd, &mm, yy,
477 (hhc == RC_EASTER_CHAR) ? EAster : TOday))
478 yy = SPECIAL_VALUE;
479 break;
480 case 'D':
481 case 'W':
482 if (!dflt_yy_set && (fiscal_month > MONTH_MIN))
483 {
484 auto int fiscal_year = yy + incr_year;
485
486
487 if (!precomp_nth_wd (hhn, hhwd, &hhn, &dd, &mm, &fiscal_year,
488 (hhc == 'D') ? DAy : WEek))
489 yy = fiscal_year;
490 }
491 else
492 (void) precomp_nth_wd (hhn, hhwd, &hhn, &dd, &mm, &yy,
493 (hhc == 'D') ? DAy : WEek);
494 break;
495 default:
496 if (islower (hhc))
497 {
498 if (rc_dvar[IDX (hhc)].dvar_local.dvar_month)
499 {
500 mm = (int) rc_dvar[IDX (hhc)].dvar_local.dvar_month;
501 dd = (int) rc_dvar[IDX (hhc)].dvar_local.dvar_day;
502 }
503 else if (rc_dvar[IDX (hhc)].dvar_global.dvar_month)
504 {
505 mm = (int) rc_dvar[IDX (hhc)].dvar_global.dvar_month;
506 dd = (int) rc_dvar[IDX (hhc)].dvar_global.dvar_day;
507 }
508 if (!dflt_yy_set && (fiscal_month > MONTH_MIN))
509 {
510 if (!precomp_date
511 (hhn, hhwd, &dd, &mm, yy + incr_year, DVar))
512 yy = SPECIAL_VALUE;
513 }
514 else if (!precomp_date (hhn, hhwd, &dd, &mm, yy, DVar))
515 yy = SPECIAL_VALUE;
516 }
517 }
518 switch (rhc)
519 {
520 case RC_EASTER_CHAR:
521 case RC_TODAY_CHAR:
522 if (!dflt_ryy_set && (fiscal_month > MONTH_MIN))
523 {
524 if (!precomp_date (rhn, rhwd, &rdd, &rmm, ryy + incr_year,
525 (rhc == RC_EASTER_CHAR) ? EAster : TOday))
526 ryy = SPECIAL_VALUE;
527 }
528 else
529 if (!precomp_date (rhn, rhwd, &rdd, &rmm, ryy,
530 (rhc == RC_EASTER_CHAR) ? EAster : TOday))
531 ryy = SPECIAL_VALUE;
532 break;
533 case 'D':
534 case 'W':
535 if (!dflt_ryy_set && (fiscal_month > MONTH_MIN))
536 {
537 auto int fiscal_year = ryy + incr_year;
538
539
540 if (!precomp_nth_wd (rhn, rhwd, &rhn, &rdd, &rmm, &fiscal_year,
541 (rhc == 'D') ? DAy : WEek))
542 ryy = fiscal_year;
543 }
544 else
545 (void) precomp_nth_wd (rhn, rhwd, &rhn, &rdd, &rmm, &ryy,
546 (rhc == 'D') ? DAy : WEek);
547 break;
548 default:
549 if (islower (rhc))
550 {
551 if (rc_dvar[IDX (rhc)].dvar_local.dvar_month)
552 {
553 rmm = (int) rc_dvar[IDX (rhc)].dvar_local.dvar_month;
554 rdd = (int) rc_dvar[IDX (rhc)].dvar_local.dvar_day;
555 }
556 else if (rc_dvar[IDX (rhc)].dvar_global.dvar_month)
557 {
558 rmm = (int) rc_dvar[IDX (rhc)].dvar_global.dvar_month;
559 rdd = (int) rc_dvar[IDX (rhc)].dvar_global.dvar_day;
560 }
561 if (!dflt_ryy_set && (fiscal_month > MONTH_MIN))
562 {
563 if (!precomp_date
564 (rhn, rhwd, &rdd, &rmm, ryy + incr_year, DVar))
565 ryy = SPECIAL_VALUE;
566 }
567 else if (!precomp_date (rhn, rhwd, &rdd, &rmm, ryy, DVar))
568 ryy = SPECIAL_VALUE;
569 }
570 }
571 if (len > len_year_max)
572 len -= len_year_max;
573 else
574 len = 0;
575 if (rlen > len_year_max)
576 rlen -= len_year_max;
577 else
578 rlen = 0;
579 /*
580 Assume current/first month of year.
581 */
582 if (!mm)
583 {
584 if (len >= 1)
585 {
586 mm = m;
587 if (len == 2)
588 len = 0;
589 else
590 len--;
591 }
592 else
593 mm = MONTH_MIN;
594 }
595 else
596 {
597 if (len == 2)
598 len = 0;
599 else
600 len--;
601 }
602 /*
603 Assume current/first day of month.
604 */
605 if (!dd)
606 {
607 if (len >= 1)
608 dd = d;
609 else
610 dd = DAY_MIN;
611 }
612 /*
613 Assume current/last month of year.
614 */
615 if (!rmm)
616 {
617 if (rlen >= 1)
618 {
619 rmm = m;
620 if (rlen == 2)
621 rlen = 0;
622 else
623 rlen--;
624 }
625 else
626 rmm = MONTH_MAX;
627 }
628 else
629 {
630 if (rlen == 2)
631 rlen = 0;
632 else
633 rlen--;
634 }
635 /*
636 Assume current/last day of month.
637 */
638 if (!rdd)
639 {
640 if (rlen >= 1)
641 rdd = d;
642 else
643 {
644 if (rmm == 2)
645 {
646 if (!dflt_ryy_set && (fiscal_month > MONTH_MIN))
647 rdd = days_of_february (ryy + incr_year);
648 else
649 rdd = days_of_february (ryy);
650 }
651 else
652 rdd = dvec[rmm - 1];
653 }
654 }
655 /*
656 If special value "99" for day `dd' is given,
657 set the day to last day of month.
658 */
659 if (dd == 99)
660 {
661 /*
662 Assume the last day of month.
663 */
664 if (mm == 2)
665 dd = days_of_february (yy);
666 else
667 dd = dvec[mm - 1];
668 }
669 if (rdd == 99)
670 {
671 /*
672 Assume the last day of month.
673 */
674 if (rmm == 2)
675 {
676 if (!dflt_ryy_set && (fiscal_month > MONTH_MIN))
677 rdd = days_of_february (ryy + incr_year);
678 else
679 rdd = days_of_february (ryy);
680 }
681 else
682 rdd = dvec[rmm - 1];
683 }
684 /*
685 If "N'th weekday of month" entry set, compute the according date.
686 */
687 if (nn)
688 nth_weekday_of_month (&dd, &mm, &yy, &nn, &is_weekday_mode);
689 if (rnn)
690 nth_weekday_of_month (&rdd, &rmm, &ryy, &rnn, &ris_weekday_mode);
691 /*
692 Proceed if (optionally specified) date is valid.
693 */
694 if ((!is_range
695 && (yy != SPECIAL_VALUE))
696 || (is_range && (yy != SPECIAL_VALUE) && (ryy != SPECIAL_VALUE)))
697 {
698 register int true_year = (y) ? y : year + incr_year;
699
700
701 if (!nn
702 && !dflt_yy_set
703 && (fiscal_month > MONTH_MIN
704 || (incr_year && (rc_tomorrow_flag || rc_week_flag))))
705 yy += incr_year;
706 if (!rnn
707 && !dflt_ryy_set
708 && (fiscal_month > MONTH_MIN
709 || (incr_year && (rc_tomorrow_flag || rc_week_flag))))
710 ryy += incr_year;
711 /*
712 If starting date of event not greater than ending
713 date of event, mark the period in according map,
714 otherwise ignore the %?... special text completely.
715 */
716 num = d_between (dd, mm, yy, rdd, rmm, ryy);
717 if (num >= 0L)
718 {
719 register int s_doy = DAY_MIN;
720 register int e_doy = DAY_LAST + 1;
721
722
723 if (special_text_char == RC_IDATE_CHAR)
724 *inclusive_date_map = TRUE;
725 else
726 *exclusive_date_map = TRUE;
727 if (yy == true_year)
728 s_doy = day_of_year (dd, mm, yy);
729 else if (yy > true_year)
730 s_doy = SPECIAL_VALUE;
731 if (ryy == true_year)
732 e_doy = day_of_year (rdd, rmm, ryy);
733 else if (ryy < true_year)
734 e_doy = SPECIAL_VALUE;
735 if ((s_doy != SPECIAL_VALUE) && (e_doy != SPECIAL_VALUE))
736 {
737 if (special_text_char == RC_IDATE_CHAR)
738 for (i = s_doy; i <= e_doy; i++)
739 inclusive_date_map[i] = TRUE;
740 else
741 for (i = s_doy; i <= e_doy; i++)
742 exclusive_date_map[i] = FALSE;
743 }
744 }
745 }
746 }
747 /*
748 Check whether a period to exclude is marked in the maps and
749 if so, avoid displaying the fixed date entry.
750 */
751 if (*inclusive_date_map || *exclusive_date_map)
752 {
753 i = day_of_year (d, m, year + incr_year - decr_year);
754 if (*inclusive_date_map && *exclusive_date_map)
755 {
756 if (!inclusive_date_map[i] || !exclusive_date_map[i])
757 return (FALSE);
758 }
759 else if (*inclusive_date_map)
760 {
761 if (!inclusive_date_map[i])
762 return (FALSE);
763 }
764 else if (!exclusive_date_map[i])
765 return (FALSE);
766 }
767
768 return (TRUE);
769 }
770
771
772
773 void
rc_clean_flags()774 rc_clean_flags ()
775 /*!
776 Cleans all global flags (except `rc_period_list')
777 which are related to the fixed date period.
778 */
779 {
780 rc_tomorrow_flag = rc_week_flag = rc_month_flag = rc_year_flag
781 = rc_week_year_flag = rc_forwards_flag = rc_backwards_flag =
782 rc_period_flag = FALSE;
783 }
784
785
786
787 Line_struct *
rc_get_date(the_line,lineptrs,is_rc_file,is_weekday_mode,d,m,y,n,len,hc,hn,hwd,filename,line_number,line_buffer,on_error_exit)788 rc_get_date (the_line, lineptrs, is_rc_file, is_weekday_mode, d, m, y, n, len,
789 hc, hn, hwd, filename, line_number, line_buffer, on_error_exit)
790 char *the_line;
791 Line_struct *lineptrs;
792 const Bool is_rc_file;
793 Bool *is_weekday_mode;
794 int *d;
795 int *m;
796 int *y;
797 int *n;
798 int *len;
799 char *hc;
800 int *hn;
801 int *hwd;
802 const char *filename;
803 const long line_number;
804 const char *line_buffer;
805 const Bool on_error_exit;
806 /*!
807 Converts the textual/string `date' of a RC-file line into a numerical date
808 and returns a pointer struct to the "day"-part and the "text"-part of the
809 line indicating whether the "day"-part contains a list or a range of days;
810 a char pointer to the "repeat"-field and to the "appears"-field if these
811 exists, and/or if a @... or *... day is encoded in "date"-part and year
812 is set to zero in the line, then this function returns holiday_mode_char
813 (==date variable) or upper-case characters 'D' or 'W' in `&hc', the day
814 displacement in `&hn' and a possible weekday name (mo...su) converted to
815 a number (1...7) in `&hwd' for further managing of such a line. If any
816 invalid date is given in `the_line', then this function either returns
817 SPECIAL_VALUE in &y or aborts the program with an error message
818 (depending on mode of operation resp., contents of `on_error_exit'
819 variable).
820 */
821 {
822 register int num_of_range_chars = 0;
823 register int num_of_repeat_chars = 0;
824 register int num_of_appears_chars = 0;
825 register int i;
826 static char str7[8]; /* For "date"-parts, length of 7 chars+'\0' maximum! */
827 auto char *ptr_char;
828 auto Bool is_hdy_mode = FALSE;
829
830
831 *hc = '\0';
832 lineptrs->day_list = lineptrs->day_range = FALSE;
833 lineptrs->repeat_part = lineptrs->appears_part = (char *) NULL;
834 (*len) = (*hn) = (*hwd) = (*n) = i = 0;
835 /*
836 Get the year from the year field of the line.
837 */
838 while (*the_line
839 && !isspace (*the_line) && isdigit (*the_line) && (i < len_year_max))
840 str7[i++] = *the_line++;
841 str7[i] = '\0';
842 *y = my_atoi (str7);
843 *len = i;
844 /*
845 Get the month from the month field of the line.
846 */
847 i = 0;
848 while (*the_line && !isspace (*the_line) && (i < 2))
849 str7[i++] = *the_line++;
850 if (i)
851 /*
852 Try to get a short (3 character) textual month name.
853 */
854 if (isalpha (*the_line) && (isupper (str7[i - 1])
855 # if USE_EASC
856 || str7[i - 1] == *AE
857 || str7[i - 1] == *OE
858 || str7[i - 1] == *UE
859 || str7[i - 1] == *AAE
860 || str7[i - 1] == *OOE || str7[i - 1] == *UUE
861 # else /* !USE_EASC */
862 || str7[i - 1] == '"'
863 # endif /* !USE_EASC */
864 || islower (str7[i - 1])))
865 str7[i++] = *the_line++;
866 str7[i] = '\0';
867 *m = my_atoi (str7);
868 if (!*m)
869 /*
870 Check for short (3 character) textual month name.
871 */
872 *m = compare_d_m_name (str7, MOnth);
873 else if (i == 3 || ((i == 2) && (!isdigit (str7[1]))))
874 {
875 /*
876 Error, invalid month field.
877 */
878 if (on_error_exit)
879 my_error (ERR_INVALID_MONTH_FIELD, filename, line_number, line_buffer,
880 *m);
881 *y = SPECIAL_VALUE;
882 }
883 /*
884 Check if @... date variable statement or *... statement is given.
885 */
886 if (i)
887 {
888 *len += i;
889 if (*str7 == RC_HDY_CHAR)
890 {
891 is_hdy_mode = TRUE;
892 if (i == 2)
893 *hc = (char) tolower (str7[1]);
894 }
895 else if (*str7 == RC_NWD_CHAR)
896 {
897 is_hdy_mode = TRUE;
898 if ((i == 2)
899 && (toupper (str7[1]) == 'D' || toupper (str7[1]) == 'W'))
900 *hc = (char) toupper (str7[1]);
901 else
902 {
903 if (i == 2)
904 /*
905 Error, invalid mode specifying character given.
906 */
907 *hc = (char) toupper (str7[1]);
908 else
909 /*
910 Error, no mode specifying character given.
911 */
912 *hc = *str7;
913 }
914 }
915 }
916 /*
917 If the special value "99" for a month `&m' is given,
918 set the month to 12 (December).
919 */
920 if (*m == 99)
921 *m = MONTH_MAX;
922 if (!is_hdy_mode
923 && (*m > MONTH_MAX
924 || (!*m
925 && (((i == 1)
926 && !isdigit (*str7))
927 || ((i == 2)
928 && (!isdigit (*str7)
929 || !isdigit (str7[1])))
930 || ((i == 3)
931 && (!isdigit (*str7)
932 || !isdigit (str7[1]) || !isdigit (str7[2])))))))
933 {
934 /*
935 Error, invalid month field given.
936 */
937 if (on_error_exit)
938 my_error (ERR_INVALID_MONTH_FIELD, filename, line_number, line_buffer,
939 *m);
940 *y = SPECIAL_VALUE;
941 }
942 /*
943 Get the day (maximum 3 characters in this case, template is either DD, WW or WWW)
944 resp., @... date variable or *... statement (maximum 7 characters in this case,
945 template is: [+|-]NNNWWW).
946 */
947 ptr_char = lineptrs->day_part = the_line;
948 i = 0;
949 while (*the_line && !isspace (*the_line) && (i < ((is_hdy_mode) ? 7 : 3)))
950 str7[i++] = *the_line++;
951 str7[i] = '\0';
952 *d = atoi (str7);
953 *len += i;
954 *is_weekday_mode = FALSE;
955 the_line--;
956 if (isalpha (*the_line) || ((i < 3) && !is_hdy_mode))
957 the_line++;
958 /*
959 Check for a list/range of days/textual day names,
960 if such a list is found, let `lineptrs->day_part' point to it
961 and return to caller for further managing this list/range.
962 */
963 while (*ptr_char && !isspace (*ptr_char))
964 {
965 if (*ptr_char == RC_DLIST_CHAR)
966 lineptrs->day_list = TRUE;
967 else if (*ptr_char == RC_DRANGE_CHAR)
968 {
969 num_of_range_chars++;
970 lineptrs->day_range = TRUE;
971 }
972 else if (*ptr_char == RC_REPEAT_CHAR)
973 {
974 num_of_repeat_chars++;
975 lineptrs->repeat_part = ptr_char;
976 }
977 else if (*ptr_char == RC_APPEARS_CHAR)
978 {
979 num_of_appears_chars++;
980 lineptrs->appears_part = ptr_char;
981 }
982 ptr_char++;
983 }
984 if (lineptrs->day_list || lineptrs->day_range)
985 {
986 if (is_rc_file)
987 {
988 if ((num_of_range_chars > 1
989 || *ptr_char == RC_DLIST_CHAR
990 || *ptr_char == RC_DRANGE_CHAR
991 || (lineptrs->day_list
992 && lineptrs->day_range)
993 || (!lineptrs->day_list
994 && !lineptrs->day_range
995 && (num_of_repeat_chars > 1
996 || num_of_appears_chars > 1))) && on_error_exit)
997 /*
998 Error, invalid list/range of days.
999 */
1000 my_error (ERR_INVALID_DATE_FIELD, filename, line_number,
1001 line_buffer, 0);
1002 /*
1003 Check if a day variable is referenced.
1004 */
1005 if (islower (*hc)
1006 && (*hc != RC_EASTER_CHAR) && (*hc != RC_TODAY_CHAR))
1007 {
1008 /*
1009 Try to assign a local date variable if there is set any,
1010 else try to assign a global date variable if there is set any,
1011 otherwise we have to skip this part.
1012 */
1013 if (rc_dvar[IDX (*hc)].dvar_local.dvar_month
1014 || rc_dvar[IDX (*hc)].dvar_global.dvar_month)
1015 {
1016 if (rc_dvar[IDX (*hc)].dvar_local.dvar_month)
1017 {
1018 *m = (int) rc_dvar[IDX (*hc)].dvar_local.dvar_month;
1019 *d = (int) rc_dvar[IDX (*hc)].dvar_local.dvar_day;
1020 }
1021 else
1022 {
1023 *m = (int) rc_dvar[IDX (*hc)].dvar_global.dvar_month;
1024 *d = (int) rc_dvar[IDX (*hc)].dvar_global.dvar_day;
1025 }
1026 }
1027 else
1028 {
1029 /*
1030 Error, no such date variable defined.
1031 */
1032 if ((warning_level >= 0) && on_error_exit)
1033 var_warning (ERR_INVALID_VAR_REFERENCE, (int) *hc,
1034 line_buffer, filename, line_number);
1035 *y = SPECIAL_VALUE;
1036 }
1037 }
1038 if (!isalpha (str7[i - 1]))
1039 (*len)--;
1040 i = 0;
1041 while (*the_line && !isspace (*the_line))
1042 {
1043 the_line++;
1044 i++;
1045 }
1046 *len += i;
1047 }
1048 else
1049 {
1050 /*
1051 Error, list/range of days is given in an expression it may not occur.
1052 */
1053 if (on_error_exit)
1054 my_error (ERR_INVALID_DATE_FIELD, filename, line_number,
1055 line_buffer, 0);
1056 *y = SPECIAL_VALUE;
1057 }
1058 }
1059 else
1060 {
1061 if (!is_rc_file && (num_of_repeat_chars || num_of_appears_chars))
1062 {
1063 /*
1064 Error, day "repeat" or "appears" coding is given in an expression
1065 it may not occur.
1066 */
1067 if (on_error_exit)
1068 my_error (ERR_INVALID_DATE_FIELD, filename, line_number,
1069 line_buffer, 0);
1070 *y = SPECIAL_VALUE;
1071 }
1072 else if (num_of_repeat_chars > 1 || num_of_appears_chars > 1)
1073 {
1074 /*
1075 Error, "repeat" or "appears" coding given twice or more.
1076 */
1077 if (on_error_exit)
1078 my_error (ERR_INVALID_DATE_FIELD, filename, line_number,
1079 line_buffer, 0);
1080 *y = SPECIAL_VALUE;
1081 }
1082 lineptrs->day_part = (char *) NULL;
1083 }
1084 /*
1085 If no list/range of days is given, try to precompute the according date.
1086 */
1087 if (lineptrs->day_part == (char *) NULL)
1088 {
1089 if (!is_hdy_mode)
1090 {
1091 /*
1092 Check for simple textual day name (either two or three characters),
1093 template WW or WWW.
1094 */
1095 if (!*d)
1096 {
1097 if (*str7)
1098 *d = compare_d_m_name (str7, DAy);
1099 if (*d)
1100 {
1101 *is_weekday_mode = TRUE;
1102 if (isdigit (str7[i - 1]))
1103 (*len)--;
1104 }
1105 else
1106 {
1107 i = 0;
1108 while (isdigit (str7[i]))
1109 i++;
1110 if (str7[i])
1111 {
1112 /*
1113 Error, invalid day field.
1114 */
1115 if (on_error_exit)
1116 my_error (ERR_INVALID_DAY_FIELD, filename,
1117 line_number, line_buffer, *d);
1118 *y = SPECIAL_VALUE;
1119 }
1120 }
1121 }
1122 else if ((i > 1) && !isdigit (str7[1]))
1123 {
1124 /*
1125 Error, invalid day field.
1126 */
1127 if (on_error_exit)
1128 my_error (ERR_INVALID_DAY_FIELD, filename, line_number,
1129 line_buffer, *d);
1130 *y = SPECIAL_VALUE;
1131 }
1132 /*
1133 Check whether a "N'th weekday of month" field exists.
1134 */
1135 if (*the_line && !isspace (*the_line))
1136 {
1137 if (isdigit (*the_line))
1138 {
1139 *n = CHR2DIG (*the_line);
1140 if (*n)
1141 {
1142 if ((*n > 5) && (*n < 9))
1143 {
1144 /*
1145 Error, invalid "N'th weekday of month" field.
1146 */
1147 if (on_error_exit)
1148 my_error (ERR_INVALID_NWD_FIELD, filename,
1149 line_number, line_buffer, *n);
1150 *y = SPECIAL_VALUE;
1151 }
1152 }
1153 }
1154 else
1155 if ((lineptrs->repeat_part == (char *) NULL)
1156 && (lineptrs->appears_part == (char *) NULL))
1157 {
1158 /*
1159 Error, missing separator between "date"-part
1160 and "text"-part.
1161 */
1162 if (on_error_exit)
1163 my_error (ERR_NO_SEPARATOR_CHAR, filename, line_number,
1164 line_buffer, 0);
1165 *y = SPECIAL_VALUE;
1166 }
1167 if (*the_line)
1168 the_line++;
1169 if (*the_line
1170 && !isspace (*the_line)
1171 && (lineptrs->repeat_part == (char *) NULL)
1172 && (lineptrs->appears_part == (char *) NULL))
1173 {
1174 /*
1175 Error, missing separator between "date"-part and "text"-part.
1176 */
1177 if (on_error_exit)
1178 my_error (ERR_NO_SEPARATOR_CHAR, filename, line_number,
1179 line_buffer, 0);
1180 *y = SPECIAL_VALUE;
1181 }
1182 if (*n && (*d < DAY_MIN || *d > DAY_MAX))
1183 {
1184 /*
1185 Error, "N'th weekday of month" entry set
1186 but invalid day encoded.
1187 */
1188 if (on_error_exit)
1189 my_error (ERR_INVALID_DAY_FIELD, filename, line_number,
1190 line_buffer, *d);
1191 *y = SPECIAL_VALUE;
1192 }
1193 (*len)++;
1194 if (lineptrs->repeat_part != (char *) NULL
1195 || lineptrs->appears_part != (char *) NULL)
1196 while (*the_line && !isspace (*the_line))
1197 {
1198 the_line++;
1199 (*len)++;
1200 }
1201 }
1202 }
1203 else
1204 {
1205 if (isdigit (*the_line))
1206 the_line++;
1207 if (*the_line
1208 && !isspace (*the_line)
1209 && (lineptrs->repeat_part == (char *) NULL)
1210 && (lineptrs->appears_part == (char *) NULL))
1211 {
1212 /*
1213 Error, missing separator character between "date"-part
1214 and "text"-part.
1215 */
1216 if (on_error_exit)
1217 my_error (ERR_NO_SEPARATOR_CHAR, filename, line_number,
1218 line_buffer, 0);
1219 *y = SPECIAL_VALUE;
1220 }
1221 /*
1222 Compute the base date of '@' date variable "date"-part of line
1223 or '*' N'th weekday of year/weekday WW[W] of N'th week
1224 in case an explicit year YYYY is given in the "date"-part.
1225 */
1226 i = atoi (str7);
1227 ptr_char = str7;
1228 if (islower (*hc))
1229 {
1230 if (*ptr_char == *ASC_LIT || *ptr_char == *DES_LIT)
1231 ptr_char++;
1232 if (*ptr_char == *ASC_LIT
1233 || *ptr_char == *DES_LIT || isalpha (*ptr_char))
1234 {
1235 /*
1236 Error, simple weekday name or invalid sign given.
1237 */
1238 if (on_error_exit)
1239 my_error (ERR_INVALID_DATE_FIELD, filename, line_number,
1240 line_buffer, 0);
1241 *hc = '\0';
1242 *d = 0;
1243 *y = SPECIAL_VALUE;
1244 }
1245 }
1246 else if (*ptr_char == *ASC_LIT || *ptr_char == *DES_LIT)
1247 {
1248 /*
1249 Error, invalid sign given.
1250 */
1251 if (on_error_exit)
1252 my_error (ERR_INVALID_DATE_FIELD, filename, line_number,
1253 line_buffer, 0);
1254 *hc = '\0';
1255 *d = 0;
1256 *y = SPECIAL_VALUE;
1257 }
1258 /*
1259 Now eat all digits.
1260 */
1261 while (isdigit (*ptr_char))
1262 ptr_char++;
1263 if (*ptr_char
1264 && (*ptr_char != RC_REPEAT_CHAR)
1265 && (*ptr_char != RC_APPEARS_CHAR))
1266 {
1267 *hwd = compare_d_m_name (ptr_char, DAy);
1268 if (!*hwd)
1269 {
1270 /*
1271 Error, invalid textual short day name given.
1272 */
1273 if (on_error_exit)
1274 my_error (ERR_INVALID_DATE_FIELD, filename, line_number,
1275 line_buffer, 0);
1276 *hc = '\0';
1277 *d = 0;
1278 *y = SPECIAL_VALUE;
1279 }
1280 }
1281 if (*y >= 0)
1282 {
1283 if (*hc == RC_EASTER_CHAR || *hc == RC_TODAY_CHAR)
1284 {
1285 if (!precomp_date (i, *hwd, d, m, *y,
1286 (*hc ==
1287 RC_EASTER_CHAR) ? EAster : TOday))
1288 {
1289 if (!*y)
1290 {
1291 /*
1292 No explicit year YYYY given in "date"-part of line.
1293 */
1294 *hn = i;
1295 *d = (*m) = 0;
1296 }
1297 else
1298 {
1299 /*
1300 Invalid relative date given.
1301 */
1302 *hc = '\0';
1303 *d = 0;
1304 *y = SPECIAL_VALUE;
1305 }
1306 }
1307 else
1308 *hc = '\0';
1309 }
1310 else if (islower (*hc))
1311 {
1312 /*
1313 Try to assign a local date variable if there is set any,
1314 else try to assign a global date variable if there is
1315 set any, otherwise we have to skip this part.
1316 */
1317 if (rc_dvar[IDX (*hc)].dvar_local.dvar_month
1318 || rc_dvar[IDX (*hc)].dvar_global.dvar_month)
1319 {
1320 if (rc_dvar[IDX (*hc)].dvar_local.dvar_month)
1321 {
1322 *m = (int) rc_dvar[IDX (*hc)].dvar_local.dvar_month;
1323 *d = (int) rc_dvar[IDX (*hc)].dvar_local.dvar_day;
1324 }
1325 else
1326 {
1327 *m =
1328 (int) rc_dvar[IDX (*hc)].dvar_global.dvar_month;
1329 *d = (int) rc_dvar[IDX (*hc)].dvar_global.dvar_day;
1330 }
1331 if (!precomp_date (i, *hwd, d, m, *y, DVar))
1332 {
1333 if (!*y)
1334 /*
1335 No explicit year YYYY given in "date"-part of line.
1336 */
1337 *hn = i;
1338 else
1339 {
1340 /*
1341 Invalid relative date given.
1342 */
1343 *hc = '\0';
1344 *d = 0;
1345 *y = SPECIAL_VALUE;
1346 }
1347 }
1348 else
1349 *hc = '\0';
1350 }
1351 else
1352 {
1353 /*
1354 Error, no such date variable defined.
1355 */
1356 if ((warning_level >= 0) && on_error_exit)
1357 var_warning (ERR_INVALID_VAR_REFERENCE, (int) *hc,
1358 line_buffer, filename, line_number);
1359 *hc = '\0';
1360 *d = 0;
1361 *y = SPECIAL_VALUE;
1362 }
1363 }
1364 else if (*hc == 'D' || *hc == 'W')
1365 {
1366 /*
1367 Try to compute the '*' N'th weekday of year resp.,
1368 weekday WW[W] of N'th week statement.
1369 */
1370 if (*y == 0)
1371 {
1372 /*
1373 No explicit year YYYY given in "date"-part of line.
1374 */
1375 *hn = i;
1376 *d = 0;
1377 *m = 0;
1378 }
1379 else
1380 if (precomp_nth_wd (i, *hwd, hn, d, m, y,
1381 (*hc == 'D') ? DAy : WEek))
1382 *hc = '\0';
1383 }
1384 else
1385 /*
1386 Error, either an invalid date variable character trails
1387 the holiday mode character '@', or an invalid character
1388 trails the "N'th weekday of year" resp., weekday
1389 WW[W] of "N'th week mode" character '*'.
1390 */
1391 if (on_error_exit)
1392 my_error (ERR_INVALID_DATE_FIELD, filename, line_number,
1393 line_buffer, 0);
1394 }
1395 if (lineptrs->repeat_part != (char *) NULL
1396 || lineptrs->appears_part != (char *) NULL)
1397 while (*the_line && !isspace (*the_line))
1398 {
1399 the_line++;
1400 (*len)++;
1401 }
1402 if (*the_line)
1403 the_line++;
1404 }
1405 }
1406 /*
1407 Now let's allocate memory for all pointers to texts of the `lineptrs'
1408 structure if we work on a resource/include file (except `text_part').
1409 That's absolutely necessary because after a potential resizing of
1410 "all strings" elsewhere in a later part of the program, these pointers
1411 could get lost otherwise. The caller has to free this memory!
1412 */
1413 if (is_rc_file)
1414 {
1415 /*
1416 ONLY IF DETECTED!
1417 */
1418 if (lineptrs->day_part != (char *) NULL)
1419 {
1420 ptr_char = lineptrs->day_part;
1421 i = 0;
1422 LOOP
1423 {
1424 if (!*ptr_char || isspace (*ptr_char))
1425 break;
1426 i++;
1427 ptr_char++;
1428 }
1429 ptr_char = lineptrs->day_part;
1430 lineptrs->day_part =
1431 (char *) my_malloc (i + 1, ERR_NO_MEMORY_AVAILABLE, __FILE__,
1432 ((long) __LINE__) - 1L, "lineptrs->day_part",
1433 0);
1434 strncpy (lineptrs->day_part, ptr_char, i);
1435 lineptrs->day_part[i] = '\0';
1436 }
1437 /*
1438 ONLY IF DETECTED!
1439 */
1440 if (lineptrs->repeat_part != (char *) NULL)
1441 {
1442 ptr_char = lineptrs->repeat_part;
1443 i = 0;
1444 LOOP
1445 {
1446 if (!*ptr_char || isspace (*ptr_char))
1447 break;
1448 i++;
1449 ptr_char++;
1450 }
1451 ptr_char = lineptrs->repeat_part;
1452 lineptrs->repeat_part =
1453 (char *) my_malloc (i + 1, ERR_NO_MEMORY_AVAILABLE, __FILE__,
1454 ((long) __LINE__) - 1L,
1455 "lineptrs->repeat_part", 0);
1456 strncpy (lineptrs->repeat_part, ptr_char, i);
1457 lineptrs->repeat_part[i] = '\0';
1458 }
1459 /*
1460 ONLY IF DETECTED!
1461 */
1462 if (lineptrs->appears_part != (char *) NULL)
1463 {
1464 ptr_char = lineptrs->appears_part;
1465 i = 0;
1466 LOOP
1467 {
1468 if (!*ptr_char || isspace (*ptr_char))
1469 break;
1470 i++;
1471 ptr_char++;
1472 }
1473 ptr_char = lineptrs->appears_part;
1474 lineptrs->appears_part =
1475 (char *) my_malloc (i + 1, ERR_NO_MEMORY_AVAILABLE, __FILE__,
1476 ((long) __LINE__) - 1L,
1477 "lineptrs->appears_part", 0);
1478 strncpy (lineptrs->appears_part, ptr_char, i);
1479 lineptrs->appears_part[i] = '\0';
1480 }
1481 if ((lineptrs->repeat_part != (char *) NULL
1482 || lineptrs->appears_part != (char *) NULL)
1483 && !is_hdy_mode
1484 && !*is_weekday_mode && !lineptrs->day_list && !lineptrs->day_range)
1485 (*len)--;
1486 }
1487 /*
1488 ALWAYS!
1489 */
1490 lineptrs->text_part = the_line;
1491
1492 return (lineptrs);
1493 }
1494
1495
1496
1497 Bool
precomp_nth_wd(diff,wd,n,day,month,year,mode)1498 precomp_nth_wd (diff, wd, n, day, month, year, mode)
1499 int diff;
1500 const int wd;
1501 int *n;
1502 int *day;
1503 int *month;
1504 int *year;
1505 const Cmode_enum mode;
1506 /*!
1507 Precomputes the date of the "N'th absolute weekday" `wd' of the year
1508 or the date of weekday `wd' of the "N'th absolute week" of the year
1509 (returned in `&day', `&month' and `&year'; if `&year' is not concrete
1510 the computed `diff' is returned by `&n'), and returns TRUE in case such
1511 a date exits in the year, otherwise FALSE.
1512 */
1513 {
1514 register int the_diff = diff;
1515 register int j = DAY_LAST + (days_of_february (*year) == 29);
1516 auto int i = 0;
1517
1518
1519 if (*year)
1520 {
1521 if (mode == DAy)
1522 {
1523 *day = DAY_MIN;
1524 *month = MONTH_MIN;
1525 if (wd)
1526 {
1527 if (the_diff == WEEK_MAX + 1 || the_diff == 99)
1528 {
1529 i = the_diff;
1530 diff = WEEK_MAX;
1531 }
1532 }
1533 else
1534 {
1535 /*
1536 If a special value "999" for `diff' is given,
1537 set it to last day of year (365|366).
1538 */
1539 if (the_diff == 999)
1540 diff = j;
1541 i = diff--;
1542 }
1543 }
1544 else
1545 {
1546 register int k = (iso_week_number) ? DAY_MIN : start_day;
1547
1548
1549 /*
1550 `mode' == WEek.
1551 */
1552 diff = i = weekno2doy (the_diff, *year, iso_week_number, k);
1553 if (diff > DAY_MIN)
1554 {
1555 diff--;
1556 k = j - diff;
1557 if (iso_week_number)
1558 j = wd;
1559 else
1560 j = SYEAR (wd, start_day);
1561 /*
1562 If a weekday of the LAST week (==99) is wanted, but this
1563 weekday doesn't exist anymore in the last week of the
1564 current year by reason it is already located in the next
1565 year, let's use the last date at which this weekday occurs
1566 in the current year instead.
1567 */
1568 if ((the_diff == 99) && (*year != YEAR_MAX) && (j > k))
1569 diff -= DAY_MAX;
1570 }
1571 else
1572 diff = 1;
1573 if (doy2date (diff, (days_of_february (*year) == 29), day, month))
1574 diff = 1;
1575 }
1576 }
1577 if (!precomp_date (diff, wd, day, month, *year, DVar))
1578 {
1579 if (!*year)
1580 {
1581 /*
1582 No explicit year YYYY given in "date"-part of line.
1583 */
1584 *day = 0;
1585 *month = 0;
1586 *n = diff;
1587 }
1588 else
1589 {
1590 /*
1591 Invalid relative date given.
1592 */
1593 *day = 0;
1594 *month = 0;
1595 *year = SPECIAL_VALUE;
1596 }
1597 return (FALSE);
1598 }
1599 else
1600 {
1601 if (wd && (mode == DAy))
1602 {
1603 register int year_old = (*year);
1604
1605
1606 if (i)
1607 for (diff = DAY_MIN; diff <= DAY_MAX; diff++)
1608 (void) next_date (day, month, year);
1609 if (((*day <= DAY_MAX)
1610 && (*year != year_old))
1611 || weekday_of_date (DAY_MIN, MONTH_MIN, *year) == wd)
1612 for (diff = DAY_MIN; diff <= DAY_MAX; diff++)
1613 (void) prev_date (day, month, year);
1614 if (i == WEEK_MAX + 1)
1615 {
1616 i = DAY_MIN;
1617 *month = MONTH_MIN;
1618 (void) precomp_date (WEEK_MAX, wd, &i, month, *year, DVar);
1619 if ((*day == i)
1620 && (weekday_of_date (DAY_MIN, MONTH_MIN, *year) != wd))
1621 {
1622 /*
1623 Error, no such 53rd weekday WW[W] of year.
1624 */
1625 *day = 0;
1626 *month = 0;
1627 *year = SPECIAL_VALUE;
1628 return (FALSE);
1629 }
1630 }
1631 }
1632 else
1633 /*
1634 `mode' == WEek.
1635 */
1636 if (!wd
1637 || i < DAY_MIN
1638 || ((the_diff <= 1) && (*day == DAY_MAX + 1) && (wd == DAY_MIN)))
1639 {
1640 if (*day >= DAY_MAX + i)
1641 *day -= DAY_MAX;
1642 else
1643 if (!wd
1644 && (i < DAY_MIN || ((*day == DAY_MIN + 1) && (i == DAY_MIN))))
1645 (*day)--;
1646 if (*day < DAY_MIN)
1647 {
1648 /*
1649 Error, N'th week doesn't contain such a weekday WW[W].
1650 */
1651 *day = 0;
1652 *month = 0;
1653 *year = SPECIAL_VALUE;
1654 return (FALSE);
1655 }
1656 }
1657 }
1658
1659 return (TRUE);
1660 }
1661
1662
1663
1664 Bool
precomp_date(diff,wd,day,month,year,mode)1665 precomp_date (diff, wd, day, month, year, mode)
1666 int diff;
1667 const int wd;
1668 int *day;
1669 int *month;
1670 const int year;
1671 const Cmode_enum mode;
1672 /*!
1673 Precomputes the date relative to Easter Sunday's date (mode==EAster),
1674 relative to today's date (mode==TOday) or relative to date variables
1675 date (mode==DVar) plus displacement `diff' or displacement `diff' `wd'
1676 (returned in `&day' and `&month'), and returns TRUE in case such a date
1677 exits in the year, otherwise FALSE.
1678 */
1679 {
1680 register int i;
1681
1682
1683 if (((mode == EAster)
1684 && (year >= EASTER_MIN)
1685 && (year <= EASTER_MAX))
1686 || ((mode == TOday
1687 || mode == DVar) && (year >= YEAR_MIN) && (year <= YEAR_MAX)))
1688 {
1689 switch (mode)
1690 {
1691 case EAster:
1692 i = knuth_easter_formula (year);
1693 break;
1694 case TOday:
1695 *day = act_day;
1696 *month = act_month;
1697 /* Fallthrough. */
1698 default:
1699 if (!valid_date (*day, *month, year))
1700 /*
1701 Error, invalid date given (e.g. 19010229).
1702 */
1703 return (FALSE);
1704 i = day_of_year (*day, *month, year);
1705 }
1706 if (wd)
1707 {
1708 /*
1709 Calculate date like: 3rd(`diff') Friday(`wd') before Easter Sunday's date.
1710 */
1711 if (wd < DAY_MIN || wd > DAY_MAX)
1712 /*
1713 Error, invalid weekday specified.
1714 */
1715 return (FALSE);
1716 else if (!diff)
1717 /*
1718 Error, a weekday but no difference specified.
1719 */
1720 return (FALSE);
1721 else if (diff == -99)
1722 {
1723 /*
1724 Detect first weekday `wd' of year.
1725 */
1726 *month = MONTH_MIN;
1727 *day = eval_holiday (DAY_MIN, *month, year, wd, TRUE);
1728 return (TRUE);
1729 }
1730 else if (diff == 99)
1731 {
1732 /*
1733 Detect last weekday `wd' of year.
1734 */
1735 *month = MONTH_MAX;
1736 *day =
1737 eval_holiday (dvec[MONTH_MAX - 1], *month, year, wd, FALSE);
1738 return (TRUE);
1739 }
1740 else
1741 {
1742 register int act_wd;
1743 auto int d;
1744 auto int m;
1745 auto int y = year;
1746
1747
1748 (void) doy2date (i, (days_of_february (y) == 29), &d, &m);
1749 act_wd = weekday_of_date (d, m, y);
1750 if (act_wd != wd)
1751 {
1752 if (diff < 0)
1753 {
1754 /*
1755 Try to detect first weekday `wd' before actual date.
1756 */
1757 while (act_wd != wd)
1758 {
1759 (void) prev_date (&d, &m, &y);
1760 act_wd = weekday_of_date (d, m, y);
1761 i--;
1762 }
1763 diff++;
1764 }
1765 else
1766 {
1767 /*
1768 Try to detect first weekday `wd' after actual date.
1769 */
1770 while (act_wd != wd)
1771 {
1772 (void) next_date (&d, &m, &y);
1773 act_wd = weekday_of_date (d, m, y);
1774 i++;
1775 }
1776 diff--;
1777 }
1778 }
1779 if (y != year)
1780 /*
1781 Error, we have left the year bounds.
1782 */
1783 return (FALSE);
1784 /*
1785 Calculate the difference.
1786 */
1787 i += diff * DAY_MAX;
1788 }
1789 }
1790 else
1791 {
1792 /*
1793 Calculate the difference.
1794 */
1795 if (diff == -999)
1796 i = 1;
1797 else if (diff == 999)
1798 i = DAY_LAST + (days_of_february (year) == 29);
1799 else
1800 i += diff;
1801 }
1802 if (doy2date (i, (days_of_february (year) == 29), day, month))
1803 return (TRUE);
1804 }
1805
1806 return (FALSE);
1807 }
1808
1809
1810
1811 void
set_dvar(line_buffer,lineptrs,filename,line_number,mode)1812 set_dvar (line_buffer, lineptrs, filename, line_number, mode)
1813 const char *line_buffer;
1814 Line_struct *lineptrs;
1815 const char *filename;
1816 const long line_number;
1817 const Var_enum mode;
1818 /*!
1819 Scans given string `line_buffer' and tries to detect a valid date variable
1820 reference, which can be:
1821 1) DVAR=``NOTHING'' --> Undefine local DVAR so we are able to use
1822 its global value. If `mode' is set to
1823 "GLobal", this "empty" assignment results
1824 an error.
1825 2) DVAR=MMDD --> Assignment of a constant date expression
1826 MMDD.
1827 3) DVAR=MMWW[W]N --> Assignment of a dynamic date expression
1828 N'th weekday WW[W] of month MM.
1829 4) DVAR=*dN[WW[W]] --> Assignment of a dynamic date expression
1830 N'th weekday WW[W] of year.
1831 5) DVAR=*wN[WW[W]] --> Assignment of a dynamic date expression
1832 weekday WW[W] of N'th week of year.
1833 6) DVAR=DVAR --> Assignment of a date variable DVAR,
1834 which must be already defined.
1835 7) DVAR=DVAR[+|-]N --> Assignment of a date variable DVAR,
1836 which must be already defined, plus/minus
1837 N days.
1838 8) DVAR=DVAR[+|-]NWW[W] --> Assignment of a date variable DVAR,
1839 which must be already defined, plus/minus
1840 N weekdays WW[W].
1841 9) DVAR++ --> Simple incrementation by one day.
1842 10) DVAR-- --> Simple decrementation by one day.
1843 11) DVAR+=[+|-]N --> Addition of a constant numeric
1844 factor [+|-]N.
1845 12) DVAR-=[+|-]N --> Subtraction of a constant numeric
1846 factor [+|-]N.
1847 13) DVAR+=[+|-]NWW[W] --> Addition of [+|-]N weekdays WW[W].
1848 14) DVAR-=[+|-]NWW[W] --> Subtraction of [+|-]N weekdays WW[W].
1849 A date variable name is valid from a...d, f...s and u...z (24 variables
1850 totally, case-insensitive), because the `e' variable is always reserved
1851 for the current Easter Sunday's date and the `t' variable is always
1852 reserved for today's date, so we must skip any reference to these
1853 variables.
1854 No whitespace characters may occur between the date variable, operator
1855 and value. Stores assignment (1)...(8) at position `date variable'
1856 into global date variable vector `rc_dvar[]' (either the local or the
1857 global ones, depending on given `mode', which can be either "GLobal"
1858 or "LOcal". Assignment (1), (3)...(5), (7), (8) and operation
1859 (9)...(14) may ONLY be used on local date variables.
1860 */
1861 {
1862 register int error = 0;
1863 auto char dvar = '\0';
1864
1865
1866 /*
1867 Skip and return error if invalid date variable name is given.
1868 */
1869 if (isalpha (*line_buffer)
1870 && (tolower (*line_buffer) != RC_EASTER_CHAR)
1871 && (tolower (*line_buffer) != RC_TODAY_CHAR))
1872 {
1873 auto int i;
1874 auto int len;
1875 auto int d = 0;
1876 auto int m = 0;
1877 auto int y = year;
1878 auto int n;
1879 auto const char *ptr_char = line_buffer;
1880 auto char op;
1881 auto char op2;
1882 static char str20[21];
1883 auto Bool is_weekday_mode;
1884 auto Bool dvar_with_displacement = FALSE;
1885 auto Bool dvar_add_sub = FALSE;
1886 auto Bool dvar_inc_dec = FALSE;
1887
1888
1889 ptr_char++;
1890 /*
1891 Check if assignment (1)...(8) is given.
1892 */
1893 if (*ptr_char != *RC_VAR_ASSIGN)
1894 {
1895 if ((*ptr_char != *RC_VAR_ADD) && (*ptr_char != *RC_VAR_SUB))
1896 /*
1897 Error, invalid first operator character found
1898 (neither '+' nor '-' given).
1899 */
1900 error = ERR_ILLEGAL_VAR_DEFINITION;
1901 else
1902 {
1903 /*
1904 Check if operation (9)...(14) is given.
1905 */
1906 op = *ptr_char++;
1907 if (*ptr_char)
1908 {
1909 op2 = *ptr_char++;
1910 if (op2 == op || op2 == *RC_VAR_ASSIGN)
1911 {
1912 if (mode == LOcal)
1913 m =
1914 (int) rc_dvar[IDX (*line_buffer)].dvar_local.
1915 dvar_month;
1916 if (m)
1917 {
1918 if (op == op2)
1919 {
1920 while (isspace (*ptr_char))
1921 ptr_char++;
1922 if (*ptr_char)
1923 /*
1924 Error, found invalid trailing characters.
1925 */
1926 error = ERR_ILLEGAL_VAR_OPERATION;
1927 else
1928 /*
1929 Either DVAR++ or DVAR-- found.
1930 */
1931 dvar_inc_dec = TRUE;
1932 }
1933 else
1934 {
1935 /*
1936 Either DVAR+=... or DVAR-=... found.
1937 */
1938 dvar_add_sub = TRUE;
1939 /*
1940 Respect a trailing sign of the value.
1941 */
1942 if (*ptr_char == *RC_VAR_ADD
1943 || *ptr_char == *RC_VAR_SUB)
1944 {
1945 if (op == *RC_VAR_SUB)
1946 {
1947 if (*ptr_char == *RC_VAR_ADD)
1948 op = *RC_VAR_SUB;
1949 else
1950 op = *RC_VAR_ADD;
1951 }
1952 else
1953 op = *ptr_char;
1954 ptr_char++;
1955 }
1956 }
1957 if (!error)
1958 goto LABEL_compute_dvar;
1959 }
1960 else
1961 {
1962 if (mode == GLobal)
1963 /*
1964 Error, operation given in global mode.
1965 */
1966 error = ERR_ILLEGAL_VAR_OPERATION;
1967 else
1968 /*
1969 Error, date variable undefined.
1970 */
1971 error = ERR_INVALID_VAR_REFERENCE;
1972 }
1973 }
1974 else
1975 /*
1976 Error, invalid second operator character found
1977 (no '=', '+' or '-' given resp.,
1978 illegal combination of '+' and '-').
1979 */
1980 error = ERR_ILLEGAL_VAR_OPERATION;
1981 }
1982 else
1983 /*
1984 Error, incomplete operator found (neither '+=', '-=', '++'
1985 nor '--' given).
1986 */
1987 error = ERR_ILLEGAL_VAR_OPERATION;
1988 }
1989 }
1990 else
1991 {
1992 /*
1993 Assignment (1)...(8) to date variable found (simple '=' given),
1994 scan expression part of date variable definition. Assignments
1995 (1), (3)...(5), (7), (8) are ONLY allowed for local date
1996 variables.
1997 */
1998 i = 0;
1999 ptr_char++;
2000 if (!*ptr_char)
2001 {
2002 /*
2003 No date assigned ("empty" assignment), set the date variable
2004 slot to zero so we are able to use its possibly set global
2005 value if this variable is referenced again at a later place
2006 within the sequence. This kind of assignment is allowed for
2007 local date variables only; for global date variables, we
2008 have to report an error instead.
2009 */
2010 if (mode == GLobal)
2011 /*
2012 Error, "empty" assignment on a global date variable given.
2013 */
2014 error = ERR_ILLEGAL_VAR_DEFINITION;
2015 }
2016 else
2017 {
2018 if (isalpha (*ptr_char) && !isalpha (*(ptr_char + 1)))
2019 {
2020 dvar = op = *ptr_char;
2021 ptr_char++;
2022 if (!*ptr_char || isspace (*ptr_char))
2023 {
2024 if (tolower (dvar) == RC_EASTER_CHAR
2025 || tolower (dvar) == RC_TODAY_CHAR)
2026 /*
2027 Error, date variable is invalid.
2028 */
2029 error = ERR_INVALID_VAR_ASSIGNMENT;
2030 else
2031 {
2032 /*
2033 If the character after '=' is alphabetic and is not
2034 trailed by digits, assume assignment (6) is given.
2035 */
2036 if (mode == GLobal)
2037 {
2038 m =
2039 (int) rc_dvar[IDX (dvar)].dvar_global.
2040 dvar_month;
2041 d =
2042 (int) rc_dvar[IDX (dvar)].dvar_global.
2043 dvar_day;
2044 }
2045 else
2046 {
2047 m =
2048 (int) rc_dvar[IDX (dvar)].dvar_local.
2049 dvar_month;
2050 d =
2051 (int) rc_dvar[IDX (dvar)].dvar_local.dvar_day;
2052 }
2053 if (!m)
2054 /*
2055 Error, date variable undefined.
2056 */
2057 error = ERR_INVALID_VAR_REFERENCE;
2058 }
2059 }
2060 else
2061 {
2062 /*
2063 Check if assignments (7)...(8) are given.
2064 */
2065 if (*ptr_char == *ASC_LIT
2066 || *ptr_char == *DES_LIT || isdigit (*ptr_char))
2067 {
2068 ptr_char--;
2069 dvar_with_displacement = TRUE;
2070 goto LABEL_compute_dvar;
2071 }
2072 else
2073 /*
2074 Error, invalid date variable name given.
2075 */
2076 error = ERR_ILLEGAL_VAR_DEFINITION;
2077 }
2078 }
2079 else
2080 {
2081 LABEL_compute_dvar:
2082 /*
2083 Assuming the string vectors have a minimum length of 1024
2084 Bytes and the maximum text length of a date variable
2085 assignment/operation in a line may not be longer than
2086 20 Bytes, let's use these 20 Bytes of the line only.
2087 */
2088 strncpy (str20, ptr_char, 20);
2089 str20[20] = '\0';
2090 if (dvar_with_displacement)
2091 sprintf (s5, "%0*d%c%s", len_year_max, y, RC_HDY_CHAR,
2092 str20);
2093 else if (dvar_add_sub)
2094 sprintf (s5, "%0*d%c%c%c%s", len_year_max, y, RC_HDY_CHAR,
2095 *line_buffer, op, str20);
2096 else if (dvar_inc_dec)
2097 sprintf (s5, "%0*d%c%c%c1", len_year_max, y, RC_HDY_CHAR,
2098 *line_buffer, op);
2099 else
2100 sprintf (s5, "%0*d%s", len_year_max, y, str20);
2101 /*
2102 `rc_get_date()' arguments `len' and `i' are dummys
2103 only and must be given. They are not used further!
2104 */
2105 (void) rc_get_date (s5, lineptrs, FALSE, &is_weekday_mode,
2106 &d, &m, &y, &n, &len, &op, &i, &i,
2107 filename, line_number, line_buffer,
2108 TRUE);
2109 if (y != SPECIAL_VALUE)
2110 {
2111 /*
2112 Check if assignments (3)...(5) are given.
2113 */
2114 if ((mode == GLobal) && (op || is_weekday_mode))
2115 error = ERR_ILLEGAL_VAR_OPERATION;
2116 else
2117 {
2118 /*
2119 Assignments (2)...(3) are given.
2120 */
2121 if (m < MONTH_MIN || m > MONTH_MAX)
2122 /*
2123 Error, invalid month given.
2124 */
2125 error = ERR_ILLEGAL_VAR_DEFINITION;
2126 else
2127 {
2128 i = dvec[m - 1];
2129 if (m == 2)
2130 i += is_leap_year;
2131 /*
2132 Check for assignment (3) DVAR=MMWW[W]N
2133 (WW=mo...su, WWW=mon...sun, N=1...5|9),
2134 e.g.: x=03mo3 sets `x' to date of 3rd Monday
2135 in March.
2136 e.g.: x=03mon3 sets `x' to date of 3rd Monday
2137 in March, too.
2138 */
2139 if (is_weekday_mode)
2140 {
2141 if (n == 9)
2142 d = eval_holiday (i, m, year, d, FALSE);
2143 else
2144 {
2145 d =
2146 eval_holiday (DAY_MIN, m, year, d,
2147 TRUE);
2148 d += (DAY_MAX * (n - 1));
2149 if (d > i)
2150 /*
2151 Month contains no such "N'th weekday of
2152 month", ignore the assignment.
2153 */
2154 error = ERR_INVALID_VAR_ASSIGNMENT;
2155 }
2156 }
2157 else
2158 {
2159 /*
2160 Assume assignment (1) is given.
2161 */
2162 if (d == 99)
2163 d = i;
2164 /*
2165 We must avoid an assigment like DVAR=0229
2166 if we are in fiscal year mode and the next
2167 year is no leap year and no `--leap-day=ARG'
2168 option is given!
2169 */
2170 if ((fiscal_month > MONTH_MIN + 1)
2171 && (days_of_february (year + 1) == 28)
2172 && !rc_feb_29_to_feb_28
2173 && !rc_feb_29_to_mar_01
2174 && (m == 2) && (d == 29))
2175 /*
2176 Year contains no such date, ignore the assignment.
2177 */
2178 error = ERR_INVALID_VAR_ASSIGNMENT;
2179 else
2180 {
2181 if (d > i)
2182 {
2183 manage_leap_day (&d, &m, year,
2184 line_buffer,
2185 filename,
2186 line_number);
2187 i = d;
2188 }
2189 if (d < DAY_MIN || d > i)
2190 /*
2191 Error, invalid day given.
2192 */
2193 error = ERR_ILLEGAL_VAR_DEFINITION;
2194 }
2195 }
2196 }
2197 }
2198 }
2199 else
2200 /*
2201 Year contains no such date, ignore the assignment.
2202 */
2203 error = ERR_INVALID_VAR_ASSIGNMENT;
2204 }
2205 }
2206 }
2207 if (!error)
2208 {
2209 /*
2210 Store the assigned/calculated date.
2211 */
2212 if (mode == GLobal)
2213 {
2214 rc_dvar[IDX (*line_buffer)].dvar_global.dvar_month = (char) m;
2215 rc_dvar[IDX (*line_buffer)].dvar_global.dvar_day = (char) d;
2216 }
2217 else
2218 {
2219 rc_dvar[IDX (*line_buffer)].dvar_local.dvar_month = (char) m;
2220 rc_dvar[IDX (*line_buffer)].dvar_local.dvar_day = (char) d;
2221 }
2222 }
2223 }
2224 else
2225 /*
2226 Error, invalid date variable name given (not a...d, f...s, u...z).
2227 */
2228 error = ERR_ILLEGAL_VAR_DEFINITION;
2229 if (error)
2230 {
2231 if ((mode == GLobal)
2232 && (error == ERR_ILLEGAL_VAR_DEFINITION
2233 || error == ERR_ILLEGAL_VAR_OPERATION))
2234 warning_level = WARN_LVL_MAX;
2235 if (warning_level >= 0)
2236 {
2237 if (!dvar)
2238 dvar = *line_buffer;
2239 var_warning (error, (int) dvar, line_buffer, filename, line_number);
2240 }
2241 }
2242 }
2243
2244
2245
2246 void
set_tvar(line_buffer,filename,line_number,mode)2247 set_tvar (line_buffer, filename, line_number, mode)
2248 const char *line_buffer;
2249 const char *filename;
2250 const long line_number;
2251 const Var_enum mode;
2252 /*!
2253 Scans given string `line_buffer' and tries to detect a valid text variable
2254 reference, which is:
2255 1) $TVAR=[TEXT] --> Assignment of a constant text expression TEXT
2256 to TVAR. TEXT may contain references to
2257 other TVAR's, which are always expanded
2258 recursively before the assignment is performed!
2259 2) $TVAR?COMMAND --> Interpreted assignment of that text to TVAR, which
2260 is created by the COMMAND on the STDOUT channel.
2261 The text may contain references to other TVAR's,
2262 which are expanded in case TVAR is referenced
2263 at a later place of program execution.
2264 3) $TVAR:COMMAND --> Uninterpreted assignment of that text to TVAR, which
2265 is created by the COMMAND on the STDOUT channel.
2266 References to other TVAR's are not expanded!
2267 4) $TVAR++ --> Simple incrementation by one (length preserved).
2268 5) $TVAR-- --> Simple decrementation by one (length preserved).
2269 6) $TVAR+=[+|-]N --> Addition of a constant numeric
2270 factor [+|-]N (length preserved).
2271 7) $TVAR-=[+|-]N --> Subtraction of a constant numeric
2272 factor [+|-]N (length preserved).
2273 A text variable name is valid from $a...$z (totally 26 variables,
2274 case-insensitve). No whitespace characters may occur between the
2275 text variable prefix character '$' and the text variable letter itself,
2276 the operator and the value.
2277 In general, assignment (1)...(3) is stored at position `text variable'
2278 into the global text variable vector `rc_tvar[]' (either the local or
2279 the global ones, depending on given `mode', which can be either "GLobal"
2280 or "LOcal".
2281 Assignment (2) inserts the text created by the COMMAND into the TVAR as is,
2282 but only if it is allowed (`--execute-command' option must be given).
2283 Assignment (3) inserts the text created by the COMMAND into the TVAR
2284 postprocessed by the Txt2gcal program, but only if it is allowed
2285 (`--execute-command' option must be given).
2286 Operation (4)...(7) may ONLY be used on local text variables (if they
2287 contain integer values).
2288 Uses the global text buffers `s5' and `s7' internally.
2289 Returns FALSE if an error occurs, otherwise TRUE.
2290 */
2291 {
2292 register int error = 0;
2293 auto char tvar = '\0';
2294
2295
2296 if (*line_buffer != RC_TVAR_CHAR)
2297 /*
2298 Error, no leading '$' character (text variable prefix) given.
2299 */
2300 error = ERR_ILLEGAL_VAR_DEFINITION;
2301 else
2302 {
2303 auto char *ptr_char = (char *) line_buffer;
2304
2305
2306 /*
2307 Skip the trailing '$' character of a text variable by default.
2308 */
2309 ptr_char++;
2310 /*
2311 Skip and return error if invalid text variable name is given.
2312 */
2313 if (!isalpha (*ptr_char))
2314 /*
2315 Error, invalid text variable name given (not a...z resp., A...Z).
2316 */
2317 error = ERR_ILLEGAL_VAR_DEFINITION;
2318 else
2319 {
2320 tvar = *ptr_char++;
2321 /*
2322 Check if assignment (1)...(3) or operation (4)...(7) is given.
2323 */
2324 if ((*ptr_char != *RC_VAR_ASSIGN)
2325 && (*ptr_char != *RC_TVAR_ICMD_ASSIGN)
2326 && (*ptr_char != *RC_TVAR_UCMD_ASSIGN)
2327 && (*ptr_char != *RC_VAR_ADD) && (*ptr_char != *RC_VAR_SUB))
2328 /*
2329 Error, invalid first operator character found
2330 (neither '=' nor '+' nor '-' nor '?' nor ':' given).
2331 */
2332 error = ERR_ILLEGAL_VAR_DEFINITION;
2333 else
2334 {
2335 register int i = 0;
2336 register int j;
2337 register int len = 0;
2338 auto char *ptr_tvar;
2339 auto char op;
2340 auto char op2 = '\0';
2341 auto char op3 = op2;
2342 auto Bool is_quoted = FALSE;
2343 auto Bool restore_tvar = FALSE;
2344
2345
2346 op = *ptr_char++;
2347 if (op)
2348 {
2349 op2 = *ptr_char;
2350 if (op2)
2351 op3 = *(ptr_char + 1);
2352 }
2353 /*
2354 Check if the assigned TEXT contains any references
2355 to other TVAR variables, if so, insert their TEXT's.
2356 */
2357 ptr_tvar = strchr (ptr_char, RC_TVAR_CHAR);
2358 if (ptr_tvar != (char *) NULL)
2359 {
2360 auto Bool global_tvar_defined;
2361 auto Bool local_tvar_set;
2362
2363
2364 do
2365 {
2366 len = (int) (ptr_tvar - ptr_char);
2367 if (len)
2368 {
2369 while ((Uint) len + i >= maxlen_max)
2370 resize_all_strings (maxlen_max << 1, TRUE,
2371 __FILE__, (long) __LINE__);
2372 strncpy (s5 + i, ptr_char, len);
2373 i += len;
2374 }
2375 s5[i] = '\0';
2376 if (i)
2377 if (s5[i - 1] == QUOTE_CHAR)
2378 is_quoted = TRUE;
2379 ptr_tvar++;
2380 if (!is_quoted && isalpha (*ptr_tvar))
2381 {
2382 global_tvar_defined = local_tvar_set = FALSE;
2383 if (rc_tvar[IDX (*ptr_tvar)].tvar_global.
2384 tvar_text != (char *) NULL)
2385 global_tvar_defined = TRUE;
2386 if (rc_tvar[IDX (*ptr_tvar)].tvar_local.tvar_text !=
2387 (char *) NULL)
2388 if (*rc_tvar[IDX (*ptr_tvar)].tvar_local.
2389 tvar_text)
2390 local_tvar_set = TRUE;
2391 /*
2392 Try to insert the value of this TVAR (that's its TEXT).
2393 */
2394 j = 0;
2395 if (global_tvar_defined
2396 && (mode == GLobal
2397 || ((mode == LOcal) && !local_tvar_set)))
2398 {
2399 j =
2400 (int) strlen (rc_tvar[IDX (*ptr_tvar)].
2401 tvar_global.tvar_text);
2402 if (j)
2403 {
2404 while ((Uint) i + j >= maxlen_max)
2405 resize_all_strings (maxlen_max << 1, TRUE,
2406 __FILE__,
2407 (long) __LINE__);
2408 strcat (s5,
2409 rc_tvar[IDX (*ptr_tvar)].
2410 tvar_global.tvar_text);
2411 }
2412 }
2413 else if ((mode == LOcal) && local_tvar_set)
2414 {
2415 j =
2416 (int) strlen (rc_tvar[IDX (*ptr_tvar)].
2417 tvar_local.tvar_text);
2418 while ((Uint) i + j >= maxlen_max)
2419 resize_all_strings (maxlen_max << 1, TRUE,
2420 __FILE__,
2421 (long) __LINE__);
2422 strcat (s5,
2423 rc_tvar[IDX (*ptr_tvar)].tvar_local.
2424 tvar_text);
2425 }
2426 if (((mode == GLobal)
2427 && global_tvar_defined)
2428 || ((mode == LOcal)
2429 && (global_tvar_defined
2430 || local_tvar_set))
2431 || ((tvar == *ptr_tvar)
2432 && (((mode == GLobal)
2433 && !global_tvar_defined)
2434 || ((mode == LOcal)
2435 && !global_tvar_defined
2436 && !local_tvar_set))))
2437 {
2438 /*
2439 Skip TVAR name.
2440 */
2441 len += 2;
2442 if (j)
2443 i += j;
2444 else
2445 /*
2446 If TVAR is "empty", remove a possibly
2447 obsolete whitespace character.
2448 */
2449 if (i)
2450 if (isspace (s5[i - 1])
2451 && isspace (*(ptr_tvar + 1)))
2452 s5[--i] = '\0';
2453 }
2454 else
2455 restore_tvar = TRUE;
2456 }
2457 else
2458 restore_tvar = TRUE;
2459 /*
2460 If TVAR isn't defined, or quoted, or an invalid
2461 TVAR name is found, don't touch it.
2462 */
2463 if (restore_tvar)
2464 {
2465 if ((Uint) i + 1 >= maxlen_max)
2466 resize_all_strings (maxlen_max << 1, TRUE,
2467 __FILE__, (long) __LINE__);
2468 s5[i++] = RC_TVAR_CHAR;
2469 len++;
2470 if (*ptr_tvar)
2471 {
2472 if ((Uint) i + 1 >= maxlen_max)
2473 resize_all_strings (maxlen_max << 1, TRUE,
2474 __FILE__,
2475 (long) __LINE__);
2476 s5[i++] = *ptr_tvar;
2477 len++;
2478 }
2479 s5[i] = '\0';
2480 }
2481 ptr_char += len;
2482 ptr_tvar = strchr (ptr_char, RC_TVAR_CHAR);
2483 restore_tvar = is_quoted = FALSE;
2484 }
2485 while (ptr_tvar != (char *) NULL);
2486 /*
2487 Add possibly trailing ordinary text.
2488 */
2489 if (*ptr_char)
2490 {
2491 i += (int) strlen (ptr_char);
2492 while ((Uint) i >= maxlen_max)
2493 resize_all_strings (maxlen_max << 1, TRUE, __FILE__,
2494 (long) __LINE__);
2495 strcat (s5, ptr_char);
2496 }
2497 i++;
2498 ptr_char = s5;
2499 }
2500 else
2501 i = (int) strlen (ptr_char) + 1;
2502 if (op == *RC_VAR_ASSIGN
2503 || op == *RC_TVAR_ICMD_ASSIGN || op == *RC_TVAR_UCMD_ASSIGN)
2504 {
2505 if (rc_execute_command
2506 && (i > 1)
2507 && (op == *RC_TVAR_ICMD_ASSIGN
2508 || op == *RC_TVAR_UCMD_ASSIGN))
2509 {
2510 static char *txt2gcal_prgr = (char *) NULL;
2511 auto char *ptr_tfn;
2512 auto char *the_command;
2513
2514
2515 /*
2516 Assignment (2)...(3) to text variable found,
2517 (':' or '?' given), so perform all necessary actions.
2518 */
2519 ptr_tfn = TMPFILENAME;
2520 if (ptr_tfn == (char *) NULL)
2521 my_error (ERR_INTERNAL_C_FUNC_FAILURE, __FILE__,
2522 ((long) __LINE__) - 2L, "tmpnam()=", 0);
2523 rc_tvar_tfn =
2524 (char *) my_malloc (strlen (ptr_tfn) + 1,
2525 ERR_NO_MEMORY_AVAILABLE, __FILE__,
2526 ((long) __LINE__) - 2L,
2527 "rc_tvar_tfn", 0);
2528 strcpy (rc_tvar_tfn, ptr_tfn);
2529 rc_tvar_tfp = fopen (rc_tvar_tfn, "w");
2530 if (rc_tvar_tfp == (FILE *) NULL)
2531 my_error (ERR_WRITE_FILE, __FILE__,
2532 ((long) __LINE__) - 2L, rc_tvar_tfn, 0);
2533 if (op == *RC_TVAR_ICMD_ASSIGN)
2534 i += (strlen (REDIRECT_OUT) + strlen (rc_tvar_tfn));
2535 else
2536 {
2537 if (txt2gcal_prgr == (char *) NULL)
2538 {
2539 /*
2540 Detect the name of the Txt2gcal executable.
2541 */
2542 # if !defined(AMIGA) || defined(__GNUC__)
2543 txt2gcal_prgr = getenv (ENV_VAR_TXT2GCALPROG);
2544 if (txt2gcal_prgr != (char *) NULL)
2545 {
2546 if (!*txt2gcal_prgr)
2547 txt2gcal_prgr = TXT2GCAL_PRGR;
2548 }
2549 else
2550 # endif /* !AMIGA || __GNUC__ */
2551 txt2gcal_prgr = TXT2GCAL_PRGR;
2552 }
2553 i += (strlen (PIPELINE) + strlen (txt2gcal_prgr)
2554 + strlen (REDIRECT_OUT) +
2555 strlen (rc_tvar_tfn));
2556 }
2557 j = i;
2558 the_command =
2559 (char *) my_malloc (i, ERR_NO_MEMORY_AVAILABLE,
2560 __FILE__, ((long) __LINE__) - 2L,
2561 "rc_tvar_tfn", 0);
2562 if (op == *RC_TVAR_ICMD_ASSIGN)
2563 sprintf (the_command, "%s%s%s", ptr_char,
2564 REDIRECT_OUT, rc_tvar_tfn);
2565 else
2566 sprintf (the_command, "%s%s%s%s%s", ptr_char,
2567 PIPELINE, txt2gcal_prgr, REDIRECT_OUT,
2568 rc_tvar_tfn);
2569 /*
2570 Execute the command and redirect the STDOUT output
2571 of it into TEMPFILE NOW.
2572 */
2573 i = my_system (the_command);
2574 if (warning_level >= 0)
2575 {
2576 while ((Uint) j + LEN_SINGLE_LINE >= maxlen_max)
2577 resize_all_strings (maxlen_max << 1, TRUE,
2578 __FILE__, (long) __LINE__);
2579 if (i == -1)
2580 {
2581 /*
2582 Error, `system()' function failed.
2583 */
2584 sprintf (s5,
2585 _
2586 ("Cannot execute command in file `%s'\nLine: %ld %s"),
2587 filename, line_number, the_command);
2588 print_text (stderr, s5);
2589 if (warning_level >= WARN_LVL_MAX)
2590 {
2591 j = (int) strlen (the_command);
2592 if ((Uint) j >= maxlen_max - 9)
2593 resize_all_strings (j + 9, FALSE,
2594 __FILE__,
2595 (long) __LINE__);
2596 sprintf (s5, "system(%s)=", the_command);
2597 my_error (ERR_INTERNAL_C_FUNC_FAILURE,
2598 __FILE__, ((long) __LINE__) - 22L,
2599 s5, i);
2600 }
2601 error = ERR_INVALID_VAR_ASSIGNMENT;
2602 }
2603 else
2604 {
2605 /*
2606 Report the exit code of command executed by the `system()' function.
2607 */
2608 sprintf (s5,
2609 _
2610 ("Command executed (exit code=%d) in file `%s'\nLine %ld: %s"),
2611 i, filename, line_number, the_command);
2612 print_text (stderr, s5);
2613 /*
2614 The command executed by the `system()' function returned
2615 a value not equal zero, so we terminate all further
2616 processing now with ERR_EXTERNAL_CMD_FAILURE exit status.
2617 */
2618 if (i && (warning_level >= WARN_LVL_MAX))
2619 my_exit (ERR_EXTERNAL_CMD_FAILURE);
2620 }
2621 }
2622 free (the_command);
2623 if (!error)
2624 {
2625 auto long lnumber = 0L;
2626 auto int llength;
2627 auto int in_pool = 0;
2628 static char rc_nl[2] = { RC_NL_CHAR, '\0' };
2629 auto char *pool = (char *) NULL;
2630 auto char *ptr_pool = (char *) NULL;
2631 auto Bool b_dummy; /* Necessary dummy for `file_read_line()' function. */
2632
2633
2634 /*
2635 Command executed successfully, we can close the
2636 TEMPFILE and re-open it.
2637 */
2638 if (fclose (rc_tvar_tfp) == EOF)
2639 my_error (ERR_WRITE_FILE, __FILE__,
2640 ((long) __LINE__) - 1L, rc_tvar_tfn, 0);
2641 rc_tvar_tfp = fopen (rc_tvar_tfn, "r");
2642 if (rc_tvar_tfp == (FILE *) NULL)
2643 my_error (ERR_READ_FILE, __FILE__,
2644 ((long) __LINE__) - 2L, rc_tvar_tfn, 0);
2645 /*
2646 Now process then contents of TEMPFILE according
2647 to the selected assignment mode.
2648 */
2649 pool = (char *) my_malloc (BUF_LEN + 1,
2650 ERR_NO_MEMORY_AVAILABLE,
2651 __FILE__,
2652 ((long) __LINE__) - 2L,
2653 "pool", 0);
2654 j = 0;
2655 *s5 = '\0';
2656 while ((ptr_pool =
2657 file_read_line (rc_tvar_tfp, &s7, &in_pool,
2658 pool, ptr_pool, rc_tvar_tfn,
2659 &lnumber, &llength, COmmon,
2660 &b_dummy, &b_dummy,
2661 &b_dummy)) != (char *) NULL)
2662 {
2663 if (op == *RC_TVAR_ICMD_ASSIGN)
2664 {
2665 /*
2666 Interpret the contents of TEMPFILE.
2667 */
2668 if ((Uint) j + llength + 2 >= maxlen_max)
2669 resize_all_strings (maxlen_max << 1, TRUE,
2670 __FILE__,
2671 (long) __LINE__);
2672 if (*s7)
2673 strcat (s5, s7);
2674 strcat (s5, rc_nl);
2675 j += (llength + 1);
2676 }
2677 else
2678 {
2679 /*
2680 Do not interpret the contents of TEMPFILE,
2681 so skip the date-part which was created by
2682 the Txt2gcal executable.
2683 */
2684 i = 0;
2685 ptr_char = s7;
2686 while (!isspace (*ptr_char))
2687 {
2688 ptr_char++;
2689 i++;
2690 }
2691 ptr_char++;
2692 i = llength - i;
2693 break;
2694 }
2695 }
2696 free (pool);
2697 if (op == *RC_TVAR_ICMD_ASSIGN)
2698 {
2699 /*
2700 Remove the last RC_NL_CHAR of the line.
2701 */
2702 i = j;
2703 s5[i - 1] = '\0';
2704 /*
2705 Check if the assigned TEXT contains any '\n'
2706 newline characters, if so, exchange them
2707 by Gcal's RC_NL_CHAR (=='~') characters.
2708 */
2709 ptr_char = strchr (s5, '\n');
2710 if (ptr_char != (char *) NULL)
2711 do
2712 {
2713 *ptr_char = RC_NL_CHAR;
2714 ptr_char = strchr (s5, '\n');
2715 }
2716 while (ptr_char != (char *) NULL);
2717 ptr_char = s5;
2718 }
2719 /*
2720 And do the necessary ending operations.
2721 */
2722 if (fclose (rc_tvar_tfp) == EOF)
2723 my_error (ERR_WRITE_FILE, __FILE__,
2724 ((long) __LINE__) - 1L, rc_tvar_tfn, 0);
2725 j = unlink (rc_tvar_tfn);
2726 if (j)
2727 my_error (ERR_INTERNAL_C_FUNC_FAILURE, __FILE__,
2728 ((long) __LINE__) - 2L,
2729 "unlink(rc_tvar_tfn)=", j);
2730 free (rc_tvar_tfn);
2731 rc_tvar_tfn = (char *) NULL;
2732 }
2733 }
2734 /*
2735 Assignment (1)...(3) to text variable found,
2736 so store TEXT into the according TVAR text variable slot.
2737 */
2738 if (mode == GLobal)
2739 {
2740 if (rc_tvar[IDX (tvar)].tvar_global.tvar_text ==
2741 (char *) NULL)
2742 rc_tvar[IDX (tvar)].tvar_global.tvar_text =
2743 (char *) my_malloc (i, ERR_NO_MEMORY_AVAILABLE,
2744 __FILE__,
2745 ((long) __LINE__) - 1L,
2746 "rc_tvar[IDX(tvar)].tvar_global.tvar_text",
2747 IDX (tvar));
2748 else
2749 rc_tvar[IDX (tvar)].tvar_global.tvar_text
2750 =
2751 (char *)
2752 my_realloc ((VOID_PTR)
2753 (rc_tvar[IDX (tvar)].tvar_global.
2754 tvar_text), i, ERR_NO_MEMORY_AVAILABLE,
2755 __FILE__, ((long) __LINE__) - 2L,
2756 "rc_tvar[IDX(tvar)].tvar_global.tvar_text",
2757 IDX (tvar));
2758 strcpy (rc_tvar[IDX (tvar)].tvar_global.tvar_text,
2759 ptr_char);
2760 }
2761 else
2762 {
2763 /*
2764 We have to store the assigned text.
2765 */
2766 if (rc_tvar[IDX (tvar)].tvar_local.tvar_text ==
2767 (char *) NULL)
2768 rc_tvar[IDX (tvar)].tvar_local.tvar_text =
2769 (char *) my_malloc (i, ERR_NO_MEMORY_AVAILABLE,
2770 __FILE__,
2771 ((long) __LINE__) - 1L,
2772 "rc_tvar[IDX(tvar)].tvar_local.tvar_text",
2773 IDX (tvar));
2774 else
2775 rc_tvar[IDX (tvar)].tvar_local.tvar_text
2776 =
2777 (char *)
2778 my_realloc ((VOID_PTR)
2779 (rc_tvar[IDX (tvar)].tvar_local.
2780 tvar_text), i, ERR_NO_MEMORY_AVAILABLE,
2781 __FILE__, ((long) __LINE__) - 2L,
2782 "rc_tvar[IDX(tvar)].tvar_local.tvar_text",
2783 IDX (tvar));
2784 strcpy (rc_tvar[IDX (tvar)].tvar_local.tvar_text,
2785 ptr_char);
2786 }
2787 }
2788 else
2789 {
2790 auto Bool tvar_inc_dec = FALSE;
2791
2792
2793 /*
2794 Check if operation (4)...(7) is given.
2795 */
2796 if (op2 == op || op2 == *RC_VAR_ASSIGN)
2797 {
2798 if (mode == LOcal)
2799 {
2800 if (rc_tvar[IDX (tvar)].tvar_local.tvar_text !=
2801 (char *) NULL)
2802 {
2803 if (*rc_tvar[IDX (tvar)].tvar_local.tvar_text)
2804 {
2805 if (op == op2)
2806 {
2807 if (op3 && !isspace (op3))
2808 /*
2809 Error, invalid trailing character found.
2810 */
2811 error = ERR_ILLEGAL_VAR_OPERATION;
2812 else
2813 {
2814 /*
2815 Either TVAR++ or TVAR-- found, so
2816 check if TVAR contains an integer value.
2817 */
2818 ptr_char =
2819 rc_tvar[IDX (tvar)].tvar_local.
2820 tvar_text;
2821 /*
2822 Eat one possibly leading sign.
2823 */
2824 if (*ptr_char == *RC_VAR_ADD
2825 || *ptr_char == *RC_VAR_SUB)
2826 ptr_char++;
2827 while (isdigit (*ptr_char))
2828 ptr_char++;
2829 if (*ptr_char)
2830 /*
2831 Error, TVAR contains no integer value.
2832 */
2833 error = ERR_ILLEGAL_VAR_OPERATION;
2834 else
2835 tvar_inc_dec = TRUE;
2836 }
2837 }
2838 else
2839 {
2840 /*
2841 Either TVAR+=... or TVAR-=... found.
2842 */
2843 ptr_char++;
2844 /*
2845 Respect a possibly leading sign of value.
2846 */
2847 if (*ptr_char == *RC_VAR_ADD
2848 || *ptr_char == *RC_VAR_SUB)
2849 {
2850 if (op == *RC_VAR_SUB)
2851 {
2852 if (*ptr_char == *RC_VAR_ADD)
2853 op = *RC_VAR_SUB;
2854 else
2855 op = *RC_VAR_ADD;
2856 }
2857 else
2858 op = *ptr_char;
2859 ptr_char++;
2860 }
2861 }
2862 }
2863 else
2864 /*
2865 Error, text variable unset.
2866 */
2867 error = ERR_INVALID_VAR_REFERENCE;
2868 }
2869 else
2870 /*
2871 Error, text variable undefined.
2872 */
2873 error = ERR_INVALID_VAR_REFERENCE;
2874 }
2875 else
2876 /*
2877 Error, operation given in global mode.
2878 */
2879 error = ERR_ILLEGAL_VAR_OPERATION;
2880 }
2881 else
2882 /*
2883 Error, invalid second operator character found (no '=',
2884 '+' or '-' given resp., illegal combination of '+'
2885 and '-').
2886 */
2887 error = ERR_ILLEGAL_VAR_OPERATION;
2888 if (!error)
2889 {
2890 static Slint num;
2891
2892
2893 /*
2894 Perform the operation and store the calculated value.
2895 */
2896 if (tvar_inc_dec)
2897 {
2898 len =
2899 strlen (rc_tvar[IDX (tvar)].tvar_local.tvar_text);
2900 num =
2901 atol (rc_tvar[IDX (tvar)].tvar_local.tvar_text);
2902 if (op == *RC_VAR_ADD)
2903 num++;
2904 else
2905 num--;
2906 }
2907 else
2908 {
2909 i = 0;
2910 while (isdigit (*ptr_char))
2911 s5[i++] = *ptr_char++;
2912 while (isspace (*ptr_char))
2913 ptr_char++;
2914 if (i && !*ptr_char)
2915 {
2916 s5[i] = '\0';
2917 len =
2918 strlen (rc_tvar[IDX (tvar)].tvar_local.
2919 tvar_text);
2920 num =
2921 atol (rc_tvar[IDX (tvar)].tvar_local.
2922 tvar_text);
2923 if (op == *RC_VAR_ADD)
2924 num += atol (s5);
2925 else
2926 num -= atol (s5);
2927 }
2928 else
2929 /*
2930 Error, non-numerical value given.
2931 */
2932 error = ERR_ILLEGAL_VAR_OPERATION;
2933 }
2934 /*
2935 Store the calculated value.
2936 */
2937 if (!error)
2938 {
2939 sprintf (s5, "%0*ld", len, num);
2940 len = (int) strlen (s5);
2941 if (len !=
2942 (int) strlen (rc_tvar[IDX (tvar)].tvar_local.
2943 tvar_text))
2944 rc_tvar[IDX (tvar)].tvar_local.tvar_text =
2945 (char *)
2946 my_realloc ((VOID_PTR)
2947 (rc_tvar[IDX (tvar)].tvar_local.
2948 tvar_text), len + 1,
2949 ERR_NO_MEMORY_AVAILABLE, __FILE__,
2950 ((long) __LINE__) - 2L,
2951 "rc_tvar[IDX(tvar)].tvar_local.tvar_text",
2952 IDX (tvar));
2953 strcpy (rc_tvar[IDX (tvar)].tvar_local.tvar_text,
2954 s5);
2955 }
2956 }
2957 }
2958 }
2959 }
2960 }
2961 if (error)
2962 {
2963 if ((mode == GLobal)
2964 && (error == ERR_ILLEGAL_VAR_DEFINITION
2965 || error == ERR_ILLEGAL_VAR_OPERATION))
2966 /*
2967 These errors always cause termination of program in global mode.
2968 */
2969 warning_level = WARN_LVL_MAX;
2970 if (warning_level >= 0)
2971 {
2972 if (*line_buffer == RC_TVAR_CHAR)
2973 tvar = *(line_buffer + 1);
2974 if (!tvar)
2975 tvar = *line_buffer;
2976 var_warning (error, (int) tvar, line_buffer, filename, line_number);
2977 }
2978 }
2979 }
2980
2981
2982
2983 void
nth_weekday_of_month(d,m,y,n,is_weekday_mode)2984 nth_weekday_of_month (d, m, y, n, is_weekday_mode)
2985 int *d;
2986 int *m;
2987 int *y;
2988 const int *n;
2989 Bool *is_weekday_mode;
2990 /*!
2991 If "N'th weekday of month" field is encoded:
2992 Compute the according date and return it in `&d', `&m' and `&y'.
2993 If a conversion error occurs, return SPECIAL_VALUE in `&y'.
2994 */
2995 {
2996 register int i;
2997 register int j = 0;
2998 auto int dd = 0;
2999 auto int mm = 0;
3000 auto Bool year_set = FALSE;
3001 auto Bool year_modified = FALSE;
3002
3003
3004 if (*n
3005 && (!rc_year_flag
3006 || (*m
3007 && rc_year_flag))
3008 && (!rc_period_list || (*m && rc_period_list)))
3009 {
3010 if (!*m
3011 && (is_3month_mode || is_3month_mode2 || fiscal_month > MONTH_MIN))
3012 /*
3013 If fiscal year resp., 3 month mode and no month encoded, skip evaluation.
3014 */
3015 ;
3016 else
3017 {
3018 *is_weekday_mode = FALSE;
3019 if (!*y)
3020 {
3021 year_set = TRUE;
3022 *y = year;
3023 }
3024 if (!*m)
3025 {
3026 *m = month;
3027 /*
3028 A `-c[N]w' or `-ct' option set:
3029 Lookahead whether the week ends in the month it started.
3030 */
3031 if (rc_week_flag || rc_tomorrow_flag)
3032 {
3033 /*
3034 <0000|YYYY>00WW[W]N event is in last week of last month of previous year.
3035 */
3036 if ((*n > 3) && (day < DAY_MIN))
3037 {
3038 i = (days_of_february (year - 1) == 29);
3039 j = day + DAY_LAST + i;
3040 (void) doy2date (j, i, &dd, &mm);
3041 }
3042 else if (*n == 1)
3043 {
3044 /*
3045 <0000|YYYY>00WW[W]N event is in first week of next month of actual year.
3046 */
3047 if ((day + DAY_MAX - 1 > 0)
3048 && (day + DAY_MAX - 1 <
3049 DAY_LAST + is_leap_year + 1))
3050 (void) doy2date (day + DAY_MAX - 1, is_leap_year, &dd,
3051 &mm);
3052 else
3053 {
3054 /*
3055 <0000|YYYY>00WW[W]N event is in first week of first month of next year.
3056 */
3057 i = (days_of_february (year + 1) == 29);
3058 j = (day + DAY_MAX - 1) - (DAY_LAST + is_leap_year);
3059 (void) doy2date (j, i, &dd, &mm);
3060 }
3061 }
3062 dd = *d;
3063 }
3064 }
3065 else if (year_set && (rc_week_flag || rc_tomorrow_flag))
3066 {
3067 if ((*n == 9)
3068 && (*m == MONTH_MAX) && (*y > YEAR_MIN) && (day < DAY_MIN))
3069 {
3070 year_modified = TRUE;
3071 (*y)--;
3072 }
3073 else
3074 if ((*n == 1)
3075 && (*m == MONTH_MIN)
3076 && (*y < YEAR_MAX)
3077 && (day + DAY_MAX >= DAY_LAST + is_leap_year))
3078 {
3079 year_modified = TRUE;
3080 (*y)++;
3081 }
3082 }
3083 if (year_set
3084 && (*y < YEAR_MAX)
3085 && ((fiscal_month > MONTH_MIN) && (*m < fiscal_month)))
3086 if (!year_modified)
3087 (*y)++;
3088 if (*m == 2)
3089 i = days_of_february (*y);
3090 else
3091 i = dvec[*m - 1];
3092 if (*n == 9)
3093 *d = eval_holiday (i, *m, *y, *d, FALSE);
3094 else
3095 {
3096 *d = eval_holiday (DAY_MIN, *m, *y, *d, TRUE);
3097 *d += (DAY_MAX * (*n - 1));
3098 /*
3099 The "N'th weekday of month" doesn't occur in month:
3100 Skip it.
3101 */
3102 if (*d > i)
3103 *y = SPECIAL_VALUE;
3104 }
3105 /*
3106 A `-c[N]w' or `-ct' option set:
3107 Correction for lookahead.
3108 */
3109 if (mm && (rc_week_flag || rc_tomorrow_flag))
3110 {
3111 if ((*n == 1) && (mm != *m))
3112 {
3113 *m = mm;
3114 if ((day + DAY_MAX - 1 > 0)
3115 && (day + DAY_MAX - 1 < DAY_LAST + is_leap_year + 1))
3116 /*
3117 Void, don't change the year of event.
3118 */
3119 ;
3120 else if (year_set && (year < YEAR_MAX))
3121 *y = year + 1;
3122 *d = eval_holiday (DAY_MIN, *m, *y, dd, TRUE);
3123 }
3124 else
3125 if ((*n > 3)
3126 && ((adate_set
3127 && (mm == *m)) || (!adate_set && (mm != *m))))
3128 {
3129 if (!adate_set)
3130 *m = mm;
3131 if (year_set && (year > YEAR_MIN))
3132 *y = year - 1;
3133 if (*n == 9)
3134 *d =
3135 eval_holiday (dvec[MONTH_MAX - 1], *m, *y, dd, FALSE);
3136 else
3137 {
3138 *d = eval_holiday (DAY_MIN, *m, *y, dd, TRUE);
3139 *d += (DAY_MAX * (*n - 1));
3140 /*
3141 The "N'th weekday of month" doesn't occur in month:
3142 Skip it
3143 */
3144 if (*d > dvec[MONTH_MAX - 1])
3145 *y = SPECIAL_VALUE;
3146 }
3147 }
3148 }
3149 }
3150 }
3151 }
3152
3153
3154
3155 Slint
d_between(d1,m1,y1,d2,m2,y2)3156 d_between (d1, m1, y1, d2, m2, y2)
3157 const int d1;
3158 const int m1;
3159 const int y1;
3160 const int d2;
3161 const int m2;
3162 const int y2;
3163 /*!
3164 Computes the amount of days between date1(base date) and date2
3165 exclusive date1 and date2, and adds 1 to the result.
3166 */
3167 {
3168 return (date2num (d2, m2, y2) - date2num (d1, m1, y1));
3169 }
3170
3171
3172
3173 Slint
w_between(d1,m1,y1,d2,m2,y2)3174 w_between (d1, m1, y1, d2, m2, y2)
3175 const int d1;
3176 const int m1;
3177 const int y1;
3178 const int d2;
3179 const int m2;
3180 const int y2;
3181 /*!
3182 Computes the amount of weeks between date1(base date) and date2
3183 exclusive date1 and date2, and adds 1 to the result.
3184 */
3185 {
3186 auto Ulint date1 = date2num (d1, m1, y1);
3187 auto Ulint date2 = date2num (d2, m2, y2);
3188 auto Slint diff;
3189 auto Slint result;
3190
3191
3192 diff =
3193 (Slint) date2 - (date1 -
3194 (SYEAR (weekday_of_date (d1, m1, y1), start_day)) + 1);
3195 result = diff / DAY_MAX;
3196 if ((diff % DAY_MAX) && (diff < 0L))
3197 result--;
3198
3199 return (result);
3200 }
3201
3202
3203
3204 Slint
m_between(m1,y1,m2,y2)3205 m_between (m1, y1, m2, y2)
3206 const int m1;
3207 const int y1;
3208 const int m2;
3209 const int y2;
3210 /*!
3211 Computes the amount of months between date1(base date) and date2
3212 exclusive date1 and date2, and adds 1 to the result.
3213 */
3214 {
3215 return (((y2 - y1) * MONTH_MAX) + (m2 - m1));
3216 }
3217
3218
3219
3220 void
manage_leap_day(day,month,year,line_buffer,filename,line_number)3221 manage_leap_day (day, month, year, line_buffer, filename, line_number)
3222 int *day;
3223 int *month;
3224 int year;
3225 const char *line_buffer;
3226 const char *filename;
3227 const long line_number;
3228 /*!
3229 Tries to set the leap day (29-Feb) either to "28-Feb" or "1-Mar"
3230 and prints a informational message in case this date modification is
3231 performed successfully (only if `--debug[=ARG]' option is given).
3232 */
3233 {
3234 register int action = 0;
3235
3236
3237 if ((*month == 2)
3238 && (*day == 29) && (rc_feb_29_to_feb_28 || rc_feb_29_to_mar_01))
3239 {
3240 if ((fiscal_month > MONTH_MIN + 1) && (year < YEAR_MAX))
3241 {
3242 if (days_of_february (year + 1) == 28)
3243 {
3244 if (rc_feb_29_to_feb_28)
3245 *day = action = 28;
3246 else
3247 {
3248 *day = action = DAY_MIN;
3249 (*month)++;
3250 }
3251 }
3252 }
3253 else if (days_of_february (year) == 28)
3254 {
3255 if (rc_feb_29_to_feb_28)
3256 *day = action = 28;
3257 else
3258 {
3259 *day = action = DAY_MIN;
3260 (*month)++;
3261 }
3262 }
3263 if ((warning_level >= 0) && action)
3264 {
3265 *s5 = '\0';
3266 print_text (stderr, s5);
3267 action = (int) strlen (filename) + LEN_SINGLE_LINE;
3268 if ((Uint) action >= maxlen_max)
3269 resize_all_strings (action + 1, FALSE, __FILE__, (long) __LINE__);
3270 sprintf (s5, _("Leap-day set to `%02d-%s' in file `%s'."),
3271 *day, month_name (*month), filename);
3272 print_text (stderr, s5);
3273 sprintf (s5, _("Line %ld: %s"), line_number, line_buffer);
3274 print_text (stderr, s5);
3275 }
3276 }
3277 }
3278
3279
3280
3281 char *
biorhythm(create_bar,axis_len,string,day,month,year,birth_day,birth_month,birth_year,emo_text,emo_phase,emo_waxes,int_text,int_phase,int_waxes,phy_text,phy_phase,phy_waxes,critical_day,positive_day,negative_day)3282 biorhythm (create_bar, axis_len, string,
3283 day, month, year, birth_day, birth_month, birth_year,
3284 emo_text, emo_phase, emo_waxes,
3285 int_text, int_phase, int_waxes,
3286 phy_text, phy_phase, phy_waxes,
3287 critical_day, positive_day, negative_day)
3288 const Bool create_bar;
3289 int axis_len;
3290 char *string;
3291 const int day;
3292 const int month;
3293 const int year;
3294 const int birth_day;
3295 const int birth_month;
3296 const int birth_year;
3297 const char *emo_text;
3298 int *emo_phase;
3299 int *emo_waxes;
3300 const char *int_text;
3301 int *int_phase;
3302 int *int_waxes;
3303 const char *phy_text;
3304 int *phy_phase;
3305 int *phy_waxes;
3306 int *critical_day;
3307 int *positive_day;
3308 int *negative_day;
3309 /*!
3310 Computes the biorhythm for a date and creates a text graphics bar line
3311 according to the computed values in case `create_bar' is set to TRUE.
3312 Uses the delivered `string' for storing such a line and returns it.
3313 The caller has to guarantee that enough `string' space is allocated.
3314 When used within Gcal, the maximum number of 100 that a single `axis_len'
3315 may have (100*2+6=>206) fits properly into the string vectors, which
3316 have 1024 Bytes by default.
3317 */
3318 {
3319 auto double x;
3320 auto Slint diff =
3321 d_between (birth_day, birth_month, birth_year, day, month, year);
3322 register int yes_phase;
3323 register int yes_waxes;
3324 register int i;
3325
3326
3327 (*critical_day) = (*positive_day) = (*negative_day) = (*emo_waxes) =
3328 (*int_waxes) = (*phy_waxes) = 0;
3329 if (create_bar)
3330 {
3331 auto char *ptr_string;
3332
3333
3334 # if 0
3335 /*
3336 Decrease `axis_len' by 1 until it divides 100 without a remainder.
3337 */
3338 if (axis_len < 1)
3339 axis_len = -axis_len;
3340 if (axis_len > 100)
3341 axis_len = 100;
3342 else
3343 while (100 % axis_len)
3344 axis_len--;
3345 # endif /* 0 */
3346 /*
3347 Initialize the biorhythm text graphics bar.
3348 */
3349 for (i = BIO_AXIS_TOTAL (axis_len), ptr_string = string; --i;)
3350 *ptr_string++ = ' ';
3351 string[BIO_AXIS_TOTAL (axis_len) - 1] = '\0';
3352 string[BIO_AXIS_EXTRA - 1] = *BIO_WANES;
3353 string[BIO_AXIS_TOTAL (axis_len) - BIO_AXIS_EXTRA - 1] = *BIO_WAXES;
3354 }
3355 /*
3356 Reduce the day difference by multiples of 21252, which is the number
3357 of days at which each biorhythm cycle restarts from the birthdate.
3358 */
3359 if (diff < 1L)
3360 diff = 21252L - (-diff % 21252L);
3361 else
3362 diff %= 21252L;
3363 /*
3364 Manage the "emotional" phase value (cycle of 28 days).
3365 */
3366 x = MY_TWO_PI * (diff - 1L) / 28.0;
3367 yes_phase = (int) ROUND (100.0 * sin (x));
3368 yes_waxes = SGN ((int) ROUND (100.0 * cos (x)));
3369 x = MY_TWO_PI * diff / 28.0;
3370 *emo_phase = (int) ROUND (100.0 * sin (x));
3371 *emo_waxes = SGN ((int) ROUND (100.0 * cos (x)));
3372 if (*emo_phase == 100)
3373 (*positive_day)++;
3374 else if ((yes_waxes == 1) && (yes_phase != 100) && (*emo_waxes <= 0))
3375 (*positive_day)++;
3376 if (*emo_phase == -100)
3377 (*negative_day)++;
3378 else if ((yes_waxes == -1) && (yes_phase != -100) && (*emo_waxes >= 0))
3379 (*negative_day)++;
3380 *emo_waxes = (*emo_waxes >= 0);
3381 if (((SGN (yes_phase) == 1)
3382 && (SGN (*emo_phase) <= 0))
3383 || ((SGN (yes_phase) == -1) && (SGN (*emo_phase) >= 0)))
3384 (*critical_day)++;
3385 /*
3386 Manage the "intellectual" phase value (cycle of 33 days).
3387 */
3388 x = MY_TWO_PI * (diff - 1L) / 33.0;
3389 yes_phase = (int) ROUND (100.0 * sin (x));
3390 yes_waxes = SGN ((int) ROUND (100.0 * cos (x)));
3391 x = MY_TWO_PI * diff / 33.0;
3392 *int_phase = (int) ROUND (100.0 * sin (x));
3393 *int_waxes = SGN ((int) ROUND (100.0 * cos (x)));
3394 if (*int_phase == 100)
3395 (*positive_day)++;
3396 else if ((yes_waxes == 1) && (yes_phase != 100) && (*int_waxes <= 0))
3397 (*positive_day)++;
3398 if (*int_phase == -100)
3399 (*negative_day)++;
3400 else if ((yes_waxes == -1) && (yes_phase != -100) && (*int_waxes >= 0))
3401 (*negative_day)++;
3402 *int_waxes = (*int_waxes >= 0);
3403 if (((SGN (yes_phase) == 1)
3404 && (SGN (*int_phase) <= 0))
3405 || ((SGN (yes_phase) == -1) && (SGN (*int_phase) >= 0)))
3406 (*critical_day)++;
3407 /*
3408 Manage the "physical" phase value (cycle of 23 days).
3409 */
3410 x = MY_TWO_PI * (diff - 1L) / 23.0;
3411 yes_phase = (int) ROUND (100.0 * sin (x));
3412 yes_waxes = SGN ((int) ROUND (100.0 * cos (x)));
3413 x = MY_TWO_PI * diff / 23.0;
3414 *phy_phase = (int) ROUND (100.0 * sin (x));
3415 *phy_waxes = SGN ((int) ROUND (100.0 * cos (x)));
3416 if (*phy_phase == 100)
3417 (*positive_day)++;
3418 else if ((yes_waxes == 1) && (yes_phase != 100) && (*phy_waxes <= 0))
3419 (*positive_day)++;
3420 if (*phy_phase == -100)
3421 (*negative_day)++;
3422 else if ((yes_waxes == -1) && (yes_phase != -100) && (*phy_waxes >= 0))
3423 (*negative_day)++;
3424 *phy_waxes = (*phy_waxes >= 0);
3425 if (((SGN (yes_phase) == 1)
3426 && (SGN (*phy_phase) <= 0))
3427 || ((SGN (yes_phase) == -1) && (SGN (*phy_phase) >= 0)))
3428 (*critical_day)++;
3429 if (create_bar)
3430 {
3431 /*
3432 Place the "emotional", "intellectual" and "physical"
3433 marker on the text graphics bar.
3434 */
3435 i = *emo_phase / BIO_AXIS_SCALE (axis_len);
3436 if (SGN (*emo_phase) >= 0)
3437 i += BIO_AXIS_ZERO (axis_len);
3438 else
3439 i = BIO_AXIS_ZERO (axis_len) + i;
3440 if (string[i] == ' ')
3441 string[i] = *emo_text;
3442 else
3443 string[i] = *BIO_OVERLAPS;
3444 i = *int_phase / BIO_AXIS_SCALE (axis_len);
3445 if (SGN (*int_phase) >= 0)
3446 i += BIO_AXIS_ZERO (axis_len);
3447 else
3448 i = BIO_AXIS_ZERO (axis_len) + i;
3449 if (string[i] == ' ')
3450 string[i] = *int_text;
3451 else
3452 string[i] = *BIO_OVERLAPS;
3453 i = *phy_phase / BIO_AXIS_SCALE (axis_len);
3454 if (SGN (*phy_phase) >= 0)
3455 i += BIO_AXIS_ZERO (axis_len);
3456 else
3457 i = BIO_AXIS_ZERO (axis_len) + i;
3458 if (string[i] == ' ')
3459 string[i] = *phy_text;
3460 else
3461 string[i] = *BIO_OVERLAPS;
3462 /*
3463 Place the accumulated "negative", "positive" and "critical"
3464 day counters on the text graphics bar.
3465 */
3466 *string = DIG2CHR (*negative_day);
3467 string[BIO_AXIS_TOTAL (axis_len) - BIO_AXIS_EXTRA] =
3468 DIG2CHR (*positive_day);
3469 string[BIO_AXIS_ZERO (axis_len)] = DIG2CHR (*critical_day);
3470 }
3471
3472 return (string);
3473 }
3474
3475
3476
3477 double
compute_distance(coor1,coor2)3478 compute_distance (coor1, coor2)
3479 const Coor_struct *coor1;
3480 const Coor_struct *coor2;
3481 /*!
3482 Returns the air line distance in Kilometers between the two geographical
3483 point locations which are delivered in the COOR1 and COOR2 structures
3484 if the member variable `the_mode' is set to zero.
3485 If `the_mode' is set to 1, the course/direction angle in degrees from
3486 COOR1 to COOR2 is returned (or SPECIAL_VALUE if an error occurs).
3487 If `the_mode' is set to 2, the course/direction angle in degrees from
3488 COOR2 to COOR1 is returned (or SPECIAL_VALUE if an error occurs).
3489 The course/direction angle is that angle, which one needs to go from
3490 the geographical point location given in one COOR? structure to the
3491 geographical point location given in the other COOR? structure.
3492 The angle values in degrees are:
3493 * North := 0.0 <= angle < 90.0
3494 * East := 90.0 <= angle < 180.0
3495 * South := 180.0 <= angle < 270.0
3496 * West := 270.0 <= angle < 0.0
3497 The longitude coordinates west of the zero meridian have a positive sign.
3498 The longitude coordinates east of the zero meridian have a negative sign.
3499 The latitude coordinates north of the equator have a positive sign.
3500 The latitude coordinates south of the equator have a negative sign.
3501 For negative numbers, all three of `*_deg', `*_min' and `*_sec' should
3502 be negative. For example, the ISO 6709 coordinate `-202233+1100010'
3503 (==+|-Latitude+|-Longitude) must be defined as `-20 -22 -33 -110 0 -10'.
3504 The spheric trigonometric formula used to calculate the distance is:
3505 R := 6371.221, the *mean* Earth radius
3506 phi1 := Latitude of COOR1
3507 phi2 := Latitude of COOR2
3508 delta_lambda := Longitude of COOR1 - Longitude of COOR2
3509 g := arc cosine (sine phi1 * sine phi2 + cosine phi1 * cosine phi2 * cosine delta_lambda)
3510 => distance_in_km := 2 * Pi * R * degree(g) / 360
3511 *** Gcal respects the flattening of the Earth in that it uses the true
3512 *** Earth radii of the given locations instead of the mean Earth radius,
3513 *** and their geocentric latitudes instead of their geodetic latitude!
3514 The spheric trigonometric formula used to calculate the direction angle
3515 for `the_mode==1' is:
3516 y := sine delta_lambda
3517 x := cosine phi1 * tangent phi2 - sine phi1 * cosine delta_lambda
3518 => (1) direction_angle_in_degrees := degree(arc tangent (y / x))
3519 An alternative formula is:
3520 => (2) direction_angle_in_degrees := degree(arc sine (cos phi2 * sine delta_lambda / sine g))
3521 Both formulaes do not move the direction angle into the correct quadrant
3522 immediately. For (1), this can be done by using the mathlib function
3523 `atan2()' instead of the mathlib function `atan()'.
3524 */
3525 {
3526 auto double lon_c1 = TORAD (coor1->lon_deg
3527 + MM2DEG (coor1->lon_min)
3528 + SS2DEG (coor1->lon_sec));
3529 auto double lat_c1 = TORAD (coor1->lat_deg
3530 + MM2DEG (coor1->lat_min)
3531 + SS2DEG (coor1->lat_sec));
3532 auto double lon_c2 = TORAD (coor2->lon_deg
3533 + MM2DEG (coor2->lon_min)
3534 + SS2DEG (coor2->lon_sec));
3535 auto double lat_c2 = TORAD (coor2->lat_deg
3536 + MM2DEG (coor2->lat_min)
3537 + SS2DEG (coor2->lat_sec));
3538 auto double delta_lambda;
3539 auto double x1;
3540 auto double x2;
3541
3542
3543 switch (coor1->the_mode)
3544 {
3545 case 0:
3546 x1 =
3547 gd_latitude2gc_latitude (lat_c1, coor1->meters_above_sea_level,
3548 &lat_c1);
3549 x2 =
3550 gd_latitude2gc_latitude (lat_c2, coor2->meters_above_sea_level,
3551 &lat_c2);
3552 if (SGN (lat_c1) == 0 || SGN (lat_c2) == 0
3553 || SGN (lat_c1) == SGN (lat_c2))
3554 {
3555 delta_lambda = (x1 + x2) * 0.5;
3556 x1 = 2.0 * MAX (x1, delta_lambda) + MIN (x1, delta_lambda)
3557 + 2.0 * MAX (x2, delta_lambda) + MIN (x2, delta_lambda);
3558 }
3559 else
3560 x1 = 4.0 * EQUATOR_EARTH_RADIUS + x1 + x2;
3561 delta_lambda = lon_c1 - lon_c2;
3562 return (DEG2DAY (TODEG (acos (sin (lat_c1) * sin (lat_c2)
3563 +
3564 cos (lat_c1) * cos (lat_c2) *
3565 cos (delta_lambda)))) * (x1 / 6000.0) *
3566 MY_TWO_PI);
3567 case 1:
3568 delta_lambda = lon_c1 - lon_c2;
3569 x1 = cos (lat_c1) * tan (lat_c2) - sin (lat_c1) * cos (delta_lambda);
3570 break;
3571 case 2:
3572 delta_lambda = lon_c2 - lon_c1;
3573 x1 = cos (lat_c2) * tan (lat_c1) - sin (lat_c2) * cos (delta_lambda);
3574 break;
3575 default:
3576 /*
3577 This case MUST be an internal error!
3578 */
3579 abort ();
3580 }
3581 x2 = sin (delta_lambda);
3582 /*
3583 Emulate the mathlib function `atan2()' so we can handle an error properly.
3584 */
3585 if (x1 > 0.0)
3586 delta_lambda = atan (x2 / x1);
3587 else if (x1 < 0.0)
3588 delta_lambda = atan (x2 / x1) + MY_PI;
3589 else if (x2 > 0.0)
3590 delta_lambda = MY_HALF_PI;
3591 else if (x2 < 0.0)
3592 delta_lambda = -MY_HALF_PI;
3593 else
3594 /*
3595 This case (x2==0 && x1==0) is treated as an error here
3596 and is managed specially!
3597 */
3598 return (SPECIAL_VALUE);
3599 if (SGN (delta_lambda) > 0)
3600 delta_lambda = MY_TWO_PI - delta_lambda;
3601 else if (SGN (delta_lambda) < 0)
3602 delta_lambda = -delta_lambda;
3603
3604 return (TODEG (delta_lambda));
3605 }
3606
3607
3608
3609 static void
var_warning(exit_status,var_name,line_buffer,filename,line_number)3610 var_warning (exit_status, var_name, line_buffer, filename, line_number)
3611 const int exit_status;
3612 const int var_name;
3613 const char *line_buffer;
3614 const char *filename;
3615 const long line_number;
3616 /*!
3617 Prints an informational message on STDERR channel in case an operation
3618 on a date or text variable is invalid. Terminates the program if
3619 `warning_level' is set to "WARN_LVL_MAX" with delivered `exit_status'.
3620 */
3621 {
3622 register int i;
3623 auto Bool with_usage = FALSE;
3624
3625
3626 if (!line_number)
3627 S_NEWLINE (stderr);
3628 else
3629 {
3630 *s5 = '\0';
3631 print_text (stderr, s5);
3632 }
3633 if (warning_level >= WARN_LVL_MAX)
3634 fprintf (stderr, _("%s: abort, "), prgr_name);
3635 i = (int) strlen (filename) + LEN_SINGLE_LINE;
3636 if ((Uint) i >= maxlen_max)
3637 resize_all_strings (i + 1, FALSE, __FILE__, (long) __LINE__);
3638 switch (exit_status)
3639 {
3640 case ERR_ILLEGAL_VAR_DEFINITION:
3641 if (line_number)
3642 sprintf (s5, _("illegal variable definition in file `%s'"), filename);
3643 else
3644 sprintf (s5, _("illegal definition of variable `%c'"),
3645 (char) var_name);
3646 if (!line_number)
3647 with_usage = TRUE;
3648 break;
3649 case ERR_ILLEGAL_VAR_OPERATION:
3650 if (line_number)
3651 sprintf (s5, _("illegal variable operation in file `%s'"), filename);
3652 else
3653 sprintf (s5, _("illegal operation on variable `%c'"),
3654 (char) var_name);
3655 if (!line_number)
3656 with_usage = TRUE;
3657 break;
3658 case ERR_INVALID_VAR_REFERENCE:
3659 sprintf (s5, _("variable `%c' undefined in file `%s'"),
3660 (char) var_name, filename);
3661 break;
3662 case ERR_INVALID_VAR_ASSIGNMENT:
3663 sprintf (s5, _("invalid value assigned to variable `%c' in file `%s'"),
3664 (char) var_name, filename);
3665 break;
3666 default:
3667 /*
3668 This case MUST be an internal error!
3669 */
3670 abort ();
3671 }
3672 if (warning_level < WARN_LVL_MAX)
3673 {
3674 *s5 = (char) toupper (*s5);
3675 strcat (s5, ".");
3676 }
3677 if (!line_number)
3678 fprintf (stderr, "%s\n", s5);
3679 else
3680 print_text (stderr, s5);
3681 if (warning_level >= WARN_LVL_MAX)
3682 {
3683 if (!line_number)
3684 fprintf (stderr, _("Invalid argument in command line given -- %s"),
3685 line_buffer);
3686 else
3687 fprintf (stderr, _("Line %ld: %s"), line_number, line_buffer);
3688 S_NEWLINE (stderr);
3689 }
3690 else
3691 {
3692 i = (int) strlen (line_buffer) + LEN_SINGLE_LINE;
3693 if ((Uint) i >= maxlen_max)
3694 resize_all_strings (i + 1, FALSE, __FILE__, (long) __LINE__);
3695 if (!line_number)
3696 sprintf (s5, _("Argument `%s' of command line ignored."),
3697 line_buffer);
3698 else
3699 sprintf (s5, _("Line %ld ignored: %s"), line_number, line_buffer);
3700 if (!line_number)
3701 fprintf (stderr, "%s\n", s5);
3702 else
3703 print_text (stderr, s5);
3704 }
3705 if (with_usage)
3706 fprintf (stderr, "%s\n%s\n", usage_msg (), lopt_msg ());
3707 if (warning_level >= WARN_LVL_MAX)
3708 my_exit (exit_status);
3709 }
3710 #endif /* USE_RC */
3711