]> mj.ucw.cz Git - libucw.git/blobdiff - ucw-json/format.c
UCW::Configure::Perl: Split off configuration of Perl paths
[libucw.git] / ucw-json / format.c
index 2c17ca5913c08ce096f2404ec89dbaf7f7963709..227a26b548e7573c399f48b71d6701f15f77915f 100644 (file)
@@ -14,6 +14,7 @@
 #include <ucw/unicode.h>
 #include <ucw-json/json.h>
 
+#include <float.h>
 #include <stdio.h>
 
 void json_set_output(struct json_context *js, struct fastbuf *fb)
@@ -21,8 +22,10 @@ void json_set_output(struct json_context *js, struct fastbuf *fb)
   js->out_fb = fb;
 }
 
-static void write_string(struct fastbuf *fb, const char *p)
+static void write_string(struct json_context *js, const char *p)
 {
+  struct fastbuf *fb = js->out_fb;
+
   bputc(fb, '"');
   for (;;)
     {
@@ -47,12 +50,45 @@ static void write_string(struct fastbuf *fb, const char *p)
              bprintf(fb, "\\u%04x", u);
            }
        }
+      else if (u >= 0x007f && (js->format_options & JSON_FORMAT_ESCAPE_NONASCII))
+       {
+         if (u < 0x10000)
+           bprintf(fb, "\\u%04x", u);
+         else if (u < 0x110000)
+           bprintf(fb, "\\u%04x\\u%04x", 0xd800 + ((u - 0x10000) >> 10), 0xdc00 + (u & 0x3ff));
+         else
+           bprintf(fb, "\\u%04x", UNI_REPLACEMENT);
+       }
       else
-       bputc(fb, u);
+       bput_utf8_32(fb, u);
     }
   bputc(fb, '"');
 }
 
+static void write_number(struct fastbuf *fb, double val)
+{
+  bprintf(fb, "%.*g", DBL_DIG+1, val);
+}
+
+static bool want_indent_p(struct json_context *js)
+{
+  return (js->format_options & JSON_FORMAT_INDENT);
+}
+
+static void write_space(struct json_context *js)
+{
+  struct fastbuf *fb = js->out_fb;
+
+  if (want_indent_p(js))
+    {
+      bputc(fb, '\n');
+      for (uint i=0; i < js->out_indent; i++)
+       bputc(fb, '\t');
+    }
+  else
+    bputc(fb, ' ');
+}
+
 void json_write_value(struct json_context *js, struct json_node *n)
 {
   struct fastbuf *fb = js->out_fb;
@@ -66,39 +102,54 @@ void json_write_value(struct json_context *js, struct json_node *n)
       bputs(fb, (n->boolean ? "true" : "false"));
       break;
     case JSON_NUMBER:
-      // FIXME: Formatting of floats
-      bprintf(fb, "%f", n->number);
+      write_number(fb, n->number);
       break;
     case JSON_STRING:
-      write_string(fb, n->string);
+      write_string(js, n->string);
       break;
     case JSON_ARRAY:
       {
-       // FIXME: Indent
-       bputs(fb, "[ ");
-       for (size_t i=0; i < GARY_SIZE(n->elements); i++)
+       if (!GARY_SIZE(n->elements))
+         bputs(fb, "[]");
+       else
          {
-           if (i)
-             bputs(fb, ", ");
-           json_write_value(js, n->elements[i]);
+           bputc(fb, '[');
+           js->out_indent++;
+           for (size_t i=0; i < GARY_SIZE(n->elements); i++)
+             {
+               if (i)
+                 bputc(fb, ',');
+               write_space(js);
+               json_write_value(js, n->elements[i]);
+             }
+           js->out_indent--;
+           write_space(js);
+           bputc(fb, ']');
          }
-       bputc(fb, ']');
        break;
       }
     case JSON_OBJECT:
       {
-       bputs(fb, "{ ");
-       // FIXME: Indent
-       for (size_t i=0; i < GARY_SIZE(n->pairs); i++)
+       if (!GARY_SIZE(n->pairs))
+         bputs(fb, "{}");
+       else
          {
-           if (i)
-             bputs(fb, ", ");
-           struct json_pair *p = &n->pairs[i];
-           write_string(fb, p->key);
-           bputs(fb, ": ");
-           json_write_value(js, p->value);
+           bputc(fb, '{');
+           js->out_indent++;
+           for (size_t i=0; i < GARY_SIZE(n->pairs); i++)
+             {
+               if (i)
+                 bputc(fb, ',');
+               write_space(js);
+               struct json_pair *p = &n->pairs[i];
+               write_string(js, p->key);
+               bputs(fb, ": ");
+               json_write_value(js, p->value);
+             }
+           js->out_indent--;
+           write_space(js);
+           bputc(fb, '}');
          }
-       bputc(fb, '}');
        break;
       }
     default: