source: BAORadio/libindi/v1/indidrivermain.c @ 612

Last change on this file since 612 was 490, checked in by campagne, 14 years ago

import libindi (JEC)

File size: 40.9 KB
Line 
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
49static void usage(void);
50static void clientMsgCB(int fd, void *arg);
51static int dispatch (XMLEle *root, char msg[]);
52static int crackDN (XMLEle *root, char **dev, char **name, char msg[]);
53static int isPropDefined(const char *property_name);
54static int crackIPState (const char *str, IPState *ip);
55static int crackISState (const char *str, ISState *ip);
56static void xmlv1(void);
57const char *pstateStr(IPState s);
58const char *sstateStr(ISState s);
59const char *ruleStr(ISRule r);
60const char *permStr(IPerm p);
61
62
63static int nroCheck;                    /* # of elements in roCheck */
64static int verbose;                     /* chatty */
65char *me;                               /* a.out name */
66static LilXML *clixml;                  /* XML parser context */
67
68/* insure RO properties are never modified. RO Sanity Check */
69typedef struct
70{
71  char propName[MAXINDINAME];
72  IPerm perm;
73} ROSC;
74
75static ROSC *roCheck;
76
77int
78main (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 */
124static 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 */
139void
140IDDefText (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 */
191void
192IDDefNumber (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 */
248void
249IDDefSwitch (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 */
302void
303IDDefLight (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 */
339void
340IDDefBLOB (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 */
390void
391IDSetText (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 */
424void
425IDSetNumber (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 */
458void
459IDSetSwitch (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 */
492void
493IDSetLight (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 */
525void
526IDSetBLOB (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 */
571void 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 */
600void
601IDMessage (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 */
624void
625IDDelete (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 */
647void
648IDSnoopDevice (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 */
662void 
663IDSnoopBLOBs (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
682int
683IEAddCallback (int readfiledes, IE_CBF *fp, void *p)
684{
685        return (addCallback (readfiledes, (CBF*)fp, p));
686}
687
688void
689IERmCallback (int callbackid)
690{
691        rmCallback (callbackid);
692}
693
694int
695IEAddTimer (int millisecs, IE_TCF *fp, void *p)
696{
697        return (addTimer (millisecs, (TCF*)fp, p));
698}
699
700void
701IERmTimer (int timerid)
702{
703        rmTimer (timerid);
704}
705
706int
707IEAddWorkProc (IE_WPF *fp, void *p)
708{
709        return (addWorkProc ((WPF*)fp, p));
710}
711
712void
713IERmWorkProc (int workprocid)
714{
715        rmWorkProc (workprocid);
716}
717
718
719int
720IEDeferLoop (int maxms, int *flagp)
721{
722        return (deferLoop (maxms, flagp));
723}
724
725int
726IEDeferLoop0 (int maxms, int *flagp)
727{
728        return (deferLoop0 (maxms, flagp));
729}
730
731/* find a member of an IText vector, else NULL */
732IText *
733IUFindText  (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 */
745INumber *
746IUFindNumber(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 */
758ISwitch *
759IUFindSwitch(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 */
773ISwitch *
774IUFindOnSwitch(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 */
786void 
787IUResetSwitch(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. */
796int 
797IUUpdateSwitch(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 */
851int 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 */
888int 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 */
917void
918IUSaveText (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
928void 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
937void 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
947void 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
963void 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
977void 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
993void 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 
1006void 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
1023void 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 */
1048int
1049IUSnoopNumber (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 */
1086int
1087IUSnoopText (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 */
1123int
1124IUSnoopLight (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 */
1163int
1164IUSnoopSwitch (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 */
1203int
1204IUSnoopBLOB (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) */
1242static void
1243usage(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 */
1258static void
1259clientMsgCB (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 */
1294static int
1295dispatch (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 */
1581static int
1582crackDN (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 */
1604const char *
1605pstateStr (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 */
1621static int
1622crackIPState (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 */
1635static int
1636crackISState (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 */
1645const char *
1646sstateStr (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 */
1658const char *
1659ruleStr (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 */
1672const char *
1673permStr (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 */
1686static void
1687xmlv1()
1688{
1689        printf ("<?xml version='1.0'?>\n");
1690}
Note: See TracBrowser for help on using the repository browser.