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