| 1 | #if 0
 | 
|---|
| 2 |     INDI
 | 
|---|
| 3 |     Copyright (C) 2003-2006 Elwood C. Downey
 | 
|---|
| 4 | 
 | 
|---|
| 5 |                         Modified by Jasem Mutlaq (2003-2006)
 | 
|---|
| 6 | 
 | 
|---|
| 7 |     This library is free software; you can redistribute it and/or
 | 
|---|
| 8 |     modify it under the terms of the GNU Lesser General Public
 | 
|---|
| 9 |     License as published by the Free Software Foundation; either
 | 
|---|
| 10 |     version 2.1 of the License, or (at your option) any later version.
 | 
|---|
| 11 | 
 | 
|---|
| 12 |     This library is distributed in the hope that it will be useful,
 | 
|---|
| 13 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
| 14 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
|---|
| 15 |     Lesser General Public License for more details.
 | 
|---|
| 16 | 
 | 
|---|
| 17 |     You should have received a copy of the GNU Lesser General Public
 | 
|---|
| 18 |     License along with this library; if not, write to the Free Software
 | 
|---|
| 19 |     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 | 
|---|
| 20 | 
 | 
|---|
| 21 | #endif
 | 
|---|
| 22 | 
 | 
|---|
| 23 | /* main() for one INDI driver process.
 | 
|---|
| 24 |  * Drivers define IS*() functions we call to deliver INDI XML arriving on stdin.
 | 
|---|
| 25 |  * Drivers call ID*() functions to send INDI XML commands to stdout.
 | 
|---|
| 26 |  * Drivers call IE*() functions to build an event-driver program.
 | 
|---|
| 27 |  * Drivers call IU*() functions to perform various common utility tasks.
 | 
|---|
| 28 |  * Troubles are reported on stderr then we exit.
 | 
|---|
| 29 |  *
 | 
|---|
| 30 |  * This requires liblilxml.
 | 
|---|
| 31 |  */
 | 
|---|
| 32 | 
 | 
|---|
| 33 | #include <stdio.h>
 | 
|---|
| 34 | #include <stdlib.h>
 | 
|---|
| 35 | #include <stdarg.h>
 | 
|---|
| 36 | #include <string.h>
 | 
|---|
| 37 | #include <errno.h>
 | 
|---|
| 38 | #include <time.h>
 | 
|---|
| 39 | #include <unistd.h>
 | 
|---|
| 40 | #include <sys/types.h>
 | 
|---|
| 41 | #include <sys/stat.h>
 | 
|---|
| 42 | 
 | 
|---|
| 43 | #include "lilxml.h"
 | 
|---|
| 44 | #include "base64.h"
 | 
|---|
| 45 | #include "eventloop.h"
 | 
|---|
| 46 | #include "indidevapi.h"
 | 
|---|
| 47 | #include "indicom.h"
 | 
|---|
| 48 | 
 | 
|---|
| 49 | static void usage(void);
 | 
|---|
| 50 | static void clientMsgCB(int fd, void *arg);
 | 
|---|
| 51 | static int dispatch (XMLEle *root, char msg[]);
 | 
|---|
| 52 | static int crackDN (XMLEle *root, char **dev, char **name, char msg[]);
 | 
|---|
| 53 | static int isPropDefined(const char *property_name);
 | 
|---|
| 54 | static int crackIPState (const char *str, IPState *ip);
 | 
|---|
| 55 | static int crackISState (const char *str, ISState *ip);
 | 
|---|
| 56 | static void xmlv1(void);
 | 
|---|
| 57 | const char *pstateStr(IPState s);
 | 
|---|
| 58 | const char *sstateStr(ISState s);
 | 
|---|
| 59 | const char *ruleStr(ISRule r);
 | 
|---|
| 60 | const char *permStr(IPerm p);
 | 
|---|
| 61 | 
 | 
|---|
| 62 | 
 | 
|---|
| 63 | static int nroCheck;                    /* # of elements in roCheck */
 | 
|---|
| 64 | static int verbose;                     /* chatty */
 | 
|---|
| 65 | char *me;                               /* a.out name */
 | 
|---|
| 66 | static LilXML *clixml;                  /* XML parser context */
 | 
|---|
| 67 | 
 | 
|---|
| 68 | /* insure RO properties are never modified. RO Sanity Check */
 | 
|---|
| 69 | typedef struct
 | 
|---|
| 70 | {
 | 
|---|
| 71 |   char propName[MAXINDINAME];
 | 
|---|
| 72 |   IPerm perm;
 | 
|---|
| 73 | } ROSC;
 | 
|---|
| 74 | 
 | 
|---|
| 75 | static ROSC *roCheck;
 | 
|---|
| 76 | 
 | 
|---|
| 77 | int
 | 
|---|
| 78 | main (int ac, char *av[])
 | 
|---|
| 79 | {
 | 
|---|
| 80 | #ifndef _WIN32
 | 
|---|
| 81 |         setgid( getgid() );
 | 
|---|
| 82 |         setuid( getuid() );
 | 
|---|
| 83 | 
 | 
|---|
| 84 |         if (geteuid() != getuid())
 | 
|---|
| 85 |             exit(255);
 | 
|---|
| 86 | #endif
 | 
|---|
| 87 | 
 | 
|---|
| 88 |         /* save handy pointer to our base name */
 | 
|---|
| 89 |         for (me = av[0]; av[0][0]; av[0]++)
 | 
|---|
| 90 |             if (av[0][0] == '/')
 | 
|---|
| 91 |                 me = &av[0][1];
 | 
|---|
| 92 | 
 | 
|---|
| 93 |         /* crack args */
 | 
|---|
| 94 |         while (--ac && (*++av)[0] == '-')
 | 
|---|
| 95 |             while (*++(*av))
 | 
|---|
| 96 |                 switch (*(*av)) {
 | 
|---|
| 97 |                 case 'v':       /* verbose */
 | 
|---|
| 98 |                     verbose++;
 | 
|---|
| 99 |                     break;
 | 
|---|
| 100 |                 default:
 | 
|---|
| 101 |                     usage();
 | 
|---|
| 102 |                 }
 | 
|---|
| 103 | 
 | 
|---|
| 104 |         /* ac remaining args starting at av[0] */
 | 
|---|
| 105 |         if (ac > 0)
 | 
|---|
| 106 |             usage();
 | 
|---|
| 107 | 
 | 
|---|
| 108 |         /* init */
 | 
|---|
| 109 |         clixml =  newLilXML();
 | 
|---|
| 110 |         addCallback (0, clientMsgCB, NULL);
 | 
|---|
| 111 |         
 | 
|---|
| 112 |         nroCheck = 0;
 | 
|---|
| 113 |         roCheck = NULL;
 | 
|---|
| 114 | 
 | 
|---|
| 115 |         /* service client */
 | 
|---|
| 116 |         eventLoop();
 | 
|---|
| 117 | 
 | 
|---|
| 118 |         /* eh?? */
 | 
|---|
| 119 |         fprintf (stderr, "%s: inf loop ended\n", me);
 | 
|---|
| 120 |         return (1);
 | 
|---|
| 121 | }
 | 
|---|
| 122 | 
 | 
|---|
| 123 | /* Return 1 is property is already cached, 0 otherwise */
 | 
|---|
| 124 | static int isPropDefined(const char *property_name)
 | 
|---|
| 125 | {
 | 
|---|
| 126 |   int i=0;
 | 
|---|
| 127 |   
 | 
|---|
| 128 |   for (i=0; i < nroCheck; i++)
 | 
|---|
| 129 |     if (!strcmp(property_name, roCheck[i].propName))
 | 
|---|
| 130 |         return 1;
 | 
|---|
| 131 | 
 | 
|---|
| 132 |   return 0;
 | 
|---|
| 133 | 
 | 
|---|
| 134 | }
 | 
|---|
| 135 | 
 | 
|---|
| 136 | /* functions we define that drivers may call */
 | 
|---|
| 137 | 
 | 
|---|
| 138 | /* tell client to create a text vector property */
 | 
|---|
| 139 | void
 | 
|---|
| 140 | IDDefText (const ITextVectorProperty *tvp, const char *fmt, ...)
 | 
|---|
| 141 | {
 | 
|---|
| 142 |         int i;
 | 
|---|
| 143 |         ROSC *SC;
 | 
|---|
| 144 | 
 | 
|---|
| 145 |         xmlv1();
 | 
|---|
| 146 |         printf ("<defTextVector\n");
 | 
|---|
| 147 |         printf ("  device='%s'\n", tvp->device);
 | 
|---|
| 148 |         printf ("  name='%s'\n", tvp->name);
 | 
|---|
| 149 |         printf ("  label='%s'\n", tvp->label);
 | 
|---|
| 150 |         printf ("  group='%s'\n", tvp->group);
 | 
|---|
| 151 |         printf ("  state='%s'\n", pstateStr(tvp->s));
 | 
|---|
| 152 |         printf ("  perm='%s'\n", permStr(tvp->p));
 | 
|---|
| 153 |         printf ("  timeout='%g'\n", tvp->timeout);
 | 
|---|
| 154 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 155 |         if (fmt) {
 | 
|---|
| 156 |             va_list ap;
 | 
|---|
| 157 |             va_start (ap, fmt);
 | 
|---|
| 158 |             printf ("  message='");
 | 
|---|
| 159 |             vprintf (fmt, ap);
 | 
|---|
| 160 |             printf ("'\n");
 | 
|---|
| 161 |             va_end (ap);
 | 
|---|
| 162 |         }
 | 
|---|
| 163 |         printf (">\n");
 | 
|---|
| 164 | 
 | 
|---|
| 165 |         for (i = 0; i < tvp->ntp; i++) {
 | 
|---|
| 166 |             IText *tp = &tvp->tp[i];
 | 
|---|
| 167 |             printf ("  <defText\n");
 | 
|---|
| 168 |             printf ("    name='%s'\n", tp->name);
 | 
|---|
| 169 |             printf ("    label='%s'>\n", tp->label);
 | 
|---|
| 170 |             printf ("      %s\n", tp->text ? tp->text : "");
 | 
|---|
| 171 |             printf ("  </defText>\n");
 | 
|---|
| 172 |         }
 | 
|---|
| 173 | 
 | 
|---|
| 174 |         printf ("</defTextVector>\n");
 | 
|---|
| 175 | 
 | 
|---|
| 176 |         if (!isPropDefined(tvp->name))
 | 
|---|
| 177 |         {       
 | 
|---|
| 178 |                 /* Add this property to insure proper sanity check */
 | 
|---|
| 179 |                 roCheck = roCheck ? (ROSC *) realloc ( roCheck, sizeof(ROSC) * (nroCheck+1))
 | 
|---|
| 180 |                                 : (ROSC *) malloc  ( sizeof(ROSC));
 | 
|---|
| 181 |                 SC      = &roCheck[nroCheck++];
 | 
|---|
| 182 |         
 | 
|---|
| 183 |                 strcpy(SC->propName, tvp->name);
 | 
|---|
| 184 |                 SC->perm = tvp->p;
 | 
|---|
| 185 |         }
 | 
|---|
| 186 |         
 | 
|---|
| 187 |         fflush (stdout);
 | 
|---|
| 188 | }
 | 
|---|
| 189 | 
 | 
|---|
| 190 | /* tell client to create a new numeric vector property */
 | 
|---|
| 191 | void
 | 
|---|
| 192 | IDDefNumber (const INumberVectorProperty *n, const char *fmt, ...)
 | 
|---|
| 193 | {
 | 
|---|
| 194 |         int i;
 | 
|---|
| 195 |         ROSC *SC;
 | 
|---|
| 196 | 
 | 
|---|
| 197 |         xmlv1();
 | 
|---|
| 198 |         printf ("<defNumberVector\n");
 | 
|---|
| 199 |         printf ("  device='%s'\n", n->device);
 | 
|---|
| 200 |         printf ("  name='%s'\n", n->name);
 | 
|---|
| 201 |         printf ("  label='%s'\n", n->label);
 | 
|---|
| 202 |         printf ("  group='%s'\n", n->group);
 | 
|---|
| 203 |         printf ("  state='%s'\n", pstateStr(n->s));
 | 
|---|
| 204 |         printf ("  perm='%s'\n", permStr(n->p));
 | 
|---|
| 205 |         printf ("  timeout='%g'\n", n->timeout);
 | 
|---|
| 206 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 207 |         if (fmt) {
 | 
|---|
| 208 |             va_list ap;
 | 
|---|
| 209 |             va_start (ap, fmt);
 | 
|---|
| 210 |             printf ("  message='");
 | 
|---|
| 211 |             vprintf (fmt, ap);
 | 
|---|
| 212 |             printf ("'\n");
 | 
|---|
| 213 |             va_end (ap);
 | 
|---|
| 214 |         }
 | 
|---|
| 215 |         printf (">\n");
 | 
|---|
| 216 | 
 | 
|---|
| 217 |         for (i = 0; i < n->nnp; i++) {
 | 
|---|
| 218 |             INumber *np = &n->np[i];
 | 
|---|
| 219 |             printf ("  <defNumber\n");
 | 
|---|
| 220 |             printf ("    name='%s'\n", np->name);
 | 
|---|
| 221 |             printf ("    label='%s'\n", np->label);
 | 
|---|
| 222 |             printf ("    format='%s'\n", np->format);
 | 
|---|
| 223 |             printf ("    min='%.20g'\n", np->min);
 | 
|---|
| 224 |             printf ("    max='%.20g'\n", np->max);
 | 
|---|
| 225 |             printf ("    step='%.20g'>\n", np->step);
 | 
|---|
| 226 |             printf ("      %.20g\n", np->value);
 | 
|---|
| 227 |             printf ("  </defNumber>\n");
 | 
|---|
| 228 |         }
 | 
|---|
| 229 | 
 | 
|---|
| 230 |         printf ("</defNumberVector>\n");
 | 
|---|
| 231 |         
 | 
|---|
| 232 |         if (!isPropDefined(n->name))
 | 
|---|
| 233 |         {       
 | 
|---|
| 234 |                 /* Add this property to insure proper sanity check */
 | 
|---|
| 235 |                 roCheck = roCheck ? (ROSC *) realloc ( roCheck, sizeof(ROSC) * (nroCheck+1))
 | 
|---|
| 236 |                                 : (ROSC *) malloc  ( sizeof(ROSC));
 | 
|---|
| 237 |                 SC      = &roCheck[nroCheck++];
 | 
|---|
| 238 |         
 | 
|---|
| 239 |                 strcpy(SC->propName, n->name);
 | 
|---|
| 240 |                 SC->perm = n->p;
 | 
|---|
| 241 | 
 | 
|---|
| 242 |         }
 | 
|---|
| 243 |         
 | 
|---|
| 244 |         fflush (stdout);
 | 
|---|
| 245 | }
 | 
