1/*************** json CPP Declares Source Code File (.H) ***************/
2/* Name: json.cpp Version 1.4 */
3/* */
4/* (C) Copyright to the author Olivier BERTRAND 2014 - 2017 */
5/* */
6/* This file contains the JSON classes functions. */
7/***********************************************************************/
8
9/***********************************************************************/
10/* Include relevant sections of the MariaDB header file. */
11/***********************************************************************/
12#include <my_global.h>
13
14/***********************************************************************/
15/* Include application header files: */
16/* global.h is header containing all global declarations. */
17/* plgdbsem.h is header containing the DB application declarations. */
18/* xjson.h is header containing the JSON classes declarations. */
19/***********************************************************************/
20#include "global.h"
21#include "plgdbsem.h"
22#include "json.h"
23
24#define ARGS MY_MIN(24,len-i),s+MY_MAX(i-3,0)
25
26#if defined(__WIN__)
27#define EL "\r\n"
28#else
29#define EL "\n"
30#undef SE_CATCH // Does not work for Linux
31#endif
32
33#if defined(SE_CATCH)
34/**************************************************************************/
35/* This is the support of catching C interrupts to prevent crashes. */
36/**************************************************************************/
37#include <eh.h>
38
39class SE_Exception {
40public:
41 SE_Exception(unsigned int n, PEXCEPTION_RECORD p) : nSE(n), eRec(p) {}
42 ~SE_Exception() {}
43
44 unsigned int nSE;
45 PEXCEPTION_RECORD eRec;
46}; // end of class SE_Exception
47
48void trans_func(unsigned int u, _EXCEPTION_POINTERS* pExp)
49{
50 throw SE_Exception(u, pExp->ExceptionRecord);
51} // end of trans_func
52
53char *GetExceptionDesc(PGLOBAL g, unsigned int e);
54#endif // SE_CATCH
55
56char *GetJsonNull(void);
57
58/***********************************************************************/
59/* IsNum: check whether this string is all digits. */
60/***********************************************************************/
61bool IsNum(PSZ s)
62{
63 for (char *p = s; *p; p++)
64 if (*p == ']')
65 break;
66 else if (!isdigit(*p) || *p == '-')
67 return false;
68
69 return true;
70} // end of IsNum
71
72/***********************************************************************/
73/* NextChr: return the first found '[' or Sep pointer. */
74/***********************************************************************/
75char *NextChr(PSZ s, char sep)
76{
77 char *p1 = strchr(s, '[');
78 char *p2 = strchr(s, sep);
79
80 if (!p2)
81 return p1;
82 else if (p1)
83 return MY_MIN(p1, p2);
84
85 return p2;
86} // end of NextChr
87
88
89/***********************************************************************/
90/* Parse a json string. */
91/* Note: when pretty is not known, the caller set pretty to 3. */
92/***********************************************************************/
93PJSON ParseJson(PGLOBAL g, char *s, int len, int *ptyp, bool *comma)
94{
95 int i, pretty = (ptyp) ? *ptyp : 3;
96 bool b = false, pty[3] = {true, true, true};
97 PJSON jsp = NULL;
98 STRG src;
99
100 if (trace(1))
101 htrc("ParseJson: s=%.10s len=%d\n", s, len);
102
103 if (!s || !len) {
104 strcpy(g->Message, "Void JSON object");
105 return NULL;
106 } else if (comma)
107 *comma = false;
108
109 src.str = s;
110 src.len = len;
111
112 // Trying to guess the pretty format
113 if (s[0] == '[' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n')))
114 pty[0] = false;
115
116 try {
117 for (i = 0; i < len; i++)
118 switch (s[i]) {
119 case '[':
120 if (jsp)
121 goto tryit;
122 else if (!(jsp = ParseArray(g, ++i, src, pty)))
123 throw 1;
124
125 break;
126 case '{':
127 if (jsp)
128 goto tryit;
129 else if (!(jsp = ParseObject(g, ++i, src, pty)))
130 throw 2;
131
132 break;
133 case ' ':
134 case '\t':
135 case '\n':
136 case '\r':
137 break;
138 case ',':
139 if (jsp && (pretty == 1 || pretty == 3)) {
140 if (comma)
141 *comma = true;
142
143 pty[0] = pty[2] = false;
144 break;
145 } // endif pretty
146
147 sprintf(g->Message, "Unexpected ',' (pretty=%d)", pretty);
148 throw 3;
149 case '(':
150 b = true;
151 break;
152 case ')':
153 if (b) {
154 b = false;
155 break;
156 } // endif b
157
158 default:
159 if (jsp)
160 goto tryit;
161 else if (!(jsp = ParseValue(g, i, src, pty)))
162 throw 4;
163
164 break;
165 }; // endswitch s[i]
166
167 if (!jsp)
168 sprintf(g->Message, "Invalid Json string '%.*s'", MY_MIN(len, 50), s);
169 else if (ptyp && pretty == 3) {
170 *ptyp = 3; // Not recognized pretty
171
172 for (i = 0; i < 3; i++)
173 if (pty[i]) {
174 *ptyp = i;
175 break;
176 } // endif pty
177
178 } // endif ptyp
179
180 } catch (int n) {
181 if (trace(1))
182 htrc("Exception %d: %s\n", n, g->Message);
183 jsp = NULL;
184 } catch (const char *msg) {
185 strcpy(g->Message, msg);
186 jsp = NULL;
187 } // end catch
188
189 return jsp;
190
191tryit:
192 if (pty[0] && (!pretty || pretty > 2)) {
193 if ((jsp = ParseArray(g, (i = 0), src, pty)) && ptyp && pretty == 3)
194 *ptyp = (pty[0]) ? 0 : 3;
195
196 return jsp;
197 } else
198 strcpy(g->Message, "More than one item in file");
199
200 return NULL;
201} // end of ParseJson
202
203/***********************************************************************/
204/* Parse a JSON Array. */
205/***********************************************************************/
206PJAR ParseArray(PGLOBAL g, int& i, STRG& src, bool *pty)
207{
208 char *s = src.str;
209 int len = src.len;
210 int level = 0;
211 bool b = (!i);
212 PJAR jarp = new(g) JARRAY;
213 PJVAL jvp = NULL;
214
215 for (; i < len; i++)
216 switch (s[i]) {
217 case ',':
218 if (level < 2) {
219 sprintf(g->Message, "Unexpected ',' near %.*s",ARGS);
220 return NULL;
221 } else
222 level = 1;
223
224 break;
225 case ']':
226 if (level == 1) {
227 sprintf(g->Message, "Unexpected ',]' near %.*s", ARGS);
228 return NULL;
229 } // endif level
230
231 jarp->InitArray(g);
232 return jarp;
233 case '\n':
234 if (!b)
235 pty[0] = pty[1] = false;
236 case '\r':
237 case ' ':
238 case '\t':
239 break;
240 default:
241 if (level == 2) {
242 sprintf(g->Message, "Unexpected value near %.*s", ARGS);
243 return NULL;
244 } else if ((jvp = ParseValue(g, i, src, pty)))
245 jarp->AddValue(g, jvp);
246 else
247 return NULL;
248
249 level = (b) ? 1 : 2;
250 break;
251 }; // endswitch s[i]
252
253 if (b) {
254 // Case of Pretty == 0
255 jarp->InitArray(g);
256 return jarp;
257 } // endif b
258
259 strcpy(g->Message, "Unexpected EOF in array");
260 return NULL;
261} // end of ParseArray
262
263/***********************************************************************/
264/* Parse a JSON Object. */
265/***********************************************************************/
266PJOB ParseObject(PGLOBAL g, int& i, STRG& src, bool *pty)
267{
268 PSZ key;
269 char *s = src.str;
270 int len = src.len;
271 int level = 0;
272 PJOB jobp = new(g) JOBJECT;
273 PJPR jpp = NULL;
274
275 for (; i < len; i++)
276 switch (s[i]) {
277 case '"':
278 if (level < 2) {
279 if ((key = ParseString(g, ++i, src))) {
280 jpp = jobp->AddPair(g, key);
281 level = 1;
282 } else
283 return NULL;
284
285 } else {
286 sprintf(g->Message, "misplaced string near %.*s", ARGS);
287 return NULL;
288 } // endif level
289
290 break;
291 case ':':
292 if (level == 1) {
293 if (!(jpp->Val = ParseValue(g, ++i, src, pty)))
294 return NULL;
295
296 level = 2;
297 } else {
298 sprintf(g->Message, "Unexpected ':' near %.*s", ARGS);
299 return NULL;
300 } // endif level
301
302 break;
303 case ',':
304 if (level < 2) {
305 sprintf(g->Message, "Unexpected ',' near %.*s", ARGS);
306 return NULL;
307 } else
308 level = 1;
309
310 break;
311 case '}':
312 if (level == 1) {
313 sprintf(g->Message, "Unexpected '}' near %.*s", ARGS);
314 return NULL;
315 } // endif level
316
317 return jobp;
318 case '\n':
319 pty[0] = pty[1] = false;
320 case '\r':
321 case ' ':
322 case '\t':
323 break;
324 default:
325 sprintf(g->Message, "Unexpected character '%c' near %.*s",
326 s[i], ARGS);
327 return NULL;
328 }; // endswitch s[i]
329
330 strcpy(g->Message, "Unexpected EOF in Object");
331 return NULL;
332} // end of ParseObject
333
334/***********************************************************************/
335/* Parse a JSON Value. */
336/***********************************************************************/
337PJVAL ParseValue(PGLOBAL g, int& i, STRG& src, bool *pty)
338{
339 char *strval, *s = src.str;
340 int n, len = src.len;
341 PJVAL jvp = new(g) JVALUE;
342
343 for (; i < len; i++)
344 switch (s[i]) {
345 case '\n':
346 pty[0] = pty[1] = false;
347 case '\r':
348 case ' ':
349 case '\t':
350 break;
351 default:
352 goto suite;
353 } // endswitch
354
355 suite:
356 switch (s[i]) {
357 case '[':
358 if (!(jvp->Jsp = ParseArray(g, ++i, src, pty)))
359 return NULL;
360
361 break;
362 case '{':
363 if (!(jvp->Jsp = ParseObject(g, ++i, src, pty)))
364 return NULL;
365
366 break;
367 case '"':
368 if ((strval = ParseString(g, ++i, src)))
369 jvp->Value = AllocateValue(g, strval, TYPE_STRING);
370 else
371 return NULL;
372
373 break;
374 case 't':
375 if (!strncmp(s + i, "true", 4)) {
376 n = 1;
377 jvp->Value = AllocateValue(g, &n, TYPE_TINY);
378 i += 3;
379 } else
380 goto err;
381
382 break;
383 case 'f':
384 if (!strncmp(s + i, "false", 5)) {
385 n = 0;
386 jvp->Value = AllocateValue(g, &n, TYPE_TINY);
387 i += 4;
388 } else
389 goto err;
390
391 break;
392 case 'n':
393 if (!strncmp(s + i, "null", 4))
394 i += 3;
395 else
396 goto err;
397
398 break;
399 case '-':
400 default:
401 if (s[i] == '-' || isdigit(s[i])) {
402 if (!(jvp->Value = ParseNumeric(g, i, src)))
403 goto err;
404
405 } else
406 goto err;
407
408 }; // endswitch s[i]
409
410 return jvp;
411
412err:
413 sprintf(g->Message, "Unexpected character '%c' near %.*s",
414 s[i], ARGS);
415 return NULL;
416} // end of ParseValue
417
418/***********************************************************************/
419/* Unescape and parse a JSON string. */
420/***********************************************************************/
421char *ParseString(PGLOBAL g, int& i, STRG& src)
422{
423 char *s = src.str;
424 uchar *p;
425 int n = 0, len = src.len;
426
427 // Be sure of memory availability
428 if (len + 1 - i > (signed)((PPOOLHEADER)g->Sarea)->FreeBlk) {
429 strcpy(g->Message, "ParseString: Out of memory");
430 return NULL;
431 } // endif len
432
433 // The size to allocate is not known yet
434 p = (uchar*)PlugSubAlloc(g, NULL, 0);
435
436 for (; i < len; i++)
437 switch (s[i]) {
438 case '"':
439 p[n++] = 0;
440 PlugSubAlloc(g, NULL, n);
441 return (char*)p;
442 case '\\':
443 if (++i < len) {
444 if (s[i] == 'u') {
445 if (len - i > 5) {
446// if (charset == utf8) {
447 char xs[5];
448 uint hex;
449
450 xs[0] = s[++i];
451 xs[1] = s[++i];
452 xs[2] = s[++i];
453 xs[3] = s[++i];
454 xs[4] = 0;
455 hex = strtoul(xs, NULL, 16);
456
457 if (hex < 0x80) {
458 p[n] = (uchar)hex;
459 } else if (hex < 0x800) {
460 p[n++] = (uchar)(0xC0 | (hex >> 6));
461 p[n] = (uchar)(0x80 | (hex & 0x3F));
462 } else if (hex < 0x10000) {
463 p[n++] = (uchar)(0xE0 | (hex >> 12));
464 p[n++] = (uchar)(0x80 | ((hex >> 6) & 0x3f));
465 p[n] = (uchar)(0x80 | (hex & 0x3f));
466 } else
467 p[n] = '?';
468
469#if 0
470 } else {
471 char xs[3];
472 UINT hex;
473
474 i += 2;
475 xs[0] = s[++i];
476 xs[1] = s[++i];
477 xs[2] = 0;
478 hex = strtoul(xs, NULL, 16);
479 p[n] = (char)hex;
480 } // endif charset
481#endif // 0
482 } else
483 goto err;
484
485 } else switch(s[i]) {
486 case 't': p[n] = '\t'; break;
487 case 'n': p[n] = '\n'; break;
488 case 'r': p[n] = '\r'; break;
489 case 'b': p[n] = '\b'; break;
490 case 'f': p[n] = '\f'; break;
491 default: p[n] = s[i]; break;
492 } // endswitch
493
494 n++;
495 } else
496 goto err;
497
498 break;
499 default:
500 p[n++] = s[i];
501 break;
502 }; // endswitch s[i]
503
504 err:
505 strcpy(g->Message, "Unexpected EOF in String");
506 return NULL;
507} // end of ParseString
508
509/***********************************************************************/
510/* Parse a JSON numeric value. */
511/***********************************************************************/
512PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src)
513{
514 char *s = src.str, buf[50];
515 int n = 0, len = src.len;
516 short nd = 0;
517 bool has_dot = false;
518 bool has_e = false;
519 bool found_digit = false;
520 PVAL valp = NULL;
521
522 for (; i < len; i++) {
523 switch (s[i]) {
524 case '.':
525 if (!found_digit || has_dot || has_e)
526 goto err;
527
528 has_dot = true;
529 break;
530 case 'e':
531 case 'E':
532 if (!found_digit || has_e)
533 goto err;
534
535 has_e = true;
536 found_digit = false;
537 break;
538 case '+':
539 if (!has_e)
540 goto err;
541
542 // fall through
543 case '-':
544 if (found_digit)
545 goto err;
546
547 break;
548 default:
549 if (isdigit(s[i])) {
550 if (has_dot && !has_e)
551 nd++; // Number of decimals
552
553 found_digit = true;
554 } else
555 goto fin;
556
557 }; // endswitch s[i]
558
559 buf[n++] = s[i];
560 } // endfor i
561
562 fin:
563 if (found_digit) {
564 buf[n] = 0;
565
566 if (has_dot || has_e) {
567 double dv = strtod(buf, NULL);
568
569 valp = AllocateValue(g, &dv, TYPE_DOUBLE, nd);
570 } else {
571 long long iv = strtoll(buf, NULL, 10);
572
573 valp = AllocateValue(g, &iv, TYPE_BIGINT);
574 } // endif has
575
576 i--; // Unstack following character
577 return valp;
578 } else {
579 strcpy(g->Message, "No digit found");
580 return NULL;
581 } // endif found_digit
582
583 err:
584 strcpy(g->Message, "Unexpected EOF in number");
585 return NULL;
586} // end of ParseNumeric
587
588/***********************************************************************/
589/* Serialize a JSON tree: */
590/***********************************************************************/
591PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty)
592{
593 PSZ str = NULL;
594 bool b = false, err = true;
595 JOUT *jp;
596 FILE *fs = NULL;
597
598 g->Message[0] = 0;
599
600 try {
601 if (!jsp) {
602 strcpy(g->Message, "Null json tree");
603 throw 1;
604 } else if (!fn) {
605 // Serialize to a string
606 jp = new(g) JOUTSTR(g);
607 b = pretty == 1;
608 } else {
609 if (!(fs = fopen(fn, "wb"))) {
610 sprintf(g->Message, MSG(OPEN_MODE_ERROR),
611 "w", (int)errno, fn);
612 strcat(strcat(g->Message, ": "), strerror(errno));
613 throw 2;
614 } else if (pretty >= 2) {
615 // Serialize to a pretty file
616 jp = new(g)JOUTPRT(g, fs);
617 } else {
618 // Serialize to a flat file
619 b = true;
620 jp = new(g)JOUTFILE(g, fs, pretty);
621 } // endif's
622
623 } // endif's
624
625 switch (jsp->GetType()) {
626 case TYPE_JAR:
627 err = SerializeArray(jp, (PJAR)jsp, b);
628 break;
629 case TYPE_JOB:
630 err = ((b && jp->Prty()) && jp->WriteChr('\t'));
631 err |= SerializeObject(jp, (PJOB)jsp);
632 break;
633 case TYPE_JVAL:
634 err = SerializeValue(jp, (PJVAL)jsp);
635 break;
636 default:
637 strcpy(g->Message, "Invalid json tree");
638 } // endswitch Type
639
640 if (fs) {
641 fputs(EL, fs);
642 fclose(fs);
643 str = (err) ? NULL : strcpy(g->Message, "Ok");
644 } else if (!err) {
645 str = ((JOUTSTR*)jp)->Strp;
646 jp->WriteChr('\0');
647 PlugSubAlloc(g, NULL, ((JOUTSTR*)jp)->N);
648 } else {
649 if (!g->Message[0])
650 strcpy(g->Message, "Error in Serialize");
651
652 } // endif's
653
654 } catch (int n) {
655 if (trace(1))
656 htrc("Exception %d: %s\n", n, g->Message);
657 str = NULL;
658 } catch (const char *msg) {
659 strcpy(g->Message, msg);
660 str = NULL;
661 } // end catch
662
663 return str;
664} // end of Serialize
665
666/***********************************************************************/
667/* Serialize a JSON Array. */
668/***********************************************************************/
669bool SerializeArray(JOUT *js, PJAR jarp, bool b)
670{
671 bool first = true;
672
673 if (b) {
674 if (js->Prty()) {
675 if (js->WriteChr('['))
676 return true;
677 else if (js->Prty() == 1 && (js->WriteStr(EL) || js->WriteChr('\t')))
678 return true;
679
680 } // endif Prty
681
682 } else if (js->WriteChr('['))
683 return true;
684
685 for (int i = 0; i < jarp->size(); i++) {
686 if (first)
687 first = false;
688 else if ((!b || js->Prty()) && js->WriteChr(','))
689 return true;
690 else if (b) {
691 if (js->Prty() < 2 && js->WriteStr(EL))
692 return true;
693 else if (js->Prty() == 1 && js->WriteChr('\t'))
694 return true;
695
696 } // endif b
697
698 if (SerializeValue(js, jarp->GetValue(i)))
699 return true;
700
701 } // endfor i
702
703 if (b && js->Prty() == 1 && js->WriteStr(EL))
704 return true;
705
706 return ((!b || js->Prty()) && js->WriteChr(']'));
707} // end of SerializeArray
708
709/***********************************************************************/
710/* Serialize a JSON Object. */
711/***********************************************************************/
712bool SerializeObject(JOUT *js, PJOB jobp)
713{
714 bool first = true;
715
716 if (js->WriteChr('{'))
717 return true;
718
719 for (PJPR pair = jobp->First; pair; pair = pair->Next) {
720 if (first)
721 first = false;
722 else if (js->WriteChr(','))
723 return true;
724
725 if (js->WriteChr('"') ||
726 js->WriteStr(pair->Key) ||
727 js->WriteChr('"') ||
728 js->WriteChr(':') ||
729 SerializeValue(js, pair->Val))
730 return true;
731
732 } // endfor i
733
734 return js->WriteChr('}');
735} // end of SerializeObject
736
737/***********************************************************************/
738/* Serialize a JSON Value. */
739/***********************************************************************/
740bool SerializeValue(JOUT *js, PJVAL jvp)
741{
742 PJAR jap;
743 PJOB jop;
744 PVAL valp;
745
746 if ((jap = jvp->GetArray()))
747 return SerializeArray(js, jap, false);
748 else if ((jop = jvp->GetObject()))
749 return SerializeObject(js, jop);
750 else if (!(valp = jvp->Value) || valp->IsNull())
751 return js->WriteStr("null");
752 else switch (valp->GetType()) {
753 case TYPE_TINY:
754 return js->WriteStr(valp->GetTinyValue() ? "true" : "false");
755 case TYPE_STRING:
756 return js->Escape(valp->GetCharValue());
757 default:
758 if (valp->IsTypeNum()) {
759 char buf[32];
760
761 return js->WriteStr(valp->GetCharString(buf));
762 } // endif valp
763
764 } // endswitch Type
765
766 strcpy(js->g->Message, "Unrecognized value");
767 return true;
768} // end of SerializeValue
769
770/* -------------------------- Class JOUTSTR -------------------------- */
771
772/***********************************************************************/
773/* JOUTSTR constructor. */
774/***********************************************************************/
775JOUTSTR::JOUTSTR(PGLOBAL g) : JOUT(g)
776{
777 PPOOLHEADER pph = (PPOOLHEADER)g->Sarea;
778
779 N = 0;
780 Max = pph->FreeBlk;
781 Max = (Max > 32) ? Max - 32 : Max;
782 Strp = (char*)PlugSubAlloc(g, NULL, 0); // Size not know yet
783} // end of JOUTSTR constructor
784
785/***********************************************************************/
786/* Concatenate a string to the Serialize string. */
787/***********************************************************************/
788bool JOUTSTR::WriteStr(const char *s)
789{
790 if (s) {
791 size_t len = strlen(s);
792
793 if (N + len > Max)
794 return true;
795
796 memcpy(Strp + N, s, len);
797 N += len;
798 return false;
799 } else
800 return true;
801
802} // end of WriteStr
803
804/***********************************************************************/
805/* Concatenate a character to the Serialize string. */
806/***********************************************************************/
807bool JOUTSTR::WriteChr(const char c)
808{
809 if (N + 1 > Max)
810 return true;
811
812 Strp[N++] = c;
813 return false;
814} // end of WriteChr
815
816/***********************************************************************/
817/* Escape and Concatenate a string to the Serialize string. */
818/***********************************************************************/
819bool JOUTSTR::Escape(const char *s)
820{
821 WriteChr('"');
822
823 for (unsigned int i = 0; s[i]; i++)
824 switch (s[i]) {
825 case '"':
826 case '\\':
827 case '\t':
828 case '\n':
829 case '\r':
830 case '\b':
831 case '\f': WriteChr('\\');
832 // fall through
833 default:
834 WriteChr(s[i]);
835 break;
836 } // endswitch s[i]
837
838 WriteChr('"');
839 return false;
840} // end of Escape
841
842/* ------------------------- Class JOUTFILE -------------------------- */
843
844/***********************************************************************/
845/* Write a string to the Serialize file. */
846/***********************************************************************/
847bool JOUTFILE::WriteStr(const char *s)
848{
849 // This is temporary
850 fputs(s, Stream);
851 return false;
852} // end of WriteStr
853
854/***********************************************************************/
855/* Write a character to the Serialize file. */
856/***********************************************************************/
857bool JOUTFILE::WriteChr(const char c)
858{
859 // This is temporary
860 fputc(c, Stream);
861 return false;
862} // end of WriteChr
863
864/***********************************************************************/
865/* Escape and Concatenate a string to the Serialize string. */
866/***********************************************************************/
867bool JOUTFILE::Escape(const char *s)
868{
869 // This is temporary
870 fputc('"', Stream);
871
872 for (unsigned int i = 0; s[i]; i++)
873 switch (s[i]) {
874 case '"': fputs("\\\"", Stream); break;
875 case '\\': fputs("\\\\", Stream); break;
876 case '\t': fputs("\\t", Stream); break;
877 case '\n': fputs("\\n", Stream); break;
878 case '\r': fputs("\\r", Stream); break;
879 case '\b': fputs("\\b", Stream); break;
880 case '\f': fputs("\\f", Stream); break;
881 default:
882 fputc(s[i], Stream);
883 break;
884 } // endswitch s[i]
885
886 fputc('"', Stream);
887 return false;
888} // end of Escape
889
890/* ------------------------- Class JOUTPRT --------------------------- */
891
892/***********************************************************************/
893/* Write a string to the Serialize pretty file. */
894/***********************************************************************/
895bool JOUTPRT::WriteStr(const char *s)
896{
897 // This is temporary
898 if (B) {
899 fputs(EL, Stream);
900 M--;
901
902 for (int i = 0; i < M; i++)
903 fputc('\t', Stream);
904
905 B = false;
906 } // endif B
907
908 fputs(s, Stream);
909 return false;
910} // end of WriteStr
911
912/***********************************************************************/
913/* Write a character to the Serialize pretty file. */
914/***********************************************************************/
915bool JOUTPRT::WriteChr(const char c)
916{
917 switch (c) {
918 case ':':
919 fputs(": ", Stream);
920 break;
921 case '{':
922 case '[':
923#if 0
924 if (M)
925 fputs(EL, Stream);
926
927 for (int i = 0; i < M; i++)
928 fputc('\t', Stream);
929#endif // 0
930
931 fputc(c, Stream);
932 fputs(EL, Stream);
933 M++;
934
935 for (int i = 0; i < M; i++)
936 fputc('\t', Stream);
937
938 break;
939 case '}':
940 case ']':
941 M--;
942 fputs(EL, Stream);
943
944 for (int i = 0; i < M; i++)
945 fputc('\t', Stream);
946
947 fputc(c, Stream);
948 B = true;
949 break;
950 case ',':
951 fputc(c, Stream);
952 fputs(EL, Stream);
953
954 for (int i = 0; i < M; i++)
955 fputc('\t', Stream);
956
957 B = false;
958 break;
959 default:
960 fputc(c, Stream);
961 } // endswitch c
962
963return false;
964} // end of WriteChr
965
966/* -------------------------- Class JOBJECT -------------------------- */
967
968/***********************************************************************/
969/* Return the number of pairs in this object. */
970/***********************************************************************/
971int JOBJECT::GetSize(bool b)
972{
973 if (b) {
974 // Return only non null pairs
975 int n = 0;
976
977 for (PJPR jpp = First; jpp; jpp = jpp->Next)
978 if (jpp->Val && !jpp->Val->IsNull())
979 n++;
980
981 return n;
982 } else
983 return Size;
984
985} // end of GetSize
986
987/***********************************************************************/
988/* Add a new pair to an Object. */
989/***********************************************************************/
990PJPR JOBJECT::AddPair(PGLOBAL g, PCSZ key)
991{
992 PJPR jpp = new(g) JPAIR(key);
993
994 if (Last)
995 Last->Next = jpp;
996 else
997 First = jpp;
998
999 Last = jpp;
1000 Size++;
1001 return jpp;
1002} // end of AddPair
1003
1004/***********************************************************************/
1005/* Return all keys as an array. */
1006/***********************************************************************/
1007PJAR JOBJECT::GetKeyList(PGLOBAL g)
1008{
1009 PJAR jarp = new(g) JARRAY();
1010
1011 for (PJPR jpp = First; jpp; jpp = jpp->Next)
1012 jarp->AddValue(g, new(g) JVALUE(g, jpp->GetKey()));
1013
1014 jarp->InitArray(g);
1015 return jarp;
1016} // end of GetKeyList
1017
1018/***********************************************************************/
1019/* Return all values as an array. */
1020/***********************************************************************/
1021PJAR JOBJECT::GetValList(PGLOBAL g)
1022{
1023 PJAR jarp = new(g) JARRAY();
1024
1025 for (PJPR jpp = First; jpp; jpp = jpp->Next)
1026 jarp->AddValue(g, jpp->GetVal());
1027
1028 jarp->InitArray(g);
1029 return jarp;
1030} // end of GetValList
1031
1032/***********************************************************************/
1033/* Get the value corresponding to the given key. */
1034/***********************************************************************/
1035PJVAL JOBJECT::GetValue(const char* key)
1036{
1037 for (PJPR jp = First; jp; jp = jp->Next)
1038 if (!strcmp(jp->Key, key))
1039 return jp->Val;
1040
1041 return NULL;
1042} // end of GetValue;
1043
1044/***********************************************************************/
1045/* Return the text corresponding to all keys (XML like). */
1046/***********************************************************************/
1047PSZ JOBJECT::GetText(PGLOBAL g, PSZ text)
1048{
1049 int n;
1050
1051 if (!text) {
1052 text = (char*)PlugSubAlloc(g, NULL, 0);
1053 text[0] = 0;
1054 n = 1;
1055 } else
1056 n = 0;
1057
1058 if (!First && n)
1059 return NULL;
1060 else if (n == 1 && Size == 1 && !strcmp(First->GetKey(), "$date")) {
1061 int i;
1062
1063 First->Val->GetText(g, text);
1064 i = (text[1] == '-' ? 2 : 1);
1065
1066 if (IsNum(text + i)) {
1067 // Date is in milliseconds
1068 int j = (int)strlen(text);
1069
1070 if (j >= 4 + i)
1071 text[j - 3] = 0; // Change it to seconds
1072 else
1073 strcpy(text, " 0");
1074
1075 } // endif text
1076
1077 } else for (PJPR jp = First; jp; jp = jp->Next)
1078 jp->Val->GetText(g, text);
1079
1080 if (n)
1081 PlugSubAlloc(g, NULL, strlen(text) + 1);
1082
1083 return text + n;
1084} // end of GetText;
1085
1086/***********************************************************************/
1087/* Merge two objects. */
1088/***********************************************************************/
1089bool JOBJECT::Merge(PGLOBAL g, PJSON jsp)
1090{
1091 if (jsp->GetType() != TYPE_JOB) {
1092 strcpy(g->Message, "Second argument is not an object");
1093 return true;
1094 } // endif Type
1095
1096 PJOB jobp = (PJOB)jsp;
1097
1098 for (PJPR jpp = jobp->First; jpp; jpp = jpp->Next)
1099 SetValue(g, jpp->GetVal(), jpp->GetKey());
1100
1101 return false;
1102} // end of Marge;
1103
1104/***********************************************************************/
1105/* Set or add a value corresponding to the given key. */
1106/***********************************************************************/
1107void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PCSZ key)
1108{
1109 PJPR jp;
1110
1111 for (jp = First; jp; jp = jp->Next)
1112 if (!strcmp(jp->Key, key)) {
1113 jp->Val = jvp;
1114 break;
1115 } // endif key
1116
1117 if (!jp) {
1118 jp = AddPair(g, key);
1119 jp->Val = jvp;
1120 } // endif jp
1121
1122} // end of SetValue
1123
1124/***********************************************************************/
1125/* Delete a value corresponding to the given key. */
1126/***********************************************************************/
1127void JOBJECT::DeleteKey(PCSZ key)
1128{
1129 PJPR jp, *pjp = &First;
1130
1131 for (jp = First; jp; jp = jp->Next)
1132 if (!strcmp(jp->Key, key)) {
1133 *pjp = jp->Next;
1134 Size--;
1135 break;
1136 } else
1137 pjp = &jp->Next;
1138
1139} // end of DeleteKey
1140
1141/***********************************************************************/
1142/* True if void or if all members are nulls. */
1143/***********************************************************************/
1144bool JOBJECT::IsNull(void)
1145{
1146 for (PJPR jp = First; jp; jp = jp->Next)
1147 if (!jp->Val->IsNull())
1148 return false;
1149
1150 return true;
1151} // end of IsNull
1152
1153/* -------------------------- Class JARRAY --------------------------- */
1154
1155/***********************************************************************/
1156/* Return the number of values in this object. */
1157/***********************************************************************/
1158int JARRAY::GetSize(bool b)
1159{
1160 if (b) {
1161 // Return only non null values
1162 int n = 0;
1163
1164 for (PJVAL jvp = First; jvp; jvp = jvp->Next)
1165 if (!jvp->IsNull())
1166 n++;
1167
1168 return n;
1169 } else
1170 return Size;
1171
1172} // end of GetSize
1173
1174/***********************************************************************/
1175/* Make the array of values from the values list. */
1176/***********************************************************************/
1177void JARRAY::InitArray(PGLOBAL g)
1178{
1179 int i;
1180 PJVAL jvp, *pjvp = &First;
1181
1182 for (Size = 0, jvp = First; jvp; jvp = jvp->Next)
1183 if (!jvp->Del)
1184 Size++;
1185
1186 if (Size > Alloc) {
1187 // No need to realloc after deleting values
1188 Mvals = (PJVAL*)PlugSubAlloc(g, NULL, Size * sizeof(PJVAL));
1189 Alloc = Size;
1190 } // endif Size
1191
1192 for (i = 0, jvp = First; jvp; jvp = jvp->Next)
1193 if (!jvp->Del) {
1194 Mvals[i++] = jvp;
1195 pjvp = &jvp->Next;
1196 Last = jvp;
1197 } else
1198 *pjvp = jvp->Next;
1199
1200} // end of InitArray
1201
1202/***********************************************************************/
1203/* Get the Nth value of an Array. */
1204/***********************************************************************/
1205PJVAL JARRAY::GetValue(int i)
1206{
1207 if (Mvals && i >= 0 && i < Size)
1208 return Mvals[i];
1209 else
1210 return NULL;
1211} // end of GetValue
1212
1213/***********************************************************************/
1214/* Add a Value to the Array Value list. */
1215/***********************************************************************/
1216PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp, int *x)
1217{
1218 if (!jvp)
1219 jvp = new(g) JVALUE;
1220
1221 if (x) {
1222 int i = 0, n = *x;
1223 PJVAL jp, *jpp = &First;
1224
1225 for (jp = First; jp && i < n; i++, jp = *(jpp = &jp->Next));
1226
1227 (*jpp) = jvp;
1228
1229 if (!(jvp->Next = jp))
1230 Last = jvp;
1231
1232 } else {
1233 if (!First)
1234 First = jvp;
1235 else if (Last == First)
1236 First->Next = Last = jvp;
1237 else
1238 Last->Next = jvp;
1239
1240 Last = jvp;
1241 Last->Next = NULL;
1242 } // endif x
1243
1244 return jvp;
1245} // end of AddValue
1246
1247/***********************************************************************/
1248/* Merge two arrays. */
1249/***********************************************************************/
1250bool JARRAY::Merge(PGLOBAL g, PJSON jsp)
1251{
1252 if (jsp->GetType() != TYPE_JAR) {
1253 strcpy(g->Message, "Second argument is not an array");
1254 return true;
1255 } // endif Type
1256
1257 PJAR arp = (PJAR)jsp;
1258
1259 for (int i = 0; i < jsp->size(); i++)
1260 AddValue(g, arp->GetValue(i));
1261
1262 InitArray(g);
1263 return false;
1264} // end of Merge
1265
1266/***********************************************************************/
1267/* Set the nth Value of the Array Value list. */
1268/***********************************************************************/
1269bool JARRAY::SetValue(PGLOBAL g, PJVAL jvp, int n)
1270{
1271 int i = 0;
1272 PJVAL jp, *jpp = &First;
1273
1274 for (jp = First; i < n; i++, jp = *(jpp = &jp->Next))
1275 if (!jp)
1276 *jpp = jp = new(g) JVALUE;
1277
1278 *jpp = jvp;
1279 jvp->Next = (jp ? jp->Next : NULL);
1280 return false;
1281} // end of SetValue
1282
1283/***********************************************************************/
1284/* Return the text corresponding to all values. */
1285/***********************************************************************/
1286PSZ JARRAY::GetText(PGLOBAL g, PSZ text)
1287{
1288 int n;
1289 PJVAL jp;
1290
1291 if (!text) {
1292 text = (char*)PlugSubAlloc(g, NULL, 0);
1293 text[0] = 0;
1294 n = 1;
1295 } else
1296 n = 0;
1297
1298 for (jp = First; jp; jp = jp->Next)
1299 jp->GetText(g, text);
1300
1301 if (n)
1302 PlugSubAlloc(g, NULL, strlen(text) + 1);
1303
1304 return text + n;
1305} // end of GetText;
1306
1307/***********************************************************************/
1308/* Delete a Value from the Arrays Value list. */
1309/***********************************************************************/
1310bool JARRAY::DeleteValue(int n)
1311{
1312 PJVAL jvp = GetValue(n);
1313
1314 if (jvp) {
1315 jvp->Del = true;
1316 return false;
1317 } else
1318 return true;
1319
1320} // end of DeleteValue
1321
1322/***********************************************************************/
1323/* True if void or if all members are nulls. */
1324/***********************************************************************/
1325bool JARRAY::IsNull(void)
1326{
1327 for (int i = 0; i < Size; i++)
1328 if (!Mvals[i]->IsNull())
1329 return false;
1330
1331 return true;
1332} // end of IsNull
1333
1334/* -------------------------- Class JVALUE- -------------------------- */
1335
1336/***********************************************************************/
1337/* Constructor for a JSON. */
1338/***********************************************************************/
1339JVALUE::JVALUE(PJSON jsp) : JSON()
1340{
1341 if (jsp->GetType() == TYPE_JVAL) {
1342 Jsp = jsp->GetJsp();
1343 Value = jsp->GetValue();
1344 } else {
1345 Jsp = jsp;
1346 Value = NULL;
1347 } // endif Type
1348
1349 Next = NULL;
1350 Del = false;
1351 Size = 1;
1352} // end of JVALUE constructor
1353
1354/***********************************************************************/
1355/* Constructor for a Value with a given string or numeric value. */
1356/***********************************************************************/
1357JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON()
1358{
1359 Jsp = NULL;
1360 Value = AllocateValue(g, valp);
1361 Next = NULL;
1362 Del = false;
1363} // end of JVALUE constructor
1364
1365/***********************************************************************/
1366/* Constructor for a given string. */
1367/***********************************************************************/
1368JVALUE::JVALUE(PGLOBAL g, PCSZ strp) : JSON()
1369{
1370 Jsp = NULL;
1371 Value = AllocateValue(g, (void*)strp, TYPE_STRING);
1372 Next = NULL;
1373 Del = false;
1374} // end of JVALUE constructor
1375
1376/***********************************************************************/
1377/* Returns the type of the Value's value. */
1378/***********************************************************************/
1379JTYP JVALUE::GetValType(void)
1380{
1381 if (Jsp)
1382 return Jsp->GetType();
1383 else if (Value)
1384 return (JTYP)Value->GetType();
1385 else
1386 return TYPE_NULL;
1387
1388} // end of GetValType
1389
1390/***********************************************************************/
1391/* Return the Value's Object value. */
1392/***********************************************************************/
1393PJOB JVALUE::GetObject(void)
1394{
1395 if (Jsp && Jsp->GetType() == TYPE_JOB)
1396 return (PJOB)Jsp;
1397
1398 return NULL;
1399} // end of GetObject
1400
1401/***********************************************************************/
1402/* Return the Value's Array value. */
1403/***********************************************************************/
1404PJAR JVALUE::GetArray(void)
1405{
1406 if (Jsp && Jsp->GetType() == TYPE_JAR)
1407 return (PJAR)Jsp;
1408
1409 return NULL;
1410} // end of GetArray
1411
1412/***********************************************************************/
1413/* Return the Value's Integer value. */
1414/***********************************************************************/
1415int JVALUE::GetInteger(void)
1416{
1417 return (Value) ? Value->GetIntValue() : 0;
1418} // end of GetInteger
1419
1420/***********************************************************************/
1421/* Return the Value's Big integer value. */
1422/***********************************************************************/
1423long long JVALUE::GetBigint(void)
1424{
1425 return (Value) ? Value->GetBigintValue() : 0;
1426} // end of GetBigint
1427
1428/***********************************************************************/
1429/* Return the Value's Double value. */
1430/***********************************************************************/
1431double JVALUE::GetFloat(void)
1432{
1433 return (Value) ? Value->GetFloatValue() : 0.0;
1434} // end of GetFloat
1435
1436/***********************************************************************/
1437/* Return the Value's String value. */
1438/***********************************************************************/
1439PSZ JVALUE::GetString(PGLOBAL g)
1440{
1441 char *p;
1442
1443 if (Value) {
1444 char buf[32];
1445
1446 if ((p = Value->GetCharString(buf)) == buf)
1447 p = PlugDup(g, buf);
1448
1449 } else
1450 p = NULL;
1451
1452 return p;
1453} // end of GetString
1454
1455/***********************************************************************/
1456/* Return the Value's String value. */
1457/***********************************************************************/
1458PSZ JVALUE::GetText(PGLOBAL g, PSZ text)
1459{
1460 if (Jsp)
1461 return Jsp->GetText(g, text);
1462
1463 char buf[32];
1464 PSZ s = (Value) ? Value->GetCharString(buf) : NULL;
1465
1466 if (s)
1467 strcat(strcat(text, " "), s);
1468 else if (GetJsonNull())
1469 strcat(strcat(text, " "), GetJsonNull());
1470
1471 return text;
1472} // end of GetText
1473
1474void JVALUE::SetValue(PJSON jsp)
1475{
1476 if (jsp && jsp->GetType() == TYPE_JVAL) {
1477 Jsp = jsp->GetJsp();
1478 Value = jsp->GetValue();
1479 } else {
1480 Jsp = jsp;
1481 Value = NULL;
1482 } // endif Type
1483
1484} // end of SetValue;
1485
1486/***********************************************************************/
1487/* Set the Value's value as the given integer. */
1488/***********************************************************************/
1489void JVALUE::SetInteger(PGLOBAL g, int n)
1490{
1491 Value = AllocateValue(g, &n, TYPE_INT);
1492 Jsp = NULL;
1493} // end of SetInteger
1494
1495/***********************************************************************/
1496/* Set the Value's Boolean value as a tiny integer. */
1497/***********************************************************************/
1498void JVALUE::SetTiny(PGLOBAL g, char n)
1499{
1500 Value = AllocateValue(g, &n, TYPE_TINY);
1501 Jsp = NULL;
1502} // end of SetTiny
1503
1504/***********************************************************************/
1505/* Set the Value's value as the given big integer. */
1506/***********************************************************************/
1507void JVALUE::SetBigint(PGLOBAL g, long long ll)
1508{
1509 Value = AllocateValue(g, &ll, TYPE_BIGINT);
1510 Jsp = NULL;
1511} // end of SetBigint
1512
1513/***********************************************************************/
1514/* Set the Value's value as the given DOUBLE. */
1515/***********************************************************************/
1516void JVALUE::SetFloat(PGLOBAL g, double f)
1517{
1518 Value = AllocateValue(g, &f, TYPE_DOUBLE, 6);
1519 Jsp = NULL;
1520} // end of SetFloat
1521
1522/***********************************************************************/
1523/* Set the Value's value as the given string. */
1524/***********************************************************************/
1525void JVALUE::SetString(PGLOBAL g, PSZ s, short c)
1526{
1527 Value = AllocateValue(g, s, TYPE_STRING, c);
1528 Jsp = NULL;
1529} // end of SetString
1530
1531/***********************************************************************/
1532/* True when its JSON or normal value is null. */
1533/***********************************************************************/
1534bool JVALUE::IsNull(void)
1535{
1536 return (Jsp) ? Jsp->IsNull() : (Value) ? Value->IsNull() : true;
1537} // end of IsNull
1538
1539