source: trunk/libtransmission/variant.c @ 13712

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

(trunk, libT) #5201 'tr_variantDict can be much faster' -- don't test to see if each dictionary value that's a string is a quark... it almost never is, so the lookup isn't worth it.

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