1 /*----------------------------------------------------------------------------+
2  |                                                                            |
3  |                       HEYU Configuration                                   |
4  |       Copyright 2002,2003,2004-2008 Charles W. Sullivan                    |
5  |                                                                            |
6  |                                                                            |
7  | As used herein, HEYU is a trademark of Daniel B. Suthers.                  |
8  | X10, CM11A, and ActiveHome are trademarks of X-10 (USA) Inc.               |
9  | The author is not affiliated with either entity.                           |
10  |                                                                            |
11  | Charles W. Sullivan                                                        |
12  | Co-author and Maintainer                                                   |
13  | Greensboro, North Carolina                                                 |
14  | Email ID: cwsulliv01                                                       |
15  | Email domain: -at- heyu -dot- org                                          |
16  |                                                                            |
17  +----------------------------------------------------------------------------*/
18 
19 /*
20  *   This program is free software: you can redistribute it and/or modify
21  *   it under the terms of the GNU General Public License as published by
22  *   the Free Software Foundation, either version 3 of the License, or
23  *   (at your option) any later version.
24  *
25  *   This program is distributed in the hope that it will be useful,
26  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
27  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  *   GNU General Public License for more details.
29  *
30  *   You should have received a copy of the GNU General Public License
31  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
32  *
33  */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 
40 #include <ctype.h>
41 #include <unistd.h>
42 #if defined(SYSV) || defined(FREEBSD) || defined(OPENBSD)
43 #include <string.h>
44 #else
45 #include <strings.h>
46 #endif
47 
48 #include <syslog.h>
49 #include <time.h>
50 
51 #include "x10.h"
52 #include "process.h"
53 #include "sun.h"
54 #include "x10state.h"
55 
56 extern int line_no, verbose;
57 extern int i_am_relay, i_am_aux, i_am_state, i_am_monitor;
58 extern char heyu_path[PATH_LEN + 1];
59 extern char alt_path[PATH_LEN + 1];
60 extern char heyu_script[PATH_LEN + 1];
61 extern long std_tzone;
62 extern char default_housecode;
63 extern char heyu_config[PATH_LEN + 1];
64 
65 extern int  fetch_x10state ( void );
66 extern int  fetch_x10state_old ( void );
67 extern struct x10global_st x10global;
68 
69 char        statefile[PATH_LEN + 1];
70 char        enginelockfile[PATH_LEN + 1];
71 char        auxfile[PATH_LEN + 1];
72 
73 unsigned int transceive_list[16];
74 
75 unsigned int   ore_ignore[32];
76 int            ore_ignore_size;
77 
78 unsigned int   sec_ignore[32];
79 int            sec_ignore_size;
80 
81 CONFIG config;
82 CONFIG *configp = NULL;
83 
84 enum {
85    OldAlias, Alias, UserSyn, Launcher, Script, Scene, RFBursts,
86    /* Directives with multiple instances allowed */
87    /* must be before 'Separator'.                */
88    Separator,
89    Tty, TtyAux, HouseCode, ForceAddr,
90    NewFormat, ScheduleFile, MaxPParms, RcsTemp, StatusTimeout,
91    SpfTimeout, DispExpMac, AckHails, Mode, ProgramDays,
92    AsIfDate, AsIfTime, CombineEvents, CompressMacros, FebKluge,
93    Latitude, Longitude, DawnOption, DuskOption, DawnSubstitute,
94    DuskSubstitute, MinDawn, MaxDawn, MinDusk, MaxDusk, CheckFiles,
95    ReportPath, ReplDelay, Ignored, ResvTimers, TrigTag, XrefApp,
96    MacTerm, AutoChain, ResOverlap, ModuleTypes, LaunchMode,
97    FunctionMode, LaunchSrc, DefaultModule, ScriptShell, ScriptMode,
98    LogDir, IsDarkOffset, EnvAliasPrefix, DevType, SunMode, Fix5A,
99    AutoFetch, PfailUpdate, BitDelay, DefRFBursts, BurstSpacing,
100    TimerTweak, RFPostDelay, RFFarbDelay, RFFarwDelay, DispRFX, LoopCount,
101    RestrictDims, StateFmt, /* StateDir, */PfailScript, CM11PostDelay,
102    StartEngine, IgnoreSilent, NoSwitch, RespoolPerms, SpoolMax,
103    CheckRILine, RIdisable, SendRetries, ScriptCtrl, Transceive, TransMissp,
104    RFForward, TransDim, StateCtrl, RFFuncMask, RingCtrl, AuxRepcounts,
105    AuxMincountRFX, HideUnchg, HideUnchgInactive, ShowChange, DispRFNoise, ArmMaxDelay, DispRawRF, ArmLogic,
106    InactiveTimeout, HeyuUmask, AutoWait, FullBright, EnginePoll,
107    RfxTscale, RfxVadscale, RfxBPscale, DispSubdir, RfxComDtrRts, RfxHiBaud,
108    RfxPowerScale, RfxWaterScale, RfxGasScale, RfxPulseScale, RfxComEnable, RfxComDisable,
109    LockupCheck, TailPath, RfxJam, DispDmxTemp, SecID16, SecIDPar, LogDateYr,
110    DmxTscale, OreTscale, OreBPscale, OreWgtscale, OreLowBatt, DispOreAll,
111    OreChgBitsT, OreChgBitsRH, OreChgBitsBP, OreChgBitsWgt, OreDataEntry, OreDispChan, OreID16,
112    OreDispFcast, LogDateUnix, InactiveTimeoutOre, DispSensorIntv, DateFormat, LockTimeout,
113    CM11QueryDelay, ElsNumber, ElsVoltage, ElsChgBitsCurr, OreWindscale, OreWindSensorDir,
114    OreRainRatescale, OreRainTotscale, OreDispBatLvl, OreWindDirMode, OreDispCount,
115    OreDispBft, OreChgBitsWsp, OreChgBitsWavsp, OreChgBitsWdir, OreChgBitsRrate, OreChgBitsRtot,
116    OreChgBitsUV, ScanMode, RfxInline,
117    OwlVoltage, OwlCalibPower, OwlCalibEnergy, OwlChgBitsPower, OwlChgBitsEnergy, OwlDispCount,
118    ArmRemote, ActiveChange, InactiveHandling, ProcessXmit, ShowFlagsMode,
119    LaunchPathAppend, LaunchPathPrefix, FixStopStartError, TtyRFXmit, ChkSumTimeout
120 
121 };
122 
123 #define CSTP   1    /* Minimal config for stop command */
124 #define CCAL   2    /* Minimal config for syscheck command */
125 #define CMLT   4    /* Multiple instances allowed */
126 #define COVR   8    /* Allow override in sched file */
127 #define CHLP  16    /* Minimal config for help command */
128 #define CIGN  32    /* Ignored (usually obsolete) */
129 #define CHID  64    /* Don't display */
130 
131 static struct conf {
132    char          *name;
133    int           mintoken;
134    int           maxtoken;
135    unsigned char isparsed;
136    unsigned char override;  /* Allow override in sched file */
137    unsigned int  flags;
138    unsigned int  switchval;
139 } command[] = {
140    {"(_alias_)",            3, 3, 0, 0, CMLT,    OldAlias },
141    {"ALIAS",                3,16, 0, 0, CMLT,    Alias },
142    {"LAUNCHER",             2,-1, 0, 0, CMLT,    Launcher},
143    {"SCRIPT",               2,-1, 0, 0, CMLT,    Script },
144    {"USERSYN",              2,-1, 0, 0, CMLT,    UserSyn },
145    {"SCENE",                2,-1, 0, 0, CMLT,    Scene },
146    {"SECTION",              1,-1, 0, 0, COVR|CMLT, IgnoreSilent},
147    /* Multiple instances OK for switchval above */
148    {"TTY",                  2, 4, 0, 0, CSTP,    Tty },
149    {"TTY_AUX",              3, 5, 0, 0, CSTP,    TtyAux},
150    {"TTY_RFXMIT",           3, 3, 0, 0, CSTP,    TtyRFXmit},
151    {"HOUSECODE",            2, 2, 0, 0, 0,       HouseCode },
152    {"FORCE_ADDR",           2, 2, 0, 0, 0,       ForceAddr },
153    {"NEWFORMAT",            1, 1, 0, 0, CIGN,    Ignored },
154    {"MACROXREF",            2, 2, 0, 0, CIGN,    Ignored },
155    {"SCHEDULE_FILE",        2, 2, 0, 0, 0,       ScheduleFile },
156    {"MAX_PPARMS",           2, 2, 0, 0, 0,       MaxPParms },
157    {"STATUS_TIMEOUT",       2, 2, 0, 0, 0,       StatusTimeout },
158    {"SPF_TIMEOUT",          2, 2, 0, 0, 0,       SpfTimeout },
159    {"RCS_TEMPERATURE",      2, 2, 0, 0, 0,       RcsTemp },
160    {"RCS_DECODE",           2, 2, 0, 0, 0,       RcsTemp },
161    {"DISPLAY_EXP_MACROS",   2, 2, 0, 0, CIGN,    Ignored },
162    {"ACK_HAILS",            2, 2, 0, 0, 0,       AckHails },
163    {"MODE",                 2, 2, 0, 1, COVR,    Mode },
164    {"PROGRAM_DAYS",         2, 2, 0, 1, COVR,    ProgramDays },
165    {"ASIF_DATE",            2, 2, 0, 1, COVR,    AsIfDate },
166    {"ASIF_TIME",            2, 2, 0, 1, COVR,    AsIfTime },
167    {"COMBINE_EVENTS",       2, 2, 0, 1, COVR,    CombineEvents },
168    {"COMPRESS_MACROS",      2, 2, 0, 1, COVR,    CompressMacros },
169    {"FEB_KLUGE",            2, 2, 0, 1, COVR,    FebKluge },
170    {"LATITUDE",             2, 2, 0, 1, COVR,    Latitude },
171    {"LONGITUDE",            2, 2, 0, 1, COVR,    Longitude },
172    {"DAWN_OPTION",          2, 2, 0, 1, COVR,    DawnOption },
173    {"DUSK_OPTION",          2, 2, 0, 1, COVR,    DuskOption },
174    {"DAWN_SUBSTITUTE",      2, 2, 0, 1, COVR|CIGN, Ignored },
175    {"DUSK_SUBSTITUTE",      2, 2, 0, 1, COVR|CIGN, Ignored },
176    {"MIN_DAWN",             2, 2, 0, 1, COVR,    MinDawn },
177    {"MAX_DAWN",             2, 2, 0, 1, COVR,    MaxDawn },
178    {"MIN_DUSK",             2, 2, 0, 1, COVR,    MinDusk },
179    {"MAX_DUSK",             2, 2, 0, 1, COVR,    MaxDusk },
180    {"WRITE_CHECK_FILES",    2, 2, 0, 1, COVR,    CheckFiles },
181    {"REPORT_PATH",          2, 2, 0, 1, COVR,    ReportPath },
182    {"REPL_DELAYED_MACROS",  2, 2, 0, 1, COVR,    ReplDelay },
183    {"RESERVED_TIMERS",      2, 2, 0, 1, COVR|CHID, ResvTimers }, /* WIP */
184    {"TRIGGER_TAG",          2, 2, 0, 1, COVR|CIGN, Ignored },
185    {"XREF_APPEND",          2, 2, 0, 1, COVR|CIGN, Ignored },
186    {"MACTERM",              2, 2, 0, 1, COVR,    MacTerm }, /* WIP */
187    {"RESOLVE_OVERLAP",      2, 2, 0, 1, COVR,    ResOverlap },
188    {"DAWNDUSK_DEF",         2, 3, 0, 1, COVR,    SunMode },
189    {"SCRIPT_MODE",          2, 2, 0, 0, 0,       ScriptMode },
190    {"SCRIPT_SHELL",         2, 2, 0, 0, 0,       ScriptShell },
191    {"LAUNCH_SOURCE",        2, 6, 0, 0, 0,       LaunchSrc },
192    {"DEFAULT_MODULE",       2, 2, 0, 0, 0,       DefaultModule },
193    {"LOG_DIR",              2, 3, 0, 0, 0,       LogDir },
194    {"DISPLAY_SUBDIR",       2, 2, 0, 0, 0,       DispSubdir },
195    {"ISDARK_OFFSET",        2, 2, 0, 0, 0,       IsDarkOffset },
196    {"ENV_ALIAS_PREFIX",     2, 2, 0, 0, 0,       EnvAliasPrefix },
197    {"FIX_5A",               2, 2, 0, 0, CHID,    Fix5A },
198    {"AUTOFETCH",            2, 2, 0, 0, 0,       AutoFetch },
199    {"POWERFAIL_UPDATE",     2, 2, 0, 0, 0,       PfailUpdate },
200    {"CM17A_BIT_DELAY",      2, 2, 0, 0, 0,       BitDelay },
201    {"DEF_RF_BURSTS",        2, 2, 0, 0, 0,       DefRFBursts },
202    {"RF_BURSTS",            3,21, 0, 0, 0,       RFBursts},
203    {"TIMER_LOOPCOUNT",      2, 2, 0, 0, 0,       LoopCount },
204    {"RF_BURST_SPACING",     2, 2, 0, 0, 0,       BurstSpacing },
205    {"RF_TIMER_TWEAK",       2, 2, 0, 0, 0,       TimerTweak },
206    {"RF_POST_DELAY",        2, 2, 0, 0, 0,       RFPostDelay },
207    {"RF_FARB_DELAY",        2, 2, 0, 0, 0,       RFFarbDelay },
208    {"RF_FARW_DELAY",        2, 2, 0, 0, 0,       RFFarwDelay },
209    {"DISPLAY_RF_XMIT",      2, 2, 0, 0, 0,       DispRFX },
210    {"RESTRICT_DIMS",        2, 2, 0, 0, 0,       RestrictDims },
211    {"STATE_FORMAT",         2, 2, 0, 0, 0,       StateFmt },
212    {"RELAY_POWERFAIL_SCRIPT", 2,-1, 0, 0, CHID,   PfailScript},
213    {"CM11_POST_DELAY",      2, 2, 0, 0, 0,       CM11PostDelay},
214    {"START_ENGINE",         2, 2, 0, 0, 0,       StartEngine},
215    {"RF_NOSWITCH",          2, 2, 0, 0, 0,       NoSwitch},
216    {"RESPOOL_PERMISSIONS",  2, 2, 0, 0, CHID,    RespoolPerms},
217    {"SPOOLFILE_MAX",        2, 2, 0, 0, 0,       SpoolMax},
218    {"CHECK_RI_LINE",        2, 2, 0, 0, 0,       CheckRILine},
219    {"RI_DISABLE",           2, 2, 0, 0, 0,       RIdisable},
220    {"SEND_RETRIES",         2, 2, 0, 0, 0,       SendRetries},
221    {"TRANSCEIVE",           2, 3, 0, 0, 0,       Transceive},
222    {"TRANSCIEVE",           2, 3, 0, 0, CHID,    TransMissp},
223    {"RFFORWARD",            2, 3, 0, 0, 0,       RFForward},
224    {"TRANS_DIMLEVEL",       2, 2, 0, 0, 0,       TransDim},
225    {"SCRIPT_CTRL",          2, 2, 0, 0, 0,       ScriptCtrl},
226    {"STATE_CTRL",           2, 2, 0, 0, 0,       StateCtrl},
227    {"RING_CTRL",            2, 2, 0, 0, 0,       RingCtrl},
228    {"RF_FUNCMASK",          2,-1, 0, 0, 0,       RFFuncMask},
229    {"AUX_REPCOUNTS",        4, 4, 0, 0, 0,       AuxRepcounts},
230    {"AUX_MINCOUNT_RFX",     2, 2, 0, 0, 0,       AuxMincountRFX},
231    {"HIDE_UNCHANGED",       2, 2, 0, 0, 0,       HideUnchg},
232    {"HIDE_UNCHANGED_INACTIVE", 2, 2, 0, 0, 0,    HideUnchgInactive},
233    {"SHOW_CHANGE",          2, 2, 0, 0, 0,       ShowChange},
234    {"DISPLAY_RF_NOISE",     2, 2, 0, 0, 0,       DispRFNoise},
235    {"ARM_MAX_DELAY",        2, 2, 0, 0, 0,       ArmMaxDelay},
236    {"DISPLAY_RAW_RF",       2, 2, 0, 0, 0,       DispRawRF},
237    {"ARM_LOGIC",            2, 2, 0, 0, 0,       ArmLogic},
238    {"INACTIVE_TIMEOUT",     2, 2, 0, 0, 0,       InactiveTimeout},
239    {"HEYU_UMASK",           2, 2, 0, 0, 0,       HeyuUmask},
240    {"AUTO_WAIT",            2, 2, 0, 0, 0,       AutoWait},
241    {"FULL_BRIGHT",          2, 2, 0, 0, 0,       FullBright},
242    {"ENGINE_POLL",          2, 2, 0, 0, 0,       EnginePoll},
243    {"DMX_TSCALE",           2, 3, 0, 0, 0,       DmxTscale},
244    {"ORE_LOWBATTERY",       2, 2, 0, 0, 0,       OreLowBatt},
245    {"ORE_TSCALE",           2, 3, 0, 0, 0,       OreTscale},
246    {"ORE_BPSCALE",          3, 4, 0, 0, 0,       OreBPscale},
247    {"ORE_WGTSCALE",         3, 3, 0, 0, 0,       OreWgtscale},
248    {"ORE_WINDSCALE",        3, 3, 0, 0, 0,       OreWindscale},
249    {"ORE_WINDSENSORDIR",    2, 2, 0, 0, 0,       OreWindSensorDir},
250    {"ORE_WINDDIR_MODE",     2, 2, 0, 0, 0,       OreWindDirMode},
251    {"ORE_RAINRATESCALE",    3, 3, 0, 0, 0,       OreRainRatescale},
252    {"ORE_RAINTOTSCALE",     3, 3, 0, 0, 0,       OreRainTotscale},
253    {"RFX_TSCALE",           2, 3, 0, 0, 0,       RfxTscale},
254    {"RFX_VADSCALE",         3, 4, 0, 0, 0,       RfxVadscale},
255    {"RFX_BPSCALE",          3, 4, 0, 0, 0,       RfxBPscale},
256    {"RFXCOM_DTR_RTS",       2, 2, 0, 0, 0,       RfxComDtrRts},
257    {"RFXCOM_HIBAUD",        2, 2, 0, 0, CHID,    RfxHiBaud},
258    {"RFX_POWERSCALE",       3, 3, 0, 0, 0,       RfxPowerScale},
259    {"RFX_WATERSCALE",       3, 3, 0, 0, 0,       RfxWaterScale},
260    {"RFX_GASSCALE",         3, 3, 0, 0, 0,       RfxGasScale},
261    {"RFX_PULSESCALE",       3, 3, 0, 0, 0,       RfxPulseScale},
262    {"RFXCOM_ENABLE",        2, 4, 0, 0, 0,       RfxComEnable},
263    {"RFXCOM_DISABLE",       2,16, 0, 0, 0,       RfxComDisable},
264    {"LOCKUP_CHECK",         2, 2, 0, 0, CHID,    LockupCheck},
265    {"TAILPATH",             2, 2, 0, 0, 0,       TailPath},
266    {"SUPPRESS_RFXJAM",      2, 2, 0, 0, 0,       RfxJam},
267    {"DISPLAY_DMXTEMP",      2, 2, 0, 0, 0,       DispDmxTemp},
268    {"SECURID_16",           2, 2, 0, 0, 0,       SecID16},
269    {"SECURID_PARITY",       2, 2, 0, 0, 0,       SecIDPar},
270    {"LOGDATE_YEAR",         2, 2, 0, 0, 0,       LogDateYr},
271    {"DISPLAY_ORE_ALL",      2, 2, 0, 0, 0,       DispOreAll},
272    {"ORE_CHGBITS_T",        2, 2, 0, 0, 0,       OreChgBitsT},
273    {"ORE_CHGBITS_RH",       2, 2, 0, 0, 0,       OreChgBitsRH},
274    {"ORE_CHGBITS_BP",       2, 2, 0, 0, 0,       OreChgBitsBP},
275    {"ORE_CHGBITS_WGT",      2, 2, 0, 0, 0,       OreChgBitsWgt},
276    {"ORE_CHGBITS_WSP",      2, 2, 0, 0, CIGN,    Ignored},
277    {"ORE_CHGBITS_WINDSP",   2, 2, 0, 0, 0,       OreChgBitsWsp},
278    {"ORE_CHGBITS_WAVSP",    2, 2, 0, 0, CIGN,    Ignored},
279    {"ORE_CHGBITS_WINDAVSP", 2, 2, 0, 0, 0,       OreChgBitsWavsp},
280    {"ORE_CHGBITS_WDIR",     2, 2, 0, 0, CIGN,    Ignored},
281    {"ORE_CHGBITS_WINDDIR",  2, 2, 0, 0, 0,       OreChgBitsWdir},
282    {"ORE_CHGBITS_RRATE",    2, 2, 0, 0, CIGN,    Ignored},
283    {"ORE_CHGBITS_RAINRATE", 2, 2, 0, 0, 0,       OreChgBitsRrate},
284    {"ORE_CHGBITS_RTOT",     2, 2, 0, 0, CIGN,    Ignored},
285    {"ORE_CHGBITS_RAINTOT",  2, 2, 0, 0, 0,       OreChgBitsRtot},
286    {"ORE_CHGBITS_UV",       2, 2, 0, 0, 0,       OreChgBitsUV},
287    {"ORE_DATA_ENTRY",       2, 2, 0, 0, 0,       OreDataEntry},
288    {"ORE_DISPLAY_CHAN",     2, 2, 0, 0, 0,       OreDispChan},
289    {"ORE_DISPLAY_BATLVL",   2, 2, 0, 0, 0,       OreDispBatLvl},
290    {"ORE_DISPLAY_RAW",      2, 2, 0, 0, CIGN,    Ignored},
291    {"ORE_DISPLAY_COUNT",    2, 2, 0, 0, 0,       OreDispCount},
292    {"ORE_DISPLAY_BEAUFORT", 2, 2, 0, 0, 0,       OreDispBft},
293    {"ORE_ID_16",            2, 2, 0, 0, 0,       OreID16},
294    {"ORE_DISPLAY_FCAST",    2, 2, 0, 0, 0,       OreDispFcast},
295    {"LOGDATE_UNIX",         2, 2, 0, 0, 0,       LogDateUnix},
296    {"DISPLAY_SENSOR_INTV",  2, 2, 0, 0, 0,       DispSensorIntv},
297    {"DATE_FORMAT",          2, 3, 0, 1, COVR,    DateFormat},
298    {"LOCK_TIMEOUT",         2, 2, 0, 0, 0,       LockTimeout},
299    {"CM11A_QUERY_DELAY",    2, 2, 0, 0, 0,       CM11QueryDelay},
300    {"ELS_VOLTAGE",          2, 2, 0, 0, 0,       ElsVoltage},
301    {"ELS_CHGBITS_CURR",     2, 2, 0, 0, 0,       ElsChgBitsCurr},
302    {"LAUNCHER_SCANMODE",    2, 2, 0, 0, 0,       ScanMode},
303    {"RFXMETER_SETUP_INLINE", 2, 2, 0, 0, 0,      RfxInline},
304    {"OWL_VOLTAGE",          2, 2, 0, 0, 0,       OwlVoltage},
305    {"OWL_CALIB_POWER",      2, 2, 0, 0, 0,       OwlCalibPower},
306    {"OWL_CALIB_ENERGY",     2, 2, 0, 0, 0,       OwlCalibEnergy},
307    {"OWL_CHGBITS_POWER",    2, 2, 0, 0, 0,       OwlChgBitsPower},
308    {"OWL_CHGBITS_ENERGY",   2, 2, 0, 0, 0,       OwlChgBitsEnergy},
309    {"OWL_DISPLAY_COUNT",    2, 2, 0, 0, 0,       OwlDispCount},
310    {"ARM_REMOTE",           2, 2, 0, 0, 0,       ArmRemote},
311 //   {"ACTIVE_CHANGE",        2, 2, 0, 0, CIGN,    Ignored},
312    {"INACTIVE_HANDLING",    2, 2, 0, 0, 0,       InactiveHandling},
313    {"PROCESS_XMIT",         2, 2, 0, 0, 0,       ProcessXmit},
314    {"SHOW_FLAGS_MODE",      2, 2, 0, 0, 0,       ShowFlagsMode},
315    {"LAUNCHPATH_APPEND",    2, 2, 2, 0, 0,       LaunchPathAppend},
316    {"LAUNCHPATH_PREFIX",    2, 2, 2, 0, 0,       LaunchPathPrefix},
317    {"FIX_STOPSTART_ERROR",  2, 2, 0, 1, COVR,    FixStopStartError},
318 //   {"CHKSUM_TIMEOUT",       2, 2, 0, 0, 0,       ChkSumTimeout},
319 
320 #if 0
321    {"ELS_NUMBER",           2, 2, 0, 0, 0,       ElsNumber},
322 #endif
323 #if 0
324    {"INACTIVE_TIMEOUT_ORE", 2, 2, 0, 0, 0,       InactiveTimeoutOre},  /* WIP */
325 #endif
326 };
327 int ncommands = ( sizeof(command)/sizeof(struct conf) );
328 
329 /* Dawn/Dusk options */
330 static struct ddopt {
331    char     *label;
332    unsigned char value;
333 } dd_option[] = {
334    {"FIRST",    FIRST },
335    {"EARLIEST", EARLIEST },
336    {"LATEST",   LATEST },
337    {"AVERAGE",  AVERAGE },
338    {"MEDIAN",   MEDIAN },
339 };
340 int nddopt = ( sizeof(dd_option)/sizeof(struct ddopt) );
341 
342 /* Launch source options - set default sources of signal */
343 /* permitted to launch a script.                         */
344 static struct lsopt {
345    char      *label;
346    unsigned int value;
347 } ls_option[] = {
348    {"SNDC",     SNDC   }, /* Sent from command line */
349    {"SNDM",     SNDM   }, /* Sent from macro executed by Timer */
350    {"SNDT",     SNDT   }, /* Sent from macro executed by Trigger */
351    {"SNDP",     SNDP   }, /* Sent from a relay powerfail script */
352    {"SNDS",     SNDS   }, /* Sent from a script */
353    {"RCVI",     RCVI   }, /* Received from the interface */
354    {"RCVT",     RCVT   }, /* Trigger which executed a macro */
355    {"SNDA",     SNDA   }, /* Transceived by aux daemon */
356    {"RCVA",     RCVA   }, /* Received via sptty from aux daemon */
357    {"ANYSRC",   LSALL  }, /* Any of the above (SNDS and SNDP not included!) */
358    {"NOSRC",    LSNONE }, /* No sources are the default */
359 };
360 int nlsopt = ( sizeof(ls_option)/sizeof(struct lsopt) );
361 
362 /* CM17A function labels.  Note: CONFIG.rf_bursts[] in    */
363 /* process.h must be at least the size of nrflabels below */
364 static struct cm17a_label {
365    char          *label;
366    unsigned char subcode;
367 } rf_label[] = {
368   {"falloff",    0 },
369   {"flightson",  1 },
370   {"fon",        2 },
371   {"foff",       3 },
372   {"fdim",       4 },
373   {"fbright",    5 },
374   {"flightsoff", 6 },
375   {"_",          7 },
376   {"_",          8 },
377   {"farb",       9 },
378   {"farw",      10 },
379 };
380 int nrflabels = ( sizeof(rf_label)/sizeof(struct cm17a_label) );
381 
382 /*---------------------------------------------------------------------+
383  | Reset the isparsed flags in the conf struct array.                  |
384  +---------------------------------------------------------------------*/
reset_isparsed_flags(void)385 void reset_isparsed_flags ( void )
386 {
387    int j;
388 
389    for ( j = 0; j < ncommands; j++ )
390       command[j].isparsed = 0;
391 
392    return;
393 }
394 
395 /*---------------------------------------------------------------------+
396  | Return 1 if all valid characters within alias label, otherwise 0    |
397  +---------------------------------------------------------------------*/
is_valid_alias_label(char * label,char ** sp)398 int is_valid_alias_label ( char *label, char **sp )
399 {
400    *sp = label;
401    while ( **sp ) {
402       if ( isalnum((int)(**sp)) || strchr("-_.", **sp) ) {
403          (*sp)++;
404 	 continue;
405       }
406       return 0;
407    }
408    return 1;
409 }
410 
411 /*---------------------------------------------------------------------+
412  | Initialize the CONFIG structure with default values.                |
413  +---------------------------------------------------------------------*/
initialize_config(void)414 void initialize_config ( void )
415 {
416    char *sp1;
417    int  j;
418 
419    /* Load default configuration values */
420    configp->read_flag = 0;
421    configp->mode = DEF_MODE;
422    (void) strncpy2(configp->schedfile, DEF_SCHEDFILE, sizeof(config.schedfile) - 1);
423    configp->asif_date = -1;
424    configp->asif_time = -1;
425    configp->program_days_in = DEF_PROGRAM_DAYS;
426    configp->program_days = DEF_PROGRAM_DAYS;
427    configp->combine_events = DEF_COMBINE_EVENTS;
428    configp->compress_macros = DEF_COMPRESS_MACROS;
429    configp->feb_kluge = DEF_FEB_KLUGE;
430    configp->housecode = DEF_HOUSECODE;
431    configp->force_addr = DEF_FORCE_ADDR;
432    configp->loc_flag = 0;
433    configp->dawn_option = DEF_DAWN_OPTION;
434    configp->dusk_option = DEF_DUSK_OPTION;
435    configp->dawn_substitute = DEF_DAWN_SUBSTITUTE;
436    configp->dusk_substitute = DEF_DUSK_SUBSTITUTE;
437    configp->min_dawn = DEF_MIN_DAWN;
438    configp->max_dawn = DEF_MAX_DAWN;
439    configp->min_dusk = DEF_MIN_DUSK;
440    configp->max_dusk = DEF_MAX_DUSK;
441    strncpy2(configp->tty, DEF_TTY, sizeof(config.tty) - 1);
442    configp->ttyaux[0] = '\0';
443    configp->auxhost[0] = '\0';
444    configp->auxport[0] = '\0';
445    configp->suffixaux[0] = '\0';
446    configp->auxdev = 0;
447    configp->newformat = 0;
448    configp->checkfiles = DEF_CHECK_FILES;
449    configp->repl_delay = DEF_REPL_DELAYED_MACROS;
450    configp->reserved_timers = DEF_RESERVED_TIMERS;
451    configp->alias_size = 0;
452    configp->aliasp = NULL;
453    configp->scenep = NULL;
454    configp->scriptp = NULL;
455    configp->launcherp = NULL;
456    configp->pfail_script = NULL;
457    configp->max_pparms = DEF_MAX_PPARMS;
458    configp->rcs_temperature = DEF_RCS_TEMPERATURE;
459    configp->trigger_tag = DEF_TRIGGER_TAG;
460    configp->display_offset = DEF_DISPLAY_OFFSET;
461    configp->xref_append = DEF_XREF_APPEND;
462    configp->ack_hails = DEF_ACK_HAILS;
463    configp->macterm = DEF_MACTERM;
464    configp->status_timeout = DEF_STATUS_TIMEOUT;
465    configp->spf_timeout = DEF_SPF_TIMEOUT;
466    configp->disp_exp_mac = DEF_DISPLAY_EXP_MACROS;
467    configp->module_types = DEF_MODULE_TYPES;
468    configp->launch_mode = DEF_LAUNCH_MODE;
469    configp->function_mode = DEF_FUNCTION_MODE;
470    configp->launch_source = DEF_LAUNCH_SOURCE;
471    configp->res_overlap = DEF_RES_OVERLAP;
472    configp->default_module = lookup_module_type(DEF_DEFAULT_MODULE);
473    configp->script_mode = DEF_SCRIPT_MODE;
474    if ( (sp1 = getenv("SHELL")) != NULL )
475       strncpy2(configp->script_shell, sp1, sizeof(config.script_shell) - 1);
476    else
477       strncpy2(configp->script_shell, "/bin/sh", sizeof(config.script_shell) - 1);
478    *configp->logfile = '\0';
479    configp->logcommon = NO;
480    configp->disp_subdir = NO_ANSWER;
481    configp->isdark_offset = DEF_ISDARK_OFFSET;
482    strncpy2(configp->env_alias_prefix, DEF_ENV_ALIAS_PREFIX, sizeof(config.env_alias_prefix) - 1);
483    configp->sunmode = DEF_SUNMODE;
484    configp->fix_5a = DEF_FIX_5A;
485    configp->cm11_post_delay = DEF_CM11_POST_DELAY;
486    configp->pfail_update = DEF_PFAIL_UPDATE;
487    configp->cm17a_bit_delay = DEF_CM17A_BIT_DELAY;
488    configp->rf_burst_spacing = DEF_RF_BURST_SPACING;
489    configp->rf_timer_tweak = DEF_RF_TIMER_TWEAK;
490    configp->rf_post_delay = DEF_RF_POST_DELAY;
491    configp->rf_farb_delay = DEF_RF_FARB_DELAY;
492    configp->rf_farw_delay = DEF_RF_FARW_DELAY;
493    configp->disp_rf_xmit = DEF_DISP_RF_XMIT;
494    configp->def_rf_bursts = DEF_RF_BURSTS;
495    for ( j = 0; j < nrflabels; j++ )
496       configp->rf_bursts[j] = DEF_RF_BURSTS;
497    configp->timer_loopcount = 0;
498    configp->restrict_dims = DEF_RESTRICT_DIMS;
499    configp->state_format = DEF_STATE_FORMAT;
500    configp->start_engine = DEF_START_ENGINE;
501    configp->rf_noswitch = DEF_RF_NOSWITCH;
502    configp->respool_perms = DEF_RESPOOL_PERMS;
503    configp->spool_max = DEF_SPOOLFILE_MAX;
504    configp->check_RI_line = DEF_CHECK_RI_LINE;
505    configp->send_retries = DEF_SEND_RETRIES;
506    configp->script_ctrl = DEF_SCRIPT_CTRL;
507    configp->transceive = DEF_TRANSCEIVE;
508    configp->rfforward = DEF_RFFORWARD;
509    configp->trans_dim = DEF_TRANS_DIMLEVEL;
510    configp->state_ctrl = DEF_STATE_CTRL;
511    configp->ring_ctrl = DEF_RING_CTRL;
512    configp->heyu_umask = DEF_HEYU_UMASK;
513    configp->log_umask = DEF_LOG_UMASK;
514    configp->rf_funcmask = DEF_RF_FUNCMASK;
515    configp->aux_repcounts[0] = 0;  /* Value set at finalize_config() */
516    configp->aux_repcounts[1] = DEF_AUX_REPCOUNT;
517    configp->aux_repcounts[2] = DEF_AUX_MAXCOUNT;
518    configp->aux_repcounts[3] = DEF_AUX_FLOODREP;
519    configp->aux_mincount_rfx = 0; /* Value set at finalize_config() */
520    configp->hide_unchanged = DEF_HIDE_UNCHANGED;
521    configp->hide_unchanged_inactive = DEF_HIDE_UNCHANGED_INACTIVE;
522    configp->show_change = DEF_SHOW_CHANGE;
523    configp->disp_rf_noise = DEF_DISP_RF_NOISE;
524    configp->arm_max_delay = DEF_ARM_MAX_DELAY;
525    configp->disp_raw_rf = DEF_DISP_RAW_RF;
526    configp->arm_logic = DEF_ARM_LOGIC;
527    configp->inactive_timeout = DEF_INACTIVE_TIMEOUT;
528    configp->device_type = DEF_DEVICE_TYPE;
529    configp->auto_wait = DEF_AUTO_WAIT;
530    configp->full_bright = DEF_FULL_BRIGHT;
531    configp->engine_poll = DEF_ENGINE_POLL;
532    configp->dmx_tscale = DEF_DMX_TSCALE;
533    configp->dmx_toffset = DEF_DMX_TOFFSET;
534    configp->ore_lobat = DEF_ORE_LOWBATTERY;
535    configp->ore_tscale = DEF_ORE_TSCALE;
536    configp->ore_toffset = DEF_ORE_TOFFSET;
537    configp->ore_bpscale = DEF_ORE_BPSCALE;
538    configp->ore_bpoffset = DEF_ORE_BPOFFSET;
539    strncpy2(configp->ore_bpunits, DEF_ORE_BPUNITS, NAME_LEN - 1);
540    configp->ore_wgtscale = DEF_ORE_WGTSCALE;
541    strncpy2(configp->ore_wgtunits, DEF_ORE_WGTUNITS, NAME_LEN - 1);
542    configp->rfx_tscale = DEF_RFX_TSCALE;
543    configp->rfx_toffset = DEF_RFX_TOFFSET;
544    configp->rfx_vadscale = DEF_RFX_VADSCALE;
545    configp->rfx_vadoffset = DEF_RFX_VADOFFSET;
546    strncpy2(configp->rfx_vadunits, DEF_RFX_VADUNITS, NAME_LEN - 1);
547    configp->rfx_bpscale = DEF_RFX_BPSCALE;
548    configp->rfx_bpoffset = DEF_RFX_BPOFFSET;
549    strncpy2(configp->rfx_bpunits, DEF_RFX_BPUNITS, NAME_LEN - 1);
550    configp->rfxcom_dtr_rts = DEF_RFXCOM_DTR_RTS;
551    configp->rfxcom_hibaud = DEF_RFXCOM_HIBAUD;
552    configp->rfx_powerscale = DEF_RFX_POWERSCALE;
553    strncpy2(configp->rfx_powerunits, DEF_RFX_POWERUNITS, NAME_LEN - 1);
554    configp->rfx_waterscale = DEF_RFX_WATERSCALE;
555    strncpy2(configp->rfx_waterunits, DEF_RFX_WATERUNITS, NAME_LEN - 1);
556    configp->rfx_gasscale = DEF_RFX_GASSCALE;
557    strncpy2(configp->rfx_gasunits, DEF_RFX_GASUNITS, NAME_LEN - 1);
558    configp->rfx_pulsescale = DEF_RFX_PULSESCALE;
559    strncpy2(configp->rfx_pulseunits, DEF_RFX_PULSEUNITS, NAME_LEN - 1);
560    configp->rfxcom_enable = DEF_RFXCOM_ENABLE;
561    configp->rfxcom_disable = DEF_RFXCOM_DISABLE;
562    configp->lockup_check = DEF_LOCKUP_CHECK;
563    strncpy2(configp->tailpath, DEF_TAILPATH, sizeof(config.tailpath) - 1);
564    configp->suppress_rfxjam = DEF_SUPPRESS_RFXJAM;
565    configp->display_dmxtemp = DEF_DISPLAY_DMXTEMP;
566    configp->securid_16 = DEF_SECURID_16;
567    configp->securid_mask = DEF_SECURID_MASK;
568    configp->securid_parity = DEF_SECURID_PARITY;
569    configp->logdate_year = DEF_LOGDATE_YEAR;
570    configp->disp_ore_all = DEF_DISPLAY_ORE_ALL;
571    configp->ore_chgbits_t = DEF_ORE_CHGBITS_T;
572    configp->ore_chgbits_rh = DEF_ORE_CHGBITS_RH;
573    configp->ore_chgbits_bp = DEF_ORE_CHGBITS_BP;
574    configp->ore_chgbits_wgt = DEF_ORE_CHGBITS_WGT;
575    configp->ore_chgbits_wsp = DEF_ORE_CHGBITS_WSP;
576    configp->ore_chgbits_wavsp = DEF_ORE_CHGBITS_WAVSP;
577    configp->ore_chgbits_wdir = DEF_ORE_CHGBITS_WDIR;
578    configp->ore_chgbits_rrate = DEF_ORE_CHGBITS_RRATE;
579    configp->ore_chgbits_rtot = DEF_ORE_CHGBITS_RTOT;
580    configp->ore_chgbits_uv = DEF_ORE_CHGBITS_UV;
581    configp->ore_data_entry = DEF_ORE_DATA_ENTRY;
582    configp->ore_display_chan = DEF_ORE_DISPLAY_CHAN;
583    configp->ore_display_batlvl = DEF_ORE_DISPLAY_BATLVL;
584    configp->ore_display_count = DEF_ORE_DISPLAY_COUNT;
585    configp->oreid_16 = DEF_OREID_16;
586    configp->ore_display_fcast = DEF_ORE_DISPLAY_FCAST;
587    configp->oreid_mask = DEF_OREID_MASK;
588    configp->logdate_unix = DEF_LOGDATE_UNIX;
589    configp->inactive_timeout_ore = DEF_INACTIVE_TIMEOUT_ORE;
590    configp->display_sensor_intv = DEF_DISPLAY_SENSOR_INTV;
591    configp->date_format = DEF_DATE_FORMAT;
592    configp->date_separator = DEF_DATE_SEPARATOR;
593    configp->lock_timeout = DEF_LOCK_TIMEOUT;
594    configp->cm11a_query_delay = DEF_CM11A_QUERY_DELAY;
595    configp->els_number = DEF_ELS_NUMBER;
596    configp->els_voltage = DEF_ELS_VOLTAGE;
597    configp->els_chgbits_curr = DEF_ELS_CHGBITS_CURR;
598    configp->ore_windscale = DEF_ORE_WINDSCALE;
599    strcpy(configp->ore_windunits, DEF_ORE_WINDUNITS);
600    configp->ore_windsensordir = DEF_ORE_WINDSENSORDIR;
601    configp->ore_winddir_mode = DEF_ORE_WINDDIR_MODE;
602    configp->ore_rainratescale = DEF_ORE_RAINRATESCALE;
603    strcpy(configp->ore_rainrateunits, DEF_ORE_RAINRATEUNITS);
604    configp->ore_raintotscale = DEF_ORE_RAINTOTSCALE;
605    strcpy(configp->ore_raintotunits, DEF_ORE_RAINTOTUNITS);
606    configp->scanmode = DEF_SCANMODE;
607    configp->rfx_inline = DEF_RFX_INLINE;
608    configp->owl_voltage = DEF_OWL_VOLTAGE;
609    configp->owl_calib_power = DEF_OWL_CALIB_POWER;
610    configp->owl_calib_energy = DEF_OWL_CALIB_ENERGY;
611    configp->owl_chgbits_power = DEF_OWL_CHGBITS_POWER;
612    configp->owl_chgbits_energy = DEF_OWL_CHGBITS_ENERGY;
613    configp->owl_display_count = DEF_OWL_DISPLAY_COUNT;
614    configp->arm_remote = DEF_ARM_REMOTE;
615    configp->active_change = DEF_ACTIVE_CHANGE;
616    configp->inactive_handling = DEF_INACTIVE_HANDLING;
617    configp->process_xmit = DEF_PROCESS_XMIT;
618    configp->show_flags_mode = DEF_SHOW_FLAGS_MODE;
619    configp->rfx_master = DEF_RFX_MASTER;
620    configp->rfx_slave = DEF_RFX_SLAVE;
621    strcpy(configp->launchpath_append, DEF_LAUNCHPATH_APPEND);
622    strcpy(configp->launchpath_prefix, DEF_LAUNCHPATH_PREFIX);
623    configp->fix_stopstart_error = DEF_FIX_STOPSTART_ERROR;
624    configp->ttyrfxmit[0] = '\0';
625    configp->rfxmit_freq = 0;
626    configp->chksum_timeout = DEF_CHKSUM_TIMEOUT;
627 
628    return;
629 }
630 
631 /*---------------------------------------------------------------------+
632  | Parse the users configuration file and save info in CONFIG struct   |
633  | for only the minimal directives required for some specific commands.|
634  | E.g., the 'heyu stop' command needs only the TTY directive.         |
635  +---------------------------------------------------------------------*/
parse_minimal_config(FILE * fd_conf,unsigned char mode,unsigned char source)636 int parse_minimal_config ( FILE *fd_conf, unsigned char mode, unsigned char source )
637 {
638    char   buffer[LINE_LEN];
639    char   *sp1;
640    int    err, errors;
641    CONFIG configtmp;
642 
643    configp = &configtmp;
644 
645    /* Make sure the isparsed flags are reset in the command list */
646    reset_isparsed_flags();
647 
648    /* Load some default configuration values */
649    initialize_config();
650 
651    line_no = 0;
652    errors = 0;
653    while ( fgets(buffer, LINE_LEN, fd_conf) != NULL ) {
654       line_no++ ;
655       buffer[LINE_LEN - 1] = '\0';
656 
657       /* Get rid of comments and blank lines */
658       if ( (sp1 = strchr(buffer, '#')) != NULL )
659          *sp1 = '\0';
660       (void) strtrim(buffer);
661       if ( buffer[0] == '\0' )
662          continue;
663 
664       err = parse_config_tail(buffer, source);
665       if ( err || *error_message() != '\0' ) {
666          fprintf(stderr, "Config Line %02d: %s\n", line_no, error_message());
667          clear_error_message();
668       }
669 
670       errors += err;
671       if ( errors > MAX_ERRORS )
672          return 1;
673    }
674 
675    /* Determine and load the user's timezone */
676    get_std_timezone();
677    configp->tzone = std_tzone;
678 
679    /* Determine if Daylight Time is ever in effect and if so,    */
680    /* the minutes after midnight it goes into and out of effect. */
681    configp->isdst = get_dst_info(0);
682 
683    /* Add configuration items from environment */
684    errors += environment_config();
685 
686    errors += finalize_config(mode);
687 
688    if ( *error_message() != '\0' ) {
689       fprintf(stderr, "%s\n", error_message());
690       clear_error_message();
691    }
692 
693    /* Free the allocated memory in the original config structure */
694    free_all_arrays(&config);
695 
696    /* Copy the temporary config into the global structure */
697    memcpy(&config, configp, sizeof(config));
698 
699    configp = &config;
700 
701    /* Done with config file */
702    line_no = 0;
703 
704    return errors;
705 }
706 
707 
708 /*---------------------------------------------------------------------+
709  | Parse the users configuration file and save info in CONFIG struct.  |
710  | First check to see if it's already been read.                       |
711  +---------------------------------------------------------------------*/
parse_config(FILE * fd_conf,unsigned char mode)712 int parse_config ( FILE *fd_conf, unsigned char mode )
713 {
714    char   buffer[LINE_LEN];
715    char   *sp1;
716    int    err, errors;
717    int    j;
718    SCENE  *scenep;
719    extern char *typename[];
720    CONFIG configtmp;
721 
722 
723    /* If the configuration file has already been read into memory, */
724    /* just return.                                                 */
725    if ( config.read_flag != 0 ) {
726       return 0;
727   }
728 
729    configp = &configtmp;
730 
731    /* Make sure the isparsed flags are reset in the command list */
732    reset_isparsed_flags();
733 
734    /* Load some default configuration values */
735    initialize_config();
736 
737    line_no = 0;
738    errors = 0;
739    while ( fgets(buffer, LINE_LEN, fd_conf) != NULL ) {
740       line_no++ ;
741       buffer[LINE_LEN - 1] = '\0';
742 
743       /* Get rid of comments and blank lines */
744       if ( (sp1 = strchr(buffer, '#')) != NULL )
745          *sp1 = '\0';
746       (void) strtrim(buffer);
747       if ( buffer[0] == '\0' )
748          continue;
749 
750       err = parse_config_tail(buffer, SRC_CONFIG);
751       if ( err || *error_message() != '\0') {
752          if ( !i_am_relay && !i_am_aux && !i_am_monitor )
753             fprintf(stderr, "Config Line %02d: %s\n", line_no, error_message());
754          clear_error_message();
755       }
756 
757       errors += err;
758       if ( errors > MAX_ERRORS )
759          return 1;
760    }
761 
762    /* Everything has now been read from the config file and stored */
763 
764    /* Verify the syntax of user-defined scenes/usersyns */
765    scenep = configp->scenep;
766    j = 0;
767    while ( scenep && scenep[j].line_no > 0 ) {
768       if ( verify_scene(scenep[j].body) != 0 ) {
769          fprintf(stderr, "Config Line %02d: %s '%s': %s\n",
770             scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
771             error_message());
772          clear_error_message();
773          if ( ++errors > MAX_ERRORS )
774             return errors;
775       }
776       else if ( *error_message() != '\0' ) {
777          /* Check warning messages */
778          fprintf(stderr, "Config Line %02d: %s '%s': %s\n",
779             scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
780             error_message());
781          clear_error_message();
782       }
783       j++;
784    }
785 
786    /* Configure module masks */
787    set_module_masks( configp->aliasp );
788 
789    /* Use the Heyu path if the user hasn't provided an alternate */
790    if ( !(*alt_path) )
791       (void)strncpy2(alt_path, heyu_path, sizeof(alt_path) - 1);
792 
793    /* Determine and load the user's timezone */
794    get_std_timezone();
795    configp->tzone = std_tzone;
796 
797    /* Determine if Daylight Time is ever in effect and if so,    */
798    /* the minutes after midnight it goes into and out of effect. */
799    configp->isdst = get_dst_info(0);
800 
801    /* Add configuration items from environment */
802    errors += environment_config();
803 
804    errors += finalize_config(mode);
805    if ( *error_message() != '\0' ) {
806       fprintf(stderr, "%s\n", error_message());
807       clear_error_message();
808    }
809    configp->read_flag = (errors == 0) ? 1 : 0;
810 
811    if ( errors > 0 )
812       return errors;
813 
814    /* Free the allocated memory in the original config structure */
815    free_all_arrays(&config);
816 
817    /* Copy the temporary config into the global structure */
818    memcpy(&config, configp, sizeof(config));
819 
820    configp = &config;
821 
822    /* Done with config file */
823    line_no = 0;
824 
825    return errors;
826 }
827 
828 
829 /*---------------------------------------------------------------------+
830  | Parse the config line in buffer.  Argument 'source' can be          |
831  | SRC_CONFIG if called from parse_config() or SRC_SCHED if called     |
832  | from parse_sched(), for configuration items which may be overridden |
833  | in the latter.                                                      |
834  +---------------------------------------------------------------------*/
parse_config_tail(char * buffer,unsigned char source)835 int parse_config_tail ( char *buffer, unsigned char source )
836 {
837    char   errbuffer[80];
838    char   searchstr[128];
839    char   directive[128];
840    char   label[(NAME_LEN + SCENE_LEN + MACRO_LEN + 1)];
841    char   token[50];
842    char   hc;
843    char   *bufp, *sp;
844    int    errors = 0;
845    int    num, value, hour, minut, len, modtype;
846    long   longvalue;
847    int    bursts;
848    int    j, k, commj, switchval;
849    int    tokc;
850    char   **tokv = NULL;
851    int    perms;
852    double dblvalue;
853 
854    extern struct rfx_disable_st rfx_disable[];
855    extern int nrfxdisable;
856 
857       bufp = buffer;
858       get_token(directive, &bufp, " \t", sizeof(directive)/sizeof(char));
859       strtrim(bufp);
860       strncpy2(searchstr, directive, sizeof(searchstr) - 1);
861       strupper(searchstr);
862 
863 
864       /* Search list of configuration commands starting */
865       /* past "oldalias".                               */
866 
867       commj = 0; switchval = OldAlias;
868       for ( j = 1; j < ncommands; j++ ) {
869          if ( !strcmp(searchstr, command[j].name) ) {
870             switchval = command[j].switchval;
871             commj = j;
872             break;
873          }
874       }
875 
876       /* See if override in schedule file is permitted */
877       if ( (source == SRC_SCHED || source == SRC_ENVIRON) &&
878                (command[commj].flags & COVR) == 0 ) {
879          if ( commj )
880             sprintf(errbuffer, "Configuration directive %s not allowed here.",
881                 command[commj].name);
882          else
883             sprintf(errbuffer, "Invalid configuration directive");
884          store_error_message(errbuffer);
885          return 1;
886       }
887 
888       /* Minimal configurations for some commands */
889       if ( source == SRC_STOP && (command[commj].flags & CSTP) == 0 )
890          return 0;
891       if ( source == SRC_HELP && (command[commj].flags & CHLP) == 0 )
892          return 0;
893       if ( source == SRC_SYSCHECK && (command[commj].flags & CCAL) == 0 )
894          return 0;
895 
896 
897       /* If the directive has a fixed number of tokens (as denoted */
898       /* by maxtokens > 0) or _might_ be an old-style alias,       */
899       /* tokenize the tail immediately.                            */
900 
901       if ( commj == 0 || command[j].maxtoken > 0 ) {
902          tokenize(bufp, " \t", &tokc, &tokv);
903       }
904       else {
905          tokc = (*bufp == '\0') ? 0 : 1 ;
906       }
907 
908 
909       /* If not found in list and it doesn't have the correct */
910       /* number of items for an alias, reject it.             */
911       if ( commj == 0  && tokc != 2 ) {
912          sprintf(errbuffer, "Invalid Directive '%s'", directive);
913          store_error_message(errbuffer);
914          return 1;
915       }
916 
917 
918       /* Unless allowed, check to see it's not a duplicate  */
919       /* of an earlier directive.                           */
920       if ( command[commj].isparsed & source && (command[commj].flags & CMLT) == 0) {
921          sprintf(errbuffer,
922             "Directive '%s' appears more than once in the file", searchstr);
923          store_error_message(errbuffer);
924          return 1;
925       }
926       else {
927          command[commj].isparsed |= source;
928       }
929 
930       /* Check that commands found on the list have the correct */
931       /* number of tokens on the line.                          */
932       if ( (tokc + 1) < command[commj].mintoken ) {
933          store_error_message("Too few items on line.");
934          return 1;
935       }
936       else if ( (command[commj].maxtoken != -1) && ((tokc + 1) > command[commj].maxtoken) ) {
937          store_error_message("Too many items on line.");
938          return 1;
939       }
940 
941       errors = 0;
942       switch ( switchval ) {
943          case Ignored :
944             sprintf(errbuffer,
945                "Directive %s is obsolete and is being ignored.", searchstr);
946             store_error_message(errbuffer);
947             break;
948 
949          case Mode :
950             (void) strupper(tokv[0]);
951             if ( !strcmp(tokv[0], "COMPATIBLE") )
952                configp->mode = COMPATIBLE;
953             else if ( !strcmp(tokv[0], "HEYU") )
954                configp->mode = HEYU_MODE;
955             else {
956                store_error_message("MODE must be COMPATIBLE or HEYU");
957                errors++;
958             }
959             break;
960 
961          case OldAlias :  /* Possible alias, else invalid command */
962             /* Check if it matches the form of an alias */
963             if ( tokc != 2 || strlen(tokv[0]) != 1 ||
964                   (hc = toupper((int)(*tokv[0]))) < 'A' || hc > 'P' ) {
965                store_error_message("Invalid Directive.");
966                errors++;
967                break;
968             }
969 	    if ( !is_valid_alias_label(directive, &sp) ) {
970 	       sprintf(errbuffer, "Invalid character '%c' in alias label.", *sp);
971 	       store_error_message(errbuffer);
972 	       errors++;
973 	       break;
974 	    }
975 
976             if ( strcmp(tokv[0], "macro") == 0 ) {
977                store_error_message("An alias label may not be the word \"macro\".");
978                errors++;
979                break;
980             }
981 
982             if ( strchr("_-", *directive ) ) {
983                store_error_message("Alias labels may not begin with '_' or '-'");
984                errors++;
985                break;
986             }
987 
988             if ( add_alias(&configp->aliasp, directive, line_no,
989                                                  hc, tokv[1], -1) < 0 ) {
990                errors++;
991             }
992             break;
993 
994          case Alias :  /* New alias format using ALIAS directive */
995             /* Expects housecode and unit code(s) to be concatenated, */
996             /* the same as they are for the command line or macro.    */
997 
998 	    if ( !is_valid_alias_label(tokv[0], &sp) ) {
999 	       sprintf(errbuffer, "Invalid character '%c' in alias label.", *sp);
1000 	       store_error_message(errbuffer);
1001 	       errors++;
1002 	       break;
1003 	    }
1004 
1005             if ( strcmp(tokv[0], "macro") == 0 ) {
1006                store_error_message("An alias label may not be the word \"macro\".");
1007                errors++;
1008                break;
1009             }
1010 
1011             if ( strchr("_-", *tokv[0] ) ) {
1012                store_error_message("Alias labels may not begin with '_' or '-'");
1013                errors++;
1014                break;
1015             }
1016 
1017             hc = toupper((int)(*tokv[1]));
1018             *tokv[1] = ' ';
1019             strtrim(tokv[1]);
1020 
1021             modtype = -1;
1022             if ( tokc > 2 ) {
1023                if ( (modtype = lookup_module_type(tokv[2])) < 0 ) {
1024                   sprintf(errbuffer, "Invalid module model '%s'", tokv[2]);
1025                   store_error_message(errbuffer);
1026                   errors++;
1027                   break;
1028                }
1029             }
1030 
1031             if ( (j = add_alias(&configp->aliasp, tokv[0], line_no,
1032                                 hc, tokv[1], modtype)) < 0 ) {
1033                errors++;
1034                break;
1035             }
1036 
1037             if ( modtype >= 0 && tokc >= 3 ) {
1038                if ( add_module_options(configp->aliasp, j, tokv + 3, tokc - 3) != 0 ) {
1039                   errors++;
1040                   break;
1041                }
1042             }
1043 
1044             break;
1045 
1046          case UserSyn :
1047             get_token(label, &bufp, " \t", sizeof(label)/sizeof(char));
1048             if ( add_scene(&configp->scenep, label, line_no, bufp, F_USYN) < 0 ) {
1049                errors++;
1050             }
1051             break;
1052 
1053          case Scene :
1054             get_token(label, &bufp, " \t", sizeof(label)/sizeof(char));
1055             if ( add_scene(&configp->scenep, label, line_no, bufp, F_SCENE) < 0 ) {
1056                errors++;
1057             }
1058             break;
1059 
1060          case AsIfDate :
1061             configp->asif_date = strtol(tokv[0], &sp, 10);
1062             if ( !strchr(" \t\n", *sp) ||
1063 	         configp->asif_date < 19700101 ||
1064                  configp->asif_date > 20380101 )  {
1065                store_error_message("ASIF_DATE must be yyyymmdd between 19700101 and 20380101");
1066                errors++;
1067             }
1068             break;
1069 
1070          case AsIfTime :
1071             num = sscanf(tokv[0], "%d:%d", &hour, &minut) ;
1072             value = 60 * hour + minut ;
1073             configp->asif_time = value;
1074             if ( num != 2 || value < 0 || value > 1439 ) {
1075                store_error_message("ASIF_TIME must be hh:mm between 00:00-23:59");
1076                errors++;
1077             }
1078             break;
1079 
1080          case ScheduleFile :
1081             strncpy2(configp->schedfile, tokv[0], sizeof(config.schedfile) - 1);
1082             break;
1083 
1084          case ProgramDays :
1085             strupper(tokv[0]);
1086             configp->program_days_in = (int)strtol(tokv[0], &sp, 10);
1087 
1088             if ( !strchr(" \t\n", *sp) ||
1089 	       configp->program_days_in < 1 ||
1090 	       configp->program_days_in > 366 ) {
1091                store_error_message("PROGRAM_DAYS outside range 1-366");
1092                errors++;
1093             }
1094             break;
1095 
1096          case CombineEvents :
1097             strupper(tokv[0]);
1098             if ( !strcmp(tokv[0], "YES") )
1099                configp->combine_events = YES;
1100             else if ( !strcmp(tokv[0], "NO") )
1101                configp->combine_events = NO;
1102             else {
1103                store_error_message("COMBINE_EVENTS must be YES or NO");
1104                errors++;
1105             }
1106             break;
1107 
1108          case CompressMacros :
1109             (void) strupper(tokv[0]);
1110             if ( !strcmp(tokv[0], "YES") )
1111                configp->compress_macros = YES;
1112             else if ( !strcmp(tokv[0], "NO") )
1113                configp->compress_macros = NO;
1114             else {
1115                store_error_message("COMPRESS_MACROS must be YES or NO");
1116                errors++;
1117             }
1118             break;
1119 
1120          case FebKluge :
1121             (void) strupper(tokv[0]);
1122             if ( !strcmp(tokv[0], "YES") )
1123                configp->feb_kluge = YES;
1124             else if ( !strcmp(tokv[0], "NO") )
1125                configp->feb_kluge = NO;
1126             else {
1127                store_error_message("FEB_KLUGE must be YES or NO");
1128                errors++;
1129             }
1130             break;
1131 
1132          case Latitude :
1133             errors += parse_latitude(tokv[0]);
1134             break;
1135 
1136          case Longitude :
1137             errors += parse_longitude(tokv[0]);
1138             break;
1139 
1140          case DawnOption :
1141             (void) strupper(tokv[0]);
1142             for ( j = 0; j < nddopt; j++ ) {
1143                if ( !strcmp(tokv[0], dd_option[j].label) )
1144                   break;
1145             }
1146             if ( j == nddopt ) {
1147                store_error_message("Invalid DAWN_OPTION");
1148                errors++;
1149                break;
1150             }
1151             configp->dawn_option = dd_option[j].value;
1152             break;
1153 
1154          case DuskOption :
1155             (void) strupper(tokv[0]);
1156             for ( j = 0; j < nddopt; j++ ) {
1157                if ( !strcmp(tokv[0], dd_option[j].label) )
1158                   break;
1159             }
1160             if ( j == nddopt ) {
1161                store_error_message("Invalid DUSK_OPTION");
1162                errors++;
1163                break;
1164             }
1165             configp->dusk_option = dd_option[j].value;
1166             break;
1167 
1168          case DawnSubstitute :
1169             num = sscanf(tokv[0], "%d:%d", &hour, &minut) ;
1170             value = 60 * hour + minut ;
1171             if ( num != 2 || value < 0 || value > 1439 ) {
1172                store_error_message("DAWN_SUBSTITUTE - must be 00:00-23:59 (hh:mm)");
1173                errors++;
1174                break;
1175             }
1176             configp->dawn_substitute = value;
1177             break;
1178 
1179          case DuskSubstitute :
1180             num = sscanf(tokv[0], "%d:%d", &hour, &minut) ;
1181             value = 60 * hour + minut ;
1182             if ( num != 2 || value < 0 || value > 1439 ) {
1183                store_error_message("DUSK_SUBSTITUTE must be 00:00-23:59 (hh:mm)");
1184                errors++;
1185                break;
1186             }
1187             configp->dusk_substitute = value;
1188             break;
1189 
1190          case MinDawn :
1191             if ( strcmp(strupper(tokv[0]), "OFF") == 0 ) {
1192                configp->min_dawn = OFF;
1193                break;
1194             }
1195             num = sscanf(tokv[0], "%d:%d", &hour, &minut);
1196             value = 60 * hour + minut ;
1197             if ( num != 2 || value < 0 || value > 1439 ) {
1198                store_error_message("MIN_DAWN must be 00:00-23:59 (hh:mm) or OFF");
1199                errors++;
1200                break;
1201             }
1202             configp->min_dawn = value;
1203             break;
1204 
1205          case MaxDawn :
1206             if ( strcmp( strupper(tokv[0]), "OFF") == 0 ) {
1207                configp->max_dawn = OFF;
1208                break;
1209             }
1210             num = sscanf(tokv[0], "%d:%d", &hour, &minut);
1211             value = 60 * hour + minut ;
1212             if ( num != 2 || value < 0 || value > 1439 ) {
1213                store_error_message("MAX_DAWN must be 00:00-23:59 (hh:mm) or OFF");
1214                errors++;
1215                break;
1216             }
1217             configp->max_dawn = value;
1218             break;
1219 
1220          case MinDusk :
1221             if ( strcmp( strupper(tokv[0]), "OFF") == 0 ) {
1222                configp->min_dusk = OFF;
1223                break;
1224             }
1225             num = sscanf(tokv[0], "%d:%d", &hour, &minut);
1226             value = 60 * hour + minut ;
1227             if ( num != 2 || value < 0 || value > 1439 ) {
1228                store_error_message("MIN_DUSK must be 00:00-23:59 (hh:mm) or OFF");
1229                errors++;
1230                break;
1231             }
1232             configp->min_dusk = value;
1233             break;
1234 
1235          case MaxDusk :
1236             if ( strcmp( strupper(tokv[0]), "OFF") == 0 ) {
1237                configp->max_dusk = OFF;
1238                break;
1239             }
1240             num = sscanf(tokv[0], "%d:%d", &hour, &minut);
1241             value = 60 * hour + minut ;
1242             if ( num != 2 || value < 0 || value > 1439 ) {
1243                store_error_message("MAX_DUSK must be 00:00-23:59 (hh:mm) or OFF");
1244                errors++;
1245                break;
1246             }
1247             configp->max_dusk = value;
1248             break;
1249 
1250          case Tty :
1251             (void) strncpy2(configp->tty, tokv[0], sizeof(config.tty) - 1);
1252 
1253 	    for ( j = 1; j < tokc; j++) {
1254 	       strupper(tokv[j]);
1255                if ( strncmp(tokv[j], "CM10A", 4) == 0 ) {
1256 	          configp->device_type |= DEV_CM10A;
1257 	       }
1258 	       else if ( strncmp(tokv[j], "CM17A", 4) == 0 ) {
1259                   configp->device_type |= DEV_CM17A;
1260 	       }
1261 	       else if ( !(strncmp(tokv[j], "CM11A", 4) == 0 ||
1262 			   strncmp(tokv[j], "CM12U", 4) == 0)    ) {
1263 	          sprintf(errbuffer, "Unsupported interface type '%s'", tokv[j]);
1264 		  store_error_message(errbuffer);
1265 	          errors++;
1266 		  break;
1267 	       }
1268 	    }
1269             break;
1270 
1271          case TtyAux :
1272             (void) strncpy2(configp->ttyaux, tokv[0], sizeof(config.ttyaux) - 1);
1273 
1274 	    strupper(tokv[1]);
1275             if ( strncmp(tokv[1], "W800RF32", 8) == 0 ) {
1276 	       configp->auxdev = DEV_W800RF32;
1277 	    }
1278 	    else if ( strncmp(tokv[1], "MR26A", 4) == 0 ) {
1279                configp->auxdev = DEV_MR26A;
1280 	    }
1281 	    else if ( strcmp(tokv[1], "RFXCOM32") == 0 ) {
1282                configp->auxdev = DEV_RFXCOM32;
1283 	    }
1284 	    else if ( strcmp(tokv[1], "RFXCOM") == 0 ) {
1285                configp->auxdev = DEF_DEV_RFXCOM;
1286 	    }
1287 	    else if ( strcmp(tokv[1], "RFXCOMVL") == 0 ) {
1288                configp->auxdev = DEV_RFXCOMVL;
1289 	    }
1290 	    else {
1291 	       sprintf(errbuffer, "Unsupported aux device '%s'", tokv[1]);
1292                store_error_message(errbuffer);
1293 	       errors++;
1294                break;
1295 	    }
1296 
1297 	    if ( ( configp->auxdev == DEV_RFXCOM32 || configp->auxdev == DEV_RFXCOMVL ) && *configp->ttyaux != '/' ) {
1298 	       sp = strchr(tokv[0], ':');
1299 
1300 	       if ( sp ) {
1301 	          j = sp++ - tokv[0];
1302                   if ( j > sizeof(config.auxhost) - 1 )
1303 	             j = sizeof(config.auxhost) - 1;
1304 
1305                   (void) strncpy2(configp->auxhost, tokv[0], j);
1306                   (void) strncpy2(configp->auxport, sp, sizeof(config.auxport) - 1);
1307 	       }
1308 	    }
1309 
1310             if ( configp->auxdev == DEV_RFXCOMVL && tokc > 2 ) {
1311                strupper(tokv[2]);
1312                if ( strcmp(tokv[2], "VISONIC") == 0 )
1313                   configp->rfx_master = VISONIC;
1314                else if ( strcmp(tokv[2], "X10") == 0 )
1315                   configp->rfx_master = RFXX10;
1316                else {
1317                   sprintf(errbuffer, "Invalid RFXCOM receiver type '%s'", tokv[2]);
1318                   store_error_message(errbuffer);
1319                   errors++;
1320                   break;
1321                }
1322                if ( tokc > 3 ) {
1323                   strupper(tokv[3]);
1324                   if ( strcmp(tokv[3], "VISONIC") == 0 )
1325                      configp->rfx_slave = VISONIC;
1326                   else if ( strcmp(tokv[3], "X10") == 0 )
1327                      configp->rfx_slave = RFXX10;
1328                   else {
1329                      sprintf(errbuffer, "Invalid RFXCOM receiver type '%s'", tokv[3]);
1330                      store_error_message(errbuffer);
1331                      errors++;
1332                      break;
1333                   }
1334                }
1335             }
1336 
1337             break;
1338 
1339          case TtyRFXmit :
1340             strncpy2(configp->ttyrfxmit, tokv[0], sizeof(config.ttyrfxmit) - 1);
1341             if ( strncmp(tokv[1], "310", 3) == 0 )
1342                configp->rfxmit_freq = MHZ310;
1343             else if ( strncmp(tokv[1], "433", 3) == 0 )
1344                configp->rfxmit_freq = MHZ433;
1345             else {
1346                store_error_message("TTY_RFXMIT frequency parameter must be 310 or 433");
1347                errors++;
1348                break;
1349             }
1350 
1351             break;
1352 
1353          case HouseCode : /* Base housecode */
1354             hc = toupper((int)(*tokv[0]));
1355             if ( (int)strlen(tokv[0]) > 1 || hc < 'A' || hc > 'Z' ) {
1356                store_error_message("Invalid HOUSECODE - must be A though P");
1357                errors++;
1358                break;
1359             }
1360             configp->housecode = hc;
1361             default_housecode = hc;
1362             break;
1363 
1364          case ForceAddr :
1365             (void) strupper(tokv[0]);
1366             if ( !strcmp(tokv[0], "YES") )
1367                configp->force_addr = YES;
1368             else if ( !strcmp(tokv[0], "NO") )
1369                configp->force_addr = NO;
1370             else {
1371                store_error_message("FORCE_ADDR must be YES or NO");
1372                errors++;
1373             }
1374             break;
1375 
1376          case NewFormat : /* New Format - allow for additional choices in future */
1377             if ( (tokc + 1) == 1 )
1378                configp->newformat = 1;
1379             else
1380                configp->newformat = (unsigned char)strtol(tokv[0], NULL, 10);
1381             break;
1382 
1383          case CheckFiles :
1384             (void) strupper(tokv[0]);
1385             if ( !strcmp(tokv[0], "YES") )
1386                configp->checkfiles = YES;
1387             else if ( !strcmp(tokv[0], "NO") )
1388                configp->checkfiles = NO;
1389             else {
1390                store_error_message("WRITE_CHECK_FILES must be YES or NO");
1391                errors++;
1392             }
1393             break;
1394 
1395          case ReportPath : /* Alternate path for report files */
1396             (void)strncpy2(alt_path, tokv[0], sizeof(alt_path) - 1);
1397             if ( alt_path[strlen(alt_path) - 1] != '/' )
1398                (void)strncat(alt_path, "/", sizeof(alt_path) - 1 - strlen(alt_path));
1399             break;
1400 
1401          case ReplDelay :
1402             (void) strupper(tokv[0]);
1403             if ( !strcmp(tokv[0], "YES") )
1404                configp->repl_delay = YES;
1405             else if ( !strcmp(tokv[0], "NO") )
1406                configp->repl_delay = NO;
1407             else {
1408                store_error_message("REPL_DELAYED_MACROS must be YES or NO");
1409                errors++;
1410             }
1411             break;
1412 
1413          case ResvTimers :
1414             (void) strupper(tokv[0]);
1415             configp->reserved_timers = (int)strtol(tokv[0], &sp, 10);
1416 
1417             if ( !strchr(" \t\n", *sp) ||
1418 	       configp->reserved_timers < 0 || configp->reserved_timers > 50 ) {
1419                store_error_message("RESERVED_TIMERS outside range 0-50");
1420                errors++;
1421             }
1422             break;
1423 
1424          case TrigTag :
1425             (void) strupper(tokv[0]);
1426             if ( !strcmp(tokv[0], "YES") )
1427                configp->trigger_tag = YES;
1428             else if ( !strcmp(tokv[0], "NO") )
1429                configp->trigger_tag = NO;
1430             else {
1431                store_error_message("TRIGGER_TAG must be YES or NO");
1432                errors++;
1433             }
1434             break;
1435 
1436          case MaxPParms :
1437             (void) strupper(tokv[0]);
1438             configp->max_pparms = (int)strtol(tokv[0], &sp, 10);
1439 
1440             if ( !strchr(" \t\n", *sp) ||
1441 	       configp->max_pparms < 1 || configp->max_pparms > 999 ) {
1442                store_error_message("MAX_PPARMS outside range 1-999");
1443                errors++;
1444             }
1445             break;
1446 
1447          case RcsTemp :
1448             /* Housecodes for which Preset commands received from   */
1449             /* thermostats are to be decoded into temperature.      */
1450             /* Information is stored as a housecode bitmap.         */
1451             sp = tokv[0];
1452             (void) strlower(sp);
1453             configp->rcs_temperature = 0;
1454             if ( !strcmp(sp, "off") || !strcmp(sp, "none") ) {
1455                break;
1456             }
1457             if ( !strcmp(sp, "all") || !strcmp(sp, "yes") ) {
1458                configp->rcs_temperature = 0xffff;
1459                break;
1460             }
1461 
1462             len = strlen(sp);
1463             if ( *sp == '[' && *(sp + len - 1) == ']' ) {
1464                /* Create the housecode bitmap */
1465                for ( j = 1; j < len - 1; j++ ) {
1466                   /* Ignore spaces and commas */
1467                   if ( sp[j] == ' ' || sp[j] == ',' )
1468                      continue;
1469                   if ( sp[j] < 'a' || sp[j] > 'p' )
1470                      break;
1471                   configp->rcs_temperature |= (1 << hc2code(sp[j]));
1472                }
1473                if ( j != len - 1 ) {
1474                   store_error_message("Invalid character in RCS_DECODE housecode list");
1475                   errors++;
1476                   break;
1477                }
1478                break;
1479             }
1480             store_error_message("RCS_DECODE must be OFF or [<housecode list>] or ALL");
1481             errors++;
1482             break;
1483 
1484          case TransMissp :
1485             store_error_message("TRANSCEIVE misspelled");
1486          case Transceive :
1487             value = 0;
1488             /* Housecodes to be transceived by the aux daemon */
1489             sp = tokv[0];
1490             (void) strlower(sp);
1491             if ( !strcmp(sp, "all") ) {
1492                configp->transceive = TR_ALL;
1493                break;
1494             }
1495 
1496             if ( !strcmp(sp, "none") ) {
1497                configp->transceive = TR_NONE;
1498                break;
1499             }
1500 
1501             if ( !strcmp(sp, "allexcept") ) {
1502                if ( tokc < 2 ) {
1503                   store_error_message("Too few parameters for TRANSCEIVE ALLEXCEPT");
1504                   errors++;
1505                   break;
1506                }
1507                sp = tokv[1];
1508                strlower(sp);
1509                value = 1;
1510             }
1511 
1512             len = strlen(sp);
1513             if ( *sp == '[' && *(sp + len - 1) == ']' ) {
1514                /* Create the housecode bitmap */
1515                configp->transceive = 0;
1516                for ( j = 1; j < len - 1; j++ ) {
1517                   /* Ignore periods and commas */
1518                   if ( sp[j] == '.' || sp[j] == ',' )
1519                      continue;
1520                   if ( sp[j] < 'a' || sp[j] > 'p' ) {
1521                      store_error_message("Invalid character in TRANSCEIVE housecode list");
1522                      errors++;
1523                      break;
1524                   }
1525                   configp->transceive |= (1 << hc2code(sp[j]));
1526                }
1527                if ( value == 1 )
1528                   configp->transceive = 0xffff & ~configp->transceive;
1529 
1530                break;
1531             }
1532             store_error_message("TRANSCEIVE must be ALL or NONE or ALLEXCEPT [<housecode list>]");
1533             errors++;
1534             break;
1535 
1536          case RFForward :
1537             value = 0;
1538             /* Housecodes to be transceived by the aux daemon */
1539             sp = tokv[0];
1540             (void) strlower(sp);
1541             if ( !strcmp(sp, "all") ) {
1542                configp->rfforward = TR_ALL;
1543                break;
1544             }
1545 
1546             if ( !strcmp(sp, "none") ) {
1547                configp->rfforward = TR_NONE;
1548                break;
1549             }
1550 
1551             if ( !strcmp(sp, "allexcept") ) {
1552                if ( tokc < 2 ) {
1553                   store_error_message("Too few parameters for RFFORWARD ALLEXCEPT");
1554                   errors++;
1555                   break;
1556                }
1557                sp = tokv[1];
1558                strlower(sp);
1559                value = 1;
1560             }
1561 
1562             len = strlen(sp);
1563             if ( *sp == '[' && *(sp + len - 1) == ']' ) {
1564                /* Create the housecode bitmap */
1565                configp->rfforward = 0;
1566                for ( j = 1; j < len - 1; j++ ) {
1567                   /* Ignore periods and commas */
1568                   if ( sp[j] == '.' || sp[j] == ',' )
1569                      continue;
1570                   if ( sp[j] < 'a' || sp[j] > 'p' ) {
1571                      store_error_message("Invalid character in RFFORWARD housecode list");
1572                      errors++;
1573                      break;
1574                   }
1575                   configp->rfforward |= (1 << hc2code(sp[j]));
1576                }
1577                if ( value == 1 )
1578                   configp->rfforward = 0xffff & ~configp->rfforward;
1579 
1580                break;
1581             }
1582             store_error_message("RFFORWARD must be ALL or NONE or ALLEXCEPT [<housecode list>]");
1583             errors++;
1584             break;
1585 
1586 
1587          case TransDim :
1588             configp->trans_dim = (unsigned char)strtol(tokv[0], &sp, 10);
1589 
1590             if ( !strchr(" \t\n", *sp) ||
1591 	       configp->trans_dim < 1 || configp->trans_dim > 22 ) {
1592                store_error_message("TRANS_DIMLEVEL outside range 1-22");
1593                errors++;
1594             }
1595             break;
1596 
1597          case StatusTimeout :
1598             configp->status_timeout = (int)strtol(tokv[0], &sp, 10);
1599 
1600             if ( !strchr(" \t\n", *sp) ||
1601 	       configp->status_timeout < 1 || configp->status_timeout > 5 ) {
1602                store_error_message("STATUS_TIMEOUT outside range 1-5");
1603                errors++;
1604             }
1605             break;
1606 
1607 
1608          case SpfTimeout :
1609             configp->spf_timeout = (int)strtol(tokv[0], &sp, 10);
1610 
1611             if ( !strchr(" \t\n", *sp) ||
1612 	       configp->spf_timeout < 1 || configp->spf_timeout > 10 ) {
1613                store_error_message("SPF_TIMEOUT outside range 1-10");
1614                errors++;
1615             }
1616             break;
1617 
1618          case XrefApp :
1619             (void) strupper(tokv[0]);
1620             if ( !strcmp(tokv[0], "YES") )
1621                configp->xref_append = YES;
1622             else if ( !strcmp(tokv[0], "NO") )
1623                configp->xref_append = NO;
1624             else {
1625                store_error_message("XREF_APPEND must be YES or NO");
1626                errors++;
1627             }
1628             break;
1629 
1630          case AckHails :
1631             (void) strupper(tokv[0]);
1632             if ( !strcmp(tokv[0], "YES") )
1633                configp->ack_hails = YES;
1634             else if ( !strcmp(tokv[0], "NO") )
1635                configp->ack_hails = NO;
1636             else {
1637                store_error_message("ACK_HAILS must be YES or NO");
1638                errors++;
1639             }
1640             break;
1641 
1642          case DispExpMac :
1643             (void) strupper(tokv[0]);
1644             if ( !strcmp(tokv[0], "YES") )
1645                configp->disp_exp_mac = YES;
1646             else if ( !strcmp(tokv[0], "NO") )
1647                configp->disp_exp_mac = NO;
1648             else {
1649                store_error_message("DISPLAY_EXP_MACROS must be YES or NO");
1650                errors++;
1651             }
1652             break;
1653 
1654          case MacTerm :
1655             (void) strupper(tokv[0]);
1656             if ( !strcmp(tokv[0], "YES") )
1657                configp->macterm = YES;
1658             else if ( !strcmp(tokv[0], "NO") )
1659                configp->macterm = NO;
1660             else {
1661                store_error_message("MACTERM must be YES or NO");
1662                errors++;
1663             }
1664             break;
1665 
1666          case AutoChain :
1667             (void) strupper(tokv[0]);
1668             if ( !strcmp(tokv[0], "YES") )
1669                configp->auto_chain = YES;
1670             else if ( !strcmp(tokv[0], "NO") )
1671                configp->auto_chain = NO;
1672             else {
1673                store_error_message("AUTO_CHAIN must be YES or NO");
1674                errors++;
1675             }
1676             break;
1677 
1678          case Launcher :
1679             if ( add_launchers(&configp->launcherp, line_no, bufp) < 0 )
1680                errors++;
1681             break;
1682 
1683          case Script :
1684             if ( add_script(&configp->scriptp, &configp->launcherp, line_no, bufp) < 0 )
1685                errors++;
1686             break;
1687 
1688          case ModuleTypes :
1689             (void) strupper(tokv[0]);
1690             if ( !strcmp(tokv[0], "YES") )
1691                configp->module_types = YES;
1692             else if ( !strcmp(tokv[0], "NO") )
1693                configp->module_types = NO;
1694             else {
1695                store_error_message("MODULE_TYPES must be YES or NO");
1696                errors++;
1697             }
1698             break;
1699 
1700          case FunctionMode :
1701             (void) strupper(tokv[0]);
1702             if ( !strcmp(tokv[0], "ACTUAL") )
1703                configp->function_mode = FUNC_ACTUAL;
1704             else if ( !strcmp(tokv[0], "GENERIC") )
1705                configp->function_mode = FUNC_GENERIC;
1706             else {
1707                store_error_message("FUNCTION_MODE must be ACTUAL or GENERIC");
1708                errors++;
1709             }
1710             break;
1711 
1712          case LaunchMode :
1713             (void) strupper(tokv[0]);
1714             if ( !strcmp(tokv[0], "SIGNAL") )
1715                configp->launch_mode = TMODE_SIGNAL;
1716             else if ( !strcmp(tokv[0], "MODULE") )
1717                configp->launch_mode = TMODE_MODULE;
1718             else {
1719                store_error_message("LAUNCH_MODE must be SIGNAL or MODULE");
1720                errors++;
1721             }
1722             break;
1723 
1724          case LaunchSrc :
1725             value = 0;
1726             for ( j = 0; j < tokc; j++ ) {
1727                strncpy2(token, tokv[j], sizeof(token));
1728                strupper(token);
1729                if ( strcmp(token, "SNDS") == 0 ) {
1730                   sprintf(errbuffer, "LAUNCH_SOURCE '%s' not allowed as a default.", tokv[j]);
1731                   store_error_message(errbuffer);
1732                   errors++;
1733                   break;
1734                }
1735                for ( k = 0; k < nlsopt; k++ ) {
1736                   if ( strcmp(token, ls_option[k].label) == 0 ) {
1737                      value |= ls_option[k].value;
1738                      break;
1739                   }
1740                }
1741                if ( k >= nlsopt ) {
1742                   sprintf(errbuffer, "Invalid LAUNCH_SOURCE option '%s'", tokv[j]);
1743                   store_error_message(errbuffer);
1744                   errors++;
1745                   break;
1746                }
1747             }
1748             if ( value & LSNONE ) {
1749                if ( value & ~LSNONE )
1750                   store_error_message("Warning: LAUNCH_SOURCE 'nosrc' cancels all others on line.");
1751                configp->launch_source = 0;
1752             }
1753             else
1754                configp->launch_source = (unsigned int)value;
1755             break;
1756 
1757          case ResOverlap :
1758             (void) strupper(tokv[0]);
1759             if ( !strcmp(tokv[0], "OLD") )
1760                configp->res_overlap = RES_OVLAP_COMBINED;
1761             else if ( !strcmp(tokv[0], "NEW") )
1762                configp->res_overlap = RES_OVLAP_SEPARATE;
1763             else {
1764                store_error_message("RESOLVE_OVERLAP must be OLD or NEW");
1765                errors++;
1766             }
1767             break;
1768 
1769          case DefaultModule :
1770             if ( (configp->default_module = lookup_module_type(tokv[0])) < 0 ) {
1771                sprintf(errbuffer, "Module type '%s' is unknown.", tokv[0]);
1772                store_error_message(errbuffer);
1773                errors++;
1774             }
1775             break;
1776 
1777          case ScriptShell :
1778             if ( access(tokv[0], X_OK) == 0 ) {
1779               strncpy2(configp->script_shell, tokv[0], sizeof(config.script_shell) - 1);
1780             }
1781             else {
1782               sprintf(errbuffer,
1783                  "An executable shell '%s' is not found.", tokv[0]);
1784               store_error_message(errbuffer);
1785               errors++;
1786             }
1787             break;
1788 
1789          case ScriptMode :
1790             (void) strupper(tokv[0]);
1791             if ( !strcmp(tokv[0], "HEYUHELPER") )
1792                configp->script_mode = HEYU_HELPER;
1793             else if ( !strncmp(tokv[0], "SCRIPT", 6) )
1794                configp->script_mode = HEYU_SCRIPT;
1795             else {
1796                store_error_message("SCRIPT_MODE must be HEYUHELPER or SCRIPT");
1797                errors++;
1798             }
1799             break;
1800 
1801          case LogDir :
1802             strncpy2(token, tokv[0], sizeof(token) - 1);
1803             strupper(token);
1804             if ( strcmp(token, "NONE") == 0 ) {
1805                *configp->logfile = '\0';
1806                break;
1807             }
1808 
1809             strncpy2(token, tokv[0], sizeof(token) - 1);
1810             strcat(token, "/");
1811             if ( check_dir_rw(token, "LOG_DIR") != 0 ) {
1812 #if 0
1813                store_error_message(
1814                   "LOG_DIR does not exist or is not writable.");
1815 #endif
1816                errors++;
1817 	    }
1818 	    else {
1819                sprintf(configp->logfile, "%s/%s",
1820 		 tokv[0], LOGFILE);
1821 	    }
1822 
1823             if ( tokc == 2 ) {
1824                strncpy2(token, tokv[1], sizeof(token) - 1);
1825                strupper(token);
1826                if ( strcmp(token, "COMMON") == 0 ) {
1827                   configp->logcommon = YES;
1828                }
1829                else {
1830                   sprintf(errbuffer,
1831                     "Token '%s' not recognized. Do you mean COMMON ?", tokv[1]);
1832                   store_error_message(errbuffer);
1833                   errors++;
1834                }
1835             }
1836 
1837             break;
1838 
1839          case DispSubdir :
1840             strupper(tokv[0]);
1841             if ( !strcmp(tokv[0], "YES") )
1842                configp->disp_subdir = YES;
1843             else if ( !strcmp(tokv[0], "NO") )
1844                configp->disp_subdir = NO;
1845             else {
1846                store_error_message("DISPLAY_SUBDIR must be YES or NO");
1847                errors++;
1848             }
1849             break;
1850 
1851 
1852          case IsDarkOffset :
1853             configp->isdark_offset = (int)strtol(tokv[0], &sp, 10);
1854 
1855             if ( !strchr(" \t\n", *sp) ||
1856 	         configp->isdark_offset < -360 || configp->isdark_offset > 360 ) {
1857                store_error_message("ISDARK_OFFSET outside range +/-360 minutes");
1858                errors++;
1859             }
1860             break;
1861 
1862 	 case EnvAliasPrefix :
1863 	    strupper(tokv[0]);
1864 	    if ( strcmp(tokv[0], "UC") == 0 )
1865                strncpy2(configp->env_alias_prefix, "X10", sizeof(config.env_alias_prefix) - 1);
1866 	    else if ( strcmp(tokv[0], "LC") == 0 )
1867 	       strncpy2(configp->env_alias_prefix, "x10", sizeof(config.env_alias_prefix) - 1);
1868 	    else {
1869 	       store_error_message("ENV_ALIAS_PREFIX must be UC or LC");
1870 	       errors++;
1871 	    }
1872 	    break;
1873 
1874 	 case SunMode :
1875 	    strupper(tokv[0]);
1876             if ( tokc == 1 ) {
1877 	       if ( strncmp(tokv[0], "RISESET", 1) == 0 ) {
1878 	          configp->sunmode = RiseSet;
1879 		  break;
1880                }
1881 	       else if ( strncmp(tokv[0], "CIVILTWI", 1) == 0 ) {
1882 	          configp->sunmode = CivilTwi;
1883 		  break;
1884                }
1885 	       else if ( strncmp(tokv[0], "NAUTTWI", 1) == 0 ) {
1886 	          configp->sunmode = NautTwi;
1887 		  break;
1888                }
1889 	       else if ( strncmp(tokv[0], "ASTROTWI", 1) == 0 ) {
1890 	          configp->sunmode = AstroTwi;
1891 		  break;
1892                }
1893 	    }
1894             else {
1895 	       sp = NULL;
1896 	       if ( strncmp(tokv[0], "OFFSET", 1) == 0 ) {
1897 	          configp->sunmode = AngleOffset;
1898                   configp->sunmode_offset = (int)strtol(tokv[1], &sp, 10);
1899                }
1900                if ( sp && strchr(" \t\n", *sp) )
1901 	          break;
1902             }
1903 	    store_error_message("DAWNDUSK_DEF must be R, C, N, A or O <int>");
1904 	    errors++;
1905 	    break;
1906 
1907          case Fix5A :
1908             (void) strupper(tokv[0]);
1909             if ( !strcmp(tokv[0], "YES") )
1910                configp->fix_5a = YES;
1911             else if ( !strcmp(tokv[0], "NO") )
1912                configp->fix_5a = NO;
1913             else {
1914                store_error_message("FIX_5A must be YES or NO");
1915                errors++;
1916             }
1917             break;
1918 
1919 	 case CM11PostDelay :
1920 	    configp->cm11_post_delay = (int)strtol(tokv[0], &sp, 10);
1921 	    if ( !strchr(" \t\n", *sp) ||
1922                  configp->cm11_post_delay < 0 ||
1923                  configp->cm11_post_delay > 1000 ) {
1924 	       store_error_message("CM11_POST_DELAY must be 0-1000");
1925 	       errors++;
1926 	    }
1927 	    break;
1928 
1929          case AutoFetch :
1930             (void) strupper(tokv[0]);
1931             if ( !strcmp(tokv[0], "YES") )
1932                configp->autofetch = YES;
1933             else if ( !strcmp(tokv[0], "NO") )
1934                configp->autofetch = NO;
1935             else {
1936                store_error_message("AUTOFETCH must be YES or NO");
1937                errors++;
1938             }
1939             break;
1940 
1941          case PfailUpdate :
1942             (void) strupper(tokv[0]);
1943             if ( !strcmp(tokv[0], "YES") )
1944                configp->pfail_update = YES;
1945             else if ( !strcmp(tokv[0], "NO") )
1946                configp->pfail_update = NO;
1947             else {
1948                store_error_message("POWERFAIL_UPDATE must be YES or NO");
1949                errors++;
1950             }
1951             break;
1952 
1953 	 case BitDelay :
1954 	    configp->cm17a_bit_delay = (int)strtol(tokv[0], &sp, 10);
1955 	    if ( !strchr(" \t\n", *sp) ||
1956                  configp->cm17a_bit_delay < 100 ||
1957                  configp->cm17a_bit_delay > 10000 ) {
1958 	       store_error_message("CM17A_BIT_DELAY must be 100-10000");
1959 	       errors++;
1960 	    }
1961 	    break;
1962 
1963          case BurstSpacing :
1964             configp->rf_burst_spacing = (int)strtol(tokv[0], &sp, 10);
1965 	    if ( !strchr(" \t\n", *sp) ||
1966                  configp->rf_burst_spacing < 80 ||
1967                  configp->rf_burst_spacing > 160 ) {
1968 	       store_error_message("RF_BURST_SPACING must be 80-160");
1969 	       errors++;
1970 	    }
1971 	    break;
1972 
1973          case TimerTweak :
1974             configp->rf_timer_tweak = (int)strtol(tokv[0], &sp, 10);
1975 	    if ( !strchr(" \t\n", *sp) ||
1976                  configp->rf_timer_tweak < 0 ||
1977                  configp->rf_timer_tweak > 50 ) {
1978 	       store_error_message("RF_TIMER_TWEAK must be 0-50");
1979 	       errors++;
1980 	    }
1981 	    break;
1982 
1983 	 case RFPostDelay :
1984 	    configp->rf_post_delay = (int)strtol(tokv[0], &sp, 10);
1985 	    if ( !strchr(" \t\n", *sp) ||
1986                  configp->rf_post_delay < 0 ||
1987                  configp->rf_post_delay > 10000 ) {
1988 	       store_error_message("RF_POST_DELAY must be 0-10000");
1989 	       errors++;
1990 	    }
1991 	    break;
1992 
1993 	 case RFFarbDelay :
1994 	    configp->rf_farb_delay = (int)strtol(tokv[0], &sp, 10);
1995 	    if ( !strchr(" \t\n", *sp) ||
1996                  configp->rf_farb_delay < 0 ||
1997                  configp->rf_farb_delay > 10000 ) {
1998 	       store_error_message("RF_FARB_DELAY must be 0-10000");
1999 	       errors++;
2000 	    }
2001 	    break;
2002 
2003 	 case RFFarwDelay :
2004 	    configp->rf_farw_delay = (int)strtol(tokv[0], &sp, 10);
2005 	    if ( !strchr(" \t\n", *sp) ||
2006                  configp->rf_farw_delay < 0 ||
2007                  configp->rf_farw_delay > 10000 ) {
2008 	       store_error_message("RF_FARW_DELAY must be 0-10000");
2009 	       errors++;
2010 	    }
2011 	    break;
2012 
2013          case DispRFX :
2014             (void) strupper(tokv[0]);
2015             if ( !strcmp(tokv[0], "YES") )
2016                configp->disp_rf_xmit = YES;
2017             else if ( !strcmp(tokv[0], "NO") )
2018                configp->disp_rf_xmit = NO;
2019 	    else if ( !strcmp(tokv[0], "VERBOSE") )
2020 	       configp->disp_rf_xmit = VERBOSE;
2021             else {
2022                store_error_message("DISPLAY_RF_XMIT must be NO or YES or VERBOSE");
2023                errors++;
2024             }
2025             break;
2026 
2027 	 case DefRFBursts :
2028 	    configp->def_rf_bursts = (int)strtol(tokv[0], &sp, 10);
2029 	    if ( !strchr(" \t\n", *sp) ||
2030                  configp->def_rf_bursts < 5 ||
2031                  configp->def_rf_bursts > 6 ) {
2032 	       store_error_message("DEF_RF_BURSTS must be 5 or 6");
2033 	       errors++;
2034 	    }
2035 	    break;
2036 
2037 	 case RFBursts :
2038             if ( tokc % 2 ) {
2039                store_error_message("Missing RF_BURSTS parameter");
2040 	       errors++;
2041 	       break;
2042 	    }
2043 
2044             for ( j = 0; j < tokc; j+=2 ) {
2045   	       bursts = strtol(tokv[j + 1], &sp, 10);
2046 	       if ( !strchr(" \t\r\n", *sp) || bursts < 1 ) {
2047 	          sprintf(errbuffer, "Invalid RF_BURSTS bursts '%s'", tokv[j + 1]);
2048 	          store_error_message(errbuffer);
2049 	          errors++;
2050 	          break;
2051 	       }
2052                strlower(tokv[j]);
2053                for ( k = 0; k < nrflabels; k++ ) {
2054                   if ( !strcmp(tokv[j], rf_label[k].label) ) {
2055                      configp->rf_bursts[rf_label[k].subcode] = bursts;
2056                      break;
2057                   }
2058                }
2059                if ( k >= nrflabels ) {
2060 	          sprintf(errbuffer, "Unknown CM17A function '%s'", tokv[j]);
2061 	          store_error_message(errbuffer);
2062 	          errors++;
2063                   break;
2064 	       }
2065             }
2066 
2067 	    break;
2068 
2069 #if 0
2070 	 case RFBursts :
2071 	    bursts = strtol(tokv[1], &sp, 10);
2072 	    if ( !strchr(" \t\n", *sp) || bursts < 1 ) {
2073 	       store_error_message("Invalid RF_BURSTS");
2074 	       errors++;
2075 	       break;
2076 	    }
2077 	    (void) strlower(tokv[0]);
2078 	    for ( j = 0; j < nrflabels; j++ ) {
2079 	       if ( !strcmp(tokv[0], rf_label[j].label) ) {
2080 	          configp->rf_bursts[rf_label[j].subcode] = bursts;
2081 		  break;
2082 	       }
2083 	    }
2084 	    if ( j >= nrflabels ) {
2085 	       sprintf(errbuffer, "Unknown CM17A function '%s'", tokv[0]);
2086 	       store_error_message(errbuffer);
2087 	       errors++;
2088 	    }
2089 	    break;
2090 #endif
2091 
2092 	 case LoopCount :
2093 	    configp->timer_loopcount = (unsigned long)strtol(tokv[0], &sp, 10);
2094 	    if ( !strchr(" \t\n", *sp) ) {
2095 	       store_error_message("Invalid TIMER_LOOPCOUNT");
2096 	       errors++;
2097 	       break;
2098 	    }
2099 	    break;
2100 
2101 	 case RestrictDims :
2102             (void) strupper(tokv[0]);
2103             if ( !strcmp(tokv[0], "YES") )
2104                configp->restrict_dims = YES;
2105             else if ( !strcmp(tokv[0], "NO") )
2106                configp->restrict_dims = NO;
2107             else {
2108                store_error_message("RESTRICT_DIMS must be YES or NO");
2109                errors++;
2110             }
2111             break;
2112 
2113 	 case StateFmt :
2114             (void) strupper(tokv[0]);
2115             if ( !strcmp(tokv[0], "NEW") )
2116                configp->state_format = NEW;
2117             else if ( !strcmp(tokv[0], "OLD") )
2118                configp->state_format = OLD;
2119             else {
2120                store_error_message("STATE_FORMAT must be NEW or OLD");
2121                errors++;
2122             }
2123             break;
2124 
2125          case PfailScript :
2126             if ( !(configp->pfail_script = strdup(bufp)) ) {
2127                store_error_message("Memory allocation error - out of memory");
2128                errors++;
2129             }
2130             break;
2131 
2132 	 case StartEngine :
2133             (void) strupper(tokv[0]);
2134             if ( !strncmp(tokv[0], "MANUAL", 3) )
2135                configp->start_engine = MANUAL;
2136             else if ( !strncmp(tokv[0], "AUTOMATIC", 4) )
2137                configp->start_engine = AUTOMATIC;
2138             else {
2139                store_error_message("START_ENGINE must be MANUAL or AUTO");
2140                errors++;
2141             }
2142             break;
2143 
2144 	 case IgnoreSilent :
2145             break;
2146 
2147 	 case NoSwitch :
2148             (void) strupper(tokv[0]);
2149             if ( !strcmp(tokv[0], "YES") )
2150                configp->rf_noswitch = YES;
2151             else if ( !strcmp(tokv[0], "NO") )
2152                configp->rf_noswitch = NO;
2153             else {
2154                store_error_message("RF_NOSWITCH must be YES or NO");
2155                errors++;
2156             }
2157             break;
2158 
2159          case RespoolPerms :
2160 #ifndef RESPOOL
2161             store_error_message(
2162               "The RESPOOL_PERMISSIONS directive is invalid for this operating system");
2163             errors++;
2164             break;
2165 #endif
2166             perms = (int)strtol(tokv[0], &sp, 8);
2167             if ( !strchr(" \t\n", *sp) || perms < 0 || perms > 07777) {
2168                store_error_message("RESPOOL_PERMISSIONS - invalid octal number");
2169                errors++;
2170             }
2171             configp->respool_perms = (unsigned int)perms;
2172             break;
2173 
2174 	 case SpoolMax :
2175 	    configp->spool_max = strtol(tokv[0], &sp, 10);
2176 	    if ( !strchr(" \t\n", *sp) ||
2177                  configp->spool_max < SPOOLFILE_ABSMIN ||
2178                  configp->spool_max > SPOOLFILE_ABSMAX ) {
2179                sprintf(errbuffer, "SPOOLFILE_MAX must be between %ul and %ul",
2180                  SPOOLFILE_ABSMIN,  SPOOLFILE_ABSMAX);
2181 	       store_error_message(errbuffer);
2182 	       errors++;
2183 	    }
2184 	    break;
2185 
2186 	 case CheckRILine :
2187             (void) strupper(tokv[0]);
2188             if ( !strcmp(tokv[0], "YES") )
2189                configp->check_RI_line = YES;
2190             else if ( !strcmp(tokv[0], "NO") )
2191                configp->check_RI_line = NO;
2192             else {
2193                store_error_message("CHECK_RI_LINE must be YES or NO");
2194                errors++;
2195             }
2196             break;
2197 
2198 	 case RIdisable :
2199             (void) strupper(tokv[0]);
2200             if ( !strcmp(tokv[0], "YES") )
2201                store_error_message("Obsolete - use RING_CTRL DISABLE");
2202             else if ( !strcmp(tokv[0], "NO") )
2203                store_error_message("Obsolete - use RING_CTRL ENABLE");
2204             else {
2205                store_error_message("Obsolete - Use RING_CTRL ENABLE or DISABLE");
2206             }
2207             errors++;
2208             break;
2209 
2210 	 case RingCtrl :
2211             (void) strupper(tokv[0]);
2212             if ( !strcmp(tokv[0], "ENABLE") )
2213                configp->ring_ctrl = ENABLE;
2214             else if ( !strcmp(tokv[0], "DISABLE") )
2215                configp->ring_ctrl = DISABLE;
2216             else {
2217                store_error_message("RING_CTRL must be ENABLE or DISABLE");
2218                errors++;
2219             }
2220             break;
2221 
2222          case SendRetries :
2223 	    configp->send_retries = (int)strtol(tokv[0], &sp, 10);
2224 	    if ( !strchr(" \t\n", *sp) ||
2225                  configp->send_retries < 0 ) {
2226 	       store_error_message("SEND_RETRIES must be > 0");
2227 	       errors++;
2228 	    }
2229 	    break;
2230 
2231 	 case ScriptCtrl :
2232             (void) strupper(tokv[0]);
2233             if ( !strcmp(tokv[0], "ENABLE") )
2234                configp->script_ctrl = ENABLE;
2235             else if ( !strcmp(tokv[0], "DISABLE") )
2236                configp->script_ctrl = DISABLE;
2237             else {
2238                store_error_message("SCRIPT_CTRL must be ENABLE or DISABLE");
2239                errors++;
2240             }
2241             break;
2242 
2243 	 case StateCtrl :
2244             (void) strupper(tokv[0]);
2245             if ( !strcmp(tokv[0], "SINGLE") )
2246                configp->state_ctrl = SC_SINGLE;
2247             else if ( !strcmp(tokv[0], "BITMAP") )
2248                configp->state_ctrl = SC_BITMAP;
2249             else {
2250                store_error_message("STATE_CTRL must be SINGLE or BITMAP");
2251                errors++;
2252             }
2253             break;
2254 
2255          case RFFuncMask :
2256             store_error_message("RF_FUNCMASK not yet implemented");
2257             errors++;
2258             break;
2259 
2260          case HideUnchg :
2261             (void) strupper(tokv[0]);
2262             if ( !strcmp(tokv[0], "YES") )
2263                configp->hide_unchanged = YES;
2264             else if ( !strcmp(tokv[0], "NO") )
2265                configp->hide_unchanged = NO;
2266             else {
2267                store_error_message("HIDE_UNCHANGED must be YES or NO");
2268                errors++;
2269             }
2270             break;
2271 
2272          case HideUnchgInactive :
2273             (void) strupper(tokv[0]);
2274             if ( !strcmp(tokv[0], "YES") )
2275                configp->hide_unchanged_inactive = YES;
2276             else if ( !strcmp(tokv[0], "NO") )
2277                configp->hide_unchanged_inactive = NO;
2278             else {
2279                store_error_message("HIDE_UNCHANGED_INACTIVE must be YES or NO");
2280                errors++;
2281             }
2282             break;
2283 
2284          case ShowChange :
2285             (void) strupper(tokv[0]);
2286             if ( !strcmp(tokv[0], "YES") )
2287                configp->show_change = YES;
2288             else if ( !strcmp(tokv[0], "NO") )
2289                configp->show_change = NO;
2290             else {
2291                store_error_message("SHOW_CHANGE must be YES or NO");
2292                errors++;
2293             }
2294             break;
2295 
2296          case AuxRepcounts :
2297             for ( j = 0; j < 3; j++ ) {
2298                value = (int)strtol(tokv[j], &sp, 10);
2299                if ( strchr(" \t\n", *sp) == NULL || value < 0 ) {
2300                   store_error_message("Invalid AUX_REPCOUNTS");
2301                   errors++;
2302                   break;
2303                }
2304                configp->aux_repcounts[j] = value;
2305             }
2306             if ( configp->aux_repcounts[0] < 1 ) {
2307                store_error_message("AUX_REPCOUNTS <min count> value must be greater than zero");
2308                errors++;
2309                break;
2310             }
2311             break;
2312 
2313          case AuxMincountRFX :
2314             value = (int)strtol(tokv[0], &sp, 10);
2315             if ( strchr(" \t\n", *sp) == NULL || value < 1 || value > 3 ) {
2316                store_error_message("AUX_MINCOUNT_RFX out of range 1-3");
2317                errors++;
2318                break;
2319             }
2320             configp->aux_mincount_rfx = value;
2321             break;
2322 
2323          case DispRFNoise :
2324             (void) strupper(tokv[0]);
2325             if ( !strcmp(tokv[0], "YES") )
2326                configp->disp_rf_noise = YES;
2327             else if ( !strcmp(tokv[0], "NO") )
2328                configp->disp_rf_noise = NO;
2329             else {
2330                store_error_message("DISPLAY_RF_NOISE must be YES or NO");
2331                errors++;
2332             }
2333             break;
2334 
2335          case ArmMaxDelay :
2336             if ( (longvalue = parse_hhmmss(tokv[0], 3)) >= 0 && longvalue < 43200 ) {
2337                configp->arm_max_delay = (int)longvalue;
2338             }
2339             else {
2340                store_error_message("Invalid ARM_MAX_DELAY");
2341                errors++;
2342             }
2343 	    break;
2344 
2345           case ArmLogic :
2346             strupper(tokv[0]);
2347             configp->arm_logic =
2348                (strcmp(tokv[0], "STRICT") == 0) ?  ArmLogicStrict :
2349                (strcmp(tokv[0], "MEDIUM") == 0) ?  ArmLogicMedium :
2350                (strcmp(tokv[0], "LOOSE")  == 0) ?  ArmLogicLoose  : 0;
2351 
2352             if ( configp->arm_logic == 0 ) {
2353                store_error_message("ARM_LOGIC must be STRICT, MEDIUM or LOOSE");
2354                errors++;
2355             }
2356 	    break;
2357 
2358          case InactiveTimeout :
2359             if ( (longvalue = parse_hhmmss(tokv[0], 3)) >= 0 && longvalue < 86400 ) {
2360                configp->inactive_timeout = longvalue;
2361             }
2362             else {
2363                store_error_message("Invalid INACTIVE_TIMEOUT");
2364                errors++;
2365             }
2366 	    break;
2367 
2368          case InactiveTimeoutOre :
2369             if ( (longvalue = parse_hhmmss(tokv[0], 3)) >= 0 && longvalue < 86400 ) {
2370                configp->inactive_timeout_ore = longvalue;
2371             }
2372             else {
2373                store_error_message("Invalid INACTIVE_TIMEOUT_ORE");
2374                errors++;
2375             }
2376 	    break;
2377 
2378          case DispRawRF :
2379             (void) strupper(tokv[0]);
2380 
2381             if ( !strcmp(tokv[0], "NONE") )
2382                configp->disp_raw_rf = DISPMODE_RF_NONE;
2383             else if ( !strcmp(tokv[0], "NOISE") )
2384                configp->disp_raw_rf = DISPMODE_RF_NOISE;
2385             else if ( !strcmp(tokv[0], "ALL") )
2386                configp->disp_raw_rf = (DISPMODE_RF_NORMAL | DISPMODE_RF_NOISE);
2387             else {
2388                store_error_message("DISPLAY_RAW_RF must be NONE or NOISE or ALL");
2389                errors++;
2390             }
2391             break;
2392 
2393          case HeyuUmask :
2394             if ( strcmp(strupper(tokv[0]), "OFF") == 0 ) {
2395                configp->heyu_umask = -1;
2396             }
2397             else {
2398                configp->heyu_umask = (int)strtol(tokv[0], &sp, 8);
2399                if ( !strchr(" \t\r\n", *sp) || configp->heyu_umask < 0 ) {
2400                   store_error_message("Invalid HEYU_UMASK value.");
2401                   errors++;
2402                }
2403             }
2404             break;
2405 
2406          case AutoWait :
2407             configp->auto_wait = (int)strtol(tokv[0], &sp, 10);
2408             if ( !strchr(" \t\n\r", *sp) ||
2409                  configp->auto_wait < 0 || configp->auto_wait > 300 ) {
2410                store_error_message("AUTO_WAIT timeout must be 0-300 seconds.");
2411                errors++;
2412             }
2413             break;
2414 
2415          case FullBright :
2416             value = (int)strtol(tokv[0], &sp, 10);
2417             if ( !strchr(" \t\n\r", *sp) || value < 16 || value > 31 ) {
2418                store_error_message("FULL_BRIGHT level must be 16 though 31");
2419                errors++;
2420             }
2421             configp->full_bright = (unsigned char)value;
2422             break;
2423 
2424          case EnginePoll :
2425             configp->engine_poll = strtol(tokv[0], &sp, 10);
2426             if ( !strchr(" \t\n\r", *sp) ||
2427                  configp->engine_poll < 100L ||
2428                  configp->engine_poll > 1000000L ) {
2429                store_error_message("ENGINE_POLL must be 100 though 1000000");
2430                errors++;
2431             }
2432             break;
2433 
2434          case DmxTscale :
2435             strupper(tokv[0]);
2436             if ( strncmp(tokv[0], "FAHRENHEIT", 1) == 0 )
2437                configp->dmx_tscale = 'F';
2438             else if ( strncmp(tokv[0], "CELSIUS", 1) == 0 )
2439                configp->dmx_tscale = 'C';
2440             else if ( strncmp(tokv[0], "KELVIN", 1) == 0 )
2441                configp->dmx_tscale = 'K';
2442             else if ( strncmp(tokv[0], "RANKINE", 1) == 0 )
2443                configp->dmx_tscale = 'R';
2444             else {
2445                store_error_message("DMX_TSCALE must be C, F, K, or R");
2446                errors++;
2447             }
2448             if ( tokc > 1 ) {
2449                configp->dmx_toffset = strtod(tokv[1], &sp);
2450                if ( !strchr(" \t\n", *sp) ) {
2451                   store_error_message("Invalid DMX_TSCALE offset.");
2452                   errors++;
2453                }
2454             }
2455             break;
2456 
2457          case OreLowBatt :
2458             value = (int)strtol(tokv[0], &sp, 10);
2459             if ( !strchr(" %\t\n\r", *sp) || value < 10 || value > 90 ) {
2460                store_error_message("ORE_LOWBATTERY level must be 10 though 90 percent");
2461                errors++;
2462             }
2463             configp->ore_lobat = (unsigned char)value;
2464             break;
2465 
2466 
2467          case OreTscale :
2468             strupper(tokv[0]);
2469             if ( strncmp(tokv[0], "FAHRENHEIT", 1) == 0 )
2470                configp->ore_tscale = 'F';
2471             else if ( strncmp(tokv[0], "CELSIUS", 1) == 0 )
2472                configp->ore_tscale = 'C';
2473             else if ( strncmp(tokv[0], "KELVIN", 1) == 0 )
2474                configp->ore_tscale = 'K';
2475             else if ( strncmp(tokv[0], "RANKINE", 1) == 0 )
2476                configp->ore_tscale = 'R';
2477             else {
2478                store_error_message("ORE_TSCALE must be C, F, K, or R");
2479                errors++;
2480             }
2481             if ( tokc > 1 ) {
2482                configp->ore_toffset = strtod(tokv[1], &sp);
2483                if ( !strchr(" \t\n", *sp) ) {
2484                   store_error_message("Invalid ORE_TSCALE offset.");
2485                   errors++;
2486                }
2487             }
2488             break;
2489 
2490          case OreBPscale :
2491             strncpy2(configp->ore_bpunits, tokv[0], NAME_LEN - 1);
2492             configp->ore_bpscale = strtod(tokv[1], &sp);
2493             if ( !strchr(" \t\n", *sp) ) {
2494                store_error_message("Invalid ORE_BPSCALE scale factor.");
2495                errors++;
2496                break;
2497             }
2498             if ( tokc > 2 ) {
2499                configp->ore_bpoffset = strtod(tokv[2], &sp);
2500                if ( !strchr(" \t\n", *sp) ) {
2501                   store_error_message("Invalid ORE_BPSCALE offset.");
2502                   errors++;
2503                   break;
2504                }
2505             }
2506             break;
2507 
2508          case OreWgtscale :
2509             strncpy2(configp->ore_wgtunits, tokv[0], NAME_LEN - 1);
2510             configp->ore_wgtscale = strtod(tokv[1], &sp);
2511             if ( !strchr(" \t\n", *sp) ) {
2512                store_error_message("Invalid ORE_WGTSCALE scale factor.");
2513                errors++;
2514             }
2515             break;
2516 
2517          case OreWindscale :
2518             strncpy2(configp->ore_windunits, tokv[0], NAME_LEN - 1);
2519             configp->ore_windscale = strtod(tokv[1], &sp);
2520             if ( !strchr(" \t\r\n", *sp) ) {
2521                store_error_message("Invalid ORE_WINDSCALE scale factor.");
2522                errors++;
2523             }
2524             break;
2525 
2526          case OreWindSensorDir :
2527             dblvalue = strtod(tokv[0], &sp);
2528             if ( !strchr(" \t\r\n", *sp) || dblvalue < -359.6 || dblvalue > 359.9 ) {
2529                store_error_message("Invalid ORE_WINDSENSORDIR angle.");
2530                errors++;
2531             }
2532             /* Store as integer decidegrees */
2533             configp->ore_windsensordir = (int)(dblvalue * 10.0);
2534             break;
2535 
2536          case OreWindDirMode :
2537             strupper(tokv[0]);
2538             if ( !strcmp(tokv[0], "POINTS") )
2539                configp->ore_winddir_mode = COMPASS_POINTS;
2540             else if ( !strcmp(tokv[0], "ANGLE") )
2541                configp->ore_winddir_mode = COMPASS_ANGLE;
2542             else if ( !strcmp(tokv[0], "BOTH") )
2543                configp->ore_winddir_mode = (COMPASS_POINTS|COMPASS_ANGLE);
2544             else {
2545                store_error_message("ORE_WINDDIR_MODE must be POINTS, ANGLE, or BOTH.");
2546                errors++;
2547             }
2548             break;
2549 
2550          case OreRainRatescale :
2551             strncpy2(configp->ore_rainrateunits, tokv[0], NAME_LEN - 1);
2552             configp->ore_rainratescale = strtod(tokv[1], &sp);
2553             if ( !strchr(" \t\r\n", *sp) ) {
2554                store_error_message("Invalid ORE_RAINRATESCALE scale factor.");
2555                errors++;
2556             }
2557             break;
2558 
2559          case OreRainTotscale :
2560             strncpy2(configp->ore_raintotunits, tokv[0], NAME_LEN - 1);
2561             configp->ore_raintotscale = strtod(tokv[1], &sp);
2562             if ( !strchr(" \t\r\n", *sp) ) {
2563                store_error_message("Invalid ORE_RAINTOTSCALE scale factor.");
2564                errors++;
2565             }
2566             break;
2567 
2568          case RfxTscale :
2569             strupper(tokv[0]);
2570             if ( strncmp(tokv[0], "FAHRENHEIT", 1) == 0 )
2571                configp->rfx_tscale = 'F';
2572             else if ( strncmp(tokv[0], "CELSIUS", 1) == 0 )
2573                configp->rfx_tscale = 'C';
2574             else if ( strncmp(tokv[0], "KELVIN", 1) == 0 )
2575                configp->rfx_tscale = 'K';
2576             else if ( strncmp(tokv[0], "RANKINE", 1) == 0 )
2577                configp->rfx_tscale = 'R';
2578             else {
2579                store_error_message("RFX_TSCALE must be C, F, K, or R");
2580                errors++;
2581             }
2582             if ( tokc > 1 ) {
2583                configp->rfx_toffset = strtod(tokv[1], &sp);
2584                if ( !strchr(" \t\n", *sp) ) {
2585                   store_error_message("Invalid RFX_TSCALE offset.");
2586                   errors++;
2587                }
2588             }
2589             break;
2590 
2591         case RfxVadscale :
2592             strncpy2(configp->rfx_vadunits, tokv[0], NAME_LEN - 1);
2593             configp->rfx_vadscale = strtod(tokv[1], &sp);
2594             if ( !strchr(" \t\n", *sp) ) {
2595                store_error_message("Invalid RFX_VADSCALE scale factor.");
2596                errors++;
2597                break;
2598             }
2599             if ( tokc > 2 ) {
2600                configp->rfx_vadoffset = strtod(tokv[2], &sp);
2601                if ( !strchr(" \t\n", *sp) ) {
2602                   store_error_message("Invalid RFX_VADSCALE offset.");
2603                   errors++;
2604                   break;
2605                }
2606             }
2607             break;
2608 
2609         case RfxBPscale :
2610             strncpy2(configp->rfx_bpunits, tokv[0], NAME_LEN - 1);
2611             configp->rfx_bpscale = strtod(tokv[1], &sp);
2612             if ( !strchr(" \t\n", *sp) ) {
2613                store_error_message("Invalid RFX_BPSCALE scale factor.");
2614                errors++;
2615                break;
2616             }
2617             if ( tokc > 2 ) {
2618                configp->rfx_bpoffset = strtod(tokv[2], &sp);
2619                if ( !strchr(" \t\n", *sp) ) {
2620                   store_error_message("Invalid RFX_BPSCALE offset.");
2621                   errors++;
2622                   break;
2623                }
2624             }
2625             break;
2626 
2627          case RfxComDtrRts :
2628             value = (int)strtol(tokv[0], &sp, 10);
2629             if ( !strchr(" \t\n\r", *sp) || value < 0 || value > 3 ) {
2630                store_error_message("RFXCOM_DTR_RTS must be 0 though 3");
2631                errors++;
2632             }
2633             configp->rfxcom_dtr_rts = (unsigned char)value;
2634             break;
2635 
2636 
2637             break;
2638 
2639          case RfxHiBaud :
2640             (void) strupper(tokv[0]);
2641             if ( !strcmp(tokv[0], "YES") )
2642                configp->rfxcom_hibaud = YES;
2643             else if ( !strcmp(tokv[0], "NO") )
2644                configp->rfxcom_hibaud = NO;
2645             else {
2646                store_error_message("RFXCOM_HIBAUD must be YES or NO");
2647                errors++;
2648             }
2649             break;
2650 
2651         case RfxPowerScale :
2652             strncpy2(configp->rfx_powerunits, tokv[0], NAME_LEN - 1);
2653             configp->rfx_powerscale = strtod(tokv[1], &sp);
2654             if ( !strchr(" \t\n", *sp) ) {
2655                store_error_message("Invalid RFX_POWERSCALE scale factor.");
2656                errors++;
2657                break;
2658             }
2659             break;
2660 
2661         case RfxWaterScale :
2662             strncpy2(configp->rfx_waterunits, tokv[0], NAME_LEN - 1);
2663             configp->rfx_waterscale = strtod(tokv[1], &sp);
2664             if ( !strchr(" \t\n", *sp) ) {
2665                store_error_message("Invalid RFX_WATERSCALE scale factor.");
2666                errors++;
2667                break;
2668             }
2669             break;
2670 
2671         case RfxGasScale :
2672             strncpy2(configp->rfx_gasunits, tokv[0], NAME_LEN - 1);
2673             configp->rfx_gasscale = strtod(tokv[1], &sp);
2674             if ( !strchr(" \t\n", *sp) ) {
2675                store_error_message("Invalid RFX_GASSCALE scale factor.");
2676                errors++;
2677                break;
2678             }
2679             break;
2680 
2681         case RfxPulseScale :
2682             strncpy2(configp->rfx_pulseunits, tokv[0], NAME_LEN - 1);
2683             configp->rfx_pulsescale = strtod(tokv[1], &sp);
2684             if ( !strchr(" \t\n", *sp) ) {
2685                store_error_message("Invalid RFX_PULSESCALE scale factor.");
2686                errors++;
2687                break;
2688             }
2689             break;
2690 
2691          case RfxComEnable :
2692             store_error_message("RFXCOM_ENABLE is obsolete; see RFXCOM_DISABLE");
2693             break;
2694 
2695 #if 0
2696          case RfxComDisable :
2697             for ( j = 0; j < tokc; j++ ) {
2698                strupper(tokv[j]);
2699                if ( strncmp(tokv[j], "ARCTECH", 3) == 0 )
2700                   configp->rfxcom_disable |= RFXCOM_ARCTECH;
2701                else if ( strncmp(tokv[j], "OREGON", 3) == 0 )
2702                   configp->rfxcom_disable |= RFXCOM_OREGON;
2703                else if ( strncmp(tokv[j], "ATIWONDER", 3) == 0 )
2704                   configp->rfxcom_disable |= RFXCOM_ATIWONDER;
2705                else if ( strncmp(tokv[j], "X10", 3) == 0 )
2706                   configp->rfxcom_disable |= RFXCOM_X10;
2707                else if ( strncmp(tokv[j], "VISONIC", 3) == 0 )
2708                   configp->rfxcom_disable |= RFXCOM_VISONIC;
2709                else if ( strncmp(tokv[j], "KOPPLA", 3) == 0 )
2710                   configp->rfxcom_disable |= RFXCOM_KOPPLA;
2711                else if ( strcmp(tokv[j], "HE_UK") == 0 )
2712                   configp->rfxcom_disable |= RFXCOM_HE_UK;
2713                else if ( strcmp(tokv[j], "HE_EU") == 0 )
2714                   configp->rfxcom_disable |= RFXCOM_HE_EU;
2715                else {
2716                   sprintf(errbuffer, "Invalid RFXCOM_DISABLE protocol %s", tokv[j]);
2717                   store_error_message(errbuffer);
2718                   errors++;
2719                   break;
2720                }
2721             }
2722             break;
2723 #endif
2724 
2725          case RfxComDisable :
2726             configp->rfxcom_disable = 0;
2727             for ( j = 0; j < tokc; j++ ) {
2728                strupper(tokv[j]);
2729                for ( k = 0; k < nrfxdisable; k++ ) {
2730                   if ( strncmp(tokv[j], rfx_disable[k].label, rfx_disable[k].minlabel) == 0 ) {
2731                      configp->rfxcom_disable |= rfx_disable[k].bitmap;
2732                      *tokv[j] = '\0';
2733                      break;
2734                   }
2735                }
2736                if ( *tokv[j] ) {
2737                   sprintf(errbuffer, "Invalid RFXCOM_DISABLE protocol %s", tokv[j]);
2738                   store_error_message(errbuffer);
2739                   errors++;
2740                   break;
2741                }
2742             }
2743             break;
2744 
2745          case LockupCheck :
2746             for ( j = 0; j < tokc; j++ ) {
2747                strupper(tokv[j]);
2748                if ( strcmp(tokv[j], "YES") == 0 )
2749                   configp->lockup_check = (CHECK_PORT | CHECK_CM11);
2750                else if ( strcmp(tokv[j], "PORT") == 0 )
2751                   configp->lockup_check = CHECK_PORT;
2752                else if ( strncmp(tokv[j], "CM11A", 4) == 0 )
2753                   configp->lockup_check = CHECK_CM11;
2754                else if ( strcmp(tokv[j], "NO") == 0 )
2755                   configp->lockup_check = 0;
2756                else {
2757                   sprintf(errbuffer, "Invalid LOCKUP_CHECK parameter %s", tokv[j]);
2758                   store_error_message(errbuffer);
2759                   errors++;
2760                   break;
2761                }
2762             }
2763             break;
2764 
2765          case TailPath :
2766             strncpy2(configp->tailpath, tokv[0], sizeof(config.tailpath));
2767             break;
2768 
2769          case RfxJam :
2770             (void) strupper(tokv[0]);
2771             if ( !strcmp(tokv[0], "YES") )
2772                configp->suppress_rfxjam = YES;
2773             else if ( !strcmp(tokv[0], "NO") )
2774                configp->suppress_rfxjam = NO;
2775             else {
2776                store_error_message("SUPPRESS_RFXJAM must be YES or NO");
2777                errors++;
2778             }
2779             break;
2780 
2781          case DispDmxTemp :
2782             (void) strupper(tokv[0]);
2783             if ( !strcmp(tokv[0], "YES") )
2784                configp->display_dmxtemp = YES;
2785             else if ( !strcmp(tokv[0], "NO") )
2786                configp->display_dmxtemp = NO;
2787             else {
2788                store_error_message("DISPLAY_DMXTEMP must be YES or NO");
2789                errors++;
2790             }
2791             break;
2792 
2793          case SecID16 :
2794             (void) strupper(tokv[0]);
2795             if ( !strcmp(tokv[0], "YES") )
2796                configp->securid_16 = YES;
2797             else if ( !strcmp(tokv[0], "NO") )
2798                configp->securid_16 = NO;
2799             else {
2800                store_error_message("SECURID_16 must be YES or NO");
2801                errors++;
2802             }
2803             break;
2804 
2805          case SecIDPar :
2806             (void) strupper(tokv[0]);
2807             if ( !strcmp(tokv[0], "YES") )
2808                configp->securid_parity = YES;
2809             else if ( !strcmp(tokv[0], "NO") )
2810                configp->securid_parity = NO;
2811             else {
2812                store_error_message("SECURID_PARITY must be YES or NO");
2813                errors++;
2814             }
2815             break;
2816 
2817          case LogDateYr :
2818             (void) strupper(tokv[0]);
2819             if ( !strcmp(tokv[0], "YES") )
2820                configp->logdate_year = YES;
2821             else if ( !strcmp(tokv[0], "NO") )
2822                configp->logdate_year = NO;
2823             else {
2824                store_error_message("LOGDATE_YEAR must be YES or NO");
2825                errors++;
2826             }
2827             break;
2828 
2829          case LogDateUnix :
2830             (void) strupper(tokv[0]);
2831             if ( !strcmp(tokv[0], "YES") )
2832                configp->logdate_unix = YES;
2833             else if ( !strcmp(tokv[0], "NO") )
2834                configp->logdate_unix = NO;
2835             else {
2836                store_error_message("LOGDATE_UNIX must be YES or NO");
2837                errors++;
2838             }
2839             break;
2840 
2841          case DispOreAll :
2842             (void) strupper(tokv[0]);
2843             if ( !strcmp(tokv[0], "YES") )
2844                configp->disp_ore_all = YES;
2845             else if ( !strcmp(tokv[0], "NO") )
2846                configp->disp_ore_all = NO;
2847             else {
2848                store_error_message("DISPLAY_ORE_ALL must be YES or NO");
2849                errors++;
2850             }
2851             break;
2852 
2853          case OreDispFcast :
2854             (void) strupper(tokv[0]);
2855             if ( !strcmp(tokv[0], "YES") )
2856                configp->ore_display_fcast = YES;
2857             else if ( !strcmp(tokv[0], "NO") )
2858                configp->ore_display_fcast = NO;
2859             else {
2860                store_error_message("ORE_DISPLAY_FCAST must be YES or NO");
2861                errors++;
2862             }
2863             break;
2864 
2865          case OreChgBitsT :
2866             value = (int)strtol(tokv[0], &sp, 10);
2867             if ( !strchr(" \t\n\r", *sp) || value < 1 || value > 255 ) {
2868                store_error_message("ORE_CHGBITS_T must be 1 though 255");
2869                errors++;
2870             }
2871             configp->ore_chgbits_t = (unsigned char)value;
2872             break;
2873 
2874          case OreChgBitsRH :
2875             value = (int)strtol(tokv[0], &sp, 10);
2876             if ( !strchr(" \t\n\r", *sp) || value < 1 || value > 255 ) {
2877                store_error_message("ORE_CHGBITS_RH must be 1 though 255");
2878                errors++;
2879             }
2880             configp->ore_chgbits_rh = (unsigned char)value;
2881             break;
2882 
2883          case OreChgBitsBP :
2884             value = (int)strtol(tokv[0], &sp, 10);
2885             if ( !strchr(" \t\n\r", *sp) || value < 1 || value > 255 ) {
2886                store_error_message("ORE_CHGBITS_BP must be 1 though 255");
2887                errors++;
2888             }
2889             configp->ore_chgbits_bp = (unsigned char)value;
2890             break;
2891 
2892          case OreChgBitsWgt :
2893             value = (int)strtol(tokv[0], &sp, 10);
2894             if ( !strchr(" \t\n\r", *sp) || value < 1 || value > 255 ) {
2895                store_error_message("ORE_CHGBITS_WGT must be 1 though 255");
2896                errors++;
2897             }
2898             configp->ore_chgbits_wgt = (unsigned char)value;
2899             break;
2900 
2901          case OreChgBitsWsp :
2902             longvalue = strtol(tokv[0], &sp, 10);
2903             if ( !strchr(" \t\n\r", *sp) || longvalue < 1 || longvalue > 65535 ) {
2904                store_error_message("ORE_CHGBITS_WINDSP must be 1 though 65535");
2905                errors++;
2906             }
2907             configp->ore_chgbits_wsp = (unsigned int)longvalue;
2908             break;
2909 
2910          case OreChgBitsWavsp :
2911             longvalue = strtol(tokv[0], &sp, 10);
2912             if ( !strchr(" \t\n\r", *sp) || longvalue < 1 || longvalue > 65535 ) {
2913                store_error_message("ORE_CHGBITS_WINDAVSP must be 1 though 65535");
2914                errors++;
2915             }
2916             configp->ore_chgbits_wavsp = (unsigned int)longvalue;
2917             break;
2918 
2919          case OreChgBitsWdir :
2920             longvalue = strtol(tokv[0], &sp, 10);
2921             if ( !strchr(" \t\n\r", *sp) || longvalue < 1 || longvalue > 720 ) {
2922                store_error_message("ORE_CHGBITS_WINDDIR must be 1 though 720");
2923                errors++;
2924             }
2925             configp->ore_chgbits_wdir = (unsigned int)longvalue;
2926             break;
2927 
2928          case OreChgBitsRrate :
2929             longvalue = strtol(tokv[0], &sp, 10);
2930             if ( !strchr(" \t\n\r", *sp) || longvalue < 1 || longvalue > 65535 ) {
2931                store_error_message("ORE_CHGBITS_RAINRATE must be 1 though 65535");
2932                errors++;
2933             }
2934             configp->ore_chgbits_rrate = (unsigned int)longvalue;
2935             break;
2936 
2937          case OreChgBitsRtot :
2938             longvalue = strtol(tokv[0], &sp, 10);
2939             if ( !strchr(" \t\n\r", *sp) || longvalue < 1 || longvalue > 65535 ) {
2940                store_error_message("ORE_CHGBITS_RAINTOT must be 1 though 65535");
2941                errors++;
2942             }
2943             configp->ore_chgbits_rtot = (unsigned int)longvalue;
2944             break;
2945 
2946          case OreChgBitsUV :
2947             value = (int)strtol(tokv[0], &sp, 10);
2948             if ( !strchr(" \t\n\r", *sp) || value < 1 || value > 255 ) {
2949                store_error_message("ORE_CHGBITS_UV must be 1 though 255");
2950                errors++;
2951             }
2952             configp->ore_chgbits_uv = (unsigned char)value;
2953             break;
2954 
2955          case OreDataEntry :
2956             (void) strupper(tokv[0]);
2957             if ( !strcmp(tokv[0], "NATIVE") )
2958                configp->ore_data_entry = NATIVE;
2959             else if ( !strcmp(tokv[0], "SCALED") )
2960                configp->ore_data_entry = SCALED;
2961             else {
2962                store_error_message("ORE_DATA_ENTRY must be NATIVE or SCALED");
2963                errors++;
2964             }
2965             break;
2966 
2967          case OreDispChan :
2968             (void) strupper(tokv[0]);
2969             if ( !strcmp(tokv[0], "YES") )
2970                configp->ore_display_chan = YES;
2971             else if ( !strcmp(tokv[0], "NO") )
2972                configp->ore_display_chan = NO;
2973             else {
2974                store_error_message("ORE_DISPLAY_CHAN must be YES or NO");
2975                errors++;
2976             }
2977             break;
2978 
2979          case OreDispBatLvl :
2980             (void) strupper(tokv[0]);
2981             if ( !strcmp(tokv[0], "YES") )
2982                configp->ore_display_batlvl = YES;
2983             else if ( !strcmp(tokv[0], "NO") )
2984                configp->ore_display_batlvl = NO;
2985             else {
2986                store_error_message("ORE_DISPLAY_BATLVL must be YES or NO");
2987                errors++;
2988             }
2989             break;
2990 
2991          case OreDispCount :
2992             (void) strupper(tokv[0]);
2993             if ( !strcmp(tokv[0], "YES") )
2994                configp->ore_display_count = YES;
2995             else if ( !strcmp(tokv[0], "NO") )
2996                configp->ore_display_count = NO;
2997             else {
2998                store_error_message("ORE_DISPLAY_COUNT must be YES or NO");
2999                errors++;
3000             }
3001             break;
3002 
3003          case OreDispBft :
3004             (void) strupper(tokv[0]);
3005             if ( !strcmp(tokv[0], "YES") )
3006                configp->ore_display_bft = YES;
3007             else if ( !strcmp(tokv[0], "NO") )
3008                configp->ore_display_bft = NO;
3009             else {
3010                store_error_message("ORE_DISPLAY_BEAUFORT must be YES or NO");
3011                errors++;
3012             }
3013             break;
3014 
3015          case OreID16 :
3016             (void) strupper(tokv[0]);
3017             if ( !strcmp(tokv[0], "YES") )
3018                configp->oreid_16 = YES;
3019             else if ( !strcmp(tokv[0], "NO") )
3020                configp->oreid_16 = NO;
3021             else {
3022                store_error_message("ORE_ID_16 must be YES or NO");
3023                errors++;
3024             }
3025             break;
3026 
3027          case DispSensorIntv :
3028             (void) strupper(tokv[0]);
3029             if ( !strcmp(tokv[0], "YES") )
3030                configp->display_sensor_intv = YES;
3031             else if ( !strcmp(tokv[0], "NO") )
3032                configp->display_sensor_intv = NO;
3033             else {
3034                store_error_message("DISPLAY_SENSOR_INTV must be YES or NO");
3035                errors++;
3036             }
3037             break;
3038 
3039          case DateFormat :
3040             (void) strupper(tokv[0]);
3041             if ( !strcmp(tokv[0], "YMD") )
3042                configp->date_format = YMD_ORDER;
3043             else if ( !strcmp(tokv[0], "DMY") )
3044                configp->date_format = DMY_ORDER;
3045             else if ( !strcmp(tokv[0], "MDY") )
3046                configp->date_format = MDY_ORDER;
3047             else {
3048                store_error_message("DATE_FORMAT order must be YMD, DMY, or MDY");
3049                errors++;
3050                break;
3051             }
3052 
3053             if ( tokc == 2 ) {
3054                if ( !strcmp(tokv[1], "'/'") || !strcmp(tokv[1], "\"/\"") || !strcmp(tokv[1], "/") )
3055                   configp->date_separator = '/';
3056                else if ( !strcmp(tokv[1], "'-'") || !strcmp(tokv[1], "\"-\"") || !strcmp(tokv[1], "-") )
3057                   configp->date_separator = '-';
3058                else if ( !strcmp(tokv[1], "'.'") || !strcmp(tokv[1], "\".\"") || !strcmp(tokv[1], ".") )
3059                   configp->date_separator = '.';
3060                else {
3061                   store_error_message("DATE_FORMAT separator must be '/', '-', or '.'");
3062                   errors++;
3063                }
3064             }
3065             break;
3066 
3067          case LockTimeout :
3068             value = (int)strtol(tokv[0], &sp, 10);
3069             if ( !strchr(" \t\n\r", *sp) || value < 5 || value > 60 ) {
3070                store_error_message("LOCK_TIMEOUT range is 5 through 60 seconds");
3071                errors++;
3072             }
3073             configp->lock_timeout = value;
3074             break;
3075 
3076          case CM11QueryDelay :
3077             value = (int)strtol(tokv[0], &sp, 10);
3078             if ( !strchr(" \t\n\r", *sp) || value < 0 || value > 100 ) {
3079                store_error_message("CM11_QUERY_DELAY range is 0 through 100 milliseconds");
3080                errors++;
3081             }
3082             configp->cm11a_query_delay = value;
3083             break;
3084 
3085          case ElsNumber :
3086             value = (int)strtol(tokv[0], &sp, 10);
3087             if ( !strchr(" \t\n\r", *sp) || value < 1 || value > 3 ) {
3088                store_error_message("ELS_NUMBER range is 1 through 3");
3089                errors++;
3090             }
3091             configp->els_number = value;
3092             break;
3093 
3094          case ElsVoltage :
3095             dblvalue = strtod(tokv[0], &sp);
3096             if ( !strchr(" \t\n\r", *sp) || dblvalue < 0.0 ) {
3097                store_error_message("ELS_VOLTAGE is invalid or negative");
3098                errors++;
3099             }
3100             configp->els_voltage = dblvalue;
3101             break;
3102 
3103          case ElsChgBitsCurr :
3104             value = (int)strtol(tokv[0], &sp, 10);
3105             if ( !strchr(" \t\n\r", *sp) || value < 1 || value > 1000 ) {
3106                store_error_message("ELS_CHGBITS_CURR is 1 through 1000");
3107                errors++;
3108             }
3109             configp->els_chgbits_curr = value;
3110             break;
3111 
3112          case ScanMode :
3113             (void) strupper(tokv[0]);
3114             if ( !strcmp(tokv[0], "CONTINUE") )
3115                configp->scanmode = FM_CONTINUE;
3116             else if ( !strcmp(tokv[0], "BREAK") )
3117                configp->scanmode = FM_BREAK;
3118             else {
3119                store_error_message("LAUNCHER_SCANMODE must be CONTINUE or BREAK");
3120                errors++;
3121             }
3122             break;
3123 
3124          case RfxInline :
3125             (void) strupper(tokv[0]);
3126             if ( !strcmp(tokv[0], "YES") )
3127                configp->rfx_inline = YES;
3128             else if ( !strcmp(tokv[0], "NO") )
3129                configp->rfx_inline = NO;
3130             else {
3131                store_error_message("RFXMETER_SETUP_INLINE must be YES or NO");
3132                errors++;
3133             }
3134             break;
3135 
3136          case OwlVoltage :
3137             dblvalue = strtod(tokv[0], &sp);
3138             if ( !strchr(" \t\n\r", *sp) || dblvalue < 0.0 ) {
3139                store_error_message("OWL_VOLTAGE is invalid or negative");
3140                errors++;
3141             }
3142             configp->owl_voltage = dblvalue;
3143             break;
3144 
3145          case OwlCalibPower :
3146             dblvalue = strtod(tokv[0], &sp);
3147             if ( !strchr(" \t\n\r", *sp) || dblvalue < 0.0 ) {
3148                store_error_message("OWL_CALIB_POWER is invalid or negative");
3149                errors++;
3150             }
3151             configp->owl_calib_power = dblvalue;
3152             break;
3153 
3154          case OwlCalibEnergy :
3155             dblvalue = strtod(tokv[0], &sp);
3156             if ( !strchr(" \t\n\r", *sp) || dblvalue < 0.0 ) {
3157                store_error_message("OWL_CALIB_ENERGY is invalid or negative");
3158                errors++;
3159             }
3160             configp->owl_calib_energy = dblvalue;
3161             break;
3162 
3163          case OwlChgBitsPower :
3164             longvalue = strtol(tokv[0], &sp, 10);
3165             if ( !strchr(" \t\n\r", *sp) || longvalue < 1L || longvalue > 10000L ) {
3166                store_error_message("OWL_CHGBITS_POWER is 1 through 10000");
3167                errors++;
3168             }
3169             configp->owl_chgbits_power = longvalue;
3170             break;
3171 
3172          case OwlChgBitsEnergy :
3173             longvalue = strtol(tokv[0], &sp, 10);
3174             if ( !strchr(" \t\n\r", *sp) || longvalue < 1L || longvalue > 100000L ) {
3175                store_error_message("OWL_CHGBITS_ENERGY is 1 through 100000");
3176                errors++;
3177             }
3178             configp->owl_chgbits_energy = longvalue;
3179             break;
3180 
3181          case OwlDispCount :
3182             (void) strupper(tokv[0]);
3183             if ( !strcmp(tokv[0], "YES") )
3184                configp->owl_display_count = YES;
3185             else if ( !strcmp(tokv[0], "NO") )
3186                configp->owl_display_count = NO;
3187             else {
3188                store_error_message("OWL_DISPLAY_COUNT must be YES or NO");
3189                errors++;
3190             }
3191             break;
3192 
3193 	 case ArmRemote :
3194             (void) strupper(tokv[0]);
3195             if ( !strncmp(tokv[0], "MANUAL", 3) )
3196                configp->arm_remote = MANUAL;
3197             else if ( !strncmp(tokv[0], "AUTOMATIC", 4) )
3198                configp->arm_remote = AUTOMATIC;
3199             else {
3200                store_error_message("ARM_REMOTE must be MANUAL or AUTO");
3201                errors++;
3202             }
3203             break;
3204 
3205          case ActiveChange :
3206             (void) strupper(tokv[0]);
3207             if ( !strcmp(tokv[0], "YES") )
3208                configp->active_change = YES;
3209             else if ( !strcmp(tokv[0], "NO") )
3210                configp->active_change = NO;
3211             else {
3212                store_error_message("ACTIVE_CHANGE must be YES or NO");
3213                errors++;
3214             }
3215             break;
3216 
3217          case InactiveHandling :
3218             (void) strupper(tokv[0]);
3219             if ( !strcmp(tokv[0], "NEW") )
3220                configp->inactive_handling = NEW;
3221             else if ( !strcmp(tokv[0], "OLD") )
3222                configp->inactive_handling = OLD;
3223             else {
3224                store_error_message("INACTIVE_HANDLING must be NEW or OLD");
3225                errors++;
3226             }
3227             break;
3228 
3229          case ProcessXmit :
3230             (void) strupper(tokv[0]);
3231             if ( !strcmp(tokv[0], "YES") )
3232                configp->process_xmit = YES;
3233             else if ( !strcmp(tokv[0], "NO") )
3234                configp->process_xmit = NO;
3235             else {
3236                store_error_message("PROCESS_XMIT must be YES or NO");
3237                errors++;
3238             }
3239             break;
3240 
3241          case ShowFlagsMode :
3242             (void) strupper(tokv[0]);
3243             if ( !strcmp(tokv[0], "NEW") )
3244                configp->show_flags_mode = NEW;
3245             else if ( !strcmp(tokv[0], "OLD") )
3246                configp->show_flags_mode = OLD;
3247             else {
3248                store_error_message("SHOW_FLAGS_MODE must be NEW or OLD");
3249                errors++;
3250             }
3251             break;
3252 
3253          case LaunchPathAppend :
3254             strtrim(tokv[0]);
3255             if ( strlen(tokv[0]) > PATH_LEN ) {
3256                store_error_message("LAUNCHPATH_APPEND too long");
3257                errors++;
3258             }
3259             else {
3260                strcpy(configp->launchpath_append, tokv[0]);
3261             }
3262             break;
3263 
3264          case LaunchPathPrefix :
3265             strtrim(tokv[0]);
3266             if ( strlen(tokv[0]) > PATH_LEN ) {
3267                store_error_message("LAUNCHPATH_PREFIX too long");
3268                errors++;
3269             }
3270             else {
3271                strcpy(configp->launchpath_prefix, tokv[0]);
3272             }
3273             break;
3274 
3275          case FixStopStartError :
3276             (void) strupper(tokv[0]);
3277             if ( !strcmp(tokv[0], "YES") )
3278                configp->fix_stopstart_error = YES;
3279             else if ( !strcmp(tokv[0], "NO") )
3280                configp->fix_stopstart_error = NO;
3281             else {
3282                store_error_message("FIX_STOPSTART_ERROR must be YES or NO");
3283                errors++;
3284             }
3285             break;
3286 
3287          case ChkSumTimeout :
3288             longvalue = strtol(tokv[0], &sp, 10);
3289             if ( !strchr(" \t\n\r", *sp) || longvalue < 1L || longvalue > 20L ) {
3290                store_error_message("CHKSUM_TIMEOUT must be 1 through 20 seconds");
3291                errors++;
3292                break;
3293             }
3294             configp->chksum_timeout = longvalue;
3295             break;
3296 
3297          default :
3298             store_error_message("Unsupported config directive");
3299             errors++;
3300             break;
3301 
3302       }
3303       free( tokv );
3304 
3305       return errors;
3306 }
3307 
3308 /*---------------------------------------------------------------------+
3309  | Get certain configuration items from environment.                   |
3310  +---------------------------------------------------------------------*/
environment_config(void)3311 int environment_config ( void )
3312 {
3313    int  j, retcode, errors = 0;
3314    char *sp;
3315    char buffer[32];
3316 
3317    static char *envars[] = {
3318      "LATITUDE",
3319      "LONGITUDE",
3320      "ASIF_DATE",
3321      "ASIF_TIME",
3322    };
3323 
3324    reset_isparsed_flags();
3325 
3326    for ( j = 0; j < (int)(sizeof(envars)/sizeof(char *)); j++ ) {
3327       if ( (sp = getenv(envars[j])) != NULL ) {
3328          sprintf(buffer, "%s %s", envars[j], sp);
3329          retcode = parse_config_tail(buffer, SRC_ENVIRON);
3330          errors += retcode;
3331          if ( retcode != 0 || *error_message() != '\0' ) {
3332             fprintf(stderr, "Environment variable %s.\n", error_message());
3333             clear_error_message();
3334          }
3335       }
3336    }
3337    return errors;
3338 }
3339 
3340 
3341 /*---------------------------------------------------------------------+
3342  | Display configuration file directives that have been overridden,    |
3343  | either by an environment variable or by a 'config' command in the   |
3344  | schedule file, as indicated by the isparsed flag.                   |
3345  +---------------------------------------------------------------------*/
display_config_overrides(FILE * fd)3346 void display_config_overrides ( FILE *fd )
3347 {
3348    int  j, count = 0;
3349    char delim = ' ';
3350 
3351    fprintf(fd, "Configuration overrides:");
3352 
3353    for ( j = 0; j < ncommands; j++ ) {
3354       if ( command[j].isparsed & (SRC_ENVIRON | SRC_SCHED)) {
3355          count++;
3356          fprintf(fd, "%c %s", delim, command[j].name);
3357          delim = ',';
3358       }
3359    }
3360    if ( !count )
3361       fprintf(fd, " -- None --");
3362 
3363    fprintf(fd, "\n\n");
3364 
3365    return;
3366 }
3367 
3368 /*---------------------------------------------------------------------+
3369  | Open the user's X10 configuration file and read only the minimal    |
3370  | directives for specific commands, like 'heyu stop'.                 |
3371  +---------------------------------------------------------------------*/
read_minimal_config(unsigned char mode,unsigned int source)3372 void read_minimal_config ( unsigned char mode, unsigned int source )
3373 {
3374 
3375    FILE        *fd ;
3376    int         error_count;
3377    char        confp[PATH_LEN + 1];
3378    extern char heyu_config[PATH_LEN + 1];
3379    extern int  verbose;
3380 
3381    find_heyu_path();
3382 
3383    strncpy2(confp, pathspec(heyu_config), sizeof(confp) - 1);
3384 
3385    if ( verbose )
3386       (void) fprintf(stdout, "Opening Heyu configuration file '%s'\n", confp);
3387 
3388    if ( !(fd = fopen(confp, "r")) ) {
3389       if ( !i_am_relay )
3390          fprintf(stderr, "Unable to find (or open) Heyu configuration file '%s'\n", confp);
3391       else
3392          syslog(LOG_ERR, "Unable to find (or open) Heyu configuration file '%s'\n", confp);
3393 
3394       return;
3395    }
3396 
3397    error_count = parse_minimal_config( fd, mode, source );
3398 
3399    if ( error_count != 0 ) {
3400       if ( !i_am_relay )
3401          fprintf(stderr, "Quitting due to errors in configuration file '%s'\n", confp);
3402       else
3403          syslog(LOG_ERR, "Quitting due to errors in configuration file '%s'\n", confp);
3404       exit(1);
3405    }
3406 
3407    (void) fclose( fd );
3408 
3409    return;
3410 }
3411 
3412 
3413 /*---------------------------------------------------------------------+
3414  | Return the index in the array of SCRIPT structures for the SCRIPT   |
3415  | having the argument 'label', or -1 if not found.                    |
3416  +---------------------------------------------------------------------*/
lookup_script(SCRIPT * scriptp,char * label)3417 int lookup_script ( SCRIPT *scriptp, char *label )
3418 {
3419    int    j = 0;
3420 
3421    while ( scriptp && scriptp[j].line_no > 0 ) {
3422       if ( strcmp(label, scriptp[j].label) == 0 )
3423          return j;
3424       j++;
3425    }
3426    return -1;
3427 }
3428 
3429 /*---------------------------------------------------------------------+
3430  | Return the index in the array of LAUNCHER structures for the first  |
3431  | LAUNCHER having the argument 'label', or -1 if not found.           |
3432  +---------------------------------------------------------------------*/
lookup_launcher(LAUNCHER * launcherp,char * label)3433 int lookup_launcher ( LAUNCHER *launcherp, char *label )
3434 {
3435    int   j = 0;
3436 
3437    while ( launcherp && launcherp[j].line_no > 0 ) {
3438       if ( strcmp(label, launcherp[j].label) == 0 )
3439          return j;
3440       j++;
3441    }
3442    return -1;
3443 }
3444 
3445 
3446 /*---------------------------------------------------------------------+
3447  | Create list of Oregon sensor IDs to be ignored and classified as    |
3448  | noise.                                                              |
3449  +---------------------------------------------------------------------*/
create_oregon_ignore_list(void)3450 int create_oregon_ignore_list ( void )
3451 {
3452 #ifdef HASORE
3453    ALIAS *aliasp;
3454    int   j, k;
3455 
3456    if ( (aliasp = configp->aliasp) == NULL )
3457       return 0;
3458 
3459    ore_ignore_size = 0;
3460    j = 0;
3461    while ( aliasp[j].line_no > 0 ) {
3462       if ( aliasp[j].vtype == RF_OREGON && (aliasp[j].optflags & MOPT_RFIGNORE) ) {
3463          k = 0;
3464          while ( k < aliasp[j].nident && ore_ignore_size < (sizeof(ore_ignore)/sizeof(unsigned int)) ) {
3465             ore_ignore[ore_ignore_size++] = aliasp[j].ident[k];
3466             k++;
3467          }
3468       }
3469       j++;
3470    }
3471 #endif /* HASORE */
3472    return 0;
3473 }
3474 
3475 /*---------------------------------------------------------------------+
3476  | Create list of Security sensor IDs to be ignored and classified as  |
3477  | noise.                                                              |
3478  +---------------------------------------------------------------------*/
create_security_ignore_list(void)3479 int create_security_ignore_list ( void )
3480 {
3481    ALIAS *aliasp;
3482    int   j, k;
3483 
3484    if ( (aliasp = configp->aliasp) == NULL )
3485       return 0;
3486 
3487    sec_ignore_size = 0;
3488    j = 0;
3489    while ( aliasp[j].line_no > 0 ) {
3490       if ( aliasp[j].vtype == RF_SEC && (aliasp[j].optflags & MOPT_RFIGNORE) ) {
3491          k = 0;
3492          while ( k < aliasp[j].nident && sec_ignore_size < (sizeof(sec_ignore)/sizeof(unsigned short)) ) {
3493             sec_ignore[sec_ignore_size++] = aliasp[j].ident[k];
3494             k++;
3495          }
3496       }
3497       j++;
3498    }
3499    return 0;
3500 }
3501 
is_sec_ignored(unsigned int saddr)3502 int is_sec_ignored ( unsigned int saddr )
3503 {
3504    int j = 0;
3505 
3506    while ( j < sec_ignore_size ) {
3507       if ( saddr == sec_ignore[j] ) {
3508          return 1;
3509       }
3510       j++;
3511    }
3512    return 0;
3513 }
3514 
3515 /*---------------------------------------------------------------------+
3516  | Resolve interrelated configuration items.                           |
3517  +---------------------------------------------------------------------*/
finalize_config(unsigned char mode)3518 int finalize_config ( unsigned char mode )
3519 {
3520    int      finalize_launchers(void);
3521    int      create_file_paths(void);
3522    mode_t   heyu_umask;
3523    int      verify_unique_ids(unsigned char);
3524    int      assign_data_storage ( void );
3525    int      set_elec1_nvar ( int );
3526 
3527    ALIAS    *aliasp;
3528 
3529    char errmsg[80];
3530    char *sp;
3531    int  j;
3532 
3533 
3534    /* Count and configure aliases */
3535    aliasp = configp->aliasp;
3536    j = 0;
3537    while ( aliasp && aliasp[j].line_no > 0 ) {
3538       if ( (aliasp[j].optflags & MOPT_HEARTBEAT) && !(aliasp[j].optflags2 & MOPT2_IATO) ) {
3539          aliasp[j].hb_timeout = configp->inactive_timeout;
3540       }
3541       j++;
3542    }
3543    configp->alias_size = j;
3544 
3545    if ( configp->heyu_umask >= 0 )
3546       umask((mode_t)configp->heyu_umask);
3547 
3548    heyu_umask = umask(0);
3549    umask(heyu_umask);
3550 
3551    if ( configp->log_umask < 0 )
3552       configp->log_umask = (int)heyu_umask;
3553 
3554    if ( configp->mode == COMPATIBLE ) {
3555       configp->program_days = 366;
3556    }
3557    else {
3558       configp->program_days = configp->program_days_in;
3559    }
3560 
3561    if ( configp->min_dusk != OFF && configp->max_dusk != OFF &&
3562         configp->min_dusk >= configp->max_dusk ) {
3563       store_error_message("MIN_DUSK must be less than MAX_DUSK");
3564       return 1;
3565    }
3566 
3567    if ( configp->min_dawn != OFF && configp->max_dawn != OFF &&
3568         configp->min_dawn >= configp->max_dawn ) {
3569       store_error_message("MIN_DAWN must be less than MAX_DAWN");
3570       return 1;
3571    }
3572 
3573    if ( configp->rfxcom_enable && configp->rfxcom_disable ) {
3574       store_error_message("RFXCOM_ENABLE (Deprecated) and RFXCOM_DISABLE are incompatible.");
3575       return 1;
3576    }
3577    if ( configp->rfxcom_disable )
3578       configp->rfxcom_enable = 0xFFFF;
3579    else if ( configp->rfxcom_enable )
3580       store_error_message("Directive RFXCOM_ENABLE is deprecated; see RFXCOM_DISABLE");
3581 
3582    if ( configp->securid_16 == NO || configp->auxdev != DEV_RFXCOMVL ) {
3583       configp->securid_mask = 0x00ffu;
3584    }
3585    else {
3586       configp->securid_mask = 0xffffu;
3587    }
3588 
3589    if ( configp->oreid_16 == NO || configp->auxdev != DEV_RFXCOMVL ) {
3590       configp->oreid_mask = 0x00ffu;
3591    }
3592    else {
3593       configp->oreid_mask = 0xffffu;
3594    }
3595 
3596    if ( verify_unique_ids(RF_ENT) != 0     ||
3597         verify_unique_ids(RF_SEC) != 0     ||
3598         verify_unique_ids(RF_XSENSOR) != 0 ||
3599         verify_unique_ids(RF_XMETER) != 0  ||
3600         verify_unique_ids(RF_DIGIMAX) != 0 ||
3601         verify_unique_ids(RF_OREGON) != 0    ) {
3602       return 1;
3603    }
3604 
3605    if ( strcmp(configp->tty, "dummy") == 0 ) {
3606       configp->device_type |= DEV_DUMMY;
3607    }
3608 
3609    /* Create suffix for lock files, e.g., ".ttyS0" */
3610 
3611    if ( (sp = strrchr(configp->tty, '/')) != NULL ) {
3612       strncpy2(configp->suffix, sp, sizeof(config.suffix) - 1);
3613       *(configp->suffix) = '.';
3614    }
3615    else {
3616       strncpy2(configp->suffix, ".", sizeof(config.suffix) - 1);
3617       strncat(configp->suffix, configp->tty,
3618                           sizeof(config.suffix) - 1 - strlen(configp->suffix));
3619    }
3620 
3621    if ( configp->auxdev ) {
3622       if ( (sp = strrchr(configp->ttyaux, '/')) != NULL ) {
3623          strncpy2(configp->suffixaux, sp, sizeof(config.suffixaux) - 1);
3624          *(configp->suffixaux) = '.';
3625       }
3626       else {
3627          strncpy2(configp->suffixaux, ".", sizeof(config.suffixaux) - 1);
3628          strncat(configp->suffixaux, configp->ttyaux,
3629                             sizeof(config.suffixaux) - 1 - strlen(configp->suffixaux));
3630       }
3631    }
3632 
3633    if ( configp->aux_repcounts[0] == 0 ) {
3634       configp->aux_repcounts[0] =
3635          (configp->auxdev == DEV_RFXCOM32  ||
3636           configp->auxdev == DEV_RFXCOMVL     ) ?
3637             DEF_AUX_MINCOUNT_RFXCOM :
3638          (configp->auxdev == DEV_MR26A) ? DEF_AUX_MINCOUNT_MR26 : DEF_AUX_MINCOUNT_W800;
3639    }
3640 
3641    if ( configp->aux_mincount_rfx == 0 ) {
3642       if (configp->auxdev == DEV_RFXCOM32  ||
3643           configp->auxdev == DEV_RFXCOMVL     ) {
3644          configp->aux_mincount_rfx = max(DEF_AUX_MINCOUNT_RFX, configp->aux_repcounts[0]);
3645       }
3646       else {
3647          configp->aux_mincount_rfx = configp->aux_repcounts[0];
3648       }
3649    }
3650 
3651    if ( *configp->logfile == '\0' ) {
3652       strncpy2(configp->logfile, DEF_LOGFILE, sizeof(config.logfile) - 1);
3653    }
3654    else if ( configp->logcommon == YES ) {
3655       strncat(configp->logfile, ".common",
3656                      sizeof(config.logfile) - 1 - strlen(configp->logfile));
3657       if ( configp->disp_subdir == NO_ANSWER )
3658          configp->disp_subdir = YES;
3659    }
3660    else {
3661       strncat(configp->logfile, configp->suffix,
3662                      sizeof(config.logfile) - 1 - strlen(configp->logfile));
3663    }
3664 
3665    /* If not set above */
3666    if ( configp->disp_subdir == NO_ANSWER )
3667       configp->disp_subdir = NO;
3668 
3669    if ( configp->hide_unchanged_inactive == NO_ANSWER )
3670       configp->hide_unchanged_inactive = configp->hide_unchanged;
3671 
3672    create_file_paths();
3673 
3674    if ( access(configp->logfile, F_OK) == 0 &&
3675         access(configp->logfile, W_OK) != 0 ) {
3676       sprintf(errmsg, "Log file '%s' is not writable - check permissions.",
3677 	 configp->logfile);
3678       store_error_message(errmsg);
3679       return 1;
3680    }
3681 
3682    if ( access(statefile, F_OK) == 0 &&
3683         access(statefile, W_OK) != 0 ) {
3684       sprintf(errmsg, "State file '%s' is not writable - check permissions.",
3685 	 statefile);
3686       store_error_message(errmsg);
3687       return 1;
3688    }
3689 
3690    for ( j = 0; j < nrflabels; j++ ) {
3691       if ( configp->rf_bursts[j] > configp->def_rf_bursts ) {
3692          sprintf(errmsg, "RF_BURSTS exceeds %d\n", configp->def_rf_bursts);
3693 	 store_error_message(errmsg);
3694 	 return 1;
3695       }
3696    }
3697 
3698 
3699    if ( configp->transceive & configp->rfforward ) {
3700       store_error_message("Housecode conflict in TRANSCEIVE and RFFORWARD");
3701       return 1;
3702    }
3703 
3704    if ( configp->device_type == DEV_DUMMY )
3705       configp->lockup_check = 0;
3706 
3707    assign_data_storage();
3708 
3709    create_oregon_ignore_list();
3710    create_security_ignore_list();
3711 
3712    set_elec1_nvar(configp->els_number);
3713 
3714 
3715 #ifdef HASRFXM
3716    create_rfxpower_panels();
3717 #endif
3718 
3719    if ( finalize_launchers() > 0 )
3720       return 1;
3721 
3722    return 0;
3723 }
3724 
3725 
3726 /*---------------------------------------------------------------------+
3727  | Add an alias to the array of ALIAS structures and return the index  |
3728  | of the new member.                                                  |
3729  +---------------------------------------------------------------------*/
add_alias(ALIAS ** aliaspp,char * label,int line_no,char housecode,char * units,int modtype)3730 int add_alias ( ALIAS **aliaspp, char *label, int line_no,
3731                               char housecode, char *units, int modtype )
3732 {
3733    static int    size, max_size;
3734    static int    strucsize = sizeof(ALIAS);
3735    int           j, k, maxlevel;
3736    int           blksize = 10;
3737    char          hc;
3738    unsigned int  bmap;
3739    unsigned long vflags, flags, xflags, kflags;
3740    char          errmsg[128];
3741    int           (*module_xlate_func(int index))();
3742 
3743    clear_error_message();
3744 
3745    /* Allocate initial block of memory */
3746    if ( *aliaspp == NULL ) {
3747       *aliaspp = (ALIAS *) calloc(blksize, strucsize );
3748       if ( *aliaspp == NULL ) {
3749          (void) fprintf(stderr, "Unable to allocate memory for Alias.\n");
3750          exit(1);
3751       }
3752       max_size = blksize;
3753       size = 0;
3754       /* Initialize it where necessary */
3755       for ( j = 0; j < max_size; j++ ) {
3756          (*aliaspp)[j].label[0] = '\0';
3757          (*aliaspp)[j].modtype = -1;
3758          (*aliaspp)[j].line_no = 0;
3759          (*aliaspp)[j].vflags = 0;
3760          (*aliaspp)[j].flags = 0;
3761          (*aliaspp)[j].xflags = 0;
3762          (*aliaspp)[j].kflags = 0;
3763          (*aliaspp)[j].optflags = 0;
3764          (*aliaspp)[j].optflags2 = 0;
3765          (*aliaspp)[j].tmin = 0;
3766          (*aliaspp)[j].tmax = 0;
3767          (*aliaspp)[j].rhmin = 0;
3768          (*aliaspp)[j].rhmax = 0;
3769          for ( k = 0; k < NOFFSET; k++ )
3770             (*aliaspp)[j].offset[k] = 0.0;
3771          (*aliaspp)[j].vtype = 0;
3772          (*aliaspp)[j].subtype = 0;
3773          (*aliaspp)[j].nident = 0;
3774          for ( k = 0; k < NIDENT; k++ ) {
3775             (*aliaspp)[j].ident[k] = 0;
3776             (*aliaspp)[j].kaku_keymap[k] = 0;
3777             (*aliaspp)[j].kaku_grpmap[k] = 0;
3778          }
3779          (*aliaspp)[j].xlate_func = NULL;
3780 //         (*aliaspp)[j].timeout = 0;
3781          (*aliaspp)[j].hb_timeout = -1L;
3782          (*aliaspp)[j].hb_index = -1;
3783          (*aliaspp)[j].nvar = 0;
3784          (*aliaspp)[j].storage_index = -1;
3785          (*aliaspp)[j].storage_units = 0;
3786          for ( k = 0; k < NFUNCLIST; k++ ) {
3787             (*aliaspp)[j].funclist[k] = 0xff;
3788             (*aliaspp)[j].statusoffset[k] = k;
3789          }
3790          (*aliaspp)[j].ext0links = 0;
3791       }
3792    }
3793 
3794    /* Check for a valid label length */
3795    if ( (int)strlen(label) > NAME_LEN ) {
3796       sprintf(errmsg,
3797          "Alias label '%s' too long - maximum %d characters", label, NAME_LEN);
3798       store_error_message(errmsg);
3799       return -1;
3800    }
3801 
3802    /* See if the alias label is already in the list.     */
3803    /* If so, it's an error.                              */
3804    if ( (j = get_alias(*aliaspp, label, &hc, &bmap)) >= 0 ) {
3805       (void) sprintf(errmsg, "Duplicate alias label '%s'", label);
3806       store_error_message(errmsg);
3807       return -1;
3808    }
3809 
3810    /* Verify that the label is not 'macro' */
3811    if ( strcmp(label, "macro") == 0 ) {
3812       store_error_message("An alias may not have the label 'macro'\n");
3813       return -1;
3814    }
3815 
3816    /* Check the housecode */
3817    if ( housecode == '_' ) {
3818       (void) sprintf(errmsg,
3819          "Alias '%s': Default housecode symbol ('_') is invalid in an alias",
3820               label);
3821       store_error_message(errmsg);
3822       return -1;
3823    }
3824    if ( (hc = toupper((int)housecode)) < 'A' || hc > 'P' ) {
3825       (void) sprintf(errmsg, "Alias '%s': Housecode '%c' outside range A-P",
3826                label, hc);
3827       store_error_message(errmsg);
3828       return -1;
3829    }
3830 
3831    /* Check the units list */
3832    if ( parse_units( units, &bmap ) != 0 ) {
3833       (void) sprintf(errmsg, "Alias '%s': ", label);
3834       add_error_prefix(errmsg);
3835       return -1;
3836    }
3837 
3838    /* Check to see that the module type (if any) specified for */
3839    /* this housecode|unit address doesn't conflict with that   */
3840    /* in a previously defined alias.                           */
3841 
3842    if ( modtype >= 0 ) {
3843       j = 0;
3844       while ( (*aliaspp)[j].line_no > 0 ) {
3845          if ( (*aliaspp)[j].housecode == hc && (*aliaspp)[j].unitbmap & bmap &&
3846               (*aliaspp)[j].modtype >= 0 ) {
3847             if ( (*aliaspp)[j].modtype != modtype ) {
3848                sprintf(errmsg,
3849                   "Module type conflicts with that defined for %c%s on Line %d",
3850                        hc, bmap2units((*aliaspp)[j].unitbmap & bmap),
3851                           (*aliaspp)[j].line_no);
3852                store_error_message(errmsg);
3853                return -1;
3854             }
3855          }
3856          j++;
3857       }
3858    }
3859 
3860 #if 0
3861    if ( modtype >= 0 ) {
3862       j = 0;
3863       while ( (*aliaspp)[j].line_no > 0 ) {
3864          if ( (*aliaspp)[j].housecode == hc && (*aliaspp)[j].unitbmap & bmap &&
3865               (*aliaspp)[j].modtype >= 0 && (*aliaspp)[j].modtype != modtype ) {
3866             sprintf(errmsg,
3867                "Module type conflicts with that defined for %c%s on Line %d",
3868                     hc, bmap2units((*aliaspp)[j].unitbmap & bmap),
3869                        (*aliaspp)[j].line_no);
3870             store_error_message(errmsg);
3871             return -1;
3872          }
3873          j++;
3874       }
3875    }
3876 #endif
3877 
3878    /* Check to see if there's an available location          */
3879    /* If not, increase the size of the memory allocation.    */
3880    /* (Always leave room for a final termination indicator.) */
3881    if ( size == (max_size - 1)) {
3882       max_size += blksize ;
3883       *aliaspp = (ALIAS *) realloc(*aliaspp, max_size * strucsize );
3884       if ( *aliaspp == NULL ) {
3885          (void) fprintf(stderr, "Unable to increase size of Alias list.\n");
3886          exit(1);
3887       }
3888 
3889       /* Initialize the new memory allocation */
3890       for ( j = size; j < max_size; j++ ) {
3891          (*aliaspp)[j].label[0] = '\0';
3892          (*aliaspp)[j].modtype = -1;
3893          (*aliaspp)[j].line_no = 0;
3894          (*aliaspp)[j].vflags = 0;
3895          (*aliaspp)[j].flags = 0;
3896          (*aliaspp)[j].xflags = 0;
3897          (*aliaspp)[j].kflags = 0;
3898          (*aliaspp)[j].optflags = 0;
3899          (*aliaspp)[j].optflags2 = 0;
3900          (*aliaspp)[j].tmin = 0;
3901          (*aliaspp)[j].tmax = 0;
3902          (*aliaspp)[j].rhmin = 0;
3903          (*aliaspp)[j].rhmax = 0;
3904          for ( k = 0; k < NOFFSET; k++ )
3905             (*aliaspp)[j].offset[k] = 0.0;
3906          (*aliaspp)[j].vtype = 0;
3907          (*aliaspp)[j].subtype = 0;
3908          (*aliaspp)[j].nident = 0;
3909          for ( k = 0; k < NIDENT; k++ ) {
3910             (*aliaspp)[j].ident[k] = 0;
3911             (*aliaspp)[j].kaku_keymap[k] = 0;
3912             (*aliaspp)[j].kaku_grpmap[k] = 0;
3913          }
3914          (*aliaspp)[j].xlate_func = NULL;
3915 //         (*aliaspp)[j].timeout = 0;
3916          (*aliaspp)[j].hb_timeout = -1L;
3917          (*aliaspp)[j].hb_index = -1;
3918          (*aliaspp)[j].nvar = 0;
3919          (*aliaspp)[j].storage_index = -1;
3920          (*aliaspp)[j].storage_units = 0;
3921          for ( k = 0; k < NFUNCLIST; k++ ) {
3922             (*aliaspp)[j].funclist[k] = 0xff;
3923             (*aliaspp)[j].statusoffset[k] = k;
3924          }
3925          (*aliaspp)[j].ext0links = 0;
3926       }
3927    }
3928 
3929    j = size;
3930    size += 1;
3931 
3932    (void) strncpy2((*aliaspp)[j].label, label, NAME_LEN);
3933    (*aliaspp)[j].line_no = line_no;
3934    (*aliaspp)[j].housecode = toupper((int)housecode);
3935    (*aliaspp)[j].hcode = hc2code(housecode);
3936    (*aliaspp)[j].unitbmap = bmap;
3937    (*aliaspp)[j].modtype = modtype;
3938    module_attributes(modtype, &vflags, &flags, &xflags, &kflags, &maxlevel);
3939    (*aliaspp)[j].vflags = vflags;
3940    (*aliaspp)[j].flags = flags;
3941    (*aliaspp)[j].xflags = xflags;
3942    (*aliaspp)[j].kflags = kflags;
3943    (*aliaspp)[j].maxlevel = maxlevel;
3944    (*aliaspp)[j].onlevel = maxlevel;
3945    (*aliaspp)[j].xlate_func = module_xlate_func(modtype);
3946 
3947    return j;
3948 }
3949 
3950 /*---------------------------------------------------------------------+
3951  | Add a scene or usersyn to the array of SCENE structures and return  |
3952  | the index of the new member.                                        |
3953  | Argument 'type' may be 1 for a scene or 2 for a usersyn             |
3954  +---------------------------------------------------------------------*/
add_scene(SCENE ** scenepp,char * label,int line_no,char * body,unsigned int type)3955 int add_scene ( SCENE **scenepp, char *label,
3956                  int line_no, char *body, unsigned int type )
3957 {
3958    static int    size, max_size ;
3959    static int    strucsize = sizeof(SCENE);
3960    int           j;
3961    char          *sp;
3962    int           cmdc, nparms;
3963    char          **cmdv;
3964    int           blksize = 10;
3965    char          errmsg[128];
3966    extern char   *typename[];
3967 
3968    /* Allocate initial block of memory */
3969    if ( *scenepp == NULL ) {
3970       *scenepp = calloc(blksize, strucsize );
3971       if ( *scenepp == NULL ) {
3972          fprintf(stderr, "Unable to allocate memory for Scene/Usersyn.\n");
3973          exit(1);
3974       }
3975       max_size = blksize;
3976       size = 0;
3977       /* Initialize it */
3978       for ( j = 0; j < max_size; j++ ) {
3979          (*scenepp)[j].label[0] = '\0';
3980          (*scenepp)[j].line_no = -1;
3981          (*scenepp)[j].nparms = 0;
3982          (*scenepp)[j].type = 0;
3983          (*scenepp)[j].body = NULL;
3984       }
3985    }
3986 
3987    /* Check for a valid scene label */
3988    if ( (int)strlen(label) > SCENE_LEN ) {
3989       sprintf(errmsg,
3990          "%s label too long - maximum %d characters", typename[type], SCENE_LEN);
3991       store_error_message(errmsg);
3992       return -1;
3993    }
3994    if ( strchr("+_-", *label) != NULL ) {
3995       sprintf(errmsg, "%s label may not may not begin with '+', '-' or '_'", typename[type]);
3996       store_error_message(errmsg);
3997       return -1;
3998    }
3999    if ( strchr(label, '$') != NULL ) {
4000       sprintf(errmsg, "%s label may not contain the '$' character", typename[type]);
4001       store_error_message(errmsg);
4002       return -1;
4003    }
4004    if ( strchr(label, ';') != NULL ) {
4005       sprintf(errmsg, "%s label may not contain the ';' character", typename[type]);
4006       store_error_message(errmsg);
4007       return -1;
4008    }
4009 
4010    /* See if the scene label is already in the list.     */
4011    /* If so, it's an error.                              */
4012    if ( (j = lookup_scene(*scenepp, label)) >= 0 ) {
4013       sprintf(errmsg,
4014          "%s label '%s' previously defined as a %s on line %d",
4015           typename[type], label, typename[(*scenepp)[j].type], (*scenepp)[j].line_no);
4016       store_error_message(errmsg);
4017       return -1;
4018    }
4019 
4020    if ( is_admin_cmd(label) || is_direct_cmd(label) ) {
4021       sprintf(errmsg, "%s label '%s' conflicts with heyu command", typename[type], label);
4022       store_error_message(errmsg);
4023       return -1;
4024    }
4025 
4026    /* Check to see if there's an available location          */
4027    /* If not, increase the size of the memory allocation.    */
4028    /* (Always leave room for a final termination indicator.) */
4029    if ( size == (max_size - 1)) {
4030       max_size += blksize ;
4031       *scenepp = realloc(*scenepp, max_size * strucsize );
4032       if ( *scenepp == NULL ) {
4033          fprintf(stderr, "Unable to increase size of Scene/Usersyn list.\n");
4034          exit(1);
4035       }
4036 
4037       /* Initialize the new memory allocation */
4038       for ( j = size; j < max_size; j++ ) {
4039          (*scenepp)[j].label[0] = '\0';
4040          (*scenepp)[j].line_no = -1;
4041          (*scenepp)[j].nparms = 0;
4042          (*scenepp)[j].type = 0;
4043          (*scenepp)[j].body = NULL;
4044       }
4045    }
4046 
4047    j = size;
4048    size += 1;
4049 
4050    /* Determine the number of replaceable parameters */
4051    sp = strdup(body);
4052    tokenize(sp, " \t;", &cmdc, &cmdv);
4053    nparms = max_parms(cmdc, cmdv);
4054    free(sp);
4055    free(cmdv);
4056    if ( *error_message() != '\0' ) {
4057       sprintf(errmsg, "%s '%s': ", typename[type], label);
4058       add_error_prefix(errmsg);
4059    }
4060    if ( nparms < 0 )
4061       return -1;
4062 
4063    sp = strdup(body);
4064    if ( sp == NULL ) {
4065       fprintf(stderr,
4066         "Unable to allocate memory for body of Scene/Usersyn '%s'.\n", label);
4067          exit(1);
4068    }
4069    strtrim(sp);
4070 
4071    (void) strncpy2((*scenepp)[j].label, label, SCENE_LEN);
4072    (*scenepp)[j].line_no = line_no;
4073    (*scenepp)[j].nparms = nparms;
4074    (*scenepp)[j].type = type;
4075    (*scenepp)[j].body = sp;
4076 
4077    return j;
4078 }
4079 
4080 /*---------------------------------------------------------------------+
4081  | Parse Latitude string [NS+-]ddd:mm and store in struct config.      |
4082  | line_no <= 0 indicates string from environment, otherwise line in   |
4083  | configuration file.                                                 |
4084  +---------------------------------------------------------------------*/
parse_latitude(char * string)4085 int parse_latitude ( char *string )
4086 {
4087    char tmpbuff[128];
4088    char *sp1, *sp2;
4089    int  sign;
4090 
4091    if ( string == NULL )
4092       return 0;
4093 
4094    (void)strncpy2(tmpbuff, string, sizeof(tmpbuff) - 1);
4095    sp1 = tmpbuff;
4096 
4097    /* For compatibility with Heyu 1 */
4098    if ( isdigit((int)(*sp1)) ) {
4099       (void)strcpy(tmpbuff, "N");
4100       (void)strncpy2(tmpbuff + 1, string, sizeof(tmpbuff) - 2);
4101    }
4102    else if ( *sp1 == '+' ) {
4103       *sp1 = 'N';
4104    }
4105    else if ( *sp1 == '-' ) {
4106       *sp1 = 'S';
4107    }
4108 
4109    switch ( toupper((int)(*sp1)) ) {
4110       case 'N' :
4111          sign = 1;
4112          break;
4113       case 'S' :
4114          sign = -1;
4115          break;
4116       default :
4117          sign = -2;
4118          break;
4119    }
4120    sp1++;
4121 
4122    if ( sign < -1 || !(sp2 = strchr(sp1, ':')) ) {
4123          store_error_message("LATITUDE invalid - must be [NS+-]dd:mm");
4124          configp->loc_flag &= ~(LATITUDE) ;
4125          return 1;
4126    }
4127 
4128    configp->lat_m = (int)strtol(sp2 + 1, NULL, 10);
4129    *sp2 = '\0';
4130    configp->lat_d = (int)strtol(sp1, NULL, 10);
4131    if ( configp->lat_d == 0 )
4132       configp->lat_m *= sign ;
4133    else
4134       configp->lat_d *= sign ;
4135 
4136    configp->latitude = ( configp->lat_d < 0 ) ?
4137          (double)configp->lat_d - (double)configp->lat_m/60. :
4138                (double)configp->lat_d + (double)configp->lat_m/60.;
4139 
4140    if ( configp->latitude < -89. || configp->latitude > 89. ) {
4141          store_error_message("LATITUDE outside range -89 to +89 degrees");
4142          configp->loc_flag &= ~(LATITUDE);
4143          return 1;
4144    }
4145 
4146    configp->loc_flag |= LATITUDE ;
4147 
4148    return 0;
4149 }
4150 
4151 /*---------------------------------------------------------------------+
4152  | Parse Longitude string [EW+-]ddd:mm and store in struct config.     |
4153  | line_no <= 0 indicates string from environment, otherwise line in   |
4154  | configuration file.                                                 |
4155  +---------------------------------------------------------------------*/
parse_longitude(char * string)4156 int parse_longitude ( char *string )
4157 {
4158    char tmpbuff[128];
4159    char *sp1, *sp2;
4160    int  sign;
4161 
4162    if ( string == NULL )
4163       return 0;
4164 
4165    (void)strncpy2(tmpbuff, string, sizeof(tmpbuff) - 1);
4166    sp1 = tmpbuff;
4167 
4168    /* For compatibility with Heyu 1, where positive longitude assumed West */
4169    if ( isdigit((int)(*sp1)) ) {
4170       (void)strcpy(tmpbuff, "W");
4171       (void)strncpy2(tmpbuff + 1, string, sizeof(tmpbuff) - 2);
4172    }
4173    else if ( *sp1 == '+' ) {
4174       *sp1 = 'W';
4175    }
4176    else if ( *sp1 == '-' ) {
4177       *sp1 = 'E';
4178    }
4179 
4180    switch ( toupper((int)(*sp1)) ) {
4181       case 'E' :
4182          sign = 1;
4183          break;
4184       case 'W' :
4185          sign = -1;
4186          break;
4187       default :
4188          sign = -2;
4189          break;
4190    }
4191    sp1++;
4192 
4193    if ( sign < -1 || !(sp2 = strchr(sp1, ':')) ) {
4194          store_error_message("LONGITUDE invalid - must be [EW+-]dd:mm");
4195          configp->loc_flag &= ~(LONGITUDE) ;
4196          return 1;
4197    }
4198 
4199    configp->lon_m = (int)strtol(sp2 + 1, NULL, 10);
4200    *sp2 = '\0';
4201    configp->lon_d = (int)strtol(sp1, NULL, 10);
4202    if ( configp->lon_d == 0 )
4203       configp->lon_m *= sign ;
4204    else
4205       configp->lon_d *= sign ;
4206 
4207    configp->longitude = ( configp->lon_d < 0 ) ?
4208       (double)configp->lon_d - (double)configp->lon_m/60. :
4209                (double)configp->lon_d + (double)configp->lon_m/60.;
4210 
4211    if ( configp->longitude < -180. || configp->longitude > 180. ) {
4212          store_error_message("LONGITUDE outside range -180 to +180 degrees");
4213          configp->loc_flag &= ~(LONGITUDE);
4214          return 1;
4215    }
4216 
4217    configp->loc_flag |= LONGITUDE ;
4218 
4219    return 0;
4220 }
4221 
4222 /*---------------------------------------------------------------------+
4223  | Check executability of a file on user's PATH                        |
4224  +---------------------------------------------------------------------*/
is_executable(char * pathname)4225 int is_executable ( char *pathname )
4226 {
4227    char pathbuffer[1024];
4228    char buffer[1024];
4229    char *sp;
4230    int  j, tokc;
4231    char **tokv;
4232 
4233    if ( access(pathname, X_OK) == 0 )
4234       return 1;
4235 
4236    if ( (sp = getenv("PATH")) == NULL )
4237       return 0;
4238 
4239    strncpy2(pathbuffer, sp, sizeof(pathbuffer) - 1);
4240 
4241    tokenize(buffer, ":", &tokc, &tokv);
4242 
4243    for ( j = 0; j < tokc; j++ ) {
4244       strncpy2(buffer, tokv[j], sizeof(buffer) - 1);
4245       strncat(buffer, "/", sizeof(buffer) - 1 - strlen(buffer));
4246       strncat(buffer, pathname, sizeof(buffer) - 1 - strlen(buffer));
4247       if ( access(buffer, X_OK) == 0 ) {
4248          free(tokv);
4249          return 1;
4250       }
4251    }
4252    free(tokv);
4253    return 0;
4254 }
4255 
4256 /*---------------------------------------------------------------------+
4257  | Free memory allocated for SCRIPT structure and contents.            |
4258  +---------------------------------------------------------------------*/
free_scripts(SCRIPT ** scriptpp)4259 void free_scripts ( SCRIPT **scriptpp )
4260 {
4261    int j = 0;
4262 
4263    if ( *scriptpp == NULL )
4264       return;
4265 
4266    while ( (*scriptpp)[j].line_no > 0 ) {
4267      if ( (*scriptpp)[j].cmdline ) {
4268         free((*scriptpp)[j].cmdline);
4269      }
4270      j++;
4271    }
4272    free(*scriptpp);
4273    *scriptpp = NULL;
4274 
4275    return;
4276 }
4277 
4278 /*---------------------------------------------------------------------+
4279  |  Free the array of SCENEs and the scene bodies therein.             |
4280  +---------------------------------------------------------------------*/
free_scenes(SCENE ** scenepp)4281 void free_scenes ( SCENE **scenepp )
4282 {
4283    int j = 0;
4284 
4285    if ( *scenepp == NULL )
4286       return;
4287 
4288    while ( (*scenepp)[j].line_no > 0 ) {
4289      if ( (*scenepp)[j].body != NULL ) {
4290         free((*scenepp)[j].body);
4291      }
4292      j++;
4293    }
4294 
4295    free((*scenepp));
4296    *scenepp = NULL;
4297    return;
4298 }
4299 
4300 /*---------------------------------------------------------------------+
4301  |  Free the array of ALIASES.                                         |
4302  +---------------------------------------------------------------------*/
free_aliases(ALIAS ** aliaspp)4303 void free_aliases ( ALIAS **aliaspp )
4304 {
4305    if ( *aliaspp == NULL )
4306       return;
4307 
4308    free((*aliaspp));
4309    *aliaspp = NULL;
4310    return;
4311 }
4312 
4313 /*---------------------------------------------------------------------+
4314  |  Free the array of LAUNCHERS.                                       |
4315  +---------------------------------------------------------------------*/
free_launchers(LAUNCHER ** launcherpp)4316 void free_launchers ( LAUNCHER **launcherpp )
4317 {
4318    if ( *launcherpp == NULL )
4319       return;
4320 
4321    free((*launcherpp));
4322    *launcherpp = NULL;
4323    return;
4324 }
4325 
4326 /*---------------------------------------------------------------------+
4327  |  Free the relay powerfail script.                                   |
4328  +---------------------------------------------------------------------*/
free_relay_powerfail_script(char ** pfail_script)4329 void free_relay_powerfail_script ( char **pfail_script )
4330 {
4331    if ( *pfail_script == NULL )
4332       return;
4333 
4334    free(*pfail_script);
4335    *pfail_script = NULL;
4336    return;
4337 }
4338 
4339 /*---------------------------------------------------------------------+
4340  |  Free arrays of ALIASes, SCENEs, SCRIPTs, and LAUNCHERs             |
4341  +---------------------------------------------------------------------*/
free_all_arrays(CONFIG * configp)4342 void free_all_arrays ( CONFIG *configp )
4343 {
4344    free_aliases(&configp->aliasp);
4345    free_scenes(&configp->scenep);
4346    free_scripts(&configp->scriptp);
4347    free_launchers(&configp->launcherp);
4348    free_relay_powerfail_script(&configp->pfail_script);
4349 
4350    return;
4351 }
4352 
4353 /*---------------------------------------------------------------------+
4354  |  Create the file pathspecs for many files.                          |
4355  +---------------------------------------------------------------------*/
create_file_paths(void)4356 int create_file_paths ( void )
4357 {
4358     sprintf(statefile, "%s", pathspec(STATE_FILE));
4359     sprintf(enginelockfile, "%s/LCK..%s%s", LOCKDIR, STATE_LOCKFILE, configp->suffix);
4360 
4361 #if 0 /* future */
4362     sprintf(spoolfile, "%s/%s%s", SPOOLDIR, SPOOLFILE, configp->suffix);
4363     sprintf(relaylockfile, "%s/LCK..%s%s", LOCKDIR, RELAYFILE, configp->suffix);
4364     sprintf(writelockfile, "%s/LCK..%s%s", LOCKDIR, WRITEFILE, configp->suffix);
4365     sprintf(ttylockfile, "%s/LCK.%s", LOCKDIR, configp->suffix);
4366 #endif /* future */
4367 
4368     return 0;
4369 }
4370 
4371 /*---------------------------------------------------------------------+
4372  | Open the user's X10 configuration file and call parse_config() to   |
4373  | parse it and fill in global structure config.  exit(1) is called    |
4374  | if the file cannot be found or read, or if it contains errors.      |
4375  +---------------------------------------------------------------------*/
get_configuration(unsigned char mode)4376 int get_configuration ( unsigned char mode )
4377 {
4378 
4379    FILE       *fd ;
4380    int        error_count;
4381    char       confp[PATH_LEN + 1];
4382    CONFIG     configtmp;
4383 
4384    /* Return if the configuration file has already been read into memory */
4385    if ( config.read_flag != 0 ) {
4386       return 0;
4387    }
4388 
4389    configp = &configtmp;
4390 
4391    find_heyu_path();
4392 
4393    strncpy2(confp, pathspec(heyu_config), sizeof(confp) - 1);
4394 
4395    if ( verbose )
4396       (void) fprintf(stdout,
4397           "Reading Heyu configuration file '%s'\n", confp);
4398 
4399    if ( !(fd = fopen(confp, "r")) ) {
4400       if ( !i_am_relay )
4401          fprintf(stderr, "Unable to find (or open) Heyu configuration file '%s'\n", confp);
4402       else
4403          syslog(LOG_ERR, "Unable to find (or open) Heyu configuration file '%s'\n", confp);
4404 
4405        exit(1);
4406    }
4407 
4408    setup_countdown_timers();
4409 
4410    error_count = parse_config( fd, mode );
4411 
4412    (void) fclose( fd );
4413 
4414    if ( error_count != 0 ) {
4415       if ( mode == CONFIG_RESTART && (i_am_aux || i_am_relay) ) {
4416          return error_count;
4417       }
4418       else {
4419          (void)fprintf(stderr,
4420                  "Quitting due to errors in configuration file '%s'\n", confp);
4421          exit(1);
4422       }
4423    }
4424 
4425    return 0;
4426 }
4427 
4428 /*---------------------------------------------------------------------+
4429  | Parse a time string X:Y:Z and return the total time represented by  |
4430  | 3600*X + 60*Y + Z, or -1 if error. With X:Y:Z = hh:mm:ss the value  |
4431  | returned will typically represent seconds.  With just Y:Z the value |
4432  | returned can be interpreted as either hh:mm or mm:ss depending on   |
4433  | the context.  A null field or empty field, i.e., "::" or ": :" is   |
4434  | flagged as an error, as is a trailing ':'.                          |
4435  +---------------------------------------------------------------------*/
parse_hhmmss(char * hhmmss,int maxfields)4436 long parse_hhmmss ( char *hhmmss, int maxfields )
4437 {
4438    int  j, tokc;
4439    long value, seconds = 0;
4440    char **tokv;
4441    char *sp;
4442    char buf[32];
4443 
4444    if ( strstr(hhmmss, "::") != NULL ||
4445         *(hhmmss + strlen(hhmmss) - 1) == ':' ||
4446         strlen(hhmmss) > (sizeof(buf) - 1) ) {
4447       return -1;
4448    }
4449 
4450    strcpy(buf, hhmmss);
4451 
4452    tokenize(buf, ":", &tokc, &tokv );
4453 
4454    if ( tokc > maxfields ) {
4455       free(tokv);
4456       return -1;
4457    }
4458 
4459    for ( j = 0; j < tokc; j++ ) {
4460       if ( *strtrim(tokv[j]) == '\0' ) {
4461          free(tokv);
4462          return -1;
4463       }
4464       else {
4465          value = strtol(tokv[j], &sp, 10);
4466          if ( strchr(" \t\n", *sp) == NULL ) {
4467             free(tokv);
4468             return -1;
4469          }
4470       }
4471       seconds = 60 * seconds + value;
4472    }
4473    free(tokv);
4474    return seconds;
4475 }
4476 
4477 /*---------------------------------------------------------------------+
4478  | Verify unique IDs for Sec, Ent or RFXSensor modules                 |
4479  +---------------------------------------------------------------------*/
verify_unique_ids(unsigned char vtype)4480 int verify_unique_ids ( unsigned char vtype )
4481 {
4482    ALIAS          *aliasp;
4483    unsigned short ident, mask;
4484    int            j, k;
4485    char           errmsg[160];
4486    int            ntable, dupes;
4487 
4488    struct idtable_st {
4489       unsigned short ident;
4490       int            index;
4491    } idtable[512];
4492 
4493    if ( !(aliasp = configp->aliasp) )
4494       return 0;
4495 
4496    mask = (vtype == RF_SEC) ? configp->securid_mask :
4497           (vtype == RF_OREGON) ? configp->oreid_mask : 0xffffu;
4498 
4499    strcpy(errmsg, "The same ID appears in ALIASes:");
4500 
4501    ntable = 0;
4502    j = 0;
4503    while ( aliasp[j].line_no > 0 ) {
4504       if ( aliasp[j].vtype == vtype ) {
4505          for ( k = 0; k < aliasp[j].nident; k++ ) {
4506             idtable[ntable].ident = aliasp[j].ident[k] & mask;
4507             idtable[ntable++].index = j;
4508          }
4509       }
4510       j++;
4511    }
4512 
4513    dupes = 0;
4514    for ( j = 0; j < ntable; j++ ) {
4515       ident = idtable[j].ident;
4516       for ( k = j + 1; k < ntable; k++ ) {
4517          if ( idtable[k].ident == ident ) {
4518             dupes++;
4519             snprintf(errmsg + strlen(errmsg), sizeof(errmsg) - 1, " (%s %s)",
4520                aliasp[idtable[j].index].label, aliasp[idtable[k].index].label);
4521          }
4522       }
4523    }
4524 
4525    if ( dupes > 0 ) {
4526       store_error_message(errmsg);
4527       return 1;
4528    }
4529 
4530    return 0;
4531 }
4532 
4533 /*---------------------------------------------------------------------+
4534  | Display configuration file stripped of comments and blank lines.    |
4535  +---------------------------------------------------------------------*/
show_configuration(void)4536 void show_configuration ( void )
4537 {
4538    FILE       *fd ;
4539    char       confp[PATH_LEN + 1];
4540    char       buffer[LINE_LEN];
4541    char       *sp;
4542 
4543    get_configuration(CONFIG_INIT);
4544 
4545    find_heyu_path();
4546 
4547    strncpy2(confp, pathspec(heyu_config), sizeof(confp) - 1);
4548 
4549    if ( !(fd = fopen(confp, "r")) ) {
4550        exit(1);
4551    }
4552 
4553    while ( fgets(buffer, sizeof(buffer), fd) != NULL ) {
4554       if ( (sp = strchr(buffer, '#')) != NULL )
4555          *sp = '\0';
4556       strtrim(buffer);
4557       if ( *buffer )
4558          printf("%s\n", buffer);
4559    }
4560 
4561    (void) fclose( fd );
4562    return;
4563 }
4564 
display_webhook_usage(void)4565 void display_webhook_usage ( void )
4566 {
4567    printf("Usage: heyu webhook <option> -L[fmt] -d|D[fmt] -m|M[fmt] -b[fmt] -ce<list> -nb<repl> [category]\n");
4568    printf("options:\n");
4569    printf("  fileinfo      Deprecated - use pathinfo\n");
4570    printf("  menuinfo      Deprecated - use helpinfo\n");
4571    printf("  pathinfo      Display pathspecs for Heyu config and log files.\n");
4572    printf("  flaginfo      Display flags, czflags, or tzflags as long ASCII bitmap.\n");
4573    printf("  flagbankinfo  Display flags, czflags, or tzflags as multiple banks of 32.\n");
4574    printf("  helpinfo      Display available help menu commands\n");
4575    printf("  config_dump   Display display configuration file directives.\n");
4576    printf("  maskinfo      Display masks for environment variable $X10_Hu\n");
4577    printf("  flagmaskinfo  Display masks for environment variable $X10_Hu_vFlags\n");
4578    printf("switches:\n");
4579    printf("  -L        Prefix with formatted line number in configuration file.\n");
4580    printf("  -d |-D    Display formatted directive label in lower | upper case.\n");
4581    printf("  -m |-M    Format multiple directive label numbering beginning with 0 | 1\n");
4582    printf("  -b        Format body of directive.\n");
4583    printf("  -ce       Characters in <list> are escaped in script command lines.\n");
4584    printf("  -nb       Null body is replaced by <repl>.\n");
4585    printf("The format specification %%V must appear in each format string ([fmt]) where\n");
4586    printf("it is replaced as appropriate by the line number, directive label, label number,\n");
4587    printf("or directive body.\n");
4588    printf("\n");
4589    printf("categories for config_dump:\n");
4590    printf("  alias\n");
4591    printf("  usersyn\n");
4592    printf("  scene\n");
4593    printf("  script\n");
4594    printf("  other    (Everything other than the above directives.)\n");
4595    printf("  <blank>  (Everything.)\n");
4596    printf("categories for pathinfo:\n");
4597    printf("  conf     Configuration file pathspec.\n");
4598    printf("  log      Logfile pathspec.\n");
4599    printf("  <blank>  Both the above.\n");
4600    printf("categories for flaginfo and flagbankinfo:\n");
4601    printf("  flags    Common flags.\n");
4602    printf("  czflags  Counter-zero flags.\n");
4603    printf("  tzflags  Timer-zero flags.\n");
4604    printf("Example:\n");
4605    printf("  heyu webhook config_dump -L\"%%V: \" -dheyu_%%V -m\\(%%V\\) -b=\\\"%%V\\\" alias\n");
4606    printf("yields:\n");
4607    printf(" 13: heyu_alias(2)=\"porch_light A7 StdWS\"\n");
4608    printf("\n");
4609    return;
4610 }
4611 
flags2asciimap(char * asciimap,unsigned long flags)4612 int flags2asciimap ( char *asciimap, unsigned long flags )
4613 {
4614    int j;
4615 
4616    for ( j = 0; j < 32; j++ ) {
4617       asciimap[j] = (flags & (1 << j)) ? '1' : '0';
4618    }
4619    asciimap[32] = '\0';
4620 
4621    return 0;
4622 }
4623 
allflags2asciimap(char * asciimap,unsigned long * flags,int num_banks)4624 int allflags2asciimap ( char *asciimap, unsigned long *flags, int num_banks )
4625 {
4626    int j, k, bank;
4627 
4628    k = 0;
4629    for ( bank = 0; bank < num_banks; bank++ ) {
4630       for ( j = 0; j < 32; j++ ) {
4631          asciimap[k++] = (flags[bank] & (1 << j)) ? '1' : '0';
4632       }
4633    }
4634    asciimap[k++] = '\0';
4635 
4636    return 0;
4637 }
4638 
4639 
4640 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++
4641  |    START WEBHOOK SECTION                             |
4642  +++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
4643 
4644 #define WF_LINE        0x00000001
4645 #define WF_UPPER       0x00000002
4646 #define WF_MULT        0x00000004
4647 #define WF_ESC         0x00000008
4648 #define WF_ALIAS       0x00000010
4649 #define WF_USYN        0x00000020
4650 #define WF_SCENE       0x00000040
4651 #define WF_SCRIPT      0x00000080
4652 #define WF_OTHER       0x00000100
4653 #define WF_CONF        0x00000200
4654 #define WF_LOG         0x00000400
4655 #define WF_NULLBODY    0x00000800
4656 #define WF_FLAGS       0x00001000
4657 #define WF_CZFLAGS     0x00002000
4658 #define WF_TZFLAGS     0x00004000
4659 #define WF_MASKS       0x00008000
4660 
4661 #define WF_ALLCAT (WF_ALIAS | WF_USYN | WF_SCENE | WF_SCRIPT | WF_OTHER)
4662 
4663 typedef struct {
4664    char label[NAME_LEN + 1];
4665    int  index;
4666 } MULTI;
4667 
4668 typedef struct {
4669    char label[NAME_LEN + 1];
4670 } IGNLIST;
4671 
4672 static struct cat_st {
4673    char *label;
4674    unsigned int flag;
4675 } catlist[] = {
4676    {"ALIAS",   WF_ALIAS },
4677    {"USERSYN", WF_USYN  },
4678    {"SCENE",   WF_SCENE },
4679    {"SCRIPT",  WF_SCRIPT},
4680    {"OTHER",   WF_OTHER },
4681 };
4682 #define NCATEGORY (sizeof(catlist)/sizeof(struct cat_st))
4683 
4684 struct webhook_st {
4685    char line_format[NAME_LEN + 1];
4686    char label_format[NAME_LEN + 1];
4687    char mult_format[NAME_LEN + 1];
4688    char body_format[NAME_LEN + 1];
4689    char escape_list[NAME_LEN + 1];
4690    char null_body[PATH_LEN + 1];
4691    unsigned long flags;
4692    int index_start;
4693    int nmult;
4694    int nignore;
4695    MULTI mtable[sizeof(command)/sizeof(struct conf)];
4696    IGNLIST igntable[sizeof(command)/sizeof(struct conf)];
4697 };
4698 
category_flag(char * category)4699 unsigned int category_flag ( char *category )
4700 {
4701    int j;
4702 
4703    for ( j = 0; j < NCATEGORY; j++ ) {
4704       if ( strcmp(category, catlist[j].label) == 0 ) {
4705          return catlist[j].flag;
4706       }
4707    }
4708    return 0;
4709 }
4710 
directive_flag(char * directive)4711 unsigned int directive_flag ( char *directive )
4712 {
4713    int j;
4714 
4715    for ( j = 0; j < NCATEGORY; j++ ) {
4716       if ( strcmp(directive, catlist[j].label) == 0 ) {
4717          return catlist[j].flag;
4718       }
4719    }
4720    return WF_OTHER;
4721 }
4722 
init_hooks(struct webhook_st * hookp)4723 void init_hooks ( struct webhook_st *hookp )
4724 {
4725 
4726    strcpy(hookp->line_format, "%d: ");
4727    strcpy(hookp->label_format, "%s");
4728    strcpy(hookp->mult_format, "[%d]");
4729    strcpy(hookp->body_format, " %s");
4730    strcpy(hookp->escape_list, "");
4731    strcpy(hookp->null_body, "");
4732    hookp->flags = 0;
4733    hookp->index_start = 0;
4734    hookp->nmult = 0;
4735    hookp->nignore = 0;
4736 
4737    return;
4738 }
4739 
set_format(char * fmt_str,char * fmt)4740 int set_format ( char *fmt_str, char *fmt )
4741 {
4742    char *sp;
4743 
4744    if ( strlen(fmt_str) > NAME_LEN ) {
4745       fprintf(stderr, "Format too long.\n");
4746       return 1;
4747    }
4748 
4749    if ( (sp = strstr(fmt_str, "%V")) == NULL ) {
4750       fprintf(stderr, "No '%%V' in format string.\n");
4751       return 1;
4752    }
4753    strncpy(sp, "..", 2);
4754 
4755    if ( strchr(fmt_str, '%') != NULL ) {
4756       fprintf(stderr, "Only '%%V' may appear in format string.\n");
4757       return 1;
4758    }
4759    strncpy(sp, fmt, 2);
4760 
4761    return 0;
4762 }
4763 
create_mult_ign_tables(struct webhook_st * hookp)4764 int create_mult_ign_tables ( struct webhook_st *hookp )
4765 {
4766    int i, j, m;
4767    MULTI  *mtable;
4768    IGNLIST *igntable;
4769 
4770    mtable = hookp->mtable;
4771    igntable = hookp->igntable;
4772 
4773    i = m = 0;
4774    for ( j = 1; j < ncommands; j++ ) {
4775       if ( command[j].flags & CMLT ) {
4776          strcpy(mtable[m].label, command[j].name);
4777          mtable[m].index = hookp->index_start;
4778          m++;
4779       }
4780       else if ( command[j].flags & CIGN ) {
4781          strcpy(igntable[i].label, command[j].name);
4782          i++;
4783       }
4784    }
4785    hookp->nmult = m;
4786    *(mtable[m].label) = '\0';
4787    hookp->nignore = i;
4788    *(igntable[i].label) = '\0';
4789 
4790    return m;
4791 }
4792 
if_ignore(char * directive,struct webhook_st * hookp)4793 int if_ignore ( char *directive, struct webhook_st *hookp )
4794 {
4795    int j;
4796    IGNLIST *igntable;
4797 
4798    igntable = hookp->igntable;
4799 
4800    for ( j = 0; j < hookp->nignore; j++ ) {
4801       if ( strcmp(directive, igntable[j].label) == 0 )
4802          return 1;
4803    }
4804    return 0;
4805 }
4806 
init_mult_table(struct webhook_st * hookp)4807 void init_mult_table ( struct webhook_st *hookp )
4808 {
4809    int k = 0;
4810    MULTI *mtable;
4811 
4812    mtable = hookp->mtable;
4813 
4814    for ( k = 0; k < hookp->nmult; k++ )
4815       mtable[k].index = hookp->index_start;
4816 
4817    return;
4818 }
4819 
4820 
mult_index(MULTI * multp,char * label)4821 int mult_index( MULTI *multp, char *label )
4822 {
4823    int index, k = 0;
4824 
4825    while ( *multp[k].label ) {
4826       if ( strcmp(multp[k].label, label) == 0 ) {
4827          index = multp[k].index;
4828          multp[k].index += 1;
4829          return index;
4830       }
4831       k++;
4832    }
4833    return -1;
4834 }
4835 
4836 
parse_switches(int argc,char * argv[],struct webhook_st * hookp)4837 int parse_switches ( int argc, char *argv[], struct webhook_st *hookp )
4838 {
4839    int j;
4840 
4841    if ( argc < 4 )
4842       return 0;
4843 
4844    for ( j = 3; j < argc; j++ ) {
4845       if ( *argv[j] == '-' ) {
4846          switch ( *(argv[j] + 1) ) {
4847             case 'L' :
4848                hookp->flags |= WF_LINE;
4849                if ( *(argv[j] + 2) != '\0' ) {
4850                   if ( set_format(argv[j] + 2, "%d") != 0 )
4851                      return 1;
4852                   strcpy(hookp->line_format, (argv[j] + 2));
4853                }
4854                break;
4855             case 'D' :
4856                hookp->flags |= WF_UPPER;
4857             case 'd' :
4858                if ( *(argv[j] + 2) != '\0' ) {
4859                   if ( set_format(argv[j] + 2, "%s") != 0 )
4860                      return 1;
4861                   strcpy(hookp->label_format, (argv[j] + 2));
4862                }
4863                break;
4864             case 'M' :
4865                hookp->index_start = 1;
4866             case 'm' :
4867                hookp->flags |= WF_MULT;
4868                if ( *(argv[j] + 2) != '\0' ) {
4869                   if ( set_format(argv[j] + 2, "%d") != 0 )
4870                      return 1;
4871                   strcpy(hookp->mult_format, (argv[j] + 2));
4872                }
4873                break;
4874             case 'b' :
4875                if ( *(argv[j] + 2) != '\0' ) {
4876                   if ( set_format(argv[j] + 2, "%s") != 0 )
4877                      return 1;
4878                   strcpy(hookp->body_format, (argv[j] + 2));
4879                }
4880                break;
4881             case 'c' :
4882                if ( *(argv[j] + 2) == 'e' ) {
4883                   hookp->flags |= WF_ESC;
4884                   if ( *(argv[j] + 3) ) {
4885                      strcpy(hookp->escape_list, (argv[j] + 3));
4886                   }
4887                }
4888                else {
4889                   fprintf(stderr, "Switch '%s' invalid.\n", argv[j]);
4890                   return 1;
4891                }
4892                break;
4893             case 'n' :
4894                if ( *(argv[j] + 2) == 'b' ) {
4895                   hookp->flags |= WF_NULLBODY;
4896                   if ( *(argv[j] + 3) ) {
4897                      strcpy(hookp->null_body, (argv[j] + 3));
4898                   }
4899                }
4900                else {
4901                   fprintf(stderr, "Switch '%s' invalid.\n", argv[j]);
4902                   return 1;
4903                }
4904                break;
4905 
4906             default :
4907                fprintf(stderr, "Switch '%s' not recognized.\n", argv[j]);
4908                return 1;
4909                break;
4910          }
4911       }
4912    }
4913    return 0;
4914 }
4915 
webhook_script_cmdline(char * cmdptr,struct webhook_st * hookp)4916 int webhook_script_cmdline ( char *cmdptr, struct webhook_st *hookp )
4917 {
4918    char buffer[LINE_LEN];
4919    char *sp, *dp;
4920 
4921    if ( !(hookp->flags & WF_ESC) )
4922       return 0;
4923 
4924    strcpy(buffer, cmdptr);
4925    sp = buffer;
4926    dp = cmdptr;
4927    while ( *sp ) {
4928       if ( strchr(hookp->escape_list, *sp) )
4929          *dp++ = '\\';
4930       *dp++ = *sp++;
4931    }
4932    *dp = '\0';
4933 
4934    return 0;
4935 }
4936 
config_dump_category(FILE * fd,unsigned int wf_flag,struct webhook_st * hookp)4937 void config_dump_category ( FILE *fd, unsigned int wf_flag, struct webhook_st *hookp )
4938 {
4939    char buffer[LINE_LEN];
4940    char outbuf[LINE_LEN];
4941    char directive[NAME_LEN + 1];
4942    char *sp, *cmdptr;
4943    int  j, tokc, line_no, index;
4944    unsigned int flags, dflag;
4945    char **tokv = NULL;
4946    MULTI *mtable;
4947    IGNLIST *igntable;
4948 
4949    rewind(fd);
4950 
4951    flags = hookp->flags;
4952    mtable = hookp->mtable;
4953    igntable = hookp->igntable;
4954 
4955    line_no = 0;
4956    while ( fgets(buffer, sizeof(buffer), fd) != NULL ) {
4957       line_no++;
4958       if ( (sp = strchr(buffer, '#')) != NULL )
4959          *sp = '\0';
4960       strtrim(buffer);
4961       if ( *buffer == '\0' )
4962          continue;
4963       sp = buffer;
4964       get_token(directive, &sp, " \t\r\n", NAME_LEN + 1);
4965       strupper(directive);
4966 
4967       if ( if_ignore(directive, hookp) )
4968          continue;
4969 
4970       dflag = directive_flag(directive);
4971       if ( !(dflag & wf_flag) )
4972          continue;
4973 
4974       index = mult_index(mtable, directive);
4975       if ( !(flags & WF_UPPER) )
4976          strlower(directive);
4977       if ( flags & WF_LINE )
4978          printf(hookp->line_format, line_no);
4979       printf(hookp->label_format, directive);
4980       if ( (flags & WF_MULT) && (index >= 0) )
4981          printf(hookp->mult_format, index);
4982 
4983       *outbuf = '\0';
4984       if ( dflag & WF_SCRIPT ) {
4985          cmdptr = strstr(sp, "::");
4986          *cmdptr = '\0';
4987          tokenize(sp, " \t\r\n", &tokc, &tokv);
4988          for ( j = 0; j < tokc; j++ ) {
4989             strcat(outbuf, tokv[j]);
4990             strcat(outbuf, " ");
4991          }
4992          *cmdptr = ':';
4993          webhook_script_cmdline(cmdptr + 2, hookp);
4994          strcat(outbuf, cmdptr);
4995       }
4996       else {
4997          tokenize(sp, " \t\r\n", &tokc, &tokv);
4998          for ( j = 0; j < tokc; j++ ) {
4999             strcat(outbuf, tokv[j]);
5000             strcat(outbuf, " ");
5001          }
5002          *(outbuf + strlen(outbuf) - 1) = '\0';
5003       }
5004 
5005       printf(hookp->body_format, outbuf);
5006       printf("\n");
5007       free(tokv);
5008    }
5009 
5010    return;
5011 }
5012 
webhook_config_dump(int argc,char * argv[])5013 int webhook_config_dump ( int argc, char *argv[] )
5014 {
5015    char category[NAME_LEN + 1];
5016    struct webhook_st hooks;
5017    MULTI *mtable;
5018    FILE       *fp;
5019    char       confp[PATH_LEN + 1];
5020    int        j;
5021 
5022    init_hooks(&hooks);
5023 
5024    if ( argc > 3 && *argv[argc - 1] != '-' ) {
5025       strncpy2(category, argv[argc - 1], NAME_LEN);
5026       strupper(category);
5027 
5028       if ( (hooks.flags = category_flag(category)) == 0 ) {
5029          fprintf(stderr, "Unsupported category '%s'\n", argv[argc - 1]);
5030          return 1;
5031       }
5032    }
5033    else {
5034       hooks.flags = 0;
5035       for ( j = 0; j < NCATEGORY; j++ ) {
5036          hooks.flags |= catlist[j].flag;
5037       }
5038    }
5039 
5040    if ( parse_switches(argc, argv, &hooks) != 0 )
5041       return 1;
5042 
5043    create_mult_ign_tables(&hooks);
5044    init_mult_table(&hooks);
5045 
5046    mtable = hooks.mtable;
5047 
5048    get_configuration(CONFIG_INIT);
5049 
5050    find_heyu_path();
5051 
5052    strncpy2(confp, pathspec(heyu_config), sizeof(confp) - 1);
5053 
5054    if ( !(fp = fopen(confp, "r")) ) {
5055        exit(1);
5056    }
5057 
5058    config_dump_category(fp, hooks.flags, &hooks);
5059 
5060    fclose( fp );
5061 
5062    return 0;
5063 }
5064 
webhook_pathinfo(int argc,char * argv[])5065 int webhook_pathinfo ( int argc, char *argv[] )
5066 {
5067    char *configlabel[] = {"conf", "CONF"};
5068    char *loglabel[] = {"log", "LOG"};
5069    char category[NAME_LEN + 1];
5070    char *label;
5071    struct webhook_st hooks;
5072 
5073    init_hooks(&hooks);
5074 
5075    if ( argc > 3 && *argv[argc - 1] != '-' ) {
5076       strncpy2(category, argv[argc - 1], NAME_LEN);
5077       strupper(category);
5078 
5079       if ( strcmp(category, "CONF") == 0 )
5080          hooks.flags |= WF_CONF;
5081       else if ( strcmp(category, "LOG") == 0 )
5082          hooks.flags |= WF_LOG;
5083       else {
5084          fprintf(stderr, "Unsupported category '%s'\n", argv[argc - 1]);
5085          return 1;
5086       }
5087    }
5088    else {
5089       hooks.flags |= (WF_CONF | WF_LOG);
5090    }
5091 
5092    if ( parse_switches(argc, argv, &hooks) != 0 )
5093       return 1;
5094 
5095    get_configuration(CONFIG_INIT);
5096 
5097    if ( hooks.flags & WF_CONF ) {
5098       find_heyu_path();
5099       label = (hooks.flags & WF_UPPER) ? configlabel[1] : configlabel[0];
5100       printf(hooks.label_format, label);
5101       printf(hooks.body_format, pathspec(heyu_config));
5102       printf("\n");
5103    }
5104 
5105    if ( hooks.flags & WF_LOG ) {
5106       label = (hooks.flags & WF_UPPER) ? loglabel[1] : loglabel[0];
5107       printf(hooks.label_format, label);
5108       if ( strcmp(configp->logfile, "/dev/null") == 0 && (hooks.flags & WF_NULLBODY) )
5109          printf(hooks.body_format, hooks.null_body);
5110       else
5111          printf(hooks.body_format, configp->logfile);
5112       printf("\n");
5113    }
5114 
5115    return 0;
5116 }
5117 
get_help_topics(char ** topic,int * ntopic)5118 void get_help_topics ( char **topic, int *ntopic )
5119 {
5120 
5121    *ntopic = 0;
5122 
5123    topic[(*ntopic)++] = "admin";
5124    topic[(*ntopic)++] = "direct";
5125    topic[(*ntopic)++] = "state";
5126    topic[(*ntopic)++] = "internal";
5127 #ifdef HASCM17A
5128    topic[(*ntopic)++] = "cm17a";
5129 #endif
5130 #ifdef HASEXT0
5131    topic[(*ntopic)++] = "shutter";
5132 #endif
5133 #ifdef HASRFXS
5134    topic[(*ntopic)++] = "rfxsensor";
5135 #endif
5136 #ifdef HASRFXM
5137    topic[(*ntopic)++] = "rfxmeter";
5138 #endif
5139 #ifdef HASDMX
5140    topic[(*ntopic)++] = "digimax";
5141 #endif
5142 #ifdef HASORE
5143    topic[(*ntopic)++] = "oregon";
5144 #endif
5145 
5146   return;
5147 
5148 }
5149 
webhook_menuinfo(int argc,char * argv[])5150 int webhook_menuinfo ( int argc, char *argv[] )
5151 {
5152    char *topic[32];
5153    int  j, ntopic;
5154 
5155    get_help_topics (topic, &ntopic);
5156 
5157    for ( j = 0; j < ntopic; j++ )
5158       printf("$help_menu[%d]=\"/heyu/help %s\";\n", j, topic[j]);
5159    return 0;
5160 }
5161 
5162 
webhook_fileinfo(int argc,char * argv[])5163 int webhook_fileinfo ( int argc, char *argv[] )
5164 {
5165 
5166    get_configuration(CONFIG_INIT);
5167    find_heyu_path();
5168    printf("$heyu_conf=\"%s\";\n", pathspec(heyu_config));
5169    if ( strcmp(configp->logfile, "/dev/null") == 0 )
5170       printf("$heyu_log=\"/tmp/\";\n");
5171    else
5172       printf("$heyu_log=\"%s\";\n", configp->logfile);
5173    return 0;
5174 
5175 }
5176 
webhook_helpinfo(int argc,char * argv[])5177 int webhook_helpinfo ( int argc, char *argv[] )
5178 {
5179    char *label;
5180    struct webhook_st hooks;
5181    int j, k, ntopic;
5182    char outformat[80];
5183    char *topic[32];
5184 
5185    init_hooks(&hooks);
5186 
5187    if ( parse_switches(argc, argv, &hooks) != 0 )
5188       return 1;
5189 
5190    label = (hooks.flags & WF_UPPER) ? "HELP" : "help" ;
5191 
5192    /* Get list of available help topics */
5193    get_help_topics(topic, &ntopic);
5194 
5195    if ( hooks.flags & WF_MULT ) {
5196       sprintf(outformat, "%s%s%s\n", hooks.label_format, hooks.mult_format, hooks.body_format);
5197       j = hooks.index_start;
5198       for ( k = 0; k < ntopic; k++ )
5199          printf(outformat, label, j++, topic[k]);
5200    }
5201    else {
5202       sprintf(outformat, "%s%s\n", hooks.label_format, hooks.body_format);
5203       for ( k = 0; k < ntopic; k++ )
5204          printf(outformat, label, topic[k]);
5205    }
5206 
5207    return 0;
5208 }
5209 
5210 
webhook_flaginfo(int argc,char * argv[])5211 int webhook_flaginfo ( int argc, char *argv[] )
5212 {
5213    char *flaglabel[] = {"flags", "FLAGS"};
5214    char *czflaglabel[] = {"czflags", "CZFLAGS"};
5215    char *tzflaglabel[] = {"tzflags", "TZFLAGS"};
5216 
5217    char category[NAME_LEN + 1];
5218    char *label;
5219    struct webhook_st hooks;
5220    int  read_x10state_file ( void );
5221 
5222    char asciimap[1030];
5223 
5224    get_configuration(CONFIG_INIT);
5225 
5226    init_hooks(&hooks);
5227 
5228    if ( argc > 3 && *argv[argc - 1] != '-' ) {
5229       strncpy2(category, argv[argc - 1], NAME_LEN);
5230       strupper(category);
5231 
5232       if ( strcmp(category, "FLAGS") == 0 )
5233          hooks.flags |= WF_FLAGS;
5234       else if ( strcmp(category, "CZFLAGS") == 0 )
5235          hooks.flags |= WF_CZFLAGS;
5236       else if ( strcmp(category, "TZFLAGS") == 0 )
5237          hooks.flags |= WF_TZFLAGS;
5238       else {
5239          fprintf(stderr, "Unsupported flaginfo category '%s'\n", argv[argc - 1]);
5240          return 1;
5241       }
5242    }
5243    else {
5244       fprintf(stderr, "Missing category.  It must be FLAGS, CZFLAGS, or TZFLAGS\n");
5245       return 1;
5246    }
5247 
5248    if ( check_for_engine() != 0 ) {
5249       fprintf(stderr, "State engine is not running.\n");
5250       return 1;
5251    }
5252 
5253    if ( fetch_x10state() != 0 ) {
5254       fprintf(stderr, "Webhook unable to read state file.\n");
5255       return 1;
5256    }
5257 
5258    if ( parse_switches(argc, argv, &hooks) != 0 )
5259       return 1;
5260 
5261    if ( hooks.flags & WF_FLAGS ) {
5262       allflags2asciimap(asciimap, x10global.flags, NUM_FLAG_BANKS);
5263       label = (hooks.flags & WF_UPPER) ? flaglabel[1] : flaglabel[0];
5264    }
5265    else if ( hooks.flags & WF_CZFLAGS ) {
5266       allflags2asciimap(asciimap, x10global.czflags, NUM_COUNTER_BANKS);
5267       label = (hooks.flags & WF_UPPER) ? czflaglabel[1] : czflaglabel[0];
5268    }
5269    else if ( hooks.flags & WF_TZFLAGS ) {
5270       allflags2asciimap(asciimap, x10global.tzflags, NUM_TIMER_BANKS);
5271       label = (hooks.flags & WF_UPPER) ? tzflaglabel[1] : tzflaglabel[0];
5272    }
5273    else {
5274       return 1;
5275    }
5276 
5277    printf(hooks.label_format, label);
5278    printf(hooks.body_format, asciimap);
5279    printf("\n");
5280 
5281    return 0;
5282 }
5283 
webhook_flagbankinfo(int argc,char * argv[])5284 int webhook_flagbankinfo ( int argc, char *argv[] )
5285 {
5286 
5287    char *flaglabel[] = {"flagbank", "FLAGBANK"};
5288    char *czflaglabel[] = {"czflagbank", "CZFLAGBANK"};
5289    char *tzflaglabel[] = {"tzflagbank", "TZFLAGBANK"};
5290 
5291    char category[NAME_LEN + 1];
5292    char *label;
5293    struct webhook_st hooks;
5294    int  read_x10state_file ( void );
5295 
5296    char asciimap[34];
5297    int  j;
5298 
5299    get_configuration(CONFIG_INIT);
5300 
5301    init_hooks(&hooks);
5302 
5303    if ( argc > 3 && *argv[argc - 1] != '-' ) {
5304       strncpy2(category, argv[argc - 1], NAME_LEN);
5305       strupper(category);
5306 
5307       if ( strcmp(category, "FLAGS") == 0 )
5308          hooks.flags |= WF_FLAGS;
5309       else if ( strcmp(category, "CZFLAGS") == 0 )
5310          hooks.flags |= WF_CZFLAGS;
5311       else if ( strcmp(category, "TZFLAGS") == 0 )
5312          hooks.flags |= WF_TZFLAGS;
5313       else {
5314          fprintf(stderr, "Unsupported flagbankinfo category '%s'\n", argv[argc - 1]);
5315          return 1;
5316       }
5317    }
5318    else {
5319       fprintf(stderr, "Missing category.  It must be FLAGS, CZFLAGS, or TZFLAGS\n");
5320       return 1;
5321    }
5322 
5323    if ( check_for_engine() != 0 ) {
5324       fprintf(stderr, "State engine is not running.\n");
5325       return 1;
5326    }
5327 
5328    if ( fetch_x10state() != 0 ) {
5329       fprintf(stderr, "Webhook unable to read x10state file.\n");
5330       return 1;
5331    }
5332 
5333    if ( parse_switches(argc, argv, &hooks) != 0 )
5334       return 1;
5335 
5336    if ( hooks.flags & WF_FLAGS ) {
5337       label = (hooks.flags & WF_UPPER) ? flaglabel[1] : flaglabel[0];
5338       for ( j = 0; j < NUM_FLAG_BANKS; j++ ) {
5339          flags2asciimap(asciimap, x10global.flags[j]);
5340          printf(hooks.label_format, label);
5341          printf(hooks.mult_format, hooks.index_start + j);
5342          printf(hooks.body_format, asciimap);
5343          printf("\n");
5344       }
5345    }
5346    else if ( hooks.flags & WF_CZFLAGS ) {
5347       label = (hooks.flags & WF_UPPER) ? czflaglabel[1] : czflaglabel[0];
5348       for ( j = 0; j < NUM_COUNTER_BANKS; j++ ) {
5349          flags2asciimap(asciimap, x10global.czflags[j]);
5350          printf(hooks.label_format, label);
5351          printf(hooks.mult_format, hooks.index_start + j);
5352          printf(hooks.body_format, asciimap);
5353          printf("\n");
5354       }
5355    }
5356    else if ( hooks.flags & WF_TZFLAGS ) {
5357       label = (hooks.flags & WF_UPPER) ? tzflaglabel[1] : tzflaglabel[0];
5358       for ( j = 0; j < NUM_TIMER_BANKS; j++ ) {
5359          flags2asciimap(asciimap, x10global.tzflags[j]);
5360          printf(hooks.label_format, label);
5361          printf(hooks.mult_format, hooks.index_start + j);
5362          printf(hooks.body_format, asciimap);
5363          printf("\n");
5364       }
5365    }
5366    else {
5367       return 1;
5368    }
5369 
5370    return 0;
5371 }
5372 
webhook_maskinfo(int argc,char * argv[])5373 int webhook_maskinfo ( int argc, char *argv[] )
5374 {
5375    char *label;
5376    int j;
5377    char minibuf[32];
5378    unsigned int mask;
5379    struct webhook_st hooks;
5380    extern int get_env_funcmask ( int, char **, unsigned int * );
5381    extern int get_env_flagmask ( int, char **, unsigned int * );
5382 
5383    init_hooks(&hooks);
5384 
5385    hooks.flags |= WF_MASKS;
5386 
5387    if ( parse_switches(argc, argv, &hooks) != 0 )
5388       return 1;
5389 
5390    j = 0;
5391    while ( get_env_funcmask(j, &label, &mask) == 0 ) {
5392       printf(hooks.label_format, label);
5393       sprintf(minibuf, "%d", mask);
5394       printf(hooks.body_format, minibuf);
5395       printf("\n");
5396       j++;
5397    }
5398 
5399    return 0;
5400 }
5401 
webhook_flagmaskinfo(int argc,char * argv[])5402 int webhook_flagmaskinfo ( int argc, char *argv[] )
5403 {
5404    char *label;
5405    int j;
5406    char minibuf[32];
5407    unsigned int mask;
5408    struct webhook_st hooks;
5409    extern int get_env_funcmask ( int, char **, unsigned int * );
5410    extern int get_env_flagmask ( int, char **, unsigned int * );
5411 
5412    init_hooks(&hooks);
5413 
5414    hooks.flags |= WF_MASKS;
5415 
5416    if ( parse_switches(argc, argv, &hooks) != 0 )
5417       return 1;
5418 
5419    j = 0;
5420    while ( get_env_flagmask(j, &label, &mask) == 0 ) {
5421       printf(hooks.label_format, label);
5422       sprintf(minibuf, "%d", mask);
5423       printf(hooks.body_format, minibuf);
5424       printf("\n");
5425       j++;
5426    }
5427 
5428    return 0;
5429 }
5430 
5431 
c_webhook(int argc,char * argv[])5432 int c_webhook ( int argc, char *argv[] )
5433 {
5434    if ( argc < 3 ) {
5435       display_webhook_usage();
5436       return 0;
5437    }
5438 
5439    if ( strcmp(argv[2], "config_dump") == 0 )
5440       return webhook_config_dump(argc, argv);
5441    else if ( strcmp(argv[2], "pathinfo") == 0 )
5442       return webhook_pathinfo(argc, argv);
5443    else if ( strcmp(argv[2], "helpinfo") == 0 )
5444       return webhook_helpinfo(argc, argv);
5445    else if ( strcmp(argv[2], "flaginfo") == 0 )
5446       return webhook_flaginfo(argc, argv);
5447    else if ( strcmp(argv[2], "flagbankinfo") == 0 )
5448       return webhook_flagbankinfo(argc, argv);
5449    else if ( strcmp(argv[2], "maskinfo") == 0 )
5450       return webhook_maskinfo(argc, argv);
5451    else if ( strcmp(argv[2], "flagmaskinfo") == 0 )
5452       return webhook_flagmaskinfo(argc, argv);
5453    else if ( strcmp(argv[2], "fileinfo") == 0 )
5454       return webhook_fileinfo(argc, argv);      /* deprecated */
5455    else if ( strcmp(argv[2], "menuinfo") == 0 )
5456       return webhook_menuinfo(argc, argv);      /* deprecated */
5457    else {
5458       fprintf(stderr, "Invalid webhook command '%s'\n", argv[2]);
5459       return 1;
5460    }
5461    return 0;
5462 }
5463 
5464 /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++
5465  |    END WEBHOOK SECTION                               |
5466  +++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
5467 
5468 
5469 
cmp_directives(const void * m1,const void * m2)5470 int cmp_directives ( const void *m1, const void *m2 )
5471 {
5472    struct conf *one, *two;
5473 
5474    one = (struct conf *)m1; two = (struct conf *)m2;
5475 
5476    return strcmp(one->name, two->name);
5477 }
5478 
c_conflist(int argc,char * argv[])5479 int c_conflist ( int argc, char *argv[] )
5480 {
5481    int j;
5482    qsort(command + 1, ncommands - 1, sizeof(struct conf), cmp_directives);
5483 
5484    for ( j = 1; j < ncommands - 1; j++ )
5485       if ( !(command[j].flags & (CIGN | CHID)) )
5486          printf("%s\n", command[j].name);
5487    printf("\n");
5488 
5489    return 0;
5490 }
5491 
5492 /*---------------------------------------------------------------------+
5493  | Compare arrays of ALIAS structures to see if any have been changed. |
5494  | Return 1 if changed or 0 if unchanged.                              |
5495  +---------------------------------------------------------------------*/
is_alias_changed(ALIAS * aliasp1,int size1,ALIAS * aliasp2,int size2)5496 int is_alias_changed ( ALIAS *aliasp1, int size1, ALIAS *aliasp2, int size2 )
5497 {
5498    ALIAS *aliaspp1, *aliaspp2;
5499    int j, k, dif;
5500 
5501    if ( size1 != size2 || size1 == 0 )
5502       return 1;
5503 
5504    for ( j = 0; j < size1; j++ ) {
5505       aliaspp1 = &aliasp1[j]; aliaspp2 = &aliasp2[j];
5506       dif = (strcmp(aliaspp1->label, aliaspp2->label))   ? 1 :
5507             (aliaspp1->housecode != aliaspp2->housecode) ? 1 :
5508             (aliaspp1->hcode     != aliaspp2->hcode)     ? 1 :
5509             (aliaspp1->unitbmap  != aliaspp2->unitbmap)  ? 1 :
5510             (aliaspp1->ext0links != aliaspp2->ext0links) ? 1 :
5511             (aliaspp1->modtype   != aliaspp2->modtype)   ? 1 :
5512             (aliaspp1->vflags    != aliaspp2->vflags)    ? 1 :
5513             (aliaspp1->flags     != aliaspp2->flags)     ? 1 :
5514             (aliaspp1->xflags    != aliaspp2->xflags)    ? 1 :
5515             (aliaspp1->optflags  != aliaspp2->optflags)  ? 1 :
5516             (aliaspp1->optflags2 != aliaspp2->optflags2) ? 1 :
5517             (aliaspp1->tmin      != aliaspp2->tmin)      ? 1 :
5518             (aliaspp1->tmax      != aliaspp2->tmax)      ? 1 :
5519             (aliaspp1->rhmin     != aliaspp2->rhmin)     ? 1 :
5520             (aliaspp1->rhmax     != aliaspp2->rhmax)     ? 1 :
5521             (aliaspp1->onlevel   != aliaspp2->onlevel)   ? 1 :
5522             (aliaspp1->maxlevel  != aliaspp2->maxlevel)  ? 1 :
5523             (aliaspp1->vtype     != aliaspp2->vtype)     ? 1 :
5524             (aliaspp1->subtype   != aliaspp2->subtype)   ? 1 :
5525             (aliaspp1->nident    != aliaspp2->nident)    ? 1 :
5526 //            (aliaspp1->timeout   != aliaspp2->timeout)   ? 1 :
5527             (aliaspp1->hb_timeout != aliaspp2->hb_timeout)   ? 1 :
5528             (aliaspp1->storage_index != aliaspp2->storage_index) ? 1 :
5529             (aliaspp1->storage_units != aliaspp2->storage_units) ? 1 : 0;
5530 
5531       if ( dif )
5532          return 1;
5533 
5534       for ( k = 0; k < aliaspp1->nident; k++ ) {
5535          if ( aliaspp1->ident[k] != aliaspp2->ident[k] )
5536             return 1;
5537       }
5538       for ( k = 0; k < NFUNCLIST; k++ ) {
5539          if ( aliaspp1->funclist[k] != aliaspp2->funclist[k] )
5540             return 1;
5541       }
5542       for ( k = 0; k < NOFFSET; k++ ) {
5543          if ( aliaspp1->offset[k] != aliaspp2->offset[k] )
5544             return 1;
5545       }
5546    }
5547 
5548   return 0;
5549 }
5550 
5551 
5552 
5553