1 
2 /*----------------------------------------------------------------------------+
3  |                                                                            |
4  |      Enhanced HEYU Functionality for Uploaded Timers and Macros            |
5  |        Copyright 2002,2003,2004,2005,2006 Charles W. Sullivan              |
6  |                                                                            |
7  |                                                                            |
8  | As used herein, HEYU is a trademark of Daniel B. Suthers.                  |
9  | X10, CM11A, and ActiveHome are trademarks of X-10 (USA) Inc.               |
10  | The author is not affiliated with either entity.                           |
11  |                                                                            |
12  | Charles W. Sullivan                                                        |
13  | Co-author and Maintainer                                                   |
14  | Greensboro, North Carolina                                                 |
15  | Email ID: cwsulliv01                                                       |
16  | Email domain: -at- heyu -dot- org                                          |
17  |                                                                            |
18  +----------------------------------------------------------------------------*/
19 
20 /*
21  *   This program is free software: you can redistribute it and/or modify
22  *   it under the terms of the GNU General Public License as published by
23  *   the Free Software Foundation, either version 3 of the License, or
24  *   (at your option) any later version.
25  *
26  *   This program is distributed in the hope that it will be useful,
27  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
28  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  *   GNU General Public License for more details.
30  *
31  *   You should have received a copy of the GNU General Public License
32  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
33  *
34  */
35 
36 #include <ctype.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #if defined(SYSV) || defined(FREEBSD) || defined(OPENBSD)
41 #include <string.h>
42 #else
43 #include <strings.h>
44 #endif
45 #include <time.h>
46 #include <errno.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <unistd.h>
50 
51 #include "x10.h"
52 #include "sun.h"
53 #include "process.h"
54 #include "version.h"
55 #include "local.h"
56 
57 #define NDSTINTV 6
58 struct dststruct {
59   long  elapsed;   /* Elapsed minutes from 00:00 hours legal time Jan 1st */
60   int   offset;    /* Minutes to add to ST to get Legal Time  during each */
61                    /*  interval.                                          */
62 } lgls[NDSTINTV], dsts[NDSTINTV], stds[NDSTINTV], stdr[NDSTINTV];
63 
64 #if 0
65 #define NDSTINTV (int)(sizeof(lgls)/sizeof(struct dststruct))
66 #endif
67 
68 /* Contains info written to or read from the x10record file */
69 struct record_info x10record = {0, 0, 0, 0, 0, 0, 0, 0};
70 struct record_info *x10recordp = &x10record;
71 
72 #define PROMSIZE 1024
73 
74 /* Global variables */
75 
76 int    line_no;
77 int    timer_size     = 0;
78 int    timer_maxsize  = 0;
79 int    timer_savesize = 0;
80 int    tevent_size    = 0;
81 int    tevent_savesize = 0;
82 int    tevent_maxsize = 0;
83 int    current_timer_generation = 0;
84 int    save_timer_generation = 0;
85 int    timer_generation_delta = 0;
86 int    current_tevent_generation = 0;
87 int    save_tevent_generation = 0;
88 int    tevent_generation_delta = 0;
89 char   default_housecode = 'A';
90 
91 long int std_tzone;    /* Timezone in seconds West of Greenwich */
92 char   *heyu_tzname[2];
93 
94 /* Directory (terminated with /) containing the critical */
95 /* heyu files x10config, x10record, and x10macroxref     */
96 char   heyu_path[PATH_LEN + 1];
97 
98 char   schedfile[PATH_LEN + 1];
99 char   heyu_script[PATH_LEN + 1];
100 
101 /* State file */
102 extern char   statefile[];
103 
104 /* Alternate optional directory (specified in x10config) */
105 /* for report and non-critical files                     */
106 char   alt_path[PATH_LEN + 1];
107 
108 char   heyu_config[PATH_LEN + 1]; /* Filename of Heyu configuration file */
109 int    is_writable;              /* Flag: Heyu directory is writable */
110 
111 /* External variables */
112 
113 extern int verbose, i_am_relay;
114 extern CONFIG config;
115 extern CONFIG *configp;
116 
117 extern struct opt_st *optptr;
118 
119 char *wday_name[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
120 
121 char *month_name[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
122                       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
123 
124 /* Extended error management functions - these allow stringing together */
125 /* error messages from a tree of function calls into a (hopefully)      */
126 /* meaningful message for the user.                                     */
127 
128 /*----------------------------------------------------------------------------+
129  | Pass back pointer and size of error message buffer - for internal use by   |
130  | functions below.                                                           |
131  +----------------------------------------------------------------------------*/
error_area(char ** pointer,int * space)132 void error_area ( char **pointer, int *space )
133 {
134    static char error_buffer[1024];
135 
136    *pointer = error_buffer;
137    *space = (sizeof(error_buffer)/sizeof(char));
138    return;
139 }
140 
141 /*----------------------------------------------------------------------------+
142  | Save an error message in error message buffer                              |
143  +----------------------------------------------------------------------------*/
store_error_message(char * message)144 void store_error_message ( char *message )
145 {
146    char *buffp = NULL;
147    int  space;
148 
149    error_area(&buffp, &space);
150 
151    if ( space < (int)strlen(message) + 1 ) {
152       fprintf(stderr, "Internal error: no space - store_error_message()\n");
153       exit(1);
154    }
155    strncpy2(buffp, message, space - 1);
156    return;
157 }
158 
159 /*----------------------------------------------------------------------------+
160  | Prepend a message to an existing error message in buffer                   |
161  +----------------------------------------------------------------------------*/
add_error_prefix(char * prefix)162 void add_error_prefix ( char *prefix )
163 {
164    char        *buffp = NULL;
165    char        *oldmsg = NULL;
166    int         space;
167 
168    error_area(&buffp, &space);
169 
170    if ( space < ((int)strlen(prefix) + (int)strlen(buffp) + 1) ) {
171       fprintf(stderr, "Internal error: no space - add_error_prefix()\n");
172       exit(1);
173    }
174    if ( (oldmsg = strdup(buffp)) == NULL ) {
175       fprintf(stderr, "Unable to allocate memory for error message\n");
176       exit(1);
177    }
178    strncpy2(buffp, prefix, space - 1);
179    strncat(buffp, oldmsg, space - 1 - strlen(buffp));
180    free(oldmsg);
181 
182    return;
183 }
184 
185 /*----------------------------------------------------------------------------+
186  | Append a message to an existing error message in buffer                    |
187  +----------------------------------------------------------------------------*/
add_error_suffix(char * suffix)188 void add_error_suffix ( char *suffix )
189 {
190    char  *buffp = NULL;
191    int   space;
192 
193    error_area(&buffp, &space);
194 
195    if ( space < ((int)strlen(buffp) + (int)strlen(suffix) + 1) ) {
196       fprintf(stderr, "Internal error: no space - add_error_suffix()\n");
197       exit(1);
198    }
199 
200    strncat(buffp, suffix, space - 1 - strlen(buffp));
201 
202    return;
203 }
204 
205 /*----------------------------------------------------------------------------+
206  | Get pointer to error message buffer                                        |
207  +----------------------------------------------------------------------------*/
error_message(void)208 char *error_message ( void )
209 {
210    char *buffp = NULL;
211    int  space;
212 
213    error_area(&buffp, &space);
214 
215    return buffp;
216 }
217 
218 /*----------------------------------------------------------------------------+
219  | Clear the error message buffer                                             |
220  +----------------------------------------------------------------------------*/
clear_error_message(void)221 void clear_error_message( void )
222 {
223    char *buffp = NULL;
224    int  space;
225 
226    error_area(&buffp, &space);
227 
228    *buffp = '\0';
229    return;
230 }
231 
232 /* Debugging functions */
tp(int point)233 void tp ( int point )
234 {
235    printf("testpoint %d\n", point);
236    fflush(stdout);
237    return ;
238 }
239 
show_timer_links(TIMER * timerp)240 int show_timer_links( TIMER *timerp )
241 {
242    int j;
243    for ( j = 0; j < timer_maxsize; j++ )
244       printf("%3d  %3d\n", j, timerp[j].link);
245    return 0;
246 }
247 
248 /*----------------------------------------------------------------------------+
249  | Display tokens                                                             |
250  +----------------------------------------------------------------------------*/
display_tokens(int tokc,char * tokv[])251 void display_tokens ( int tokc, char *tokv[] )
252 {
253    int j;
254 
255    for ( j = 0; j < tokc; j++ ) {
256       printf(" \"%s\"", tokv[j]);
257    }
258    printf("\n");
259    return;
260 }
261 
262 
263 /*----------------------------------------------------------------------------+
264  | Verify that the start and stop times in any timer are not the same.        |
265  | (If they are, CM11A will not launch the stop macro.)                       |
266  | Return the number of timers failing the test.                              |
267  +----------------------------------------------------------------------------*/
check_timer_start_stop(TIMER * timerp)268 int check_timer_start_stop ( TIMER *timerp )
269 {
270    int j, count;
271 
272    if ( !timerp )
273       return 0;
274 
275    j = 0;
276    count = 0;
277    while ( timerp[j].line_no > 0 ) {
278       if ( timerp[j].generation != current_timer_generation ||
279            timerp[j].flag_start == NO_EVENT ||
280            timerp[j].flag_stop  == NO_EVENT     ) {
281          j++;
282          continue;
283       }
284       if ( timerp[j].offset_stop == timerp[j].offset_start )
285          count++;
286       j++;
287    }
288    return count;
289 }
290 
291 /*----------------------------------------------------------------------------+
292  | Verify an unbroken chain of TEVENT links.                                  |
293  +----------------------------------------------------------------------------*/
verify_tevent_links(TEVENT * teventp)294 int verify_tevent_links ( TEVENT *teventp )
295 {
296    int  *linker, *linkee;
297    int  j, k, start, size, retcode;
298    static int sizeint = sizeof(int);
299 
300    if ( !teventp )
301       return 0;
302 
303    size = 0;
304    while ( teventp[size].line_no > 0 )
305       size++;
306 
307    linker = calloc( size, sizeint );
308    if ( linker == NULL ) {
309       (void)fprintf(stderr, "verify_tevent_links() - Unable to allocate memory 1\n");
310       return -1;
311    }
312    linkee = calloc( size, sizeint );
313    if ( linkee == NULL ) {
314       (void)fprintf(stderr, "verify_tevent_links() - Unable to allocate memory 2\n");
315       return -1;
316    }
317 
318    /* Find the start of the chain, i.e., the tevent not linked to by any other */
319    for ( j = 0; j < size; j++ ) {
320       linker[j] = 0;
321    }
322    for ( j = 0; j < size; j++ ) {
323       k = teventp[j].link;
324       if ( k > (size - 1) ) {
325          (void)fprintf(stderr,
326            "verify_tevent_links() - link %d out of bound %d at index %d\n", k, size - 1, j);
327          return -1;
328       }
329       if ( k >= 0 )
330          linker[k] = 1;
331    }
332    for ( start = 0; start < size; start++ ) {
333       if ( linker[start] == 0 )
334          break;
335    }
336 
337 
338    for ( j = 0; j < size; j++ ) {
339       linkee[j] = 0;
340       linker[j] = -1;
341    }
342 
343    k = start;
344    for ( j = 0; j < size; j++ ) {
345       k = teventp[k].link;
346       if ( k < 0 && j < (size - 1) ) {
347          (void)fprintf(stderr,
348            "verify_tevent_links() - premature end of %d length chain at index %d\n", size, j);
349          return -1;
350       }
351       if ( k > (size - 1) ) {
352          (void)fprintf(stderr,
353            "verify_tevent_links() - link %d out of bound %d at index %d\n", k, size - 1, j);
354          return -1;
355       }
356 
357       if ( k >= 0 ) {
358          linkee[k] += 1;
359          linker[k] = j;
360       }
361    }
362 
363    retcode = 0;
364    for ( k = 1; k < size; k++ ) {
365       if ( linkee[k] == 0 ) {
366          (void)fprintf(stderr, "teventp[linkee[%d]] is not in chain.\n", k);
367          retcode = 1;
368       }
369       if ( linkee[k] > 1 ) {
370          (void)fprintf(stderr, "teventp[linkee[%d]] is multiply linked.\n", k);
371          retcode = 1;
372       }
373       if ( linkee[k] == -1 ) {
374          (void)fprintf(stderr, "End of chain at index %d\n", linker[k]);
375          retcode = 1;
376       }
377    }
378 
379    free(linker);
380    free(linkee);
381 
382    return retcode;
383 }
384 
385 
386 /*----------------------------------------------------------------------------+
387  | Verify an unbroken chain of TIMER links.                                   |
388  +----------------------------------------------------------------------------*/
verify_timer_links(TIMER * timerp)389 int verify_timer_links ( TIMER *timerp )
390 {
391    int  *linker, *linkee;
392    int  j, k, start, size, retcode;
393    static int sizeint = sizeof(int);
394 
395    if ( !timerp )
396       return 0;
397 
398    size = 0;
399    while ( timerp[size].line_no > 0 )
400       size++;
401 
402    linker = calloc( size, sizeint );
403    if ( linker == NULL ) {
404       (void)fprintf(stderr, "verify_timer_links() - Unable to allocate memory 1\n");
405       return -1;
406    }
407    linkee = calloc( size, sizeint );
408    if ( linkee == NULL ) {
409       (void)fprintf(stderr, "verify_timer_links() - Unable to allocate memory 2\n");
410       return -1;
411    }
412 
413    /* Find the start of the chain, i.e., the timer not linked to by any other */
414    for ( j = 0; j < size; j++ ) {
415       linker[j] = 0;
416    }
417    for ( j = 0; j < size; j++ ) {
418       k = timerp[j].link;
419       if ( k > (size - 1) ) {
420          (void)fprintf(stderr,
421            "verify_timer_links() - link %d out of bound %d at index %d\n", k, size - 1, j);
422          return -1;
423       }
424       if ( k >= 0 )
425          linker[k] = 1;
426    }
427    for ( start = 0; start < size; start++ ) {
428       if ( linker[start] == 0 )
429          break;
430    }
431 
432 
433    for ( j = 0; j < size; j++ ) {
434       linkee[j] = 0;
435       linker[j] = -1;
436    }
437 
438 
439    for ( j = 0; j < size; j++ ) {
440       linkee[j] = 0;
441       linker[j] = -1;
442    }
443 
444    k = start;
445    for ( j = 0; j < size; j++ ) {
446       k = timerp[k].link;
447       if ( k < 0 && j < (size - 1) ) {
448          (void)fprintf(stderr,
449            "verify_timerp_links() - premature end of %d length chain at index %d\n", size, j);
450          return -1;
451       }
452       if ( k > (size - 1) ) {
453          (void)fprintf(stderr,
454            "verify_timerp_links() - link %d out of bound %d at index %d\n", k, size, j);
455          return -1;
456       }
457 
458       if ( k >= 0 ) {
459          linkee[k] += 1;
460          linker[k] = j;
461       }
462    }
463 
464    retcode = 0;
465    for ( k = 1; k < size; k++ ) {
466       if ( linkee[k] == 0 ) {
467          (void)fprintf(stderr, "timerp[linkee[%d]] is not in chain.\n", k);
468          retcode = 1;
469       }
470       if ( linkee[k] > 1 ) {
471          (void)fprintf(stderr, "timerp[linkee[%d]] is multiply linked.\n", k);
472          retcode = 1;
473       }
474       if ( linkee[k] == -1 ) {
475          (void)fprintf(stderr, "End of chain at index %d\n", linker[k]);
476          retcode = 1;
477       }
478    }
479 
480    free( linker );
481    free( linkee );
482 
483    return retcode;
484 }
485 
486 
487 /* String/array manipulation functions */
488 
489 /*----------------------------------------------------------------------------+
490  | Trim leading and trailing whitespace from argument string and return       |
491  | pointer to string. Argument string itself is modified.                     |
492  +----------------------------------------------------------------------------*/
strtrim(char * string)493 char *strtrim( char *string )
494 {
495    char *ss ;
496    char *sd ;
497 
498    ss = sd = string ;
499    /* Move pointer to first non-whitespace character */
500    while ( *ss == ' ' || *ss == '\t' || *ss == '\n' || *ss == '\r')
501       ss++ ;
502 
503    /* Close up leading whitespace */
504    while ( (*sd++ = *ss++) != '\0' )
505       ;
506 
507    /* Back up pointer to character before terminating NULL */
508    sd -= 2 ;
509    /* Replace trailing whitespace with NULLs */
510    while( sd >= string
511          && ( *sd == ' ' || *sd == '\t' || *sd == '\n' || *sd == '\r' ) )
512       *sd-- = '\0' ;
513 
514    return string ;
515 }
516 
517 /*----------------------------------------------------------------------------+
518  | Convert a string to lower case.  Return pointer to string.                 |
519  +----------------------------------------------------------------------------*/
strlower(char * string)520 char *strlower ( char *string )
521 {
522    char *sp = string ;
523 
524    while ( *sp ) {
525       *sp = tolower((int)(*sp));
526       sp++ ;
527    }
528    return string;
529 }
530 
531 /*----------------------------------------------------------------------------+
532  | Convert a string to Upper case.  Return pointer to string.                 |
533  +----------------------------------------------------------------------------*/
strupper(char * string)534 char *strupper ( char *string )
535 {
536    char *sp = string ;
537 
538    while ( *sp ) {
539       *sp = toupper((int)(*sp));
540       sp++ ;
541    }
542    return string;
543 }
544 
545 /*----------------------------------------------------------------------------+
546  | Copy n characters from source to target string and append a trailing null. |
547  | Return a pointer to the target.  (Length of target string must be n+1).    |
548  +----------------------------------------------------------------------------*/
strncpy2(char * target,char * source,int n)549 char *strncpy2 ( char *target, char *source, int n )
550 {
551    char *sp, *tp;
552    int  count;
553 
554    sp = source; tp = target; count = n;
555    while ( count-- > 0 && *sp ) {
556       *tp++ = *sp++ ;
557    }
558    *tp = '\0';
559 
560    return target;
561 }
562 
563 /*----------------------------------------------------------------------------+
564  | Convert high and low ulongs of a ulonglong to a double.                    |
565  +----------------------------------------------------------------------------*/
hilo2dbl(unsigned long high,unsigned long low)566 double hilo2dbl ( unsigned long high, unsigned long low )
567 {
568 #ifdef HASULL
569    unsigned long long ull;
570 
571    ull = (unsigned long long)high << 32 | (unsigned long long)low;
572    return (double)ull;
573 #else
574    return (double)high * 4294967296.0 + (double)low;
575 #endif
576 }
577 
578 
579 /*--------------------------------------------------------------------+
580  | Break up the string 'str' into tokens delimited by characters in   |
581  | 'delim' and create the list 'tokv' of pointers to these tokens.    |
582  | The original string is modified.  Free 'tokv' after use.           |
583  +--------------------------------------------------------------------*/
tokenize(char * str,char * delim,int * tokc,char *** tokv)584 int tokenize ( char *str, char *delim, int *tokc, char ***tokv )
585 {
586    char *sp, *tp;
587    static int sizchptr = sizeof(char *);
588 
589    *tokc = 0;
590    *tokv = NULL;
591    sp = str;
592    while ( *sp != '\0' ) {
593       /* Bypass leading delimiters */
594       while ( *sp != '\0' && strchr(delim, *sp) != NULL )
595          sp++;
596       if ( *sp == '\0' )
597          return 0;
598       tp = sp;
599       /* Advance to the next delimiter */
600       while ( *sp != '\0' && strchr(delim, *sp) == NULL )
601          sp++;
602       /* Terminate the token if not already at end of string */
603       if ( *sp != '\0' )
604          *sp++ = '\0';
605       /* Allocate space for the pointer */
606       if ( *tokc == 0 )
607          *tokv = calloc(1, sizchptr);
608       else
609          *tokv = realloc(*tokv, (*tokc + 1) * sizchptr);
610       if ( *tokv == NULL ) {
611          fprintf(stderr, "Unable to allocate memory in tokenize()\n");
612          exit(1);
613       }
614       (*tokv)[(*tokc)++] = tp;
615    }
616 
617    /* Add a terminating NULL */
618    if ( *tokc == 0 )
619       *tokv = calloc(1, sizchptr);
620    else
621       *tokv = realloc(*tokv, (*tokc + 1) * sizchptr);
622    if ( *tokv == NULL ) {
623       fprintf(stderr, "Unable to allocate memory in tokenize()\n");
624       exit(1);
625    }
626    (*tokv)[(*tokc)] = (char *)NULL;
627 
628    return 0;
629 }
630 
631 /*--------------------------------------------------------------------+
632  | Copy maxlen-1 characters to target of the token delimited by delim |
633  | from the char string *nextpp.  On return, *nextpp points to the    |
634  | character following the token (or the part copied thereof).        |
635  | The original string is unchanged.                                  |
636  +--------------------------------------------------------------------*/
get_token(char * target,char ** nextpp,char * delim,int maxlen)637 char *get_token( char *target, char **nextpp, char *delim, int maxlen )
638 {
639    char *sp, *tp;
640    int  count = 0;
641 
642    sp = *nextpp;
643    tp = target;
644 
645    /* Bypass leading delimiters */
646    while ( *sp && strchr(delim, *sp) ) {
647       sp++ ;
648    }
649 
650    /* Transfer up to maxlen-1 characters */
651    while ( *sp && !strchr(delim, *sp) && count < maxlen ) {
652       *tp++ = *sp++ ;
653       count++ ;
654    }
655 
656    *nextpp = sp;
657 
658    /* Terminate with a null character */
659    *tp = '\0';
660 
661    return target;
662 }
663 
664 /*---------------------------------------------------------------------+
665  | Left rotate the contents of an array.                               |
666  +---------------------------------------------------------------------*/
lrotarray(unsigned char * array,int length)667 void lrotarray ( unsigned char *array, int length )
668 {
669    unsigned char hold;
670    int           j;
671 
672    hold = array[0];
673    for ( j = 0; j < length - 1; j++ )
674       array[j] = array[j + 1];
675 
676    array[length - 1] = hold;
677 
678    return;
679 }
680 
681 /*---------------------------------------------------------------------+
682  | Right rotate the contents of an array.                              |
683  +---------------------------------------------------------------------*/
rrotarray(unsigned char * array,int length)684 void rrotarray ( unsigned char *array, int length )
685 {
686    unsigned char hold;
687    int           j;
688 
689    hold = array[length - 1];
690    for ( j = length - 1; j > 0; j-- )
691       array[j] = array[j - 1];
692 
693    array[0] = hold;
694 
695    return;
696 }
697 
698 
699 /* X10 Encoding/Decoding functions */
700 
701 /*---------------------------------------------------------------------+
702  | Convert housecode letter to x10 code.                               |
703  +---------------------------------------------------------------------*/
hc2code(char hc)704 unsigned char hc2code ( char hc )
705 {
706    /* X10 codes for housecode letters A through P */
707    static unsigned char code[] =
708                  {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
709 
710    return code[(toupper((int)hc) - 'A') & 0x0f];
711 }
712 
713 /*---------------------------------------------------------------------+
714  | Convert x10 code to housecode letter.                               |
715  +---------------------------------------------------------------------*/
code2hc(unsigned char code)716 char code2hc ( unsigned char code )
717 {
718    char *hcode = "MECKOGAINFDLPHBJ";
719 
720    return hcode[code & 0x0fu];
721 }
722 
723 /*---------------------------------------------------------------------+
724  | Convert X10 unit number (1-16) to x10 code.                         |
725  +---------------------------------------------------------------------*/
unit2code(int unit)726 unsigned char unit2code ( int unit )
727 {
728    /* X10 codes for unit 1 through 16 */
729    static unsigned char code[] =
730                  {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
731 
732    return (unit > 0 && unit < 17) ? code[unit - 1] : 0xff;
733 }
734 
735 /*---------------------------------------------------------------------+
736  | Convert X10 code to X10 unit number (1-16).                         |
737  +---------------------------------------------------------------------*/
code2unit(unsigned char code)738 int code2unit ( unsigned char code )
739 {
740    static int units[] = {13,5,3,11,15,7,1,9,14,6,4,12,16,8,2,10};
741 
742    return units[code & 0x0fu];
743 }
744 
745 /*---------------------------------------------------------------------+
746  | Convert bit position (0-15) in X10 bitmap to unit number.           |
747  +---------------------------------------------------------------------*/
bitpos2unit(int bitpos)748 int bitpos2unit ( int bitpos )
749 {
750    static int units[] = {13,5,3,11,15,7,1,9,14,6,4,12,16,8,2,10};
751 
752    return units[bitpos & 0x0f];
753 }
754 
755 /*---------------------------------------------------------------------+
756  | Return X10 bitmap for Days of Week string.                          |
757  +---------------------------------------------------------------------*/
dow2bmap(char * dow)758 unsigned char dow2bmap ( char *dow )
759 {
760    char *pattern = "smtwtfs";
761    unsigned char bmap = 0;
762    unsigned char mask = 0x01;
763    char buffer[16];
764    int  j;
765 
766    if ( strlen(dow) != 7 )
767       return 0xff;
768 
769    strncpy2(buffer, dow, sizeof(buffer) - 1);
770    strlower(buffer);
771 
772    for ( j = 0; j < 7; j++ ) {
773       if ( buffer[j] == pattern[j] ) {
774          bmap |= mask;
775          mask = mask << 1 ;
776       }
777       else if ( buffer[j] == '.' )
778          mask = mask << 1 ;
779       else
780          return 0xff ;
781    }
782    return bmap;
783 }
784 
785 /*---------------------------------------------------------+
786  | Return Days of Week string for X10 bitmap argument      |
787  +---------------------------------------------------------*/
bmap2dow(unsigned char bmap)788 char *bmap2dow( unsigned char bmap )
789 {
790    static char buff[10];
791    int    j;
792    char   *days = "smtwtfs";
793    char   *err  = "-error-";
794    unsigned char mask = 0x01;
795 
796    if ( bmap > 127 )
797       return err;
798 
799    for ( j = 0; j < 7; j++ ) {
800       buff[j] = (mask & bmap) ? days[j] : '.' ;
801       mask = mask << 1;
802    }
803    buff[7] = '\0';
804    return buff;
805 }
806 
807 /*---------------------------------------------------------+
808  | Return Linux tm_wday (Sun = 0) for X10 bitmap.          |
809  | (Assumes only one wday represented in the bitmap.)      |
810  +---------------------------------------------------------*/
bmap2wday(unsigned char bmap)811 int bmap2wday ( unsigned char bmap )
812 {
813    int j;
814 
815    j = 0;
816    while ( (bmap = (bmap >> 1)) != 0 )
817       j++;
818 
819    return j;
820 }
821 
822 /*---------------------------------------------------------+
823  | Return ASCII weekday name for X10 DOW bitmap.           |
824  | (Assumes only one wday represented in the bitmap.)      |
825  +---------------------------------------------------------*/
bmap2ascdow(unsigned char bmap)826 char *bmap2ascdow ( unsigned char bmap )
827 {
828    return wday_name[bmap2wday(bmap)];
829 }
830 
831 /*---------------------------------------------------------------------+
832  | Reverse the order of the lower 4 bits, e.g., 1 -> 8 and 8 -> 1      |
833  | while leaving the upper bits unchanged.                             |
834  +---------------------------------------------------------------------*/
rev_low_nybble(unsigned char input)835 unsigned char rev_low_nybble ( unsigned char input )
836 {
837    static unsigned char rev_table[] =
838      {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15};
839 
840    int  j;
841 
842    j = (int)(input & 0x0fu);
843 
844    return (input & 0xf0u) | rev_table[j];
845 }
846 
847 /*---------------------------------------------------------------------+
848  | Reverse the order of the bits in a byte.                            |
849  +---------------------------------------------------------------------*/
rev_byte_bits(unsigned char input)850 unsigned char rev_byte_bits ( unsigned char input )
851 {
852    int           j;
853    unsigned char output = 0;
854 
855    for ( j = 0; j < 8; j++ ) {
856       if ( input & (1 << j) )
857          output |= (0x80 >> j);
858    }
859    return output;
860 }
861 
862 /*---------------------------------------------------------------------+
863  | Rotate the x10 Day of Week bitmap one bit to the left.  This has    |
864  | the effect of moving the day of an event into the following day,    |
865  | e.g., bmap(".mt..fs") ==> bmap("s.tw..s")                           |
866  +---------------------------------------------------------------------*/
lrotbmap(unsigned char bmap)867 unsigned char lrotbmap ( unsigned char bmap )
868 {
869    return  ( ((bmap & 0x40u) >> 6) | ( (bmap & 0x3fu) << 1 ) );
870 }
871 
872 /*---------------------------------------------------------------------+
873  | Rotate the x10 Day of Week bitmap one bit to the right.  This has   |
874  | the effect of moving the day of an event into the preceding day,    |
875  | e.g., bmap(".mt..fs") ==> bmap("sm..tf.")                           |
876  +---------------------------------------------------------------------*/
rrotbmap(unsigned char bmap)877 unsigned char rrotbmap ( unsigned char bmap )
878 {
879    return  ( ((bmap & 0x01u) << 6) | ( (bmap & 0x7fu) >> 1 ) );
880 }
881 
882 /*---------------------------------------------------------------------+
883  | Return the code for a unit in a bitmap which is presumed to contain |
884  | only a single unit or 0.  If the bitmap contains more than a single |
885  | unit, an error value of 0xff is returned.                           |
886  +---------------------------------------------------------------------*/
single_bmap_unit(unsigned int bitmap)887 unsigned char single_bmap_unit( unsigned int bitmap )
888 {
889    unsigned int   invmap[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
890    unsigned int   mask = 1;
891    unsigned char  units[17], nunits;
892    int            j;
893 
894    nunits = 0; units[0] = 0;
895    for ( j = 0; j < 16; j++ ) {
896       mask = 0x01 << invmap[j] ;
897       if ( bitmap & mask ) {
898          units[nunits++] = invmap[j];
899       }
900    }
901 
902    return (nunits <= 1) ? units[0] : 0xff;
903 }
904 
905 
906 /*---------------------------------------------------------------------+
907  | Parse the units list and return an X10 unit bitmap containing only  |
908  | the first unit in the units list, with unit 0 acceptable.           |
909  +---------------------------------------------------------------------*/
units2single(char * str)910 unsigned int units2single ( char *str )
911 {
912    static int   umap[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
913    int          unit;
914    char         buffer[256];
915    char         errmsg[64];
916    char         *tail, *sp;
917 
918    tail = str;
919    if ( *get_token(buffer, &tail, "-,", 255) == '\0' )
920       return 0;
921 
922    unit = (int)strtol(buffer, &sp, 10);
923    if ( *sp ) {
924       sprintf(errmsg,
925          "Warning: Invalid character '%c' in units list (ignored).\n", *sp);
926       store_error_message(errmsg);
927       return 0;
928    }
929    if ( unit < 0 || unit > 16 ) {
930       sprintf(errmsg, "Warning: Unit number %d outside range 0-16 (ignored).\n", unit);
931       store_error_message(errmsg);
932       return 0;
933    }
934 
935    if ( unit == 0 )
936       return 0;
937 
938    return ( 1 << umap[unit - 1] );
939 }
940 
941 /*---------------------------------------------------------------------+
942  | Parse the flags list and return a long bitmap, with bit 0 = flag 1, |
943  | bit 1 = flag 2, etc.                                                |
944  +---------------------------------------------------------------------*/
flags2longmap(char * str)945 unsigned long flags2longmap ( char *str )
946 {
947 
948    char          buffer[256];
949    char          errmsg[80];
950    char          *tail, *sp;
951    int           flagmax = 32;
952 
953    int           flist[32];
954    int           j, ustart, flag;
955    unsigned long longmap;
956 
957    if ( strchr(str, '*') ) {
958       longmap = 0xffffffff;
959       return longmap;
960    }
961 
962    for ( j = 0; j < 32; j++ )
963       flist[j] = 0;
964 
965    ustart = 0; tail = str;
966    while ( *(get_token(buffer, &tail, "-,", 255)) ) {
967       flag = (int)strtol(buffer, &sp, 10);
968       if ( *sp ) {
969          sprintf(errmsg, "Invalid char '%c' in flags list.", *sp);
970          store_error_message(errmsg);
971          return 0;
972       }
973       if ( flag < 1 || flag > flagmax ) {
974          sprintf(errmsg, "Flag number %d outside range 1-%d.", flag, flagmax);
975          store_error_message(errmsg);
976          return 0;
977       }
978 
979       if ( *tail == ',' || *tail == '\0' ) {
980          if ( ustart ) {
981             for ( j = ustart; j <= flag; j++ )
982                flist[j-1] = 1;
983             ustart = 0;
984             continue;
985          }
986          else {
987             flist[flag-1] = 1;
988             continue;
989          }
990       }
991       else {
992          ustart = flag;
993       }
994    }
995 
996    longmap = 0;
997    for ( j = 0; j < 32; j++ )
998       longmap |= (flist[j]) << j;
999 
1000    return longmap;
1001 }
1002 
1003 
1004 
1005 /*---------------------------------------------------------------------+
1006  | Parse the flags list and return a bitmap, with bit 0 = flag 1,      |
1007  | bit 1 = flag 2, etc.                                                |
1008  +---------------------------------------------------------------------*/
flags2bmap(char * str)1009 unsigned int flags2bmap ( char *str )
1010 {
1011 
1012    char         buffer[256];
1013    char         errmsg[80];
1014    char         *tail, *sp;
1015 
1016    int          flist[16];
1017    int          j, ustart, flag;
1018    unsigned int bmap;
1019 
1020    if ( strchr(str, '*') ) {
1021       bmap = 0xffff;
1022       return bmap;
1023    }
1024 
1025    for ( j = 0; j < 16; j++ )
1026       flist[j] = 0;
1027 
1028    ustart = 0; tail = str;
1029    while ( *(get_token(buffer, &tail, "-,", 255)) ) {
1030       flag = (int)strtol(buffer, &sp, 10);
1031       if ( *sp ) {
1032          sprintf(errmsg, "Invalid char '%c' in flags list.", *sp);
1033          store_error_message(errmsg);
1034          return 0;
1035       }
1036       if ( flag < 1 || flag > 16 ) {
1037          sprintf(errmsg, "Flag number %d outside range 1-16.", flag);
1038          store_error_message(errmsg);
1039          return 0;
1040       }
1041 
1042       if ( *tail == ',' || *tail == '\0' ) {
1043          if ( ustart ) {
1044             for ( j = ustart; j <= flag; j++ )
1045                flist[j-1] = 1;
1046             ustart = 0;
1047             continue;
1048          }
1049          else {
1050             flist[flag-1] = 1;
1051             continue;
1052          }
1053       }
1054       else {
1055          ustart = flag;
1056       }
1057    }
1058 
1059    bmap = 0;
1060    for ( j = 0; j < 16; j++ )
1061       bmap |= (flist[j]) << j;
1062 
1063    return bmap;
1064 }
1065 
1066 /*---------------------------------------------------------------------+
1067  | Parse the units list and return the X10 unit bitmap.                |
1068  +---------------------------------------------------------------------*/
units2bmap(char * str)1069 unsigned int units2bmap ( char *str )
1070 {
1071 
1072    static int   umap[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
1073    char         buffer[256];
1074    char         errmsg[80];
1075    char         *tail, *sp;
1076 
1077    int          ulist[16];
1078    int          j, ustart, unit;
1079    unsigned int bmap;
1080 
1081    for ( j = 0; j < 16; j++ )
1082       ulist[j] = 0;
1083 
1084    ustart = 0; tail = str;
1085    while ( *(get_token(buffer, &tail, "-,", 255)) ) {
1086       unit = (int)strtol(buffer, &sp, 10);
1087       if ( *sp ) {
1088          sprintf(errmsg, "Invalid char '%c' in units list.", *sp);
1089          store_error_message(errmsg);
1090          return 0;
1091       }
1092       if ( unit < 1 || unit > 16 ) {
1093          sprintf(errmsg, "Unit number %d outside range 1-16.", unit);
1094          store_error_message(errmsg);
1095          return 0;
1096       }
1097 
1098       if ( *tail == ',' || *tail == '\0' ) {
1099          if ( ustart ) {
1100             for ( j = ustart; j <= unit; j++ )
1101                ulist[j-1] = 1;
1102             ustart = 0;
1103             continue;
1104          }
1105          else {
1106             ulist[unit-1] = 1;
1107             continue;
1108          }
1109       }
1110       else {
1111          ustart = unit;
1112       }
1113    }
1114 
1115    bmap = 0;
1116    for ( j = 0; j < 16; j++ )
1117       bmap |= (ulist[j]) << umap[j];
1118 
1119    return bmap;
1120 }
1121 
1122 /*---------------------------------------------------------------------+
1123  | Return string listing X10 units for X10 bitmap argument.            |
1124  +---------------------------------------------------------------------*/
bmap2units(unsigned int bmap)1125 char *bmap2units ( unsigned int bmap )
1126 {
1127    static int   invmap[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
1128    static char  buffer[128];
1129    char         minbuf[8];
1130    unsigned int mask = 0x01;
1131    int          j, count, nunits;
1132    int          units[17];
1133    int          first;
1134 
1135    if ( bmap == 0 ) {
1136       (void)strncpy2(buffer, "0", sizeof(buffer) - 1);
1137       return buffer;
1138    }
1139 
1140    nunits = 0;
1141    for ( j = 0; j < 16; j++ ) {
1142       mask = 0x01 << invmap[j] ;
1143       if ( bmap & mask ) {
1144          units[nunits++] = j + 1;
1145       }
1146    }
1147    units[nunits++] = 0;
1148 
1149    buffer[0] = buffer[1] = '\0';
1150    first = units[0];
1151    count = 1;
1152    for (j=1; j < nunits; j++) {
1153       if ( units[j] == units[j-1] + 1) {
1154          count++;
1155          continue;
1156       }
1157 
1158       switch ( count ) {
1159          case 1 :
1160             sprintf(minbuf, ",%d", first);
1161             break;
1162          case 2 :
1163             sprintf(minbuf, ",%d,%d", first, units[j-1]);
1164             break;
1165          default :
1166             sprintf(minbuf, ",%d-%d", first, units[j-1]);
1167             break;
1168       }
1169 
1170       (void) strncat(buffer, minbuf, sizeof(buffer) - 1 - strlen(buffer));
1171       first = units[j];
1172       count = 1;
1173    }
1174 
1175    return buffer + 1;
1176 }
1177 
1178 /*---------------------------------------------------------------------+
1179  | Return a 16 character ASCII string displaying in descending order   |
1180  | an X10 unit bitmap, i.e., char[0] -> unit 16, char[15] -> unit 1.   |
1181  | The argument chrs is a two-character string, the 1st character of   |
1182  | represents 'unset' units and the 2nd character the 'set' bits.      |
1183  | Example: With chrs = "01", a bitmap for units 1,5,6                 |
1184  | (bitmap 0x0242) will be represented as "0000000000110001".          |
1185  +---------------------------------------------------------------------*/
bmap2rasc(unsigned int bitmap,char * chrs)1186 char *bmap2rasc ( unsigned int bitmap, char *chrs )
1187 {
1188    int j;
1189    static char outbuf[17];
1190 
1191    for ( j = 0; j < 16; j++ ) {
1192       if ( bitmap & (1 << j) )
1193          outbuf[16 - code2unit(j)] = chrs[1];
1194       else
1195          outbuf[16 - code2unit(j)] = chrs[0];
1196    }
1197    outbuf[16] = '\0';
1198    return outbuf;
1199 }
1200 
1201 /*---------------------------------------------------------------------+
1202  | Return a 16 character ASCII string displaying in ascending order    |
1203  | an X10 unit bitmap, i.e., char[0] -> unit 1, char[15] -> unit 16.   |
1204  | The argument chrs is a two-character string, the 1st character of   |
1205  | represents 'unset' units and the 2nd character the 'set' bits.      |
1206  | Example: With chrs = "01", a bitmap for units 1,5,6                 |
1207  | (bitmap 0x0242) will be represented as "1000110000000000".          |
1208  +---------------------------------------------------------------------*/
bmap2asc(unsigned int bitmap,char * chrs)1209 char *bmap2asc ( unsigned int bitmap, char *chrs )
1210 {
1211    int j;
1212    static char outbuf[17];
1213 
1214    for ( j = 0; j < 16; j++ ) {
1215       if ( bitmap & (1 << j) )
1216          outbuf[code2unit(j) - 1] = chrs[1];
1217       else
1218          outbuf[code2unit(j) - 1] = chrs[0];
1219    }
1220    outbuf[16] = '\0';
1221    return outbuf;
1222 }
1223 
1224 /*---------------------------------------------------------------------+
1225  | Parse the list of integers and return a linear bitmap.  The list    |
1226  | is comprised of comma-separated positive integers or ranges of      |
1227  | integers, e.g., 3,2,4-7,11  The integers are restricted to the      |
1228  | values minval through maxval (within the range 0-31).               |
1229  +---------------------------------------------------------------------*/
list2linmap(char * str,int minval,int maxval)1230 unsigned long list2linmap ( char *str, int minval, int maxval )
1231 {
1232 
1233    char          buffer[256];
1234    char          errmsg[80];
1235    char          *tail, *sp;
1236 
1237    int           ulist[32];
1238    int           j, ustart, unit, temp;
1239    unsigned long linmap;
1240 
1241    for ( j = 0; j < 32; j++ )
1242       ulist[j] = 0;
1243 
1244    ustart = -1; tail = str;
1245    while ( *(get_token(buffer, &tail, "-,", 255)) ) {
1246       unit = (int)strtol(buffer, &sp, 10);
1247       if ( *sp ) {
1248          sprintf(errmsg, "Invalid char '%c' in the list.", *sp);
1249          store_error_message(errmsg);
1250          return 0;
1251       }
1252       if ( unit < minval || unit > maxval ) {
1253          sprintf(errmsg, "outside range %d-%d", minval, maxval);
1254          store_error_message(errmsg);
1255          return 0;
1256       }
1257 
1258       if ( *tail == ',' || *tail == '\0' ) {
1259          if ( ustart >= 0 ) {
1260             if ( unit < ustart ) {
1261                temp = ustart;
1262                ustart = unit;
1263                unit = temp;
1264             }
1265             for ( j = ustart; j <= unit; j++ )
1266                ulist[j] = 1;
1267             ustart = 0;
1268             continue;
1269          }
1270          else {
1271             ulist[unit] = 1;
1272             continue;
1273          }
1274       }
1275       else {
1276          ustart = unit;
1277       }
1278    }
1279 
1280    linmap = 0;
1281    for ( j = 0; j < 32; j++ )
1282       linmap |= (ulist[j]) << j;
1283 
1284    return linmap;
1285 }
1286 
1287 /*---------------------------------------------------------------------+
1288  | Return a comma-separated list of integers and ranges of integers    |
1289  | represented by the linear bitmap argument, where bit0 -> 0, etc.    |
1290  +---------------------------------------------------------------------*/
linmap2list(unsigned long linmap)1291 char *linmap2list ( unsigned long linmap )
1292 {
1293    static char   buffer[128];
1294    char          minbuf[8];
1295    unsigned long mask = 1;
1296    int           j, count, nunits;
1297    int           units[32];
1298    int           first;
1299 
1300    buffer[0] = buffer[1] = '\0';
1301    if ( linmap == 0 ) {
1302       return buffer;
1303    }
1304 
1305    nunits = 0;
1306    for ( j = 0; j < 31; j++ ) {
1307       mask = 1 << j ;
1308       if ( linmap & mask ) {
1309          units[nunits++] = j;
1310       }
1311    }
1312    units[nunits++] = 0;
1313 
1314    first = units[0];
1315    count = 1;
1316    for (j = 1; j < nunits; j++) {
1317       if ( units[j] == units[j-1] + 1) {
1318          count++;
1319          continue;
1320       }
1321 
1322       switch ( count ) {
1323          case 1 :
1324             sprintf(minbuf, ",%d", first);
1325             break;
1326          case 2 :
1327             sprintf(minbuf, ",%d,%d", first, units[j-1]);
1328             break;
1329          default :
1330             sprintf(minbuf, ",%d-%d", first, units[j-1]);
1331             break;
1332       }
1333 
1334       (void) strncat(buffer, minbuf, sizeof(buffer) - 1 - strlen(buffer));
1335       first = units[j];
1336       count = 1;
1337    }
1338 
1339    return buffer + 1;
1340 }
1341 
1342 /* Date and Calendar functions */
1343 
1344 /*------------------------------------------------------------------+
1345  | Determine the user's standard timezone, defined here as the      |
1346  | offset in seconds of local Standard Time from GMT, with West of  |
1347  | Greenwich positive, and store in global variable std_tzone.      |
1348  | Some C libraries provide this directly as a global variable;     |
1349  | for others it must be determined from struct member tm_gmtoff,   |
1350  | which provides the offset of local Legal Time and has the        |
1351  | opposite sign.                                                   |
1352  +------------------------------------------------------------------*/
1353 #ifdef HASTZ
get_std_timezone(void)1354 void get_std_timezone ( void )
1355 {
1356    struct tm  *tmp;
1357    time_t     now;
1358 
1359    /* Fill in the tm structure for the current date */
1360    time(&now);
1361    tmp = localtime(&now);
1362 
1363    /* The library includes the global variable "timezone" */
1364 
1365    std_tzone = (long)timezone;
1366    return;
1367 }
1368 #else
get_std_timezone(void)1369 void get_std_timezone ( void )
1370 {
1371    struct tm  *tmp;
1372    time_t     now;
1373    long int   jan_off, jul_off;
1374 
1375    /* Fill in the tm structure for the current date */
1376    time(&now);
1377    tmp = localtime(&now);
1378 
1379    /* struct tm includes the element tm_gmtoff  */
1380 
1381    /* Get the GMT offset for January */
1382    tmp->tm_mon = 0;
1383    tmp->tm_mday = 1;
1384    tmp->tm_hour = 12;
1385    tmp->tm_min = tmp->tm_sec = 0;
1386    tmp->tm_isdst = -1;
1387    mktime(tmp);
1388 
1389    jan_off = tmp->tm_gmtoff;
1390 
1391    /* Get the GMT offset for July */
1392    tmp->tm_mon = 6;
1393    tmp->tm_mday = 1;
1394    tmp->tm_hour = 12;
1395    tmp->tm_min = tmp->tm_sec = 0;
1396    tmp->tm_isdst = -1;
1397    mktime(tmp);
1398 
1399    jul_off = tmp->tm_gmtoff;
1400 
1401    /* The lesser value corresponds to Standard Time.*/
1402    /* Change sign to make West of Greenwich positive */
1403 
1404    std_tzone = -min(jan_off, jul_off);
1405    return;
1406 }
1407 #endif   /* End of #ifdef */
1408 
1409 /*------------------------------------------------------------------+
1410  | For some places in the world, e.g., Australia, there's no        |
1411  | distinction in the current Linux timezone files between TZ names |
1412  | for Standard and Daylight time.  We gerry-rig that here, at      |
1413  | least for Heyu's purposes.                                       |
1414  +------------------------------------------------------------------*/
fix_tznames(void)1415 void fix_tznames ( void )
1416 {
1417    time_t      now;
1418    extern char *tzname[], *heyu_tzname[];
1419    static char std[16], dst[16];
1420 
1421    /* Get current date and time */
1422    time(&now) ;
1423    (void)localtime(&now);
1424 
1425    (void)strncpy2(std, tzname[0], sizeof(std) - 1);
1426    (void)strncpy2(dst, tzname[1], sizeof(dst) - 1);
1427    if ( strcmp(tzname[0], tzname[1]) == 0 )
1428       (void)strncat(dst, " (DST)", sizeof(dst) - 1 - strlen(dst));
1429 
1430    heyu_tzname[0] = std;
1431    heyu_tzname[1] = dst;
1432 
1433    return;
1434 }
1435 
1436 /*-----------------------------------------------------------------+
1437  | Return pointer to string containing "asif" date and time.       |
1438  +-----------------------------------------------------------------*/
asif_time_string(void)1439 char *asif_time_string ( void )
1440 {
1441    time_t      now;
1442    struct tm   *tms;
1443    static char buffer[32];
1444    extern char *heyu_tzname[];
1445 
1446    fix_tznames();
1447 
1448    time(&now);
1449    tms = localtime(&now);
1450 
1451    if ( configp->asif_date > 0 ) {
1452       tms->tm_year  = (int)(configp->asif_date / 10000L) - 1900;
1453       tms->tm_mon   = (int)(configp->asif_date % 10000L) / 100 - 1;
1454       tms->tm_mday  = (int)(configp->asif_date % 100L);
1455    }
1456    if ( configp->asif_time >= 0 ) {
1457       tms->tm_hour  = 0;
1458       tms->tm_min   = configp->asif_time;
1459       tms->tm_sec   = 0;
1460    }
1461    tms->tm_isdst = -1;
1462 
1463    (void)mktime(tms);
1464 
1465    (void)sprintf(buffer, "%s %s %02d %4d %02d:%02d:%02d %s",
1466      wday_name[tms->tm_wday], month_name[tms->tm_mon], tms->tm_mday, tms->tm_year + 1900,
1467         tms->tm_hour, tms->tm_min, tms->tm_sec, heyu_tzname[tms->tm_isdst]);
1468 
1469    return buffer;
1470 }
1471 
1472 
1473 /*-----------------------------------------------------------------+
1474  | Return pointer to string containing current system Legal Time.  |
1475  +-----------------------------------------------------------------*/
legal_time_string(void)1476 char *legal_time_string ( void )
1477 {
1478    time_t      now;
1479    struct tm   *tms;
1480    static char buffer[32];
1481    extern char *heyu_tzname[];
1482 
1483    fix_tznames();
1484 
1485    time(&now);
1486    tms = localtime(&now);
1487 
1488 #if 0
1489    (void)sprintf(buffer, "%s %s %02d %4d %02d:%02d:%02d %s",
1490      wday_name[tms->tm_wday], month_name[tms->tm_mon], tms->tm_mday, tms->tm_year + 1900,
1491         tms->tm_hour, tms->tm_min, tms->tm_sec, heyu_tzname[tms->tm_isdst]);
1492 #endif
1493    (void)sprintf(buffer, "%s %02d %s %4d %02d:%02d:%02d %s",
1494      wday_name[tms->tm_wday], tms->tm_mday, month_name[tms->tm_mon], tms->tm_year + 1900,
1495         tms->tm_hour, tms->tm_min, tms->tm_sec, heyu_tzname[tms->tm_isdst]);
1496 
1497 
1498    return buffer;
1499 }
1500 
1501 /*-----------------------------------------------------------------+
1502  | Return pointer to tm structure with local Standard Time.        |
1503  +-----------------------------------------------------------------*/
stdtime(const time_t * timep)1504 struct tm *stdtime( const time_t *timep )
1505 {
1506    time_t          now;
1507 
1508    /* Set the local timezone variable */
1509 
1510    get_std_timezone();
1511 
1512    now = *timep - (time_t)std_tzone;
1513 
1514    return gmtime(&now);
1515 }
1516 
1517 /*-----------------------------------------------------------------+
1518  | Get count of days for argument date at minutes after 00:00      |
1519  | time, counted from 1 Jan 1970 00:00 hours Standard time in the  |
1520  | local timezone.                                                 |
1521  +-----------------------------------------------------------------*/
day_count(int year,int month,int mday,int minutes)1522 long day_count ( int year, int month, int mday, int minutes )
1523 {
1524    time_t       now;
1525    struct tm    mytm, *tms;
1526 
1527    tms = &mytm;
1528    tms->tm_year = year - 1900;
1529    tms->tm_mon  = month - 1;
1530    tms->tm_mday = mday;
1531    tms->tm_hour = 0;
1532    tms->tm_min  = minutes;
1533    tms->tm_sec = 0;
1534    tms->tm_isdst = -1;
1535 
1536    now = mktime(tms);
1537 
1538    return ((long)now - std_tzone) / 86400L;
1539 
1540 }
1541 
1542 /*-----------------------------------------------------------------+
1543  | Given the count of days from 1 Jan 1970 at 00:00:00 GMT,        |
1544  | return the Julian Day corresponding to Noon on that day.        |
1545  +-----------------------------------------------------------------*/
daycount2JD(long int daycount)1546 long int daycount2JD ( long int daycount )
1547 {
1548    return (daycount + 2440588L);
1549 }
1550 
1551 /*-----------------------------------------------------------------+
1552  | Return 1 if the argument year is a leap year; 0 otherwise.      |
1553  +-----------------------------------------------------------------*/
isleapyear(int year)1554 int isleapyear ( int year )
1555 {
1556    return ((year % 400) == 0) ? 1 :
1557           ((year % 100) == 0) ? 0 :
1558           ((year % 4 )  == 0) ? 1 : 0 ;
1559 }
1560 
1561 #if 0
1562 /*-----------------------------------------------------------------+
1563  | Determine elapsed minutes from 0:00 hrs Standard Time on Jan 1  |
1564  | of the current year until the next NDSTINTV changes between     |
1565  | Standard/Daylight Time.  Store results in global struct         |
1566  | dststruct array lgls[] and in struct config.                    |
1567  +-----------------------------------------------------------------*/
1568 int get_dst_info_old ( int year )
1569 {
1570    time_t      now, seconds, startsec, jan1sec, jul1sec, delta ;
1571    struct tm   jultms, *tmjan, *tmjul, *tms;
1572    int         indx, nintv, val, startval, dstminutes;
1573    int         iter, result = -1, restart;
1574    int         offset[2];
1575 
1576    /* Get current date and time */
1577    time(&now) ;
1578    tmjan = localtime(&now);
1579 
1580    if ( year >= 1970 )
1581       tmjan->tm_year = year - 1900;
1582 
1583    /* Get calendar seconds at 0:00 hours Legal Time on Jan 1st of this year */
1584    tmjan->tm_mon = 0;
1585    tmjan->tm_mday = 1;
1586    tmjan->tm_hour = 0;
1587    tmjan->tm_min = tmjan->tm_sec = 0;
1588    tmjan->tm_isdst = -1;
1589 
1590    jan1sec = mktime(tmjan);
1591 
1592    tmjul = &jultms;
1593    memcpy( (void *)tmjul, (void *)tmjan, sizeof(struct tm) );
1594 
1595    /* Calendar seconds at same legal time on July 1st */
1596    tmjul->tm_mon = 6;
1597    tmjul->tm_mday = 1;
1598    tmjul->tm_hour = 0;
1599    tmjul->tm_min = tmjul->tm_sec = 0;
1600    tmjul->tm_isdst = -1;
1601 
1602    jul1sec = mktime(tmjul);
1603 
1604    /* Reduce difference by full days of 86400 seconds */
1605    dstminutes = (int)((jul1sec - jan1sec) % (time_t)86400 / (time_t)60);
1606    dstminutes = min( dstminutes, 1440 - dstminutes );
1607    configp->dstminutes = dstminutes;
1608 
1609    offset[0] = 0;
1610    offset[1] = dstminutes;
1611 
1612    /* Reduce to seconds at 0:00 hours Standard Time */
1613    jan1sec = ((jan1sec - std_tzone) / (time_t)86400) * (time_t)86400 + std_tzone;
1614 
1615    if ( (val = tmjan->tm_isdst) > 0 ) {
1616       /* Daylight time in Southern hemisphere */
1617       configp->isdst = 1;
1618       indx = 1;
1619       startval = val;
1620    }
1621    else if ( (val = tmjul->tm_isdst) > 0 ) {
1622       /* Daylight time in Northern hemisphere */
1623       configp->isdst = 1;
1624       indx = 0;
1625       startval = 0;
1626    }
1627    else {
1628       /* Daylight time never in effect */
1629       configp->isdst = 0;
1630       for ( nintv = 0; nintv < NDSTINTV; nintv++ )
1631          lgls[nintv].elapsed = -1;
1632          stds[nintv].elapsed = -1;
1633       return 0;
1634    }
1635 
1636    nintv = 0;
1637    startsec = jan1sec;
1638    while ( nintv < NDSTINTV ) {
1639       iter = 0;
1640       result = -1;
1641       restart = 1;
1642       while ( iter < 1000 && !iter_mgr(result, (long *)(&delta), 30*86400L, &restart) ) {
1643          iter++;
1644          seconds = startsec + delta;
1645          tms = localtime(&seconds);
1646          result = (tms->tm_isdst == startval) ? -1 : 1 ;
1647       }
1648       if ( iter > 999 ) {
1649          (void) fprintf(stderr, "convergence error in get_dst_info()\n");
1650          exit(1);
1651       }
1652 
1653       /* Store as elapsed minutes from 0:00 hours Jan 1 Standard Time */
1654       /* adjusted for changeover from daylight to standard time and   */
1655       /* for changeover from standard to daylight time.               */
1656 
1657       lgls[nintv].elapsed = (long)(seconds - jan1sec)/60L + offset[indx];
1658       lgls[nintv].offset = offset[indx];
1659       if ( UNDEF_TIME == DST_TIME )
1660          stds[nintv].elapsed = lgls[nintv].elapsed;
1661       else
1662          stds[nintv].elapsed = (long)(seconds - jan1sec)/60L + dstminutes;
1663 
1664       stds[nintv].offset = offset[indx];
1665       stdr[nintv].elapsed = (long)(seconds - jan1sec)/60L;
1666       stdr[nintv].offset = offset[indx];
1667       nintv++;
1668 
1669       indx = (indx + 1) % 2;
1670       startval = (startval == val) ? 0 : val;
1671       startsec = seconds + (time_t)86400 ;
1672 
1673    }
1674 
1675    return nintv;
1676 }
1677 #endif
1678 
1679 /*-----------------------------------------------------------------+
1680  | Determine whether DST is in effect on a given yday & minutes    |
1681  | (measured from midnight) by comparison with the data in struct  |
1682  | dststruct dsts[], which must previously have been loaded by a   |
1683  | call to function get_dst_info().  The number of minutes to add  |
1684  | to Standard to get Legal Time is returned.                      |
1685  +-----------------------------------------------------------------*/
isdst_test(int yday,int minutes)1686 int isdst_test ( int yday, int minutes )
1687 {
1688    int   j;
1689    long  elapsed;
1690 
1691    elapsed = (long)1440 * (long)yday + (long) minutes;
1692 
1693    for ( j = 0; j < NDSTINTV; j++ ) {
1694       if ( elapsed < lgls[j].elapsed )
1695          return lgls[j].offset;
1696    }
1697 
1698    return 0;
1699 }
1700 
1701 
1702 
1703 /*-----------------------------------------------------------------+
1704  | Return the appropriate time adjustment for periods of Standard  |
1705  | and Daylight Time.                                              |
1706  +-----------------------------------------------------------------*/
time_adjust(int yday,int minutes,unsigned char mode)1707 int time_adjust ( int yday, int minutes, unsigned char mode )
1708 {
1709    int    j, offset = 0;
1710    long   elapsed;
1711 
1712 
1713    elapsed = (long)1440 * (long)yday + (long) minutes;
1714 
1715    if ( mode == LGL2STD ) {
1716       for ( j = 0; j < NDSTINTV; j++ ) {
1717          if ( elapsed < stds[j].elapsed ) {
1718             offset = stds[j].offset;
1719             break;
1720          }
1721       }
1722    }
1723    else {
1724       for ( j = 0; j < NDSTINTV; j++ ) {
1725          if ( elapsed < stdr[j].elapsed ) {
1726             offset = stdr[j].offset;
1727             break;
1728          }
1729       }
1730    }
1731 
1732    return offset;
1733 }
1734 
1735 
1736 /*---------------------------------------------------------+
1737  | Display system calendar configuration:                  |
1738  |   Current date and time.                                |
1739  |   Timezone.                                             |
1740  |   Begin and end of Daylight Time for the current year.  |
1741  +---------------------------------------------------------*/
display_sys_calendar(void)1742 void display_sys_calendar ( void )
1743 {
1744    extern  char *heyu_tzname[];
1745 
1746    /* Months beginning with 1 */
1747    static  char *m_names[] = {
1748      "", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1749          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1750    };
1751 
1752 
1753    static  char    *display[] = {
1754      "Daylight Time begins  yday: %-4d %s %02d %s %4d at %02d:%02d %s\n",
1755      "Standard Time resumes yday: %-4d %s %02d %s %4d at %02d:%02d %s\n"
1756    };
1757 
1758    time_t    now;
1759    struct tm *tms ;
1760    int       j, k;
1761    int       month, day, yday, wday, minute, year, year0;
1762    long      jan1day;
1763 
1764    /* Determine the user's standard timezone */
1765    get_std_timezone();
1766 
1767    /* Create distinctive TZ names if necessary */
1768    fix_tznames();
1769 
1770    /* Get current date and time */
1771    time(&now) ;
1772    tms = localtime(&now);
1773 
1774    /* Display current date and time */
1775    (void) printf("\nDate: %s %02d %s %04d    yday: %-3d\n",
1776         wday_name[tms->tm_wday], tms->tm_mday, month_name[tms->tm_mon],
1777         1900+tms->tm_year, tms->tm_yday);
1778    (void) printf("Time: %02d:%02d:%02d  %s\n",
1779         tms->tm_hour, tms->tm_min, tms->tm_sec,
1780         heyu_tzname[tms->tm_isdst == 0 ? 0 : 1] );
1781 
1782    year0 = tms->tm_year + 1900;
1783 
1784    /* Display Timezone */
1785    if ( timezone < 0L )
1786       (void) printf("Standard Time Zone: %.2f hours East of Greenwich\n",
1787                    -(double)std_tzone / 3600.);
1788    else
1789       (void) printf("Standard Time Zone: %.2f hours West of Greenwich\n",
1790                     (double)std_tzone / 3600.);
1791 
1792    if ( !get_dst_info(0) ) {
1793       (void) printf("Daylight time is not in effect at any time during the year.\n");
1794       return;
1795    }
1796    else {
1797       /* Get the starting day for the year */
1798       tms->tm_year = year0 - 1900;
1799       tms->tm_mon = 0;
1800       tms->tm_mday = 1;
1801       tms->tm_hour = 0;
1802       tms->tm_min = tms->tm_sec = 0;
1803       tms->tm_isdst = -1;
1804       now = mktime(tms);
1805       jan1day = (long)((now - std_tzone) / (time_t)86400);
1806 
1807       /* Display the dates in the order they occur during the year. */
1808       for ( j = 0; j < NDSTINTV; j++ ) {
1809          if ( lgls[j].elapsed < 0 )
1810             continue;
1811          k = lgls[j].offset == 0 ? 0 : 1 ;
1812          yday = lgls[j].elapsed / 1440;
1813          minute = lgls[j].elapsed % 1440;
1814          yday2date( jan1day, yday, &year, &month, &day, &wday );
1815 
1816          (void)printf(display[k], yday, wday_name[wday], day,
1817                m_names[month], year, minute/60, minute%60, heyu_tzname[k] );
1818       }
1819    }
1820    (void) printf("\n");
1821 
1822    return;
1823 }
1824 
1825 /*---------------------------------------------------------------------+
1826  | Get today's date info and save in CALEND structure.                 |
1827  +---------------------------------------------------------------------*/
calendar_today(CALEND * calendp)1828 void calendar_today ( CALEND *calendp )
1829 {
1830    time_t      now;
1831    struct tm   tmstr, *tmp, *tms;
1832    int         year, month, mday, minutes;
1833 
1834    if ( !configp->read_flag ) {
1835       (void) fprintf(stderr,
1836       "Function calendar_today() says: Configuration file has not yet been read.\n");
1837       exit(1);
1838    }
1839 
1840    fix_tznames();
1841 
1842    tmp = &tmstr;
1843 
1844    time(&now);
1845    tms = localtime(&now);
1846 
1847    calendp->asif_flag = ASIF_NONE;
1848 
1849    if ( configp->asif_date > 0 ) {
1850       tms->tm_year  = (int)(configp->asif_date / 10000L) - 1900;
1851       tms->tm_mon   = (int)(configp->asif_date % 10000L) / 100 - 1;
1852       tms->tm_mday  = (int)(configp->asif_date % 100L);
1853       calendp->asif_flag |= ASIF_DATE;
1854    }
1855    if ( configp->asif_time >= 0 ) {
1856       tms->tm_hour  = 0;
1857       tms->tm_min   = configp->asif_time;
1858       tms->tm_sec   = 0;
1859       calendp->asif_flag |= ASIF_TIME;
1860    }
1861    tms->tm_isdst = -1;
1862 
1863    now = mktime(tms);
1864 
1865    calendp->year    = year    = tms->tm_year + 1900;
1866    calendp->month   = month   = tms->tm_mon + 1;
1867    calendp->mday    = mday    = tms->tm_mday;
1868    calendp->minutes = minutes = 60 * tms->tm_hour + tms->tm_min;
1869 
1870    calendp->jan1day = day_count( year, 1, 1, 0 );
1871    calendp->create_day = day_count( year, month, mday, minutes )
1872                                 - calendp->jan1day;
1873 
1874    if ( configp->mode == COMPATIBLE ) {
1875       calendp->today = calendp->jan1day;
1876       calendp->yday  = 0;
1877       calendp->day_zero = 0;
1878    }
1879    else {
1880       calendp->today = day_count( year, month, mday, minutes );
1881       calendp->yday = calendp->today - calendp->jan1day;
1882       calendp->day_zero = calendp->yday;
1883    }
1884 
1885    get_dst_info(year);
1886 
1887    calendp->valid = 1;
1888 
1889    return;
1890 }
1891 
1892 
1893 /*---------------------------------------------------------------------+
1894  | Advance the date info in a CALEND structure by argument ndays.      |
1895  +---------------------------------------------------------------------*/
advance_calendar(CALEND * calendp,int ndays)1896 void advance_calendar ( CALEND *calendp, int ndays )
1897 {
1898    struct tm *tms, mytm;
1899 
1900    if ( !calendp || calendp->valid != 1 ) {
1901       (void) fprintf(stderr,
1902          "advance_calendar() : No valid existing CALEND structure to advance.\n");
1903       exit(1);
1904    }
1905 
1906    tms = &mytm;
1907    tms->tm_year = calendp->year - 1900;
1908    tms->tm_mon  = calendp->month - 1;
1909    tms->tm_mday = calendp->mday + ndays;
1910    tms->tm_min  = calendp->minutes;
1911    tms->tm_hour = tms->tm_sec = 0;
1912    tms->tm_isdst = -1;
1913 
1914    (void) mktime(tms);
1915 
1916    calendp->year    = tms->tm_year + 1900;
1917    calendp->month   = tms->tm_mon + 1;
1918    calendp->mday    = tms->tm_mday;
1919 
1920    calendp->minutes = (configp->asif_time > 0) ? configp->asif_time :
1921 	                        60 * tms->tm_hour + tms->tm_min;
1922    calendp->isdst   = tms->tm_isdst;
1923 
1924    calendp->today = day_count( calendp->year, calendp->month,
1925                          calendp->mday, calendp->minutes);
1926 
1927    calendp->jan1day = day_count( calendp->year, 1, 1, 0);
1928 
1929    calendp->yday    = calendp->today - calendp->jan1day;
1930 
1931    calendp->day_zero = (configp->mode == COMPATIBLE) ?  0 : calendp->yday;
1932 
1933    /* Turn off the asif_date flag */
1934    calendp->asif_flag &= ~ASIF_DATE;
1935 
1936    return;
1937 }
1938 
1939 /* Other functions */
1940 
1941 /*---------------------------------------------------------------------+
1942  | Return string containing full pathname to a Heyu file.              |
1943  +---------------------------------------------------------------------*/
pathspec(char * filename)1944 char *pathspec ( char *filename )
1945 {
1946    static char full_path[PATH_LEN + 1];
1947 
1948    (void)strncpy2(full_path, heyu_path, sizeof(full_path) - 1);
1949    (void)strncat(full_path, filename, sizeof(full_path) - 1 - strlen(full_path));
1950 
1951    return full_path;
1952 }
1953 
1954 /*---------------------------------------------------------------------+
1955  | Return string containing full pathname to a Heyu report file.       |
1956  +---------------------------------------------------------------------*/
altpathspec(char * filename)1957 char *altpathspec ( char *filename )
1958 {
1959    extern char alt_path[];
1960    static char full_path[PATH_LEN + 1];
1961 
1962    (void)strncpy2(full_path, alt_path, sizeof(full_path) - 1);
1963    (void)strncat(full_path, filename, sizeof(full_path) - 1 - strlen(full_path));
1964 
1965    return full_path;
1966 }
1967 
1968 /*---------------------------------------------------------------------+
1969  | Search the array of SCENEs for label.  Return the index in the      |
1970  | array if found, otherwise return -1.                                |
1971  +---------------------------------------------------------------------*/
lookup_scene(SCENE * scenep,char * label)1972 int lookup_scene ( SCENE *scenep, char *label )
1973 {
1974    int j = 0;
1975 
1976    while ( scenep && scenep[j].line_no > 0 ) {
1977       if ( strcmp(label, scenep[j].label) == 0 ) {
1978          return j;
1979       }
1980       j++;
1981    }
1982 
1983    return -1;
1984 }
1985 
1986 
1987 
1988 
1989 /*---------------------------------------------------------------------+
1990  | Search the array of ALIAS structures for the argument alias label.  |
1991  | If found, pass the encoded housecode and unit bitmap back through   |
1992  | the argument list and return the index in the array.  Otherwise     |
1993  | return -1.                                                          |
1994  +---------------------------------------------------------------------*/
get_alias(ALIAS * aliasp,char * label,char * hc,unsigned int * bmap)1995 int get_alias ( ALIAS *aliasp, char *label, char *hc, unsigned int *bmap )
1996 {
1997    int  j;
1998 
1999    j = 0;
2000    while ( aliasp && aliasp[j].line_no > 0 ) {
2001       if ( !strcmp(aliasp[j].label, label) ) {
2002          *hc    = aliasp[j].housecode;
2003          *bmap  = aliasp[j].unitbmap;
2004          return j;
2005       }
2006       j++ ;
2007    }
2008    *hc    = '_';
2009    *bmap  = 0;
2010 
2011    return -1;
2012 }
2013 
2014 /*---------------------------------------------------------------------+
2015  | Search the array of ALIAS structures for multiple instances of      |
2016  | aliases having the argument housecode and bitmap.  Return each      |
2017  | alias index as found and update the index pointer to the following  |
2018  | address.  Return -1 when no more aliases are found.                 |
2019  +---------------------------------------------------------------------*/
lookup_alias_mult(char hc,unsigned int bitmap,int * indxp)2020 int lookup_alias_mult ( char hc, unsigned int bitmap, int *indxp )
2021 {
2022    ALIAS *aliasp;
2023    int   j;
2024 
2025    aliasp = configp->aliasp;
2026    hc = toupper((int)hc);
2027 
2028    j = *indxp;
2029    while ( aliasp && aliasp[j].line_no > 0 ) {
2030       if ( hc == aliasp[j].housecode &&
2031              bitmap == aliasp[j].unitbmap  ) {
2032          *indxp = j + 1;
2033          return j;
2034       }
2035       j++;
2036    }
2037    return -1;
2038 }
2039 
2040 /*---------------------------------------------------------------------+
2041  | Search the array of ALIAS structures for multiple instances of      |
2042  | aliases having the argument housecode and bitmap.  Return each      |
2043  | alias label as found and update the index pointer to the following  |
2044  | address.  Return NULL when no more aliases are found.               |
2045  +---------------------------------------------------------------------*/
lookup_label_mult(char hc,unsigned int bitmap,int * indxp)2046 char *lookup_label_mult ( char hc, unsigned int bitmap, int *indxp )
2047 {
2048    ALIAS *aliasp;
2049    int   j;
2050 
2051    aliasp = configp->aliasp;
2052    hc = toupper((int)hc);
2053 
2054    j = *indxp;
2055    while ( aliasp && aliasp[j].line_no > 0 ) {
2056       if ( hc == aliasp[j].housecode &&
2057              bitmap == aliasp[j].unitbmap  ) {
2058          *indxp = j + 1;
2059          return aliasp[j].label;
2060       }
2061       j++;
2062    }
2063    return (char *)NULL;
2064 }
2065 
2066 /*---------------------------------------------------------------------+
2067  | Search the array of ALIAS structures for multiple instances of      |
2068  | aliases having the argument housecode and bitmap.  Return each      |
2069  | alias label as found and update the index pointer to the following  |
2070  | address.  Return NULL when no more aliases are found.               |
2071  +---------------------------------------------------------------------*/
alias_rev_lookup_mult(char hc,unsigned int bitmap,int * indxp)2072 char *alias_rev_lookup_mult ( char hc, unsigned int bitmap, int *indxp )
2073 {
2074    ALIAS *aliasp;
2075    int   j;
2076 
2077    aliasp = configp->aliasp;
2078    hc = toupper((int)hc);
2079 
2080    j = *indxp;
2081    while ( aliasp && aliasp[j].line_no > 0 ) {
2082       if ( hc == aliasp[j].housecode &&
2083              bitmap == aliasp[j].unitbmap  ) {
2084          *indxp = j + 1;
2085          return aliasp[j].label;
2086       }
2087       j++;
2088    }
2089    return (char *)NULL;
2090 }
2091 
2092 #if 0
2093 /*---------------------------------------------------------------------+
2094  | Search the array of ALIAS structures for an alias with optflag      |
2095  | MOPT_SENSOR and having the argument housecode and bitmap.  Return   |
2096  | the timestamp if found, otherwise 0.                                |
2097  +---------------------------------------------------------------------*/
2098 long get_sensor_timestamp ( unsigned char hcode, unsigned char ucode )
2099 {
2100    char         hc;
2101    unsigned int bitmap;
2102    int          j;
2103 
2104    if ( !(aliasp->configp->aliasp) )
2105       return 0;
2106 
2107    hc = hc2code(hcode);
2108    bitmap = (1 << ucode);
2109 
2110    j = 0;
2111    while ( aliasp && aliasp[j].line_no > 0 ) {
2112       if ( hc == aliasp[j].housecode &&
2113            bitmap == aliasp[j].unitbmap &&
2114            aliasp[j].optflag & MOPT_SENSOR ) {
2115          return aliasp[j].timestamp;
2116       }
2117       j++;
2118    }
2119    return 0;
2120 }
2121 #endif
2122 
2123 /*---------------------------------------------------------------------+
2124  | Search the array of ALIAS structures for an alias having the        |
2125  | argument housecode and bitmap.  Return the alias label if found,    |
2126  | otherwise "_no_alias_".                                             |
2127  +---------------------------------------------------------------------*/
lookup_label(char hc,unsigned int bitmap)2128 char *lookup_label ( char hc, unsigned int bitmap )
2129 {
2130    ALIAS *aliasp;
2131    int   j;
2132 
2133    aliasp = configp->aliasp;
2134    hc = toupper((int)hc);
2135 
2136    j = 0;
2137    while ( aliasp && aliasp[j].line_no > 0 ) {
2138       if ( hc == aliasp[j].housecode &&
2139            bitmap == aliasp[j].unitbmap  ) {
2140          return aliasp[j].label;
2141       }
2142       j++;
2143    }
2144    return "_no_alias_";
2145 }
2146 
2147 /*---------------------------------------------------------------------+
2148  | Search the array of ALIAS structures for an alias having the        |
2149  | argument housecode, bitmap and ident.  Return the alias index if    |
2150  | found, otherwise -1.                                                |
2151  +---------------------------------------------------------------------*/
alias_rev_index(char hc,unsigned int bitmap,unsigned char vtype,unsigned long ident)2152 int alias_rev_index ( char hc, unsigned int bitmap, unsigned char vtype, unsigned long ident )
2153 {
2154    ALIAS *aliasp;
2155    int   j, k;
2156    unsigned long mask;
2157 
2158    aliasp = configp->aliasp;
2159    hc = toupper((int)hc);
2160 
2161    mask = (vtype == RF_SEC) ? configp->securid_mask : 0xffffffffu;
2162 
2163    ident &= mask;
2164 
2165    j = 0;
2166    while ( aliasp && aliasp[j].line_no > 0 ) {
2167       if ( hc == aliasp[j].housecode &&
2168            bitmap == aliasp[j].unitbmap &&
2169            vtype == aliasp[j].vtype ) {
2170          if ( vtype == 0 )
2171             return j;
2172          for ( k = 0; k < aliasp[j].nident; k++ ) {
2173             if ( ident == (aliasp[j].ident[k] & mask) ) {
2174                return j;
2175             }
2176          }
2177       }
2178       j++;
2179    }
2180    return -1;
2181 }
2182 
2183 /*---------------------------------------------------------------------+
2184  | Search the array of ALIAS structures for an alias having the        |
2185  | argument housecode and bitmap.  Return the alias label if found,    |
2186  | otherwise "_no_alias_".                                             |
2187  +---------------------------------------------------------------------*/
alias_rev_lookup(char hc,unsigned int bitmap)2188 char *alias_rev_lookup ( char hc, unsigned int bitmap )
2189 {
2190    ALIAS *aliasp;
2191    int   j;
2192 
2193    aliasp = configp->aliasp;
2194    hc = toupper((int)hc);
2195 
2196    j = 0;
2197    while ( aliasp && aliasp[j].line_no > 0 ) {
2198       if ( hc == aliasp[j].housecode &&
2199            bitmap == aliasp[j].unitbmap  ) {
2200          return aliasp[j].label;
2201       }
2202       j++;
2203    }
2204    return "_no_alias_";
2205 }
2206 
2207 /*---------------------------------------------------------------------+
2208  | Search the array of ALIAS structures for the argument alias label.  |
2209  | If found, pass the encoded housecode and unit bitmap back through   |
2210  | the argument list and return the index in the array.  Otherwise     |
2211  | return -1.                                                          |
2212  +---------------------------------------------------------------------*/
alias_lookup(char * label,char * hc,unsigned int * bmap)2213 int alias_lookup ( char *label, char *hc, unsigned int *bmap )
2214 {
2215    return get_alias( configp->aliasp, label, hc, bmap );
2216 }
2217 
2218 /*---------------------------------------------------------------------+
2219  | Return a count of the total number of defined aliases.              |
2220  +---------------------------------------------------------------------*/
alias_count(void)2221 int alias_count ( void )
2222 {
2223    ALIAS *aliasp;
2224    int   count = 0;
2225 
2226    aliasp = configp->aliasp;
2227 
2228    while ( aliasp && aliasp[count].line_no > 0 )
2229       count++;
2230 
2231    return count;
2232 }
2233 
2234 /*---------------------------------------------------------------------+
2235  | Return a count of the total number of defined RFXSensors.           |
2236  +---------------------------------------------------------------------*/
rfxsensor_count(void)2237 int rfxsensor_count ( void )
2238 {
2239    ALIAS *aliasp;
2240    int   j, count = 0;
2241 
2242    aliasp = configp->aliasp;
2243 
2244    j = 0;
2245    while ( aliasp && aliasp[j].line_no > 0 ) {
2246       if ( aliasp[j].vtype == RF_XSENSOR )
2247          count++;
2248       j++;
2249    }
2250 
2251    return count;
2252 }
2253 
2254 /*---------------------------------------------------------------------+
2255  | Return a count of the total number of defined RFXMeters.            |
2256  +---------------------------------------------------------------------*/
rfxmeter_count(void)2257 int rfxmeter_count ( void )
2258 {
2259    ALIAS *aliasp;
2260    int   j, count = 0;
2261 
2262    aliasp = configp->aliasp;
2263 
2264    j = 0;
2265    while ( aliasp && aliasp[j].line_no > 0 ) {
2266       if ( aliasp[j].vtype == RF_XMETER )
2267          count++;
2268       j++;
2269    }
2270 
2271    return count;
2272 }
2273 
2274 /*---------------------------------------------------------------------+
2275  | Return a count of aliases with a specific vtype                     |
2276  +---------------------------------------------------------------------*/
alias_vtype_count(unsigned char vtype)2277 int alias_vtype_count ( unsigned char vtype )
2278 {
2279    ALIAS *aliasp;
2280    int   j, count = 0;
2281 
2282    aliasp = configp->aliasp;
2283 
2284    j = 0;
2285    while ( aliasp && aliasp[j].line_no > 0 ) {
2286       if ( aliasp[j].vtype == vtype )
2287          count++;
2288       j++;
2289    }
2290 
2291    return count;
2292 }
2293 
2294 
2295 /*---------------------------------------------------------------------+
2296  | Return the macro execution time of the earliest event whose time    |
2297  | is defined by "now+NN" in the schedule file.  The time includes the |
2298  | macro delay time, if any, and is in minutes after 00:00 Legal Time. |
2299  | If no such event defined, return -1                                 |
2300  | (This function must be called before the input time has been        |
2301  | converted from Legal to Standard Time.)                             |
2302  +---------------------------------------------------------------------*/
get_first_now_time(TEVENT * teventp)2303 int get_first_now_time ( TEVENT *teventp )
2304 {
2305    int  j;
2306    int  exec_time, earliest;
2307 
2308    j = 0;
2309    earliest = 9999;
2310    while ( teventp && teventp[j].line_no > 0 ) {
2311       if ( teventp[j].generation == current_tevent_generation &&
2312            (teventp[j].flag & NOW_EVENT) )  {
2313          exec_time = teventp[j].offset + teventp[j].delay;
2314          earliest = min(earliest, exec_time);
2315       }
2316       j++;
2317    }
2318    if ( earliest < 9999 )
2319       return earliest;
2320 
2321    return -1;
2322 }
2323 
2324 
2325 /*---------------------------------------------------------------------+
2326  | Interpret the time token from a timer command as either the         |
2327  | clock time in minutes or the offset from dawn or dusk in minutes.   |
2328  | Return a flag indicating whether Clock or Dawn or Dusk relative.    |
2329  | The time token may also be "now[+offset]" for quick testing.        |
2330  +---------------------------------------------------------------------*/
parse_time_token(char * str,int * stime)2331 int parse_time_token ( char *str, int *stime )
2332 {
2333    int       hour, min, flag = 0;
2334    char      buf[64];
2335    char      *sp;
2336    struct tm *tms;
2337    time_t    now;
2338 
2339    (void) strncpy2(buf, str, 63);
2340    (void) strlower(strtrim(buf));
2341 
2342    if ( *(sp = buf + strlen(buf) - 1) == 's' ) {
2343       flag |= SEC_EVENT;
2344       *sp = '\0';
2345    }
2346 
2347    if ( !strncmp(buf, "dawn", 4) ) {
2348       flag |= DAWN_EVENT ;
2349       *stime = (*(buf+4)) ? (int)strtol(buf+4, NULL, 10) : 0 ;
2350    }
2351    else if ( !strncmp(buf, "dusk", 4) ) {
2352       flag |= DUSK_EVENT ;
2353       *stime = (*(buf+4)) ? (int)strtol(buf+4, NULL, 10) : 0 ;
2354    }
2355    else if ( !strncmp(buf, "now", 3) ) {
2356       /* (For quick testing timers) */
2357       *stime = (*(buf+3)) ? (int)strtol(buf+3, NULL, 10) : 0 ;
2358       if ( *stime < 0 || flag & SEC_EVENT ) {
2359          flag = INVALID_EVENT;
2360          return flag;
2361       }
2362       time(&now);
2363       tms = localtime(&now);
2364       /* Round up, allowing at least 15 seconds for uploading */
2365       *stime += 60 * tms->tm_hour + tms->tm_min + (tms->tm_sec + 75)/60;
2366       if ( *stime < 0 || *stime > 1439 ) {
2367          flag = INVALID_EVENT;
2368          return flag;
2369       }
2370       else
2371          flag |= (CLOCK_EVENT | NOW_EVENT);
2372    }
2373    else if ( (sp = strchr(buf, ':')) != NULL ) {
2374       min = (int)strtol(sp+1, NULL, 10);
2375       *sp = '\0';
2376       hour = ( *buf ) ? (int)strtol(buf, NULL, 10) : 0 ;
2377       *stime = 60 * hour + min ;
2378       if ( *stime < 0 || *stime > 1439 )
2379          flag = INVALID_EVENT;
2380       else
2381          flag |= CLOCK_EVENT;
2382    }
2383    else {
2384       flag = INVALID_EVENT ;
2385    }
2386 
2387    return flag;
2388 }
2389 
2390 
2391 /*-----------------------------------------------------------+
2392  | Return the index of a child tevent which is a duplicate   |
2393  | of the parent except that it's one generation higher and  |
2394  | the links of both parent and child are updated to insert  |
2395  | the child into the linked list after the parent.          |
2396  +-----------------------------------------------------------*/
spawn_child_tevent(TEVENT ** teventpp,int parent_index)2397 int spawn_child_tevent ( TEVENT **teventpp, int parent_index )
2398 {
2399 
2400    int        cindx;
2401 
2402    /* Get a new tevent */
2403 
2404    cindx = tevent_index( teventpp );
2405 
2406    /* Copy all fields from the parent tevent */
2407    (void) memcpy( (void *)(*teventpp + cindx),
2408             (void *)(*teventpp + parent_index), sizeof(TEVENT) );
2409 
2410    /* Reset the flags defined as "don't copy" */
2411    (*teventpp)[cindx].flag &= ~(NOCOPY_EVENTS);
2412 
2413    /* Set the new generation */
2414    increment_tevent_generation( *teventpp, cindx );
2415 
2416    /* Insert the child into the linked list */
2417    (*teventpp)[cindx].link = (*teventpp)[parent_index].link;
2418    (*teventpp)[parent_index].link = cindx;
2419 
2420    /* Set the reverse link */
2421    (*teventpp)[cindx].plink = parent_index;
2422 
2423    return cindx;
2424 }
2425 
2426 /*-----------------------------------------------------------+
2427  | Return the index of a child timer which is a duplicate    |
2428  | of the parent except that it's one generation higher and  |
2429  | the links of both parent and child are updated to insert  |
2430  | the child into the linked list after the parent.          |
2431  +-----------------------------------------------------------*/
spawn_child_timer(TIMER ** timerpp,int parent_index)2432 int spawn_child_timer ( TIMER **timerpp, int parent_index )
2433 {
2434 
2435    int        cindx;
2436 
2437    /* Get a new timer */
2438 
2439    cindx = timer_index( timerpp );
2440 
2441    /* Copy all fields from the parent timer */
2442    (void) memcpy( (void *)(*timerpp + cindx),
2443             (void *)(*timerpp + parent_index), sizeof(TIMER) );
2444 
2445    /* Set the new generation */
2446    increment_timer_generation( *timerpp, cindx );
2447 
2448    /* Insert the child into the linked list */
2449    (*timerpp)[cindx].link = (*timerpp)[parent_index].link;
2450    (*timerpp)[parent_index].link = cindx;
2451 
2452    return cindx;
2453 }
2454 
2455 
2456 /*---------------------------------------------------------------------+
2457  | Return next available index in array of TIMER structures            |
2458  +---------------------------------------------------------------------*/
timer_index(TIMER ** timerpp)2459 int timer_index ( TIMER **timerpp )
2460 {
2461    extern int timer_size, timer_maxsize ;
2462    int        j = 0;
2463    int        blksize = 20;
2464    static int siztimer = sizeof(TIMER);
2465 
2466    /* Allocate initial block of memory */
2467    if ( *timerpp == NULL ) {
2468       *timerpp = calloc(blksize, siztimer );
2469       if ( *timerpp == NULL ) {
2470          (void) fprintf(stderr, "Unable to allocate memory for Timer.\n");
2471          exit(1);
2472       }
2473       timer_maxsize = blksize;
2474       timer_size = 0;
2475       for ( j = 0; j < timer_maxsize; j++ ) {
2476          (*timerpp)[j].line_no = -1 ;
2477          (*timerpp)[j].link = j + 1;
2478          (*timerpp)[j].cancel = 0;
2479       }
2480    }
2481 
2482    /* Check to see if there's an available location          */
2483    /* If not, increase the size of the memory allocation.    */
2484    /* (Always leave room for a final termination indicator.) */
2485    if ( timer_size == (timer_maxsize - 1)) {
2486       timer_maxsize += blksize ;
2487       *timerpp = realloc(*timerpp, timer_maxsize * siztimer );
2488       if ( *timerpp == NULL ) {
2489          (void) fprintf(stderr, "Unable to increase size of Timer list.\n");
2490          exit(1);
2491       }
2492 
2493       /* Initialize the new memory allocation */
2494       for ( j = timer_size; j < timer_maxsize; j++ ) {
2495          (*timerpp)[j].line_no = -1;
2496          (*timerpp)[j].link = j + 1;
2497          (*timerpp)[j].cancel = 0;
2498       }
2499    }
2500 
2501    j = timer_size;
2502    timer_size += 1;
2503 
2504    return j;
2505 }
2506 
2507 /*---------------------------------------------------------------------+
2508  | Return next available index in array of TEVENT structures           |
2509  +---------------------------------------------------------------------*/
tevent_index(TEVENT ** teventpp)2510 int tevent_index ( TEVENT **teventpp )
2511 {
2512    extern int tevent_size, tevent_maxsize ;
2513    int        j;
2514    int        blksize = 20;
2515    static int siztevent = sizeof(TEVENT);
2516 
2517    /* Allocate initial block of memory */
2518    if ( *teventpp == NULL ) {
2519       *teventpp = calloc(blksize, siztevent );
2520       if ( *teventpp == NULL ) {
2521          (void) fprintf(stderr, "Unable to allocate memory for tevent.\n");
2522          exit(1);
2523       }
2524       tevent_maxsize = blksize;
2525       tevent_size = 0;
2526       for ( j = 0; j < tevent_maxsize; j++ ) {
2527          (*teventpp)[j].line_no = -1 ;
2528          (*teventpp)[j].combined = 0;
2529          (*teventpp)[j].plink = -1;
2530          (*teventpp)[j].chain_len = 1;
2531          (*teventpp)[j].link = j + 1;
2532       }
2533    }
2534 
2535    /* Check to see if there's an available location          */
2536    /* If not, increase the size of the memory allocation.    */
2537    /* (Always leave room for a final termination indicator.) */
2538    if ( tevent_size == (tevent_maxsize - 1)) {
2539       tevent_maxsize += blksize ;
2540       *teventpp = realloc(*teventpp, tevent_maxsize * siztevent );
2541       if ( *teventpp == NULL ) {
2542          (void) fprintf(stderr, "Unable to increase size of tevent list.\n");
2543          exit(1);
2544       }
2545 
2546       /* Initialize the new memory allocation */
2547       for ( j = tevent_size; j < tevent_maxsize; j++ ) {
2548          (*teventpp)[j].line_no = -1;
2549          (*teventpp)[j].combined = 0;
2550          (*teventpp)[j].plink = -1;
2551          (*teventpp)[j].chain_len = 1;
2552          (*teventpp)[j].link = j + 1;
2553       }
2554    }
2555 
2556    j = tevent_size;
2557    tevent_size += 1;
2558 
2559    return j;
2560 }
2561 
2562 /*---------------------------------------------------------------------+
2563  | Save the state of TIMERs and TEVENTs                                |
2564  +---------------------------------------------------------------------*/
save_state(TIMER * timerp,TEVENT * teventp)2565 void save_state ( TIMER *timerp, TEVENT *teventp )
2566 {
2567    extern int timer_size, timer_savesize, save_timer_generation;
2568    extern int tevent_size, tevent_savesize, save_tevent_generation;
2569 
2570    if ( timerp ) {
2571        timer_savesize = timer_size;
2572        save_timer_generation = current_timer_generation;
2573    }
2574 
2575    if ( teventp ) {
2576       tevent_savesize = tevent_size;
2577       save_tevent_generation = current_tevent_generation;
2578    }
2579 
2580    return;
2581 }
2582 
2583 /*---------------------------------------------------------------------+
2584  | Save the initial TIMER configuration                                |
2585  +---------------------------------------------------------------------*/
save_timer_config(TIMER * timerp)2586 void save_timer_config ( TIMER *timerp )
2587 {
2588     extern int timer_size, timer_savesize, save_timer_generation;
2589 
2590     if ( timerp ) {
2591        timer_savesize = timer_size;
2592        save_timer_generation = current_timer_generation;
2593     }
2594 
2595     return;
2596  }
2597 
2598 /*---------------------------------------------------------------------+
2599  | Save the initial TEVENT configuration                               |
2600  +---------------------------------------------------------------------*/
save_tevent_config(TEVENT * teventp)2601 void save_tevent_config ( TEVENT *teventp )
2602 {
2603    extern int tevent_size, tevent_savesize, save_tevent_generation;
2604 
2605    if ( teventp ) {
2606       tevent_savesize = tevent_size;
2607       save_tevent_generation = current_tevent_generation;
2608    }
2609 
2610    return;
2611 }
2612 
2613 /*---------------------------------------------------------------------+
2614  | Restore TIMER and TEVENT configuration to saved state.              |
2615  +---------------------------------------------------------------------*/
restore_state(TIMER * timerp,TEVENT * teventp)2616 void restore_state ( TIMER *timerp, TEVENT *teventp )
2617 {
2618    extern int timer_size, timer_savesize, timer_maxsize;
2619    extern int save_timer_generation;
2620    extern int tevent_size, tevent_savesize, tevent_maxsize;
2621    extern int save_tevent_generation;
2622    int        j;
2623 
2624    if ( timerp ) {
2625       timer_size = timer_savesize;
2626       for ( j = timer_size; j < timer_maxsize; j++ ) {
2627          timerp[j].line_no = -1;
2628          timerp[j].link = j + 1;
2629       }
2630       current_timer_generation = save_timer_generation;
2631    }
2632 
2633    if ( teventp ) {
2634       tevent_size = tevent_savesize;
2635       for ( j = tevent_size; j < tevent_maxsize; j++ ) {
2636          teventp[j].line_no = -1;
2637          teventp[j].link = j + 1;
2638          teventp[j].trig = -1;
2639       }
2640       current_tevent_generation = save_tevent_generation;
2641    }
2642    return;
2643 }
2644 
2645 /*---------------------------------------------------------------------+
2646  | Restore TIMER configuration to saved state.                         |
2647  +---------------------------------------------------------------------*/
restore_timer_config(TIMER * timerp)2648 void restore_timer_config ( TIMER *timerp )
2649 {
2650     extern int timer_size, timer_savesize, timer_maxsize;
2651     extern int save_timer_generation;
2652     int        j;
2653 
2654     if ( timerp ) {
2655        timer_size = timer_savesize;
2656        for ( j = timer_size; j < timer_maxsize; j++ ) {
2657           timerp[j].line_no = -1;
2658           timerp[j].link = j + 1;
2659        }
2660        current_timer_generation = save_timer_generation;
2661     }
2662     return ;
2663 }
2664 
2665 /*---------------------------------------------------------------------+
2666  | Restore TEVENT configuration to saved state.                        |
2667  +---------------------------------------------------------------------*/
restore_tevent_config(TEVENT * teventp)2668 void restore_tevent_config ( TEVENT *teventp )
2669 {
2670    extern int tevent_size, tevent_savesize, tevent_maxsize;
2671    extern int save_tevent_generation;
2672    int        j;
2673 
2674    if ( teventp ) {
2675       tevent_size = tevent_savesize;
2676       for ( j = tevent_size; j < tevent_maxsize; j++ ) {
2677          teventp[j].line_no = -1;
2678          teventp[j].link = j + 1;
2679          teventp[j].trig = -1;
2680       }
2681       current_tevent_generation = save_tevent_generation;
2682    }
2683    return;
2684 }
2685 
2686 /*---------------------------------------------------------------------+
2687  | Update the timer generation counter and return the new generation.  |
2688  +---------------------------------------------------------------------*/
update_current_timer_generation(void)2689 int update_current_timer_generation ( void )
2690 {
2691    extern int current_timer_generation, timer_generation_delta;
2692 
2693    current_timer_generation += timer_generation_delta;
2694    timer_generation_delta = 0;
2695 
2696    return current_timer_generation;
2697 }
2698 
2699 /*---------------------------------------------------------------------+
2700  | Update the tevent generation counter and return the new generation. |
2701  +---------------------------------------------------------------------*/
update_current_tevent_generation(void)2702 int update_current_tevent_generation ( void )
2703 {
2704    extern int current_tevent_generation, tevent_generation_delta;
2705 
2706    current_tevent_generation += tevent_generation_delta;
2707    tevent_generation_delta = 0;
2708 
2709    return current_tevent_generation;
2710 }
2711 
2712 /*---------------------------------------------------------------------+
2713  | Increment the current tevent generation by at least one.            |
2714  +---------------------------------------------------------------------*/
update_current_tevent_generation_anyway(void)2715 int update_current_tevent_generation_anyway ( void )
2716 {
2717    extern int tevent_generation_delta;
2718 
2719    if ( tevent_generation_delta == 0 )
2720       tevent_generation_delta = 1;
2721 
2722    return update_current_tevent_generation();
2723 }
2724 
2725 /*---------------------------------------------------------------------+
2726  | Increment the generation of a tevent (without changing the current  |
2727  | generation).                                                        |
2728  +---------------------------------------------------------------------*/
increment_tevent_generation(TEVENT * teventp,int index)2729 void increment_tevent_generation ( TEVENT *teventp, int index )
2730 {
2731    extern int tevent_generation_delta;
2732 
2733    teventp[index].generation += 1;
2734    tevent_generation_delta = 1;
2735 
2736    return;
2737 }
2738 
2739 /*---------------------------------------------------------------------+
2740  | Increment the generation of a timer (without changing the current   |
2741  | generation).                                                        |
2742  +---------------------------------------------------------------------*/
increment_timer_generation(TIMER * timerp,int index)2743 void increment_timer_generation ( TIMER *timerp, int index )
2744 {
2745    extern int timer_generation_delta;
2746 
2747    if ( timerp )
2748       timerp[index].generation += 1;
2749 
2750    timer_generation_delta = 1;
2751 
2752    return;
2753 }
2754 
2755 /*---------------------------------------------------------------------+
2756  | Return next available index in array of TRIGGER structures          |
2757  +---------------------------------------------------------------------*/
trigger_index(TRIGGER ** triggerpp)2758 int trigger_index ( TRIGGER **triggerpp )
2759 {
2760    static int size, max_size ;
2761    int   j;
2762    int   blksize = 5;
2763    static int strucsize = sizeof(TRIGGER);
2764 
2765    /* Allocate initial block of memory */
2766    if ( *triggerpp == NULL ) {
2767       *triggerpp = calloc(blksize, strucsize );
2768       if ( *triggerpp == NULL ) {
2769          (void) fprintf(stderr, "Unable to allocate memory for Trigger.\n");
2770          exit(1);
2771       }
2772       max_size = blksize;
2773       size = 0;
2774       for ( j = 0; j < max_size; j++ ) {
2775          (*triggerpp)[j].line_no = -1 ;
2776       }
2777    }
2778 
2779    /* Check to see if there's an available location          */
2780    /* If not, increase the size of the memory allocation.    */
2781    /* (Always leave room for a final termination indicator.) */
2782    if ( size == (max_size - 1)) {
2783       max_size += blksize ;
2784       *triggerpp = realloc(*triggerpp, max_size * strucsize );
2785       if ( *triggerpp == NULL ) {
2786          (void) fprintf(stderr, "Unable to increase size of Trigger list.\n");
2787          exit(1);
2788       }
2789 
2790       /* Initialize the new memory allocation */
2791       for ( j = size; j < max_size; j++ ) {
2792          (*triggerpp)[j].line_no = -1;
2793       }
2794    }
2795 
2796    j = size;
2797    size += 1;
2798 
2799    return j;
2800 }
2801 
2802 
2803 /*---------------------------------------------------------------------+
2804  | Return an index to the list of macro elements with sufficient       |
2805  | room for the argument number of bytes.                              |
2806  +---------------------------------------------------------------------*/
macro_element_index(unsigned char ** elementpp,int nbytes)2807 int macro_element_index ( unsigned char **elementpp, int nbytes )
2808 {
2809    static int size, max_size ;
2810    static int ucsize = sizeof(unsigned char);
2811    int   j;
2812    int   blksize = 50;   /* Must be at least greater than the largest */
2813                           /* sized macro element (currently 6 bytes).  */
2814 
2815    /* Allocate initial block of memory to begin with */
2816    if ( *elementpp == NULL ) {
2817       max_size = max(blksize, nbytes);
2818       *elementpp = calloc(max_size, ucsize );
2819       if ( *elementpp == NULL ) {
2820          (void) fprintf(stderr, "Unable to allocate memory for macro element.\n");
2821          exit(1);
2822       }
2823       size = 0;
2824       for ( j = 0; j < max_size; j++ ) {
2825          (*elementpp)[j] = 0 ;
2826       }
2827    }
2828 
2829    /* Check to see if there's an available location with enough room. */
2830    /* If not, increase the size of the memory allocation.             */
2831    if ( (size + nbytes) > max_size ) {
2832       max_size += max(blksize, nbytes) ;
2833       *elementpp = realloc(*elementpp, (max_size * ucsize) );
2834       if ( *elementpp == NULL ) {
2835          (void) fprintf(stderr, "Unable to increase size of macro element list.\n");
2836          exit(1);
2837       }
2838       for ( j = size; j < max_size; j++ ) {
2839          (*elementpp)[j] = 0 ;
2840       }
2841    }
2842 
2843    j = size;
2844    size += nbytes;
2845 
2846    return j;
2847 }
2848 
2849 /*---------------------------------------------------------------------+
2850  | Return the index in array MACRO to the argument macro_name.         |
2851  | If not found, create the entry.                                     |
2852  | Also mark the flag in MACRO with the type of command which          |
2853  | referenced or defined it, i.e., timer, trigger, or macro.           |
2854  | If defined more than once by a macro command, mark it as a          |
2855  | duplicate.                                                          |
2856  +---------------------------------------------------------------------*/
macro_index(MACRO ** macropp,char * macro_label,unsigned char refer)2857 int macro_index ( MACRO **macropp, char *macro_label,
2858                                                    unsigned char refer )
2859 {
2860    static int size, max_size ;
2861    int    j;
2862    int    blksize = 10;
2863    static int sizmacro = sizeof(MACRO);
2864 
2865    /* Allocate initial block of memory */
2866    if ( *macropp == NULL ) {
2867       *macropp = calloc(blksize, sizmacro );
2868       if ( *macropp == NULL ) {
2869          (void) fprintf(stderr,
2870             "Unable to allocate memory for Macro list.\n");
2871          exit(1);
2872       }
2873       max_size = blksize;
2874       size = 0;
2875       for ( j = 0; j < max_size; j++ ) {
2876          (*macropp)[j].line_no = -1;
2877          (*macropp)[j].refer = 0 ;
2878          (*macropp)[j].label[0] = '\0' ;
2879          (*macropp)[j].use = USED ;
2880          (*macropp)[j].modflag = UNMODIFIED;
2881          (*macropp)[j].isnull = 0;
2882          (*macropp)[j].trig = -1;
2883          (*macropp)[j].nelem = 0;
2884          (*macropp)[j].total = 0;
2885       }
2886       /* Make the first macro in the list the null macro */
2887       /* and give it the label "null".                     */
2888       (*macropp)[0].line_no = 9999;
2889       (*macropp)[0].isnull = 1;
2890       (*macropp)[0].refer = MACRO_PARSER;
2891       (void) strncpy2((*macropp)[0].label, "null", MACRO_LEN);
2892       size = 1;
2893    }
2894 
2895    /* See if the macro name is already in the list.      */
2896    /* If so, add parser's identity and return its index. */
2897    /* However if this and the existing entry were made   */
2898    /* by the macro parser, also flag it as a duplicate.  */
2899 
2900    for ( j = 0; j < size; j++ ) {
2901       if ( !strcmp(macro_label, (*macropp)[j].label) ) {
2902          if ( (*macropp)[j].refer & (refer & MACRO_PARSER) )
2903             refer |= MACRO_DUPLICATE ;
2904          (*macropp)[j].refer |= refer ;
2905          return j;
2906       }
2907    }
2908 
2909    /* Check to see if there's room for the new macro name. */
2910    /* If not, increase the size of the memory allocation. */
2911    /* (Always leave room for a final terminating NULL.)   */
2912 
2913    if ( size == (max_size - 1)) {
2914       max_size += blksize ;
2915       *macropp = realloc(*macropp, max_size * sizmacro );
2916       if ( *macropp == NULL ) {
2917          (void) fprintf(stderr, "Unable to increase size of Macro list.\n");
2918          exit(1);
2919       }
2920 
2921       /* Initialize the new memory allocation */
2922       for ( j = size; j < max_size; j++ ) {
2923          (*macropp)[j].line_no = -1;
2924          (*macropp)[j].refer = 0;
2925          (*macropp)[j].label[0] = '\0' ;
2926          (*macropp)[j].use = USED ;
2927          (*macropp)[j].modflag = UNMODIFIED;
2928          (*macropp)[j].isnull = 0;
2929          (*macropp)[j].trig = -1;
2930          (*macropp)[j].nelem = 0;
2931          (*macropp)[j].total = 0;
2932       }
2933    }
2934 
2935    /* Now add the new macro label, mark caller's identity in flag, */
2936    /* and return the index to its position in the list */
2937 
2938    j = size ;
2939    (void) strncpy2((*macropp)[j].label, macro_label, MACRO_LEN);
2940    (*macropp)[j].refer |= refer ;
2941    size += 1 ;
2942 
2943    return j ;
2944 }
2945 
2946 /*---------------------------------------------------------------------+
2947  | Return next available index in array of intervals.                  |
2948  +---------------------------------------------------------------------*/
intv_index(int ** intvp,unsigned int * size)2949 int intv_index ( int **intvp , unsigned int *size )
2950 {
2951    static int max_size ;
2952    static int sizeint = sizeof(int);
2953    int   j;
2954    int   blksize = 100;
2955 
2956    /* Allocate initial block of memory */
2957    if ( *intvp == NULL ) {
2958       *intvp = calloc(blksize, sizeint );
2959       if ( *intvp == NULL ) {
2960          (void) fprintf(stderr,
2961             "Unable to allocate memory for intervals list.\n");
2962          exit(1);
2963       }
2964       max_size = blksize;
2965       *size = 0;
2966    }
2967 
2968    /* Check to see if there's an available location          */
2969    /* If not, increase the size of the memory allocation.    */
2970    /* (Always leave room for a final termination indicator.) */
2971    if ( *size == (unsigned int)(max_size - 2)) {
2972       max_size += blksize ;
2973       *intvp = realloc(*intvp, max_size * sizeint );
2974       if ( *intvp == NULL ) {
2975          (void) fprintf(stderr,
2976             "Unable to increase size of intervals list.\n");
2977          exit(1);
2978       }
2979    }
2980 
2981    j = *size;
2982    *size += 1;
2983 
2984    return j;
2985 }
2986 
2987 /*---------------------------------------------------------+
2988  | Trace a substitute event to the current generation.     |
2989  +---------------------------------------------------------*/
find_substitute(TEVENT * teventp,int subindex)2990 int find_substitute ( TEVENT *teventp, int subindex )
2991 {
2992    int  j;
2993 
2994    j = subindex;
2995    while ( teventp[j].generation < (unsigned char)current_tevent_generation )
2996       j = teventp[j].link;
2997 
2998    return j;
2999 }
3000 
3001 
3002 /*---------------------------------------------------------+
3003  | Fix the begin or end date for February if necessary.    |
3004  +---------------------------------------------------------*/
fix_february(int dsbegin,int dsend,int year,int * dbeginp,int * dendp)3005 void fix_february ( int dsbegin, int dsend, int year,
3006                                   int *dbeginp, int *dendp )
3007 {
3008    /* Pass back dates as-is in the event there's no change */
3009    *dbeginp = dsbegin;
3010    *dendp   = dsend;
3011 
3012    /* If the user has set FEB_KLUGE=YES in the x10config      */
3013    /* file, interpret either Feb 28 or Feb 29 as the last day */
3014    /* of that month.                                          */
3015    if ( configp->feb_kluge == YES ) {
3016       /* Fix end of February date for the year */
3017       if ( dsbegin == 228 || dsbegin == 229 )
3018          *dbeginp = isleapyear(year) ? 229 : 228 ;
3019       if ( dsend   == 228 || dsend   == 229 )
3020          *dendp   = isleapyear(year) ? 229 : 228 ;
3021 
3022       /* Same thing, if the user has specified reverse dates. */
3023       /* ( parse_sched() will have added 12 months to the end */
3024       /* date, to push it into the following year.)           */
3025 
3026       if ( dsend   == 1428 || dsend == 1429 )
3027          *dendp   = isleapyear(year + 1) ? 1429 : 1428;
3028    }
3029 
3030    /* Fix Feb 30th for the year, regardless */
3031    if ( dsbegin == 230 )
3032       *dbeginp = isleapyear(year) ? 229 : 228;
3033    if ( dsend   == 230 )
3034       *dendp   = isleapyear(year) ? 229 : 228;
3035 
3036    /* Same thing, if user has specified a reversed date range */
3037    if ( dsend   == 1430 )
3038       *dendp   = isleapyear(year + 1) ? 1429 : 1428;
3039 
3040    return;
3041 }
3042 
3043 /*---------------------------------------------------------+
3044  | Determine new tevent begin/end dates, filtering the     |
3045  | date ranges specified in the x10sched file by the       |
3046  | interval of N days (1-366) beginning today for which    |
3047  | the CM11a is to be programmed.                          |
3048  +---------------------------------------------------------*/
resolve_dates(TEVENT ** teventpp,CALEND * calendp,int ndays)3049 void resolve_dates ( TEVENT **teventpp, CALEND *calendp, int ndays )
3050 {
3051    extern int    current_tevent_generation;
3052    int           j, k, cindx, today, year, day_min, day_max;
3053    int           offset, minutes, dayminute, delay, sec_adjust, sec_nomin;
3054    int           dsbegin, dsend, dbegin, dend, beg, end;
3055    int           notify, valb, vald, size;
3056    int           day_zero, arrmax, day_adj;
3057    int           bday[32], eday[32], keep[32], shday[32];
3058 
3059    unsigned char dow_bmap, bmap;
3060    unsigned int  flag;
3061 
3062    unsigned char breaker[734]; /* 366 + 366 + 2 */
3063    int           dst[734];
3064 
3065    /* Nothing to do if no tevents */
3066    if ( !(*teventpp) )
3067       return;
3068 
3069    if ( verbose )
3070       (void)printf("Entering resolve_dates() at generation %d\n",
3071                               current_tevent_generation);
3072 
3073    year       = calendp->year;
3074    today      = calendp->yday;
3075    day_zero   = calendp->day_zero;
3076 
3077    arrmax    = (isleapyear(year) ? 366 : 365) +
3078                (isleapyear(year + 1) ? 366 : 365) + 1;
3079 
3080    j = 0;
3081    while ( (*teventpp)[j].line_no > 0 ) {
3082       if ( (*teventpp)[j].generation != current_tevent_generation ) {
3083          j++;
3084          continue;
3085       }
3086 
3087       /* Get dates (which are stored as mmdd = 100*mm + dd) */
3088       dsbegin    = (*teventpp)[j].sched_beg;
3089       dsend      = (*teventpp)[j].sched_end;
3090 
3091       flag       = (*teventpp)[j].flag;
3092       dow_bmap   = (*teventpp)[j].dow_bmap;
3093       notify     = min((*teventpp)[j].notify, ndays);
3094       sec_adjust = (*teventpp)[j].security;
3095       delay      = (*teventpp)[j].delay;
3096       minutes    = (*teventpp)[j].offset;
3097 
3098       day_adj = 0;
3099 
3100       /* Adjustment for security mode if necessary */
3101       if ( flag & CLOCK_EVENT ) {
3102          minutes = minutes - sec_adjust;
3103          if ( minutes < 0 ) {
3104             /* Event occurs on the previous day */
3105             minutes += 1440;
3106             day_adj = -1;
3107             dow_bmap = rrotbmap( dow_bmap );
3108          }
3109          if ( minutes > 1439 ) {
3110             /* Event occurs on the following day */
3111             minutes -= 1440;
3112             day_adj = 1;
3113             dow_bmap = lrotbmap( dow_bmap );
3114          }
3115       }
3116       else {
3117          minutes = (*teventpp)[j].offset - sec_adjust;
3118       }
3119 
3120       for ( k = 0; k < 32; k++ )
3121          shday[k] = day_adj;
3122 
3123 
3124       /* Initialize breaker and DST breaker arrays */
3125       for ( k = 0; k < 734; k++ ) {
3126          breaker[k] = 0;
3127          dst[k] = 0;
3128       }
3129 
3130       /* Determine the day numbering as of Noon */
3131       dayminute = 12 * 60;
3132 
3133       /* Fix February dates for this year if necessary */
3134       fix_february( dsbegin, dsend, year, &dbegin, &dend );
3135 
3136       /* Get day counts from Jan 1 of this year, offsetting by 1 day so we */
3137       /* can keep track of events which are shifted into the previous day. */
3138 
3139       beg = 1 + day_count( year, dbegin/100, dbegin % 100, dayminute ) - calendp->jan1day;
3140       end = 1 + day_count( year, dend/100, dend % 100, dayminute ) - calendp->jan1day;
3141 
3142       /* Limit the upper end */
3143       end = min(end, arrmax - 1);
3144 
3145       if ( beg < 0 || end < 0 || beg > arrmax - 1 || end > arrmax - 1 ) {
3146          (void)fprintf(stderr,
3147            "Line %2d-%d: resolve_dates(): Begin/End Error 1: Begin = %d, End = %d\n",
3148            (*teventpp)[j].line_no, (*teventpp)[j].pos, beg, end);
3149          exit(1);
3150       }
3151 
3152       for ( k = beg + day_adj; k <= end + day_adj; k++ ) {
3153          breaker[k] = 1;
3154       }
3155 
3156 
3157       /* If the user has specified reverse dates, account for the */
3158       /* beginning of year interval for this year carried over    */
3159       /* from last year.                                          */
3160       if ( dsend > 1231 ) {
3161          fix_february( dsbegin, dsend, year - 1, &dbegin, &dend );
3162          beg = 1 + day_count( year, 1, 1, dayminute ) - calendp->jan1day;
3163          end = 1 + day_count( year, (dend - 1200)/100, (dend - 1200) % 100, dayminute ) - calendp->jan1day;
3164 
3165          for ( k = beg + day_adj; k <= end + day_adj; k++ ) {
3166             breaker[k] = 1;
3167          }
3168       }
3169 
3170       /* Fix February dates for next year if necessary */
3171       fix_february( dsbegin, dsend, year + 1, &dbegin, &dend );
3172 
3173       /* Get day counts for the same dates next year, but still */
3174       /* counting from Jan 1 of this year.                      */
3175 
3176       beg = 1 + day_count( year, dbegin/100 + 12, dbegin % 100, dayminute ) - calendp->jan1day;
3177       end = 1 + day_count( year, dend/100 + 12, dend % 100, dayminute ) - calendp->jan1day;
3178 
3179       /* Limit the upper end */
3180       end = min(end, arrmax - 1);
3181 
3182       if ( beg < 0 || end < 0 ||  beg > arrmax - 1 || end > arrmax - 1 ) {
3183          (void)fprintf(stderr,
3184             "Line %d-%d: resolve_dates(): Begin/End Error 2: Begin = %d, End = %d\n",
3185            (*teventpp)[j].line_no, (*teventpp)[j].pos, beg, end);
3186          exit(1);
3187       }
3188 
3189       for ( k = beg + day_adj; k <= end + day_adj; k++ ) {
3190          breaker[k] = 1;
3191       }
3192 
3193 
3194       sec_nomin  = (flag & SEC_EVENT) ? SECURITY_OFFSET_ADJUST : 0;
3195 
3196       /* If the event is a clock event, set up the dst array to */
3197       /* indicate periods of Standard and Daylight Time         */
3198 
3199       if ( flag & CLOCK_EVENT ) {
3200          for ( k = 0; k < 733; k++ ) {
3201             dst[k + 1] = time_adjust(k, minutes + delay + sec_nomin, LGL2STD);
3202 	 }
3203          dst[0] = dst[arrmax];
3204       }
3205 
3206 
3207       /* Here we create a list of interval begin and end days     */
3208       /* for the specified date range and for periods of Standard */
3209       /* and Daylight Time.                                       */
3210 
3211       size = 0;
3212       valb = breaker[0];
3213       vald = dst[0];
3214       bday[size] = 0;
3215       for ( k = 1; k < arrmax; k++ ) {
3216          if ( breaker[k] != valb || dst[k] != vald ) {
3217             if ( !valb ) {
3218                bday[size] = k;
3219                valb = breaker[k];
3220                vald = dst[k];
3221                continue;
3222             }
3223             eday[size] = k - 1;
3224             bday[++size] = k;
3225             valb = breaker[k];
3226             vald = dst[k];
3227          }
3228       }
3229       if ( valb )
3230          eday[size++] = k - 1;
3231 
3232 
3233       /* Store the date ranges in the tevent structure */
3234 
3235       for ( k = 0; k < size; k++ ) {
3236          beg = bday[k];
3237          end = eday[k];
3238 
3239          offset = minutes;
3240          bmap = dow_bmap;
3241 
3242          /* Adjust for DST as required */
3243          if ( flag & CLOCK_EVENT ) {
3244             offset -= dst[beg];
3245             if ( offset < 0 ) {
3246                /* Event occurs on previous day */
3247                offset += 1440;
3248                bmap = rrotbmap(bmap);
3249                shday[k] = -1;
3250                bday[k] = beg -= 1;
3251                eday[k] = end -= 1;
3252             }
3253             if ( offset > 1439 ) {
3254                /* Event occurs on following day */
3255                offset -= 1440;
3256                bmap = lrotbmap(bmap);
3257                shday[k] = 1;
3258                bday[k] = beg += 1;
3259                eday[k] = end += 1;
3260             }
3261          }
3262 
3263 
3264          /* Now discard/clip the intervals outside the program days */
3265          /* range and create a new generation of tevents for those  */
3266          /* which remain.                                           */
3267 
3268          day_min = today + 1;
3269          day_max = (today + ndays - 1) + 1;
3270 
3271          /* For substitute events */
3272          if ( flag & SUBST_EVENT ) {
3273             if ( beg <= day_min && end >= day_min ) {
3274                beg = end = day_min;
3275                flag &= ~TMP_EVENT;
3276             }
3277             else {
3278                continue;
3279             }
3280          }
3281 
3282          /* For "expire-dd" events only */
3283          if ( notify >= 0 )
3284              day_min = day_max - notify;
3285 
3286          /* Discard intervals entirely outside the program days range */
3287          if ( end < day_min || beg > day_max ) {
3288             keep[k] = 0;
3289             continue;
3290          }
3291          keep[k] = 1;
3292 
3293          beg = max( beg, day_min );
3294          end = min( end, day_max );
3295 
3296          cindx = spawn_child_tevent( teventpp, j );
3297 
3298          /* Store the resolved begin and end days in the new tevent */
3299          /* structure, removing the one-day offset added earlier.   */
3300          /* In HEYU mode, the intervals are offset such that today  */
3301          /* is day 0 in the CM11a clock.                            */
3302 
3303          (*teventpp)[cindx].resolv_beg = (beg - 1 - day_zero);
3304          (*teventpp)[cindx].resolv_end = (end - 1 - day_zero);
3305          (*teventpp)[cindx].offset = offset;
3306          (*teventpp)[cindx].dow_bmap = bmap;
3307          (*teventpp)[cindx].flag = flag & ~(NOCOPY_EVENTS);
3308 
3309          /* Internal check */
3310          if ( beg - 1 - day_zero  < 0 || end < beg ) {
3311             (void)fprintf(stderr,
3312                 "Internal error: Line %d: resolve_dates(): beg = %d, end = %d  day_zero = %d\n",
3313                 (*teventpp)[j].line_no, beg, end, day_zero);
3314          }
3315       }
3316 
3317       /* Go back and check the discarded interval so as to note any event */
3318       /* which was "lost" by being shifted outside the program-days range */
3319       /* due to security or DST day adjustments.                          */
3320 
3321       for ( k = 0; k < size; k++ ) {
3322          if ( keep[k] || shday[k] == 0 )
3323             continue;
3324          if ( shday[k] == -1 && eday[k] == (today + 1) - 1 ) {
3325            (*teventpp)[j].lostday = today - day_zero;
3326            (*teventpp)[j].flag |= LOST_EVENT;
3327          }
3328          if ( shday[k] ==  1 && bday[k] == (today + 1) + 1 ) {
3329             (*teventpp)[j].lostday = today + ndays - 1 - day_zero;
3330             (*teventpp)[j].flag |= LOST_EVENT;
3331          }
3332       }
3333       j++ ;
3334    }
3335 
3336 
3337    /* Disable all (unused) temporary events in the child generations */
3338    j = 0;
3339    while ( (*teventpp)[j].line_no > 0 ) {
3340       if ( (*teventpp)[j].generation == (current_tevent_generation + 1) &&
3341            (*teventpp)[j].flag & TMP_EVENT ) {
3342          (*teventpp)[j].flag = NO_EVENT;
3343       }
3344       j++;
3345    }
3346 
3347 
3348    /* Update the generation regardless of whether */
3349    /* or not there are any active tevents.        */
3350 
3351    (void) update_current_tevent_generation_anyway();
3352 
3353 
3354    return;
3355 }
3356 
3357 /*---------------------------------------------------------+
3358  | Resolve Timer options DAWNGT, DAWNLT, DUSKGT, DUSKLT    |
3359  +---------------------------------------------------------*/
resolve_dawndusk_options(TEVENT ** teventpp,CALEND * calendp)3360 void resolve_dawndusk_options ( TEVENT **teventpp, CALEND *calendp )
3361 {
3362    extern int    current_tevent_generation;
3363 
3364    unsigned char ddoptions;
3365    int           j, k, cindx, beg, end, val, size;
3366    int           breaker[366];
3367    int           scode[366], dawn[366], dusk[366];
3368    int           intv[32];
3369    int           day_zero, opttime, stime;
3370 
3371    double        latitude, longitude;
3372    time_t        tzone;
3373 
3374    long          julianday;
3375 
3376    /* See if we need to do anything here */
3377 
3378    if ( *teventpp == NULL )
3379       return;
3380 
3381    ddoptions = 0;
3382    j = 0;
3383    while ( (*teventpp)[j].line_no > 0 ) {
3384       if ( (*teventpp)[j].generation == current_tevent_generation &&
3385            (*teventpp)[j].flag != NO_EVENT ) {
3386          ddoptions |= (*teventpp)[j].ddoptions;
3387       }
3388       j++;
3389    }
3390    if ( ddoptions == 0 )
3391       return;
3392 
3393    /* Load arrays with Dawn and Dusk values */
3394 
3395    /* Get geographic location and timezone from   */
3396    /* that stored in the global CONFIG structure. */
3397 
3398    if ( configp->loc_flag != (LATITUDE | LONGITUDE) ) {
3399       (void)fprintf(stderr,
3400          "LATITUDE and/or LONGITUDE not specified in %s\n",
3401 	    pathspec(CONFIG_FILE));
3402       exit(1);
3403    }
3404 
3405    latitude  = configp->latitude;
3406    longitude = configp->longitude;
3407    tzone     = configp->tzone;
3408 
3409    /* Calculate sunrise and sunset for 366 days from today's */
3410    /* date and store in arrays.                              */
3411 
3412    /* Get today's Julian Day - the big number, not the day   */
3413    /* of the year.                                           */
3414 
3415    julianday = daycount2JD(calendp->today);
3416 
3417    for ( j = 0; j < 366; j++ ) {
3418       scode[j] = suntimes( latitude, longitude, tzone, julianday,
3419          configp->sunmode, configp->sunmode_offset, &dawn[j], &dusk[j], NULL, NULL );
3420       julianday++ ;
3421    }
3422 
3423    /* For Arctic/Antarctic regions - Substitute a dawn and/or  */
3424    /* dusk time for days when the sun is continually above or  */
3425    /* below the horizon, or no sunrise or sunset.              */
3426 
3427    for ( j = 0; j < 366; j++ ) {
3428       if ( scode[j] & UP_ALL_DAY ) {
3429          dawn[j] = 1;     /* 00:01 */
3430 	 dusk[j] = 1438;  /* 23:58 */
3431       }
3432       else if ( scode[j] & DOWN_ALL_DAY ) {
3433 	 dawn[j] = 1438;
3434 	 dusk[j] = 1;
3435       }
3436       else if ( scode[j] & NO_SUNRISE ) {
3437 	 dawn[j] = 1;
3438       }
3439       else if ( scode[j] & NO_SUNSET ) {
3440 	 dusk[j] = 1438;
3441       }
3442    }
3443 
3444 
3445    j = 0;
3446    while ( (*teventpp)[j].line_no > 0 ) {
3447       if ( (*teventpp)[j].generation != current_tevent_generation ||
3448            (*teventpp)[j].flag == NO_EVENT ) {
3449          j++;
3450          continue;
3451       }
3452       if ( (ddoptions = (*teventpp)[j].ddoptions) == 0 ) {
3453          increment_tevent_generation( *teventpp, j );
3454          j++;
3455          continue;
3456       }
3457 
3458       for ( k = 0; k < 366; k++ )
3459          breaker[k] = 1;
3460 
3461       day_zero = calendp->day_zero;
3462 
3463       if ( ddoptions & DAWNGT ) {
3464          opttime = (*teventpp)[j].dawngt;
3465          for ( k = 0; k < 366; k++ ) {
3466             stime = opttime - time_adjust(k + day_zero, opttime, LGL2STD);
3467             if ( dawn[k] <= stime )
3468                breaker[k] = 0;
3469          }
3470       }
3471       if ( ddoptions & DAWNLT ) {
3472          opttime = (*teventpp)[j].dawnlt;
3473          for ( k = 0; k < 366; k++ ) {
3474             stime = opttime - time_adjust(k + day_zero, opttime, LGL2STD);
3475             if ( dawn[k] >= stime )
3476                breaker[k] = 0;
3477          }
3478       }
3479       if ( ddoptions & DUSKGT ) {
3480          opttime = (*teventpp)[j].duskgt;
3481          for ( k = 0; k < 366; k++ ) {
3482             stime = opttime - time_adjust(k + day_zero, opttime, LGL2STD);
3483             if ( dusk[k] <= stime )
3484                breaker[k] = 0;
3485          }
3486       }
3487       if ( ddoptions & DUSKLT ) {
3488          opttime = (*teventpp)[j].dusklt;
3489          for ( k = 0; k < 366; k++ ) {
3490             stime = opttime - time_adjust(k + day_zero, opttime, LGL2STD);
3491             if ( dusk[k] >= stime )
3492                breaker[k] = 0;
3493          }
3494       }
3495 
3496       beg = (*teventpp)[j].resolv_beg;
3497       end = (*teventpp)[j].resolv_end;
3498       val = breaker[beg];
3499 
3500       size = 0;
3501       intv[size++] = beg;
3502       for ( k = beg; k <= end; k++ ) {
3503          if ( val != breaker[k] ) {
3504             if ( k > beg ) {
3505                intv[size++] = k - 1;
3506                intv[size++] = k;
3507             }
3508             val = breaker[k];
3509          }
3510       }
3511       intv[size++] = end;
3512 
3513       for ( k = 0; k < size; k += 2 ) {
3514          beg = intv[k];
3515          end = intv[k + 1];
3516 
3517          cindx = spawn_child_tevent( teventpp, j );
3518 
3519          (*teventpp)[cindx].resolv_beg = beg;
3520          (*teventpp)[cindx].resolv_end = end;
3521          if ( breaker[beg] == 0 )
3522             (*teventpp)[cindx].flag |= CANCEL_EVENT;
3523       }
3524 
3525       j++;
3526    }
3527    (void) update_current_tevent_generation();
3528 
3529 
3530    return;
3531 }
3532 
3533 
3534 /*---------------------------------------------------------------------+
3535  | Duplicate a macro and its elements, giving it a new label (which    |
3536  | must be unique), and return the index to it.                        |
3537  +---------------------------------------------------------------------*/
macro_dupe(MACRO ** macropp,int macindex,unsigned char ** elementp,char * newlabel)3538 int macro_dupe ( MACRO **macropp, int macindex,
3539                                unsigned char **elementp, char *newlabel )
3540 {
3541    int  j, total, eindx;
3542 
3543    /* Verify the new label is unique */
3544    j = 0;
3545    while( (*macropp)[j].line_no > 0 )  {
3546       if ( strcmp(newlabel, (*macropp)[j].label) == 0 ) {
3547          (void)fprintf(stderr, "Internal error: New macro label is not unique.\n");
3548          exit(1);
3549       }
3550       j++;
3551    }
3552 
3553    j = macro_index( macropp, newlabel, DERIVED_MACRO );
3554 
3555    memcpy((void *)(*macropp + j), (void *)(*macropp + macindex), sizeof(MACRO));
3556 
3557    (void)strncpy2((*macropp)[j].label, newlabel, MACRO_LEN);
3558    (*macropp)[j].line_no = 9999;
3559    (*macropp)[j].modflag |= DELAY_MOD;
3560 
3561    /* Copy the macro elements */
3562    total = (*macropp)[macindex].total;
3563    eindx = macro_element_index(elementp, total);
3564    memcpy((void *)(*elementp + eindx),
3565            (void *)(*elementp + (*macropp)[macindex].element),
3566                  total * sizeof(unsigned char));
3567    (*macropp)[j].element = eindx;
3568 
3569    return j;
3570 }
3571 
3572 
3573 /*---------------------------------------------------------------------+
3574  | Special duplicate macro.                                            |
3575  | Check to see whether a macro with the original label and zero       |
3576  | delay already exists.  If so, return its index.  If not, create a   |
3577  | duplicate with a '_' prefixed label and zero delay.                 |
3578  +---------------------------------------------------------------------*/
macro_dupe_special(MACRO ** macropp,int macindex,unsigned char ** elementp)3579 int macro_dupe_special ( MACRO **macropp, int macindex,
3580                                               unsigned char **elementp )
3581 {
3582    int  j, total, eindx;
3583    char *label;
3584    char labuf[MACRO_LEN + 2];
3585 
3586    label = (*macropp)[macindex].label;
3587    labuf[0] = '_';
3588    (void)strncpy2(labuf + 1, label, sizeof(labuf) - 2);
3589    labuf[MACRO_LEN] = '\0';
3590 
3591    j = 0;
3592    while( (*macropp)[j].line_no > 0 )  {
3593       if ( (strcmp(label, (*macropp)[j].label) == 0  ||
3594             strcmp(labuf, (*macropp)[j].label) == 0)  &&
3595            (*macropp)[j].delay == 0 ) {
3596          return j;
3597       }
3598       j++;
3599    }
3600 
3601    j = macro_index( macropp, labuf, DERIVED_MACRO );
3602 
3603    memcpy((void *)(*macropp + j), (void *)(*macropp + macindex), sizeof(MACRO));
3604 
3605    (void)strncpy2((*macropp)[j].label, labuf, MACRO_LEN);
3606    (*macropp)[j].line_no = 9999;
3607    (*macropp)[j].delay = 0;
3608    (*macropp)[j].modflag |= DELAY_MOD;
3609 
3610    /* Copy the macro elements */
3611    total = (*macropp)[macindex].total;
3612    eindx = macro_element_index(elementp, total);
3613    memcpy((void *)(*elementp + eindx),
3614            (void *)(*elementp + (*macropp)[macindex].element),
3615                  total * sizeof(unsigned char));
3616    (*macropp)[j].element = eindx;
3617 
3618    return j;
3619 }
3620 
3621 /*---------------------------------------------------------------------+
3622  | Clear tevent tree of disabled events by spawning another generation |
3623  | without them                                                        |
3624  +---------------------------------------------------------------------*/
clear_disabled_events(TEVENT ** teventpp)3625 void clear_disabled_events ( TEVENT **teventpp )
3626 {
3627    int j;
3628 
3629    if ( *teventpp == NULL )
3630       return;
3631 
3632    if ( verbose )
3633       (void)printf("Entering clear_disabled_events() at generation %d\n",
3634                               current_tevent_generation);
3635 
3636    j = 0;
3637    while ( (*teventpp)[j].line_no > 0 ) {
3638       if ( (*teventpp)[j].generation == current_tevent_generation &&
3639            (*teventpp)[j].flag != NO_EVENT &&
3640            ((*teventpp)[j].flag & CANCEL_EVENT) == 0 ) {
3641          (void) spawn_child_tevent( teventpp, j );
3642       }
3643       j++;
3644    }
3645 
3646    (void) update_current_tevent_generation_anyway();
3647 
3648    return;
3649 }
3650 
3651 /*---------------------------------------------------------------------+
3652  | Generate a pseudo random variation +/- 30 min for an offset         |
3653  | but remaining within the range 0-1439                               |
3654  +---------------------------------------------------------------------*/
randomize_offset(int offset)3655 int randomize_offset ( int offset )
3656 {
3657    /* Balanced pseudo-randoms */
3658    static int harrand[24] = {6,18,19,28,21,12,10,11,23,6,11,4,21,
3659                          15,8,22,26,22,24,15,12,25,4,17};
3660    time_t   now;
3661    int      r;
3662    long int day;
3663 
3664    time(&now);
3665    day = ((long)now - configp->tzone)/ 86400L;
3666    r = (day % 2L) ? harrand[day % 23L] : -harrand[day % 23L];
3667    if ( (offset + r) < 0 || (offset + r) > 1439 )
3668       return (offset - r);
3669    return (offset + r);
3670 }
3671 
3672 /*---------------------------------------------------------------------+
3673  | For events where security mode is set, 30 minutes are to be         |
3674  | subtracted from the event time so the "random" variation will be    |
3675  | +/- 30 minutes when the CM11a adds the variable time 0-60 minutes   |
3676  | in this mode.                                                       |
3677  | However since the CM11a can not wrap times past 23:59 into the next |
3678  | day, the adjusted time cannot be earlier than 0:00 or later than    |
3679  | 22:59.  If such would otherwise be the case, we use the delayed     |
3680  | macro capability of the CM11a to correct the situation.             |
3681  |                                                                     |
3682  | This function does not actually change the timer offsets, but       |
3683  | determines the amount of the adjustment, leaving the actual         |
3684  | adjustment to function resolve_dates().                             |
3685  +---------------------------------------------------------------------*/
security_adjust_legal(TEVENT ** teventpp,MACRO ** macropp,unsigned char ** elementp)3686 void security_adjust_legal ( TEVENT **teventpp, MACRO **macropp,
3687                                               unsigned char **elementp )
3688 {
3689    int           j, count, modify;
3690    int           macro, mindx, submac, cindx, sindx;
3691    int           good1, good2, offset, delay, event, delta;
3692    char          *label;
3693    unsigned int  flag;
3694 
3695    if ( !(*teventpp) )
3696       return;
3697 
3698    if ( verbose )
3699       (void)printf("Entering security_adjust_legal() at generation %d\n",
3700                               current_tevent_generation);
3701 
3702    /* These are the lower and upper bounds of the region where     */
3703    /* the event time can simply be adjusted without problems, i.e. */
3704    /* when after subtracting 30 minutes the resulting time falls   */
3705    /* between 0:00 and 22:59 at any time of year.                  */
3706 
3707    good1 = SECURITY_OFFSET_ADJUST + configp->dstminutes;
3708    good2 = 1440 - SECURITY_OFFSET_ADJUST - 1;
3709 
3710    /* See if there are any security clock events that will require */
3711    /* modifying the timer and/or macro.                            */
3712    j = 0;
3713    modify = NO;
3714    while ( (*teventpp)[j].line_no > 0 ) {
3715       count++;
3716 
3717       if ( (*teventpp)[j].generation != current_tevent_generation ||
3718            (flag = (*teventpp)[j].flag) == NO_EVENT ||
3719            (flag & SEC_EVENT) == 0 ||
3720            (flag & (DAWN_EVENT | DUSK_EVENT)) != 0 ) {
3721          j++;
3722          continue;
3723       }
3724 
3725       event = (*teventpp)[j].offset + (*teventpp)[j].delay;
3726 
3727       if ( event % 1440 >= good1 && event % 1440 <= good2 ) {
3728          j++;
3729          continue;
3730       }
3731       else {
3732          modify = YES;
3733          j++;
3734          continue;
3735       }
3736    }
3737    count = j;
3738 
3739    /* Simple situation - store the adjustment to be made later. */
3740    if ( modify == NO ) {
3741       for ( j = 0; j < count; j++ ) {
3742          if ( (*teventpp)[j].generation != current_tevent_generation ||
3743               (*teventpp)[j].flag == NO_EVENT ) {
3744             continue;
3745 	 }
3746 	 if ( (*teventpp)[j].flag & SEC_EVENT )
3747             (*teventpp)[j].security = SECURITY_OFFSET_ADJUST;
3748 	 else
3749             (*teventpp)[j].security = 0;
3750       }
3751       return ;
3752    }
3753 
3754    /* More complex case - create a new timer and possibly a delayed */
3755    /* macro if necessary.                                           */
3756    j = 0;
3757    while ( (*teventpp)[j].line_no > 0 ) {
3758       if ( (*teventpp)[j].generation != current_tevent_generation ||
3759            (*teventpp)[j].flag == NO_EVENT ) {
3760          j++;
3761          continue;
3762       }
3763 
3764       cindx = spawn_child_tevent( teventpp, j );
3765 
3766       flag   = (*teventpp)[cindx].flag;
3767       offset = (*teventpp)[cindx].offset;
3768       delay  = (*teventpp)[cindx].delay;
3769       event  = offset + delay;
3770 
3771       if ( !(flag & SEC_EVENT) ) {
3772          (*teventpp)[cindx].security = 0;
3773          j++;
3774          continue;
3775       }
3776 
3777       if ( flag & (DAWN_EVENT | DUSK_EVENT) ||
3778            (event >= good1 && event <= good2) ) {
3779          (*teventpp)[cindx].security = SECURITY_OFFSET_ADJUST;
3780          j++;
3781          continue;
3782       }
3783 
3784       /* Create a new macro duplicating the original */
3785 
3786       macro = (*teventpp)[j].macro;
3787       label = unique_macro_name( &macro, 1, *macropp, SECUR_MAC_PREFIX );
3788 
3789       mindx = macro_dupe ( macropp, macro, elementp, label );
3790 
3791       (*macropp)[mindx].line_no = 9999;
3792       (*macropp)[mindx].delay = delay = 0;
3793       (*macropp)[mindx].modflag |= DELAY_MOD;
3794 
3795       (*teventpp)[cindx].offset = offset = event;
3796       (*teventpp)[cindx].macro = mindx;
3797       (*teventpp)[cindx].flag |= (PRT_EVENT);
3798       (*teventpp)[cindx].print += 1;
3799       (*teventpp)[cindx].plink = j;
3800 
3801 
3802       if ( offset < good1 ) {
3803          delta = good1 - offset;
3804          if ( delay >= delta ) {
3805             /* Delay can be decreased, keeping the event in the same day */
3806             delay -= delta;
3807             (*teventpp)[cindx].security = SECURITY_OFFSET_ADJUST - delta;
3808          }
3809          else {
3810             /* Delay must be increased, shifting event into the previous day */
3811             delta = 1440 + offset - good2;
3812             delay += delta;
3813             (*teventpp)[cindx].security = SECURITY_OFFSET_ADJUST + delta;
3814 
3815             /* Create a non-security substitute event for use as first day if needed */
3816             sindx = spawn_child_tevent( teventpp, j );
3817             submac = macro_dupe_special(macropp, (*teventpp)[j].macro, elementp);
3818 
3819             event = (*teventpp)[j].offset + (*teventpp)[j].delay;
3820             (*teventpp)[sindx].offset = randomize_offset(event % 1440);
3821             (*teventpp)[sindx].delay = 0;
3822             (*teventpp)[sindx].macro = submac;
3823             (*teventpp)[sindx].flag |= (SUBST_EVENT | TMP_EVENT);
3824             (*teventpp)[sindx].plink = j;
3825             if ( submac != (*teventpp)[j].macro ) {
3826                (*teventpp)[sindx].flag |= PRT_EVENT;
3827                (*teventpp)[sindx].print += 1;
3828             }
3829             (*teventpp)[sindx].flag &= ~SEC_EVENT;
3830 
3831          }
3832       }
3833       else  {
3834          /* Offset > good2 */
3835          delta = offset - good2;
3836          if ( delay < 240 - delta ) {
3837             /* Delay can be increased - keeping event in the same day */
3838             delay += delta;
3839             (*teventpp)[cindx].security = SECURITY_OFFSET_ADJUST + delta;
3840          }
3841          else {
3842             /* Delay can only be decreased, shifting event into the following day */
3843             delta = 1440 + good1 - offset;
3844             delay -= delta;
3845             (*teventpp)[cindx].security = SECURITY_OFFSET_ADJUST - delta;
3846             /* Create a non-security substitute event for use as last day if needed */
3847             sindx = spawn_child_tevent( teventpp, j );
3848             submac = macro_dupe_special(macropp, (*teventpp)[j].macro, elementp);
3849 
3850             event = (*teventpp)[j].offset + (*teventpp)[j].delay;
3851             (*teventpp)[sindx].offset = randomize_offset(event % 1440);
3852             (*teventpp)[sindx].delay = 0;
3853             (*teventpp)[sindx].macro = submac;
3854             (*teventpp)[sindx].flag |= (SUBST_EVENT | TMP_EVENT);
3855             (*teventpp)[sindx].flag &= ~SEC_EVENT;
3856          }
3857       }
3858 
3859       (*macropp)[mindx].delay = delay;
3860       (*teventpp)[cindx].delay = delay;
3861 
3862       j++;
3863    }
3864 
3865    (void)update_current_tevent_generation();
3866 
3867    return;
3868 }
3869 
3870 /*---------------------------------------------------------------------+
3871  | Warn of duplicate timer in schedule file                            |
3872  +---------------------------------------------------------------------*/
warn_duplicate_timer(TIMER * timerp)3873 int warn_duplicate_timer ( TIMER *timerp )
3874 {
3875    int  j, k, count;
3876    int  *arrp;
3877    static int sizint = sizeof(int);
3878 
3879    if ( !timerp )
3880       return 0;
3881 
3882    j = 0;
3883    while ( timerp[j].line_no > 0 )
3884       j++;
3885 
3886    count = j;
3887    if ( (arrp = calloc(count, sizint)) == NULL ) {
3888       fprintf(stderr, "warn_duplicate_timer() - Unable to allocate memory.\n");
3889       exit(1);
3890    }
3891 
3892    for ( j = 0; j < count; j++ )
3893       arrp[j] = 1;
3894 
3895    for ( j = 0; j < count; j++ ) {
3896          if ( arrp[j] == 0 )
3897             continue;
3898       for ( k = j + 1; k < count; k++ ) {
3899          if ( arrp[k] == 0 )
3900             continue;
3901          if ( timerp[j].generation   == timerp[k].generation   &&
3902               timerp[j].dow_bmap     == timerp[k].dow_bmap     &&
3903               timerp[j].sched_beg    == timerp[k].sched_beg    &&
3904               timerp[j].sched_end    == timerp[k].sched_end    &&
3905               timerp[j].offset_start == timerp[k].offset_start &&
3906               timerp[j].offset_stop  == timerp[k].offset_stop  &&
3907               timerp[j].flag_start   == timerp[k].flag_start   &&
3908               timerp[j].flag_stop    == timerp[k].flag_stop    &&
3909               timerp[j].notify       == timerp[k].notify       &&
3910               timerp[j].macro_start  == timerp[k].macro_start  &&
3911               timerp[j].macro_stop   == timerp[k].macro_stop     ) {
3912             fprintf(stderr, "Lines %02d,%02d: Warning - Duplicate timer\n",
3913                timerp[j].line_no, timerp[k].line_no);
3914             arrp[k] = 0;
3915          }
3916       }
3917    }
3918    free(arrp);
3919    return 0;
3920 }
3921 
3922 /*---------------------------------------------------------------------+
3923  | Warn of duplicate timed event in input timers.                      |
3924  +---------------------------------------------------------------------*/
warn_duplicate_tevent(TEVENT * teventp)3925 int warn_duplicate_tevent ( TEVENT *teventp )
3926 {
3927    int  j, k, count;
3928    int  *arrp;
3929    static int sizint = sizeof(int);
3930 
3931    if ( !teventp )
3932       return 0;
3933 
3934    j = 0;
3935    while ( teventp[j].line_no > 0 )
3936       j++;
3937 
3938    count = j;
3939    if ( (arrp = calloc(count, sizint)) == NULL ) {
3940       fprintf(stderr, "warn_duplicate_tevent() - Unable to allocate memory.\n");
3941       exit(1);
3942    }
3943 
3944    for ( j = 0; j < count; j++ )
3945       arrp[j] = 1;
3946 
3947    for ( j = 0; j < count; j++ ) {
3948          if ( arrp[j] == 0 )
3949             continue;
3950       for ( k = j + 1; k < count; k++ ) {
3951          if ( arrp[k] == 0 )
3952             continue;
3953          if ( teventp[j].generation == teventp[k].generation   &&
3954               teventp[j].dow_bmap   == teventp[k].dow_bmap     &&
3955               teventp[j].sched_beg  == teventp[k].sched_beg    &&
3956               teventp[j].sched_end  == teventp[k].sched_end    &&
3957               teventp[j].offset     == teventp[k].offset       &&
3958               teventp[j].flag       == teventp[k].flag         &&
3959               teventp[j].notify     == teventp[k].notify       &&
3960               teventp[j].macro      == teventp[k].macro           ) {
3961             fprintf(stderr,
3962                "Lines %02d,%02d: Warning - Duplicate Timed Event\n",
3963                   teventp[j].line_no, teventp[k].line_no);
3964             arrp[k] = 0;
3965          }
3966       }
3967    }
3968    free(arrp);
3969    return 0;
3970 }
3971 
3972 /*---------------------------------------------------------------------+
3973  | Warn of duplicate trigger initiator in schedule file                |
3974  +---------------------------------------------------------------------*/
warn_duplicate_trigger(TRIGGER * triggerp)3975 int warn_duplicate_trigger ( TRIGGER *triggerp )
3976 {
3977    int  j, k, count;
3978    int  *arrp;
3979    static char *offon[] = {"off", "on"};
3980    static int sizint = sizeof(int);
3981 
3982    if ( !triggerp )
3983       return 0;
3984 
3985    j = 0;
3986    while ( triggerp[j].line_no > 0 )
3987       j++;
3988 
3989    count = j;
3990    if ( (arrp = calloc(count, sizint)) == NULL ) {
3991       fprintf(stderr, "warn_duplicate_trigger() - Unable to allocate memory.\n");
3992       exit(1);
3993    }
3994 
3995    for ( j = 0; j < count; j++ )
3996       arrp[j] = 1;
3997 
3998    for ( j = 0; j < count; j++ ) {
3999          if ( arrp[j] == 0 )
4000             continue;
4001       for ( k = j + 1; k < count; k++ ) {
4002          if ( arrp[k] == 0 )
4003             continue;
4004          if ( triggerp[j].housecode == triggerp[k].housecode &&
4005               triggerp[j].unitcode  == triggerp[k].unitcode  &&
4006               triggerp[j].command   == triggerp[k].command      ) {
4007             fprintf(stderr,
4008                "Lines %02d,%02d: Warning - Duplicate trigger '%c%d %s'\n",
4009                   triggerp[j].line_no, triggerp[k].line_no,
4010                   code2hc(triggerp[j].housecode),
4011                   code2unit(triggerp[j].unitcode),
4012                   offon[triggerp[j].command] );
4013             arrp[k] = 0;
4014          }
4015       }
4016    }
4017    free(arrp);
4018    return 0;
4019 }
4020 
4021 
4022 /*---------------------------------------------------------------------+
4023  | Parse schedule file.  Store parameters for timers, triggers, and    |
4024  | macros in their respective structure arrays.                        |
4025  +---------------------------------------------------------------------*/
parse_sched(FILE * fd_sched,TIMER ** timerpp,TRIGGER ** triggerpp,MACRO ** macropp,unsigned char ** elementp)4026 int parse_sched ( FILE *fd_sched, TIMER **timerpp, TRIGGER **triggerpp,
4027                               MACRO **macropp, unsigned char **elementp )
4028 {
4029    extern int  line_no, current_timer_generation, timer_generation_delta;
4030 
4031    int         j;
4032    int         index;
4033    int         err, errors;
4034 
4035    char        buffer[LINE_LEN];
4036    char        cmdbuf[256];
4037    char        cmdbufl[256];
4038    char        *sp, *tail;
4039 
4040    /* Make two passes through the schedule file, the first */
4041    /* concerned only with configuration overrides.         */
4042 
4043    line_no = 0;
4044    errors = 0;
4045    while ( fgets(buffer, LINE_LEN, fd_sched) != NULL ) {
4046       line_no++ ;
4047 
4048       /* Make sure buffer is properly terminated */
4049       buffer[LINE_LEN - 1] = '\0';
4050 
4051       /* Get rid of comment strings */
4052       if ( (sp = strchr(buffer, '#')) != NULL )
4053          *sp = '\0';
4054 
4055       /* Verify the entire line fits into the buffer */
4056       if ( strlen(buffer) == (LINE_LEN - 2) ) {
4057          (void) fprintf(stderr,
4058            "Line %02d: Line too long.\n", line_no);
4059          if ( ++errors > MAX_ERRORS )
4060             return errors;
4061          continue;
4062       }
4063 
4064       /* Remove leading and trailing whitespace */
4065       (void) strtrim(buffer);
4066 
4067       if ( *buffer == '\0')
4068          continue;
4069 
4070       tail = buffer;
4071 
4072       /* Get the first token on the line. */
4073       (void) get_token(cmdbuf, &tail, " \t", 255);
4074 
4075       strncpy2(cmdbufl, cmdbuf, sizeof(cmdbufl) - 1);
4076       strlower(cmdbufl);
4077 
4078       if ( strcmp(cmdbufl, "config") == 0 )  {
4079          err = parse_config_tail(tail, SRC_SCHED);
4080          if ( err || *error_message() != '\0' ) {
4081             fprintf(stderr,
4082               "Line %02d: %s\n", line_no, error_message());
4083             clear_error_message();
4084          }
4085          errors += err;
4086          if ( errors > MAX_ERRORS )
4087             return errors;
4088       }
4089    }
4090    finalize_config(CONFIG_INIT);
4091 
4092 
4093    /* Second pass */
4094    rewind(fd_sched);
4095 
4096    line_no = 0;
4097    while ( fgets(buffer, LINE_LEN, fd_sched) != NULL ) {
4098       line_no++ ;
4099 
4100       /* Make sure buffer is properly terminated */
4101       buffer[LINE_LEN - 1] = '\0';
4102 
4103       /* Get rid of comment strings */
4104       if ( (sp = strchr(buffer, '#')) != NULL )
4105          *sp = '\0';
4106 
4107       /* Verify the entire line fit into the buffer */
4108       if ( strlen(buffer) == (LINE_LEN - 2) ) {
4109          (void) fprintf(stderr,
4110            "Line %02d: Line too long.\n", line_no);
4111          if ( ++errors > MAX_ERRORS )
4112             return errors;
4113          continue;
4114       }
4115 
4116       /* Remove leading and trailing whitespace */
4117       (void) strtrim(buffer);
4118 
4119       if ( *buffer == '\0')
4120          continue;
4121 
4122       tail = buffer;
4123 
4124       /* Get the first token on the line. */
4125       (void) get_token(cmdbuf, &tail, " \t", 255);
4126 
4127       strncpy2(cmdbufl, cmdbuf, sizeof(cmdbufl) - 1);
4128       strlower(cmdbufl);
4129 
4130       /* Pass the tail of the command line to the appropriate */
4131       /* parsing function.                                    */
4132 
4133       if ( strcmp(cmdbufl, "timer") == 0 )  {
4134          errors += parse_timer(timerpp, macropp, tail);
4135       }
4136       else if ( strcmp(cmdbufl, "trigger") == 0 ) {
4137          errors += parse_trigger(triggerpp, macropp, tail);
4138       }
4139       else if ( strcmp(cmdbufl, "macro") == 0 ) {
4140          errors += parse_macro(macropp, elementp, tail);
4141       }
4142       else if ( strcmp(cmdbufl, "config") == 0 ) {
4143          /* Ignore config lines in this pass */
4144          continue;
4145       }
4146       else if ( strcmp(cmdbufl, "section") == 0 ) {
4147          continue;
4148       }
4149       else {
4150          (void) fprintf(stderr,
4151            "Line %02d: Unrecognized keyword '%s' in schedule file.\n", line_no, cmdbuf);
4152          errors++ ;
4153       }
4154       if ( errors > MAX_ERRORS )
4155          return errors;
4156    }
4157 
4158 
4159    /* Verify that all macros referenced by timers and triggers have */
4160    /* been defined, and that macros have not been multiply defined. */
4161    /* Warn if a macro is unused by timers or triggers.              */
4162    /* Also, link the timers together in a linked list and set the   */
4163    /* generation as generation 0.                                   */
4164    /* Also store the macro delay times in the timer.                */
4165 
4166    current_timer_generation = 0;
4167    timer_generation_delta = 0;
4168 
4169    if (*timerpp) {
4170       j = 0;
4171       while ( (*timerpp)[j].line_no > 0 ) {
4172          (*timerpp)[j].link = j + 1;
4173          (*timerpp)[j].generation = 0;
4174          index = (*timerpp)[j].macro_start ;
4175          if ( !((*macropp)[index].refer & MACRO_PARSER) ) {
4176             (void) fprintf(stderr, "Line %02d: Macro '%s' is not defined.\n",
4177                       (*timerpp)[j].line_no, (*macropp)[index].label );
4178             if ( ++errors > MAX_ERRORS )
4179                return errors;
4180          }
4181          (*timerpp)[j].delay_start = (*macropp)[index].delay;
4182 
4183 
4184          index = (*timerpp)[j].macro_stop ;
4185          if ( !((*macropp)[index].refer & MACRO_PARSER) ) {
4186             (void) fprintf(stderr, "Line %02d: Macro '%s' is not defined.\n",
4187                       (*timerpp)[j].line_no, (*macropp)[index].label );
4188             if ( ++errors > MAX_ERRORS )
4189                return errors;
4190          }
4191          (*timerpp)[j].delay_stop = (*macropp)[index].delay;
4192 
4193          j++;
4194       }
4195       /* Terminate the linked list with a -1 */
4196       (*timerpp)[j - 1].link = -1;
4197 
4198       warn_duplicate_timer(*timerpp);
4199    }
4200 
4201    if (*triggerpp) {
4202       j = 0;
4203       while ( (*triggerpp)[j].line_no > 0 ) {
4204          index = (*triggerpp)[j].macro ;
4205          if ( !((*macropp)[index].refer & MACRO_PARSER) ) {
4206             (void) fprintf(stderr, "Line %02d: Macro '%s' is not defined.\n",
4207                       (*triggerpp)[j].line_no, (*macropp)[index].label );
4208             errors++;
4209          }
4210          if ( errors > MAX_ERRORS )
4211             return errors;
4212 
4213          j++;
4214       }
4215 
4216       if ( configp->macterm == YES )
4217          warn_duplicate_trigger(*triggerpp);
4218    }
4219 
4220    if (*macropp) {
4221       /* Start with j = 1, ignoring the null macro */
4222       j = 1;
4223       while ( (*macropp)[j].line_no > 0 ) {
4224          if ( (*macropp)[j].refer & MACRO_DUPLICATE ) {
4225             (void) fprintf(stderr, "Line %02d: Macro '%s' is multiply defined.\n",
4226                       (*macropp)[j].line_no, (*macropp)[j].label );
4227             if ( ++errors > MAX_ERRORS )
4228                return errors;
4229          }
4230          if ( !((*macropp)[j].refer & (TIMER_PARSER | TRIGGER_PARSER)) ) {
4231             (void) fprintf(stderr,
4232                "Line %02d: Warning: Macro '%s' is not called by a timer or trigger and will be ignored.\n",
4233                       (*macropp)[j].line_no, (*macropp)[j].label );
4234          }
4235          j++;
4236       }
4237    }
4238    line_no = 0;
4239    return errors;
4240 }
4241 
4242 /*---------------------------------------------------------------------+
4243  | Parse the tail of a timer command.                                  |
4244  +---------------------------------------------------------------------*/
parse_timer_old_old(TIMER ** timerpp,MACRO ** macropp,char * tail)4245 int parse_timer_old_old( TIMER **timerpp, MACRO **macropp, char *tail)
4246 {
4247    extern   int line_no;
4248 
4249    int      mdays[] = {0,31,30,31,30,31,30,31,31,30,31,30,31};
4250 
4251    unsigned int    flag;
4252    int      count;
4253    int      time;
4254    int      index, macindex;
4255    int      month, day;
4256 
4257    char     weekdays[16];
4258    char     daterange[16];
4259    char     datebegin[16], dateend[16];
4260    char     tstart[20], tstop[20];
4261    char     macstart[40], macstop[40];
4262 
4263    count = sscanf(tail, "%10s %15s %16s %16s %37s %37s",
4264             weekdays, daterange, tstart, tstop,
4265             macstart, macstop);
4266    if ( count != 6 ) {
4267       (void) fprintf(stderr, "Line %02d: Invalid timer command.\n", line_no);
4268       return 1;
4269    }
4270 
4271    /* If both start and stop macros are null macros, just return */
4272    if ( !strcmp(macstart, "null") && !strcmp(macstop, "null") )
4273       return 0;
4274 
4275    /* Get the index in array timerp to store the timer parameters. */
4276    index = timer_index( timerpp );
4277 
4278    (*timerpp)[index].line_no = line_no;
4279    (*timerpp)[index].line1 = (*timerpp)[index].line2 = line_no;
4280    (*timerpp)[index].pos1 = 1;
4281    (*timerpp)[index].pos2 = 2;
4282 
4283    if ( ((*timerpp)[index].dow_bmap = dow2bmap(weekdays)) == 0xff ) {
4284       (void) fprintf(stderr, "Line %02d: Timer contains invalid weekdays string.\n", line_no);
4285       return 1;
4286    }
4287 
4288    /* Determine whether the command contains clock or dawn or dusk relative */
4289    /* events and get time offset from either midnight or dawn/dusk.         */
4290 
4291    if ( (flag = parse_time_token(tstart, &time)) == INVALID_EVENT ) {
4292       (void) fprintf(stderr, "Line %d: Timer contains invalid start time token.\n", line_no);
4293       return 1;
4294    }
4295    else {
4296       (*timerpp)[index].offset_start = time;
4297       (*timerpp)[index].flag_start = flag ;
4298    }
4299 
4300    if ( (flag = parse_time_token(tstop, &time)) == INVALID_EVENT ) {
4301       (void) fprintf(stderr, "Line %d: Timer contains invalid stop time token.\n", line_no);
4302       return 1;
4303    }
4304    else {
4305       (*timerpp)[index].offset_stop = time;
4306       (*timerpp)[index].flag_stop = flag ;
4307    }
4308 
4309    (void) strtrim(daterange);
4310 
4311    /* Check for the special case "expire-ddd" */
4312    (*timerpp)[index].notify = -1;
4313    if ( !strncmp(daterange, "expire", 6) ) {
4314       /* Fill in full year, store days, and adjust later */
4315       (void)strncpy2(datebegin, "01/01", sizeof(datebegin) - 1);
4316       (void)strncpy2(dateend, "12/31", sizeof(dateend) - 1);
4317 
4318       if ( sscanf(daterange, "expire-%d", &day) == 1 )
4319          (*timerpp)[index].notify = day;
4320       else
4321          (*timerpp)[index].notify = 0;
4322    }
4323    /* Otherwise look for the standard "mm/dd-mm/dd" string */
4324    else if ( sscanf(daterange, "%5s-%5s", datebegin, dateend) != 2 ) {
4325       (void)fprintf(stderr,
4326          "Line %02d: Invalid timer date range %s.\n", line_no, daterange);
4327       return 1;
4328    }
4329 
4330    if ( sscanf(datebegin, "%d/%d", &month, &day) != 2 ||
4331           month < 1 || month > 12 || day < 1 || day > mdays[month] ) {
4332       (void)fprintf(stderr,
4333          "Line %02d: Invalid timer mm/dd begin date.\n", line_no);
4334       return 1;
4335    }
4336    (*timerpp)[index].sched_beg = 100*month + day;  /* Save as mmdd */
4337 
4338    if ( sscanf(dateend, "%d/%d", &month, &day) != 2 ||
4339           month < 1 || month > 12 || day < 1 || day > mdays[month] ) {
4340       (void)fprintf(stderr,
4341          "Line %02d: Invalid timer mm/dd end date.\n", line_no);
4342       return 1;
4343    }
4344    (*timerpp)[index].sched_end = 100*month + day;  /* Save as mmdd */
4345 
4346    /* If the user has specified reversed dates, move the end date */
4347    /* into the following year by adding 12 months.                */
4348    if ( (*timerpp)[index].sched_end < (*timerpp)[index].sched_beg )
4349       (*timerpp)[index].sched_end += 1200;
4350 
4351    if ( (int)strlen(macstart) > MACRO_LEN || (int)strlen(macstop) > MACRO_LEN )  {
4352       (void) fprintf(stderr,
4353          "Line %02d: Macro name exceeds %d characters.\n", line_no, MACRO_LEN);
4354       return 1;
4355    }
4356 
4357    /* Register the macro labels and store the indexes thereto. */
4358    /* If the macro is the null macro, correct the timer flag.  */
4359 
4360    (*timerpp)[index].macro_start = macindex
4361             = macro_index(macropp, macstart, TIMER_PARSER);
4362    if ( macindex == NULL_MACRO_INDEX )
4363       (*timerpp)[index].flag_start = NO_EVENT;
4364 
4365    (*timerpp)[index].macro_stop  = macindex
4366             = macro_index(macropp, macstop,  TIMER_PARSER);
4367    if ( macindex == NULL_MACRO_INDEX )
4368       (*timerpp)[index].flag_stop = NO_EVENT;
4369 
4370    /* For convenience, store the combined start and stop flags */
4371    (*timerpp)[index].flag_combined =
4372        (*timerpp)[index].flag_start | (*timerpp)[index].flag_stop;
4373 
4374    return 0;
4375 }
4376 
4377 
4378 /*---------------------------------------------------------------------+
4379  | Parse the tail of a timer command, with DAWNLT,...etc options.      |
4380  +---------------------------------------------------------------------*/
parse_timer_old(TIMER ** timerpp,MACRO ** macropp,char * tail)4381 int parse_timer_old ( TIMER **timerpp, MACRO **macropp, char *tail)
4382 {
4383    extern   int line_no;
4384 
4385    int      mdays[] = {0,31,30,31,30,31,30,31,31,30,31,30,31};
4386 
4387    unsigned int  flag;
4388    int      time;
4389    int      index, macindex;
4390    int      month, day;
4391    int      j, tokc;
4392    char     **tokv = NULL;
4393    char     datebegin[16], dateend[16];
4394    char     minibuf[32];
4395 
4396    tokenize(tail, " \t\n", &tokc, &tokv);
4397 
4398    /* Required tokens are:
4399         [0] Weekday map
4400         [1] Date range
4401         [2] Start time
4402         [3] Stop time
4403         [4] Start macro
4404         [5] Stop macro
4405       Optional tokens:
4406         Keyword 'option' and keywords for supported options.
4407    */
4408 
4409    if ( tokc < 6 ) {
4410       fprintf(stderr, "Line %02d: Invalid timer command.\n", line_no);
4411       free(tokv);
4412       return 1;
4413    }
4414 
4415    /* If both start and stop macros are null macros, just return */
4416    if ( !strcmp(tokv[4], "null") && !strcmp(tokv[5], "null") ) {
4417       free(tokv);
4418       return 0;
4419    }
4420 
4421    /* Get the index in array timerp to store the timer parameters. */
4422    index = timer_index( timerpp );
4423 
4424    (*timerpp)[index].line_no = line_no;
4425    (*timerpp)[index].line1 = (*timerpp)[index].line2 = line_no;
4426    (*timerpp)[index].pos1 = 1;
4427    (*timerpp)[index].pos2 = 2;
4428 
4429    if ( ((*timerpp)[index].dow_bmap = dow2bmap(tokv[0])) == 0xff ) {
4430       (void) fprintf(stderr, "Line %02d: Timer contains invalid weekdays string.\n", line_no);
4431       free(tokv);
4432       return 1;
4433    }
4434 
4435    /* Determine whether the command contains clock or dawn or dusk relative */
4436    /* events and get time offset from either midnight or dawn/dusk.         */
4437 
4438    if ( (flag = parse_time_token(tokv[2], &time)) == INVALID_EVENT ) {
4439       (void) fprintf(stderr, "Line %d: Timer contains invalid start time token.\n", line_no);
4440       free(tokv);
4441       return 1;
4442    }
4443    else {
4444       (*timerpp)[index].offset_start = time;
4445       (*timerpp)[index].flag_start = flag ;
4446    }
4447 
4448    if ( (flag = parse_time_token(tokv[3], &time)) == INVALID_EVENT ) {
4449       (void) fprintf(stderr, "Line %d: Timer contains invalid stop time token.\n", line_no);
4450       free(tokv);
4451       return 1;
4452    }
4453    else {
4454       (*timerpp)[index].offset_stop = time;
4455       (*timerpp)[index].flag_stop = flag ;
4456    }
4457 
4458    (void) strtrim(tokv[1]);
4459 
4460    /* Check for the special case "expire-ddd" */
4461    (*timerpp)[index].notify = -1;
4462    if ( !strncmp(tokv[1], "expire", 6) ) {
4463       /* Fill in full year, store days, and adjust later */
4464       (void)strncpy2(datebegin, "01/01", sizeof(datebegin) - 1);
4465       (void)strncpy2(dateend, "12/31", sizeof(dateend) - 1);
4466 
4467       if ( sscanf(tokv[1], "expire-%d", &day) == 1 )
4468          (*timerpp)[index].notify = day;
4469       else
4470          (*timerpp)[index].notify = 0;
4471    }
4472    /* Otherwise look for the standard "mm/dd-mm/dd" string */
4473    else if ( sscanf(tokv[1], "%5s-%5s", datebegin, dateend) != 2 ) {
4474       (void)fprintf(stderr,
4475          "Line %02d: Invalid timer date range %s.\n", line_no, tokv[1]);
4476       free(tokv);
4477       return 1;
4478    }
4479 
4480    if ( sscanf(datebegin, "%d/%d", &month, &day) != 2 ||
4481           month < 1 || month > 12 || day < 1 || day > mdays[month] ) {
4482       (void)fprintf(stderr,
4483          "Line %02d: Invalid timer mm/dd begin date.\n", line_no);
4484       free(tokv);
4485       return 1;
4486    }
4487    (*timerpp)[index].sched_beg = 100*month + day;  /* Save as mmdd */
4488 
4489    if ( sscanf(dateend, "%d/%d", &month, &day) != 2 ||
4490           month < 1 || month > 12 || day < 1 || day > mdays[month] ) {
4491       (void)fprintf(stderr,
4492          "Line %02d: Invalid timer mm/dd end date.\n", line_no);
4493       free(tokv);
4494       return 1;
4495    }
4496    (*timerpp)[index].sched_end = 100*month + day;  /* Save as mmdd */
4497 
4498    /* If the user has specified reversed dates, move the end date */
4499    /* into the following year by adding 12 months.                */
4500    if ( (*timerpp)[index].sched_end < (*timerpp)[index].sched_beg )
4501       (*timerpp)[index].sched_end += 1200;
4502 
4503    if ( (int)strlen(tokv[4]) > MACRO_LEN || (int)strlen(tokv[5]) > MACRO_LEN )  {
4504       (void) fprintf(stderr,
4505          "Line %02d: Macro name exceeds %d characters.\n", line_no, MACRO_LEN);
4506       free(tokv);
4507       return 1;
4508    }
4509 
4510    /* Register the macro labels and store the indexes thereto. */
4511    /* If the macro is the null macro, correct the timer flag.  */
4512 
4513    (*timerpp)[index].macro_start = macindex
4514             = macro_index(macropp, tokv[4], TIMER_PARSER);
4515    if ( macindex == NULL_MACRO_INDEX )
4516       (*timerpp)[index].flag_start = NO_EVENT;
4517 
4518    (*timerpp)[index].macro_stop  = macindex
4519             = macro_index(macropp, tokv[5],  TIMER_PARSER);
4520    if ( macindex == NULL_MACRO_INDEX )
4521       (*timerpp)[index].flag_stop = NO_EVENT;
4522 
4523    /* For convenience, store the combined start and stop flags */
4524    (*timerpp)[index].flag_combined =
4525        (*timerpp)[index].flag_start | (*timerpp)[index].flag_stop;
4526 
4527    /* Look for timer Dawn/Dusk options */
4528 
4529    (*timerpp)[index].ddoptions = 0;
4530    (*timerpp)[index].dawnlt = -1;
4531    (*timerpp)[index].dawngt = -1;
4532    (*timerpp)[index].dusklt = -1;
4533    (*timerpp)[index].duskgt = -1;
4534 
4535    for ( j = 6; j < tokc; j++ ) {
4536       strncpy2(minibuf, tokv[j], 31);
4537       strupper(minibuf);
4538       if ( strcmp("DAWNGT", minibuf) == 0 ) {
4539          if ( (*timerpp)[index].ddoptions & DAWNGT ) {
4540             fprintf(stderr, "Line %02d: Duplicate DAWNGT option in Timer.\n", line_no);
4541             free(tokv);
4542             return 1;
4543          }
4544          if ( tokc < j + 2 ) {
4545             fprintf(stderr, "Line %02d: Missing hh:mm following DAWNGT option\n", line_no);
4546             free(tokv);
4547             return 1;
4548          }
4549          if ( (flag = parse_time_token(tokv[j + 1], &time)) != CLOCK_EVENT ) {
4550             fprintf(stderr, "Line %d: Invalid DAWNGT time token '%s'.\n", line_no, tokv[j + 1]);
4551             free(tokv);
4552             return 1;
4553          }
4554 
4555          (*timerpp)[index].dawngt = time;
4556          (*timerpp)[index].ddoptions |= DAWNGT;
4557          j++;
4558       }
4559       else if ( strcmp("DAWNLT", minibuf) == 0 ) {
4560          if ( (*timerpp)[index].ddoptions & DAWNLT ) {
4561             fprintf(stderr, "Line %02d: Duplicate DAWNLT option in Timer.\n", line_no);
4562             free(tokv);
4563             return 1;
4564          }
4565          if ( tokc < j + 2 ) {
4566             fprintf(stderr, "Line %02d: Missing hh:mm following DAWNLT option\n", line_no);
4567             free(tokv);
4568             return 1;
4569          }
4570          if ( (flag = parse_time_token(tokv[j + 1], &time)) != CLOCK_EVENT ) {
4571             fprintf(stderr, "Line %d: Invalid DAWNLT time token '%s'.\n", line_no, tokv[j + 1]);
4572             free(tokv);
4573             return 1;
4574          }
4575 
4576          (*timerpp)[index].dawnlt = time;
4577          (*timerpp)[index].ddoptions |= DAWNLT;
4578          j++;
4579       }
4580       else if ( strcmp("DUSKGT", minibuf) == 0 ) {
4581          if ( (*timerpp)[index].ddoptions & DUSKGT ) {
4582             fprintf(stderr, "Line %02d: Duplicate DUSKGT option in Timer.\n", line_no);
4583             free(tokv);
4584             return 1;
4585          }
4586          if ( tokc < j + 2 ) {
4587             fprintf(stderr, "Line %02d: Missing hh:mm following DUSKGT option\n", line_no);
4588             free(tokv);
4589             return 1;
4590          }
4591          if ( (flag = parse_time_token(tokv[j + 1], &time)) != CLOCK_EVENT ) {
4592             fprintf(stderr, "Line %d: Invalid DUSKGT hh:mm token '%s'.\n", line_no, tokv[j + 1]);
4593             free(tokv);
4594             return 1;
4595          }
4596 
4597          (*timerpp)[index].duskgt = time;
4598          (*timerpp)[index].ddoptions |= DUSKGT;
4599          j++;
4600       }
4601       else if ( strcmp("DUSKLT", minibuf) == 0 ) {
4602          if ( (*timerpp)[index].ddoptions & DUSKLT ) {
4603             fprintf(stderr, "Line %02d: Duplicate DUSKLT option in Timer.\n", line_no);
4604             free(tokv);
4605             return 1;
4606          }
4607          if ( tokc < j + 2 ) {
4608             fprintf(stderr, "Line %02d: Missing hh:mm following DUSKLT option\n", line_no);
4609             free(tokv);
4610             return 1;
4611          }
4612          if ( (flag = parse_time_token(tokv[j + 1], &time)) != CLOCK_EVENT ) {
4613             fprintf(stderr, "Line %d: Invalid DUSKLT hh:mm token '%s'.\n", line_no, tokv[j + 1]);
4614             free(tokv);
4615             return 1;
4616          }
4617 
4618          (*timerpp)[index].dusklt = time;
4619          (*timerpp)[index].ddoptions |= DUSKLT;
4620          j++;
4621       }
4622       else {
4623          fprintf(stderr, "Line %02d: Invalid Timer option '%s'.\n", line_no, tokv[j]);
4624          free(tokv);
4625          return 1;
4626       }
4627    }
4628 
4629    free(tokv);
4630    return 0;
4631 }
4632 
4633 
4634 /*---------------------------------------------------------------------+
4635  | Parse the timer date range, observing the user's DATE_FORMAT for    |
4636  | the order in which month and day are entered.                       |
4637  | For any format, the separator between month and day may be any of   |
4638  | '/', '-', or '.'.                                                   |
4639  | The separator between the two dates may be '-' or ':'.              |
4640  +---------------------------------------------------------------------*/
parse_date_range(char * range,int * mmdd_begin,int * mmdd_end)4641 int parse_date_range ( char *range, int *mmdd_begin, int *mmdd_end )
4642 {
4643    char *sp, *rp;
4644    int  j;
4645    int  val[4], mon1, day1, mon2, day2;
4646    char msg[80];
4647 
4648    static int  mdays[] = {0,31,30,31,30,31,30,31,31,30,31,30,31};
4649    static char *sepstr[] = {"/-.", "-:", "/-.", " /t/n/r"};
4650 
4651 
4652    rp = range;
4653    for ( j = 0; j < 4; j++ ) {
4654       val[j] = (int)strtol(rp, &sp, 10);
4655       if ( !strchr(sepstr[j], *sp) ) {
4656          snprintf(msg, sizeof(msg) - 1, "Invalid timer date range '%s'.", range);
4657          store_error_message(msg);
4658          return 1;
4659       }
4660       rp = sp + 1;
4661    }
4662 
4663    if ( configp->date_format == DMY_ORDER ) {
4664       day1 = val[0]; mon1 = val[1];
4665       day2 = val[2]; mon2 = val[3];
4666    }
4667    else {
4668       mon1 = val[0]; day1 = val[1];
4669       mon2 = val[2]; day2 = val[3];
4670    }
4671 
4672    if ( mon1 < 1 || mon1 > 12 || day1 < 1 || day1 > mdays[mon1] ) {
4673       store_error_message("Invalid timer begin date.");
4674       return 1;
4675    }
4676 
4677    if ( mon2 < 1 || mon2 > 12 || day2 < 1 || day2 > mdays[mon2] ) {
4678       store_error_message("Invalid timer end date.");
4679       return 1;
4680    }
4681 
4682    *mmdd_begin = 100 * mon1 + day1;
4683    *mmdd_end   = 100 * mon2 + day2;
4684 
4685    /* If the user has specified reversed dates, move the end date */
4686    /* into the following year by adding 12 months.                */
4687    if ( *mmdd_end < *mmdd_begin )
4688       *mmdd_end += 1200;
4689 
4690    return 0;
4691 }
4692 
4693 /*---------------------------------------------------------------------+
4694  | Parse the tail of a timer command, with DAWNLT,...etc options.      |
4695  +---------------------------------------------------------------------*/
parse_timer(TIMER ** timerpp,MACRO ** macropp,char * tail)4696 int parse_timer ( TIMER **timerpp, MACRO **macropp, char *tail)
4697 {
4698    extern   int line_no;
4699 
4700 //   int      mdays[] = {0,31,30,31,30,31,30,31,31,30,31,30,31};
4701 
4702    unsigned int  flag;
4703    int      time;
4704    int      index, macindex;
4705 //   int      month, day;
4706    int      day;
4707    int      j, tokc;
4708    char     **tokv = NULL;
4709 //   char     datebegin[16], dateend[16];
4710    char     minibuf[32];
4711    int      mmdd_begin, mmdd_end;
4712 
4713    tokenize(tail, " \t\n", &tokc, &tokv);
4714 
4715    /* Required tokens are:
4716         [0] Weekday map
4717         [1] Date range
4718         [2] Start time
4719         [3] Stop time
4720         [4] Start macro
4721         [5] Stop macro
4722       Optional tokens:
4723         Keyword 'option' and keywords for supported options.
4724    */
4725 
4726    if ( tokc < 6 ) {
4727       fprintf(stderr, "Line %02d: Invalid timer command.\n", line_no);
4728       free(tokv);
4729       return 1;
4730    }
4731 
4732    /* If both start and stop macros are null macros, just return */
4733    if ( !strcmp(tokv[4], "null") && !strcmp(tokv[5], "null") ) {
4734       free(tokv);
4735       return 0;
4736    }
4737 
4738    /* Get the index in array timerp to store the timer parameters. */
4739    index = timer_index( timerpp );
4740 
4741    (*timerpp)[index].line_no = line_no;
4742    (*timerpp)[index].line1 = (*timerpp)[index].line2 = line_no;
4743    (*timerpp)[index].pos1 = 1;
4744    (*timerpp)[index].pos2 = 2;
4745 
4746    if ( ((*timerpp)[index].dow_bmap = dow2bmap(tokv[0])) == 0xff ) {
4747       (void) fprintf(stderr, "Line %02d: Timer contains invalid weekdays string.\n", line_no);
4748       free(tokv);
4749       return 1;
4750    }
4751 
4752    /* Determine whether the command contains clock or dawn or dusk relative */
4753    /* events and get time offset from either midnight or dawn/dusk.         */
4754 
4755    if ( (flag = parse_time_token(tokv[2], &time)) == INVALID_EVENT ) {
4756       (void) fprintf(stderr, "Line %d: Timer contains invalid start time token.\n", line_no);
4757       free(tokv);
4758       return 1;
4759    }
4760    else {
4761       (*timerpp)[index].offset_start = time;
4762       (*timerpp)[index].flag_start = flag ;
4763    }
4764 
4765    if ( (flag = parse_time_token(tokv[3], &time)) == INVALID_EVENT ) {
4766       (void) fprintf(stderr, "Line %d: Timer contains invalid stop time token.\n", line_no);
4767       free(tokv);
4768       return 1;
4769    }
4770    else {
4771       (*timerpp)[index].offset_stop = time;
4772       (*timerpp)[index].flag_stop = flag ;
4773    }
4774 
4775    (void) strtrim(tokv[1]);
4776 
4777    /* Check for the special case "expire-ddd" */
4778    (*timerpp)[index].notify = -1;
4779    if ( !strncmp(tokv[1], "expire", 6) ) {
4780       /* Fill in full year, store days, and adjust later */
4781       mmdd_begin = 101;
4782       mmdd_end = 1231;
4783 
4784       if ( sscanf(tokv[1], "expire-%d", &day) == 1 )
4785          (*timerpp)[index].notify = day;
4786       else
4787          (*timerpp)[index].notify = 0;
4788    }
4789    /* Otherwise look for the standard "mm/dd-mm/dd" string */
4790    /* or its equivalent in the user's date format          */
4791    else if ( parse_date_range(tokv[1], &mmdd_begin, &mmdd_end) != 0 ) {
4792       fprintf(stderr, "Line %02d: %s\n", line_no, error_message());
4793       free(tokv);
4794       return 1;
4795    }
4796    (*timerpp)[index].sched_beg = mmdd_begin;
4797    (*timerpp)[index].sched_end = mmdd_end;
4798 
4799 
4800    if ( (int)strlen(tokv[4]) > MACRO_LEN || (int)strlen(tokv[5]) > MACRO_LEN )  {
4801       (void) fprintf(stderr,
4802          "Line %02d: Macro name exceeds %d characters.\n", line_no, MACRO_LEN);
4803       free(tokv);
4804       return 1;
4805    }
4806 
4807    /* Register the macro labels and store the indexes thereto. */
4808    /* If the macro is the null macro, correct the timer flag.  */
4809 
4810    (*timerpp)[index].macro_start = macindex
4811             = macro_index(macropp, tokv[4], TIMER_PARSER);
4812    if ( macindex == NULL_MACRO_INDEX )
4813       (*timerpp)[index].flag_start = NO_EVENT;
4814 
4815    (*timerpp)[index].macro_stop  = macindex
4816             = macro_index(macropp, tokv[5],  TIMER_PARSER);
4817    if ( macindex == NULL_MACRO_INDEX )
4818       (*timerpp)[index].flag_stop = NO_EVENT;
4819 
4820    /* For convenience, store the combined start and stop flags */
4821    (*timerpp)[index].flag_combined =
4822        (*timerpp)[index].flag_start | (*timerpp)[index].flag_stop;
4823 
4824    /* Look for timer Dawn/Dusk options */
4825 
4826    (*timerpp)[index].ddoptions = 0;
4827    (*timerpp)[index].dawnlt = -1;
4828    (*timerpp)[index].dawngt = -1;
4829    (*timerpp)[index].dusklt = -1;
4830    (*timerpp)[index].duskgt = -1;
4831 
4832    for ( j = 6; j < tokc; j++ ) {
4833       strncpy2(minibuf, tokv[j], 31);
4834       strupper(minibuf);
4835       if ( strcmp("DAWNGT", minibuf) == 0 ) {
4836          if ( (*timerpp)[index].ddoptions & DAWNGT ) {
4837             fprintf(stderr, "Line %02d: Duplicate DAWNGT option in Timer.\n", line_no);
4838             free(tokv);
4839             return 1;
4840          }
4841          if ( tokc < j + 2 ) {
4842             fprintf(stderr, "Line %02d: Missing hh:mm following DAWNGT option\n", line_no);
4843             free(tokv);
4844             return 1;
4845          }
4846          if ( (flag = parse_time_token(tokv[j + 1], &time)) != CLOCK_EVENT ) {
4847             fprintf(stderr, "Line %d: Invalid DAWNGT time token '%s'.\n", line_no, tokv[j + 1]);
4848             free(tokv);
4849             return 1;
4850          }
4851 
4852          (*timerpp)[index].dawngt = time;
4853          (*timerpp)[index].ddoptions |= DAWNGT;
4854          j++;
4855       }
4856       else if ( strcmp("DAWNLT", minibuf) == 0 ) {
4857          if ( (*timerpp)[index].ddoptions & DAWNLT ) {
4858             fprintf(stderr, "Line %02d: Duplicate DAWNLT option in Timer.\n", line_no);
4859             free(tokv);
4860             return 1;
4861          }
4862          if ( tokc < j + 2 ) {
4863             fprintf(stderr, "Line %02d: Missing hh:mm following DAWNLT option\n", line_no);
4864             free(tokv);
4865             return 1;
4866          }
4867          if ( (flag = parse_time_token(tokv[j + 1], &time)) != CLOCK_EVENT ) {
4868             fprintf(stderr, "Line %d: Invalid DAWNLT time token '%s'.\n", line_no, tokv[j + 1]);
4869             free(tokv);
4870             return 1;
4871          }
4872 
4873          (*timerpp)[index].dawnlt = time;
4874          (*timerpp)[index].ddoptions |= DAWNLT;
4875          j++;
4876       }
4877       else if ( strcmp("DUSKGT", minibuf) == 0 ) {
4878          if ( (*timerpp)[index].ddoptions & DUSKGT ) {
4879             fprintf(stderr, "Line %02d: Duplicate DUSKGT option in Timer.\n", line_no);
4880             free(tokv);
4881             return 1;
4882          }
4883          if ( tokc < j + 2 ) {
4884             fprintf(stderr, "Line %02d: Missing hh:mm following DUSKGT option\n", line_no);
4885             free(tokv);
4886             return 1;
4887          }
4888          if ( (flag = parse_time_token(tokv[j + 1], &time)) != CLOCK_EVENT ) {
4889             fprintf(stderr, "Line %d: Invalid DUSKGT hh:mm token '%s'.\n", line_no, tokv[j + 1]);
4890             free(tokv);
4891             return 1;
4892          }
4893 
4894          (*timerpp)[index].duskgt = time;
4895          (*timerpp)[index].ddoptions |= DUSKGT;
4896          j++;
4897       }
4898       else if ( strcmp("DUSKLT", minibuf) == 0 ) {
4899          if ( (*timerpp)[index].ddoptions & DUSKLT ) {
4900             fprintf(stderr, "Line %02d: Duplicate DUSKLT option in Timer.\n", line_no);
4901             free(tokv);
4902             return 1;
4903          }
4904          if ( tokc < j + 2 ) {
4905             fprintf(stderr, "Line %02d: Missing hh:mm following DUSKLT option\n", line_no);
4906             free(tokv);
4907             return 1;
4908          }
4909          if ( (flag = parse_time_token(tokv[j + 1], &time)) != CLOCK_EVENT ) {
4910             fprintf(stderr, "Line %d: Invalid DUSKLT hh:mm token '%s'.\n", line_no, tokv[j + 1]);
4911             free(tokv);
4912             return 1;
4913          }
4914 
4915          (*timerpp)[index].dusklt = time;
4916          (*timerpp)[index].ddoptions |= DUSKLT;
4917          j++;
4918       }
4919       else {
4920          fprintf(stderr, "Line %02d: Invalid Timer option '%s'.\n", line_no, tokv[j]);
4921          free(tokv);
4922          return 1;
4923       }
4924    }
4925 
4926    free(tokv);
4927    return 0;
4928 }
4929 
4930 /*---------------------------------------------------------------------+
4931  | Parse the tail of a trigger command.                                |
4932  +---------------------------------------------------------------------*/
parse_trigger(TRIGGER ** triggerpp,MACRO ** macropp,char * tail)4933 int parse_trigger( TRIGGER **triggerpp, MACRO **macropp, char *tail)
4934 {
4935    extern int line_no;
4936 
4937    int          index, macindex;
4938    char         hc;
4939    int          tokc;
4940    char         **tokv;
4941    unsigned int bitmap, aflags;
4942 
4943    tokenize(tail, " \t\n", &tokc, &tokv);
4944 
4945    /* Expected tokens are:       */
4946    /*   tokv[0] = Housecode|Unit */
4947    /*   tokv[1] = "on" or "off"  */
4948    /*   tokv[2] = macro label    */
4949 
4950    if ( tokc < 3 ) {
4951       (void) fprintf(stderr,
4952          "Line %02d: Invalid trigger (too few fields).\n", line_no);
4953       free(tokv);
4954       return 1;
4955    }
4956    if ( tokc > 3 ) {
4957       (void) fprintf(stderr,
4958          "Line %02d: Invalid trigger (too many fields).\n", line_no);
4959       free(tokv);
4960       return 1;
4961    }
4962 
4963    index = trigger_index( triggerpp );
4964    (*triggerpp)[index].line_no = line_no;
4965 
4966    aflags = parse_addr(tokv[0], &hc, &bitmap);
4967 
4968    if ( !(aflags & A_VALID) ) {
4969       fprintf(stderr, "Line %02d: %s\n", line_no, error_message());
4970       clear_error_message();
4971       free(tokv);
4972       return 1;
4973    }
4974    if ( !(aflags & A_HCODE) || !(aflags & A_BMAP) ||
4975                     aflags & (A_PLUS | A_MINUS | A_DUMMY | A_MULT) ) {
4976       fprintf(stderr,
4977          "Line %02d: Invalid trigger Housecode|Unit '%s'\n",
4978              line_no, tokv[0]);
4979       free(tokv);
4980       return 1;
4981    }
4982 
4983    (*triggerpp)[index].housecode = hc2code(hc);
4984    (*triggerpp)[index].unitcode = single_bmap_unit(bitmap);
4985 
4986    if ( strcmp(tokv[1], "on") == 0 )
4987       (*triggerpp)[index].command = TRIGGER_ON;
4988    else if ( strcmp(tokv[1], "off") == 0 )
4989       (*triggerpp)[index].command = TRIGGER_OFF;
4990    else {
4991       (void) fprintf(stderr,
4992          "Line %02d: Trigger command not 'on' or 'off'.\n", line_no);
4993       free(tokv);
4994       return 1;
4995    }
4996 
4997    if ( (int)strlen(tokv[2]) > MACRO_LEN ) {
4998       (void) fprintf(stderr,
4999           "Line %02d: Macro label exceeds %d characters.\n",
5000               line_no, MACRO_LEN);
5001       free(tokv);
5002       return 1;
5003    }
5004 
5005    if ( !strcmp(tokv[2], "null") ) {
5006       (void) fprintf(stderr,
5007          "Line %02d: Macro label \"null\" is not valid in a trigger.\n",
5008          line_no);
5009       free(tokv);
5010       return 1;
5011    }
5012 
5013    /* Register the macro label and store the index thereto */
5014    macindex =  macro_index(macropp, tokv[2], TRIGGER_PARSER);
5015    (*triggerpp)[index].macro = macindex;
5016 
5017    /* Cross-reference the macro with the trigger */
5018    (*macropp)[macindex].trig = index;
5019 
5020    free(tokv);
5021    return 0;
5022 }
5023 
5024 
5025 /*---------------------------------------------------------------------+
5026  | Parse the tail of a macro command in the upload schedule.           |
5027  +---------------------------------------------------------------------*/
parse_macro(MACRO ** macropp,unsigned char ** elementp,char * tail)5028 int parse_macro ( MACRO **macropp, unsigned char **elementp, char *tail)
5029 {
5030    extern int    line_no;
5031    extern char   *typename[];
5032 
5033    int           i, j, k, m, n, index, loc, retcode = 0;
5034    int           delay, nelem, nbytes;
5035    char          buffer[1024];
5036    unsigned char elembuff[256];
5037    int           macc, cmdc, tokc, scmdc;
5038    char          **macv, **cmdv, **scmdv, **tokv;
5039    char          *sp, *scp, *tailsave;
5040    SCENE         *scenep;
5041 
5042    scenep = configp->scenep;
5043 
5044    /* First token is macro label */
5045    if ( *(get_token(buffer, &tail, " \t", 255)) == '\0') {
5046        fprintf(stderr, "Line %02d: Too few fields.\n", line_no);
5047        return 1;
5048    }
5049 
5050    if ( (int)strlen(buffer) > MACRO_LEN ) {
5051       fprintf(stderr, "Line %02d: Macro label '%s' exceeds %d characters.\n",
5052          line_no, buffer, MACRO_LEN);
5053       return 1;
5054    }
5055 
5056    if ( !strcmp(buffer, "null") ) {
5057       fprintf(stderr, "Line %02d: null macro may not be re-defined.\n", line_no);
5058       return 1;
5059    }
5060 
5061    if ( strchr("_+-$", *buffer) != NULL ) {
5062       fprintf(stderr,
5063          "Line %02d: Macro labels beginning with '_', '+', '-', '$' are reserved.\n", line_no);
5064       return 1;
5065    }
5066 
5067    /* Get index in array of MACRO structures */
5068    index = macro_index(macropp, buffer, MACRO_PARSER);
5069    (*macropp)[index].line_no = line_no;
5070 
5071    /* Get the index into the element array where we will start to */
5072    /* store the elements.                                         */
5073    (*macropp)[index].element = macro_element_index( elementp, 0 );
5074 
5075    /* Second token is delay time (optional if zero) */
5076    tailsave = tail;
5077    if ( *(get_token(buffer, &tail, " \t", 255)) == '\0') {
5078        fprintf(stderr, "Line %02d: Too few fields.\n", line_no);
5079        return 1;
5080    }
5081 
5082    delay = (int)strtol(buffer, &sp, 10);
5083 
5084    if ( !strchr(" \t", *sp) ) {
5085       /* Non-numeric, so use default zero delay and backup tail pointer */
5086       delay = 0;
5087       tail = tailsave;
5088    }
5089 
5090    if ( delay < 0 || delay > 240 ) {
5091       fprintf(stderr,
5092         "Line %02d: Macro delay is outside range 0 - 240 minutes.\n", line_no);
5093       return 1;
5094    }
5095 
5096    (*macropp)[index].delay = (unsigned char)delay;
5097 
5098    /* Divide the remainder of the tail into ';' delimited x10 commands */
5099    tokenize(tail, ";", &macc, &macv);
5100 
5101    for ( i = 0; i < macc; i++ ) {
5102       strtrim(macv[i]);
5103       if ( *macv[i] == '\0' )
5104          continue;
5105       /* Divide commands into tokens */
5106       tokenize(macv[i], " \t", &cmdc, &cmdv);
5107       /* See if it's a user-defined scene from the config file */
5108       if ( (k = lookup_scene(scenep, cmdv[0])) >= 0 ) {
5109          /* Check if needed number of parameters are supplied */
5110          if ( cmdc < scenep[k].nparms + 1 ) {
5111             fprintf(stderr,
5112               "Line %02d: (Config %02d: %s '%s'): Too few parameters - %d required.\n",
5113                  line_no, scenep[k].line_no, typename[scenep[k].type], scenep[k].label,
5114                  scenep[k].nparms);
5115             free(cmdv);
5116             free(macv);
5117             return 1;
5118          }
5119          else if ( cmdc > scenep[k].nparms + 1 ) {
5120             fprintf(stderr,
5121               "Line %02d: (Config %02d: %s '%s'): Too many parameters - only %d accepted.\n",
5122                  line_no, scenep[k].line_no, typename[scenep[k].type], scenep[k].label,
5123                  scenep[k].nparms);
5124             free(cmdv);
5125             free(macv);
5126             return 1;
5127          }
5128 
5129          /* Tokenize individual commands in scene */
5130          scp = strdup(scenep[k].body);
5131          tokenize(scp, ";", &scmdc, &scmdv);
5132          for ( m = 0; m < scmdc; m++ ) {
5133             strtrim(scmdv[m]);
5134 	    if ( *scmdv[m] == '\0' )
5135                continue;
5136             /* Break up commands into tokens */
5137             tokenize(scmdv[m], " \t", &tokc, &tokv);
5138             /* Substitute for any dummy parameters */
5139             replace_dummy_parms(tokc, &tokv, cmdv);
5140 
5141             retcode = macro_command(tokc, tokv, &nelem, &nbytes, elembuff);
5142 
5143             /* Free each member of tokv, then tokv itself */
5144             for ( j = 0; j < tokc; j++ )
5145                free(tokv[j]);
5146             free(tokv);
5147 
5148             if ( retcode != 0 ) {
5149                /* Display stacked error message from macro_command() */
5150                fprintf(stderr, "Line %02d: (Config %02d: %s '%s'): %s.\n",
5151                   line_no, scenep[k].line_no, typename[scenep[k].type],
5152                   scenep[k].label, error_message());
5153                clear_error_message();
5154                free(scmdv);
5155                free(scp);
5156                free(cmdv);
5157                free(macv);
5158                return 1;
5159             }
5160             else if ( *error_message() ) {
5161                /* Display any warning message */
5162                fprintf(stderr, "Line %02d: %s.\n", line_no, error_message());
5163                clear_error_message();
5164             }
5165 
5166             (*macropp)[index].nelem += nelem;
5167             (*macropp)[index].total += nbytes;
5168 
5169             /* Make space for the element and copy it there */
5170             loc = macro_element_index( elementp, nbytes );
5171             for ( n = 0; n < nbytes; n++ ) {
5172                (*elementp)[loc + n] = elembuff[n];
5173             }
5174          }
5175          free(scmdv);
5176          free(scp);
5177       }
5178       else {
5179          /* It's an individual command */
5180          retcode = macro_command(cmdc, cmdv, &nelem, &nbytes, elembuff);
5181          free(cmdv);
5182          if ( retcode != 0 ) {
5183             /* Display stacked error message from macro_command() */
5184             fprintf(stderr, "Line %02d: %s.\n", line_no, error_message());
5185             free(macv);
5186             return 1;
5187          }
5188          (*macropp)[index].nelem += nelem;
5189          (*macropp)[index].total += nbytes;
5190 
5191          /* Make space for the element and copy it there */
5192          loc = macro_element_index( elementp, nbytes );
5193          for ( n = 0; n < nbytes; n++ )
5194             (*elementp)[loc + n] = elembuff[n];
5195       }
5196    }
5197    free(macv);
5198 
5199    /* Check that the macro has at least one element */
5200    if ( (*macropp)[index].nelem < 1 ) {
5201       fprintf(stderr, "Line %02d: macro contains no commands.\n", line_no);
5202       return 1;
5203    }
5204 
5205    return 0;
5206 }
5207 
5208 
5209 /*----------------------------------------------------------------------------+
5210  | Split timers into individual tevents                                       |
5211  +----------------------------------------------------------------------------*/
split_timers(TIMER * timerp,TEVENT ** teventpp)5212 void split_timers ( TIMER *timerp, TEVENT **teventpp )
5213 {
5214    int        j, indx = 0;
5215 
5216    if ( !timerp )
5217       return;
5218 
5219    j = 0;
5220    while( timerp[j].line_no > 0 ) {
5221       if ( timerp[j].flag_start != NO_EVENT ) {
5222          indx = tevent_index( teventpp );
5223          (*teventpp)[indx].line_no     = timerp[j].line_no;
5224          (*teventpp)[indx].pos         = 1;
5225          (*teventpp)[indx].link        = indx + 1;
5226          (*teventpp)[indx].timer       = j;
5227          (*teventpp)[indx].generation  = 0;
5228          (*teventpp)[indx].dow_bmap    = timerp[j].dow_bmap;
5229          (*teventpp)[indx].sched_beg   = timerp[j].sched_beg;
5230          (*teventpp)[indx].sched_end   = timerp[j].sched_end;
5231          (*teventpp)[indx].notify      = timerp[j].notify;
5232          (*teventpp)[indx].resolv_beg  = timerp[j].resolv_beg;
5233          (*teventpp)[indx].resolv_end  = timerp[j].resolv_end;
5234          (*teventpp)[indx].flag        = timerp[j].flag_start | PRT_EVENT;
5235          (*teventpp)[indx].print       = 1;
5236          (*teventpp)[indx].flag2       = 0;
5237          (*teventpp)[indx].cancel      = timerp[j].cancel;
5238          (*teventpp)[indx].offset      = timerp[j].offset_start;
5239          (*teventpp)[indx].delay       = timerp[j].delay_start;
5240          (*teventpp)[indx].security    = timerp[j].security_start;
5241          (*teventpp)[indx].ddoptions   = timerp[j].ddoptions;
5242          (*teventpp)[indx].dawnlt      = timerp[j].dawnlt;
5243          (*teventpp)[indx].dawngt      = timerp[j].dawngt;
5244          (*teventpp)[indx].dusklt      = timerp[j].dusklt;
5245          (*teventpp)[indx].duskgt      = timerp[j].duskgt;
5246          (*teventpp)[indx].macro       = timerp[j].macro_start;
5247          (*teventpp)[indx].ptr         = timerp[j].ptr_start;
5248          (*teventpp)[indx].intv	       = timerp[j].intv;
5249          (*teventpp)[indx].nintv       = timerp[j].nintv;
5250       }
5251       if ( timerp[j].flag_stop  != NO_EVENT ) {
5252          indx = tevent_index( teventpp );
5253          (*teventpp)[indx].line_no     = timerp[j].line_no;
5254          (*teventpp)[indx].pos         = 2;
5255          (*teventpp)[indx].link        = indx + 1;
5256          (*teventpp)[indx].timer       = j;
5257          (*teventpp)[indx].generation  = 0;
5258          (*teventpp)[indx].dow_bmap    = timerp[j].dow_bmap;
5259          (*teventpp)[indx].sched_beg   = timerp[j].sched_beg;
5260          (*teventpp)[indx].sched_end   = timerp[j].sched_end;
5261          (*teventpp)[indx].notify      = timerp[j].notify;
5262          (*teventpp)[indx].resolv_beg  = timerp[j].resolv_beg;
5263          (*teventpp)[indx].resolv_end  = timerp[j].resolv_end;
5264          (*teventpp)[indx].flag        = timerp[j].flag_stop | PRT_EVENT;
5265          (*teventpp)[indx].print       = 1;
5266          (*teventpp)[indx].flag2       = 0;
5267          (*teventpp)[indx].cancel      = timerp[j].cancel;
5268          (*teventpp)[indx].offset      = timerp[j].offset_stop;
5269          (*teventpp)[indx].delay       = timerp[j].delay_stop;
5270          (*teventpp)[indx].security    = timerp[j].security_stop;
5271          (*teventpp)[indx].ddoptions   = timerp[j].ddoptions;
5272          (*teventpp)[indx].dawnlt      = timerp[j].dawnlt;
5273          (*teventpp)[indx].dawngt      = timerp[j].dawngt;
5274          (*teventpp)[indx].dusklt      = timerp[j].dusklt;
5275          (*teventpp)[indx].duskgt      = timerp[j].duskgt;
5276          (*teventpp)[indx].macro       = timerp[j].macro_stop;
5277          (*teventpp)[indx].ptr         = timerp[j].ptr_start;
5278          (*teventpp)[indx].intv	       = timerp[j].intv;
5279          (*teventpp)[indx].nintv       = timerp[j].nintv;
5280       }
5281       j++ ;
5282    }
5283    (*teventpp)[indx].link = -1;
5284    return;
5285 }
5286 
5287 /*----------------------------------------------------------------------------+
5288  | Associate each tevent with the trigger which call its macro (if any).      |
5289  +----------------------------------------------------------------------------*/
associate_tevent_triggers(TEVENT * teventp,MACRO * macrop)5290 void associate_tevent_triggers ( TEVENT *teventp, MACRO *macrop )
5291 {
5292    int j;
5293 
5294    if ( !teventp )
5295       return;
5296 
5297    j = 0;
5298    while ( teventp[j].line_no > 0 ) {
5299       teventp[j].trig = macrop[teventp[j].macro].trig;
5300       j++;
5301    }
5302 
5303    return;
5304 }
5305 
5306 /*----------------------------------------------------------------------------+
5307  | Replace events involving delayed macros with new events calling new macros |
5308  | with zero delay.  (Necessary to avoid pending delayed macros, which are    |
5309  | erased when a new schedule is uploaded.)  Security events (handled         |
5310  | separately) and Dawn/Dusk related events are not modified.                 |
5311  +----------------------------------------------------------------------------*/
replace_delayed_events(TEVENT ** teventpp,MACRO ** macropp,unsigned char ** elementp)5312 void replace_delayed_events ( TEVENT **teventpp, MACRO **macropp,
5313                                                       unsigned char **elementp )
5314 {
5315    int           j, cindx, macro, delay;
5316    unsigned int  flag, needed;
5317 
5318    if ( *teventpp == NULL )
5319       return;
5320 
5321    if ( verbose )
5322       (void)printf("Entering replace_delayed_events() at generation %d\n",
5323               current_tevent_generation);
5324 
5325    /* Check whether we need to do anything at all */
5326    needed = NO;
5327    j = 0;
5328    while ( (*teventpp)[j].line_no > 0 ) {
5329       if ( (*teventpp)[j].generation != current_tevent_generation ||
5330            (flag = (*teventpp)[j].flag) == NO_EVENT ||
5331            (*teventpp)[j].delay == 0 ||
5332            (flag & SEC_EVENT) != 0 ||
5333            (flag & CLOCK_EVENT) == 0         ) {
5334          j++;
5335          continue;
5336       }
5337       needed = YES;
5338       break;
5339    }
5340 
5341    if ( needed == NO )
5342       return;
5343 
5344    j = 0;
5345    while ( (*teventpp)[j].line_no > 0 ) {
5346       if ( (*teventpp)[j].generation != current_tevent_generation ||
5347            (flag = (*teventpp)[j].flag) == NO_EVENT ) {
5348          j++;
5349          continue;
5350       }
5351 
5352       /* Create a new tevent */
5353       cindx = spawn_child_tevent( teventpp, j );
5354 
5355       if ( (delay = (*teventpp)[j].delay) == 0 ||
5356            (flag & SEC_EVENT) != 0 ||
5357            (flag & CLOCK_EVENT) == 0  ) {
5358          j++;
5359          continue;
5360       }
5361 
5362       if ( verbose )
5363          (void)printf("Replacing macro '%s' with non-delayed macro.\n",
5364             (*macropp)[(*teventpp)[j].macro].label);
5365 
5366       /* Create a duplicate macro with a new name and zero delay */
5367       macro = macro_dupe_special(macropp, (*teventpp)[j].macro, elementp);
5368 
5369       (*teventpp)[cindx].offset += (*teventpp)[j].delay;
5370       (*teventpp)[cindx].delay = 0;
5371       (*teventpp)[cindx].macro = macro;
5372       (*teventpp)[cindx].flag |= (PRT_EVENT);
5373       (*teventpp)[cindx].print += 1;
5374 
5375       j++;
5376    }
5377 
5378    (void)update_current_tevent_generation();
5379 
5380    return;
5381 }
5382 
5383 
5384 /*----------------------------------------------------------------------------+
5385  | Try to create a unique macro name for a combined macro which will fit      |
5386  | in the allowed space and yet be meaningful to the user.                    |
5387  +----------------------------------------------------------------------------*/
unique_macro_name(int * maclist,int listlen,MACRO * macrop,char * prefix)5388 char *unique_macro_name ( int *maclist, int listlen, MACRO *macrop, char *prefix )
5389 {
5390    /* Quick fix for now */
5391    static char macname[MACRO_LEN + 1];
5392    static int  macnum = 0;
5393 
5394    (void)sprintf(macname, "%s_%02d", prefix, macnum++);
5395    return macname;
5396 }
5397 
5398 /*----------------------------------------------------------------------------+
5399  | Return 1 if two tevents are the same except for the macros, and the macros |
5400  | have the same delay time.                                                  |
5401  +----------------------------------------------------------------------------*/
is_tevent_similar(int one,int two,TEVENT * teventp)5402 int is_tevent_similar ( int one, int two, TEVENT *teventp )
5403 {
5404    unsigned int mask = (TIME_EVENTS | SEC_EVENT);
5405 
5406    return teventp[one].generation   !=  teventp[two].generation     ?  0 :
5407           teventp[one].dow_bmap     !=  teventp[two].dow_bmap       ?  0 :
5408           teventp[one].sched_beg    !=  teventp[two].sched_beg      ?  0 :
5409           teventp[one].sched_end    !=  teventp[two].sched_end      ?  0 :
5410           teventp[one].notify       !=  teventp[two].notify         ?  0 :
5411          (teventp[one].flag & mask) != (teventp[two].flag & mask)   ?  0 :
5412           teventp[one].offset       !=  teventp[two].offset         ?  0 :
5413           teventp[one].notify       !=  teventp[two].notify         ?  0 :
5414           teventp[one].ddoptions    !=  teventp[two].ddoptions      ?  0 :
5415           teventp[one].dawnlt       !=  teventp[two].dawnlt         ?  0 :
5416           teventp[one].dawngt       !=  teventp[two].dawngt         ?  0 :
5417           teventp[one].dusklt       !=  teventp[two].dusklt         ?  0 :
5418           teventp[one].duskgt       !=  teventp[two].duskgt         ?  0 :
5419           teventp[one].delay        !=  teventp[two].delay          ?  0 : 1;
5420 }
5421 
5422 /*----------------------------------------------------------------------------+
5423  | Combine similar tevents if possible, by creating secondary macros.         |
5424  +----------------------------------------------------------------------------*/
combine_similar_tevents(TEVENT ** teventpp,MACRO ** macropp,unsigned char ** elementp)5425 void combine_similar_tevents ( TEVENT **teventpp, MACRO **macropp, unsigned char **elementp )
5426 {
5427    int  j, k, m;
5428    int  sm, nsim, bsm, linki, linkm;
5429    int  elem, mac, nelem, loc, indx, cindx, ibuff;
5430    int  simlist[256];
5431    int  maclist[256];
5432    unsigned char elembuff[4096];
5433    char macname[MACRO_LEN];
5434 
5435    if ( *teventpp == NULL )
5436       return ;
5437 
5438    if ( verbose )
5439       (void)printf("Entering combine_similar_events() at generation %d\n",
5440            current_tevent_generation);
5441 
5442    /* Reset the "done" flags */
5443    j = 0;
5444    while ( (*teventpp)[j].line_no > 0 ) {
5445       (*teventpp)[j].done = 0;
5446        j++;
5447    }
5448 
5449    j = 0;
5450    while ( (*teventpp)[j].line_no > 0 ) {
5451       if ( (*teventpp)[j].generation != current_tevent_generation  ||
5452            (*teventpp)[j].flag == NO_EVENT ||
5453            (*teventpp)[j].done  )   {
5454          j++;
5455          continue;
5456       }
5457 
5458       /* Compile a list of similar tevents, i.e., with same date range, */
5459       /* dow bmap, type, offset, security, macro delay.                 */
5460       k = j + 1;
5461       nsim = 0;
5462       simlist[nsim++] = j;
5463 
5464       while ( (*teventpp)[k].line_no > 0 )   {
5465          if ( (*teventpp)[k].generation != current_tevent_generation ||
5466               (*teventpp)[k].flag == NO_EVENT ||
5467               (*teventpp)[k].done ) {
5468             k++;
5469             continue;
5470          }
5471          if ( is_tevent_similar(j, k, *teventpp) )
5472             simlist[nsim++] = k;
5473          k++;
5474       }
5475 
5476       /* Make a list of the corresponding macro indexes */
5477       for ( k = 0; k < nsim; k++ )
5478          maclist[k] = (*teventpp)[simlist[k]].macro;
5479 
5480       /* Reorder the links to group together similar tevents */
5481       /* for reporting purposes.                             */
5482 
5483       for ( k = nsim - 1; k > 0; k-- ) {
5484          (*teventpp)[simlist[k]].plink = simlist[k - 1];
5485       }
5486 
5487       indx = j;
5488       for ( k = 1; k < nsim; k++ ) {
5489          sm = simlist[k];
5490          if ( (*teventpp)[indx].link == sm ) {
5491             indx = sm;
5492             continue;
5493          }
5494 
5495          m = 0;
5496          bsm = -1;
5497          while ( (*teventpp)[m].line_no > 0 ) {
5498             if ( (*teventpp)[m].link == sm ) {
5499                bsm = m;
5500                break;
5501             }
5502             m++;
5503          }
5504 
5505          if ( bsm < 0 ) {
5506             (void)fprintf(stderr,
5507                  "Internal error: combine_similar_tevents(): No back link to tevent %d\n", sm);
5508             exit(1);
5509          }
5510 
5511          linki = (*teventpp)[indx].link;
5512          linkm = (*teventpp)[sm].link;
5513 
5514          (*teventpp)[sm].link = linki;
5515          (*teventpp)[indx].link = sm;
5516          (*teventpp)[bsm].link = linkm;
5517          indx = sm;
5518       }
5519 
5520       /* Create a new TEVENT linked to the last in the group */
5521       cindx = spawn_child_tevent( teventpp, simlist[nsim - 1]);
5522 
5523       /* Replace its line_no and pos with that of the first */
5524       /* in the group.                                      */
5525       (*teventpp)[cindx].line_no = (*teventpp)[simlist[0]].line_no;
5526       (*teventpp)[cindx].pos     = (*teventpp)[simlist[0]].pos;
5527 
5528       /* Store the number of combined events */
5529       (*teventpp)[cindx].combined = nsim;
5530 
5531       /* If more than 1 tevent in the list, flag them for printing */
5532       if ( nsim > 1 ) {
5533          for ( k = 0; k < nsim; k++ )
5534             (*teventpp)[simlist[k]].flag |= PRT_EVENT;
5535          (*teventpp)[cindx].flag |= (PRT_EVENT | COMB_EVENT);
5536          (*teventpp)[cindx].print = nsim + 1;
5537       }
5538 
5539       /* If more than 1 tevent in the list, we must create a */
5540       /* new macro for the combined tevent.                  */
5541 
5542       if ( nsim > 1 ) {
5543          /* Concatenate the macro elements of the similar events */
5544          ibuff = 0;
5545          nelem = 0;
5546          for ( k = 0; k < nsim; k++ ) {
5547             mac = maclist[k];
5548             elem = (*macropp)[mac].element;
5549             nelem += (*macropp)[mac].nelem;
5550             for ( m = 0; m < (*macropp)[mac].total; m++ ) {
5551                elembuff[ibuff++] = (*elementp)[elem++];
5552             }
5553          }
5554 
5555          /* Create a combined macro */
5556          (void)strncpy2(macname, unique_macro_name(maclist, nsim,
5557                         *macropp, COMB_MAC_PREFIX), sizeof(macname) - 1);
5558 
5559          mac = macro_index(macropp, macname, DERIVED_MACRO);
5560          (*teventpp)[cindx].macro = mac;
5561 
5562          loc = macro_element_index( elementp, ibuff );
5563 
5564          (*macropp)[mac].element = loc;
5565          (*macropp)[mac].total = ibuff;
5566          (*macropp)[mac].nelem = nelem;
5567          (*macropp)[mac].line_no = 9999;
5568          /* (*macropp)[mac].modflag |= COMBINED; */
5569          (*macropp)[mac].delay = (*teventpp)[cindx].delay;
5570 
5571          for ( k = 0; k < ibuff; k++ ) {
5572             (*elementp)[loc++] = elembuff[k];
5573          }
5574       }
5575 
5576       /* Mark the similar tevents as "done" */
5577       for ( k = 0; k < nsim; k++ )  {
5578          sm = simlist[k];
5579          (*teventpp)[sm].done = 1;
5580       }
5581    }
5582 
5583    update_current_tevent_generation();
5584 
5585    return;
5586 }
5587 
5588 
5589 /*----------------------------------------------------------------------------+
5590  | Return 1 if two tevents have the same dow_bmask and begin/end days but     |
5591  | not the same offsets.  (If a timer has two events with the same time,      |
5592  | only the first will be executed.)                                          |
5593  +----------------------------------------------------------------------------*/
is_compatible(TEVENT * teventp1,TEVENT * teventp2)5594 int is_compatible ( TEVENT *teventp1, TEVENT *teventp2 )
5595 {
5596    return teventp1->dow_bmap   != teventp2->dow_bmap   ?  0 :
5597           teventp1->resolv_beg != teventp2->resolv_beg ?  0 :
5598           teventp1->resolv_end != teventp2->resolv_end ?  0 :
5599           teventp1->offset     == teventp2->offset     ?  0 :
5600           teventp1->notify     != teventp2->notify     ?  0 : 1 ;
5601 
5602 }
5603 
5604 /*----------------------------------------------------------------------------+
5605  | Copy data from two tevents into one timer.  If the second index is -1, the |
5606  | "stop" part of the timer will be a null event.                             |
5607  +----------------------------------------------------------------------------*/
copy_tevents_to_timer(int tev1,int tev2,int timr,TEVENT * teventp,TIMER * timerp)5608 void copy_tevents_to_timer ( int tev1, int tev2, int timr,
5609                                               TEVENT *teventp, TIMER *timerp)
5610 {
5611    extern int current_timer_generation;
5612 
5613    if ( !teventp )
5614       return;
5615 
5616    timerp[timr].line_no       = 9999;
5617    timerp[timr].generation    = current_timer_generation;
5618    timerp[timr].dow_bmap      = teventp[tev1].dow_bmap;
5619    timerp[timr].sched_beg     = teventp[tev1].sched_beg;
5620    timerp[timr].sched_end     = teventp[tev1].sched_end;
5621    timerp[timr].notify        = teventp[tev1].notify;
5622    timerp[timr].resolv_beg    = teventp[tev1].resolv_beg;
5623    timerp[timr].resolv_end    = teventp[tev1].resolv_end;
5624 
5625    timerp[timr].line1          = teventp[tev1].line_no;
5626    timerp[timr].pos1           = teventp[tev1].pos;
5627    timerp[timr].tevent_start   = tev1;
5628    timerp[timr].flag_start     = teventp[tev1].flag;
5629    timerp[timr].flag_combined  = teventp[tev1].flag | teventp[tev1].flag2;
5630    timerp[timr].offset_start   = teventp[tev1].offset;
5631    timerp[timr].delay_start    = teventp[tev1].delay;
5632    timerp[timr].security_start = teventp[tev1].security;
5633    timerp[timr].macro_start    = teventp[tev1].macro;
5634 
5635    if ( tev2 >= 0 ) {
5636       timerp[timr].line2          = teventp[tev2].line_no;
5637       timerp[timr].pos2           = teventp[tev2].pos;
5638       timerp[timr].tevent_stop    = tev2;
5639       timerp[timr].flag_stop      = teventp[tev2].flag;
5640       timerp[timr].flag_combined |= teventp[tev2].flag | teventp[tev2].flag2;
5641       timerp[timr].offset_stop    = teventp[tev2].offset;
5642       timerp[timr].delay_stop     = teventp[tev2].delay;
5643       timerp[timr].security_stop  = teventp[tev2].security;
5644       timerp[timr].macro_stop     = teventp[tev2].macro;
5645    }
5646    else {
5647       timerp[timr].line2          = 0;
5648       timerp[timr].pos2           = 0;
5649       timerp[timr].tevent_stop    = -1;
5650       timerp[timr].flag_stop      = NO_EVENT;
5651       timerp[timr].offset_stop    = NULL_TIME;
5652       timerp[timr].delay_stop     = 0;
5653       timerp[timr].security_stop  = 0;
5654       timerp[timr].macro_stop     = NULL_MACRO_INDEX;
5655    }
5656 
5657    return;
5658 }
5659 
5660 
5661 /*----------------------------------------------------------------------------+
5662  | Recombine tevents into new timers.                                         |
5663  +----------------------------------------------------------------------------*/
reconstruct_timers(TEVENT * teventp,TIMER ** timerpp)5664 int reconstruct_timers ( TEVENT *teventp, TIMER **timerpp )
5665 {
5666    extern int current_tevent_generation;
5667    extern int timer_generation_delta;
5668 
5669    int          j, k, endch, tnum, indx = 0, matched;
5670    unsigned int flag;
5671 
5672    if ( !teventp )
5673       return 0;
5674 
5675    if ( verbose )
5676       (void)printf("Entering reconstruct_timers() at tevent generation %d\n",
5677            current_tevent_generation);
5678 
5679    timer_generation_delta = 1;
5680    update_current_timer_generation();
5681 
5682    /* Find the end of the existing linked timer chain */
5683    endch = -1;
5684    tnum = 0;
5685    while ( (*timerpp)[tnum].line_no > 0 ) {
5686       if ( (*timerpp)[tnum].link == -1 )
5687          endch = tnum;
5688       tnum++;
5689    }
5690 
5691    /* Count the number of timed events and reset the "done" flags */
5692    tnum = 0;
5693    while ( teventp[tnum].line_no > 0 ) {
5694       teventp[tnum].done = 0;
5695       tnum++;
5696    }
5697 
5698    /* First try to combine like events, i.e., Dawn with Dawn, */
5699    /* Dusk with Dusk, or Clock with Clock.                    */
5700 
5701    for ( j = 0; j < tnum; j++ ) {
5702       if ( teventp[j].generation != current_tevent_generation ||
5703            teventp[j].flag == NO_EVENT ||
5704            teventp[j].flag & CHAIN_EVENT ||
5705            teventp[j].done )
5706          continue;
5707 
5708       flag = teventp[j].flag & TIME_EVENTS;
5709       matched = 0;
5710       for ( k = j + 1; k < tnum; k++ ) {
5711          if ( teventp[k].generation != current_tevent_generation ||
5712               teventp[k].flag == NO_EVENT ||
5713               (teventp[k].flag & TIME_EVENTS) != flag  ||
5714               teventp[k].done )
5715             continue;
5716          if ( is_compatible(&teventp[j], &teventp[k]) ) {
5717             matched = 1;
5718             break;
5719          }
5720       }
5721 
5722       if ( matched ) {
5723          /* Two tevents found which can be combined into one timer */
5724          indx = timer_index(timerpp);
5725          copy_tevents_to_timer( j, k, indx, teventp, *timerpp );
5726 
5727          /* Remove these tevents from further consideration */
5728          teventp[j].done = 1;
5729          teventp[k].done = 1;
5730          /* Link to existing chain */
5731          (*timerpp)[endch].link = indx;
5732          (*timerpp)[indx].link = -1;
5733          endch = indx;
5734       }
5735    }
5736 
5737    /* Next try to combine any leftover Dawn/Dusk events with */
5738    /* each other if allowed.                                 */
5739    if ( configp->res_overlap == RES_OVLAP_COMBINED ) {
5740       for ( j = 0; j < tnum; j++ ) {
5741          if ( teventp[j].generation != current_tevent_generation ||
5742               teventp[j].flag == NO_EVENT ||
5743               teventp[j].flag & (CLOCK_EVENT | CHAIN_EVENT) ||
5744               teventp[j].done )
5745             continue;
5746 
5747          matched = 0;
5748          for ( k = j + 1; k < tnum; k++ ) {
5749             if ( teventp[k].generation != current_tevent_generation ||
5750                  teventp[k].flag == NO_EVENT ||
5751                  teventp[k].flag & (CLOCK_EVENT | CHAIN_EVENT) ||
5752                  teventp[k].done )
5753                continue;
5754             if ( is_compatible(&teventp[j], &teventp[k]) ) {
5755                matched = 1;
5756                break;
5757             }
5758          }
5759 
5760          if ( matched ) {
5761             /* Two tevents found which can be combined into one timer */
5762             indx = timer_index(timerpp);
5763             copy_tevents_to_timer( j, k, indx, teventp, *timerpp );
5764 
5765             /* Remove these tevents from further consideration */
5766             teventp[j].done = 1;
5767             teventp[k].done = 1;
5768             /* Link to existing chain */
5769             (*timerpp)[endch].link = indx;
5770             (*timerpp)[indx].link = -1;
5771             endch = indx;
5772          }
5773          else {
5774             /* Singleton tevent; set the timer stop event to null */
5775             indx = timer_index(timerpp);
5776             copy_tevents_to_timer( j, -1, indx, teventp, *timerpp );
5777 
5778             /* Remove this tevent from further consideration */
5779             teventp[j].done = 1;
5780             /* Link to existing chain */
5781             (*timerpp)[endch].link = indx;
5782             (*timerpp)[indx].link = -1;
5783             endch = indx;
5784          }
5785       }
5786    }
5787 
5788    /* Finally, try to combine all remaining events, excluding */
5789    /* Dawn and Dusk combinations.                             */
5790    for ( j = 0; j < tnum; j++ ) {
5791       if ( teventp[j].generation != current_tevent_generation ||
5792            teventp[j].flag == NO_EVENT ||
5793            teventp[j].flag & CHAIN_EVENT ||
5794            teventp[j].done )
5795          continue;
5796 
5797       matched = 0;
5798       flag = teventp[j].flag & (DAWN_EVENT | DUSK_EVENT);
5799       for ( k = j + 1; k < tnum; k++ ) {
5800          if ( teventp[k].generation != current_tevent_generation ||
5801               teventp[k].flag == NO_EVENT ||
5802               teventp[k].flag & CHAIN_EVENT ||
5803               (teventp[k].flag & (DAWN_EVENT | DUSK_EVENT) && flag)  ||
5804               teventp[k].done )
5805             continue;
5806          if ( is_compatible(&teventp[j], &teventp[k]) && (configp->fix_stopstart_error == NO) ) {
5807             matched = 1;
5808             break;
5809          }
5810       }
5811 
5812       if ( matched ) {
5813          /* Two tevents found which can be combined into one timer */
5814          indx = timer_index(timerpp);
5815          copy_tevents_to_timer( j, k, indx, teventp, *timerpp );
5816 
5817          /* Remove these tevents from further consideration */
5818          teventp[j].done = 1;
5819          teventp[k].done = 1;
5820          /* Link to existing chain */
5821          (*timerpp)[endch].link = indx;
5822          (*timerpp)[indx].link = -1;
5823          endch = indx;
5824       }
5825       else {
5826          /* Singleton tevent; set the timer stop event to null */
5827          indx = timer_index(timerpp);
5828          copy_tevents_to_timer( j, -1, indx, teventp, *timerpp );
5829 
5830          /* Remove this tevent from further consideration */
5831          teventp[j].done = 1;
5832          /* Link to existing chain */
5833          (*timerpp)[endch].link = indx;
5834          (*timerpp)[indx].link = -1;
5835          endch = indx;
5836       }
5837    }
5838    return indx;
5839 }
5840 
5841 /*---------------------------------------------------------------------+
5842  | Store a sequence number 1-7 with each trigger corresponding to its  |
5843  | order among those triggers which execute the same macro.            |
5844  | Return non-zero if the sequence number is greater than 7, which is  |
5845  | the largest number the CM11A hardware can report (reserving 7 for   |
5846  | cases of 7 or higher) to unambiguously identify the particular      |
5847  | trigger.                                                            |
5848  +---------------------------------------------------------------------*/
set_trigger_tags(TRIGGER * triggerp)5849 int set_trigger_tags ( TRIGGER *triggerp )
5850 {
5851    int j, count, warn;
5852    int index[1000];
5853 
5854    if ( triggerp == NULL )
5855       return 0;
5856 
5857    for ( j = 0; j < 1000; j++ )
5858       index[j] = 0;
5859 
5860    j = 0;
5861    warn = 0;
5862    while ( triggerp[j].line_no > 0 ) {
5863       count = ++index[triggerp[j].macro];
5864       if ( count > 6 )
5865          warn = 1;
5866       triggerp[j].tag = min(7, count);
5867       j++;
5868    }
5869 
5870    return warn;
5871 }
5872 
5873 
5874 /*----------------------------------------------------------------------------+
5875  | Mark the macros actually in use in the current program interval            |
5876  +----------------------------------------------------------------------------*/
identify_macros_in_use(MACRO * macrop,TEVENT * teventp)5877 void identify_macros_in_use ( MACRO *macrop, TEVENT *teventp )
5878 {
5879    extern int current_tevent_generation;
5880 
5881    int j;
5882 
5883    /* First, those called by triggers.  Others not */
5884    j = 0;
5885    while ( macrop[j].label[0] != '\0' ) {
5886       /* Set all offsets to zero */
5887       macrop[j].offset = 0;
5888 
5889       if ( macrop[j].refer & TRIGGER_PARSER )
5890          macrop[j].use = USED;
5891       else
5892          macrop[j].use = NOTUSED;
5893       j++;
5894    }
5895 
5896    /* Then those called by tevents which are active in this interval */
5897    if ( teventp == NULL )
5898       return;
5899 
5900    j = 0;
5901    while ( teventp[j].line_no > 0 ) {
5902       if ( teventp[j].generation == current_tevent_generation &&
5903            teventp[j].flag != NO_EVENT ) {
5904          macrop[teventp[j].macro].use = USED;
5905       }
5906       j++;
5907    }
5908 
5909    return;
5910 }
5911 
5912 /*----------------------------------------------------------+
5913  | Create separate tevents for any interval where dawn/dusk |
5914  | tevent intervals partially overlap, so that on any given |
5915  | date each tevent will have the same value for dawn and/or|
5916  | dusk.                                                    |
5917  +----------------------------------------------------------*/
resolve_tevent_overlap_comb(TEVENT ** teventpp)5918 void resolve_tevent_overlap_comb ( TEVENT **teventpp )
5919 {
5920    extern int    current_tevent_generation;
5921    int           breaker[366];
5922    unsigned int  tflag, flag[366];
5923    int           intv[732];
5924    int           beg, end;
5925    int           i, j, val, indx, cindx, size, nintv;
5926 
5927    if ( !(*teventpp) )
5928       return;
5929 
5930    if ( verbose )
5931       (void)printf("Entering resolve_tevent_overlap_comb() at generation %d\n",
5932               current_tevent_generation);
5933 
5934    /* If there are no dawn/dusk tevents, there's nothing */
5935    /* to do here.                                       */
5936 
5937    i = 0;
5938    val = 0;
5939    while ( (*teventpp)[i].line_no > 0 ) {
5940       if ( (*teventpp)[i].generation != current_tevent_generation ) {
5941          i++;
5942          continue;
5943       }
5944       if ( (*teventpp)[i].flag & (DAWN_EVENT | DUSK_EVENT) )
5945          val++;
5946       i++;
5947    }
5948 
5949    if ( val == 0 )
5950       return;
5951 
5952 
5953    /* Initialize arrays for a breaker and for flags */
5954    for ( j = 0; j < 366; j++ ) {
5955       breaker[j] = 0;
5956       flag[j] = 0;
5957    }
5958 
5959    /* Fill in the breaker array for dawn/dusk tevents.   */
5960    /* Increment the generation of clock tevents          */
5961    /* and we're done with them.                          */
5962    i = 0;
5963    while ( (*teventpp)[i].line_no > 0 ) {
5964       if ( (*teventpp)[i].generation != current_tevent_generation ) {
5965          i++;
5966          continue;
5967       }
5968 
5969       if ( (*teventpp)[i].flag & (DAWN_EVENT | DUSK_EVENT) ) {
5970          for ( j = (*teventpp)[i].resolv_beg; j <= (*teventpp)[i].resolv_end; j++ )
5971             breaker[j] += 1;
5972       }
5973       else {
5974          (*teventpp)[i].flag2 = 0;
5975          increment_tevent_generation( *teventpp, i );
5976       }
5977       i++;
5978    }
5979 
5980 
5981    size = 0;
5982    i = 0;
5983    while ( (*teventpp)[i].line_no > 0 ) {
5984       if ( (*teventpp)[i].generation != current_tevent_generation ) {
5985          i++;
5986          continue;
5987       }
5988 
5989       tflag = (*teventpp)[i].flag & (DAWN_EVENT | DUSK_EVENT) ;
5990       beg = (*teventpp)[i].resolv_beg;
5991       end = (*teventpp)[i].resolv_end;
5992       val = breaker[beg];
5993       (*teventpp)[i].intv = size;
5994       intv[size++] = beg;
5995       for ( j = beg; j <= end; j++ ) {
5996          /* Flags for all sun timers are OR'd together. */
5997          flag[j] |= tflag;
5998          if ( val != breaker[j] ) {
5999             if ( j > beg ) {
6000                intv[size++] = j - 1;
6001                intv[size++] = j;
6002             }
6003             val = breaker[j];
6004          }
6005       }
6006       intv[size++] = end;
6007       (*teventpp)[i].nintv = (size - (*teventpp)[i].intv) / 2;
6008       i++;
6009    }
6010 
6011    i = 0;
6012    while ( (*teventpp)[i].line_no > 0 ) {
6013       if ( (*teventpp)[i].generation != current_tevent_generation ) {
6014          i++;
6015          continue;
6016       }
6017 
6018       nintv = (*teventpp)[i].nintv;
6019       indx = (*teventpp)[i].intv + 2 * (nintv - 1);
6020       for ( j = 0; j < nintv; j++ ) {
6021          beg = intv[indx];
6022          end = intv[indx + 1];
6023          indx -= 2;
6024          tflag = flag[beg];
6025 
6026          cindx = spawn_child_tevent( teventpp, i );
6027 
6028          /* The child tevent flag2 indicates whether to resolve */
6029          /* sun times based on dawn only, dusk only, or both.   */
6030 
6031          (*teventpp)[cindx].flag2 |= tflag;
6032          (*teventpp)[cindx].resolv_beg = beg;
6033          (*teventpp)[cindx].resolv_end = end;
6034       }
6035       i++;
6036    }
6037 
6038    (void) update_current_tevent_generation();
6039 
6040    return;
6041 }
6042 
6043 /*----------------------------------------------------------+
6044  | Create separate tevents for any interval where dawn/dusk |
6045  | tevent intervals partially overlap, so that on any given |
6046  | date each tevent will have the same value for dawn and/or|
6047  | dusk.                                                    |
6048  | This alternate function does dawn and dusk separately.   |
6049  +----------------------------------------------------------*/
resolve_tevent_overlap_sep(TEVENT ** teventpp)6050 void resolve_tevent_overlap_sep ( TEVENT **teventpp )
6051 {
6052    extern int    current_tevent_generation;
6053    int           breaker[366];
6054    unsigned int  dflag;
6055    int           intv[732];
6056    int           beg, end;
6057    int           i, j, k, val, indx, cindx, size, nintv;
6058 
6059    if ( !(*teventpp) )
6060       return;
6061 
6062    if ( verbose )
6063       (void)printf("Entering resolve_tevent_overlap_sep() at generation %d\n",
6064               current_tevent_generation);
6065 
6066    /* If there are no dawn/dusk tevents, there's nothing */
6067    /* to do here.                                       */
6068 
6069    i = 0;
6070    val = 0;
6071    while ( (*teventpp)[i].line_no > 0 ) {
6072       if ( (*teventpp)[i].generation != current_tevent_generation ) {
6073          i++;
6074          continue;
6075       }
6076       if ( (*teventpp)[i].flag & (DAWN_EVENT | DUSK_EVENT) )
6077          val++;
6078       i++;
6079    }
6080    if ( val == 0 )
6081       return;
6082 
6083    /* Increment the generation of non-dawn/dusk tevents  */
6084    /* and then we're done with them.                     */
6085    i = 0;
6086    while ( (*teventpp)[i].line_no > 0 ) {
6087       if ( (*teventpp)[i].generation != current_tevent_generation ||
6088            (*teventpp)[i].flag & (DAWN_EVENT | DUSK_EVENT) ) {
6089          i++;
6090          continue;
6091       }
6092       (*teventpp)[i].flag2 = 0;
6093       increment_tevent_generation( *teventpp, i );
6094 
6095       i++;
6096    }
6097 
6098    /* Set dflag for the first pass through the loop */
6099    dflag = DAWN_EVENT;
6100    for ( k = 0; k < 2; k++ ) {
6101       /* Initialize arrays for a breaker and for flags */
6102       for ( i = 0; i < 366; i++ ) {
6103          breaker[i] = 0;
6104       }
6105 
6106       /* Fill in the breaker array */
6107       i = 0;
6108       while ( (*teventpp)[i].line_no > 0 ) {
6109          if ( (*teventpp)[i].generation != current_tevent_generation ||
6110               ((*teventpp)[i].flag & dflag) == 0 ) {
6111             i++;
6112             continue;
6113          }
6114          for ( j = (*teventpp)[i].resolv_beg; j <= (*teventpp)[i].resolv_end; j++ )
6115             breaker[j] += 1;
6116          i++;
6117       }
6118 
6119       size = 0;
6120       i = 0;
6121       while ( (*teventpp)[i].line_no > 0 ) {
6122          if ( (*teventpp)[i].generation != current_tevent_generation ||
6123               ((*teventpp)[i].flag & dflag) == 0 ) {
6124             i++;
6125             continue;
6126          }
6127 
6128          beg = (*teventpp)[i].resolv_beg;
6129          end = (*teventpp)[i].resolv_end;
6130          val = breaker[beg];
6131          (*teventpp)[i].intv = size;
6132          intv[size++] = beg;
6133          for ( j = beg; j <= end; j++ ) {
6134             if ( val != breaker[j] ) {
6135                if ( j > beg ) {
6136                   intv[size++] = j - 1;
6137                   intv[size++] = j;
6138                }
6139                val = breaker[j];
6140             }
6141          }
6142          intv[size++] = end;
6143          (*teventpp)[i].nintv = (size - (*teventpp)[i].intv) / 2;
6144          i++;
6145       }
6146 
6147       i = 0;
6148       while ( (*teventpp)[i].line_no > 0 ) {
6149          if ( (*teventpp)[i].generation != current_tevent_generation ||
6150               ((*teventpp)[i].flag & dflag) == 0 ) {
6151             i++;
6152             continue;
6153          }
6154 
6155          nintv = (*teventpp)[i].nintv;
6156          indx = (*teventpp)[i].intv + 2 * (nintv - 1);
6157          for ( j = 0; j < nintv; j++ ) {
6158             beg = intv[indx];
6159             end = intv[indx + 1];
6160             indx -= 2;
6161 
6162             cindx = spawn_child_tevent( teventpp, i );
6163 
6164             /* The child tevent flag2 indicates whether to resolve */
6165             /* sun times based on dawn only, dusk only, or both.   */
6166 
6167             (*teventpp)[cindx].flag2 |= dflag;
6168             (*teventpp)[cindx].resolv_beg = beg;
6169             (*teventpp)[cindx].resolv_end = end;
6170          }
6171          i++;
6172       }
6173       /* Set dflag for second pass through the loop */
6174       dflag = DUSK_EVENT;
6175    }
6176 
6177    (void) update_current_tevent_generation();
6178 
6179    return;
6180 }
6181 
6182 
6183 
6184 /*---------------------------------------------------------+
6185  | Manage a binary search for the highest integer yielding |
6186  | a "good" result, i.e., last_result => 0, such that      |
6187  | integer+1 yields a "bad" result, i.e., last_result < 0. |
6188  | The function returns 0 until such time as the value in  |
6189  | next_try is the desired integer, when it then returns 1.|
6190  +---------------------------------------------------------*/
iter_mgr(int last_result,long * next_try,long max_step,int * restart)6191 int iter_mgr ( int last_result, long *next_try,
6192                                  long max_step, int *restart )
6193 {
6194    static long last_bad, last_good ;
6195    static int  stage;
6196    long        step;
6197 
6198    if ( *restart ) {
6199       *restart = 0;
6200       stage = 0 ;
6201    }
6202 
6203    switch ( stage ) {
6204       case 0 :
6205          /* Start by trying 0 */
6206          stage = 1;
6207          *next_try = 0L;
6208          return 0;
6209       case 1 :
6210          /* Then try 1 */
6211          stage = 2 ;
6212          if ( last_result < 0 ) {
6213             last_bad = 0L;
6214             *next_try = 1L;
6215             return 0;
6216          }
6217          else {
6218             return 1;
6219          }
6220       case 2 :
6221          /* Now keep doubling until we get a "good" result */
6222          if ( last_result < 0 ) {
6223             last_bad = *next_try;
6224             step = min( *next_try + 1L, max_step );
6225             *next_try += step;
6226             return 0;
6227          }
6228          else {
6229             last_good = *next_try;
6230             if ( last_result == 0 || (last_good - last_bad) == 1L )
6231                return 1;
6232             else {
6233                stage = 3;
6234                *next_try = (last_bad + last_good) / 2L;
6235                return 0;
6236             }
6237          }
6238       case 3 :
6239          /* Now keep splitting the range until the values  */
6240          /* yielding good and bad results differ by only 1 */
6241          if ( last_result < 0 ) {
6242             last_bad = *next_try;
6243             *next_try = max((last_bad + last_good)/2L, last_bad + 1L);
6244             return 0;
6245          }
6246          else {
6247             if ( last_result == 0L || (last_good - last_bad) == 1L )
6248                return 1;
6249             last_good = *next_try;
6250             *next_try = max((last_bad + last_good)/2L, last_bad + 1L);
6251             return 0;
6252          }
6253    }
6254    return 0;   /* Keep the compiler happy */
6255 }
6256 
6257 /*---------------------------------------------------------+
6258  | Return the fixed time approximating dawn or dusk times  |
6259  | over an interval from the array of daily values, the    |
6260  | beginning and ending year days, and the option chosen   |
6261  | by the user.  Recognized options (defines) are: FIRST,  |
6262  | EARLIEST, LATEST, AVERAGE, and MEDIAN.                  |
6263  +---------------------------------------------------------*/
set_suntime(int * daily_sun,int begin_day,int end_day,unsigned char option,int * error)6264 int set_suntime ( int *daily_sun, int begin_day, int end_day,
6265                        unsigned char option, int *error )
6266 {
6267    int      rmin, rmax, dsj, value;
6268    int      j;
6269    long int sum;
6270 
6271    if ( begin_day > end_day ) {
6272       (void)fprintf(stderr,
6273          "set_suntime(): Begin day (%d) > End day (%d)\n",
6274                begin_day, end_day);
6275       return -1;
6276    }
6277 
6278    rmin = rmax = daily_sun[begin_day];
6279    sum = 0L;
6280    for ( j = begin_day; j <= end_day; j++ ) {
6281       dsj = daily_sun[j];
6282       sum += (long)dsj;
6283       rmin = min(rmin, dsj);
6284       rmax = max(rmax, dsj);
6285    }
6286    *error = rmax - rmin;
6287 
6288    switch ( option ) {
6289       case FIRST :
6290          /* Use the value from the first day of the interval */
6291          value = daily_sun[begin_day];
6292          break;
6293       case EARLIEST :
6294          /* Use earliest value over the interval */
6295          value = rmin;
6296          break;
6297       case LATEST :
6298          /* Use latest value over the interval */
6299          value = rmax;
6300          break;
6301       case AVERAGE :
6302          /* Use average value over the interval */
6303          value = (int)(sum / (long)(end_day - begin_day + 1));
6304          break;
6305       case MEDIAN :
6306          /* Use median value over the interval */
6307          value = (rmin + rmax) / 2;
6308          break;
6309       default :
6310          (void)fprintf(stderr,
6311            "set_suntime(): Option (%d) not recognized.", option);
6312          value = -1;
6313          break;
6314    }
6315 
6316    return value;
6317 }
6318 
6319 /*---------------------------------------------------------+
6320  | For each timer with dawn/dusk based events, create one  |
6321  | or more new timers with subintervals of the date range, |
6322  | with the subintervals chosen to minimize the error      |
6323  | in the dawn/dusk times over that subinterval subject to |
6324  | the available CM11a memory free space.                  |
6325  | Then resolve the dawn/dusk based times into clock times.|
6326  | The resulting error in dawn/dusk times from actual      |
6327  | (as determined by our sunrise/sunset calculator) is     |
6328  | passed back to the caller.                              |
6329  +---------------------------------------------------------*/
resolve_sun_times(TIMER ** timerpp,CALEND * calendp,int freespace,int * tot_timers,int * max_error)6330 int resolve_sun_times( TIMER **timerpp, CALEND *calendp,
6331              int freespace, int *tot_timers, int *max_error )
6332 {
6333    extern int    line_no, current_timer_generation;
6334 
6335    double        latitude, longitude;
6336    time_t        tzone;
6337 
6338    long          julianday;
6339    long          errval;
6340 
6341    unsigned int  size, sizelimit /*, totintv = 0 */;
6342 
6343    int           dawn[366], dusk[366], scode[366], dummy[366], dst[366];
6344    int           *ptr1 = NULL, *ptr2 = NULL;
6345    int           *intvp = NULL;
6346    int           i, j, ii;
6347    int           r, rmin, rmax, s, smin, smax, iter;
6348    int           value;
6349    int           indx, cindx;
6350    int           result = -1, restart;
6351    int           dbegin, dend;
6352    int           year, month, day, yday;
6353    int           dawnerr, duskerr, error;
6354    int           sun_timers, res_timers;
6355    int           status = 0, totintv = 0;
6356 
6357    enum        { STtime, DLtime };
6358 
6359 
6360    /* Report error as zero in case of early return */
6361    *max_error = 0;
6362 
6363    /* If no timers at all, simply return */
6364    if ( !(*timerpp) ) {
6365       *tot_timers = 0;
6366       *max_error = 0;
6367       return 0;
6368    }
6369 
6370    if ( verbose )
6371       (void)printf("Entering resolve_sun_times() at timer generation %d\n",
6372                 current_timer_generation);
6373 
6374    /* Get geographic location and timezone from   */
6375    /* that stored in the global CONFIG structure. */
6376 
6377    if ( configp->loc_flag != (LATITUDE | LONGITUDE) ) {
6378       (void)fprintf(stderr,
6379          "LATITUDE and/or LONGITUDE not specified in %s\n",
6380 	    pathspec(CONFIG_FILE));
6381       exit(1);
6382    }
6383 
6384    latitude  = configp->latitude;
6385    longitude = configp->longitude;
6386    tzone     = configp->tzone;
6387 
6388    /* Get date information from the CALEND structure */
6389 
6390    year        = calendp->year;
6391    month       = calendp->month;
6392    day         = calendp->mday;
6393    yday        = calendp->yday;
6394 
6395    /* Count the timers which are already fully resolved and  */
6396    /* count the timers needing dawn/dusk time resolution.    */
6397    /* Change the generation of the fully-resolved timers to  */
6398    /* the next generation.                                   */
6399 
6400    j = 0;
6401    res_timers = sun_timers = 0;
6402    while ( (*timerpp)[j].line_no > 0 ) {
6403       if ( (*timerpp)[j].generation != current_timer_generation ||
6404            (*timerpp)[j].flag_combined == NO_EVENT ) {
6405          j++;
6406          continue;
6407       }
6408 
6409       if ( ((*timerpp)[j].flag_combined & TIME_EVENTS) == CLOCK_EVENT ) {
6410          res_timers++;
6411       }
6412       else {
6413          sun_timers++;
6414       }
6415       j++;
6416    }
6417 
6418    /* If there are no timers needing dawn/dusk resolution,   */
6419    /* we're done here and can return.                        */
6420 
6421    if ( sun_timers == 0 ) {
6422       *tot_timers = res_timers;
6423       *max_error = 0;
6424       (void) update_current_timer_generation();
6425       return 0;
6426    }
6427 
6428 
6429    /* Calculate sunrise and sunset for 366 days from today's */
6430    /* date and store in arrays.                              */
6431 
6432    /* Get today's Julian Day - the big number, not the day   */
6433    /* of the year.                                           */
6434 
6435    julianday = daycount2JD(calendp->today);
6436 
6437    for ( j = 0; j < 366; j++ ) {
6438       scode[j] = suntimes( latitude, longitude, tzone, julianday,
6439          configp->sunmode, configp->sunmode_offset, &dawn[j], &dusk[j], NULL, NULL );
6440       julianday++ ;
6441    }
6442 
6443    /* Also create a dummy array */
6444    for ( j = 0; j < 366; j++ )
6445       dummy[j] = 0;
6446 
6447    /* For Arctic/Antarctic regions - Substitute a dawn and/or  */
6448    /* dusk time for days when the sun is continually above or  */
6449    /* below the horizon, or no sunrise or sunset.              */
6450 
6451    for ( j = 0; j < 366; j++ ) {
6452       if ( scode[j] & UP_ALL_DAY ) {
6453          dawn[j] = 1;     /* 00:01 */
6454 	 dusk[j] = 1438;  /* 23:58 */
6455       }
6456       else if ( scode[j] & DOWN_ALL_DAY ) {
6457 	 dawn[j] = 1438;
6458 	 dusk[j] = 1;
6459       }
6460       else if ( scode[j] & NO_SUNRISE ) {
6461 	 dawn[j] = 1;
6462       }
6463       else if ( scode[j] & NO_SUNSET ) {
6464 	 dusk[j] = 1438;
6465       }
6466    }
6467 
6468    /* Put upper and/or lower bounds on dawn if specified in    */
6469    /* the config file.                                         */
6470 
6471    if ( configp->min_dawn != OFF || configp->max_dawn != OFF ) {
6472       for ( j = 0; j < 366; j++ )
6473          dst[j] = time_adjust(j + yday, dawn[j], LGL2STD);
6474       if ( configp->min_dawn != OFF ) {
6475          for ( j = 0; j < 366; j++ )
6476             dawn[j] = max(dawn[j], configp->min_dawn - dst[j]);
6477       }
6478       if ( configp->max_dawn != OFF ) {
6479          for ( j = 0; j < 366; j++ )
6480             dawn[j] = min(dawn[j], configp->max_dawn - dst[j]);
6481       }
6482    }
6483 
6484    /* Put upper and/or lower bounds on dusk if specified in    */
6485    /* the config file.                                         */
6486 
6487    if ( configp->min_dusk != OFF || configp->max_dusk != OFF ) {
6488       for ( j = 0; j < 366; j++ )
6489          dst[j] = time_adjust(j + yday, dusk[j], LGL2STD);
6490       if ( configp->min_dusk != OFF ) {
6491          for ( j = 0; j < 366; j++ )
6492             dusk[j] = max(dusk[j], configp->min_dusk - dst[j]);
6493       }
6494       if ( configp->max_dusk != OFF ) {
6495          for ( j = 0; j < 366; j++ )
6496             dusk[j] = min(dusk[j], configp->max_dusk - dst[j]);
6497       }
6498    }
6499 
6500    /* In the following section, the date interval specified in */
6501    /* a timer is broken up into subintervals, each subinterval */
6502    /* having the same sunrise and/or sunset time error over    */
6503    /* the subinterval.  The error is minimized subject to the  */
6504    /* constraint of the available freespace in the CM11a       */
6505    /* memory - each subinterval will require a separate timer  */
6506    /* occupying 9 bytes of memory.                             */
6507 
6508    /* Set pointers to arrays depending whether clock or        */
6509    /* dawn/dusk timers and store them in the timer structure.  */
6510 
6511    i = 0;
6512    while (  (*timerpp)[i].line_no > 0  ) {
6513       if ( (*timerpp)[i].generation != current_timer_generation ||
6514            (*timerpp)[i].flag_combined == NO_EVENT ) {
6515          i++;
6516          continue;
6517       }
6518 
6519       switch ( (*timerpp)[i].flag_combined & (DAWN_EVENT | DUSK_EVENT) ) {
6520          case DAWN_EVENT :
6521             (*timerpp)[i].ptr_start = dawn;
6522             (*timerpp)[i].ptr_stop  = dawn;
6523             break;
6524 
6525          case DUSK_EVENT :
6526             (*timerpp)[i].ptr_start = dusk;
6527             (*timerpp)[i].ptr_stop  = dusk;
6528             break;
6529 
6530          case (DAWN_EVENT | DUSK_EVENT) :
6531             (*timerpp)[i].ptr_start = dawn;
6532             (*timerpp)[i].ptr_stop  = dusk;
6533             break;
6534 
6535          default :
6536             (*timerpp)[i].ptr_start = dummy;
6537             (*timerpp)[i].ptr_stop  = dummy;
6538             break;
6539       }
6540 
6541       /* If both the start and stop pointers are the same */
6542       /* we need use only one of them.                    */
6543 
6544       (*timerpp)[i].num_ptr =
6545         (*timerpp)[i].ptr_start == (*timerpp)[i].ptr_stop ? 1 : 2 ;
6546 
6547       i++;
6548    }
6549 
6550    /* Now start the iteration to determine the minimum    */
6551    /* error in dawn and or dusk times consistant with     */
6552    /* the available freespace in the CM11a memory.        */
6553 
6554    /* Each iteration starts with a specified error in     */
6555    /* the dawn or dusk value and determines the total     */
6556    /* number of intervals required.  Then iterates the    */
6557    /* error until the total number of intervals will just */
6558    /* fit in the available CM11a memory freespace.        */
6559 
6560    /* Upper limit to speed up the iteration and prevent   */
6561    /* possible integer overflow in weird cases.           */
6562    sizelimit = 2 * PROMSIZE / 9 + 100 ;
6563 
6564    iter = 0;
6565    restart = 1;
6566    while ( iter < 1000 && !iter_mgr( result, &errval,
6567                                          1000L, &restart) ) {
6568       iter++;
6569       totintv = 0;
6570       size = 0;
6571 
6572       i = 0;
6573       while ( (*timerpp)[i].line_no > 0 ) {
6574 
6575          if ( size > sizelimit )
6576             break;
6577 
6578          if ( (*timerpp)[i].generation != current_timer_generation ||
6579               (*timerpp)[i].flag_combined == NO_EVENT ) {
6580             i++;
6581             continue;
6582          }
6583 
6584          (*timerpp)[i].intv = size;
6585          ptr1 = (*timerpp)[i].ptr_start;
6586          ptr2 = (*timerpp)[i].ptr_stop;
6587 
6588          indx = 0;
6589          dbegin = (*timerpp)[i].resolv_beg;
6590          dend   = (*timerpp)[i].resolv_end;
6591          ii = intv_index(&intvp, &size);
6592          intvp[ii] = dbegin;
6593 
6594          switch ( (*timerpp)[i].num_ptr ) {
6595             case 1 :
6596                rmin = rmax = ptr1[dbegin];
6597                for ( j = dbegin; j <= dend; j++ ) {
6598                   r = ptr1[j];
6599                   rmin = min(rmin, r); rmax = max(rmax, r);
6600                   if ( (rmax - rmin) > errval ) {
6601                      if ( j > dbegin ) {
6602                         ii = intv_index(&intvp, &size);
6603                         intvp[ii] = j-1;
6604                         ii = intv_index(&intvp, &size);
6605                         intvp[ii] = j;
6606                      }
6607                      rmin = rmax = r;
6608                   }
6609                }
6610                break;
6611 
6612             case 2 :
6613                rmin = rmax = ptr1[dbegin];
6614                smin = smax = ptr2[dbegin];
6615                for ( j = dbegin; j <= dend; j++ ) {
6616                   r = ptr1[j]; s = ptr2[j];
6617                   rmin = min(rmin, r); smin = min(smin, s);
6618                   rmax = max(rmax, r); smax = max(smax, s);
6619                   if ( (rmax - rmin) > errval ||
6620                        (smax - smin) > errval     ) {
6621                      if ( j > dbegin ) {
6622                         ii = intv_index(&intvp, &size);
6623                         intvp[ii] = j-1;
6624                         ii = intv_index(&intvp, &size);
6625                         intvp[ii] = j;
6626                      }
6627                      rmin = rmax = r;
6628                      smax = smin = s;
6629                   }
6630                }
6631                break;
6632          }
6633          ii = intv_index(&intvp, &size);
6634          intvp[ii] = dend;
6635 
6636          (*timerpp)[i].nintv = (size - (*timerpp)[i].intv)/2 ;
6637          i++;
6638       }
6639       totintv = size/2;
6640 
6641       result = freespace > 9 * totintv ?  1 :
6642                freespace < 9 * totintv ? -1 : 0  ;
6643 
6644       /* If the error exceeds 2 days ( 2880 minutes ) at this */
6645       /* point there's no point in trying further.            */
6646 
6647       if ( errval > (2*24*60) ) {
6648          (void) fprintf(stderr, "Insufficient CM11a memory.\n");
6649          return 1;
6650       }
6651    }
6652 
6653    /* Check that the iteration limit has not been exceeded */
6654    if ( iter >= 1000 ) {
6655       (void) fprintf(stderr, "Iteration limit exceeded.\n");
6656       return 1;
6657    }
6658 
6659    /* These are the max error in dawn/dusk times from "actual" */
6660    /* and the total number of timers to be programmed.         */
6661    *max_error = (int)errval;
6662    *tot_timers = (int)totintv;
6663 
6664    /* In the following, a new timer is created for each  */
6665    /* date subinterval and linked in a chain to the      */
6666    /* original (parent) timers.  The offsets from dawn   */
6667    /* and dusk are replaced by clock times.              */
6668 
6669    i = 0;
6670    while ( (*timerpp)[i].line_no > 0 ) {
6671 
6672       if ( (*timerpp)[i].generation != current_timer_generation ||
6673            (*timerpp)[i].flag_combined == NO_EVENT  ) {
6674          i++;
6675          continue;
6676       }
6677 
6678       line_no = (*timerpp)[i].line_no ;
6679       dawnerr = duskerr = 0;
6680 
6681       indx = (*timerpp)[i].intv;
6682       for ( j = 0; j < (*timerpp)[i].nintv; j++ ) {
6683 
6684          dbegin = intvp[indx];
6685          dend   = intvp[indx + 1];
6686          indx += 2;
6687 
6688          /* Create a child timer */
6689          cindx = spawn_child_timer( timerpp, i );
6690 
6691          (*timerpp)[cindx].resolv_beg = dbegin;
6692          (*timerpp)[cindx].resolv_end = dend;
6693 
6694          /* Add the fixed time for dawn/dusk to the offsets therefrom */
6695          /* stored in the timer structures.                           */
6696 
6697          switch ( (*timerpp)[cindx].flag_start & (DAWN_EVENT | DUSK_EVENT) ) {
6698             case DAWN_EVENT :
6699                value = set_suntime(dawn, dbegin, dend, configp->dawn_option, &error);
6700                (*timerpp)[cindx].offset_start += value;
6701                (*timerpp)[cindx].error_start = error;
6702                break;
6703             case DUSK_EVENT :
6704                value = set_suntime(dusk, dbegin, dend, configp->dusk_option, &error);
6705                (*timerpp)[cindx].offset_start += value;
6706                (*timerpp)[cindx].error_start = error;
6707                break;
6708             default :
6709                value = 0;
6710                (*timerpp)[cindx].error_start = 0;
6711                break;
6712          }
6713 
6714          if ( (*timerpp)[cindx].flag_start != NO_EVENT  &&
6715              ( (*timerpp)[cindx].offset_start < 0  ||
6716                          (*timerpp)[cindx].offset_start > 1439 ) ) {
6717               (void)fprintf(stderr,
6718                   "Line %d: Expanded timer[%d] start time %d falls outside range 0-1439\n",
6719                       (*timerpp)[cindx].line1, cindx, (*timerpp)[cindx].offset_start );
6720               status = 1;
6721          }
6722 
6723          switch ( (*timerpp)[cindx].flag_stop & (DAWN_EVENT | DUSK_EVENT)) {
6724             case DAWN_EVENT :
6725                value = set_suntime(dawn, dbegin, dend, configp->dawn_option, &error);
6726                (*timerpp)[cindx].offset_stop += value;
6727                (*timerpp)[cindx].error_stop = error;
6728                break;
6729             case DUSK_EVENT :
6730                value = set_suntime(dusk, dbegin, dend, configp->dusk_option, &error);
6731                (*timerpp)[cindx].offset_stop += value;
6732                (*timerpp)[cindx].error_stop = error;
6733                break;
6734             default :
6735                value = 0;
6736                (*timerpp)[cindx].error_stop = 0;
6737                break;
6738          }
6739 
6740          if ( (*timerpp)[cindx].flag_stop != NO_EVENT &&
6741                  ( (*timerpp)[cindx].offset_stop < 0  ||
6742                    (*timerpp)[cindx].offset_stop > 1439 ) ) {
6743               (void)fprintf(stderr,
6744                   "Line %02d: Expanded timer[%d] stop time %d falls outside range 0-1439\n",
6745                       (*timerpp)[cindx].line2, cindx, (*timerpp)[cindx].offset_stop );
6746               status = 1;
6747          }
6748       }             /* end j loop */
6749       i++ ;
6750    }             /* end i loop */
6751 
6752    /* We're finished with the array of intervals */
6753    free( intvp );
6754    intvp = NULL;
6755 
6756    (void) update_current_timer_generation();
6757 
6758    return status;
6759 }
6760 
6761 
6762 /*---------------------------------------------------------------------+
6763  | Return the amount of CM11a memory available (bytes) when that used  |
6764  | by triggers, macros, timers, internal pointers and list terminators |
6765  | is subtracted from the total size.  Entering with one of the        |
6766  | structure pointers set to NULL will exclude it from the count.      |
6767  +---------------------------------------------------------------------*/
get_freespace(int generation,TIMER * timerp,TRIGGER * triggerp,MACRO * macrop)6768 int get_freespace( int generation, TIMER *timerp, TRIGGER *triggerp,
6769                                                          MACRO *macrop )
6770 {
6771    int   j;
6772    int   timclk, timsun, timcount, trig, mac, macsize, used;
6773 
6774    j = 0; timclk = 0; timsun = 0;
6775    while ( timerp && timerp[j].line_no >= 0 ) {
6776       if ( timerp[j].generation != generation  ||
6777            timerp[j].flag_combined == NO_EVENT   ) {
6778          j++;
6779          continue;
6780       }
6781       if ( (timerp[j].flag_combined & TIME_EVENTS) == CLOCK_EVENT )
6782          timclk++ ;
6783       else if ( timerp[j].flag_combined & (DAWN_EVENT | DUSK_EVENT) )
6784          timsun++ ;
6785       else
6786          (void) fprintf(stderr, "Timer error: Index = %d  Flag = %02xh\n",
6787               j, (timerp[j]).flag_combined );
6788       j++;
6789    }
6790    timcount = timclk + timsun ;
6791 
6792    j = 0; trig = 0;
6793    while ( triggerp && triggerp[j++].line_no >= 0 )
6794       trig++;
6795 
6796    j = 0; mac = 0; macsize = 0;
6797    while ( macrop && macrop[j].line_no >= 0 ) {
6798       if ( !macrop[j].isnull && macrop[j].use == USED ) {
6799          mac++;
6800          macsize += macrop[j].total;
6801       }
6802       j++;
6803    }
6804 
6805    /* Note: This formula MUST be the same as that used */
6806    /* in function create_memory_image_[high|low]().               */
6807    used = 2                           /* Offset of Trigger table */
6808       + 9 * timcount + 1              /* Timers + terminator */
6809       + 3 * trig + 2                  /* Triggers + terminator */
6810       + 2 * mac + macsize             /* User defined macros */
6811       + 2;                            /* Terminator */
6812    if ( configp->macterm == YES )
6813       used += mac;
6814 
6815    return (PROMSIZE - used) ;
6816 }
6817 
6818 /*---------------------------------------------------------------------+
6819  | Create the memory image to be downloaded to the CM11a               |
6820  | This function loads the macros at the top of memory.                |
6821  | Note: The formula used to compute free space at the end of function |
6822  | get_freespace() must agree with what is done in this function.      |
6823  +---------------------------------------------------------------------*/
create_memory_image_high(unsigned char * prommap,TIMER * timerp,TRIGGER * triggerp,MACRO * macrop,unsigned char * elementp)6824 void create_memory_image_high ( unsigned char *prommap, TIMER *timerp,
6825              TRIGGER *triggerp, MACRO *macrop, unsigned char *elementp )
6826 {
6827    extern   int  current_timer_generation;
6828 
6829    int           j, k, size, offset;
6830    int           begin, end, start, stop;
6831    int           macro1, macro2;
6832    unsigned int  security1, security2;
6833 
6834    /* Initialize memory image */
6835    for ( j = 0; j < PROMSIZE; j++ )
6836       prommap[j] = 0xff;
6837 
6838    /* Store zeros in the last two bytes for the terminator */
6839    prommap[PROMSIZE - 1] = 0;
6840    prommap[PROMSIZE - 2] = 0;
6841 
6842    offset = PROMSIZE - 2;
6843 
6844    /* Establish the offset of each macro in the eeprom and store  */
6845    /* in the memory image.      Macros are loaded at the top of   */
6846    /* memory but in the order they were defined, i.e., the last   */
6847    /* macro defined by a macro command in the user's schedule     */
6848    /* will be highest in memory, just below the terminator.       */
6849 
6850    /* Get the total space to be occupied by the macros */
6851    size = 0;
6852    j = 0;
6853    while ( macrop[j].line_no > 0 ) {
6854       if ( !macrop[j].isnull && macrop[j].use == USED ) {
6855          if ( configp->macterm == YES )
6856             size += macrop[j].total + 3;
6857          else
6858             size += macrop[j].total + 2;
6859       }
6860       j++;
6861    }
6862 
6863    offset -= size;
6864 
6865    macrop[NULL_MACRO_INDEX].offset = 0;
6866 
6867    j = 0;
6868    while ( macrop[j].line_no > 0 ) {
6869       if ( !macrop[j].isnull && macrop[j].use == USED ) {
6870          /* Save the offset */
6871          macrop[j].offset = offset;
6872 
6873          prommap[offset++] = macrop[j].delay;
6874 
6875          /* Make sure the macro doesn't have too many elements */
6876          if ( macrop[j].nelem > 255 ) {
6877             fprintf(stderr, "Combined macro %s has too many (%d) elements.\n",
6878                macrop[j].label, macrop[j].nelem);
6879             exit(1);
6880          }
6881 
6882          prommap[offset++] = (unsigned char)macrop[j].nelem;
6883 
6884          for ( k = 0; k < macrop[j].total; k++ )  {
6885             prommap[offset++] = elementp[macrop[j].element + k];
6886          }
6887          if ( configp->macterm == YES )
6888             prommap[offset++] = 0;
6889       }
6890       j++;
6891    }
6892 
6893    /* Store current timers */
6894    j = 0; offset = 2;
6895    while ( timerp && timerp[j].line_no > 0 ) {
6896       if ( timerp[j].generation != current_timer_generation  ||
6897            timerp[j].flag_combined == NO_EVENT  ) {
6898          j++;
6899          continue;
6900       }
6901 
6902       prommap[offset]   = timerp[j].dow_bmap ;
6903       timerp[j].memloc = offset;
6904       begin = timerp[j].resolv_beg;
6905       end   = timerp[j].resolv_end;
6906       prommap[offset+1] = (unsigned char)(begin & 0xff) ;
6907       prommap[offset+2] = (unsigned char)(end   & 0xff) ;
6908 
6909       /* (The extra steps here handle the case of null macros) */
6910       start = min(0x0f, timerp[j].offset_start / 120);
6911       stop  = min(0x0f, timerp[j].offset_stop / 120);
6912       prommap[offset+3] = (unsigned char)((start << 4) | stop);
6913 
6914       start = 0x7f & (timerp[j].offset_start - 120 * start);
6915       stop  = 0x7f & (timerp[j].offset_stop  - 120 * stop);
6916 
6917       prommap[offset+4] = (unsigned char)(((begin & 0x100) >> 1) | start);
6918       prommap[offset+5] = (unsigned char)(((end & 0x100) >> 1) | stop);
6919 
6920       security1 = timerp[j].flag_start & SEC_EVENT ? SECURITY_ON : 0;
6921       security2 = timerp[j].flag_stop  & SEC_EVENT ? SECURITY_ON : 0;
6922       macro1 = macrop[timerp[j].macro_start].offset;
6923       macro2 = macrop[timerp[j].macro_stop ].offset;
6924       prommap[offset+6] = (unsigned char)((macro1 & 0x300) >> 4)   |
6925                           (unsigned char)((security1 & 0x03u) << 6)  |
6926                           (unsigned char)((macro2 & 0x300) >> 8)   |
6927                           (unsigned char)((security2 & 0x03u) << 2);
6928 
6929       prommap[offset+7] = (unsigned char)(macro1 & 0xff);
6930       prommap[offset+8] = (unsigned char)(macro2 & 0xff);
6931 
6932       offset += 9;
6933 
6934       j++;
6935    }
6936    /* Terminate the timers */
6937    prommap[offset++] = 0xff ;
6938 
6939    /* Load offset of trigger table into location 0 */
6940    prommap[0] = (unsigned char)((offset & 0xff00) >> 8);
6941    prommap[1] = (unsigned char)(offset & 0xff);
6942 
6943    /* Now store the triggers @ 3 bytes each + 2 terminators */
6944    j = 0;
6945    while ( triggerp && triggerp[j].line_no > 0 ) {
6946       triggerp[j].memloc = offset;
6947       prommap[offset++]   = triggerp[j].housecode << 4 |
6948                           triggerp[j].unitcode;
6949       macro1 = macrop[triggerp[j].macro].offset;
6950       prommap[offset++] = (unsigned char)((triggerp[j].command) << 7) |
6951                           (triggerp[j].tag << 4) |
6952                           (unsigned char)((macro1 & 0xf00) >> 8);
6953       prommap[offset++] = (unsigned char)(macro1 & 0xff);
6954       j++ ;
6955    }
6956    /* Terminate with 2 bytes of 0xff */
6957    prommap[offset++] = 0xff;
6958    prommap[offset++] = 0xff;
6959 
6960    return ;
6961 }
6962 
6963 /*---------------------------------------------------------------------+
6964  | Create the memory image to be downloaded to the CM11a               |
6965  | This one loads the macros immediately folowing the triggers.        |
6966  | Note: The formula used to compute free space at the end of function |
6967  | get_freespace() must agree with what is done in this function.      |
6968  +---------------------------------------------------------------------*/
create_memory_image_low(unsigned char * prommap,TIMER * timerp,TRIGGER * triggerp,MACRO * macrop,unsigned char * elementp)6969 void create_memory_image_low ( unsigned char *prommap, TIMER *timerp,
6970              TRIGGER *triggerp, MACRO *macrop, unsigned char *elementp )
6971 {
6972    extern   int  current_timer_generation;
6973 
6974    int           j, k, offset;
6975    int           begin, end, start, stop;
6976    int           macro1, macro2;
6977    unsigned int  security1, security2;
6978 
6979    /* Initialize memory image */
6980    for ( j = 0; j < PROMSIZE; j++ )
6981       prommap[j] = 0x00;
6982 
6983 
6984    /* Add up the space required for timers and triggers */
6985    offset = 2;  /* Initial jump instruction */
6986    j = 0;
6987    while ( timerp && timerp[j].line_no > 0 ) {
6988       if ( timerp[j].generation != current_timer_generation  ||
6989            timerp[j].flag_combined == NO_EVENT  ) {
6990          j++;
6991          continue;
6992       }
6993       offset += 9;
6994       j++;
6995    }
6996 
6997    offset += 1;  /* Timer terminator */
6998 
6999    /* Now count the triggers @ 3 bytes each + 2 terminators */
7000    j = 0;
7001    while ( triggerp && triggerp[j].line_no > 0 ) {
7002       offset += 3;
7003       j++;
7004    }
7005 
7006    offset += 2;  /* Trigger terminator */
7007 
7008    /* Load the macros, storing their offsets */
7009 
7010    macrop[NULL_MACRO_INDEX].offset = 0;
7011 
7012    j = 0;
7013    while ( macrop[j].line_no > 0 ) {
7014       if ( !macrop[j].isnull && macrop[j].use == USED ) {
7015          /* Save the offset */
7016          macrop[j].offset = offset;
7017 
7018          prommap[offset++] = macrop[j].delay;
7019 
7020          /* Make sure the macro doesn't have too many elements */
7021          if ( macrop[j].nelem > 255 ) {
7022             fprintf(stderr, "Combined macro %s has too many (%d) elements.\n",
7023                macrop[j].label, macrop[j].nelem);
7024             exit(1);
7025          }
7026 
7027          prommap[offset++] = (unsigned char)macrop[j].nelem;
7028 
7029          for ( k = 0; k < macrop[j].total; k++ )  {
7030             prommap[offset++] = elementp[macrop[j].element + k];
7031          }
7032          if ( configp->macterm == YES )
7033             prommap[offset++] = 0;
7034       }
7035       j++;
7036    }
7037 
7038    /* Store two zeros for the terminator */
7039    prommap[offset++] = 0;
7040    prommap[offset++] = 0;
7041 
7042    /* Go back and load the timers and triggers. */
7043 
7044    j = 0; offset = 2;
7045    while ( timerp && timerp[j].line_no > 0 ) {
7046       if ( timerp[j].generation != current_timer_generation  ||
7047            timerp[j].flag_combined == NO_EVENT  ) {
7048          j++;
7049          continue;
7050       }
7051 
7052       prommap[offset]   = timerp[j].dow_bmap ;
7053       timerp[j].memloc = offset;
7054       begin = timerp[j].resolv_beg;
7055       end   = timerp[j].resolv_end;
7056       prommap[offset+1] = (unsigned char)(begin & 0xff) ;
7057       prommap[offset+2] = (unsigned char)(end   & 0xff) ;
7058 
7059       /* (The extra steps here handle the case of null macros) */
7060       start = min(0x0f, timerp[j].offset_start / 120);
7061       stop  = min(0x0f, timerp[j].offset_stop / 120);
7062       prommap[offset+3] = (unsigned char)((start << 4) | stop);
7063 
7064       start = 0x7f & (timerp[j].offset_start - 120 * start);
7065       stop  = 0x7f & (timerp[j].offset_stop  - 120 * stop);
7066 
7067       prommap[offset+4] = (unsigned char)(((begin & 0x100) >> 1) | start);
7068       prommap[offset+5] = (unsigned char)(((end & 0x100) >> 1) | stop);
7069 
7070       security1 = timerp[j].flag_start & SEC_EVENT ? SECURITY_ON : 0;
7071       security2 = timerp[j].flag_stop  & SEC_EVENT ? SECURITY_ON : 0;
7072       macro1 = macrop[timerp[j].macro_start].offset;
7073       macro2 = macrop[timerp[j].macro_stop ].offset;
7074       prommap[offset+6] = (unsigned char)((macro1 & 0x300) >> 4)   |
7075                           (unsigned char)((security1 & 0x03u) << 6)  |
7076                           (unsigned char)((macro2 & 0x300) >> 8)   |
7077                           (unsigned char)((security2 & 0x03u) << 2);
7078 
7079       prommap[offset+7] = (unsigned char)(macro1 & 0xff);
7080       prommap[offset+8] = (unsigned char)(macro2 & 0xff);
7081 
7082       offset += 9;
7083 
7084       j++;
7085    }
7086    /* Terminate the timers */
7087    prommap[offset++] = 0xff ;
7088 
7089    /* Load offset of trigger table into location 0 */
7090    prommap[0] = (unsigned char)((offset & 0xff00) >> 8);
7091    prommap[1] = (unsigned char)(offset & 0xff);
7092 
7093    /* Now store the triggers @ 3 bytes each + 2 terminators */
7094    j = 0;
7095    while ( triggerp && triggerp[j].line_no > 0 ) {
7096       triggerp[j].memloc = offset;
7097       prommap[offset++]   = triggerp[j].housecode << 4 |
7098                           triggerp[j].unitcode;
7099       macro1 = macrop[triggerp[j].macro].offset;
7100       prommap[offset++] = (unsigned char)((triggerp[j].command) << 7) |
7101                           (triggerp[j].tag << 4) |
7102                           (unsigned char)((macro1 & 0xf00) >> 8);
7103       prommap[offset++] = (unsigned char)(macro1 & 0xff);
7104       j++ ;
7105    }
7106    /* Terminate with 2 bytes of 0xff */
7107    prommap[offset++] = 0xff;
7108    prommap[offset++] = 0xff;
7109 
7110    return ;
7111 }
7112 
7113 
7114 /*---------------------------------------------------------------------+
7115  | Write the EEPROM memory image to disk as a pure binary file.        |
7116  +---------------------------------------------------------------------*/
write_image_bin(char * pathname,unsigned char * prommap)7117 int write_image_bin ( char *pathname, unsigned char *prommap )
7118 {
7119    FILE   *fd_bin;
7120 
7121    if ( !(fd_bin = fopen(pathname, "wb")) ) {
7122       (void) fprintf(stderr,
7123          "Unable to open CM11a memory image binary file '%s' for writing.\n",
7124             pathname);
7125       return 1;
7126    }
7127 
7128    if ( fwrite( prommap, 1, PROMSIZE, fd_bin) != PROMSIZE ) {
7129       (void) fprintf(stderr, "Unable to write memory image file '%s'\n",
7130                        pathname );
7131       (void) fclose( fd_bin );
7132       return 1;
7133    }
7134    (void) fclose( fd_bin );
7135 
7136    return 0;
7137 }
7138 
7139 /*---------------------------------------------------------------------+
7140  | Write the EEPROM memory image to disk as a hexadecimal dump.        |
7141  +---------------------------------------------------------------------*/
write_image_hex(char * pathname,unsigned char * prommap)7142 int write_image_hex ( char *pathname, unsigned char *prommap )
7143 {
7144    FILE    *fd_hex;
7145    char    outbuf[80];
7146    char    buf[16];
7147 
7148    int     j, k, loc;
7149 
7150    if ( !(fd_hex = fopen(pathname, "w")) ) {
7151       (void) fprintf(stderr,
7152          "Unable to open CM11a memory image hex dump file '%s' for writing.\n",
7153             pathname);
7154       return 1;
7155    }
7156 
7157    loc = 0;
7158    for ( j = 0; j < PROMSIZE; j += 16 ) {
7159       (void) sprintf(outbuf, "%03X   ", loc);
7160       for ( k = 0; k < 8; k++ ) {
7161          (void) sprintf(buf, "%02X ", prommap[j+k]);
7162          (void) strncat(outbuf, buf, sizeof(outbuf) - 1 - strlen(outbuf));
7163       }
7164       (void) strncat(outbuf, " ", sizeof(outbuf) - 1 - strlen(outbuf));
7165       for ( k = 8; k < 16; k++ ) {
7166          (void) sprintf(buf, "%02X ", prommap[j+k]);
7167          (void) strncat(outbuf, buf, sizeof(outbuf) - 1 - strlen(outbuf));
7168       }
7169       (void) fprintf(fd_hex, "%s\n", outbuf);
7170       loc += 16;
7171    }
7172    (void) fclose(fd_hex);
7173    return 0;
7174 }
7175 
7176 /*---------------------------------------------------------------------*
7177  | Store information to be written to or read from the X10record file  |
7178  | in one place.                                                       |
7179  +---------------------------------------------------------------------*/
store_record_info(CALEND * calendp)7180 void store_record_info ( CALEND *calendp )
7181 {
7182 
7183    x10record.isready = 1;
7184    x10record.dayset = calendp->today;
7185    x10record.yday  = calendp->yday;
7186    x10record.day_zero = calendp->day_zero;
7187    x10record.tzone    = configp->tzone;
7188    x10record.flags = ( timer_size > 0 ) ? HAS_TIMERS : 0;
7189    x10record.flags |= configp->mode;
7190    x10record.dstminutes = configp->dstminutes;
7191    x10record.program_days = configp->program_days;
7192 
7193    return;
7194 }
7195 
7196 /*---------------------------------------------------------------------*
7197  | Function to delete the X10record file; for use when the CM11a       |
7198  | EEPROM is erased.                                                   |
7199  +---------------------------------------------------------------------*/
remove_record_file(void)7200 void remove_record_file ( void )
7201 {
7202    int   code;
7203 
7204    if ( verbose )
7205       printf("Deleting X10 Record File %s\n", pathspec(RECORD_FILE));
7206 
7207    code = remove( pathspec(RECORD_FILE) ) ;
7208    if ( code != 0 && errno != 2 ) {
7209       (void)fprintf(stderr,
7210          "WARNING: Unable to delete X10 Record File %s - errno = %d\n",
7211           pathspec(RECORD_FILE), errno);
7212    }
7213    return;
7214 }
7215 
7216 /*---------------------------------------------------------------------+
7217  | Write a file with configuration and calendar information which      |
7218  | heyu will need to save to properly set the CM11a clock-calendar at  |
7219  | any later time.                                                     |
7220  +---------------------------------------------------------------------*/
write_record_file(char * pathname,CALEND * calendp)7221 int write_record_file ( char *pathname, CALEND *calendp )
7222 {
7223    FILE *fd;
7224 
7225    store_record_info( calendp );
7226 
7227    if ( !(fd = fopen( pathname, "w" )) ) {
7228       (void) fprintf(stderr, "Unable to open record file '%s' for writing.\n",
7229          pathname);
7230       exit(1);
7231    }
7232 
7233    (void)fprintf(fd, "# Generated by heyu - do not delete or modify.\n");
7234 
7235    (void)fprintf(fd, "# Config:   %s\n", pathspec(heyu_config));
7236    (void)fprintf(fd, "# Schedule: %s\n", schedfile);
7237    (void)fprintf(fd, "# Uploaded: %s\n", legal_time_string());
7238 
7239    (void)fprintf(fd, "%ld %d %d %ld %d %d %d\n",
7240        x10record.dayset,
7241        x10record.yday,
7242        x10record.day_zero,
7243        x10record.tzone,
7244        x10record.flags,
7245        x10record.dstminutes,
7246        x10record.program_days);
7247 
7248    (void) fclose(fd);
7249 
7250    return 0;
7251 }
7252 
7253 
7254 /*---------------------------------------------------------------------+
7255  | Read info from x10record file and store in record_info structure.   |
7256  | Return one of the following values:                                 |
7257  |  VALID_RECORD_FILE                                                  |
7258  |  NO_RECORD_FILE                                                     |
7259  |  BAD_RECORD_FILE  (i.e., corrupted)                                 |
7260  +---------------------------------------------------------------------*/
read_record_file(void)7261 int read_record_file ( void )
7262 {
7263    extern int verbose, i_am_relay;
7264 
7265    FILE *fd;
7266    char buffer[256];
7267    int  n, lines;
7268 
7269    x10record.isready = 0;
7270 
7271    if ( verbose && i_am_relay != 1 ) {
7272       (void)printf("Searching for %s\n", pathspec(RECORD_FILE));
7273    }
7274 
7275    if ( !(fd = fopen( pathspec(RECORD_FILE), "r" )) ) {
7276       if ( verbose && i_am_relay != 1 ) {
7277          (void)printf("File %s is absent\n", pathspec(RECORD_FILE));
7278       }
7279       return NO_RECORD_FILE;
7280    }
7281 
7282    if ( verbose && i_am_relay != 1 ) {
7283       (void)printf("%s is present\n", pathspec(RECORD_FILE));
7284    }
7285 
7286    n = 0; lines = 0;
7287    while ( fgets(buffer, 256, fd) != NULL ) {
7288      lines++;
7289      (void)strtrim(buffer);
7290      if ( *buffer == '#' || *buffer == '\0' )
7291         continue;
7292      n = sscanf(buffer, "%ld %d %d %ld %u %d %d",
7293        &x10record.dayset,
7294        &x10record.yday,
7295        &x10record.day_zero,
7296        &x10record.tzone,
7297        &x10record.flags,
7298        &x10record.dstminutes,
7299        &x10record.program_days);
7300 
7301      break;
7302    }
7303    (void) fclose( fd );
7304 
7305    /* A zero-length record file is equivalent to no record file. */
7306    if ( lines == 0 ) {
7307       if ( verbose && i_am_relay != 1 ) {
7308          (void)printf("File %s is empty\n", pathspec(RECORD_FILE));
7309       }
7310       return NO_RECORD_FILE;
7311    }
7312 
7313    if ( n != 7 ) {
7314       if ( verbose && i_am_relay != 1 ) {
7315          (void)printf("File %s is corrupted\n", pathspec(RECORD_FILE));
7316       }
7317       return BAD_RECORD_FILE;
7318    }
7319 
7320    x10record.isready = 1;
7321 
7322    return VALID_RECORD_FILE;
7323 }
7324 
7325 
7326 /*---------------------------------------------------------------------+
7327  | Return the number of days >= 0 until expiration of an uploaded      |
7328  | schedule from the x10 Record File and the system date.              |
7329  |   Return SCHEDULE_EXPIRED if the schedule has expired.              |
7330  |   Return NO_EXPIRATION if there are no timers defined.              |
7331  |   Return NO_RECORD_FILE if no (or empty) record file.               |
7332  |   Return BAD_RECORD_FILE if the record file is corrupted.           |
7333  +---------------------------------------------------------------------*/
get_upload_expire(void)7334 int get_upload_expire ( void )
7335 {
7336    int     retcode, elapsed, expire;
7337    long    daynow;
7338    time_t  now;
7339 
7340    retcode = read_record_file();
7341 
7342    if ( retcode != VALID_RECORD_FILE )
7343       return retcode;
7344 
7345    if ( !(x10record.flags & HAS_TIMERS) )
7346       return NO_EXPIRATION;
7347 
7348    time(&now);
7349    daynow = ((long)now - x10record.tzone)/86400L;
7350 
7351    elapsed = (int)(daynow - x10record.dayset) + x10record.yday - x10record.day_zero;
7352    expire = x10record.program_days - elapsed;
7353 
7354    if ( expire < 0 )
7355       return SCHEDULE_EXPIRED;
7356 
7357    return expire;
7358 }
7359 
7360 /*---------------------------------------------------------------------+
7361  | Translate the readings from the CM11a clock to Legal Time, using    |
7362  | what is recorded in the X10 Record File (if one exists), otherwise  |
7363  | assume the CM11a clock is set to Standard Time.  The arguments are: |
7364  |    *Idaysp   Day of Week bitmap (Sun = 1, Sat = 64)                 |
7365  |    *Ijdayp   Day of Year counter (0-365)                            |
7366  |    *Ihrp     Hours (0-23)                                           |
7367  |    *Iminp    Minutes (0-59)                                         |
7368  |    *Isecp    Seconds (0-59)                                         |
7369  |    *expire   Days until expiration of uploaded schedule (0-365) or  |
7370  |              invalidation code (-1 to -4)                           |
7371  +---------------------------------------------------------------------*/
cm11a_to_legal(int * Idaysp,int * Ijdayp,int * Ihrp,int * Iminp,int * Isecp,int * expire)7372 struct tm *cm11a_to_legal ( int *Idaysp, int *Ijdayp, int *Ihrp,
7373 	           int *Iminp, int *Isecp, int *expire )
7374 {
7375 
7376    struct tm     *tms, *tmp;
7377    static struct tm tmstat;
7378    time_t        dtimep;
7379    long          daynow;
7380    int           delta;
7381    unsigned char bmap;
7382 
7383    /* Get the user's timezone */
7384    get_std_timezone();
7385 
7386    /* Fix TZ names if necessary */
7387    fix_tznames();
7388 
7389    tms = &tmstat;
7390 
7391    time(&dtimep);
7392    tmp = stdtime(&dtimep);
7393    memcpy((void *)tms, (void *)tmp, sizeof(struct tm));
7394 
7395    /* Load the required data into the x10record structure if the program */
7396    /* has not already done so.                                           */
7397    *expire = get_upload_expire();
7398 
7399    if ( *expire != NO_RECORD_FILE && *expire != BAD_RECORD_FILE ) {
7400       delta = *Ijdayp - x10record.yday + x10record.day_zero;
7401       daynow = x10record.dayset + (long)delta;
7402    }
7403    else {
7404       daynow = *Ijdayp - tms->tm_yday + ((long)dtimep - std_tzone)/86400L ;
7405    }
7406 
7407    dtimep = (time_t)3600 * (time_t)(*Ihrp) + (time_t)60 * (time_t)(*Iminp) +
7408      (time_t)(*Isecp) + (time_t)86400 * (time_t)daynow + (time_t)std_tzone;
7409    tmp = localtime(&dtimep);
7410    memcpy((void *)tms, (void *)tmp, sizeof(struct tm));
7411 
7412    /* Adjust the displayed day of the week if the day has changed */
7413    /* into the previous or following day                          */
7414    if ( tms->tm_hour < *Ihrp ) {
7415       bmap = (unsigned char)(*Idaysp);
7416       *Idaysp = (int)lrotbmap(bmap);
7417    }
7418 
7419    tms->tm_wday = bmap2wday((unsigned char)(*Idaysp));
7420    *Ihrp = tms->tm_hour;
7421    *Iminp = tms->tm_min;
7422    *Isecp = tms->tm_sec;
7423    *Ijdayp = tms->tm_yday;
7424 
7425    return tms;
7426 }
7427 
7428 /*---------------------------------------------------------------------+
7429  | Return a pointer to a tm structure containing the appropriate       |
7430  | settings for the CM11a clock based on the argument time_t timep and |
7431  | what is recorded in the x10 Record File (if one exists).            |
7432  +---------------------------------------------------------------------*/
legal_to_cm11a(time_t * dtimep)7433 struct tm *legal_to_cm11a ( time_t *dtimep )
7434 {
7435    extern int      i_am_relay;
7436 
7437    static struct tm tmstat;
7438 
7439    struct tm     *tms, *tmp;
7440    time_t        seconds;
7441    long          daynow;
7442    int           note;
7443 
7444    /* Maintain our own static structure because the structure built in */
7445    /* to the standard time functions is overwritten each call.         */
7446    tms = &tmstat;
7447 
7448    /* Load the record file data into the x10record structure */
7449    (void) read_record_file();
7450 
7451    tmp = stdtime(dtimep);
7452    memcpy( (void *)tms, (void *)tmp, sizeof(struct tm) );
7453 
7454    /* Just return the pointer if no (or corrupt) x10record file */
7455    /* or if the schedule does not expire because there are no   */
7456    /* timers defined.                                           */
7457    if ( x10record.isready == 0 || !(x10record.flags | HAS_TIMERS) ) {
7458       return tms;
7459    }
7460 
7461    /* Force the date to be greater than or the same as the date the CM11a */
7462    /* was programmed by incrementing the year if necessary. (We can't set */
7463    /* the CM11a clock to a negative day count.)                           */
7464 
7465    note = 0;
7466    while ( *dtimep - (time_t)std_tzone < (time_t)(x10record.dayset * 86400L) ) {
7467       tms->tm_year += 1;
7468       *dtimep = mktime(tms);
7469       note = 1;
7470    }
7471    if ( note && i_am_relay != 1 ) {
7472       (void)fprintf(stderr,
7473          "Date adjusted forward to year %d\n", tms->tm_year + 1900);
7474    }
7475 
7476    seconds = *dtimep - (time_t)std_tzone;
7477 
7478    /* Convert to Standard Time */
7479    if ( tms->tm_isdst > 0 ) {
7480       tmp = gmtime(&seconds);
7481       memcpy( (void *)tms, (void *)tmp, sizeof(struct tm) );
7482    }
7483 
7484    daynow = (long)seconds/86400L;
7485 
7486    /* Set the value of the yday according to the Heyu programming mode */
7487    tms->tm_yday = (int)(daynow - x10record.dayset);
7488    return tms;
7489 }
7490 
7491 /*---------------------------------------------------------------------+
7492  | Display status message                                              |
7493  +---------------------------------------------------------------------*/
display_status_message(int expire)7494 void display_status_message ( int expire )
7495 {
7496    switch ( expire ) {
7497      case NO_RECORD_FILE :
7498       (void) fprintf(stdout, "No schedule has been uploaded by Heyu.\n");
7499       break;
7500      case NO_EXPIRATION :
7501       (void) fprintf(stdout, "Uploaded schedule does not expire.\n");
7502       break;
7503      case BAD_RECORD_FILE :
7504       (void) fprintf(stdout, "X10 Record File '%s' has been corrupted.\n",
7505                               pathspec(RECORD_FILE));
7506       break;
7507      case SCHEDULE_EXPIRED :
7508       (void) fprintf(stdout, "Uploaded schedule has expired.\n");
7509       break;
7510      default :
7511       (void) fprintf(stdout, "Uploaded schedule will expire in %d days.\n",
7512                expire);
7513       break;
7514    }
7515    return;
7516 }
7517 
7518 /*---------------------------------------------------------------------+
7519  | Display CM11a status and required CM11a clock settings.             |
7520  | display_mode = 1 displays human-readable message.                   |
7521  | display_mode = 0 displays only the code, for use with scripts.      |
7522  +---------------------------------------------------------------------*/
display_cm11a_status(int display_mode)7523 void display_cm11a_status ( int display_mode )
7524 {
7525    int expire;
7526 
7527    expire = get_upload_expire();
7528 
7529    if ( display_mode == 1 )
7530       display_status_message( expire );
7531    else
7532       (void) fprintf(stdout, "%d\n", expire);
7533 
7534    return;
7535 }
7536 
7537 /*---------------------------------------------------------------------+
7538  | qsort() compare function for write_macroxref()                      |
7539  +---------------------------------------------------------------------*/
compmac(struct macindx * one,struct macindx * two)7540 int compmac( struct macindx *one, struct macindx *two )
7541 {
7542    return (one->offset - two->offset) ;
7543 }
7544 
7545 /*---------------------------------------------------------------------+
7546  | Write a macro xref table, i.e., label vs offset in EEPROM, to disk. |
7547  | Optionally append macro helper tokens.                              |
7548  +---------------------------------------------------------------------*/
write_macroxref(char * pathname,MACRO * macrop,unsigned char * elementp,int ichksum)7549 int write_macroxref ( char *pathname, MACRO *macrop, unsigned char *elementp, int ichksum )
7550 {
7551    FILE          *fd ;
7552    int           i, j, k, m, index, mask, count;
7553    unsigned char cmdcode, hcode;
7554    unsigned int  bmap;
7555    char          hc;
7556    int           (*fptr)() = &compmac;
7557 
7558    static int strucsize = sizeof(struct macindx);
7559    static char *label[] = {"", "", "On","Off", "Dim", "Bright"};
7560 
7561    struct macindx *macp;
7562 
7563    if ( !(fd = fopen(pathname, "w")) ) {
7564       (void) fprintf(stderr,
7565          "Unable to open macro xref file '%s' for write.\n", pathname);
7566       return 1;
7567    }
7568 
7569    /* Write the image checksum as a label at address 0 */
7570    fprintf(fd, "   0  %d\n", ichksum);
7571 
7572 
7573    /* Count macros in use */
7574 
7575    count = 0;
7576    j = 0;
7577    while ( macrop[j].line_no > 0 ) {
7578       if ( !macrop[j].isnull && macrop[j].use == USED )
7579          count++;
7580       j++;
7581    }
7582 
7583    if ( (macp = calloc( count, strucsize  )) == NULL ) {
7584       fprintf(stderr, "write_macroxref() - Unable to allocate memory.\n");
7585       exit(1);
7586    }
7587 
7588    count = 0;
7589    j = 0;
7590    while ( macrop[j].line_no > 0 ) {
7591       if ( !macrop[j].isnull && macrop[j].use == USED ) {
7592          macp[count].index = j;
7593          macp[count].offset = macrop[j].offset ;
7594          count++;
7595       }
7596       j++ ;
7597    }
7598 
7599    qsort ( macp, count, sizeof(struct macindx), fptr );
7600 
7601    for ( j = 0; j < count; j++ ) {
7602       index = macp[j].index;
7603       (void) fprintf(fd, "%4d  %s",
7604          macp[j].offset, macrop[index].label);
7605 
7606       if ( configp->xref_append == YES ) {
7607          i = macrop[index].element ;
7608          for ( k = 0; k < macrop[index].nelem; k++ ) {
7609             cmdcode = elementp[i] & 0x0fu;
7610             hcode = (elementp[i] & 0xf0u) >> 4;
7611             hc = tolower((int)code2hc(hcode));
7612             bmap = elementp[i+1] << 8 | elementp[i+2];
7613 
7614             if ( cmdcode > 1 && cmdcode < 6 ) {
7615                mask = 1;
7616                for ( m = 0; m < 16; m++ ) {
7617                   if ( bmap & mask )
7618                      fprintf(fd, " %c%d%s", hc, bitpos2unit(m), label[cmdcode]);
7619                   mask = mask << 1;
7620                }
7621                i += (cmdcode < 4) ? 3 : 4;
7622             }
7623             else {
7624                i += (cmdcode == 7) ? 6 : 3;
7625             }
7626          }
7627       }
7628       fprintf(fd, "\n");
7629    }
7630 
7631    (void) fclose(fd);
7632 
7633    free( macp );
7634 
7635    return 0;
7636 }
7637 
7638 /*---------------------------------------------------------------------+
7639  | Compute a 12 bit checksum of the memory image                       |
7640  +---------------------------------------------------------------------*/
image_chksum(unsigned char * prommap)7641 int image_chksum ( unsigned char *prommap )
7642 {
7643    long int      sum = 0;
7644    unsigned char *sp;
7645 
7646    sp = prommap;
7647    while ( sp < (prommap + PROMSIZE) )
7648       sum += (long)(*sp++);
7649 
7650    return (int)(sum & 0x0fff);
7651 }
7652 
7653 /*---------------------------------------------------------------------+
7654  | Return the macro label corresponding to the argument address in the |
7655  | macro xref file.  Return "Unknown" if no file or no address match.  |
7656  | If the file includes the checksum of the image file (entry at       |
7657  | address zero), pass it back through the argument list.              |
7658  +---------------------------------------------------------------------*/
lookup_macro(int address,char * maclabel,int * ichksum)7659 int lookup_macro ( int address, char *maclabel, int *ichksum )
7660 {
7661    FILE        *fd;
7662    char        *sp;
7663    char        buffer[127];
7664    char        minibuf[MACRO_LEN + 1];
7665 
7666    *ichksum = -1;
7667 
7668    if ( !(fd = fopen(pathspec(MACROXREF_FILE), "r")) ) {
7669       strncpy2(maclabel, "_unknown_", MACRO_LEN);
7670       return 0;
7671    }
7672 
7673    while ( fgets(buffer, sizeof(buffer)/sizeof(char), fd) != NULL ) {
7674       if ( (int)strtol(buffer, &sp, 10) == 0 ) {
7675          get_token(minibuf, &sp, " \t\n", MACRO_LEN);
7676          *ichksum = (int)strtol(minibuf, &sp, 10);
7677       }
7678       else if ( (int)strtol(buffer, &sp, 10) == address ) {
7679          get_token(minibuf, &sp, " \t\n", MACRO_LEN);
7680          strncpy2(maclabel, minibuf, MACRO_LEN);
7681          fclose(fd);
7682          return 1;
7683       }
7684    }
7685    fclose(fd);
7686    strncpy2(maclabel, "_unknown_", MACRO_LEN);
7687    return 0;
7688 }
7689 
7690 /*---------------------------------------------------------------------+
7691  | Return the macro EEPROM address corresponding to the argument macro |
7692  | name argument in the x10macroxref file.  Return -1 if not found.    |
7693  | If the file includes the checksum of the image file (entry at       |
7694  | address zero), pass it back through the argument list.              |
7695  +---------------------------------------------------------------------*/
macro_rev_lookup(char * macname,int * ichksum)7696 int macro_rev_lookup ( char *macname, int *ichksum )
7697 {
7698    FILE        *fd;
7699    char        *sp;
7700    char        buffer[127];
7701    int         val, macaddr;
7702    int         tokc;
7703    char        **tokv;
7704 
7705    if ( !(fd = fopen(pathspec(MACROXREF_FILE), "r")) )
7706       return -1;
7707 
7708    *ichksum = -1;
7709    macaddr = -1;
7710 
7711    while ( fgets(buffer, sizeof(buffer)/sizeof(char), fd) != NULL ) {
7712       tokenize(buffer, " \t\n", &tokc, &tokv);
7713       if ( tokc < 2 ) {
7714          free(tokv);
7715          continue;
7716       }
7717       if ( (val = (int)strtol(tokv[0], &sp, 10)) == 0 && *sp == '\0' )
7718          *ichksum = (int)strtol(tokv[1], &sp, 10);
7719       else if ( strcmp(tokv[1], macname) == 0 ) {
7720          macaddr = val;
7721          free(tokv);
7722          break;
7723       }
7724       free(tokv);
7725    }
7726    fclose(fd);
7727 
7728    return macaddr;
7729 }
7730 
7731 
7732 /*---------------------------------------------------------------------+
7733  | This function supervises the whole job of reading the config and    |
7734  | schedule files and creating the CM11a memory image file.  It also   |
7735  | creates a bunch of other files for reference and/or debugging.      |
7736  |                                                                     |
7737  | If the argument is PROC_UPLOAD, the memory image is uploaded to the |
7738  | CM11a interface, the X10 Record File is written, and the CM11a      |
7739  | clock is set to the appropriate time.                               |
7740  +---------------------------------------------------------------------*/
process_data(int proc_code)7741 int process_data ( int proc_code )
7742 {
7743    FILE          *fd;
7744 
7745    TIMER         *timerp = NULL;
7746    TEVENT        *teventp = NULL;
7747    TRIGGER       *triggerp = NULL;
7748    MACRO         *macrop = NULL;
7749    CALEND        today, *calendp;
7750 
7751    unsigned char prommap[PROMSIZE];
7752 
7753    unsigned char *elementp = NULL;
7754    char          *sp;
7755    int           retcode, freespace, tot_timers, max_error;
7756    int           ss_error;
7757    int           ichksum;
7758    int           now_time = -1;
7759    extern int    c_setclock(int, char **);
7760    extern void   upload_eeprom_image(unsigned char *);
7761 
7762 
7763    /* Open and parse the x10 configuration file and store */
7764    /* the information in the global CONFIG structure.     */
7765 
7766    get_configuration(CONFIG_INIT);
7767    line_no = 0;
7768 
7769    /* See if the user has specified the full path for a schedule   */
7770    /* file by a command line option or in an environment variable, */
7771    /* otherwise use what's been specified in the configuration     */
7772    /* file (or its default).                                       */
7773 
7774    if ( !((sp = optptr->schedp) || (sp = getenv("X10SCHED"))) )
7775       sp = pathspec(configp->schedfile);
7776 
7777    (void)strncpy2(schedfile, sp, sizeof(schedfile) - 1);
7778 
7779    /* Open the x10 schedule file */
7780    if ( !(fd = fopen(schedfile, "r")) ) {
7781       (void)fprintf(stderr, "Unable to open schedule file '%s'\n",
7782                                            schedfile);
7783       exit(1);
7784    }
7785 
7786    (void) printf("Schedule: %s\n", schedfile);
7787 
7788    /* Parse the schedule file and store the data in structure arrays */
7789    retcode = parse_sched ( fd, &timerp, &triggerp, &macrop, &elementp );
7790    (void) fclose( fd );
7791 
7792    if ( retcode ) {
7793       (void) fprintf(stderr,
7794          "Quitting due to errors in schedule file '%s'\n", schedfile);
7795       exit(1);
7796    }
7797 
7798    if ( !timerp && !triggerp ) {
7799       (void) fprintf(stderr,
7800          "No Timers or Triggers defined in schedule file %s\n", schedfile);
7801       exit(1);
7802    }
7803 
7804    /* Get today's date information and store it in a */
7805    /* CALEND structure.                              */
7806 
7807    calendp = &today;
7808    calendar_today( calendp );
7809 
7810    /* Split the timers into individual timed events for start and stop */
7811    split_timers( timerp, &teventp );
7812 
7813    /* Save the earliest macro execution time if a timer in the    */
7814    /* schedule file specified the time as "now[+NN]".             */
7815    now_time = get_first_now_time(teventp);
7816 
7817    /* If a macro in a tevent is also called by a trigger, associate */
7818    /* the tevent with the trigger.                                  */
7819    associate_tevent_triggers( teventp, macrop );
7820 
7821    /* Replace delayed events with undelayed events having increased */
7822    /* offsets (and new macros) to compensate if requested           */
7823    if ( configp->repl_delay == YES )
7824       replace_delayed_events( &teventp, &macrop, &elementp );
7825 
7826    /* Combine similar tevents if requested */
7827    if ( configp->combine_events == YES )
7828       combine_similar_tevents( &teventp, &macrop, &elementp );
7829 
7830    /* Compress macros by merging unit codes for the same */
7831    /* HouseCode and Command.                             */
7832    if ( configp->compress_macros == YES )
7833       compress_macros( macrop, elementp );
7834 
7835    /* Adjust the times for events programmed with Security mode */
7836    security_adjust_legal( &teventp, &macrop, &elementp );
7837 
7838    /* Convert the programmed month/day ranges into day counts   */
7839    /* over the programmed interval and adjust purely Clock      */
7840    /* events for Daylight time if applicable.                   */
7841 
7842    resolve_dates( &teventp, calendp, configp->program_days );
7843 
7844    /* Resolve Dawn/Dusk timer options, if any */
7845    resolve_dawndusk_options( &teventp, calendp );
7846 
7847    clear_disabled_events( &teventp );
7848 
7849    /* Resolve overlapping date ranges for Dawn/Dusk relative    */
7850    /* events so that on any given day all events will have the  */
7851    /* same value for Dawn and/or Dusk.                          */
7852 
7853    if ( configp->res_overlap == RES_OVLAP_COMBINED )
7854       resolve_tevent_overlap_comb( &teventp ); /* Older method */
7855    else
7856       resolve_tevent_overlap_sep( &teventp );
7857 
7858    /* Mark macros actually in use over the programmed interval. */
7859    identify_macros_in_use(macrop, teventp);
7860 
7861    /* Internal check */
7862    verify_tevent_links( teventp );
7863 
7864    /* Recombine the individual tevents into timers */
7865    (void) reconstruct_timers( teventp, &timerp );
7866 
7867    /* Check total freespace before going any further. */
7868    freespace = get_freespace(current_timer_generation, timerp, triggerp, macrop );
7869 
7870    /* Subtract timer space we are holding in reserve */
7871    freespace -= 9 * configp->reserved_timers;
7872 
7873    if ( freespace < 0 ) {
7874       (void) fprintf(stderr,
7875          "Schedule too large: CM11a memory size exceeded by %d bytes ( = %.1f%% )\n",
7876              -freespace, 100. * (double)(-freespace)/(double)PROMSIZE);
7877       exit(1);
7878    }
7879 
7880    /* Get freespace excluding timers  */
7881    freespace = get_freespace(-1, NULL, triggerp, macrop)
7882                                            - 9 * configp->reserved_timers;
7883 
7884    /* Resolve any dawn/dusk based timers into a sequence of clock */
7885    /* based timers, choosing the date intervals so as to minimize */
7886    /* the error in dawn and/or dusk times over each interval      */
7887    /* subject to the constraint of the available freespace.       */
7888 
7889    retcode = resolve_sun_times( &timerp, calendp, freespace,
7890                                        &tot_timers, &max_error);
7891    if ( retcode ) {
7892       (void)fprintf(stderr,
7893                "Quitting due to errors.\n");
7894        return 1;
7895    }
7896 
7897    /* Set a sequence number for each trigger, warning if */
7898    /* the number exceeds 6.                              */
7899    if ( set_trigger_tags( triggerp ) ) {
7900       (void)printf("Warning: More than 6 triggers reference the same macro.\n");
7901    }
7902 
7903    /* Get the remaining freespace. */
7904    freespace = get_freespace( current_timer_generation, timerp, triggerp,
7905                                            macrop );
7906 
7907    if ( calendp->asif_flag & (ASIF_DATE | ASIF_TIME) )
7908       (void)printf("Simulation as if %s\n", asif_time_string());
7909 
7910    (void) printf("Expanded timers = %3d\n", tot_timers);
7911    (void) printf(
7912 	"Max dawn/dusk error over the %d day period = %d minutes.\n",
7913                  configp->program_days, max_error);
7914    (void) printf(
7915        "Interface memory free = %d bytes ( = %.1f%% )\n",
7916                  freespace, 100.*(double)freespace/(double)PROMSIZE);
7917 
7918    /* Check for start time = stop time error */
7919    ss_error = check_timer_start_stop(timerp);
7920 
7921    /* Internal check */
7922     verify_timer_links( timerp );
7923 
7924    /* Create the memory image to be downloaded to the CM11a */
7925    #ifdef LOADLOW
7926    create_memory_image_low ( prommap, timerp, triggerp, macrop,
7927                                                     elementp );
7928    #else
7929    create_memory_image_high ( prommap, timerp, triggerp, macrop,
7930                                                     elementp );
7931    #endif  /* End of #ifdef */
7932 
7933    /* Compute a (16 bit) checksum for the memory image */
7934    ichksum = image_chksum( prommap );
7935 
7936    /* Create a report for the user */
7937    (void) final_report( altpathspec(REPORT_FILE), calendp, timerp,
7938                         teventp, triggerp, macrop, elementp, proc_code );
7939    if ( ss_error ) {
7940       (void) fprintf(stderr, "*** Internal error: stop time = start time ***\n");
7941       (void) fprintf(stderr,
7942          "%d timers - see OUTPUT TIMERS section in file %s for details.\n",
7943                             ss_error, altpathspec(REPORT_FILE));
7944       (void) fprintf(stderr,
7945          "Add line \"config FIX_STOPSTART_ERROR YES\" to schedule file and rerun.\n");
7946       (void) fprintf(stderr, "Quitting due to errors.\n");
7947       return 1;
7948    }
7949    else
7950       (void) fprintf(stdout, "See file %s for details.\n",
7951                                        altpathspec(REPORT_FILE));
7952 
7953    /* Upload the EEPROM image to the CM11a and create a file  */
7954    /* listing the dates and configuration which is necessary  */
7955    /* if we later want to update the CM11a clock or check the */
7956    /* status of the uploaded data.                            */
7957 
7958    if ( proc_code == PROC_UPLOAD ) {
7959       upload_eeprom_image( prommap );
7960 
7961       (void) write_record_file(pathspec(RECORD_FILE), calendp);
7962 
7963       (void) write_macroxref( pathspec(MACROXREF_FILE),
7964                                            macrop, elementp, ichksum);
7965 
7966       (void) write_image_bin( pathspec(IMAGE_FILE), prommap);
7967 
7968       (void) printf("Setting interface clock to current Standard Time.\n");
7969       c_setclock( 1, NULL );
7970 
7971    }
7972    else if ( configp->checkfiles == YES ) {
7973       (void) printf("Writing .check and .hex files.\n");
7974 
7975       /* Write the same record file with a ".check" extension */
7976       (void) write_record_file( pathspec(RECORD_FILE_CHECK), calendp );
7977 
7978       /* Write the same macroxref file with a ".check" extension */
7979       (void) write_macroxref( pathspec(MACROXREF_FILE_CHECK),
7980                                          macrop, elementp, ichksum);
7981 
7982       /* Write the same image file with a ".check" extension */
7983       (void) write_image_bin( pathspec(IMAGE_FILE_CHECK), prommap);
7984 
7985       /* Write the image file in hex format */
7986       (void) write_image_hex( pathspec(IMAGE_HEX_FILE), prommap );
7987    }
7988 
7989    /* Display "now" time, if any */
7990    if ( now_time >= 0 )
7991       (void) printf("The first 'now+NN' event macro will execute at %02d:%02d:00\n",
7992          now_time / 60, now_time % 60 );
7993 
7994    free( timerp );
7995    free( teventp );
7996    free( triggerp );
7997    free( macrop );
7998    free( elementp );
7999 
8000    return 0;
8001 }
8002 
8003 /*---------------------------------------------------------------------+
8004  | This function simulates execution of heyu by cron on a daily basis  |
8005  | for the next 366 days, to insure there are no snags in the schedule |
8006  | file.  No files other than a cronreport file are written.           |
8007  +---------------------------------------------------------------------*/
crontest(void)8008 int crontest ( void )
8009 {
8010    FILE          *fd_sched, *fd_cron;
8011 
8012    TIMER         *timerp = NULL;
8013    TEVENT        *teventp = NULL;
8014    TRIGGER       *triggerp = NULL;
8015    MACRO         *macrop = NULL;
8016    unsigned char *elementp = NULL;
8017 
8018    CALEND        today, *calendp;
8019 
8020    int           retcode, freespace, minfree, maxfree;
8021    int           min_timers, max_timers, tot_timers;
8022    int           max_error, least_error, worst_error;
8023    int           j;
8024    char          *sp;
8025 
8026    /* Open and parse the x10 configuration file and store */
8027    /* the information in the global CONFIG structure.     */
8028 
8029    get_configuration(CONFIG_INIT);
8030 
8031    /* See if the user has specified the full path for a schedule   */
8032    /* file by a command line option or in an environment variable, */
8033    /* otherwise use what's been specified in the configuration     */
8034    /* file (or its default).                                       */
8035 
8036    if ( !((sp = optptr->schedp) || (sp = getenv("X10SCHED"))) )
8037       sp = pathspec(configp->schedfile);
8038 
8039    (void)strncpy2(schedfile, sp, sizeof(schedfile) - 1);
8040 
8041    /* Open the x10 schedule file */
8042    if ( !(fd_sched = fopen(schedfile, "r")) ) {
8043       (void)fprintf(stderr, "Unable to open schedule file '%s'\n",
8044                                            schedfile);
8045       exit(1);
8046    }
8047 
8048    /* Parse the schedule file and store the data in structure arrays */
8049    retcode = parse_sched( fd_sched, &timerp, &triggerp, &macrop, &elementp );
8050    (void) fclose( fd_sched );
8051 
8052    if ( retcode ) {
8053       (void) fprintf(stderr,
8054          "Quitting due to errors in schedule file '%s'\n", schedfile);
8055       exit(1);
8056    }
8057 
8058    if ( configp->mode == COMPATIBLE ) {
8059       (void)fprintf(stderr,
8060           "croncheck is not applicable when configured for COMPATIBLE mode.\n");
8061       exit(1);
8062    }
8063 
8064 
8065 
8066 
8067    /* Nothing to do if no timers */
8068    if ( !timerp ) {
8069       (void) printf("No timers found in schedule file - nothing to do.\n");
8070       exit(0);
8071    }
8072 
8073    /* Get today's date information and store it in a */
8074    /* a CALEND structure.                            */
8075 
8076    calendp = &today;
8077    calendar_today( calendp );
8078 
8079    /* Split the timers into individual timed events for start and stop */
8080    split_timers( timerp, &teventp );
8081 
8082    /* If a macro in a tevent is also called by a trigger, associate */
8083    /* the tevent with the trigger.                                  */
8084    associate_tevent_triggers ( teventp, macrop );
8085 
8086    /* Replace delayed events with undelayed events having increased */
8087    /* offsets (and new macros) to compensate if requested           */
8088    if ( configp->repl_delay == YES )
8089       replace_delayed_events ( &teventp, &macrop, &elementp );
8090 
8091    /* Combine similar tevents if requested */
8092    if ( configp->combine_events == YES )
8093       combine_similar_tevents ( &teventp, &macrop, &elementp );
8094 
8095    /* Compress macros by merging unit codes for the same */
8096    /* HouseCode and Command and eliminating duplicates.  */
8097    if ( configp->compress_macros == YES )
8098       compress_macros ( macrop, elementp );
8099 
8100    /* Adjust the times for events programmed with Security mode */
8101    security_adjust_legal ( &teventp, &macrop, &elementp );
8102 
8103    /* Save the timer and tevents up to this point */
8104    save_state ( timerp, teventp );
8105 
8106    /* Open a file to store the detailed results */
8107    if ( !(fd_cron = fopen(altpathspec(CRON_REPORT_FILE), "w")) ) {
8108       (void)fprintf(stderr, "Unable to open cron report file '%s'\n",
8109                     altpathspec(CRON_REPORT_FILE));
8110       exit(1);
8111    }
8112 
8113    (void)fprintf(fd_cron, "Results of Daily Operation for the next 366 Days\n");
8114    (void)fprintf(fd_cron, "================================================\n\n");
8115 
8116    least_error = 10000;
8117    worst_error = 0;
8118 
8119    minfree = 10000;
8120    maxfree = 0;
8121 
8122    min_timers  = 10000;
8123    max_timers  = 0;
8124 
8125    (void) fprintf(stdout,  "Begin date Time    Timers   Freespace   Dawn/Dusk error\n");
8126    (void) fprintf(stdout,  "---------- -----   ------  -----------  ---------------\n");
8127    (void) fprintf(fd_cron, "Begin date Time    Timers   Freespace   Dawn/Dusk error\n");
8128    (void) fprintf(fd_cron, "---------- -----   ------  -----------  ---------------\n");
8129 
8130    for ( j = 0; j < 366; j++ ) {
8131       (void) fprintf(stdout,  "%4d/%02d/%02d %02d:%02d    ",
8132              calendp->year, calendp->month, calendp->mday,
8133 	     calendp->minutes / 60, calendp->minutes % 60);
8134       fflush(stdout);
8135 
8136       (void) fprintf(fd_cron, "%4d/%02d/%02d %02d:%02d    ",
8137              calendp->year, calendp->month, calendp->mday,
8138 	     calendp->minutes / 60, calendp->minutes % 60);
8139 
8140       /* Convert the programmed month/day ranges into day counts   */
8141       /* over the programmed interval and adjust purely Clock      */
8142       /* events for Daylight time if applicable.                   */
8143 
8144       resolve_dates ( &teventp, calendp, configp->program_days );
8145 
8146       /* Resolve Dawn/Dusk timer options, if any */
8147       resolve_dawndusk_options( &teventp, calendp );
8148 
8149       clear_disabled_events( &teventp );
8150 
8151       /* Resolve overlapping date ranges for Dawn/Dusk relative    */
8152       /* events so that on any given day all events will have the  */
8153       /* same value for Dawn and/or Dusk.                          */
8154 
8155       if ( configp->res_overlap == RES_OVLAP_COMBINED )
8156          resolve_tevent_overlap_comb( &teventp ); /* Older method */
8157       else
8158          resolve_tevent_overlap_sep( &teventp );
8159 
8160       /* Mark macros actually in use over the programmed interval. */
8161       identify_macros_in_use(macrop, teventp);
8162 
8163       /* Recombine the individual tevents into timers */
8164       (void) reconstruct_timers( teventp, &timerp );
8165 
8166       /* Check total freespace before going any further. */
8167       freespace = get_freespace(current_timer_generation, timerp, triggerp, macrop );
8168 
8169       /* Subtract timer space we are holding in reserve */
8170       freespace -= 9 * configp->reserved_timers;
8171 
8172       if ( freespace < 0 ) {
8173          (void) fprintf(stderr,
8174             "Schedule too large: CM11a memory size exceeded by %d bytes ( = %.1f%% )\n",
8175                 -freespace, 100. * (double)(-freespace)/(double)PROMSIZE);
8176          exit(1);
8177       }
8178 
8179       /* Get freespace excluding timers   */
8180 
8181       freespace = get_freespace(-1, NULL, triggerp, macrop )
8182                                                - 9 * configp->reserved_timers;
8183 
8184       /* Resolve any dawn/dusk based timers into a sequence of clock */
8185       /* based timers, choosing the date intervals so as to minimize */
8186       /* the error in dawn and/or dusk times over each interval      */
8187       /* subject to the constraint of the available freespace.       */
8188 
8189       retcode = resolve_sun_times( &timerp, calendp, freespace,
8190                                        &tot_timers, &max_error);
8191       if ( retcode ) {
8192          (void)fprintf(stderr,
8193                   "\nQuitting due to errors.\n");
8194           return 1;
8195       }
8196 
8197       min_timers = min(min_timers, tot_timers);
8198       max_timers = max(max_timers, tot_timers);
8199 
8200       least_error = min(least_error,  max_error);
8201       worst_error = max(worst_error, max_error);
8202 
8203       /* Get the remaining freespace. */
8204       freespace = get_freespace( current_timer_generation, timerp, triggerp,
8205                                         macrop );
8206 
8207       minfree = min(minfree, freespace);
8208       maxfree = max(maxfree, freespace);
8209 
8210       /* Display the results */
8211       (void) fprintf(fd_cron, "%3d   %4d (%4.1f%%)  %4d\n",
8212          tot_timers, freespace, 100.*(double)freespace/1024., max_error);
8213 
8214       (void) fprintf(stdout,  "%3d   %4d (%4.1f%%)  %4d\r",
8215          tot_timers, freespace, 100.*(double)freespace/1024., max_error);
8216       fflush(stdout);
8217 
8218       /* Check for start time = stop time error */
8219       if ( check_timer_start_stop(timerp) > 0 ) {
8220          (void) fprintf(stderr, "Internal error: stop time = start time\n");
8221          (void) fprintf(stderr, "CM11A Bug! - will not execute stop event macro\n");
8222          exit(1);
8223       }
8224 
8225       /* Advance our calendar by one day */
8226       advance_calendar( calendp, 1 );
8227 
8228       /* Restore timers and tevents to the state saved before the */
8229       /* first pass through the loop.                             */
8230       restore_state ( timerp, teventp );
8231    }
8232 
8233    (void) printf("Overall results:%40s\n", " ");
8234    (void) fprintf(stdout,  "Minimum             %3d   %4d (%4.1f%%)  %4d\n",
8235         min_timers, minfree, 100.*(double)minfree/1024., least_error);
8236    (void) fprintf(stdout,  "Maximum             %3d   %4d (%4.1f%%)  %4d\n",
8237         max_timers, maxfree, 100.*(double)maxfree/1024., worst_error);
8238    (void) fprintf(stdout, "See file %s for details.\n", altpathspec(CRON_REPORT_FILE));
8239 
8240    (void) fprintf(fd_cron, "\nOverall results:\n");
8241    (void) fprintf(fd_cron, "Minimum             %3d   %4d (%4.1f%%)  %4d\n",
8242         min_timers, minfree, 100.*(double)minfree/1024., least_error);
8243    (void) fprintf(fd_cron, "Maximum             %3d   %4d (%4.1f%%)  %4d\n",
8244         max_timers, maxfree, 100.*(double)maxfree/1024., worst_error);
8245 
8246    (void) fclose( fd_cron );
8247 
8248    free( timerp );
8249    free( teventp );
8250    free( triggerp );
8251    free( macrop );
8252    free( elementp );
8253 
8254    return 0;
8255 }
8256 
8257 
8258 /*---------------------------------------------------------------------+
8259  | Write a table of daily sunrise/set or twilight to disk using        |
8260  | location and timezone parameters from the configuration file.       |
8261  +---------------------------------------------------------------------*/
write_sun_table(int format,int year,int sunmode,int offset,int timemode)8262 int write_sun_table ( int format, int year, int sunmode, int offset, int timemode )
8263 {
8264    FILE *fd_sun;
8265    char filename[256];
8266    static char *fname[] = {"SunRiseSet", "CivilTwilight",
8267       "NautTwilight", "AstronTwilight", "SunAngleOffset", };
8268 
8269    get_configuration(CONFIG_INIT);
8270 
8271    if ( configp->loc_flag != (unsigned char)(LATITUDE | LONGITUDE) ) {
8272       fprintf(stderr,
8273        "LATITUDE and/or LONGITUDE not specified in %s\n",
8274                                          pathspec(CONFIG_FILE));
8275       return 1;
8276    }
8277 
8278    if ( format == FMT_PORTRAIT )
8279       sprintf(filename, "%s_%d.txt", altpathspec(fname[sunmode]), year);
8280    else
8281       sprintf(filename, "%s_%d_wide.txt", altpathspec(fname[sunmode]), year);
8282 
8283    if ( (fd_sun = fopen(filename, "w")) ) {
8284       printf("Writing file %s\n", filename);
8285       if ( format == FMT_PORTRAIT ) {
8286          display_sun_table(fd_sun, year, configp->tzone, sunmode, offset, timemode,
8287              configp->lat_d, configp->lat_m, configp->lon_d, configp->lon_m);
8288       }
8289       else {
8290          display_sun_table_wide(fd_sun, year, configp->tzone, sunmode, offset, timemode,
8291              configp->lat_d, configp->lat_m, configp->lon_d, configp->lon_m);
8292       }
8293    }
8294    else {
8295       fprintf(stderr, "Unable to open file '%s' for write\n",
8296                                                         filename);
8297       return 1;
8298    }
8299 
8300    return 0;
8301 }
8302 
8303 
8304 /*---------------------------------------------------------------------+
8305  | Convert a yday measured from Jan 1 of year0 to year, month, day.    |
8306  +---------------------------------------------------------------------*/
yday2datestr(long jan1day,int yday)8307 char *yday2datestr ( long jan1day, int yday )
8308 
8309 {
8310 
8311    static char buffer[20];
8312    time_t     now;
8313    long       delta_days;
8314 
8315    if ( !configp->read_flag ) {
8316       get_std_timezone();
8317       configp->tzone = std_tzone;
8318    }
8319 
8320    delta_days = (long)yday + jan1day;
8321    now = (time_t)(86400L * delta_days + configp->tzone );
8322 
8323    gendate(buffer, now, YES, NO);
8324 
8325    return buffer;
8326 }
8327 
8328 /*---------------------------------------------------------------------+
8329  | Convert a yday measured from Jan 1 of year0 to year, month, day.    |
8330  +---------------------------------------------------------------------*/
yday2date(long jan1day,int yday,int * year,int * month,int * mday,int * wday)8331 void yday2date ( long jan1day, int yday, int *year, int *month,
8332                                                    int *mday, int *wday )
8333 
8334 {
8335 
8336    time_t     now;
8337    struct tm  *tms;
8338    long       delta_days;
8339 
8340    if ( !configp->read_flag ) {
8341       get_std_timezone();
8342       configp->tzone = std_tzone;
8343    }
8344 
8345    delta_days = (long)yday + jan1day;
8346    now = (time_t)(86400L * delta_days + configp->tzone );
8347 
8348    tms = localtime( &now );
8349    *year  = tms->tm_year + 1900;
8350    *month = tms->tm_mon + 1;
8351    *mday  = tms->tm_mday;
8352    *wday  = tms->tm_wday;
8353 
8354    return;
8355 }
8356 
8357 /*---------------------------------------------------------------------+
8358  | Symbols for event flags.                                            |
8359  +---------------------------------------------------------------------*/
flag_def(unsigned int flag)8360 char flag_def( unsigned int flag )
8361 {
8362    return (flag & CLOCK_EVENT)   ? 'C' :
8363           (flag & DAWN_EVENT)    ? 'R' :
8364           (flag & DUSK_EVENT)    ? 'S' : 'X' ;
8365 }
8366 
8367 /*---------------------------------------------------------------------+
8368  | Qsort compare function for display events()                         |
8369  +---------------------------------------------------------------------*/
comp_events(struct ev_s * one,struct ev_s * two)8370 int comp_events ( struct ev_s *one, struct ev_s *two )
8371 {
8372    return
8373           (one->line > two->line) ?  1 :
8374           (one->line < two->line) ? -1 :
8375           (one->pos  > two->pos)  ?  1 :
8376           (one->pos  < two->pos)  ? -1 :
8377           (one->flag > two->flag) ?  1 :
8378           (one->flag < two->flag) ? -1 :
8379           (one->beg  > two->beg)  ?  1 :
8380           (one->beg  < two->beg)  ? -1 : 0 ;
8381 }
8382 
8383 /*---------------------------------------------------------------------+
8384  | Find the start of the tevent chain, i.e., the tevent not linked to  |
8385  | by any other.                                                       |
8386  +---------------------------------------------------------------------*/
find_startchain(TEVENT * teventp)8387 int find_startchain ( TEVENT *teventp )
8388 {
8389    int j, k, size, *evrlink;
8390    static int sizint = sizeof(int);
8391 
8392    size = 0;
8393    while ( teventp[size].line_no > 0 )
8394       size++;
8395 
8396    if ( (evrlink = calloc( size, sizint )) == NULL ) {
8397       fprintf(stderr, "find_startchain() - Unable to allocate memory.\n");
8398       exit(1);
8399    }
8400 
8401    for ( j = 0; j < size; j++ )
8402       evrlink[j] = 0;
8403 
8404    for ( j = 0; j < size; j++ ) {
8405       k = teventp[j].link;
8406       if ( k > (size - 1) ) {
8407          (void)fprintf(stderr,
8408            "find_startchain() - link %d out of bound %d at index %d\n", k, size - 1, j);
8409          return -1;
8410       }
8411       if ( k >= 0 )
8412          evrlink[k] = 1;
8413    }
8414    for ( j = 0; j < size; j++ ) {
8415       if ( evrlink[j] == 0 )
8416          break;
8417    }
8418 
8419    free(evrlink);
8420 
8421    return j;
8422 }
8423 
8424 
8425 /*---------------------------------------------------------------------+
8426  | Display the amount of EEPROM memory used by timers, triggers, and   |
8427  | macros.                                                             |
8428  +---------------------------------------------------------------------*/
display_eeprom_usage(FILE * fd,TIMER * timerp,TRIGGER * triggerp,MACRO * macrop)8429 void display_eeprom_usage ( FILE *fd, TIMER *timerp, TRIGGER *triggerp,
8430                                                          MACRO *macrop )
8431 {
8432    int j;
8433    int sun_event, clk_event, ntimers, ntriggers, nmacros, macspace;
8434    int ovhead, free;
8435 
8436    sun_event = clk_event = ntimers = 0;
8437    j = 0;
8438    while ( timerp && timerp[j].line_no > 0 ) {
8439       if ( timerp[j].generation != current_timer_generation ||
8440            timerp[j].flag_combined == NO_EVENT ) {
8441          j++;
8442          continue;
8443       }
8444       ntimers++;
8445       if ( timerp[j].flag_start & (DAWN_EVENT | DUSK_EVENT) )
8446          sun_event++;
8447       else if ( timerp[j].flag_start & CLOCK_EVENT )
8448          clk_event++;
8449       if ( timerp[j].flag_stop & (DAWN_EVENT | DUSK_EVENT) )
8450          sun_event++;
8451       else if ( timerp[j].flag_stop & CLOCK_EVENT )
8452          clk_event++;
8453 
8454       j++;
8455    }
8456 
8457    ntriggers = 0;
8458    j = 0;
8459    while ( triggerp && triggerp[j].line_no > 0 ) {
8460       ntriggers++;
8461       j++;
8462    }
8463 
8464    nmacros = macspace = 0;
8465    j = 0;
8466    while ( macrop[j].line_no > 0 ) {
8467       if ( !macrop[j].isnull && macrop[j].use == USED ) {
8468          nmacros++;
8469          macspace += macrop[j].total + 2;
8470       }
8471       j++;
8472    }
8473    macspace += (configp->macterm == YES) ? nmacros : 0 ;
8474 
8475    ovhead = 2 +  /* Initial jump */
8476             1 +  /* Timer terminator */
8477             2 +  /* Trigger terminator */
8478             2 ;  /* Final macro terminator */
8479 
8480    free = (PROMSIZE)
8481         - 9 * ntimers
8482         - 3 * ntriggers
8483         - macspace
8484         - ovhead;
8485 
8486    fprintf(fd, "\nEEPROM Utilization\n");
8487    fprintf(fd, "Type      Nbr  Size\n");
8488    fprintf(fd, "----      ---  ----\n");
8489    fprintf(fd, "Timers    %3d  %4d (%4.1f%%)\n",
8490          ntimers, 9 * ntimers, 900. * (float)(ntimers) / 1024. );
8491    fprintf(fd, "Triggers  %3d  %4d (%4.1f%%)\n",
8492          ntriggers, 3 * ntriggers, 300. * (float)ntriggers / 1024. );
8493    fprintf(fd, "Macros    %3d  %4d (%4.1f%%)\n",
8494          nmacros, macspace, 100. * (float)macspace / 1024. );
8495    fprintf(fd, "Overhead       %4d (%4.1f%%)\n",
8496          ovhead, 100. * (float)ovhead / 1024.);
8497    fprintf(fd, "Freespace      %4d (%4.1f%%)\n",
8498          free, 100. * (float)free / 1024.);
8499    fprintf(fd, "               ----\n");
8500    fprintf(fd, "Total          %4d\n\n", PROMSIZE);
8501 
8502    return ;
8503 
8504 }
8505 
8506 /*---------------------------------------------------------------------+
8507  | Display timer options                                               |
8508  +---------------------------------------------------------------------*/
display_timer_options(TIMER * timerp,int index)8509 char *display_timer_options ( TIMER *timerp, int index )
8510 {
8511    static char   buffer[64];
8512    char          minibuf[32];
8513    unsigned char ddopts;
8514 
8515    *buffer = '\0';
8516    if ( !(ddopts = timerp[index].ddoptions) )
8517       return buffer;
8518    if ( ddopts & DAWNGT ) {
8519       sprintf(minibuf, "  DawnGT %02d:%02d",
8520          timerp[index].dawngt / 60, timerp[index].dawngt % 60);
8521       strncat(buffer, minibuf, sizeof(buffer) - 1 - strlen(buffer));
8522    }
8523    if ( ddopts & DAWNLT ) {
8524       sprintf(minibuf, "  DawnLT %02d:%02d",
8525          timerp[index].dawnlt / 60, timerp[index].dawnlt % 60);
8526       strncat(buffer, minibuf, sizeof(buffer) - 1 - strlen(buffer));
8527    }
8528    if ( ddopts & DUSKGT ) {
8529       sprintf(minibuf, "  DuskGT %02d:%02d",
8530          timerp[index].duskgt / 60, timerp[index].duskgt % 60);
8531       strncat(buffer, minibuf, sizeof(buffer) - 1 - strlen(buffer));
8532    }
8533    if ( ddopts & DUSKLT ) {
8534       sprintf(minibuf, "  DuskLT %02d:%02d",
8535          timerp[index].dusklt / 60, timerp[index].dusklt % 60);
8536       strncat(buffer, minibuf, sizeof(buffer) - 1 - strlen(buffer));
8537    }
8538 
8539    return buffer;
8540 }
8541 
8542 
8543 /*---------------------------------------------------------------------+
8544  | Display timed events included in timers.                            |
8545  | Null events are ignored.                                            |
8546  +---------------------------------------------------------------------*/
display_events(FILE * fd,TIMER * timerp,TEVENT * teventp,MACRO * macrop,CALEND * calendp)8547 int display_events ( FILE *fd, TIMER *timerp, TEVENT *teventp,
8548                                  MACRO *macrop, CALEND *calendp )
8549 {
8550    extern int  current_timer_generation;
8551 
8552    struct ev_s  event[1024];
8553    int          (*fptr)() = &comp_events;
8554 
8555    int          prtlist[1024];
8556    int          j, k, m, startchain, j1, j2, nevents, count, retcode;
8557    int          line, pos, ncomb, macro, error, sflag;
8558    int          dayzero, /*year, month, day, wday,*/ sched_end;
8559    int          beg, end, endprev/*, year2, month2, day2*/;
8560    int          createday, endday, lostday;
8561    unsigned int flag, ddflag;
8562    long         jan1day;
8563    char         shift, secur = ' ';
8564    int          legal, secnom, tadj, offset = 0;
8565    char         *sec_flag = " sss";
8566    char         note[6];
8567    char         minibuf[32];
8568    unsigned char ddoptions;
8569    char         datestr1[16], datestr2[16];
8570 
8571    if ( timerp == NULL )
8572       return 0;
8573 
8574    jan1day = calendp->jan1day;
8575    dayzero = calendp->day_zero;
8576    createday = calendp->create_day;
8577    endday  = calendp->yday + configp->program_days - 1;
8578 
8579    startchain = find_startchain( teventp );
8580 
8581    ddflag = NO_EVENT;
8582    nevents = 0;
8583    j = 0;
8584    while ( timerp[j].line_no > 0 ) {
8585       if ( timerp[j].generation != current_timer_generation ) {
8586           j++;
8587           continue;
8588       }
8589       if ( timerp[j].macro_start != NULL_MACRO_INDEX ) {
8590          event[nevents].tevent = timerp[j].tevent_start;
8591          event[nevents].timer = j;
8592          event[nevents].line = timerp[j].line1;
8593          event[nevents].pos = timerp[j].pos1;
8594          event[nevents].tpos = 1;
8595          event[nevents].beg = timerp[j].resolv_beg;
8596          event[nevents].flag = timerp[j].flag_start & SEC_EVENT;
8597          ddflag |= timerp[j].flag_start & TIME_EVENTS;
8598          nevents++;
8599       }
8600       if ( timerp[j].macro_stop != NULL_MACRO_INDEX ) {
8601          event[nevents].tevent = timerp[j].tevent_stop;
8602          event[nevents].timer = j;
8603          event[nevents].line = timerp[j].line2;
8604          event[nevents].pos = timerp[j].pos2;
8605          event[nevents].tpos = 2;
8606          event[nevents].beg = timerp[j].resolv_beg;
8607          event[nevents].flag = timerp[j].flag_stop & SEC_EVENT;
8608          ddflag |= timerp[j].flag_stop & TIME_EVENTS;
8609          nevents++;
8610       }
8611       j++;
8612    }
8613 
8614    (void)fprintf( fd, "TIMED EVENTS as expanded and included in uploaded Timers\n");
8615    if ( nevents == 0 ) {
8616       (void)fprintf( fd, "-- None --\n\n" );
8617       retcode = 0;
8618    }
8619    else
8620       retcode = 1;
8621 
8622    /* Sort the events */
8623    qsort((void *)event, nevents, sizeof(struct ev_s), fptr);
8624 
8625 
8626    if ( nevents > 0 ) {
8627       if ( ddflag & (DAWN_EVENT | DUSK_EVENT) ) {
8628          (void)fprintf( fd,
8629              "Line  Week-  Interval                        Sched/   Macro/ Dawn/Dusk\n");
8630          (void)fprintf( fd,
8631              "-Pos   days  Beg End  Date begin Date end    Expand   A.E.T.   Error\n");
8632          (void)fprintf( fd,
8633              "---- ------- --- ---  ---------- ----------  -----    -----  --------\n");
8634       }
8635       else {
8636          (void)fprintf( fd,
8637              "Line  Week-  Interval                        Sched/   Macro/\n");
8638          (void)fprintf( fd,
8639              "-Pos   days  Beg End  Date begin Date end    Expand   A.E.T.\n");
8640          (void)fprintf( fd,
8641              "---- ------- --- ---  ---------- ----------  -----    ------\n");
8642       }
8643    }
8644 
8645 
8646    /* Isolate a group of events to print */
8647    line = event[0].line;
8648    pos = event[0].pos;
8649    sflag = event[0].flag;
8650    j1 = j2 = 0;
8651 
8652    while ( j1 < nevents ) {
8653       while ( j2 < nevents &&
8654               event[j2].line == line  &&
8655               event[j2].pos  == pos   &&
8656               event[j2].flag == sflag     ) {
8657          j2++;
8658       }
8659 
8660       /* Find the original tevent(s)/timer(s) which gave birth */
8661       /* to this event and display them.                       */
8662 
8663       ncomb = 0;
8664       j = event[j1].tevent;
8665       count = teventp[j].print;
8666       while ( count > 0 && j >= 0 ) {
8667          if ( teventp[j].generation == 0 ) {
8668             teventp[j].flag |= ACTIVE_EVENT;
8669          }
8670          if ( teventp[j].flag & PRT_EVENT ) {
8671             prtlist[ncomb++] = j;
8672             count--;
8673          }
8674          j = teventp[j].plink;
8675       }
8676 
8677       ddoptions = 0;
8678       for ( k = ncomb - 1; k >= 0; k-- ) {
8679          m = prtlist[k];
8680          j = teventp[m].timer;
8681 
8682          macro = teventp[m].macro;
8683          flag = teventp[m].flag;
8684          offset = teventp[m].offset;
8685          pos = teventp[m].pos;
8686          secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
8687          line = timerp[j].line_no;
8688 	 ddoptions |= timerp[j].ddoptions;
8689 
8690          if ( /* teventp[m].*/ flag & COMB_EVENT )
8691             (void)fprintf( fd, " **  ");
8692          else
8693             (void)fprintf(fd, "%02d-%d ", line, pos);
8694 
8695          if ( timerp[j].notify >= 0 ) {
8696             (void)fprintf( fd, "%s          expire-%-3d      ",
8697                bmap2dow(timerp[j].dow_bmap), timerp[j].notify);
8698          }
8699          else {
8700             sched_end = timerp[j].sched_end;
8701             if ( sched_end > 1231 ) {
8702                /* Remove the kluge for reversed date range */
8703                sched_end -= 1200;
8704             }
8705 
8706             fprintf(fd, "%s          ", bmap2dow(timerp[j].dow_bmap));
8707             switch ( configp->date_format ) {
8708                case MDY_ORDER :
8709                    fprintf(fd, "%02d%c%02d      %02d%c%02d       ",
8710                       timerp[j].sched_beg / 100, configp->date_separator, timerp[j].sched_beg % 100,
8711                       sched_end / 100, configp->date_separator, sched_end % 100);
8712                    break;
8713                case DMY_ORDER :
8714                    fprintf(fd, "%02d%c%02d      %02d%c%02d       ",
8715                       timerp[j].sched_beg % 100, configp->date_separator, timerp[j].sched_beg / 100,
8716                       sched_end % 100, configp->date_separator, sched_end / 100);
8717                    break;
8718                case YMD_ORDER :
8719                    fprintf(fd, "     %02d%c%02d      %02d%c%02d  ",
8720                       timerp[j].sched_beg / 100, configp->date_separator, timerp[j].sched_beg % 100,
8721                       sched_end / 100, configp->date_separator, sched_end % 100);
8722                    break;
8723                default :
8724                    break;
8725             }
8726 
8727 #if 0
8728             (void)fprintf( fd, "%s          %02d/%02d      %02d/%02d",
8729                   bmap2dow(timerp[j].dow_bmap),
8730                   timerp[j].sched_beg / 100, timerp[j].sched_beg % 100,
8731                   sched_end / 100, sched_end % 100);
8732 #endif
8733          }
8734 
8735          if ( flag & CLOCK_EVENT ) {
8736             (void)fprintf( fd, "%02d:%02d%c   %s%s\n",
8737                offset / 60, offset % 60, secur, macrop[macro].label,
8738                display_timer_options(timerp, j));
8739          }
8740          else if ( flag & DAWN_EVENT ) {
8741             (void)sprintf(minibuf, "dawn%+d%c", offset, secur);
8742             (void)fprintf( fd, "%-15s %s%s\n", minibuf, macrop[macro].label,
8743                display_timer_options(timerp, j));
8744          }
8745          else if ( flag & DUSK_EVENT ) {
8746             (void)sprintf(minibuf, "dusk%+d%c", offset, secur);
8747             (void)fprintf( fd, "%-15s %s%s\n", minibuf, macrop[macro].label,
8748                display_timer_options(timerp, j));
8749          }
8750          else {
8751             (void)fprintf( fd, "\n");
8752          }
8753          m = teventp[m].link;
8754       }
8755 
8756       /* Now display events as expanded by heyu. */
8757 
8758       endprev = 0;
8759       for ( k = j1; k < j2; k++ ) {
8760          j = event[k].timer;
8761          if ( event[k].tpos == 1 ) {
8762             offset = timerp[j].offset_start;
8763             flag   = timerp[j].flag_start;
8764             secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
8765             secnom = flag & SEC_EVENT ? SECURITY_OFFSET_ADJUST : 0;
8766             macro  = timerp[j].macro_start;
8767             error  = timerp[j].error_start;
8768          }
8769          else {
8770             offset = timerp[j].offset_stop;
8771             flag   = timerp[j].flag_stop;
8772             secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
8773             secnom = flag & SEC_EVENT ? SECURITY_OFFSET_ADJUST : 0;
8774             macro  = timerp[j].macro_stop;
8775             error  = timerp[j].error_stop;
8776          }
8777          if ( flag & SEC_EVENT )
8778             (void)strncpy2(note, "s", sizeof(note) - 1);
8779          else
8780             note[0] = '\0';
8781 
8782          if ( flag & SUBST_EVENT )
8783             (void)strncpy2(note, " r", sizeof(note) - 1);
8784 
8785          /* Display blank line if there is a gap in dates */
8786 	 /* as the result of timer options.               */
8787          if ( k > j1 && ddoptions && timerp[j].resolv_beg != (endprev + 1))
8788             (void)fprintf( fd, "\n");
8789          endprev = timerp[j].resolv_end;
8790 
8791          (void)fprintf( fd, "     %s %03d-%03d",
8792             bmap2dow(timerp[j].dow_bmap),
8793             timerp[j].resolv_beg, timerp[j].resolv_end);
8794 
8795 
8796          /* Determine the actual event execution legal time */
8797          legal = offset + macrop[macro].delay + secnom;
8798 
8799          tadj  = time_adjust(timerp[j].resolv_beg + dayzero, legal, STD2LGL);
8800          if ( time_adjust(timerp[j].resolv_end + dayzero, legal, STD2LGL) != tadj )
8801             (void)strncat(note, "*", sizeof(note) - 1 - strlen(note));
8802          legal += tadj;
8803          if ( legal < 0 ) {
8804             legal += 1440;
8805             shift = '<';
8806          }
8807          else if ( legal > 1439 ) {
8808             legal -= 1440;
8809             shift = '>';
8810          }
8811          else {
8812             shift = ' ';
8813          }
8814 
8815          fprintf(fd, "  %s", yday2datestr(jan1day, timerp[j].resolv_beg + dayzero));
8816          fprintf(fd, " %s",  yday2datestr(jan1day, timerp[j].resolv_end + dayzero));
8817 
8818 #if 0
8819          yday2date(   (void)sprintf(buffer, "%s %s %02d %4d %02d:%02d:%02d %s",
8820      wday_name[tms->tm_wday], month_name[tms->tm_mon], tms->tm_mday, tms->tm_year + 1900,
8821         tms->tm_hour, tms->tm_min, tms->tm_sec, heyu_tzname[tms->tm_isdst]);
8822 jan1day, timerp[j].resolv_beg + dayzero,
8823                                           &year, &month, &day, &wday);
8824          (void)fprintf( fd, "  %02d/%02d/%04d-", month, day, year);
8825          yday2date(jan1day, timerp[j].resolv_end + dayzero,
8826                                            &year, &month, &day, &wday);
8827          (void)fprintf( fd, "%02d/%02d/%04d", month, day, year);
8828 #endif
8829 
8830 
8831 
8832 
8833          if ( flag & (DAWN_EVENT | DUSK_EVENT) )
8834             (void)fprintf( fd,
8835               "  %02d:%02d%c  %c%02d:%02d%-4s[%2d]\n",
8836                 offset / 60, offset % 60, secur, shift, legal / 60, legal % 60, note, error);
8837          else
8838             (void)fprintf( fd,
8839               "  %02d:%02d%c  %c%02d:%02d%-4s\n",
8840                 offset / 60, offset % 60, secur, shift, legal / 60, legal % 60, note);
8841       }
8842 
8843       (void)fprintf( fd, "\n");
8844       j1 = j2 ;
8845       line = event[j1].line;
8846       pos  = event[j1].pos;
8847       sflag = event[j1].flag;
8848    }
8849 
8850    /* Display LOST events */
8851    (void)fprintf( fd, "TIMED EVENTS day-shifted out of this period.\n");
8852 
8853    j = startchain; nevents = 0;
8854    while ( j >= 0 ) {
8855       if ( teventp[j].flag & LOST_EVENT )
8856          prtlist[nevents++] = j;
8857       j = teventp[j].link;
8858    }
8859 
8860    if ( nevents == 0 ) {
8861       (void)fprintf( fd, "-- None --\n\n");
8862    }
8863    else {
8864       (void)fprintf( fd,
8865           "Line  Wdays  Beg End  Date begin Date end    Time     Macro\n");
8866       (void)fprintf( fd,
8867           "---- ------- --- ---  ---------- ----------  -----    -----\n");
8868 
8869       for ( k = 0; k < nevents; k++ ) {
8870          j = prtlist[k];
8871          lostday = teventp[j].lostday;
8872 //         yday2date(jan1day, lostday + dayzero , &year, &month, &day, &wday);
8873          strcpy(datestr1, yday2datestr(jan1day, lostday + dayzero));
8874 
8875          if ( teventp[j].flag & COMB_EVENT )
8876             (void)fprintf( fd, " **  ");
8877          else
8878             (void)fprintf( fd, "%02d-%d ", teventp[j].line_no, teventp[j].pos);
8879 
8880 #if 0
8881          (void)fprintf( fd, "%s %03d-%03d  %02d/%02d/%4d-%02d/%02d/%4d  %02d:%02d%c   %s\n",
8882             bmap2dow(teventp[j].dow_bmap), lostday, lostday,
8883             month, day, year, month, day, year,
8884             teventp[j].offset / 60, teventp[j].offset % 60,
8885             sec_flag[teventp[j].flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0],
8886             macrop[teventp[j].macro].label);
8887 #endif
8888          (void)fprintf( fd, "%s %03d-%03d  %s %s  %02d:%02d%c   %s\n",
8889             bmap2dow(teventp[j].dow_bmap), lostday, lostday,
8890             datestr1, datestr1,
8891             teventp[j].offset / 60, teventp[j].offset % 60,
8892             sec_flag[teventp[j].flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0],
8893             macrop[teventp[j].macro].label);
8894 
8895 
8896 
8897       }
8898       (void)fprintf( fd, "\n\n");
8899    }
8900 
8901 
8902    /* Display CANCELLED events */
8903    (void)fprintf( fd, "TIMED EVENTS skipped per Timer options.\n");
8904 
8905    j = startchain; nevents = 0;
8906    while ( j >= 0 ) {
8907       if ( teventp[j].flag & CANCEL_EVENT )
8908          prtlist[nevents++] = j;
8909       j = teventp[j].link;
8910    }
8911 
8912    if ( nevents == 0 ) {
8913       (void)fprintf( fd, "-- None --\n\n");
8914    }
8915    else {
8916       (void)fprintf( fd,
8917           "Line  Wdays  Beg End  Date begin Date end    StdTime  Macro\n");
8918       (void)fprintf( fd,
8919           "---- ------- --- ---  ---------- ----------  -------  -----\n");
8920 
8921       for ( k = 0; k < nevents; k++ ) {
8922          j = prtlist[k];
8923          beg    = teventp[j].resolv_beg;
8924          end    = teventp[j].resolv_end;
8925          offset = teventp[j].offset;
8926          macro  = teventp[j].macro;
8927          flag   = teventp[j].flag;
8928          secur = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
8929 
8930 //         yday2date(jan1day, beg + dayzero, &year, &month, &day, &wday);
8931 //         yday2date(jan1day, end + dayzero, &year2, &month2, &day2, &wday);
8932 
8933          strcpy(datestr1, yday2datestr(jan1day, beg + dayzero));
8934          strcpy(datestr2, yday2datestr(jan1day, end + dayzero));
8935 
8936          if ( teventp[j].flag & COMB_EVENT )
8937             (void)fprintf( fd, " **  ");
8938          else
8939             (void)fprintf( fd, "%02d-%d ", teventp[j].line_no, teventp[j].pos);
8940 
8941          (void)fprintf( fd, "%s %03d-%03d  %s %s",
8942             bmap2dow(teventp[j].dow_bmap), beg, end,
8943 //            month, day, year, month2, day2, year2);
8944             datestr1, datestr2);
8945 
8946 
8947          if ( flag & CLOCK_EVENT ) {
8948             (void)sprintf(minibuf, "  %02d:%02d%c",
8949                offset / 60, offset % 60, secur);
8950             (void)fprintf( fd, "%-10s %s\n", minibuf, macrop[macro].label);
8951          }
8952          else if ( flag & DAWN_EVENT ) {
8953             (void)sprintf(minibuf, "  dawn%+d%c", offset, secur);
8954             (void)fprintf( fd, "%-10s %s\n", minibuf, macrop[macro].label);
8955          }
8956          else if ( flag & DUSK_EVENT ) {
8957             (void)sprintf(minibuf, "  dusk%+d%c", offset, secur);
8958             (void)fprintf( fd, "%-10s %s\n", minibuf, macrop[macro].label);
8959          }
8960          else {
8961             (void)fprintf( fd, "\n");
8962 	 }
8963 
8964       }
8965       (void)fprintf( fd, "\n\n");
8966    }
8967 
8968 
8969    /* Display the tevents not included in this period */
8970    (void)fprintf( fd, "TIMED EVENTS from schedule not active during this period.\n");
8971 
8972    j = startchain; nevents = 0;
8973    while ( j >= 0 ) {
8974       if ( teventp[j].generation == 0 && (teventp[j].flag & ACTIVE_EVENT) == 0 )
8975          prtlist[nevents++] = j;
8976       j = teventp[j].link;
8977    }
8978 
8979    if ( nevents == 0 ) {
8980       (void)fprintf( fd, "-- None --\n\n");
8981       return retcode;
8982    }
8983    else {
8984       (void)fprintf( fd,
8985           "Line  Wdays  Beg End  Date begin Date end    Time     Macro\n");
8986       (void)fprintf( fd,
8987           "---- ------- --- ---  ---------- ----------  -----    -----\n");
8988 
8989       for ( k = 0; k < nevents; k++ ) {
8990          m = prtlist[k];
8991          teventp[m].flag &= ~PRT_EVENT;
8992          j = teventp[m].timer;
8993 
8994          macro = teventp[m].macro;
8995          if ( teventp[m].pos == 1 ) {
8996             offset = timerp[j].offset_start;
8997             flag   = timerp[j].flag_start;
8998             secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
8999             line   = timerp[j].line_no;
9000             pos    = 1;
9001          }
9002          else {
9003             offset = timerp[j].offset_stop;
9004             flag   = timerp[j].flag_stop;
9005             secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
9006             line   = timerp[j].line_no;
9007             pos    = 2;
9008          }
9009 
9010          if ( teventp[m].flag & COMB_EVENT )
9011             (void)fprintf( fd, " **  ");
9012          else
9013             (void)fprintf(fd, "%02d-%d ", line, pos);
9014 
9015          if ( timerp[j].notify >= 0 ) {
9016             (void)fprintf( fd, "%s          expire-%-3d      ",
9017                bmap2dow(timerp[j].dow_bmap), timerp[j].notify);
9018          }
9019          else {
9020             sched_end = timerp[j].sched_end;
9021             if ( sched_end > 1231 ) {
9022                /* Remove the kluge for reversed date range */
9023                sched_end -= 1200;
9024             }
9025 
9026 #if 0
9027             (void)fprintf( fd, "%s          %02d/%02d      %02d/%02d",
9028                   bmap2dow(timerp[j].dow_bmap),
9029                   timerp[j].sched_beg / 100, timerp[j].sched_beg % 100,
9030                   sched_end / 100, sched_end % 100);
9031 #endif
9032             fprintf(fd, "%s          ", bmap2dow(timerp[j].dow_bmap));
9033             switch ( configp->date_format ) {
9034                case MDY_ORDER :
9035                    fprintf(fd, "%02d%c%02d      %02d%c%02d       ",
9036                       timerp[j].sched_beg / 100, configp->date_separator, timerp[j].sched_beg % 100,
9037                       sched_end / 100, configp->date_separator, sched_end % 100);
9038                    break;
9039                case DMY_ORDER :
9040                    fprintf(fd, "%02d%c%02d      %02d%c%02d       ",
9041                       timerp[j].sched_beg % 100, configp->date_separator, timerp[j].sched_beg / 100,
9042                       sched_end % 100, configp->date_separator, sched_end / 100);
9043                    break;
9044                case YMD_ORDER :
9045                    fprintf(fd, "     %02d%c%02d      %02d%c%02d  ",
9046                       timerp[j].sched_beg / 100, configp->date_separator, timerp[j].sched_beg % 100,
9047                       sched_end / 100, configp->date_separator, sched_end % 100);
9048                    break;
9049                default :
9050                    break;
9051             }
9052 
9053 
9054          }
9055          if ( flag & CLOCK_EVENT ) {
9056             (void)fprintf( fd, "%02d:%02d%c   %s\n",
9057                offset / 60, offset % 60, secur, macrop[macro].label);
9058          }
9059          else if ( flag & DAWN_EVENT ) {
9060             (void)fprintf( fd,
9061             "dawn%-+3d%c %s\n", offset, secur, macrop[macro].label);
9062          }
9063          else if ( flag & DUSK_EVENT ) {
9064             (void)fprintf( fd,
9065             "dusk%-+3d%c %s\n", offset, secur, macrop[macro].label);
9066          }
9067          else {
9068             (void)fprintf( fd, "\n");
9069          }
9070       }
9071    }
9072 
9073    return retcode;
9074 }
9075 
9076 #if 0
9077 /*---------------------------------------------------------------------+
9078  | Display timed events included in timers.                            |
9079  | Null events are ignored.                                            |
9080  +---------------------------------------------------------------------*/
9081 int display_events_old ( FILE *fd, TIMER *timerp, TEVENT *teventp,
9082                                  MACRO *macrop, CALEND *calendp )
9083 {
9084    extern int  current_timer_generation;
9085 
9086    struct ev_s  event[1024];
9087    int          (*fptr)() = &comp_events;
9088 
9089    int          prtlist[1024];
9090    int          j, k, m, startchain, j1, j2, nevents, count, retcode;
9091    int          line, pos, ncomb, macro, error, sflag;
9092    int          dayzero, year, month, day, wday, sched_end;
9093    int          beg, end, endprev, year2, month2, day2;
9094    int          createday, endday, lostday;
9095    unsigned int flag, ddflag;
9096    long         jan1day;
9097    char         shift, secur = ' ';
9098    int          legal, secnom, tadj, offset = 0;
9099    char         *sec_flag = " sss";
9100    char         note[6];
9101    char         minibuf[32];
9102    unsigned char ddoptions;
9103 
9104    if ( timerp == NULL )
9105       return 0;
9106 
9107    jan1day = calendp->jan1day;
9108    dayzero = calendp->day_zero;
9109    createday = calendp->create_day;
9110    endday  = calendp->yday + configp->program_days - 1;
9111 
9112    startchain = find_startchain( teventp );
9113 
9114    ddflag = NO_EVENT;
9115    nevents = 0;
9116    j = 0;
9117    while ( timerp[j].line_no > 0 ) {
9118       if ( timerp[j].generation != current_timer_generation ) {
9119           j++;
9120           continue;
9121       }
9122       if ( timerp[j].macro_start != NULL_MACRO_INDEX ) {
9123          event[nevents].tevent = timerp[j].tevent_start;
9124          event[nevents].timer = j;
9125          event[nevents].line = timerp[j].line1;
9126          event[nevents].pos = timerp[j].pos1;
9127          event[nevents].tpos = 1;
9128          event[nevents].beg = timerp[j].resolv_beg;
9129          event[nevents].flag = timerp[j].flag_start & SEC_EVENT;
9130          ddflag |= timerp[j].flag_start & TIME_EVENTS;
9131          nevents++;
9132       }
9133       if ( timerp[j].macro_stop != NULL_MACRO_INDEX ) {
9134          event[nevents].tevent = timerp[j].tevent_stop;
9135          event[nevents].timer = j;
9136          event[nevents].line = timerp[j].line2;
9137          event[nevents].pos = timerp[j].pos2;
9138          event[nevents].tpos = 2;
9139          event[nevents].beg = timerp[j].resolv_beg;
9140          event[nevents].flag = timerp[j].flag_stop & SEC_EVENT;
9141          ddflag |= timerp[j].flag_stop & TIME_EVENTS;
9142          nevents++;
9143       }
9144       j++;
9145    }
9146 
9147    (void)fprintf( fd, "TIMED EVENTS as expanded and included in uploaded Timers\n");
9148    if ( nevents == 0 ) {
9149       (void)fprintf( fd, "-- None --\n\n" );
9150       retcode = 0;
9151    }
9152    else
9153       retcode = 1;
9154 
9155    /* Sort the events */
9156    qsort((void *)event, nevents, sizeof(struct ev_s), fptr);
9157 
9158 
9159    if ( nevents > 0 ) {
9160       if ( ddflag & (DAWN_EVENT | DUSK_EVENT) ) {
9161          (void)fprintf( fd,
9162              "Line  Week-  Interval                        Sched/   Macro/ Dawn/Dusk\n");
9163          (void)fprintf( fd,
9164              "-Pos   days  Beg End  Date begin Date end    Expand   A.E.T.   Error\n");
9165          (void)fprintf( fd,
9166              "---- ------- --- ---  ---------- ----------  -----    -----  --------\n");
9167       }
9168       else {
9169          (void)fprintf( fd,
9170              "Line  Week-  Interval                        Sched/   Macro/\n");
9171          (void)fprintf( fd,
9172              "-Pos   days  Beg End  Date begin Date end    Expand   A.E.T.\n");
9173          (void)fprintf( fd,
9174              "---- ------- --- ---  ---------- ----------  -----    ------\n");
9175       }
9176    }
9177 
9178 
9179    /* Isolate a group of events to print */
9180    line = event[0].line;
9181    pos = event[0].pos;
9182    sflag = event[0].flag;
9183    j1 = j2 = 0;
9184 
9185    while ( j1 < nevents ) {
9186       while ( j2 < nevents &&
9187               event[j2].line == line  &&
9188               event[j2].pos  == pos   &&
9189               event[j2].flag == sflag     ) {
9190          j2++;
9191       }
9192 
9193       /* Find the original tevent(s)/timer(s) which gave birth */
9194       /* to this event and display them.                       */
9195 
9196       ncomb = 0;
9197       j = event[j1].tevent;
9198       count = teventp[j].print;
9199       while ( count > 0 && j >= 0 ) {
9200          if ( teventp[j].generation == 0 ) {
9201             teventp[j].flag |= ACTIVE_EVENT;
9202          }
9203          if ( teventp[j].flag & PRT_EVENT ) {
9204             prtlist[ncomb++] = j;
9205             count--;
9206          }
9207          j = teventp[j].plink;
9208       }
9209 
9210       ddoptions = 0;
9211       for ( k = ncomb - 1; k >= 0; k-- ) {
9212          m = prtlist[k];
9213          j = teventp[m].timer;
9214 
9215          macro = teventp[m].macro;
9216          flag = teventp[m].flag;
9217          offset = teventp[m].offset;
9218          pos = teventp[m].pos;
9219          secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
9220          line = timerp[j].line_no;
9221 	 ddoptions |= timerp[j].ddoptions;
9222 
9223          if ( /* teventp[m].*/ flag & COMB_EVENT )
9224             (void)fprintf( fd, " **  ");
9225          else
9226             (void)fprintf(fd, "%02d-%d ", line, pos);
9227 
9228          if ( timerp[j].notify >= 0 ) {
9229             (void)fprintf( fd, "%s          expire-%-3d      ",
9230                bmap2dow(timerp[j].dow_bmap), timerp[j].notify);
9231          }
9232          else {
9233             sched_end = timerp[j].sched_end;
9234             if ( sched_end > 1231 ) {
9235                /* Remove the kluge for reversed date range */
9236                sched_end -= 1200;
9237             }
9238             (void)fprintf( fd, "%s          %02d/%02d      %02d/%02d",
9239                   bmap2dow(timerp[j].dow_bmap),
9240                   timerp[j].sched_beg / 100, timerp[j].sched_beg % 100,
9241                   sched_end / 100, sched_end % 100);
9242          }
9243 
9244          if ( flag & CLOCK_EVENT ) {
9245             (void)fprintf( fd, "       %02d:%02d%c   %s%s\n",
9246                offset / 60, offset % 60, secur, macrop[macro].label,
9247                display_timer_options(timerp, j));
9248          }
9249          else if ( flag & DAWN_EVENT ) {
9250             (void)sprintf(minibuf, "       dawn%+d%c", offset, secur);
9251             (void)fprintf( fd, "%-15s %s%s\n", minibuf, macrop[macro].label,
9252                display_timer_options(timerp, j));
9253          }
9254          else if ( flag & DUSK_EVENT ) {
9255             (void)sprintf(minibuf, "       dusk%+d%c", offset, secur);
9256             (void)fprintf( fd, "%-15s %s%s\n", minibuf, macrop[macro].label,
9257                display_timer_options(timerp, j));
9258          }
9259          else {
9260             (void)fprintf( fd, "\n");
9261          }
9262          m = teventp[m].link;
9263       }
9264 
9265       /* Now display events as expanded by heyu. */
9266 
9267       endprev = 0;
9268       for ( k = j1; k < j2; k++ ) {
9269          j = event[k].timer;
9270          if ( event[k].tpos == 1 ) {
9271             offset = timerp[j].offset_start;
9272             flag   = timerp[j].flag_start;
9273             secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
9274             secnom = flag & SEC_EVENT ? SECURITY_OFFSET_ADJUST : 0;
9275             macro  = timerp[j].macro_start;
9276             error  = timerp[j].error_start;
9277          }
9278          else {
9279             offset = timerp[j].offset_stop;
9280             flag   = timerp[j].flag_stop;
9281             secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
9282             secnom = flag & SEC_EVENT ? SECURITY_OFFSET_ADJUST : 0;
9283             macro  = timerp[j].macro_stop;
9284             error  = timerp[j].error_stop;
9285          }
9286          if ( flag & SEC_EVENT )
9287             (void)strncpy2(note, "s", sizeof(note) - 1);
9288          else
9289             note[0] = '\0';
9290 
9291          if ( flag & SUBST_EVENT )
9292             (void)strncpy2(note, " r", sizeof(note) - 1);
9293 
9294          /* Display blank line if there is a gap in dates */
9295 	 /* as the result of timer options.               */
9296          if ( k > j1 && ddoptions && timerp[j].resolv_beg != (endprev + 1))
9297             (void)fprintf( fd, "\n");
9298          endprev = timerp[j].resolv_end;
9299 
9300          (void)fprintf( fd, "     %s %03d-%03d",
9301             bmap2dow(timerp[j].dow_bmap),
9302             timerp[j].resolv_beg, timerp[j].resolv_end);
9303 
9304 
9305          /* Determine the actual event execution legal time */
9306          legal = offset + macrop[macro].delay + secnom;
9307 
9308          tadj  = time_adjust(timerp[j].resolv_beg + dayzero, legal, STD2LGL);
9309          if ( time_adjust(timerp[j].resolv_end + dayzero, legal, STD2LGL) != tadj )
9310             (void)strncat(note, "*", sizeof(note) - 1 - strlen(note));
9311          legal += tadj;
9312          if ( legal < 0 ) {
9313             legal += 1440;
9314             shift = '<';
9315          }
9316          else if ( legal > 1439 ) {
9317             legal -= 1440;
9318             shift = '>';
9319          }
9320          else {
9321             shift = ' ';
9322          }
9323 
9324          yday2date(jan1day, timerp[j].resolv_beg + dayzero,
9325                                           &year, &month, &day, &wday);
9326          (void)fprintf( fd, "  %02d/%02d/%04d-", month, day, year);
9327          yday2date(jan1day, timerp[j].resolv_end + dayzero,
9328                                            &year, &month, &day, &wday);
9329          (void)fprintf( fd, "%02d/%02d/%04d", month, day, year);
9330          if ( flag & (DAWN_EVENT | DUSK_EVENT) )
9331             (void)fprintf( fd,
9332               "  %02d:%02d%c  %c%02d:%02d%-4s[%2d]\n",
9333                 offset / 60, offset % 60, secur, shift, legal / 60, legal % 60, note, error);
9334          else
9335             (void)fprintf( fd,
9336               "  %02d:%02d%c  %c%02d:%02d%-4s\n",
9337                 offset / 60, offset % 60, secur, shift, legal / 60, legal % 60, note);
9338       }
9339 
9340       (void)fprintf( fd, "\n");
9341       j1 = j2 ;
9342       line = event[j1].line;
9343       pos  = event[j1].pos;
9344       sflag = event[j1].flag;
9345    }
9346 
9347    /* Display LOST events */
9348    (void)fprintf( fd, "TIMED EVENTS day-shifted out of this period.\n");
9349 
9350    j = startchain; nevents = 0;
9351    while ( j >= 0 ) {
9352       if ( teventp[j].flag & LOST_EVENT )
9353          prtlist[nevents++] = j;
9354       j = teventp[j].link;
9355    }
9356 
9357    if ( nevents == 0 ) {
9358       (void)fprintf( fd, "-- None --\n\n");
9359    }
9360    else {
9361       (void)fprintf( fd,
9362           "Line  Wdays  Beg End  Date begin Date end    Time     Macro\n");
9363       (void)fprintf( fd,
9364           "---- ------- --- ---  ---------- ----------  -----    -----\n");
9365 
9366       for ( k = 0; k < nevents; k++ ) {
9367          j = prtlist[k];
9368          lostday = teventp[j].lostday;
9369          yday2date(jan1day, lostday + dayzero , &year, &month, &day, &wday);
9370 
9371          if ( teventp[j].flag & COMB_EVENT )
9372             (void)fprintf( fd, " **  ");
9373          else
9374             (void)fprintf( fd, "%02d-%d ", teventp[j].line_no, teventp[j].pos);
9375 
9376          (void)fprintf( fd, "%s %03d-%03d  %02d/%02d/%4d-%02d/%02d/%4d  %02d:%02d%c   %s\n",
9377             bmap2dow(teventp[j].dow_bmap), lostday, lostday,
9378             month, day, year, month, day, year,
9379             teventp[j].offset / 60, teventp[j].offset % 60,
9380             sec_flag[teventp[j].flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0],
9381             macrop[teventp[j].macro].label);
9382       }
9383       (void)fprintf( fd, "\n\n");
9384    }
9385 
9386 
9387    /* Display CANCELLED events */
9388    (void)fprintf( fd, "TIMED EVENTS skipped per Timer options.\n");
9389 
9390    j = startchain; nevents = 0;
9391    while ( j >= 0 ) {
9392       if ( teventp[j].flag & CANCEL_EVENT )
9393          prtlist[nevents++] = j;
9394       j = teventp[j].link;
9395    }
9396 
9397    if ( nevents == 0 ) {
9398       (void)fprintf( fd, "-- None --\n\n");
9399    }
9400    else {
9401       (void)fprintf( fd,
9402           "Line  Wdays  Beg End  Date begin Date end    StdTime  Macro\n");
9403       (void)fprintf( fd,
9404           "---- ------- --- ---  ---------- ----------  -------  -----\n");
9405 
9406       for ( k = 0; k < nevents; k++ ) {
9407          j = prtlist[k];
9408          beg    = teventp[j].resolv_beg;
9409          end    = teventp[j].resolv_end;
9410          offset = teventp[j].offset;
9411          macro  = teventp[j].macro;
9412          flag   = teventp[j].flag;
9413          secur = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
9414 
9415          yday2date(jan1day, beg + dayzero, &year, &month, &day, &wday);
9416          yday2date(jan1day, end + dayzero, &year2, &month2, &day2, &wday);
9417 
9418          if ( teventp[j].flag & COMB_EVENT )
9419             (void)fprintf( fd, " **  ");
9420          else
9421             (void)fprintf( fd, "%02d-%d ", teventp[j].line_no, teventp[j].pos);
9422 
9423          (void)fprintf( fd, "%s %03d-%03d  %02d/%02d/%4d-%02d/%02d/%4d",
9424             bmap2dow(teventp[j].dow_bmap), beg, end,
9425             month, day, year, month2, day2, year2);
9426 
9427 
9428          if ( flag & CLOCK_EVENT ) {
9429             (void)sprintf(minibuf, "  %02d:%02d%c",
9430                offset / 60, offset % 60, secur);
9431             (void)fprintf( fd, "%-10s %s\n", minibuf, macrop[macro].label);
9432          }
9433          else if ( flag & DAWN_EVENT ) {
9434             (void)sprintf(minibuf, "  dawn%+d%c", offset, secur);
9435             (void)fprintf( fd, "%-10s %s\n", minibuf, macrop[macro].label);
9436          }
9437          else if ( flag & DUSK_EVENT ) {
9438             (void)sprintf(minibuf, "  dusk%+d%c", offset, secur);
9439             (void)fprintf( fd, "%-10s %s\n", minibuf, macrop[macro].label);
9440          }
9441          else {
9442             (void)fprintf( fd, "\n");
9443 	 }
9444 
9445       }
9446       (void)fprintf( fd, "\n\n");
9447    }
9448 
9449 
9450    /* Display the tevents not included in this period */
9451    (void)fprintf( fd, "TIMED EVENTS from schedule not active during this period.\n");
9452 
9453    j = startchain; nevents = 0;
9454    while ( j >= 0 ) {
9455       if ( teventp[j].generation == 0 && (teventp[j].flag & ACTIVE_EVENT) == 0 )
9456          prtlist[nevents++] = j;
9457       j = teventp[j].link;
9458    }
9459 
9460    if ( nevents == 0 ) {
9461       (void)fprintf( fd, "-- None --\n\n");
9462       return retcode;
9463    }
9464    else {
9465       (void)fprintf( fd,
9466           "Line  Wdays  Beg End  Date begin Date end    Time     Macro\n");
9467       (void)fprintf( fd,
9468           "---- ------- --- ---  ---------- ----------  -----    -----\n");
9469 
9470       for ( k = 0; k < nevents; k++ ) {
9471          m = prtlist[k];
9472          teventp[m].flag &= ~PRT_EVENT;
9473          j = teventp[m].timer;
9474 
9475          macro = teventp[m].macro;
9476          if ( teventp[m].pos == 1 ) {
9477             offset = timerp[j].offset_start;
9478             flag   = timerp[j].flag_start;
9479             secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
9480             line   = timerp[j].line_no;
9481             pos    = 1;
9482          }
9483          else {
9484             offset = timerp[j].offset_stop;
9485             flag   = timerp[j].flag_stop;
9486             secur  = sec_flag[flag & SEC_EVENT ? SECURITY_ON & 0x03 : 0];
9487             line   = timerp[j].line_no;
9488             pos    = 2;
9489          }
9490 
9491          if ( teventp[m].flag & COMB_EVENT )
9492             (void)fprintf( fd, " **  ");
9493          else
9494             (void)fprintf(fd, "%02d-%d ", line, pos);
9495 
9496          if ( timerp[j].notify >= 0 ) {
9497             (void)fprintf( fd, "%s          expire-%-3d      ",
9498                bmap2dow(timerp[j].dow_bmap), timerp[j].notify);
9499          }
9500          else {
9501             sched_end = timerp[j].sched_end;
9502             if ( sched_end > 1231 ) {
9503                /* Remove the kluge for reversed date range */
9504                sched_end -= 1200;
9505             }
9506             (void)fprintf( fd, "%s          %02d/%02d      %02d/%02d",
9507                   bmap2dow(timerp[j].dow_bmap),
9508                   timerp[j].sched_beg / 100, timerp[j].sched_beg % 100,
9509                   sched_end / 100, sched_end % 100);
9510          }
9511          if ( flag & CLOCK_EVENT ) {
9512             (void)fprintf( fd, "       %02d:%02d%c   %s\n",
9513                offset / 60, offset % 60, secur, macrop[macro].label);
9514          }
9515          else if ( flag & DAWN_EVENT ) {
9516             (void)fprintf( fd,
9517             "       dawn%-+3d%c %s\n", offset, secur, macrop[macro].label);
9518          }
9519          else if ( flag & DUSK_EVENT ) {
9520             (void)fprintf( fd,
9521             "       dusk%-+3d%c %s\n", offset, secur, macrop[macro].label);
9522          }
9523          else {
9524             (void)fprintf( fd, "\n");
9525          }
9526       }
9527    }
9528 
9529    return retcode;
9530 }
9531 #endif
9532 
9533 /*---------------------------------------------------------------------+
9534  | Display timers for final_report()                                   |
9535  | Also check for stop time = start time error and return 1 if so,     |
9536  | otherwise return 0 if OK.                                           |
9537  +---------------------------------------------------------------------*/
display_timers(FILE * fd,TIMER * timerp,TEVENT * teventp,MACRO * macrop)9538 int display_timers( FILE *fd, TIMER *timerp, TEVENT *teventp, MACRO *macrop )
9539 {
9540    extern int current_timer_generation;
9541    int        j, maxlabel, status;
9542    char       *sec_flag = " sss";
9543 
9544    if ( !timerp ) {
9545       (void)fprintf( fd, "\n");
9546       return 0;
9547    }
9548 
9549    /* Get maximum length of "Start" macro labels used in the timers */
9550    maxlabel = 0;
9551    j = 0;
9552    while ( timerp[j].line_no > 0 ) {
9553       if ( timerp[j].generation == current_timer_generation ) {
9554          maxlabel = max(maxlabel, (int)strlen(macrop[timerp[j].macro_start].label));
9555       }
9556       j++;
9557    }
9558 
9559    (void) fprintf( fd,
9560        "OUTPUT TIMERS - Times in minutes after midnight Standard Time\n-------------\n");
9561    (void) fprintf( fd,
9562        "(Codes: C = Clock, R = Dawn, S = Dusk, X = null, s = Security)\n");
9563    if ( configp->display_offset == YES )
9564       (void) fprintf( fd, "Loc  ");
9565    (void) fprintf( fd,
9566       "Line Line   WkDay   Beg End  F Time   F Time   %-*s  Macro\n",
9567            maxlabel, "Macro");
9568    if ( configp->display_offset == YES )
9569       (void) fprintf( fd, "---  ");
9570    (void) fprintf( fd,
9571       "---- ----  -------  --- ---  - ----   - ----   %-*s  -----\n",
9572            maxlabel, "-----");
9573 
9574 
9575    j = 0;
9576    status = 0;
9577    while ( timerp[j].line_no > 0 ) {
9578       if ( timerp[j].generation != current_timer_generation ) {
9579          j++;
9580          continue;
9581       }
9582 
9583       /* Display the offset of the timer in the memory image */
9584       if ( configp->display_offset == YES )
9585          (void) fprintf( fd, "%03x  ", timerp[j].memloc);
9586 
9587       /* Override line number and position for a combined event because */
9588       /* it's not valid anyway; otherwise display it.                   */
9589 
9590       if ( teventp[timerp[j].tevent_start].flag & COMB_EVENT )
9591          (void) fprintf( fd, " **  " );
9592       else
9593         (void) fprintf( fd, "%02d-%1d ",timerp[j].line1, timerp[j].pos1);
9594 
9595       if ( timerp[j].flag_stop == NO_EVENT )
9596          (void) fprintf( fd, "----  " );
9597       else if ( teventp[timerp[j].tevent_stop].flag & COMB_EVENT )
9598          (void) fprintf( fd, " **   " );
9599       else
9600         (void) fprintf( fd, "%02d-%1d  ",timerp[j].line2, timerp[j].pos2);
9601 
9602       /* Display the rest of the data. */
9603 
9604       (void) fprintf( fd, "%7s  %03d-%03d  %c %04d%c  %c %04d%c  %-*s  %s\n",
9605          bmap2dow(timerp[j].dow_bmap),
9606          timerp[j].resolv_beg, timerp[j].resolv_end,
9607          flag_def(timerp[j].flag_start & TIME_EVENTS),
9608          timerp[j].offset_start,
9609          sec_flag[timerp[j].flag_start & SEC_EVENT ? SECURITY_ON & 0x03 : 0],
9610          flag_def(timerp[j].flag_stop & TIME_EVENTS),
9611          timerp[j].offset_stop,
9612          sec_flag[timerp[j].flag_stop & SEC_EVENT ? SECURITY_ON & 0x03 : 0],
9613          maxlabel,
9614          macrop[timerp[j].macro_start].label,
9615          macrop[timerp[j].macro_stop].label);
9616 
9617       if ( timerp[j].offset_stop == timerp[j].offset_start ) {
9618          fprintf(fd, "***** Internal error: stop time = start time *****\n");
9619          fprintf(fd, "      CM11A bug! - will not launch macro '%s'\n",
9620             macrop[timerp[j].macro_stop].label);
9621          status = 1;
9622       }
9623 
9624       j++;
9625    }
9626    (void)fprintf( fd, "\n");
9627    return status;
9628 }
9629 
9630 
9631 /*---------------------------------------------------------------------+
9632  | Generate a final report of Timers, Triggers, and Macros in use.     |
9633  +---------------------------------------------------------------------*/
final_report(char * pathname,CALEND * calendp,TIMER * timerp,TEVENT * teventp,TRIGGER * triggerp,MACRO * macrop,unsigned char * elementp,int proc_code)9634 int final_report ( char *pathname, CALEND *calendp, TIMER *timerp,
9635    TEVENT *teventp, TRIGGER *triggerp, MACRO *macrop,
9636    unsigned char *elementp, int proc_code)
9637 {
9638    FILE          *fd;
9639    int           j;
9640    int           original, current;
9641    int           status;
9642 
9643    static char   *off_on[] = {"off", "on "};
9644 
9645    original = 0;
9646    current  = update_current_timer_generation();
9647 
9648    if ( !(fd = fopen(pathname, "w")) ) {
9649       (void) fprintf(stderr, "Unable to open report file '%s' for writing.\n",
9650                  pathname);
9651       return -1;
9652    }
9653 
9654    (void)fprintf( fd, "Report of expanded Timers, Triggers, and Macros\n");
9655    (void)fprintf( fd, "===============================================\n");
9656 
9657    /* Display Config & Schedule files and the Date and Time created */
9658    (void)fprintf( fd, "Version: %s\n", VERSION);
9659    (void)fprintf( fd, "Config File:   %s\n", pathspec(heyu_config));
9660    (void)fprintf( fd, "Schedule File: %s\n", schedfile);
9661    (void)fprintf( fd, "Report Date: %s\n\n", legal_time_string());
9662 
9663    (void)fprintf( fd, "Upload schedule to the interface: %s\n\n",
9664       (proc_code == PROC_UPLOAD) ? "YES" : "NO" );
9665 
9666    display_config_overrides( fd );
9667 
9668    if ( calendp->asif_flag & (ASIF_DATE | ASIF_TIME) )
9669       (void)fprintf( fd, "Simulation as if %s\n", asif_time_string());
9670 
9671    if ( configp->mode == HEYU_MODE )
9672      (void)fprintf( fd, "Configured for HEYU mode; Programmed for %d Days.\n\n",
9673           configp->program_days);
9674    else
9675      (void)fprintf( fd, "Configured for COMPATIBLE mode.\n\n");
9676 
9677    (void)fprintf( fd, "Dawn/Dusk defined as ");
9678    (void)fprintf( fd,
9679        ((configp->sunmode == RiseSet) ? "Sunrise/Sunset\n\n" :
9680 	(configp->sunmode == CivilTwi) ? "Civil Twilight\n\n" :
9681 	(configp->sunmode == NautTwi) ? "Nautical Twilight\n\n" :
9682 	(configp->sunmode == AstroTwi) ? "Astronomical Twilight\n\n" :
9683 	(configp->sunmode == AngleOffset) ? "Sun centre at %d angle minutes below horizon\n\n" : "???"),
9684 	 configp->sunmode_offset);
9685 
9686    (void) fprintf( fd, "Actual Event Times (A.E.T.) include macro delay, if any.\n");
9687    (void) fprintf( fd, "Schedule Times and A.E.T. are Civil Time.\n");
9688 
9689    (void) fprintf( fd, "Expanded times are Standard Time.\n\n");
9690 
9691    (void) fprintf( fd, "Symbols used:\n");
9692    (void) fprintf( fd, "  **  Combined event.\n");
9693    (void) fprintf( fd, "  *   Time change during interval.\n");
9694    (void) fprintf( fd, "  >/< Next/Previous day.\n");
9695    (void) fprintf( fd, "  r   Heyu-generated random initial security day.\n\n");
9696 
9697    /* Display individual timed events included in timers */
9698 
9699    display_events( fd, timerp, teventp, macrop, calendp );
9700 
9701    (void)fprintf( fd, "\n");
9702 
9703    /* Display the uploaded timers */
9704 
9705    status = display_timers( fd, timerp, teventp, macrop );
9706 
9707    (void)fprintf( fd, "\n");
9708 
9709    /* Display triggers */
9710 
9711    (void) fprintf( fd, "TRIGGERS\n--------\n");
9712    j = 0;
9713    while ( triggerp && triggerp[j].line_no > 0 ) {
9714       if ( configp->display_offset == YES )
9715          (void) fprintf( fd, "%03x ", triggerp[j].memloc);
9716 
9717       (void) fprintf( fd, "trigger  %c%-2d %3s  %s\n",
9718             code2hc(triggerp[j].housecode),
9719             code2unit(triggerp[j].unitcode),
9720             off_on[triggerp[j].command],
9721             macrop[triggerp[j].macro].label);
9722       j++;
9723    }
9724 
9725    if ( triggerp == NULL )
9726       (void) fprintf( fd, "-- None --\n");
9727 
9728    (void) fprintf( fd, "\n");
9729 
9730    /* Display uploaded macros */
9731 
9732    (void) fprintf( fd,
9733      "\nMACROS uploaded  ( * denotes modified delay and/or compression)\n---------------\n");
9734    (void) display_macros( fd, USED, macrop, elementp );
9735 
9736 
9737    /* Display unused macros */
9738 
9739    (void)fprintf(fd,"\nMACROS unused/combined and omitted from upload\n");
9740    (void)fprintf(fd,"----------------------------------------------\n");
9741 
9742    if ( !display_macros( fd, NOTUSED, macrop, elementp ) )
9743       (void)fprintf( fd, "-- None --\n");
9744 
9745    display_eeprom_usage( fd, timerp, triggerp, macrop );
9746 
9747    fclose( fd );
9748 
9749    return status;
9750 }
9751 
9752 
9753 /*---------------------------------------------------------------------+
9754  | Store in global variable heyu_path the directory containing the     |
9755  | heyu configuration file and check if this directory is writable.    |
9756  | Store the actual configuration filename in heyu_configp->             |
9757  | Set flag is_writable if heyu_path is writable.                      |
9758  +---------------------------------------------------------------------*/
find_heyu_path(void)9759 void find_heyu_path( void )
9760 {
9761    extern int  is_writable;
9762    extern int  verbose;
9763    extern int  i_am_relay;
9764 
9765    FILE   *fd;
9766    char   *sp, *sp1;
9767    int    pid;
9768    char   subdir[PATH_LEN + 1];
9769    char   tmpfile[PATH_LEN + 1];
9770    static char envconf[PATH_LEN + 1];
9771    struct stat statbuf;
9772 
9773    int    ignoret;
9774 
9775    /* Find the configuration file. */
9776 
9777    for (;;) {
9778       /* Look for command line option or X10CONFIG environment variable */
9779       if ( (sp = optptr->configp) || (!optptr->subp && (sp = getenv("X10CONFIG"))) ) {
9780          if ( (sp1 = strrchr(sp, '/')) == NULL ) {
9781 	    (void)strncpy2(heyu_path, "./", sizeof(heyu_path) - 1);
9782 	    (void)strncpy2(heyu_config, sp, sizeof(heyu_config) - 1);
9783          }
9784          else {
9785 	    sp1++;
9786 	    (void)strncpy2(heyu_path, sp, sp1 - sp);
9787 	    (void)strncpy2(heyu_config, sp1, sizeof(heyu_config) - 1);
9788          }
9789 	 if ( !(*heyu_config) ) {
9790 	    (void)fprintf(stderr, "No filename specified for '%s'\n", sp);
9791 	    exit(1);
9792 	 }
9793 
9794          /* Verify there is a readable config file */
9795          if ( verbose && i_am_relay != 1 )
9796             (void)printf("Searching for '%s'\n", pathspec(heyu_config));
9797 	 if ( stat(pathspec(heyu_config), &statbuf) != 0 ||
9798 	                       !(statbuf.st_mode & S_IFREG) ) {
9799 	    (void)fprintf(stderr,
9800 		"Bad configuration file '%s' specified.\n",
9801 		        pathspec(heyu_config));
9802 	    exit(1);
9803 	 }
9804 
9805          if ( (fd = fopen(pathspec(heyu_config), "r")) ) {
9806             (void)fclose(fd);
9807 	    if ( verbose && i_am_relay != 1 )
9808 	       (void)printf("Found configuration file '%s'\n",
9809 			       pathspec(heyu_config) );
9810             break;
9811          }
9812          else {
9813             (void)fprintf(stderr,
9814                "Unable to find (or open) Heyu configuration file '%s'\n",
9815 	                       pathspec(heyu_config) );
9816             exit(1);
9817          }
9818       }
9819 
9820       /* Next check for subdirectory command line option */
9821       /* (-0 ... -9) or HEYUSUB  environment variable.   */
9822       if ( (sp = optptr->subp) ) {
9823          sprintf(subdir, "%s%s/", HEYUSUB_PREFIX, sp);
9824       }
9825       else if ( (sp = getenv("HEYUSUB")) ) {
9826          /* Remove leading / if any */
9827          if ( *sp == '/' )
9828             (void)strncpy2(subdir, sp + 1, sizeof(subdir) - 1);
9829          else
9830             (void)strncpy2(subdir, sp, sizeof(subdir) - 1);
9831 
9832          if ( *(sp = subdir + (strlen(subdir) - 1)) != '/' )
9833             (void)strncat(subdir, "/", sizeof(subdir) - 1 - strlen(subdir));
9834       }
9835       else {
9836          *subdir = '\0';
9837       }
9838 
9839       /* Look in the user's $HOME/HOMEBASEDIR/ directory */
9840       if ( (sp = getenv("HOME")) ) {
9841          (void)strncpy2(heyu_path, sp, sizeof(heyu_path) - 1);
9842          if ( *(sp = heyu_path + strlen(heyu_path) - 1) != '/')
9843             (void)strncat(heyu_path, "/", sizeof(heyu_path) - 1 - strlen(heyu_path));
9844          (void)strncat(heyu_path, HOMEBASEDIR, sizeof(heyu_path) - 1 - strlen(heyu_path));
9845          (void)strncat(heyu_path, subdir, sizeof(heyu_path) - 1 - strlen(heyu_path));
9846 
9847          /* Verify there is a readable config file */
9848          if ( verbose && i_am_relay != 1 )
9849             (void)printf("Searching for '%s'\n", pathspec(CONFIG_FILE));
9850 
9851          if ( (fd = fopen(pathspec(CONFIG_FILE), "r")) ) {
9852             (void)fclose(fd);
9853             (void)strncpy2(heyu_config, CONFIG_FILE, sizeof(heyu_config) - 1);
9854             break;
9855          }
9856       }
9857 
9858       /* Next look in SYSBASEDIR directory (or subdirectory thereof) */
9859       (void)sprintf(heyu_path, "%s/%s", SYSBASEDIR, subdir);
9860 
9861       /* Verify there is a readable config file */
9862       if ( verbose && i_am_relay != 1 )
9863          (void)printf("Searching for '%s'\n", pathspec(CONFIG_FILE_ETC));
9864       if ( (fd = fopen(pathspec(CONFIG_FILE_ETC), "r")) ) {
9865          (void)fclose(fd);
9866          (void)strncpy2(heyu_config, CONFIG_FILE_ETC, sizeof(heyu_config) - 1);
9867          break;
9868       }
9869       else if ( !i_am_relay ) {
9870          (void)fprintf(stderr, "Unable to find Heyu configuration file.\n"),
9871          exit(1);
9872       }
9873    }
9874 
9875    if ( verbose && i_am_relay != 1 )
9876       (void)printf("Found configuration file '%s'\n", pathspec(heyu_config));
9877 
9878    snprintf(envconf, sizeof(envconf) - 1, "X10CONFIG=%s", pathspec(heyu_config));
9879    putenv(envconf);
9880 
9881 
9882    /* Verify the heyu_path directory is readable and writable by  */
9883    /* creating and writing to a temporary file, then reading back */
9884    /* what we wrote.                                              */
9885 
9886    sprintf(tmpfile, "heyu.%d.tmp", (int)getpid());
9887    if ( (fd = fopen(pathspec(tmpfile), "w")) ) {
9888       fprintf(fd, "%d\n", (int)getpid());
9889       fclose(fd);
9890    }
9891    else {
9892       is_writable = 0;
9893       (void)fprintf(stderr, "Unable to write in Heyu directory '%s'\n", heyu_path);
9894       return;
9895    }
9896    if ( (fd = fopen(pathspec(tmpfile), "r")) ) {
9897       ignoret = fscanf(fd, "%d\n", &pid);
9898       fclose(fd);
9899       if ( pid == getpid() ) {
9900          remove(pathspec(tmpfile));
9901          is_writable = 1;
9902          if ( verbose && i_am_relay != 1 )
9903             (void)printf("Heyu directory %s is writable.\n", heyu_path);
9904 
9905          return;
9906       }
9907    }
9908    else {
9909       fprintf(stderr, "Read error while testing Heyu directory writeability\n");
9910       remove(pathspec(tmpfile));
9911       is_writable = 0;
9912    }
9913 
9914    return;
9915 }
9916 
9917 /*----------------------------------------------------------------------------+
9918  | Compress the elements in a macro by merging the unit bmaps for commands    |
9919  | which are otherwise similar for the same housecodes and removing           |
9920  | duplicate commands, e.g.:                                                  |
9921  |  A1 ON; B1 OFF; A2 ON --> A1,2 ON; B1 OFF                                  |
9922  |  A1 ON; A1 OFF; A1 ON --> A1 ON; A1 OFF                                    |
9923  |                                                                            |
9924  | Element ordering is otherwise preserved to the extent possible.            |
9925  |                                                                            |
9926  | Commands like DIM are merged only if they have the same "data", e.g. the   |
9927  | same dim values.                                                           |
9928  +----------------------------------------------------------------------------*/
compress_elements(unsigned char * elemlist,int * nelem,int * total)9929 int compress_elements ( unsigned char *elemlist, int *nelem, int *total )
9930 {
9931    int      j, k, m, matched;
9932    int      indxj, lenj, unitj, cmdlen, outelem, ntot;
9933    int      *index, *length, *done;
9934 
9935    unsigned char hcfunj, cmdcode;
9936    unsigned char *outbuff;
9937    static int sizint = sizeof(int);
9938    static int sizuchr = sizeof(unsigned char);
9939 
9940    index   = calloc( *total, sizint );
9941    length  = calloc( *total, sizint );
9942    done    = calloc( *total, sizint );
9943    outbuff = calloc( *total, sizuchr );
9944 
9945    if ( index == NULL || length == NULL || done == NULL || outbuff == NULL ) {
9946       fprintf(stderr, "compress_elements() - Unable to allocate memory.\n");
9947       exit(1);
9948    }
9949 
9950    j = 0; k = 0;
9951    while ( j < *total ) {
9952       index[k] = j;
9953       done[k] = 0;
9954       cmdcode = elemlist[j] & 0x0fu;
9955 
9956       /* Find the length of the macro element in the master table */
9957       if ( (cmdlen = macro_element_length(cmdcode)) <= 0 ) {
9958          (void)fprintf(stderr,
9959             "compress_elements(): Internal table error.\n");
9960          return 0;
9961       }
9962 
9963       j += length[k] = cmdlen;
9964       k++;
9965    }
9966    if ( k != *nelem )  {
9967       (void)fprintf(stderr,
9968          "compress_elements(): Mismatch in number of elements.\n");
9969       return 0;
9970    }
9971 
9972    ntot = 0; outelem = 0;
9973    for ( j = 0; j < *nelem; j++ ) {
9974       if ( done[j] )
9975          continue;
9976 
9977       done[j] = 1;
9978       indxj = index[j];
9979       hcfunj = elemlist[indxj];
9980       lenj = length[j];
9981       unitj = ntot + 1;
9982       for ( k = indxj; k < indxj + lenj; k++ )
9983          outbuff[ntot++] = elemlist[k];
9984       outelem++;
9985 
9986       for ( k = j + 1; k < *nelem; k++ ) {
9987          if ( done[k] )
9988             continue;
9989          if ( hcfunj == elemlist[index[k]] && lenj == length[k] ) {
9990             /* Compare the element data bytes */
9991             matched = 1;
9992             for ( m = 3; m < lenj; m++ ) {
9993                if ( elemlist[indxj + m] != elemlist[index[k] + m] ) {
9994                   matched = 0;
9995                   break;
9996                }
9997             }
9998             if ( matched ) {
9999                /* Combine the unit bmap */
10000                outbuff[unitj] |= elemlist[index[k] + 1];
10001                outbuff[unitj + 1] |= elemlist[index[k] + 2];
10002                done[k] = 1;
10003             }
10004          }
10005       }
10006    }
10007 
10008    for ( j = 0; j < ntot; j++ )
10009       elemlist[j] = outbuff[j];
10010 
10011    *nelem = outelem;
10012    *total = ntot;
10013 
10014    free(index);
10015    free(length);
10016    free(done);
10017    free(outbuff);
10018 
10019    return 1;
10020 }
10021 
10022 /*----------------------------------------------------------------------------+
10023  | Compress the elements in all macros.                                       |
10024  +----------------------------------------------------------------------------*/
compress_macros(MACRO * macrop,unsigned char * elementp)10025 void compress_macros ( MACRO *macrop, unsigned char *elementp )
10026 {
10027    int      j, oldtotal;
10028    unsigned char *ptr;
10029 
10030    j = 1;
10031    while ( macrop[j].line_no > 0 ) {
10032       oldtotal = macrop[j].total;
10033       ptr = &elementp[macrop[j].element];
10034       if ( !compress_elements(ptr, &macrop[j].nelem, &macrop[j].total) )
10035          exit(1);
10036       if ( macrop[j].total < oldtotal )
10037          macrop[j].modflag |= COMPRESSED;
10038       j++;
10039    }
10040 
10041    return;
10042 }
10043 
10044 /*---------------------------------------------------------------------+
10045  | Read a 1024 byte file containing a CM11a memory image and upload    |
10046  | it to the CM11a interface.                                          |
10047  +---------------------------------------------------------------------*/
upload_image_from_file(char * pathspec)10048 int upload_image_from_file ( char *pathspec )
10049 {
10050    FILE          *fd;
10051    int           size;
10052    unsigned char prommap[2 * PROMSIZE];
10053    extern void   upload_eeprom_image(unsigned char *);
10054 
10055    if ( !(fd = fopen(pathspec, "r")) ) {
10056       (void)fprintf(stderr, "Unable to read file '%s'\n", pathspec);
10057       exit(1);
10058    }
10059 
10060    /* Try reading more than 1024 bytes to verify the size is correct */
10061    size = fread( (void *)prommap, 1, 2 * PROMSIZE, fd);
10062 
10063    if ( size != PROMSIZE ) {
10064       (void)fprintf(stderr, "File size is not %d bytes.\n", PROMSIZE);
10065       exit(1);
10066    }
10067 
10068    /* Upload the image to the interface */
10069 
10070    upload_eeprom_image( prommap );
10071 
10072    return 0;
10073 }
10074 
10075 
10076 /*---------------------------------------------------------------------+
10077  | Handle Heyu 'upload' command and arguments.                         |
10078  +---------------------------------------------------------------------*/
c_upload(int argc,char * argv[])10079 int c_upload ( int argc, char *argv[] )
10080 {
10081    extern  int  is_writable;
10082 
10083    int retcode;
10084 
10085    if ( invalidate_for_cm10a() != 0 )
10086       return 1;
10087 
10088    if ( argc == 2 ) {
10089       if ( !is_writable ) {
10090          (void)fprintf(stderr,
10091             "Heyu directory %s must be writable - quitting.\n", heyu_path);
10092          return 1;
10093       }
10094       retcode = process_data(PROC_UPLOAD);
10095 
10096       return retcode;
10097    }
10098 
10099    if ( argc == 3 ) {
10100       if ( strcmp("status", argv[2]) == 0 ) {
10101          display_cm11a_status(1);
10102 	 retcode = 0;
10103       }
10104       else if ( strcmp("cronstatus", argv[2]) == 0 ) {
10105          display_cm11a_status(0);
10106 	 retcode = 0;
10107       }
10108       else if ( !is_writable ) {
10109          (void)fprintf(stderr,
10110              "Heyu directory %s must be writable - quitting.\n", heyu_path);
10111          retcode = 1;
10112       }
10113       else if ( strcmp("check", strlower(argv[2])) == 0 ) {
10114          retcode = process_data(PROC_CHECK);
10115       }
10116       else if ( strcmp("croncheck", argv[2]) == 0 ) {
10117          retcode = crontest();
10118       }
10119       else {
10120          (void)fprintf(stderr, "Usage: %s %s [check|croncheck|status|cronstatus]\n",
10121             argv[0], argv[1]);
10122          retcode = 1;
10123       }
10124    }
10125    else if ( argc == 4 && strcmp("imagefile", strlower(argv[2])) == 0 ) {
10126       /* Undocumented - for experimental use only */
10127       retcode = upload_image_from_file(argv[3]);
10128    }
10129    else {
10130       (void)fprintf(stderr, "Usage: %s %s [check|croncheck|status|cronstatus]\n",
10131          argv[0], argv[1]);
10132       retcode = 1;
10133    }
10134 
10135    return retcode;
10136 }
10137 
10138 /*---------------------------------------------------------------------+
10139  | Usage messages for 'heyu utility'                                   |
10140  +---------------------------------------------------------------------*/
display_utility_usage(int argc,char * argv[])10141 void display_utility_usage( int argc, char *argv[] )
10142 {
10143    int  j = 0;
10144 
10145    char *optmsg[] = {
10146       "syscheck     Display your system calendar/clock configuration.\n",
10147       "dawndusk     Display Dawn/Dusk today, per Dawn/Dusk definition",
10148       "             in configuration file.\n",
10149       "suntable [-r|c|n|a|s|w] [yyyy]",
10150       "             Write a file of daily dawn/dusk times (by default",
10151       "             per Dawn/Dusk definition in configuration file)",
10152       "             for the current year or year yyyy.",
10153       "             Override options:",
10154       "                -r  Display Sunrise and Sunset",
10155       "                -c  Display Civil Twilights",
10156       "                -n  Display Nautical Twilights",
10157       "                -a  Display Astronomical Twilights",
10158       "             Other options:",
10159       "                -s  Display Standard Time instead of Civil Time",
10160       "                -w  Wide format.  (Printing on US letter or A4 size paper",
10161       "                    requires Landscape mode and 8-pt fixed font.)\n",
10162       "calibrate    Calibration for fast timing loops.\n",
10163    };
10164 
10165    (void)fprintf(stdout, "%s %s  options:\n", argv[0], argv[1]);
10166 
10167    for ( j = 0; j < (sizeof(optmsg)/sizeof(char *)); j++ )
10168      (void)fprintf(stdout, "   %s\n", optmsg[j]);
10169 
10170    return;
10171 }
10172 
10173 /*---------------------------------------------------------------------+
10174  | Set the various status bits described in Sec 8 of protocol.txt      |
10175  +---------------------------------------------------------------------*/
c_set_status(int argc,char * argv[])10176 int c_set_status ( int argc, char *argv[] )
10177 {
10178    extern int c_set_status_bits( unsigned char );
10179 
10180    if ( strcmp("newbattery", strlower(argv[1])) == 0 ) {
10181       return c_set_status_bits( RESET_BATTERY_TIMER );
10182    }
10183    else if ( strcmp("purge", strlower(argv[1])) == 0 ) {
10184       return c_set_status_bits( PURGE_DELAYED_MACROS );
10185    }
10186    else if ( strcmp("clear", strlower(argv[1])) == 0 ) {
10187       return c_set_status_bits( MONITORED_STATUS_CLEAR );
10188    }
10189    else if ( strcmp("reserved", strlower(argv[1])) == 0 ) {
10190       /* Undocumented */
10191       /* This one to test whether the "reserved" bit actually */
10192       /* doesn't do anything.                                 */
10193       return c_set_status_bits( RESERVED_STATUS_BIT );
10194    }
10195    return 0;
10196 }
10197 
10198 /*---------------------------------------------------------------------+
10199  | Handle Heyu 'utility' command and arguments.                        |
10200  | (There's no spool file open so commands are restricted.)            |
10201  +---------------------------------------------------------------------*/
c_utility(int argc,char * argv[])10202 int c_utility ( int argc, char *argv[] )
10203 {
10204    int           j, sunmode, offset, timemode, format, year;
10205    time_t        utc0_dawn, utc0_dusk;
10206    time_t        now;
10207    struct tm     *tmp;
10208    char          *sp;
10209 
10210    static char   *sunmodelabel[] = {"Sunrise/Sunset", "Civil Twilight",
10211         "Nautical Twilight", "Astronomical Twilight", "Sun angle offset = %d'", };
10212 
10213    unsigned long loopcount;
10214    unsigned long loop_calibrate ( void );
10215    extern void display_env_masks ( void );
10216 
10217    if ( argc >= 3 ) {
10218       if ( strcmp("syscheck", strlower(argv[2])) == 0 ) {
10219          get_configuration(CONFIG_INIT);
10220          display_sys_calendar();
10221          return 0;
10222       }
10223       else if ( strcmp("suntable", argv[2]) == 0 ) {
10224          get_configuration(CONFIG_INIT);
10225 	 if ( configp->loc_flag != (LATITUDE | LONGITUDE) ) {
10226 	    fprintf(stderr,
10227 	       "LATITUDE and/or LONGITUDE not specified in %s\n",
10228                   pathspec(CONFIG_FILE));
10229             return 1;
10230          }
10231          now = time(NULL);
10232          year = localtime(&now)->tm_year + 1900;
10233          sunmode = configp->sunmode;
10234          offset = configp->sunmode_offset;
10235          timemode = TIMEMODE_CIVIL;
10236          format = FMT_PORTRAIT;
10237 
10238          for ( j = 3; j < argc; j++ ) {
10239             if ( *argv[j] == '-' ) {
10240 	       if ( strlen(argv[j]) >= 2 ) {
10241                   sp = argv[j] + 1;
10242                   if ( *sp == 'w' && strlen(argv[j]) == 2 ) {
10243                      format = FMT_LANDSCAPE;
10244 		     continue;
10245                   }
10246                   else if ( *sp == 's' && strlen(argv[j]) == 2 ) {
10247                      timemode = TIMEMODE_STANDARD;
10248 		     continue;
10249                   }
10250                   else if ( *sp == 'o' ) {
10251                      sunmode = AngleOffset;
10252 	             if ( strlen(argv[j]) > 2 || ( argc > j + 1 && strlen(argv[j + 1]) > 0 ) ) {
10253 		        if ( strlen(argv[j]) > 2 )
10254 		           offset = strtol(argv[j] + 2, &sp, 10);
10255 		        else
10256 		           offset = strtol(argv[++j], &sp, 10);
10257 			if (*sp == '\0')
10258 		        	continue;
10259 		     }
10260 	          }
10261 	          else if ( strlen(argv[j]) == 2 ) {
10262 	             if ( *sp == 'r' ) {
10263 		        sunmode = RiseSet;
10264                         continue;
10265                      }
10266                      else if ( *sp == 'c' ) {
10267 		        sunmode = CivilTwi;
10268                         continue;
10269                      }
10270                      else if ( *sp == 'n' ) {
10271 		        sunmode = NautTwi;
10272                         continue;
10273                      }
10274                      else if ( *sp == 'a' ) {
10275 		        sunmode = AstroTwi;
10276                         continue;
10277                      }
10278                   }
10279                }
10280                fprintf(stderr, "Invalid parameter '%s'\n", argv[j]);
10281                return 1;
10282             }
10283             else {
10284                year = (int)strtol(argv[j], &sp, 10);
10285                if ( !strchr(" /t/n", *sp) || year < 1970 || year > 2099 ) {
10286                   fprintf(stderr, "Invalid year '%s'\n", argv[argc - 1]);
10287                   return 1;
10288                }
10289             }
10290          }
10291 
10292          return write_sun_table(format, year, sunmode, offset, timemode);
10293       }
10294       else if (strcmp("dawndusk", argv[2]) == 0 ) {
10295          get_configuration(CONFIG_INIT);
10296          fix_tznames();
10297 	 if ( configp->loc_flag != (LATITUDE | LONGITUDE) ) {
10298 	    fprintf(stderr,
10299 	       "LATITUDE and/or LONGITUDE not specified in %s\n",
10300                   pathspec(CONFIG_FILE));
10301             return 1;
10302          }
10303          now = time(NULL);
10304          local_dawndusk(now, &utc0_dawn, &utc0_dusk);
10305          tmp = localtime(&utc0_dawn);
10306          printf("Dawn = %02d:%02d %s", tmp->tm_hour, tmp->tm_min, heyu_tzname[tmp->tm_isdst]);
10307          tmp = localtime(&utc0_dusk);
10308          printf("  Dusk = %02d:%02d %s  ", tmp->tm_hour,  tmp->tm_min, heyu_tzname[tmp->tm_isdst]);
10309          printf(sunmodelabel[configp->sunmode], configp->sunmode_offset);
10310          printf("\n");
10311          return 0;
10312       }
10313 
10314       else if (strcmp("calibrate", argv[2]) == 0 ) {
10315          printf("Calibrating for fast timing loops.\n");
10316 	 loopcount = loop_calibrate();  /* countdown for 1 second */
10317 	 if ( loopcount == 0 ) {
10318 	    fprintf(stderr, "Internal error: Calibration failed - possible overflow.\n");
10319 	    return 1;
10320 	 }
10321 	 printf("Paste this line into your Heyu configuration file:\n");
10322 	 printf("TIMER_LOOPCOUNT  %lu\n", loopcount);
10323          return 0;
10324       }
10325       else {
10326          display_utility_usage( argc, argv );
10327          return 1;
10328       }
10329    }
10330    else {
10331       display_utility_usage( argc, argv );
10332       return 1;
10333    }
10334 
10335    return 0;
10336 }
10337 
10338 /*---------------------------------------------------------------------+
10339  | Display aliases from user's config file.                            |
10340  +---------------------------------------------------------------------*/
show_aliases(ALIAS * aliasptr,unsigned int hcodemap)10341 void show_aliases ( ALIAS *aliasptr, unsigned int hcodemap )
10342 {
10343    int  j, count, maxlabel, maxunits;
10344    char hc;
10345 
10346    printf(" [Aliases]\n");
10347 
10348    if ( aliasptr == (ALIAS *)NULL ) {
10349       printf("  -none-\n\n");
10350       return;
10351    }
10352 
10353    j = 0;
10354    count = 0;
10355    maxlabel = maxunits = 0;
10356    while ( aliasptr[j].label[0] != '\0' ) {
10357       maxlabel = max(maxlabel, (int)strlen(aliasptr[j].label));
10358       maxunits = max(maxunits, (int)strlen(bmap2units(aliasptr[j].unitbmap)));
10359       count++;
10360       j++;
10361    }
10362 
10363    if ( count == 0 ) {
10364       printf("  -none-\n");
10365       return;
10366    }
10367 
10368    for ( hc = 'A'; hc <= 'P'; hc++ ) {
10369       if ( (hcodemap & (1 << hc2code(hc))) == 0 ) {
10370          continue;
10371       }
10372       for ( j = 0; j < count; j++ ) {
10373          if ( toupper((int)aliasptr[j].housecode) == hc ) {
10374             printf("  alias  %-*s  %c%-*s  %s  %s\n", maxlabel, aliasptr[j].label,
10375               aliasptr[j].housecode, maxunits, bmap2units(aliasptr[j].unitbmap),
10376               lookup_module_name(aliasptr[j].modtype), display_module_options(j));
10377          }
10378       }
10379    }
10380 
10381    printf("\n");
10382 
10383    return;
10384 }
10385 
10386 /*---------------------------------------------------------------------+
10387  | Display scenes from user's config file.                             |
10388  +---------------------------------------------------------------------*/
show_scenes(SCENE * scenep)10389 void show_scenes ( SCENE *scenep )
10390 {
10391    int  j, count, maxlen;
10392 
10393    printf(" [Scenes]\n");
10394    j = 0;
10395    count = 0;
10396    maxlen = 0;
10397    while ( scenep && scenep[j].label[0] != '\0' ) {
10398       if ( scenep[j].type == F_SCENE ) {
10399          maxlen = max(maxlen, (int)strlen(scenep[j].label));
10400          count++;
10401       }
10402       j++;
10403    }
10404 
10405    if ( count == 0 ) {
10406       printf("  -none-\n\n");
10407       return;
10408    }
10409 
10410    j = 0;
10411    while ( scenep && scenep[j].label[0] != '\0' ) {
10412       if ( scenep[j].type == F_SCENE )
10413          printf("  scene  %-*s  %s\n",
10414                maxlen, scenep[j].label, scenep[j].body);
10415       j++;
10416    }
10417    printf("\n");
10418    return;
10419 }
10420 
10421 /*---------------------------------------------------------------------+
10422  | Display usersyns from user's config file.                           |
10423  +---------------------------------------------------------------------*/
show_usersyns(SCENE * scenep)10424 void show_usersyns ( SCENE *scenep )
10425 {
10426    int  j, count, maxlen;
10427 
10428    printf(" [Usersyns]\n");
10429    j = 0;
10430    count = 0;
10431    maxlen = 0;
10432    while ( scenep && scenep[j].label[0] != '\0' ) {
10433       if ( scenep[j].type == F_USYN ) {
10434           maxlen = max(maxlen, (int)strlen(scenep[j].label));
10435           count++;
10436       }
10437       j++;
10438    }
10439 
10440    if ( count == 0 ) {
10441       printf("  -none-\n\n");
10442       return;
10443    }
10444 
10445    j = 0;
10446    while ( scenep && scenep[j].label[0] != '\0' ) {
10447       if ( scenep[j].type == F_USYN ) {
10448          printf("  usersyn  %-*s  %s\n",
10449                maxlen, scenep[j].label, scenep[j].body);
10450       }
10451       j++;
10452    }
10453    printf("\n");
10454    return;
10455 }
10456 
10457 
10458 /*---------------------------------------------------------------------+
10459  | 'show' command #1 - for information which can be determined from    |
10460  | the user's config file.  Return 1 if command not recognized here.   |
10461  +---------------------------------------------------------------------*/
c_show1(int argc,char * argv[])10462 int c_show1 ( int argc, char *argv[] )
10463 {
10464    void x10state_show ( unsigned char );
10465    void remove_x10state_file ( void );
10466    void send_x10state_command ( unsigned char, unsigned char );
10467    int read_x10state_file (void );
10468    void show_housemap ( void );
10469    void show_module_mask ( unsigned char );
10470    void show_launcher ( unsigned char );
10471    void show_powerfail_launcher ( void );
10472    void show_sensorfail_launcher ( void );
10473    void show_rfflood_launcher ( void );
10474    void show_timeout_launcher ( void );
10475    void show_exec_launcher ( void );
10476    void show_hourly_launcher ( void );
10477    void show_all_launchers ( void );
10478    void show_configuration ( void );
10479    void show_configuration_compressed ( void );
10480 
10481    char hc;
10482    unsigned int hcodemap;
10483    int j, k;
10484 
10485 #if 0
10486    static struct showlist_st {
10487       char type;
10488       int  minlabel;
10489       int  minparm;
10490       int  maxparm;
10491       char *option;
10492    } showlist = {
10493       {1, 2, "al[iases] [H]   Aliases defined in config file\n"},
10494       {2, 2, "ar[med]         Armed status of system (*)\n"},
10495       {1, 2, "sc[enes]        Scenes defined in config file\n"},
10496       {2, 2, "se[nsors]       Security sensor states (*)\n"},
10497       {1, 1, "u[sersyns]      Usersyns defined in config file\n"},
10498       {1, 1, "m[odules] H     Module attributes, housecode H\n"},
10499       {1, 1, "l[aunchers] [H] Launchers, all or only housecode H or -p -s -r -t\n"},
10500       {2, 1, "h[ousemap] [H]  Overall system state, or details housecode H (*)\n"},
10501       {1, 2, "da[wndusk]      Display Dawn and Dusk times for today (*)\n"},
10502       {2, 2, "di[mlevels]     Dim levels of modules as percent (*)\n"},
10503       {2, 2, "ra[wlevels]     Native levels of modules (*)\n"},
10504       {2, 1, "f[lags]         Flag states (*)\n"},
10505       {2, 2, "ti[mers]        Display active timers 1-16 (*)\n"},
10506       {2, 2, "ts[tamp] Hu     Date and time of last signal on Hu (*)\n"},
10507       {2, 1, "g[roups] H      Extended group assignments and levels (*)\n"},
10508       {1, 1, "c[onfig]        Display stripped Heyu configuration file\n"},
10509       {2, 4, "rfxs[ensors]    Tabular display of all RFXSensors (*)\n"},
10510       {2, 4, "rfxm[eters]     Tabular display of all RFXMeters (*)\n"},
10511       {2, 1, "o[thers]        Cumulative received address map (*) - clear with\n"},
10512       {0, 0, "                  'heyu initothers' or 'heyu initstate'\n"},
10513       {0, 0, "(*) Require the heyu state engine to be running\n"},
10514       {},
10515 
10516       while ( showlist[j].minlabel > 0 ) {
10517          for ( j = 0; j < strlen(argv[2]; j++ ) {
10518             if ( strncmp(arg
10519 
10520 #endif
10521 
10522 
10523 
10524 
10525 
10526    if ( argc < 3 ) {
10527       printf("heyu show options:\n");
10528       printf("  al[iases] [H]   Aliases defined in config file\n");
10529       printf("  ar[med]         Armed status of system (*)\n");
10530       printf("  sc[enes]        Scenes defined in config file\n");
10531       printf("  se[nsors]       Sensor status (*)\n");
10532       printf("  u[sersyns]      Usersyns defined in config file\n");
10533       printf("  m[odules] H     Module attributes, housecode H\n");
10534       printf("  l[aunchers] [H] Launchers, all or only housecode H or -e|p|r|s|t\n");
10535       printf("  h[ousemap] [H]  Overall system state, or details housecode H (*)\n");
10536       printf("  da[wndusk]      Display Dawn and Dusk times for today (*)\n");
10537 #ifdef HASDMX
10538       printf("  dim[levels]     Dim levels of modules as percent (*)\n");
10539 #else
10540       printf("  di[mlevels]     Dim levels of modules as percent (*)\n");
10541 #endif
10542       printf("  ra[wlevels]     Native levels of modules (*)\n");
10543       printf("  f[lags]         Flag states (*)\n");
10544       printf("  ti[mers]        Display active timers (*)\n");
10545       printf("  ts[tamp] Hu     Date and time of last signal on Hu (*)\n");
10546       printf("  g[roups] H      Extended group assignments and levels (*)\n");
10547       printf("  c[onfig]        Display stripped Heyu configuration file\n");
10548       printf("  x[10security]   Tabular display of all X10 Security sensors (*)\n");
10549 
10550 #ifdef HASRFXS
10551 #ifdef HASRFXM
10552       printf("  rfxs[ensors]    Tabular display of all RFXSensors (*)\n");
10553 #else
10554       printf("  rf[xsensors]    Tabular display of all RFXSensors (*)\n");
10555 #endif
10556 #endif
10557 
10558 #ifdef HASRFXM
10559 #ifdef HASRFXS
10560       printf("  rfxm[eters]     Tabular display of all RFXMeters (*)\n");
10561 #else
10562       printf("  rf[xmeters]     Tabular display of all RFXMeters (*)\n");
10563 #endif
10564 #endif
10565 #ifdef HASDMX
10566       printf("  dig[imax]       Tabular display of all DigiMax (*)\n");
10567 #endif
10568 #ifdef HASORE
10569       printf("  or[egon]        Tabular display of all Oregon sensors (*)\n");
10570       printf("  ot[hers]        Cumulative received address map (*) - clear with\n");
10571 #else
10572       printf("  o[thers]        Cumulative received address map (*) - clear with\n");
10573 #endif
10574       printf("                    'heyu initothers' or 'heyu initstate'\n");
10575       printf("  (*) Require the heyu state engine to be running\n");
10576       return 0;
10577    }
10578 
10579    get_configuration(CONFIG_INIT);
10580 
10581    if ( strlen(argv[2]) == 1 && strchr("asdtro", *argv[2]) ) {
10582       fprintf(stderr, "'%s' is ambiguous - supply more characters\n", argv[2]);
10583       exit(1);
10584    }
10585    else if ( strncmp("aliases", argv[2], 2) == 0 ) {
10586       hcodemap = 0;
10587       for ( j = 3; j < argc; j++ ) {
10588          for ( k = 0; k < strlen(argv[j]); k++ ) {
10589             hc = toupper((int)((unsigned char)(*(argv[j] + k))));
10590             if ( hc < 'A' || hc > 'P' ) {
10591                fprintf(stderr, "Invalid housecode '%c'\n", *(argv[j] + k));
10592                exit(1);
10593             }
10594             hcodemap |= 1 << hc2code(hc);
10595          }
10596       }
10597       hcodemap = (hcodemap == 0) ? 0xffff : hcodemap;
10598       show_aliases(configp->aliasp, hcodemap);
10599    }
10600    else if ( strncmp("scenes", argv[2], 2) == 0 )
10601       show_scenes(configp->scenep);
10602    else if ( strncmp("usersyns", argv[2], 1) == 0 )
10603       show_usersyns(configp->scenep);
10604    else if ( strncmp("modules", argv[2], 1) == 0 ) {
10605       if ( argc < 4 ) {
10606          fprintf(stderr, "Housecode needed\n");
10607          exit(1);
10608       }
10609       hc = toupper((int)(*argv[3]));
10610       if ( hc < 'A' || hc > 'P' ) {
10611          fprintf(stderr, "Invalid housecode '%s'\n", argv[3]);
10612          exit(1);
10613       }
10614       show_module_mask(hc2code(hc));
10615    }
10616    else if ( strncmp("launchers", argv[2], 1) == 0 ) {
10617       if ( argc < 4 ) {
10618          show_all_launchers();
10619          printf("\n");
10620       }
10621       else if ( strncmp(argv[3], "-powerfail", 2) == 0 ) {
10622          show_powerfail_launcher();
10623          printf("\n");
10624       }
10625       else if ( strncmp(argv[3], "-sensorfail", 2) == 0 ) {
10626          show_sensorfail_launcher();
10627          printf("\n");
10628       }
10629       else if ( strncmp(argv[3], "-rfflood", 2) == 0 ) {
10630          show_rfflood_launcher();
10631          printf("\n");
10632       }
10633       else if ( strncmp(argv[3], "-timeout", 2) == 0 ) {
10634          show_timeout_launcher();
10635          printf("\n");
10636       }
10637       else if ( strncmp(argv[3], "-exec", 2) == 0 ) {
10638          show_exec_launcher();
10639          printf("\n");
10640       }
10641       else if ( strncmp(argv[3], "-hourly", 2) == 0 ) {
10642          show_hourly_launcher();
10643          printf("\n");
10644       }
10645       else {
10646          hc = toupper((int)(*argv[3]));
10647          if ( hc < 'A' || hc > 'P' ) {
10648             fprintf(stderr, "Invalid housecode '%s'\n", argv[3]);
10649             exit(1);
10650          }
10651          show_launcher(hc2code(hc));
10652          printf("\n");
10653       }
10654    }
10655    else if ( strncmp("config", argv[2], 1) == 0 ) {
10656       show_configuration();
10657       printf("\n");
10658    }
10659    else {
10660       return 1;
10661    }
10662 
10663    return 0;
10664 }
10665 
10666 /*---------------------------------------------------------------------+
10667  | 'show' command #2 - for information requiring the state engine be   |
10668  | running.                                                            |
10669  +---------------------------------------------------------------------*/
10670 int c_show2 ( int argc, char *argv[] )
10671 {
10672    void x10state_show ( unsigned char );
10673    int  fetch_x10state ( void );
10674    void show_housemap ( void );
10675    void show_module_mask ( unsigned char );
10676    void show_launcher ( unsigned char );
10677    void show_all_dimlevels_raw ( void );
10678    void show_sticky_addr ( void );
10679    void show_flags_old (void );
10680    void show_geoflags (void );
10681    void show_long_flags ( void );
10682    int  check_for_engine ( void );
10683    int  read_x10state_file ( void );
10684    int  show_signal_timestamp ( char * );
10685    int  show_security_sensors ( void );
10686    void show_armed_status ( void );
10687    int  show_global_timers ( void );
10688    int  show_state_dawndusk (void );
10689    void show_extended_groups ( unsigned char );
10690    int  show_rfxsensors ( void );
10691    int  show_rfxmeters ( void );
10692    int  show_digimax ( void );
10693    int  show_oregon ( void );
10694    int  show_x10_security ( void );
10695 
10696    char hc;
10697 
10698    if ( check_for_engine() != 0 ) {
10699       fprintf(stderr, "State engine is not running.\n");
10700       return 1;
10701    }
10702 
10703    if ( argc < 3 ) {
10704       fprintf(stderr, "Too few arguments\n");
10705       return 1;
10706    }
10707    if ( read_x10state_file() != 0 ) {
10708       fprintf(stderr, "Unable to read state file.\n");
10709       return 1;
10710    }
10711 
10712    if ( strncmp("housemap", argv[2], 1) == 0 ) {
10713       if ( argc == 3 ) {
10714          show_housemap();
10715       }
10716       else {
10717          if ( fetch_x10state() != 0 )
10718             return 1;
10719          hc = toupper((int)(*argv[3]));
10720          if ( hc < 'A' || hc > 'P' ) {
10721             fprintf(stderr, "Invalid housecode '%s'\n", argv[3]);
10722             exit(1);
10723          }
10724          x10state_show(hc2code(hc));
10725       }
10726    }
10727    else if ( strncmp("dimlevels", argv[2], 3) == 0 ) {
10728       show_all_dimlevels();
10729    }
10730    else if ( strncmp("rawlevels", argv[2], 2) == 0 ) {
10731       show_all_dimlevels_raw();
10732    }
10733    else if ( strncmp("flags", argv[2], 1) == 0 ) {
10734       if ( fetch_x10state() != 0 )
10735          return 1;
10736       if ( configp->show_flags_mode == NEW )
10737          show_long_flags();
10738       else
10739          show_flags_old();
10740    }
10741    else if ( strncmp("geoflags", argv[2], 2) == 0 ) {
10742       if ( fetch_x10state() != 0 )
10743          return 1;
10744       show_geoflags();
10745    }
10746    else if ( strncmp("armed", argv[2], 2) == 0 ) {
10747       if ( fetch_x10state() != 0 )
10748          return 1;
10749       show_armed_status();
10750    }
10751    else if ( strncmp("sensors", argv[2], 2) == 0 ) {
10752       show_security_sensors();
10753    }
10754    else if ( strncmp("others", argv[2], 2) == 0 ) {
10755       if ( fetch_x10state() != 0 )
10756          return 1;
10757       show_sticky_addr();
10758    }
10759    else if ( strncmp("tstamp", argv[2], 2) == 0 ) {
10760       if ( argc < 4 ) {
10761          fprintf(stderr, "Too few parameters.\n");
10762          return 1;
10763       }
10764       else if ( argc > 4 ) {
10765          fprintf(stderr, "Too many parameters.\n");
10766          return 1;
10767       }
10768       return show_signal_timestamp(argv[3]);
10769    }
10770    else if ( strncmp("timers", argv[2], 2) == 0 ) {
10771       if ( fetch_x10state() != 0 )
10772          return 1;
10773       return show_global_timers();
10774    }
10775    else if ( strncmp("dawndusk", argv[2], 2) == 0 ) {
10776       if ( fetch_x10state() != 0 )
10777          return 1;
10778       return show_state_dawndusk();
10779    }
10780    else if ( strncmp("groups", argv[2], 1) == 0 ) {
10781       if ( argc < 4 ) {
10782          fprintf(stderr, "Housecode needed\n");
10783          return 1;
10784       }
10785       hc = toupper((int)(*argv[3]));
10786       if ( hc < 'A' || hc > 'P' ) {
10787          fprintf(stderr, "Invalid housecode '%s'\n", argv[3]);
10788          return 1;
10789       }
10790       show_extended_groups(hc2code(hc));
10791    }
10792    else if ( strncmp("x10security", argv[2], 1) == 0 ) {
10793       return show_x10_security();
10794    }
10795 
10796 #ifdef HASRFXM
10797    else if ( strncmp("rfxmeters", argv[2], 4) == 0 ) {
10798       return show_rfxmeters();
10799    }
10800 #ifdef HASRFXS
10801    else if ( (strlen(argv[2]) == 2 && strcmp("rf",  argv[2]) == 0) ||
10802              (strlen(argv[2]) == 3 && strcmp("rfx", argv[2]) == 0)    ) {
10803      fprintf(stderr, "%s is ambiguous - supply more characters.\n", argv[2]);
10804      return 1;
10805    }
10806 #else
10807    else if ( strncmp(argv[2], "rf", 2) == 0 ) {
10808      return show_rfxmeters();
10809    }
10810 #endif
10811 #endif
10812 
10813 
10814 #ifdef HASRFXS
10815    else if ( strncmp("rfxsensors", argv[2], 4) == 0 ) {
10816       return show_rfxsensors();
10817    }
10818 #ifdef HASRFXM
10819    else if ( (strlen(argv[2]) == 2 && strcmp("rf",  argv[2]) == 0) ||
10820              (strlen(argv[2]) == 3 && strcmp("rfx", argv[2]) == 0)    ) {
10821       fprintf(stderr, "%s is ambiguous - supply more characters.\n", argv[2]);
10822       return 1;
10823    }
10824 #else
10825    else if ( strncmp(argv[2], "rf", 2) == 0 ) {
10826       return show_rfxsensors();
10827    }
10828 #endif
10829 #endif
10830 
10831    else if ( strncmp(argv[2], "digimax", 3) == 0 ) {
10832       return show_digimax();
10833    }
10834 #ifdef HASORE
10835    else if ( strncmp(argv[2], "oregon", 2) == 0 ) {
10836       return show_oregon();
10837    }
10838 #endif
10839 
10840    else {
10841       fprintf(stderr, "Show argument '%s' not recognized.\n", argv[2]);
10842       exit(1);
10843    }
10844 
10845    return 0;
10846 }
10847 
10848 
10849 
10850 /*---------------------------------------------------------------------+
10851  |  Translate a macro from the image of an EEPROM in memory into       |
10852  |  direct commands and send to the CM11A as direct commands.          |
10853  |  Argument 'image' is a pointer to the 1024 byte image buffer in     |
10854  |  memory.  Argument 'macaddr' is the offset of the macro in that     |
10855  |  buffer.                                                            |
10856  |  The macro delay is ignored, and operation does not chain to a      |
10857  |  subsequent delayed macro as it would in actual CM11A operation.    |
10858  |  Return 0 if successful, otherwise 1 on first failure.              |
10859  +---------------------------------------------------------------------*/
10860 int send_macro_immediate ( unsigned char *image, unsigned int macaddr )
10861 {
10862    unsigned char hcode, func = 0xff, nelem;
10863    unsigned char buf[16];
10864    unsigned char *sp;
10865    unsigned int  bitmap, staflag;
10866    int           k, retcode = 0;
10867    int           syncmode;
10868    int           line_sync_mode(void);
10869 
10870    syncmode = line_sync_mode();
10871 
10872    /* Ignore delay */
10873    sp = image + macaddr + 1;
10874    nelem = *sp++;
10875 
10876    /* Sent each element */
10877    for ( k = 0; k < (int)nelem; k++ ) {
10878       if ( sp < (image + 5) || sp > (image + 0x3ff) ) {
10879          fprintf(stderr, "Macro element is outside EEPROM image\n");
10880          return 1;
10881       }
10882       hcode = (sp[0] & 0xf0u) >> 4;
10883       func =  sp[0] & 0x0fu;
10884       bitmap = (sp[1] << 8) | sp[2];
10885       staflag = 0;
10886 
10887       /* Send the address */
10888       if ( send_address(hcode, bitmap, syncmode) != 0 ) {
10889          fprintf(stderr, "Unable to send address\n");
10890          return 1;
10891       }
10892 
10893       /* Send the function */
10894       if ( func == 4 || func == 5 ) {
10895          /* Dim or Bright function */
10896          if ( sp[3] & 0x80u ) {
10897             /* Brighten before dimming */
10898             buf[0] = 0x06 | (22 << 3);
10899             buf[1] = (sp[0] & 0xf0u) | 5;
10900             if ( (retcode = send_command(buf, 2, 0, syncmode)) != 0 )
10901                break;
10902          }
10903          buf[0] = 0x06 | ((sp[3] & 0x7fu) << 3);
10904          buf[1] = sp[0];
10905 
10906          if ( (retcode = send_command(buf, 2, 0, syncmode)) != 0 )
10907             break;
10908          sp += 4;
10909       }
10910       else if ( func == 7 ) {
10911          /* Extended function */
10912          buf[0] = 0x07u;
10913          buf[1] = (sp[0] & 0xf0u) | 0x07u;
10914          buf[2] = sp[3];
10915          buf[3] = sp[4];
10916          buf[4] = sp[5];
10917 
10918          staflag = (buf[4] == 0x37) ? 1 : 0;
10919 
10920          /* Kluge "fix" for checksum 5A problem.    */
10921          /* CM11A seems to disregard the dim field  */
10922          /* in the header byte.                     */
10923          if ( checksum(buf, 5) == 0x5A && configp->fix_5a == YES )
10924             buf[0] = 0x0F;
10925 
10926          if ( (retcode = send_command(buf, 5, staflag, syncmode)) != 0 )
10927             break;
10928          sp += 6;
10929       }
10930       else if ( func == 15 ) {
10931          /* Status Request function */
10932          staflag = 1;
10933          buf[0] = 0x06;
10934          buf[1] = sp[0];
10935          if ( (retcode = send_command(buf, 2, staflag, syncmode)) != 0 )
10936             break;
10937          sp += 3;
10938       }
10939       else {
10940          /* Basic function */
10941          buf[0] = 0x06;
10942          buf[1] = sp[0];
10943          if ( (retcode = send_command(buf, 2, 0, syncmode)) != 0 )
10944             break;
10945          sp += 3;
10946       }
10947    }
10948 
10949    if ( retcode != 0 )
10950       fprintf(stderr, "Unable to send command function %d\n", func);
10951 
10952    return retcode;
10953 }
10954 
10955 /*---------------------------------------------------------------------+
10956  |  Translate a macro from the image of an EEPROM in memory into       |
10957  |  direct commands and send to the CM11A as direct commands.          |
10958  |  Argument 'image' is a pointer to the 1024 byte image buffer in     |
10959  |  memory.  Argument 'macaddr' is the offset of the macro in that     |
10960  |  buffer.                                                            |
10961  |  The macro delay itself is ignored, but operation chains to a       |
10962  |  subsequent delayed macro as it would in actual CM11A operation.    |
10963  |  Return 0 if successful, otherwise 1 on first failure.              |
10964  +---------------------------------------------------------------------*/
10965 int send_macro_chain_immediate ( unsigned char *image, unsigned int macaddr )
10966 {
10967    unsigned char hcode, func = 0xff, nelem;
10968    unsigned char buf[16];
10969    unsigned char *sp;
10970    unsigned char ischained;
10971    unsigned int  bitmap, staflag;
10972    int           k, retcode = 0;
10973    int           syncmode;
10974    int           line_sync_mode(void);
10975 
10976    ischained = 1;
10977    sp = image + macaddr + 1;
10978 
10979    syncmode = line_sync_mode();
10980 
10981    while ( ischained != 0 ) {
10982       nelem = *sp++;
10983 
10984       /* Sent each element */
10985       for ( k = 0; k < (int)nelem; k++ ) {
10986          if ( sp < (image + 5) || sp > (image + 0x3ff) ) {
10987             fprintf(stderr, "Macro element is outside EEPROM image\n");
10988             return 1;
10989          }
10990          hcode = (sp[0] & 0xf0u) >> 4;
10991          func =  sp[0] & 0x0fu;
10992          bitmap = (sp[1] << 8) | sp[2];
10993          staflag = 0;
10994 
10995          /* Send the address */
10996          if ( send_address(hcode, bitmap, syncmode) != 0 ) {
10997             fprintf(stderr, "Unable to send address\n");
10998             return 1;
10999          }
11000 
11001          /* Send the function */
11002          if ( func == 4 || func == 5 ) {
11003             /* Dim or Bright function */
11004             if ( sp[3] & 0x80u ) {
11005                /* Brighten before dimming */
11006                buf[0] = 0x06 | (22 << 3);
11007                buf[1] = (sp[0] & 0xf0u) | 5;
11008                if ( (retcode = send_command(buf, 2, 0, syncmode)) != 0 )
11009                   break;
11010             }
11011             buf[0] = 0x06 | ((sp[3] & 0x7fu) << 3);
11012             buf[1] = sp[0];
11013 
11014             if ( (retcode = send_command(buf, 2, 0, syncmode)) != 0 )
11015                break;
11016             sp += 4;
11017          }
11018          else if ( func == 7 ) {
11019             /* Extended function */
11020             buf[0] = 0x07;
11021             buf[1] = (sp[0] & 0xf0u) | 0x07u;
11022             buf[2] = sp[3];
11023             buf[3] = sp[4];
11024             buf[4] = sp[5];
11025 
11026             staflag = (buf[4] == 0x37) ? 1 : 0;
11027 
11028             /* Kluge "fix" for checksum 5A problem.    */
11029             /* CM11A seems to disregard the dim field  */
11030             /* in the header byte.                     */
11031             if ( checksum(buf, 5) == 0x5A && configp->fix_5a == YES )
11032                buf[0] = 0x0F;
11033 
11034             if ( (retcode = send_command(buf, 5, staflag, syncmode)) != 0 )
11035                break;
11036             sp += 6;
11037          }
11038          else if ( func == 15 ) {
11039             /* Status Request function */
11040             buf[0] = 0x06;
11041             buf[1] = sp[0];
11042             staflag = 1;
11043             if ( (retcode = send_command(buf, 2, staflag, syncmode)) != 0 )
11044                break;
11045             sp += 3;
11046          }
11047          else {
11048             /* Basic function */
11049             buf[0] = 0x06;
11050             buf[1] = sp[0];
11051             if ( (retcode = send_command(buf, 2, 0, syncmode)) != 0 )
11052                break;
11053             sp += 3;
11054          }
11055       }
11056       ischained = *sp++;
11057    }
11058 
11059    if ( retcode != 0 )
11060       fprintf(stderr, "Unable to send command function %d\n", func);
11061 
11062    return retcode;
11063 }
11064 
11065 
11066 /*---------------------------------------------------------------------+
11067  |  Return the total length in bytes of the macro pointed to by macptr |
11068  +---------------------------------------------------------------------*/
11069 unsigned char macro_length ( unsigned char *macptr )
11070 {
11071    unsigned char k, nelem, func;
11072    unsigned char *sp;
11073 
11074    sp = macptr + 1;
11075    nelem = *sp++;
11076 
11077    for ( k = 0; k < nelem; k++ ) {
11078       func = *sp & 0x0fu;
11079       if ( func == 4 || func == 5 )
11080          sp += 4;
11081       else if ( func == 7 )
11082          sp += 6;
11083       else
11084          sp += 3;
11085    }
11086    return (sp - macptr);
11087 }
11088 
11089 /* Structure used by c_catchup() */
11090 struct catchup_st {
11091    unsigned int  start;
11092    unsigned int  macaddr;
11093    unsigned char delay;
11094 };
11095 
11096 /*---------------------------------------------------------------------+
11097  | qsort compare function for c_catchup()                              |
11098  | Delayed events are ordered before undelayed events if the actual    |
11099  | execution times are the same.                                       |
11100  +---------------------------------------------------------------------*/
11101 int cmp_catchup ( struct catchup_st *one, struct catchup_st *two )
11102 {
11103    return  (one->start < two->start) ? -1 :
11104            (one->start > two->start) ?  1 :
11105            (one->delay > two->delay) ? -1 :
11106            (one->delay < two->delay) ?  1 : 0 ;
11107 }
11108 
11109 /*---------------------------------------------------------------------+
11110  |  Execute the commands in macros in an uploaded schedule for timer   |
11111  |  events scheduled between 0:00 hours and the current time today.    |
11112  +---------------------------------------------------------------------*/
11113 int c_catchup ( int argc, char *argv[] )
11114 {
11115    time_t        now;
11116    struct tm     *tmp;
11117    int           j, ichksum, size;
11118    unsigned int           day, minutes;
11119    int           (*fptr)() = &cmp_catchup;
11120    unsigned char dow, dow_bmap;
11121    unsigned char *image, *sp, *dp;
11122    unsigned int  beg, end, start, stop, delay;
11123    unsigned int  macstart, macstop, macaddr;
11124    unsigned char *image_ptr(void);
11125    struct catchup_st table[210];
11126    char     maclabel[MACRO_LEN + 1];
11127 
11128    if ( invalidate_for_cm10a() != 0 )
11129       return 1;
11130 
11131    switch ( get_upload_expire() ) {
11132      case NO_RECORD_FILE :
11133         fprintf(stderr, "No schedule has been uploaded by Heyu.\n");
11134         return 1;
11135      case NO_EXPIRATION :
11136         fprintf(stderr, "Uploaded schedule contains no Timers.\n");
11137         return 1;
11138      case BAD_RECORD_FILE :
11139         fprintf(stderr, "X10 Record File has been corrupted.\n");
11140         return 1;
11141      case SCHEDULE_EXPIRED :
11142         fprintf(stderr, "Uploaded schedule has expired.\n");
11143         return 1;
11144      default :
11145         break;
11146    }
11147 
11148    lookup_macro(0, maclabel, &ichksum);
11149    if ( !loadcheck_image(ichksum) ) {
11150       fprintf(stderr, "Unable to load x10image file, ichksum = %d\n", ichksum);
11151       return 1;
11152    }
11153    image = image_ptr();
11154 
11155    time(&now);
11156    tmp = legal_to_cm11a(&now);
11157    minutes = 60 * tmp->tm_hour + tmp->tm_min + ((tmp->tm_sec > 0) ? 1 : 0);
11158 
11159    day = tmp->tm_yday;
11160    dow = 1 << (tmp->tm_wday);
11161 
11162    sp = image + 2;
11163    size = 0;
11164    while ( *sp != 0xff ) {
11165       dow_bmap = sp[0];
11166       beg = sp[1] | ((sp[4] & 0x80u) << 1);
11167       end = sp[2] | ((sp[5] & 0x80u) << 1);
11168       if ( !(dow & dow_bmap) || day < beg || day > end ) {
11169          sp += 9;
11170          continue;
11171       }
11172       start = 120 * ((sp[3] & 0xf0u) >> 4) + (sp[4] & 0x7fu) +
11173                    SECURITY_OFFSET_ADJUST * ((sp[6] & 0x80u) >> 7);
11174       stop  = 120 * (sp[3] & 0x0fu) + (sp[5] & 0x7fu) +
11175                    SECURITY_OFFSET_ADJUST * ((sp[6] & 0x08u) >> 3);
11176       macstart = sp[7] | ((sp[6] & 0x30u) << 4);
11177       macstop = sp[8] | ((sp[6] & 0x03u) << 8);
11178 
11179       /* Add macro delay to start time */
11180       delay = *(image + macstart);
11181       start += delay;
11182       if ( start < minutes ) {
11183          table[size].start = start;
11184          table[size].macaddr = macstart;
11185          table[size].delay = (delay > 0) ? 1 : 0;
11186          size++;
11187          /* Include any chained macros */
11188          dp = image + macstart;
11189          while ( *(dp += macro_length(dp)) > 0 ) {
11190             start += *dp;
11191             if ( start >= minutes )
11192                break;
11193             table[size].start = start;
11194             table[size].macaddr = dp - image;
11195             table[size].delay = 1;
11196             size++;
11197          }
11198       }
11199 
11200       delay = *(image + macstop);
11201       stop  += delay;
11202       if ( stop < minutes ) {
11203          table[size].start = stop;
11204          table[size].macaddr = macstop;
11205          table[size].delay = (delay > 0) ? 1 : 0;
11206          size++;
11207          /* Include any chained macros */
11208          dp = image + macstop;
11209          while ( *(dp += macro_length(dp)) > 0 ) {
11210             stop += *dp;
11211             if ( stop >= minutes )
11212                break;
11213             table[size].start = stop;
11214             table[size].macaddr = dp - image;
11215             table[size].delay = 1;
11216             size++;
11217          }
11218       }
11219       sp += 9;
11220    }
11221 
11222    if ( size == 0 ) {
11223       fprintf(stderr,
11224         "No macros scheduled for execution before now.\n");
11225       return 0;
11226    }
11227 
11228    /* Sort the table in order of ascending execution times */
11229    qsort((void *)table, size, (sizeof(struct catchup_st)), fptr);
11230 
11231    /* Now send the macro commands */
11232    for ( j = 0; j < size; j++ ) {
11233       macaddr = table[j].macaddr;
11234       lookup_macro(macaddr, maclabel, &ichksum);
11235       printf("Emulating macro %s at address %d\n",
11236                    maclabel, macaddr);
11237       fflush(stdout);
11238       if ( send_macro_immediate(image, macaddr) != 0 )
11239          return 1;
11240    }
11241 
11242    return 0;
11243 }
11244 
11245 
11246 /*---------------------------------------------------------------------+
11247  |  Execute the commands in an uploaded macro.                         |
11248  +---------------------------------------------------------------------*/
11249 int c_macro ( int argc, char *argv[] )
11250 {
11251    int           macaddr, ichksum;
11252    unsigned char *image;
11253    unsigned char *image_ptr(void);
11254 
11255    if ( invalidate_for_cm10a() != 0 )
11256       return 1;
11257 
11258    switch ( get_upload_expire() ) {
11259      case NO_RECORD_FILE :
11260         fprintf(stderr, "No schedule has been uploaded by Heyu.\n");
11261         return 1;
11262      case BAD_RECORD_FILE :
11263         fprintf(stderr, "X10 Record File has been corrupted.\n");
11264         return 1;
11265      default :
11266         break;
11267    }
11268 
11269    if ( argc < 3 ) {
11270       fprintf(stderr, "Macro name needed.\n");
11271       return 1;
11272    }
11273 
11274    if ( (macaddr = macro_rev_lookup(argv[2], &ichksum)) < 0 ) {
11275       fprintf(stderr, "Macro '%s' not found in %s file.\n",
11276                                     argv[2], pathspec(MACROXREF_FILE));
11277       return 1;
11278    }
11279 
11280    if ( !loadcheck_image(ichksum) ) {
11281       fprintf(stderr, "Unable to load x10image file, ichksum = %d\n", ichksum);
11282       return 1;
11283    }
11284    image = image_ptr();
11285 
11286    printf("Emulating macro %s at address %d\n", argv[2], macaddr);
11287    fflush(stdout);
11288 
11289    if ( send_macro_immediate(image, macaddr) != 0 )
11290       return 1;
11291 
11292    return 0;
11293 }
11294 
11295 /*---------------------------------------------------------------------+
11296  |  Read heyu options from the command line and store pointers in      |
11297  |  structure opt_st.  Return the number of argv[] tokens used for the |
11298  |  options, or -1 if invalid usage or invalid option.                 |
11299  |  Options supported:                                                 |
11300  |    -v                 Verbose mode.                                 |
11301  |    -c <pathname>      Configuration file.                           |
11302  |    -s <pathname>      Schedule file.                                |
11303  |    -0 ... -9          Subdirectory of $HOME/.heyu/ or /etc/heyu/    |
11304  |                        where config file is stored, e.g.,           |
11305  |                         -2 => $HOME/.heyu/2/x10config               |
11306  |    -tr                Sync with rising AC slope.                    |
11307  |    -tf                Sync with falling AC slope.                   |
11308  +---------------------------------------------------------------------*/
11309 int heyu_getopt( int argc, char **argv, struct opt_st *optptr )
11310 {
11311    int j, ntokens;
11312 
11313    optptr->configp = NULL;
11314    optptr->schedp = NULL;
11315    optptr->subp = NULL;
11316    optptr->dispsub[0] = '\0';
11317    optptr->verbose = 0;
11318    optptr->linesync = NO_SYNC;
11319 
11320    if ( argc < 2 )
11321       return -1;
11322 
11323    ntokens = 0;
11324    for ( j = 1; j < argc; j++ ) {
11325       if ( *argv[j] != '-' )
11326          break;
11327 
11328       if ( strncmp(argv[j], "-v", 2) == 0 ) {
11329          if ( strlen(argv[j]) == 2 ) {
11330             optptr->verbose = 1;
11331             ntokens++;
11332          }
11333          else {
11334             fprintf(stderr, "Invalid option '%s'\n", argv[j]);
11335             return -1;
11336          }
11337       }
11338       else if ( strncmp(argv[j], "-c", 2) == 0 ) {
11339          if ( (int)strlen(argv[j]) > 2 ) {
11340             optptr->configp = argv[j] + 1;
11341             ntokens++;
11342          }
11343          else if ( (j + 1) < argc && *argv[j + 1] != '-' ) {
11344             optptr->configp = argv[j + 1];
11345             ntokens += 2;
11346             j++;
11347          }
11348          else {
11349             fprintf(stderr, "Invalid option usage '%s'\n", argv[j]);
11350             return -1;
11351          }
11352       }
11353       else if ( strncmp(argv[j], "-s", 2) == 0 ) {
11354          if ( (int)strlen(argv[j]) > 2 ) {
11355             optptr->schedp = argv[j] + 2;
11356             ntokens++;
11357          }
11358          else if ( (j + 1) < argc && *(argv[j + 1]) != '-' ) {
11359             optptr->schedp = argv[j + 1];
11360             ntokens += 2;
11361             j++;
11362          }
11363          else {
11364             fprintf(stderr, "Invalid option usage '%s'\n", argv[j]);
11365             return -1;
11366          }
11367       }
11368       else if ( strncmp(argv[j], "-tr", 3) == 0 ) {
11369          if ( strlen(argv[j]) == 3 ) {
11370             optptr->linesync = RISE_SYNC;
11371             ntokens++;
11372          }
11373          else {
11374             fprintf(stderr, "Invalid option '%s'\n", argv[j]);
11375             return -1;
11376          }
11377       }
11378       else if ( strncmp(argv[j], "-tf", 3) == 0 ) {
11379          if ( strlen(argv[j]) == 3 ) {
11380             optptr->linesync = FALL_SYNC;
11381             ntokens++;
11382          }
11383          else {
11384             fprintf(stderr, "Invalid option '%s'\n", argv[j]);
11385             return -1;
11386          }
11387       }
11388       else if ( strlen(argv[j]) == 2 && isdigit((int)(*(argv[j] + 1))) ) {
11389          optptr->subp = argv[j] + 1;
11390          strncpy2(optptr->dispsub, argv[j] + 1, NAME_LEN - 1);
11391          ntokens++;
11392       }
11393 
11394       else {
11395          fprintf(stderr, "Invalid parameter '%s'\n", argv[j]);
11396          return -1;
11397       }
11398    }
11399 
11400    if ( optptr->subp && optptr->configp ) {
11401       fprintf(stderr,
11402         "Options -%s and -c may not both be specified.\n", optptr->subp);
11403       return -1;
11404    }
11405 
11406    return ntokens;
11407 }
11408 
11409 /*---------------------------------------------------------------------+
11410  | Return the line synchronization mode specified by CL option.        |
11411  +---------------------------------------------------------------------*/
11412 int line_sync_mode ( void )
11413 {
11414    return optptr->linesync;
11415 }
11416 
11417 /*---------------------------------------------------------------------+
11418  | Return the current system time as seconds after 0:00:00 hours legal |
11419  | (wall clock) time.                                                  |
11420  +---------------------------------------------------------------------*/
11421 long int systime_now ( void )
11422 {
11423    time_t     now;
11424    struct tm  *tms;
11425 
11426    time(&now);
11427    tms = localtime(&now);
11428 
11429    return ( 3600 * tms->tm_hour + 60 * tms->tm_min + tms->tm_sec);
11430 }
11431 
11432 
11433 /*---------------------------------------------------------------------+
11434  | Locate the macro address (if any) corresponding to the argument     |
11435  | trigger hcode|ucode and function (1 = on, 0 = off).  Return 1 if    |
11436  | found and pass back the macro address through the argument list.    |
11437  | Otherwise return 0.                                                 |
11438  +---------------------------------------------------------------------*/
11439 int locate_triggered_macro ( unsigned char *imagep, char hc, int unit,
11440       char *trig_func, int *macaddr)
11441 {
11442    unsigned char *cp;
11443    unsigned char trigaddr, funcmask;
11444 
11445    trigaddr = hc2code(hc) << 4 | unit2code(unit);
11446    funcmask = (!strcmp(strlower(trig_func), "off")) ? 0x00 : 0x80;
11447 
11448    cp = imagep + (imagep[0] << 8) + imagep[1];
11449 
11450    while (*cp != 0xffu && *(cp+1) != 0xffu && cp < (imagep + PROMSIZE) ) {
11451       if ( *cp == trigaddr && (*(cp+1) & 0x80u) == funcmask ) {
11452          *macaddr = (*(cp+1) & 0x0fu) << 8 | *(cp+2);
11453          return 1;
11454       }
11455       cp += 3;
11456    }
11457    return 0;
11458 }
11459 
11460 /*---------------------------------------------------------------------+
11461  |  Execute commands in a uploaded macro image as if triggered.        |
11462  |    argv[2] = Hu address, argv[3] = "on" or "off"                    |
11463  +---------------------------------------------------------------------*/
11464 int c_trigger ( int argc, char *argv[] )
11465 {
11466    int           macaddr;
11467    int           ichksum, xchksum;
11468    unsigned char *imagep;
11469    unsigned char *image_ptr(void);
11470    char          maclabel[MACRO_LEN + 1];
11471    unsigned int  aflags, bitmap;
11472    char          hc, unit;
11473 
11474    if ( invalidate_for_cm10a() != 0 )
11475       return 1;
11476 
11477    switch ( get_upload_expire() ) {
11478      case NO_RECORD_FILE :
11479         fprintf(stderr, "No schedule has been uploaded by Heyu.\n");
11480         return 1;
11481      default :
11482         break;
11483    }
11484 
11485    if ( argc != 4 || (strcmp(argv[3], "on") != 0 && strcmp(argv[3], "off") != 0) ) {
11486       fprintf(stderr, "Usage: %s trigger Hu on|off\n", argv[0]);
11487       return 1;
11488    }
11489 
11490    aflags = parse_addr(argv[2], &hc, &bitmap);
11491    if ( !(aflags & A_VALID) || (aflags & (A_PLUS | A_MINUS | A_MULT)) || bitmap == 0 ) {
11492       fprintf(stderr, "Invalid Housecode|Unit address %s\n", argv[2]);
11493       return 1;
11494    }
11495    unit = code2unit(single_bmap_unit(bitmap ));
11496 
11497    if ( load_image(&ichksum) == 0 ) {
11498       fprintf(stderr, "Unable to load x10image file.\n");
11499       return 1;
11500    }
11501    imagep = image_ptr();
11502 
11503    if ( locate_triggered_macro(imagep, hc, unit, argv[3], &macaddr) == 0 ) {
11504       fprintf(stderr, "No trigger for '%c%d %s' found in file %s.\n",
11505                    hc, unit, argv[3], pathspec(IMAGE_FILE));
11506       return 1;
11507    }
11508 
11509    if ( lookup_macro(macaddr, maclabel, &xchksum) == 0 ) {
11510       fprintf(stderr, "Unknown macro at triggered address 0x%03x\n", macaddr);
11511       return 1;
11512    }
11513    if ( xchksum != ichksum ) {
11514       fprintf(stderr, "Mismatch between x10image and x10macroxref files.\n");
11515       return 1;
11516    }
11517 
11518    printf("Emulating triggered macro %s\n", maclabel);
11519    fflush(stdout);
11520 
11521    if ( send_macro_immediate(imagep, macaddr) != 0 )
11522       return 1;
11523 
11524    return 0;
11525 }
11526 
11527 /*---------------------------------------------------------------------+
11528  |  Execute the commands in macros in an uploaded schedule for timer   |
11529  |  events scheduled between 0:00 hours and the current time today.    |
11530  +---------------------------------------------------------------------*/
11531 int c_timer_times ( int argc, char *argv[] )
11532 {
11533    time_t        now;
11534    struct tm     *tmp;
11535    int           j, ichksum, size;
11536    unsigned int           day, minutes;
11537    int           (*fptr)() = &cmp_catchup;
11538    unsigned char dow, dow_bmap;
11539    unsigned char *image, *sp, *dp;
11540    unsigned int  beg, end, start, stop, delay;
11541    unsigned int  macstart, macstop, macaddr;
11542    unsigned char *image_ptr(void);
11543    struct catchup_st table[210];
11544    char     maclabel[MACRO_LEN + 1];
11545 
11546    if ( invalidate_for_cm10a() != 0 )
11547       return 1;
11548 
11549    switch ( get_upload_expire() ) {
11550      case NO_RECORD_FILE :
11551         fprintf(stderr, "No schedule has been uploaded by Heyu.\n");
11552         return 1;
11553      case NO_EXPIRATION :
11554         fprintf(stderr, "Uploaded schedule contains no Timers.\n");
11555         return 1;
11556      case BAD_RECORD_FILE :
11557         fprintf(stderr, "X10 Record File has been corrupted.\n");
11558         return 1;
11559      case SCHEDULE_EXPIRED :
11560         fprintf(stderr, "Uploaded schedule has expired.\n");
11561         return 1;
11562      default :
11563         break;
11564    }
11565 
11566    lookup_macro(0, maclabel, &ichksum);
11567    if ( !loadcheck_image(ichksum) ) {
11568       fprintf(stderr, "Unable to load x10image file, ichksum = %d\n", ichksum);
11569       return 1;
11570    }
11571    image = image_ptr();
11572 
11573    time(&now);
11574    tmp = legal_to_cm11a(&now);
11575    minutes = 60 * tmp->tm_hour + tmp->tm_min + ((tmp->tm_sec > 0) ? 1 : 0);
11576 
11577    day = tmp->tm_yday;
11578    dow = 1 << (tmp->tm_wday);
11579 
11580 minutes = 1440;
11581 
11582    sp = image + 2;
11583    size = 0;
11584    while ( *sp != 0xff ) {
11585       dow_bmap = sp[0];
11586       beg = sp[1] | ((sp[4] & 0x80u) << 1);
11587       end = sp[2] | ((sp[5] & 0x80u) << 1);
11588       if ( !(dow & dow_bmap) || day < beg || day > end ) {
11589          sp += 9;
11590          continue;
11591       }
11592       start = 120 * ((sp[3] & 0xf0u) >> 4) + (sp[4] & 0x7fu) +
11593                    SECURITY_OFFSET_ADJUST * ((sp[6] & 0x80u) >> 7);
11594       stop  = 120 * (sp[3] & 0x0fu) + (sp[5] & 0x7fu) +
11595                    SECURITY_OFFSET_ADJUST * ((sp[6] & 0x08u) >> 3);
11596       macstart = sp[7] | ((sp[6] & 0x30u) << 4);
11597       macstop = sp[8] | ((sp[6] & 0x03u) << 8);
11598 
11599       /* Add macro delay to start time */
11600       delay = *(image + macstart);
11601       start += delay;
11602       if ( start < minutes ) {
11603          table[size].start = start;
11604          table[size].macaddr = macstart;
11605          table[size].delay = (delay > 0) ? 1 : 0;
11606          size++;
11607          /* Include any chained macros */
11608          dp = image + macstart;
11609          while ( *(dp += macro_length(dp)) > 0 ) {
11610             start += *dp;
11611             if ( start >= minutes )
11612                break;
11613             table[size].start = start;
11614             table[size].macaddr = dp - image;
11615             table[size].delay = 1;
11616             size++;
11617          }
11618       }
11619 
11620       delay = *(image + macstop);
11621       stop  += delay;
11622       if ( stop < minutes ) {
11623          table[size].start = stop;
11624          table[size].macaddr = macstop;
11625          table[size].delay = (delay > 0) ? 1 : 0;
11626          size++;
11627          /* Include any chained macros */
11628          dp = image + macstop;
11629          while ( *(dp += macro_length(dp)) > 0 ) {
11630             stop += *dp;
11631             if ( stop >= minutes )
11632                break;
11633             table[size].start = stop;
11634             table[size].macaddr = dp - image;
11635             table[size].delay = 1;
11636             size++;
11637          }
11638       }
11639       sp += 9;
11640    }
11641 
11642    if ( size == 0 ) {
11643       fprintf(stderr,
11644         "No macros scheduled for execution before now.\n");
11645       return 0;
11646    }
11647 
11648    /* Sort the table in order of ascending execution times */
11649    qsort((void *)table, size, (sizeof(struct catchup_st)), fptr);
11650 
11651    /* Now display the execution times and macro */
11652    for ( j = 0; j < size; j++ ) {
11653       macaddr = table[j].macaddr;
11654       lookup_macro(macaddr, maclabel, &ichksum);
11655       start = table[j].start;
11656       printf("%02d:%02d  %s\n", start / 60, start % 60, maclabel);
11657    }
11658 
11659    return 0;
11660 }
11661 
11662 /*-----------------------------------------------------------------+
11663  | Determine elapsed minutes from 0:00 hrs Standard Time on Jan 1  |
11664  | of the current year until the next NDSTINTV changes between     |
11665  | Standard/Daylight Time.  Store results in global struct         |
11666  | dststruct array lgls[] and in struct config.                    |
11667  +-----------------------------------------------------------------*/
11668 int get_dst_info ( int startyear )
11669 {
11670    time_t      now, seconds, startsec, jan1sec, jul1sec, epoch ;
11671    struct tm   jantms, jultms, *tmjan, *tmjul, *tms;
11672    int         indx, sindx = 0, j, nintv, val, startval, dstminutes, year;
11673    int         iter, result = -1, restart;
11674    int         offset[2];
11675    long        delta;
11676 
11677    /* Get current date and time */
11678    time(&now) ;
11679    tms = localtime(&now);
11680 
11681    year = tms->tm_year + 1900;
11682    if ( startyear >= 1970 )
11683       year = startyear;
11684 
11685    tms->tm_year = year - 1900;
11686 
11687    /* Get calendar seconds at 0:00 hours Legal Time on Jan 1st of this year */
11688    tms->tm_mon = 0;
11689    tms->tm_mday = 1;
11690    tms->tm_hour = 0;
11691    tms->tm_min = tms->tm_sec = 0;
11692    tms->tm_isdst = -1;
11693 
11694    epoch = mktime(tms);
11695    epoch = ((epoch - std_tzone) / (time_t)86400) * (time_t)86400 + std_tzone;
11696 
11697    tmjan = &jantms;
11698 
11699    for ( j = 0; j < NDSTINTV/2; j++ ) {
11700 
11701       /* Get calendar seconds at 0:00 hours Legal Time on Jan 1st of the year */
11702       tmjan->tm_year = year + j - 1900;
11703       tmjan->tm_mon = 0;
11704       tmjan->tm_mday = 1;
11705       tmjan->tm_hour = 0;
11706       tmjan->tm_min = tmjan->tm_sec = 0;
11707       tmjan->tm_isdst = -1;
11708 
11709       jan1sec = mktime(tmjan);
11710 
11711       tmjul = &jultms;
11712 
11713       /* Calendar seconds at same legal time on July 1st */
11714       tmjul->tm_year = year + j - 1900;
11715       tmjul->tm_mon = 6;
11716       tmjul->tm_mday = 1;
11717       tmjul->tm_hour = 0;
11718       tmjul->tm_min = tmjul->tm_sec = 0;
11719       tmjul->tm_isdst = -1;
11720 
11721       jul1sec = mktime(tmjul);
11722 
11723       /* Reduce difference by full days of 86400 seconds */
11724       dstminutes = (int)((jul1sec - jan1sec) % (time_t)86400 / (time_t)60);
11725       dstminutes = min( dstminutes, 1440 - dstminutes );
11726 
11727       offset[0] = 0;
11728       offset[1] = dstminutes;
11729 
11730       /* Reduce to seconds at 0:00 hours Standard Time */
11731       jan1sec = ((jan1sec - std_tzone) / (time_t)86400) * (time_t)86400 + std_tzone;
11732 
11733       if ( (val = tmjan->tm_isdst) > 0 ) {
11734          /* Daylight time in Southern hemisphere */
11735          indx = 1;
11736          startval = val;
11737       }
11738       else if ( (val = tmjul->tm_isdst) > 0 ) {
11739          /* Daylight time in Northern hemisphere */
11740          indx = 0;
11741          startval = 0;
11742       }
11743       else {
11744          /* Daylight time not in effect this year */
11745          lgls[2 * j].elapsed = lgls[2 * j + 1].elapsed = -1;
11746          lgls[2 * j].offset  = lgls[2 * j + 1].offset = 0;
11747          stds[2 * j].elapsed = stds[2 * j + 1].elapsed = -1;
11748          stds[2 * j].offset  = stds[2 * j + 1].offset = 0;
11749          stdr[2 * j].elapsed = stdr[2 * j + 1].elapsed = -1;
11750          stdr[2 * j].offset  = stdr[2 * j + 1].offset = 0;
11751          continue;
11752       }
11753 
11754       startsec = jan1sec;
11755       for ( nintv = 0; nintv < 2; nintv++ ) {
11756          iter = 0;
11757          result = -1;
11758          restart = 1;
11759          while ( iter < 1000 && !iter_mgr(result, &delta, 30*86400L, &restart) ) {
11760             iter++;
11761             seconds = startsec + (time_t)delta;
11762             tms = localtime(&seconds);
11763             result = (tms->tm_isdst == startval) ? -1 : 1 ;
11764          }
11765          if ( iter > 999 ) {
11766             (void) fprintf(stderr, "convergence error in get_dst_info()\n");
11767             exit(1);
11768          }
11769 
11770          /* Store as elapsed minutes from 0:00 hours Jan 1 Standard Time at   */
11771          /* the start year, adjusted for changeover from daylight to standard */
11772          /* time and for changeover from standard to daylight time.           */
11773 
11774          sindx = 2 * j + nintv;
11775 
11776          lgls[sindx].elapsed = (long)(seconds - epoch)/60L + offset[indx];
11777          lgls[sindx].offset = offset[indx];
11778          if ( UNDEF_TIME == DST_TIME )
11779             stds[sindx].elapsed = lgls[sindx].elapsed;
11780          else
11781             stds[sindx].elapsed = (long)(seconds - epoch)/60L + dstminutes;
11782 
11783          stds[sindx].offset = offset[indx];
11784          stdr[sindx].elapsed = (long)(seconds - epoch)/60L;
11785          stdr[sindx].offset = offset[indx];
11786 
11787          indx = (indx + 1) % 2;
11788          startval = (startval == val) ? 0 : val;
11789          startsec = seconds + (time_t)86400 ;
11790 
11791       }
11792    }
11793 
11794    configp->dstminutes = max(lgls[0].offset, lgls[1].offset);
11795    if ( lgls[0].elapsed > 0 ) {
11796       configp->isdst = 1;
11797    }
11798 
11799 #if 0
11800    for (iter = 0; iter < NDSTINTV; iter++ )
11801       printf("lgls[%d].elapsed = %ld\n", iter, lgls[iter].elapsed);
11802 #endif
11803 
11804    return sindx;
11805 }
11806 
11807