source: trunk/libtransmission/variant.c @ 13669

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

copyediting: indentation, whitespace

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