1 
2 /*----------------------------------------------------------------------------+
3  |                                                                            |
4  |                    Enhanced HEYU Functionality                             |
5  |             Copyright 2002-2008 Charles W. Sullivan                        |
6  |                                                                            |
7  |                                                                            |
8  | As used herein, HEYU is a trademark of Daniel B. Suthers.                  |
9  | X10, CM11A, and ActiveHome are trademarks of X-10 (USA) Inc.               |
10  | The author is not affiliated with either entity.                           |
11  |                                                                            |
12  | Charles W. Sullivan                                                        |
13  | Co-author and Maintainer                                                   |
14  | Greensboro, North Carolina                                                 |
15  | Email ID: cwsulliv01                                                       |
16  | Email domain: -at- heyu -dot- org                                          |
17  |                                                                            |
18  +----------------------------------------------------------------------------*/
19 
20 /*
21  *   This program is free software: you can redistribute it and/or modify
22  *   it under the terms of the GNU General Public License as published by
23  *   the Free Software Foundation, either version 3 of the License, or
24  *   (at your option) any later version.
25  *
26  *   This program is distributed in the hope that it will be useful,
27  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
28  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  *   GNU General Public License for more details.
30  *
31  *   You should have received a copy of the GNU General Public License
32  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
33  *
34  */
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <sys/types.h>
40 #include <sys/stat.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 #if (defined(NSLEEP) || defined(ATTSVR4))
49 #include <sys/time.h>
50 #endif /* NSLEEP | ATTSVR4 */
51 
52 #include <syslog.h>
53 #include <time.h>
54 #include <sys/time.h>
55 #include <limits.h>
56 #include "x10.h"
57 #include "process.h"
58 #include "x10state.h"
59 
60 #if defined(LINUX)
61 #define RandMax  RAND_MAX
62 #elif defined(NETBSD)
63 #define RandMax RANDOM_MAX
64 #elif defined DARWIN
65 #define RandMax LONG_MAX
66 #else
67 #define RandMax LONG_MAX
68 #endif
69 
70 extern int tty, tty_aux;
71 extern int line_no;
72 extern int sptty, i_am_relay, i_am_aux, i_am_state;
73 extern int heyu_parent;
74 extern int verbose;
75 extern int xwrite(), xread(), exread(), sxread(), check4poll();
76 extern int is_modem_support(void);
77 extern CONFIG  config;
78 extern CONFIG  *configp;
79 
80 extern struct x10global_st x10global;
81 extern x10hcode_t *x10state;
82 
83 
84 unsigned char alert_cmd[6];
85 unsigned char alert_ack[3];
86 
87 int special_func = 0;
88 
89 char *typename[] = {"???", "Scene", "Usersyn"};
90 
91 #if 0  /* Reference - Flags for parse_addr() function */
92 #define A_VALID   1
93 #define A_HCODE   2
94 #define A_BMAP    4
95 #define A_PLUS    8
96 #define A_MINUS  16
97 #define A_ALIAS  32
98 #define A_DUMMY  64
99 #define A_MULT  128
100 #define A_STAR  256
101 #endif /* Reference */
102 
103 /* Arguments for the various commands */
104 static char *helparg[] = {
105    "[H]",                /* 0 HC optional */
106    "H",                  /* 1 HC only */
107    "Hu",                 /* 2 HC | single unit */
108    "HU",                 /* 3 HC | unit string */
109    "HU <level>",         /* 4 Dim/Bright level*/
110    "HU <level>",         /* 5 Preset dim level*/
111    "<level>",            /* 6 Preset dim level only */
112    "HU <level>",         /* 7 Extended Preset level*/
113    "<T/F> HU <Data>",    /* 8 Arbitrary extended code command (hex)*/
114    "<command ...>",      /* 9 Send HC|Funct only, no address */
115    "xx xx xx ...",       /* 10 Arbitrary hex bytes */
116    "H <\"text\">",       /* 11 Arbitrary quoted text message */
117    "",                   /* 12 No arguments */
118    "[<command>]",        /* 13 For help command */
119    "N.NNN",              /* 14 Pause delay */
120    "HU [HU [...]]",      /* 15 Multiple addresses */
121    "HU <command>",       /* 16 For Turn command */
122    "<query_command>",    /* 17 Special functions */
123    "HU <linked>",        /* 18 Preset for macros */
124    "H <mode 0-3>",       /* 19 Ext status_ack mode */
125    "<\"text\">",         /* 20 Message to log file */
126    "NNN",                /* 21 Delay in minutes */
127    "H[U] <count>",       /* 22 RF Dim/Bright */
128    "xx xx <count>",      /* 23 Two hex bytes */
129    "HU <count>",         /* 24 RF Dimbo */
130    "H[u]",               /* 25 State commands */
131    "n[,n...]",           /* 26 Flag set/clear commands */
132    "H[U]",               /* 27 Status flag clear command */
133    "HU <byte>",          /* 28 Virtual data */
134    "N <hh:mm:ss>",       /* 29 Countdown timer */
135    "xxxx xxxx ...",      /* 30 Multiple 16-bit hex words */
136    "<parameters>",       /* 31 Special for UX17/23 command */
137    "HU g <level>",       /* 32 Include in group at level*/
138    "HU g[,g,...]",       /* 33 Delete from group(s) */
139    "H G",                /* 34 Execute group[.subgroup] functions */
140    "HU G",               /* 35 Include in group[.subgroup] */
141    "H g[,g,...]",        /* 36 Delete from group(s) */
142    "HU <level> <ramp>",  /* 37 Extended Preset level with ramp */
143    "N <count>",          /* 38 Set counter */
144    "N",                  /* 39 Increment or Decrement counter */
145    "[parameters]",       /* 40 Arm system */
146    "N [MIN] MAX",        /* 41 Random countdown timer */
147    "[MIN] MAX",          /* 42 Random delay */
148 };
149 
150 static char *options[] = {
151     "Usage: heyu [options] <command>   (Run 'heyu help' for commands)\n",
152     " [Options]",
153     "    -v             Enable verbose mode",
154     "    -c <pathname>  Specify full configuration file pathname",
155     "    -s <pathname>  Specify full schedule file pathname",
156     "    -0 ... -9      Config file is in subdirectory /0 ... /9",
157     "                   of standard location, e.g., $HOME/.heyu/3/x10config",
158 };
159 
160 /* Descriptions of "administrative" commands */
161 static char *helpadmin[][3] = {
162     {"info","","Display CM11A registers, clock, and upload status"},
163     {"help","[category|command]","This screen [category or command]"},
164     {"help","help","Explain help usage"},
165     {"start","","Start Heyu background processes (daemons)"},
166     {"stop","","Stop Heyu background processes"},
167     {"restart","","Reconfigure running Heyu background processes"},
168     {"engine","","Start the (optional) Heyu state engine daemon"},
169     {"aux","","Start the (optional) Heyu auxiliary RF input daemon"},
170     {"monitor","","Monitor X10 activity (end with <BREAK>)"},
171     {"date","","Return date in date(1) input format"},
172     {"erase","","Zero CM11A EEPROM, erasing all events and macros"},
173     {"syn","","Display built-in synonyms for direct commands"},
174     {"<label>","","Execute a scene or usersyn defined in the config file"},
175     {"show","<option>","(Enter 'heyu show' to see options)"},
176     {"script_ctrl","<option>","Launch scripts disable|enable"},
177     {"initstate","[H[U]]","Zero entire state table or just for address H[U]"},
178     {"initothers","","Zero cumulative address table"},
179     {"reset","[H]","Reset interface to housecode H or default"},
180     {"setclock","","Set CM11A clock to system clock (per schedule)"},
181     {"readclock","","Display CM11A and system clocks (per schedule)"},
182     {"newbattery","","Reset CM11A battery timer to zero"},
183     {"purge","","Cancel pending CM11A delayed macros"},
184     {"clear","","Clear CM11A registers"},
185     {"list","","Display Lock, Spool, and System base directory names"},
186     {"upload","[check|croncheck]","Upload schedule to CM11A or check schedule file"},
187     {"upload","status|cronstatus","Display status of uploaded schedule"},
188     {"catchup","","Emulate uploaded Timers from 0:00 to current time today"},
189     {"trigger","Hu on|off","Emulate Trigger in uploaded schedule"},
190     {"macro","<label>","Emulate Macro in uploaded schedule"},
191     {"utility","<option>","(Enter 'heyu utility' to see options)"},
192     {"logmsg","<\"text\">","Display text message in log file and/or monitor"},
193     {"wait","[timeout]","Wait until macro execution is completed"},
194     {"cm10a_init","","Initialize CM10A interface. (For CM10A only!)"},
195     {"restore_groups","","Restore extended group and xconfig settings"},
196     {"logtail","[N]","Display tail [N lines] of log file"},
197     {"sec_emu","Hu <func> <flags>","Emulate an X10 Security signal"},
198     {"launch","<parameters>","Launch script. Enter 'heyu launch' for usage"},
199     {"conflist","","Display list of all configuration directives"},
200     {"modlist","","Display list of all module types"},
201     {"stateflaglist","","Display list of all state flags"},
202     {"masklist","","Display lists of all environment variable masks"},
203     {"webhook","<parameters>","Enter 'heyu webhook' for usage"},
204     {"version","","Display the Heyu version and exit"},
205     {NULL, NULL, NULL}
206 };
207 
208 static char *helpstate[][3] = {
209     {"enginestate","","Display 1 if state engine is running, else 0"},
210     {"armedstate","","Bitmap: 1 = armed, 2 = home, 4 = armpending, 8 = tamper"},
211     {"sensorfault","","Bitmap: 1 = low battery, 2 = inactive, 8 = tamper"},
212     {"flagstate","n","Boolean state of flag n"},
213     {"nightstate","","Boolean state of night flag"},
214     {"darkstate","","Boolean state of dark flag"},
215     {"sunstate","","Bitmap: 1 = night, 2 = dark"},
216     {"spendstate","H[u]","Status-pending bitmap of H or Boolean Hu"},
217     {"onstate","H[u]","On-state bitmap of H or Boolean Hu"},
218     {"offstate","H[u]","On-state complement bitmap of H or Boolean Hu"},
219     {"dimstate","H[u]","Dim-state bitmap of H or Boolean Hu"},
220     {"fullonstate","H[u]","FullOn-state bitmap of H or Boolean Hu"},
221     {"chgstate","H[u]","Changed-state bitmap of H or Boolean Hu"},
222     {"alertstate","H[u]","Alert-state bitmap of H or Boolean Hu"},
223     {"clearstate","H[u]","Clear-state bitmap of H or Boolean Hu"},
224     {"auxalertstate","H[u]","AuxAlert-state bitmap of H or Boolean Hu"},
225     {"auxclearstate","H[u]","AuxClear-state bitmap of H or Boolean Hu"},
226     {"addrstate","H[u]","Addressed-state bitmap of H or Boolean Hu"},
227     {"activestate","H[u]","Active-state bitmap of H or Boolean Hu"},
228     {"inactivestate","H[u]","Inactive-state bitmap of H or Boolean Hu"},
229     {"lobatstate","H[u]","Low_Battery-state bitmap of H or Boolean Hu"},
230     {"validstate","H[u]","Valid function state bitmap of H or Boolean Hu"},
231     {"statestr","H","State mini-bitmaps of all units as ASCII string"},
232     {"dimlevel","Hu","Brighness level of module Hu as 0-100%"},
233     {"rawlevel","Hu","Native level (0-210, 1-32, or 0-63) of module Hu"},
234     {"memlevel","Hu","Stored level 0-100% for module Hu with memory"},
235     {"rawmemlevel","Hu","Stored native level for module Hu with memory"},
236     {"heyu_state","Hu","Heyu script environment state bitmap (as integer)"},
237     {"heyu_rawstate","Hu","Heyu script raw environment state bitmap (as integer)"},
238     {"heyu_vflagstate","Hu","Heyu script vFlag environment state bitmap (as integer)"},
239 #if 0
240 #if defined(HASRFXS) || defined(HASRFXM)
241     {"rfxflag_state","Hu","RFXsensor/RFXmeter flag state bitmap (as integer)"},
242 #endif
243 #ifdef HASORE
244     {"oreflag_state","Hu","Oregon (Electrisave, Owl) flag state bitmap (as integer)"},
245 #endif
246 #ifdef HASDMX
247     {"dmxflag_state","Hu","Digimax flag state bitmap (as integer)"},
248 #endif
249 #endif
250     {"xtend_state","Hu","Xtend script environment state bitmap (as integer)"},
251     {"rcstemp","H","RCS compatible temperature (stored value)"},
252     {"fetchstate","","See man page heyu(1)"},
253     {NULL, NULL, NULL}
254 };
255 
256 #ifdef HASRFXS
257 static char *helprfxsensor[][3] = {
258     {"rfxtemp","Hu","Temperature"},
259     {"rfxrh","Hu","Relative Humidity"},
260     {"rfxbp","Hu","Barometric Pressure"},
261     {"rfxpot","Hu","Potentiometer setting"},
262     {"rfxvs","Hu","Supply Voltage"},
263     {"rfxvad","Hu","A/D Voltage"},
264     {"rfxvadi","Hu","Internal A/D Voltage"},
265     {"rfxlobat","Hu","Low Battery state (Boolean)"},
266     {"rfxtemp2","Hu","Second Temperature"},
267     {NULL, NULL, NULL}
268 };
269 #endif /* HASRFXS */
270 
271 #ifdef HASRFXM
272 static char *helprfxmeter[][3] = {
273     {"rfxpower","Hu","Watt-Hour meter reading"},
274     {"rfxpanel","[n]","Total Watt-Hours for Power Panel [n]"},
275     {"rfxwater","Hu","Water meter reading"},
276     {"rfxgas","Hu","Gas meter reading"},
277     {"rfxpulse","Hu","Pulse meter reading"},
278     {"rfxcount","Hu","Raw counter of any meter"},
279     {NULL, NULL, NULL}
280 };
281 #endif /* HASRFXM */
282 
283 #ifdef HASDMX
284 static char *helpdigimax[][3] = {
285     {"dmxtemp","Hu","DigiMax current temperature (C)"},
286     {"dmxsetpoint","Hu","DigiMax setpoint temperature (C)"},
287     {"dmxstatus","Hu","DigiMax On/Off status (1 = On)"},
288     {"dmxmode","Hu","DigiMax Heat/Cool mode (1 = Heat)"},
289     {NULL, NULL, NULL}
290 };
291 #endif /* HASDMX */
292 
293 #ifdef HASORE
294 static char *helporegon[][3] = {
295     {"oretemp","Hu","Oregon sensor Temperature"},
296     {"orerh","Hu","Oregon sensor Relative Humidity"},
297     {"orebp","Hu","Oregon sensor Barometric Pressure"},
298     {"orewindavsp","Hu","Oregon sensor Wind Average Speed"},
299     {"orewindsp","Hu","Oregon sensor Wind Instantaneous Speed"},
300     {"orewinddir","Hu","Oregon sensor Wind Direction"},
301     {"orerainrate","Hu","Oregon sensor Rainfall Rate"},
302     {"oreraintot","Hu","Oregon sensor Total Rainfall"},
303     {"ore_emu","Hu <func> <data>","Enter data in Oregon emulation module"},
304     {"elscurr","Hu","Electrisave CM113 Current"},
305     {"owlpower","Hu","Owl CM119 Power"},
306     {"owlenergy","Hu","Owl CM119 Energy"},
307     {NULL, NULL, NULL}
308 };
309 #endif /* HASORE */
310 
311 /* Descriptions of direct and uploaded macro commands */
312 static char *helpdirect[] = {
313    /*  0 */ "Turn units ON",
314    /*  1 */ "Turn units OFF",
315    /*  2 */ "Turn All Lights ON",
316    /*  3 */ "Turn All Lights OFF (**)",
317    /*  4 */ "Turn All Units OFF",
318    /*  5 */ "Dim units by <level> (1-22)",
319    /*  6 */ "Dim units by <level> (1-22) after full bright",
320    /*  7 */ "Brighten units by <level> (1-22)",
321    /*  8 */ "Brighten units by <level> (1-22) after full bright",
322    /*  9 */ "Request ON/OFF status (two-way modules)",
323    /* 10 */ "Status Acknowledge ON",
324    /* 11 */ "Status Acknowledge OFF",
325    /* 12 */ "Preset units to <level> (1-32)",
326    /* 13 */ "Preset to <level> (1-32) (function only)",
327    /* 14 */ "Extended Preset <level> (0-63) (LM14A)",
328    /* 15 */ "Extended All Units ON (LM14A)",
329    /* 16 */ "Extended All Units OFF (LM14A)",
330    /* 17 */ "Extended Status Request (LM14A)",
331    /* 18 */ "Extended Status Acknowledge",
332    /* 19 */ "Extended command - general",
333    /* 20 */ "Extended command - general, await ack",
334    /* 21 */ "Send arbitrary hex bytes as addresses",
335    /* 22 */ "Send Housecode|Units addresses only",
336    /* 23 */ "Send command function only",
337    /* 24 */ "Hail other devices",
338    /* 25 */ "Hail Acknowledge",
339    /* 26 */ "Send quoted text (special protocol)",
340    /* 27 */ "Data Transfer (function code 0xC)",
341    /* 28 */ "Send All_Units_Off to All Housecodes",
342    /* 29 */ "Extended Turn Units Full ON (LM14A)",
343    /* 30 */ "Extended Turn Units Full OFF (LM14A)",
344    /* 31 */ "This screen, or specific command help",
345    /* 32 */ "Display synonyms for direct commands",
346    /* 33 */ "Hail other devices, await ack",
347    /* 34 */ "Legacy status command as above",
348    /* 35 */ "Legacy preset command as above",
349    /* 36 */ "Pause for N.NNN seconds",
350    /* 37 */ "Turn Units 1-16 ON",
351    /* 38 */ "Change state on|off|up|down [vv]",
352    /* 39 */ "Legacy turn command as above",
353    /* 40 */ "Request temperature (RCS compatible)",
354    /* 41 */ "Send bytes directly to interface",
355    /* 42 */ "Limited Preset for macros - see manpage",
356    /* 43 */ "Extended config auto status mode (LM14A)",
357    /* 44 */ "Request RCS compatible status",
358    /* 45 */ "Delay for NNN minutes, NNN = 0-240",
359    /* 46 */ "Pause until next tick of system clock",
360    /* 47 */ "Transmit RF On",
361    /* 48 */ "Transmit RF Off",
362    /* 49 */ "Transmit RF Dims [after On]",
363    /* 50 */ "Transmit RF Brights [after On]",
364    /* 51 */ "Transmit RF All Lights On",
365    /* 51 */ "Transmit RF All Lights Off (**)",
366    /* 53 */ "Transmit RF All Units Off",
367    /* 54 */ "Transmit RF Arbitrary 2-byte hex code",
368    /* 55 */ "Reset CM17A device",
369    /* 56 */ "Transmit RF Dims after Off",
370    /* 57 */ "Transmit RF Dims [after On] (#)",
371    /* 58 */ "Transmit RF Brights [after On] (#)",
372    /* 59 */ "Transmit RF Dims after Off (#)",
373    /* 60 */ "Transmit RF Arbitrary 2-byte hex code (#)",
374    /* 61 */ "Open shutter to level (0-25), enforce limit",
375    /* 62 */ "Set limit (0-25) and open shutter to limit",
376    /* 63 */ "Open shutter to level (0-25) and cancel limit",
377    /* 64 */ "Open all shutters fully and cancel limit",
378    /* 65 */ "Close all shutters fully",
379    /* 66 */ "Extended module power-up signal (LM14A)",
380    /* 67 */ "Set one or more flags (@)",
381    /* 68 */ "Clear one or more flags (@)",
382    /* 69 */ "Clear status-pending flags (@)",
383    /* 70 */ "Write data (0-255) to virtual module (@)",
384    /* 71 */ "Set countdown timer N to hh:mm:ss (@)",
385    /* 72 */ "Reset all countdown timers to zero (@)",
386    /* 73 */ "Transmit RF multiple 16-bit hex words",
387    /* 74 */ "Clear security tamper flags (@)",
388    /* 75 */ "Transmit RF multiple 16-bit hex words (#)",
389    /* 76 */ "Special for UX23A/UX17A - see manpage",
390    /* 77 */ "Special for UX23A/UX17A - see manpage (#)",
391    /* 78 */ "Sleep for N.NNN seconds",
392    /* 79 */ "Include in Group (G = 0-3) at current level",
393    /* 80 */ "Remove HU from one or more Groups (g = 0-3)",
394    /* 81 */ "Execute extended Group G function (LM14A)",
395    /* 82 */ "Extended Group G Status Request (LM14A)",
396    /* 83 */ "Remove H from one or more Groups (g = 0-3)",
397    /* 84 */ "Include in Group (g = 0-3) at <level>",
398    /* 85 */ "Extended Group G Off (**)",
399    /* 86 */ "Extended Group G Dim one level (**)",
400    /* 87 */ "Extended Group G Bright one level (**)",
401    /* 88 */ "Extended Preset <level> (0-63) <ramp> (0-3)",
402    /* 89 */ "Set counter N to <count> (0-64K) (@)",
403    /* 90 */ "Increment counter N by 1 (@)",
404    /* 91 */ "Decrement counter N by 1 (@)",
405    /* 92 */ "Decrement counter N and skip if Zero (@)",
406    /* 93 */ "Decrement counter N and skip if Greater than Zero (@)",
407    /* 94 */ "Arm system [home|away] [min|max] (@)",
408    /* 95 */ "Disarm system (@)",
409    /* 96 */ "Delay random time [MIN-]MAX minutes (0-240)",
410    /* 97 */ "Set timer N to random [MIN-]MAX hh:mm:ss (@)",
411    /* 98 */ "Write mem data (0-255) to virtual module (@)",
412    /* 99 */ "Null command - does nothing (@)",
413    /*100 */ "Dec counter N and skip if Non-Zero or Initial Zero (@)",
414    /*101 */ "Null command - does nothing",
415    /*102 */ "Emulate Status Acknowledge for 1-way modules",
416 };
417 
418 
display_helpnotes(void)419 void display_helpnotes ( void )
420 {
421    printf("\n  (*)  Not available for use in uploaded macros.\n");
422    printf("  (**) Many lamp modules do NOT support this command.\n");
423 #ifdef HASCM17A
424    printf("  (#)  Fast CM17A command - see man x10cm17a(5) for configuration.\n");
425 #endif
426    printf("  (@)  Ignored unless state engine is running.\n");
427 
428    return;
429 }
430 
display_manpage_list(void)431 void display_manpage_list ( void )
432 {
433    printf("\n Man pages:\n heyu(1), x10config(5), x10sched(5), x10scripts(5), x10aux(5)");
434 #ifdef HASCM17A
435    printf(", x10cm17a(5)");
436 #endif
437 #ifdef HASRFXS
438    printf(", x10rfxsensors(5)");
439 #endif
440 #ifdef HASRFXM
441    printf(", x10rfxmeters(5)");
442 #endif
443 #ifdef HASDMX
444    printf(", x10digimax(5)");
445 #endif
446 #ifdef HASORE
447    printf(", x10oregon(5)");
448 #endif
449 #ifdef HASKAKU
450    printf(", x10kaku(5)");
451 #endif
452    printf(".\n\n");
453    return;
454 }
455 
456 
457 /* Flags for x10command table below */
458 #define  F_ALL  0x00000001 /* "All" command; no unit code required on input */
459 #define  F_DED  0x00000002 /* Display extended code data byte in report */
460 #define  F_NOA  0x00000004 /* Suppress HC|Unit address byte transmission */
461 #define  F_BRI  0x00000008 /* Brighten before dimming */
462 #define  F_FAD  0x00000010 /* Force HC|Unit address byte(s) transmission */
463 #define  F_ARB  0x00000020 /* Use special format for arbitrary extended code */
464 #define  F_NMA  0x00000040 /* Unavailable as an uploadable macro */
465 #define  F_HID  0x00000080 /* Hidden command */
466 #define  F_STA  0x00000100 /* Status/xStatus Request (await status) */
467 #define  F_NUM  0x00000200 /* Arguments are all numbers */
468 #define  F_HLP  0x00000400 /* For help menu only - not a CM11A command */
469 #define  F_NMAI 0x00000800 /* Unavailable and ignored for uploaded macro */
470 #define  F_TRN  0x00001000 /* For Turn command */
471 #define  F_SPF  0x00002000 /* For special function processing */
472 #define  F_MAC  0x00004000 /* Special version for uploaded macros */
473 #define  F_FIR  0x00008000 /* CM17A (Firecracker) command */
474 #define  F_FFM  0x00010000 /* CM17A "fast" mode */
475 #define  F_SHU  0x00020000 /* Extended Type 0 (Marmitek SW10) shutter commands */
476 #define  F_EXT  0x00040000 /* Extended Type 3 (LM14A, AM14A) commands */
477 #define  F_FLSC 0x00080000 /* Flag set and clear commands */
478 #define  F_GRP  0x00100000 /* Extended code group commands */
479 #define  F_ONN  0x00200000 /* On required before brightening */
480 #define  F_INT  0x00400000 /* Internal - no PLC interface required */
481 #define  F_DUM  0x00800000 /* No PLC interface required */
482 
483 #define  F_RFS (F_FIR | F_NMA)
484 #define  F_RFF (F_FIR | F_NMA | F_FFM)
485 #define  F_FLG (F_FLSC | F_NMA | F_NUM)
486 #define  F_XGP (F_GRP | F_EXT)
487 
488 /*---------------------------------------------------------------------+
489  | Master table of commands and synonyms.  The first of any grouping   |
490  | having all fields identical except for the label is considered the  |
491  | command; subsequent entries in the grouping are treated as built-in |
492  | synonyms.                                                           |
493  +---------------------------------------------------------------------*/
494 /* Names for arbitrary extended code functions */
495 #define XARBNAME   "xfunc"
496 #define XARBNAMEW  "xfuncw"
497 
498 static struct cmd_list {
499    char          *label;
500    unsigned char code;
501    int           length;   /* Length as a macro */
502    int           minargs;
503    int           maxargs;
504    unsigned long flags;
505    unsigned char subcode;  /* Type|command for extended code commands */
506    unsigned char subsubcode;
507    unsigned char argtype;
508    unsigned char help;
509 } x10command[] = {
510    {"on",               2, 3, 1, 1, 0,                0, 0, 3, 0 },   /* Simple ON command */
511    {"turnon",           2, 3, 1, 1, 0,                0, 0, 3, 0 },   /* Simple ON command */
512    {"off",              3, 3, 1, 1, 0,                0, 0, 3, 1 },   /* Simple OFF command */
513    {"turnoff",          3, 3, 1, 1, 0,                0, 0, 3, 1 },   /* Simple OFF command */
514    {"bright",           5, 4, 2, 2, 0,                0, 0, 4, 7 },   /* Brighten command */
515    {"turnup",           5, 4, 2, 2, 0,                0, 0, 4, 7 },   /* Brighten command */
516    {"up",               5, 4, 2, 2, 0,                0, 0, 4, 7 },   /* Brighten command */
517    {"brighten",         5, 4, 2, 2, 0,                0, 0, 4, 7 },   /* Brighten command */
518    {"tbright",          5, 4, 2, 2, F_NMA | F_HID,    1, 0, 4, 7 },   /* Brighten command - triggered */
519    {"brightb",          5, 4, 2, 2, F_BRI,            0, 0, 4, 8 },   /* Brighten command with brighten first */
520    {"bbright",          5, 4, 2, 2, F_BRI,            0, 0, 4, 8 },   /* Brighten command with brighten first */
521    {"dim",              4, 4, 2, 2, 0,                0, 0, 4, 5 },   /* Dim command without brighten first */
522    {"turndown",         4, 4, 2, 2, 0,                0, 0, 4, 5 },   /* Dim command without brighten first */
523    {"down",             4, 4, 2, 2, 0,                0, 0, 4, 5 },   /* Dim command without brighten first */
524    {"dimb",             4, 4, 2, 2, F_BRI,            0, 0, 4, 6 },   /* Dim command with brighten first */
525    {"bdim",             4, 4, 2, 2, F_BRI,            0, 0, 4, 6 },   /* Dim command with brighten first */
526    {"obdim",            4, 4, 2, 2, F_BRI | F_ONN,    0, 0, 4, 6 },   /* Dim command with on, brighten first */
527    {"tdim",             4, 4, 2, 2, F_NMA | F_HID,    1, 0, 4, 5 },   /* Dim command - triggered */
528    {"lightson",         1, 3, 1, 1, F_ALL,            0, 0, 1, 2 },   /* All_Lights_On */
529    {"lights_on",        1, 3, 1, 1, F_ALL,            0, 0, 1, 2 },   /* All_Lights_On */
530    {"all_lights_on",    1, 3, 1, 1, F_ALL,            0, 0, 1, 2 },   /* All_Lights_On */
531    {"alllightson",      1, 3, 1, 1, F_ALL,            0, 0, 1, 2 },   /* All_Lights_On */
532    {"lightsoff",        6, 3, 1, 1, F_ALL,            0, 0, 1, 3 },   /* All_Lights_Off */
533    {"lights_off",       6, 3, 1, 1, F_ALL,            0, 0, 1, 3 },   /* All_Lights_Off */
534    {"all_lights_off",   6, 3, 1, 1, F_ALL,            0, 0, 1, 3 },   /* All_Lights_Off */
535    {"alllightsoff",     6, 3, 1, 1, F_ALL,            0, 0, 1, 3 },   /* All_Lights_Off */
536    {"allon",           26,-1, 1, 1, F_ALL,            0, 0, 1,37 },   /* Units 1-16 On  */
537    {"all_on",          26,-1, 1, 1, F_ALL,            0, 0, 1,37 },   /* Units 1-16 On  */
538    {"all_units_on",    26,-1, 1, 1, F_ALL,            0, 0, 1,37 },   /* Units 1-16 On  */
539    {"allunitson",      26,-1, 1, 1, F_ALL,            0, 0, 1,37 },   /* Units 1-16 On  */
540    {"units_on",        26,-1, 1, 1, F_ALL,            0, 0, 1,37 },   /* Units 1-16 On  */
541    {"unitson",         26,-1, 1, 1, F_ALL,            0, 0, 1,37 },   /* Units 1-16 On  */
542    {"alloff",           0, 3, 1, 1, F_ALL,            0, 0, 1, 4 },   /* All_Units_Off  */
543    {"all_off",          0, 3, 1, 1, F_ALL,            0, 0, 1, 4 },   /* All_Units_Off  */
544    {"all_units_off",    0, 3, 1, 1, F_ALL,            0, 0, 1, 4 },   /* All_Units_Off  */
545    {"allunitsoff",      0, 3, 1, 1, F_ALL,            0, 0, 1, 4 },   /* All_Units_Off  */
546    {"units_off",        0, 3, 1, 1, F_ALL,            0, 0, 1, 4 },   /* All_Units_Off  */
547    {"unitsoff",         0, 3, 1, 1, F_ALL,            0, 0, 1, 4 },   /* All_Units_Off  */
548    {"turn",            27,-1, 1,-1, F_TRN,            0, 0,16,38 },   /* Turn command */
549    {"preset",          10, 3, 2, 2, F_NMA,            0, 0, 5,12 },   /* Old Preset (not for macro) */
550    {"preset_dim",      10, 3, 2, 2, F_NMA,            0, 0, 5,12 },   /* Old Preset (not for macro) */
551    {"preset",          11, 3, 2, 2, F_NMA | F_HID,    0, 0, 5,12 },   /* Old Preset (not for macro) */
552    {"preset_dim",      11, 3, 2, 2, F_NMA | F_HID,    0, 0, 5,12 },   /* Old Preset (not for macro) */
553    {"mpreset",         10, 3, 2, 2, F_MAC,            0, 0,18,42 },   /* Limited old Preset for macros */
554    {"macro_preset",    10, 3, 2, 2, F_MAC,            0, 0,18,42 },   /* Limited old Preset for macros */
555    {"mpreset",         11, 3, 2, 2, F_MAC | F_HID,    0, 0,18,42 },   /* Limited old Preset for macros */
556    {"macro_preset",    11, 3, 2, 2, F_MAC | F_HID,    0, 0,18,42 },   /* Limited old Preset for macros */
557    {"preset_level",    10, 3, 1, 1, F_NOA | F_NUM,    0, 0, 6,13 },   /* Old Preset with no address byte */
558    {"presetlevel",     10, 3, 1, 1, F_NOA | F_NUM,    0, 0, 6,13 },   /* Old Preset with no address byte */
559    {"preset_level",    11, 3, 1, 1, F_NOA|F_NUM|F_HID, 0, 0, 6,13 },   /* Old Preset with no address byte */
560    {"presetlevel",     11, 3, 1, 1, F_NOA|F_NUM|F_HID, 0, 0, 6,13 },   /* Old Preset with no address byte */
561    {"status",          15, 3, 1, 1, F_STA,            0, 0, 3, 9 },   /* Status Request */
562    {"status_req",      15, 3, 1, 1, F_STA,            0, 0, 3, 9 },   /* Status Request */
563    {"statusreq",       15, 3, 1, 1, F_STA,            0, 0, 3, 9 },   /* Status Request */
564    {"status_on",       13, 3, 1, 1, F_ALL,            0, 0, 3,10 },   /* Status On */
565    {"statuson",        13, 3, 1, 1, F_ALL,            0, 0, 3,10 },   /* Status On */
566    {"status_off",      14, 3, 1, 1, F_ALL,            0, 0, 3,11 },   /* Status Off */
567    {"statusoff",       14, 3, 1, 1, F_ALL,            0, 0, 3,11 },   /* Status Off */
568    {"status_emu",      48,-1, 1, 1, F_NMA|F_HID,      0, 0, 2,102},   /* Emulate Status Ack */
569    {"hail",             8, 3, 0, 1, F_ALL,            0, 0, 0,24 },   /* Hail Request */
570    {"hail_req",         8, 3, 0, 1, F_ALL,            0, 0, 0,24 },   /* Hail Request */
571    {"hailw",            8, 3, 0, 1, F_ALL|F_STA|F_NMA, 0, 0, 0,33 },   /* Hail Request, wait for ack */
572    {"hail_ack",         9, 3, 0, 1, F_ALL,            0, 0, 0,25 },   /* Hail Acknowledge */
573    {"hailack",          9, 3, 0, 1, F_ALL,            0, 0, 0,25 },   /* Hail Acknowledge */
574    {"data_xfer",       12, 3, 1, 1, F_ALL,            0, 0, 1,27 },   /* Extended data transfer (0xC) */
575    {"dataxfer",        12, 3, 1, 1, F_ALL,            0, 0, 1,27 },   /* Extended data transfer (0xC) */
576 
577    {"xon",              7, 6, 1, 1, F_EXT | F_DED, 0xfe, 0, 3,29 },   /* Extended Full ON (LM14A) */
578    {"xoff",             7, 6, 1, 1, F_EXT | F_DED, 0xfd, 0, 3,30 },   /* Extended Full OFF (LM14A) */
579    {"xpreset",          7, 6, 2, 2, F_EXT | F_DED, 0x31, 0, 7,14 },   /* Extended Preset Dim (LM14A) */
580    {"ext_preset",       7, 6, 2, 2, F_EXT | F_DED, 0x31, 0, 7,14 },   /* Extended Preset Dim (LM14A) */
581    {"xdim",             7, 6, 2, 2, F_EXT | F_DED, 0x31, 0, 7,14 },   /* Extended Preset Dim (LM14A) */
582    {"xpresetramp",      7, 6, 3, 3, F_EXT|F_DED|F_HID|F_NMA, 0x31, 0,37,88 }, /* Extended Preset Dim with ramp */
583    {"xallon",           7, 6, 1, 1, F_EXT | F_ALL, 0x33, 0, 1,15 },   /* Extended All Units On */
584    {"xall_on",          7, 6, 1, 1, F_EXT | F_ALL, 0x33, 0, 1,15 },   /* Extended All Units On */
585    {"ext_all_on",       7, 6, 1, 1, F_EXT | F_ALL, 0x33, 0, 1,15 },   /* Extended All Units On */
586    {"xalloff",          7, 6, 1, 1, F_EXT | F_ALL, 0x34, 0, 1,16 },   /* Extended All Units Off */
587    {"xall_off",         7, 6, 1, 1, F_EXT | F_ALL, 0x34, 0, 1,16 },   /* Extended All Units Off */
588    {"ext_all_off",      7, 6, 1, 1, F_EXT | F_ALL, 0x34, 0, 1,16 },   /* Extended All Units Off */
589    {"xstatus",          7, 6, 1, 1, F_EXT | F_STA, 0x37, 0, 3,17 },   /* Extended Status Request */
590    {"ext_status",       7, 6, 1, 1, F_EXT | F_STA, 0x37, 0, 3,17 },   /* Extended Status Request */
591    {"ext_status_req",   7, 6, 1, 1, F_EXT | F_STA, 0x37, 0, 3,17 },   /* Extended Status Request */
592    {"xconfig",          7, 6, 2, 2, F_EXT|F_ALL|F_DED, 0x3b, 0,19,43 },   /* Extended Auto Status Mode */
593    {"xpowerup",         7, 6, 1, 1, F_EXT,         0x37, 1, 3,66 },   /* Extended Module PowerUp */
594    {"xpup",             7, 6, 1, 1, F_EXT,         0x37, 1, 3,66 },   /* Extended Module PowerUp */
595    {"xgrpadd",          7, 6, 2, 2, F_XGP,         0x30, 0,35,79 },   /* Extended add to group */
596    {"xga",              7, 6, 2, 2, F_XGP,         0x30, 0,35,79 },   /* Extended add to group */
597    {"xgrpaddlvl",       7, 6, 3, 3, F_XGP,         0x32, 0,32,84 },   /* Extended add to group at level */
598    {"xgal",             7, 6, 3, 3, F_XGP,         0x32, 0,32,84 },   /* Extended add to group at level */
599    {"xgrprem",          7, 6, 2, 2, F_XGP,         0x35, 0,33,80 },   /* Extended remove from group(s) */
600    {"xgr",              7, 6, 2, 2, F_XGP,         0x35, 0,33,80 },   /* Extended remove from group(s) */
601    {"xgrpremall",       7, 6, 2, 2, F_XGP | F_ALL, 0x35, 3,36,83 },   /* Extended delete from group(s) */
602    {"xgra",             7, 6, 2, 2, F_XGP | F_ALL, 0x35, 3,36,83 },   /* Extended delete from group(s) */
603    {"xgrpexec",         7, 6, 2, 2, F_XGP | F_ALL, 0x36, 0,34,81 },   /* Extended delete from group */
604    {"xgx",              7, 6, 2, 2, F_XGP | F_ALL, 0x36, 0,34,81 },   /* Extended delete from group */
605    {"xgrpexec",         7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x36, 2,34,81 },   /* (Needed for report.txt) */
606    {"xgrpstatus",       7, 6, 2, 2, F_XGP | F_STA, 0x37, 2,35,82 },   /* Extended group status request */
607    {"xgs",              7, 6, 2, 2, F_XGP | F_STA, 0x37, 2,35,82 },   /* Extended group status request */
608    {"xgrpstatusreq",    7, 6, 2, 2, F_XGP | F_STA, 0x37, 2,35,82 },   /* Extended group status request */
609    {"xgrpstatus",       7, 6, 2, 2, F_XGP|F_STA|F_HID, 0x37, 3,35,82 },  /* (Needed for report.txt) */
610    {"xgrpoff",          7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x36, 1,34,85 },   /* Ext group Off - maybe!!!*/
611    {"xgrpdim",          7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x3c, 0,34,86 },   /* Ext group Dim */
612    {"xgrpdim",          7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x3c, 2,34,86 },  /* (Needed for report.txt) */
613    {"xgrpbright",       7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x3c, 1,34,87 },   /* Ext group Bright */
614    {"xgrpbright",       7, 6, 2, 2, F_XGP|F_ALL|F_HID, 0x3c, 3,34,87 },  /* (Needed for report.txt) */
615    { XARBNAME,          7, 6, 3, 3, F_ARB,         0xff, 0, 8,19 },   /* Extended code - arbitrary */
616    { XARBNAMEW,         7, 6, 3, 3, F_ARB|F_STA|F_NMA, 0xff, 0,8,20 }, /* Extended Status Req - arbitrary */
617 
618    {"address",         20,-1, 1,-1, F_NMA,            0, 0,15,22 },   /* Address bytes only (*)*/
619    {"function",        21,-1, 1,-1, F_NOA,            0, 0, 9,23 },   /* HC|Func byte only */
620    {"kill_all_hc",     24,-1, 0, 0, 0,                0, 0,12,28 },   /* Send All_Units_Off to all housecodes */
621    {"pause",           25,-1, 1, 1, F_NMA|F_NUM|F_DUM, 0, 0,14,36 },   /* Pause seconds (locked) */
622    {"sleep",           41,-1, 1, 1, F_NMA|F_NUM|F_DUM, 0, 0,14,78 },   /* Sleep seconds (unlocked) */
623    {"delay",           32,-1, 1, 1, F_NMA|F_NUM|F_DUM, 0, 0,21,45 },   /* Delay minutes (unlocked) */
624    {"rdelay",          45,-1, 1, 2, F_NMA|F_NUM|F_DUM, 0, 0,42,96 },   /* Random delay minutes (unlocked) */
625    {"rcs_req",         28,-1, 1,-1, F_SPF | F_NMA,    0, 0,17,44 },   /* RCS-compatible general status query */
626    {"temp_req",        28,-1, 1,-1, F_SPF | F_NMA,    0, 0,17,40 },   /* RCS-compatible temperature query */
627    {"vdata",           37,-1, 2, 2, F_NMA|F_DUM,      0, 0,28,70 },   /* Virtual data */
628    {"vdatam",          37,-1, 2, 2, F_NMA|F_DUM,      1, 0,28,98 },   /* Virtual memory data */
629 
630    {"command2cm11a",   31,-1, 1,-1, F_NMA|F_NUM|F_HID, 0, 0,10,41 },  /* Send bytes directly to interface */
631    {"bytes2cm11a",     29,-1, 1,-1, F_NMA|F_NUM|F_HID, 0, 0,10,41 },  /* Send bytes directly to interface */
632    {"sendbytes",       22,-1, 1,-1, F_NMA|F_NUM|F_HID, 0, 0,10,21 },   /* Send arbitrary hex bytes (*)*/
633    {"sendtext",        23,-1, 2, 2, F_NMA|F_ALL|F_HID, 0, 0,11,26 },   /* Send (quoted) text message (*)*/
634    {"pausetick",       33,-1, 0, 0, F_NMA|F_HID|F_DUM, 0, 0,12,46 },   /* Pause until next tick of sys clock */
635 
636    {"arm",             43,-1, 0, 4, F_NMA|F_NUM|F_DUM, SETFLAG, 0,40,94 },   /* Arm system */
637    {"disarm",          43,-1, 0, 0, F_NMA|F_NUM|F_DUM, CLRFLAG, 0,12,95 },   /* Disarm system */
638 
639    {"setflag",         35,-1, 1, 1, F_FLG|F_DUM,     SETFLAG, 0,26,67 },   /* Set software flags */
640    {"setflags",        35,-1, 1, 1, F_FLG|F_DUM,     SETFLAG, 0,26,67 },   /* Set software flags */
641    {"clrflag",         35,-1, 1, 1, F_FLG|F_DUM,     CLRFLAG, 0,26,68 },   /* Clear software flags */
642    {"clrflags",        35,-1, 1, 1, F_FLG|F_DUM,     CLRFLAG, 0,26,68 },   /* Clear software flags */
643    {"settimer",        38,-1, 2, 2, F_NMA|F_NUM|F_DUM,     0, 0,29,71 },   /* Set countdown timer */
644    {"setrtimer",       46,-1, 2, 3, F_NMA|F_NUM|F_DUM,     0, 0,41,97 },   /* Set random countdown timer */
645    {"clrtimers",       39,-1, 0, 0, F_NMA|F_NUM|F_DUM,     0, 0,12,72 },   /* Reset all timers to zero */
646    {"clrspend",        36,-1, 1, 1, F_NMA|F_DUM,           0, 0,27,69 },   /* Clear status flags */
647    {"clrstatus",       36,-1, 1, 1, F_NMA|F_DUM,           0, 0,27,69 },   /* Clear status flags */
648    {"clrtamper",       40,-1, 0, 0, F_NMA|F_NUM|F_DUM,     0, 0,12,74 },   /* Clear tamper flags */
649 
650    {"setcount",        42,-1, 2, 2, F_NMA|F_NUM|F_DUM, CNT_SET, 0,38,89 },   /* Set counter value */
651    {"setc",            42,-1, 2, 2, F_NMA|F_NUM|F_DUM, CNT_SET, 0,38,89 },   /* Set counter value */
652    {"inccount",        42,-1, 1, 1, F_NMA|F_NUM|F_DUM, CNT_INC, 0,39,90 },   /* Increment counter value */
653    {"incc",            42,-1, 1, 1, F_NMA|F_NUM|F_DUM, CNT_INC, 0,39,90 },   /* Increment counter value */
654    {"deccount",        42,-1, 1, 1, F_NMA|F_NUM|F_DUM, CNT_DEC, 0,39,91 },   /* Decrement counter value */
655    {"decc",            42,-1, 1, 1, F_NMA|F_NUM|F_DUM, CNT_DEC, 0,39,91 },   /* Decrement counter value */
656    {"null",            47,-1, 0, 0, F_NMA|F_DUM,           0, 0, 12,101 },   /* Null, does nothing */
657 
658    {"@arm",            43,-1, 0, 4, F_FLG|F_INT, SETFLAG, 0,40,94 },   /* Arm system */
659    {"@disarm",         43,-1, 0, 0, F_FLG|F_INT, CLRFLAG, 0,12,95 },   /* Disarm system */
660 
661    {"@setflag",        35,-1, 1, 1, F_FLG|F_INT, SETFLAG, 0,26,67 },   /* Set software flags */
662    {"@setflags",       35,-1, 1, 1, F_FLG|F_INT, SETFLAG, 0,26,67 },   /* Set software flags */
663    {"@setf",           35,-1, 1, 1, F_FLG|F_INT, SETFLAG, 0,26,67 },   /* Set software flags */
664    {"@clrflag",        35,-1, 1, 1, F_FLG|F_INT, CLRFLAG, 0,26,68 },   /* Clear software flags */
665    {"@clrflags",       35,-1, 1, 1, F_FLG|F_INT, CLRFLAG, 0,26,68 },   /* Clear software flags */
666    {"@clrf",           35,-1, 1, 1, F_FLG|F_INT, CLRFLAG, 0,26,68 },   /* Clear software flags */
667    {"@settimer",       38,-1, 2, 2, F_NMA|F_NUM|F_INT, 0, 0,29,71 },   /* Set countdown timer */
668    {"@sett",           38,-1, 2, 2, F_NMA|F_NUM|F_INT, 0, 0,29,71 },   /* Set countdown timer */
669    {"@setrtimer",      46,-1, 2, 3, F_NMA|F_NUM|F_INT, 0, 0,41,97 },   /* Set random countdown timer */
670    {"@clrtimers",      39,-1, 0, 0, F_NMA|F_NUM|F_INT, 0, 0,12,72 },   /* Reset all timers to zero */
671    {"@clrt",           39,-1, 0, 0, F_NMA|F_NUM|F_INT, 0, 0,12,72 },   /* Reset all timers to zero */
672    {"@clrspend",       36,-1, 1, 1, F_NMA|F_INT,       0, 0,27,69 },   /* Clear status pending flags */
673    {"@clrstatus",      36,-1, 1, 1, F_NMA|F_INT,       0, 0,27,69 },   /* Clear status pending flags */
674    {"@clrs",           36,-1, 1, 1, F_NMA|F_INT,       0, 0,27,69 },   /* Clear status pending flags */
675    {"@vdata",          37,-1, 2, 2, F_NMA|F_INT,       0, 0,28,70 },   /* Virtual data */
676    {"@vdatam",         37,-1, 2, 2, F_NMA|F_INT,       1, 0,28,98 },   /* Virtual memory data */
677 
678    {"@setcount",       42,-1, 2, 2, F_NMA|F_NUM|F_INT, CNT_SET, 0,38,89 },   /* Set counter value */
679    {"@setc",           42,-1, 2, 2, F_NMA|F_NUM|F_INT, CNT_SET, 0,38,89 },   /* Set counter value */
680    {"@inccount",       42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_INC, 0,39,90 },   /* Increment counter value */
681    {"@incc",           42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_INC, 0,39,90 },   /* Increment counter value */
682    {"@deccount",       42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DEC, 0,39,91 },   /* Decrement counter value */
683    {"@decc",           42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DEC, 0,39,91 },   /* Decrement counter value */
684    {"@decskpz",        42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DSKPZ,  0,39,92 },   /* Dec Skip Z */
685    {"@decskpgz",       42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DSKPNZ, 0,39,93 },   /* Dec Skip GTZ */
686    {"@decskpnziz",     42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DSKPNZIZ, 0,39,100 }, /* Dec Skip NZ or Init Z */
687    {"@decskpnz",       42,-1, 1, 1, F_NMA|F_NUM|F_INT, CNT_DSKPNZIZ, 0,39,100 }, /* Dec Skip NZ or Init Z */
688 
689    {"@null",           44,-1, 0, 0, F_NMA|F_INT,       0,       0,12,99 },   /* Null command */
690 
691 
692 
693 #ifdef HASEXT0   /* Extended Type 0 (SW10 shutter controller) commands */
694    {"shopen",           7, 6, 2, 2, F_DED | F_SHU, 0x03, 0, 7,63 },   /* Shutter open, ignore limit */
695    {"shopenlim",        7, 6, 2, 2, F_DED | F_SHU, 0x01, 0, 7,61 },   /* Shutter open, enforce limit */
696    {"shsetlim",         7, 6, 2, 2, F_DED | F_SHU, 0x02, 0, 7,62 },   /* Shutter set limit */
697    {"shopenall",        7, 6, 1, 1, F_ALL | F_SHU, 0x04, 0, 1,64 },   /* Shutter all full open */
698    {"shcloseall",       7, 6, 1, 1, F_ALL | F_SHU, 0x0B, 0, 1,65 },   /* Shutter all full close */
699 #endif
700 
701 #ifdef HASCM17A  /* CM17A ("Firecracker") commands */
702    {"freset",          34,-1, 0, 0, F_RFS,            8, 0,12,55 },   /* CM17A Reset */
703    {"fon",             34,-1, 1, 1, F_RFS,            2, 0, 3,47 },   /* CM17A On RF command */
704    {"foff",            34,-1, 1, 1, F_RFS,            3, 0, 3,48 },   /* CM17A Off RF command */
705    {"fbright",         34,-1, 2, 2, F_RFS,            5, 0,22,50 },   /* CM17A Bright RF command */
706    {"fdim",            34,-1, 2, 2, F_RFS,            4, 0,22,49 },   /* CM17A Dim RF command */
707    {"fdimbo",          34,-1, 2, 2, F_RFS,            7, 0,24,56 },   /* CM17A Dimb RF command */
708    {"flightson",       34,-1, 1, 1, F_RFS | F_ALL,    1, 0, 1,51 },   /* CM17A AllLightsOn RF command */
709    {"flightsoff",      34,-1, 1, 1, F_RFS | F_ALL,    6, 0, 1,52 },   /* CM17A AllLightsOff RF command */
710    {"falloff",         34,-1, 1, 1, F_RFS | F_ALL,    0, 0, 1,53 },   /* CM17A AllUnitsOff RF command */
711    {"farb",            34,-1, 3, 3, F_RFS | F_NUM,    9, 0,23,54 },   /* CM17A Arbitrary RF command */
712    {"farw",            34,-1, 1,-1, F_RFS | F_NUM,   10, 0,30,73 },   /* CM17A Arbit 16 bit RF command */
713    {"flux",            34,-1, 3,-1, F_RFS | F_NUM,   11, 0,31,76 },   /* CM17A Arbit UX17/23 RF command */
714    {"ffbright",        34,-1, 2, 2, F_RFF,            5, 0,22,58 },   /* CM17A Fast Bright RF command */
715    {"ffdim",           34,-1, 2, 2, F_RFF,            4, 0,22,57 },   /* CM17A Fast Dim RF command */
716    {"ffdimbo",         34,-1, 2, 2, F_RFF,            7, 0,24,59 },   /* CM17A Dimb RF command */
717    {"ffarb",           34,-1, 3, 3, F_RFF | F_NUM,    9, 0,23,60 },   /* CM17A Fast Arbit RF command */
718    {"ffarw",           34,-1, 1,-1, F_RFF | F_NUM,   10, 0,30,75 },   /* CM17A Arbit 16 bit RF command */
719    {"fflux",           34,-1, 3,-1, F_RFF | F_NUM,   11, 0,31,77 },   /* CM17A Arbit UX17/23 RF command */
720 #endif
721 };
722 int nx10cmds = sizeof(x10command)/sizeof(struct cmd_list);
723 
724 
725 /* Function labels for monitor display (must align with enum in process.h) */
726 /* (with fake add-on function "AllOn") */
727 
728 char *funclabel[76] = {
729    "AllOff", "LightsOn", "On", "Off", "Dim", "Bright", "LightsOff",
730    "Extended", "Hail", "HailAck", "Preset", "Preset",
731    "DataXfer", "StatusOn", "StatusOff", "StatusReq", "AllOn",
732    "xPowerUp", "vData", "vDataM", "Panic", "Arm", "Disarm",
733    "Alert", "Clear", "Test", "sLightsOn", "sLightsOff", "secTamper",
734    "sDusk", "sDawn", "AkeyOn", "AkeyOff", "BkeyOn", "BkeyOff",
735    "rfxTemp", "rfxTemp2", "rfxRH", "rfxBP", "rfxVad", "rfxPot", "rfxVs",
736    "rfxLoBat", "rfxOther", "rfxPulse", "rfxPower", "rfxWater", "rfxGas",
737    "rfxCount", "dmxTemp", "dmxOn", "dmxOff", "dmxSetpoint",
738    "oreTemp", "oreRH", "oreBP", "oreWgt",
739    "oreWindSp", "oreWindAvSp", "oreWindDir",
740    "oreRainRate", "oreRainTot", "elsCurr", "oreUV",
741    "kOff", "kOn", "kGrpOff", "kGrpOn", "kUnkFunc", "kPreset", "kGrpPreset", "kUnkPresetFunc",
742    "owlPower", "owlEnergy", "Inactive",
743    "_Invalid_",
744 };
745 
746 
747 char *ext3funclabel[17] = {
748    "", "xPreset", "", "xAllOn", "xAllOff", "", "", "xStatusReq", "xStatusAck",
749    "", "", "xConfig", "", "", "", "", "xPowerUp",
750 };
751 
752 char *rfxfunclabel[8] = {
753    "rfxTemp", "rfxTemp2", "rfxHumidity", "rfxPressure", "rfxVad",
754    "rfxVs", "rfxLoBat", "rfxOther",
755 };
756 
757 
rcs_temp(int function,int predim,char hc,int unit)758 char *rcs_temp ( int function, int predim, char hc, int unit )
759 {
760    static char buffer[80];
761    int         temp;
762 
763    if ( (function != 10 && function != 11) || unit < 11 ) {
764       buffer[0] = '\0';
765       return (char *)NULL;
766    }
767 
768    temp = -60 + (predim - 1) + 32 * (unit - 11);
769    sprintf(buffer, " Temperature =%3d  : Location %c\n",
770       temp, toupper((int)hc));
771 
772    return buffer;
773 }
774 
775 /*---------------------------------------------------------------------+
776  | Return the length of a macro element corresponding to the           |
777  | argument command code.                                              |
778  +---------------------------------------------------------------------*/
macro_element_length(unsigned char cmdcode)779 int macro_element_length ( unsigned char cmdcode )
780 {
781    int j, length = -1;
782 
783 
784    for ( j = 0; j < nx10cmds; j++ ) {
785       if ( x10command[j].code == cmdcode && x10command[j].length > 0 ) {
786          length = x10command[j].length;
787          break;
788       }
789    }
790    return length;
791 }
792 
793 /*---------------------------------------------------------------------+
794  | Return 1 if argument command label is in the administrative command |
795  | (help) table, otherwise return 0.                                   |
796  +---------------------------------------------------------------------*/
is_admin_cmd(char * label)797 int is_admin_cmd ( char *label )
798 {
799    int j;
800 
801    j = 0;
802    while ( helpadmin[j][0] != NULL ) {
803       if ( strcmp(label, helpadmin[j][0]) == 0 )
804          return 1;
805       j++;
806    }
807    return 0;
808 }
809 
810 /*---------------------------------------------------------------------+
811  | Return 1 if argument command label is in the x10command[] table,    |
812  | otherwise return 0.                                                 |
813  +---------------------------------------------------------------------*/
is_direct_cmd(char * label)814 int is_direct_cmd ( char *label )
815 {
816    int j;
817 
818    for ( j = 0; j < nx10cmds; j++ ) {
819       if ( strcmp(label, x10command[j].label) == 0 &&
820            !(x10command[j].flags & F_HLP) )
821          return 1;
822    }
823    return 0;
824 }
825 
826 /*---------------------------------------------------------------------+
827  | Return 1 if argument command label is a scene or is in the          |
828  | x10command[] table, otherwise return 0.                             |
829  +---------------------------------------------------------------------*/
is_heyu_cmd(char * label)830 int is_heyu_cmd ( char *label )
831 {
832    if ( strchr(label, ';') || strchr(label, ' ') || strchr(label, '\t') ) {
833       /* Possible compound command */
834       return 1;
835    }
836 
837    if ( lookup_scene(configp->scenep, label) >= 0 ) {
838       return 1;
839    }
840 
841    if ( is_direct_cmd(label) ) {
842       return 1;
843    }
844 
845    return 0;
846 }
847 
848 
849 /*---------------------------------------------------------------------+
850  | Return a double random number between 0.0 and 1.0                   |
851  +---------------------------------------------------------------------*/
random_float(void)852 double random_float ( void )
853 {
854    static   int is_seeded;
855    unsigned int seed;
856    struct timeval tvstruct, *tv = &tvstruct;
857 
858    if ( !is_seeded ) {
859       gettimeofday(tv, NULL);
860       seed = (unsigned long)tv->tv_sec + (unsigned long)tv->tv_usec;
861       srandom(seed);
862       is_seeded = 1;
863    }
864    return (double)random() / (double)RandMax;
865 }
866 
867 /*---------------------------------------------------------------------+
868  | Sleep for argument milliseconds.                                    |
869  +---------------------------------------------------------------------*/
millisleep(long millisec)870 void millisleep( long millisec )
871 {
872    #ifdef NSLEEP
873    struct timestruc_t tspec;
874 
875    if ( millisec == 0 )
876       return;
877 
878    tspec.tv_sec = millisec / 1000;
879    tspec.tv_nsec = 1000000L * (millisec % 1000);
880 
881    while ( nsleep( &tspec, &tspec ) == -1 );
882    #else
883 #ifdef ATTSVR4
884    struct timeval tspec;
885 #else
886    struct timespec tspec;
887 #endif /* ATTSVR4 */
888 
889    if ( millisec == 0 )
890       return;
891 
892 #ifdef ATTSVR4
893    tspec.tv_sec = millisec / 1000;
894    tspec.tv_usec = 1000 * (millisec % 1000);
895    while ( usleep(tspec.tv_usec) == -1 );
896 #else
897    tspec.tv_sec = millisec / 1000;
898    tspec.tv_nsec = 1000000L * (millisec % 1000);
899    while ( nanosleep( &tspec, &tspec ) == -1 );
900 #endif /* ATTSVR4 */
901    #endif  /* NSLEEP */
902 
903    return;
904 }
905 
906 /*---------------------------------------------------------------------+
907  | Sleep for argument microseconds.                                    |
908  +---------------------------------------------------------------------*/
microsleep(long microsec)909 void microsleep( long microsec )
910 {
911    #ifdef NSLEEP
912    struct timestruc_t tspec;
913 
914    if ( microsec == 0 )
915       return;
916 
917    tspec.tv_sec = microsec / 1000000L;
918    tspec.tv_nsec = 1000L * (microsec % 1000000L);
919 
920    while ( nsleep( &tspec, &tspec ) == -1 );
921    #else
922 #ifdef ATTSVR4
923    struct timeval tspec;
924 #else
925    struct timespec tspec;
926 #endif
927 
928    if ( microsec == 0 )
929       return;
930 
931 #ifdef ATTSVR4
932    tspec.tv_sec = microsec / 1000000;
933    tspec.tv_usec = microsec % 1000000;
934    while ( usleep(tspec.tv_usec) == -1 );
935 #else
936    tspec.tv_sec = microsec / 1000000L;
937    tspec.tv_nsec = 1000L * (microsec % 1000000L);
938    while ( nanosleep( &tspec, &tspec ) == -1 );
939 #endif /* ATTSVR4 */
940    #endif /* NSLEEP */
941 
942    return;
943 }
944 
945 /*---------------------------------------------------------------------+
946  | Compare two commands.  Return 0 if identical except for label.      |
947  +---------------------------------------------------------------------*/
cmp_commands(struct cmd_list * one,struct cmd_list * two)948 static int cmp_commands( struct cmd_list *one, struct cmd_list *two )
949 {
950 
951    if ( !one || !two ||
952         one->flags   != two->flags   ||
953 	one->code    != two->code    ||
954 	one->subcode != two->subcode ||
955         one->length  != two->length  ||
956         one->minargs != two->minargs ||
957 	one->maxargs != two->maxargs ||
958 	one->argtype != two->argtype ||
959 	one->help    != two->help        )
960       return 1;
961    return 0;
962 }
963 
964 
965 /*---------------------------------------------------------------------+
966  | Parse the units string.  Pass back the X10 units bitmap through the |
967  | argument list.  Return 0 if valid bitmap, otherwise write error msg |
968  | to error handler and return 1.  Unit 0 is considered a valid unit   |
969  | number but doesn't appear in the bitmap unless it's the only unit,  |
970  | whereupon the bitmap will be 0.                                     |
971  +---------------------------------------------------------------------*/
parse_units(char * str,unsigned int * bitmap)972 int parse_units ( char *str, unsigned int *bitmap )
973 {
974 
975    static int   umap[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
976    char         buffer[256];
977    char         errmsg[128];
978    char         *tail, *sp;
979 
980    int          ulist[17];
981    int          j, ustart, unit;
982 
983    clear_error_message();
984 
985    if ( strcmp(str, "*") == 0 ) {
986       *bitmap = 0xffff;
987       return 0;
988    }
989 
990    for ( j = 0; j < 17; j++ )
991       ulist[j] = 0;
992 
993    ustart = -1;
994    tail = str;
995    while ( *(get_token(buffer, &tail, "-,", 255)) ) {
996       unit = (int)strtol(buffer, &sp, 10);
997       if ( *sp ) {
998          sprintf(errmsg, "Invalid character '%c' in units list", *sp);
999          store_error_message(errmsg);
1000          return 1;
1001       }
1002 
1003       if ( unit < 0 || unit > 16 ) {
1004          sprintf(errmsg, "Unit number outside range 0-16");
1005          store_error_message(errmsg);
1006          return 1;
1007       }
1008 
1009       if ( *tail == ',' || *tail == '\0' ) {
1010          if ( ustart > -1 ) {
1011             if ( ustart <= unit ) {
1012                for ( j = ustart; j <= unit; j++ )
1013                   ulist[j] = 1;
1014                ustart = -1;
1015                continue;
1016             }
1017             else {
1018                for ( j = unit; j <= ustart; j++ )
1019                   ulist[j] = 1;
1020                ustart = -1;
1021                continue;
1022             }
1023          }
1024          else {
1025             ulist[unit] = 1;
1026             continue;
1027          }
1028       }
1029       else {
1030          ustart = unit;
1031       }
1032    }
1033 
1034    *bitmap = 0;
1035    for ( j = 0; j < 16; j++ )
1036       *bitmap |= (ulist[j+1]) << umap[j];
1037 
1038    return 0;
1039 }
1040 
1041 /*---------------------------------------------------------------------+
1042  | Return the number of units represented by a bitmap                  |
1043  +---------------------------------------------------------------------*/
count_bitmap_units(unsigned int bitmap)1044 int count_bitmap_units ( unsigned int bitmap )
1045 {
1046    int j, mask, count;
1047 
1048    mask = 1;
1049    count = 0;
1050    for ( j = 0; j < 16; j++ ) {
1051       if ( bitmap & mask )
1052          count++;
1053       mask = mask << 1;
1054    }
1055    return count;
1056 }
1057 
1058 /*---------------------------------------------------------------------+
1059  | Parse the Houscode|Units token, which may be comprised of:          |
1060  |   Optional '+' or '-' prefix, plus:                                 |
1061  |   An ALIAS, or:                                                     |
1062  |     Housecode letter in range A-P, or '_' for placeholder.          |
1063  |     Heyu format units string, e.g., 2,3,5-8,10,13-15                |
1064  | Pass back the Housecode letter and X10 units bitmap through the     |
1065  | argument list.                                                      |
1066  | Return flags indicating the presence of each component:             |
1067  |   A_VALID    Valid address.                                         |
1068  |   A_HCODE    Housecode A-P has been returned.                       |
1069  |   A_BMAP     Non-zero bitmap has been returned                      |
1070  |   A_PLUS     Prefix '+' sign                                        |
1071  |   A_MINUS    Prefix '-' sign                                        |
1072  |   A_ALIAS    Housecode and bitmap came from an alias in config      |
1073  |   A_DUMMY    Replaceable address, e.g., $1, $3                      |
1074  |   A_MULT     Bitmap contains multiple units.                        |
1075  |   A_STAR     Asterisk ('*') for units list.                         |
1076  +---------------------------------------------------------------------*/
parse_addr(char * token,char * housecode,unsigned int * bitmap)1077 unsigned int parse_addr ( char *token, char *housecode,
1078                                                      unsigned int *bitmap )
1079 {
1080    char          errmsg[128];
1081    unsigned int  flags = 0;
1082    char          *sp;
1083    char          hc;
1084 
1085    sp = token;
1086 
1087    if ( strchr(sp, '$') ) {
1088       /* Supply a fake value that will always pass scene verification */
1089       /* for a parameter containing a positional parameter.           */
1090       *housecode = 'A';
1091       *bitmap = 1;
1092       flags = (A_DUMMY | A_VALID | A_HCODE | A_BMAP | A_PLUS);
1093       return flags;
1094    }
1095 
1096    if ( *sp == '+' )  {
1097       flags |= A_PLUS;
1098       sp++;
1099    }
1100    else if ( *sp == '-' ) {
1101       flags |= A_MINUS;
1102       sp++;
1103    }
1104 
1105    if ( *sp == '\0' ) {
1106       *housecode = '_';
1107       *bitmap = 0;
1108       flags |= A_VALID;
1109       return flags;
1110    }
1111 
1112 
1113    if ( alias_lookup(sp, housecode, bitmap) >= 0 ) {
1114       flags |= (A_ALIAS | A_HCODE | A_VALID);
1115       if ( *bitmap )
1116          flags |= A_BMAP;
1117       if ( count_bitmap_units(*bitmap) > 1 )
1118          flags |= A_MULT;
1119       return flags;
1120    }
1121 
1122    hc = toupper((int)(*sp));
1123 
1124    if ( hc == '_' )
1125       hc = configp->housecode;
1126 
1127    if ( (hc != '_') && (hc < 'A' || hc > 'P') ) {
1128       *housecode = '_';
1129       *bitmap = 0;
1130       sprintf(errmsg, "Invalid housecode '%c' in HU '%s'", *sp, token);
1131       store_error_message(errmsg);
1132       flags &= ~(A_VALID | A_HCODE);
1133       return flags;
1134    }
1135 
1136    *housecode = hc;
1137    if ( hc != '_' )
1138       flags |= A_HCODE;
1139    sp++;
1140 
1141    if ( *sp == '\0' ) {
1142       *bitmap = 0;
1143       flags |= A_VALID;
1144       return flags;
1145    }
1146    else if ( *sp == '*' ) {
1147       *bitmap = 0xffff;
1148       flags |= (A_VALID | A_STAR);
1149       return flags;
1150    }
1151    else if ( parse_units(sp, bitmap) != 0 ) {
1152       /* Prefix the message written to error handler */
1153       sprintf(errmsg, "HU '%s': ", token);
1154       add_error_prefix(errmsg);
1155       return 0;
1156    }
1157 
1158    if ( *bitmap )
1159       flags |= A_BMAP;
1160    if ( count_bitmap_units(*bitmap) > 1 )
1161       flags |= A_MULT;
1162 
1163    flags |= A_VALID;
1164 
1165    return flags;
1166 }
1167 
1168 /*---------------------------------------------------------------+
1169  |  Return the 8-bit checksum of the bytes in argument buffer.   |
1170  +---------------------------------------------------------------*/
checksum(unsigned char * buffer,int length)1171 unsigned char checksum( unsigned char *buffer, int length )
1172 {
1173    int j;
1174    unsigned char sum = 0;
1175 
1176    for ( j = 0; j < length; j++ )
1177       sum += buffer[j];
1178 
1179    return (sum & 0xff);
1180 }
1181 
1182 
1183 /*---------------------------------------------------------------+
1184  | Create buffers with three two-byte command function codes and |
1185  | their checksums (which must all be different and less than    |
1186  | 0x80).  These are used to alert the relay that a command with |
1187  | a checksum 0x5A will be immediately following.                |
1188  +---------------------------------------------------------------*/
create_alerts(void)1189 void create_alerts ( void )
1190 {
1191    extern unsigned char alert_cmd[6];
1192    extern unsigned char alert_ack[3];
1193 
1194    static unsigned char cmdbytes[] = {0x86,0xCC, 0x46,0xCC, 0x86,0xBC};
1195 
1196    int j;
1197    unsigned char *sp;
1198 
1199    memcpy(alert_cmd, (void *)cmdbytes, 6);
1200 
1201    for ( j = 0; j < 3; j++ ) {
1202       sp = alert_cmd + j + j;
1203       alert_ack[j] = checksum(sp, 2);
1204    }
1205    if ( verbose ) {
1206       fprintf(stderr, "Alert acks are 0x%02x, 0x%02x, 0x%02x\n",
1207          alert_ack[0], alert_ack[1], alert_ack[2]);
1208    }
1209 
1210    return;
1211 }
1212 
1213 
1214 /*---------------------------------------------------------------+
1215  | Alert function which sends three function codes in a row      |
1216  | without the "WRMI" byte after each.  When the relay receives  |
1217  | the sequence of three checksums, it is alerted that the next  |
1218  | 0x5A it receives is a checksum rather than an incoming signal.|
1219  +---------------------------------------------------------------*/
send_checksum_alert(void)1220 int send_checksum_alert ( void )
1221 {
1222    extern unsigned char alert_cmd[6];
1223    extern unsigned char alert_ack[3];
1224 
1225    int  k, nread;
1226    char inbuf[16];
1227    int  xread();
1228    int  ignoret;
1229 
1230    send_x10state_command(ST_CHKSUM, 0);
1231 
1232    for ( k = 0; k < 3; k++ ) {
1233          ignoret = write(tty, (char *)(alert_cmd + k + k), 2);
1234          nread = exread(sptty, inbuf, 1, 1);
1235          if ( nread != 1 || inbuf[0] != alert_ack[k] ) {
1236             return 1;
1237          }
1238    }
1239    return 0;
1240 
1241    fprintf(stderr, "send_checksum_alert() failed.\n");
1242 
1243    check4poll(0, 0);
1244    return 1;
1245 }
1246 
1247 /*---------------------------------------------------------------+
1248  |  Display a message from one of the send_* functions.          |
1249  +---------------------------------------------------------------*/
display_send_message(char * message)1250 void display_send_message ( char *message )
1251 {
1252    if ( i_am_aux || i_am_relay )
1253       display_x10state_message(message);
1254    else
1255       fprintf(stderr, "%s\n", message);
1256    return;
1257 }
1258 
1259 /*---------------------------------------------------------------+
1260  |  Send the contents of the buffer to the interface.            |
1261  |  Return 0 if successful, 1 otherwise.                         |
1262  |  Argument 'chkoff' - which byte starts the checksum.          |
1263  |  Argument 'syncmode' - external triggering.                   |
1264  +---------------------------------------------------------------*/
send_buffer(unsigned char * buffer,int length,int chkoff,int timeout,int syncmode)1265 int send_buffer ( unsigned char *buffer, int length,
1266                           int chkoff, int timeout, int syncmode )
1267 {
1268    unsigned char chksum;
1269    unsigned char inbuff[16];
1270    int           nread;
1271    char          msgbuff[80];
1272    extern int    wait_external_trigger(int);
1273 
1274    if ( tty == TTY_DUMMY ) {
1275       display_send_message("Command is invalid for dummy TTY.");
1276       return 1;
1277    }
1278 
1279    chksum = checksum(buffer + chkoff, length - chkoff);
1280 
1281    if ( chksum == 0x5a ) {
1282       if ( verbose )
1283          display_send_message("Alert relay to command with checksum 0x5A");
1284       if ( send_checksum_alert() != 0 )
1285          return 1;
1286    }
1287 
1288    (void) xwrite(tty, (char *) buffer, length);
1289 
1290    /* get a check sum in reply */
1291 
1292    nread = xread(sptty, inbuff, 1, 1);
1293 
1294    if ( chksum == inbuff[0] ) {
1295       /* Checksum is OK; tell interface to transmit the code. */
1296       if ( verbose )
1297          display_send_message("Checksum confirmed");
1298 
1299       /* Defer sending until the appropriate half-cycle if so          */
1300       /* specified (assuming we have the external hardware installed). */
1301       if ( syncmode != NO_SYNC )
1302          wait_external_trigger(syncmode);
1303 
1304       (void) xwrite(tty, "\00" , 1);	/* WRMI (we really mean it) */
1305    }
1306    else if (!nread) {
1307       if ( verbose )
1308          display_send_message("Checksum NOT confirmed, no response from interface");
1309       syslog(LOG_WARNING, "Checksum NOT confirmed, no response from interface");
1310       return 1;
1311    }
1312    else  {
1313       if ( verbose ) {
1314          sprintf(msgbuff, "Checksum %02x NOT confirmed - should be %02x\n",
1315             inbuff[0], chksum);
1316          display_send_message(msgbuff);
1317       }
1318       return 1;
1319    }
1320 
1321    if ( (nread = xread(sptty, inbuff, 1, timeout)) >= 1 ) {
1322       if( inbuff[0] == 0x55 ) {
1323          /* interface is ready again */
1324          if ( verbose )
1325             display_send_message("Interface is ready.");
1326          return 0;
1327       }
1328       else if ( nread == 1 && chksum == 0x5a ) {
1329          if ( verbose )
1330             display_send_message("Checksum 0x5a workaround.");
1331          return 0;
1332       }
1333       else if ( nread == 1 && inbuff[0] == 0x5a ) {
1334          /* Incoming command - forget about the 0x55 */
1335          if ( verbose )
1336             display_send_message("send_buffer() interrupted by incoming command");
1337          return 2;
1338       }
1339       else {
1340          sprintf(msgbuff,
1341           "Interface not ready - received %d bytes beginning %02x\n",
1342             nread, inbuff[0]);
1343          display_send_message(msgbuff);
1344          return 1;
1345       }
1346    }
1347 
1348    return 1;
1349 }
1350 
1351 
1352 /*---------------------------------------------------------------+
1353  |  Send an encoded housecode|bitmap to the interface.           |
1354  |  Try up to 3 times.  Return 0 if successful, otherwise 1.     |
1355  |  Argument housecode is a X10 encoded char A-P                 |
1356  |  Argument bitmap is encoded per X10                           |
1357  +---------------------------------------------------------------*/
send_address(unsigned char hcode,unsigned int bitmap,int syncmode)1358 int send_address ( unsigned char hcode, unsigned int bitmap,
1359                                       int syncmode )
1360 {
1361    unsigned int  mask;
1362    unsigned char buffer[4];
1363    char          msgbuff[80];
1364    int           j, k, result = 1;
1365    int           count, timeout = 10;
1366    extern int    is_ring ( void );
1367 
1368    if ( tty == TTY_DUMMY ) {
1369       display_send_message("Command is invalid for dummy TTY.");
1370       return 1;
1371    }
1372 
1373    if ( bitmap == 0 )
1374       return 0;
1375 
1376    /* Defer sending if Ring Indicator line is asserted, which */
1377    /* indicates there is incoming X10 data.                   */
1378    if ( configp->check_RI_line == YES ) {
1379       count = 10;
1380       while ( is_ring() > 0 && --count ) {
1381          sleep(1);
1382          if ( verbose && !i_am_relay )
1383             display_send_message("Defer while RI line is asserted.");
1384       }
1385       if ( !count )
1386          display_send_message("RI serial line may be stuck.");
1387    }
1388 
1389    if ( busywait(configp->auto_wait) != 0 ) {
1390       display_send_message("Unable to send address bytes - busywait() timeout.");
1391       return 1;
1392    }
1393 
1394    /* Inform other processes of parent process */
1395    send_x10state_command(ST_SOURCE, (unsigned char)heyu_parent);
1396 
1397    hcode = hcode << 4;
1398 
1399    mask = 1;
1400    for ( j = 0; j < 16; j++ ) {
1401       if ( bitmap & mask ) {
1402          buffer[0] = 0x04;
1403          buffer[1] = hcode | j ;
1404 
1405 	 /* Kluge "fix" for checksum 5A problem. */
1406 	 /* CM11A seems to disregard a bit in the Dim field. */
1407          /* (The only address affected by this is G1) */
1408 	 if ( checksum(buffer, 2) == 0x5A && configp->fix_5a == YES )
1409 	    buffer[0] = 0x0C;
1410 
1411          if ( verbose ) {
1412             sprintf(msgbuff, "Sending address bytes: %02x %02x\n",
1413                buffer[0], buffer[1]);
1414             display_send_message(msgbuff);
1415          }
1416 
1417          for ( k = 0; k < configp->send_retries; k++ ) {
1418             if ( (result = send_buffer(buffer, 2, 0, timeout, syncmode)) == 0 )
1419                break;
1420             sleep(1);
1421          }
1422 
1423          /* Delay so many powerline cycles between commands */
1424          millisleep((long)configp->cm11_post_delay);
1425 
1426          if ( result != 0 ) {
1427             display_send_message("Unable to send address bytes");
1428             return 1;
1429          }
1430       }
1431 
1432       mask = mask << 1;
1433    }
1434 
1435    return 0;
1436 }
1437 
1438 /*---------------------------------------------------------------+
1439  |  Send an X10 command buffer to the interface. Try up to 3     |
1440  |  times.  Return 0 if sucessful, otherwise 1.                  |
1441  +---------------------------------------------------------------*/
send_command(unsigned char * buffer,int len,unsigned int showstatus,int syncmode)1442 int send_command ( unsigned char *buffer, int len,
1443                 unsigned int showstatus, int syncmode )
1444 {
1445    int        j, result = 1;
1446    int        count, timeout = 10;
1447    char       msgbuff[128];
1448    extern int is_ring ( void );
1449 
1450    if ( tty == TTY_DUMMY ) {
1451       display_send_message("Command is invalid for dummy TTY.");
1452       return 1;
1453    }
1454 
1455    if ( len == 0 )
1456       return 0;
1457 
1458    /* Defer sending if Ring Indicator line is asserted, which */
1459    /* indicates there is incoming X10 data.                   */
1460    if ( !i_am_aux && configp->check_RI_line == YES ) {
1461       count = 10;
1462       while ( is_ring() > 0 && --count ) {
1463          sleep(1);
1464          if ( verbose && !i_am_relay )
1465             display_send_message("Defer while RI line is asserted.");
1466       }
1467       if ( !count )
1468          display_send_message("RI serial line may be stuck.");
1469    }
1470 
1471    if ( busywait(configp->auto_wait) != 0 ) {
1472       display_send_message("Unable to send command bytes - busywait() timeout.");
1473       return 1;
1474    }
1475 
1476    /* Inform other processes of parent process */
1477    send_x10state_command(ST_SOURCE, (unsigned char)heyu_parent);
1478 
1479    if ( verbose ) {
1480       sprintf(msgbuff, "Sending command bytes:");
1481       for ( j = 0; j < len; j++ )
1482          sprintf(msgbuff + strlen(msgbuff), " %02x", buffer[j]);
1483       display_send_message(msgbuff);
1484    }
1485 
1486    for ( j = 0; j < configp->send_retries; j++ )  {
1487       if ( (result = send_buffer(buffer, len, 0, timeout, syncmode)) == 0 ||
1488            result == 2 ) {
1489          break;
1490       }
1491       sleep(1);
1492    }
1493    if ( result != 0 ) {
1494       display_send_message("Unable to send command bytes");
1495       return 1;
1496    }
1497 
1498    if ( special_func > 0 ) {
1499       /* Command has called for special processing of */
1500       /* reply from module, e.g, decoding of preset   */
1501       /* reply from TempLinc into temperature         */
1502       for ( j = 0; j < configp->spf_timeout; j++ ) {
1503          check4poll(0, 1);
1504       }
1505    }
1506    else if ( showstatus ) {
1507       /* Display status if expected */
1508       for ( j = 0; j < configp->status_timeout; j++ ) {
1509          check4poll(1, 1);
1510       }
1511    }
1512    else {
1513          check4poll(0, 0);
1514    }
1515 
1516    /* Delay so many powerline cycles between commands */
1517    millisleep((long)configp->cm11_post_delay);
1518 
1519    if ( verbose )
1520       display_send_message("Transmission OK");
1521 
1522    return 0;
1523 
1524 }
1525 
1526 /*---------------------------------------------------------------+
1527  | Disable the CM11A from asserting the RI serial line.          |
1528  +---------------------------------------------------------------*/
ri_disable(void)1529 int ri_disable ( void )
1530 {
1531    unsigned char buf[2];
1532 
1533    buf[0] = 0xdb;
1534 
1535    return send_buffer(buf, 1, 0, 1, 0);
1536 }
1537 
1538 
1539 /*---------------------------------------------------------------+
1540  | Enable the CM11A to assert the RI serial line.                |
1541  +---------------------------------------------------------------*/
ri_enable(void)1542 int ri_enable ( void )
1543 {
1544    unsigned char buf[2];
1545 
1546    buf[0] = 0xeb;
1547 
1548    return send_buffer(buf, 1, 0, 1, 0);
1549 }
1550 
1551 
1552 /*---------------------------------------------------------------+
1553  | Send an RI control command, either enable or disable,         |
1554  | depending on the config directive RING_CTRL.                  |
1555  +---------------------------------------------------------------*/
ri_control(void)1556 int ri_control ( void )
1557 {
1558    unsigned char buf[2];
1559 
1560    buf[0] = (configp->ring_ctrl == DISABLE) ? 0xdb : 0xeb ;
1561 
1562    return send_buffer(buf, 1, 0, 1, 0);
1563 }
1564 
1565 /*---------------------------------------------------------------+
1566  | Send an RI control command from the Heyu relay, either enable |
1567  | or disable, depending on the config directive RING_CTRL.      |
1568  +---------------------------------------------------------------*/
relay_ri_control(void)1569 int relay_ri_control ( void )
1570 {
1571     int      j, nread;
1572     unsigned char outbuf[2], inbuf[2];
1573 
1574     int ignoret;
1575 
1576     outbuf[0] = (configp->ring_ctrl == DISABLE) ? 0xdb : 0xeb;
1577 
1578     for ( j = 0; j < 3; j++ ) {
1579        ignoret = write(tty, (char *) outbuf, 1);
1580        nread = sxread(tty, inbuf, 1, 1);
1581 
1582        if ( nread != 1 || inbuf[0] != outbuf[0] ) {
1583           continue;
1584        }
1585 
1586        ignoret = write(tty, "\0", 1);   /* WRMI */
1587        nread = sxread(tty, inbuf, 1, 1);
1588 
1589        if ( nread == 1 && inbuf[0] == 0x55 ) {
1590           return 0;
1591        }
1592     }
1593 
1594     millisleep(SETCLOCK_DELAY);
1595 
1596     return 1;
1597 }
1598 
1599 
1600 
1601 
1602 /*---------------------------------------------------------------+
1603  | Command to disable the CM11A RI line.                         |
1604  +---------------------------------------------------------------*/
c_ri_disable(int argc,char * argv[])1605 int c_ri_disable ( int argc, char *argv[] )
1606 {
1607    int  j, result = 0;
1608 
1609    for ( j = 0; j < 3; j++ )  {
1610       if ( (result = ri_disable()) == 0 )
1611          break;
1612       sleep(1);
1613    }
1614 
1615    if ( result == 0 )
1616       printf("CM11A RI serial line disabled.\n");
1617    else
1618       fprintf(stderr, "No response to ri_disable command.\n");
1619 
1620    check4poll(0, 0);
1621 
1622    return result;
1623 }
1624 
1625 /*---------------------------------------------------------------+
1626  | Command to enable the CM11A RI line.                          |
1627  +---------------------------------------------------------------*/
c_ri_enable(int argc,char * argv[])1628 int c_ri_enable ( int argc, char *argv[] )
1629 {
1630    int  j, result = 0;
1631 
1632    for ( j = 0; j < 3; j++ )  {
1633       if ( (result = ri_enable()) == 0 )
1634          break;
1635       sleep(1);
1636    }
1637 
1638    if ( result == 0 )
1639       printf("CM11A RI serial line enabled.\n");
1640    else
1641       fprintf(stderr, "No response to ri_enable command.\n");
1642 
1643    check4poll(0, 0);
1644 
1645    return result;
1646 }
1647 
1648 /*---------------------------------------------------------------+
1649  | Ping the CM11A retries times.                                 |
1650  | Return 0 if response OK, 1 otherwise.                         |
1651  +---------------------------------------------------------------*/
relay_ping(int tty,int retries)1652 int relay_ping ( int tty, int retries )
1653 {
1654     int      j, nread;
1655     unsigned char outbuf[2], inbuf[2];
1656 
1657     int ignoret;
1658 
1659     outbuf[0] = (configp->ring_ctrl == DISABLE) ? 0xdb : 0xeb;
1660 
1661     for ( j = 0; j < retries; j++ ) {
1662        ignoret = write(tty, (char *) outbuf, 1);
1663        nread = sxread(tty, inbuf, 1, 1);
1664 
1665        if ( nread != 1 || inbuf[0] != outbuf[0] ) {
1666           continue;
1667        }
1668 
1669        ignoret = write(tty, "\0", 1);   /* WRMI */
1670        nread = sxread(tty, inbuf, 1, 1);
1671 
1672        if ( nread == 1 && inbuf[0] == 0x55 ) {
1673           return 0;
1674        }
1675     }
1676 
1677     millisleep(SETCLOCK_DELAY);
1678 
1679     return 1;
1680 }
1681 
1682 
1683 /*---------------------------------------------------------------+
1684  | Ping the CM11A by sending the RI control command.             |
1685  +---------------------------------------------------------------*/
c_ping(int argc,char * argv[])1686 int c_ping ( int argc, char *argv[] )
1687 {
1688    int  j, result = 0;
1689 
1690    for ( j = 0; j < 3; j++ )  {
1691       if ( (result = ri_control()) == 0 )
1692          break;
1693       sleep(1);
1694    }
1695 
1696    if ( result == 0 )
1697       printf("Ping response OK\n");
1698    else
1699       fprintf(stderr, "No response to ping.\n");
1700 
1701    check4poll(0, 0);
1702 
1703    return result;
1704 }
1705 
1706 /*---------------------------------------------------------------+
1707  | Called by c_busywait() below.                                 |
1708  +---------------------------------------------------------------*/
busywait(int timeout)1709 int busywait ( int timeout )
1710 {
1711    int  j, nread;
1712    unsigned char buf[2], inbuf[16];
1713    int xread();
1714 
1715    int ignoret;
1716 
1717    if ( timeout <= 0 )
1718       return 0;
1719 
1720    /* RI control byte used for ping */
1721    buf[0] = (configp->ring_ctrl == DISABLE) ? 0xdb : 0xeb ;
1722    buf[1] = 0x00;
1723 
1724    send_x10state_command (ST_BUSYWAIT, 1);
1725 
1726    for ( j = 0; j < timeout; j++ ) {
1727       ignoret = write(tty, (char *)buf, 1);
1728       nread = xread(sptty, (char *)inbuf, 1, 1);
1729 
1730       if ( nread != 1 || inbuf[0] != buf[0] ) {
1731          millisleep(60);
1732          continue;
1733       }
1734       ignoret = write(tty, (char *)(buf + 1), 1);
1735       nread = xread(sptty, (char *)inbuf, 1, 1);
1736 
1737       if ( nread == 1 && inbuf[0] == 0x55u ) {
1738          return 0;
1739       }
1740       break;
1741    }
1742 
1743    send_x10state_command (ST_BUSYWAIT, 0);
1744    check4poll(0, 0);
1745    return 1;
1746 }
1747 
1748 /*---------------------------------------------------------------+
1749  | Wait for CM11A to respond to "pings" or until timeout.        |
1750  | Return 1 if timeout or error, otherwise 0.                    |
1751  +---------------------------------------------------------------*/
c_busywait(int argc,char * argv[])1752 int c_busywait ( int argc, char *argv[] )
1753 {
1754    int  timeout;
1755    char *sp;
1756 
1757    timeout = 30;
1758 
1759    if ( argc > 2 ) {
1760       timeout = (int)strtol(argv[2], &sp, 10);
1761       if ( !strchr(" \t\n", *sp) || timeout < 0 || timeout > 300 ) {
1762          fprintf(stderr, "Invalid %s timeout parameter.\n", argv[1]);
1763          return 1;
1764       }
1765    }
1766    return busywait(timeout);
1767 }
1768 
1769 /*---------------------------------------------------------------+
1770  | Display help for engine internal commands.                    |
1771  +---------------------------------------------------------------*/
display_internal_help(void)1772 void display_internal_help ( void )
1773 {
1774    int  j;
1775    int  szlbl, szarg, sztot;
1776    char buffer[128];
1777    struct cmd_list *one = NULL, *two;
1778 
1779    /* Get max string lengths for formatting output */
1780    szlbl = szarg = sztot = 0;
1781    for ( j = 0; j < nx10cmds; j++ ) {
1782       if ( x10command[j].flags & F_HID || !(x10command[j].flags & F_INT) )  {
1783          continue;
1784       }
1785       sztot = max(sztot, ((int)strlen(x10command[j].label) +
1786                       (int)strlen(helparg[x10command[j].argtype])) );
1787    }
1788    for ( j = 0; j < nx10cmds; j++ ) {
1789       if ( x10command[j].flags & F_HID || !(x10command[j].flags & F_INT) )  {
1790          continue;
1791       }
1792       two = &x10command[j];
1793       if ( cmp_commands(one, two) ) {
1794          sprintf(buffer, "%s  %s", x10command[j].label, helparg[x10command[j].argtype]);
1795          printf("  %-*s  %s%s\n", sztot + 2, buffer,
1796              helpdirect[x10command[j].help],
1797              (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
1798          one = two;
1799       }
1800    }
1801    return;
1802 }
1803 
1804 /*---------------------------------------------------------------+
1805  | Display help for general command list other than direct or    |
1806  | cm17a commands.                                               |
1807  +---------------------------------------------------------------*/
display_gen_help(char * helplist[][3])1808 void display_gen_help ( char *helplist[][3] )
1809 {
1810    int  j;
1811    int  szlbl, szarg, sztot;
1812    char buffer[128];
1813 
1814    /* Get max string lengths for formatting output */
1815    szlbl = szarg = sztot = 0;
1816    j = 0;
1817    while ( helplist[j][0] != NULL ) {
1818       szlbl = max(szlbl, (int)strlen(helplist[j][0]));
1819       szarg = max(szarg, (int)strlen(helplist[j][1]));
1820       sztot = max(sztot, ((int)strlen(helplist[j][0]) +
1821                                (int)strlen(helplist[j][1])));
1822       j++;
1823    }
1824 
1825    j = 0;
1826    while ( helplist[j][0] != NULL ) {
1827       /* Compact format */
1828       sprintf(buffer, "%s  %s", helplist[j][0], helplist[j][1]);
1829       printf("  heyu  %-*s  %s\n", sztot + 2, buffer, helplist[j][2]);
1830       j++;
1831    }
1832    return;
1833 }
1834 
1835 /*---------------------------------------------------------------+
1836  | Display help for argument command, or all commands if empty   |
1837  | string.                                                       |
1838  +---------------------------------------------------------------*/
1839 #define HELP_OPTIONS   1
1840 #define HELP_ADMIN     2
1841 #define HELP_STATE     4
1842 #define HELP_DIRECT    8
1843 #define HELP_CM17A    16
1844 #define HELP_RFXS     32
1845 #define HELP_RFXM     64
1846 #define HELP_INT     128
1847 #define HELP_SHUTTER 256
1848 #define HELP_HELP    512
1849 #define HELP_DMX    1024
1850 #define HELP_ORE    2048
1851 #define HELP_ALL    (HELP_OPTIONS | HELP_ADMIN | HELP_STATE | HELP_DIRECT | HELP_CM17A | HELP_RFXS | HELP_RFXM | HELP_INT | HELP_SHUTTER | HELP_DMX | HELP_ORE )
1852 #define COMPACT_FORMAT
command_help(char * command)1853 void command_help ( char *command )
1854 {
1855    int j, ext0done = 0;
1856    int szlbl, szarg, sztot, list = 0;
1857    struct cmd_list *one = NULL, *two;
1858    char buffer[256];
1859 
1860    strlower(command);
1861    list = (*command == '\0')                    ?  HELP_ALL     :
1862           (!strcmp(command,  "help"))           ?  HELP_HELP    :
1863           (!strncmp(command, "options", 6))     ?  HELP_OPTIONS :
1864           (!strncmp(command, "admin", 5))       ?  HELP_ADMIN   :
1865           (!strcmp(command,  "state"))          ?  HELP_STATE   :
1866           (!strcmp(command,  "direct"))         ?  HELP_DIRECT  :
1867           (!strcmp(command,  "shutter"))        ?  HELP_SHUTTER :
1868           (!strncmp(command, "cm17a", 4))       ?  HELP_CM17A   :
1869           (!strcmp(command,  "firecracker"))    ?  HELP_CM17A   :
1870           (!strncmp(command, "rfxsensor", 4))   ?  HELP_RFXS    :
1871           (!strncmp(command, "rfxmeter", 4))    ?  HELP_RFXM    :
1872           (!strncmp(command, "digimax", 3))     ?  HELP_DMX     :
1873           (!strncmp(command, "oregon", 3))      ?  HELP_ORE     :
1874           (!strncmp(command, "internal", 3))    ?  HELP_INT     : 0;
1875 #if 0
1876           (!strncmp(command, "rfx", 3))      ?  (HELP_RFXS | HELP_RFXM)    : 0 ;
1877 #endif
1878 
1879 #ifdef HASRFXS
1880       j = 0;
1881       while ( helprfxsensor[j][0] != NULL ) {
1882          if ( strcmp(command, helprfxsensor[j][0]) == 0 ) {
1883             printf("Usage: heyu  %s %s  %s\n", helprfxsensor[j][0],
1884                 helprfxsensor[j][1], helprfxsensor[j][2]);
1885             return;
1886          }
1887          j++;
1888       }
1889 #endif /* HASRFXS */
1890 
1891 #ifdef HASRFXM
1892       j = 0;
1893       while ( helprfxmeter[j][0] != NULL ) {
1894          if ( strcmp(command, helprfxmeter[j][0]) == 0 ) {
1895             printf("Usage: heyu  %s %s  %s\n", helprfxmeter[j][0],
1896                 helprfxmeter[j][1], helprfxmeter[j][2]);
1897             return;
1898          }
1899          j++;
1900       }
1901 #endif /* HASRFXM */
1902 
1903 #ifdef HASDMX
1904       j = 0;
1905       while ( helpdigimax[j][0] != NULL ) {
1906          if ( strcmp(command, helpdigimax[j][0]) == 0 ) {
1907             printf("Usage: heyu  %s %s  %s\n", helpdigimax[j][0],
1908                 helpdigimax[j][1], helpdigimax[j][2]);
1909             return;
1910          }
1911          j++;
1912       }
1913 #endif /* HASDMX */
1914 
1915 #ifdef HASORE
1916       j = 0;
1917       while ( helporegon[j][0] != NULL ) {
1918          if ( strcmp(command, helporegon[j][0]) == 0 ) {
1919             printf("Usage: heyu  %s %s  %s\n", helporegon[j][0],
1920                 helporegon[j][1], helporegon[j][2]);
1921             return;
1922          }
1923          j++;
1924       }
1925 #endif /* HASORE */
1926 
1927    if ( list & HELP_OPTIONS ) {
1928       /* Display options */
1929       printf("\n");
1930       for ( j = 0; j < (int)(sizeof(options)/sizeof(char *)); j++ ) {
1931          printf("%s\n", options[j]);
1932       }
1933    }
1934 
1935    if ( list & HELP_HELP ) {
1936       printf("\nUsage: heyu help [category|command]  All help [or specific Category or Command]\n");
1937       printf(" Categories: options admin direct");
1938 #ifdef HASEXT0
1939       printf(" shutter");
1940 #endif
1941 #ifdef HASCM17A
1942       printf(" cm17a");
1943 #endif
1944       printf(" internal");
1945       printf(" state");
1946 #ifdef HASRFXS
1947       printf(" rfxsensor");
1948 #endif
1949 #ifdef HASRFXM
1950       printf(" rfxmeter");
1951 #endif
1952 #ifdef HASDMX
1953       printf(" digimax");
1954 #endif
1955 #ifdef HASORE
1956       printf(" oregon");
1957 #endif
1958       printf(" webhook");
1959       printf("\n");
1960    }
1961 
1962    if ( list & HELP_ADMIN ) {
1963       /* Administrative commands */
1964       printf("\n [Administrative commands  (H = Housecode)]\n");
1965       display_gen_help(helpadmin);
1966    }
1967 
1968    if ( list & HELP_STATE ) {
1969       /* State commands */
1970       printf("\n [State commands  (H = Housecode, u = Single unit) - require heyu engine]\n");
1971       display_gen_help(helpstate);
1972    }
1973 
1974 #if HASRFXS
1975    if ( (list & HELP_STATE) || (list & HELP_RFXS) ) {
1976       /* RFXSensor state commands */
1977       printf("\n [RFXSensor state commands  (H = Housecode, u = Single unit) - require heyu engine]\n");
1978       display_gen_help(helprfxsensor);
1979    }
1980 #endif /* HASRFXS */
1981 
1982 #ifdef HASRFXM
1983    if ( (list & HELP_STATE) || (list & HELP_RFXM) ) {
1984       /* RFXMeter state commands */
1985       printf("\n [RFXMeter state commands  (H = Housecode, u = Single unit) - require heyu engine]\n");
1986       display_gen_help(helprfxmeter);
1987    }
1988 #endif /* HASRFXM */
1989 
1990 #ifdef HASDMX
1991    if ( (list & HELP_STATE) || (list & HELP_DMX) ) {
1992       /* Digimax state commands */
1993       printf("\n [DigiMax state commands  (H = Housecode, u = Single unit) - require heyu engine]\n");
1994       display_gen_help(helpdigimax);
1995    }
1996 #endif /* HASDMX */
1997 
1998 #ifdef HASORE
1999    if ( (list & HELP_STATE) || (list & HELP_ORE) ) {
2000       /* Oregon state commands */
2001       printf("\n [Oregon group state commands  (H = Housecode, u = Single unit) - require heyu engine]\n");
2002       display_gen_help(helporegon);
2003    }
2004 #endif /* HASORE */
2005 
2006    if ( list & HELP_DIRECT ) {
2007       /* Direct commands */
2008       /* Get max string lengths for formatting output */
2009       szlbl = szarg = sztot = 0;
2010       for ( j = 0; j < nx10cmds; j++ ) {
2011          if ( x10command[j].flags & (F_HID | F_INT))
2012             continue;
2013          sztot = max(sztot, ((int)strlen(x10command[j].label) +
2014                       (int)strlen(helparg[x10command[j].argtype])) );
2015       }
2016       printf("\n [Direct commands  (H = Housecode, U = Units list)]\n");
2017       for ( j = 0; j < nx10cmds; j++ ) {
2018 	 two = &x10command[j];
2019 	 if ( x10command[j].flags & (F_HID | F_INT | F_FIR | F_SHU) ) {
2020 	    /* Command not shown in this menu */
2021             continue;
2022 	 }
2023          if ( cmp_commands(one, two) ) {
2024             sprintf(buffer, "%s  %s", x10command[j].label, helparg[x10command[j].argtype]);
2025             printf("  heyu  %-*s  %s%s\n", sztot + 2, buffer,
2026                 helpdirect[x10command[j].help],
2027                 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2028 	    one = two;
2029 	 }
2030       }
2031 
2032       #ifdef HASEXT0
2033       printf("\n [Shutter (Extended Type 0) direct commands  (H = Housecode, U = Units list)]\n");
2034       for ( j = 0; j < nx10cmds; j++ ) {
2035          two = &x10command[j];
2036 	 if ( !(x10command[j].flags & F_SHU) ) {
2037             continue;
2038 	 }
2039          if ( cmp_commands(one, two) ) {
2040             sprintf(buffer, "%s  %s", x10command[j].label, helparg[x10command[j].argtype]);
2041             printf("  heyu  %-*s  %s%s\n", sztot + 2, buffer,
2042                 helpdirect[x10command[j].help],
2043                 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2044 	    one = two;
2045 	 }
2046       }
2047       #endif /* End ifdef */
2048       ext0done = 1;
2049    }
2050 
2051    #ifdef HASEXT0
2052    if ( list & HELP_SHUTTER && !ext0done) {
2053       printf("\n [Shutter (Extended Type 0) direct commands  (H = Housecode, U = Units list)]\n");
2054       /* Get max string lengths for formatting output */
2055       szlbl = szarg = sztot = 0;
2056       for ( j = 0; j < nx10cmds; j++ ) {
2057          if ( x10command[j].flags & F_HID || !(x10command[j].flags & F_SHU) )
2058             continue;
2059          sztot = max(sztot, ((int)strlen(x10command[j].label) +
2060                       (int)strlen(helparg[x10command[j].argtype])) );
2061       }
2062 
2063       for ( j = 0; j < nx10cmds; j++ ) {
2064          two = &x10command[j];
2065          if ( !(x10command[j].flags & F_SHU) ) {
2066             continue;
2067          }
2068          if ( cmp_commands(one, two) ) {
2069             sprintf(buffer, "%s  %s", x10command[j].label, helparg[x10command[j].argtype]);
2070             printf("  heyu  %-*s  %s%s\n", sztot + 2, buffer,
2071                 helpdirect[x10command[j].help],
2072                 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2073             one = two;
2074          }
2075       }
2076    }
2077    #endif /* End ifdef */
2078 
2079    #ifdef HASCM17A
2080    if ( list & HELP_CM17A ) {
2081       /* Get max string lengths for formatting output */
2082       szlbl = szarg = sztot = 0;
2083       for ( j = 0; j < nx10cmds; j++ ) {
2084          if ( x10command[j].flags & F_HID )
2085             continue;
2086          sztot = max(sztot, ((int)strlen(x10command[j].label) +
2087                       (int)strlen(helparg[x10command[j].argtype])) );
2088       }
2089 
2090       printf("\n [CM17A \"Firecracker\" commands (H = Housecode, U = Units list)]\n");
2091       for ( j = 0; j < nx10cmds; j++ ) {
2092          two = &x10command[j];
2093 	 if ( !(x10command[j].flags & F_FIR) ) {
2094             continue;
2095 	 }
2096          if ( cmp_commands(one, two) ) {
2097             sprintf(buffer, "%s  %s", x10command[j].label, helparg[x10command[j].argtype]);
2098             printf("  heyu  %-*s  %s%s\n", sztot + 2, buffer,
2099                 helpdirect[x10command[j].help],
2100                 (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2101 	    one = two;
2102 	 }
2103       }
2104    }
2105    #endif /* End ifdef */
2106 
2107    if ( list & HELP_INT ) {
2108       /* Internal engine precommands */
2109       printf("\n [Internal engine precommands  (See man pages)]\n");
2110       display_internal_help();
2111    }
2112 
2113    if ( list & (HELP_DIRECT | HELP_CM17A) ) {
2114       display_helpnotes();
2115       display_manpage_list();
2116       return;
2117    }
2118    else if ( list > 0 ) {
2119       display_manpage_list();
2120       return;
2121    }
2122    else if ( list == 0 ) {
2123       /* Display specific command */
2124 
2125       j = 0;
2126       while ( helpadmin[j][0] != NULL ) {
2127          if ( strcmp(command, helpadmin[j][0]) == 0 ) {
2128             printf("Usage: heyu  %s %s  %s\n", helpadmin[j][0],
2129                 helpadmin[j][1], helpadmin[j][2]);
2130             return;
2131          }
2132          j++;
2133       }
2134 
2135       j = 0;
2136       while ( helpstate[j][0] != NULL ) {
2137          if ( strcmp(command, helpstate[j][0]) == 0 ) {
2138             printf("Usage: heyu  %s %s  %s\n", helpstate[j][0],
2139                 helpstate[j][1], helpstate[j][2]);
2140             return;
2141          }
2142          j++;
2143       }
2144 
2145       for ( j = 0; j < nx10cmds; j++ ) {
2146 	 if ( strcmp(command, x10command[j].label) == 0 )
2147             break;
2148       }
2149       if ( j < nx10cmds ) {
2150          printf("Usage: %s%s %s   %s%s\n",
2151             (x10command[j].flags & F_INT) ? "" : "heyu  ",
2152             x10command[j].label,
2153             helparg[x10command[j].argtype], helpdirect[x10command[j].help],
2154             (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2155       }
2156 #if 0
2157       if ( j < nx10cmds ) {
2158          printf("Usage: heyu  %s %s   %s%s\n", x10command[j].label,
2159             helparg[x10command[j].argtype], helpdirect[x10command[j].help],
2160             (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
2161       }
2162 #endif
2163       else {
2164 	 printf("Command '%s' not recognized.\n", command);
2165          return;
2166       }
2167       return;
2168    }
2169 }
2170 
2171 
2172 /*---------------------------------------------------------------+
2173  | Send a hail_ack (for use only by state engine)                |
2174  +---------------------------------------------------------------*/
acknowledge_hail(void)2175 void acknowledge_hail ( void )
2176 {
2177    extern   char default_housecode;
2178    extern   int  heyu_parent;
2179    int      save_parent;
2180    unsigned char buf[4];
2181    unsigned char hcode;
2182 
2183    hcode = hc2code(default_housecode);
2184 
2185    buf[0] = 0x06;
2186    buf[1] = hcode << 4 | 9;
2187 
2188    /* Temporarily set heyu_parent so that ack will be sent */
2189    /* as if from a script.                                */
2190 
2191    save_parent = heyu_parent;
2192    heyu_parent = D_ENGINE;
2193    send_command(buf, 2, 0, NO_SYNC);
2194    heyu_parent = save_parent;
2195 
2196    return;
2197 }
2198 
2199 
2200 /*---------------------------------------------------------------+
2201  | Display a list of all commands and their synonyms.            |
2202  +---------------------------------------------------------------*/
display_syn(void)2203 void display_syn ( void )
2204 {
2205    int j;
2206    int szlbl;
2207    char delim = ' ';
2208    struct cmd_list *one = NULL, *two;
2209 
2210    /* Get max label length for formatting output */
2211    szlbl = 0;
2212    for ( j = 0; j < nx10cmds; j++ ) {
2213       if ( x10command[j].flags & (F_HLP | F_HID) )
2214          continue;
2215       szlbl = max(szlbl, (int)strlen(x10command[j].label));
2216    }
2217 
2218    printf(" %-*s   %s\n", szlbl, "Command","Built-In Synonyms");
2219    printf(" %-*s   %s",   szlbl, "-------","-----------------");
2220 
2221    for ( j = 0; j < nx10cmds; j++ ) {
2222       two = &x10command[j];
2223       if ( x10command[j].flags & (F_HLP | F_HID) ) {
2224          /* Don't display hidden or purely help commands */
2225          continue;
2226       }
2227       if ( cmp_commands(one, two) ) {
2228          printf("\n %-*s ", szlbl, x10command[j].label);
2229          one = two;
2230          delim = ' ';
2231       }
2232       else {
2233          printf("%c %s", delim, x10command[j].label);
2234          delim = ',';
2235       }
2236    }
2237    printf("\n");
2238    return;
2239 }
2240 
2241 
2242 
cmd_usage(FILE * fd,int index)2243 void cmd_usage ( FILE *fd, int index )
2244 {
2245    fprintf(fd, "Usage: heyu %s %s\n",
2246         x10command[index].label, helparg[x10command[index].argtype]);
2247 }
2248 
2249 /*---------------------------------------------------------------+
2250  | Send to the CM11A or merely verify the syntax of the command  |
2251  | represented by the argc tokens in list argv[] according to    |
2252  | input argument 'mode':                                        |
2253  |   mode = CMD_VERIFY   Merely verify the syntax                |
2254  |   mode = CMD_RUN      Actually send the command               |
2255  |   mode = CMD_INT      Run internal engine precommand          |
2256  | Return 0 if successful; 1 otherwise.                          |
2257  | (The CMD_VERIFY mode is intended for verifying the syntax     |
2258  | of scene commands in the x10config file.)                     |
2259  +---------------------------------------------------------------*/
2260 
direct_command(int argc,char * argv[],int mode)2261 int direct_command( int argc, char *argv[], int mode )
2262 {
2263 
2264 #ifdef HASCM17A  /* Used only for CM17A commands */
2265     extern unsigned int rf_unit_code[16];
2266     extern unsigned int rf_func_code[7];
2267     extern unsigned int rf_nosw_code[8];
2268     extern int display_rf_xmit(unsigned char, unsigned int, int);
2269     extern int write_cm17a( unsigned int, int, unsigned char );
2270     extern int reset_cm17a( void );
2271     extern void rf_post_delay( void );
2272     extern void rf_farb_delay( void );
2273     extern void rf_farw_delay( void );
2274     extern void rf_flux_delay( long );
2275 
2276     int           precode;
2277     int           bursts, burstgroup, savebursts;
2278     int           byte1, byte2;
2279     unsigned int  rfword, rfonword, noswitch, nsw;
2280     unsigned char rfhcode, rfmode = RF_SLOW;
2281     long          lxdata;
2282     int           flux_delay;
2283 #endif  /* End of HASCM17A block */
2284 
2285     extern int line_sync_mode( void );
2286     extern int send_flag_update ( unsigned int, unsigned char );
2287     extern int send_long_flag_update ( unsigned char, unsigned long, unsigned char );
2288     extern int clear_status_flags ( unsigned char, unsigned int );
2289     extern void send_settimer_msg ( unsigned short, long );
2290     extern int  munlock ( char * );
2291     extern int  lock_for_write ( void );
2292     extern int  parse_ext3_group ( char *, unsigned char *, unsigned char * );
2293     extern int  set_counter ( int, unsigned short, unsigned char );
2294     extern void send_setcounter_msg ( int, unsigned short, unsigned char );
2295     extern void engine_update_flags ( unsigned char, unsigned long, unsigned char );
2296     extern void engine_internal_vdata ( unsigned char, unsigned char, unsigned char, unsigned char );
2297     extern int  set_globsec_flags ( unsigned char );
2298     extern char *datstrf ( void );
2299     extern char *display_armed_status ( void );
2300     extern int  read_x10state_file ( void );
2301     int         get_armed_parms ( int, char **, unsigned char *, unsigned char );
2302     extern int    line_no;
2303     int           j, k, icmd, nargs, radix, subcode, subsubcode;
2304     unsigned int  mask, aflags, bitmap, sendmap;
2305     unsigned long flags;
2306     char          *label, *sp;
2307     char          hc;
2308     char          errmsg[128];
2309     int           dimval, dimcode, fullbright, timeout;
2310     long          delay, mindelay, maxdelay;
2311     double        pause;
2312     unsigned char cmdbuf[16], cmdcode, hcode, switchv, switchcode, testbuf[128];
2313     unsigned char vtype;
2314     int           minargs, maxargs;
2315     int           xdata = 0;
2316     int           ramp;
2317     unsigned char group, subgroup;
2318     static char   reorder[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
2319     int           syncmode;
2320     int           timer_no;
2321     long          timer_countdown, min_countdown, max_countdown;
2322     int           value;
2323     long          longvalue;
2324     int           counter_no;
2325     unsigned short counter_value;
2326     char          writefilename[PATH_LEN + 1];
2327     unsigned char sflags;
2328 
2329 int flaglist2banks ( unsigned long *flagbank, int banks, char *str );
2330 unsigned long flagbank[NUM_FLAG_BANKS];
2331 
2332     timeout = 10;
2333     line_no = 0;
2334     flags = 0;
2335     special_func = 0;
2336     syncmode = line_sync_mode();
2337 
2338     clear_error_message();
2339 
2340     /* Handle a few special cases */
2341 
2342     if ( strcmp(argv[0], "function") == 0 ) {
2343        /* Special case: "function" command */
2344        if ( argc < 2 ) {
2345           store_error_message("Command 'function': Too few parameters");
2346           return 1;
2347        }
2348        argv++;
2349        argc--;
2350        flags |= F_NOA;
2351     }
2352     else if ( strcmp(argv[0], "turn") == 0 ) {
2353        /* Special case: "turn" command */
2354        if ( argc < 3 ) {
2355           store_error_message("Command 'turn': Too few parameters");
2356           return 1;
2357        }
2358        argv++;
2359        argc--;
2360        flags |= F_TRN;
2361        /* Swap first two parameters */
2362        sp = argv[0];
2363        argv[0] = argv[1];
2364        argv[1] = sp;
2365     }
2366     else if ( strcmp(argv[0], "temp_req") == 0 ||
2367               strcmp(argv[0], "rcs_req")  == 0 ) {
2368        if ( argc < 2 ) {
2369 	  sprintf(errmsg, "Command '%s': Too few parameters", argv[0]);
2370           store_error_message(errmsg);
2371           return 1;
2372        }
2373        special_func = (mode == CMD_VERIFY) ? 0 : 1;
2374        argv++;
2375        argc--;
2376        flags |= F_SPF;
2377     }
2378     nargs = argc - 1;
2379 
2380     /* Identify the command */
2381     for ( icmd = 0; icmd < nx10cmds; icmd++ ) {
2382        if ( !strcmp(argv[0], x10command[icmd].label) )
2383           break;
2384     }
2385     if ( icmd == nx10cmds ) {
2386        sprintf(errmsg, "Invalid command '%s'", argv[0]);
2387        store_error_message(errmsg);
2388        return 1;
2389     }
2390 
2391     label    = x10command[icmd].label;
2392     minargs  = x10command[icmd].minargs;
2393     maxargs  = x10command[icmd].maxargs;
2394     flags   |= x10command[icmd].flags;
2395     cmdcode  = x10command[icmd].code;
2396     subcode  = x10command[icmd].subcode;
2397     subsubcode = x10command[icmd].subsubcode;
2398     radix    = (flags & F_ARB) ?  16 : 10;
2399 
2400     if ( flags & F_INT && mode != CMD_INT ) {
2401        sprintf(errmsg, "Invalid use of internal engine precommand '%s'", label);
2402        store_error_message(errmsg);
2403        return 1;
2404     }
2405 
2406     if ( (configp->device_type & DEV_DUMMY) && !(flags & (F_INT | F_DUM)) ) {
2407        sprintf(errmsg, "Command '%s' is not valid for TTY dummy", label);
2408        store_error_message(errmsg);
2409        return 1;
2410     }
2411 
2412 #ifndef HASCM17A
2413     /* CM17A support not included */
2414     if ( flags & F_FIR ) {
2415        sprintf(errmsg,
2416 	  "Command '%s': Heyu was compiled without CM17A support.", label);
2417        store_error_message(errmsg);
2418        return 1;
2419     }
2420 #else
2421     if ( flags & F_FIR && mode != CMD_VERIFY && !is_modem_support() ) {
2422        sprintf(errmsg,
2423        "Command '%s': CM17A operation is unsupported by serial port hardware.", label);
2424        store_error_message(errmsg);
2425        return 1;
2426     }
2427 #endif /* End ifndef */
2428 
2429     /* Help menu items are invalid in scenes */
2430     if ( flags & F_HLP && mode == CMD_VERIFY ) {
2431        sprintf(errmsg, "Command '%s' is not valid in scenes/usersyns", label);
2432        store_error_message(errmsg);
2433        return 1;
2434     }
2435 
2436     /* If this is just a help menu item, return */
2437     if ( flags & F_HLP )
2438        return 0;
2439 
2440     /* Only commands with command code <= 6  or code == 26 or 34-7 are valid with "turn" */
2441     if ( flags & F_TRN && !(cmdcode <= 6 || cmdcode == 26 || (cmdcode == 34 && subcode <= 7)) ) {
2442        sprintf(errmsg,
2443          "Command '%s' cannot be used with the 'turn' command", label);
2444        store_error_message(errmsg);
2445        return 1;
2446     }
2447 
2448     if ( nargs < minargs ) {
2449        sprintf(errmsg, "Command '%s': Too few parameters", label);
2450        store_error_message(errmsg);
2451        return 1;
2452     }
2453     if ( maxargs != -1 && nargs > maxargs ) {
2454        sprintf(errmsg, "Command '%s': Too many parameters", label);
2455        store_error_message(errmsg);
2456        return 1;
2457     }
2458 
2459     /* Handle a few special cases */
2460 
2461     if ( flags & F_ARB ) {
2462        /* If arbitrary extended code, get subcode and adjust argument count */
2463        if ( strchr(argv[1], '$') && mode == CMD_VERIFY) {
2464           /* Fake a dummy parameter for scene verification */
2465           subcode = 0x30;
2466           sp = " ";
2467        }
2468        else {
2469           subcode = (int)strtol(argv[1], &sp, radix);
2470        }
2471 
2472        if ( !strchr(" \t\n", *sp) || subcode < 0 || subcode > 0xff ) {
2473           sprintf(errmsg,
2474              "Command '%s': Extended function code '%s' outside range 00-0xff",
2475                  label, argv[1]);
2476           store_error_message(errmsg);
2477           return 1;
2478        }
2479        argv++;
2480        argc--;
2481        nargs--;
2482     }
2483 
2484 
2485     /* Default HC|Unit */
2486     hc = '_';
2487     bitmap = 0;
2488     aflags = A_VALID;
2489 
2490     if ( !(flags & F_NUM) && nargs > 0 ) {
2491        /* parse it as an alias or HC|Units token. */
2492        aflags = parse_addr(argv[1], &hc, &bitmap);
2493     }
2494 
2495     /* Make sure a positional  parameter didn't slip through */
2496     if ( aflags & A_DUMMY && mode == CMD_RUN ) {
2497        sprintf(errmsg,
2498           "Command '%s': Unresolved positional parameter '%s'",
2499              label, argv[1]);
2500        store_error_message(errmsg);
2501        return 1;
2502     }
2503 
2504     if ( !(aflags & A_VALID) ) {
2505        sprintf(errmsg, "Command '%s': ", label);
2506        add_error_prefix(errmsg);
2507        return 1;
2508     }
2509 
2510     if ( !(aflags & A_HCODE) && !(flags & F_NUM) && minargs > 0 )  {
2511        sprintf(errmsg, "Command '%s': Housecode required", label);
2512        store_error_message(errmsg);
2513        return 1;
2514     }
2515 
2516     /* Heyu shouldn't wait for (x)status_ack when executed from a script */
2517     if ( heyu_parent == D_ENGINE )
2518        flags &= ~F_STA;
2519 
2520     switchv = cmdcode;
2521 
2522     switch ( switchv ) {
2523 
2524        case 21 : /* Function only - we should never get here */
2525        case 27 : /* Turn command - we should never get here */
2526        case 28 : /* Temp_Req - we should never get here */
2527        case 99 : /* Legacy commands - we should never get here */
2528           fprintf(stderr, "Internal error - direct_command() - case %d\n", switchv);
2529           return 1;
2530 
2531        case  8 :  /* Hail */
2532        case  9 :  /* Hail Ack */
2533           if ( !(aflags & A_HCODE) ) {
2534              hc = configp->housecode;
2535           }
2536        case  0 :  /* All Units Off  */
2537        case  1 :  /* All Lights On  */
2538        case  6 :  /* All Lights Off */
2539        case 12 :  /* Data Transfer */
2540           if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO ) {
2541              sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
2542              store_error_message(errmsg);
2543              bitmap = 0;
2544           }
2545           if ( bitmap == 0 && (aflags & A_PLUS || configp->force_addr == YES) )
2546              bitmap = 1;
2547 
2548        case  2 :  /* On */
2549        case  3 :  /* Off */
2550        case 13 :  /* Status On */
2551        case 14 :  /* Status Off */
2552           if ( aflags & A_MINUS || flags & F_NOA )
2553              bitmap = 0;
2554           else if ( bitmap == 0 && !(flags & F_ALL) ) {
2555              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2556              store_error_message(errmsg);
2557              return 1;
2558           }
2559 
2560           if ( mode == CMD_VERIFY )
2561              break;
2562 
2563           hcode = hc2code(hc);
2564 
2565           cmdbuf[0] = 0x06;
2566           cmdbuf[1] = (hcode << 4) | cmdcode;
2567 
2568 
2569           if ( send_address(hcode, bitmap, syncmode) != 0 ||
2570                send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2571              return 1;
2572 
2573           break;
2574 
2575        case 48 : /* Status Ack emulation (experimental) */
2576           if ( aflags & A_MINUS || bitmap == 0 ) {
2577              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2578              store_error_message(errmsg);
2579              return 1;
2580           }
2581           else if ( aflags & A_MULT ) {
2582              sprintf(errmsg, "Command '%s': Only a single unitcode is allowed", label);
2583              store_error_message(errmsg);
2584              return 1;
2585           }
2586 
2587           if ( mode == CMD_VERIFY )
2588              break;
2589 
2590           if ( read_x10state_file() != 0 ) {
2591              sprintf(errmsg, "Command '%s': Unable to read state file", label);
2592              store_error_message(errmsg);
2593              return 1;
2594           }
2595 
2596           hcode = hc2code(hc);
2597 
2598           cmdcode = (x10state[hcode].state[OnState] & bitmap) ? 13 : 14; /* StatusOn : StatusOff */
2599 
2600           cmdbuf[0] = 0x06;
2601           cmdbuf[1] = (hcode << 4) | cmdcode;
2602 
2603           if ( send_address(hcode, bitmap, syncmode) != 0 ||
2604                send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2605              return 1;
2606 
2607           break;
2608 
2609        case  4 :  /* Dim */
2610        case  5 :  /* Bright */
2611           if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
2612              /* Fake a dim value for a dummy parameter */
2613              dimval = 10;
2614              sp = " ";
2615           }
2616           else {
2617              dimval = (int)strtol(argv[2], &sp, 10);
2618           }
2619 
2620           if ( !strchr(" \t\n", *sp) ) {
2621 	     sprintf(errmsg,
2622 		"Command '%s': Invalid Dim/Bright parameter '%s'", label, argv[2]);
2623              store_error_message(errmsg);
2624 	     return 1;
2625 	  }
2626 	  else if ( configp->restrict_dims == YES && (dimval < 1 || dimval > 22) ) {
2627              sprintf(errmsg,
2628                 "Command '%s': Dim/Bright value outside range 1-22", label);
2629              store_error_message(errmsg);
2630 	     return 1;
2631 	  }
2632 	  else if ( configp->restrict_dims == NO && (dimval < 0 || dimval > 31) ) {
2633              sprintf(errmsg,
2634                 "Command '%s': Dim/Bright value outside range 0-31", label);
2635              store_error_message(errmsg);
2636              return 1;
2637           }
2638 
2639           if ( aflags & A_MINUS || flags & F_NOA )
2640              bitmap = 0;
2641           else if ( bitmap == 0 ) {
2642              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2643              store_error_message(errmsg);
2644              return 1;
2645           }
2646 
2647           if ( mode == CMD_VERIFY )
2648              break;
2649 
2650           hcode = hc2code(hc);
2651 
2652 	  fullbright = configp->full_bright;
2653 
2654           syncmode = subcode ? RISE_SYNC : syncmode ;
2655 
2656 	  /* Kluge for housecode D to circumvent 0x5A checksum problem */
2657 	  /* when sending dim Dx 22. Set dim value to 23 instead.      */
2658 	  if ( hcode == hc2code('D') && configp->fix_5a == YES ) {
2659 	     fullbright = 23;
2660 	     dimval = (dimval == 22) ? 23 : dimval;
2661 	  }
2662 
2663           /* Address the device(s) */
2664           if ( send_address(hcode, bitmap, syncmode) != 0 ) {
2665              return 1;
2666           }
2667 
2668           /* If On is required (e.g., for WS467-1) send it */
2669           if ( flags & F_ONN ) {
2670              cmdbuf[0] = 0x06;
2671              cmdbuf[1] = (hcode << 4) | 0x02;
2672              if ( send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2673                 return 1;
2674           }
2675 
2676           /* If "brighten before dimming" flag is set, emulate a CM11A macro */
2677           /* by first brightening to full brightness.                        */
2678           if ( flags & F_BRI ) {
2679              cmdbuf[0] = (fullbright << 3) | 0x06;
2680              cmdbuf[1] = (hcode << 4) | 5;
2681 
2682              cmdbuf[2] = (dimval << 3) | 0x06;
2683              cmdbuf[3] = (hcode << 4) | cmdcode;
2684 
2685              if ( send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 ||
2686                   send_command(cmdbuf + 2, 2, (flags & F_STA), syncmode) != 0 ) {
2687                 return 1;
2688              }
2689           }
2690           else {
2691              cmdbuf[0] = (dimval << 3) | 0x06;
2692              cmdbuf[1] = (hcode << 4) | cmdcode;
2693 
2694              if ( send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2695                 return 1;
2696           }
2697 
2698           break;
2699 
2700        case 10 :  /* Old style preset 1 or preset_level 1 */
2701        case 11 :  /* Old style preset 2 or preset_level 2 */
2702           if ( aflags & A_MINUS || flags & F_NOA )
2703              bitmap = 0;
2704           else if ( bitmap == 0 ) {
2705              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2706              store_error_message(errmsg);
2707              return 1;
2708           }
2709 
2710           /* (nargs = 2 for preset; nargs = 1 for preset_level) */
2711 
2712           if ( strchr(argv[nargs], '$') && mode == CMD_VERIFY ) {
2713              /* Fake a dim dummy parameter for scene verification */
2714              dimval = 10;
2715              sp = " ";
2716           }
2717           else
2718              dimval = (int)strtol(argv[nargs], &sp, 10);
2719 
2720           if ( !strchr(" \t\n", *sp) || dimval < 1 || dimval > 32 ) {
2721              sprintf(errmsg, "Command '%s': dim level outside range 1-32", label);
2722              store_error_message(errmsg);
2723              return 1;
2724           }
2725 
2726           if ( mode == CMD_VERIFY )
2727              break;
2728 
2729           if ( (dimcode = dimval) > 16 ) {
2730              cmdcode = 11;
2731              dimcode -= 16;
2732           }
2733           dimcode = rev_low_nybble(dimcode - 1);
2734 
2735           hcode = hc2code(hc);
2736 
2737           if ( flags & F_MAC && dimcode != hcode ) {
2738              sprintf(errmsg, "Command '%s': Preset level %d not supported for housecode %c",
2739                 label, dimval, hc);
2740              store_error_message(errmsg);
2741              return 1;
2742           }
2743 
2744           cmdbuf[0] = 0x06;
2745           cmdbuf[1] = (dimcode << 4) | cmdcode;
2746 
2747           if ( send_address(hcode, bitmap, syncmode) != 0 ||
2748                send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2749              return 1;
2750 
2751           break;
2752 
2753        case 15 :  /* Status Req */
2754           sendmap = bitmap;
2755           if ( aflags & A_MINUS || flags & F_NOA ) {
2756              bitmap = 1;
2757              sendmap = 0;
2758           }
2759           else if ( bitmap == 0 ) {
2760              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
2761              store_error_message(errmsg);
2762              return 1;
2763           }
2764 
2765           if ( mode == CMD_VERIFY )
2766              break;
2767 
2768           hcode = hc2code(hc);
2769 
2770           cmdbuf[0] = 0x06;
2771           cmdbuf[1] = (hcode << 4) | cmdcode;
2772 
2773           if ( flags & F_SPF )
2774              flags &= ~F_STA;
2775 
2776           /* Send each address/command pair individually to allow */
2777           /* associating a response with a particular unit code   */
2778           mask = 1;
2779           for ( k = 0; k < 16; k++ )  {
2780              if ( bitmap & mask ) {
2781                 if ( send_address(hcode, sendmap & mask, syncmode) != 0 ||
2782                      send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
2783                    return 1;
2784              }
2785              mask = mask << 1;
2786           }
2787           break;
2788 
2789        case 20 : /* Send address bytes only */
2790           /* Start over again with the address tokens */
2791           for ( k = 1; k < argc; k++ ) {
2792              aflags = parse_addr(argv[k], &hc, &bitmap);
2793              if ( !(aflags & A_VALID) ) {
2794                 sprintf(errmsg, "Command '%s': ", label);
2795                 add_error_prefix(errmsg);
2796                 return 1;
2797              }
2798              else if ( !(aflags & A_HCODE) || bitmap == 0 ) {
2799                 sprintf(errmsg,
2800                    "Command '%s': invalid HU or Alias '%s'", label, argv[k]);
2801                 store_error_message(errmsg);
2802                 return 1;
2803              }
2804 
2805              if ( mode == CMD_VERIFY )
2806                 continue;
2807 
2808              hcode = hc2code(hc);
2809 
2810              if ( send_address(hcode, bitmap, syncmode) != 0 )
2811                 return 1;
2812 
2813              /* A delay between addresses may be needed for */
2814              /* programming some SwitchLinc devices.  (The  */
2815              /* TempLinc programs OK without it.)           */
2816              millisleep(60);
2817           }
2818           break;
2819 
2820        case 29 : /* Send bytes directly to cm11a (highly experimental) */
2821        case 31 : /* Send bytes directly to cm11a for transmission (highly experimental) */
2822           /* First check that each arg is a byte */
2823           for ( k = 1; k < argc; k++ ) {
2824              if ( strchr(argv[k], '$') && mode == CMD_VERIFY ) {
2825                 /* Fake a dummy value for scene verification */
2826                 xdata = 10;
2827                 sp = " ";
2828              }
2829              else {
2830                 xdata = (int)strtol(argv[k], &sp, 16);
2831              }
2832              if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 0xff ) {
2833                 sprintf(errmsg,
2834                    "Command '%s': Data '%s' outside range 0-FF", label, argv[k]);
2835                 store_error_message(errmsg);
2836                 return 1;
2837              }
2838           }
2839 
2840           if ( mode == CMD_VERIFY )
2841              break;
2842 
2843           for ( k = 1; k < argc; k++ ) {
2844              xdata = (int)strtol(argv[k], NULL, 16);
2845              testbuf[k-1] = xdata;
2846           }
2847 
2848 	  if ( switchv == 29 )
2849              (void) xwrite(tty, (char *) testbuf, argc - 1);
2850           else
2851 	     send_command(testbuf, argc - 1, 0, syncmode);
2852           break;
2853 
2854 
2855        case 22 : /* Send arbitrary series of hex bytes as addresses */
2856           /* First check that each byte is in range 00-FF */
2857           for ( k = 1; k < argc; k++ ) {
2858              if ( strchr(argv[k], '$') && mode == CMD_VERIFY ) {
2859                 /* Fake a dummy value for scene verification */
2860                 xdata = 10;
2861                 sp = " ";
2862              }
2863              else {
2864                 xdata = (int)strtol(argv[k], &sp, 16);
2865              }
2866              if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 0xff ) {
2867                 sprintf(errmsg,
2868                    "Command '%s': Data '%s' outside range 0-FF", label, argv[k]);
2869                 store_error_message(errmsg);
2870                 return 1;
2871              }
2872           }
2873 
2874           if ( mode == CMD_VERIFY )
2875              break;
2876 
2877           for ( k = 1; k < argc; k++ ) {
2878              xdata = (int)strtol(argv[k], NULL, 16);
2879              if ( send_address(xdata >> 4, (1 << (xdata & 0x0f)), syncmode) != 0 )
2880                 return 1;
2881           }
2882           break;
2883 
2884        case 23 : /* Send (quoted) text message */
2885           /* Format:                                                          */
2886           /* Send each character as two address bytes - high nybble first     */
2887           if ( !(aflags & A_DUMMY) && bitmap != 0 ) {
2888              sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
2889              store_error_message(errmsg);
2890              bitmap = 0;
2891           }
2892           sp = argv[2];
2893 
2894           if ( mode == CMD_VERIFY )
2895              break;
2896 
2897           hcode = hc2code(hc);
2898 
2899           printf("Sending %d characters as Hcode|hi Hcode|low bytes:\n",
2900                 (int)strlen(sp));
2901 
2902           while ( *sp != '\0' ) {
2903              if ( send_address(hcode, (1 << (*sp >> 4)), syncmode) != 0 ||
2904                   send_address(hcode, (1 << (*sp & 0x0f)), syncmode) != 0 )
2905                 return 1;
2906              printf("%c", *sp);
2907              fflush(stdout);
2908              sp++;
2909           }
2910           printf("\n");
2911           break;
2912 
2913        case 24 :  /* Send All_Units_Off to all Housecodes */
2914           if ( mode == CMD_VERIFY )
2915              break;
2916 
2917           bitmap = (configp->force_addr == YES) ?  1 : 0;
2918           cmdbuf[0] = 0x06;
2919 	  for ( j = 0; j < 16; j++ ) {
2920 	     hcode = reorder[j];
2921 	     cmdbuf[1] = (hcode << 4);
2922 	     if ( send_address(hcode, bitmap, syncmode) != 0 ||
2923                   send_command(cmdbuf, 2, 0, syncmode) != 0 )
2924                 return 1;
2925              millisleep(60);
2926 	  }
2927 	  break;
2928 
2929        case 25 : /* Pause (for use in scenes) */
2930           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
2931              /* Fake a dummy parameter for scene verification */
2932              pause = 1.0;
2933              sp = " ";
2934           }
2935           else {
2936              pause = strtod(argv[1], &sp);
2937           }
2938           if ( !strchr(" \t\n", *sp) || pause < 0.0 ) {
2939              sprintf(errmsg, "Command '%s': invalid Pause value '%s'", label, argv[1]);
2940              store_error_message(errmsg);
2941              return 1;
2942           }
2943 
2944           if ( mode == CMD_VERIFY )
2945              break;
2946 
2947           if ( verbose )
2948              printf("pause %.3f seconds\n", pause);
2949           millisleep((int)(1000. * pause));
2950 
2951           break;
2952 
2953        case 41 : /* Sleep (for use in scenes) */
2954           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
2955              /* Fake a dummy parameter for scene verification */
2956              pause = 1.0;
2957              sp = " ";
2958           }
2959           else {
2960              pause = strtod(argv[1], &sp);
2961           }
2962           if ( !strchr(" \t\n", *sp) || pause < 0.0 ) {
2963              sprintf(errmsg, "Command '%s': invalid sleep value '%s'", label, argv[1]);
2964              store_error_message(errmsg);
2965              return 1;
2966           }
2967 
2968           if ( mode == CMD_VERIFY )
2969              break;
2970 
2971           sprintf(writefilename, "%s%s", WRITEFILE, configp->suffix);
2972           munlock(writefilename);
2973 
2974           millisleep((int)(1000. * pause));
2975 
2976           if ( lock_for_write() != 0 ) {
2977              fprintf(stderr, "Unable to re-lock after sleep command\n");
2978              exit(1);
2979           }
2980 
2981           break;
2982 
2983        case 45 : /* Random delay in minutes */
2984           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
2985              /* Fake a dummy parameter for scene verification */
2986              delay = 1;
2987              sp = "";
2988           }
2989           else {
2990              delay = strtol(argv[1], &sp, 10);
2991           }
2992 
2993           if ( !strchr(" \t\n", *sp) || delay < 0 || delay > 240 ) {
2994              sprintf(errmsg, "Command '%s': Invalid delay time '%s'", label, argv[1]);
2995              store_error_message(errmsg);
2996              return 1;
2997           }
2998 
2999           if ( nargs == 1 ) {
3000              maxdelay = delay;
3001              mindelay = 0;
3002              sp = "";
3003           }
3004           else {
3005              mindelay = delay;
3006              if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3007                 /* Fake a dummy parameter for scene verification */
3008                 maxdelay = 5;
3009                 sp = " ";
3010              }
3011              else {
3012                 maxdelay = strtol(argv[2], &sp, 10);
3013              }
3014           }
3015 
3016           if ( !strchr(" \t\n", *sp) || maxdelay < 0 || maxdelay > 240 ) {
3017              sprintf(errmsg, "Command '%s': Invalid delay time '%s'", label, argv[1]);
3018              store_error_message(errmsg);
3019              return 1;
3020           }
3021 
3022           if ( maxdelay < mindelay ) {
3023              delay = maxdelay;
3024              maxdelay = mindelay;
3025              mindelay = delay;
3026           }
3027 
3028           if ( mode == CMD_VERIFY )
3029              break;
3030 
3031           delay = (double)(maxdelay - mindelay) * random_float() + 0.5;
3032           delay += mindelay;
3033 
3034           if ( !i_am_relay && !i_am_aux ) {
3035              printf("%s Random delay %ld minutes\n", datstrf(), delay);
3036              fflush(stdout);
3037           }
3038 
3039           sprintf(writefilename, "%s%s", WRITEFILE, configp->suffix);
3040           munlock(writefilename);
3041 
3042           millisleep(60000L * delay);
3043 
3044           if ( lock_for_write() != 0 ) {
3045              fprintf(stderr, "Unable to re-lock after rdelay command\n");
3046              exit(1);
3047           }
3048 
3049           break;
3050 
3051        case 32 : /* Delay in minutes */
3052           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3053              /* Fake a dummy parameter for scene verification */
3054              delay = 1;
3055              sp = " ";
3056           }
3057           else {
3058              delay = strtol(argv[1], &sp, 10);
3059           }
3060           if ( !strchr(" \t\n", *sp) || delay < 0 || delay > 240 ) {
3061              sprintf(errmsg, "Command '%s': Invalid delay time '%s'", label, argv[1]);
3062              store_error_message(errmsg);
3063              return 1;
3064           }
3065 
3066           if ( mode == CMD_VERIFY )
3067              break;
3068 
3069           if ( verbose ) {
3070              printf("%s Delay %ld minutes\n", datstrf(), delay);
3071           }
3072 
3073           sprintf(writefilename, "%s%s", WRITEFILE, configp->suffix);
3074           munlock(writefilename);
3075 
3076           millisleep(60000L * delay);
3077 
3078           if ( lock_for_write() != 0 ) {
3079              fprintf(stderr, "Unable to re-lock after delay command\n");
3080              exit(1);
3081           }
3082 
3083           break;
3084 
3085        case 33 :  /* Wait for next tick of system clock */
3086 	  if ( mode == CMD_VERIFY )
3087 	     break;
3088 
3089 	  wait_next_tick();
3090 
3091 	  break;
3092 
3093        case 26 :  /* All Units On */
3094           if ( bitmap != 0 && !(aflags & A_MINUS) ) {
3095              sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3096              store_error_message(errmsg);
3097           }
3098           bitmap = 0xFFFF;
3099 
3100           if ( mode == CMD_VERIFY )
3101              break;
3102 
3103           hcode = hc2code(hc);
3104 
3105           cmdbuf[0] = 0x06;
3106           cmdbuf[1] = (hcode << 4) | 2 ;
3107 
3108           if ( send_address(hcode, bitmap, syncmode) != 0 ||
3109                send_command(cmdbuf, 2, (flags & F_STA), syncmode) != 0 )
3110              return 1;
3111 
3112           break;
3113 
3114        case 30 :  /* Show X10 state */
3115           if ( mode == CMD_VERIFY )
3116              break;
3117 
3118           hcode = hc2code(hc);
3119 
3120           cmdbuf[0] = 0x7b;
3121           cmdbuf[1] = 0;
3122           cmdbuf[2] = hcode << 4;
3123 
3124           xwrite(tty, cmdbuf, 3);
3125           millisleep(100);
3126 
3127           break;
3128 
3129        case 35 : /* Set or Clear flags */
3130 
3131           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3132              /* Fake a dummy parameter for scene verification */
3133              sp = "1";
3134           }
3135           else {
3136              sp = argv[1];
3137           }
3138 
3139           if ( flaglist2banks(flagbank, NUM_FLAG_BANKS, sp) ) {
3140              return 1;
3141           }
3142 
3143           if ( mode == CMD_VERIFY )
3144              break;
3145 
3146           if ( flags & F_INT ) {
3147              for ( j = 0; j < NUM_FLAG_BANKS; j++ ) {
3148                 if ( flagbank[j] )
3149                    engine_update_flags(j, flagbank[j], subcode);
3150              }
3151              break;
3152           }
3153 
3154           if ( verbose ) {
3155              if ( subcode == SETFLAG )
3156                 printf("Set flags %s\n", argv[1]);
3157              else
3158                 printf("Clear flags %s\n", argv[1]);
3159           }
3160 
3161           for ( j = 0; j < NUM_FLAG_BANKS; j++ ) {
3162              if ( flagbank[j] )
3163                 send_long_flag_update(j, flagbank[j], subcode);
3164           }
3165 
3166           break;
3167 #if 0
3168        case 35 : /* Set or Clear flags */
3169           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3170              /* Fake a dummy parameter for scene verification */
3171              longmap = 1;
3172           }
3173           else if ( (longmap = flags2longmap(argv[1])) == 0 ) {
3174 /*
3175              sprintf(errmsg, "Command '%s': invalid flags specified '%s'", label, argv[1]);
3176              store_error_message(errmsg);
3177 */
3178              return 1;
3179           }
3180 
3181           if ( mode == CMD_VERIFY )
3182              break;
3183 
3184           if ( flags & F_INT ) {
3185              engine_update_flags(0, longmap, subcode);
3186              break;
3187           }
3188 
3189           if ( verbose ) {
3190              if ( subcode == SETFLAG )
3191                 printf("Set flags %s\n", argv[1]);
3192              else
3193                 printf("Clear flags %s\n", argv[1]);
3194           }
3195 
3196           send_long_flag_update(0, longmap, subcode);
3197 
3198           break;
3199 #endif
3200 
3201        case 36 : /* Clear status flags */
3202           if ( mode == CMD_VERIFY )
3203              break;
3204 
3205           if ( aflags & A_MINUS || bitmap == 0 )
3206              bitmap = 0xffff;
3207 
3208           if ( flags & F_INT ) {
3209              clear_statusflags(hc2code(hc), bitmap);
3210              break;
3211           }
3212 
3213           send_clear_statusflags(hc2code(hc), bitmap);
3214 
3215           break;
3216 
3217        case 37 : /* Virtual Data */
3218 	  if ( aflags & A_PLUS || aflags & A_MINUS ) {
3219 	     sprintf(errmsg,
3220 	       "Command '%s': '+/-' prefixes invalid for vdata[x] command.", label);
3221 	     store_error_message(errmsg);
3222 	     return 1;
3223 	  }
3224 	  if ( flags & F_NOA ) {
3225              sprintf(errmsg, "'function' is invalid for vdata[x] command.");
3226              store_error_message(errmsg);
3227              return 1;
3228 	  }
3229           if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3230              /* Fake a dim value for a dummy parameter */
3231              dimval = 10;
3232              sp = " ";
3233           }
3234           else {
3235              strlower(argv[2]);
3236              if ( strncmp(argv[2], "0x", 2) == 0 )
3237                 dimval = (int)strtol(argv[2], &sp, 16);
3238              else
3239                 dimval = (int)strtol(argv[2], &sp, 10);
3240           }
3241 
3242           if ( !strchr(" \t\n", *sp) || dimval < 0 || dimval > 0xff ) {
3243 	     sprintf(errmsg,
3244 		"Command '%s': Invalid virtual data parameter '%s'", label, argv[2]);
3245              store_error_message(errmsg);
3246 	     return 1;
3247 	  }
3248 
3249           if ( mode == CMD_VERIFY )
3250              break;
3251 
3252           hcode = hc2code(hc);
3253           vtype = (subcode == 1) ? RF_VDATAM : RF_STD;
3254 
3255           if ( flags & F_INT ) {
3256              mask = 1;
3257              for ( j = 0; j < 16; j++ ) {
3258                 if ( bitmap & mask ) {
3259                    engine_internal_vdata(hcode, j, dimval, vtype);
3260                 }
3261                 mask = mask << 1;
3262              }
3263              if ( i_am_state )
3264                 write_x10state_file();
3265 
3266              break;
3267 
3268           }
3269 
3270           mask = 1;
3271           for ( j = 0; j < 16; j++ ) {
3272              if ( bitmap & mask ) {
3273                 send_virtual_data((hcode << 4) | j, dimval, vtype, 0, 0, 0, 0);
3274              }
3275              mask = mask << 1;
3276           }
3277 
3278           break;
3279 
3280        case 38 : /* Set user countdown timer */
3281           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3282              /* Fake a dummy parameter for scene verification */
3283              timer_no = 1;
3284           }
3285           else {
3286              timer_no = (int)strtol(argv[1], &sp, 10);
3287              if ( !strchr(" \t\n\r", *sp) ) {
3288                 sprintf(errmsg,
3289 		 "Command '%s': Invalid timer number '%s'", label, argv[1]);
3290                 store_error_message(errmsg);
3291                 return 1;
3292              }
3293           }
3294 
3295           if ( timer_no < 1 || timer_no > NUM_USER_TIMERS ) {
3296              sprintf(errmsg,
3297                 "Command '%s': Timer number outside range 1-%d", label, NUM_USER_TIMERS);
3298              store_error_message(errmsg);
3299              return 1;
3300           }
3301 
3302 
3303           if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3304              /* Fake a dummy parameter for scene verification */
3305              timer_countdown = 10;
3306           }
3307           else {
3308              timer_countdown = parse_hhmmss(argv[2], 3);
3309              if ( timer_countdown < 0 ) {
3310                 sprintf(errmsg,
3311 		 "Command '%s': Invalid timer countdown '%s'", label, argv[2]);
3312                 store_error_message(errmsg);
3313                 return 1;
3314              }
3315           }
3316 
3317           if ( mode == CMD_VERIFY )
3318              break;
3319 
3320           if ( flags & F_INT ) {
3321              set_user_timer_countdown(timer_no, timer_countdown);
3322              display_settimer_setting(timer_no, timer_countdown);
3323              break;
3324           }
3325 
3326           send_settimer_msg(timer_no, timer_countdown);
3327 
3328           break;
3329 
3330        case 46 : /* Set user random countdown timer */
3331           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3332              /* Fake a dummy parameter for scene verification */
3333              timer_no = 1;
3334           }
3335           else {
3336              timer_no = (int)strtol(argv[1], &sp, 10);
3337              if ( !strchr(" \t\n\r", *sp) ) {
3338                 sprintf(errmsg,
3339 		 "Command '%s': Invalid timer number '%s'", label, argv[1]);
3340                 store_error_message(errmsg);
3341                 return 1;
3342              }
3343           }
3344 
3345           if ( timer_no < 1 || timer_no > NUM_USER_TIMERS ) {
3346              sprintf(errmsg,
3347                 "Command '%s': Timer number outside range 1-%d", label, NUM_USER_TIMERS);
3348              store_error_message(errmsg);
3349              return 1;
3350           }
3351 
3352 
3353           if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3354              /* Fake a dummy parameter for scene verification */
3355              timer_countdown = 10;
3356           }
3357           else {
3358              /* Get first time parameter */
3359              timer_countdown = parse_hhmmss(argv[2], 3);
3360              if ( timer_countdown < 0 ) {
3361                 sprintf(errmsg,
3362 		 "Command '%s': Invalid timer countdown '%s'", label, argv[2]);
3363                 store_error_message(errmsg);
3364                 return 1;
3365              }
3366           }
3367 
3368           if ( nargs == 2 ) {
3369              max_countdown = timer_countdown;
3370              min_countdown = 1;
3371           }
3372           else {
3373              min_countdown = timer_countdown;
3374              if ( strchr(argv[3], '$') && mode == CMD_VERIFY ) {
3375                 max_countdown = 5;
3376              }
3377              else {
3378                 max_countdown = parse_hhmmss(argv[3], 3);
3379                 if ( max_countdown < 0 ) {
3380                    sprintf(errmsg,
3381                     "Command '%s': Invalid timer countdown '%s'", label, argv[2]);
3382                    store_error_message(errmsg);
3383                    return 1;
3384                 }
3385              }
3386           }
3387 
3388           if ( max_countdown < min_countdown ) {
3389              longvalue = min_countdown;
3390              min_countdown = max_countdown;
3391              max_countdown = longvalue;
3392           }
3393 
3394           if ( mode == CMD_VERIFY )
3395              break;
3396 
3397           timer_countdown = (double)(max_countdown - min_countdown) * random_float() + 0.5;
3398           timer_countdown += min_countdown;
3399 
3400           if ( flags & F_INT ) {
3401              set_user_timer_countdown(timer_no, timer_countdown);
3402              display_settimer_setting(timer_no, timer_countdown);
3403              break;
3404           }
3405 
3406           send_settimer_msg(timer_no, timer_countdown);
3407 
3408           break;
3409 
3410        case 39 : /* Reset all user timers */
3411 
3412           if ( mode == CMD_VERIFY )
3413              break;
3414 
3415           if ( flags & F_INT ) {
3416              reset_user_timers();
3417              break;
3418           }
3419 
3420           send_x10state_command(ST_CLRTIMERS, 0);
3421 
3422           break;
3423 
3424        case 40 : /* Clear tamper flags */
3425 
3426           if ( mode == CMD_VERIFY )
3427              break;
3428 
3429           send_x10state_command(ST_CLRTAMPER, 0);
3430 
3431           break;
3432 
3433 
3434        case 42 : /* Set counter */
3435           if ( strchr(argv[1], '$') && mode == CMD_VERIFY ) {
3436              /* Fake a dummy parameter for scene verification */
3437              counter_no = 1;
3438           }
3439           else {
3440              counter_no = (int)strtol(argv[1], &sp, 10);
3441              if ( !strchr(" \t\n\r", *sp) ) {
3442                 sprintf(errmsg,
3443 		 "Command '%s': Invalid counter number '%s'", label, argv[1]);
3444                 store_error_message(errmsg);
3445                 return 1;
3446              }
3447           }
3448 
3449           if ( counter_no < 1 || counter_no > (32 * NUM_COUNTER_BANKS) ) {
3450              sprintf(errmsg,
3451                 "Command '%s': Counter number outside range 1-%d", label, 32 * NUM_COUNTER_BANKS);
3452              store_error_message(errmsg);
3453              return 1;
3454           }
3455 
3456           counter_value = 10;
3457           if ( subcode == CNT_SET ) {
3458              if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3459                 /* Fake a dummy parameter for scene verification */
3460                 counter_value = 10;
3461              }
3462              else  {
3463                 value = (int)strtol(argv[2], &sp, 10);
3464                 if ( !strchr(" \t\n\r", *sp) || value < 0 || value > 0xffff ) {
3465                    sprintf(errmsg,
3466                       "Command '%s': Invalid counter value '%s'", label, argv[2]);
3467                    store_error_message(errmsg);
3468                    return 1;
3469                 }
3470                 counter_value = (unsigned short)value;
3471              }
3472           }
3473 
3474           if ( mode == CMD_VERIFY )
3475              break;
3476 
3477           if ( flags & F_INT ) {
3478              return set_counter(counter_no, counter_value, subcode);
3479              break;
3480           }
3481 
3482           send_setcounter_msg(counter_no, counter_value, subcode);
3483 
3484           break;
3485 
3486        case 43 : /* Arm or Disarm system */
3487           if ( subcode == CLRFLAG ) {
3488              sflags = 0;
3489           }
3490           else {
3491              if ( get_armed_parms(argc, argv, &sflags, mode) != 0 )
3492                 return 1;
3493           }
3494 
3495           if ( mode == CMD_VERIFY )
3496              break;
3497 
3498           if ( subcode == SETFLAG ) {
3499              read_x10state_file();
3500              warn_zone_faults(stdout, "");
3501           }
3502 
3503           if ( flags & F_INT ) {
3504              set_globsec_flags(sflags);
3505              write_x10state_file();
3506              printf("%s %s\n", datstrf(), display_armed_status());
3507              fflush(stdout);
3508           }
3509           else {
3510              send_x10state_command(ST_SECFLAGS, (unsigned char)sflags);
3511           }
3512 
3513           break;
3514 
3515        case 47 : /* Null direct command (does nothing) */
3516        case 44 : /* Null internal precommand (does nothing) */
3517             break;
3518 
3519        case  7 :  /* Extended Code commands */
3520           sendmap = (aflags & A_PLUS) ? bitmap : 0 ;
3521           if ( sendmap == 0 && configp->force_addr == YES )
3522              sendmap = 0xFFFF;
3523 
3524           hcode = hc2code(hc);
3525 
3526           if ( flags & F_ARB )
3527              switchcode = 0xff;
3528           else
3529              switchcode = subcode;
3530 
3531           switch ( switchcode ) {
3532              case 0x30 :  /* Include in group[.subgroup] at current level */
3533                 if ( bitmap == 0 ) {
3534                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3535                    store_error_message(errmsg);
3536                    return 1;
3537                 }
3538 
3539                 /* Get group */
3540                 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3541                    /* Fake a dummy parameter for scene verification */
3542                    group = 0;
3543                    subgroup = 0;
3544                 }
3545                 else {
3546                    if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
3547                       sprintf(errmsg, "Command '%s': %s", label, error_message());
3548                       store_error_message(errmsg);
3549                       return 1;
3550                    }
3551                 }
3552 
3553                 xdata = group << 6;
3554 
3555                 if ( subgroup & 0x80u ) {
3556                    xdata |= (subgroup & 0x0fu) | 0x20u;
3557                 }
3558                 break;
3559 
3560              case 0x32 : /* Include in group at level */
3561                 if ( bitmap == 0 ) {
3562                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3563                    store_error_message(errmsg);
3564                    return 1;
3565                 }
3566 
3567                 /* Get group */
3568                 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3569                    /* Fake a dummy parameter for scene verification */
3570                    group = 0;
3571                    subgroup = 0;
3572                 }
3573                 else {
3574                    if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
3575                       sprintf(errmsg, "Command '%s': %s", label, error_message());
3576                       store_error_message(errmsg);
3577                       return 1;
3578                    }
3579                 }
3580                 if ( subgroup & 0x80u ) {
3581                    sprintf(errmsg,
3582                      "Command '%s': Cannot specify level with group.subgroup", label);
3583                    store_error_message(errmsg);
3584                    return 1;
3585                 }
3586 
3587                 /* Get level 0-63 */
3588                 if ( strchr(argv[3], '$') && mode == CMD_VERIFY ) {
3589                    /* Fake a dummy parameter for scene verification */
3590                    dimval = 10;
3591                    sp = " ";
3592                 }
3593                 else {
3594                    dimval = (int)strtol(argv[3], &sp, 10);
3595                 }
3596 
3597                 if ( !strchr(" \t\n", *sp) || dimval < 0 || dimval > 63 ) {
3598                    sprintf(errmsg,
3599                      "Command '%s': Extended Preset level outside range 0-63", label);
3600                    store_error_message(errmsg);
3601                    return 1;
3602                 }
3603                 xdata = (group << 6) | dimval;
3604                 break;
3605 
3606              case 0x31 :  /* Extended Preset Dim */
3607                 if ( bitmap == 0 ) {
3608                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3609                    store_error_message(errmsg);
3610                    return 1;
3611                 }
3612 
3613                 /* Get dim level */
3614                 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3615                    /* Fake a dummy parameter for scene verification */
3616                    xdata = 10;
3617                    sp = " ";
3618                 }
3619                 else {
3620                    xdata = (int)strtol(argv[2], &sp, 10);
3621                 }
3622 
3623                 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 63 ) {
3624                    sprintf(errmsg,
3625                      "Command '%s': Extended Preset level outside range 0-63", label);
3626                    store_error_message(errmsg);
3627                    return 1;
3628                 }
3629 
3630                 /* Get ramp rate */
3631                 ramp = 0;
3632                 if ( nargs == 3 ) {
3633                    if ( strchr(argv[3], '$') && mode == CMD_VERIFY ) {
3634                       /* Fake a dummy parameter for scene verification */
3635                       ramp = 0;
3636                       sp = " ";
3637                    }
3638                    else {
3639                       ramp = (int)strtol(argv[3], &sp, 10);
3640                    }
3641                 }
3642                 if ( !strchr(" \t\n", *sp) || ramp < 0 || ramp > 3 ) {
3643                    sprintf(errmsg,
3644                      "Command '%s': Extended Preset ramp rate outside range 0-3", label);
3645                    store_error_message(errmsg);
3646                    return 1;
3647                 }
3648                 xdata |= ramp << 6;
3649 
3650                 break;
3651 
3652              case 0x35 : /* Remove from extended group(s) */
3653                 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3654                    xdata = 0x01;
3655                 }
3656                 else if ( (xdata = (int)list2linmap(argv[2], 0, 3)) == 0 ) {
3657                    sprintf(errmsg, "Command '%s': Group number %s", label, error_message());
3658                    store_error_message(errmsg);
3659                    return 1;
3660                 }
3661 
3662                 if ( subsubcode == 0 ) {
3663                    if ( bitmap == 0 ) {
3664                       sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3665                       store_error_message(errmsg);
3666                       return 1;
3667                    }
3668                 }
3669                 else {
3670                    xdata |= 0xf0;
3671                    if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) &&
3672                         configp->force_addr == NO) {
3673                       sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3674                       store_error_message(errmsg);
3675                       *errmsg = '\0';
3676                       bitmap = 1;
3677                    }
3678                    else if ( bitmap == 0 ) {
3679                       bitmap = 1;
3680                    }
3681                 }
3682 
3683                 if ( aflags & A_PLUS )
3684                    sendmap = bitmap;
3685 
3686                 break;
3687 
3688              case 0x36 : /* Execute group function */
3689              case 0x3c : /* Extended Group Dim/Bright */
3690                 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) ) {
3691                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3692                    store_error_message(errmsg);
3693                    bitmap = 1;
3694                 }
3695                 else if ( bitmap == 0 )
3696                    bitmap = 1;
3697 
3698                 if ( aflags & A_PLUS )
3699                    sendmap = bitmap;
3700 
3701                 /* Get group */
3702                 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3703                    /* Fake a dummy parameter for scene verification */
3704                    group = 0;
3705                    subgroup = 0;
3706                    sp = " ";
3707                 }
3708                 else {
3709                    if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
3710                       sprintf(errmsg, "Command '%s': %s", label, error_message());
3711                       store_error_message(errmsg);
3712                       return 1;
3713                    }
3714                 }
3715 
3716                 xdata = (group << 6) | (subsubcode << 4);
3717                 if ( subgroup & 0x80u ) {
3718                    xdata |= 0x20u | (subgroup & 0x0f);
3719                 }
3720                 break;
3721 
3722              case 0x3b : /* Set module auto status_ack mode */
3723                 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) ) {
3724                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3725                    store_error_message(errmsg);
3726                    bitmap = 1;
3727                 }
3728                 else if ( bitmap == 0 )
3729                    bitmap = 1;
3730 
3731                 if ( aflags & A_PLUS )
3732                    sendmap = bitmap;
3733 
3734                 /* Get mode (0-3) */
3735                 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3736                    /* Fake a dummy parameter for scene verification */
3737                    xdata = 3;
3738                    sp = " ";
3739                 }
3740                 else {
3741                    xdata = (int)strtol(argv[2], &sp, 10);
3742                 }
3743 
3744                 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 3 ) {
3745                    sprintf(errmsg,
3746                      "Command '%s': Extended Config Mode outside range 0-3", label);
3747                    store_error_message(errmsg);
3748                    return 1;
3749                 }
3750                 break;
3751 
3752              case 0xfe :  /* Extended ON ( = Extended Preset Dim level 63) */
3753                 if ( bitmap == 0 ) {
3754                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3755                    store_error_message(errmsg);
3756                    return 1;
3757                 }
3758                 subcode = 0x31;
3759                 xdata = 63;
3760                 break;
3761 
3762              case 0xfd :  /* Extended OFF ( = Extended Preset Dim level 0) */
3763                 if ( bitmap == 0 ) {
3764                    sprintf(errmsg, "command '%s': No Unitcode specified", label);
3765                    store_error_message(errmsg);
3766                    return 1;
3767                 }
3768                 subcode = 0x31;
3769                 xdata = 0;
3770                 break;
3771 
3772              case 0x33 :  /* Extended All On */
3773              case 0x34 :  /* Extended All Off */
3774              case 0x04 :  /* Shutter Open All */
3775              case 0x0B :  /* Shutter Close All */
3776 
3777                 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) ) {
3778                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3779                    store_error_message(errmsg);
3780                    bitmap = 1;
3781                 }
3782                 else if ( bitmap == 0 )
3783                    bitmap = 1;
3784 
3785                 if ( aflags & A_PLUS )
3786                    sendmap = bitmap;
3787 
3788                 xdata = 0;
3789                 break;
3790 
3791              case 0x37 : /* Extended Status Requests */
3792                 if ( subsubcode == 0 ) {
3793                    /* Extended Output Status Req */
3794                    if ( bitmap == 0 ) {
3795                       sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3796                       store_error_message(errmsg);
3797                       return 1;
3798                    }
3799                    xdata = 0;
3800                 }
3801                 else if ( subsubcode == 1 ) {
3802                    /* Extended Power Up */
3803                    if ( bitmap == 0 ) {
3804                       sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3805                       store_error_message(errmsg);
3806                       return 1;
3807                    }
3808                    xdata = 0x10;
3809                 }
3810                 else  {
3811                    /* Group Status Req */
3812                    /* Get group */
3813                    if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3814                       /* Fake a dummy parameter for scene verification */
3815                       group = 0;
3816                       subgroup = 0;
3817                    }
3818                    else  {
3819                       if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
3820                          sprintf(errmsg, "Command '%s': %s", label, error_message());
3821                          store_error_message(errmsg);
3822                          return 1;
3823                       }
3824                       subsubcode = (subgroup & 0x80u) ? 3 : 2;
3825                    }
3826                    xdata = (group << 6) | (subsubcode << 4) | (subgroup & 0x0fu);
3827                 }
3828                 break;
3829 
3830 
3831              case 0x01 : /* Shutter open, observe limit */
3832              case 0x03 : /* Shutter open, disregard limit */
3833              case 0x02 : /* Shutter set limit */
3834                 if ( bitmap == 0 ) {
3835                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3836                    store_error_message(errmsg);
3837                    return 1;
3838                 }
3839                 /* Get dim level */
3840                 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3841                    /* Fake a dummy parameter for scene verification */
3842                    xdata = 10;
3843                    sp = " ";
3844                 }
3845                 else {
3846                    xdata = (int)strtol(argv[2], &sp, 10);
3847                 }
3848 
3849                 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 25 ) {
3850                    sprintf(errmsg,
3851                      "Command '%s': Shutter level outside range 0-25", label);
3852                    store_error_message(errmsg);
3853                    return 1;
3854                 }
3855                 break;
3856 
3857 
3858              case 0xff :  /* Arbitrary Extended Function */
3859                 if ( bitmap == 0 ) {
3860                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3861                    store_error_message(errmsg);
3862                    return 1;
3863                 }
3864                 if ( strchr(argv[2], '$') && mode == CMD_VERIFY ) {
3865                    /* Fake a dummy parameter for scene verification */
3866                    xdata = 10;
3867                    sp = " ";
3868                 }
3869                 else {
3870                    xdata = (int)strtol(argv[2], &sp, 16);
3871                 }
3872                 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 0xff ) {
3873                    sprintf(errmsg,
3874                       "Command '%s': Data '%s' outside range 0-FF", label, argv[2]);
3875                    store_error_message(errmsg);
3876                    return 1;
3877                 }
3878                 break ;
3879 
3880              default :
3881                 sprintf(errmsg, "Command '%s': Not implemented", label);
3882                 store_error_message(errmsg);
3883                 return 1;
3884           }
3885 
3886           if ( mode == CMD_VERIFY )
3887              break;
3888 
3889           /* Header for extended code */
3890           cmdbuf[0] = 0x07;
3891 
3892           /* House code and extended code function */
3893           cmdbuf[1] = (hcode << 4) | 0x07 ;
3894 
3895           /* Unit code */
3896           /* cmdbuf[2] = unit code is filled in below */
3897 
3898           /* "data" value */
3899           cmdbuf[3] = (unsigned char)(xdata & 0xff);
3900 
3901           /* Extended Type/command */
3902           cmdbuf[4] = (unsigned char)(subcode & 0xff);
3903 
3904 
3905           mask = 1;
3906           for ( j = 0; j < 16; j++ ) {
3907              if ( bitmap & mask ) {
3908                 cmdbuf[2] = j;
3909 
3910 		/* Kluge "fix" for checksum 5A problem.    */
3911 		/* CM11A seems to disregard the dim field  */
3912 		/* in the header byte.                     */
3913 		if ( checksum(cmdbuf, 5) == 0x5A && configp->fix_5a == YES )
3914 		   cmdbuf[0] = 0x0F;
3915 
3916                 if ( send_address(hcode, sendmap & mask, syncmode) != 0 ||
3917                      send_command(cmdbuf, 5, flags & F_STA, syncmode) != 0 )
3918                    return 1;
3919              }
3920              mask = mask << 1;
3921           }
3922 
3923           break;
3924 
3925 #ifdef HASCM17A
3926        case 34 : /* CM17A "Firecracker" RF commands */
3927           noswitch = 0;
3928 
3929 	  if ( aflags & A_PLUS && !(aflags & A_DUMMY) ) {
3930 	     sprintf(errmsg,
3931 	       "Command '%s': '+' prefix invalid for RF commands.", label);
3932 	     store_error_message(errmsg);
3933 	     return 1;
3934 	  }
3935 	  if ( flags & F_NOA ) {
3936              sprintf(errmsg, "'function' is invalid for RF commands.");
3937              store_error_message(errmsg);
3938              return 1;
3939 	  }
3940 	  if ( flags & F_FFM )
3941 	     rfmode = RF_FAST;
3942 
3943 	  switch ( subcode ) {
3944 	     case 0 :  /* RF All Units Off */
3945 	     case 1 :  /* RF All Lights On */
3946 	     case 6 :  /* RF All Lights Off */
3947 		if ( bitmap != 0 && !(aflags & A_MINUS) && !(aflags & A_PLUS)) {
3948                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
3949                    store_error_message(errmsg);
3950 		}
3951 
3952 		if ( mode == CMD_VERIFY )
3953 		   return 0;
3954 
3955                 if ( configp->rf_noswitch == YES )
3956                    noswitch = rf_nosw_code[subcode];
3957 
3958                 bursts = configp->rf_bursts[subcode];
3959 
3960 		rfhcode = rev_low_nybble(hc2code(hc));
3961 
3962 		rfword = (rfhcode << 12) | rf_func_code[subcode] | noswitch;
3963                 display_rf_xmit(subcode, rfword, bursts);
3964 		if ( write_cm17a(rfword, bursts, rfmode) != 0 ) {
3965                    sprintf(errmsg,
3966 		      "Command '%s': Unable to write to CM17A device.", label);
3967                    store_error_message(errmsg);
3968                    return 1;
3969                 }
3970 		rf_post_delay();
3971 
3972 		break;
3973 
3974 	     case 2 :  /* RF On */
3975 	     case 3 :  /* RF Off */
3976 		if ( aflags & A_MINUS ) {
3977 		   sprintf(errmsg,
3978 		     "Command '%s': '-' prefix invalid for this command.\n", label);
3979 		   store_error_message(errmsg);
3980 		   return 1;
3981                 }
3982 
3983 		if ( bitmap == 0 ) {
3984                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
3985                    store_error_message(errmsg);
3986                    return 1;
3987                 }
3988 
3989                 if ( mode == CMD_VERIFY )
3990                    return 0;
3991 
3992                 if ( configp->rf_noswitch == YES )
3993                    noswitch = rf_nosw_code[subcode];
3994 
3995                 bursts = configp->rf_bursts[subcode];
3996 
3997 		rfhcode = rev_low_nybble(hc2code(hc));
3998 
3999 		mask = 1;
4000                 for ( j = 0; j < 16; j++ ) {
4001                    if ( bitmap & mask ) {
4002                       nsw = (bitmap & 0x00c0) ? noswitch : 0;  /* Units 1 or 9 */
4003 		      rfword = (rfhcode << 12) | rf_unit_code[j] | rf_func_code[subcode] | nsw;
4004                       display_rf_xmit(subcode, rfword, bursts);
4005 		      if ( write_cm17a(rfword, bursts, rfmode) != 0 ) {
4006                          sprintf(errmsg,
4007 			   "Command '%s': Unable to write to CM17A device.", label);
4008                          store_error_message(errmsg);
4009                          return 1;
4010                       }
4011 		      rf_post_delay();
4012 		   }
4013 		   mask = mask << 1;
4014 		}
4015 
4016 		break;
4017 
4018 	     case 7 :  /* RF Dim from full bright (turn Off before dimming) */
4019 
4020 		if ( aflags & A_MINUS ) {
4021 		   sprintf(errmsg,
4022                       "Command '%s': '-' prefix invalid for this command", label);
4023 		   store_error_message(errmsg);
4024 		   return 1;
4025 		}
4026 		if ( bitmap == 0 ) {
4027                    sprintf(errmsg,
4028 		     "Command '%s': No Unitcode specified", label);
4029                    store_error_message(errmsg);
4030                    return 1;
4031 		}
4032 
4033 	     case 4 :  /* RF Dim */
4034 	     case 5 :  /* RF Bright */
4035 
4036 		if ( (aflags & A_MINUS) )
4037 		   bitmap = 0;
4038 
4039 		sp = " ";
4040 		if ( strchr(argv[2], '$') && mode == CMD_VERIFY )
4041 		   bursts = 1;
4042 		else
4043 		   bursts = strtol(argv[2], &sp, 10);
4044 
4045 		if ( !strchr(" \t\n", *sp) || bursts < 1 || bursts > 255 ) {
4046                    sprintf(errmsg, "Command '%s': Invalid argument '%s'", label, argv[2]);
4047                    store_error_message(errmsg);
4048                    return 1;
4049                 }
4050 
4051                 if ( mode == CMD_VERIFY )
4052 		   break;
4053 
4054 		if ( subcode == 7 ) {
4055 		   subcode = 4;
4056 		   precode = 3;   /* Turn Off before dimming */
4057 		}
4058 		else
4059 	           precode = 2;   /* Turn On before dimming */
4060 
4061                 if ( configp->rf_noswitch == YES )
4062                    noswitch = rf_nosw_code[precode];
4063 
4064 		burstgroup = min(bursts, (int)configp->rf_bursts[subcode]);
4065 
4066 		if ( rfmode == RF_FAST )
4067 		   burstgroup = bursts;
4068 
4069 		rfhcode = rev_low_nybble(hc2code(hc));
4070 
4071 		if ( bitmap == 0 ) {
4072                    /* Transmit dims/brights only */
4073 		   rfword = (rfhcode << 12) | rf_func_code[subcode];
4074                    display_rf_xmit(subcode, rfword, bursts);
4075 		   while ( bursts > 0 ) {
4076                       if ( write_cm17a(rfword, min(bursts, burstgroup), rfmode) != 0 ) {
4077                          sprintf(errmsg,
4078                             "Command '%s': Unable to write to CM17A device.", label);
4079                          store_error_message(errmsg);
4080                          return 1;
4081                       }
4082                       rf_post_delay();
4083 		      bursts -= burstgroup;
4084                    }
4085 		}
4086 		else {
4087                    /* Precede dims/brights with an On (or Off) command to set the address. */
4088 		   mask = 1;
4089 		   savebursts = bursts;
4090 		   for ( j = 0; j < 16; j++ ) {
4091                       if ( bitmap & mask ) {
4092                          nsw = (bitmap & 0x00c0) ? noswitch : 0;  /* Units 1 or 9 */
4093 		         rfonword = (rfhcode << 12) | rf_unit_code[j] | rf_func_code[precode] | nsw;
4094                          display_rf_xmit(precode, rfonword, configp->rf_bursts[precode]);
4095 			 rfword = (rfhcode << 12) | rf_func_code[subcode];
4096                          display_rf_xmit(subcode, rfword, bursts);
4097                          if ( write_cm17a(rfonword, configp->rf_bursts[precode], RF_SLOW) != 0 ) {
4098                             sprintf(errmsg,
4099 			      "Command '%s': Unable to write to CM17A device.", label);
4100                             store_error_message(errmsg);
4101                             return 1;
4102                          }
4103 		         rf_post_delay();
4104 
4105 			 while ( bursts > 0 ) {
4106                             if ( write_cm17a(rfword, min(bursts, burstgroup), rfmode) != 0 ) {
4107                                sprintf(errmsg,
4108 				 "Command '%s': Unable to write to CM17A device.", label);
4109                                store_error_message(errmsg);
4110                                return 1;
4111                             }
4112 		            rf_post_delay();
4113 			    bursts -= burstgroup;
4114 			 }
4115                       }
4116 		      mask = mask << 1;
4117 		      bursts = savebursts;
4118 		   }
4119 		}
4120 
4121 		break;
4122 
4123 	     case 9 : /* RF Arbitary 2-byte code */
4124 		byte1 = byte2 = 0;
4125 		sp = " ";
4126 		if ( strchr(argv[1], '$') && mode == CMD_VERIFY )
4127 		   byte1 = 0x10;
4128 		else
4129 		   byte1 = strtol(argv[1], &sp, 16);
4130 		if ( !strchr(" \t\n", *sp) || byte1 < 0 || byte1 > 0xff ) {
4131                    sprintf(errmsg,
4132 		     "Command '%s': Invalid argument '%s'", label, argv[1]);
4133                    store_error_message(errmsg);
4134                    return 1;
4135                 }
4136 
4137 		sp = " ";
4138 		if ( strchr(argv[2], '$') && mode == CMD_VERIFY )
4139 		   byte2 = 0x10;
4140 		else
4141 		   byte2 = strtol(argv[2], &sp, 16);
4142 		if ( !strchr(" \t\n", *sp) || byte2 < 0 || byte2 > 0xff ) {
4143                    sprintf(errmsg,
4144 		     "Command '%s': Invalid argument '%s'", label, argv[2]);
4145                    store_error_message(errmsg);
4146                    return 1;
4147                 }
4148 
4149 	        if ( strchr(argv[3], '$') && mode == CMD_VERIFY )
4150 		   bursts = 1;
4151 		else
4152                    bursts = strtol(argv[3], &sp, 10);
4153 
4154                 if ( !strchr(" \t\n", *sp) || bursts < 1 || bursts > 255 ) {
4155                    sprintf(errmsg,
4156 		        "Command '%s': Invalid <count> parameter", label);
4157 	       	   store_error_message(errmsg);
4158 	     	   return 1;
4159                 }
4160 
4161                 if ( mode == CMD_VERIFY )
4162 	           return 0;
4163 
4164 		burstgroup = min(bursts, (int)configp->rf_bursts[subcode]);
4165 		if ( rfmode == RF_FAST )
4166 		   burstgroup = bursts;
4167 
4168 		rfword = byte1 << 8 | byte2;
4169                 display_rf_xmit(subcode, rfword, bursts);
4170 
4171                 while ( bursts > 0 ) {
4172 		   if ( write_cm17a(rfword, min(bursts, burstgroup), rfmode) != 0 ) {
4173                       sprintf(errmsg,
4174 		        "Command '%s': Unable to write to CM17A device.", label);
4175                       store_error_message(errmsg);
4176                       return 1;
4177                    }
4178 		   rf_farb_delay();
4179 		   bursts -= burstgroup;
4180 		}
4181 
4182                 break;
4183 
4184              case 8 :  /* Reset CM17A */
4185 
4186 		if ( mode == CMD_VERIFY )
4187 		   return 0;
4188 
4189                 if ( reset_cm17a() != 0 ) {
4190                    sprintf(errmsg,
4191 		     "Command '%s': Unable to reset CM17A device.", label);
4192                    store_error_message(errmsg);
4193                    return 1;
4194                 }
4195 
4196                 break;
4197 
4198              case 10 : /* farw - Multiple arbitrary 16 bit rfwords */
4199 
4200                 /* First check that each rfword is in range 0000-FFFF */
4201                 for ( k = 1; k < argc; k++ ) {
4202                    if ( strchr(argv[k], '$') && mode == CMD_VERIFY ) {
4203                       /* Fake a dummy value for scene verification */
4204                       lxdata = 10;
4205                       sp = " ";
4206                    }
4207                    else {
4208                       lxdata = strtol(argv[k], &sp, 16);
4209                    }
4210                    if ( !strchr(" \t\n\r", *sp) || lxdata < 0 || lxdata > 0xffff ) {
4211                       sprintf(errmsg,
4212                          "Command '%s': Data '%s' outside range 0-FFFF", label, argv[k]);
4213                       store_error_message(errmsg);
4214                       return 1;
4215                    }
4216                 }
4217 
4218                 if ( mode == CMD_VERIFY )
4219                    break;
4220 
4221                 bursts = configp->rf_bursts[subcode];
4222                 for ( k = 1; k < argc; k++ ) {
4223                    lxdata = strtol(argv[k], &sp, 16);
4224                    if ( !strchr(" \t\n\r", *sp) || lxdata < 0 || lxdata > 0xffff ) {
4225                       sprintf(errmsg,
4226                          "Command '%s': Data '%s' outside range 0-FFFF", label, argv[k]);
4227                       store_error_message(errmsg);
4228                       return 1;
4229                    }
4230                    rfword = (unsigned int)lxdata;
4231                    display_rf_xmit(10, rfword, bursts);
4232                    if ( write_cm17a(rfword, bursts, rfmode) != 0 ) {
4233                       sprintf(errmsg,
4234                           "Command '%s': Unable to write to CM17A device.", label);
4235                       store_error_message(errmsg);
4236                       return 1;
4237                    }
4238                    rf_farw_delay();
4239                 }
4240                 break;
4241 
4242              case 11 : /* flux - Multiple arbitrary 16 bit rfwords */
4243 
4244                 /* First two parameters are number of bursts and post-delay */
4245                 /* in milliseconds.  Both are decimal.                      */
4246                 if ( strchr(argv[1], '&') && mode == CMD_VERIFY ) {
4247                    bursts = 5;
4248                    sp = " ";
4249                 }
4250                 else {
4251                    bursts = strtol(argv[1], &sp, 10);
4252                 }
4253                 if ( !strchr(" \t\n\r", *sp) || bursts < 1 ) {
4254                    sprintf(errmsg,
4255                       "Command '%s': Invalid bursts number '%s'", label, argv[1]);
4256                    store_error_message(errmsg);
4257                    return 1;
4258                 }
4259 
4260                 if ( strchr(argv[2], '&') && mode == CMD_VERIFY ) {
4261                    flux_delay = 200;
4262                    sp = " ";
4263                 }
4264                 else {
4265                    flux_delay = strtol(argv[2], &sp, 10);
4266                 }
4267                 if ( !strchr(" \t\n\r", *sp) || flux_delay < 0 ) {
4268                    sprintf(errmsg,
4269                       "Command '%s': Invalid delay '%s'", label, argv[2]);
4270                    store_error_message(errmsg);
4271                    return 1;
4272                 }
4273 
4274                 /* Subsequent parameters are 16-bit hex rfwords.*/
4275                 /* Check that each rfword is in range 0000-FFFF */
4276                 for ( k = 3; k < argc; k++ ) {
4277                    if ( strchr(argv[k], '$') && mode == CMD_VERIFY ) {
4278                       /* Fake a dummy value for scene verification */
4279                       lxdata = 10;
4280                       sp = " ";
4281                    }
4282                    else {
4283                       lxdata = strtol(argv[k], &sp, 16);
4284                    }
4285                    if ( !strchr(" \t\n\r", *sp) || lxdata < 0 || lxdata > 0xffff ) {
4286                       sprintf(errmsg,
4287                          "Command '%s': Data '%s' outside range 0-FFFF", label, argv[k]);
4288                       store_error_message(errmsg);
4289                       return 1;
4290                    }
4291                 }
4292 
4293                 if ( mode == CMD_VERIFY )
4294                    break;
4295 
4296                 for ( k = 3; k < argc; k++ ) {
4297                    lxdata = strtol(argv[k], &sp, 16);
4298                    if ( !strchr(" \t\n\r", *sp) || lxdata < 0 || lxdata > 0xffff ) {
4299                       sprintf(errmsg,
4300                          "Command '%s': Data '%s' outside range 0-FFFF", label, argv[k]);
4301                       store_error_message(errmsg);
4302                       return 1;
4303                    }
4304                    rfword = (unsigned int)lxdata;
4305                    display_rf_xmit(11, rfword, bursts);
4306                    if ( write_cm17a(rfword, bursts, rfmode) != 0 ) {
4307                       sprintf(errmsg,
4308                           "Command '%s': Unable to write to CM17A device.", label);
4309                       store_error_message(errmsg);
4310                       return 1;
4311                    }
4312                    rf_flux_delay(flux_delay);
4313                 }
4314                 break;
4315 
4316 	     default :
4317 		break;
4318 	  }
4319           break;
4320 #endif /* End of HASCM17A */
4321 
4322        default :
4323           sprintf(errmsg, "Command '%s': Not implemented", label);
4324           store_error_message(errmsg);
4325           return 1;
4326     }
4327 
4328     return 0;
4329 }
4330 
4331 /*---------------------------------------------------------------+
4332  | Generate the elements for an uploaded macro and pass back     |
4333  | through the argument list.  Return 0 if successful, otherwise |
4334  | write message to error handling routine and return 1.         |
4335  +---------------------------------------------------------------*/
macro_command(int argc,char * argv[],int * nelem,int * nbytes,unsigned char * elements)4336 int macro_command( int argc, char *argv[],
4337                  int *nelem, int *nbytes, unsigned char *elements )
4338 {
4339 
4340     int           j, k, icmd, nargs, radix, subcode, subsubcode;
4341     unsigned int  mask, aflags, bitmap, sendmap;
4342     unsigned long flags;
4343     char          *label, *sp;
4344     unsigned char *ep;
4345     char          hc;
4346     char          errmsg[128];
4347     int           dimval, dimcode, delay, level;
4348     unsigned char group, subgroup;
4349     unsigned char cmdcode, hcode, switchv, switchcode;
4350     int           minargs, maxargs;
4351     /* unsigned */ int  xdata;
4352     static char   reorder[] = {6,14,2,10,1,9,5,13,7,15,3,11,0,8,4,12};
4353     extern int    parse_ext3_group ( char *, unsigned char *, unsigned char * );
4354 
4355     clear_error_message();
4356 
4357     flags = 0;
4358 
4359     /* Handle a few special cases */
4360 
4361     if ( strcmp(argv[0], "function") == 0 ) {
4362        /* Special case: "function" command */
4363        if ( argc < 2 ) {
4364           store_error_message("Command 'function': Too few parameters");
4365           return 1;
4366        }
4367        argv++;
4368        argc--;
4369        flags |= F_NOA;
4370     }
4371     else if ( strcmp(argv[0], "turn") == 0 ) {
4372        /* Special case: "turn" command */
4373        if ( argc < 3 ) {
4374           store_error_message("Command 'turn': Too few parameters");
4375           return 1;
4376        }
4377        flags |= F_TRN;
4378        argv++;
4379        argc--;
4380        /* Swap first two parameters */
4381        sp = argv[0];
4382        argv[0] = argv[1];
4383        argv[1] = sp;
4384     }
4385     nargs = argc - 1;
4386 
4387     /* Identify the command */
4388     for ( icmd = 0; icmd < nx10cmds; icmd++ ) {
4389        if ( !strcmp(argv[0], x10command[icmd].label) )
4390           break;
4391     }
4392     if ( icmd == nx10cmds ) {
4393        sprintf(errmsg, "Invalid command '%s'", argv[0]);
4394        store_error_message(errmsg);
4395        return 1;
4396     }
4397 
4398     label    = x10command[icmd].label;
4399     minargs  = x10command[icmd].minargs;
4400     maxargs  = x10command[icmd].maxargs;
4401     flags   |= x10command[icmd].flags;
4402     cmdcode  = x10command[icmd].code;
4403     subcode  = x10command[icmd].subcode;
4404     subsubcode  = x10command[icmd].subsubcode;
4405     radix    = (flags & F_ARB) ?  16 : 10;
4406 
4407     /* Make sure this command is available for uploaded macros */
4408     if ( flags & F_NMA || flags & F_HLP ) {
4409        sprintf(errmsg, "'%s' command is invalid for uploaded macros", label);
4410        store_error_message(errmsg);
4411        return 1;
4412     }
4413 
4414     if ( flags & F_NMAI ) {
4415        sprintf(errmsg, "Warning: '%s' command is ignored for uploaded macros", label);
4416        store_error_message(errmsg);
4417        *nelem = *nbytes = 0;
4418        return 0;
4419     }
4420 
4421     if ( nargs < minargs ) {
4422        sprintf(errmsg, "Command '%s': Too few arguments", label);
4423        store_error_message(errmsg);
4424        return 1;
4425     }
4426     if ( maxargs != -1 && nargs > maxargs ) {
4427        cmd_usage(stderr, icmd );
4428        sprintf(errmsg, "Command '%s': Too many arguments", label);
4429        store_error_message(errmsg);
4430        return 1;
4431     }
4432 
4433     /* Handle a few special cases */
4434 
4435     if ( flags & F_ARB ) {
4436        /* If arbitrary extended code, get subcode and adjust argument count */
4437        subcode = (int)strtol(argv[1], &sp, radix);
4438        argv++;
4439        argc--;
4440        nargs--;
4441        if ( !strchr(" \t\n", *sp) || subcode < 0 || subcode > 0xff ) {
4442           sprintf(errmsg, "Command '%s': Extended code function outside range 00-0xff", label);
4443           store_error_message(errmsg);
4444           return 1;
4445        }
4446     }
4447 
4448     /* Only commands with command code <= 6  or code == 26 are valid with "turn" */
4449     if ( flags & F_TRN && cmdcode > 6 && cmdcode != 26 ) {
4450        sprintf(errmsg,
4451          "Command '%s' cannot be used with the 'turn' command", label);
4452        store_error_message(errmsg);
4453        return 1;
4454     }
4455 
4456     /* Default HC|Unit */
4457     hc = '_';
4458     bitmap = 0;
4459     aflags = A_VALID;
4460 
4461     if ( !(flags & F_NUM) && nargs > 0 ) {
4462        /* parse it as an alias or HC|Units token. */
4463        aflags = parse_addr(argv[1], &hc, &bitmap);
4464     }
4465 
4466     if ( !(aflags & A_VALID) ) {
4467        sprintf(errmsg, "Command '%s': ", label);
4468        add_error_prefix(errmsg);
4469        return 1;
4470     }
4471 
4472     /* Make sure a positional parameter didn't slip through */
4473     if ( aflags & A_DUMMY ) {
4474        sprintf(errmsg,
4475           "Command '%s': Unresolved positional parameter '%s'",
4476              label, argv[1]);
4477        store_error_message(errmsg);
4478        return 1;
4479     }
4480 
4481     if ( !(aflags & A_HCODE) && !(flags & F_NUM) && minargs > 0 )  {
4482        sprintf(errmsg, "Command '%s': Housecode required", label);
4483        store_error_message(errmsg);
4484        return 1;
4485     }
4486 
4487     switchv = cmdcode;
4488 
4489     switch ( switchv ) {
4490 
4491        case 20 : /* Send address bytes only */
4492        case 21 : /* Function only */
4493        case 22 : /* Send arbitrary series of hex bytes */
4494        case 23 : /* Send (quoted) text message */
4495        case 25 : /* Pause */
4496        case 27 : /* Turn */
4497        case 28 : /* Temp_Req */
4498        case 99 : /* Legacy commands */
4499           /* We should never get here */
4500              sprintf(errmsg, "Internal error - Invalid macro command '%s'", label);
4501              store_error_message(errmsg);
4502              return 1;
4503 
4504        case  8 :  /* Hail */
4505        case  9 :  /* Hail Ack */
4506           if ( !(aflags & A_HCODE) ) {
4507              hc = configp->housecode;
4508           }
4509        case  0 :  /* All Units Off  */
4510        case  1 :  /* All Lights On  */
4511        case  6 :  /* All Lights Off */
4512        case 12 :  /* Data Transfer */
4513           if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4514              sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4515              store_error_message(errmsg);
4516              *errmsg = '\0';
4517              bitmap = 0;
4518           }
4519           if ( bitmap == 0 && (aflags & A_PLUS || configp->force_addr == YES) )
4520              bitmap = 1;
4521 
4522        case  2 :  /* On */
4523        case  3 :  /* Off */
4524        case 13 :  /* Status On */
4525        case 14 :  /* Status Off */
4526           if ( aflags & A_MINUS || flags & F_NOA )
4527              bitmap = 0;
4528           else if ( bitmap == 0 && !(flags & F_ALL) ) {
4529              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4530              store_error_message(errmsg);
4531              return 1;
4532           }
4533 
4534           hcode = hc2code(hc);
4535 
4536           elements[0] = (hcode << 4) | cmdcode;
4537           elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4538           elements[2] = (unsigned char)((bitmap & 0x00ff));
4539           *nelem = 1;
4540           *nbytes = 3;
4541 
4542           break;
4543 
4544        case  4 :  /* Dim */
4545        case  5 :  /* Bright */
4546           dimval = (int)strtol(argv[2], &sp, 10);
4547           if ( !strchr(" \t\n", *sp) ) {
4548 	     sprintf(errmsg,
4549 		"Command '%s': Invalid Dim/Bright parameter '%s'", label, argv[2]);
4550              store_error_message(errmsg);
4551 	     return 1;
4552 	  }
4553 	  else if ( configp->restrict_dims == YES && (dimval < 1 || dimval > 22) ) {
4554              sprintf(errmsg,
4555                 "Command '%s': Dim/Bright value outside range 1-22", label);
4556              store_error_message(errmsg);
4557 	     return 1;
4558 	  }
4559 	  else if ( configp->restrict_dims == NO && (dimval < 0 || dimval > 31) ) {
4560              sprintf(errmsg,
4561                 "Command '%s': Dim/Bright value outside range 0-31", label);
4562              store_error_message(errmsg);
4563              return 1;
4564           }
4565 
4566           if ( aflags & A_MINUS || flags & F_NOA )
4567              bitmap = 0;
4568           else if ( bitmap == 0 ) {
4569              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4570              store_error_message(errmsg);
4571              return 1;
4572           }
4573 
4574           hcode = hc2code(hc);
4575 
4576           if ( flags & F_ONN ) {
4577              /* On signal needed before bright/dim (WS467-1 redesign) */
4578              elements[0] = (hcode << 4) | 0x02;
4579              elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4580              elements[2] = (unsigned char)((bitmap & 0x00ff));
4581              elements[3] = (hcode << 4) | cmdcode;
4582              elements[4] = 0;
4583              elements[5] = 0;
4584              elements[6] = (unsigned char)(dimval | ((flags & F_BRI) ? 0x80 : 0));
4585              *nelem = 2;
4586              *nbytes = 7;
4587           }
4588           else {
4589              elements[0] = (hcode << 4) | cmdcode;
4590              elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4591              elements[2] = (unsigned char)((bitmap & 0x00ff));
4592              elements[3] = (unsigned char)(dimval | ((flags & F_BRI) ? 0x80 : 0));
4593              *nelem = 1;
4594              *nbytes = 4;
4595           }
4596 
4597           break;
4598 
4599        case 10 :  /* Old style preset 1 */
4600        case 11 :  /* Old style preset 2 */
4601 
4602           if ( nargs == 2 )
4603              /* Limited preset for macros */
4604              dimval = (int)strtol(argv[2], &sp, 10);
4605           else
4606              /* preset level only */
4607              dimval = (int)strtol(argv[1], &sp, 10);
4608 
4609           if ( !strchr(" \t\n", *sp) || dimval < 1 || dimval > 32 ) {
4610              sprintf(errmsg, "Command '%s': Level outside range 1-32", label);
4611              store_error_message(errmsg);
4612              return 1;
4613           }
4614 
4615           if ( (dimcode = dimval) > 16 ) {
4616              cmdcode = 11;
4617              dimcode -= 16;
4618           }
4619           dimcode = rev_low_nybble(dimcode - 1);
4620 
4621           if ( nargs == 2 && (hcode = hc2code(hc)) != dimcode  ) {
4622              sprintf(errmsg, "Command '%s': Preset level %d not supported for housecode %c",
4623                 label, dimval, hc);
4624              store_error_message(errmsg);
4625              return 1;
4626           }
4627 
4628           if ( aflags & A_MINUS || flags & F_NOA )
4629              bitmap = 0;
4630           else if ( bitmap == 0 ) {
4631              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4632              store_error_message(errmsg);
4633              return 1;
4634           }
4635 
4636           elements[0] = (dimcode << 4) | cmdcode;
4637           elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4638           elements[2] = (unsigned char)((bitmap & 0x00ff));
4639           *nelem = 1;
4640           *nbytes = 3;
4641 
4642           break;
4643 
4644        case 15 :  /* Status Req */
4645           sendmap = bitmap;
4646           if ( aflags & A_MINUS || flags & F_NOA ) {
4647              bitmap = 1;
4648              sendmap = 0;
4649           }
4650           else if ( bitmap == 0 ) {
4651              sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4652              store_error_message(errmsg);
4653              return 1;
4654           }
4655 
4656           hcode = hc2code(hc);
4657 
4658           ep = elements;
4659           *nelem = 0;
4660           *nbytes = 0;
4661 
4662           /* Send each address/command pair individually to allow */
4663           /* associating a response to a particular unit code     */
4664           mask = 1;
4665           for ( k = 0; k < 16; k++ )  {
4666              if ( bitmap & mask ) {
4667                 *ep++ = (hcode << 4) | cmdcode;
4668                 *ep++ = (unsigned char)((bitmap & 0xff00) >> 8);
4669                 *ep++ = (unsigned char)((bitmap & 0x00ff));
4670                 *nelem += 1;
4671                 *nbytes += 3;
4672              }
4673              mask = mask << 1;
4674           }
4675           break;
4676 
4677        case 24 :  /* Send All_Units_Off to all Housecodes */
4678           bitmap = (configp->force_addr == YES) ?  1 : 0;
4679           ep = elements;
4680 	  for ( j = 0; j < 16; j++ ) {
4681 	     hcode = reorder[j];    /* Send housecodes in alphabetic order */
4682              *ep++ = (hcode << 4);  /* cmdcode = 0 */
4683              *ep++ = (unsigned char)((bitmap & 0xff00) >> 8);
4684              *ep++ = (unsigned char)((bitmap & 0x00ff));
4685           }
4686           *nelem = 16;
4687           *nbytes = 48;
4688 
4689 	  break;
4690 
4691        case 26 :  /* All Units On (same as 'on H1-16') */
4692           if ( bitmap != 0 && !(aflags & A_MINUS) ) {
4693              sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4694              store_error_message(errmsg);
4695           }
4696           bitmap = 0xFFFF;
4697 
4698           hcode = hc2code(hc);
4699 
4700           elements[0] = (hcode << 4) | 2 ;
4701           elements[1] = (unsigned char)((bitmap & 0xff00) >> 8);
4702           elements[2] = (unsigned char)((bitmap & 0x00ff));
4703           *nelem = 1;
4704           *nbytes = 3;
4705 
4706           break;
4707 
4708        case 32 :  /* Delay in minutes */
4709           delay = (int)strtol(argv[1], &sp, 10);
4710           if ( !strchr(" \t\n", *sp) ) {
4711              sprintf(errmsg,
4712 	        "Command '%s': Invalid character '%c' in delay value", label, *sp);
4713              store_error_message(errmsg);
4714              return 1;
4715           }
4716           if ( delay < 0 || delay > 240 ) {
4717              sprintf(errmsg, "Command '%s': value %d outside range 0-240 minutes",
4718 		 label, delay);
4719              store_error_message(errmsg);
4720              return 1;
4721           }
4722 
4723 	  sprintf(errmsg, "Command '%s': Not yet implemented for macros", label);
4724 	  store_error_message(errmsg);
4725 	  return 1;
4726 
4727        case  7 :  /* Extended Code commands */
4728           sendmap = (aflags & A_PLUS) ? bitmap : 0 ;
4729           if ( sendmap == 0 && configp->force_addr == YES )
4730              sendmap = 0xFFFF;
4731 
4732           hcode = hc2code(hc);
4733           xdata = 0;
4734 
4735           if ( flags & F_ARB )
4736              switchcode = 0xff;
4737           else
4738              switchcode = subcode;
4739 
4740           switch ( switchcode ) {
4741              case 0x30 :  /* Extended Group Include */
4742                 if ( bitmap == 0 ) {
4743                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4744                    store_error_message(errmsg);
4745                    return 1;
4746                 }
4747 
4748                 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4749                    sprintf(errmsg,
4750                       "Command '%s': %s", label, error_message());
4751                    store_error_message(errmsg);
4752                    return 1;
4753                 }
4754 
4755                 xdata = (group << 6);
4756                 if ( subgroup & 0x80u ) {
4757                    xdata |= (subgroup & 0x0fu) | 0x20u;
4758                 }
4759 
4760                 break;
4761 
4762              case 0x31 :  /* Extended Preset Dim */
4763                 if ( bitmap == 0 ) {
4764                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4765                    store_error_message(errmsg);
4766                    return 1;
4767                 }
4768 
4769                 /* Get dim level */
4770                 xdata = (int)strtol(argv[2], &sp, 10);
4771                 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 63 ) {
4772                    sprintf(errmsg, "Command '%s': Extended Preset level outside range 0-63", label);
4773                    store_error_message(errmsg);
4774                    return 1;
4775                 }
4776                 break;
4777 
4778              case 0x32 :  /* Extended Group Include at Level*/
4779                 if ( bitmap == 0 ) {
4780                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4781                    store_error_message(errmsg);
4782                    return 1;
4783                 }
4784 
4785                 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4786                    sprintf(errmsg,
4787                       "Command '%s': %s", label, error_message());
4788                    store_error_message(errmsg);
4789                    return 1;
4790                 }
4791 
4792                 xdata = (group << 6);
4793 
4794                 if ( subgroup & 0x80u ) {
4795                    sprintf(errmsg,
4796                       "Command '%s': Cannot specify group.subgroup with level", label);
4797                    store_error_message(errmsg);
4798                    return 1;
4799                 }
4800 
4801                 level = (int)strtol(argv[3], &sp, 10);
4802                 if ( !strchr(" \t\n", *sp) || level < 0 || level > 63 ) {
4803                    sprintf(errmsg,
4804                      "Command '%s': Level outside range 0-63", label);
4805                    store_error_message(errmsg);
4806                    return 1;
4807                 }
4808                 xdata |= (level & 0x3f);
4809 
4810                 break;
4811 
4812              case 0x33 :  /* Extended All On */
4813              case 0x34 :  /* Extended All Off */
4814                 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4815                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4816                    store_error_message(errmsg);
4817                    *errmsg = '\0';
4818                    bitmap = 1;
4819                 }
4820                 else if ( bitmap == 0 )
4821                    bitmap = 1;
4822 
4823                 if ( aflags & A_PLUS )
4824                    sendmap = bitmap;
4825 
4826                 break;
4827 
4828              case 0x35 :  /* Remove from group(s) */
4829                 if ( (xdata = (int)list2linmap(argv[2], 0, 3)) == 0 ) {
4830                    sprintf(errmsg, "Command '%s': Group number %s", label, error_message());
4831                    store_error_message(errmsg);
4832                    return 1;
4833                 }
4834 
4835                 if ( subsubcode == 0 ) {
4836                    if ( bitmap == 0 ) {
4837                       sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4838                       store_error_message(errmsg);
4839                       return 1;
4840                    }
4841                 }
4842                 else {
4843                    xdata |= 0xf0;
4844                    if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) &&
4845                         configp->force_addr == NO) {
4846                       sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4847                       store_error_message(errmsg);
4848                       *errmsg = '\0';
4849                       bitmap = 1;
4850                    }
4851                    else if ( bitmap == 0 ) {
4852                       bitmap = 1;
4853                    }
4854                 }
4855 
4856                 if ( aflags & A_PLUS )
4857                    sendmap = bitmap;
4858 
4859                 break;
4860 
4861              case 0x36 :  /* Execute group functions */
4862                 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4863                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4864                    store_error_message(errmsg);
4865                    *errmsg = '\0';
4866                    bitmap = 1;
4867                 }
4868                 else if ( bitmap == 0 ) {
4869                    bitmap = 1;
4870                 }
4871 
4872                 if ( aflags & A_PLUS )
4873                    sendmap = bitmap;
4874 
4875                 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4876                    sprintf(errmsg,
4877                       "Command '%s': %s", label, error_message());
4878                    store_error_message(errmsg);
4879                    return 1;
4880                 }
4881 
4882                 xdata = group << 6;
4883 
4884                 if ( subgroup & 0x80u ) {
4885                    xdata |= 0x20u | (subgroup & 0x0f);
4886                 }
4887 
4888                 if ( subsubcode == 1 ) {
4889                    xdata |= 0x10u;
4890                 }
4891 
4892                 break;
4893 
4894              case 0x37 :  /* Extended Status Requests */
4895                 if ( bitmap == 0 ) {
4896                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4897                    store_error_message(errmsg);
4898                    return 1;
4899                 }
4900 
4901                 if ( subsubcode == 0 ) {
4902                    /* Output status request */
4903                    xdata = 0;
4904                 }
4905                 else if ( subsubcode == 1 ) {
4906                    /* Powerup status request */
4907                    xdata = 0x10;
4908                 }
4909                 else {
4910                    if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4911                       sprintf(errmsg,
4912                          "Command '%s': %s", label, error_message());
4913                       store_error_message(errmsg);
4914                       return 1;
4915                    }
4916 
4917                    xdata = (group << 6) | 0x20u;
4918 
4919                    if ( subgroup & 0x80u ) {
4920                       xdata |= 0x10u | (subgroup & 0x0f);
4921                    }
4922                 }
4923 
4924                 break;
4925 
4926              case 0x3b :  /* Extended Configure Auto Status_Ack Mode */
4927                 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4928                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4929                    store_error_message(errmsg);
4930                    *errmsg = '\0';
4931                    bitmap = 1;
4932                 }
4933                 else if ( bitmap == 0 )
4934                    bitmap = 1;
4935 
4936                 if ( aflags & A_PLUS )
4937                    sendmap = bitmap;
4938 
4939                 /* Get mode */
4940                 xdata = (int)strtol(argv[2], &sp, 10);
4941                 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 3 ) {
4942                    sprintf(errmsg, "Command '%s': Extended Config Mode outside range 0-3", label);
4943                    store_error_message(errmsg);
4944                    return 1;
4945                 }
4946                 break;
4947 
4948              case 0x3c :  /* Extended group bright/dim functions */
4949                 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
4950                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
4951                    store_error_message(errmsg);
4952                    *errmsg = '\0';
4953                    bitmap = 1;
4954                 }
4955                 else if ( bitmap == 0 ) {
4956                    bitmap = 1;
4957                 }
4958 
4959                 if ( aflags & A_PLUS )
4960                    sendmap = bitmap;
4961 
4962                 if ( parse_ext3_group(argv[2], &group, &subgroup) != 0 ) {
4963                    sprintf(errmsg,
4964                       "Command '%s': %s", label, error_message());
4965                    store_error_message(errmsg);
4966                    return 1;
4967                 }
4968 
4969                 xdata = group << 6;
4970 
4971                 if ( subgroup & 0x80u ) {
4972                    xdata |= 0x20u | (subgroup & 0x0f);
4973                 }
4974 
4975                 if ( subsubcode == 1 ) {
4976                    xdata |= 0x10u;
4977                 }
4978 
4979                 break;
4980 
4981              case 0xfd :  /* Extended OFF ( = Extended Preset Dim level 0) */
4982                 if ( bitmap == 0 ) {
4983                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4984                    store_error_message(errmsg);
4985                    return 1;
4986                 }
4987                 subcode = 0x31;
4988                 xdata = 0;
4989                 break;
4990 
4991              case 0xfe :  /* Extended ON ( = Extended Preset Dim level 63) */
4992                 if ( bitmap == 0 ) {
4993                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
4994                    store_error_message(errmsg);
4995                    return 1;
4996                 }
4997                 subcode = 0x31;
4998                 xdata = 63;
4999                 break;
5000 
5001              case 0xff :  /* Arbitrary Extended Function */
5002                 if ( bitmap == 0 ) {
5003                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
5004                    return 1;
5005                 }
5006                 xdata = (int)strtol(argv[2], &sp, 16);
5007                 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 0xff ) {
5008                    sprintf(errmsg,
5009                       "Command '%s': Data '%s' outside range 0-FF", label, argv[2]);
5010                    store_error_message(errmsg);
5011                    return 1;
5012                 }
5013                 break ;
5014 
5015              case 0x01 :  /* Shutter open, observe limit */
5016              case 0x03 :  /* Shutter open, ignore limit */
5017              case 0x02 :  /* Set shutter limit */
5018                 if ( bitmap == 0 ) {
5019                    sprintf(errmsg, "Command '%s': No Unitcode specified", label);
5020                    store_error_message(errmsg);
5021                    return 1;
5022                 }
5023 
5024                 /* Get dim level */
5025                 xdata = (int)strtol(argv[2], &sp, 10);
5026                 if ( !strchr(" \t\n", *sp) || xdata < 0 || xdata > 25 ) {
5027                    sprintf(errmsg, "Command '%s': Shutter level outside range 0-25", label);
5028                    store_error_message(errmsg);
5029                    return 1;
5030                 }
5031                 break;
5032 
5033              case 0x04 :  /* Shutter Open All */
5034              case 0x0B :  /* Shutter Close All */
5035 
5036                 if ( bitmap != 0 && !(aflags & A_PLUS) && !(aflags & A_MINUS) && configp->force_addr == NO) {
5037                    sprintf(errmsg, "Unitcode ignored for '%s' command.", label);
5038                    store_error_message(errmsg);
5039                    *errmsg = '\0';
5040                    bitmap = 1;
5041                 }
5042                 else if ( bitmap == 0 )
5043                    bitmap = 1;
5044 
5045                 if ( aflags & A_PLUS )
5046                    sendmap = bitmap;
5047 
5048                 break;
5049 
5050              default :
5051                 sprintf(errmsg, "Command not implemented");
5052                 store_error_message(errmsg);
5053                 return 1;
5054           }
5055 
5056           mask = 1;
5057           ep = elements;
5058           *nelem = 0;
5059           *nbytes = 0;
5060           for ( j = 0; j < 16; j++ ) {
5061              if ( bitmap & mask ) {
5062                 *ep++ = (hcode << 4) | cmdcode;
5063                 *ep++ = (unsigned char)(((sendmap & mask) & 0xff00) >> 8);
5064                 *ep++ = (unsigned char)(((sendmap & mask) & 0x00ff));
5065                 *ep++ = j;
5066                 *ep++ = (unsigned char)(xdata & 0xff);
5067                 *ep++ = (unsigned char)(subcode & 0xff);
5068                 *nelem += 1;
5069                 *nbytes += 6;
5070              }
5071              mask = mask << 1;
5072           }
5073           break;
5074 
5075        default :
5076           sprintf(errmsg, "Command not implemented");
5077           store_error_message(errmsg);
5078           return 1;
5079     }
5080 
5081     return 0;
5082 }
5083 
5084 
5085 /*---------------------------------------------------------------+
5086  | Return the maximum number of positional parameters in a       |
5087  | scene, i.e., the highest integer 'nn' represented in a token  |
5088  | a token of the form "$nn".  Return -1 if the "nn" is missing, |
5089  | is less than 1, or is not an integer.                         |
5090  +---------------------------------------------------------------*/
max_parms(int tokc,char * tokv[])5091 int max_parms ( int tokc, char *tokv[] )
5092 {
5093    int  j, parmno, count, maxparm = 0;
5094    int  bracket;
5095    char *sp, *tp;
5096    char errmsg[128];
5097    int  parmchk[1000];
5098 
5099    for ( j = 0; j < 32; j++ )
5100       parmchk[j] = 0;
5101 
5102    for ( j = 1; j < tokc; j++ ) {
5103       tp = tokv[j];
5104       while ((sp = strchr(tp, '$')) != NULL ) {
5105          sp++;
5106          bracket = (*sp == '{') ? 1 : 0 ;
5107          parmno = (int)strtol(sp + bracket, &sp, 10);
5108          if ( (bracket && *sp != '}') || parmno < 1 ) {
5109             sprintf(errmsg,
5110                "Command '%s': Invalid positional parameter in '%s'",
5111                    tokv[0], tokv[j]);
5112             store_error_message(errmsg);
5113             return -1;
5114          }
5115          if ( parmno > configp->max_pparms ) {
5116             sprintf(errmsg, "Too many (%d) positional parameters - max %d",
5117                parmno, configp->max_pparms);
5118             store_error_message(errmsg);
5119             return -1;
5120          }
5121          parmchk[parmno] = 1;
5122          maxparm = max(maxparm, parmno);
5123          if ( bracket )
5124             sp++;
5125          tp = sp;
5126       }
5127    }
5128 
5129    count = 0;
5130    for ( j = 1; j < 32; j++ )
5131       count += parmchk[j];
5132 
5133    /* Warn the user if all positional parameters within the */
5134    /* range 1 through maxparm arene't used in the scene.    */
5135    if ( count < maxparm ) {
5136       sprintf(errmsg,
5137         "Warning: Not all positional parameters specified are used");
5138       store_error_message(errmsg);
5139    }
5140    return maxparm;
5141 }
5142 
5143 
5144 /*---------------------------------------------------------------+
5145  | Verify that the syntax of commands in a scene is correct.     |
5146  +---------------------------------------------------------------*/
verify_scene(char * body)5147 int verify_scene ( char *body )
5148 {
5149    int  j;
5150    int  cmdc, tokc, count;
5151    char **cmdv, **tokv;
5152    char *sp;
5153 
5154    sp = strdup(body);
5155 
5156    /* Separate scene into individual commands */
5157    tokenize(sp, ";", &cmdc, &cmdv);
5158    count = 0;
5159    for ( j = 0; j < cmdc; j++ ) {
5160       strtrim(cmdv[j]);
5161       if ( *cmdv[j] == '\0' )
5162          continue;
5163       count++;
5164       /* Separate commands into individual tokens */
5165       tokenize(cmdv[j], " \t", &tokc, &tokv);
5166       /* Verify the individual command */
5167       if ( direct_command(tokc, tokv, CMD_VERIFY) != 0 ) {
5168          free(tokv);
5169          free(cmdv);
5170          free(sp);
5171          return 1;
5172       }
5173       free(tokv);
5174    }
5175    free(cmdv);
5176    free(sp);
5177    if ( !count ) {
5178       store_error_message("Warning: Contains no commands.");
5179    }
5180    return 0;
5181 }
5182 
5183 
5184 /*---------------------------------------------------------------+
5185  | Replace the positional parameters in the members of list tokv |
5186  | with actual values.  It is presumed that the number of        |
5187  | members in 'actual' has already be verified to be sufficient. |
5188  | The new _members_ in tokvp must be freed after use.           |
5189  +---------------------------------------------------------------*/
replace_dummy_parms(int tokc,char *** tokvp,char * actual[])5190 void replace_dummy_parms ( int tokc, char ***tokvp, char *actual[] )
5191 {
5192    int j, parmno, bracket;
5193    char *sp;
5194    char buffer1[128];
5195    char buffer2[128];
5196 
5197    for ( j = 0; j < tokc; j++ ) {
5198       strncpy2(buffer1, (*tokvp)[j], sizeof(buffer1) - 1);
5199 
5200       while ( (sp = strchr(buffer1, '$')) != NULL ) {
5201          strncpy2(buffer2, buffer1, (sp - buffer1));
5202          sp++;
5203          bracket = (*sp == '{') ? 1 : 0 ;
5204          parmno = (int)strtol(sp + bracket, &sp, 10);
5205          strncat(buffer2, actual[parmno], sizeof(buffer2) - 1 - strlen(buffer2));
5206          if ( bracket )
5207             sp++;
5208 	 strncat(buffer2, sp, sizeof(buffer2) - 1 - strlen(buffer2));
5209          strncpy2(buffer1, buffer2, sizeof(buffer1) - 1);
5210       }
5211       if ( ((*tokvp)[j] = strdup(buffer1)) == NULL ) {
5212          fprintf(stderr, "Unable to allocate memory in replace_dummy_parms()\n");
5213          exit(1);
5214       }
5215    }
5216 
5217    return;
5218 }
5219 
5220 /*---------------------------------------------------------------+
5221  | Message when 'heyu linewidth NN' is entered at the PC command |
5222  | prompt.  This command is intended to be intercepted by the    |
5223  | WEBCLI web interface and be an instruction to that interface. |
5224  +---------------------------------------------------------------*/
display_linewidth_msg(void)5225 int display_linewidth_msg ( void )
5226 {
5227    printf("Dummy command - intended for web interface.\n");
5228    return 0;
5229 }
5230 
5231 /*---------------------------------------------------------------+
5232  | Handler for direct commands and scenes entered from the       |
5233  | command line.                                                 |
5234  +---------------------------------------------------------------*/
c_command(int argc,char * argv[])5235 int c_command( int argc, char *argv[] )
5236 {
5237    int    j, k, m, cmdc, tokc, retcode;
5238    char   **cmdv, **tokv;
5239    char   *sp;
5240    SCENE  *scenep;
5241 
5242    scenep = config.scenep;
5243 
5244     /* Determine whether 2nd level command under "cmd". */
5245     /* If so, offset all arguments by 1.                */
5246     if ( strcmp(argv[1], "cmd") == 0 ) {
5247        argv++;
5248        argc--;
5249     }
5250     if ( argc < 2 )
5251        return 1;
5252 
5253     /* Special case: "help" command */
5254     if ( strcmp(argv[1], "help") == 0 ) {
5255        if ( argc < 3 )
5256 	  command_help("");
5257        else
5258 	  command_help(argv[2]);
5259        return 0;
5260     }
5261 
5262     /* Special case: "syn" (display synonyms) command */
5263     if ( strcmp(argv[1], "syn") == 0 ) {
5264        display_syn();
5265        return 0;
5266     }
5267 
5268     /* Special case: "linewidth" dummy command */
5269     if ( strcmp(argv[1], "linewidth") == 0 ) {
5270        display_linewidth_msg();
5271        return 0;
5272     }
5273 
5274     /* If the argument contains one or more ';' characters, it's */
5275     /* tentatively a compound ("command-line macro") command.    */
5276 #if 0
5277 if ( (sp = strstr(argv[1], "\\;")) != NULL ) {
5278   strncpy(sp, "; ", 2);
5279   printf("argv[1] = '%s'\n", argv[1]);
5280 }
5281 #endif
5282     if ( strchr(argv[1], ';') || strchr(argv[1], ' ') || strchr(argv[1], '\t') ) {
5283        sp = argv[1];
5284        while ( (sp = strstr(sp, "\\;")) != NULL ) {
5285           strncpy(sp, "; ", 2);
5286           sp++;
5287        }
5288        if ( verify_scene(argv[1]) != 0 ) {
5289           fprintf(stderr, "%s\n", error_message());
5290           clear_error_message();
5291           return 1;
5292        }
5293        /* Separate commands delimited by ';' */
5294        sp = strdup(argv[1]);
5295        tokenize(sp, ";", &cmdc, &cmdv);
5296        for ( k = 0; k < cmdc; k++ ) {
5297 	  strtrim(cmdv[k]);
5298 	  if ( *cmdv[k] == '\0' )
5299              continue;
5300 
5301           tokenize(cmdv[k], " \t", &tokc, &tokv);
5302 
5303           retcode = direct_command(tokc, tokv, CMD_RUN);
5304 
5305           free(tokv);
5306 
5307           if ( retcode != 0 ) {
5308              /* Display fatal message from error handler */
5309              fprintf(stderr, "%s\n", error_message());
5310              clear_error_message();
5311              free(cmdv);
5312              return retcode;
5313           }
5314        }
5315        free(cmdv);
5316        free(sp);
5317 
5318        return 0;
5319     }
5320 
5321 
5322     /* See if it's a user-defined scene */
5323     if ( (j = lookup_scene(scenep, argv[1])) >= 0 ) {
5324 
5325        if ( argc < (scenep[j].nparms + 2) ) {
5326           fprintf(stderr,
5327              "(Config %02d: %s '%s'): Too few parameters - %d required.\n",
5328                 scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
5329                 scenep[j].nparms);
5330           return 1;
5331        }
5332        else if ( argc > (scenep[j].nparms + 2) ) {
5333           fprintf(stderr,
5334              "(Config %02d: %s '%s'): Too many parameters - only %d accepted.\n",
5335                 scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
5336                 scenep[j].nparms);
5337           return 1;
5338        }
5339 
5340        sp = strdup(scenep[j].body);
5341        /* Separate scene commands delimited by ';' */
5342        tokenize(sp, ";", &cmdc, &cmdv);
5343        for ( k = 0; k < cmdc; k++ ) {
5344 	  strtrim(cmdv[k]);
5345 	  if ( *cmdv[k] == '\0' )
5346              continue;
5347           tokenize(cmdv[k], " ", &tokc, &tokv);
5348 
5349           /* Substitute for any positional parameters */
5350           /* This function allocates new memory for   */
5351           /* each parameter since the replacement may */
5352           /* be longer than the string in argv, so    */
5353           /* each must afterwarsds be freed.          */
5354           replace_dummy_parms(tokc, &tokv, argv + 1);
5355 
5356           retcode = direct_command(tokc, tokv, CMD_RUN);
5357 
5358           /* Free each member of tokv, then tokv itself */
5359           for ( m = 0; m < tokc; m++ )
5360              free(tokv[m]);
5361           free(tokv);
5362 
5363           if ( retcode != 0 ) {
5364              /* Display fatal message from error handler */
5365              fprintf(stderr, "(Config %02d: %s '%s'): %s\n",
5366                 scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
5367                 error_message());
5368              clear_error_message();
5369              free(cmdv);
5370              free(sp);
5371              return retcode;
5372           }
5373           else if ( *error_message() ) {
5374              /* Display warning from error handler */
5375              fprintf(stderr, "(Config %02d: %s '%s'): %s\n",
5376                 scenep[j].line_no, typename[scenep[j].type], scenep[j].label,
5377                 error_message());
5378           }
5379           clear_error_message();
5380        }
5381        free(cmdv);
5382        free(sp);
5383 
5384        return 0;
5385     }
5386 
5387     /* It's an individual command.                      */
5388     /* Reduce the argument list so that the X10 command */
5389     /* itself becomes argv[0]                           */
5390 
5391     retcode = direct_command(--argc, ++argv, CMD_RUN);
5392     if ( retcode != 0 ) {
5393        fprintf(stderr, "%s\n", error_message());
5394        clear_error_message();
5395        return retcode;
5396     }
5397     else if ( *error_message() ) {
5398        fprintf(stderr, "%s\n", error_message());
5399        clear_error_message();
5400     }
5401 
5402     return 0;
5403 }
5404 
5405 
5406 
5407 /*---------------------------------------------------------------------+
5408  | Display macros for final_report()                                   |
5409  | Argument 'type' is either USED or NOTUSED.                          |
5410  +---------------------------------------------------------------------*/
display_macros(FILE * fd,unsigned char type,MACRO * macrop,unsigned char * elementp)5411 int display_macros ( FILE *fd, unsigned char type,
5412                            MACRO *macrop, unsigned char *elementp )
5413 {
5414    int           i, j, k, m, switchval, label_max, minargs;
5415    char          delim, modflag;
5416    unsigned char cmdcode, subcode, subsubcode, hcode, subunit, bflag, dim;
5417    unsigned int  bmap;
5418    unsigned long flags;
5419 
5420    /* Find the maximum length of a macro label */
5421    label_max = 0;
5422    j = 0;
5423    while ( macrop[j].line_no > 0 ) {
5424       /* Skip null macro and those of the wrong type (used/unused) */
5425       if ( macrop[j].use != type || macrop[j].isnull ) {
5426          j++;
5427          continue;
5428       }
5429 
5430       /* Skip derived macros which aren't used */
5431       if ( type == NOTUSED && (macrop[j].refer & DERIVED_MACRO) ) {
5432          j++;
5433          continue;
5434       }
5435 
5436       label_max = max(label_max, (int)strlen(macrop[j].label) );
5437       j++;
5438    }
5439 
5440    if ( label_max == 0 )
5441       return 0;
5442 
5443    /* Now display the macros */
5444    j = 0;
5445    while ( macrop[j].line_no > 0 ) {
5446       if ( macrop[j].use != type || macrop[j].isnull ) {
5447          j++;
5448          continue;
5449       }
5450 
5451       /* Skip derived macros which aren't used */
5452       if ( type == NOTUSED && (macrop[j].refer & DERIVED_MACRO) ) {
5453          j++;
5454          continue;
5455       }
5456 
5457       modflag = (macrop[j].modflag == UNMODIFIED) ? ' ' : '*' ;
5458 
5459       if ( type == USED && configp->display_offset == YES )
5460          (void) fprintf( fd, "%03x macro  %-*s %3d %c", macrop[j].offset,
5461              label_max, macrop[j].label, macrop[j].delay, modflag);
5462       else
5463          (void) fprintf( fd, "macro  %-*s %3d %c", label_max,
5464              macrop[j].label, macrop[j].delay, modflag);
5465 
5466 
5467       delim = ' ';
5468       i = macrop[j].element ;
5469       for ( k = 0; k < macrop[j].nelem; k++ ) {
5470          cmdcode = elementp[i] & 0x0f;
5471          hcode = code2hc((elementp[i] & 0xf0u) >> 4);
5472          bmap = elementp[i+1] << 8 | elementp[i+2];
5473          switchval = -1;
5474 
5475          bflag = 0;
5476          if ( cmdcode == 4 || cmdcode == 5 ) {
5477             /* Dim or Bright command                    */
5478             /* Check both command code and brighten bit */
5479             bflag = (elementp[i+3] & 0x80) ? F_BRI : 0;
5480             for ( m = 0; m < nx10cmds; m++ ) {
5481                if ( x10command[m].code == cmdcode &&
5482                     (x10command[m].flags & F_BRI) == bflag ) {
5483                   switchval = m;
5484                   break;
5485                }
5486             }
5487          }
5488          else if ( cmdcode == 10 || cmdcode == 11 ) {
5489             /* Preset command */
5490             if ( bmap == 0 ) {
5491                /* Check for preset_level only */
5492                for ( m = 0; m < nx10cmds; m++ ) {
5493                   if ( x10command[m].code == cmdcode &&
5494                        x10command[m].flags & F_NOA ) {
5495                      switchval = m;
5496                      break;
5497                   }
5498                }
5499             }
5500             else {
5501                /* Check for mpreset */
5502                for ( m = 0; m < nx10cmds; m++ ) {
5503                   if ( x10command[m].code == cmdcode &&
5504                        x10command[m].flags & F_MAC ) {
5505                      switchval = m;
5506                      break;
5507                   }
5508                }
5509             }
5510          }
5511          else {
5512             for ( m = 0; m < nx10cmds; m++ ) {
5513                if ( x10command[m].code == cmdcode ) {
5514                   switchval = m;
5515                   break;
5516                }
5517             }
5518          }
5519 
5520          if ( switchval < 0 || switchval == nx10cmds ) {
5521             (void) fprintf(stderr, "display_macros(): Internal error 1\n");
5522 
5523             break;
5524          }
5525 
5526          switch ( cmdcode ) {
5527             case 0 :  /* All Units Off */
5528             case 1 :  /* All Lights On */
5529             case 6 :  /* All Lights Off */
5530             case 8 :  /* Hail Request */
5531             case 9 :  /* Hail Ack */
5532             case 12 : /* Data Transfer */
5533 	    case 13 : /* Status On */
5534 	    case 14 : /* Status Off */
5535                /* Bitmap is normally zero for these commands */
5536                if ( bmap ) {
5537                   /* Display the '+' prefixed HC and unit */
5538                   fprintf( fd, "%c %s +%c%s",
5539                       delim, x10command[switchval].label, hcode,
5540                       bmap2units(bmap) );
5541                }
5542                else {
5543                   /* Just the HC */
5544                   fprintf( fd, "%c %s %c",
5545                       delim, x10command[switchval].label, hcode);
5546                }
5547 
5548                i += x10command[switchval].length;
5549                break;
5550 
5551             case 2 :  /* On */
5552             case 3 :  /* Off */
5553             case 15 : /* Status Request */
5554                if ( bmap ) {
5555                   fprintf( fd, "%c %s %c%s",
5556                       delim, x10command[switchval].label, hcode,
5557                       bmap2units(bmap) );
5558                }
5559                else {
5560                   fprintf( fd, "%c %s -%c",
5561                       delim, x10command[switchval].label, hcode);
5562                }
5563 
5564                i += x10command[switchval].length;
5565                break;
5566 
5567             case 4 :  /* Dim or Dimb */
5568             case 5 :  /* Bright or Brightb */
5569                if ( bmap ) {
5570                   fprintf( fd, "%c %s %c%s %d",
5571                      delim, x10command[switchval].label, hcode,
5572                      bmap2units(bmap), elementp[i+3] & 0x1f );
5573                }
5574                else {
5575                   fprintf( fd, "%c %s -%c %d",
5576                      delim, x10command[switchval].label, hcode,
5577                      elementp[i+3] & 0x1f );
5578                }
5579 
5580                i += x10command[switchval].length;
5581 
5582                break;
5583 
5584             case 10 : /* Preset Dim (1) - mpreset or level only */
5585             case 11 : /* Preset Dim (2) - mpreset or level only */
5586                dim = rev_low_nybble((elementp[i] & 0xf0u) >> 4) + 1;
5587                dim += (cmdcode == 11) ? 16 : 0;
5588                if ( bmap == 0 ) {
5589                   fprintf( fd, "%c %s %d",
5590                      delim, x10command[switchval].label, dim);
5591                }
5592                else {
5593                   fprintf( fd, "%c %s %c%s %d",
5594                      delim, x10command[switchval].label,
5595                        hcode, bmap2units(bmap), dim );
5596                }
5597 
5598                i += x10command[switchval].length;
5599                break;
5600 
5601             case  7 : /* Extended Code */
5602                subcode = elementp[i+5];
5603                if ( subcode == 0x37 || subcode == 0x36 || subcode == 0x35 || subcode == 0x3c ) {
5604                   subsubcode = (elementp[i+4] & 0x30) >> 4;
5605                   for ( m = 0; m < nx10cmds; m++ ) {
5606                      if ( x10command[m].code == cmdcode &&
5607                           x10command[m].subcode == subcode &&
5608                           x10command[m].subsubcode == subsubcode )
5609                         break;
5610                   }
5611                }
5612                else {
5613                   for ( m = 0; m < nx10cmds; m++ ) {
5614                      if ( x10command[m].code == cmdcode &&
5615                           x10command[m].subcode == subcode )
5616                        break;
5617                   }
5618                }
5619 
5620                /* If not standard command, locate entry for arbitrary code */
5621                if ( m >= nx10cmds ) {
5622                   for ( m = 0; m < nx10cmds; m++ ) {
5623                      if ( !(strcmp(x10command[m].label, XARBNAME)) )
5624                         break;
5625                   }
5626                }
5627 
5628                flags = x10command[m].flags;
5629                minargs = x10command[m].minargs;
5630                subsubcode = x10command[m].subsubcode;
5631 
5632                if ( flags & F_ARB )
5633                   fprintf( fd, "%c %s %02X ", delim, x10command[m].label, subcode);
5634                else
5635                   fprintf( fd, "%c %s ", delim, x10command[m].label);
5636 
5637                /* Display the HC, prefixed with a '+' if bitmap is not 0 */
5638                if ( bmap )
5639                   fprintf(fd, "+%c", hcode);
5640                else
5641                   fprintf( fd, "%c", hcode);
5642 
5643                /* Display the unit, if any */
5644                if ( !(flags & F_ALL) || flags & F_FAD || flags & F_ARB ) {
5645                   fprintf( fd, "%d", code2unit(elementp[i+3] & 0x0f) );
5646 		  subunit = (elementp[i+3] & 0xf0u) >> 4;
5647 		  if ( subunit > 0 )
5648 		     fprintf( fd, "(%02x)", subunit );
5649 	       }
5650                else if ( flags & F_ALL && bmap ) {
5651                   fprintf( fd, "%s", bmap2units(bmap) );
5652                }
5653 
5654                /* Display the data byte info if required */
5655                if ( subcode == 0x30u || subcode == 0x36u || subcode == 0x3cu ) {
5656                   if ( elementp[i+4] & 0x20u )
5657                      fprintf(fd, " %d.%-2d", elementp[i+4] >> 6, (elementp[i+4] & 0x0fu) + 1);
5658                   else
5659                      fprintf(fd, " %d", elementp[i+4] >> 6);
5660                }
5661                else if ( subcode == 0x35u ) {
5662                   fprintf( fd, " %s", linmap2list(elementp[i+4] & 0x0f));
5663                }
5664                else if ( subcode == 0x37u && (elementp[i+4] & 0x20u) ) {
5665                   if ( elementp[i+4] & 0x10u )
5666                      fprintf(fd, " %d.%-2d", elementp[i+4] >> 6, (elementp[i+4] & 0x0fu) + 1);
5667                   else
5668                      fprintf(fd, " %d", elementp[i+4] >> 6);
5669                }
5670                else if ( flags & F_GRP ) {
5671                   if ( minargs > 1 )
5672                      fprintf( fd, " %d", elementp[i+4] >> 6);
5673                   if ( minargs > 2 )
5674                      fprintf( fd, " %02d", elementp[i+4] & 0x3f);
5675                }
5676                else if ( flags & F_DED )
5677                   fprintf( fd, " %02d", elementp[i+4]);
5678                else if ( flags & F_ARB )
5679                   fprintf( fd, " %02X", elementp[i+4]);
5680 
5681                i += x10command[m].length;
5682                break;
5683 
5684             default :
5685                fprintf(stderr, "display_macros(): Internal error 2.\n");
5686                break;
5687          }
5688 
5689          delim = ';';
5690       }
5691       (void) fprintf( fd, "\n" );
5692       j++ ;
5693    }
5694 
5695    return 1;
5696 }
5697 
5698 /*---------------------------------------------------------------------+
5699  | Display error message and return 1 if configured for a CM10A        |
5700  | interface.  Otherwise just return 0.                                |
5701  +---------------------------------------------------------------------*/
invalidate_for_cm10a(void)5702 int invalidate_for_cm10a ( void )
5703 {
5704    if ( configp->device_type & DEV_CM10A ) {
5705       fprintf(stderr, "Command is invalid for CM10A interface.\n");
5706       return 1;
5707    }
5708    return 0;
5709 }
5710 
5711 /*---------------------------------------------------------------------+
5712  | Instruct daemons to re-read the config file.                        |
5713  +---------------------------------------------------------------------*/
c_restart_old(int argc,char * argv[])5714 int c_restart_old ( int argc, char *argv[] )
5715 {
5716    send_x10state_command(ST_RESTART, 0);
5717    return 0;
5718 }
5719 
5720 
5721 /*---------------------------------------------------------------------+
5722  | Instruct daemons to re-read the config file.                        |
5723  +---------------------------------------------------------------------*/
c_restart(int argc,char * argv[])5724 int c_restart ( int argc, char *argv[] )
5725 {
5726    FILE *fp;
5727    char message[127];
5728    char restart_r[PATH_LEN + 1];
5729    char restart_a[PATH_LEN + 1];
5730    int  rflag = 1, aflag = 0;
5731    int  count = 100;
5732    struct stat rstat, astat;
5733 
5734    char *ignoretp;
5735 
5736    strncpy2(restart_r, pathspec(RESTART_RELAY_FILE), PATH_LEN);
5737 
5738    if ( config.ttyaux[0] != '\0' /* check_for_aux() == 0 */ ) {
5739       strncpy2(restart_a, pathspec(RESTART_AUX_FILE), PATH_LEN);
5740       aflag = 1;
5741    }
5742 
5743    if ( !(fp = fopen(restart_r, "w")) ) {
5744       fprintf(stderr, "Unable to restart heyu_relay.\n");
5745       return 1;
5746    }
5747    fclose(fp);
5748 
5749    if ( aflag ) {
5750       if ( !(fp = fopen(restart_a, "w")) ) {
5751          fprintf(stderr, "Unable to restart heyu_aux.\n");
5752          unlink(restart_r);
5753          return 1;
5754       }
5755       fclose(fp);
5756    }
5757 
5758    while ( count-- && (rflag || aflag) ) {
5759       millisleep(100);
5760       rflag = (rflag && !stat(restart_r, &rstat)) ? 1 : 0;
5761       aflag = (aflag && !stat(restart_a, &astat)) ? 1 : 0;
5762    }
5763 
5764    if ( count == 0 ) {
5765       if ( rflag ) {
5766          fp = fopen(restart_r, "r");
5767          ignoretp = fgets(message, sizeof(message), fp);
5768          fprintf(stderr, "%s", message);
5769          fclose(fp);
5770          fprintf(stderr, "Restart heyu_relay failed.\n");
5771          unlink(restart_r);
5772       }
5773       if ( aflag ) {
5774          fp = fopen(restart_a, "r");
5775          ignoretp = fgets(message, sizeof(message), fp);
5776          fprintf(stderr, "%s", message);
5777          fclose(fp);
5778          fprintf(stderr, "Restart heyu_aux failed.\n");
5779          unlink(restart_a);
5780       }
5781       return 1;
5782    }
5783 
5784    return 0;
5785 }
5786 
5787 /*---------------------------------------------------------------------+
5788  | Global script enable/disable                                        |
5789  +---------------------------------------------------------------------*/
c_script_ctrl(int argc,char * argv[])5790 int c_script_ctrl ( int argc, char *argv[] )
5791 {
5792    extern int check_for_engine ( void );
5793    int funct;
5794 
5795    if ( argc != 3 ||
5796       (funct = (strcmp(strlower(argv[2]), "disable") == 0) ? NO  :
5797                (strcmp(argv[2], "enable") == 0) ? YES : -1) < 0 ) {
5798       fprintf(stderr, "Usage: %s %s disable|enable\n", argv[0], argv[1]);
5799       return 1;
5800    }
5801 
5802    if ( check_for_engine() != 0 ) {
5803       fprintf(stderr, "Heyu state engine is not running.\n");
5804       return 1;
5805    }
5806 
5807    send_x10state_command(ST_LAUNCH, (unsigned char)funct);
5808    return 0;
5809 }
5810 
5811 
5812 /*---------------------------------------------------------------------+
5813  | Test powerfail script                                               |
5814  +---------------------------------------------------------------------*/
c_powerfailtest(int argc,char * argv[])5815 int c_powerfailtest ( int argc, char *argv[] )
5816 {
5817    extern int check_for_engine ( void );
5818    unsigned char bootflag = R_NOTATSTART;
5819 
5820    if ( argc < 3 ) {
5821       fprintf(stderr, "Parameter 'boot' or 'notboot' required.\n");
5822       return 1;
5823    }
5824 
5825    strlower(argv[2]);
5826    if ( strcmp(argv[2], "boot") == 0 )
5827       bootflag = R_ATSTART;
5828    else if ( strcmp(argv[2], "notboot") == 0 )
5829       bootflag = R_NOTATSTART;
5830    else {
5831       fprintf(stderr, "Parameter must be 'boot' or 'notboot'\n");
5832       return 1;
5833    }
5834 
5835    if ( check_for_engine() != 0 ) {
5836       fprintf(stderr, "State engine is not running.\n");
5837       return 1;
5838    }
5839 
5840    send_x10state_command(ST_PFAIL, bootflag);
5841 
5842    return 0;
5843 }
5844 
5845 /*---------------------------------------------------------------------+
5846  | Get security flags for home, away, min, max.                        |
5847  +---------------------------------------------------------------------*/
get_armed_parms(int argc,char * argv[],unsigned char * sflags,unsigned char mode)5848 int get_armed_parms ( int argc, char *argv[], unsigned char *sflags, unsigned char mode )
5849 {
5850    int           j;
5851    unsigned long lflags;
5852    char          msg[80];
5853 
5854 #if 0
5855    if ( check_for_engine() != 0 ) {
5856       store_error_message("Engine is not running.");
5857       return 1;
5858    }
5859 #endif
5860 
5861    lflags = GLOBSEC_ARMED;
5862    *sflags = (lflags >> GLOBSEC_SHIFT);
5863 
5864    for ( j = 1; j < argc; j++ ) {
5865       if ( strchr(argv[j], '$') && mode == CMD_VERIFY )
5866          continue;
5867       strlower(argv[j]);
5868       if ( strcmp(argv[j], "home") == 0 )
5869          lflags |= GLOBSEC_HOME;
5870       else if ( strcmp(argv[j], "away") == 0 )
5871          lflags &= ~GLOBSEC_HOME;
5872       else if ( strcmp(argv[j], "min") == 0 ) {
5873          lflags |= GLOBSEC_ARMED;
5874          lflags &= ~GLOBSEC_PENDING;
5875       }
5876       else if ( strcmp(argv[j], "max") == 0 ) {
5877          lflags &= ~GLOBSEC_ARMED;
5878          lflags |= GLOBSEC_PENDING;
5879       }
5880       else {
5881          sprintf(msg, "Invalid arm parameter '%s'\n", argv[j]);
5882          store_error_message(msg);
5883          return 1;
5884       }
5885    }
5886 
5887    *sflags = (lflags >> GLOBSEC_SHIFT);
5888 
5889    return 0;
5890 }
5891 
5892 /*---------------------------------------------------------------------+
5893  | Set the system flags for Armed or Disarmed - LEGACY                 |
5894  +---------------------------------------------------------------------*/
c_arm(int argc,char * argv[])5895 int c_arm ( int argc, char *argv[] )
5896 {
5897    int j;
5898    unsigned long sflags;
5899 
5900    if ( argc < 2 ) {
5901       fprintf(stderr, "Too few parameters.\n");
5902       return 1;
5903    }
5904 
5905    if ( check_for_engine() != 0 ) {
5906       fprintf(stderr, "Engine is not running.\n");
5907       return 1;
5908    }
5909 
5910 
5911    if ( strcmp(argv[1], "_disarm") == 0 ) {
5912       send_x10state_command(ST_SECFLAGS, 0);
5913       return 0;
5914    }
5915    else if ( strcmp(argv[1], "_arm") != 0 ) {
5916       fprintf(stderr, "Invalid command '%s'\n", argv[1]);
5917       return 1;
5918    }
5919 
5920    sflags = GLOBSEC_ARMED;
5921 
5922    for ( j = 2; j < argc; j++ ) {
5923       strlower(argv[j]);
5924       if ( strcmp(argv[j], "home") == 0 )
5925          sflags |= GLOBSEC_HOME;
5926       else if ( strcmp(argv[j], "away") == 0 )
5927          continue;
5928       else if ( strcmp(argv[j], "min") == 0 ) {
5929          sflags |= GLOBSEC_ARMED;
5930          sflags &= ~GLOBSEC_PENDING;
5931       }
5932       else if ( strcmp(argv[j], "max") == 0 ) {
5933          sflags &= ~GLOBSEC_ARMED;
5934          sflags |= GLOBSEC_PENDING;
5935       }
5936       else {
5937          fprintf(stderr, "Invalid arm parameter '%s'\n", argv[j]);
5938          return 1;
5939       }
5940    }
5941 
5942    sflags = sflags >> GLOBSEC_SHIFT;
5943 
5944    send_x10state_command(ST_SECFLAGS, (unsigned char)sflags);
5945 
5946    return 0;
5947 }
5948 
5949 /*---------------------------------------------------------------------+
5950  | Read ext3 code group as either absolute, e.g., "0", or relative,    |
5951  | e.g. "0.1").  Subgroups are entered and displayed as numbered 1-16  |
5952  | but converted to 0x80-0x8f to distinguish internal subgroup 0 from  |
5953  | absolute group 0                                                    |
5954  +---------------------------------------------------------------------*/
parse_ext3_group(char * token,unsigned char * group,unsigned char * subgroup)5955 int parse_ext3_group ( char *token, unsigned char *group, unsigned char *subgroup )
5956 {
5957    char errmsg[80];
5958    char *sp, *tp;
5959    int  igroup;
5960 
5961    igroup = (int)strtol(token, &sp, 10);
5962 
5963    if ( strchr(" ./t/n", *sp) == NULL || igroup < 0 || igroup > 3 ) {
5964       sprintf(errmsg, "Invalid group '%s'", token);
5965       store_error_message(errmsg);
5966       return 1;
5967    }
5968    else if ( *sp == '.' ) {
5969       *group = (unsigned char)igroup;
5970       tp = sp + 1;
5971       igroup = (int)strtol(tp, &sp, 10);
5972       if ( strchr(" /t/n", *sp) == NULL || igroup < 0 || igroup > 16 ) {
5973          sprintf(errmsg, "Invalid subgroup '%s'", tp);
5974          store_error_message(errmsg);
5975          return 1;
5976       }
5977       else if ( igroup == 0 ) {
5978          *subgroup = 0;
5979       }
5980       else {
5981          *subgroup = (unsigned char)(igroup - 1) | 0x80;
5982       }
5983    }
5984    else {
5985       *group = (unsigned char)igroup;
5986       *subgroup = 0;
5987    }
5988 
5989    return 0;
5990 }
5991 
5992 
restore_groups(unsigned char hcode)5993 int restore_groups ( unsigned char hcode )
5994 {
5995 
5996    int           group, lastgroup;
5997    int           tokc;
5998    char          **tokv = NULL;
5999    unsigned char ucode, xdata, gflag, grflag;
6000    int           unit, xgrc;
6001    unsigned int  bitmap;
6002    char          hc;
6003    char          cmdline[80];
6004    unsigned char grpmember[16], grpaddr[16], grplevel[16][4], xconfigmode;
6005 
6006    extern unsigned int modmask[][16];
6007    extern int fetch_x10state ( void );
6008    extern int copy_xmodule_info ( unsigned char, unsigned char [], unsigned char [],
6009               unsigned char [][4], unsigned char * );
6010 
6011    if ( fetch_x10state() != 0 )
6012       return 1;
6013 
6014    hc = code2hc(hcode);
6015 
6016    /* Copy xmodule info from x10state table to temp arrays */
6017    copy_xmodule_info(hcode, grpmember, grpaddr, grplevel, &xconfigmode);
6018 
6019    /* Determine if groups and group reference is used for any unit */
6020    grflag = gflag = 0;
6021    for ( ucode = 0; ucode < 16; ucode++ ) {
6022       if ( grpmember[ucode] & 0x0fu ) {
6023          gflag = 1;
6024          if ( grpaddr[ucode] & 0x80u ) {
6025             grflag = 1;
6026             break;
6027          }
6028       }
6029    }
6030 
6031    if ( gflag == 0 ) {
6032       /* No groups - restore xconfig mode if required and return */
6033       if ( modmask[Ext3StatusMask][hcode] & 0xffff ) {
6034          sprintf(cmdline, "%s 3b %c1 %02x", XARBNAME, hc, xconfigmode);
6035          tokenize(cmdline, " ", &tokc, &tokv);
6036          direct_command(tokc, tokv, CMD_RUN);
6037          free(tokv);
6038       }
6039       return 0;
6040    }
6041 
6042    /* Remove all units from all groups via xfunc 35 */
6043    sprintf(cmdline, "%s 35 %c1 ff", XARBNAME, hc);
6044    tokenize(cmdline, " ", &tokc, &tokv);
6045    direct_command(tokc, tokv, CMD_RUN);
6046    free(tokv);
6047 
6048    if ( grflag && xconfigmode > 0 ) {
6049       /* Disable extended status ack */
6050       sprintf(cmdline, "%s 3b %c1 00", XARBNAME, hc);
6051       tokenize(cmdline, " ", &tokc, &tokv);
6052       direct_command(tokc, tokv, CMD_RUN);
6053       free(tokv);
6054    }
6055 
6056    for ( ucode = 0; ucode < 16; ucode++ ) {
6057       if ( (grpmember[ucode] & 0x0fu) == 0 )
6058          continue;
6059 
6060       unit = code2unit(ucode);
6061       bitmap = 1 << ucode;
6062 
6063       xgrc = (modmask[Ext3GrpRelT1Mask][hcode] & bitmap) ? 1 :
6064              (modmask[Ext3GrpRelT2Mask][hcode] & bitmap) ? 2 :
6065              (modmask[Ext3GrpRelT3Mask][hcode] & bitmap) ? 3 :
6066              (modmask[Ext3GrpRelT4Mask][hcode] & bitmap) ? 4 : 0;
6067 
6068       lastgroup = 4;
6069       for ( group = 0; group < 4; group++ ) {
6070          if ( (grpmember[ucode] & (1 << group)) == 0 )
6071             continue;
6072 
6073          if ( grpaddr[ucode] & 0x80u ) {
6074             /* Relative to group reference */
6075             /* Preset module to level with xfunc 31 */
6076             xdata = grplevel[ucode][group] & 0x3fu;
6077             sprintf(cmdline, "%s 31 %c%d %02x", XARBNAME, hc, unit, xdata);
6078             tokenize(cmdline, " ", &tokc, &tokv);
6079             if ( direct_command(tokc, tokv, CMD_RUN) != 0 )
6080                fprintf(stderr, "%s\n", error_message());
6081             free(tokv);
6082             /* Add to group with reference with xfunc 30 */
6083             xdata = (group << 6) | 0x20 | (grpaddr[ucode] & 0x0fu);
6084             sprintf(cmdline, "%s 30 %c%d %02x", XARBNAME, hc, unit, xdata);
6085             tokenize(cmdline, " ", &tokc, &tokv);
6086             if ( direct_command(tokc, tokv, CMD_RUN) != 0 )
6087                fprintf(stderr, "%s\n", error_message());
6088             free(tokv);
6089          }
6090          else {
6091             /* Set Absolute group with xfunc 32 */
6092             xdata = (group << 6) | (grplevel[ucode][group] & 0x3fu);
6093             sprintf(cmdline, "%s 32 %c%d %02x", XARBNAME, hc, unit, xdata);
6094             tokenize(cmdline, " ", &tokc, &tokv);
6095             if ( direct_command(tokc, tokv, CMD_RUN) != 0 )
6096                fprintf(stderr, "%s\n", error_message());
6097             free(tokv);
6098          }
6099       }
6100    }
6101 
6102    /* Restore xstatus if required */
6103    if ( modmask[Ext3StatusMask][hcode] & 0xffff ) {
6104       sprintf(cmdline, "%s 3b %c1 %02x", XARBNAME, hc, xconfigmode);
6105       tokenize(cmdline, " ", &tokc, &tokv);
6106       direct_command(tokc, tokv, CMD_RUN);
6107       free(tokv);
6108    }
6109 
6110    return 0;
6111 }
6112 
c_restore_groups(int argc,char * argv[])6113 int c_restore_groups ( int argc, char *argv[] )
6114 {
6115    char hc;
6116 
6117    for ( hc = 'A'; hc <= 'P'; hc++ ) {
6118       if ( restore_groups(hc2code(hc)) != 0 ) {
6119          fprintf(stderr, "restore_groups failed for housecode %c\n", hc);
6120          return 1;
6121       }
6122    }
6123 
6124    return 0;
6125 }
6126 
6127 
6128 #if 0
6129 void display_help ( char *helplist[][3] )
6130 {
6131    int  j;
6132    int  szlbl, szarg, sztot;
6133    char buffer[128];
6134 
6135    /* Get max string lengths for formatting output */
6136    szlbl = szarg = sztot = 0;
6137    j = 0;
6138    while ( helplist[j][0] != NULL ) {
6139       szlbl = max(szlbl, (int)strlen(helplist[j][0]));
6140       szarg = max(szarg, (int)strlen(helplist[j][1]));
6141       sztot = max(sztot, ((int)strlen(helplist[j][0]) +
6142                                (int)strlen(helplist[j][1])));
6143       j++;
6144    }
6145 
6146    j = 0;
6147    while ( helplist[j][0] != NULL ) {
6148       #ifdef COMPACT_FORMAT
6149          /* Compact format */
6150          sprintf(buffer, "%s  %s", helplist[j][0], helplist[j][1]);
6151          printf("  heyu  %-*s  %s\n", sztot + 2, buffer, helplist[j][2]);
6152       #else
6153          printf("  heyu  %-*s %-*s  %s\n", szlbl, helplist[j][0],
6154           szarg, helplist[j][1], helplist[j][2]);
6155       #endif  /* End ifdef */
6156       j++;
6157    }
6158    return;
6159 }
6160 #endif
6161 
6162 
6163 #if 0
6164 void display_dir_help ( unsigned long flags, unsigned long notflags )
6165 {
6166    int  j;
6167    int  szlbl, szarg, sztot;
6168    char buffer[128];
6169    struct cmd_list *one = NULL, *two;
6170 
6171    /* Direct commands */
6172    /* Get max string lengths for formatting output */
6173    szlbl = szarg = sztot = 0;
6174    for ( j = 0; j < nx10cmds; j++ ) {
6175       if ( x10command[j].flags & F_HID ||
6176            x10command[j].flags & notflags ||
6177            !(x10command[j].flags & flags) )  {
6178          continue;
6179       }
6180       sztot = max(sztot, ((int)strlen(x10command[j].label) +
6181                       (int)strlen(helparg[x10command[j].argtype])) );
6182    }
6183    for ( j = 0; j < nx10cmds; j++ ) {
6184       two = &x10command[j];
6185       if ( cmp_commands(one, two) ) {
6186          sprintf(buffer, "%s  %s", x10command[j].label, helparg[x10command[j].argtype]);
6187          printf("  heyu  %-*s  %s%s\n", sztot + 2, buffer,
6188              helpdirect[x10command[j].help],
6189              (x10command[j].flags & (F_NMA | F_NMAI)) ? " (*)" : "");
6190          one = two;
6191       }
6192    }
6193    return;
6194 }
6195 #endif
6196 
6197 #if 0
6198 int c_logtail_new ( int argc, char *argv[] )
6199 {
6200    int  lines;
6201    char cmdline[PATH_LEN + PATH_LEN + 20];
6202    char *sp;
6203 
6204    get_configuration(CONFIG_INIT);
6205 
6206    if ( strcmp(configp->logfile, "/dev/null") == 0 ) {
6207       fprintf(stderr, "LOG_DIR not defined in config file.\n");
6208       return 1;
6209    }
6210 
6211    if ( *configp->tailpath == '\0' ) {
6212       strcpy(cmdline, "tail");
6213    }
6214    else {
6215       strncpy2(cmdline, configp->tailpath, PATH_LEN);
6216    }
6217 
6218    if ( argc == 3 ) {
6219       if ( strcmp(argv[2], "-f") == 0 ) {
6220          sprintf(cmdline + strlen(cmdline), " -f %s", configp->logfile);
6221          system(cmdline);
6222          for (;;) {
6223             sleep(1);
6224          }
6225       }
6226 
6227       lines = (int)strtol(argv[2], &sp, 10);
6228       if ( strchr(" \t\r\n", *sp) == NULL ) {
6229          fprintf(stderr, "Invalid 'tail' lines '%s'\n", argv[2]);
6230          return 1;
6231       }
6232       lines = (lines < 0) ? -lines : lines;
6233       sprintf(cmdline + strlen(cmdline), " -%d", lines);
6234    }
6235 
6236    sprintf(cmdline + strlen(cmdline), " %s", configp->logfile);
6237 
6238    return system(cmdline);
6239 
6240 }
6241 #endif
6242 
c_logtail(int argc,char * argv[])6243 int c_logtail ( int argc, char *argv[] )
6244 {
6245    int  lines;
6246    char cmdline[PATH_LEN + PATH_LEN + 20];
6247    char *sp;
6248 
6249    get_configuration(CONFIG_INIT);
6250 
6251    if ( strcmp(configp->logfile, "/dev/null") == 0 ) {
6252       fprintf(stderr, "LOG_DIR not defined in config file.\n");
6253       return 1;
6254    }
6255 
6256    if ( *configp->tailpath == '\0' ) {
6257       strcpy(cmdline, "tail");
6258    }
6259    else {
6260       strcpy(cmdline, configp->tailpath);
6261    }
6262 
6263    if ( argc == 3 ) {
6264       lines = (int)strtol(argv[2], &sp, 10);
6265       if ( strchr(" \t\r\n", *sp) == NULL ) {
6266          fprintf(stderr, "Invalid 'tail' lines '%s'\n", argv[2]);
6267          return 1;
6268       }
6269       lines = (lines < 0) ? -lines : lines;
6270       sprintf(cmdline + strlen(cmdline), " -%d", lines);
6271    }
6272 
6273    sprintf(cmdline + strlen(cmdline), " %s", configp->logfile);
6274 
6275    return system(cmdline);
6276 
6277 }
6278 
6279 
6280