|---|
| 246 | 
 | 
|---|
| 247 | /* tell client to create a new switch vector property */
 | 
|---|
| 248 | void
 | 
|---|
| 249 | IDDefSwitch (const ISwitchVectorProperty *s, const char *fmt, ...)
 | 
|---|
| 250 | 
 | 
|---|
| 251 | {
 | 
|---|
| 252 |         int i;
 | 
|---|
| 253 |         ROSC *SC;
 | 
|---|
| 254 | 
 | 
|---|
| 255 |         xmlv1();
 | 
|---|
| 256 |         printf ("<defSwitchVector\n");
 | 
|---|
| 257 |         printf ("  device='%s'\n", s->device);
 | 
|---|
| 258 |         printf ("  name='%s'\n", s->name);
 | 
|---|
| 259 |         printf ("  label='%s'\n", s->label);
 | 
|---|
| 260 |         printf ("  group='%s'\n", s->group);
 | 
|---|
| 261 |         printf ("  state='%s'\n", pstateStr(s->s));
 | 
|---|
| 262 |         printf ("  perm='%s'\n", permStr(s->p));
 | 
|---|
| 263 |         printf ("  rule='%s'\n", ruleStr (s->r));
 | 
|---|
| 264 |         printf ("  timeout='%g'\n", s->timeout);
 | 
|---|
| 265 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 266 |         if (fmt) {
 | 
|---|
| 267 |             va_list ap;
 | 
|---|
| 268 |             va_start (ap, fmt);
 | 
|---|
| 269 |             printf ("  message='");
 | 
|---|
| 270 |             vprintf (fmt, ap);
 | 
|---|
| 271 |             printf ("'\n");
 | 
|---|
| 272 |             va_end (ap);
 | 
|---|
| 273 |         }
 | 
|---|
| 274 |         printf (">\n");
 | 
|---|
| 275 | 
 | 
|---|
| 276 |         for (i = 0; i < s->nsp; i++) {
 | 
|---|
| 277 |             ISwitch *sp = &s->sp[i];
 | 
|---|
| 278 |             printf ("  <defSwitch\n");
 | 
|---|
| 279 |             printf ("    name='%s'\n", sp->name);
 | 
|---|
| 280 |             printf ("    label='%s'>\n", sp->label);
 | 
|---|
| 281 |             printf ("      %s\n", sstateStr(sp->s));
 | 
|---|
| 282 |             printf ("  </defSwitch>\n");
 | 
|---|
| 283 |         }
 | 
|---|
| 284 | 
 | 
|---|
| 285 |         printf ("</defSwitchVector>\n");
 | 
|---|
| 286 |         
 | 
|---|
| 287 |         if (!isPropDefined(s->name))
 | 
|---|
| 288 |         {       
 | 
|---|
| 289 |                 /* Add this property to insure proper sanity check */
 | 
|---|
| 290 |                 roCheck = roCheck ? (ROSC *) realloc ( roCheck, sizeof(ROSC) * (nroCheck+1))
 | 
|---|
| 291 |                                 : (ROSC *) malloc  ( sizeof(ROSC));
 | 
|---|
| 292 |                 SC      = &roCheck[nroCheck++];
 | 
|---|
| 293 |         
 | 
|---|
| 294 |                 strcpy(SC->propName, s->name);
 | 
|---|
| 295 |                 SC->perm = s->p;
 | 
|---|
| 296 |         }
 | 
|---|
| 297 |         
 | 
|---|
| 298 |         fflush (stdout);
 | 
|---|
| 299 | }
 | 
|---|
| 300 | 
 | 
|---|
| 301 | /* tell client to create a new lights vector property */
 | 
|---|
| 302 | void
 | 
|---|
| 303 | IDDefLight (const ILightVectorProperty *lvp, const char *fmt, ...)
 | 
|---|
| 304 | {
 | 
|---|
| 305 |         int i;
 | 
|---|
| 306 | 
 | 
|---|
| 307 |         xmlv1();
 | 
|---|
| 308 |         printf ("<defLightVector\n");
 | 
|---|
| 309 |         printf ("  device='%s'\n", lvp->device);
 | 
|---|
| 310 |         printf ("  name='%s'\n", lvp->name);
 | 
|---|
| 311 |         printf ("  label='%s'\n", lvp->label);
 | 
|---|
| 312 |         printf ("  group='%s'\n", lvp->group);
 | 
|---|
| 313 |         printf ("  state='%s'\n", pstateStr(lvp->s));
 | 
|---|
| 314 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 315 |         if (fmt) {
 | 
|---|
| 316 |             va_list ap;
 | 
|---|
| 317 |             va_start (ap, fmt);
 | 
|---|
| 318 |             printf ("  message='");
 | 
|---|
| 319 |             vprintf (fmt, ap);
 | 
|---|
| 320 |             printf ("'\n");
 | 
|---|
| 321 |             va_end (ap);
 | 
|---|
| 322 |         }
 | 
|---|
| 323 |         printf (">\n");
 | 
|---|
| 324 | 
 | 
|---|
| 325 |         for (i = 0; i < lvp->nlp; i++) {
 | 
|---|
| 326 |             ILight *lp = &lvp->lp[i];
 | 
|---|
| 327 |             printf ("  <defLight\n");
 | 
|---|
| 328 |             printf ("    name='%s'\n", lp->name);
 | 
|---|
| 329 |             printf ("    label='%s'>\n", lp->label);
 | 
|---|
| 330 |             printf ("      %s\n", pstateStr(lp->s));
 | 
|---|
| 331 |             printf ("  </defLight>\n");
 | 
|---|
| 332 |         }
 | 
|---|
| 333 | 
 | 
|---|
| 334 |         printf ("</defLightVector>\n");
 | 
|---|
| 335 |         fflush (stdout);
 | 
|---|
| 336 | }
 | 
|---|
| 337 | 
 | 
|---|
| 338 | /* tell client to create a new BLOB vector property */
 | 
|---|
| 339 | void
 | 
|---|
| 340 | IDDefBLOB (const IBLOBVectorProperty *b, const char *fmt, ...)
 | 
|---|
| 341 | {
 | 
|---|
| 342 |   int i;
 | 
|---|
| 343 |   ROSC *SC;
 | 
|---|
| 344 | 
 | 
|---|
| 345 |         xmlv1();
 | 
|---|
| 346 |         printf ("<defBLOBVector\n");
 | 
|---|
| 347 |         printf ("  device='%s'\n", b->device);
 | 
|---|
| 348 |         printf ("  name='%s'\n", b->name);
 | 
|---|
| 349 |         printf ("  label='%s'\n", b->label);
 | 
|---|
| 350 |         printf ("  group='%s'\n", b->group);
 | 
|---|
| 351 |         printf ("  state='%s'\n", pstateStr(b->s));
 | 
|---|
| 352 |         printf ("  perm='%s'\n", permStr(b->p));
 | 
|---|
| 353 |         printf ("  timeout='%g'\n", b->timeout);
 | 
|---|
| 354 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 355 |         if (fmt) {
 | 
|---|
| 356 |             va_list ap;
 | 
|---|
| 357 |             va_start (ap, fmt);
 | 
|---|
| 358 |             printf ("  message='");
 | 
|---|
| 359 |             vprintf (fmt, ap);
 | 
|---|
| 360 |             printf ("'\n");
 | 
|---|
| 361 |             va_end (ap);
 | 
|---|
| 362 |         }
 | 
|---|
| 363 |         printf (">\n");
 | 
|---|
| 364 | 
 | 
|---|
| 365 |   for (i = 0; i < b->nbp; i++) {
 | 
|---|
| 366 |     IBLOB *bp = &b->bp[i];
 | 
|---|
| 367 |     printf ("  <defBLOB\n");
 | 
|---|
| 368 |     printf ("    name='%s'\n", bp->name);
 | 
|---|
| 369 |     printf ("    label='%s'\n", bp->label);
 | 
|---|
| 370 |     printf ("  />\n");
 | 
|---|
| 371 |   }
 | 
|---|
| 372 | 
 | 
|---|
| 373 |         printf ("</defBLOBVector>\n");
 | 
|---|
| 374 | 
 | 
|---|
| 375 |         if (!isPropDefined(b->name))
 | 
|---|
| 376 |         {       
 | 
|---|
| 377 |                 /* Add this property to insure proper sanity check */
 | 
|---|
| 378 |                 roCheck = roCheck ? (ROSC *) realloc ( roCheck, sizeof(ROSC) * (nroCheck+1))
 | 
|---|
| 379 |                                 : (ROSC *) malloc  ( sizeof(ROSC));
 | 
|---|
| 380 |                 SC      = &roCheck[nroCheck++];
 | 
|---|
| 381 |         
 | 
|---|
| 382 |                 strcpy(SC->propName, b->name);
 | 
|---|
| 383 |                 SC->perm = b->p;
 | 
|---|
| 384 |         }
 | 
|---|
| 385 | 
 | 
|---|
| 386 |         fflush (stdout);
 | 
|---|
| 387 | }
 | 
|---|
| 388 | 
 | 
|---|
| 389 | /* tell client to update an existing text vector property */
 | 
|---|
| 390 | void
 | 
|---|
| 391 | IDSetText (const ITextVectorProperty *tvp, const char *fmt, ...)
 | 
|---|
| 392 | {
 | 
|---|
| 393 |         int i;
 | 
|---|
| 394 | 
 | 
|---|
| 395 |         xmlv1();
 | 
|---|
| 396 |         printf ("<setTextVector\n");
 | 
|---|
| 397 |         printf ("  device='%s'\n", tvp->device);
 | 
|---|
| 398 |         printf ("  name='%s'\n", tvp->name);
 | 
|---|
| 399 |         printf ("  state='%s'\n", pstateStr(tvp->s));
 | 
|---|
| 400 |         printf ("  timeout='%g'\n", tvp->timeout);
 | 
|---|
| 401 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 402 |         if (fmt) {
 | 
|---|
| 403 |             va_list ap;
 | 
|---|
| 404 |             va_start (ap, fmt);
 | 
|---|
| 405 |             printf ("  message='");
 | 
|---|
| 406 |             vprintf (fmt, ap);
 | 
|---|
| 407 |             printf ("'\n");
 | 
|---|
| 408 |             va_end (ap);
 | 
|---|
| 409 |         }
 | 
|---|
| 410 |         printf (">\n");
 | 
|---|
| 411 | 
 | 
|---|
| 412 |         for (i = 0; i < tvp->ntp; i++) {
 | 
|---|
| 413 |             IText *tp = &tvp->tp[i];
 | 
|---|
| 414 |             printf ("  <oneText name='%s'>\n", tp->name);
 | 
|---|
| 415 |             printf ("      %s\n", tp->text ? tp->text : "");
 | 
|---|
| 416 |             printf ("  </oneText>\n");
 | 
|---|
| 417 |         }
 | 
|---|
| 418 | 
 | 
|---|
| 419 |         printf ("</setTextVector>\n");
 | 
|---|
| 420 |         fflush (stdout);
 | 
|---|
| 421 | }
 | 
|---|
| 422 | 
 | 
|---|
| 423 | /* tell client to update an existing numeric vector property */
 | 
|---|
| 424 | void
 | 
|---|
| 425 | IDSetNumber (const INumberVectorProperty *nvp, const char *fmt, ...)
 | 
