source: trunk/libtransmission/variant.c @ 14521

Last change on this file since 14521 was 14521, checked in by mikedld, 6 years ago

Cut internal macro names to have equal number of underscores on each side

  • Property svn:keywords set to Date Rev Author Id
File size: 30.1 KB
Line 
1/*
2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: variant.c 14521 2015-05-04 19:58:34Z mikedld $
8 */
9
10#include <assert.h>
11#include <errno.h>
12#include <stdlib.h> /* strtod(), realloc(), qsort() */
13#include <string.h>
14
15#ifdef _WIN32
16 #include <share.h>
17#endif
18
19#include <locale.h> /* setlocale() */
20
21#include <event2/buffer.h>
22
23#define __LIBTRANSMISSION_VARIANT_MODULE__
24#include "transmission.h"
25#include "ConvertUTF.h"
26#include "error.h"
27#include "file.h"
28#include "log.h"
29#include "utils.h" /* tr_new(), tr_free() */
30#include "variant.h"
31#include "variant-common.h"
32
33/**
34***
35**/
36
37static bool
38tr_variantIsContainer (const tr_variant * v)
39{
40  return tr_variantIsList (v) || tr_variantIsDict (v);
41}
42
43static bool
44tr_variantIsSomething (const tr_variant * v)
45{
46  return tr_variantIsContainer (v)
47      || tr_variantIsInt (v)
48      || tr_variantIsString (v)
49      || tr_variantIsReal (v)
50      || tr_variantIsBool (v);
51}
52
53void
54tr_variantInit (tr_variant * v, char type)
55{
56  v->type = type;
57  memset (&v->val, 0, sizeof(v->val));
58}
59
60/***
61****
62***/
63
64static const struct tr_variant_string STRING_INIT =
65{
66  .type = TR_STRING_TYPE_QUARK,
67  .quark = TR_KEY_NONE,
68  .len = 0,
69  .str.str = ""
70};
71
72static void
73tr_variant_string_clear (struct tr_variant_string * str)
74{
75  if (str->type == TR_STRING_TYPE_HEAP)
76    tr_free ((char*)(str->str.str));
77
78  *str = STRING_INIT;
79}
80
81/* returns a const pointer to the variant's string */
82static const char *
83tr_variant_string_get_string (const struct tr_variant_string * str)
84{
85  const char * ret;
86
87  switch (str->type)
88    {
89      case TR_STRING_TYPE_BUF: ret = str->str.buf; break;
90      case TR_STRING_TYPE_HEAP: ret = str->str.str; break;
91      case TR_STRING_TYPE_QUARK: ret = str->str.str; break;
92      default: ret = NULL;
93    }
94
95  return ret;
96}
97
98static void
99tr_variant_string_set_quark (struct tr_variant_string  * str,
100                             const tr_quark              quark)
101{
102  tr_variant_string_clear (str);
103
104  str->type = TR_STRING_TYPE_QUARK;
105  str->quark = quark;
106  str->str.str = tr_quark_get_string (quark, &str->len);
107}
108
109static void
110tr_variant_string_set_string (struct tr_variant_string  * str,
111                              const char                * bytes,
112                              int                         len)
113{
114  tr_variant_string_clear (str);
115
116  if (bytes == NULL)
117    len = 0;
118  else if (len < 0)
119    len = strlen (bytes);
120
121  if ((size_t)len < sizeof(str->str.buf))
122    {
123      str->type = TR_STRING_TYPE_BUF;
124      memcpy (str->str.buf, bytes, len);
125      str->str.buf[len] = '\0';
126      str->len = len;
127    }
128  else
129    {
130      char * tmp = tr_new (char, len+1);
131      memcpy (tmp, bytes, len);
132      tmp[len] = '\0';
133      str->type = TR_STRING_TYPE_HEAP;
134      str->str.str = tmp;
135      str->len = len;
136    }
137}
138
139
140/***
141****
142***/
143
144static inline const char *
145getStr (const tr_variant * v)
146{
147  assert (tr_variantIsString (v));
148
149  return tr_variant_string_get_string (&v->val.s);
150}
151
152static int
153dictIndexOf (const tr_variant * dict, const tr_quark key)
154{
155  if (tr_variantIsDict (dict))
156    {
157      const tr_variant * walk;
158      const tr_variant * const begin = dict->val.l.vals;
159      const tr_variant * const end = begin + dict->val.l.count;
160
161      for (walk=begin; walk!=end; ++walk)
162        if (walk->key == key)
163          return walk - begin;
164    }
165
166  return -1;
167}
168
169tr_variant *
170tr_variantDictFind (tr_variant * dict, const tr_quark key)
171{
172  const int i = dictIndexOf (dict, key);
173
174  return i < 0 ? NULL : dict->val.l.vals+i;
175}
176
177static bool
178tr_variantDictFindType (tr_variant      * dict,
179                        const tr_quark    key,
180                        int               type,
181                        tr_variant     ** setme)
182{
183  return tr_variantIsType (*setme = tr_variantDictFind (dict, key), type);
184}
185
186size_t
187tr_variantListSize (const tr_variant * list)
188{
189  return tr_variantIsList (list) ? list->val.l.count : 0;
190}
191
192tr_variant*
193tr_variantListChild (tr_variant * v,
194                     size_t    i)
195{
196  tr_variant * ret = NULL;
197
198  if (tr_variantIsList (v) && (i < v->val.l.count))
199    ret = v->val.l.vals + i;
200
201  return ret;
202}
203
204bool
205tr_variantListRemove (tr_variant * list, size_t i)
206{
207  bool removed = false;
208
209  if (tr_variantIsList (list) && (i < list->val.l.count))
210    {
211      removed = true;
212      tr_variantFree (&list->val.l.vals[i]);
213      tr_removeElementFromArray (list->val.l.vals, i,
214                                 sizeof (tr_variant),
215                                 list->val.l.count--);
216    }
217
218  return removed;
219}
220
221bool
222tr_variantGetInt (const tr_variant  * v,
223                  int64_t           * setme)
224{
225  bool success = false;
226
227  if (!success && ((success = tr_variantIsInt (v))))
228    if (setme)
229      *setme = v->val.i;
230
231  if (!success && ((success = tr_variantIsBool (v))))
232    if (setme)
233      *setme = v->val.b ? 1 : 0;
234
235  return success;
236}
237
238bool
239tr_variantGetStr (const tr_variant   * v,
240                  const char        ** setme,
241                  size_t             * len)
242{
243  const bool success = tr_variantIsString (v);
244
245  if (success)
246    *setme = getStr (v);
247
248  if (len != NULL)
249    *len = success ? v->val.s.len : 0;
250
251  return success;
252}
253
254bool
255tr_variantGetRaw (const tr_variant   * v,
256                  const uint8_t     ** setme_raw,
257                  size_t             * setme_len)
258{
259  const bool success = tr_variantIsString (v);
260
261  if (success)
262    {
263      *setme_raw = (uint8_t*) getStr (v);
264      *setme_len = v->val.s.len;
265    }
266
267  return success;
268}
269
270bool
271tr_variantGetBool (const tr_variant * v, bool * setme)
272{
273  const char * str;
274  bool success = false;
275
276  if ((success = tr_variantIsBool (v)))
277    *setme = v->val.b;
278
279  if (!success && tr_variantIsInt (v))
280    if ((success = (v->val.i==0 || v->val.i==1)))
281      *setme = v->val.i!=0;
282
283  if (!success && tr_variantGetStr (v, &str, NULL))
284    if ((success = (!strcmp (str,"true") || !strcmp (str,"false"))))
285      *setme = !strcmp (str,"true");
286
287  return success;
288}
289
290bool
291tr_variantGetReal (const tr_variant * v, double * setme)
292{
293  bool success = false;
294
295  if (!success && ((success = tr_variantIsReal (v))))
296    *setme = v->val.d;
297
298  if (!success && ((success = tr_variantIsInt (v))))
299    *setme = v->val.i;
300
301  if (!success && tr_variantIsString (v))
302    {
303      char * endptr;
304      char locale[128];
305      double d;
306
307      /* the json spec requires a '.' decimal point regardless of locale */
308      tr_strlcpy (locale, setlocale (LC_NUMERIC, NULL), sizeof (locale));
309      setlocale (LC_NUMERIC, "POSIX");
310      d  = strtod (getStr (v), &endptr);
311      setlocale (LC_NUMERIC, locale);
312
313      if ((success = (getStr (v) != endptr) && !*endptr))
314        *setme = d;
315    }
316
317  return success;
318}
319
320bool
321tr_variantDictFindInt (tr_variant      * dict,
322                       const tr_quark    key,
323                       int64_t         * setme)
324{
325  tr_variant * child = tr_variantDictFind (dict, key);
326  return tr_variantGetInt (child, setme);
327}
328
329bool
330tr_variantDictFindBool (tr_variant      * dict,
331                        const tr_quark    key,
332                        bool            * setme)
333{
334  tr_variant * child = tr_variantDictFind (dict, key);
335  return tr_variantGetBool (child, setme);
336}
337
338bool
339tr_variantDictFindReal (tr_variant      * dict,
340                        const tr_quark    key,
341                        double          * setme)
342{
343  tr_variant * child = tr_variantDictFind (dict, key);
344  return tr_variantGetReal (child, setme);
345}
346
347bool
348tr_variantDictFindStr (tr_variant       * dict,
349                        const tr_quark    key,
350                        const char     ** setme,
351                        size_t          * len)
352{
353  tr_variant * child = tr_variantDictFind (dict, key);
354  return tr_variantGetStr (child, setme, len);
355}
356
357bool
358tr_variantDictFindList (tr_variant       * dict,
359                        const tr_quark     key,
360                        tr_variant      ** setme)
361{
362  return tr_variantDictFindType (dict, key, TR_VARIANT_TYPE_LIST, setme);
363}
364
365bool
366tr_variantDictFindDict (tr_variant       * dict,
367                        const tr_quark     key,
368                        tr_variant      ** setme)
369{
370  return tr_variantDictFindType (dict, key, TR_VARIANT_TYPE_DICT, setme);
371}
372
373bool
374tr_variantDictFindRaw (tr_variant      * dict,
375                       const tr_quark    key,
376                       const uint8_t  ** setme_raw,
377                       size_t          * setme_len)
378{
379  tr_variant * child = tr_variantDictFind (dict, key);
380  return tr_variantGetRaw (child, setme_raw, setme_len);
381}
382
383/***
384****
385***/
386
387void
388tr_variantInitRaw (tr_variant * v, const void * src, size_t byteCount)
389{
390  tr_variantInit (v, TR_VARIANT_TYPE_STR);
391  tr_variant_string_set_string (&v->val.s, src, byteCount);
392}
393
394void
395tr_variantInitQuark (tr_variant * v, const tr_quark q)
396{
397  tr_variantInit (v, TR_VARIANT_TYPE_STR);
398  tr_variant_string_set_quark (&v->val.s, q);
399}
400
401void
402tr_variantInitStr (tr_variant * v, const void * str, int len)
403{
404  tr_variantInit (v, TR_VARIANT_TYPE_STR);
405  tr_variant_string_set_string (&v->val.s, str, len);
406}
407
408void
409tr_variantInitBool (tr_variant * v, bool value)
410{
411  tr_variantInit (v, TR_VARIANT_TYPE_BOOL);
412  v->val.b = value != 0;
413}
414
415void
416tr_variantInitReal (tr_variant * v, double value)
417{
418  tr_variantInit (v, TR_VARIANT_TYPE_REAL);
419  v->val.d = value;
420}
421
422void
423tr_variantInitInt (tr_variant * v, int64_t value)
424{
425  tr_variantInit (v, TR_VARIANT_TYPE_INT);
426  v->val.i = value;
427}
428
429void
430tr_variantInitList (tr_variant * v, size_t reserve_count)
431{
432  tr_variantInit (v, TR_VARIANT_TYPE_LIST);
433  tr_variantListReserve (v, reserve_count);
434}
435
436static void
437containerReserve (tr_variant * v, size_t count)
438{
439  const size_t needed = v->val.l.count + count;
440
441  assert (tr_variantIsContainer (v));
442
443  if (needed > v->val.l.alloc)
444    {
445      /* scale the alloc size in powers-of-2 */
446      size_t n = v->val.l.alloc ? v->val.l.alloc : 8;
447      while (n < needed)
448        n *= 2u;
449
450      v->val.l.vals = tr_renew (tr_variant, v->val.l.vals, n);
451      v->val.l.alloc = n;
452    }
453}
454
455void
456tr_variantListReserve (tr_variant * list, size_t count)
457{
458  assert (tr_variantIsList (list));
459  containerReserve (list, count);
460}
461
462void
463tr_variantInitDict (tr_variant * v, size_t reserve_count)
464{
465  tr_variantInit (v, TR_VARIANT_TYPE_DICT);
466  tr_variantDictReserve (v, reserve_count);
467}
468
469void
470tr_variantDictReserve (tr_variant  * dict,
471                       size_t        reserve_count)
472{
473  assert (tr_variantIsDict (dict));
474  containerReserve (dict, reserve_count);
475}
476
477tr_variant *
478tr_variantListAdd (tr_variant * list)
479{
480  tr_variant * child;
481
482  assert (tr_variantIsList (list));
483
484  containerReserve (list, 1);
485  child = &list->val.l.vals[list->val.l.count++];
486  child->key = 0;
487  tr_variantInit (child, TR_VARIANT_TYPE_INT);
488
489  return child;
490}
491
492tr_variant *
493tr_variantListAddInt (tr_variant  * list,
494                      int64_t       val)
495{
496  tr_variant * child = tr_variantListAdd (list);
497  tr_variantInitInt (child, val);
498  return child;
499}
500
501tr_variant *
502tr_variantListAddReal (tr_variant  * list,
503                       double        val)
504{
505  tr_variant * child = tr_variantListAdd (list);
506  tr_variantInitReal (child, val);
507  return child;
508}
509
510tr_variant *
511tr_variantListAddBool (tr_variant  * list,
512                       bool          val)
513{
514  tr_variant * child = tr_variantListAdd (list);
515  tr_variantInitBool (child, val);
516  return child;
517}
518
519tr_variant *
520tr_variantListAddStr (tr_variant  * list,
521                      const char  * val)
522{
523  tr_variant * child = tr_variantListAdd (list);
524  tr_variantInitStr (child, val, -1);
525  return child;
526}
527
528tr_variant *
529tr_variantListAddQuark (tr_variant     * list,
530                        const tr_quark   val)
531{
532  tr_variant * child = tr_variantListAdd (list);
533  tr_variantInitQuark (child, val);
534  return child;
535}
536
537tr_variant *
538tr_variantListAddRaw (tr_variant  * list,
539                      const void  * val,
540                      size_t        len)
541{
542  tr_variant * child = tr_variantListAdd (list);
543  tr_variantInitRaw (child, val, len);
544  return child;
545}
546
547tr_variant*
548tr_variantListAddList (tr_variant  * list,
549                       size_t        reserve_count)
550{
551  tr_variant * child = tr_variantListAdd (list);
552  tr_variantInitList (child, reserve_count);
553  return child;
554}
555
556tr_variant*
557tr_variantListAddDict (tr_variant  * list,
558                       size_t        reserve_count)
559{
560  tr_variant * child = tr_variantListAdd (list);
561  tr_variantInitDict (child, reserve_count);
562  return child;
563}
564
565tr_variant *
566tr_variantDictAdd (tr_variant      * dict,
567                   const tr_quark    key)
568{
569  tr_variant * val;
570
571  assert (tr_variantIsDict (dict));
572
573  containerReserve (dict, 1);
574
575  val = dict->val.l.vals + dict->val.l.count++;
576  tr_variantInit (val, TR_VARIANT_TYPE_INT);
577  val->key = key;
578  return val;
579}
580
581static tr_variant*
582dictFindOrAdd (tr_variant * dict, const tr_quark key, int type)
583{
584  tr_variant * child;
585
586  /* see if it already exists, and if so, try to reuse it */
587  if ((child = tr_variantDictFind (dict, key)))
588    {
589      if (!tr_variantIsType (child, type))
590        {
591          tr_variantDictRemove (dict, key);
592          child = NULL;
593        }
594      else if (child->type == TR_VARIANT_TYPE_STR)
595        {
596          tr_variant_string_clear (&child->val.s);
597        }
598    }
599
600  /* if it doesn't exist, create it */
601  if (child == NULL)
602    child = tr_variantDictAdd (dict, key);
603
604  return child;
605}
606
607tr_variant*
608tr_variantDictAddInt (tr_variant      * dict,
609                      const tr_quark    key,
610                      int64_t           val)
611{
612  tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_INT);
613  tr_variantInitInt (child, val);
614  return child;
615}
616
617tr_variant*
618tr_variantDictAddBool (tr_variant      * dict,
619                       const tr_quark    key,
620                       bool              val)
621{
622  tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_BOOL);
623  tr_variantInitBool (child, val);
624  return child;
625}
626
627tr_variant*
628tr_variantDictAddReal (tr_variant      * dict,
629                       const tr_quark    key,
630                       double            val)
631{
632  tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_REAL);
633  tr_variantInitReal (child, val);
634  return child;
635}
636
637tr_variant*
638tr_variantDictAddQuark (tr_variant      * dict,
639                        const tr_quark    key,
640                        const tr_quark    val)
641{
642  tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_STR);
643  tr_variantInitQuark (child, val);
644  return child;
645}
646
647tr_variant*
648tr_variantDictAddStr (tr_variant      * dict,
649                      const tr_quark    key,
650                      const char      * val)
651{
652  tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_STR);
653  tr_variantInitStr (child, val, -1);
654  return child;
655}
656
657tr_variant*
658tr_variantDictAddRaw (tr_variant      * dict,
659                      const tr_quark    key,
660                      const void      * src,
661                      size_t            len)
662{
663  tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_STR);
664  tr_variantInitRaw (child, src, len);
665  return child;
666}
667
668tr_variant*
669tr_variantDictAddList (tr_variant     * dict,
670                       const tr_quark   key,
671                       size_t           reserve_count)
672{
673  tr_variant * child = tr_variantDictAdd (dict, key);
674  tr_variantInitList (child, reserve_count);
675  return child;
676}
677
678tr_variant*
679tr_variantDictAddDict (tr_variant     * dict,
680                       const tr_quark   key,
681                       size_t           reserve_count)
682{
683  tr_variant * child = tr_variantDictAdd (dict, key);
684  tr_variantInitDict (child, reserve_count);
685  return child;
686}
687
688tr_variant *
689tr_variantDictSteal (tr_variant       * dict,
690                     const tr_quark     key,
691                     tr_variant       * value)
692{
693  tr_variant * child = tr_variantDictAdd (dict, key);
694  *child = *value;
695  child->key = key;
696  tr_variantInit (value, value->type);
697  return child;
698}
699
700bool
701tr_variantDictRemove (tr_variant     * dict,
702                      const tr_quark   key)
703{
704  bool removed = false;
705  const int i = dictIndexOf (dict, key);
706
707  if (i >= 0)
708    {
709      const int last = dict->val.l.count - 1;
710
711      tr_variantFree (&dict->val.l.vals[i]);
712
713      if (i != last)
714        dict->val.l.vals[i] = dict->val.l.vals[last];
715
716      --dict->val.l.count;
717
718       removed = true;
719    }
720
721  return removed;
722}
723
724/***
725****  BENC WALKING
726***/
727
728struct KeyIndex
729{
730  const char * keystr;
731  tr_variant * val;
732};
733
734static int
735compareKeyIndex (const void * va, const void * vb)
736{
737  const struct KeyIndex * a = va;
738  const struct KeyIndex * b = vb;
739
740  return strcmp (a->keystr, b->keystr);
741}
742
743struct SaveNode
744{
745  const tr_variant * v;
746  tr_variant sorted;
747  size_t childIndex;
748  bool isVisited;
749};
750
751static void
752nodeConstruct (struct SaveNode   * node,
753               const tr_variant  * v,
754               bool                sort_dicts)
755{
756  node->isVisited = false;
757  node->childIndex = 0;
758
759  if (sort_dicts && tr_variantIsDict(v))
760    {
761      /* make node->sorted a sorted version of this dictionary */
762
763      size_t i;
764      const size_t n = v->val.l.count;
765      struct KeyIndex * tmp = tr_new (struct KeyIndex, n);
766
767      for (i=0; i<n; i++)
768        {
769          tmp[i].val = v->val.l.vals+i;
770          tmp[i].keystr = tr_quark_get_string (tmp[i].val->key, NULL);
771        }
772
773      qsort (tmp, n, sizeof (struct KeyIndex), compareKeyIndex);
774
775      tr_variantInitDict (&node->sorted, n);
776      for (i=0; i<n; ++i)
777        node->sorted.val.l.vals[i] = *tmp[i].val;
778      node->sorted.val.l.count = n;
779
780      tr_free (tmp);
781
782      node->v = &node->sorted;
783    }
784  else
785    {
786      node->v = v;
787    }
788}
789
790static void
791nodeDestruct (struct SaveNode * node)
792{
793  if (node->v == &node->sorted)
794    tr_free (node->sorted.val.l.vals);
795}
796
797/**
798 * This function's previous recursive implementation was
799 * easier to read, but was vulnerable to a smash-stacking
800 * attack via maliciously-crafted data. (#667)
801 */
802void
803tr_variantWalk (const tr_variant               * v,
804                const struct VariantWalkFuncs  * walkFuncs,
805                void                           * user_data,
806                bool                             sort_dicts)
807{
808  int stackSize = 0;
809  int stackAlloc = 64;
810  struct SaveNode * stack = tr_new (struct SaveNode, stackAlloc);
811
812  nodeConstruct (&stack[stackSize++], v, sort_dicts);
813
814  while (stackSize > 0)
815    {
816      struct SaveNode * node = &stack[stackSize-1];
817      const tr_variant * v;
818
819      if (!node->isVisited)
820        {
821          v = node->v;
822          node->isVisited = true;
823        }
824      else if (tr_variantIsContainer(node->v) && (node->childIndex < node->v->val.l.count))
825        {
826          const int index = node->childIndex++;
827          v = node->v->val.l.vals + index;
828
829          if (tr_variantIsDict (node->v))
830            {
831              tr_variant tmp;
832              tr_variantInitQuark (&tmp, v->key);
833              walkFuncs->stringFunc (&tmp, user_data);
834            }
835        }
836      else /* done with this node */
837        {
838          if (tr_variantIsContainer (node->v))
839            walkFuncs->containerEndFunc (node->v, user_data);
840          --stackSize;
841          nodeDestruct (node);
842          continue;
843        }
844
845      if (v) switch (v->type)
846        {
847          case TR_VARIANT_TYPE_INT:
848            walkFuncs->intFunc (v, user_data);
849            break;
850
851          case TR_VARIANT_TYPE_BOOL:
852            walkFuncs->boolFunc (v, user_data);
853            break;
854
855          case TR_VARIANT_TYPE_REAL:
856            walkFuncs->realFunc (v, user_data);
857            break;
858
859          case TR_VARIANT_TYPE_STR:
860            walkFuncs->stringFunc (v, user_data);
861            break;
862
863          case TR_VARIANT_TYPE_LIST:
864            if (v == node->v)
865              {
866                walkFuncs->listBeginFunc (v, user_data);
867              }
868            else
869              {
870                if (stackAlloc == stackSize)
871                  {
872                    stackAlloc *= 2;
873                    stack = tr_renew (struct SaveNode, stack, stackAlloc);
874                  }
875                nodeConstruct (&stack[stackSize++], v, sort_dicts);
876              }
877            break;
878
879          case TR_VARIANT_TYPE_DICT:
880            if (v == node->v)
881              {
882                walkFuncs->dictBeginFunc (v, user_data);
883              }
884            else
885              {
886                if (stackAlloc == stackSize)
887                  {
888                    stackAlloc *= 2;
889                    stack = tr_renew (struct SaveNode, stack, stackAlloc);
890                  }
891                nodeConstruct (&stack[stackSize++], v, sort_dicts);
892              }
893            break;
894
895          default:
896            /* did caller give us an uninitialized val? */
897            tr_logAddError ("%s", _("Invalid metadata"));
898            break;
899        }
900    }
901
902  tr_free (stack);
903}
904
905/****
906*****
907****/
908
909static void
910freeDummyFunc (const tr_variant * v UNUSED, void * buf UNUSED)
911{}
912
913static void
914freeStringFunc (const tr_variant * v, void * unused UNUSED)
915{
916  tr_variant_string_clear (&((tr_variant*)v)->val.s);
917}
918
919static void
920freeContainerEndFunc (const tr_variant * v, void * unused UNUSED)
921{
922  tr_free (v->val.l.vals);
923}
924
925static const struct VariantWalkFuncs freeWalkFuncs = { freeDummyFunc,
926                                                       freeDummyFunc,
927                                                       freeDummyFunc,
928                                                       freeStringFunc,
929                                                       freeDummyFunc,
930                                                       freeDummyFunc,
931                                                       freeContainerEndFunc };
932
933void
934tr_variantFree (tr_variant * v)
935{
936  if (tr_variantIsSomething (v))
937    tr_variantWalk (v, &freeWalkFuncs, NULL, false);
938}
939
940/***
941****
942***/
943
944static void
945tr_variantListCopy (tr_variant * target, const tr_variant * src)
946{
947  int i = 0;
948  const tr_variant * val;
949
950  while ((val = tr_variantListChild ((tr_variant*)src, i++)))
951    {
952      if (tr_variantIsBool (val))
953       {
954          bool boolVal = 0;
955          tr_variantGetBool (val, &boolVal);
956          tr_variantListAddBool (target, boolVal);
957       }
958     else if (tr_variantIsReal (val))
959       {
960         double realVal = 0;
961         tr_variantGetReal (val, &realVal);
962         tr_variantListAddReal (target, realVal);
963       }
964     else if (tr_variantIsInt (val))
965       {
966         int64_t intVal = 0;
967         tr_variantGetInt (val, &intVal);
968         tr_variantListAddInt (target, intVal);
969       }
970     else if (tr_variantIsString (val))
971       {
972         size_t len;
973         const char * str;
974         tr_variantGetStr (val, &str, &len);
975         tr_variantListAddRaw (target, str, len);
976       }
977     else if (tr_variantIsDict (val))
978       {
979         tr_variantMergeDicts (tr_variantListAddDict (target, 0), val);
980       }
981     else if (tr_variantIsList (val))
982       {
983         tr_variantListCopy (tr_variantListAddList (target, 0), val);
984       }
985     else
986       {
987         tr_logAddError ("tr_variantListCopy skipping item");
988       }
989   }
990}
991
992static size_t
993tr_variantDictSize (const tr_variant * dict)
994{
995  return tr_variantIsDict (dict) ? dict->val.l.count : 0;
996}
997
998bool
999tr_variantDictChild (tr_variant   * dict,
1000                     size_t         n,
1001                     tr_quark     * key,
1002                     tr_variant  ** val)
1003{
1004  bool success = 0;
1005
1006  assert (tr_variantIsDict (dict));
1007
1008  if (tr_variantIsDict (dict) && (n<dict->val.l.count))
1009    {
1010      *key = dict->val.l.vals[n].key;
1011      *val = dict->val.l.vals+n;
1012      success = true;
1013    }
1014
1015  return success;
1016}
1017
1018void
1019tr_variantMergeDicts (tr_variant * target, const tr_variant * source)
1020{
1021  size_t i;
1022  const size_t sourceCount = tr_variantDictSize (source);
1023
1024  assert (tr_variantIsDict (target));
1025  assert (tr_variantIsDict (source));
1026
1027  tr_variantDictReserve (target, sourceCount + tr_variantDictSize(target));
1028
1029  for (i=0; i<sourceCount; ++i)
1030    {
1031      tr_quark key;
1032      tr_variant * val;
1033      tr_variant * t;
1034
1035      if (tr_variantDictChild ((tr_variant*)source, i, &key, &val))
1036        {
1037          if (tr_variantIsBool (val))
1038            {
1039              bool boolVal;
1040              tr_variantGetBool (val, &boolVal);
1041              tr_variantDictAddBool (target, key, boolVal);
1042            }
1043          else if (tr_variantIsReal (val))
1044            {
1045              double realVal = 0;
1046              tr_variantGetReal (val, &realVal);
1047              tr_variantDictAddReal (target, key, realVal);
1048            }
1049          else if (tr_variantIsInt (val))
1050            {
1051              int64_t intVal = 0;
1052              tr_variantGetInt (val, &intVal);
1053              tr_variantDictAddInt (target, key, intVal);
1054            }
1055          else if (tr_variantIsString (val))
1056            {
1057              size_t len;
1058              const char * str;
1059              tr_variantGetStr (val, &str, &len);
1060              tr_variantDictAddRaw (target, key, str, len);
1061            }
1062          else if (tr_variantIsDict (val) && tr_variantDictFindDict (target, key, &t))
1063            {
1064              tr_variantMergeDicts (t, val);
1065            }
1066          else if (tr_variantIsList (val))
1067            {
1068              if (tr_variantDictFind (target, key) == NULL)
1069                tr_variantListCopy (tr_variantDictAddList (target, key, tr_variantListSize (val)), val);
1070            }
1071          else if (tr_variantIsDict (val))
1072            {
1073              tr_variant * target_dict = tr_variantDictFind (target, key);
1074
1075              if (target_dict == NULL)
1076                target_dict = tr_variantDictAddDict (target, key, tr_variantDictSize (val));
1077
1078              if (tr_variantIsDict (target_dict))
1079                tr_variantMergeDicts (target_dict, val);
1080            }
1081          else
1082            {
1083              tr_logAddDebug ("tr_variantMergeDicts skipping \"%s\"", tr_quark_get_string(key,NULL));
1084            }
1085        }
1086    }
1087}
1088
1089/***
1090****
1091***/
1092
1093struct evbuffer *
1094tr_variantToBuf (const tr_variant * v, tr_variant_fmt fmt)
1095{
1096  char lc_numeric[128];
1097  struct evbuffer * buf = evbuffer_new();
1098
1099  /* parse with LC_NUMERIC="C" to ensure a "." decimal separator */
1100  tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
1101  setlocale (LC_NUMERIC, "C");
1102
1103  evbuffer_expand (buf, 4096); /* alloc a little memory to start off with */
1104
1105  switch (fmt)
1106    {
1107      case TR_VARIANT_FMT_BENC:
1108        tr_variantToBufBenc (v, buf);
1109        break;
1110
1111      case TR_VARIANT_FMT_JSON:
1112        tr_variantToBufJson (v, buf, false);
1113        break;
1114
1115      case TR_VARIANT_FMT_JSON_LEAN:
1116        tr_variantToBufJson (v, buf, true);
1117        break;
1118    }
1119
1120  /* restore the previous locale */
1121  setlocale (LC_NUMERIC, lc_numeric);
1122  return buf;
1123}
1124
1125char*
1126tr_variantToStr (const tr_variant * v, tr_variant_fmt fmt, int * len)
1127{
1128  struct evbuffer * buf = tr_variantToBuf (v, fmt);
1129  const size_t n = evbuffer_get_length (buf);
1130  char * ret = evbuffer_free_to_str (buf);
1131  if (len != NULL)
1132    *len = (int) n;
1133  return ret;
1134}
1135
1136int
1137tr_variantToFile (const tr_variant  * v,
1138                  tr_variant_fmt      fmt,
1139                  const char        * filename)
1140{
1141  char * tmp;
1142  tr_sys_file_t fd;
1143  int err = 0;
1144  char * real_filename;
1145  tr_error * error = NULL;
1146
1147  /* follow symlinks to find the "real" file, to make sure the temporary
1148   * we build with tr_sys_file_open_temp() is created on the right partition */
1149  if ((real_filename = tr_sys_path_resolve (filename, NULL)) != NULL)
1150    filename = real_filename;
1151
1152  /* if the file already exists, try to move it out of the way & keep it as a backup */
1153  tmp = tr_strdup_printf ("%s.tmp.XXXXXX", filename);
1154  fd = tr_sys_file_open_temp (tmp, &error);
1155  if (fd != TR_BAD_SYS_FILE)
1156    {
1157      uint64_t nleft;
1158
1159      /* save the variant to a temporary file */
1160      {
1161        struct evbuffer * buf = tr_variantToBuf (v, fmt);
1162        const char * walk = (const char *) evbuffer_pullup (buf, -1);
1163        nleft = evbuffer_get_length (buf);
1164
1165        while (nleft > 0)
1166          {
1167            uint64_t n;
1168            if (!tr_sys_file_write (fd, walk, nleft, &n, &error))
1169              {
1170                err = error->code;
1171                break;
1172              }
1173
1174            nleft -= n;
1175            walk += n;
1176          }
1177
1178        evbuffer_free (buf);
1179      }
1180
1181      tr_sys_file_close (fd, NULL);
1182
1183      if (nleft > 0)
1184        {
1185          tr_logAddError (_("Couldn't save temporary file \"%1$s\": %2$s"), tmp, error->message);
1186          tr_sys_path_remove (tmp, NULL);
1187          tr_error_free (error);
1188        }
1189      else
1190        {
1191          tr_error_clear (&error);
1192          if (tr_sys_path_rename (tmp, filename, &error))
1193            {
1194              tr_logAddInfo (_("Saved \"%s\""), filename);
1195            }
1196          else
1197            {
1198              err = error->code;
1199              tr_logAddError (_("Couldn't save file \"%1$s\": %2$s"), filename, error->message);
1200              tr_sys_path_remove (tmp, NULL);
1201              tr_error_free (error);
1202            }
1203        }
1204    }
1205  else
1206    {
1207      err = error->code;
1208      tr_logAddError (_("Couldn't save temporary file \"%1$s\": %2$s"), tmp, error->message);
1209      tr_error_free (error);
1210    }
1211
1212  tr_free (tmp);
1213  tr_free (real_filename);
1214  return err;
1215}
1216
1217/***
1218****
1219***/
1220
1221bool
1222tr_variantFromFile (tr_variant      * setme,
1223                    tr_variant_fmt    fmt,
1224                    const char      * filename,
1225                    tr_error       ** error)
1226{
1227  bool ret = false;
1228  uint8_t * buf;
1229  size_t buflen;
1230
1231  buf = tr_loadFile (filename, &buflen, error);
1232  if (buf != NULL)
1233    {
1234      if (tr_variantFromBuf (setme, fmt, buf, buflen, filename, NULL) == 0)
1235        ret = true;
1236      else
1237        tr_error_set_literal (error, 0, _("Unable to parse file content"));
1238
1239      tr_free (buf);
1240    }
1241
1242  return ret;
1243}
1244
1245int
1246tr_variantFromBuf (tr_variant      * setme,
1247                   tr_variant_fmt    fmt,
1248                   const void      * buf,
1249                   size_t            buflen,
1250                   const char      * optional_source,
1251                   const char     ** setme_end)
1252{
1253  int err;
1254  char lc_numeric[128];
1255
1256  /* parse with LC_NUMERIC="C" to ensure a "." decimal separator */
1257  tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
1258  setlocale (LC_NUMERIC, "C");
1259
1260  switch (fmt)
1261    {
1262      case TR_VARIANT_FMT_JSON:
1263      case TR_VARIANT_FMT_JSON_LEAN:
1264        err = tr_jsonParse (optional_source, buf, buflen, setme, setme_end);
1265        break;
1266
1267      default /* TR_VARIANT_FMT_BENC */:
1268        err = tr_variantParseBenc (buf, ((const char*)buf)+buflen, setme, setme_end);
1269        break;
1270    }
1271
1272  /* restore the previous locale */
1273  setlocale (LC_NUMERIC, lc_numeric);
1274  return err;
1275}
Note: See TracBrowser for help on using the repository browser.