add --tag-from-json v0.1.7
authornu774 <honeycomb77@gmail.com>
Fri, 15 Feb 2013 13:26:17 +0000 (22:26 +0900)
committernu774 <honeycomb77@gmail.com>
Fri, 15 Feb 2013 13:26:17 +0000 (22:26 +0900)
MSVC/fdkaac.vcxproj
Makefile.am
README
src/main.c
src/metadata.c [new file with mode: 0644]
src/metadata.h [new file with mode: 0644]
src/parson.c [new file with mode: 0644]
src/parson.h [new file with mode: 0644]
version.h

index 71ec9b671735014ff988fa9497a30cca7717f895..a2d139d8963f4267a816b3dddcef278e062930d6 100644 (file)
@@ -100,6 +100,8 @@ copy ..\fdk-aac\libSYS\include\machine_type.h include\fdk-aac\ </Command>
     <ClCompile Include="..\src\lpcm.c" />
     <ClCompile Include="..\src\m4af.c" />
     <ClCompile Include="..\src\main.c" />
+    <ClCompile Include="..\src\metadata.c" />
+    <ClCompile Include="..\src\parson.c" />
     <ClCompile Include="..\src\progress.c" />
     <ClCompile Include="..\src\wav_reader.c" />
   </ItemGroup>
@@ -110,6 +112,8 @@ copy ..\fdk-aac\libSYS\include\machine_type.h include\fdk-aac\ </Command>
     <ClInclude Include="..\src\lpcm.h" />
     <ClInclude Include="..\src\m4af.h" />
     <ClInclude Include="..\src\m4af_endian.h" />
+    <ClInclude Include="..\src\metadata.h" />
+    <ClInclude Include="..\src\parson.h" />
     <ClInclude Include="..\src\progress.h" />
     <ClInclude Include="..\src\wav_reader.h" />
   </ItemGroup>
index b69e467007f5aeb6dc847cd323e88a5859811588..62d3c0ceb51a5d1c59c50419f1de913fd0da0bbe 100644 (file)
@@ -8,6 +8,8 @@ fdkaac_SOURCES = \
     src/lpcm.c       \
     src/m4af.c       \
     src/main.c       \
+    src/metadata.c   \
+    src/parson.c     \
     src/progress.c   \
     src/wav_reader.c
 
diff --git a/README b/README
index 007af3adfbfd2ee88a10618e3576bc073f86ffd8..4bd55b3eaaf39d3a209c6faf2cd76f9304df7595 100644 (file)
--- a/README
+++ b/README
@@ -50,3 +50,53 @@ Therefore, only use UTF-8 (without BOM) when setting text tags by this option.
 On the other hand, --tag / --long-tag (and other command line arguments) are
 converted from locale character encoding to UTF-8 on Posix environment.
 On Windows, command line arguments are always treated as Unicode.
+
+Tagging using JSON
+------------------
+With --tag-from-json, fdkaac can read JSON file and set tags from it.
+By default, tags are assumed to be in the root object(dictionary) like this:
+
+{
+  "title": "No Expectations",
+  "artist": "The Rolling Stones",
+  "album": "Beggars Banquet",
+  "track": 2
+}
+
+In this case, you can simply specify the filename like:
+--tag-from-json=/path/to/json
+
+If the object containing tags is placed somewhere else, you can optionally
+specify the path of the object with dotted notation.
+
+{
+  "format" : {
+    "filename" : "Middle Curse.flac",
+    "nb_streams" : 1,
+    "format_name" : "flac",
+    "format_long_name" : "raw FLAC",
+    "start_time" : "N/A",
+    "duration" : "216.146667",
+    "size" : "11851007.000000",
+    "bit_rate" : "438628.000000",
+    "tags" : {
+      "ALBUM" : "Scary World Theory",
+      "ARTIST" : "Lali Puna",
+      "DATE" : "2001",
+      "DISCID" : "9208CC0A",
+      "TITLE" : "Middle Curse",
+      "TRACKTOTAL" : "10",
+      "track" : "2"
+    }
+  }
+}
+
+In this example, tags are placed under the object "format.tags".
+("format" is a child of the root, and "tags" is a child of the "format").
+In this case, you can say:
+--tag-from-json=/path/to/json?format.tags
+
+For your information, ffprobe of ffmpeg project (or avprobe of libav) can
+output media information/metadata in json format like this.
+
+Note that not all tags can be read/written this way.
index 8649b0a5bfe4cc8598bda6d42725fb4ec2c5ae41..59f2b979b469d8bc07af17bec662e3755fcd3886 100644 (file)
@@ -38,6 +38,7 @@
 #include "m4af.h"
 #include "progress.h"
 #include "version.h"
+#include "metadata.h"
 
 #define PROGNAME "fdkaac"
 
@@ -181,17 +182,18 @@ PROGNAME " %s\n"
 " --tag-from-file <fcc>:<filename>\n"
 "                              Same as above, but value is read from file.\n"
 " --long-tag <name>:<value>    Set arbitrary tag as iTunes custom metadata.\n"
+" --tag-from-json <filename[?dot_notation]>\n"
+"                              Read tags from JSON. By default, tags are\n"
+"                              assumed to be direct children of the root\n"
+"                              object(dictionary).\n"
+"                              Optionally, position of the dictionary\n"
+"                              that contains tags can be specified with\n"
+"                              dotted notation.\n"
+"                              Example:\n"
+"                                --tag-from-json /path/to/json?format.tags\n"
     , fdkaac_version);
 }
 
-typedef struct aacenc_tag_entry_t {
-    uint32_t tag;
-    const char *name;
-    const char *data;
-    uint32_t data_size;
-    int is_file_name;
-} aacenc_tag_entry_t;
-
 typedef struct aacenc_param_ex_t {
     AACENC_PARAMS
 
@@ -205,33 +207,10 @@ typedef struct aacenc_param_ex_t {
     unsigned raw_rate;
     const char *raw_format;
 
-    aacenc_tag_entry_t *tag_table;
-    unsigned tag_count;
-    unsigned tag_table_capacity;
-} aacenc_param_ex_t;
+    aacenc_tag_param_t tags;
 
-static
-void param_add_itmf_entry(aacenc_param_ex_t *params, uint32_t tag,
-                          const char *key, const char *value, uint32_t size,
-                          int is_file_name)
-{
-    aacenc_tag_entry_t *entry;
-    if (params->tag_count == params->tag_table_capacity) {
-        unsigned newsize = params->tag_table_capacity;
-        newsize = newsize ? newsize * 2 : 1;
-        params->tag_table =
-            realloc(params->tag_table, newsize * sizeof(aacenc_tag_entry_t));
-        params->tag_table_capacity = newsize;
-    }
-    entry = params->tag_table + params->tag_count;
-    entry->tag = tag;
-    if (tag == M4AF_FOURCC('-','-','-','-'))
-        entry->name = key;
-    entry->data = value;
-    entry->data_size = size;
-    entry->is_file_name = is_file_name;
-    params->tag_count++;
-}
+    char *json_filename;
+} aacenc_param_ex_t;
 
 static
 int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
@@ -245,6 +224,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
 #define OPT_SHORT_TAG            M4AF_FOURCC('s','t','a','g')
 #define OPT_SHORT_TAG_FILE       M4AF_FOURCC('s','t','g','f')
 #define OPT_LONG_TAG             M4AF_FOURCC('l','t','a','g')
