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