|---|
| 426 | {
 | 
|---|
| 427 |         int i;
 | 
|---|
| 428 | 
 | 
|---|
| 429 |         xmlv1();
 | 
|---|
| 430 |         printf ("<setNumberVector\n");
 | 
|---|
| 431 |         printf ("  device='%s'\n", nvp->device);
 | 
|---|
| 432 |         printf ("  name='%s'\n", nvp->name);
 | 
|---|
| 433 |         printf ("  state='%s'\n", pstateStr(nvp->s));
 | 
|---|
| 434 |         printf ("  timeout='%g'\n", nvp->timeout);
 | 
|---|
| 435 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 436 |         if (fmt) {
 | 
|---|
| 437 |             va_list ap;
 | 
|---|
| 438 |             va_start (ap, fmt);
 | 
|---|
| 439 |             printf ("  message='");
 | 
|---|
| 440 |             vprintf (fmt, ap);
 | 
|---|
| 441 |             printf ("'\n");
 | 
|---|
| 442 |             va_end (ap);
 | 
|---|
| 443 |         }
 | 
|---|
| 444 |         printf (">\n");
 | 
|---|
| 445 | 
 | 
|---|
| 446 |         for (i = 0; i < nvp->nnp; i++) {
 | 
|---|
| 447 |             INumber *np = &nvp->np[i];
 | 
|---|
| 448 |             printf ("  <oneNumber name='%s'>\n", np->name);
 | 
|---|
| 449 |             printf ("      %.20g\n", np->value);
 | 
|---|
| 450 |             printf ("  </oneNumber>\n");
 | 
|---|
| 451 |         }
 | 
|---|
| 452 | 
 | 
|---|
| 453 |         printf ("</setNumberVector>\n");
 | 
|---|
| 454 |         fflush (stdout);
 | 
|---|
| 455 | }
 | 
|---|
| 456 | 
 | 
|---|
| 457 | /* tell client to update an existing switch vector property */
 | 
|---|
| 458 | void
 | 
|---|
| 459 | IDSetSwitch (const ISwitchVectorProperty *svp, const char *fmt, ...)
 | 
|---|
| 460 | {
 | 
|---|
| 461 |         int i;
 | 
|---|
| 462 | 
 | 
|---|
| 463 |         xmlv1();
 | 
|---|
| 464 |         printf ("<setSwitchVector\n");
 | 
|---|
| 465 |         printf ("  device='%s'\n", svp->device);
 | 
|---|
| 466 |         printf ("  name='%s'\n", svp->name);
 | 
|---|
| 467 |         printf ("  state='%s'\n", pstateStr(svp->s));
 | 
|---|
| 468 |         printf ("  timeout='%g'\n", svp->timeout);
 | 
|---|
| 469 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 470 |         if (fmt) {
 | 
|---|
| 471 |             va_list ap;
 | 
|---|
| 472 |             va_start (ap, fmt);
 | 
|---|
| 473 |             printf ("  message='");
 | 
|---|
| 474 |             vprintf (fmt, ap);
 | 
|---|
| 475 |             printf ("'\n");
 | 
|---|
| 476 |             va_end (ap);
 | 
|---|
| 477 |         }
 | 
|---|
| 478 |         printf (">\n");
 | 
|---|
| 479 | 
 | 
|---|
| 480 |         for (i = 0; i < svp->nsp; i++) {
 | 
|---|
| 481 |             ISwitch *sp = &svp->sp[i];
 | 
|---|
| 482 |             printf ("  <oneSwitch name='%s'>\n", sp->name);
 | 
|---|
| 483 |             printf ("      %s\n", sstateStr(sp->s));
 | 
|---|
| 484 |             printf ("  </oneSwitch>\n");
 | 
|---|
| 485 |         }
 | 
|---|
| 486 | 
 | 
|---|
| 487 |         printf ("</setSwitchVector>\n");
 | 
|---|
| 488 |         fflush (stdout);
 | 
|---|
| 489 | }
 | 
|---|
| 490 | 
 | 
|---|
| 491 | /* tell client to update an existing lights vector property */
 | 
|---|
| 492 | void
 | 
|---|
| 493 | IDSetLight (const ILightVectorProperty *lvp, const char *fmt, ...)
 | 
|---|
| 494 | {
 | 
|---|
| 495 |         int i;
 | 
|---|
| 496 | 
 | 
|---|
| 497 |         xmlv1();
 | 
|---|
| 498 |         printf ("<setLightVector\n");
 | 
|---|
| 499 |         printf ("  device='%s'\n", lvp->device);
 | 
|---|
| 500 |         printf ("  name='%s'\n", lvp->name);
 | 
|---|
| 501 |         printf ("  state='%s'\n", pstateStr(lvp->s));
 | 
|---|
| 502 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 503 |         if (fmt) {
 | 
|---|
| 504 |             va_list ap;
 | 
|---|
| 505 |             va_start (ap, fmt);
 | 
|---|
| 506 |             printf ("  message='");
 | 
|---|
| 507 |             vprintf (fmt, ap);
 | 
|---|
| 508 |             printf ("'\n");
 | 
|---|
| 509 |             va_end (ap);
 | 
|---|
| 510 |         }
 | 
|---|
| 511 |         printf (">\n");
 | 
|---|
| 512 | 
 | 
|---|
| 513 |         for (i = 0; i < lvp->nlp; i++) {
 | 
|---|
| 514 |             ILight *lp = &lvp->lp[i];
 | 
|---|
| 515 |             printf ("  <oneLight name='%s'>\n", lp->name);
 | 
|---|
| 516 |             printf ("      %s\n", pstateStr(lp->s));
 | 
|---|
| 517 |             printf ("  </oneLight>\n");
 | 
|---|
| 518 |         }
 | 
|---|
| 519 | 
 | 
|---|
| 520 |         printf ("</setLightVector>\n");
 | 
|---|
| 521 |         fflush (stdout);
 | 
|---|
| 522 | }
 | 
|---|
| 523 | 
 | 
|---|
| 524 | /* tell client to update an existing BLOB vector property */
 | 
|---|
| 525 | void
 | 
|---|
| 526 | IDSetBLOB (const IBLOBVectorProperty *bvp, const char *fmt, ...)
 | 
|---|
| 527 | {
 | 
|---|
| 528 |         int i;
 | 
|---|
| 529 | 
 | 
|---|
| 530 |         xmlv1();
 | 
|---|
| 531 |         printf ("<setBLOBVector\n");
 | 
|---|
| 532 |         printf ("  device='%s'\n", bvp->device);
 | 
|---|
| 533 |         printf ("  name='%s'\n", bvp->name);
 | 
|---|
| 534 |         printf ("  state='%s'\n", pstateStr(bvp->s));
 | 
|---|
| 535 |         printf ("  timeout='%g'\n", bvp->timeout);
 | 
|---|
| 536 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 537 |         if (fmt) {
 | 
|---|
| 538 |             va_list ap;
 | 
|---|
| 539 |             va_start (ap, fmt);
 | 
|---|
| 540 |             printf ("  message='");
 | 
|---|
| 541 |             vprintf (fmt, ap);
 | 
|---|
| 542 |             printf ("'\n");
 | 
|---|
| 543 |             va_end (ap);
 | 
|---|
| 544 |         }
 | 
|---|
| 545 |         printf (">\n");
 | 
|---|
| 546 | 
 | 
|---|
| 547 |         for (i = 0; i < bvp->nbp; i++) {
 | 
|---|
| 548 |             IBLOB *bp = &bvp->bp[i];
 | 
|---|
| 549 |             unsigned char *encblob;
 | 
|---|
| 550 |             int j, l;
 | 
|---|
| 551 | 
 | 
|---|
| 552 |             printf ("  <oneBLOB\n");
 | 
|---|
| 553 |             printf ("    name='%s'\n", bp->name);
 | 
|---|
| 554 |             printf ("    size='%d'\n", bp->size);
 | 
|---|
| 555 |             printf ("    format='%s'>\n", bp->format);
 | 
|---|
| 556 | 
 | 
|---|
| 557 |             encblob = malloc (4*bp->bloblen/3+4);
 | 
|---|
| 558 |             l = to64frombits(encblob, bp->blob, bp->bloblen);
 | 
|---|
| 559 |             for (j = 0; j < l; j += 72)
 | 
|---|
| 560 |                 printf ("%.72s\n", encblob+j);
 | 
|---|
| 561 |             free (encblob);
 | 
|---|
| 562 | 
 | 
|---|
| 563 |             printf ("  </oneBLOB>\n");
 | 
|---|
| 564 |         }
 | 
|---|
| 565 | 
 | 
|---|
| 566 |   printf ("</setBLOBVector>\n");
 | 
|---|
| 567 |   fflush (stdout);
 | 
|---|
| 568 | }
 | 
|---|
| 569 | 
 | 
|---|
| 570 | /* tell client to update min/max elements of an existing number vector property */
 | 
|---|
| 571 | void IUUpdateMinMax(const INumberVectorProperty *nvp)
 | 
|---|
| 572 | {
 | 
|---|
| 573 |   int i;
 | 
|---|
| 574 | 
 | 
|---|
| 575 |   xmlv1();
 | 
|---|
| 576 |   printf ("<setNumberVector\n");
 | 
|---|
| 577 |   printf ("  device='%s'\n", nvp->device);
 | 
|---|
| 578 |   printf ("  name='%s'\n", nvp->name);
 | 
|---|
| 579 |   printf ("  state='%s'\n", pstateStr(nvp->s));
 | 
|---|
| 580 |   printf ("  timeout='%g'\n", nvp->timeout);
 | 
|---|
| 581 |   printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 582 |   printf (">\n");
 | 
|---|
| 583 | 
 | 
|---|
| 584 |   for (i = 0; i < nvp->nnp; i++) {
 | 
|---|
| 585 |     INumber *np = &nvp->np[i];
 | 
|---|
| 586 |     printf ("  <oneNumber name='%s'\n", np->name);
 | 
|---|
| 587 |     printf ("    min='%g'\n", np->min);
 | 
|---|
| 588 |     printf ("    max='%g'\n", np->max);
 | 
|---|
| 589 |     printf ("    step='%g'\n", np->step);
 | 
|---|
| 590 |     printf(">\n");
 | 
|---|
| 591 |     printf ("      %g\n", np->value);
 | 
|---|
| 592 |     printf ("  </oneNumber>\n");
 | 
|---|
| 593 |   }
 | 
|---|
| 594 | 
 | 
|---|
| 595 |   printf ("</setNumberVector>\n");
 | 
|---|
| 596 |   fflush (stdout);
 | 
|---|
| 597 | }
 | 
|---|
| 598 | 
 | 
|---|
| 599 | /* send client a message for a specific device or at large if !dev */
 | 
|---|
| 600 | void
 | 
|---|
| 601 | IDMessage (const char *dev, const char *fmt, ...)
 | 
|---|
| 602 | {
 | 
|---|
| 603 | 
 | 
|---|
| 604 |         xmlv1();
 | 
|---|
| 605 |         printf ("<message\n");
 | 
|---|
| 606 |         if (dev)
 | 
|---|
| 607 |             printf (" device='%s'\n", dev);
 | 
|---|
| 608 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 609 |         if (fmt) {
 | 
|---|
| 610 |             va_list ap;
 | 
|---|
| 611 |             va_start (ap, fmt);
 | 
|---|
| 612 |             printf ("  message='");
 | 
|---|
| 613 |             vprintf (fmt, ap);
 | 
|---|
| 614 |             printf ("'\n");
 | 
|---|
| 615 |             va_end (ap);
 | 
|---|
| 616 |         }
 | 
|---|
| 617 |         printf ("/>\n");
 | 
|---|
| 618 |         fflush (stdout);
 | 
|---|
| 619 | }
 | 
|---|
| 620 | 
 | 
|---|
| 621 | /* tell Client to delete the property with given name on given device, or
 | 
|---|
| 622 |  * entire device if !name
 | 
|---|
| 623 |  */
 | 
|---|
| 624 | void
 | 
|---|
| 625 | IDDelete (const char *dev, const char *name, const char *fmt, ...)
 | 
|---|
| 626 | {
 | 
|---|
| 627 |         xmlv1();
 | 
|---|
| 628 |         printf ("<delProperty\n  device='%s'\n", dev);
 | 
|---|
| 629 |         if (name)
 | 
|---|
| 630 |             printf (" name='%s'\n", name);
 | 
|---|
| 631 |         printf ("  timestamp='%s'\n", timestamp());
 | 
|---|
| 632 |         if (fmt) {
 | 
|---|
| 633 |             va_list ap;
 | 
|---|
| 634 |             va_start (ap, fmt);
 | 
|---|
| 635 |             printf ("  message='");
 | 
|---|
| 636 |             vprintf (fmt, ap);
 | 
|---|
| 637 |             printf ("'\n");
 | 
|---|
| 638 |             va_end (ap);
 | 
|---|
| 639 |         }
 | 
|---|
| 640 |         printf ("/>\n");
 | 
|---|
| 641 |         fflush (stdout);
 | 
|---|
| 642 | }
 | 
|---|
| 643 | 
 | 
|---|
| 644 | /* tell indiserver we want to snoop on the given device/property.
 | 
|---|
| 645 |  * name ignored if NULL or empty.
 | 
|---|
| 646 |  */
 | 
|---|
| 647 | void
 | 