+#define OPT_TAG_FROM_JSON        M4AF_FOURCC('t','f','j','s')
 
     static struct option long_options[] = {
         { "help",             no_argument,       0, 'h' },
@@ -282,6 +262,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
         { "tag",              required_argument, 0, OPT_SHORT_TAG          },
         { "tag-from-file",    required_argument, 0, OPT_SHORT_TAG_FILE     },
         { "long-tag",         required_argument, 0, OPT_LONG_TAG           },
+        { "tag-from-json",    required_argument, 0, OPT_TAG_FROM_JSON      },
         { 0,                  0,                 0, 0                      },
     };
     params->afterburner = 1;
@@ -395,7 +376,8 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
         case M4AF_TAG_TRACK:
         case M4AF_TAG_DISK:
         case M4AF_TAG_TEMPO:
-            param_add_itmf_entry(params, ch, 0, optarg, strlen(optarg), 0);
+            aacenc_param_add_itmf_entry(&params->tags, ch, 0, optarg,
+                                        strlen(optarg), 0);
             break;
         case OPT_SHORT_TAG:
         case OPT_SHORT_TAG_FILE:
@@ -428,10 +410,14 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
                     for (; *optarg; ++optarg)
                         fcc = ((fcc << 8) | (*optarg & 0xff));
                 }
-                param_add_itmf_entry(params, fcc, optarg, val, strlen(val),
-                                     ch == OPT_SHORT_TAG_FILE);
+                aacenc_param_add_itmf_entry(&params->tags, fcc, optarg,
+                                            val, strlen(val),
+                                            ch == OPT_SHORT_TAG_FILE);
             }
             break;
+        case OPT_TAG_FROM_JSON:
+            params->json_filename = optarg;
+            break;
         default:
             return usage(), -1;
         }
@@ -538,155 +524,6 @@ END:
     return rc;
 }
 
-static
-char *load_tag_from_file(const char *path, uint32_t *data_size)
-{
-    FILE *fp = 0;
-    char *data = 0;
-    int64_t size;
-
-    if ((fp = aacenc_fopen(path, "rb")) == NULL) {
-        aacenc_fprintf(stderr, "WARNING: %s: %s\n", path, strerror(errno));
-        goto END;
-    }
-    fseeko(fp, 0, SEEK_END);
-    size = ftello(fp);
-    if (size > 5*1024*1024) {
-        aacenc_fprintf(stderr, "WARNING: %s: size too large\n", path);
-        goto END;
-    }
-    fseeko(fp, 0, SEEK_SET);
-    data = malloc(size + 1);
-    if (data) fread(data, 1, size, fp);
-    data[size] = 0;
-    *data_size = (uint32_t)size;
-END:
-    if (fp) fclose(fp);
-    return data;
-}
-
-static
-void put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag)
-{
-    unsigned m, n = 0;
-    const char *data = tag->data;
-    uint32_t data_size = tag->data_size;
-    char *file_contents = 0;
-
-    if (tag->is_file_name) {
-        data = file_contents = load_tag_from_file(tag->data, &data_size);
-        if (!data) return;
-    }
-    switch (tag->tag) {
-    case M4AF_TAG_TRACK:
-        if (sscanf(data, "%u/%u", &m, &n) >= 1)
-            m4af_add_itmf_track_tag(m4af, m, n);
-        break;
-    case M4AF_TAG_DISK:
-        if (sscanf(data, "%u/%u", &m, &n) >= 1)
-            m4af_add_itmf_disk_tag(m4af, m, n);
-        break;
-    case M4AF_TAG_GENRE_ID3:
-        if (sscanf(data, "%u", &n) == 1)
-            m4af_add_itmf_genre_tag(m4af, n);
-        break;
-    case M4AF_TAG_TEMPO:
-        if (sscanf(data, "%u", &n) == 1)
-            m4af_add_itmf_int16_tag(m4af, tag->tag, n);
-        break;
-    case M4AF_TAG_COMPILATION:
-    case M4AF_FOURCC('a','k','I','D'):
-    case M4AF_FOURCC('h','d','v','d'):
-    case M4AF_FOURCC('p','c','s','t'):
-    case M4AF_FOURCC('p','g','a','p'):
-    case M4AF_FOURCC('r','t','n','g'):
-    case M4AF_FOURCC('s','t','i','k'):
-        if (sscanf(data, "%u", &n) == 1)
-            m4af_add_itmf_int8_tag(m4af, tag->tag, n);
-        break;
-    case M4AF_FOURCC('a','t','I','D'):
-    case M4AF_FOURCC('c','m','I','D'):
-    case M4AF_FOURCC('c','n','I','D'):
-    case M4AF_FOURCC('g','e','I','D'):
-    case M4AF_FOURCC('s','f','I','D'):
-    case M4AF_FOURCC('t','v','s','n'):
-    case M4AF_FOURCC('t','v','s','s'):
-        if (sscanf(data, "%u", &n) == 1)
-            m4af_add_itmf_int32_tag(m4af, tag->tag, n);
-        break;
-    case M4AF_FOURCC('p','l','I','D'):
-        {
-            int64_t qn;
-            if (sscanf(data, "%" SCNd64, &qn) == 1)
-                m4af_add_itmf_int64_tag(m4af, tag->tag, qn);
-            break;
-        }
-    case M4AF_TAG_ARTWORK:
-        {
-            int data_type = 0;
-            if (!memcmp(data, "GIF", 3))
-                data_type = M4AF_GIF;
-            else if (!memcmp(data, "\xff\xd8\xff", 3))
-                data_type = M4AF_JPEG;
-            else if (!memcmp(data, "\x89PNG", 4))
-                data_type = M4AF_PNG;
-            if (data_type)
-                m4af_add_itmf_short_tag(m4af, tag->tag, data_type,
-                                        data, data_size);
-            break;
-        }
-    case M4AF_FOURCC('-','-','-','-'):
-        {
-            char *u8 = aacenc_to_utf8(data);
-            m4af_add_itmf_long_tag(m4af, tag->name, u8);
-            free(u8);
-            break;
-        }
-    case M4AF_TAG_TITLE:
-    case M4AF_TAG_ARTIST:
-    case M4AF_TAG_ALBUM:
-    case M4AF_TAG_GENRE:
-    case M4AF_TAG_DATE:
-    case M4AF_TAG_COMPOSER:
-    case M4AF_TAG_GROUPING:
-    case M4AF_TAG_COMMENT:
-    case M4AF_TAG_LYRICS:
-    case M4AF_TAG_TOOL:
-    case M4AF_TAG_ALBUM_ARTIST:
-    case M4AF_TAG_DESCRIPTION:
-    case M4AF_TAG_LONG_DESCRIPTION:
-    case M4AF_TAG_COPYRIGHT:
-    case M4AF_FOURCC('a','p','I','D'):
-    case M4AF_FOURCC('c','a','t','g'):
-    case M4AF_FOURCC('k','e','y','w'):
-    case M4AF_FOURCC('p','u','r','d'):
-    case M4AF_FOURCC('p','u','r','l'):
-    case M4AF_FOURCC('s','o','a','a'):
-    case M4AF_FOURCC('s','o','a','l'):
-    case M4AF_FOURCC('s','o','a','r'):
-    case M4AF_FOURCC('s','o','c','o'):
-    case M4AF_FOURCC('s','o','n','m'):
-    case M4AF_FOURCC('s','o','s','n'):
-    case M4AF_FOURCC('t','v','e','n'):
-    case M4AF_FOURCC('t','v','n','n'):
-    case M4AF_FOURCC('t','v','s','h'):
-    case M4AF_FOURCC('x','i','d',' '):
-    case M4AF_FOURCC('\xa9','e','n','c'):
-    case M4AF_FOURCC('\xa9','s','t','3'):
-        {
-            char *u8 = aacenc_to_utf8(data);
-            m4af_add_itmf_string_tag(m4af, tag->tag, u8);
-            free(u8);
-            break;
-        }
-    default:
-        fprintf(stderr, "WARNING: unknown/unsupported tag: %c%c%c%c\n",
-                tag->tag >> 24, (tag->tag >> 16) & 0xff,
-                (tag->tag >> 8) & 0xff, tag->tag & 0xff);
-    }
-    if (file_contents) free(file_contents);
-}
-
 static
 void put_tool_tag(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
                   HANDLE_AACENCODER encoder)
