source: trunk/libtransmission/variant.c @ 14320

Last change on this file since 14320 was 14320, checked in by jordan, 8 years ago

(trunk, libT) #4160 'foreign character support' -- merge mike.dld's 4160-02b-path.patch, which updates the codebase to use the new tr_sys_path_*() portability wrappers introduced in 4160-02a

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