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
45 /*! INDI property type */
46 enum
47 {
48 INDI_NUMBER,
49 INDI_SWITCH,
50 INDI_TEXT,
51 INDI_LIGHT,
52 INDI_BLOB,
53 INDI_UNKNOWN
54 };
55
56 /* Return index of property property if already cached, -1 otherwise */
isPropDefined(const char * property_name,const char * device_name)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! */
escapeXML(const char * s,unsigned int MAX_BUF_SIZE)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, "&", 5);
82 out += 5;
83 break;
84 case '\'':
85 strncpy(out, "'", 6);
86 out += 6;
87 break;
88 case '"':
89 strncpy(out, """, 6);
90 out += 6;
91 break;
92 case '<':
93 strncpy(out, "<", 4);
94 out += 4;
95 break;
96 case '>':
97 strncpy(out, ">", 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 */
IDDelete(const char * dev,const char * name,const char * fmt,...)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", entityXML(message));
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 */
IDSnoopDevice(const char * snooped_device,const char * snooped_property)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 */
IDSnoopBLOBs(const char * snooped_device,const char * snooped_property,BLOBHandling bh)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
IEAddCallback(int readfiledes,IE_CBF * fp,void * p)186 int IEAddCallback(int readfiledes, IE_CBF *fp, void *p)
187 {
188 return (addCallback(readfiledes, (CBF *)fp, p));
189 }
190
IERmCallback(int callbackid)191 void IERmCallback(int callbackid)
192 {
193 rmCallback(callbackid);
194 }
195
IEAddTimer(int millisecs,IE_TCF * fp,void * p)196 int IEAddTimer(int millisecs, IE_TCF *fp, void *p)
197 {
198 return (addTimer(millisecs, (TCF *)fp, p));
199 }
200
IERmTimer(int timerid)201 void IERmTimer(int timerid)
202 {
203 rmTimer(timerid);
204 }
205
IEAddWorkProc(IE_WPF * fp,void * p)206 int IEAddWorkProc(IE_WPF *fp, void *p)
207 {
208 return (addWorkProc((WPF *)fp, p));
209 }
210
IERmWorkProc(int workprocid)211 void IERmWorkProc(int workprocid)
212 {
213 rmWorkProc(workprocid);
214 }
215
IEDeferLoop(int maxms,int * flagp)216 int IEDeferLoop(int maxms, int *flagp)
217 {
218 return (deferLoop(maxms, flagp));
219 }
220
IEDeferLoop0(int maxms,int * flagp)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. */
IUUpdateSwitch(ISwitchVectorProperty * svp,ISState * states,char * names[],int n)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 */
IUUpdateNumber(INumberVectorProperty * nvp,double values[],char * names[],int n)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 */
IUUpdateText(ITextVectorProperty * tvp,char * texts[],char * names[],int n)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 */
IUUpdateBLOB(IBLOBVectorProperty * bvp,int sizes[],int blobsizes[],char * blobs[],char * formats[],char * names[],int n)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
IUSaveBLOB(IBLOB * bp,int size,int blobsize,char * blob,char * format)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
IUFillSwitch(ISwitch * sp,const char * name,const char * label,ISState s)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
IUFillLight(ILight * lp,const char * name,const char * label,IPState s)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
IUFillNumber(INumber * np,const char * name,const char * label,const char * format,double min,double max,double step,double value)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
IUFillText(IText * tp,const char * name,const char * label,const char * initialText)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
457 if (tp->text && tp->text[0])
458 free(tp->text);
459 tp->text = NULL;
460
461 tp->tvp = NULL;
462 tp->aux0 = NULL;
463 tp->aux1 = NULL;
464
465 if (initialText && strlen(initialText) > 0)
466 IUSaveText(tp, initialText);
467
468 free(escapedName);
469 free(escapedLabel);
470 }
471
IUFillBLOB(IBLOB * bp,const char * name,const char * label,const char * format)472 void IUFillBLOB(IBLOB *bp, const char *name, const char *label, const char *format)
473 {
474 char *escapedName = escapeXML(name, MAXINDINAME);
475 char *escapedLabel = escapeXML(label, MAXINDILABEL);
476
477 memset(bp, 0, sizeof(IBLOB));
478 strncpy(bp->name, escapedName, MAXINDINAME);
479
480 if (label[0])
481 strncpy(bp->label, escapedLabel, MAXINDILABEL);
482 else
483 strncpy(bp->label, escapedName, MAXINDILABEL);
484
485 strncpy(bp->format, format, MAXINDIBLOBFMT);
486 bp->blob = 0;
487 bp->bloblen = 0;
488 bp->size = 0;
489 bp->bvp = 0;
490 bp->aux0 = 0;
491 bp->aux1 = 0;
492 bp->aux2 = 0;
493
494 free(escapedName);
495 free(escapedLabel);
496 }
497
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)498 void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name,
499 const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
500 {
501 char *escapedName = escapeXML(name, MAXINDINAME);
502 char *escapedLabel = escapeXML(label, MAXINDILABEL);
503
504 strncpy(svp->device, dev, MAXINDIDEVICE);
505 strncpy(svp->name, escapedName, MAXINDINAME);
506
507 if (label[0])
508 strncpy(svp->label, escapedLabel, MAXINDILABEL);
509 else
510 strncpy(svp->label, escapedName, MAXINDILABEL);
511 strncpy(svp->group, group, MAXINDIGROUP);
512 strcpy(svp->timestamp, "");
513
514 svp->p = p;
515 svp->r = r;
516 svp->timeout = timeout;
517 svp->s = s;
518 svp->sp = sp;
519 svp->nsp = nsp;
520
521 free(escapedName);
522 free(escapedLabel);
523 }
524
IUFillLightVector(ILightVectorProperty * lvp,ILight * lp,int nlp,const char * dev,const char * name,const char * label,const char * group,IPState s)525 void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char *dev, const char *name,
526 const char *label, const char *group, IPState s)
527 {
528 char *escapedName = escapeXML(name, MAXINDINAME);
529 char *escapedLabel = escapeXML(label, MAXINDILABEL);
530
531 strncpy(lvp->device, dev, MAXINDIDEVICE);
532 strncpy(lvp->name, escapedName, MAXINDINAME);
533
534 if (label[0])
535 strncpy(lvp->label, escapedLabel, MAXINDILABEL);
536 else
537 strncpy(lvp->label, escapedName, MAXINDILABEL);
538 strncpy(lvp->group, group, MAXINDIGROUP);
539 strcpy(lvp->timestamp, "");
540
541 lvp->s = s;
542 lvp->lp = lp;
543 lvp->nlp = nlp;
544
545 free(escapedName);
546 free(escapedLabel);
547 }
548
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)549 void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name,
550 const char *label, const char *group, IPerm p, double timeout, IPState s)
551 {
552 char *escapedName = escapeXML(name, MAXINDINAME);
553 char *escapedLabel = escapeXML(label, MAXINDILABEL);
554
555 strncpy(nvp->device, dev, MAXINDIDEVICE);
556 strncpy(nvp->name, escapedName, MAXINDINAME);
557
558 if (label[0])
559 strncpy(nvp->label, escapedLabel, MAXINDILABEL);
560 else
561 strncpy(nvp->label, escapedName, MAXINDILABEL);
562 strncpy(nvp->group, group, MAXINDIGROUP);
563 strcpy(nvp->timestamp, "");
564
565 nvp->p = p;
566 nvp->timeout = timeout;
567 nvp->s = s;
568 nvp->np = np;
569 nvp->nnp = nnp;
570
571 free(escapedName);
572 free(escapedLabel);
573 }
574
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)575 void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name,
576 const char *label, const char *group, IPerm p, double timeout, IPState s)
577 {
578 char *escapedName = escapeXML(name, MAXINDINAME);
579 char *escapedLabel = escapeXML(label, MAXINDILABEL);
580
581 strncpy(tvp->device, dev, MAXINDIDEVICE);
582 strncpy(tvp->name, escapedName, MAXINDINAME);
583
584 if (label[0])
585 strncpy(tvp->label, escapedLabel, MAXINDILABEL);
586 else
587 strncpy(tvp->label, escapedName, MAXINDILABEL);
588 strncpy(tvp->group, group, MAXINDIGROUP);
589 strcpy(tvp->timestamp, "");
590
591 tvp->p = p;
592 tvp->timeout = timeout;
593 tvp->s = s;
594 tvp->tp = tp;
595 tvp->ntp = ntp;
596
597 free(escapedName);
598 free(escapedLabel);
599 }
600
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)601 void IUFillBLOBVector(IBLOBVectorProperty *bvp, IBLOB *bp, int nbp, const char *dev, const char *name,
602 const char *label, const char *group, IPerm p, double timeout, IPState s)
603 {
604 char *escapedName = escapeXML(name, MAXINDINAME);
605 char *escapedLabel = escapeXML(label, MAXINDILABEL);
606
607 memset(bvp, 0, sizeof(IBLOBVectorProperty));
608 strncpy(bvp->device, dev, MAXINDIDEVICE);
609 strncpy(bvp->name, escapedName, MAXINDINAME);
610
611 if (label[0])
612 strncpy(bvp->label, escapedLabel, MAXINDILABEL);
613 else
614 strncpy(bvp->label, escapedName, MAXINDILABEL);
615
616 strncpy(bvp->group, group, MAXINDIGROUP);
617 strcpy(bvp->timestamp, "");
618
619 bvp->p = p;
620 bvp->timeout = timeout;
621 bvp->s = s;
622 bvp->bp = bp;
623 bvp->nbp = nbp;
624
625 free(escapedName);
626 free(escapedLabel);
627 }
628
629 /*****************************************************************************
630 * convenience functions for use in your implementation of ISSnoopDevice().
631 */
632
633 /* crack the snooped driver setNumberVector or defNumberVector message into
634 * the given INumberVectorProperty.
635 * return 0 if type, device and name match and all members are present, else
636 * return -1
637 */
IUSnoopNumber(XMLEle * root,INumberVectorProperty * nvp)638 int IUSnoopNumber(XMLEle *root, INumberVectorProperty *nvp)
639 {
640 char *dev, *name;
641 XMLEle *ep;
642 int i;
643
644 /* check and crack type, device, name and state */
645 if (strcmp(tagXMLEle(root) + 3, "NumberVector") || crackDN(root, &dev, &name, NULL) < 0)
646 return (-1);
647 if (strcmp(dev, nvp->device) || strcmp(name, nvp->name))
648 return (-1); /* not this property */
649 (void)crackIPState(findXMLAttValu(root, "state"), &nvp->s);
650
651 /* match each INumber with a oneNumber */
652 locale_char_t *orig = indi_locale_C_numeric_push();
653 for (i = 0; i < nvp->nnp; i++)
654 {
655 for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
656 {
657 if (!strcmp(tagXMLEle(ep) + 3, "Number") && !strcmp(nvp->np[i].name, findXMLAttValu(ep, "name")))
658 {
659 if (f_scansexa(pcdataXMLEle(ep), &nvp->np[i].value) < 0)
660 {
661 indi_locale_C_numeric_pop(orig);
662 return (-1); /* bad number format */
663 }
664 break;
665 }
666 }
667 if (!ep)
668 {
669 indi_locale_C_numeric_pop(orig);
670 return (-1); /* element not found */
671 }
672 }
673 indi_locale_C_numeric_pop(orig);
674
675 /* ok */
676 return (0);
677 }
678
679 /* crack the snooped driver setTextVector or defTextVector message into
680 * the given ITextVectorProperty.
681 * return 0 if type, device and name match and all members are present, else
682 * return -1
683 */
IUSnoopText(XMLEle * root,ITextVectorProperty * tvp)684 int IUSnoopText(XMLEle *root, ITextVectorProperty *tvp)
685 {
686 char *dev, *name;
687 XMLEle *ep;
688 int i;
689
690 /* check and crack type, device, name and state */
691 if (strcmp(tagXMLEle(root) + 3, "TextVector") || crackDN(root, &dev, &name, NULL) < 0)
692 return (-1);
693 if (strcmp(dev, tvp->device) || strcmp(name, tvp->name))
694 return (-1); /* not this property */
695 (void)crackIPState(findXMLAttValu(root, "state"), &tvp->s);
696
697 /* match each IText with a oneText */
698 for (i = 0; i < tvp->ntp; i++)
699 {
700 for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
701 {
702 if (!strcmp(tagXMLEle(ep) + 3, "Text") && !strcmp(tvp->tp[i].name, findXMLAttValu(ep, "name")))
703 {
704 IUSaveText(&tvp->tp[i], pcdataXMLEle(ep));
705 break;
706 }
707 }
708 if (!ep)
709 return (-1); /* element not found */
710 }
711
712 /* ok */
713 return (0);
714 }
715
716 /* crack the snooped driver setLightVector or defLightVector message into
717 * the given ILightVectorProperty. it is not necessary that all ILight names
718 * be found.
719 * return 0 if type, device and name match, else return -1.
720 */
IUSnoopLight(XMLEle * root,ILightVectorProperty * lvp)721 int IUSnoopLight(XMLEle *root, ILightVectorProperty *lvp)
722 {
723 char *dev, *name;
724 XMLEle *ep;
725 int i;
726
727 /* check and crack type, device, name and state */
728 if (strcmp(tagXMLEle(root) + 3, "LightVector") || crackDN(root, &dev, &name, NULL) < 0)
729 return (-1);
730 if (strcmp(dev, lvp->device) || strcmp(name, lvp->name))
731 return (-1); /* not this property */
732
733 (void)crackIPState(findXMLAttValu(root, "state"), &lvp->s);
734
735 /* match each oneLight with one ILight */
736 for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
737 {
738 if (!strcmp(tagXMLEle(ep) + 3, "Light"))
739 {
740 const char *name = findXMLAttValu(ep, "name");
741 for (i = 0; i < lvp->nlp; i++)
742 {
743 if (!strcmp(lvp->lp[i].name, name))
744 {
745 if (crackIPState(pcdataXMLEle(ep), &lvp->lp[i].s) < 0)
746 {
747 return (-1); /* unrecognized state */
748 }
749 break;
750 }
751 }
752 }
753 }
754
755 /* ok */
756 return (0);
757 }
758
759 /* crack the snooped driver setSwitchVector or defSwitchVector message into the
760 * given ISwitchVectorProperty. it is not necessary that all ISwitch names be
761 * found.
762 * return 0 if type, device and name match, else return -1.
763 */
IUSnoopSwitch(XMLEle * root,ISwitchVectorProperty * svp)764 int IUSnoopSwitch(XMLEle *root, ISwitchVectorProperty *svp)
765 {
766 char *dev, *name;
767 XMLEle *ep;
768 int i;
769
770 /* check and crack type, device, name and state */
771 if (strcmp(tagXMLEle(root) + 3, "SwitchVector") || crackDN(root, &dev, &name, NULL) < 0)
772 return (-1);
773 if (strcmp(dev, svp->device) || strcmp(name, svp->name))
774 return (-1); /* not this property */
775 (void)crackIPState(findXMLAttValu(root, "state"), &svp->s);
776
777 /* match each oneSwitch with one ISwitch */
778 for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
779 {
780 if (!strcmp(tagXMLEle(ep) + 3, "Switch"))
781 {
782 const char *name = findXMLAttValu(ep, "name");
783 for (i = 0; i < svp->nsp; i++)
784 {
785 if (!strcmp(svp->sp[i].name, name))
786 {
787 if (crackISState(pcdataXMLEle(ep), &svp->sp[i].s) < 0)
788 {
789 return (-1); /* unrecognized state */
790 }
791 break;
792 }
793 }
794 }
795 }
796
797 /* ok */
798 return (0);
799 }
800
801 /* crack the snooped driver setBLOBVector message into the given
802 * IBLOBVectorProperty. it is not necessary that all IBLOB names be found.
803 * return 0 if type, device and name match, else return -1.
804 * N.B. we assume any existing blob in bvp has been malloced, which we free
805 * and replace with a newly malloced blob if found.
806 */
IUSnoopBLOB(XMLEle * root,IBLOBVectorProperty * bvp)807 int IUSnoopBLOB(XMLEle *root, IBLOBVectorProperty *bvp)
808 {
809 char *dev, *name;
810 XMLEle *ep;
811
812 /* check and crack type, device, name and state */
813 if (strcmp(tagXMLEle(root), "setBLOBVector") || crackDN(root, &dev, &name, NULL) < 0)
814 return (-1);
815
816 if (strcmp(dev, bvp->device) || strcmp(name, bvp->name))
817 return (-1); /* not this property */
818
819 crackIPState(findXMLAttValu(root, "state"), &bvp->s);
820
821 for (ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
822 {
823 if (strcmp(tagXMLEle(ep), "oneBLOB") == 0)
824 {
825 XMLAtt *na = findXMLAtt(ep, "name");
826 if (na == NULL)
827 return (-1);
828
829 IBLOB *bp = IUFindBLOB(bvp, valuXMLAtt(na));
830
831 if (bp == NULL)
832 return (-1);
833
834 XMLAtt *fa = findXMLAtt(ep, "format");
835 XMLAtt *sa = findXMLAtt(ep, "size");
836 XMLAtt *ec = findXMLAtt(ep, "enclen");
837 if (fa && sa && ec)
838 {
839 int enclen = atoi(valuXMLAtt(ec));
840 bp->blob = realloc(bp->blob, 3 * enclen / 4);
841 bp->bloblen = from64tobits_fast(bp->blob, pcdataXMLEle(ep), enclen);
842 strncpy(bp->format, valuXMLAtt(fa), MAXINDIFORMAT);
843 bp->size = atoi(valuXMLAtt(sa));
844 }
845 }
846 }
847
848 /* ok */
849 return (0);
850 }
851
852 /* callback when INDI client message arrives on stdin.
853 * collect and dispatch when see outter element closure.
854 * exit if OS trouble or see incompatable INDI version.
855 * arg is not used.
856 */
clientMsgCB(int fd,void * arg)857 void clientMsgCB(int fd, void *arg)
858 {
859 (void)arg;
860 char buf[MAXRBUF], msg[MAXRBUF], *bp;
861 int nr;
862
863 /* one read */
864 nr = read(fd, buf, sizeof(buf));
865 if (nr < 0)
866 {
867 fprintf(stderr, "%s: %s\n", me, strerror(errno));
868 exit(1);
869 }
870 if (nr == 0)
871 {
872 fprintf(stderr, "%s: EOF\n", me);
873 exit(1);
874 }
875
876 /* crack and dispatch when complete */
877 for (bp = buf; nr-- > 0; bp++)
878 {
879 XMLEle *root = readXMLEle(clixml, *bp, msg);
880 if (root)
881 {
882 if (dispatch(root, msg) < 0)
883 fprintf(stderr, "%s dispatch error: %s\n", me, msg);
884 delXMLEle(root);
885 }
886 else if (msg[0])
887 fprintf(stderr, "%s XML error: %s\n", me, msg);
888 }
889 }
890
891 /* crack the given INDI XML element and call driver's IS* entry points as they
892 * are recognized.
893 * return 0 if ok else -1 with reason in msg[].
894 * N.B. exit if getProperties does not proclaim a compatible version.
895 */
dispatch(XMLEle * root,char msg[])896 int dispatch(XMLEle *root, char msg[])
897 {
898 char *rtag = tagXMLEle(root);
899 XMLEle *ep;
900 int n, i = 0;
901
902 if (verbose)
903 prXMLEle(stderr, root, 0);
904
905 if (!strcmp(rtag, "getProperties"))
906 {
907 XMLAtt *ap, *name, *dev;
908 double v;
909
910 /* check version */
911 ap = findXMLAtt(root, "version");
912 if (!ap)
913 {
914 fprintf(stderr, "%s: getProperties missing version\n", me);
915 exit(1);
916 }
917 v = atof(valuXMLAtt(ap));
918 if (v > INDIV)
919 {
920 fprintf(stderr, "%s: client version %g > %g\n", me, v, INDIV);
921 exit(1);
922 }
923
924 // Get device
925 dev = findXMLAtt(root, "device");
926
927 // Get property name
928 name = findXMLAtt(root, "name");
929
930 if (name && dev)
931 {
932 int index = isPropDefined(valuXMLAtt(name), valuXMLAtt(dev));
933 if (index < 0)
934 return 0;
935
936 ROSC *prop = propCache + index;
937 switch (prop->type)
938 {
939 /* JM 2019-07-18: Why are we using setXXX here? should be defXXX */
940 case INDI_NUMBER:
941 //IDSetNumber((INumberVectorProperty *)(prop->ptr), NULL);
942 IDDefNumber((INumberVectorProperty *)(prop->ptr), NULL);
943 return 0;
944
945 case INDI_SWITCH:
946 //IDSetSwitch((ISwitchVectorProperty *)(prop->ptr), NULL);
947 IDDefSwitch((ISwitchVectorProperty *)(prop->ptr), NULL);
948 return 0;
949
950 case INDI_TEXT:
951 //IDSetText((ITextVectorProperty *)(prop->ptr), NULL);
952 IDDefText((ITextVectorProperty *)(prop->ptr), NULL);
953 return 0;
954
955 case INDI_BLOB:
956 //IDSetBLOB((IBLOBVectorProperty *)(prop->ptr), NULL);
957 IDDefBLOB((IBLOBVectorProperty *)(prop->ptr), NULL);
958 return 0;
959 default:
960 return 0;
961 }
962 }
963
964 ISGetProperties(dev ? valuXMLAtt(dev) : NULL);
965 return (0);
966 }
967
968 /* other commands might be from a snooped device.
969 * we don't know here which devices are being snooped so we send
970 * all remaining valid messages
971 */
972 if (!strcmp(rtag, "setNumberVector") || !strcmp(rtag, "setTextVector") || !strcmp(rtag, "setLightVector") ||
973 !strcmp(rtag, "setSwitchVector") || !strcmp(rtag, "setBLOBVector") || !strcmp(rtag, "defNumberVector") ||
974 !strcmp(rtag, "defTextVector") || !strcmp(rtag, "defLightVector") || !strcmp(rtag, "defSwitchVector") ||
975 !strcmp(rtag, "defBLOBVector") || !strcmp(rtag, "message") || !strcmp(rtag, "delProperty"))
976 {
977 ISSnoopDevice(root);
978 return (0);
979 }
980
981 char *dev, *name;
982 /* pull out device and name */
983 if (crackDN(root, &dev, &name, msg) < 0)
984 return (-1);
985
986 if (isPropDefined(name, dev) < 0)
987 {
988 snprintf(msg, MAXRBUF, "Property %s is not defined in %s.", name, dev);
989 return -1;
990 }
991
992 /* ensure property is not RO */
993 for (i = 0; i < nPropCache; i++)
994 {
995 if (!strcmp(propCache[i].propName, name) && !strcmp(propCache[i].devName, dev))
996 {
997 if (propCache[i].perm == IP_RO)
998 {
999 snprintf(msg, MAXRBUF, "Cannot set read-only property %s", name);
1000 return -1;
1001 }
1002 else
1003 break;
1004 }
1005 }
1006
1007 /* check tag in surmised decreasing order of likelyhood */
1008
1009 if (!strcmp(rtag, "newNumberVector"))
1010 {
1011 static double *doubles;
1012 static char **names;
1013 static int maxn;
1014
1015 /* seed for reallocs */
1016 if (!doubles)
1017 {
1018 doubles = (double *)malloc(1);
1019 names = (char **)malloc(1);
1020 }
1021
1022 // Set locale to C and save previous value
1023 locale_char_t *orig = indi_locale_C_numeric_push();
1024
1025 /* pull out each name/value pair */
1026 for (n = 0, ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1027 {
1028 if (strcmp(tagXMLEle(ep), "oneNumber") == 0)
1029 {
1030 XMLAtt *na = findXMLAtt(ep, "name");
1031 if (na)
1032 {
1033 if (n >= maxn)
1034 {
1035 /* grow for this and another */
1036 int newsz = (maxn = n + 1) * sizeof(double);
1037 doubles = (double *)realloc(doubles, newsz);
1038 newsz = maxn * sizeof(char *);
1039 names = (char **)realloc(names, newsz);
1040 }
1041 if (f_scansexa(pcdataXMLEle(ep), &doubles[n]) < 0)
1042 IDMessage(dev, "[ERROR] %s: Bad format %s", name, pcdataXMLEle(ep));
1043 else
1044 names[n++] = valuXMLAtt(na);
1045 }
1046 }
1047 }
1048
1049 // Reset locale settings to original value
1050 indi_locale_C_numeric_pop(orig);
1051
1052 /* invoke driver if something to do, but not an error if not */
1053 if (n > 0)
1054 ISNewNumber(dev, name, doubles, names, n);
1055 else
1056 IDMessage(dev, "[ERROR] %s: newNumberVector with no valid members", name);
1057 return (0);
1058 }
1059
1060 if (!strcmp(rtag, "newSwitchVector"))
1061 {
1062 static ISState *states;
1063 static char **names;
1064 static int maxn;
1065 XMLEle *ep;
1066
1067 /* seed for reallocs */
1068 if (!states)
1069 {
1070 states = (ISState *)malloc(1);
1071 names = (char **)malloc(1);
1072 }
1073
1074 /* pull out each name/state pair */
1075 for (n = 0, ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1076 {
1077 if (strcmp(tagXMLEle(ep), "oneSwitch") == 0)
1078 {
1079 XMLAtt *na = findXMLAtt(ep, "name");
1080 if (na)
1081 {
1082 if (n >= maxn)
1083 {
1084 int newsz = (maxn = n + 1) * sizeof(ISState);
1085 states = (ISState *)realloc(states, newsz);
1086 newsz = maxn * sizeof(char *);
1087 names = (char **)realloc(names, newsz);
1088 }
1089 if (strncmp(pcdataXMLEle(ep), "On", 2) == 0)
1090 {
1091 states[n] = ISS_ON;
1092 names[n] = valuXMLAtt(na);
1093 n++;
1094 }
1095 else if (strcmp(pcdataXMLEle(ep), "Off") == 0)
1096 {
1097 states[n] = ISS_OFF;
1098 names[n] = valuXMLAtt(na);
1099 n++;
1100 }
1101 else
1102 IDMessage(dev, "[ERROR] %s: must be On or Off: %s", name, pcdataXMLEle(ep));
1103 }
1104 }
1105 }
1106
1107 /* invoke driver if something to do, but not an error if not */
1108 if (n > 0)
1109 ISNewSwitch(dev, name, states, names, n);
1110 else
1111 IDMessage(dev, "[ERROR] %s: newSwitchVector with no valid members", name);
1112 return (0);
1113 }
1114
1115 if (!strcmp(rtag, "newTextVector"))
1116 {
1117 static char **texts;
1118 static char **names;
1119 static int maxn;
1120
1121 /* seed for reallocs */
1122 if (!texts)
1123 {
1124 texts = (char **)malloc(1);
1125 names = (char **)malloc(1);
1126 }
1127
1128 /* pull out each name/text pair */
1129 for (n = 0, ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1130 {
1131 if (strcmp(tagXMLEle(ep), "oneText") == 0)
1132 {
1133 XMLAtt *na = findXMLAtt(ep, "name");
1134 if (na)
1135 {
1136 if (n >= maxn)
1137 {
1138 int newsz = (maxn = n + 1) * sizeof(char *);
1139 texts = (char **)realloc(texts, newsz);
1140 names = (char **)realloc(names, newsz);
1141 }
1142 texts[n] = pcdataXMLEle(ep);
1143 names[n] = valuXMLAtt(na);
1144 n++;
1145 }
1146 }
1147 }
1148
1149 /* invoke driver if something to do, but not an error if not */
1150 if (n > 0)
1151 ISNewText(dev, name, texts, names, n);
1152 else
1153 IDMessage(dev, "[ERROR] %s: set with no valid members", name);
1154 return (0);
1155 }
1156
1157 if (!strcmp(rtag, "newBLOBVector"))
1158 {
1159 static char **blobs;
1160 static char **names;
1161 static char **formats;
1162 static int *blobsizes;
1163 static int *sizes;
1164 static int maxn;
1165 int i;
1166
1167 /* seed for reallocs */
1168 if (!blobs)
1169 {
1170 blobs = (char **)malloc(1);
1171 names = (char **)malloc(1);
1172 formats = (char **)malloc(1);
1173 blobsizes = (int *)malloc(1);
1174 sizes = (int *)malloc(1);
1175 }
1176
1177 /* pull out each name/BLOB pair, decode */
1178 for (n = 0, ep = nextXMLEle(root, 1); ep; ep = nextXMLEle(root, 0))
1179 {
1180 if (strcmp(tagXMLEle(ep), "oneBLOB") == 0)
1181 {
1182 XMLAtt *na = findXMLAtt(ep, "name");
1183 XMLAtt *fa = findXMLAtt(ep, "format");
1184 XMLAtt *sa = findXMLAtt(ep, "size");
1185 XMLAtt *el = findXMLAtt(ep, "enclen");
1186 if (na && fa && sa)
1187 {
1188 if (n >= maxn)
1189 {
1190 int newsz = (maxn = n + 1) * sizeof(char *);
1191 blobs = (char **)realloc(blobs, newsz);
1192 names = (char **)realloc(names, newsz);
1193 formats = (char **)realloc(formats, newsz);
1194 newsz = maxn * sizeof(int);
1195 sizes = (int *)realloc(sizes, newsz);
1196 blobsizes = (int *)realloc(blobsizes, newsz);
1197 }
1198 int bloblen = pcdatalenXMLEle(ep);
1199 // enclen is optional and not required by INDI protocol
1200 if (el)
1201 bloblen = atoi(valuXMLAtt(el));
1202 blobs[n] = malloc(3 * bloblen / 4);
1203 blobsizes[n] = from64tobits_fast(blobs[n], pcdataXMLEle(ep), bloblen);
1204 names[n] = valuXMLAtt(na);
1205 formats[n] = valuXMLAtt(fa);
1206 sizes[n] = atoi(valuXMLAtt(sa));
1207 n++;
1208 }
1209 }
1210 }
1211
1212 /* invoke driver if something to do, but not an error if not */
1213 if (n > 0)
1214 {
1215 ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
1216 for (i = 0; i < n; i++)
1217 free(blobs[i]);
1218 }
1219 else
1220 IDMessage(dev, "[ERROR] %s: newBLOBVector with no valid members", name);
1221 return (0);
1222 }
1223
1224 sprintf(msg, "Unknown command: %s", rtag);
1225 return (1);
1226 }
1227
IUReadConfig(const char * filename,const char * dev,const char * property,int silent,char errmsg[])1228 int IUReadConfig(const char *filename, const char *dev, const char *property, int silent, char errmsg[])
1229 {
1230 char *rname, *rdev;
1231 XMLEle *root = NULL, *fproot = NULL;
1232 LilXML *lp = newLilXML();
1233
1234 FILE *fp = IUGetConfigFP(filename, dev, "r", errmsg);
1235
1236 if (fp == NULL)
1237 return -1;
1238
1239 char whynot[MAXRBUF];
1240 fproot = readXMLFile(fp, lp, whynot);
1241
1242 delLilXML(lp);
1243
1244 if (fproot == NULL)
1245 {
1246 snprintf(errmsg, MAXRBUF, "Unable to parse config XML: %s", whynot);
1247 fclose(fp);
1248 return -1;
1249 }
1250
1251 if (nXMLEle(fproot) > 0 && silent != 1)
1252 IDMessage(dev, "[INFO] Loading device configuration...");
1253
1254 for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1255 {
1256 /* pull out device and name */
1257 if (crackDN(root, &rdev, &rname, errmsg) < 0)
1258 {
1259 fclose(fp);
1260 delXMLEle(fproot);
1261 return -1;
1262 }
1263
1264 // It doesn't belong to our device??
1265 if (strcmp(dev, rdev))
1266 continue;
1267
1268 if ((property && !strcmp(property, rname)) || property == NULL)
1269 {
1270 dispatch(root, errmsg);
1271 if (property)
1272 break;
1273 }
1274 }
1275
1276 if (nXMLEle(fproot) > 0 && silent != 1)
1277 IDMessage(dev, "[INFO] Device configuration applied.");
1278
1279 fclose(fp);
1280 delXMLEle(fproot);
1281
1282 return (0);
1283 }
1284
IUSaveDefaultConfig(const char * source_config,const char * dest_config,const char * dev)1285 void IUSaveDefaultConfig(const char *source_config, const char *dest_config, const char *dev)
1286 {
1287 char configFileName[MAXRBUF], configDefaultFileName[MAXRBUF];
1288
1289 if (source_config)
1290 strncpy(configFileName, source_config, MAXRBUF);
1291 else
1292 {
1293 if (getenv("INDICONFIG"))
1294 strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1295 else
1296 snprintf(configFileName, MAXRBUF, "%s/.indi/%s_config.xml", getenv("HOME"), dev);
1297 }
1298
1299 if (dest_config)
1300 strncpy(configDefaultFileName, dest_config, MAXRBUF);
1301 else if (getenv("INDICONFIG"))
1302 snprintf(configDefaultFileName, MAXRBUF, "%s.default", getenv("INDICONFIG"));
1303 else
1304 snprintf(configDefaultFileName, MAXRBUF, "%s/.indi/%s_config.xml.default", getenv("HOME"), dev);
1305
1306 // If the default doesn't exist, create it.
1307 if (access(configDefaultFileName, F_OK))
1308 {
1309 FILE *fpin = fopen(configFileName, "r");
1310 if (fpin != NULL)
1311 {
1312 FILE *fpout = fopen(configDefaultFileName, "w");
1313 if (fpout != NULL)
1314 {
1315 int ch = 0;
1316 while ((ch = getc(fpin)) != EOF)
1317 putc(ch, fpout);
1318 fclose(fpout);
1319 }
1320 fclose(fpin);
1321 }
1322 }
1323 }
1324
IUGetConfigSwitch(const char * dev,const char * property,const char * member,ISState * value)1325 int IUGetConfigSwitch(const char *dev, const char *property, const char *member, ISState *value)
1326 {
1327 char *rname, *rdev;
1328 XMLEle *root = NULL, *fproot = NULL;
1329 char errmsg[MAXRBUF];
1330 LilXML *lp = newLilXML();
1331 int valueFound=0;
1332
1333 FILE *fp = IUGetConfigFP(NULL, dev, "r", errmsg);
1334
1335 if (fp == NULL)
1336 return -1;
1337
1338 fproot = readXMLFile(fp, lp, errmsg);
1339
1340 if (fproot == NULL)
1341 {
1342 fclose(fp);
1343 return -1;
1344 }
1345
1346 for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1347 {
1348 /* pull out device and name */
1349 if (crackDN(root, &rdev, &rname, errmsg) < 0)
1350 {
1351 fclose(fp);
1352 delXMLEle(fproot);
1353 return -1;
1354 }
1355
1356 // It doesn't belong to our device??
1357 if (strcmp(dev, rdev))
1358 continue;
1359
1360 if ((property && !strcmp(property, rname)) || property == NULL)
1361 {
1362 XMLEle *oneSwitch = NULL;
1363 for (oneSwitch = nextXMLEle(root, 1); oneSwitch != NULL; oneSwitch = nextXMLEle(root, 0))
1364 {
1365 if (!strcmp(member, findXMLAttValu(oneSwitch, "name")))
1366 {
1367 if (crackISState(pcdataXMLEle(oneSwitch), value) == 0)
1368 valueFound = 1;
1369 break;
1370 }
1371 }
1372 break;
1373 }
1374 }
1375
1376 fclose(fp);
1377 delXMLEle(fproot);
1378 delLilXML(lp);
1379
1380 return (valueFound == 1 ? 0 : -1);
1381 }
1382
IUGetConfigNumber(const char * dev,const char * property,const char * member,double * value)1383 int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
1384 {
1385 char *rname, *rdev;
1386 XMLEle *root = NULL, *fproot = NULL;
1387 char errmsg[MAXRBUF];
1388 LilXML *lp = newLilXML();
1389 int valueFound=0;
1390
1391 FILE *fp = IUGetConfigFP(NULL, dev, "r", errmsg);
1392
1393 if (fp == NULL)
1394 return -1;
1395
1396 fproot = readXMLFile(fp, lp, errmsg);
1397
1398 if (fproot == NULL)
1399 {
1400 fclose(fp);
1401 return -1;
1402 }
1403
1404 for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1405 {
1406 /* pull out device and name */
1407 if (crackDN(root, &rdev, &rname, errmsg) < 0)
1408 {
1409 fclose(fp);
1410 delXMLEle(fproot);
1411 return -1;
1412 }
1413
1414 // It doesn't belong to our device??
1415 if (strcmp(dev, rdev))
1416 continue;
1417
1418 if ((property && !strcmp(property, rname)) || property == NULL)
1419 {
1420 XMLEle *oneNumber = NULL;
1421 for (oneNumber = nextXMLEle(root, 1); oneNumber != NULL; oneNumber = nextXMLEle(root, 0))
1422 {
1423 if (!strcmp(member, findXMLAttValu(oneNumber, "name")))
1424 {
1425 *value = atof(pcdataXMLEle(oneNumber));
1426 valueFound = 1;
1427 break;
1428 }
1429 }
1430 break;
1431 }
1432 }
1433
1434 fclose(fp);
1435 delXMLEle(fproot);
1436 delLilXML(lp);
1437
1438 return (valueFound == 1 ? 0 : -1);
1439 }
1440
IUGetConfigText(const char * dev,const char * property,const char * member,char * value,int len)1441 int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
1442 {
1443 char *rname, *rdev;
1444 XMLEle *root = NULL, *fproot = NULL;
1445 char errmsg[MAXRBUF];
1446 LilXML *lp = newLilXML();
1447 int valueFound=0;
1448
1449 FILE *fp = IUGetConfigFP(NULL, dev, "r", errmsg);
1450
1451 if (fp == NULL)
1452 return -1;
1453
1454 fproot = readXMLFile(fp, lp, errmsg);
1455
1456 if (fproot == NULL)
1457 {
1458 fclose(fp);
1459 return -1;
1460 }
1461
1462 for (root = nextXMLEle(fproot, 1); root != NULL; root = nextXMLEle(fproot, 0))
1463 {
1464 /* pull out device and name */
1465 if (crackDN(root, &rdev, &rname, errmsg) < 0)
1466 {
1467 fclose(fp);
1468 delXMLEle(fproot);
1469 return -1;
1470 }
1471
1472 // It doesn't belong to our device??
1473 if (strcmp(dev, rdev))
1474 continue;
1475
1476 if ((property && !strcmp(property, rname)) || property == NULL)
1477 {
1478 XMLEle *oneText = NULL;
1479 for (oneText = nextXMLEle(root, 1); oneText != NULL; oneText = nextXMLEle(root, 0))
1480 {
1481 if (!strcmp(member, findXMLAttValu(oneText, "name")))
1482 {
1483 strncpy(value, pcdataXMLEle(oneText), len);
1484 valueFound = 1;
1485 break;
1486 }
1487 }
1488 break;
1489 }
1490 }
1491
1492 fclose(fp);
1493 delXMLEle(fproot);
1494 delLilXML(lp);
1495
1496 return (valueFound == 1 ? 0 : -1);
1497 }
1498
1499 /* send client a message for a specific device or at large if !dev */
IDMessage(const char * dev,const char * fmt,...)1500 void IDMessage(const char *dev, const char *fmt, ...)
1501 {
1502 pthread_mutex_lock(&stdout_mutex);
1503
1504 xmlv1();
1505 printf("<message\n");
1506 if (dev)
1507 printf(" device='%s'\n", dev);
1508 printf(" timestamp='%s'\n", timestamp());
1509 if (fmt)
1510 {
1511 va_list ap;
1512 va_start(ap, fmt);
1513 char message[MAXINDIMESSAGE];
1514 printf(" message='");
1515 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1516 char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1517 printf("%s'\n", escapedMessage);
1518 free(escapedMessage);
1519 va_end(ap);
1520 }
1521 printf("/>\n");
1522 fflush(stdout);
1523
1524 pthread_mutex_unlock(&stdout_mutex);
1525 }
1526
IUPurgeConfig(const char * filename,const char * dev,char errmsg[])1527 int IUPurgeConfig(const char *filename, const char *dev, char errmsg[])
1528 {
1529 char configFileName[MAXRBUF];
1530 char configDir[MAXRBUF];
1531
1532 snprintf(configDir, MAXRBUF, "%s/.indi/", getenv("HOME"));
1533
1534 if (filename)
1535 strncpy(configFileName, filename, MAXRBUF);
1536 else
1537 {
1538 if (getenv("INDICONFIG"))
1539 strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1540 else
1541 snprintf(configFileName, MAXRBUF, "%s%s_config.xml", configDir, dev);
1542 }
1543
1544 if (remove(configFileName) != 0)
1545 {
1546 snprintf(errmsg, MAXRBUF, "Unable to purge configuration file %s. Error %s", configFileName, strerror(errno));
1547 return -1;
1548 }
1549
1550 return 0;
1551 }
1552
IUGetConfigFP(const char * filename,const char * dev,const char * mode,char errmsg[])1553 FILE *IUGetConfigFP(const char *filename, const char *dev, const char *mode, char errmsg[])
1554 {
1555 char configFileName[MAXRBUF];
1556 char configDir[MAXRBUF];
1557 struct stat st;
1558 FILE *fp = NULL;
1559
1560 snprintf(configDir, MAXRBUF, "%s/.indi/", getenv("HOME"));
1561
1562 if (filename)
1563 strncpy(configFileName, filename, MAXRBUF);
1564 else
1565 {
1566 if (getenv("INDICONFIG"))
1567 strncpy(configFileName, getenv("INDICONFIG"), MAXRBUF);
1568 else
1569 snprintf(configFileName, MAXRBUF, "%s%s_config.xml", configDir, dev);
1570 }
1571
1572 if (stat(configDir, &st) != 0)
1573 {
1574 if (mkdir(configDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
1575 {
1576 snprintf(errmsg, MAXRBUF, "Unable to create config directory. Error %s: %s", configDir, strerror(errno));
1577 return NULL;
1578 }
1579 }
1580
1581 stat(configFileName, &st);
1582 /* If file is owned by root and current user is NOT root then abort */
1583 if ( (st.st_uid == 0 && getuid() != 0) || (st.st_gid == 0 && getgid() != 0) )
1584 {
1585 strncpy(errmsg, "Config file is owned by root! This will lead to serious errors. To fix this, run: sudo chown -R $USER:$USER ~/.indi", MAXRBUF);
1586 return NULL;
1587 }
1588
1589 fp = fopen(configFileName, mode);
1590 if (fp == NULL)
1591 {
1592 snprintf(errmsg, MAXRBUF, "Unable to open config file. Error loading file %s: %s", configFileName,
1593 strerror(errno));
1594 return NULL;
1595 }
1596
1597 return fp;
1598 }
1599
IUSaveConfigTag(FILE * fp,int ctag,const char * dev,int silent)1600 void IUSaveConfigTag(FILE *fp, int ctag, const char *dev, int silent)
1601 {
1602 if (!fp)
1603 return;
1604
1605 /* Opening tag */
1606 if (ctag == 0)
1607 {
1608 fprintf(fp, "<INDIDriver>\n");
1609 if (silent != 1)
1610 IDMessage(dev, "[INFO] Saving device configuration...");
1611 }
1612 /* Closing tag */
1613 else
1614 {
1615 fprintf(fp, "</INDIDriver>\n");
1616 if (silent != 1)
1617 IDMessage(dev, "[INFO] Device configuration saved.");
1618 }
1619 }
1620
IUSaveConfigNumber(FILE * fp,const INumberVectorProperty * nvp)1621 void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
1622 {
1623 int i;
1624
1625 locale_char_t *orig = indi_locale_C_numeric_push();
1626 fprintf(fp, "<newNumberVector device='%s' name='%s'>\n", nvp->device, nvp->name);
1627
1628 for (i = 0; i < nvp->nnp; i++)
1629 {
1630 INumber *np = &nvp->np[i];
1631 fprintf(fp, " <oneNumber name='%s'>\n", np->name);
1632 fprintf(fp, " %.20g\n", np->value);
1633 fprintf(fp, " </oneNumber>\n");
1634 }
1635
1636 fprintf(fp, "</newNumberVector>\n");
1637 indi_locale_C_numeric_pop(orig);
1638 }
1639
IUSaveConfigText(FILE * fp,const ITextVectorProperty * tvp)1640 void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
1641 {
1642 int i;
1643
1644 fprintf(fp, "<newTextVector device='%s' name='%s'>\n", tvp->device, tvp->name);
1645
1646 for (i = 0; i < tvp->ntp; i++)
1647 {
1648 IText *tp = &tvp->tp[i];
1649 fprintf(fp, " <oneText name='%s'>\n", tp->name);
1650 fprintf(fp, " %s\n", tp->text ? tp->text : "");
1651 fprintf(fp, " </oneText>\n");
1652 }
1653
1654 fprintf(fp, "</newTextVector>\n");
1655 }
1656
IUSaveConfigSwitch(FILE * fp,const ISwitchVectorProperty * svp)1657 void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
1658 {
1659 int i;
1660
1661 fprintf(fp, "<newSwitchVector device='%s' name='%s'>\n", svp->device, svp->name);
1662
1663 for (i = 0; i < svp->nsp; i++)
1664 {
1665 ISwitch *sp = &svp->sp[i];
1666 fprintf(fp, " <oneSwitch name='%s'>\n", sp->name);
1667 fprintf(fp, " %s\n", sstateStr(sp->s));
1668 fprintf(fp, " </oneSwitch>\n");
1669 }
1670
1671 fprintf(fp, "</newSwitchVector>\n");
1672 }
1673
IUSaveConfigBLOB(FILE * fp,const IBLOBVectorProperty * bvp)1674 void IUSaveConfigBLOB(FILE *fp, const IBLOBVectorProperty *bvp)
1675 {
1676 int i;
1677
1678 fprintf(fp, "<newBLOBVector device='%s' name='%s'>\n", bvp->device, bvp->name);
1679
1680 for (i = 0; i < bvp->nbp; i++)
1681 {
1682 IBLOB *bp = &bvp->bp[i];
1683 unsigned char *encblob = NULL;
1684 int l = 0;
1685
1686 fprintf(fp, " <oneBLOB\n");
1687 fprintf(fp, " name='%s'\n", bp->name);
1688 fprintf(fp, " size='%d'\n", bp->size);
1689 fprintf(fp, " format='%s'>\n", bp->format);
1690
1691 encblob = malloc(4 * bp->bloblen / 3 + 4);
1692 l = to64frombits(encblob, bp->blob, bp->bloblen);
1693 size_t written = 0;
1694
1695 while ((int)written < l)
1696 {
1697 size_t towrite = ((l - written) > 72) ? 72 : l - written;
1698 size_t wr = fwrite(encblob + written, 1, towrite, fp);
1699
1700 fputc('\n', fp);
1701 if (wr > 0)
1702 written += wr;
1703 }
1704 free(encblob);
1705
1706 fprintf(fp, " </oneBLOB>\n");
1707 }
1708
1709 fprintf(fp, "</newBLOBVector>\n");
1710 }
1711
1712 /* tell client to create a text vector property */
IDDefText(const ITextVectorProperty * tvp,const char * fmt,...)1713 void IDDefText(const ITextVectorProperty *tvp, const char *fmt, ...)
1714 {
1715 int i;
1716 ROSC *SC;
1717
1718 pthread_mutex_lock(&stdout_mutex);
1719
1720 xmlv1();
1721 locale_char_t *orig = indi_locale_C_numeric_push();
1722 printf("<defTextVector\n");
1723 printf(" device='%s'\n", tvp->device);
1724 printf(" name='%s'\n", tvp->name);
1725 printf(" label='%s'\n", tvp->label);
1726 printf(" group='%s'\n", tvp->group);
1727 printf(" state='%s'\n", pstateStr(tvp->s));
1728 printf(" perm='%s'\n", permStr(tvp->p));
1729 printf(" timeout='%g'\n", tvp->timeout);
1730 printf(" timestamp='%s'\n", timestamp());
1731 if (fmt)
1732 {
1733 va_list ap;
1734 va_start(ap, fmt);
1735 char message[MAXINDIMESSAGE];
1736 printf(" message='");
1737 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1738 char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1739 printf("%s'\n", escapedMessage);
1740 free(escapedMessage);
1741 va_end(ap);
1742 }
1743 printf(">\n");
1744
1745 for (i = 0; i < tvp->ntp; i++)
1746 {
1747 IText *tp = &tvp->tp[i];
1748 printf(" <defText\n");
1749 printf(" name='%s'\n", tp->name);
1750 printf(" label='%s'>\n", tp->label);
1751 printf(" %s\n", tp->text ? tp->text : "");
1752 printf(" </defText>\n");
1753 }
1754
1755 printf("</defTextVector>\n");
1756
1757 if (isPropDefined(tvp->name, tvp->device) < 0)
1758 {
1759 /* Add this property to insure proper sanity check */
1760 propCache =
1761 propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1762 SC = &propCache[nPropCache++];
1763
1764 strcpy(SC->propName, tvp->name);
1765 strcpy(SC->devName, tvp->device);
1766 SC->perm = tvp->p;
1767 SC->ptr = tvp;
1768 SC->type = INDI_TEXT;
1769 }
1770
1771 indi_locale_C_numeric_pop(orig);
1772 fflush(stdout);
1773
1774 pthread_mutex_unlock(&stdout_mutex);
1775 }
1776
1777 /* tell client to create a new numeric vector property */
IDDefNumber(const INumberVectorProperty * n,const char * fmt,...)1778 void IDDefNumber(const INumberVectorProperty *n, const char *fmt, ...)
1779 {
1780 int i;
1781 ROSC *SC;
1782
1783 pthread_mutex_lock(&stdout_mutex);
1784
1785 xmlv1();
1786 locale_char_t *orig = indi_locale_C_numeric_push();
1787 printf("<defNumberVector\n");
1788 printf(" device='%s'\n", n->device);
1789 printf(" name='%s'\n", n->name);
1790 printf(" label='%s'\n", n->label);
1791 printf(" group='%s'\n", n->group);
1792 printf(" state='%s'\n", pstateStr(n->s));
1793 printf(" perm='%s'\n", permStr(n->p));
1794 printf(" timeout='%g'\n", n->timeout);
1795 printf(" timestamp='%s'\n", timestamp());
1796
1797 if (fmt)
1798 {
1799 va_list ap;
1800 va_start(ap, fmt);
1801 char message[MAXINDIMESSAGE];
1802 printf(" message='");
1803 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1804 char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1805 printf("%s'\n", escapedMessage);
1806 free(escapedMessage);
1807 va_end(ap);
1808 }
1809 printf(">\n");
1810
1811 for (i = 0; i < n->nnp; i++)
1812 {
1813 INumber *np = &n->np[i];
1814
1815 printf(" <defNumber\n");
1816 printf(" name='%s'\n", np->name);
1817 printf(" label='%s'\n", np->label);
1818 printf(" format='%s'\n", np->format);
1819 printf(" min='%.20g'\n", np->min);
1820 printf(" max='%.20g'\n", np->max);
1821 printf(" step='%.20g'>\n", np->step);
1822 printf(" %.20g\n", np->value);
1823
1824 printf(" </defNumber>\n");
1825 }
1826
1827 printf("</defNumberVector>\n");
1828
1829 if (isPropDefined(n->name, n->device) < 0)
1830 {
1831 /* Add this property to insure proper sanity check */
1832 propCache =
1833 propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1834 SC = &propCache[nPropCache++];
1835
1836 strcpy(SC->propName, n->name);
1837 strcpy(SC->devName, n->device);
1838 SC->perm = n->p;
1839 SC->ptr = n;
1840 SC->type = INDI_NUMBER;
1841 }
1842
1843 indi_locale_C_numeric_pop(orig);
1844 fflush(stdout);
1845
1846 pthread_mutex_unlock(&stdout_mutex);
1847 }
1848
1849 /* tell client to create a new switch vector property */
IDDefSwitch(const ISwitchVectorProperty * s,const char * fmt,...)1850 void IDDefSwitch(const ISwitchVectorProperty *s, const char *fmt, ...)
1851
1852 {
1853 int i;
1854 ROSC *SC;
1855
1856 pthread_mutex_lock(&stdout_mutex);
1857
1858 xmlv1();
1859 locale_char_t *orig = indi_locale_C_numeric_push();
1860 printf("<defSwitchVector\n");
1861 printf(" device='%s'\n", s->device);
1862 printf(" name='%s'\n", s->name);
1863 printf(" label='%s'\n", s->label);
1864 printf(" group='%s'\n", s->group);
1865 printf(" state='%s'\n", pstateStr(s->s));
1866 printf(" perm='%s'\n", permStr(s->p));
1867 printf(" rule='%s'\n", ruleStr(s->r));
1868 printf(" timeout='%g'\n", s->timeout);
1869 printf(" timestamp='%s'\n", timestamp());
1870 if (fmt)
1871 {
1872 va_list ap;
1873 va_start(ap, fmt);
1874 char message[MAXINDIMESSAGE];
1875 printf(" message='");
1876 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1877 char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1878 printf("%s'\n", escapedMessage);
1879 free(escapedMessage);
1880 va_end(ap);
1881 }
1882 printf(">\n");
1883
1884 for (i = 0; i < s->nsp; i++)
1885 {
1886 ISwitch *sp = &s->sp[i];
1887 printf(" <defSwitch\n");
1888 printf(" name='%s'\n", sp->name);
1889 printf(" label='%s'>\n", sp->label);
1890 printf(" %s\n", sstateStr(sp->s));
1891 printf(" </defSwitch>\n");
1892 }
1893
1894 printf("</defSwitchVector>\n");
1895
1896 if (isPropDefined(s->name, s->device) < 0)
1897 {
1898 /* Add this property to insure proper sanity check */
1899 propCache =
1900 propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
1901 SC = &propCache[nPropCache++];
1902
1903 strcpy(SC->propName, s->name);
1904 strcpy(SC->devName, s->device);
1905 SC->perm = s->p;
1906 SC->ptr = s;
1907 SC->type = INDI_SWITCH;
1908 }
1909
1910 indi_locale_C_numeric_pop(orig);
1911 fflush(stdout);
1912
1913 pthread_mutex_unlock(&stdout_mutex);
1914 }
1915
1916 /* tell client to create a new lights vector property */
IDDefLight(const ILightVectorProperty * lvp,const char * fmt,...)1917 void IDDefLight(const ILightVectorProperty *lvp, const char *fmt, ...)
1918 {
1919 int i;
1920
1921 pthread_mutex_lock(&stdout_mutex);
1922
1923 xmlv1();
1924 printf("<defLightVector\n");
1925 printf(" device='%s'\n", lvp->device);
1926 printf(" name='%s'\n", lvp->name);
1927 printf(" label='%s'\n", lvp->label);
1928 printf(" group='%s'\n", lvp->group);
1929 printf(" state='%s'\n", pstateStr(lvp->s));
1930 printf(" timestamp='%s'\n", timestamp());
1931 if (fmt)
1932 {
1933 va_list ap;
1934 va_start(ap, fmt);
1935 char message[MAXINDIMESSAGE];
1936 printf(" message='");
1937 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1938 char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1939 printf("%s'\n", escapedMessage);
1940 free(escapedMessage);
1941 va_end(ap);
1942 }
1943 printf(">\n");
1944
1945 for (i = 0; i < lvp->nlp; i++)
1946 {
1947 ILight *lp = &lvp->lp[i];
1948 printf(" <defLight\n");
1949 printf(" name='%s'\n", lp->name);
1950 printf(" label='%s'>\n", lp->label);
1951 printf(" %s\n", pstateStr(lp->s));
1952 printf(" </defLight>\n");
1953 }
1954
1955 printf("</defLightVector>\n");
1956 fflush(stdout);
1957
1958 pthread_mutex_unlock(&stdout_mutex);
1959 }
1960
1961 /* tell client to create a new BLOB vector property */
IDDefBLOB(const IBLOBVectorProperty * b,const char * fmt,...)1962 void IDDefBLOB(const IBLOBVectorProperty *b, const char *fmt, ...)
1963 {
1964 int i;
1965 ROSC *SC;
1966
1967 pthread_mutex_lock(&stdout_mutex);
1968
1969 xmlv1();
1970 locale_char_t *orig = indi_locale_C_numeric_push();
1971 printf("<defBLOBVector\n");
1972 printf(" device='%s'\n", b->device);
1973 printf(" name='%s'\n", b->name);
1974 printf(" label='%s'\n", b->label);
1975 printf(" group='%s'\n", b->group);
1976 printf(" state='%s'\n", pstateStr(b->s));
1977 printf(" perm='%s'\n", permStr(b->p));
1978 printf(" timeout='%g'\n", b->timeout);
1979 printf(" timestamp='%s'\n", timestamp());
1980 if (fmt)
1981 {
1982 va_list ap;
1983 va_start(ap, fmt);
1984 char message[MAXINDIMESSAGE];
1985 printf(" message='");
1986 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
1987 char *escapedMessage = escapeXML(message, MAXINDIMESSAGE);
1988 printf("%s'\n", escapedMessage);
1989 free(escapedMessage);
1990 va_end(ap);
1991 }
1992 printf(">\n");
1993
1994 for (i = 0; i < b->nbp; i++)
1995 {
1996 IBLOB *bp = &b->bp[i];
1997 printf(" <defBLOB\n");
1998 printf(" name='%s'\n", bp->name);
1999 printf(" label='%s'\n", bp->label);
2000 printf(" />\n");
2001 }
2002
2003 printf("</defBLOBVector>\n");
2004
2005 if (isPropDefined(b->name, b->device) < 0)
2006 {
2007 /* Add this property to insure proper sanity check */
2008 propCache =
2009 propCache ? (ROSC *)realloc(propCache, sizeof(ROSC) * (nPropCache + 1)) : (ROSC *)malloc(sizeof(ROSC));
2010 SC = &propCache[nPropCache++];
2011
2012 strcpy(SC->propName, b->name);
2013 strcpy(SC->devName, b->device);
2014 SC->perm = b->p;
2015 SC->ptr = b;
2016 SC->type = INDI_BLOB;
2017 }
2018
2019 indi_locale_C_numeric_pop(orig);
2020 fflush(stdout);
2021
2022 pthread_mutex_unlock(&stdout_mutex);
2023 }
2024
2025 /* tell client to update an existing text vector property */
IDSetText(const ITextVectorProperty * tvp,const char * fmt,...)2026 void IDSetText(const ITextVectorProperty *tvp, const char *fmt, ...)
2027 {
2028 int i;
2029
2030 pthread_mutex_lock(&stdout_mutex);
2031
2032 xmlv1();
2033 locale_char_t *orig = indi_locale_C_numeric_push();
2034 printf("<setTextVector\n");
2035 printf(" device='%s'\n", tvp->device);
2036 printf(" name='%s'\n", tvp->name);
2037 printf(" state='%s'\n", pstateStr(tvp->s));
2038 printf(" timeout='%g'\n", tvp->timeout);
2039 printf(" timestamp='%s'\n", timestamp());
2040 if (fmt)
2041 {
2042 va_list ap;
2043 va_start(ap, fmt);
2044 char message[MAXINDIMESSAGE];
2045 printf(" message='");
2046 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
2047 printf("%s'\n", entityXML(message));
2048 va_end(ap);
2049 }
2050 printf(">\n");
2051
2052 for (i = 0; i < tvp->ntp; i++)
2053 {
2054 IText *tp = &tvp->tp[i];
2055 printf(" <oneText name='%s'>\n", tp->name);
2056 printf(" %s\n", tp->text ? entityXML(tp->text) : "");
2057 printf(" </oneText>\n");
2058 }
2059
2060 printf("</setTextVector>\n");
2061 indi_locale_C_numeric_pop(orig);
2062 fflush(stdout);
2063
2064 pthread_mutex_unlock(&stdout_mutex);
2065 }
2066
2067 /* tell client to update an existing numeric vector property */
IDSetNumber(const INumberVectorProperty * nvp,const char * fmt,...)2068 void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt, ...)
2069 {
2070 int i;
2071
2072 pthread_mutex_lock(&stdout_mutex);
2073
2074 xmlv1();
2075 locale_char_t *orig = indi_locale_C_numeric_push();
2076 printf("<setNumberVector\n");
2077 printf(" device='%s'\n", nvp->device);
2078 printf(" name='%s'\n", nvp->name);
2079 printf(" state='%s'\n", pstateStr(nvp->s));
2080 printf(" timeout='%g'\n", nvp->timeout);
2081 printf(" timestamp='%s'\n", timestamp());
2082 if (fmt)
2083 {
2084 va_list ap;
2085 va_start(ap, fmt);
2086 char message[MAXINDIMESSAGE];
2087 printf(" message='");
2088 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
2089 printf("%s'\n", entityXML(message));
2090 va_end(ap);
2091 }
2092 printf(">\n");
2093
2094 for (i = 0; i < nvp->nnp; i++)
2095 {
2096 INumber *np = &nvp->np[i];
2097 printf(" <oneNumber name='%s'>\n", np->name);
2098 printf(" %.20g\n", np->value);
2099 printf(" </oneNumber>\n");
2100 }
2101
2102 printf("</setNumberVector>\n");
2103 indi_locale_C_numeric_pop(orig);
2104 fflush(stdout);
2105
2106 pthread_mutex_unlock(&stdout_mutex);
2107 }
2108
2109 /* tell client to update an existing switch vector property */
IDSetSwitch(const ISwitchVectorProperty * svp,const char * fmt,...)2110 void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt, ...)
2111 {
2112 int i;
2113
2114 pthread_mutex_lock(&stdout_mutex);
2115
2116 xmlv1();
2117 locale_char_t *orig = indi_locale_C_numeric_push();
2118 printf("<setSwitchVector\n");
2119 printf(" device='%s'\n", svp->device);
2120 printf(" name='%s'\n", svp->name);
2121 printf(" state='%s'\n", pstateStr(svp->s));
2122 printf(" timeout='%g'\n", svp->timeout);
2123 printf(" timestamp='%s'\n", timestamp());
2124 if (fmt)
2125 {
2126 va_list ap;
2127 va_start(ap, fmt);
2128 char message[MAXINDIMESSAGE];
2129 printf(" message='");
2130 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
2131 printf("%s'\n", entityXML(message));
2132 va_end(ap);
2133 }
2134 printf(">\n");
2135
2136 for (i = 0; i < svp->nsp; i++)
2137 {
2138 ISwitch *sp = &svp->sp[i];
2139 printf(" <oneSwitch name='%s'>\n", sp->name);
2140 printf(" %s\n", sstateStr(sp->s));
2141 printf(" </oneSwitch>\n");
2142 }
2143
2144 printf("</setSwitchVector>\n");
2145 indi_locale_C_numeric_pop(orig);
2146 fflush(stdout);
2147
2148 pthread_mutex_unlock(&stdout_mutex);
2149 }
2150
2151 /* tell client to update an existing lights vector property */
IDSetLight(const ILightVectorProperty * lvp,const char * fmt,...)2152 void IDSetLight(const ILightVectorProperty *lvp, const char *fmt, ...)
2153 {
2154 int i;
2155
2156 pthread_mutex_lock(&stdout_mutex);
2157
2158 xmlv1();
2159 printf("<setLightVector\n");
2160 printf(" device='%s'\n", lvp->device);
2161 printf(" name='%s'\n", lvp->name);
2162 printf(" state='%s'\n", pstateStr(lvp->s));
2163 printf(" timestamp='%s'\n", timestamp());
2164 if (fmt)
2165 {
2166 va_list ap;
2167 va_start(ap, fmt);
2168 char message[MAXINDIMESSAGE];
2169 printf(" message='");
2170 vsnprintf(message, MAXINDIMESSAGE, fmt, ap);
2171 printf("%s'\n", entityXML(message));
2172 va_end(ap);
2173 }
2174 printf(">\n");
2175
2176 for (i = 0; i < lvp->nlp; i++)
2177 {
2178 ILight *lp = &lvp->lp[i];
2179 printf(" <oneLight name='%s'>\n", lp->name);
2180 printf(" %s\n", pstateStr(lp->s));
2181 printf(" </oneLight>\n");
2182 }
2183
2184 printf("</setLightVector>\n");
2185 fflush(stdout);
2186
2187 pthread_mutex_unlock(&stdout_mutex);
2188 }
2189
2190 /* tell client to update an existing BLOB vector property */
IDSetBLOB(const IBLOBVectorProperty * bvp,const char * fmt,...)2191 void IDSetBLOB(const IBLOBVectorProperty *bvp, const char *fmt, ...)
2192 {
2193 int i;
2194
2195 pthread_mutex_lock(&stdout_mutex);
2196
2197 xmlv1();
2198 locale_char_t *orig = indi_locale_C_numeric_push();
2199 printf("<setBLOBVector\n");
2200 printf(" device='%s'\n", bvp->device);
2201 printf(" name='%s'\n", bvp->name);
2202 printf(" state='%s'\n", pstateStr(bvp->s));
2203 printf(" timeout='%g'\n", bvp->timeout);
2204 printf(" timestamp='%s'\n", timestamp());
2205 if (fmt)
2206 {
2207 va_list ap;
2208 va_start(ap, fmt);
2209 printf(" message='");
2210 vprintf(fmt, ap);
2211 printf("'\n");
2212 va_end(ap);
2213 }
2214 printf(">\n");
2215
2216 for (i = 0; i < bvp->nbp; i++)
2217 {
2218 IBLOB *bp = &bvp->bp[i];
2219 unsigned char *encblob;
2220 int l;
2221
2222 printf(" <oneBLOB\n");
2223 printf(" name='%s'\n", bp->name);
2224 printf(" size='%d'\n", bp->size);
2225
2226 // If size is zero, we are only sending a state-change
2227 if (bp->size == 0)
2228 {
2229 printf(" enclen='0'\n");
2230 printf(" format='%s'>\n", bp->format);
2231 }
2232 else
2233 {
2234 encblob = malloc(4 * bp->bloblen / 3 + 4);
2235 l = to64frombits(encblob, bp->blob, bp->bloblen);
2236 printf(" enclen='%d'\n", l);
2237 printf(" format='%s'>\n", bp->format);
2238 size_t written = 0;
2239
2240 while ((int)written < l)
2241 {
2242 size_t towrite = ((l - written) > 72) ? 72 : l - written;
2243 size_t wr = fwrite(encblob + written, 1, towrite, stdout);
2244
2245 if (wr > 0)
2246 written += wr;
2247 if ((written % 72) == 0)
2248 fputc('\n', stdout);
2249 }
2250
2251 if ((written % 72) != 0)
2252 fputc('\n', stdout);
2253
2254 free(encblob);
2255 }
2256
2257 printf(" </oneBLOB>\n");
2258 }
2259
2260 printf("</setBLOBVector>\n");
2261 indi_locale_C_numeric_pop(orig);
2262 fflush(stdout);
2263
2264 pthread_mutex_unlock(&stdout_mutex);
2265 }
2266
2267 /* tell client to update min/max elements of an existing number vector property */
IUUpdateMinMax(const INumberVectorProperty * nvp)2268 void IUUpdateMinMax(const INumberVectorProperty *nvp)
2269 {
2270 int i;
2271
2272 pthread_mutex_lock(&stdout_mutex);
2273 xmlv1();
2274 locale_char_t *orig = indi_locale_C_numeric_push();
2275 printf("<setNumberVector\n");
2276 printf(" device='%s'\n", nvp->device);
2277 printf(" name='%s'\n", nvp->name);
2278 printf(" state='%s'\n", pstateStr(nvp->s));
2279 printf(" timeout='%g'\n", nvp->timeout);
2280 printf(" timestamp='%s'\n", timestamp());
2281 printf(">\n");
2282
2283 for (i = 0; i < nvp->nnp; i++)
2284 {
2285 INumber *np = &nvp->np[i];
2286 printf(" <oneNumber name='%s'\n", np->name);
2287 printf(" min='%g'\n", np->min);
2288 printf(" max='%g'\n", np->max);
2289 printf(" step='%g'\n", np->step);
2290 printf(">\n");
2291 printf(" %g\n", np->value);
2292 printf(" </oneNumber>\n");
2293 }
2294
2295 printf("</setNumberVector>\n");
2296 indi_locale_C_numeric_pop(orig);
2297 fflush(stdout);
2298 pthread_mutex_unlock(&stdout_mutex);
2299 }
2300
IUFindIndex(const char * needle,char ** hay,unsigned int n)2301 int IUFindIndex(const char *needle, char **hay, unsigned int n)
2302 {
2303 int i = 0;
2304
2305 for (i = 0; i < (int)n; i++)
2306 {
2307 if (!strcmp(hay[i], needle))
2308 return i;
2309 }
2310 return -1;
2311 }
2312