int main()

in src/main.c [246:507]


int main(int argc, char * const *argv) {
  if (argc < 2) {
    usage();
    return 0;
  }
  const char *command = argv[1];
  if (strcmp(command, "info") == 0) {
    if (argc < 3) {
      usage_info();
      return 1;
    }
    return info(argc - 2, argv + 2);
  } else if (strcmp(command, "get") == 0) {
    if (argc < 4) {
      usage_get();
      return 1;
    }
    const char *index_filename = argv[2];
    char *log_filename = sparkey_create_log_filename(index_filename);
    if (log_filename == NULL) {
      fprintf(stderr, "index filename must end with .spi\n");
      return 1;
    }
    int retval = get(argv[2], log_filename, argv[3]);
    free(log_filename);
    return retval;
  } else if (strcmp(command, "writehash") == 0) {
    if (argc < 3) {
      usage_writehash();
      return 1;
    }
    const char *log_filename = argv[2];
    char *index_filename = sparkey_create_index_filename(log_filename);
    if (index_filename == NULL) {
      fprintf(stderr, "log filename must end with .spl\n");
      return 1;
    }
    int retval = writehash(index_filename, log_filename);
    free(index_filename);
    return retval;
  } else if (strcmp(command, "createlog") == 0) {
    opterr = 0;
    optind = 2;
    int opt_char;
    int block_size = COMP_DEFAULT_BLOCKSIZE;
    sparkey_compression_type compression_type = SPARKEY_COMPRESSION_NONE;
    while ((opt_char = getopt (argc, argv, "b:c:")) != -1) {
      switch (opt_char) {
      case 'b':
        if (sscanf(optarg, "%d", &block_size) != 1) {
          fprintf(stderr, "Block size must be an integer, but was '%s'\n", optarg);
          return 1;
        }
        if (block_size > COMP_MAX_BLOCKSIZE || block_size < COMP_MIN_BLOCKSIZE) {
          fprintf(stderr, "Block size %d, not in range. Max is %d, min is %d\n",
          block_size, COMP_MAX_BLOCKSIZE, COMP_MIN_BLOCKSIZE);
          return 1;
        }
        break;
      case 'c':
        if (strcmp(optarg, "none") == 0) {
          compression_type = SPARKEY_COMPRESSION_NONE;
        } else if (strcmp(optarg, "snappy") == 0) {
          compression_type = SPARKEY_COMPRESSION_SNAPPY;
        } else if (strcmp(optarg, "zstd") == 0) {
          compression_type = SPARKEY_COMPRESSION_ZSTD;
        } else {
          fprintf(stderr, "Invalid compression type: '%s'\n", optarg);
          return 1;
        }
        break;
      case '?':
        if (optopt == 'b' || optopt == 'c') {
          fprintf(stderr, "Option -%c requires an argument.\n", optopt);
        } else if (isprint(optopt)) {
          fprintf(stderr, "Unknown option '-%c'.\n", optopt);
        } else {
          fprintf(stderr, "Unknown option character '\\x%x'.\n", optopt);
        }
        return 1;
      default:
        fprintf(stderr, "Unknown option parsing failure\n");
        return 1;
      }
    }

    if (optind >= argc) {
      usage_createlog();
      return 1;
    }

    const char *log_filename = argv[optind];
    sparkey_logwriter *writer;
    assert(sparkey_logwriter_create(&writer, log_filename,
      compression_type, block_size));
    assert(sparkey_logwriter_close(&writer));
    return 0;
  } else if (strcmp(command, "appendlog") == 0) {
    opterr = 0;
    optind = 2;
    int opt_char;
    char delimiter = '\t';
    while ((opt_char = getopt (argc, argv, "d:")) != -1) {
      switch (opt_char) {
      case 'd':
        if (strlen(optarg) != 1) {
          fprintf(stderr, "delimiter must be one character, but was '%s'\n", optarg);
          return 1;
        }
        delimiter = optarg[0];
        break;
      case '?':
        if (optopt == 'd') {
          fprintf(stderr, "Option -%c requires an argument.\n", optopt);
        } else if (isprint(optopt)) {
          fprintf(stderr, "Unknown option '-%c'.\n", optopt);
        } else {
          fprintf(stderr, "Unknown option character '\\x%x'.\n", optopt);
        }
        return 1;
      default:
        fprintf(stderr, "Unknown option parsing failure\n");
        return 1;
      }
    }

    if (optind >= argc) {
      usage_appendlog();
      return 1;
    }

    const char *log_filename = argv[optind];
    sparkey_logwriter *writer;
    assert(sparkey_logwriter_append(&writer, log_filename));
    int rc = append(writer, delimiter, stdin);
    assert(sparkey_logwriter_close(&writer));
    return rc;
  } else if (strcmp(command, "rewrite") == 0) {
    opterr = 0;
    optind = 2;
    int opt_char;
    int block_size = -1;
    sparkey_compression_type compression_type = SPARKEY_COMPRESSION_NONE;
    int compression_set = 0;
    while ((opt_char = getopt (argc, argv, "b:c:")) != -1) {
      switch (opt_char) {
      case 'b':
        if (sscanf(optarg, "%d", &block_size) != 1) {
          fprintf(stderr, "Block size must be an integer, but was '%s'\n", optarg);
          return 1;
        }
        if (block_size > COMP_MAX_BLOCKSIZE || block_size < COMP_MIN_BLOCKSIZE) {
          fprintf(stderr, "Block size %d, not in range. Max is %d, min is %d\n",
          block_size, COMP_MAX_BLOCKSIZE, COMP_MIN_BLOCKSIZE);
          return 1;
        }
        break;
      case 'c':
        compression_set = 1;
        if (strcmp(optarg, "none") == 0) {
          compression_type = SPARKEY_COMPRESSION_NONE;
        } else if (strcmp(optarg, "snappy") == 0) {
          compression_type = SPARKEY_COMPRESSION_SNAPPY;
        } else if (strcmp(optarg, "zstd") == 0) {
          compression_type = SPARKEY_COMPRESSION_ZSTD;
        } else {
          fprintf(stderr, "Invalid compression type: '%s'\n", optarg);
          return 1;
        }
        break;
      case '?':
        if (optopt == 'b' || optopt == 'c') {
          fprintf(stderr, "Option -%c requires an argument.\n", optopt);
        } else if (isprint(optopt)) {
          fprintf(stderr, "Unknown option '-%c'.\n", optopt);
        } else {
          fprintf(stderr, "Unknown option character '\\x%x'.\n", optopt);
        }
        return 1;
      default:
        fprintf(stderr, "Unknown option parsing failure\n");
        return 1;
      }
    }

    if (optind + 1 >= argc) {
      usage_rewrite();
      return 1;
    }

    const char *input_index_filename = argv[optind];
    const char *output_index_filename = argv[optind + 1];

    if (strcmp(input_index_filename, output_index_filename) == 0) {
      fprintf(stderr, "input and output must be different.\n");
      return 1;
    }

    char *input_log_filename = sparkey_create_log_filename(input_index_filename);
    if (input_log_filename == NULL) {
      fprintf(stderr, "input filename must end with .spi but was '%s'\n", input_index_filename);
      return 1;
    }

    char *output_log_filename = sparkey_create_log_filename(output_index_filename);
    if (output_log_filename == NULL) {
      fprintf(stderr, "output filename must end with .spi but was '%s'\n", output_index_filename);
      return 1;
    }

    sparkey_hashreader *reader;
    assert(sparkey_hash_open(&reader, input_index_filename, input_log_filename));
    sparkey_logreader *logreader = sparkey_hash_getreader(reader);
    if (!compression_set) {
      compression_type = sparkey_logreader_get_compression_type(logreader);
    }
    if (block_size == -1) {
      block_size = sparkey_logreader_get_compression_blocksize(logreader);
    }

    // TODO: skip rewrite if compression type and block size are unchanged, and there is no garbage in the log

    sparkey_logwriter *writer;
    assert(sparkey_logwriter_create(&writer, output_log_filename, compression_type, block_size));

    sparkey_logiter *iter;
    assert(sparkey_logiter_create(&iter, logreader));

    uint8_t *keybuf = malloc(sparkey_logreader_maxkeylen(logreader));
    uint8_t *valuebuf = malloc(sparkey_logreader_maxvaluelen(logreader));

    while (1) {
      assert(sparkey_logiter_next(iter, logreader));
      if (sparkey_logiter_state(iter) != SPARKEY_ITER_ACTIVE) {
        break;
      }
      uint64_t wanted_keylen = sparkey_logiter_keylen(iter);
      uint64_t actual_keylen;
      assert(sparkey_logiter_fill_key(iter, logreader, wanted_keylen, keybuf, &actual_keylen));
      uint64_t wanted_valuelen = sparkey_logiter_valuelen(iter);
      uint64_t actual_valuelen;
      assert(sparkey_logiter_fill_value(iter, logreader, wanted_valuelen, valuebuf, &actual_valuelen));

      assert(sparkey_logwriter_put(writer, wanted_keylen, keybuf, wanted_valuelen, valuebuf));
    }
    free(keybuf);
    free(valuebuf);
    sparkey_logiter_close(&iter);
    assert(sparkey_logwriter_close(&writer));
    sparkey_hash_close(&reader);

    writehash(output_index_filename, output_log_filename);

    return 0;
  } else if (strcmp(command, "help") == 0 || strcmp(command, "--help") == 0 || strcmp(command, "-h") == 0) {
    usage();
    return 0;
  } else {
    fprintf(stderr, "Unknown command: %s\n", command);
    return 1;
  }
}