source: trunk/libtransmission/variant.c @ 14327

Last change on this file since 14327 was 14327, checked in by jordan, 8 years ago

(trunk, libt) #4160 - the slow slog to catch trunk up to mike.dld's 4160 diff continues. This step applies 4160-03b-file.patch, which replaces native file operations with the tr_sys_file_*() portability wrappers added in r14321.

  • Property svn:keywords set to Date Rev Author Id
File size: 29.7 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 14327 2014-07-28 04:13:38Z jordan $
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
688bool
689tr_variantDictRemove (tr_variant     * dict,
690                      const tr_quark   key)
691{
692  bool removed = false;
693  const int i = dictIndexOf (dict, key);
694
695  if (i >= 0)
696    {
697      const int last = dict->val.l.count - 1;
698
699      tr_variantFree (&dict->val.l.vals[i]);
700
701      if (i != last)
702        dict->val.l.vals[i] = dict->val.l.vals[last];
703
704      --dict->val.l.count;
705
706       removed = true;
707    }
708
709  return removed;
710}
711
712/***
713****  BENC WALKING
714***/
715
716struct KeyIndex
717{
718  const char * keystr;
719  tr_variant * val;
720};
721
722static int
723compareKeyIndex (const void * va, const void * vb)
724{
725  const struct KeyIndex * a = va;
726  const struct KeyIndex * b = vb;
727
728  return strcmp (a->keystr, b->keystr);
729}
730
731struct SaveNode
732{
733  const tr_variant * v;
734  tr_variant sorted;
735  size_t childIndex;
736  bool isVisited;
737};
738
739static void
740nodeConstruct (struct SaveNode   * node,
741               const tr_variant  * v,
742               bool                sort_dicts)
743{
744  node->isVisited = false;
745  node->childIndex = 0;
746
747  if (sort_dicts && tr_variantIsDict(v))
748    {
749      /* make node->sorted a sorted version of this dictionary */
750
751      size_t i;
752      const size_t n = v->val.l.count;
753      struct KeyIndex * tmp = tr_new (struct KeyIndex, n);
754
755      for (i=0; i<n; i++)
756        {
757          tmp[i].val = v->val.l.vals+i;
758          tmp[i].keystr = tr_quark_get_string (tmp[i].val->key, NULL);
759        }
760
761      qsort (tmp, n, sizeof (struct KeyIndex), compareKeyIndex);
762
763      tr_variantInitDict (&node->sorted, n);
764      for (i=0; i<n; ++i)
765        node->sorted.val.l.vals[i] = *tmp[i].val;
766      node->sorted.val.l.count = n;
767
768      tr_free (tmp);
769
770      node->v = &node->sorted;
771    }
772  else
773    {
774      node->v = v;
775    }
776}
777
778static void
779nodeDestruct (struct SaveNode * node)
780{
781  if (node->v == &node->sorted)
782    tr_free (node->sorted.val.l.vals);
783}
784
785/**
786 * This function's previous recursive implementation was
787 * easier to read, but was vulnerable to a smash-stacking
788 * attack via maliciously-crafted data. (#667)
789 */
790void
791tr_variantWalk (const tr_variant               * v,
792                const struct VariantWalkFuncs  * walkFuncs,
793                void                           * user_data,
794                bool                             sort_dicts)
795{
796  int stackSize = 0;
797  int stackAlloc = 64;
798  struct SaveNode * stack = tr_new (struct SaveNode, stackAlloc);
799
800  nodeConstruct (&stack[stackSize++], v, sort_dicts);
801
802  while (stackSize > 0)
803    {
804      struct SaveNode * node = &stack[stackSize-1];
805      const tr_variant * v;
806
807      if (!node->isVisited)
808        {
809          v = node->v;
810          node->isVisited = true;
811        }
812      else if (tr_variantIsContainer(node->v) && (node->childIndex < node->v->val.l.count))
813        {
814          const int index = node->childIndex++;
815          v = node->v->val.l.vals + index;
816
817          if (tr_variantIsDict (node->v))
818            {
819              tr_variant tmp;
820              tr_variantInitQuark (&tmp, v->key);
821              walkFuncs->stringFunc (&tmp, user_data);
822            }
823        }
824      else /* done with this node */
825        {
826          if (tr_variantIsContainer (node->v))
827            walkFuncs->containerEndFunc (node->v, user_data);
828          --stackSize;
829          nodeDestruct (node);
830          continue;
831        }
832
833      if (v) switch (v->type)
834        {
835          case TR_VARIANT_TYPE_INT:
836            walkFuncs->intFunc (v, user_data);
837            break;
838
839          case TR_VARIANT_TYPE_BOOL:
840            walkFuncs->boolFunc (v, user_data);
841            break;
842
843          case TR_VARIANT_TYPE_REAL:
844            walkFuncs->realFunc (v, user_data);
845            break;
846
847          case TR_VARIANT_TYPE_STR:
848            walkFuncs->stringFunc (v, user_data);
849            break;
850
851          case TR_VARIANT_TYPE_LIST:
852            if (v == node->v)
853              {
854                walkFuncs->listBeginFunc (v, user_data);
855              }
856            else
857              {
858                if (stackAlloc == stackSize)
859                  {
860                    stackAlloc *= 2;
861                    stack = tr_renew (struct SaveNode, stack, stackAlloc);
862                  }
863                nodeConstruct (&stack[stackSize++], v, sort_dicts);
864              }
865            break;
866
867          case TR_VARIANT_TYPE_DICT:
868            if (v == node->v)
869              {
870                walkFuncs->dictBeginFunc (v, user_data);
871              }
872            else
873              {
874                if (stackAlloc == stackSize)
875                  {
876                    stackAlloc *= 2;
877                    stack = tr_renew (struct SaveNode, stack, stackAlloc);
878                  }
879                nodeConstruct (&stack[stackSize++], v, sort_dicts);
880              }
881            break;
882
883          default:
884            /* did caller give us an uninitialized val? */
885            tr_logAddError ("%s", _("Invalid metadata"));
886            break;
887        }
888    }
889
890  tr_free (stack);
891}
892
893/****
894*****
895****/
896
897static void
898freeDummyFunc (const tr_variant * v UNUSED, void * buf UNUSED)
899{}
900
901static void
902freeStringFunc (const tr_variant * v, void * unused UNUSED)
903{
904  tr_variant_string_clear (&((tr_variant*)v)->val.s);
905}
906
907static void
908freeContainerEndFunc (const tr_variant * v, void * unused UNUSED)
909{
910  tr_free (v->val.l.vals);
911}
912
913static const struct VariantWalkFuncs freeWalkFuncs = { freeDummyFunc,
914                                                       freeDummyFunc,
915                                                       freeDummyFunc,
916                                                       freeStringFunc,
917                                                       freeDummyFunc,
918                                                       freeDummyFunc,
919                                                       freeContainerEndFunc };
920
921void
922tr_variantFree (tr_variant * v)
923{
924  if (tr_variantIsSomething (v))
925    tr_variantWalk (v, &freeWalkFuncs, NULL, false);
926}
927
928/***
929****
930***/
931
932static void
933tr_variantListCopy (tr_variant * target, const tr_variant * src)
934{
935  int i = 0;
936  const tr_variant * val;
937
938  while ((val = tr_variantListChild ((tr_variant*)src, i++)))
939    {
940      if (tr_variantIsBool (val))
941       {
942          bool boolVal = 0;
943          tr_variantGetBool (val, &boolVal);
944          tr_variantListAddBool (target, boolVal);
945       }
946     else if (tr_variantIsReal (val))
947       {
948         double realVal = 0;
949         tr_variantGetReal (val, &realVal);
950         tr_variantListAddReal (target, realVal);
951       }
952     else if (tr_variantIsInt (val))
953       {
954         int64_t intVal = 0;
955         tr_variantGetInt (val, &intVal);
956         tr_variantListAddInt (target, intVal);
957       }
958     else if (tr_variantIsString (val))
959       {
960         size_t len;
961         const char * str;
962         tr_variantGetStr (val, &str, &len);
963         tr_variantListAddRaw (target, str, len);
964       }
965     else if (tr_variantIsDict (val))
966       {
967         tr_variantMergeDicts (tr_variantListAddDict (target, 0), val);
968       }
969     else if (tr_variantIsList (val))
970       {
971         tr_variantListCopy (tr_variantListAddList (target, 0), val);
972       }
973     else
974       {
975         tr_logAddError ("tr_variantListCopy skipping item");
976       }
977   }
978}
979
980static size_t
981tr_variantDictSize (const tr_variant * dict)
982{
983  return tr_variantIsDict (dict) ? dict->val.l.count : 0;
984}
985
986bool
987tr_variantDictChild (tr_variant   * dict,
988                     size_t         n,
989                     tr_quark     * key,
990                     tr_variant  ** val)
991{
992  bool success = 0;
993
994  assert (tr_variantIsDict (dict));
995
996  if (tr_variantIsDict (dict) && (n<dict->val.l.count))
997    {
998      *key = dict->val.l.vals[n].key;
999      *val = dict->val.l.vals+n;
1000      success = true;
1001    }
1002
1003  return success;
1004}
1005
1006void
1007tr_variantMergeDicts (tr_variant * target, const tr_variant * source)
1008{
1009  size_t i;
1010  const size_t sourceCount = tr_variantDictSize (source);
1011
1012  assert (tr_variantIsDict (target));
1013  assert (tr_variantIsDict (source));
1014
1015  tr_variantDictReserve (target, sourceCount + tr_variantDictSize(target));
1016
1017  for (i=0; i<sourceCount; ++i)
1018    {
1019      tr_quark key;
1020      tr_variant * val;
1021      tr_variant * t;
1022
1023      if (tr_variantDictChild ((tr_variant*)source, i, &key, &val))
1024        {
1025          if (tr_variantIsBool (val))
1026            {
1027              bool boolVal;
1028              tr_variantGetBool (val, &boolVal);
1029              tr_variantDictAddBool (target, key, boolVal);
1030            }
1031          else if (tr_variantIsReal (val))
1032            {
1033              double realVal = 0;
1034              tr_variantGetReal (val, &realVal);
1035              tr_variantDictAddReal (target, key, realVal);
1036            }
1037          else if (tr_variantIsInt (val))
1038            {
1039              int64_t intVal = 0;
1040              tr_variantGetInt (val, &intVal);
1041              tr_variantDictAddInt (target, key, intVal);
1042            }
1043          else if (tr_variantIsString (val))
1044            {
1045              size_t len;
1046              const char * str;
1047              tr_variantGetStr (val, &str, &len);
1048              tr_variantDictAddRaw (target, key, str, len);
1049            }
1050          else if (tr_variantIsDict (val) && tr_variantDictFindDict (target, key, &t))
1051            {
1052              tr_variantMergeDicts (t, val);
1053            }
1054          else if (tr_variantIsList (val))
1055            {
1056              if (tr_variantDictFind (target, key) == NULL)
1057                tr_variantListCopy (tr_variantDictAddList (target, key, tr_variantListSize (val)), val);
1058            }
1059          else if (tr_variantIsDict (val))
1060            {
1061              tr_variant * target_dict = tr_variantDictFind (target, key);
1062
1063              if (target_dict == NULL)
1064                target_dict = tr_variantDictAddDict (target, key, tr_variantDictSize (val));
1065
1066              if (tr_variantIsDict (target_dict))
1067                tr_variantMergeDicts (target_dict, val);
1068            }
1069          else
1070            {
1071              tr_logAddDebug ("tr_variantMergeDicts skipping \"%s\"", tr_quark_get_string(key,NULL));
1072            }
1073        }
1074    }
1075}
1076
1077/***
1078****
1079***/
1080
1081struct evbuffer *
1082tr_variantToBuf (const tr_variant * v, tr_variant_fmt fmt)
1083{
1084  char lc_numeric[128];
1085  struct evbuffer * buf = evbuffer_new();
1086
1087  /* parse with LC_NUMERIC="C" to ensure a "." decimal separator */
1088  tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
1089  setlocale (LC_NUMERIC, "C");
1090
1091  evbuffer_expand (buf, 4096); /* alloc a little memory to start off with */
1092
1093  switch (fmt)
1094    {
1095      case TR_VARIANT_FMT_BENC:
1096        tr_variantToBufBenc (v, buf);
1097        break;
1098
1099      case TR_VARIANT_FMT_JSON:
1100        tr_variantToBufJson (v, buf, false);
1101        break;
1102
1103      case TR_VARIANT_FMT_JSON_LEAN:
1104        tr_variantToBufJson (v, buf, true);
1105        break;
1106    }
1107
1108  /* restore the previous locale */
1109  setlocale (LC_NUMERIC, lc_numeric);
1110  return buf;
1111}
1112
1113char*
1114tr_variantToStr (const tr_variant * v, tr_variant_fmt fmt, int * len)
1115{
1116  struct evbuffer * buf = tr_variantToBuf (v, fmt);
1117  const size_t n = evbuffer_get_length (buf);
1118  char * ret = evbuffer_free_to_str (buf);
1119  if (len != NULL)
1120    *len = (int) n;
1121  return ret;
1122}
1123
1124int
1125tr_variantToFile (const tr_variant  * v,
1126                  tr_variant_fmt      fmt,
1127                  const char        * filename)
1128{
1129  char * tmp;
1130  tr_sys_file_t fd;
1131  int err = 0;
1132  char * real_filename;
1133  tr_error * error = NULL;
1134
1135  /* follow symlinks to find the "real" file, to make sure the temporary
1136   * we build with tr_sys_file_open_temp() is created on the right partition */
1137  if ((real_filename = tr_sys_path_resolve (filename, NULL)) != NULL)
1138    filename = real_filename;
1139
1140  /* if the file already exists, try to move it out of the way & keep it as a backup */
1141  tmp = tr_strdup_printf ("%s.tmp.XXXXXX", filename);
1142  fd = tr_sys_file_open_temp (tmp, &error);
1143  if (fd != TR_BAD_SYS_FILE)
1144    {
1145      uint64_t nleft;
1146
1147      /* save the variant to a temporary file */
1148      {
1149        struct evbuffer * buf = tr_variantToBuf (v, fmt);
1150        const char * walk = (const char *) evbuffer_pullup (buf, -1);
1151        nleft = evbuffer_get_length (buf);
1152
1153        while (nleft > 0)
1154          {
1155            uint64_t n;
1156            if (!tr_sys_file_write (fd, walk, nleft, &n, &error))
1157              {
1158                err = error->code;
1159                break;
1160              }
1161
1162            nleft -= n;
1163            walk += n;
1164          }
1165
1166        evbuffer_free (buf);
1167      }
1168
1169      tr_sys_file_close (fd, NULL);
1170
1171      if (nleft > 0)
1172        {
1173          tr_logAddError (_("Couldn't save temporary file \"%1$s\": %2$s"), tmp, error->message);
1174          tr_sys_path_remove (tmp, NULL);
1175          tr_error_free (error);
1176        }
1177      else
1178        {
1179          tr_error_clear (&error);
1180          if (tr_sys_path_rename (tmp, filename, &error))
1181            {
1182              tr_logAddInfo (_("Saved \"%s\""), filename);
1183            }
1184          else
1185            {
1186              err = error->code;
1187              tr_logAddError (_("Couldn't save file \"%1$s\": %2$s"), filename, error->message);
1188              tr_sys_path_remove (tmp, NULL);
1189              tr_error_free (error);
1190            }
1191        }
1192    }
1193  else
1194    {
1195      err = error->code;
1196      tr_logAddError (_("Couldn't save temporary file \"%1$s\": %2$s"), tmp, error->message);
1197      tr_error_free (error);
1198    }
1199
1200  tr_free (tmp);
1201  tr_free (real_filename);
1202  return err;
1203}
1204
1205/***
1206****
1207***/
1208
1209int
1210tr_variantFromFile (tr_variant      * setme,
1211                    tr_variant_fmt    fmt,
1212                    const char      * filename)
1213{
1214  int err;
1215  size_t buflen;
1216  uint8_t * buf;
1217  const int old_errno = errno;
1218
1219  errno = 0;
1220  buf = tr_loadFile (filename, &buflen);
1221
1222  if (errno)
1223    err = errno;
1224  else
1225    err = tr_variantFromBuf (setme, fmt, buf, buflen, filename, NULL);
1226
1227  tr_free (buf);
1228  errno = old_errno;
1229  return err;
1230}
1231
1232int
1233tr_variantFromBuf (tr_variant      * setme,
1234                   tr_variant_fmt    fmt,
1235                   const void      * buf,
1236                   size_t            buflen,
1237                   const char      * optional_source,
1238                   const char     ** setme_end)
1239{
1240  int err;
1241  char lc_numeric[128];
1242
1243  /* parse with LC_NUMERIC="C" to ensure a "." decimal separator */
1244  tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
1245  setlocale (LC_NUMERIC, "C");
1246
1247  switch (fmt)
1248    {
1249      case TR_VARIANT_FMT_JSON:
1250      case TR_VARIANT_FMT_JSON_LEAN:
1251        err = tr_jsonParse (optional_source, buf, buflen, setme, setme_end);
1252        break;
1253
1254      default /* TR_VARIANT_FMT_BENC */:
1255        err = tr_variantParseBenc (buf, ((const char*)buf)+buflen, setme, setme_end);
1256        break;
1257    }
1258
1259  /* restore the previous locale */
1260  setlocale (LC_NUMERIC, lc_numeric);
1261  return err;
1262}
Note: See TracBrowser for help on using the repository browser.