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( ¯o, 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, ¯op, &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, ¯op, &elementp );
7825
7826 /* Combine similar tevents if requested */
7827 if ( configp->combine_events == YES )
7828 combine_similar_tevents( &teventp, ¯op, &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, ¯op, &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, ¯op, &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, ¯op, &elementp );
8090
8091 /* Combine similar tevents if requested */
8092 if ( configp->combine_events == YES )
8093 combine_similar_tevents ( &teventp, ¯op, &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, ¯op, &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, ¯op[j].nelem, ¯op[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