|---|
| 648 | IDSnoopDevice (const char *snooped_device_name, char *snooped_property_name)
 | 
|---|
| 649 | {
 | 
|---|
| 650 |         xmlv1();
 | 
|---|
| 651 |         if (snooped_property_name && snooped_property_name[0])
 | 
|---|
| 652 |             printf ("<getProperties device='%s' name='%s'/>\n",
 | 
|---|
| 653 |                                     snooped_device_name, snooped_property_name);
 | 
|---|
| 654 |         else
 | 
|---|
| 655 |             printf ("<getProperties device='%s'/>\n", snooped_device_name);
 | 
|---|
| 656 |         fflush (stdout);
 | 
|---|
| 657 | }
 | 
|---|
| 658 | 
 | 
|---|
| 659 | /* tell indiserver whether we want BLOBs from the given snooped device.
 | 
|---|
| 660 |  * silently ignored if given device is not already registered for snooping.
 | 
|---|
| 661 |  */
 | 
|---|
| 662 | void 
 | 
|---|
| 663 | IDSnoopBLOBs (const char *snooped_device, BLOBHandling bh)
 | 
|---|
| 664 | {
 | 
|---|
| 665 |         const char *how;
 | 
|---|
| 666 | 
 | 
|---|
| 667 |         switch (bh) {
 | 
|---|
| 668 |         case B_NEVER: how = "Never"; break;
 | 
|---|
| 669 |         case B_ALSO:  how = "Also";  break;
 | 
|---|
| 670 |         case B_ONLY:  how = "Only";  break;
 | 
|---|
| 671 |         default: return;
 | 
|---|
| 672 |         }
 | 
|---|
| 673 | 
 | 
|---|
| 674 |         xmlv1();
 | 
|---|
| 675 |         printf ("<enableBLOB device='%s'>%s</enableBLOB>\n",
 | 
|---|
| 676 |                                                 snooped_device, how);
 | 
|---|
| 677 |         fflush (stdout);
 | 
|---|
| 678 | }
 | 
|---|
| 679 | 
 | 
|---|
| 680 | /* "INDI" wrappers to the more generic eventloop facility. */
 | 
|---|
| 681 | 
 | 
|---|
| 682 | int
 | 
|---|
| 683 | IEAddCallback (int readfiledes, IE_CBF *fp, void *p)
 | 
|---|
| 684 | {
 | 
|---|
| 685 |         return (addCallback (readfiledes, (CBF*)fp, p));
 | 
|---|
| 686 | }
 | 
|---|
| 687 | 
 | 
|---|
| 688 | void
 | 
|---|
| 689 | IERmCallback (int callbackid)
 | 
|---|
| 690 | {
 | 
|---|
| 691 |         rmCallback (callbackid);
 | 
|---|
| 692 | }
 | 
|---|
| 693 | 
 | 
|---|
| 694 | int
 | 
|---|
| 695 | IEAddTimer (int millisecs, IE_TCF *fp, void *p)
 | 
|---|
| 696 | {
 | 
|---|
| 697 |         return (addTimer (millisecs, (TCF*)fp, p));
 | 
|---|
| 698 | }
 | 
|---|
| 699 | 
 | 
|---|
| 700 | void
 | 
|---|
| 701 | IERmTimer (int timerid)
 | 
|---|
| 702 | {
 | 
|---|
| 703 |         rmTimer (timerid);
 | 
|---|
| 704 | }
 | 
|---|
| 705 | 
 | 
|---|
| 706 | int
 | 
|---|
| 707 | IEAddWorkProc (IE_WPF *fp, void *p)
 | 
|---|
| 708 | {
 | 
|---|
| 709 |         return (addWorkProc ((WPF*)fp, p));
 | 
|---|
| 710 | }
 | 
|---|
| 711 | 
 | 
|---|
| 712 | void
 | 
|---|
| 713 | IERmWorkProc (int workprocid)
 | 
|---|
| 714 | {
 | 
|---|
| 715 |         rmWorkProc (workprocid);
 | 
|---|
| 716 | }
 | 
|---|
| 717 | 
 | 
|---|
| 718 | 
 | 
|---|
| 719 | int
 | 
|---|
| 720 | IEDeferLoop (int maxms, int *flagp)
 | 
|---|
| 721 | {
 | 
|---|
| 722 |         return (deferLoop (maxms, flagp));
 | 
|---|
| 723 | }
 | 
|---|
| 724 | 
 | 
|---|
| 725 | int
 | 
|---|
| 726 | IEDeferLoop0 (int maxms, int *flagp)
 | 
|---|
| 727 | {
 | 
|---|
| 728 |         return (deferLoop0 (maxms, flagp));
 | 
|---|
| 729 | }
 | 
|---|
| 730 | 
 | 
|---|
| 731 | /* find a member of an IText vector, else NULL */
 | 
|---|
| 732 | IText *
 | 
|---|
| 733 | IUFindText  (const ITextVectorProperty *tvp, const char *name)
 | 
|---|
| 734 | {
 | 
|---|
| 735 |         int i;
 | 
|---|
| 736 | 
 | 
|---|
| 737 |         for (i = 0; i < tvp->ntp; i++)
 | 
|---|
| 738 |             if (strcmp (tvp->tp[i].name, name) == 0)
 | 
|---|
| 739 |                 return (&tvp->tp[i]);
 | 
|---|
| 740 |         fprintf (stderr, "No IText '%s' in %s.%s\n",name,tvp->device,tvp->name);
 | 
|---|
| 741 |         return (NULL);
 | 
|---|
| 742 | }
 | 
|---|
| 743 | 
 | 
|---|
| 744 | /* find a member of an INumber vector, else NULL */
 | 
|---|
| 745 | INumber *
 | 
|---|
| 746 | IUFindNumber(const INumberVectorProperty *nvp, const char *name)
 | 
|---|
| 747 | {
 | 
|---|
| 748 |         int i;
 | 
|---|
| 749 | 
 | 
|---|
| 750 |         for (i = 0; i < nvp->nnp; i++)
 | 
|---|
| 751 |             if (strcmp (nvp->np[i].name, name) == 0)
 | 
|---|
| 752 |                 return (&nvp->np[i]);
 | 
|---|
| 753 |         fprintf(stderr,"No INumber '%s' in %s.%s\n",name,nvp->device,nvp->name);
 | 
|---|
| 754 |         return (NULL);
 | 
|---|
| 755 | }
 | 
|---|
| 756 | 
 | 
|---|
| 757 | /* find a member of an ISwitch vector, else NULL */
 | 
|---|
| 758 | ISwitch *
 | 
|---|
| 759 | IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
 | 
|---|
| 760 | {
 | 
|---|
| 761 |         int i;
 | 
|---|
| 762 | 
 | 
|---|
| 763 |         for (i = 0; i < svp->nsp; i++)
 | 
|---|
| 764 |             if (strcmp (svp->sp[i].name, name) == 0)
 | 
|---|
| 765 |                 return (&svp->sp[i]);
 | 
|---|
| 766 |         fprintf(stderr,"No ISwitch '%s' in %s.%s\n",name,svp->device,svp->name);
 | 
|---|
| 767 |         return (NULL);
 | 
|---|
| 768 | }
 | 
|---|
| 769 | 
 | 
|---|
| 770 | /* find an ON member of an ISwitch vector, else NULL.
 | 
|---|
| 771 |  * N.B. user must make sense of result with ISRule in mind.
 | 
|---|
| 772 |  */
 | 
|---|
| 773 | ISwitch *
 | 
|---|
| 774 | IUFindOnSwitch(const ISwitchVectorProperty *svp)
 | 
|---|
| 775 | {
 | 
|---|
| 776 |         int i;
 | 
|---|
| 777 | 
 | 
|---|
| 778 |         for (i = 0; i < svp->nsp; i++)
 | 
|---|
| 779 |             if (svp->sp[i].s == ISS_ON)
 | 
|---|
| 780 |                 return (&svp->sp[i]);
 | 
|---|
| 781 |         /*fprintf(stderr, "No ISwitch On in %s.%s\n", svp->device, svp->name);*/
 | 
|---|
| 782 |         return (NULL);
 | 
|---|
| 783 | }
 | 
|---|
| 784 | 
 | 
|---|
| 785 | /* Set all switches to off */
 | 
|---|
| 786 | void 
 | 
|---|
| 787 | IUResetSwitch(ISwitchVectorProperty *svp)
 | 
|---|
| 788 | {
 | 
|---|
| 789 |   int i;
 | 
|---|
| 790 |   
 | 
|---|
| 791 |   for (i = 0; i < svp->nsp; i++)
 | 
|---|
| 792 |     svp->sp[i].s = ISS_OFF;
 | 
|---|
| 793 | }
 | 
|---|
| 794 | 
 | 
|---|
| 795 | /* Update property switches in accord with states and names. */
 | 
|---|
| 796 | int 
 | 
|---|
| 797 | IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
 | 
|---|
| 798 | {
 | 
|---|
| 799 |  int i=0;
 | 
|---|
| 800 |  ISwitch *sp;
 | 
|---|
| 801 |  char sn[MAXINDINAME];
 | 
|---|
| 802 | 
 | 
|---|
| 803 |  /* store On switch name */
 | 
|---|
| 804 |  if (svp->r == ISR_1OFMANY)
 | 
|---|
| 805 |  {
 | 
|---|
| 806 |         sp = IUFindOnSwitch(svp);
 | 
|---|
| 807 |         if (sp) strncpy(sn, sp->name, MAXINDINAME);
 | 
|---|
| 808 |  
 | 
|---|
| 809 |         IUResetSwitch(svp);
 | 
|---|
| 810 |  }
 | 
|---|
| 811 |  
 | 
|---|
| 812 |  for (i = 0; i < n ; i++)
 | 
|---|
| 813 |  {
 | 
|---|
| 814 |    sp = IUFindSwitch(svp, names[i]);
 | 
|---|
| 815 |          
 | 
|---|
| 816 |    if (!sp)
 | 
|---|
| 817 |    {
 | 
|---|
| 818 |               svp->s = IPS_IDLE;
 | 
|---|
| 819 |               IDSetSwitch(svp, "Error: %s is not a member of %s property.", names[i], svp->name);
 | 
|---|
| 820 |               return -1;
 | 
|---|
| 821 |    }
 | 
|---|
| 822 |          
 | 
|---|
| 823 |    sp->s = states[i]; 
 | 
|---|
| 824 |  }
 | 
|---|
| 825 |  
 | 
|---|
| 826 |  /* Consistency checks for ISR_1OFMANY after update. */
 | 
|---|
| 827 |  if (svp->r == ISR_1OFMANY)
 | 
|---|
| 828 |  {
 | 
|---|
| 829 |         int t_count=0;
 | 
|---|
| 830 |         for (i=0; i < svp->nsp; i++)
 | 
|---|
| 831 |         {
 | 
|---|
| 832 |                 if (svp->sp[i].s == ISS_ON)
 | 
|---|
| 833 |                         t_count++;
 | 
|---|
| 834 |         }
 | 
|---|
| 835 |         if (t_count != 1)
 | 
|---|
| 836 |         {
 | 
|---|
| 837 |                 IUResetSwitch(svp);
 | 
|---|
| 838 |                 sp = IUFindSwitch(svp, sn);
 | 
|---|
| 839 |                 if (sp) sp->s = ISS_ON;
 | 
|---|
| 840 |                 svp->s = IPS_IDLE;
 | 
|---|
| 841 |                 IDSetSwitch(svp, "Error: invalid state switch for property %s. %s.", svp->name, t_count == 0 ? "No switch is on" : "Too many switches are on");
 | 
|---|
| 842 |                 return -1;
 | 
|---|
| 843 |         }
 | 
|---|
| 844 |  }
 | 
|---|
| 845 |                 
 | 
|---|
| 846 |  return 0;
 | 
|---|
| 847 | 
 | 
|---|
| 848 | }
 | 
|---|
| 849 | 
 | 
|---|
| 850 | /* Update property numbers in accord with values and names */
 | 
|---|
| 851 | int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
 | 
|---|
| 852 | {
 | 
|---|
| 853 |   int i=0;
 | 
|---|
| 854 |   
 | 
|---|
| 855 |   INumber *np;
 | 
|---|
| 856 |   
 | 
|---|
| 857 |   for (i = 0; i < n; i++)
 | 
|---|
| 858 |   {
 | 
|---|
| 859 |     np = IUFindNumber(nvp, names[i]);
 | 
|---|
| 860 |     if (!np)
 | 
|---|
| 861 |     {
 | 
|---|
| 862 |         nvp->s = IPS_IDLE;
 | 
|---|
| 863 |         IDSetNumber(nvp, "Error: %s is not a member of %s property.", names[i], nvp->name);
 | 
|---|
| 864 |         return -1;
 | 
|---|
| 865 |     }
 | 
|---|
| 866 |     
 | 
|---|
| 867 |     if (values[i] < np->min || values[i] > np->max)
 | 
|---|
| 868 |     {
 | 
|---|
| 869 |        nvp->s = IPS_IDLE;
 | 
|---|
| 870 |        IDSetNumber(nvp, "Error: Invalid range. Valid range is from %g to %g", np->min, np->max);
 | 
|---|
| 871 |        return -1;
 | 
|---|
| 872 |     }
 | 
|---|
| 873 |       
 | 
|---|
| 874 |   }
 | 
|---|
| 875 | 
 | 
|---|
| 876 |   /* First loop checks for error, second loop set all values atomically*/
 | 
|---|
| 877 |   for (i=0; i < n; i++)
 | 
|---|
| 878 |   {
 | 
|---|
| 879 |     np = IUFindNumber(nvp, names[i]);
 | 
|---|
| 880 |     np->value = values[i];
 | 
|---|
| 881 |   }
 | 
|---|
| 882 | 
 | 
|---|
| 883 |   return 0;
 | 
|---|
| 884 | 
 | 
|---|
| 885 | }
 | 
|---|
| 886 | 
 | 
|---|
| 887 | /* Update property text in accord with texts and names */
 | 
|---|
| 888 | int IUUpdateText(ITextVectorProperty *tvp, char * texts[], char *names[], int n)
 | 
|---|
| 889 | {
 | 
|---|
| 890 |   int i=0;
 | 
|---|
| 891 |   
 | 
|---|
| 892 |   IText *tp;
 | 
|---|
| 893 |   
 | 
|---|
| 894 |   for (i = 0; i < n; i++)
 | 
|---|
| 895 |   {
 | 
|---|
| 896 |     tp = IUFindText(tvp, names[i]);
 | 
|---|
| 897 |     if (!tp)
 | 
|---|
| 898 |     {
 | 
|---|
| 899 |         tvp->s = IPS_IDLE;
 | 
|---|
| 900 |         IDSetText(tvp, "Error: %s is not a member of %s property.", names[i], tvp->name);
 | 
|---|
| 901 |         return -1;
 | 
|---|
| 902 |     }
 | 
|---|
| 903 |   }
 | 
|---|
| 904 | 
 | 
|---|
| 905 |   /* First loop checks for error, second loop set all values atomically*/
 | 
|---|
| 906 |   for (i=0; i < n; i++)
 | 
|---|
| 907 |   {
 | 
|---|
| 908 |     tp = IUFindText(tvp, names[i]);
 | 
|---|
| 909 |     IUSaveText(tp, texts[i]);
 | 
|---|
| 910 |   }
 | 
|---|
| 911 | 
 | 
|---|
| 912 |   return 0;
 | 
|---|
| 913 | 
 | 
|---|
| 914 | }
 | 
|---|
| 915 | 
 | 
|---|
| 916 | /* save malloced copy of newtext in tp->text, reusing if not first time */
 | 
|---|
| 917 | void
 | 
|---|
| 918 | IUSaveText (IText *tp, const char *newtext)
 | 
|---|
| 919 | {
 | 
|---|
| 920 |         /* seed for realloc */
 | 
|---|
| 921 |         if (tp->text == NULL)
 | 
|---|
| 922 |             tp->text = malloc (1);
 | 
|---|
| 923 | 
 | 
|---|
| 924 |         /* copy in fresh string */
 | 
|---|
| 925 |         tp->text = strcpy (realloc (tp->text, strlen(newtext)+1), newtext);
 | 
|---|
| 926 | }
 | 
|---|
| 927 | 
 | 
|---|
| 928 | void IUFillSwitch(ISwitch *sp, const char *name, const char * label, ISState s)
 | 
|---|
| 929 | {
 | 
|---|
| 930 |   strcpy(sp->name, name);
 | 
|---|
| 931 |   strcpy(sp->label, label);
 | 
|---|
| 932 |   sp->s = s;
 | 
|---|
| 933 |   sp->svp = NULL;
 | 
|---|
| 934 |   sp->aux = NULL;
 | 
|---|
| 935 | }
 | 
|---|
| 936 | 
 | 
|---|
| 937 | void IUFillLight(ILight *lp, const char *name, const char * label, IPState s)
 | 
|---|
| 938 | {
 | 
|---|
| 939 |   strcpy(lp->name, name);
 | 
|---|
| 940 |   strcpy(lp->label, label);
 | 
|---|
| 941 |   lp->s = s;
 | 
|---|
| 942 |   lp->lvp = NULL;
 | 
|---|
| 943 |   lp->aux = NULL;
 | 
|---|
| 944 | }
 | 
|---|
| 945 | 
 | 
|---|
| 946 | 
 | 
|---|
| 947 | void IUFillNumber(INumber *np, const char *name, const char * label, const char *format, double min, double max, double step, double value)
 | 
|---|
| 948 | {
 | 
|---|
| 949 | 
 | 
|---|
| 950 |   strcpy(np->name, name);
 | 
|---|
| 951 |   strcpy(np->label, label);
 | 
|---|
| 952 |   strcpy(np->format, format);
 | 
|---|
| 953 |   
 | 
|---|
| 954 |   np->min       = min;
 | 
|---|
| 955 |   np->max       = max;
 | 
|---|
| 956 |   np->step      = step;
 | 
|---|
| 957 |   np->value     = value;
 | 
|---|
| 958 |   np->nvp       = NULL;
 | 
|---|
| 959 |   np->aux0      = NULL;
 | 
|---|
| 960 |   np->aux1      = NULL;
 | 
|---|
| 961 | }
 | 
|---|
| 962 | 
 | 
|---|
| 963 | void IUFillText(IText *tp, const char *name, const char * label, const char *initialText)
 | 
|---|
| 964 | {
 | 
|---|
| 965 | 
 | 
|---|
| 966 |   strcpy(tp->name, name);
 | 
|---|
| 967 |   strcpy(tp->label, label);
 | 
|---|
| 968 |   tp->text = NULL;
 | 
|---|
| 969 |   tp->tvp  = NULL;
 | 
|---|
| 970 |   tp->aux0 = NULL;
 | 
|---|
| 971 |   tp->aux1 = NULL;
 | 
|---|
| 972 | 
 | 
|---|
| 973 |   IUSaveText(tp, initialText);
 | 
|---|
| 974 | 
 | 
|---|
| 975 | }
 | 
|---|
| 976 | 
 | 
|---|
| 977 | void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char * dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
 | 
|---|
| 978 | {
 | 
|---|
| 979 |   strcpy(svp->device, dev);
 | 
|---|
| 980 |   strcpy(svp->name, name);
 | 
|---|
| 981 |   strcpy(svp->label, label);
 | 
|---|
| 982 |   strcpy(svp->group, group);
 | 
|---|
| 983 |   strcpy(svp->timestamp, "");
 | 
|---|
| 984 |   
 | 
|---|
| 985 |   svp->p        = p;
 | 
|---|
| 986 |   svp->r        = r;
 | 
|---|
| 987 |   svp->timeout  = timeout;
 | 
|---|
| 988 |   svp->s        = s;
 | 
|---|
| 989 |   svp->sp       = sp;
 | 
|---|
| 990 |   svp->nsp      = nsp;
 | 
|---|
| 991 | }
 | 
|---|
| 992 | 
 | 
|---|
| 993 | void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char * dev, const char *name, const char *label, const char *group, IPState s)
 | 
|---|
| 994 | {
 | 
|---|
| 995 |   strcpy(lvp->device, dev);
 | 
|---|
| 996 |   strcpy(lvp->name, name);
 | 
|---|
| 997 |   strcpy(lvp->label, label);
 | 
|---|
| 998 |   strcpy(lvp->group, group);
 | 
|---|
| 999 |   strcpy(lvp->timestamp, "");
 | 
|---|
| 1000 |   
 | 
|---|
| 1001 |   lvp->s        = s;
 | 
|---|
| 1002 |   lvp->lp       = lp;
 | 
|---|
| 1003 |   lvp->nlp      = nlp;
 | 
|---|
| 1004 | }
 | 
|---|
| 1005 |  
 | 
|---|
| 1006 | void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char * dev, const char *name, const char *label, const char* group, IPerm p, double timeout, IPState s)
 | 
|---|
| 1007 | {
 | 
|---|
| 1008 |   
 | 
|---|
| 1009 |   strcpy(nvp->device, dev);
 | 
|---|
| 1010 |   strcpy(nvp->name, name);
 | 
|---|
| 1011 |   strcpy(nvp->label, label);
 | 
|---|
| 1012 |   strcpy(nvp->group, group);
 | 
|---|
| 1013 |   strcpy(nvp->timestamp, "");
 | 
|---|
| 1014 |   
 | 
|---|
| 1015 |   nvp->p        = p;
 | 
|---|
| 1016 |   nvp->timeout  = timeout;
 | 
|---|
| 1017 |   nvp->s        = s;
 | 
|---|
| 1018 |   nvp->np       = np;
 | 
|---|
| 1019 |   nvp->nnp      = nnp;
 | 
|---|
| 1020 |   
 | 
|---|
| 1021 | }
 | 
|---|
| 1022 | 
 | 
|---|
| 1023 | void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char * dev, const char *name, const char *label, const char* group, IPerm p, double timeout, IPState s)
 | 
|---|
| 1024 | {
 | 
|---|
| 1025 |   strcpy(tvp->device, dev);
 | 
|---|
| 1026 |   strcpy(tvp->name, name);
 | 
|---|
| 1027 |   strcpy(tvp->label, label);
 | 
|---|
| 1028 |   strcpy(tvp->group, group);
 | 
|---|
| 1029 |   strcpy(tvp->timestamp, "");
 | 
|---|
| 1030 |   
 | 
|---|
| 1031 |   tvp->p        = p;
 | 
|---|
| 1032 |   tvp->timeout  = timeout;
 | 
|---|
| 1033 |   tvp->s        = s;
 | 
|---|
| 1034 |   tvp->tp       = tp;
 | 
|---|
| 1035 |   tvp->ntp      = ntp;
 | 
|---|
| 1036 | 
 | 
|---|
| 1037 | }
 | 
|---|
| 1038 | 
 | 
|---|
| 1039 | /*****************************************************************************
 | 
|---|
| 1040 |  * convenience functions for use in your implementation of ISSnoopDevice().
 | 
|---|
| 1041 |  */
 | 
|---|
| 1042 | 
 | 
|---|
| 1043 | /* crack the snooped driver setNumberVector or defNumberVector message into
 | 
|---|
| 1044 |  * the given INumberVectorProperty.
 | 
|---|
| 1045 |  * return 0 if type, device and name match and all members are present, else
 | 
|---|
| 1046 |  * return -1
 | 
|---|
| 1047 |  */
 | 
|---|
| 1048 | int
 | 
|---|
| 1049 | IUSnoopNumber (XMLEle *root, INumberVectorProperty *nvp)
 | 
|---|
| 1050 | {
 | 
|---|
| 1051 |         char *dev, *name;
 | 
|---|
| 1052 |         XMLEle *ep;
 | 
|---|
| 1053 |         int i;
 | 
|---|
| 1054 | 
 | 
|---|
| 1055 |         /* check and crack type, device, name and state */
 | 
|---|
| 1056 |         if (strcmp (tagXMLEle(root)+3, "NumberVector") ||
 | 
|---|
| 1057 |                                         crackDN (root, &dev, &name, NULL) < 0)
 | 
|---|
| 1058 |             return (-1);
 | 
|---|
| 1059 |         if (strcmp (dev, nvp->device) || strcmp (name, nvp->name))
 | 
|---|
| 1060 |             return (-1);        /* not this property */
 | 
|---|
| 1061 |         (void) crackIPState (findXMLAttValu (root,"state"), &nvp->s);
 | 
|---|
| 1062 | 
 | 
|---|
| 1063 |         /* match each INumber with a oneNumber */
 | 
|---|
| 1064 |         for (i = 0; i < nvp->nnp; i++) {
 | 
|---|
| 1065 |             for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1066 |                 if (!strcmp (tagXMLEle(ep), "oneNumber") &&
 | 
|---|
| 1067 |                         !strcmp (nvp->np[i].name, findXMLAttValu(ep, "name"))) {
 | 
|---|
| 1068 |                     if (f_scansexa (pcdataXMLEle(ep), &nvp->np[i].value) < 0)
 | 
|---|
| 1069 |                         return (-1);    /* bad number format */
 | 
|---|
| 1070 |                     break;
 | 
|---|
| 1071 |                 }
 | 
|---|
| 1072 |             }
 | 
|---|
| 1073 |             if (!ep)
 | 
|---|
| 1074 |                 return (-1);    /* element not found */
 | 
|---|
| 1075 |         }
 | 
|---|
| 1076 | 
 | 
|---|
| 1077 |         /* ok */
 | 
|---|
| 1078 |         return (0);
 | 
|---|
| 1079 | }
 | 
|---|
| 1080 | 
 | 
|---|
| 1081 | /* crack the snooped driver setTextVector or defTextVector message into
 | 
|---|
| 1082 |  * the given ITextVectorProperty.
 | 
|---|
| 1083 |  * return 0 if type, device and name match and all members are present, else
 | 
|---|
| 1084 |  * return -1
 | 
|---|
| 1085 |  */
 | 
|---|
| 1086 | int
 | 
|---|
| 1087 | IUSnoopText (XMLEle *root, ITextVectorProperty *tvp)
 | 
