Instrument Neutral Distributed Interface INDI  1.6.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, "[ERROR] %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, "[ERROR] %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, "[ERROR] %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, "[ERROR] %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, "[ERROR] %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, "[ERROR] %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, "[INFO] 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, "[INFO] 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 int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
1316 {
1317  char *rname, *rdev;
1318  XMLEle *root = NULL, *fproot = NULL;
1319  char errmsg[MAXRBUF];
1320  LilXML *lp = newLilXML();
1321  int valueFound=0;
1322 
1323  FILE *fp = IUGetConfigFP(NULL, dev, "r", errmsg);
1324 
1325  if (fp == NULL)
1326  return -1;
1327 
1328  fproot = readXMLFile(fp, lp, errmsg);
1329 
1330  if (fproot == NULL)
1331  {
1332  fclose(fp);
1333  return -1;
1334  }
1335 
1336  for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1337  {
1338  /* pull out device and name */
1339  if (crackDN(root, &rdev, &rname, errmsg) < 0)
1340  {
1341  fclose(fp);
1342  return -1;
1343  }
1344 
1345  // It doesn't belong to our device??
1346  if (strcmp(dev, rdev))
1347  continue;
1348 
1349  if ((property && !strcmp(property, rname)) || property == NULL)
1350  {
1351  XMLEle *oneNumber = NULL;
1352  for (oneNumber = nextXMLEle(root, 1); oneNumber != NULL; oneNumber = nextXMLEle(root, 0))
1353  {
1354  if (!strcmp(member, findXMLAttValu(oneNumber, "name")))
1355  {
1356  *value = atof(pcdataXMLEle(oneNumber));
1357  valueFound = 1;
1358  break;
1359  }
1360  }
1361  break;
1362  }
1363  }
1364 
1365  fclose(fp);
1366  delXMLEle(fproot);
1367  delLilXML(lp);
1368 
1369  return (valueFound == 1 ? 0 : -1);
1370 }
1371 
1372 /* send client a message for a specific device or at large if !dev */
1373 void IDMessage(const char *dev, const char *fmt, ...)
1374 {
1375  pthread_mutex_lock(&stdout_mutex);
1376 
1377  xmlv1();
1378  printf("<message\n");
1379  if (dev)
1380  printf(" device='%s'\n", dev);
1381  printf(" timestamp='%s'\n", timestamp());
1382  if (fmt)
1383  {
1384  va_list ap;
1385  va_start(ap, fmt);
1386  char message[MAXINDIMESSAGE];
1387  printf(" message='");
1388  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1389  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1390  va_end(ap);
1391  }
1392  printf("/>\n");
1393  fflush(stdout);
1394 
1395  pthread_mutex_unlock(&stdout_mutex);
1396 }
1397 
1398 FILE *IUGetConfigFP(const char *filename, const char *dev, const char *mode, char errmsg[])
1399 {
1400  char configFileName[MAXRBUF];
1401  char configDir[MAXRBUF];
1402  struct stat st;
1403  FILE *fp = NULL;
1404 
1405  snprintf(configDir, MAXRBUF, "%s/.indi/", getenv("HOME"));
1406 
1407  if (filename)
1408  strncpy(configFileName, filename, MAXRBUF);
1409  else
1410  {
1411  if (getenv("INDICONFIG"))
1412  strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1413  else
1414  snprintf(configFileName, MAXRBUF, "%s%s_config.xml", configDir, dev);
1415  }
1416 
1417  if (stat(configDir, &st) != 0)
1418  {
1419  if (mkdir(configDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
1420  {
1421  snprintf(errmsg, MAXRBUF, "Unable to create config directory. Error %s: %s\n", configDir, strerror(errno));
1422  return NULL;
1423  }
1424  }
1425 
1426  fp = fopen(configFileName, mode);
1427  if (fp == NULL)
1428  {
1429  snprintf(errmsg, MAXRBUF, "Unable to open config file. Error loading file %s: %s\n", configFileName,
1430  strerror(errno));
1431  return NULL;
1432  }
1433 
1434  return fp;
1435 }
1436 
1437 void IUSaveConfigTag(FILE *fp, int ctag, const char *dev, int silent)
1438 {
1439  if (!fp)
1440  return;
1441 
1442  /* Opening tag */
1443  if (ctag == 0)
1444  {
1445  fprintf(fp, "<INDIDriver>\n");
1446  if (silent != 1)
1447  IDMessage(dev, "[INFO] Saving device configuration...");
1448  }
1449  /* Closing tag */
1450  else
1451  {
1452  fprintf(fp, "</INDIDriver>\n");
1453  if (silent != 1)
1454  IDMessage(dev, "[INFO] Device configuration saved.");
1455  }
1456 }
1457 
1458 void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
1459 {
1460  int i;
1461 
1462  locale_char_t *orig = indi_locale_C_numeric_push();
1463  fprintf(fp, "<newNumberVector device='%s' name='%s'>\n", nvp->device, nvp->name);
1464 
1465  for (i = 0; i < nvp->nnp; i++)
1466  {
1467  INumber *np = &nvp->np[i];
1468  fprintf(fp, " <oneNumber name='%s'>\n", np->name);
1469  fprintf(fp, " %.20g\n", np->value);
1470  fprintf(fp, " </oneNumber>\n");
1471  }
1472 
1473  fprintf(fp, "</newNumberVector>\n");
1474  indi_locale_C_numeric_pop(orig);
1475 }
1476 
1477 void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
1478 {
1479  int i;
1480 
1481  fprintf(fp, "<newTextVector device='%s' name='%s'>\n", tvp->device, tvp->name);
1482 
1483  for (i = 0; i < tvp->ntp; i++)
1484  {
1485  IText *tp = &tvp->tp[i];
1486  fprintf(fp, " <oneText name='%s'>\n", tp->name);
1487  fprintf(fp, " %s\n", tp->text ? tp->text : "");
1488  fprintf(fp, " </oneText>\n");
1489  }
1490 
1491  fprintf(fp, "</newTextVector>\n");
1492 }
1493 
1494 void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
1495 {
1496  int i;
1497 
1498  fprintf(fp, "<newSwitchVector device='%s' name='%s'>\n", svp->device, svp->name);
1499 
1500  for (i = 0; i < svp->nsp; i++)
1501  {
1502  ISwitch *sp = &svp->sp[i];
1503  fprintf(fp, " <oneSwitch name='%s'>\n", sp->name);
1504  fprintf(fp, " %s\n", sstateStr(sp->s));
1505  fprintf(fp, " </oneSwitch>\n");
1506  }
1507 
1508  fprintf(fp, "</newSwitchVector>\n");
1509 }
1510 
1511 void IUSaveConfigBLOB(FILE *fp, const IBLOBVectorProperty *bvp)
1512 {
1513  int i;
1514 
1515  fprintf(fp, "<newBLOBVector device='%s' name='%s'>\n", bvp->device, bvp->name);
1516 
1517  for (i = 0; i < bvp->nbp; i++)
1518  {
1519  IBLOB *bp = &bvp->bp[i];
1520  unsigned char *encblob = NULL;
1521  int l = 0;
1522 
1523  fprintf(fp, " <oneBLOB\n");
1524  fprintf(fp, " name='%s'\n", bp->name);
1525  fprintf(fp, " size='%d'\n", bp->size);
1526  fprintf(fp, " format='%s'>\n", bp->format);
1527 
1528  encblob = malloc(4 * bp->bloblen / 3 + 4);
1529  l = to64frombits(encblob, bp->blob, bp->bloblen);
1530  size_t written = 0;
1531 
1532  while ((int)written < l)
1533  {
1534  size_t towrite = ((l - written) > 72) ? 72 : l - written;
1535  size_t wr = fwrite(encblob + written, 1, towrite, fp);
1536 
1537  fputc('\n', fp);
1538  if (wr > 0)
1539  written += wr;
1540  }
1541  free(encblob);
1542 
1543  fprintf(fp, " </oneBLOB>\n");
1544  }
1545 
1546  fprintf(fp, "</newBLOBVector>\n");
1547 }
1548 
1549 /* tell client to create a text vector property */
1550 void IDDefText(const ITextVectorProperty *tvp, const char *fmt, ...)
1551 {
1552  int i;
1553  ROSC *SC;
1554 
1555  pthread_mutex_lock(&stdout_mutex);
1556 
1557  xmlv1();
1558  locale_char_t *orig = indi_locale_C_numeric_push();
1559  printf("<defTextVector\n");
1560  printf(" device='%s'\n", tvp->device);
1561  printf(" name='%s'\n", tvp->name);
1562  printf(" label='%s'\n", tvp->label);
1563  printf(" group='%s'\n", tvp->group);
1564  printf(" state='%s'\n", pstateStr(tvp->s));
1565  printf(" perm='%s'\n", permStr(tvp->p));
1566  printf(" timeout='%g'\n", tvp->timeout);
1567  printf(" timestamp='%s'\n", timestamp());
1568  if (fmt)
1569  {
1570  va_list ap;
1571  va_start(ap, fmt);
1572  char message[MAXINDIMESSAGE];
1573  printf(" message='");
1574  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1575  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1576  va_end(ap);
1577  }
1578  printf(">\n");
1579 
1580  for (i = 0; i < tvp->ntp; i++)
1581  {
1582  IText *tp = &tvp->tp[i];
1583  printf(" <defText\n");
1584  printf(" name='%s'\n", tp->name);
1585  printf(" label='%s'>\n", tp->label);
1586  printf(" %s\n", tp->text ? tp->text : "");
1587  printf(" </defText>\n");
1588  }
1589 
1590  printf("</defTextVector>\n");
1591 
1592  if (isPropDefined(tvp->name, tvp->device) < 0)
1593  {
1594  /* Add this property to insure proper sanity check */
1595  propCache =
1596  propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1597  SC = &propCache[nPropCache++];
1598 
1599  strcpy(SC->propName, tvp->name);
1600  strcpy(SC->devName, tvp->device);
1601  SC->perm = tvp->p;
1602  SC->ptr = tvp;
1603  SC->type = INDI_TEXT;
1604  }
1605 
1606  indi_locale_C_numeric_pop(orig);
1607  fflush(stdout);
1608 
1609  pthread_mutex_unlock(&stdout_mutex);
1610 }
1611 
1612 /* tell client to create a new numeric vector property */
1613 void IDDefNumber(const INumberVectorProperty *n, const char *fmt, ...)
1614 {
1615  int i;
1616  ROSC *SC;
1617 
1618  pthread_mutex_lock(&stdout_mutex);
1619 
1620  xmlv1();
1621  locale_char_t *orig = indi_locale_C_numeric_push();
1622  printf("<defNumberVector\n");
1623  printf(" device='%s'\n", n->device);
1624  printf(" name='%s'\n", n->name);
1625  printf(" label='%s'\n", n->label);
1626  printf(" group='%s'\n", n->group);
1627  printf(" state='%s'\n", pstateStr(n->s));
1628  printf(" perm='%s'\n", permStr(n->p));
1629  printf(" timeout='%g'\n", n->timeout);
1630  printf(" timestamp='%s'\n", timestamp());
1631 
1632  if (fmt)
1633  {
1634  va_list ap;
1635  va_start(ap, fmt);
1636  char message[MAXINDIMESSAGE];
1637  printf(" message='");
1638  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1639  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1640  va_end(ap);
1641  }
1642  printf(">\n");
1643 
1644  for (i = 0; i < n->nnp; i++)
1645  {
1646  INumber *np = &n->np[i];
1647 
1648  printf(" <defNumber\n");
1649  printf(" name='%s'\n", np->name);
1650  printf(" label='%s'\n", np->label);
1651  printf(" format='%s'\n", np->format);
1652  printf(" min='%.20g'\n", np->min);
1653  printf(" max='%.20g'\n", np->max);
1654  printf(" step='%.20g'>\n", np->step);
1655  printf(" %.20g\n", np->value);
1656 
1657  printf(" </defNumber>\n");
1658  }
1659 
1660  printf("</defNumberVector>\n");
1661 
1662  if (isPropDefined(n->name, n->device) < 0)
1663  {
1664  /* Add this property to insure proper sanity check */
1665  propCache =
1666  propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1667  SC = &propCache[nPropCache++];
1668 
1669  strcpy(SC->propName, n->name);
1670  strcpy(SC->devName, n->device);
1671  SC->perm = n->p;
1672  SC->ptr = n;
1673  SC->type = INDI_NUMBER;
1674  }
1675 
1676  indi_locale_C_numeric_pop(orig);
1677  fflush(stdout);
1678 
1679  pthread_mutex_unlock(&stdout_mutex);
1680 }
1681 
1682 /* tell client to create a new switch vector property */
1683 void IDDefSwitch(const ISwitchVectorProperty *s, const char *fmt, ...)
1684 
1685 {
1686  int i;
1687  ROSC *SC;
1688 
1689  pthread_mutex_lock(&stdout_mutex);
1690 
1691  xmlv1();
1692  locale_char_t *orig = indi_locale_C_numeric_push();
1693  printf("<defSwitchVector\n");
1694  printf(" device='%s'\n", s->device);
1695  printf(" name='%s'\n", s->name);
1696  printf(" label='%s'\n", s->label);
1697  printf(" group='%s'\n", s->group);
1698  printf(" state='%s'\n", pstateStr(s->s));
1699  printf(" perm='%s'\n", permStr(s->p));
1700  printf(" rule='%s'\n", ruleStr(s->r));
1701  printf(" timeout='%g'\n", s->timeout);
1702  printf(" timestamp='%s'\n", timestamp());
1703  if (fmt)
1704  {
1705  va_list ap;
1706  va_start(ap, fmt);
1707  char message[MAXINDIMESSAGE];
1708  printf(" message='");
1709  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1710  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1711  va_end(ap);
1712  }
1713  printf(">\n");
1714 
1715  for (i = 0; i < s->nsp; i++)
1716  {
1717  ISwitch *sp = &s->sp[i];
1718  printf(" <defSwitch\n");
1719  printf(" name='%s'\n", sp->name);
1720  printf(" label='%s'>\n", sp->label);
1721  printf(" %s\n", sstateStr(sp->s));
1722  printf(" </defSwitch>\n");
1723  }
1724 
1725  printf("</defSwitchVector>\n");
1726 
1727  if (isPropDefined(s->name, s->device) < 0)
1728  {
1729  /* Add this property to insure proper sanity check */
1730  propCache =
1731  propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1732  SC = &propCache[nPropCache++];
1733 
1734  strcpy(SC->propName, s->name);
1735  strcpy(SC->devName, s->device);
1736  SC->perm = s->p;
1737  SC->ptr = s;
1738  SC->type = INDI_SWITCH;
1739  }
1740 
1741  indi_locale_C_numeric_pop(orig);
1742  fflush(stdout);
1743 
1744  pthread_mutex_unlock(&stdout_mutex);
1745 }
1746 
1747 /* tell client to create a new lights vector property */
1748 void IDDefLight(const ILightVectorProperty *lvp, const char *fmt, ...)
1749 {
1750  int i;
1751 
1752  pthread_mutex_lock(&stdout_mutex);
1753 
1754  xmlv1();
1755  printf("<defLightVector\n");
1756  printf(" device='%s'\n", lvp->device);
1757  printf(" name='%s'\n", lvp->name);
1758  printf(" label='%s'\n", lvp->label);
1759  printf(" group='%s'\n", lvp->group);
1760  printf(" state='%s'\n", pstateStr(lvp->s));
1761  printf(" timestamp='%s'\n", timestamp());
1762  if (fmt)
1763  {
1764  va_list ap;
1765  va_start(ap, fmt);
1766  char message[MAXINDIMESSAGE];
1767  printf(" message='");
1768  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1769  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1770  va_end(ap);
1771  }
1772  printf(">\n");
1773 
1774  for (i = 0; i < lvp->nlp; i++)
1775  {
1776  ILight *lp = &lvp->lp[i];
1777  printf(" <defLight\n");
1778  printf(" name='%s'\n", lp->name);
1779  printf(" label='%s'>\n", lp->label);
1780  printf(" %s\n", pstateStr(lp->s));
1781  printf(" </defLight>\n");
1782  }
1783 
1784  printf("</defLightVector>\n");
1785  fflush(stdout);
1786 
1787  pthread_mutex_unlock(&stdout_mutex);
1788 }
1789 
1790 /* tell client to create a new BLOB vector property */
1791 void IDDefBLOB(const IBLOBVectorProperty *b, const char *fmt, ...)
1792 {
1793  int i;
1794  ROSC *SC;
1795 
1796  pthread_mutex_lock(&stdout_mutex);
1797 
1798  xmlv1();
1799  locale_char_t *orig = indi_locale_C_numeric_push();
1800  printf("<defBLOBVector\n");
1801  printf(" device='%s'\n", b->device);
1802  printf(" name='%s'\n", b->name);
1803  printf(" label='%s'\n", b->label);
1804  printf(" group='%s'\n", b->group);
1805  printf(" state='%s'\n", pstateStr(b->s));
1806  printf(" perm='%s'\n", permStr(b->p));
1807  printf(" timeout='%g'\n", b->timeout);
1808  printf(" timestamp='%s'\n", timestamp());
1809  if (fmt)
1810  {
1811  va_list ap;
1812  va_start(ap, fmt);
1813  char message[MAXINDIMESSAGE];
1814  printf(" message='");
1815  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1816  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1817  va_end(ap);
1818  }
1819  printf(">\n");
1820 
1821  for (i = 0; i < b->nbp; i++)
1822  {
1823  IBLOB *bp = &b->bp[i];
1824  printf(" <defBLOB\n");
1825  printf(" name='%s'\n", bp->name);
1826  printf(" label='%s'\n", bp->label);
1827  printf(" />\n");
1828  }
1829 
1830  printf("</defBLOBVector>\n");
1831 
1832  if (isPropDefined(b->name, b->device) < 0)
1833  {
1834  /* Add this property to insure proper sanity check */
1835  propCache =
1836  propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1837  SC = &propCache[nPropCache++];
1838 
1839  strcpy(SC->propName, b->name);
1840  strcpy(SC->devName, b->device);
1841  SC->perm = b->p;
1842  SC->ptr = b;
1843  SC->type = INDI_BLOB;
1844  }
1845 
1846  indi_locale_C_numeric_pop(orig);
1847  fflush(stdout);
1848 
1849  pthread_mutex_unlock(&stdout_mutex);
1850 }
1851 
1852 /* tell client to update an existing text vector property */
1853 void IDSetText(const ITextVectorProperty *tvp, const char *fmt, ...)
1854 {
1855  int i;
1856 
1857  pthread_mutex_lock(&stdout_mutex);
1858 
1859  xmlv1();
1860  locale_char_t *orig = indi_locale_C_numeric_push();
1861  printf("<setTextVector\n");
1862  printf(" device='%s'\n", tvp->device);
1863  printf(" name='%s'\n", tvp->name);
1864  printf(" state='%s'\n", pstateStr(tvp->s));
1865  printf(" timeout='%g'\n", tvp->timeout);
1866  printf(" timestamp='%s'\n", timestamp());
1867  if (fmt)
1868  {
1869  va_list ap;
1870  va_start(ap, fmt);
1871  char message[MAXINDIMESSAGE];
1872  printf(" message='");
1873  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1874  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1875  va_end(ap);
1876  }
1877  printf(">\n");
1878 
1879  for (i = 0; i < tvp->ntp; i++)
1880  {
1881  IText *tp = &tvp->tp[i];
1882  printf(" <oneText name='%s'>\n", tp->name);
1883  printf(" %s\n", tp->text ? tp->text : "");
1884  printf(" </oneText>\n");
1885  }
1886 
1887  printf("</setTextVector>\n");
1888  indi_locale_C_numeric_pop(orig);
1889  fflush(stdout);
1890 
1891  pthread_mutex_unlock(&stdout_mutex);
1892 }
1893 
1894 /* tell client to update an existing numeric vector property */
1895 void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt, ...)
1896 {
1897  int i;
1898 
1899  pthread_mutex_lock(&stdout_mutex);
1900 
1901  xmlv1();
1902  locale_char_t *orig = indi_locale_C_numeric_push();
1903  printf("<setNumberVector\n");
1904  printf(" device='%s'\n", nvp->device);
1905  printf(" name='%s'\n", nvp->name);
1906  printf(" state='%s'\n", pstateStr(nvp->s));
1907  printf(" timeout='%g'\n", nvp->timeout);
1908  printf(" timestamp='%s'\n", timestamp());
1909  if (fmt)
1910  {
1911  va_list ap;
1912  va_start(ap, fmt);
1913  char message[MAXINDIMESSAGE];
1914  printf(" message='");
1915  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1916  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1917  va_end(ap);
1918  }
1919  printf(">\n");
1920 
1921  for (i = 0; i < nvp->nnp; i++)
1922  {
1923  INumber *np = &nvp->np[i];
1924  printf(" <oneNumber name='%s'>\n", np->name);
1925  printf(" %.20g\n", np->value);
1926  printf(" </oneNumber>\n");
1927  }
1928 
1929  printf("</setNumberVector>\n");
1930  indi_locale_C_numeric_pop(orig);
1931  fflush(stdout);
1932 
1933  pthread_mutex_unlock(&stdout_mutex);
1934 }
1935 
1936 /* tell client to update an existing switch vector property */
1937 void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt, ...)
1938 {
1939  int i;
1940 
1941  pthread_mutex_lock(&stdout_mutex);
1942 
1943  xmlv1();
1944  locale_char_t *orig = indi_locale_C_numeric_push();
1945  printf("<setSwitchVector\n");
1946  printf(" device='%s'\n", svp->device);
1947  printf(" name='%s'\n", svp->name);
1948  printf(" state='%s'\n", pstateStr(svp->s));
1949  printf(" timeout='%g'\n", svp->timeout);
1950  printf(" timestamp='%s'\n", timestamp());
1951  if (fmt)
1952  {
1953  va_list ap;
1954  va_start(ap, fmt);
1955  char message[MAXINDIMESSAGE];
1956  printf(" message='");
1957  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1958  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1959  va_end(ap);
1960  }
1961  printf(">\n");
1962 
1963  for (i = 0; i < svp->nsp; i++)
1964  {
1965  ISwitch *sp = &svp->sp[i];
1966  printf(" <oneSwitch name='%s'>\n", sp->name);
1967  printf(" %s\n", sstateStr(sp->s));
1968  printf(" </oneSwitch>\n");
1969  }
1970 
1971  printf("</setSwitchVector>\n");
1972  indi_locale_C_numeric_pop(orig);
1973  fflush(stdout);
1974 
1975  pthread_mutex_unlock(&stdout_mutex);
1976 }
1977 
1978 /* tell client to update an existing lights vector property */
1979 void IDSetLight(const ILightVectorProperty *lvp, const char *fmt, ...)
1980 {
1981  int i;
1982 
1983  pthread_mutex_lock(&stdout_mutex);
1984 
1985  xmlv1();
1986  printf("<setLightVector\n");
1987  printf(" device='%s'\n", lvp->device);
1988  printf(" name='%s'\n", lvp->name);
1989  printf(" state='%s'\n", pstateStr(lvp->s));
1990  printf(" timestamp='%s'\n", timestamp());
1991  if (fmt)
1992  {
1993  va_list ap;
1994  va_start(ap, fmt);
1995  char message[MAXINDIMESSAGE];
1996  printf(" message='");
1997  vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1998  printf("%s'\n", escapeXML(message, MAXINDIMESSAGE));
1999  va_end(ap);
2000  }
2001  printf(">\n");
2002 
2003  for (i = 0; i < lvp->nlp; i++)
2004  {
2005  ILight *lp = &lvp->lp[i];
2006  printf(" <oneLight name='%s'>\n", lp->name);
2007  printf(" %s\n", pstateStr(lp->s));
2008  printf(" </oneLight>\n");
2009  }
2010 
2011  printf("</setLightVector>\n");
2012  fflush(stdout);
2013 
2014  pthread_mutex_unlock(&stdout_mutex);
2015 }
2016 
2017 /* tell client to update an existing BLOB vector property */
2018 void IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt, ...)
2019 {
2020  int i;
2021 
2022  pthread_mutex_lock(&stdout_mutex);
2023 
2024  xmlv1();
2025  locale_char_t *orig = indi_locale_C_numeric_push();
2026  printf("<setBLOBVector\n");
2027  printf(" device='%s'\n", bvp->device);
2028  printf(" name='%s'\n", bvp->name);
2029  printf(" state='%s'\n", pstateStr(bvp->s));
2030  printf(" timeout='%g'\n", bvp->timeout);
2031  printf(" timestamp='%s'\n", timestamp());
2032  if (fmt)
2033  {
2034  va_list ap;
2035  va_start(ap, fmt);
2036  printf(" message='");
2037  vprintf(fmt, ap);
2038  printf("'\n");
2039  va_end(ap);
2040  }
2041  printf(">\n");
2042 
2043  for (i = 0; i < bvp->nbp; i++)
2044  {
2045  IBLOB *bp = &bvp->bp[i];
2046  unsigned char *encblob;
2047  int l;
2048 
2049  printf(" <oneBLOB\n");
2050  printf(" name='%s'\n", bp->name);
2051  printf(" size='%d'\n", bp->size);
2052 
2053  // If size is zero, we are only sending a state-change
2054  if (bp->size == 0)
2055  {
2056  printf(" enclen='0'\n");
2057  printf(" format='%s'>\n", bp->format);
2058  }
2059  else
2060  {
2061  encblob = malloc(4 * bp->bloblen / 3 + 4);
2062  l = to64frombits(encblob, bp->blob, bp->bloblen);
2063  printf(" enclen='%d'\n", l);
2064  printf(" format='%s'>\n", bp->format);
2065  size_t written = 0;
2066 
2067  while ((int)written < l)
2068  {
2069  size_t towrite = ((l - written) > 72) ? 72 : l - written;
2070  size_t wr = fwrite(encblob + written, 1, towrite, stdout);
2071 
2072  if (wr > 0)
2073  written += wr;
2074  if ((written % 72) == 0)
2075  fputc('\n', stdout);
2076  }
2077 
2078  if ((written % 72) != 0)
2079  fputc('\n', stdout);
2080 
2081  free(encblob);
2082  }
2083 
2084  printf(" </oneBLOB>\n");
2085  }
2086 
2087  printf("</setBLOBVector>\n");
2088  indi_locale_C_numeric_pop(orig);
2089  fflush(stdout);
2090 
2091  pthread_mutex_unlock(&stdout_mutex);
2092 }
2093 
2094 /* tell client to update min/max elements of an existing number vector property */
2096 {
2097  int i;
2098 
2099  pthread_mutex_lock(&stdout_mutex);
2100  xmlv1();
2101  locale_char_t *orig = indi_locale_C_numeric_push();
2102  printf("<setNumberVector\n");
2103  printf(" device='%s'\n", nvp->device);
2104  printf(" name='%s'\n", nvp->name);
2105  printf(" state='%s'\n", pstateStr(nvp->s));
2106  printf(" timeout='%g'\n", nvp->timeout);
2107  printf(" timestamp='%s'\n", timestamp());
2108  printf(">\n");
2109 
2110  for (i = 0; i < nvp->nnp; i++)
2111  {
2112  INumber *np = &nvp->np[i];
2113  printf(" <oneNumber name='%s'\n", np->name);
2114  printf(" min='%g'\n", np->min);
2115  printf(" max='%g'\n", np->max);
2116  printf(" step='%g'\n", np->step);
2117  printf(">\n");
2118  printf(" %g\n", np->value);
2119  printf(" </oneNumber>\n");
2120  }
2121 
2122  printf("</setNumberVector>\n");
2123  indi_locale_C_numeric_pop(orig);
2124  fflush(stdout);
2125  pthread_mutex_unlock(&stdout_mutex);
2126 }
2127 
2128 int IUFindIndex(const char *needle, char **hay, unsigned int n)
2129 {
2130  int i = 0;
2131 
2132  for (i = 0; i < (int)n; i++)
2133  {
2134  if (!strcmp(hay[i], needle))
2135  return i;
2136  }
2137  return -1;
2138 }
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indidriver.c:1477
void * aux0
Definition: indiapi.h:217
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1420
void * aux0
Definition: indiapi.h:440
int verbose
struct _ISwitchVectorProperty * svp
Definition: indiapi.h:338
LilXML * clixml
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1411
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:215
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double.
Definition: indicom.c:181
const char * pstateStr(IPState s)
Definition: indicom.c:1157
void * aux1
Definition: indiapi.h:277
char label[MAXINDILABEL]
Definition: indiapi.h:458
Light vector property descriptor.
Definition: indiapi.h:397
void IUSaveConfigTag(FILE *fp, int ctag, const char *dev, int silent)
Add opening or closing tag to a configuration file.
Definition: indidriver.c:1437
char label[MAXINDILABEL]
Definition: indiapi.h:384
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:354
void * aux
Definition: indiapi.h:340
const char * timestamp()
Create an ISO 8601 formatted time stamp. The format is YYYY-MM-DDTHH:MM:SS.
Definition: indicom.c:300
char device[MAXINDIDEVICE]
Definition: indiapi.h:229
#define MAXINDINAME
Definition: indiapi.h:172
char * text
Definition: indiapi.h:213
Public interface to INDI&#39;s eventloop mechanism.
int IEDeferLoop0(int maxms, int *flagp)
Definition: indidriver.c:221
char format[MAXINDIFORMAT]
Definition: indiapi.h:263
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:336
char format[MAXINDIBLOBFMT]
Definition: indiapi.h:430
#define MAXINDIDEVICE
Definition: indiapi.h:174
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:1853
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:1937
char propName[MAXINDINAME]
Definition: indidriver.h:41
double step
Definition: indiapi.h:269
IPState
Property state.
Definition: indiapi.h:143
One light descriptor.
Definition: indiapi.h:379
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:454
void IDDefNumber(const INumberVectorProperty *n, const char *fmt,...)
Tell client to create a number number property.
Definition: indidriver.c:1613
void IDMessage(const char *dev, const char *fmt,...)
Function Drivers call to send log messages to Clients.
Definition: indidriver.c:1373
int from64tobits_fast(char *out, const char *in, int inlen)
Definition: base64.c:92
#define MAXINDIGROUP
Definition: indiapi.h:175
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:1398
ROSC * propCache
One number descriptor.
Definition: indiapi.h:256
char timestamp[MAXINDITSTAMP]
Definition: indiapi.h:414
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:308
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:423
int isPropDefined(const char *property_name, const char *device_name)
Definition: indidriver.c:57
char device[MAXINDIDEVICE]
Definition: indiapi.h:400
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:177
void IDDefBLOB(const IBLOBVectorProperty *b, const char *fmt,...)
Tell client to create a BLOB vector property.
Definition: indidriver.c:1791
Definition: indiapi.h:165
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:436
IPState s
Definition: indiapi.h:386
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:2095
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:113
char group[MAXINDIGROUP]
Definition: indiapi.h:356
struct _IBLOBVectorProperty * bvp
Definition: indiapi.h:438
void delXMLEle(XMLEle *ep)
Delete an XML element.
Definition: lilxml.c:164
#define INDIV
Definition: indiapi.h:119
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:1895
void IUSaveConfigBLOB(FILE *fp, const IBLOBVectorProperty *bvp)
Add a BLOB vector property value to the configuration file.
Definition: indidriver.c:1511
void( IE_CBF)(int readfiledes, void *userpointer)
Signature of a callback.
Definition: indidevapi.h:324
Switch vector property descriptor.
Definition: indiapi.h:347
char label[MAXINDILABEL]
Definition: indiapi.h:306
Namespace to encapsulate INDI client, drivers, and mediator classes. Developers can subclass the base...
#define MAXINDIMESSAGE
Definition: indiapi.h:179
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:304
int bloblen
Definition: indiapi.h:434
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:334
IPerm perm
Definition: indidriver.h:43
const char * permStr(IPerm p)
Definition: indicom.c:1266
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:329
char timestamp[MAXINDITSTAMP]
Definition: indiapi.h:472
IText * IUFindText(const ITextVectorProperty *tvp, const char *name)
Find an IText member in a vector text property.
Definition: indicom.c:1315
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:388
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:273
char name[MAXINDINAME]
Definition: indiapi.h:402
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidriver.c:1458
char label[MAXINDILABEL]
Definition: indiapi.h:428
char label[MAXINDILABEL]
Definition: indiapi.h:233
void * aux2
Definition: indiapi.h:444
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:451
char label[MAXINDILABEL]
Definition: indiapi.h:404
char name[MAXINDINAME]
Definition: indiapi.h:259
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:1363
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:1291
const char * ruleStr(ISRule r)
Definition: indicom.c:1249
void * blob
Definition: indiapi.h:432
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:1494
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:1196
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indicom.c:1327
char name[MAXINDINAME]
Definition: indiapi.h:426
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.
int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
IUGetConfigNumber Opens configuration file and reads number property.
Definition: indidriver.c:1315
Number vector property descriptor.
Definition: indiapi.h:299
int crackIPState(const char *str, IPState *ip)
Extract property state (Idle, OK, Busy, Alert) from the supplied string.
Definition: indicom.c:1178
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:206
int fd
Definition: indiserver.c:114
void IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt,...)
Tell client to update an existing BLOB vector property.
Definition: indidriver.c:2018
char name[MAXINDINAME]
Definition: indiapi.h:352
char group[MAXINDIGROUP]
Definition: indiapi.h:406
void IDDefLight(const ILightVectorProperty *lvp, const char *fmt,...)
Tell client to create a light vector property.
Definition: indidriver.c:1748
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:1979
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:176
double min
Definition: indiapi.h:265
int IUFindIndex(const char *needle, char **hay, unsigned int n)
Returns the index of the string in a string array.
Definition: indidriver.c:2128
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:211
void( TCF)(void *)
Signature of a timer function.
Definition: eventloop.h:47
void * aux1
Definition: indiapi.h:442
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:231
char name[MAXINDINAME]
Definition: indiapi.h:382
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:219
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:267
void * aux0
Definition: indiapi.h:275
IPerm
Permission hint, with respect to client.
Definition: indiapi.h:164
ISState
Switch state.
Definition: indiapi.h:134
char name[MAXINDINAME]
Definition: indiapi.h:209
char label[MAXINDILABEL]
Definition: indiapi.h:261
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:1339
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:1377
int type
Definition: indidriver.h:45
void xmlv1()
Definition: indicom.c:1283
void rmCallback(int cid)
Definition: eventloop.c:186
const char * sstateStr(ISState s)
Definition: indicom.c:1234
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:247
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:173
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:460
double min(void)
Text vector property descriptor.
Definition: indiapi.h:226
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:456
int errno
char device[MAXINDIDEVICE]
Definition: indiapi.h:350
void * aux
Definition: indiapi.h:390
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:154
char name[MAXINDINAME]
Definition: indiapi.h:332
void IDDefSwitch(const ISwitchVectorProperty *s, const char *fmt,...)
Tell client to create a switch vector property.
Definition: indidriver.c:1683
char timestamp[MAXINDITSTAMP]
Definition: indiapi.h:370
char timestamp[MAXINDITSTAMP]
Definition: indiapi.h:320
char device[MAXINDIDEVICE]
Definition: indiapi.h:302
char group[MAXINDIGROUP]
Definition: indiapi.h:235
Implementations for common driver routines.
void IDDefText(const ITextVectorProperty *tvp, const char *fmt,...)
Tell client to create a text vector property.
Definition: indidriver.c:1550
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:271