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