1 /*
2  *  Functions for manipulating HTS messages
3  *  Copyright (C) 2007 Andreas Öman
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <assert.h>
20 #include <sys/types.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include "build.h"
27 #include "htsmsg.h"
28 #include "misc/dbl.h"
29 #include "htsmsg_json.h"
30 #include "memoryinfo.h"
31 
32 #if ENABLE_SLOW_MEMORYINFO
33 memoryinfo_t htsmsg_memoryinfo = { .my_name = "htsmsg" };
34 memoryinfo_t htsmsg_field_memoryinfo = { .my_name = "htsmsg field" };
35 #endif
36 
37 static void htsmsg_clear(htsmsg_t *msg);
38 static htsmsg_t *htsmsg_field_get_msg ( htsmsg_field_t *f, int islist );
39 
40 /**
41  *
42  */
43 static void
htsmsg_field_data_destroy(htsmsg_field_t * f)44 htsmsg_field_data_destroy(htsmsg_field_t *f)
45 {
46   switch(f->hmf_type) {
47   case HMF_MAP:
48   case HMF_LIST:
49     htsmsg_clear(&f->hmf_msg);
50     break;
51 
52   case HMF_STR:
53     if(f->hmf_flags & HMF_ALLOCED) {
54 #if ENABLE_SLOW_MEMORYINFO
55       memoryinfo_remove(&htsmsg_field_memoryinfo, strlen(f->hmf_str) + 1);
56 #endif
57       free((void *)f->hmf_str);
58     }
59     break;
60 
61   case HMF_BIN:
62     if(f->hmf_flags & HMF_ALLOCED) {
63 #if ENABLE_SLOW_MEMORYINFO
64       memoryinfo_remove(&htsmsg_field_memoryinfo, f->hmf_binsize);
65 #endif
66       free((void *)f->hmf_bin);
67     }
68     break;
69   default:
70     break;
71   }
72   f->hmf_flags &= ~HMF_ALLOCED;
73 }
74 
75 /**
76  *
77  */
78 void
htsmsg_field_destroy(htsmsg_t * msg,htsmsg_field_t * f)79 htsmsg_field_destroy(htsmsg_t *msg, htsmsg_field_t *f)
80 {
81 #if ENABLE_SLOW_MEMORYINFO
82   size_t asize = 0;
83 #endif
84 
85   TAILQ_REMOVE(&msg->hm_fields, f, hmf_link);
86 
87   htsmsg_field_data_destroy(f);
88 
89   if (f->hmf_flags & HMF_NAME_ALLOCED) {
90 #if ENABLE_SLOW_MEMORYINFO
91     asize += strlen(f->hmf_name);
92 #endif
93     free((void *)f->hmf_name);
94   }
95 #if ENABLE_SLOW_MEMORYINFO
96   memoryinfo_free(&htsmsg_field_memoryinfo,
97                   sizeof(*f) + f->hmf_edata_size + asize);
98 #endif
99   free(f);
100 }
101 
102 /*
103  *
104  */
105 static void
htsmsg_clear(htsmsg_t * msg)106 htsmsg_clear(htsmsg_t *msg)
107 {
108   htsmsg_field_t *f;
109 
110   while((f = TAILQ_FIRST(&msg->hm_fields)) != NULL)
111     htsmsg_field_destroy(msg, f);
112 }
113 
114 
115 /*
116  *
117  */
118 htsmsg_field_t *
htsmsg_field_add(htsmsg_t * msg,const char * name,int type,int flags,size_t esize)119 htsmsg_field_add(htsmsg_t *msg, const char *name, int type, int flags, size_t esize)
120 {
121   size_t nsize = 0;
122   htsmsg_field_t *f;
123 #if ENABLE_SLOW_MEMORYINFO
124   size_t asize = 0;
125 #endif
126 
127   if((flags & HMF_NAME_INALLOCED) && name)
128     nsize = strlen(name) + 1;
129   f = malloc(sizeof(htsmsg_field_t) + nsize + esize);
130   if(f == NULL)
131     return NULL;
132   TAILQ_INSERT_TAIL(&msg->hm_fields, f, hmf_link);
133 
134   if(msg->hm_islist) {
135     assert(name == NULL);
136   } else {
137     assert(name != NULL);
138   }
139 
140   if(flags & HMF_NAME_INALLOCED) {
141     if (name) {
142       f->hmf_name = f->hmf_edata;
143       strcpy(f->hmf_edata, name);
144     } else {
145       f->hmf_name = NULL;
146     }
147   } else if(flags & HMF_NAME_ALLOCED) {
148     f->hmf_name = name ? strdup(name) : NULL;
149 #if ENABLE_SLOW_MEMORYINFO
150     asize = name ? strlen(name) + 1 : 0;
151 #endif
152   } else {
153     f->hmf_name = name;
154   }
155 
156   if(esize) {
157     if(type == HMF_STR)
158       f->hmf_str = f->hmf_edata + nsize;
159     else if(type == HMF_BIN)
160       f->hmf_bin = f->hmf_edata + nsize;
161   }
162 
163   f->hmf_type = type;
164   f->hmf_flags = flags;
165 #if ENABLE_SLOW_MEMORYINFO
166   f->hmf_edata_size = nsize + esize;
167   memoryinfo_alloc(&htsmsg_field_memoryinfo,
168                    sizeof(htsmsg_field_t) + f->hmf_edata_size + asize);
169 #endif
170   return f;
171 }
172 
173 
174 /*
175  *
176  */
177 htsmsg_field_t *
htsmsg_field_find(htsmsg_t * msg,const char * name)178 htsmsg_field_find(htsmsg_t *msg, const char *name)
179 {
180   htsmsg_field_t *f;
181 
182   if (msg == NULL || name == NULL)
183     return NULL;
184   TAILQ_FOREACH(f, &msg->hm_fields, hmf_link) {
185     if(f->hmf_name != NULL && !strcmp(f->hmf_name, name))
186       return f;
187   }
188   return NULL;
189 }
190 
191 
192 /*
193  *
194  */
195 htsmsg_field_t *
htsmsg_field_last(htsmsg_t * msg)196 htsmsg_field_last(htsmsg_t *msg)
197 {
198   if (msg == NULL)
199     return NULL;
200   return TAILQ_LAST(&msg->hm_fields, htsmsg_field_queue);
201 }
202 
203 
204 /**
205  *
206  */
207 int
htsmsg_delete_field(htsmsg_t * msg,const char * name)208 htsmsg_delete_field(htsmsg_t *msg, const char *name)
209 {
210   htsmsg_field_t *f;
211 
212   if((f = htsmsg_field_find(msg, name)) == NULL)
213     return HTSMSG_ERR_FIELD_NOT_FOUND;
214   htsmsg_field_destroy(msg, f);
215   return 0;
216 }
217 
218 
219 /**
220  *
221  */
222 int
htsmsg_is_empty(htsmsg_t * msg)223 htsmsg_is_empty(htsmsg_t *msg)
224 {
225   if (msg == NULL)
226     return 1;
227 
228   assert(msg->hm_data == NULL);
229   return TAILQ_EMPTY(&msg->hm_fields);
230 }
231 
232 
233 /*
234  *
235  */
236 htsmsg_t *
htsmsg_create_map(void)237 htsmsg_create_map(void)
238 {
239   htsmsg_t *msg;
240 
241   msg = malloc(sizeof(htsmsg_t));
242   if (msg) {
243     TAILQ_INIT(&msg->hm_fields);
244     msg->hm_data = NULL;
245     msg->hm_data_size = 0;
246     msg->hm_islist = 0;
247 #if ENABLE_SLOW_MEMORYINFO
248     memoryinfo_alloc(&htsmsg_memoryinfo, sizeof(htsmsg_t));
249 #endif
250   }
251   return msg;
252 }
253 
254 /*
255  *
256  */
257 htsmsg_t *
htsmsg_create_list(void)258 htsmsg_create_list(void)
259 {
260   htsmsg_t *msg;
261 
262   msg = malloc(sizeof(htsmsg_t));
263   if (msg) {
264     TAILQ_INIT(&msg->hm_fields);
265     msg->hm_data = NULL;
266     msg->hm_data_size = 0;
267     msg->hm_islist = 1;
268 #if ENABLE_SLOW_MEMORYINFO
269     memoryinfo_alloc(&htsmsg_memoryinfo, sizeof(htsmsg_t));
270 #endif
271   }
272   return msg;
273 }
274 
275 
276 /*
277  *
278  */
279 void
htsmsg_destroy(htsmsg_t * msg)280 htsmsg_destroy(htsmsg_t *msg)
281 {
282 #if ENABLE_SLOW_MEMORYINFO
283   size_t size = 0;
284 #endif
285 
286   if(msg == NULL)
287     return;
288 
289   htsmsg_clear(msg);
290   if (msg->hm_data) {
291     free((void *)msg->hm_data);
292 #if ENABLE_SLOW_MEMORYINFO
293     size += msg->hm_data_size;
294 #endif
295   }
296 #if ENABLE_SLOW_MEMORYINFO
297   memoryinfo_free(&htsmsg_memoryinfo, sizeof(htsmsg_t) + size);
298 #endif
299   free(msg);
300 }
301 
302 /*
303  *
304  */
305 void
htsmsg_add_bool(htsmsg_t * msg,const char * name,int b)306 htsmsg_add_bool(htsmsg_t *msg, const char *name, int b)
307 {
308   htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_BOOL, HMF_NAME_INALLOCED, 0);
309   f->hmf_bool = !!b;
310 }
311 
312 /*
313  *
314  */
315 void
htsmsg_set_bool(htsmsg_t * msg,const char * name,int b)316 htsmsg_set_bool(htsmsg_t *msg, const char *name, int b)
317 {
318   htsmsg_field_t *f = htsmsg_field_find(msg, name);
319   if (!f)
320     f = htsmsg_field_add(msg, name, HMF_BOOL, HMF_NAME_INALLOCED, 0);
321   f->hmf_bool = !!b;
322 }
323 
324 /*
325  *
326  */
327 void
htsmsg_add_s64(htsmsg_t * msg,const char * name,int64_t s64)328 htsmsg_add_s64(htsmsg_t *msg, const char *name, int64_t s64)
329 {
330   htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_S64, HMF_NAME_INALLOCED, 0);
331   f->hmf_s64 = s64;
332 }
333 
334 /*
335  *
336  */
337 int
htsmsg_set_s64(htsmsg_t * msg,const char * name,int64_t s64)338 htsmsg_set_s64(htsmsg_t *msg, const char *name, int64_t s64)
339 {
340   htsmsg_field_t *f = htsmsg_field_find(msg, name);
341   if (!f)
342     f = htsmsg_field_add(msg, name, HMF_S64, HMF_NAME_INALLOCED, 0);
343   if (f->hmf_type != HMF_S64)
344     return 1;
345   f->hmf_s64 = s64;
346   return 0;
347 }
348 
349 
350 /*
351  *
352  */
353 void
htsmsg_add_dbl(htsmsg_t * msg,const char * name,double dbl)354 htsmsg_add_dbl(htsmsg_t *msg, const char *name, double dbl)
355 {
356   htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_DBL, HMF_NAME_INALLOCED, 0);
357   f->hmf_dbl = dbl;
358 }
359 
360 
361 
362 /*
363  *
364  */
365 void
htsmsg_add_str(htsmsg_t * msg,const char * name,const char * str)366 htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str)
367 {
368   htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_STR, HMF_NAME_INALLOCED,
369                                        strlen(str) + 1);
370   strcpy((char *)f->hmf_str, str);
371   f->hmf_flags |= HMF_INALLOCED;
372 }
373 
374 /*
375  *
376  */
377 void
htsmsg_add_str2(htsmsg_t * msg,const char * name,const char * str)378 htsmsg_add_str2(htsmsg_t *msg, const char *name, const char *str)
379 {
380   if (msg && name && str)
381     htsmsg_add_str(msg, name, str);
382 }
383 
384 /*
385  *
386  */
387 void
htsmsg_add_str_exclusive(htsmsg_t * msg,const char * str)388 htsmsg_add_str_exclusive(htsmsg_t *msg, const char *str)
389 {
390   htsmsg_field_t *f;
391 
392   assert(msg->hm_islist);
393 
394   TAILQ_FOREACH(f, &msg->hm_fields, hmf_link) {
395     assert(f->hmf_type == HMF_STR);
396     if (strcmp(f->hmf_str, str) == 0)
397       return;
398   }
399 
400   htsmsg_add_str(msg, NULL, str);
401 }
402 
403 /*
404  *
405  */
406 int
htsmsg_field_set_str(htsmsg_field_t * f,const char * str)407 htsmsg_field_set_str(htsmsg_field_t *f, const char *str)
408 {
409   if (f->hmf_type != HMF_STR)
410     return 1;
411   if (f->hmf_flags & HMF_ALLOCED) {
412 #if ENABLE_SLOW_MEMORYINFO
413     memoryinfo_remove(&htsmsg_field_memoryinfo, strlen(f->hmf_str) + 1);
414 #endif
415     free((void *)f->hmf_str);
416   }
417   else if (f->hmf_flags & HMF_INALLOCED) {
418     if (strlen(f->hmf_str) >= strlen(str)) {
419       strcpy((char *)f->hmf_str, str);
420       return 0;
421     }
422     f->hmf_flags &= ~HMF_INALLOCED;
423   }
424   f->hmf_flags |= HMF_ALLOCED;
425   f->hmf_str = strdup(str);
426 #if ENABLE_SLOW_MEMORYINFO
427   memoryinfo_alloc(&htsmsg_field_memoryinfo, strlen(str) + 1);
428 #endif
429   return 0;
430 }
431 
432 /*
433  *
434  */
435 int
htsmsg_field_set_str_force(htsmsg_field_t * f,const char * str)436 htsmsg_field_set_str_force(htsmsg_field_t *f, const char *str)
437 {
438   if (f->hmf_type != HMF_STR) {
439     htsmsg_field_data_destroy(f);
440     f->hmf_type = HMF_STR;
441     f->hmf_str = "";
442   }
443   return htsmsg_field_set_str(f, str);
444 }
445 
446 /*
447  *
448  */
449 int
htsmsg_set_str(htsmsg_t * msg,const char * name,const char * str)450 htsmsg_set_str(htsmsg_t *msg, const char *name, const char *str)
451 {
452   htsmsg_field_t *f = htsmsg_field_find(msg, name);
453   if (!f) {
454     htsmsg_add_str(msg, name, str);
455     return 0;
456   }
457   return htsmsg_field_set_str(f, str);
458 }
459 
460 /*
461  *
462  */
463 void
htsmsg_add_bin(htsmsg_t * msg,const char * name,const void * bin,size_t len)464 htsmsg_add_bin(htsmsg_t *msg, const char *name, const void *bin, size_t len)
465 {
466   htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_BIN, HMF_NAME_INALLOCED, len);
467   f->hmf_bin = f->hmf_str;
468   f->hmf_binsize = len;
469   f->hmf_flags |= HMF_INALLOCED;
470   memcpy((void *)f->hmf_bin, bin, len);
471 }
472 
473 /*
474  *
475  */
476 void
htsmsg_add_binptr(htsmsg_t * msg,const char * name,const void * bin,size_t len)477 htsmsg_add_binptr(htsmsg_t *msg, const char *name, const void *bin, size_t len)
478 {
479   htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_BIN, HMF_NAME_INALLOCED, 0);
480   f->hmf_bin = bin;
481   f->hmf_binsize = len;
482 }
483 
484 
485 /*
486  *
487  */
488 static htsmsg_t *
htsmsg_field_set_msg(htsmsg_field_t * f,htsmsg_t * sub)489 htsmsg_field_set_msg(htsmsg_field_t *f, htsmsg_t *sub)
490 {
491   assert(sub->hm_data == NULL);
492   f->hmf_msg.hm_data = NULL;
493   f->hmf_msg.hm_data_size = 0;
494   f->hmf_msg.hm_islist = sub->hm_islist;
495   TAILQ_MOVE(&f->hmf_msg.hm_fields, &sub->hm_fields, hmf_link);
496 #if ENABLE_SLOW_MEMORYINFO
497   memoryinfo_free(&htsmsg_memoryinfo, sizeof(htsmsg_t));
498 #endif
499   free(sub);
500 
501   if (f->hmf_type == (f->hmf_msg.hm_islist ? HMF_LIST : HMF_MAP))
502     return &f->hmf_msg;
503 
504   return NULL;
505 }
506 
507 /*
508  *
509  */
510 htsmsg_t *
htsmsg_add_msg(htsmsg_t * msg,const char * name,htsmsg_t * sub)511 htsmsg_add_msg(htsmsg_t *msg, const char *name, htsmsg_t *sub)
512 {
513   htsmsg_field_t *f;
514 
515   f = htsmsg_field_add(msg, name, sub->hm_islist ? HMF_LIST : HMF_MAP,
516 		       HMF_NAME_INALLOCED, 0);
517   return htsmsg_field_set_msg(f, sub);
518 }
519 
520 /*
521  *
522  */
523 htsmsg_t *
htsmsg_set_msg(htsmsg_t * msg,const char * name,htsmsg_t * sub)524 htsmsg_set_msg(htsmsg_t *msg, const char *name, htsmsg_t *sub)
525 {
526   htsmsg_field_t *f = htsmsg_field_find(msg, name);
527   if (!f)
528     return htsmsg_add_msg(msg, name, sub);
529   htsmsg_field_data_destroy(f);
530   return htsmsg_field_set_msg(f, sub);
531 }
532 
533 
534 
535 /*
536  *
537  */
538 void
htsmsg_add_msg_extname(htsmsg_t * msg,const char * name,htsmsg_t * sub)539 htsmsg_add_msg_extname(htsmsg_t *msg, const char *name, htsmsg_t *sub)
540 {
541   htsmsg_field_t *f;
542 
543   f = htsmsg_field_add(msg, name, sub->hm_islist ? HMF_LIST : HMF_MAP, 0, 0);
544 
545   assert(sub->hm_data == NULL);
546   f->hmf_msg.hm_data = NULL;
547   f->hmf_msg.hm_data_size = 0;
548   TAILQ_MOVE(&f->hmf_msg.hm_fields, &sub->hm_fields, hmf_link);
549   f->hmf_msg.hm_islist = sub->hm_islist;
550 #if ENABLE_SLOW_MEMORYINFO
551   memoryinfo_free(&htsmsg_memoryinfo, sizeof(htsmsg_t));
552 #endif
553   free(sub);
554 }
555 
556 
557 
558 /**
559  *
560  */
561 int
htsmsg_get_s64(htsmsg_t * msg,const char * name,int64_t * s64p)562 htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p)
563 {
564   htsmsg_field_t *f;
565 
566   if((f = htsmsg_field_find(msg, name)) == NULL)
567     return HTSMSG_ERR_FIELD_NOT_FOUND;
568 
569   return htsmsg_field_get_s64(f, s64p);
570 }
571 
572 int
htsmsg_field_get_s64(htsmsg_field_t * f,int64_t * s64p)573 htsmsg_field_get_s64
574   (htsmsg_field_t *f, int64_t *s64p)
575 {
576   switch(f->hmf_type) {
577   default:
578     return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
579   case HMF_STR:
580     *s64p = strtoll(f->hmf_str, NULL, 0);
581     break;
582   case HMF_S64:
583     *s64p = f->hmf_s64;
584     break;
585   case HMF_BOOL:
586     *s64p = f->hmf_bool;
587     break;
588   case HMF_DBL:
589     *s64p = f->hmf_dbl;
590     break;
591   }
592   return 0;
593 }
594 
595 
596 /**
597  *
598  */
599 
600 int
bool_check(const char * str)601 bool_check(const char *str)
602 {
603   if (str &&
604       (!strcmp(str, "yes")  ||
605        !strcmp(str, "true") ||
606        !strcmp(str, "on") ||
607        !strcmp(str, "1")))
608     return 1;
609   return 0;
610 }
611 
612 int
htsmsg_field_get_bool(htsmsg_field_t * f,int * boolp)613 htsmsg_field_get_bool
614   ( htsmsg_field_t *f, int *boolp )
615 {
616   switch(f->hmf_type) {
617   default:
618     return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
619   case HMF_STR:
620     *boolp = bool_check(f->hmf_str);
621     break;
622   case HMF_S64:
623     *boolp = f->hmf_s64 ? 1 : 0;
624     break;
625   case HMF_BOOL:
626     *boolp = f->hmf_bool;
627     break;
628   }
629   return 0;
630 }
631 
632 int
htsmsg_get_bool(htsmsg_t * msg,const char * name,int * boolp)633 htsmsg_get_bool
634   (htsmsg_t *msg, const char *name, int *boolp)
635 {
636   htsmsg_field_t *f;
637 
638   if((f = htsmsg_field_find(msg, name)) == NULL)
639     return HTSMSG_ERR_FIELD_NOT_FOUND;
640 
641   return htsmsg_field_get_bool(f, boolp);
642 }
643 
644 int
htsmsg_get_bool_or_default(htsmsg_t * msg,const char * name,int def)645 htsmsg_get_bool_or_default(htsmsg_t *msg, const char *name, int def)
646 {
647   int ret;
648   return htsmsg_get_bool(msg, name, &ret) ? def : ret;
649 }
650 
651 
652 /**
653  *
654  */
655 int64_t
htsmsg_get_s64_or_default(htsmsg_t * msg,const char * name,int64_t def)656 htsmsg_get_s64_or_default(htsmsg_t *msg, const char *name, int64_t def)
657 {
658   int64_t s64;
659   return htsmsg_get_s64(msg, name, &s64) ? def : s64;
660 }
661 
662 
663 /*
664  *
665  */
666 int
htsmsg_get_u32(htsmsg_t * msg,const char * name,uint32_t * u32p)667 htsmsg_get_u32(htsmsg_t *msg, const char *name, uint32_t *u32p)
668 {
669   int r;
670   int64_t s64;
671 
672   if((r = htsmsg_get_s64(msg, name, &s64)) != 0)
673     return r;
674 
675   if(s64 < 0 || s64 > 0xffffffffLL)
676     return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
677 
678   *u32p = s64;
679   return 0;
680 }
681 
682 int
htsmsg_field_get_u32(htsmsg_field_t * f,uint32_t * u32p)683 htsmsg_field_get_u32(htsmsg_field_t *f, uint32_t *u32p)
684 {
685   int r;
686   int64_t s64;
687 
688   if ((r = htsmsg_field_get_s64(f, &s64)))
689     return r;
690 
691   if (s64 < 0 || s64 > 0xffffffffL)
692       return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
693 
694   *u32p = s64;
695   return 0;
696 }
697 
698 /**
699  *
700  */
701 int
htsmsg_get_u32_or_default(htsmsg_t * msg,const char * name,uint32_t def)702 htsmsg_get_u32_or_default(htsmsg_t *msg, const char *name, uint32_t def)
703 {
704   uint32_t u32;
705   return htsmsg_get_u32(msg, name, &u32) ? def : u32;
706 }
707 
708 
709 /**
710  *
711  */
712 int32_t
htsmsg_get_s32_or_default(htsmsg_t * msg,const char * name,int32_t def)713 htsmsg_get_s32_or_default(htsmsg_t *msg, const char *name, int32_t def)
714 {
715   int32_t s32;
716   return htsmsg_get_s32(msg, name, &s32) ? def : s32;
717 }
718 
719 
720 
721 /*
722  *
723  */
724 int
htsmsg_get_s32(htsmsg_t * msg,const char * name,int32_t * s32p)725 htsmsg_get_s32(htsmsg_t *msg, const char *name, int32_t *s32p)
726 {
727   int r;
728   int64_t s64;
729 
730   if((r = htsmsg_get_s64(msg, name, &s64)) != 0)
731     return r;
732 
733   if(s64 < -0x80000000LL || s64 > 0x7fffffffLL)
734     return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
735 
736   *s32p = s64;
737   return 0;
738 }
739 
740 
741 /*
742  *
743  */
744 int
htsmsg_get_dbl(htsmsg_t * msg,const char * name,double * dblp)745 htsmsg_get_dbl(htsmsg_t *msg, const char *name, double *dblp)
746 {
747   htsmsg_field_t *f;
748 
749   if((f = htsmsg_field_find(msg, name)) == NULL)
750     return HTSMSG_ERR_FIELD_NOT_FOUND;
751 
752   return htsmsg_field_get_dbl(f, dblp);
753 }
754 
755 int
htsmsg_field_get_dbl(htsmsg_field_t * f,double * dblp)756 htsmsg_field_get_dbl
757   ( htsmsg_field_t *f, double *dblp )
758 {
759   switch (f->hmf_type) {
760     case HMF_S64:
761       *dblp = (double)f->hmf_s64;
762       break;
763     case HMF_DBL:
764       *dblp = f->hmf_dbl;
765       break;
766     case HMF_STR:
767       *dblp = my_str2double(f->hmf_str, NULL);
768       // TODO: better safety checks?
769       break;
770     default:
771       return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
772   }
773   return 0;
774 }
775 
776 /*
777  *
778  */
779 int
htsmsg_get_bin(htsmsg_t * msg,const char * name,const void ** binp,size_t * lenp)780 htsmsg_get_bin(htsmsg_t *msg, const char *name, const void **binp,
781 	       size_t *lenp)
782 {
783   htsmsg_field_t *f;
784 
785   if((f = htsmsg_field_find(msg, name)) == NULL)
786     return HTSMSG_ERR_FIELD_NOT_FOUND;
787 
788   if(f->hmf_type != HMF_BIN)
789     return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
790 
791   *binp = f->hmf_bin;
792   *lenp = f->hmf_binsize;
793   return 0;
794 }
795 
796 /**
797  *
798  */
799 const char *
htsmsg_field_get_string(htsmsg_field_t * f)800 htsmsg_field_get_string(htsmsg_field_t *f)
801 {
802   char buf[128];
803 
804   switch(f->hmf_type) {
805   default:
806     return NULL;
807   case HMF_STR:
808     break;
809   case HMF_BOOL:
810     htsmsg_field_set_str_force(f, f->hmf_bool ? "true" : "false");
811     break;
812   case HMF_S64:
813     snprintf(buf, sizeof(buf), "%"PRId64, f->hmf_s64);
814     htsmsg_field_set_str_force(f, buf);
815     break;
816   case HMF_DBL:
817     snprintf(buf, sizeof(buf), "%lf", f->hmf_dbl);
818     htsmsg_field_set_str_force(f, buf);
819     break;
820   }
821   return f->hmf_str;
822 }
823 
824 /*
825  *
826  */
827 const char *
htsmsg_get_str(htsmsg_t * msg,const char * name)828 htsmsg_get_str(htsmsg_t *msg, const char *name)
829 {
830   htsmsg_field_t *f;
831 
832   if((f = htsmsg_field_find(msg, name)) == NULL)
833     return NULL;
834   return htsmsg_field_get_string(f);
835 
836 }
837 
838 /*
839  *
840  */
841 htsmsg_t *
htsmsg_get_map(htsmsg_t * msg,const char * name)842 htsmsg_get_map(htsmsg_t *msg, const char *name)
843 {
844   htsmsg_field_t *f;
845 
846   if((f = htsmsg_field_find(msg, name)) == NULL)
847     return NULL;
848 
849   return htsmsg_field_get_map(f);
850 }
851 
852 htsmsg_t *
htsmsg_field_get_map(htsmsg_field_t * f)853 htsmsg_field_get_map(htsmsg_field_t *f)
854 {
855   return htsmsg_field_get_msg(f, 0);
856 }
857 
858 /**
859  *
860  */
861 htsmsg_t *
htsmsg_get_map_multi(htsmsg_t * msg,...)862 htsmsg_get_map_multi(htsmsg_t *msg, ...)
863 {
864   va_list ap;
865   const char *n;
866 
867   va_start(ap, msg);
868   while(msg != NULL && (n = va_arg(ap, char *)) != NULL)
869     msg = htsmsg_get_map(msg, n);
870   va_end(ap);
871 
872   return msg;
873 }
874 
875 /**
876  *
877  */
878 const char *
htsmsg_get_str_multi(htsmsg_t * msg,...)879 htsmsg_get_str_multi(htsmsg_t *msg, ...)
880 {
881   va_list ap;
882   const char *n, *r = NULL;
883   htsmsg_field_t *f;
884 
885   va_start(ap, msg);
886   while((n = va_arg(ap, char *)) != NULL) {
887     if((f = htsmsg_field_find(msg, n)) == NULL)
888       break;
889     else if(f->hmf_type == HMF_STR) {
890       r = f->hmf_str;
891       break;
892     } else if(f->hmf_type == HMF_MAP)
893       msg = &f->hmf_msg;
894     else
895       break;
896   }
897   va_end(ap);
898 
899   return r;
900 }
901 
902 
903 
904 /*
905  *
906  */
907 htsmsg_t *
htsmsg_get_list(htsmsg_t * msg,const char * name)908 htsmsg_get_list(htsmsg_t *msg, const char *name)
909 {
910   htsmsg_field_t *f;
911 
912   if((f = htsmsg_field_find(msg, name)) == NULL)
913     return NULL;
914 
915   return htsmsg_field_get_list(f);
916 }
917 
918 htsmsg_t *
htsmsg_field_get_list(htsmsg_field_t * f)919 htsmsg_field_get_list ( htsmsg_field_t *f )
920 {
921   return htsmsg_field_get_msg(f, 1);
922 }
923 
924 static htsmsg_t *
htsmsg_field_get_msg(htsmsg_field_t * f,int islist)925 htsmsg_field_get_msg ( htsmsg_field_t *f, int islist )
926 {
927   htsmsg_t *m;
928 
929   /* Deserialize JSON (will keep either list or map) */
930   if (f->hmf_type == HMF_STR) {
931     if ((m = htsmsg_json_deserialize(f->hmf_str))) {
932       if (f->hmf_flags & HMF_ALLOCED) {
933 #if ENABLE_SLOW_MEMORYINFO
934         memoryinfo_free(&htsmsg_field_memoryinfo, strlen(f->hmf_str) + 1);
935 #endif
936         free((void*)f->hmf_str);
937       }
938       f->hmf_type          = m->hm_islist ? HMF_LIST : HMF_MAP;
939       f->hmf_msg.hm_islist = m->hm_islist;
940       f->hmf_msg.hm_data   = NULL;
941       f->hmf_msg.hm_data_size = 0;
942       TAILQ_MOVE(&f->hmf_msg.hm_fields, &m->hm_fields, hmf_link);
943 #if ENABLE_SLOW_MEMORYINFO
944       memoryinfo_free(&htsmsg_memoryinfo, sizeof(htsmsg_t));
945 #endif
946       free(m);
947     }
948   }
949 
950   if (f->hmf_type == (islist ? HMF_LIST : HMF_MAP))
951     return &f->hmf_msg;
952 
953   return NULL;
954 }
955 
956 /**
957  *
958  */
959 htsmsg_t *
htsmsg_detach_submsg(htsmsg_field_t * f)960 htsmsg_detach_submsg(htsmsg_field_t *f)
961 {
962   htsmsg_t *r = htsmsg_create_map();
963 
964   TAILQ_MOVE(&r->hm_fields, &f->hmf_msg.hm_fields, hmf_link);
965   TAILQ_INIT(&f->hmf_msg.hm_fields);
966   r->hm_islist = f->hmf_type == HMF_LIST;
967   return r;
968 }
969 
970 
971 /*
972  *
973  */
974 static void
htsmsg_print0(htsmsg_t * msg,int indent)975 htsmsg_print0(htsmsg_t *msg, int indent)
976 {
977   htsmsg_field_t *f;
978   int i;
979 
980   TAILQ_FOREACH(f, &msg->hm_fields, hmf_link) {
981 
982     for(i = 0; i < indent; i++) printf("\t");
983 
984     printf("%s (", f->hmf_name ?: "");
985 
986     switch(f->hmf_type) {
987 
988     case HMF_MAP:
989       printf("MAP) = {\n");
990       htsmsg_print0(&f->hmf_msg, indent + 1);
991       for(i = 0; i < indent; i++) printf("\t");
992       printf("}\n");
993       break;
994 
995     case HMF_LIST:
996       printf("LIST) = {\n");
997       htsmsg_print0(&f->hmf_msg, indent + 1);
998       for(i = 0; i < indent; i++) printf("\t");
999       printf("}\n");
1000       break;
1001 
1002     case HMF_STR:
1003       printf("STR) = \"%s\"\n", f->hmf_str);
1004       break;
1005 
1006     case HMF_BIN:
1007       printf("BIN) = [");
1008       for(i = 0; i < f->hmf_binsize - 1; i++)
1009 	printf("%02x.", ((uint8_t *)f->hmf_bin)[i]);
1010       printf("%02x]\n", ((uint8_t *)f->hmf_bin)[i]);
1011       break;
1012 
1013     case HMF_S64:
1014       printf("S64) = %" PRId64 "\n", f->hmf_s64);
1015       break;
1016 
1017     case HMF_BOOL:
1018       printf("BOOL) = %s\n", f->hmf_bool ? "true" : "false");
1019       break;
1020 
1021     case HMF_DBL:
1022       printf("DBL) = %f\n", f->hmf_dbl);
1023       break;
1024     }
1025   }
1026 }
1027 
1028 /*
1029  *
1030  */
1031 void
htsmsg_print(htsmsg_t * msg)1032 htsmsg_print(htsmsg_t *msg)
1033 {
1034   htsmsg_print0(msg, 0);
1035 }
1036 
1037 
1038 /**
1039  *
1040  */
1041 static void
htsmsg_copy_i(htsmsg_t * src,htsmsg_t * dst)1042 htsmsg_copy_i(htsmsg_t *src, htsmsg_t *dst)
1043 {
1044   htsmsg_field_t *f;
1045   htsmsg_t *sub;
1046 
1047   TAILQ_FOREACH(f, &src->hm_fields, hmf_link) {
1048 
1049     switch(f->hmf_type) {
1050 
1051     case HMF_MAP:
1052     case HMF_LIST:
1053       sub = f->hmf_type == HMF_LIST ?
1054 	htsmsg_create_list() : htsmsg_create_map();
1055       htsmsg_copy_i(&f->hmf_msg, sub);
1056       htsmsg_add_msg(dst, f->hmf_name, sub);
1057       break;
1058 
1059     case HMF_STR:
1060       htsmsg_add_str(dst, f->hmf_name, f->hmf_str);
1061       break;
1062 
1063     case HMF_S64:
1064       htsmsg_add_s64(dst, f->hmf_name, f->hmf_s64);
1065       break;
1066 
1067     case HMF_BOOL:
1068       htsmsg_add_bool(dst, f->hmf_name, f->hmf_bool);
1069       break;
1070 
1071     case HMF_BIN:
1072       htsmsg_add_bin(dst, f->hmf_name, f->hmf_bin, f->hmf_binsize);
1073       break;
1074 
1075     case HMF_DBL:
1076       htsmsg_add_dbl(dst, f->hmf_name, f->hmf_dbl);
1077       break;
1078     }
1079   }
1080 }
1081 
1082 htsmsg_t *
htsmsg_copy(htsmsg_t * src)1083 htsmsg_copy(htsmsg_t *src)
1084 {
1085   htsmsg_t *dst;
1086   if (src == NULL) return NULL;
1087   dst = src->hm_islist ? htsmsg_create_list() : htsmsg_create_map();
1088   htsmsg_copy_i(src, dst);
1089   return dst;
1090 }
1091 
1092 /**
1093  *
1094  */
1095 int
htsmsg_cmp(htsmsg_t * m1,htsmsg_t * m2)1096 htsmsg_cmp(htsmsg_t *m1, htsmsg_t *m2)
1097 {
1098   htsmsg_field_t *f1, *f2;
1099 
1100   if (m1 == NULL && m2 == NULL)
1101     return 0;
1102   if (m1 == NULL || m2 == NULL)
1103     return 1;
1104 
1105   f2 = TAILQ_FIRST(&m2->hm_fields);
1106   TAILQ_FOREACH(f1, &m1->hm_fields, hmf_link) {
1107 
1108     if (f2 == NULL)
1109       return 1;
1110 
1111     if (f1->hmf_type != f2->hmf_type)
1112       return 1;
1113     if (strcmp(f1->hmf_name ?: "", f2->hmf_name ?: ""))
1114       return 1;
1115 
1116     switch(f1->hmf_type) {
1117 
1118     case HMF_MAP:
1119     case HMF_LIST:
1120       if (htsmsg_cmp(&f1->hmf_msg, &f2->hmf_msg))
1121         return 1;
1122       break;
1123 
1124     case HMF_STR:
1125       if (strcmp(f1->hmf_str, f2->hmf_str))
1126         return 1;
1127       break;
1128 
1129     case HMF_S64:
1130       if (f1->hmf_s64 != f2->hmf_s64)
1131         return 1;
1132       break;
1133 
1134     case HMF_BOOL:
1135       if (f1->hmf_bool != f2->hmf_bool)
1136         return 1;
1137       break;
1138 
1139     case HMF_BIN:
1140       if (f1->hmf_binsize != f2->hmf_binsize)
1141         return 1;
1142       if (memcmp(f1->hmf_bin, f2->hmf_bin, f1->hmf_binsize))
1143         return 1;
1144       break;
1145 
1146     case HMF_DBL:
1147       if (f1->hmf_dbl != f2->hmf_dbl)
1148         return 1;
1149       break;
1150     }
1151 
1152     f2 = TAILQ_NEXT(f2, hmf_link);
1153   }
1154 
1155   if (f2)
1156     return 1;
1157   return 0;
1158 }
1159 
1160 /**
1161  *
1162  */
1163 htsmsg_t *
htsmsg_get_map_in_list(htsmsg_t * m,int num)1164 htsmsg_get_map_in_list(htsmsg_t *m, int num)
1165 {
1166   htsmsg_field_t *f;
1167 
1168   HTSMSG_FOREACH(f, m) {
1169     if(!--num)
1170       return htsmsg_get_map_by_field(f);
1171   }
1172   return NULL;
1173 }
1174 
1175 
1176 /**
1177  *
1178  */
1179 htsmsg_t *
htsmsg_get_map_by_field_if_name(htsmsg_field_t * f,const char * name)1180 htsmsg_get_map_by_field_if_name(htsmsg_field_t *f, const char *name)
1181 {
1182   if(f->hmf_type != HMF_MAP)
1183     return NULL;
1184   if(strcmp(f->hmf_name, name))
1185     return NULL;
1186   return &f->hmf_msg;
1187 }
1188 
1189 
1190 /**
1191  *
1192  */
1193 const char *
htsmsg_get_cdata(htsmsg_t * m,const char * field)1194 htsmsg_get_cdata(htsmsg_t *m, const char *field)
1195 {
1196   return htsmsg_get_str_multi(m, field, "cdata", NULL);
1197 }
1198 
1199 /**
1200  * Convert list to CSV string
1201  *
1202  * Note: this will NOT work for lists of complex types
1203  */
1204 char *
htsmsg_list_2_csv(htsmsg_t * m,char delim,int human)1205 htsmsg_list_2_csv(htsmsg_t *m, char delim, int human)
1206 {
1207   int alloc, used, first = 1;
1208   char *ret;
1209   htsmsg_field_t *f;
1210   char sep[3];
1211   const char *ssep;
1212   if (!m->hm_islist) return NULL;
1213 
1214 #define REALLOC(l)\
1215   if ((alloc - used) < l) {\
1216     alloc = MAX((l)*2, alloc*2);\
1217     ret   = realloc(ret, alloc);\
1218   }\
1219 
1220   ret  = malloc(alloc = 512);
1221   *ret = 0;
1222   used = 0;
1223   if (human) {
1224     sep[0] = delim;
1225     if (human & 2) {
1226       sep[1] = '\0';
1227     } else {
1228       sep[1] = ' ';
1229       sep[2] = '\0';
1230     }
1231     ssep = "";
1232   } else {
1233     sep[0] = delim;
1234     sep[1] = '\0';
1235     ssep = "\"";
1236   }
1237   HTSMSG_FOREACH(f, m) {
1238     if (f->hmf_type == HMF_STR) {
1239       REALLOC(4 + strlen(f->hmf_str));
1240       used += sprintf(ret+used, "%s%s%s%s", !first ? sep : "", ssep, f->hmf_str, ssep);
1241     } else if (f->hmf_type == HMF_S64) {
1242       REALLOC(34); // max length is actually 20 chars + 2
1243       used += sprintf(ret+used, "%s%"PRId64, !first ? sep : "", f->hmf_s64);
1244     } else if (f->hmf_type == HMF_BOOL) {
1245       REALLOC(12); // max length is actually 10 chars + 2
1246       used += sprintf(ret+used, "%s%d", !first ? sep : "", f->hmf_bool);
1247     } else {
1248       // TODO: handle doubles
1249       free(ret);
1250       return NULL;
1251     }
1252     first = 0;
1253   }
1254 
1255   return ret;
1256 }
1257 
1258 htsmsg_t *
htsmsg_csv_2_list(const char * str,char delim)1259 htsmsg_csv_2_list(const char *str, char delim)
1260 {
1261   char *tokbuf, *tok, *saveptr = NULL, *p;
1262   const char d[2] = { delim, '\0' };
1263   htsmsg_t *m = htsmsg_create_list();
1264 
1265   if (str) {
1266     tokbuf = strdup(str);
1267     tok = strtok_r(tokbuf, d, &saveptr);
1268     while (tok) {
1269       if (tok[0] == '"') {
1270         p = ++tok;
1271         while (*p && *p != '"') {
1272           if (*p == '\\') {
1273             p++;
1274             if (*p)
1275               p++;
1276             continue;
1277           }
1278           p++;
1279         }
1280         *p = '\0';
1281       }
1282       htsmsg_add_str(m, NULL, tok);
1283       tok = strtok_r(NULL, ",", &saveptr);
1284     }
1285     free(tokbuf);
1286   }
1287   return m;
1288 }
1289 
1290 /*
1291  *
1292  */
1293 htsmsg_t *
htsmsg_create_key_val(const char * key,const char * val)1294 htsmsg_create_key_val(const char *key, const char *val)
1295 {
1296   htsmsg_t *r = htsmsg_create_map();
1297   if (r) {
1298     htsmsg_add_str(r, "key", key);
1299     htsmsg_add_str(r, "val", val);
1300   }
1301   return r;
1302 }
1303