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