|---|
| 1088 | {
 | 
|---|
| 1089 |         char *dev, *name;
 | 
|---|
| 1090 |         XMLEle *ep;
 | 
|---|
| 1091 |         int i;
 | 
|---|
| 1092 | 
 | 
|---|
| 1093 |         /* check and crack type, device, name and state */
 | 
|---|
| 1094 |         if (strcmp (tagXMLEle(root)+3, "TextVector") ||
 | 
|---|
| 1095 |                                         crackDN (root, &dev, &name, NULL) < 0)
 | 
|---|
| 1096 |             return (-1);
 | 
|---|
| 1097 |         if (strcmp (dev, tvp->device) || strcmp (name, tvp->name))
 | 
|---|
| 1098 |             return (-1);        /* not this property */
 | 
|---|
| 1099 |         (void) crackIPState (findXMLAttValu (root,"state"), &tvp->s);
 | 
|---|
| 1100 | 
 | 
|---|
| 1101 |         /* match each IText with a oneText */
 | 
|---|
| 1102 |         for (i = 0; i < tvp->ntp; i++) {
 | 
|---|
| 1103 |             for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1104 |                 if (!strcmp (tagXMLEle(ep), "oneText") &&
 | 
|---|
| 1105 |                         !strcmp (tvp->tp[i].name, findXMLAttValu(ep, "name"))) {
 | 
|---|
| 1106 |                     IUSaveText (&tvp->tp[i], pcdataXMLEle(ep));
 | 
|---|
| 1107 |                     break;
 | 
|---|
| 1108 |                 }
 | 
|---|
| 1109 |             }
 | 
|---|
| 1110 |             if (!ep)
 | 
|---|
| 1111 |                 return (-1);    /* element not found */
 | 
|---|
| 1112 |         }
 | 
|---|
| 1113 | 
 | 
|---|
| 1114 |         /* ok */
 | 
|---|
| 1115 |         return (0);
 | 
|---|
| 1116 | }
 | 
|---|
| 1117 | 
 | 
|---|
| 1118 | /* crack the snooped driver setLightVector or defLightVector message into
 | 
|---|
| 1119 |  * the given ILightVectorProperty. it is not necessary that all ILight names
 | 
|---|
| 1120 |  * be found.
 | 
|---|
| 1121 |  * return 0 if type, device and name match, else return -1.
 | 
|---|
| 1122 |  */
 | 
|---|
| 1123 | int
 | 
|---|
| 1124 | IUSnoopLight (XMLEle *root, ILightVectorProperty *lvp)
 | 
|---|
| 1125 | {
 | 
|---|
| 1126 |         char *dev, *name;
 | 
|---|
| 1127 |         XMLEle *ep;
 | 
|---|
| 1128 |         int i;
 | 
|---|
| 1129 | 
 | 
|---|
| 1130 |         /* check and crack type, device, name and state */
 | 
|---|
| 1131 |         if (strcmp (tagXMLEle(root)+3, "LightVector") ||
 | 
|---|
| 1132 |                                         crackDN (root, &dev, &name, NULL) < 0)
 | 
|---|
| 1133 |             return (-1);
 | 
|---|
| 1134 |         if (strcmp (dev, lvp->device) || strcmp (name, lvp->name))
 | 
|---|
| 1135 |             return (-1);        /* not this property */
 | 
|---|
| 1136 | 
 | 
|---|
| 1137 |         (void) crackIPState (findXMLAttValu (root,"state"), &lvp->s);
 | 
|---|
| 1138 | 
 | 
|---|
| 1139 |         /* match each oneLight with one ILight */
 | 
|---|
| 1140 |         for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1141 |             if (!strcmp (tagXMLEle(ep), "oneLight")) {
 | 
|---|
| 1142 |                 const char *name = findXMLAttValu (ep, "name");
 | 
|---|
| 1143 |                 for (i = 0; i < lvp->nlp; i++) {
 | 
|---|
| 1144 |                     if (!strcmp (lvp->lp[i].name, name)) {
 | 
|---|
| 1145 |                         if (crackIPState(pcdataXMLEle(ep), &lvp->lp[i].s) < 0) {
 | 
|---|
| 1146 |                             return (-1);        /* unrecognized state */
 | 
|---|
| 1147 |                         }
 | 
|---|
| 1148 |                         break;
 | 
|---|
| 1149 |                     }
 | 
|---|
| 1150 |                 }
 | 
|---|
| 1151 |             }
 | 
|---|
| 1152 |         }
 | 
|---|
| 1153 | 
 | 
|---|
| 1154 |         /* ok */
 | 
|---|
| 1155 |         return (0);
 | 
|---|
| 1156 | }
 | 
|---|
| 1157 | 
 | 
|---|
| 1158 | /* crack the snooped driver setSwitchVector or defSwitchVector message into the
 | 
|---|
| 1159 |  * given ISwitchVectorProperty. it is not necessary that all ISwitch names be
 | 
|---|
| 1160 |  * found.
 | 
|---|
| 1161 |  * return 0 if type, device and name match, else return -1.
 | 
|---|
| 1162 |  */
 | 
|---|
| 1163 | int
 | 
|---|
| 1164 | IUSnoopSwitch (XMLEle *root, ISwitchVectorProperty *svp)
 | 
|---|
| 1165 | {
 | 
|---|
| 1166 |         char *dev, *name;
 | 
|---|
| 1167 |         XMLEle *ep;
 | 
|---|
| 1168 |         int i;
 | 
|---|
| 1169 | 
 | 
|---|
| 1170 |         /* check and crack type, device, name and state */
 | 
|---|
| 1171 |         if (strcmp (tagXMLEle(root)+3, "SwitchVector") ||
 | 
|---|
| 1172 |                                         crackDN (root, &dev, &name, NULL) < 0)
 | 
|---|
| 1173 |             return (-1);
 | 
|---|
| 1174 |         if (strcmp (dev, svp->device) || strcmp (name, svp->name))
 | 
|---|
| 1175 |             return (-1);        /* not this property */
 | 
|---|
| 1176 |         (void) crackIPState (findXMLAttValu (root,"state"), &svp->s);
 | 
|---|
| 1177 | 
 | 
|---|
| 1178 |         /* match each oneSwitch with one ISwitch */
 | 
|---|
| 1179 |         for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1180 |             if (!strcmp (tagXMLEle(ep), "oneSwitch")) {
 | 
|---|
| 1181 |                 const char *name = findXMLAttValu (ep, "name");
 | 
|---|
| 1182 |                 for (i = 0; i < svp->nsp; i++) {
 | 
|---|
| 1183 |                     if (!strcmp (svp->sp[i].name, name)) {
 | 
|---|
| 1184 |                         if (crackISState(pcdataXMLEle(ep), &svp->sp[i].s) < 0) {
 | 
|---|
| 1185 |                             return (-1);        /* unrecognized state */
 | 
|---|
| 1186 |                         }
 | 
|---|
| 1187 |                         break;
 | 
|---|
| 1188 |                     }
 | 
|---|
| 1189 |                 }
 | 
|---|
| 1190 |             }
 | 
|---|
| 1191 |         }
 | 
|---|
| 1192 | 
 | 
|---|
| 1193 |         /* ok */
 | 
|---|
| 1194 |         return (0);
 | 
|---|
| 1195 | }
 | 
|---|
| 1196 | 
 | 
|---|
| 1197 | /* crack the snooped driver setBLOBVector message into the given
 | 
|---|
| 1198 |  * IBLOBVectorProperty. it is not necessary that all IBLOB names be found.
 | 
|---|
| 1199 |  * return 0 if type, device and name match, else return -1.
 | 
|---|
| 1200 |  * N.B. we assume any existing blob in bvp has been malloced, which we free
 | 
|---|
| 1201 |  *   and replace with a newly malloced blob if found.
 | 
|---|
| 1202 |  */
 | 
|---|
| 1203 | int
 | 
|---|
| 1204 | IUSnoopBLOB (XMLEle *root, IBLOBVectorProperty *bvp)
 | 
|---|
| 1205 | {
 | 
|---|
| 1206 |         char *dev, *name;
 | 
|---|
| 1207 |         XMLEle *ep;
 | 
|---|
| 1208 |         int i;
 | 
|---|
| 1209 | 
 | 
|---|
| 1210 |         /* check and crack type, device, name and state */
 | 
|---|
| 1211 |         if (strcmp (tagXMLEle(root), "setBLOBVector") ||
 | 
|---|
| 1212 |                                         crackDN (root, &dev, &name, NULL) < 0)
 | 
|---|
| 1213 |             return (-1);
 | 
|---|
| 1214 |         if (strcmp (dev, bvp->device) || strcmp (name, bvp->name))
 | 
|---|
| 1215 |             return (-1);        /* not this property */
 | 
|---|
| 1216 |         (void) crackIPState (findXMLAttValu (root,"state"), &bvp->s);
 | 
|---|
| 1217 | 
 | 
|---|
| 1218 |         /* match each oneBLOB with one IBLOB */
 | 
|---|
| 1219 |         for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1220 |             if (!strcmp (tagXMLEle(ep), "oneBLOB")) {
 | 
|---|
| 1221 |                 const char *name = findXMLAttValu (ep, "name");
 | 
|---|
| 1222 |                 for (i = 0; i < bvp->nbp; i++) {
 | 
|---|
| 1223 |                     IBLOB *bp = &bvp->bp[i];
 | 
|---|
| 1224 |                     if (!strcmp (bp->name, name)) {
 | 
|---|
| 1225 |                         strcpy (bp->format, findXMLAttValu (ep,"format"));
 | 
|---|
| 1226 |                         bp->size = atof (findXMLAttValu (ep,"size"));
 | 
|---|
| 1227 |                         bp->bloblen = pcdatalenXMLEle(ep)+1;
 | 
|---|
| 1228 |                         if (bp->blob)
 | 
|---|
| 1229 |                             free (bp->blob);
 | 
|---|
| 1230 |                         bp->blob = strcpy(malloc(bp->bloblen),pcdataXMLEle(ep));
 | 
|---|
| 1231 |                         break;
 | 
|---|
| 1232 |                     }
 | 
|---|
| 1233 |                 }
 | 
|---|
| 1234 |             }
 | 
|---|
| 1235 |         }
 | 
|---|
| 1236 | 
 | 
|---|
| 1237 |         /* ok */
 | 
|---|
| 1238 |         return (0);
 | 
|---|
| 1239 | }
 | 
|---|
| 1240 | 
 | 
|---|
| 1241 | /* print usage message and exit (1) */
 | 
|---|
| 1242 | static void
 | 
|---|
| 1243 | usage(void)
 | 
|---|
| 1244 | {
 | 
|---|
| 1245 |         fprintf (stderr, "Usage: %s [options]\n", me);
 | 
|---|
| 1246 |         fprintf (stderr, "Purpose: INDI Device driver framework.\n");
 | 
|---|
| 1247 |         fprintf (stderr, "Options:\n");
 | 
|---|
| 1248 |         fprintf (stderr, " -v    : more verbose to stderr\n");
 | 
|---|
| 1249 | 
 | 
|---|
| 1250 |         exit (1);
 | 
|---|
| 1251 | }
 | 
|---|
| 1252 | 
 | 
|---|
| 1253 | /* callback when INDI client message arrives on stdin.
 | 
|---|
| 1254 |  * collect and dispatch when see outter element closure.
 | 
|---|
| 1255 |  * exit if OS trouble or see incompatable INDI version.
 | 
|---|
| 1256 |  * arg is not used.
 | 
|---|
| 1257 |  */
 | 
|---|
| 1258 | static void
 | 
|---|
| 1259 | clientMsgCB (int fd, void *arg)
 | 
|---|
| 1260 | {
 | 
|---|
| 1261 |         char buf[1024], msg[1024], *bp;
 | 
|---|
| 1262 |         int nr;
 | 
|---|
| 1263 |         arg=arg;
 | 
|---|
| 1264 | 
 | 
|---|
| 1265 |         /* one read */
 | 
|---|
| 1266 |         nr = read (fd, buf, sizeof(buf));
 | 
|---|
| 1267 |         if (nr < 0) {
 | 
|---|
| 1268 |             fprintf (stderr, "%s: %s\n", me, strerror(errno));
 | 
|---|
| 1269 |             exit(1);
 | 
|---|
| 1270 |         }
 | 
|---|
| 1271 |         if (nr == 0) {
 | 
|---|
| 1272 |             fprintf (stderr, "%s: EOF\n", me);
 | 
|---|
| 1273 |             exit(1);
 | 
|---|
| 1274 |         }
 | 
|---|
| 1275 | 
 | 
|---|
| 1276 |         /* crack and dispatch when complete */
 | 
|---|
| 1277 |         for (bp = buf; nr-- > 0; bp++) {
 | 
|---|
| 1278 |             XMLEle *root = readXMLEle (clixml, *bp, msg);
 | 
|---|
| 1279 |             if (root) {
 | 
|---|
| 1280 |                 if (dispatch (root, msg) < 0)
 | 
|---|
| 1281 |                     fprintf (stderr, "%s dispatch error: %s\n", me, msg);
 | 
|---|
| 1282 |                 delXMLEle (root);
 | 
|---|
| 1283 |             } else if (msg[0])
 | 
|---|
| 1284 |                 fprintf (stderr, "%s XML error: %s\n", me, msg);
 | 
|---|
| 1285 |         }
 | 
|---|
| 1286 | 
 | 
|---|
| 1287 | }
 | 
