NULL is a pointer, not string terminator
[oweals/cde.git] / cde / lib / csa / reparser.y
1 /* $XConsortium: reparser.y /main/2 1996/11/11 11:52:15 drk $ */
2 %{
3 /*
4  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
5  *  (c) Copyright 1993, 1994 International Business Machines Corp.
6  *  (c) Copyright 1993, 1994 Novell, Inc.
7  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
8  */
9
10 #include <EUSCompat.h>
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <time.h>
14 #include "rerule.h"
15 #include "iso8601.h"
16
17 /* The parsed rule is stored in this structure */
18 RepeatEvent     *_DtCm_repeat_info;
19
20 extern int               yylex();
21 extern void              yyerror(char*);
22
23 static int CompareNums(const void *, const void *);
24 static int CompareDayTime(const void *, const void *);
25 static int CompareWeekDayTime(const void *, const void *);
26 static unsigned int *ConvertNumList(NumberList *, unsigned int * /* Return */);
27 static WeekDayTime *ConvertWeekDayTime(WeekDayTimeList *, unsigned int *);
28 static NumberList *AllocNumber(unsigned int);
29 static WeekDayTimeList *AllocWeekDayTimeList(NumberList*, NumberList *,
30                         NumberList *);
31 static RepeatEvent *HandleEndDate(RepeatEvent *, time_t);
32 static RepeatEvent *DeriveMinuteEvent(unsigned int, unsigned int);
33 static RepeatEvent *DeriveDailyEvent(unsigned int, NumberList *,
34                         unsigned int, RepeatEvent *);
35 static RepeatEvent *DeriveWeeklyEvent(unsigned int, DayTimeList *,
36                         unsigned int, RepeatEvent *);
37 static RepeatEvent *DeriveMonthlyEvent(RepeatType, unsigned int, void *,
38                         unsigned int, RepeatEvent *);
39 static RepeatEvent *DeriveYearlyEvent(RepeatType, unsigned int, NumberList *,
40                         unsigned int, RepeatEvent *);
41
42 %}
43
44 %token ERROR ENDMARKER DURATION NUMBER FIRSTWEEK SECONDWEEK THIRDWEEK
45 %token FOURTHWEEK FIFTHWEEK LASTWEEK SECONDLAST THIRDLAST FOURTHLAST            
46 %token FIFTHLAST MINUTECOMMAND DAILYCOMMAND WEEKLYCOMMAND MONTHPOSCOMMAND       
47 %token MONTHDAYCOMMAND YEARDAYCOMMAND YEARMONTHCOMMAND LASTDAY SUNDAY
48 %token MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY DATE 
49
50 %union  {
51                 int              number;
52                 RepeatEvent     *re;
53                 NumberList      *nl;
54                 WeekDay          weekday;
55                 WeekNumber       weeknum;
56                 DayTime         *dt;
57                 DayTimeList     *dtl;
58                 WeekDayTimeList *wdtl;
59                 time_t           enddate;
60                 char             date[64];
61         }
62
63 %type <re>      start begin minuteEvent dailyEvent weeklyEvent monthlyPosEvent
64                 monthlyDayEvent yearlyByMonth yearlyByDay
65 %type <nl>      time0List timeList dayOfMonthList monthOfYearList
66                 dayOfYearList occurrenceList genericNumberList 
67                 generic0NumberList weekdayList
68 %type <weekday> SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY
69                 weekday
70 %type <number>  duration NUMBER occurrence endMarker time
71 %type <dtl>     weekdayTimeList
72 %type <wdtl>    weekDayTime
73 %type <dt>      weekdayTimePair
74 %type <date>    DATE
75 %type <enddate> endDate
76
77 %% /* Beginning of rules section */
78
79 start           : begin 
80                     {
81                         _DtCm_repeat_info = $$;
82                     }
83                 | error
84                     {
85                         /*
86                          * XXX: Memory leak: We need to free up any portion
87                          * of the re struct that has be allocated before the
88                          * error was encountered.
89                          */
90                         _DtCm_repeat_info = NULL;
91                     }
92                 ;
93
94 begin           : minuteEvent endDate
95                     {
96                         $$ = HandleEndDate($1, $2);
97                     }
98                 | dailyEvent endDate
99                     {
100                         $$ = HandleEndDate($1, $2);
101                     }
102                 | weeklyEvent endDate
103                     {
104                         $$ = HandleEndDate($1, $2);
105                     }
106                 | monthlyPosEvent endDate
107                     {
108                         $$ = HandleEndDate($1, $2);
109                     }
110                 | monthlyDayEvent endDate
111                     {
112                         $$ = HandleEndDate($1, $2);
113                     }
114                 | yearlyByMonth endDate
115                     {
116                         $$ = HandleEndDate($1, $2);
117                     }
118                 | yearlyByDay endDate
119                     {
120                         $$ = HandleEndDate($1, $2);
121                     }
122                 ;
123
124 minuteEvent     : /* empty */
125                     {
126                         $$ = NULL;
127                     }
128                 | MINUTECOMMAND NUMBER duration
129                     {
130 #ifdef MINUTE
131                         $$ = DeriveMinuteEvent($2, $3);
132 #else
133                         $$ = NULL;
134 #endif /* MINUTE */
135                     }
136                 ;
137
138 dailyEvent
139                 : DAILYCOMMAND NUMBER time0List duration minuteEvent
140                     {
141                         $$ = DeriveDailyEvent($2, $3, $4, $5);
142                         if ($5) $$ = NULL;
143                     }
144                 ;
145
146 weeklyEvent
147                 : WEEKLYCOMMAND NUMBER weekdayTimeList duration minuteEvent
148                     {
149                         $$ = DeriveWeeklyEvent($2, $3, $4, $5);
150                         if ($5) $$ = NULL;
151                     }
152                 | WEEKLYCOMMAND NUMBER duration minuteEvent 
153                     {
154                         $$ = DeriveWeeklyEvent($2, NULL, $3, $4);
155                         if ($4) $$ = NULL;
156                     }
157                 ;
158
159 monthlyPosEvent
160                 : MONTHPOSCOMMAND NUMBER weekDayTime duration minuteEvent
161                     {
162                         $$ = DeriveMonthlyEvent(RT_MONTHLY_POSITION,
163                                                 $2, $3, $4, $5);
164                         if ($5) $$ = NULL;
165                     }
166                 | MONTHPOSCOMMAND NUMBER weekDayTime duration dailyEvent 
167                     {
168                         $$ = DeriveMonthlyEvent(RT_MONTHLY_POSITION,
169                                                 $2, $3, $4, $5);
170                         if ($5) $$ = NULL;
171                     }
172                 | MONTHPOSCOMMAND NUMBER weekDayTime duration weeklyEvent
173                     {
174                         $$ = DeriveMonthlyEvent(RT_MONTHLY_POSITION,
175                                                 $2, $3, $4, $5);
176                         if ($5) $$ = NULL;
177                     }
178                 ;
179
180 monthlyDayEvent : MONTHDAYCOMMAND NUMBER dayOfMonthList duration minuteEvent
181                     {
182                         $$ = DeriveMonthlyEvent(RT_MONTHLY_DAY, $2, $3, $4, $5);
183                         if ($5) $$ = NULL;
184                     }
185                 | MONTHDAYCOMMAND NUMBER dayOfMonthList duration dailyEvent
186                     {
187                         $$ = DeriveMonthlyEvent(RT_MONTHLY_DAY, $2, $3, $4, $5);
188                         if ($5) $$ = NULL;
189                     }
190                 | MONTHDAYCOMMAND NUMBER dayOfMonthList duration weeklyEvent
191                     {
192                         $$ = DeriveMonthlyEvent(RT_MONTHLY_DAY, $2, $3, $4, $5);
193                         if ($5) $$ = NULL;
194                     }
195                 ;
196
197 yearlyByMonth   : YEARMONTHCOMMAND NUMBER monthOfYearList duration minuteEvent 
198                     {
199                         $$ = DeriveYearlyEvent(RT_YEARLY_MONTH, $2, $3, $4, $5);
200                         if ($5) $$ = NULL;
201                     }
202                 | YEARMONTHCOMMAND NUMBER monthOfYearList duration dailyEvent 
203                     {
204                         $$ = DeriveYearlyEvent(RT_YEARLY_MONTH, $2, $3, $4, $5);
205                         if ($5) $$ = NULL;
206                     }
207                 | YEARMONTHCOMMAND NUMBER monthOfYearList duration weeklyEvent 
208                     {
209                         $$ = DeriveYearlyEvent(RT_YEARLY_MONTH, $2, $3, $4, $5);
210                         if ($5) $$ = NULL;
211                     }
212                 | YEARMONTHCOMMAND NUMBER monthOfYearList duration
213                                                                 monthlyPosEvent 
214                     {
215                         $$ = DeriveYearlyEvent(RT_YEARLY_MONTH, $2, $3, $4, $5);
216                         if ($5) $$ = NULL;
217                     }
218                 | YEARMONTHCOMMAND NUMBER monthOfYearList duration
219                                                                 monthlyDayEvent 
220                     {
221                         $$ = DeriveYearlyEvent(RT_YEARLY_MONTH, $2, $3, $4, $5);
222                         if ($5) $$ = NULL;
223                     }
224                 ;
225
226 yearlyByDay     : YEARDAYCOMMAND NUMBER dayOfYearList duration minuteEvent 
227                     {
228                         $$ = DeriveYearlyEvent(RT_YEARLY_DAY, $2, $3, $4, $5);
229                         if ($5) $$ = NULL;
230                     }
231                 | YEARDAYCOMMAND NUMBER dayOfYearList duration dailyEvent 
232                     {
233                         $$ = DeriveYearlyEvent(RT_YEARLY_DAY, $2, $3, $4, $5);
234                         if ($5) $$ = NULL;
235                     }
236                 | YEARDAYCOMMAND NUMBER dayOfYearList duration weeklyEvent 
237                     {
238                         $$ = DeriveYearlyEvent(RT_YEARLY_DAY, $2, $3, $4, $5);
239                         if ($5) $$ = NULL;
240                     }
241                 | YEARDAYCOMMAND NUMBER dayOfYearList duration monthlyPosEvent 
242                     {
243                         $$ = DeriveYearlyEvent(RT_YEARLY_DAY, $2, $3, $4, $5);
244                         if ($5) $$ = NULL;
245                     }
246                 | YEARDAYCOMMAND NUMBER dayOfYearList duration monthlyDayEvent 
247                     {
248                         $$ = DeriveYearlyEvent(RT_YEARLY_DAY, $2, $3, $4, $5);
249                         if ($5) $$ = NULL;
250                     }
251                 ;
252
253                 /* e.g. 1W MO TU 1300 1400 */
254 weekDayTime     : /* empty */
255                     {
256                         $$ = NULL;
257                     }
258                 | weekDayTime occurrenceList weekdayList
259                     {
260                         WeekDayTimeList *wdtl;
261
262                         wdtl = AllocWeekDayTimeList($2, $3, NULL);
263
264                         if ($1 == NULL) {
265                                 $$ = wdtl;
266                         } else {
267                                 wdtl->wdtl_next = $$->wdtl_next;
268                                 $$->wdtl_next = wdtl;
269                         }
270                     }
271                 | weekDayTime occurrenceList weekdayList timeList
272                     {
273                         WeekDayTimeList *wdtl;
274
275                         wdtl = AllocWeekDayTimeList($2, $3, $4);
276
277                         if ($1 == NULL) {
278                                 $$ = wdtl;
279                         } else {
280                                 wdtl->wdtl_next = $$->wdtl_next;
281                                 $$->wdtl_next = wdtl;
282                         }
283                     }
284                 ;
285
286                 /* e.g. MO TU WE TH */
287 weekdayList     : weekday
288                     {
289                         $$ = AllocNumber($1);
290                     }
291                 | weekdayList weekday
292                     {
293                         NumberList      *nl;
294
295                         nl = AllocNumber($2);
296
297                         if ($1 == NULL) {
298                                 $$ = nl;
299                         } else {
300                                 nl->nl_next = $$->nl_next;
301                                 $$->nl_next = nl;
302                         }
303                     }
304                 ;
305
306                 /* e.g. 1W 3W 2L LW */
307 occurrenceList  : occurrence
308                     {
309                         $$ = AllocNumber($1);
310                     }
311                 | occurrenceList occurrence
312                     {
313                         NumberList      *nl;
314
315                         nl = AllocNumber($2);
316
317                         if ($1 == NULL) {
318                                 $$ = nl;
319                         } else {
320                                 nl->nl_next = $$->nl_next;
321                                 $$->nl_next = nl;
322                         }
323                    }
324                 ;
325
326                 /* e.g. MO 1300 1500 2000 */
327 weekdayTimePair : weekday time0List
328                     {
329                         DayTime         *dt;
330
331                         dt = (DayTime *)calloc(1, sizeof(DayTime));
332
333                         dt->dt_day = $1;
334                                 /* Convert from list to array, sort */
335
336                         dt->dt_time =
337                                 (Time *)ConvertNumList($2, &(dt->dt_ntime));
338                         $$ = dt;
339                     }
340                 ;
341
342                 /* e.g. MO 1300 1500 2000 TU 1200 1400 */
343 weekdayTimeList : weekdayTimePair  
344                     {
345                         DayTimeList     *dtl;
346
347                         dtl = (DayTimeList *)calloc(1, sizeof(DayTimeList));
348
349                         dtl->dtl_daytime = $1;
350                         dtl->dtl_next = NULL;
351
352                         $$ = dtl;
353
354                     }
355                 | weekdayTimeList weekdayTimePair
356                     {
357                         DayTimeList     *dtl,
358                                         *dtl_end;
359
360                         dtl = (DayTimeList *)calloc(1, sizeof(DayTimeList));
361
362                         dtl->dtl_daytime = $2;
363                         dtl->dtl_next = NULL;
364
365                         if ($1 == NULL) {
366                                 $$ = dtl;
367                         } else {
368                                 /* Insert the new entry at the end.  This is
369                                  * so that MO TU 200 300 TH will maintain the
370                                  * same order in the list since MO uses the
371                                  * times next to TU and TH is dependent on the
372                                  * appt time.
373                                  */
374                                 dtl_end = $$;
375                                 while (dtl_end->dtl_next)
376                                         dtl_end = dtl_end->dtl_next;
377                                 
378                                 dtl_end->dtl_next = dtl;
379                         }
380                    }
381                 ;
382
383                 /* e.g. 1+ 2+ 3- 1- */
384 occurrence      : FIRSTWEEK endMarker
385                     {
386                         $$ = WK_F1;
387                         if ($2) RE_SET_FLAG($$);
388                     }
389                 | SECONDWEEK endMarker
390                     {
391                         $$ = WK_F2;
392                         if ($2) RE_SET_FLAG($$);
393                     }
394                 | THIRDWEEK endMarker
395                     {
396                         $$ = WK_F3;
397                         if ($2) RE_SET_FLAG($$);
398                     }
399                 | FOURTHWEEK endMarker
400                     {
401                         $$ = WK_F4;
402                         if ($2) RE_SET_FLAG($$);
403                     }
404                 | FIFTHWEEK endMarker
405                     {
406                         $$ = WK_F5;
407                         if ($2) RE_SET_FLAG($$);
408                     }
409                 | LASTWEEK endMarker
410                     {
411                         $$ = WK_L1;
412                         if ($2) RE_SET_FLAG($$);
413                     }
414                 | SECONDLAST endMarker
415                     {
416                         $$ = WK_L2;
417                         if ($2) RE_SET_FLAG($$);
418                     }
419                 | THIRDLAST endMarker
420                     {
421                         $$ = WK_L3;
422                         if ($2) RE_SET_FLAG($$);
423                     }
424                 | FOURTHLAST endMarker
425                     {
426                         $$ = WK_L4;
427                         if ($2) RE_SET_FLAG($$);
428                     }
429                 | FIFTHLAST endMarker
430                     {
431                         $$ = WK_L5;
432                         if ($2) RE_SET_FLAG($$);
433                     }
434                 ;
435
436 endDate : /* empty */
437                     {
438                         $$ = '\0';
439                     }
440         | DATE
441                     {
442                         if (_csa_iso8601_to_tick($1, &$$) == -1)
443                                 $$ = '\0';
444                     }
445                 ;
446
447                 /* e.g. MO TU WE */
448 weekday         : SUNDAY endMarker
449                     {
450                         $$ = WD_SUN;
451                         if ($2) RE_SET_FLAG($$);
452                     }
453                 | MONDAY endMarker
454                     {
455                         $$ = WD_MON;
456                         if ($2) RE_SET_FLAG($$);
457                     }
458                 | TUESDAY endMarker
459                     {
460                         $$ = WD_TUE;
461                         if ($2) RE_SET_FLAG($$);
462                     }
463                 | WEDNESDAY endMarker
464                     {
465                         $$ = WD_WED;
466                         if ($2) RE_SET_FLAG($$);
467                     }
468                 | THURSDAY endMarker
469                     {
470                         $$ = WD_THU;
471                         if ($2) RE_SET_FLAG($$);
472                     }
473                 | FRIDAY endMarker
474                     {
475                         $$ = WD_FRI;
476                         if ($2) RE_SET_FLAG($$);
477                     }
478                 | SATURDAY endMarker
479                     {
480                         $$ = WD_SAT;
481                         if ($2) RE_SET_FLAG($$);
482                     }
483                 ;
484
485                 /* e.g. 0100 1200 1300 or NULL */
486 time0List       : /* empty */
487                    {
488                         $$ = NULL;
489                    }
490                 | time0List time
491                    {
492                         NumberList      *nl;
493
494                         nl = AllocNumber($2);
495
496                         if ($1 == NULL) {
497                                 $$ = nl;
498                         } else {
499                                 nl->nl_next = $$->nl_next;
500                                 $$->nl_next = nl;
501                         }
502                    }
503                 ;
504
505                 /* e.g. 1000 */
506 time            : NUMBER endMarker
507                     {
508                         if ($2)
509                                 RE_SET_FLAG($1);
510                         $$ = $1;
511                     }
512
513                 ;
514
515                 /* e.g. $ */
516 endMarker       : /* empty */
517                     {
518                         $$ = FALSE;
519                     }
520                 | ENDMARKER
521                     {
522                         $$ = TRUE;
523                     }
524                 ;
525
526                 /* e.g. #10 */
527 duration        : /* empty */
528                     {
529                         /* If no duration then default to 2 - set later if 
530                          * end date not used.
531                          */
532                         $$ = RE_NOTSET;
533                     }
534                 | DURATION NUMBER
535                     {
536                         /* If duration == 0 then repeat forever */
537                         if ($2 == 0)
538                                 $$ = RE_INFINITY; 
539                         else
540                                 $$ = $2;
541                     }
542                 ;
543
544 timeList        : genericNumberList
545 dayOfMonthList  : generic0NumberList
546 monthOfYearList : generic0NumberList
547 dayOfYearList   : generic0NumberList
548
549 generic0NumberList: /* empty */
550                     {
551                         $$ = NULL;
552                     }
553                 | genericNumberList
554                     {
555                         $$ = $1;
556                     }
557
558 genericNumberList
559                 : NUMBER endMarker
560                     {
561                         if ($2)
562                                 RE_SET_FLAG($1);
563
564                         $$ = AllocNumber($1);
565                     }
566                 | LASTDAY endMarker
567                     {
568                         int     number = RE_LASTDAY;
569
570                         if ($2)
571                                 RE_SET_FLAG(number);
572
573                         $$ = AllocNumber(number);
574                     }
575                 | genericNumberList NUMBER endMarker
576                     {
577                         NumberList      *nl;
578
579                         if ($3)
580                                 RE_SET_FLAG($2);
581
582                         nl = AllocNumber($2);
583
584                         if ($1 == NULL) {
585                                 $$ = nl;
586                         } else {
587                                 nl->nl_next = $$->nl_next;
588                                 $$->nl_next = nl;
589                         }
590                     }
591                 | genericNumberList LASTDAY endMarker
592                     {
593                         NumberList      *nl;
594                         int              number = RE_LASTDAY;
595
596                         if ($3)
597                                 RE_SET_FLAG(number);
598
599                         nl = AllocNumber(number);
600
601                         if ($1 == NULL) {
602                                 $$ = nl;
603                         } else {
604                                 nl->nl_next = $$->nl_next;
605                                 $$->nl_next = nl;
606                         }
607                     }
608                 ;
609
610 %%
611
612 /*
613  * Convert the NumberList (linked list) to an array, sort the array.
614  */
615 static unsigned int *
616 ConvertNumList(
617         NumberList      *nl,
618         unsigned int    *count)
619 {
620         NumberList      *nl_head = nl;
621         unsigned int    *array;
622         int              i = 0;
623
624         if (!nl) return (unsigned int *)NULL;
625
626         while (nl) {
627                 i++;
628                 nl = nl->nl_next;
629         }
630
631         array = (unsigned int *) calloc(i, sizeof(unsigned int));
632         i = 0;
633                         /* Convert the list into an array */
634         nl = nl_head;
635         while (nl) {
636                 NumberList      *nl_prev;
637
638                 array[i] = nl->nl_number;
639                 i++;
640
641                 nl_prev = nl;
642                 nl = nl->nl_next;
643
644                 free(nl_prev);
645         }
646
647         *count = i;
648
649                         /* Sort the array */
650         qsort((void *)array, i, sizeof(unsigned int), CompareNums);
651
652         return array;
653 }
654
655 /*
656  * Convert the DayTimeList to an array, sort the array.
657  */
658 static DayTime *
659 ConvertDayTime(
660         DayTimeList     *dtl,
661         unsigned int    *count)
662 {
663         DayTimeList     *dtl_head = dtl;
664         DayTime         *daytime_array;
665         int              i = 0,
666                          no_time = -1;
667
668         if (!dtl) return (DayTime *)NULL;
669
670         while (dtl) {
671                 i++;
672                 dtl = dtl->dtl_next;
673         }
674
675         daytime_array = (DayTime *) calloc(i, sizeof(DayTime));
676         i = 0;
677                         /* Convert the list into an array */
678         dtl = dtl_head;
679         while (dtl) {
680                 DayTimeList     *dtl_prev;
681
682                 daytime_array[i].dt_day = dtl->dtl_daytime->dt_day;
683                 daytime_array[i].dt_ntime = dtl->dtl_daytime->dt_ntime;
684                 daytime_array[i].dt_time = dtl->dtl_daytime->dt_time;
685                 i++;
686
687                 dtl_prev = dtl;
688                 dtl = dtl->dtl_next;
689                         /* alloc'ed in <weekdayTimeList> */
690                 free(dtl_prev);
691         }
692
693         *count = i;
694
695         for (i = 0; i < *count; i++) {
696                 if (daytime_array[i].dt_time == NULL) {
697                         if (no_time == -1)
698                                 no_time = i;
699                 } else {
700                         if (no_time != -1) {
701                                 int j;
702
703                                 for (j = no_time; j < i; j++) {
704                                         daytime_array[j].dt_ntime = 
705                                                 daytime_array[i].dt_ntime;
706                                         daytime_array[j].dt_time = 
707                                                 (Time *)calloc(
708                                                      daytime_array[j].dt_ntime,
709                                                      sizeof(Time));
710                                         memcpy(daytime_array[j].dt_time,
711                                                daytime_array[i].dt_time,
712                                                daytime_array[j].dt_ntime *
713                                                                 sizeof(Time));
714                                 }
715                                 no_time = -1;
716                         }
717                 }
718         }
719
720                         /* Sort the array */
721         qsort((void *)daytime_array, *count, sizeof(DayTime), CompareDayTime);
722
723         return daytime_array;
724 }
725
726 /*
727  * Used by qsort()
728  */
729 static int
730 CompareNums(
731         const void      *data1,
732         const void      *data2)
733 {
734         const unsigned int      *i = (const unsigned int *)data1;
735         const unsigned int      *j = (const unsigned int *)data2;
736
737         if ((unsigned )RE_MASK_STOP(*i) > (unsigned )RE_MASK_STOP(*j))
738                 return(1);
739         if ((unsigned )RE_MASK_STOP(*i) < (unsigned )RE_MASK_STOP(*j))
740                 return(-1);
741         return (0);
742 }
743
744 /*
745  * Used by qsort()
746  */
747 static int
748 CompareDayTime(
749         const void      *data1,
750         const void      *data2)
751 {
752         const DayTime   *i = (const DayTime *)data1;
753         const DayTime   *j = (const DayTime *)data2;
754
755         if ((unsigned )RE_MASK_STOP(i->dt_day) >
756                                         (unsigned )RE_MASK_STOP(j->dt_day))
757                 return(1);
758         if ((unsigned )RE_MASK_STOP(i->dt_day) <
759                                         (unsigned )RE_MASK_STOP(j->dt_day))
760                 return(-1);
761         return (0);
762 }
763
764 /*
765  * Used by qsort()
766  */
767 static int
768 CompareWeekDayTime(
769         const void      *data1,
770         const void      *data2)
771 {
772         const WeekDayTime       *i = (const WeekDayTime *)data1;
773         const WeekDayTime       *j = (const WeekDayTime *)data2;
774
775         if ((unsigned )RE_MASK_STOP(i->wdt_week[0]) >
776                                         (unsigned )RE_MASK_STOP(j->wdt_week[0]))
777                 return(1);
778         if ((unsigned )RE_MASK_STOP(i->wdt_week[0]) <
779                                         (unsigned )RE_MASK_STOP(j->wdt_week[0]))
780                 return(-1);
781         return (0);
782 }
783
784 static NumberList *
785 AllocNumber(
786         unsigned int num)
787 {
788         NumberList      *nl;
789
790         nl = (NumberList *)calloc(1, sizeof(NumberList));
791
792         nl->nl_number = num;
793         nl->nl_next = NULL;
794
795         return nl;
796 }
797
798 /*
799  * Given three NumberLists convert them into arrays and return a WeekDayTime.
800  */
801 static WeekDayTimeList *
802 AllocWeekDayTimeList(
803         NumberList      *week_list,
804         NumberList      *day_list,
805         NumberList      *time_list)
806 {
807         WeekDayTime     *wdt;
808         WeekDayTimeList *wdtl;
809
810         wdt = (WeekDayTime *)calloc(1, sizeof(WeekDayTime));
811         wdtl = (WeekDayTimeList *)calloc(1, sizeof(WeekDayTimeList));
812
813         wdt->wdt_week =
814                 (WeekNumber *)ConvertNumList(week_list, &(wdt->wdt_nweek));
815         wdt->wdt_day =
816                 (WeekDay *)ConvertNumList(day_list, &(wdt->wdt_nday));
817         wdt->wdt_time =
818                 (Time *)ConvertNumList(time_list, &(wdt->wdt_ntime));
819         wdtl->wdtl_weektime = wdt;
820
821         return wdtl;
822 }
823
824 /*
825  * Convert the DayTimeList to an array, sort the array.
826  */
827 static WeekDayTime *
828 ConvertWeekDayTime(
829         WeekDayTimeList *wdtl,
830         unsigned int    *count)
831 {
832         WeekDayTimeList *wdtl_head = wdtl;
833         WeekDayTime     *array;
834         int              i = 0;
835
836         if (!wdtl) return (WeekDayTime *)NULL;
837
838         while (wdtl) {
839                 i++;
840                 wdtl = wdtl->wdtl_next;
841         }
842
843         array = (WeekDayTime *) calloc(i, sizeof(WeekDayTime));
844         i = 0;
845                         /* Convert the list into an array */
846         wdtl = wdtl_head;
847         while (wdtl) {
848                 WeekDayTimeList *wdtl_prev;
849
850                 array[i].wdt_day = wdtl->wdtl_weektime->wdt_day;
851                 array[i].wdt_nday = wdtl->wdtl_weektime->wdt_nday;
852                 array[i].wdt_time = wdtl->wdtl_weektime->wdt_time;
853                 array[i].wdt_ntime = wdtl->wdtl_weektime->wdt_ntime;
854                 array[i].wdt_week = wdtl->wdtl_weektime->wdt_week;
855                 array[i].wdt_nweek = wdtl->wdtl_weektime->wdt_nweek;
856                 i++;
857
858                 wdtl_prev = wdtl;
859                 wdtl = wdtl->wdtl_next;
860
861                 free(wdtl_prev);
862         }
863
864         *count = i;
865
866                         /* Sort the array */
867         qsort((void *)array, i, sizeof(WeekDayTime), CompareWeekDayTime);
868
869         return array;
870 }
871
872 static RepeatEvent *
873 HandleEndDate(
874         RepeatEvent *re,
875         time_t       enddate)
876 {
877         if (re) {
878                 if (enddate) {
879                         re->re_end_date = enddate;
880                 } else if (re->re_duration == RE_NOTSET) {
881                         re->re_duration = 2;
882                 }
883         }
884         return re;
885 }
886
887 /*
888  * Create a RepeatEvent for the minute portion of a rule.
889  */
890 static RepeatEvent *
891 DeriveMinuteEvent(
892         unsigned int     interval,
893         unsigned int     duration)
894 {
895         RepeatEvent     *re;
896
897         if (interval < 1)
898                 return NULL;
899
900         re = (RepeatEvent *)calloc(1, sizeof(RepeatEvent));
901                                 
902         re->re_interval = interval;
903         re->re_duration = duration;
904         re->re_type = RT_MINUTE;
905
906         return re;
907 }
908
909 /*
910  * Create a RepeatEvent for the daily portion of a rule.
911  */
912 static RepeatEvent *
913 DeriveDailyEvent(
914         unsigned int     interval,
915         NumberList      *time_list,
916         unsigned int     duration,
917         RepeatEvent     *other_event)
918 {
919         RepeatEvent     *re;
920         DailyData       *dd;
921         NumberList      *nl;
922         int              i = 0;
923
924         dd = (DailyData *)calloc(1, sizeof(DailyData));
925
926                 /* Convert from list to array, sort */
927         dd->dd_time = (Time *)ConvertNumList(time_list, &(dd->dd_ntime));
928
929         if (interval < 1)
930                 return NULL;
931
932         re = (RepeatEvent *)calloc(1, sizeof(RepeatEvent));
933                                 
934         re->re_interval = interval;
935         re->re_duration = duration;
936         re->re_type = RT_DAILY;
937         re->re_data.re_daily = dd;
938
939                 /* If there is a minuteEvent, tack it on */
940         if (other_event) {
941                 re->re_next = other_event;
942                 other_event->re_prev = re;
943         }
944
945         return re;
946 }
947
948 /*
949  * Create a RepeatEvent for the weekly portion of a rule.
950  */
951 static RepeatEvent *
952 DeriveWeeklyEvent(
953         unsigned int     interval,
954         DayTimeList     *dtl,
955         unsigned int     duration,
956         RepeatEvent     *other_event)
957 {
958         RepeatEvent     *re;
959         WeeklyData      *wd;
960
961         wd = (WeeklyData *)calloc(1, sizeof(WeeklyData));
962
963                 /* Convert from list to array, sort */
964         wd->wd_daytime = ConvertDayTime(dtl, &(wd->wd_ndaytime));
965
966         if (interval < 1)
967                 return NULL;
968
969         re = (RepeatEvent *)calloc(1, sizeof(RepeatEvent));
970                                 
971         re->re_interval = interval;
972         re->re_duration = duration;
973         re->re_type = RT_WEEKLY;
974         re->re_data.re_weekly = wd;
975
976         if (other_event) {
977                 re->re_next = other_event;
978                 other_event->re_prev = re;
979         }
980
981         return re;
982 }
983
984 /*
985  * Create a RepeatEvent for the monthly portion of a rule.
986  */
987 static RepeatEvent *
988 DeriveMonthlyEvent(
989         RepeatType       type,
990         unsigned int     interval,
991         void            *data_list,
992         unsigned int     duration,
993         RepeatEvent     *other_event)
994 {
995         RepeatEvent     *re;
996         MonthlyData     *md;
997
998         md = (MonthlyData *)calloc(1, sizeof(MonthlyData));
999
1000                         /* Convert from list to array, sort */
1001         if (type == RT_MONTHLY_POSITION) {
1002                 md->md_weektime = ConvertWeekDayTime(
1003                         (WeekDayTimeList *)data_list, &(md->md_nitems));
1004         } else {
1005                 md->md_days = ConvertNumList(
1006                         (NumberList *)data_list, &(md->md_nitems));
1007         }
1008
1009         if (interval < 1)
1010                 return NULL;
1011
1012         re = (RepeatEvent *)calloc(1, sizeof(RepeatEvent));
1013                                 
1014         re->re_interval = interval;
1015         re->re_duration = duration;
1016         re->re_type = type;
1017         re->re_data.re_monthly = md;
1018
1019                 /* If there is an another event, tack it on */
1020         if (other_event) {
1021                 re->re_next = other_event;
1022                 other_event->re_prev = re;
1023         }
1024
1025         return re;
1026 }
1027
1028 /*
1029  * Create a RepeatEvent for the yearly portion of a rule.
1030  */
1031 static RepeatEvent *
1032 DeriveYearlyEvent(
1033         RepeatType       type,
1034         unsigned int     interval,
1035         NumberList      *nl,
1036         unsigned int     duration,
1037         RepeatEvent     *other_event)
1038 {
1039         RepeatEvent     *re;
1040         YearlyData      *yd;
1041
1042         yd = (YearlyData *)calloc(1, sizeof(YearlyData));
1043
1044                         /* Convert from list to array, sort */
1045         yd->yd_items = ConvertNumList(nl, &(yd->yd_nitems));
1046
1047         if (interval < 1)
1048                 return NULL;
1049
1050         re = (RepeatEvent *)calloc(1, sizeof(RepeatEvent));
1051                                 
1052         re->re_interval = interval;
1053         re->re_duration = duration;
1054         re->re_type = type;
1055         re->re_data.re_yearly = yd;
1056
1057                 /* If there is an another event, tack it on */
1058         if (other_event) {
1059                 re->re_next = other_event;
1060                 other_event->re_prev = re;
1061         }
1062
1063         return re;
1064 }
1065
1066 void
1067 yyerror(
1068         char    *str)
1069 {
1070         /* Don't do anything */
1071 }