text: /*
   * Copyright 2018-present MongoDB, Inc.
   *
   * 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.
   */
  
  /* -- sphinx-include-start -- */
  /* Reports the maximum nested depth of a BSON document. */
  #include <bson/bson.h>
  
  #include <assert.h>
  #include <stdio.h>
  #include <stdlib.h>
  
  typedef struct {
     uint32_t depth;
     int max_depth;
     bool valid;
  } check_depth_t;
  
  bool
  _check_depth_document (const bson_iter_t *iter,
                         const char *key,
                         const bson_t *v_document,
                         void *data);
  
  static const bson_visitor_t check_depth_funcs = {
     NULL,
     NULL,
     NULL,
     NULL,
     NULL,
     _check_depth_document,
     _check_depth_document,
     NULL,
  };
  
  bool
  _check_depth_document (const bson_iter_t *iter,
                         const char *key,
                         const bson_t *v_document,
                         void *data)
  {
     check_depth_t *state = (check_depth_t *) data;
     bson_iter_t child;
  
     if (!bson_iter_init (&child, v_document)) {
        fprintf (stderr, "corrupt\n");
        return true; /* cancel */
     }
  
     state->depth++;
     if (state->depth > state->max_depth) {
        state->valid = false;
        return true; /* cancel */
     }
  
     bson_iter_visit_all (&child, &check_depth_funcs, state);
     state->depth--;
     return false; /* continue */
  }
  
  void
  check_depth (const bson_t *bson, int max_depth)
  {
     bson_iter_t iter;
     check_depth_t state = {0};
  
     if (!bson_iter_init (&iter, bson)) {
        fprintf (stderr, "corrupt\n");
     }
  
     state.valid = true;
     state.max_depth = max_depth;
     _check_depth_document (&iter, NULL, bson, &state);
     if (!state.valid) {
        printf ("document exceeds maximum depth of %d\n", state.max_depth);
     } else {
        char *as_json = bson_as_canonical_extended_json (bson, NULL);
        printf ("document %s ", as_json);
        printf ("is valid\n");
        bson_free (as_json);
     }
  }
  
  int
  main (int argc, char **argv)
  {
     bson_reader_t *bson_reader;
     const bson_t *bson;
     bool reached_eof;
     char *filename;
     bson_error_t error;
     int max_depth;
  
     if (argc != 3) {
        fprintf (stderr, "usage: %s FILE MAX_DEPTH\n", argv[0]);
        fprintf (stderr, "Checks that the depth of the BSON contained in FILE\n");
        fprintf (stderr, "does not exceed MAX_DEPTH\n");
     }
  
     filename = argv[1];
     max_depth = atoi (argv[2]);
     bson_reader = bson_reader_new_from_file (filename, &error);
     if (!bson_reader) {
        printf ("could not read %s: %s\n", filename, error.message);
        return 1;
     }
  
     while ((bson = bson_reader_read (bson_reader, &reached_eof))) {
        check_depth (bson, max_depth);
     }
  
     if (!reached_eof) {
        printf ("error reading BSON\n");
     }
  
     bson_reader_destroy (bson_reader);
     return 0;
  }
  