/* bson-inspect.c - BSON inspector, example application. * Copyright 2011, 2012 Gergely Nagy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include static void _indent (gint level, gboolean verbose) { gint i; if (!verbose) return; for (i = 1; i <= level; i++) printf (" "); } static void bson_dump (bson *b, gint ilevel, gboolean verbose, gboolean as_array) { bson_cursor *c; gboolean first = TRUE; c = bson_cursor_new (b); while (bson_cursor_next (c)) { if (!first) { printf (", "); if (verbose) printf ("\n"); } first = FALSE; if (verbose) { _indent (ilevel, verbose); printf ("/* type='%s'; */\n", bson_cursor_type_as_string (c) + 10); } _indent (ilevel, verbose); if (!as_array) { printf ("\"%s\" : ", bson_cursor_key (c)); } switch (bson_cursor_type (c)) { case BSON_TYPE_DOUBLE: { gdouble d; bson_cursor_get_double (c, &d); printf ("%f", d); break; } case BSON_TYPE_STRING: { const gchar *s; gchar *s2; bson_cursor_get_string (c, &s); s2 = g_strescape (s, NULL); printf ("\"%s\"", s2); g_free (s2); break; } case BSON_TYPE_OID: { const guint8 *oid; gint j; bson_cursor_get_oid (c, &oid); printf ("ObjectId( \""); for (j = 0; j < 12; j++) printf ("%02x", oid[j]); printf ("\" )"); break; } case BSON_TYPE_BOOLEAN: { gboolean b; bson_cursor_get_boolean (c, &b); printf ((b) ? "true" : "false"); break; } case BSON_TYPE_REGEXP: { const gchar *r, *o; gchar *r2, *o2; bson_cursor_get_regex (c, &r, &o); r2 = g_strescape (r, NULL); o2 = g_strescape (o, NULL); printf ("Regex(\"/%s/%s\")", r2, o2); g_free (r2); g_free (o2); break; } case BSON_TYPE_NULL: { printf ("null"); break; } case BSON_TYPE_JS_CODE: { const gchar *js; gchar *js2; bson_cursor_get_javascript (c, &js); js2 = g_strescape (js, NULL); printf ("%s", js2); g_free (js2); break; } case BSON_TYPE_SYMBOL: { const gchar *s; gchar *s2; bson_cursor_get_symbol (c, &s); s2 = g_strescape (s, NULL); printf ("%s", s2); g_free (s2); break; } case BSON_TYPE_INT32: { gint32 l32; bson_cursor_get_int32 (c, &l32); printf ("%d", l32); break; } case BSON_TYPE_INT64: { gint64 l64; bson_cursor_get_int64 (c, &l64); printf ("%" G_GINT64_FORMAT, l64); break; } case BSON_TYPE_DOCUMENT: { bson *sd; bson_cursor_get_document (c, &sd); printf ("{ "); if (verbose) printf ("/* size='%d' */\n", bson_size (sd)); bson_dump (sd, ilevel + 1, verbose, FALSE); if (verbose) { printf ("\n"); _indent (ilevel, verbose); printf ("}"); } else printf (" }"); bson_free (sd); break; } case BSON_TYPE_ARRAY: { bson *sa; bson_cursor_get_array (c, &sa); printf ("[ "); if (verbose) printf ("/* size='%d' */\n", bson_size (sa)); bson_dump (sa, ilevel + 1, verbose, TRUE); if (verbose) { printf ("\n"); _indent (ilevel, verbose); printf ("]"); } else printf (" ]"); bson_free (sa); break; } case BSON_TYPE_BINARY: { const guint8 *data; gint32 size; bson_binary_subtype t; gchar *b64; bson_cursor_get_binary (c, &t, &data, &size); b64 = g_base64_encode (data, size); printf ("{ "); if (verbose) { printf ("/* size='%d' */\n", size); _indent (ilevel + 1, verbose); } printf ("\"$binary\" : \"%s\",", b64); if (verbose) { printf ("\n"); _indent (ilevel + 1, verbose); } else printf (" "); printf ("\"$type\" : \"%02d\"", t); if (verbose) { printf ("\n"); _indent (ilevel, verbose); } else printf (" "); printf ("}"); g_free (b64); break; } case BSON_TYPE_JS_CODE_W_SCOPE: case BSON_TYPE_UNDEFINED: case BSON_TYPE_UTC_DATETIME: case BSON_TYPE_DBPOINTER: case BSON_TYPE_TIMESTAMP: case BSON_TYPE_MIN: case BSON_TYPE_MAX: default: printf ("\"\""); break; } } bson_cursor_free (c); } int main (int argc, char *argv[]) { int fd; off_t offs = 0; bson *b; guint8 *data; struct stat st; gint64 i = 1; GOptionContext *context; gboolean verbose = FALSE; GError *error = NULL; GOptionEntry entries[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; context = g_option_context_new ("- inspect a BSON dump"); g_option_context_add_main_entries (context, entries, "bson-inspect"); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_print ("option parsing failed: %s\n", error->message); exit (1); } if (argc < 2) { gchar **nargv; argc = 2; nargv = g_new (gchar *, 3); nargv[0] = argv[0]; nargv[1] = "--help"; nargv[2] = NULL; g_option_context_parse (context, &argc, (gchar ***)&nargv, &error); exit (1); } fd = open (argv[1], O_RDONLY); if (fd == -1) { fprintf (stderr, "Error opening file '%s': %s\n", argv[1], strerror (errno)); exit (1); } if (fstat (fd, &st) != 0) { fprintf (stderr, "Error fstat()ing file '%s': %s\n", argv[1], strerror (errno)); close (fd); exit (1); } data = mmap (NULL, (size_t)st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { fprintf (stderr, "Error mmap()ing file '%s': %s\n", argv[1], strerror (errno)); close (fd); exit (1); } while (offs < st.st_size) { b = bson_new_from_data ((const guint8 *)(data + offs), bson_stream_doc_size (data, offs) - 1); bson_finish (b); offs += bson_size (b); if (verbose) printf ("/* Document #%" G_GUINT64_FORMAT "; size='%d' */\n", i, bson_size (b)); printf ("{ "); if (verbose) printf ("\n"); bson_dump (b, 1, verbose, FALSE); if (verbose) printf ("\n}\n"); else printf (" }\n"); if (verbose) printf ("\n"); bson_free (b); i++; } munmap (data, st.st_size); close (fd); return 0; }