|---|
| 1288 | 
 | 
|---|
| 1289 | /* crack the given INDI XML element and call driver's IS* entry points as they
 | 
|---|
| 1290 |  *   are recognized.
 | 
|---|
| 1291 |  * return 0 if ok else -1 with reason in msg[].
 | 
|---|
| 1292 |  * N.B. exit if getProperties does not proclaim a compatible version.
 | 
|---|
| 1293 |  */
 | 
|---|
| 1294 | static int
 | 
|---|
| 1295 | dispatch (XMLEle *root, char msg[])
 | 
|---|
| 1296 | {
 | 
|---|
| 1297 |         char *rtag = tagXMLEle(root);
 | 
|---|
| 1298 |         XMLEle *ep;
 | 
|---|
| 1299 |         int n,i=0;
 | 
|---|
| 1300 | 
 | 
|---|
| 1301 |         if (verbose)
 | 
|---|
| 1302 |             prXMLEle (stderr, root, 0);
 | 
|---|
| 1303 | 
 | 
|---|
| 1304 |         /* check tag in surmised decreasing order of likelyhood */
 | 
|---|
| 1305 | 
 | 
|---|
| 1306 |         if (!strcmp (rtag, "newNumberVector")) {
 | 
|---|
| 1307 |             static double *doubles;
 | 
|---|
| 1308 |             static char **names;
 | 
|---|
| 1309 |             static int maxn;
 | 
|---|
| 1310 |             char *dev, *name;
 | 
|---|
| 1311 | 
 | 
|---|
| 1312 |             /* pull out device and name */
 | 
|---|
| 1313 |             if (crackDN (root, &dev, &name, msg) < 0)
 | 
|---|
| 1314 |                 return (-1);
 | 
|---|
| 1315 | 
 | 
|---|
| 1316 |             /* seed for reallocs */
 | 
|---|
| 1317 |             if (!doubles) {
 | 
|---|
| 1318 |                 doubles = (double *) malloc (1);
 | 
|---|
| 1319 |                 names = (char **) malloc (1);
 | 
|---|
| 1320 |             }
 | 
|---|
| 1321 | 
 | 
|---|
| 1322 |             /* pull out each name/value pair */
 | 
|---|
| 1323 |             for (n = 0, ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1324 |                 if (strcmp (tagXMLEle(ep), "oneNumber") == 0) {
 | 
|---|
| 1325 |                     XMLAtt *na = findXMLAtt (ep, "name");
 | 
|---|
| 1326 |                     if (na) {
 | 
|---|
| 1327 |                         if (n >= maxn) {
 | 
|---|
| 1328 |                             /* grow for this and another */
 | 
|---|
| 1329 |                             int newsz = (maxn=n+1)*sizeof(double);
 | 
|---|
| 1330 |                             doubles = (double *) realloc(doubles,newsz);
 | 
|---|
| 1331 |                             newsz = maxn*sizeof(char *);
 | 
|---|
| 1332 |                             names = (char **) realloc (names, newsz);
 | 
|---|
| 1333 |                         }
 | 
|---|
| 1334 |                         if (f_scansexa (pcdataXMLEle(ep), &doubles[n]) < 0)
 | 
|---|
| 1335 |                             IDMessage (dev,"%s: Bad format %s", name,
 | 
|---|
| 1336 |                                                             pcdataXMLEle(ep));
 | 
|---|
| 1337 |                         else
 | 
|---|
| 1338 |                             names[n++] = valuXMLAtt(na);
 | 
|---|
| 1339 |                     }
 | 
|---|
| 1340 |                 }
 | 
|---|
| 1341 |             }
 | 
|---|
| 1342 |             
 | 
|---|
| 1343 |             /* insure property is not RO */
 | 
|---|
| 1344 |             for (i=0; i < nroCheck; i++)
 | 
|---|
| 1345 |             {
 | 
|---|
| 1346 |               if (!strcmp(roCheck[i].propName, name))
 | 
|---|
| 1347 |               {
 | 
|---|
| 1348 |                if (roCheck[i].perm == IP_RO)
 | 
|---|
| 1349 |                  return -1;
 | 
|---|
| 1350 |               }
 | 
|---|
| 1351 |             }
 | 
|---|
| 1352 | 
 | 
|---|
| 1353 |             /* invoke driver if something to do, but not an error if not */
 | 
|---|
| 1354 |             if (n > 0)
 | 
|---|
| 1355 |                 ISNewNumber (dev, name, doubles, names, n);
 | 
|---|
| 1356 |             else
 | 
|---|
| 1357 |                 IDMessage(dev,"%s: newNumberVector with no valid members",name);
 | 
|---|
| 1358 |             return (0);
 | 
|---|
| 1359 |         }
 | 
|---|
| 1360 | 
 | 
|---|
| 1361 |         if (!strcmp (rtag, "newSwitchVector")) {
 | 
|---|
| 1362 |             static ISState *states;
 | 
|---|
| 1363 |             static char **names;
 | 
|---|
| 1364 |             static int maxn;
 | 
|---|
| 1365 |             char *dev, *name;
 | 
|---|
| 1366 |             XMLEle *ep;
 | 
|---|
| 1367 | 
 | 
|---|
| 1368 |             /* pull out device and name */
 | 
|---|
| 1369 |             if (crackDN (root, &dev, &name, msg) < 0)
 | 
|---|
| 1370 |                 return (-1);
 | 
|---|
| 1371 | 
 | 
|---|
| 1372 |             /* seed for reallocs */
 | 
|---|
| 1373 |             if (!states) {
 | 
|---|
| 1374 |                 states = (ISState *) malloc (1);
 | 
|---|
| 1375 |                 names = (char **) malloc (1);
 | 
|---|
| 1376 |             }
 | 
|---|
| 1377 | 
 | 
|---|
| 1378 |             /* pull out each name/state pair */
 | 
|---|
| 1379 |             for (n = 0, ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1380 |                 if (strcmp (tagXMLEle(ep), "oneSwitch") == 0) {
 | 
|---|
| 1381 |                     XMLAtt *na = findXMLAtt (ep, "name");
 | 
|---|
| 1382 |                     if (na) {
 | 
|---|
| 1383 |                         if (n >= maxn) {
 | 
|---|
| 1384 |                             int newsz = (maxn=n+1)*sizeof(ISState);
 | 
|---|
| 1385 |                             states = (ISState *) realloc(states, newsz);
 | 
|---|
| 1386 |                             newsz = maxn*sizeof(char *);
 | 
|---|
| 1387 |                             names = (char **) realloc (names, newsz);
 | 
|---|
| 1388 |                         }
 | 
|---|
| 1389 |                         if (strcmp (pcdataXMLEle(ep),"On") == 0) {
 | 
|---|
| 1390 |                             states[n] = ISS_ON;
 | 
|---|
| 1391 |                             names[n] = valuXMLAtt(na);
 | 
|---|
| 1392 |                             n++;
 | 
|---|
| 1393 |                         } else if (strcmp (pcdataXMLEle(ep),"Off") == 0) {
 | 
|---|
| 1394 |                             states[n] = ISS_OFF;
 | 
|---|
| 1395 |                             names[n] = valuXMLAtt(na);
 | 
|---|
| 1396 |                             n++;
 | 
|---|
| 1397 |                         } else 
 | 
|---|
| 1398 |                             IDMessage (dev, "%s: must be On or Off: %s", name,
 | 
|---|
| 1399 |                                                             pcdataXMLEle(ep));
 | 
|---|
| 1400 |                     }
 | 
|---|
| 1401 |                 }
 | 
|---|
| 1402 |             }
 | 
|---|
| 1403 | 
 | 
|---|
| 1404 |             /* insure property is not RO */
 | 
|---|
| 1405 |             for (i=0; i < nroCheck; i++)
 | 
|---|
| 1406 |             {
 | 
|---|
| 1407 |               if (!strcmp(roCheck[i].propName, name))
 | 
|---|
| 1408 |               {
 | 
|---|
| 1409 |                if (roCheck[i].perm == IP_RO)
 | 
|---|
| 1410 |                  return -1;
 | 
|---|
| 1411 |               }
 | 
|---|
| 1412 |             }
 | 
|---|
| 1413 |             
 | 
|---|
| 1414 |             /* invoke driver if something to do, but not an error if not */
 | 
|---|
| 1415 |             if (n > 0)
 | 
|---|
| 1416 |                 ISNewSwitch (dev, name, states, names, n);
 | 
|---|
| 1417 |             else
 | 
|---|
| 1418 |                 IDMessage(dev,"%s: newSwitchVector with no valid members",name);
 | 
|---|
| 1419 |             return (0);
 | 
|---|
| 1420 |         }
 | 
|---|
| 1421 | 
 | 
|---|
| 1422 |         if (!strcmp (rtag, "newTextVector")) {
 | 
|---|
| 1423 |             static char **texts;
 | 
|---|
| 1424 |             static char **names;
 | 
|---|
| 1425 |             static int maxn;
 | 
|---|
| 1426 |             char *dev, *name;
 | 
|---|
| 1427 | 
 | 
|---|
| 1428 |             /* pull out device and name */
 | 
|---|
| 1429 |             if (crackDN (root, &dev, &name, msg) < 0)
 | 
|---|
| 1430 |                 return (-1);
 | 
|---|
| 1431 | 
 | 
|---|
| 1432 |             /* seed for reallocs */
 | 
|---|
| 1433 |             if (!texts) {
 | 
|---|
| 1434 |                 texts = (char **) malloc (1);
 | 
|---|
| 1435 |                 names = (char **) malloc (1);
 | 
|---|
| 1436 |             }
 | 
|---|
| 1437 | 
 | 
|---|
| 1438 |             /* pull out each name/text pair */
 | 
|---|
| 1439 |             for (n = 0, ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1440 |                 if (strcmp (tagXMLEle(ep), "oneText") == 0) {
 | 
|---|
| 1441 |                     XMLAtt *na = findXMLAtt (ep, "name");
 | 
|---|
| 1442 |                     if (na) {
 | 
|---|
| 1443 |                         if (n >= maxn) {
 | 
|---|
| 1444 |                             int newsz = (maxn=n+1)*sizeof(char *);
 | 
|---|
| 1445 |                             texts = (char **) realloc (texts, newsz);
 | 
|---|
| 1446 |                             names = (char **) realloc (names, newsz);
 | 
|---|
| 1447 |                         }
 | 
|---|
| 1448 |                         texts[n] = pcdataXMLEle(ep);
 | 
|---|
| 1449 |                         names[n] = valuXMLAtt(na);
 | 
|---|
| 1450 |                         n++;
 | 
|---|
| 1451 |                     }
 | 
|---|
| 1452 |                 }
 | 
|---|
| 1453 |             }
 | 
|---|
| 1454 |             
 | 
|---|
| 1455 |             /* insure property is not RO */
 | 
|---|
| 1456 |             for (i=0; i < nroCheck; i++)
 | 
|---|
| 1457 |             {
 | 
|---|
| 1458 |               if (!strcmp(roCheck[i].propName, name))
 | 
|---|
| 1459 |               {
 | 
|---|
| 1460 |                if (roCheck[i].perm == IP_RO)
 | 
|---|
| 1461 |                  return -1;
 | 
|---|
| 1462 |               }
 | 
|---|
| 1463 |             }
 | 
|---|
| 1464 | 
 | 
|---|
| 1465 |             /* invoke driver if something to do, but not an error if not */
 | 
|---|
| 1466 |             if (n > 0)
 | 
|---|
| 1467 |                 ISNewText (dev, name, texts, names, n);
 | 
|---|
| 1468 |             else
 | 
|---|
| 1469 |                 IDMessage (dev, "%s: set with no valid members", name);
 | 
|---|
| 1470 |             return (0);
 | 
|---|
| 1471 |         }
 | 
|---|
| 1472 | 
 | 
|---|
| 1473 |         if (!strcmp (rtag, "newBLOBVector")) {
 | 
|---|
| 1474 |             static char **blobs;
 | 
|---|
| 1475 |             static char **names;
 | 
|---|
| 1476 |             static char **formats;
 | 
|---|
| 1477 |             static int *blobsizes;
 | 
|---|
| 1478 |             static int *sizes;
 | 
|---|
| 1479 |             static int maxn;
 | 
|---|
| 1480 |             char *dev, *name;
 | 
|---|
| 1481 |             int i;
 | 
|---|
| 1482 | 
 | 
|---|
| 1483 |             /* pull out device and name */
 | 
|---|
| 1484 |             if (crackDN (root, &dev, &name, msg) < 0)
 | 
|---|
| 1485 |                 return (-1);
 | 
|---|
| 1486 | 
 | 
|---|
| 1487 |             /* seed for reallocs */
 | 
|---|
| 1488 |             if (!blobs) {
 | 
|---|
| 1489 |                 blobs = (char **) malloc (1);
 | 
|---|
| 1490 |                 names = (char **) malloc (1);
 | 
|---|
| 1491 |                 formats = (char **) malloc (1);
 | 
|---|
| 1492 |                 blobsizes = (int *) malloc (1);
 | 
|---|
| 1493 |                 sizes = (int *) malloc (1);
 | 
|---|
| 1494 |             }
 | 
|---|
| 1495 | 
 | 
|---|
| 1496 |             /* pull out each name/BLOB pair, decode */
 | 
|---|
| 1497 |             for (n = 0, ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) {
 | 
|---|
| 1498 |                 if (strcmp (tagXMLEle(ep), "oneBLOB") == 0) {
 | 
|---|
| 1499 |                     XMLAtt *na = findXMLAtt (ep, "name");
 | 
|---|
| 1500 |                     XMLAtt *fa = findXMLAtt (ep, "format");
 | 
|---|
| 1501 |                     XMLAtt *sa = findXMLAtt (ep, "size");
 | 
|---|
| 1502 |                     if (na && fa && sa) {
 | 
|---|
| 1503 |                         if (n >= maxn) {
 | 
|---|
| 1504 |                             int newsz = (maxn=n+1)*sizeof(char *);
 | 
|---|
| 1505 |                             blobs = (char **) realloc (blobs, newsz);
 | 
|---|
| 1506 |                             names = (char **) realloc (names, newsz);
 | 
|---|
| 1507 |                             formats = (char **) realloc(formats,newsz);
 | 
|---|
| 1508 |                             newsz = maxn*sizeof(int);
 | 
|---|
| 1509 |                             sizes = (int *) realloc(sizes,newsz);
 | 
|---|
| 1510 |                             blobsizes = (int *) realloc(blobsizes,newsz);
 | 
|---|
| 1511 |                         }
 | 
|---|
| 1512 |                         blobs[n] = malloc (3*pcdatalenXMLEle(ep)/4);
 | 
|---|
| 1513 |                         blobsizes[n] = from64tobits(blobs[n], pcdataXMLEle(ep));
 | 
|---|
| 1514 |                         names[n] = valuXMLAtt(na);
 | 
|---|
| 1515 |                         formats[n] = valuXMLAtt(fa);
 | 
|---|
| 1516 |                         sizes[n] = atoi(valuXMLAtt(sa));
 | 
|---|
| 1517 |                         n++;
 | 
|---|
| 1518 |                     }
 | 
|---|
| 1519 |                 }
 | 
|---|
| 1520 |             }
 | 
|---|
| 1521 | 
 | 
|---|
| 1522 |             /* invoke driver if something to do, but not an error if not */
 | 
|---|
| 1523 |             if (n > 0) {
 | 
|---|
| 1524 |                 ISNewBLOB (dev, name, sizes, blobsizes, blobs, formats,names,n);
 | 
|---|
| 1525 |                 for (i = 0; i < n; i++)
 | 
|---|
| 1526 |                     free (blobs[i]);
 | 
|---|
| 1527 |             } else
 | 
|---|
| 1528 |                 IDMessage (dev, "%s: newBLOBVector with no valid members",name);
 | 
|---|
| 1529 |             return (0);
 | 
|---|
| 1530 |         }
 | 
|---|
| 1531 | 
 | 
|---|
| 1532 |         if (!strcmp (rtag, "getProperties")) {
 | 
|---|
| 1533 |             XMLAtt *ap;
 | 
|---|
| 1534 |             double v;
 | 
|---|
| 1535 | 
 | 
|---|
| 1536 |             /* check version */
 | 
|---|
| 1537 |             ap = findXMLAtt (root, "version");
 | 
|---|
| 1538 |             if (!ap) {
 | 
|---|
| 1539 |                 fprintf (stderr, "%s: getProperties missing version\n", me);
 | 
|---|
| 1540 |                 exit(1);
 | 
|---|
| 1541 |             }
 | 
|---|
| 1542 |             v = atof (valuXMLAtt(ap));
 | 
|---|
| 1543 |             if (v > INDIV) {
 | 
|---|
| 1544 |                 fprintf (stderr, "%s: client version %g > %g\n", me, v, INDIV);
 | 
|---|
| 1545 |                 exit(1);
 | 
|---|
| 1546 |             }
 | 
|---|
| 1547 | 
 | 
|---|
| 1548 |             /* ok */
 | 
|---|
| 1549 |             ap = findXMLAtt (root, "device");
 | 
|---|
| 1550 |             ISGetProperties (ap ? valuXMLAtt(ap) : NULL);
 | 
|---|
| 1551 |             return (0);
 | 
|---|
| 1552 |         }
 | 
|---|
| 1553 | 
 | 
|---|
| 1554 |         /* other commands might be from a snooped device.
 | 
|---|
| 1555 |          * we don't know here which devices are being snooped so we send
 | 
|---|
| 1556 |          * all remaining valid messages
 | 
|---|
| 1557 |          */
 | 
|---|
| 1558 |         if (        !strcmp (rtag, "setNumberVector") ||
 | 
|---|
| 1559 |                     !strcmp (rtag, "setTextVector") ||
 | 
|---|
| 1560 |                     !strcmp (rtag, "setLightVector") ||
 | 
|---|
| 1561 |                     !strcmp (rtag, "setSwitchVector") ||
 | 
|---|
| 1562 |                     !strcmp (rtag, "setBLOBVector") ||
 | 
|---|
| 1563 |                     !strcmp (rtag, "defNumberVector") ||
 | 
|---|
| 1564 |                     !strcmp (rtag, "defTextVector") ||
 | 
|---|
| 1565 |                     !strcmp (rtag, "defLightVector") ||
 | 
|---|
| 1566 |                     !strcmp (rtag, "defSwitchVector") ||
 | 
|---|
| 1567 |                     !strcmp (rtag, "defBLOBVector") ||
 | 
|---|
| 1568 |                     !strcmp (rtag, "message") ||
 | 
|---|
| 1569 |                     !strcmp (rtag, "delProperty")) {
 | 
|---|
| 1570 |             ISSnoopDevice (root);
 | 
|---|
| 1571 |             return (0);
 | 
|---|
| 1572 |         }
 | 
|---|
| 1573 | 
 | 
|---|
| 1574 |         sprintf (msg, "Unknown command: %s", rtag);
 | 
|---|
| 1575 |         return(1);
 | 
|---|
| 1576 | }
 | 