@@ -720,9 +557,13 @@ int finalize_m4a(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
                  HANDLE_AACENCODER encoder)
 {
     unsigned i;
-    aacenc_tag_entry_t *tag = params->tag_table;
-    for (i = 0; i < params->tag_count; ++i, ++tag)
-        put_tag_entry(m4af, tag);
+    aacenc_tag_entry_t *tag = params->tags.tag_table;
+
+    if (params->json_filename)
+        aacenc_put_tags_from_json(m4af, params->json_filename);
+
+    for (i = 0; i < params->tags.tag_count; ++i, ++tag)
+        aacenc_put_tag_entry(m4af, tag);
 
     put_tool_tag(m4af, params, encoder);
 
@@ -901,7 +742,7 @@ END:
     if (ofp) fclose(ofp);
     if (encoder) aacEncClose(&encoder);
     if (output_filename) free(output_filename);
-    if (params.tag_table) free(params.tag_table);
+    if (params.tags.tag_table) free(params.tags.tag_table);
 
     return result;
 }
diff --git a/src/metadata.c b/src/metadata.c
new file mode 100644 (file)
index 0000000..c84b0f4
--- /dev/null
@@ -0,0 +1,378 @@
+#if HAVE_CONFIG_H
+#  include "config.h"
+#endif
+#if HAVE_STDINT_H
+#  include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H
+#  include <inttypes.h>
+#elif defined(_MSC_VER)
+#  define SCNd64 "I64d"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include "m4af.h"
+#include "metadata.h"
+#include "compat.h"
+#include "parson.h"
+
+typedef struct tag_key_mapping_t {
+    const char *name;
+    uint32_t fcc;
+} tag_key_mapping_t;
+
+enum {
+    TAG_TOTAL_DISCS  = 1,
+    TAG_TOTAL_TRACKS = 2
+};
+
+static tag_key_mapping_t tag_mapping_table[] = {
+    { "album",                      M4AF_TAG_ALBUM               },
+    { "albumartist",                M4AF_TAG_ALBUM_ARTIST        },
+    { "albumartistsort",            M4AF_FOURCC('s','o','a','a') },
+    { "albumartistsortorder",       M4AF_FOURCC('s','o','a','a') },
+    { "albumsort",                  M4AF_FOURCC('s','o','a','l') },
+    { "albumsortorder",             M4AF_FOURCC('s','o','a','l') },
+    { "artist",                     M4AF_TAG_ARTIST              },
+    { "artistsort",                 M4AF_FOURCC('s','o','a','r') },
+    { "artistsortorder",            M4AF_FOURCC('s','o','a','r') },
+    { "band",                       M4AF_TAG_ALBUM_ARTIST        },
+    { "bpm",                        M4AF_TAG_TEMPO               },
+    { "comment",                    M4AF_TAG_COMMENT             },
+    { "compilation",                M4AF_TAG_COMPILATION         },
+    { "composer",                   M4AF_TAG_COMPOSER            },
+    { "composersort",               M4AF_FOURCC('s','o','c','o') },
+    { "composersortorder",          M4AF_FOURCC('s','o','c','o') },
+    { "contentgroup",               M4AF_TAG_GROUPING            },
+    { "copyright",                  M4AF_TAG_COPYRIGHT           },
+    { "date",                       M4AF_TAG_DATE                },
+    { "disc",                       M4AF_TAG_DISK                },
+    { "disctotal",                  TAG_TOTAL_DISCS              },
+    { "discnumber",                 M4AF_TAG_DISK                },
+    { "genre",                      M4AF_TAG_GENRE               },
+    { "grouping",                   M4AF_TAG_GROUPING            },
+    { "itunescompilation",          M4AF_TAG_COMPILATION         },
+    { "lyrics",                     M4AF_TAG_LYRICS              },
+    { "title",                      M4AF_TAG_TITLE               },
+    { "titlesort",                  M4AF_FOURCC('s','o','n','m') },
+    { "titlesortorder",             M4AF_FOURCC('s','o','n','m') },
+    { "totaldiscs",                 TAG_TOTAL_DISCS              },
+    { "totaltracks",                TAG_TOTAL_TRACKS             },
+    { "track",                      M4AF_TAG_TRACK               },
+    { "tracknumber",                M4AF_TAG_TRACK               },
+    { "tracktotal",                 TAG_TOTAL_TRACKS             },
+    { "unsyncedlyrics",             M4AF_TAG_LYRICS              },
+    { "year",                       M4AF_TAG_DATE                },
+};
+
+static
+int tag_key_comparator(const void *k, const void *v)
+{
+    return strcmp((const char *)k, ((tag_key_mapping_t*)v)->name);
+}
+
+static
+uint32_t get_tag_fcc_from_name(const char *name)
+{
+    char *name_p = 0, *p;
+    const tag_key_mapping_t *ent;
+
+    name_p = malloc(strlen(name) + 1);
+    for (p = name_p; *name; ++name) {
+        unsigned char c = *name;
+        if (c != ' ' && c != '-' && c != '_')
+            *p++ = tolower(c);
+    }
+    *p = 0;
+    ent = bsearch(name_p, tag_mapping_table,
+                  sizeof(tag_mapping_table) / sizeof(tag_mapping_table[0]),
+                  sizeof(tag_mapping_table[0]),
+                  tag_key_comparator);
+    free(name_p);
+    return ent ? ent->fcc : 0;
+}
+
+char *aacenc_load_tag_from_file(const char *path, uint32_t *data_size)
+{
+    FILE *fp = 0;
+    char *data = 0;
+    int64_t size;
+
+    if ((fp = aacenc_fopen(path, "rb")) == NULL) {
+        aacenc_fprintf(stderr, "WARNING: %s: %s\n", path, strerror(errno));
+        goto END;
+    }
+    fseeko(fp, 0, SEEK_END);
+    size = ftello(fp);
+    if (size > 5*1024*1024) {
+        aacenc_fprintf(stderr, "WARNING: %s: size too large\n", path);
+        goto END;
+    }
+    fseeko(fp, 0, SEEK_SET);
+    data = malloc(size + 1);
+    if (data) fread(data, 1, size, fp);
+    data[size] = 0;
+    *data_size = (uint32_t)size;
+END:
+    if (fp) fclose(fp);
+    return data;
+}
+
+void aacenc_param_add_itmf_entry(aacenc_tag_param_t *params, uint32_t tag,
+                                 const char *key, const char *value,
+                                 uint32_t size, int is_file_name)
+{
+    aacenc_tag_entry_t *entry;
+
+    if (!is_file_name && !size)
+        return;
+    if (params->tag_count == params->tag_table_capacity) {
+        unsigned newsize = params->tag_table_capacity;
+        newsize = newsize ? newsize * 2 : 1;
+        params->tag_table =
+            realloc(params->tag_table, newsize * sizeof(aacenc_tag_entry_t));
+        params->tag_table_capacity = newsize;
+    }
+    entry = params->tag_table + params->tag_count;
+    entry->tag = tag;
+    if (tag == M4AF_FOURCC('-','-','-','-'))
+        entry->name = key;
+    entry->data = value;
+    entry->data_size = size;
+    entry->is_file_name = is_file_name;
+    params->tag_count++;
+}
+
+static
+void tag_put_number_pair(m4af_ctx_t *m4af, uint32_t fcc,
+                         const char *snumber, const char *stotal)
+{
+    unsigned number = 0, total = 0;
+    char buf[128];
+    aacenc_tag_entry_t entry = { 0 };
+
+    if (snumber) sscanf(snumber, "%u", &number);
+    if (stotal)  sscanf(stotal,  "%u", &total);
+    if (number) {
+        if (total) sprintf(buf, "%u/%u", number, total);
+        else       sprintf(buf, "%u",    number);
+        entry.tag = fcc;
+        entry.data = buf;
+        entry.data_size = strlen(buf);
+        aacenc_put_tag_entry(m4af, &entry);
+    }
+}
+
+void aacenc_put_tags_from_json(m4af_ctx_t *m4af, const char *json_filename)
+{
+    char *data = 0;
+    JSON_Value *json = 0;
+    JSON_Object *root;
+    size_t i, nelts;
+    uint32_t data_size;
+    char *json_dot_path;
+    char *filename = 0;
+    char *disc = 0;
+    char *track = 0;
+    char *total_discs = 0;
+    char *total_tracks = 0;
+    aacenc_tag_entry_t entry = { 0 };
+    
+    filename = strdup(json_filename);
+    if ((json_dot_path = strchr(filename, '?')) != 0)
+        *json_dot_path++ = '\0';
+
+    if (!(data = aacenc_load_tag_from_file(filename, &data_size)))
+        goto DONE;
+    if (!(json = json_parse_string(data))) {
+        aacenc_fprintf(stderr, "WARNING: failed to parse JSON\n");
+        goto DONE;
+    }
+    root = json_value_get_object(json);
+    if (json_dot_path) {
+        if (!(root = json_object_dotget_object(root, json_dot_path))) {
+            aacenc_fprintf(stderr, "WARNING: %s not found in JSON\n",
+                           json_dot_path);
+            goto DONE;
+        }
+    }
+    nelts = json_object_get_count(root);
+    for (i = 0; i < nelts; ++i) {
+        char buf[256];
+        const char *key = 0;
+        const char *val = 0;
+        uint32_t fcc = 0;
+        JSON_Value_Type type;
+
+        key = json_object_get_name(root, i);
+        type = json_value_get_type(json_object_get_value(root, key));
+        if (type == JSONString)
+            val = json_object_get_string(root, key);
+        else if (type == JSONNumber) {
+            double num = json_object_get_number(root, key);
+            sprintf(buf, "%g", num);
+            val = buf;
+        } else if (type == JSONBoolean) {
+            int n = json_object_get_boolean(root, key);
+            sprintf(buf, "%d", n);
+            val = buf;
+        }
+        fcc = get_tag_fcc_from_name(key);
+        if (!val || !fcc)
+            continue;
+
+        switch (fcc) {
+        case TAG_TOTAL_DISCS:
+            total_discs = strdup(val); break;
+        case TAG_TOTAL_TRACKS:
+            total_tracks = strdup(val); break;
+        case M4AF_TAG_DISK:
+            disc = strdup(val); break;
+        case M4AF_TAG_TRACK:
+            track = strdup(val); break;
+        default:
+            {
+                entry.tag = fcc;
+                entry.data = val;
+                entry.data_size = strlen(val);
+                aacenc_put_tag_entry(m4af, &entry);
+            }
+        }
+    }
+    tag_put_number_pair(m4af, M4AF_TAG_TRACK, track, total_tracks);
+    tag_put_number_pair(m4af, M4AF_TAG_DISK,  disc,  total_discs);
+DONE:
+    if (track) free(track);
+    if (disc) free(disc);
+    if (total_tracks) free(total_tracks);
+    if (total_discs) free(total_discs);
+    if (data) free(data);
+    if (filename) free(filename);
+    if (json) json_value_free(json);
+}
+
+void aacenc_put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag)
+{
+    unsigned m, n = 0;
+    const char *data = tag->data;
+    uint32_t data_size = tag->data_size;
+    char *file_contents = 0;
+
+    if (tag->is_file_name) {
+        data = file_contents = aacenc_load_tag_from_file(tag->data, &data_size);
+        if (!data) return;
+    }
+    switch (tag->tag) {
+    case M4AF_TAG_TRACK:
+        if (sscanf(data, "%u/%u", &m, &n) >= 1)
+            m4af_add_itmf_track_tag(m4af, m, n);
+        break;
+    case M4AF_TAG_DISK:
+        if (sscanf(data, "%u/%u", &m, &n) >= 1)
+            m4af_add_itmf_disk_tag(m4af, m, n);
+        break;
+    case M4AF_TAG_GENRE_ID3:
+        if (sscanf(data, "%u", &n) == 1)
+            m4af_add_itmf_genre_tag(m4af, n);
+        break;
+    case M4AF_TAG_TEMPO:
+        if (sscanf(data, "%u", &n) == 1)
+            m4af_add_itmf_int16_tag(m4af, tag->tag, n);
+        break;
+    case M4AF_TAG_COMPILATION:
+    case M4AF_FOURCC('a','k','I','D'):
+    case M4AF_FOURCC('h','d','v','d'):
+    case M4AF_FOURCC('p','c','s','t'):
+    case M4AF_FOURCC('p','g','a','p'):
+    case M4AF_FOURCC('r','t','n','g'):
+    case M4AF_FOURCC('s','t','i','k'):
+        if (sscanf(data, "%u", &n) == 1)
+            m4af_add_itmf_int8_tag(m4af, tag->tag, n);
+        break;
+    case M4AF_FOURCC('a','t','I','D'):
+    case M4AF_FOURCC('c','m','I','D'):
+    case M4AF_FOURCC('c','n','I','D'):
+    case M4AF_FOURCC('g','e','I','D'):
+    case M4AF_FOURCC('s','f','I','D'):
+    case M4AF_FOURCC('t','v','s','n'):
+    case M4AF_FOURCC('t','v','s','s'):
+        if (sscanf(data, "%u", &n) == 1)
+            m4af_add_itmf_int32_tag(m4af, tag->tag, n);
+        break;
+    case M4AF_FOURCC('p','l','I','D'):
+        {
+            int64_t qn;
+            if (sscanf(data, "%" SCNd64, &qn) == 1)
+                m4af_add_itmf_int64_tag(m4af, tag->tag, qn);
+            break;
+        }
+    case M4AF_TAG_ARTWORK:
+        {
+            int data_type = 0;
+            if (!memcmp(data, "GIF", 3))
+                data_type = M4AF_GIF;
+            else if (!memcmp(data, "\xff\xd8\xff", 3))
+                data_type = M4AF_JPEG;
+            else if (!memcmp(data, "\x89PNG", 4))
+                data_type = M4AF_PNG;
+            if (data_type)
+                m4af_add_itmf_short_tag(m4af, tag->tag, data_type,
+                                        data, data_size);
+            break;
+        }
+    case M4AF_FOURCC('-','-','-','-'):
+        {
+            char *u8 = aacenc_to_utf8(data);
+            m4af_add_itmf_long_tag(m4af, tag->name, u8);
+            free(u8);
+            break;
+        }
+    case M4AF_TAG_TITLE:
+    case M4AF_TAG_ARTIST:
+    case M4AF_TAG_ALBUM:
+    case M4AF_TAG_GENRE:
+    case M4AF_TAG_DATE:
+    case M4AF_TAG_COMPOSER:
+    case M4AF_TAG_GROUPING:
+    case M4AF_TAG_COMMENT:
+    case M4AF_TAG_LYRICS:
+    case M4AF_TAG_TOOL:
+    case M4AF_TAG_ALBUM_ARTIST:
+    case M4AF_TAG_DESCRIPTION:
+    case M4AF_TAG_LONG_DESCRIPTION:
+    case M4AF_TAG_COPYRIGHT:
+    case M4AF_FOURCC('a','p','I','D'):
+    case M4AF_FOURCC('c','a','t','g'):
+    case M4AF_FOURCC('k','e','y','w'):
+    case M4AF_FOURCC('p','u','r','d'):
+    case M4AF_FOURCC('p','u','r','l'):
+    case M4AF_FOURCC('s','o','a','a'):
+    case M4AF_FOURCC('s','o','a','l'):
+    case M4AF_FOURCC('s','o','a','r'):
+    case M4AF_FOURCC('s','o','c','o'):
+    case M4AF_FOURCC('s','o','n','m'):
+    case M4AF_FOURCC('s','o','s','n'):
+    case M4AF_FOURCC('t','v','e','n'):
+    case M4AF_FOURCC('t','v','n','n'):
+    case M4AF_FOURCC('t','v','s','h'):
+    case M4AF_FOURCC('x','i','d',' '):
+    case M4AF_FOURCC('\xa9','e','n','c'):
+    case M4AF_FOURCC('\xa9','s','t','3'):
+        {
+            char *u8 = aacenc_to_utf8(data);
+            m4af_add_itmf_string_tag(m4af, tag->tag, u8);
+            free(u8);
+            break;
+        }
+    default:
+        fprintf(stderr, "WARNING: unknown/unsupported tag: %c%c%c%c\n",
+                tag->tag >> 24, (tag->tag >> 16) & 0xff,
+                (tag->tag >> 8) & 0xff, tag->tag & 0xff);
+    }
+    if (file_contents) free(file_contents);
+}
+
+
diff --git a/src/metadata.h b/src/metadata.h
new file mode 100644 (file)
index 0000000..66a8762
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef METADATA_H
+#define METADATA_H
+
+typedef struct aacenc_tag_entry_t {
+    uint32_t    tag;
+    const char *name;
+    const char *data;
+    uint32_t    data_size;
+    int         is_file_name;
+} aacenc_tag_entry_t;
+
+typedef struct aacenc_tag_param_t {
+    aacenc_tag_entry_t *tag_table;
+    unsigned tag_count;
+    unsigned tag_table_capacity;
+} aacenc_tag_param_t;
+
+char *aacenc_load_tag_from_file(const char *path, uint32_t *data_size);
+
+void aacenc_param_add_itmf_entry(aacenc_tag_param_t *params, uint32_t tag,
+                                 const char *key, const char *value,
+                                 uint32_t size, int is_file_name);
+
+void aacenc_put_tags_from_json(m4af_ctx_t *m4af, const char *json_filename);
+
+void aacenc_put_tag_entry(m4af_ctx_t *m4af, const aacenc_tag_entry_t *tag);
+
+#endif
diff --git a/src/parson.c b/src/parson.c
new file mode 100644 (file)
index 0000000..6e01a87
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ Parson ( http://kgabis.github.com/parson/ )
+ Copyright (c) 2012 Krzysztof Gabis
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include "parson.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define ERROR                      0
+#define SUCCESS                    1
+#define STARTING_CAPACITY         15
+#define ARRAY_MAX_CAPACITY    122880 /* 15*(2^13) */
+#define OBJECT_MAX_CAPACITY      960 /* 15*(2^6)  */
+#define MAX_NESTING               19
+#define sizeof_token(a)       (sizeof(a) - 1)
+#define skip_char(str)        ((*str)++)
+#define skip_whitespaces(str) while (isspace(**string)) { skip_char(string); }
+#define MAX(a, b)             ((a) > (b) ? (a) : (b))
+
+#define parson_malloc(a)     malloc(a)
+#define parson_free(a)       free((void*)a)
+#define parson_realloc(a, b) realloc(a, b)
+
+/* Type definitions */
+typedef union json_value_value {
+    const char  *string;
+    double       number;
+    JSON_Object *object;
+    JSON_Array  *array;
+    int          boolean;
+    int          null;
+} JSON_Value_Value;
+
+struct json_value_t {
+    JSON_Value_Type     type;
+    JSON_Value_Value    value;
+};
+
+struct json_object_t {
+    const char **names;
+    JSON_Value **values;
+    size_t       count;
+    size_t       capacity;
+};
+
+struct json_array_t {
+    JSON_Value **items;
+    size_t       count;
+    size_t       capacity;
+};
+
+/* Various */
+static int    try_realloc(void **ptr, size_t new_size);
+static char * parson_strndup(const char *string, size_t n);
+static int    is_utf(const unsigned char *string);
+static int    is_decimal(const char *string, size_t length);
+
+/* JSON Object */
+static JSON_Object * json_object_init(void);
+static int           json_object_add(JSON_Object *object, const char *name, JSON_Value *value);
+static int           json_object_resize(JSON_Object *object, size_t capacity);
+static JSON_Value  * json_object_nget_value(const JSON_Object *object, const char *name, size_t n);
+static void          json_object_free(JSON_Object *object);
+
+/* JSON Array */
+static JSON_Array * json_array_init(void);
+static int          json_array_add(JSON_Array *array, JSON_Value *value);
+static int          json_array_resize(JSON_Array *array, size_t capacity);
+static void         json_array_free(JSON_Array *array);
+
+/* JSON Value */
+static JSON_Value * json_value_init_object(void);
+static JSON_Value * json_value_init_array(void);
+static JSON_Value * json_value_init_string(const char *string);
+static JSON_Value * json_value_init_number(double number);
+static JSON_Value * json_value_init_boolean(int boolean);
+static JSON_Value * json_value_init_null(void);
+
+/* Parser */
+static void         skip_quotes(const char **string);
+static const char * get_processed_string(const char **string);
+static JSON_Value * parse_object_value(const char **string, size_t nesting);
+static JSON_Value * parse_array_value(const char **string, size_t nesting);
+static JSON_Value * parse_string_value(const char **string);
+static JSON_Value * parse_boolean_value(const char **string);
+static JSON_Value * parse_number_value(const char **string);
+static JSON_Value * parse_null_value(const char **string);
+static JSON_Value * parse_value(const char **string, size_t nesting);
+
+/* Various */
+static int try_realloc(void **ptr, size_t new_size) {
+    void *reallocated_ptr = parson_realloc(*ptr, new_size);
+    if (!reallocated_ptr) { return ERROR; }
+    *ptr = reallocated_ptr;
+    return SUCCESS;
+}
+
+static char * parson_strndup(const char *string, size_t n) {
+    char *output_string = (char*)parson_malloc(n + 1);
+    if (!output_string) { return NULL; }
+    output_string[n] = '\0';
+    strncpy(output_string, string, n);
+    return output_string;
+}
+
+static int is_utf(const unsigned char *s) {
+    return isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]);
+}
+
+static int is_decimal(const char *string, size_t length) {
+    if (length > 1 && string[0] == '0' && string[1] != '.') { return 0; }
+    if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { return 0; }
+    while (length--) { if (strchr("xX", string[length])) { return 0; } }
+    return 1;
+}
+
+/* JSON Object */
+static JSON_Object * json_object_init(void) {
+    JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));
+    if (!new_obj) { return NULL; }
+    new_obj->names = (const char**)NULL;
+    new_obj->values = (JSON_Value**)NULL;
+    new_obj->capacity = 0;
+    new_obj->count = 0;
+    return new_obj;
+}
+
+static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value) {
+    size_t index;
+    if (object->count >= object->capacity) {
+        size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY);
+        if (new_capacity > OBJECT_MAX_CAPACITY) { return ERROR; }
+        if (json_object_resize(object, new_capacity) == ERROR) { return ERROR; }
+    }
+    if (json_object_get_value(object, name) != NULL) { return ERROR; }
+    index = object->count;
+    object->names[index] = parson_strndup(name, strlen(name));
+    if (!object->names[index]) { return ERROR; }
+    object->values[index] = value;
+    object->count++;
+    return SUCCESS;
+}
+
+static int json_object_resize(JSON_Object *object, size_t capacity) {
+    if (try_realloc((void**)&object->names, capacity * sizeof(char*)) == ERROR) { return ERROR; }
+    if (try_realloc((void**)&object->values, capacity * sizeof(JSON_Value*)) == ERROR) { return ERROR; }
+    object->capacity = capacity;
+    return SUCCESS;
+}
+
+static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) {
+    size_t i, name_length;
+    for (i = 0; i < json_object_get_count(object); i++) {
+        name_length = strlen(object->names[i]);
+        if (name_length != n) { continue; }
+        if (strncmp(object->names[i], name, n) == 0) { return object->values[i]; }
+    }
+    return NULL;
+}
+
+static void json_object_free(JSON_Object *object) {
+    while(object->count--) {
+        parson_free(object->names[object->count]);
+        json_value_free(object->values[object->count]);
+    }
+    parson_free(object->names);
+    parson_free(object->values);
+    parson_free(object);
+}
+
+/* JSON Array */
+static JSON_Array * json_array_init(void) {
+    JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array));
+    if (!new_array) { return NULL; }
+    new_array->items = (JSON_Value**)NULL;
+    new_array->capacity = 0;
+    new_array->count = 0;
+    return new_array;
+}
+
+static int json_array_add(JSON_Array *array, JSON_Value *value) {
+    if (array->count >= array->capacity) {
+        size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);
+        if (new_capacity > ARRAY_MAX_CAPACITY) { return ERROR; }
+        if (!json_array_resize(array, new_capacity)) { return ERROR; }
+    }
+    array->items[array->count] = value;
+    array->count++;
+    return SUCCESS;
+}
+
+static int json_array_resize(JSON_Array *array, size_t capacity) {
+    if (try_realloc((void**)&array->items, capacity * sizeof(JSON_Value*)) == ERROR) { return ERROR; }
+    array->capacity = capacity;
+    return SUCCESS;
+}
+
+static void json_array_free(JSON_Array *array) {
+    while (array->count--) { json_value_free(array->items[array->count]); }
+    parson_free(array->items);
+    parson_free(array);
+}
+
+/* JSON Value */
+static JSON_Value * json_value_init_object(void) {
+    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
+    if (!new_value) { return NULL; }
+    new_value->type = JSONObject;
+    new_value->value.object = json_object_init();
+    if (!new_value->value.object) { parson_free(new_value); return NULL; }
+    return new_value;
+}
+
+static JSON_Value * json_value_init_array(void) {
+    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
+    if (!new_value) { return NULL; }
+    new_value->type = JSONArray;
+    new_value->value.array = json_array_init();
+    if (!new_value->value.array) { parson_free(new_value); return NULL; }
+    return new_value;
+}
+
+static JSON_Value * json_value_init_string(const char *string) {
+    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
+    if (!new_value) { return NULL; }
+    new_value->type = JSONString;
+    new_value->value.string = string;
+    return new_value;
+}
+
+static JSON_Value * json_value_init_number(double number) {
+    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
+    if (!new_value) { return NULL; }
+    new_value->type = JSONNumber;
+    new_value->value.number = number;
+    return new_value;
+}
+
+static JSON_Value * json_value_init_boolean(int boolean) {
+    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
+    if (!new_value) { return NULL; }
+    new_value->type = JSONBoolean;
+    new_value->value.boolean = boolean;
+    return new_value;
+}
+
+static JSON_Value * json_value_init_null(void) {
+    JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
+    if (!new_value) { return NULL; }
+    new_value->type = JSONNull;
+    return new_value;
+}
+
+/* Parser */
+static void skip_quotes(const char **string) {
+    skip_char(string);
+    while (**string != '\"') {
+        if (**string == '\0') { return; }
+        if (**string == '\\') { skip_char(string); if (**string == '\0') { return; }}
+        skip_char(string);
+    }
+    skip_char(string);
+}
+
+/* Returns contents of a string inside double quotes and parses escaped
+ characters inside.
+ Example: "\u006Corem ipsum" -> lorem ipsum */
+static const char * get_processed_string(const char **string) {
+    const char *string_start = *string;
+    char *output, *processed_ptr, *unprocessed_ptr, current_char;
+    unsigned int utf_val;
+    skip_quotes(string);
+    if (**string == '\0') { return NULL; }
+    output = parson_strndup(string_start + 1, *string  - string_start - 2);
+    if (!output) { return NULL; }
+    processed_ptr = unprocessed_ptr = output;
+    while (*unprocessed_ptr) {
+        current_char = *unprocessed_ptr;
+        if (current_char == '\\') {
+            unprocessed_ptr++;
+            current_char = *unprocessed_ptr;
+            switch (current_char) {
+                case '\"': case '\\': case '/': break;
+                case 'b': current_char = '\b'; break;
+                case 'f': current_char = '\f'; break;
+                case 'n': current_char = '\n'; break;
+                case 'r': current_char = '\r'; break;
+                case 't': current_char = '\t'; break;
+                case 'u':
+                    unprocessed_ptr++;
+                    if (!is_utf((const unsigned char*)unprocessed_ptr) ||
+                        sscanf(unprocessed_ptr, "%4x", &utf_val) == EOF) {
+                        parson_free(output); return NULL;
+                    }
+                    if (utf_val < 0x80) {
+                        current_char = utf_val;
+                    } else if (utf_val < 0x800) {
+                        *processed_ptr++ = (utf_val >> 6) | 0xC0;
+                        current_char = ((utf_val | 0x80) & 0xBF);
+                    } else {
+                        *processed_ptr++ = (utf_val >> 12) | 0xE0;
+                        *processed_ptr++ = (((utf_val >> 6) | 0x80) & 0xBF);
+                        current_char = ((utf_val | 0x80) & 0xBF);
+                    }
+                    unprocessed_ptr += 3;
+                    break;
+                default:
+                    parson_free(output);
+                    return NULL;
+                    break;
+            }
+        } else if ((unsigned char)current_char < 0x20) { /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */
+            parson_free(output);
+            return NULL;
+        }
+        *processed_ptr = current_char;
+        processed_ptr++;
+        unprocessed_ptr++;
+    }
+    *processed_ptr = '\0';
+    if (try_realloc((void**)&output, strlen(output) + 1) == ERROR) { return NULL; }
+    return output;
+}
+
+static JSON_Value * parse_value(const char **string, size_t nesting) {
+    if (nesting > MAX_NESTING) { return NULL; }
+    skip_whitespaces(string);
+    switch (**string) {
+        case '{':
+            return parse_object_value(string, nesting + 1);
+        case '[':
+            return parse_array_value(string, nesting + 1);
+        case '\"':
+            return parse_string_value(string);
+        case 'f': case 't':
+            return parse_boolean_value(string);
+        case '-':
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+            return parse_number_value(string);
+        case 'n':
+            return parse_null_value(string);
+        default:
+            return NULL;
+    }
+}
+
+static JSON_Value * parse_object_value(const char **string, size_t nesting) {
+    JSON_Value *output_value = json_value_init_object(), *new_value = NULL;
+    JSON_Object *output_object = json_value_get_object(output_value);
+    const char *new_key = NULL;
+    if (!output_value) { return NULL; }
+    skip_char(string);
+    skip_whitespaces(string);
+    if (**string == '}') { skip_char(string); return output_value; } /* empty object */
+    while (**string != '\0') {
+        new_key = get_processed_string(string);
+        skip_whitespaces(string);
+        if (!new_key || **string != ':') {
+            json_value_free(output_value);
+            return NULL;
+        }
+        skip_char(string);
+        new_value = parse_value(string, nesting);
+        if (!new_value) {
+            parson_free(new_key);
+            json_value_free(output_value);
+            return NULL;
+        }
+        if(!json_object_add(output_object, new_key, new_value)) {
+            parson_free(new_key);
+            parson_free(new_value);
+            json_value_free(output_value);
+            return NULL;
+        }
+        parson_free(new_key);
+        skip_whitespaces(string);
+        if (**string != ',') { break; }
+        skip_char(string);
+        skip_whitespaces(string);
+    }
+    skip_whitespaces(string);
+    if (**string != '}' || /* Trim object after parsing is over */
+         json_object_resize(output_object, json_object_get_count(output_object)) == ERROR) {
+        json_value_free(output_value);
+        return NULL;
+    }
+    skip_char(string);
+    return output_value;
+}
+
+static JSON_Value * parse_array_value(const char **string, size_t nesting) {
+    JSON_Value *output_value = json_value_init_array(), *new_array_value = NULL;
+    JSON_Array *output_array = json_value_get_array(output_value);
+    if (!output_value) { return NULL; }
+    skip_char(string);
+    skip_whitespaces(string);
+    if (**string == ']') { /* empty array */
+        skip_char(string);
+        return output_value;
+    }
+    while (**string != '\0') {
+        new_array_value = parse_value(string, nesting);
+        if (!new_array_value) {
+            json_value_free(output_value);
+            return NULL;
+        }
+        if(json_array_add(output_array, new_array_value) == ERROR) {
+            parson_free(new_array_value);
+            json_value_free(output_value);
+            return NULL;
+        }
+        skip_whitespaces(string);
+        if (**string != ',') { break; }
+        skip_char(string);
+        skip_whitespaces(string);
+    }
+    skip_whitespaces(string);
+    if (**string != ']' || /* Trim array after parsing is over */
+         json_array_resize(output_array, json_array_get_count(output_array)) == ERROR) {
+        json_value_free(output_value);
+        return NULL;
+    }
+    skip_char(string);
+    return output_value;
+}
+
+static JSON_Value * parse_string_value(const char **string) {
+    const char *new_string = get_processed_string(string);
+    if (!new_string) { return NULL; }
+    return json_value_init_string(new_string);
+}
+
+static JSON_Value * parse_boolean_value(const char **string) {
+    size_t true_token_size = sizeof_token("true");
+    size_t false_token_size = sizeof_token("false");
+    if (strncmp("true", *string, true_token_size) == 0) {
+        *string += true_token_size;
+        return json_value_init_boolean(1);
+    } else if (strncmp("false", *string, false_token_size) == 0) {
+        *string += false_token_size;
+        return json_value_init_boolean(0);
+    }
+    return NULL;
+}
+
+static JSON_Value * parse_number_value(const char **string) {
+    char *end;
+    double number = strtod(*string, &end);
+    JSON_Value *output_value;
+    if (is_decimal(*string, end - *string)) {
+        *string = end;
+        output_value = json_value_init_number(number);
+    } else {
+        output_value = NULL;
+    }
+    return output_value;
+}
+
+static JSON_Value * parse_null_value(const char **string) {
+    size_t token_size = sizeof_token("null");
+    if (strncmp("null", *string, token_size) == 0) {
+        *string += token_size;
+        return json_value_init_null();
+    }
+    return NULL;
+}
+
+/* Parser API */
+JSON_Value * json_parse_file(const char *filename) {
+    FILE *fp = fopen(filename, "r");
+    size_t file_size;
+    char *file_contents;
+    JSON_Value *output_value;
+    if (!fp) { return NULL; }
+    fseek(fp, 0L, SEEK_END);
+    file_size = ftell(fp);
+    rewind(fp);
+    file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1));
+    if (!file_contents) { fclose(fp); return NULL; }
+    fread(file_contents, file_size, 1, fp);
+    fclose(fp);
+    file_contents[file_size] = '\0';
+    output_value = json_parse_string(file_contents);
+    parson_free(file_contents);
+    return output_value;
+}
+
+JSON_Value * json_parse_string(const char *string) {
+    if (!string || (*string != '{' && *string != '[')) { return NULL; }
+    return parse_value((const char**)&string, 0);
+}
+
+/* JSON Object API */
+JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) {
+    return json_object_nget_value(object, name, strlen(name));
+}
+
+const char * json_object_get_string(const JSON_Object *object, const char *name) {
+    return json_value_get_string(json_object_get_value(object, name));
+}
+
+double json_object_get_number(const JSON_Object *object, const char *name) {
+    return json_value_get_number(json_object_get_value(object, name));
+}
+
+JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) {
+    return json_value_get_object(json_object_get_value(object, name));
+}
+
+JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) {
+    return json_value_get_array(json_object_get_value(object, name));
+}
+
+int json_object_get_boolean(const JSON_Object *object, const char *name) {
+    return json_value_get_boolean(json_object_get_value(object, name));
+}
+
+JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) {
+    const char *dot_position = strchr(name, '.');
+    if (!dot_position) { return json_object_get_value(object, name); }
+    object = json_value_get_object(json_object_nget_value(object, name, dot_position - name));
+    return json_object_dotget_value(object, dot_position + 1);
+}
+
+const char * json_object_dotget_string(const JSON_Object *object, const char *name) {
+    return json_value_get_string(json_object_dotget_value(object, name));
+}
+
+double json_object_dotget_number(const JSON_Object *object, const char *name) {
+    return json_value_get_number(json_object_dotget_value(object, name));
+}
+
+JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) {
+    return json_value_get_object(json_object_dotget_value(object, name));
+}
+
+JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) {
+    return json_value_get_array(json_object_dotget_value(object, name));
+}
+
+int json_object_dotget_boolean(const JSON_Object *object, const char *name) {
+    return json_value_get_boolean(json_object_dotget_value(object, name));
+}
+
+size_t json_object_get_count(const JSON_Object *object) {
+    return object ? object->count : 0;
+}
+
+const char * json_object_get_name(const JSON_Object *object, size_t index) {
+    if (index >= json_object_get_count(object)) { return NULL; }
+    return object->names[index];
+}
+
+/* JSON Array API */
+JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) {
+    if (index >= json_array_get_count(array)) { return NULL; }
+    return array->items[index];
+}
+
+const char * json_array_get_string(const JSON_Array *array, size_t index) {
+    return json_value_get_string(json_array_get_value(array, index));
+}
+
+double json_array_get_number(const JSON_Array *array, size_t index) {
+    return json_value_get_number(json_array_get_value(array, index));
+}
+
+JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) {
+    return json_value_get_object(json_array_get_value(array, index));
+}
+
+JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) {
+    return json_value_get_array(json_array_get_value(array, index));
+}
+
+int json_array_get_boolean(const JSON_Array *array, size_t index) {
+    return json_value_get_boolean(json_array_get_value(array, index));
+}
+
+size_t json_array_get_count(const JSON_Array *array) {
+    return array ? array->count : 0;
+}
+
+/* JSON Value API */
+JSON_Value_Type json_value_get_type(const JSON_Value *value) {
+    return value ? value->type : JSONError;
+}
+
+JSON_Object * json_value_get_object(const JSON_Value *value) {
+    return json_value_get_type(value) == JSONObject ? value->value.object : NULL;
+}
+
+JSON_Array * json_value_get_array(const JSON_Value *value) {
+    return json_value_get_type(value) == JSONArray ? value->value.array : NULL;
+}
+
+const char * json_value_get_string(const JSON_Value *value) {
+    return json_value_get_type(value) == JSONString ? value->value.string : NULL;
+}
+
+double json_value_get_number(const JSON_Value *value) {
+    return json_value_get_type(value) == JSONNumber ? value->value.number : 0;
+}
+
+int json_value_get_boolean(const JSON_Value *value) {
+    return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1;
+}
+
+void json_value_free(JSON_Value *value) {
+    switch (json_value_get_type(value)) {
+        case JSONObject:
+            json_object_free(value->value.object);
+            break;
+        case JSONString:
+            if (value->value.string) { parson_free(value->value.string); }
+            break;
+        case JSONArray:
+            json_array_free(value->value.array);
+            break;
+        default:
+            break;
+    }
+    parson_free(value);
+}
diff --git a/src/parson.h b/src/parson.h
new file mode 100644 (file)
index 0000000..00728b1
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ Parson ( http://kgabis.github.com/parson/ )
+ Copyright (c) 2012 Krzysztof Gabis
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef parson_parson_h
+#define parson_parson_h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif    
+    
+#include <stddef.h>   /* size_t */    
+    
+/* Types and enums */
+typedef struct json_object_t JSON_Object;
+typedef struct json_array_t  JSON_Array;
+typedef struct json_value_t  JSON_Value;
+
+typedef enum json_value_type {
+    JSONError   = 0,
+    JSONNull    = 1,
+    JSONString  = 2,
+    JSONNumber  = 3,
+    JSONObject  = 4,
+    JSONArray   = 5,
+    JSONBoolean = 6
+} JSON_Value_Type;
+
+/* Parses first JSON value in a file, returns NULL in case of error */
+JSON_Value  * json_parse_file(const char *filename);
+
+/*  Parses first JSON value in a string, returns NULL in case of error */
+JSON_Value  * json_parse_string(const char *string);
+
+/* JSON Object */
+JSON_Value  * json_object_get_value  (const JSON_Object *object, const char *name);
+const char  * json_object_get_string (const JSON_Object *object, const char *name);
+JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
+JSON_Array  * json_object_get_array  (const JSON_Object *object, const char *name);
+double        json_object_get_number (const JSON_Object *object, const char *name);
+int           json_object_get_boolean(const JSON_Object *object, const char *name);
+
+/* dotget functions enable addressing values with dot notation in nested objects,
+ just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
+ Because valid names in JSON can contain dots, some values may be inaccessible
+ this way. */
+JSON_Value  * json_object_dotget_value  (const JSON_Object *object, const char *name);
+const char  * json_object_dotget_string (const JSON_Object *object, const char *name);
+JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
+JSON_Array  * json_object_dotget_array  (const JSON_Object *object, const char *name);
+double        json_object_dotget_number (const JSON_Object *object, const char *name);
+int           json_object_dotget_boolean(const JSON_Object *object, const char *name);
+
+/* Functions to get available names */
+size_t        json_object_get_count(const JSON_Object *object);
+const char  * json_object_get_name (const JSON_Object *object, size_t index);
+    
+/* JSON Array */
+JSON_Value  * json_array_get_value  (const JSON_Array *array, size_t index);
+const char  * json_array_get_string (const JSON_Array *array, size_t index);
+JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
+JSON_Array  * json_array_get_array  (const JSON_Array *array, size_t index);
+double        json_array_get_number (const JSON_Array *array, size_t index);
+int           json_array_get_boolean(const JSON_Array *array, size_t index);
+size_t        json_array_get_count  (const JSON_Array *array);
+
+/* JSON Value */
+JSON_Value_Type json_value_get_type   (const JSON_Value *value);
+JSON_Object *   json_value_get_object (const JSON_Value *value);
+JSON_Array  *   json_value_get_array  (const JSON_Value *value);
+const char  *   json_value_get_string (const JSON_Value *value);
+double          json_value_get_number (const JSON_Value *value);
+int             json_value_get_boolean(const JSON_Value *value);
+void            json_value_free       (JSON_Value *value);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 2efe25b7331e620e90ebe4ac8e79819a2b274db9..1e3bbdc6864e6b705ad72da490e2c75971ac5de1 100644 (file)
--- a/version.h
+++ b/version.h
@@ -1,4 +1,4 @@
 #ifndef VERSION_H
 #define VERSION_H
-const char *fdkaac_version = "0.1.6";
+const char *fdkaac_version = "0.1.7";
 #endif
This page took 0.033998 seconds and 4 git commands to generate.