source: trunk/libtransmission/variant.c @ 14533

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

#5851: Use per-thread locale setup if possible

This prevents crashes on concurrent tr_variantFromBuf, tr_variantToBuf
and tr_variantGetReal use.

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