|---|
| 1577 | 
 | 
|---|
| 1578 | /* pull out device and name attributes from root.
 | 
|---|
| 1579 |  * return 0 if ok else -1 with reason in msg[].
 | 
|---|
| 1580 |  */
 | 
|---|
| 1581 | static int
 | 
|---|
| 1582 | crackDN (XMLEle *root, char **dev, char **name, char msg[])
 | 
|---|
| 1583 | {
 | 
|---|
| 1584 |         XMLAtt *ap;
 | 
|---|
| 1585 | 
 | 
|---|
| 1586 |         ap = findXMLAtt (root, "device");
 | 
|---|
| 1587 |         if (!ap) {
 | 
|---|
| 1588 |             sprintf (msg, "%s requires 'device' attribute", tagXMLEle(root));
 | 
|---|
| 1589 |             return (-1);
 | 
|---|
| 1590 |         }
 | 
|---|
| 1591 |         *dev = valuXMLAtt(ap);
 | 
|---|
| 1592 | 
 | 
|---|
| 1593 |         ap = findXMLAtt (root, "name");
 | 
|---|
| 1594 |         if (!ap) {
 | 
|---|
| 1595 |             sprintf (msg, "%s requires 'name' attribute", tagXMLEle(root));
 | 
|---|
| 1596 |             return (-1);
 | 
|---|
| 1597 |         }
 | 
|---|
| 1598 |         *name = valuXMLAtt(ap);
 | 
|---|
| 1599 | 
 | 
|---|
| 1600 |         return (0);
 | 
|---|
| 1601 | }
 | 
|---|
| 1602 | 
 | 
|---|
| 1603 | /* return static string corresponding to the given property or light state */
 | 
|---|
| 1604 | const char *
 | 
|---|
| 1605 | pstateStr (IPState s)
 | 
|---|
| 1606 | {
 | 
|---|
| 1607 |         switch (s) {
 | 
|---|
| 1608 |         case IPS_IDLE:  return ("Idle");
 | 
|---|
| 1609 |         case IPS_OK:    return ("Ok");
 | 
|---|
| 1610 |         case IPS_BUSY:  return ("Busy");
 | 
|---|
| 1611 |         case IPS_ALERT: return ("Alert");
 | 
|---|
| 1612 |         default:
 | 
|---|
| 1613 |             fprintf (stderr, "Impossible IPState %d\n", s);
 | 
|---|
| 1614 |             exit(1);
 | 
|---|
| 1615 |         }
 | 
|---|
| 1616 | }
 | 
|---|
| 1617 | 
 | 
|---|
| 1618 | /* crack string into IPState.
 | 
|---|
| 1619 |  * return 0 if ok, else -1
 | 
|---|
| 1620 |  */
 | 
|---|
| 1621 | static int
 | 
|---|
| 1622 | crackIPState (const char *str, IPState *ip)
 | 
|---|
| 1623 | {
 | 
|---|
| 1624 |              if (!strcmp (str, "Idle"))  *ip = IPS_IDLE;
 | 
|---|
| 1625 |         else if (!strcmp (str, "Ok"))    *ip = IPS_OK;
 | 
|---|
| 1626 |         else if (!strcmp (str, "Busy"))  *ip = IPS_BUSY;
 | 
|---|
| 1627 |         else if (!strcmp (str, "Alert")) *ip = IPS_ALERT;
 | 
|---|
| 1628 |         else return (-1);
 | 
|---|
| 1629 |         return (0);
 | 
|---|
| 1630 | }
 | 
|---|
| 1631 | 
 | 
|---|
| 1632 | /* crack string into ISState.
 | 
|---|
| 1633 |  * return 0 if ok, else -1
 | 
|---|
| 1634 |  */
 | 
|---|
| 1635 | static int
 | 
|---|
| 1636 | crackISState (const char *str, ISState *ip)
 | 
|---|
| 1637 | {
 | 
|---|
| 1638 |              if (!strcmp (str, "On"))  *ip = ISS_ON;
 | 
|---|
| 1639 |         else if (!strcmp (str, "Off")) *ip = ISS_OFF;
 | 
|---|
| 1640 |         else return (-1);
 | 
|---|
| 1641 |         return (0);
 | 
|---|
| 1642 | }
 | 
|---|
| 1643 | 
 | 
|---|
| 1644 | /* return static string corresponding to the given switch state */
 | 
|---|
| 1645 | const char *
 | 
|---|
| 1646 | sstateStr (ISState s)
 | 
|---|
| 1647 | {
 | 
|---|
| 1648 |         switch (s) {
 | 
|---|
| 1649 |         case ISS_ON:  return ("On");
 | 
|---|
| 1650 |         case ISS_OFF: return ("Off");
 | 
|---|
| 1651 |         default:
 | 
|---|
| 1652 |             fprintf (stderr, "Impossible ISState %d\n", s);
 | 
|---|
| 1653 |             exit(1);
 | 
|---|
| 1654 |         }
 | 
|---|
| 1655 | }
 | 
|---|
| 1656 | 
 | 
|---|
| 1657 | /* return static string corresponding to the given Rule */
 | 
|---|
| 1658 | const char *
 | 
|---|
| 1659 | ruleStr (ISRule r)
 | 
|---|
| 1660 | {
 | 
|---|
| 1661 |         switch (r) {
 | 
|---|
| 1662 |         case ISR_1OFMANY: return ("OneOfMany");
 | 
|---|
| 1663 |         case ISR_ATMOST1: return ("AtMostOne");
 | 
|---|
| 1664 |         case ISR_NOFMANY: return ("AnyOfMany");
 | 
|---|
| 1665 |         default:
 | 
|---|
| 1666 |             fprintf (stderr, "Impossible ISRule %d\n", r);
 | 
|---|
| 1667 |             exit(1);
 | 
|---|
| 1668 |         }
 | 
|---|
| 1669 | }
 | 
|---|
| 1670 | 
 | 
|---|
| 1671 | /* return static string corresponding to the given IPerm */
 | 
|---|
| 1672 | const char *
 | 
|---|
| 1673 | permStr (IPerm p)
 | 
|---|
| 1674 | {
 | 
|---|
| 1675 |         switch (p) {
 | 
|---|
| 1676 |         case IP_RO: return ("ro");
 | 
|---|
| 1677 |         case IP_WO: return ("wo");
 | 
|---|
| 1678 |         case IP_RW: return ("rw");
 | 
|---|
| 1679 |         default:
 | 
|---|
| 1680 |             fprintf (stderr, "Impossible IPerm %d\n", p);
 | 
|---|
| 1681 |             exit(1);
 | 
|---|
| 1682 |         }
 | 
|---|
| 1683 | }
 | 
|---|
| 1684 | 
 | 
|---|
| 1685 | /* print the boilerplate comment introducing xml */
 | 
|---|
| 1686 | static void
 | 
|---|
| 1687 | xmlv1()
 | 
|---|
| 1688 | {
 | 
|---|
| 1689 |         printf ("<?xml version='1.0'?>\n");
 | 
|---|
| 1690 | }
 | 
|---|