source: trunk/libtransmission/variant.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

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