source: trunk/libtransmission/variant.c @ 13672

Last change on this file since 13672 was 13672, checked in by jordan, 9 years ago

(trunk, libT) faster JSON parsing for tr_variant. This mostly helps the Qt client, which makes heavy use of the JSON-based RPC calls.

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