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