"Fossies" - the Fresh Open Source Software Archive

Member "mlr-5.9.1/c/cli/mlrcli.c" (3 Sep 2020, 95444 Bytes) of package /linux/misc/mlr-5.9.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mlrcli.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.9.0_vs_5.9.1.

    1 #include <stdio.h>
    2 #include <stdlib.h>
    3 #include <string.h>
    4 
    5 #include "input/line_readers.h"
    6 
    7 #include "lib/mlr_arch.h"
    8 #include "lib/mlrutil.h"
    9 #include "lib/mlr_globals.h"
   10 #include "lib/mtrand.h"
   11 #include "containers/slls.h"
   12 #include "containers/lhmss.h"
   13 #include "containers/lhmsll.h"
   14 #include "input/lrec_readers.h"
   15 #include "dsl/function_manager.h"
   16 #include "dsl/mlr_dsl_cst.h"
   17 #include "mapping/mappers.h"
   18 #include "output/lrec_writers.h"
   19 #include "cli/mlrcli.h"
   20 #include "cli/quoting.h"
   21 #include "cli/argparse.h"
   22 #include "auxents/aux_entries.h"
   23 
   24 #ifdef HAVE_CONFIG_H
   25 #include "config.h"
   26 #define VERSION_STRING PACKAGE_VERSION
   27 #else
   28 #include "mlrvers.h"
   29 #define VERSION_STRING MLR_VERSION
   30 #endif
   31 
   32 // ----------------------------------------------------------------
   33 #define DEFAULT_OFMT                     "%lf"
   34 #define DEFAULT_OQUOTING                 QUOTE_MINIMAL
   35 #define DEFAULT_JSON_FLATTEN_SEPARATOR   ":"
   36 #define DEFAULT_OOSVAR_FLATTEN_SEPARATOR ":"
   37 #define DEFAULT_COMMENT_STRING           "#"
   38 
   39 // ASCII 1f and 1e
   40 #define ASV_FS "\x1f"
   41 #define ASV_RS "\x1e"
   42 
   43 #define ASV_FS_FOR_HELP "0x1f"
   44 #define ASV_RS_FOR_HELP "0x1e"
   45 
   46 // Unicode code points U+241F and U+241E, encoded as UTF-8.
   47 #define USV_FS "\xe2\x90\x9f"
   48 #define USV_RS "\xe2\x90\x9e"
   49 
   50 #define USV_FS_FOR_HELP "U+241F (UTF-8 0xe2909f)"
   51 #define USV_RS_FOR_HELP "U+241E (UTF-8 0xe2909e)"
   52 
   53 // ----------------------------------------------------------------
   54 static mapper_setup_t* mapper_lookup_table[] = {
   55 
   56     &mapper_altkv_setup,
   57     &mapper_bar_setup,
   58     &mapper_bootstrap_setup,
   59     &mapper_cat_setup,
   60     &mapper_check_setup,
   61     &mapper_clean_whitespace_setup,
   62     &mapper_count_setup,
   63     &mapper_count_distinct_setup,
   64     &mapper_count_similar_setup,
   65     &mapper_cut_setup,
   66     &mapper_decimate_setup,
   67     &mapper_fill_down_setup,
   68     &mapper_filter_setup,
   69     &mapper_format_values_setup,
   70     &mapper_fraction_setup,
   71     &mapper_grep_setup,
   72     &mapper_group_by_setup,
   73     &mapper_group_like_setup,
   74     &mapper_having_fields_setup,
   75     &mapper_head_setup,
   76     &mapper_histogram_setup,
   77     &mapper_join_setup,
   78     &mapper_label_setup,
   79     &mapper_least_frequent_setup,
   80     &mapper_merge_fields_setup,
   81     &mapper_most_frequent_setup,
   82     &mapper_nest_setup,
   83     &mapper_nothing_setup,
   84     &mapper_put_setup,
   85     &mapper_regularize_setup,
   86     &mapper_remove_empty_columns_setup,
   87     &mapper_rename_setup,
   88     &mapper_reorder_setup,
   89     &mapper_repeat_setup,
   90     &mapper_reshape_setup,
   91     &mapper_sample_setup,
   92     &mapper_sec2gmt_setup,
   93     &mapper_sec2gmtdate_setup,
   94     &mapper_seqgen_setup,
   95     &mapper_shuffle_setup,
   96     &mapper_skip_trivial_records_setup,
   97     &mapper_sort_setup,
   98     // xxx temp for 5.4.0 -- will continue work after
   99     // &mapper_sort_within_records_setup,
  100     &mapper_stats1_setup,
  101     &mapper_stats2_setup,
  102     &mapper_step_setup,
  103     &mapper_tac_setup,
  104     &mapper_tail_setup,
  105     &mapper_tee_setup,
  106     &mapper_top_setup,
  107     &mapper_uniq_setup,
  108     &mapper_unsparsify_setup,
  109 
  110 };
  111 static int mapper_lookup_table_length = sizeof(mapper_lookup_table) / sizeof(mapper_lookup_table[0]);
  112 
  113 // ----------------------------------------------------------------
  114 static void cli_load_mlrrc(cli_opts_t* popts);
  115 static void cli_try_load_mlrrc(cli_opts_t* popts, char* path);
  116 static int handle_mlrrc_line_1(cli_opts_t* popts, char* line);
  117 static int handle_mlrrc_line_2(cli_opts_t* popts, char* line);
  118 static int handle_mlrrc_line_3(cli_opts_t* popts, char* line);
  119 static int handle_mlrrc_line_4(cli_opts_t* popts, char** argv, int argc);
  120 
  121 static int cli_handle_misc_options(char** argv, int argc, int *pargi, cli_opts_t* popts);
  122 
  123 static lhmss_t* get_desc_to_chars_map();
  124 static lhmsll_t* get_default_repeat_ifses();
  125 static lhmsll_t* get_default_repeat_ipses();
  126 static lhmss_t* get_default_fses();
  127 static lhmss_t* get_default_pses();
  128 static lhmss_t* get_default_rses();
  129 static void free_opt_singletons();
  130 static char* rebackslash(char* sep);
  131 
  132 static void main_usage_long(FILE* o, char* argv0);
  133 static void main_usage_short(FILE* o, char* argv0);
  134 static void main_usage_synopsis(FILE* o, char* argv0);
  135 static void main_usage_examples(FILE* o, char* argv0, char* leader);
  136 static void list_all_verbs_raw(FILE* o);
  137 static void list_all_verbs(FILE* o, char* leader);
  138 static void main_usage_help_options(FILE* o, char* argv0);
  139 static void main_usage_mlrrc(FILE* o, char* argv0);
  140 static void main_usage_functions(FILE* o, char* argv0, char* leader);
  141 static void main_usage_data_format_examples(FILE* o, char* argv0);
  142 static void main_usage_data_format_options(FILE* o, char* argv0);
  143 static void main_usage_comments_in_data(FILE* o, char* argv0);
  144 static void main_usage_format_conversion_keystroke_saver_options(FILE* o, char* argv0);
  145 static void main_usage_compressed_data_options(FILE* o, char* argv0);
  146 static void main_usage_separator_options(FILE* o, char* argv0);
  147 static void main_usage_csv_options(FILE* o, char* argv0);
  148 static void main_usage_double_quoting(FILE* o, char* argv0);
  149 static void main_usage_numerical_formatting(FILE* o, char* argv0);
  150 static void main_usage_other_options(FILE* o, char* argv0);
  151 static void main_usage_then_chaining(FILE* o, char* argv0);
  152 static void main_usage_auxents(FILE* o, char* argv0);
  153 static void main_usage_see_also(FILE* o, char* argv0);
  154 static void print_type_arithmetic_info(FILE* o, char* argv0);
  155 static void usage_all_verbs(char* argv0);
  156 static void usage_unrecognized_verb(char* argv0, char* arg);
  157 
  158 static void check_arg_count(char** argv, int argi, int argc, int n);
  159 static mapper_setup_t* look_up_mapper_setup(char* verb);
  160 
  161 static int handle_terminal_usage(char** argv, int argc, int argi);
  162 
  163 static char* lhmss_get_or_die(lhmss_t* pmap, char* key);
  164 static int lhmsll_get_or_die(lhmsll_t* pmap, char* key);
  165 
  166 // ----------------------------------------------------------------
  167 cli_opts_t* parse_command_line(int argc, char** argv, sllv_t** ppmapper_list) {
  168     cli_opts_t* popts = mlr_malloc_or_die(sizeof(cli_opts_t));
  169 
  170     int argi = 1;
  171 
  172     // Set defaults for options
  173     cli_opts_init(popts);
  174 
  175     // Try .mlrrc overrides (then command-line on top of that).
  176     // A --norc flag (if provided) must come before all other options.
  177     // Or, they can set the environment variable MLRRC="__none__".
  178     if (argc >= 2 && streq(argv[1], "--norc")) {
  179         argi++;
  180     } else {
  181         cli_load_mlrrc(popts);
  182     }
  183 
  184     for (; argi < argc; /* variable increment: 1 or 2 depending on flag */) {
  185 
  186         if (argv[argi][0] != '-') {
  187             break; // No more flag options to process
  188         } else if (handle_terminal_usage(argv, argc, argi)) {
  189             exit(0);
  190         } else if (cli_handle_reader_options(argv, argc, &argi, &popts->reader_opts)) {
  191             // handled
  192         } else if (cli_handle_writer_options(argv, argc, &argi, &popts->writer_opts)) {
  193             // handled
  194         } else if (cli_handle_reader_writer_options(argv, argc, &argi, &popts->reader_opts, &popts->writer_opts)) {
  195             // handled
  196         } else if (cli_handle_misc_options(argv, argc, &argi, popts)) {
  197             // handled
  198         } else {
  199             // unhandled
  200             usage_unrecognized_verb(MLR_GLOBALS.bargv0, argv[argi]);
  201         }
  202     }
  203 
  204     cli_apply_defaults(popts);
  205 
  206     lhmss_t* default_rses = get_default_rses();
  207     lhmss_t* default_fses = get_default_fses();
  208     lhmss_t* default_pses = get_default_pses();
  209     lhmsll_t* default_repeat_ifses = get_default_repeat_ifses();
  210     lhmsll_t* default_repeat_ipses = get_default_repeat_ipses();
  211 
  212     if (popts->reader_opts.irs == NULL)
  213         popts->reader_opts.irs = lhmss_get_or_die(default_rses, popts->reader_opts.ifile_fmt);
  214     if (popts->reader_opts.ifs == NULL)
  215         popts->reader_opts.ifs = lhmss_get_or_die(default_fses, popts->reader_opts.ifile_fmt);
  216     if (popts->reader_opts.ips == NULL)
  217         popts->reader_opts.ips = lhmss_get_or_die(default_pses, popts->reader_opts.ifile_fmt);
  218 
  219     if (popts->reader_opts.allow_repeat_ifs == NEITHER_TRUE_NOR_FALSE)
  220         popts->reader_opts.allow_repeat_ifs = lhmsll_get_or_die(default_repeat_ifses, popts->reader_opts.ifile_fmt);
  221     if (popts->reader_opts.allow_repeat_ips == NEITHER_TRUE_NOR_FALSE)
  222         popts->reader_opts.allow_repeat_ips = lhmsll_get_or_die(default_repeat_ipses, popts->reader_opts.ifile_fmt);
  223 
  224     if (popts->writer_opts.ors == NULL)
  225         popts->writer_opts.ors = lhmss_get_or_die(default_rses, popts->writer_opts.ofile_fmt);
  226     if (popts->writer_opts.ofs == NULL)
  227         popts->writer_opts.ofs = lhmss_get_or_die(default_fses, popts->writer_opts.ofile_fmt);
  228     if (popts->writer_opts.ops == NULL)
  229         popts->writer_opts.ops = lhmss_get_or_die(default_pses, popts->writer_opts.ofile_fmt);
  230 
  231     if (streq(popts->writer_opts.ofile_fmt, "pprint") && strlen(popts->writer_opts.ofs) != 1) {
  232         fprintf(stderr, "%s: OFS for PPRINT format must be single-character; got \"%s\".\n",
  233             MLR_GLOBALS.bargv0, popts->writer_opts.ofs);
  234         return NULL;
  235     }
  236 
  237     // Construct the mapper list for single use, e.g. the normal streaming case wherein the
  238     // mappers operate on all input files. Also retain information needed to construct them
  239     // for each input file, for in-place mode.
  240     popts->mapper_argb = argi;
  241     popts->original_argv = argv;
  242     popts->non_in_place_argv = copy_argv(argv);
  243     popts->argc = argc;
  244     *ppmapper_list = cli_parse_mappers(popts->non_in_place_argv, &argi, argc, popts);
  245 
  246     for ( ; argi < argc; argi++) {
  247         slls_append(popts->filenames, argv[argi], NO_FREE);
  248     }
  249 
  250     if (popts->no_input) {
  251         slls_free(popts->filenames);
  252         popts->filenames = NULL;
  253     }
  254 
  255     if (popts->do_in_place && (popts->filenames == NULL || popts->filenames->length == 0)) {
  256         fprintf(stderr, "%s: -I option (in-place operation) requires input files.\n", MLR_GLOBALS.bargv0);
  257         exit(1);
  258     }
  259 
  260     if (popts->have_rand_seed) {
  261         mtrand_init(popts->rand_seed);
  262     } else {
  263         mtrand_init_default();
  264     }
  265 
  266     return popts;
  267 }
  268 
  269 // ----------------------------------------------------------------
  270 // Returns a list of mappers, from the starting point in argv given by *pargi. Bumps *pargi to
  271 // point to remaining post-mapper-setup args, i.e. filenames.
  272 sllv_t* cli_parse_mappers(char** argv, int* pargi, int argc, cli_opts_t* popts) {
  273     sllv_t* pmapper_list = sllv_alloc();
  274     int argi = *pargi;
  275 
  276     // Allow then-chains to start with an initial 'then': 'mlr verb1 then verb2 then verb3' or
  277     // 'mlr then verb1 then verb2 then verb3'. Particuarly useful in backslashy scripting contexts.
  278     if ((argc - argi) >= 1 && streq(argv[argi], "then")) {
  279         argi++;
  280     }
  281 
  282     if ((argc - argi) < 1) {
  283         fprintf(stderr, "%s: no verb supplied.\n", MLR_GLOBALS.bargv0);
  284         main_usage_short(stderr, MLR_GLOBALS.bargv0);
  285         exit(1);
  286     }
  287 
  288     // Note that the command-line parsers can operate destructively on argv, e.g. verbs
  289     // which take comma-delimited field names splitting on commas.  For this reason we
  290     // need to duplicate argv on each in-place run within the streamer module. But before
  291     // that ever happens, here we run through the verb-parsers once to find out where it
  292     // is on the command line that the verbs and their arguments end and the filenames
  293     // begin.
  294 
  295     while (TRUE) {
  296         check_arg_count(argv, argi, argc, 1);
  297         char* verb = argv[argi];
  298 
  299         mapper_setup_t* pmapper_setup = look_up_mapper_setup(verb);
  300         if (pmapper_setup == NULL) {
  301             fprintf(stderr, "%s: verb \"%s\" not found. Please use \"%s --help\" for a list.\n",
  302                 MLR_GLOBALS.bargv0, verb, MLR_GLOBALS.bargv0);
  303             exit(1);
  304         }
  305 
  306         if ((argc - argi) >= 2) {
  307             if (streq(argv[argi+1], "-h") || streq(argv[argi+1], "--help")) {
  308                 pmapper_setup->pusage_func(stdout, MLR_GLOBALS.bargv0, verb);
  309                 exit(0);
  310             }
  311         }
  312 
  313         // It's up to the parse func to print its usage on CLI-parse failure.
  314         // Also note: this assumes main reader/writer opts are all parsed
  315         // *before* mapper parse-CLI methods are invoked.
  316         mapper_t* pmapper = pmapper_setup->pparse_func(&argi, argc, argv,
  317             &popts->reader_opts, &popts->writer_opts);
  318         if (pmapper == NULL) {
  319             exit(1);
  320         }
  321 
  322         if (pmapper_setup->ignores_input && pmapper_list->length == 0) {
  323             // e.g. then-chain starts with seqgen
  324             popts->no_input = TRUE;
  325         }
  326 
  327         sllv_append(pmapper_list, pmapper);
  328 
  329         if (argi >= argc || !streq(argv[argi], "then"))
  330             break;
  331         argi++;
  332     }
  333 
  334     *pargi = argi;
  335     return pmapper_list;
  336 }
  337 
  338 // ----------------------------------------------------------------
  339 void cli_opts_free(cli_opts_t* popts) {
  340     if (popts == NULL)
  341         return;
  342 
  343     slls_free(popts->filenames);
  344     free_argv_copy(popts->non_in_place_argv);
  345     free(popts);
  346     free_opt_singletons();
  347 }
  348 
  349 // ----------------------------------------------------------------
  350 static lhmss_t* singleton_pdesc_to_chars_map = NULL;
  351 static lhmss_t* get_desc_to_chars_map() {
  352     if (singleton_pdesc_to_chars_map == NULL) {
  353         singleton_pdesc_to_chars_map = lhmss_alloc();
  354         lhmss_put(singleton_pdesc_to_chars_map, "cr",        "\r",       NO_FREE);
  355         lhmss_put(singleton_pdesc_to_chars_map, "crcr",      "\r\r",     NO_FREE);
  356         lhmss_put(singleton_pdesc_to_chars_map, "newline",   "\n",       NO_FREE);
  357         lhmss_put(singleton_pdesc_to_chars_map, "lf",        "\n",       NO_FREE);
  358         lhmss_put(singleton_pdesc_to_chars_map, "lflf",      "\n\n",     NO_FREE);
  359         lhmss_put(singleton_pdesc_to_chars_map, "crlf",      "\r\n",     NO_FREE);
  360         lhmss_put(singleton_pdesc_to_chars_map, "crlfcrlf",  "\r\n\r\n", NO_FREE);
  361         lhmss_put(singleton_pdesc_to_chars_map, "tab",       "\t",       NO_FREE);
  362         lhmss_put(singleton_pdesc_to_chars_map, "space",     " ",        NO_FREE);
  363         lhmss_put(singleton_pdesc_to_chars_map, "comma",     ",",        NO_FREE);
  364         lhmss_put(singleton_pdesc_to_chars_map, "newline",   "\n",       NO_FREE);
  365         lhmss_put(singleton_pdesc_to_chars_map, "pipe",      "|",        NO_FREE);
  366         lhmss_put(singleton_pdesc_to_chars_map, "slash",     "/",        NO_FREE);
  367         lhmss_put(singleton_pdesc_to_chars_map, "colon",     ":",        NO_FREE);
  368         lhmss_put(singleton_pdesc_to_chars_map, "semicolon", ";",        NO_FREE);
  369         lhmss_put(singleton_pdesc_to_chars_map, "equals",    "=",        NO_FREE);
  370     }
  371     return singleton_pdesc_to_chars_map;
  372 }
  373 // Always strdup so the caller can unconditionally free our return value
  374 char* cli_sep_from_arg(char* arg) {
  375     char* chars = lhmss_get(get_desc_to_chars_map(), arg);
  376     if (chars != NULL) // E.g. crlf
  377         return mlr_strdup_or_die(chars);
  378     else // E.g. '\r\n'
  379         return mlr_alloc_unbackslash(arg);
  380 }
  381 
  382 // ----------------------------------------------------------------
  383 static lhmss_t* singleton_default_rses = NULL;
  384 static lhmss_t* singleton_default_fses = NULL;
  385 static lhmss_t* singleton_default_pses = NULL;
  386 static lhmsll_t* singleton_default_repeat_ifses = NULL;
  387 static lhmsll_t* singleton_default_repeat_ipses = NULL;
  388 
  389 static lhmss_t* get_default_rses() {
  390     if (singleton_default_rses == NULL) {
  391         singleton_default_rses = lhmss_alloc();
  392 
  393         lhmss_put(singleton_default_rses, "gen",      "N/A",  NO_FREE);
  394         lhmss_put(singleton_default_rses, "dkvp",     "auto",  NO_FREE);
  395         lhmss_put(singleton_default_rses, "json",     "auto",  NO_FREE);
  396         lhmss_put(singleton_default_rses, "nidx",     "auto",  NO_FREE);
  397         lhmss_put(singleton_default_rses, "csv",      "auto",  NO_FREE);
  398         lhmss_put(singleton_default_rses, "csvlite",  "auto",  NO_FREE);
  399         lhmss_put(singleton_default_rses, "markdown", "auto",  NO_FREE);
  400         lhmss_put(singleton_default_rses, "pprint",   "auto",  NO_FREE);
  401         lhmss_put(singleton_default_rses, "xtab",     "(N/A)", NO_FREE);
  402     }
  403     return singleton_default_rses;
  404 }
  405 
  406 static lhmss_t* get_default_fses() {
  407     if (singleton_default_fses == NULL) {
  408         singleton_default_fses = lhmss_alloc();
  409         lhmss_put(singleton_default_fses, "gen",      "(N/A)",  NO_FREE);
  410         lhmss_put(singleton_default_fses, "dkvp",     ",",      NO_FREE);
  411         lhmss_put(singleton_default_fses, "json",     "(N/A)",  NO_FREE);
  412         lhmss_put(singleton_default_fses, "nidx",     " ",      NO_FREE);
  413         lhmss_put(singleton_default_fses, "csv",      ",",      NO_FREE);
  414         lhmss_put(singleton_default_fses, "csvlite",  ",",      NO_FREE);
  415         lhmss_put(singleton_default_fses, "markdown", "(N/A)",  NO_FREE);
  416         lhmss_put(singleton_default_fses, "pprint",   " ",      NO_FREE);
  417         lhmss_put(singleton_default_fses, "xtab",     "auto",   NO_FREE);
  418     }
  419     return singleton_default_fses;
  420 }
  421 
  422 static lhmss_t* get_default_pses() {
  423     if (singleton_default_pses == NULL) {
  424         singleton_default_pses = lhmss_alloc();
  425         lhmss_put(singleton_default_pses, "gen",      "(N/A)", NO_FREE);
  426         lhmss_put(singleton_default_pses, "dkvp",     "=",     NO_FREE);
  427         lhmss_put(singleton_default_pses, "json",     "(N/A)", NO_FREE);
  428         lhmss_put(singleton_default_pses, "nidx",     "(N/A)", NO_FREE);
  429         lhmss_put(singleton_default_pses, "csv",      "(N/A)", NO_FREE);
  430         lhmss_put(singleton_default_pses, "csvlite",  "(N/A)", NO_FREE);
  431         lhmss_put(singleton_default_pses, "markdown", "(N/A)", NO_FREE);
  432         lhmss_put(singleton_default_pses, "pprint",   "(N/A)", NO_FREE);
  433         lhmss_put(singleton_default_pses, "xtab",     " ",     NO_FREE);
  434     }
  435     return singleton_default_pses;
  436 }
  437 
  438 static lhmsll_t* get_default_repeat_ifses() {
  439     if (singleton_default_repeat_ifses == NULL) {
  440         singleton_default_repeat_ifses = lhmsll_alloc();
  441         lhmsll_put(singleton_default_repeat_ifses, "gen",      FALSE, NO_FREE);
  442         lhmsll_put(singleton_default_repeat_ifses, "dkvp",     FALSE, NO_FREE);
  443         lhmsll_put(singleton_default_repeat_ifses, "json",     FALSE, NO_FREE);
  444         lhmsll_put(singleton_default_repeat_ifses, "csv",      FALSE, NO_FREE);
  445         lhmsll_put(singleton_default_repeat_ifses, "csvlite",  FALSE, NO_FREE);
  446         lhmsll_put(singleton_default_repeat_ifses, "markdown", FALSE, NO_FREE);
  447         lhmsll_put(singleton_default_repeat_ifses, "nidx",     FALSE, NO_FREE);
  448         lhmsll_put(singleton_default_repeat_ifses, "xtab",     FALSE, NO_FREE);
  449         lhmsll_put(singleton_default_repeat_ifses, "pprint",   TRUE,  NO_FREE);
  450     }
  451     return singleton_default_repeat_ifses;
  452 }
  453 
  454 static lhmsll_t* get_default_repeat_ipses() {
  455     if (singleton_default_repeat_ipses == NULL) {
  456         singleton_default_repeat_ipses = lhmsll_alloc();
  457         lhmsll_put(singleton_default_repeat_ipses, "gen",      FALSE, NO_FREE);
  458         lhmsll_put(singleton_default_repeat_ipses, "dkvp",     FALSE, NO_FREE);
  459         lhmsll_put(singleton_default_repeat_ipses, "json",     FALSE, NO_FREE);
  460         lhmsll_put(singleton_default_repeat_ipses, "csv",      FALSE, NO_FREE);
  461         lhmsll_put(singleton_default_repeat_ipses, "csvlite",  FALSE, NO_FREE);
  462         lhmsll_put(singleton_default_repeat_ipses, "markdown", FALSE, NO_FREE);
  463         lhmsll_put(singleton_default_repeat_ipses, "nidx",     FALSE, NO_FREE);
  464         lhmsll_put(singleton_default_repeat_ipses, "xtab",     TRUE,  NO_FREE);
  465         lhmsll_put(singleton_default_repeat_ipses, "pprint",   FALSE, NO_FREE);
  466     }
  467     return singleton_default_repeat_ipses;
  468 }
  469 
  470 static void free_opt_singletons() {
  471     lhmss_free(singleton_pdesc_to_chars_map);
  472     lhmss_free(singleton_default_rses);
  473     lhmss_free(singleton_default_fses);
  474     lhmss_free(singleton_default_pses);
  475     lhmsll_free(singleton_default_repeat_ifses);
  476     lhmsll_free(singleton_default_repeat_ipses);
  477 }
  478 
  479 // For displaying the default separators in on-line help
  480 static char* rebackslash(char* sep) {
  481     if (streq(sep, "\r"))
  482         return "\\r";
  483     else if (streq(sep, "\n"))
  484         return "\\n";
  485     else if (streq(sep, "\r\n"))
  486         return "\\r\\n";
  487     else if (streq(sep, "\t"))
  488         return "\\t";
  489     else if (streq(sep, " "))
  490         return "space";
  491     else
  492         return sep;
  493 }
  494 
  495 // ----------------------------------------------------------------
  496 static void main_usage_short(FILE* fp, char* argv0) {
  497     fprintf(stderr, "Please run \"%s --help\" for detailed usage information.\n", argv0);
  498     exit(1);
  499 }
  500 
  501 // ----------------------------------------------------------------
  502 // The main_usage_long() function is split out into subroutines in support of the
  503 // manpage autogenerator.
  504 
  505 static void main_usage_long(FILE* o, char* argv0) {
  506     main_usage_synopsis(o, argv0);
  507     fprintf(o, "\n");
  508 
  509     fprintf(o, "Command-line-syntax examples:\n");
  510     main_usage_examples(o, argv0, "  ");
  511     fprintf(o, "\n");
  512 
  513     fprintf(o, "Data-format examples:\n");
  514     main_usage_data_format_examples(o, argv0);
  515     fprintf(o, "\n");
  516 
  517     fprintf(o, "Help options:\n");
  518     main_usage_help_options(o, argv0);
  519     fprintf(o, "\n");
  520 
  521     fprintf(o, "Customization via .mlrrc:\n");
  522     main_usage_mlrrc(o, argv0);
  523     fprintf(o, "\n");
  524 
  525     fprintf(o, "Verbs:\n");
  526     list_all_verbs(o, "  ");
  527     fprintf(o, "\n");
  528 
  529     fprintf(o, "Functions for the filter and put verbs:\n");
  530     main_usage_functions(o, argv0, "  ");
  531     fprintf(o, "\n");
  532 
  533     fprintf(o, "Data-format options, for input, output, or both:\n");
  534     main_usage_data_format_options(o, argv0);
  535     fprintf(o, "\n");
  536 
  537     fprintf(o, "Comments in data:\n");
  538     main_usage_comments_in_data(o, argv0);
  539     fprintf(o, "\n");
  540 
  541     fprintf(o, "Format-conversion keystroke-saver options, for input, output, or both:\n");
  542     main_usage_format_conversion_keystroke_saver_options(o, argv0);
  543     fprintf(o, "\n");
  544 
  545     fprintf(o, "Compressed-data options:\n");
  546     main_usage_compressed_data_options(o, argv0);
  547     fprintf(o, "\n");
  548 
  549     fprintf(o, "Separator options, for input, output, or both:\n");
  550     main_usage_separator_options(o, argv0);
  551     fprintf(o, "\n");
  552 
  553     fprintf(o, "Relevant to CSV/CSV-lite input only:\n");
  554     main_usage_csv_options(o, argv0);
  555     fprintf(o, "\n");
  556 
  557     fprintf(o, "Double-quoting for CSV output:\n");
  558     main_usage_double_quoting(o, argv0);
  559     fprintf(o, "\n");
  560 
  561     fprintf(o, "Numerical formatting:\n");
  562     main_usage_numerical_formatting(o, argv0);
  563     fprintf(o, "\n");
  564 
  565     fprintf(o, "Other options:\n");
  566     main_usage_other_options(o, argv0);
  567     fprintf(o, "\n");
  568 
  569     fprintf(o, "Then-chaining:\n");
  570     main_usage_then_chaining(o, argv0);
  571     fprintf(o, "\n");
  572 
  573     fprintf(o, "Auxiliary commands:\n");
  574     main_usage_auxents(o, argv0);
  575     fprintf(o, "\n");
  576 
  577     main_usage_see_also(o, argv0);
  578 }
  579 
  580 static void main_usage_synopsis(FILE* o, char* argv0) {
  581     fprintf(o, "Usage: %s [I/O options] {verb} [verb-dependent options ...] {zero or more file names}\n", argv0);
  582 }
  583 
  584 static void main_usage_examples(FILE* o, char* argv0, char* leader) {
  585 
  586     fprintf(o, "%s%s --csv cut -f hostname,uptime mydata.csv\n", leader, argv0);
  587     fprintf(o, "%s%s --tsv --rs lf filter '$status != \"down\" && $upsec >= 10000' *.tsv\n", leader, argv0);
  588     fprintf(o, "%s%s --nidx put '$sum = $7 < 0.0 ? 3.5 : $7 + 2.1*$8' *.dat\n", leader, argv0);
  589     fprintf(o, "%sgrep -v '^#' /etc/group | %s --ifs : --nidx --opprint label group,pass,gid,member then sort -f group\n", leader, argv0);
  590     fprintf(o, "%s%s join -j account_id -f accounts.dat then group-by account_name balances.dat\n", leader, argv0);
  591     fprintf(o, "%s%s --json put '$attr = sub($attr, \"([0-9]+)_([0-9]+)_.*\", \"\\1:\\2\")' data/*.json\n", leader, argv0);
  592     fprintf(o, "%s%s stats1 -a min,mean,max,p10,p50,p90 -f flag,u,v data/*\n", leader, argv0);
  593     fprintf(o, "%s%s stats2 -a linreg-pca -f u,v -g shape data/*\n", leader, argv0);
  594     fprintf(o, "%s%s put -q '@sum[$a][$b] += $x; end {emit @sum, \"a\", \"b\"}' data/*\n", leader, argv0);
  595     fprintf(o, "%s%s --from estimates.tbl put '\n", leader, argv0);
  596     fprintf(o, "  for (k,v in $*) {\n");
  597     fprintf(o, "    if (is_numeric(v) && k =~ \"^[t-z].*$\") {\n");
  598     fprintf(o, "      $sum += v; $count += 1\n");
  599     fprintf(o, "    }\n");
  600     fprintf(o, "  }\n");
  601     fprintf(o, "  $mean = $sum / $count # no assignment if count unset'\n");
  602     fprintf(o, "%s%s --from infile.dat put -f analyze.mlr\n", leader, argv0);
  603     fprintf(o, "%s%s --from infile.dat put 'tee > \"./taps/data-\".$a.\"-\".$b, $*'\n", leader, argv0);
  604     fprintf(o, "%s%s --from infile.dat put 'tee | \"gzip > ./taps/data-\".$a.\"-\".$b.\".gz\", $*'\n", leader, argv0);
  605     fprintf(o, "%s%s --from infile.dat put -q '@v=$*; dump | \"jq .[]\"'\n", leader, argv0);
  606     fprintf(o, "%s%s --from infile.dat put  '(NR %% 1000 == 0) { print > stderr, \"Checkpoint \".NR}'\n",
  607         leader, argv0);
  608 }
  609 
  610 static void list_all_verbs_raw(FILE* o) {
  611     for (int i = 0; i < mapper_lookup_table_length; i++) {
  612         fprintf(o, "%s\n", mapper_lookup_table[i]->verb);
  613     }
  614 }
  615 
  616 static void list_all_verbs(FILE* o, char* leader) {
  617     char* separator = " ";
  618     int leaderlen = strlen(leader);
  619     int separatorlen = strlen(separator);
  620     int linelen = leaderlen;
  621     int j = 0;
  622     for (int i = 0; i < mapper_lookup_table_length; i++) {
  623         char* verb = mapper_lookup_table[i]->verb;
  624         int verblen = strlen(verb);
  625         linelen += separatorlen + verblen;
  626         if (linelen >= 80) {
  627             fprintf(o, "\n");
  628             linelen = leaderlen + separatorlen + verblen;
  629             j = 0;
  630         }
  631         if (j == 0)
  632             fprintf(o, "%s", leader);
  633         fprintf(o, "%s%s", separator, verb);
  634         j++;
  635     }
  636     fprintf(o, "\n");
  637 }
  638 
  639 static void main_usage_help_options(FILE* o, char* argv0) {
  640     fprintf(o, "  -h or --help                 Show this message.\n");
  641     fprintf(o, "  --version                    Show the software version.\n");
  642     fprintf(o, "  {verb name} --help           Show verb-specific help.\n");
  643     fprintf(o, "  --help-all-verbs             Show help on all verbs.\n");
  644     fprintf(o, "  -l or --list-all-verbs       List only verb names.\n");
  645     fprintf(o, "  -L                           List only verb names, one per line.\n");
  646     fprintf(o, "  -f or --help-all-functions   Show help on all built-in functions.\n");
  647     fprintf(o, "  -F                           Show a bare listing of built-in functions by name.\n");
  648     fprintf(o, "  -k or --help-all-keywords    Show help on all keywords.\n");
  649     fprintf(o, "  -K                           Show a bare listing of keywords by name.\n");
  650 }
  651 
  652 static void main_usage_mlrrc(FILE* o, char* argv0) {
  653     fprintf(o, "You can set up personal defaults via a $HOME/.mlrrc and/or ./.mlrrc.\n");
  654     fprintf(o, "For example, if you usually process CSV, then you can put \"--csv\" in your .mlrrc file\n");
  655     fprintf(o, "and that will be the default input/output format unless otherwise specified on the command line.\n");
  656     fprintf(o, "\n");
  657     fprintf(o, "The .mlrrc file format is one \"--flag\" or \"--option value\" per line, with the leading \"--\" optional.\n");
  658     fprintf(o, "Hash-style comments and blank lines are ignored.\n");
  659     fprintf(o, "\n");
  660     fprintf(o, "Sample .mlrrc:\n");
  661     fprintf(o, "# Input and output formats are CSV by default (unless otherwise specified\n");
  662     fprintf(o, "# on the mlr command line):\n");
  663     fprintf(o, "csv\n");
  664     fprintf(o, "# These are no-ops for CSV, but when I do use JSON output, I want these\n");
  665     fprintf(o, "# pretty-printing options to be used:\n");
  666     fprintf(o, "jvstack\n");
  667     fprintf(o, "jlistwrap\n");
  668     fprintf(o, "\n");
  669     fprintf(o, "How to specify location of .mlrrc:\n");
  670     fprintf(o, "* If $MLRRC is set:\n");
  671     fprintf(o, "  o If its value is \"__none__\" then no .mlrrc files are processed.\n");
  672     fprintf(o, "  o Otherwise, its value (as a filename) is loaded and processed. If there are syntax\n");
  673     fprintf(o, "    errors, they abort mlr with a usage message (as if you had mistyped something on the\n");
  674     fprintf(o, "    command line). If the file can't be loaded at all, though, it is silently skipped.\n");
  675     fprintf(o, "  o Any .mlrrc in your home directory or current directory is ignored whenever $MLRRC is\n");
  676     fprintf(o, "    set in the environment.\n");
  677     fprintf(o, "* Otherwise:\n");
  678     fprintf(o, "  o If $HOME/.mlrrc exists, it's then processed as above.\n");
  679     fprintf(o, "  o If ./.mlrrc exists, it's then also processed as above.\n");
  680     fprintf(o, "  (I.e. current-directory .mlrrc defaults are stacked over home-directory .mlrrc defaults.)\n");
  681     fprintf(o, "\n");
  682     fprintf(o, "See also:\n");
  683     fprintf(o, "https://johnkerl.org/miller/doc/customization.html\n");
  684 }
  685 
  686 static void main_usage_functions(FILE* o, char* argv0, char* leader) {
  687     fmgr_t* pfmgr = fmgr_alloc();
  688     fmgr_list_functions(pfmgr, o, leader);
  689     fmgr_free(pfmgr, NULL);
  690     fprintf(o, "\n");
  691     fprintf(o, "Please use \"%s --help-function {function name}\" for function-specific help.\n", argv0);
  692 }
  693 
  694 static void main_usage_data_format_examples(FILE* o, char* argv0) {
  695     fprintf(o,
  696         "  DKVP: delimited key-value pairs (Miller default format)\n"
  697         "  +---------------------+\n"
  698         "  | apple=1,bat=2,cog=3 | Record 1: \"apple\" => \"1\", \"bat\" => \"2\", \"cog\" => \"3\"\n"
  699         "  | dish=7,egg=8,flint  | Record 2: \"dish\" => \"7\", \"egg\" => \"8\", \"3\" => \"flint\"\n"
  700         "  +---------------------+\n"
  701         "\n"
  702         "  NIDX: implicitly numerically indexed (Unix-toolkit style)\n"
  703         "  +---------------------+\n"
  704         "  | the quick brown     | Record 1: \"1\" => \"the\", \"2\" => \"quick\", \"3\" => \"brown\"\n"
  705         "  | fox jumped          | Record 2: \"1\" => \"fox\", \"2\" => \"jumped\"\n"
  706         "  +---------------------+\n"
  707         "\n"
  708         "  CSV/CSV-lite: comma-separated values with separate header line\n"
  709         "  +---------------------+\n"
  710         "  | apple,bat,cog       |\n"
  711         "  | 1,2,3               | Record 1: \"apple => \"1\", \"bat\" => \"2\", \"cog\" => \"3\"\n"
  712         "  | 4,5,6               | Record 2: \"apple\" => \"4\", \"bat\" => \"5\", \"cog\" => \"6\"\n"
  713         "  +---------------------+\n"
  714         "\n"
  715         "  Tabular JSON: nested objects are supported, although arrays within them are not:\n"
  716         "  +---------------------+\n"
  717         "  | {                   |\n"
  718         "  |  \"apple\": 1,        | Record 1: \"apple\" => \"1\", \"bat\" => \"2\", \"cog\" => \"3\"\n"
  719         "  |  \"bat\": 2,          |\n"
  720         "  |  \"cog\": 3           |\n"
  721         "  | }                   |\n"
  722         "  | {                   |\n"
  723         "  |   \"dish\": {         | Record 2: \"dish:egg\" => \"7\", \"dish:flint\" => \"8\", \"garlic\" => \"\"\n"
  724         "  |     \"egg\": 7,       |\n"
  725         "  |     \"flint\": 8      |\n"
  726         "  |   },                |\n"
  727         "  |   \"garlic\": \"\"      |\n"
  728         "  | }                   |\n"
  729         "  +---------------------+\n"
  730         "\n"
  731         "  PPRINT: pretty-printed tabular\n"
  732         "  +---------------------+\n"
  733         "  | apple bat cog       |\n"
  734         "  | 1     2   3         | Record 1: \"apple => \"1\", \"bat\" => \"2\", \"cog\" => \"3\"\n"
  735         "  | 4     5   6         | Record 2: \"apple\" => \"4\", \"bat\" => \"5\", \"cog\" => \"6\"\n"
  736         "  +---------------------+\n"
  737         "\n"
  738         "  XTAB: pretty-printed transposed tabular\n"
  739         "  +---------------------+\n"
  740         "  | apple 1             | Record 1: \"apple\" => \"1\", \"bat\" => \"2\", \"cog\" => \"3\"\n"
  741         "  | bat   2             |\n"
  742         "  | cog   3             |\n"
  743         "  |                     |\n"
  744         "  | dish 7              | Record 2: \"dish\" => \"7\", \"egg\" => \"8\"\n"
  745         "  | egg  8              |\n"
  746         "  +---------------------+\n"
  747         "\n"
  748         "  Markdown tabular (supported for output only):\n"
  749         "  +-----------------------+\n"
  750         "  | | apple | bat | cog | |\n"
  751         "  | | ---   | --- | --- | |\n"
  752         "  | | 1     | 2   | 3   | | Record 1: \"apple => \"1\", \"bat\" => \"2\", \"cog\" => \"3\"\n"
  753         "  | | 4     | 5   | 6   | | Record 2: \"apple\" => \"4\", \"bat\" => \"5\", \"cog\" => \"6\"\n"
  754         "  +-----------------------+\n");
  755 }
  756 
  757 static void main_usage_data_format_options(FILE* o, char* argv0) {
  758     fprintf(o, "  --idkvp   --odkvp   --dkvp      Delimited key-value pairs, e.g \"a=1,b=2\"\n");
  759     fprintf(o, "                                  (this is Miller's default format).\n");
  760     fprintf(o, "\n");
  761     fprintf(o, "  --inidx   --onidx   --nidx      Implicitly-integer-indexed fields\n");
  762     fprintf(o, "                                  (Unix-toolkit style).\n");
  763     fprintf(o, "  -T                              Synonymous with \"--nidx --fs tab\".\n");
  764     fprintf(o, "\n");
  765     fprintf(o, "  --icsv    --ocsv    --csv       Comma-separated value (or tab-separated\n");
  766     fprintf(o, "                                  with --fs tab, etc.)\n");
  767     fprintf(o, "\n");
  768     fprintf(o, "  --itsv    --otsv    --tsv       Keystroke-savers for \"--icsv --ifs tab\",\n");
  769     fprintf(o, "                                  \"--ocsv --ofs tab\", \"--csv --fs tab\".\n");
  770     fprintf(o, "  --iasv    --oasv    --asv       Similar but using ASCII FS %s and RS %s\n",
  771         ASV_FS_FOR_HELP, ASV_RS_FOR_HELP);
  772     fprintf(o, "  --iusv    --ousv    --usv       Similar but using Unicode FS %s\n",
  773         USV_FS_FOR_HELP);
  774     fprintf(o, "                                  and RS %s\n",
  775         USV_RS_FOR_HELP);
  776     fprintf(o, "\n");
  777     fprintf(o, "  --icsvlite --ocsvlite --csvlite Comma-separated value (or tab-separated\n");
  778     fprintf(o, "                                  with --fs tab, etc.). The 'lite' CSV does not handle\n");
  779     fprintf(o, "                                  RFC-CSV double-quoting rules; is slightly faster;\n");
  780     fprintf(o, "                                  and handles heterogeneity in the input stream via\n");
  781     fprintf(o, "                                  empty newline followed by new header line. See also\n");
  782     fprintf(o, "                                  http://johnkerl.org/miller/doc/file-formats.html#CSV/TSV/etc.\n");
  783     fprintf(o, "\n");
  784     fprintf(o, "  --itsvlite --otsvlite --tsvlite Keystroke-savers for \"--icsvlite --ifs tab\",\n");
  785     fprintf(o, "                                  \"--ocsvlite --ofs tab\", \"--csvlite --fs tab\".\n");
  786     fprintf(o, "  -t                              Synonymous with --tsvlite.\n");
  787     fprintf(o, "  --iasvlite --oasvlite --asvlite Similar to --itsvlite et al. but using ASCII FS %s and RS %s\n",
  788         ASV_FS_FOR_HELP, ASV_RS_FOR_HELP);
  789     fprintf(o, "  --iusvlite --ousvlite --usvlite Similar to --itsvlite et al. but using Unicode FS %s\n",
  790         USV_FS_FOR_HELP);
  791     fprintf(o, "                                  and RS %s\n",
  792         USV_RS_FOR_HELP);
  793     fprintf(o, "\n");
  794     fprintf(o, "  --ipprint --opprint --pprint    Pretty-printed tabular (produces no\n");
  795     fprintf(o, "                                  output until all input is in).\n");
  796     fprintf(o, "                      --right     Right-justifies all fields for PPRINT output.\n");
  797     fprintf(o, "                      --barred    Prints a border around PPRINT output\n");
  798     fprintf(o, "                                  (only available for output).\n");
  799     fprintf(o, "\n");
  800     fprintf(o, "            --omd                 Markdown-tabular (only available for output).\n");
  801     fprintf(o, "\n");
  802     fprintf(o, "  --ixtab   --oxtab   --xtab      Pretty-printed vertical-tabular.\n");
  803     fprintf(o, "                      --xvright   Right-justifies values for XTAB format.\n");
  804     fprintf(o, "\n");
  805     fprintf(o, "  --ijson   --ojson   --json      JSON tabular: sequence or list of one-level\n");
  806     fprintf(o, "                                  maps: {...}{...} or [{...},{...}].\n");
  807     fprintf(o, "    --json-map-arrays-on-input    JSON arrays are unmillerable. --json-map-arrays-on-input\n");
  808     fprintf(o, "    --json-skip-arrays-on-input   is the default: arrays are converted to integer-indexed\n");
  809     fprintf(o, "    --json-fatal-arrays-on-input  maps. The other two options cause them to be skipped, or\n");
  810     fprintf(o, "                                  to be treated as errors.  Please use the jq tool for full\n");
  811     fprintf(o, "                                  JSON (pre)processing.\n");
  812     fprintf(o, "                      --jvstack   Put one key-value pair per line for JSON\n");
  813     fprintf(o, "                                  output.\n");
  814     fprintf(o, "                --jsonx --ojsonx  Keystroke-savers for --json --jvstack\n");
  815     fprintf(o, "                --jsonx --ojsonx  and --ojson --jvstack, respectively.\n");
  816     fprintf(o, "                      --jlistwrap Wrap JSON output in outermost [ ].\n");
  817     fprintf(o, "                    --jknquoteint Do not quote non-string map keys in JSON output.\n");
  818     fprintf(o, "                     --jvquoteall Quote map values in JSON output, even if they're\n");
  819     fprintf(o, "                                  numeric.\n");
  820     fprintf(o, "              --jflatsep {string} Separator for flattening multi-level JSON keys,\n");
  821     fprintf(o, "                                  e.g. '{\"a\":{\"b\":3}}' becomes a:b => 3 for\n");
  822     fprintf(o, "                                  non-JSON formats. Defaults to %s.\n",
  823         DEFAULT_JSON_FLATTEN_SEPARATOR);
  824     fprintf(o, "\n");
  825     fprintf(o, "  -p is a keystroke-saver for --nidx --fs space --repifs\n");
  826     fprintf(o, "\n");
  827     fprintf(o, "  Examples: --csv for CSV-formatted input and output; --idkvp --opprint for\n");
  828     fprintf(o, "  DKVP-formatted input and pretty-printed output.\n");
  829     fprintf(o, "\n");
  830     fprintf(o, "  Please use --iformat1 --oformat2 rather than --format1 --oformat2.\n");
  831     fprintf(o, "  The latter sets up input and output flags for format1, not all of which\n");
  832     fprintf(o, "  are overridden in all cases by setting output format to format2.\n");
  833 }
  834 
  835 static void main_usage_comments_in_data(FILE* o, char* argv0) {
  836     fprintf(o, "  --skip-comments                 Ignore commented lines (prefixed by \"%s\")\n",
  837         DEFAULT_COMMENT_STRING);
  838     fprintf(o, "                                  within the input.\n");
  839     fprintf(o, "  --skip-comments-with {string}   Ignore commented lines within input, with\n");
  840     fprintf(o, "                                  specified prefix.\n");
  841     fprintf(o, "  --pass-comments                 Immediately print commented lines (prefixed by \"%s\")\n",
  842         DEFAULT_COMMENT_STRING);
  843     fprintf(o, "                                  within the input.\n");
  844     fprintf(o, "  --pass-comments-with {string}   Immediately print commented lines within input, with\n");
  845     fprintf(o, "                                  specified prefix.\n");
  846     fprintf(o, "Notes:\n");
  847     fprintf(o, "* Comments are only honored at the start of a line.\n");
  848     fprintf(o, "* In the absence of any of the above four options, comments are data like\n");
  849     fprintf(o, "  any other text.\n");
  850     fprintf(o, "* When pass-comments is used, comment lines are written to standard output\n");
  851     fprintf(o, "  immediately upon being read; they are not part of the record stream.\n");
  852     fprintf(o, "  Results may be counterintuitive. A suggestion is to place comments at the\n");
  853     fprintf(o, "  start of data files.\n");
  854 }
  855 
  856 static void main_usage_format_conversion_keystroke_saver_options(FILE* o, char* argv0) {
  857     fprintf(o, "As keystroke-savers for format-conversion you may use the following:\n");
  858     fprintf(o, "        --c2t --c2d --c2n --c2j --c2x --c2p --c2m\n");
  859     fprintf(o, "  --t2c       --t2d --t2n --t2j --t2x --t2p --t2m\n");
  860     fprintf(o, "  --d2c --d2t       --d2n --d2j --d2x --d2p --d2m\n");
  861     fprintf(o, "  --n2c --n2t --n2d       --n2j --n2x --n2p --n2m\n");
  862     fprintf(o, "  --j2c --j2t --j2d --j2n       --j2x --j2p --j2m\n");
  863     fprintf(o, "  --x2c --x2t --x2d --x2n --x2j       --x2p --x2m\n");
  864     fprintf(o, "  --p2c --p2t --p2d --p2n --p2j --p2x       --p2m\n");
  865     fprintf(o, "The letters c t d n j x p m refer to formats CSV, TSV, DKVP, NIDX, JSON, XTAB,\n");
  866     fprintf(o, "PPRINT, and markdown, respectively. Note that markdown format is available for\n");
  867     fprintf(o, "output only.\n");
  868 }
  869 
  870 static void main_usage_compressed_data_options(FILE* o, char* argv0) {
  871     fprintf(o, "  --prepipe {command} This allows Miller to handle compressed inputs. You can do\n");
  872     fprintf(o, "  without this for single input files, e.g. \"gunzip < myfile.csv.gz | %s ...\".\n",
  873         argv0);
  874     fprintf(o, "\n");
  875     fprintf(o, "  However, when multiple input files are present, between-file separations are\n");
  876     fprintf(o, "  lost; also, the FILENAME variable doesn't iterate. Using --prepipe you can\n");
  877     fprintf(o, "  specify an action to be taken on each input file. This pre-pipe command must\n");
  878     fprintf(o, "  be able to read from standard input; it will be invoked with\n");
  879     fprintf(o, "    {command} < {filename}.\n");
  880     fprintf(o, "  Examples:\n");
  881     fprintf(o, "    %s --prepipe 'gunzip'\n", argv0);
  882     fprintf(o, "    %s --prepipe 'zcat -cf'\n", argv0);
  883     fprintf(o, "    %s --prepipe 'xz -cd'\n", argv0);
  884     fprintf(o, "    %s --prepipe cat\n", argv0);
  885     fprintf(o, "    %s --prepipe-gunzip\n", argv0);
  886     fprintf(o, "    %s --prepipe-zcat\n", argv0);
  887     fprintf(o, "  Note that this feature is quite general and is not limited to decompression\n");
  888     fprintf(o, "  utilities. You can use it to apply per-file filters of your choice.\n");
  889     fprintf(o, "  For output compression (or other) utilities, simply pipe the output:\n");
  890     fprintf(o, "    %s ... | {your compression command}\n", argv0);
  891     fprintf(o, "\n");
  892     fprintf(o, "  There are shorthands --prepipe-zcat and --prepipe-gunzip which are\n");
  893     fprintf(o, "  valid in .mlrrc files. The --prepipe flag is not valid in .mlrrc\n");
  894     fprintf(o, "  files since that would put execution of the prepipe command under \n");
  895     fprintf(o, "  control of the .mlrrc file.\n");
  896 }
  897 
  898 static void main_usage_separator_options(FILE* o, char* argv0) {
  899     fprintf(o, "  --rs     --irs     --ors              Record separators, e.g. 'lf' or '\\r\\n'\n");
  900     fprintf(o, "  --fs     --ifs     --ofs  --repifs    Field separators, e.g. comma\n");
  901     fprintf(o, "  --ps     --ips     --ops              Pair separators, e.g. equals sign\n");
  902     fprintf(o, "\n");
  903     fprintf(o, "  Notes about line endings:\n");
  904     fprintf(o, "  * Default line endings (--irs and --ors) are \"auto\" which means autodetect from\n");
  905     fprintf(o, "    the input file format, as long as the input file(s) have lines ending in either\n");
  906     fprintf(o, "    LF (also known as linefeed, '\\n', 0x0a, Unix-style) or CRLF (also known as\n");
  907     fprintf(o, "    carriage-return/linefeed pairs, '\\r\\n', 0x0d 0x0a, Windows style).\n");
  908     fprintf(o, "  * If both irs and ors are auto (which is the default) then LF input will lead to LF\n");
  909     fprintf(o, "    output and CRLF input will lead to CRLF output, regardless of the platform you're\n");
  910     fprintf(o, "    running on.\n");
  911     fprintf(o, "  * The line-ending autodetector triggers on the first line ending detected in the input\n");
  912     fprintf(o, "    stream. E.g. if you specify a CRLF-terminated file on the command line followed by an\n");
  913     fprintf(o, "    LF-terminated file then autodetected line endings will be CRLF.\n");
  914     fprintf(o, "  * If you use --ors {something else} with (default or explicitly specified) --irs auto\n");
  915     fprintf(o, "    then line endings are autodetected on input and set to what you specify on output.\n");
  916     fprintf(o, "  * If you use --irs {something else} with (default or explicitly specified) --ors auto\n");
  917     fprintf(o, "    then the output line endings used are LF on Unix/Linux/BSD/MacOSX, and CRLF on Windows.\n");
  918     fprintf(o, "\n");
  919     fprintf(o, "  Notes about all other separators:\n");
  920     fprintf(o, "  * IPS/OPS are only used for DKVP and XTAB formats, since only in these formats\n");
  921     fprintf(o, "    do key-value pairs appear juxtaposed.\n");
  922     fprintf(o, "  * IRS/ORS are ignored for XTAB format. Nominally IFS and OFS are newlines;\n");
  923     fprintf(o, "    XTAB records are separated by two or more consecutive IFS/OFS -- i.e.\n");
  924     fprintf(o, "    a blank line. Everything above about --irs/--ors/--rs auto becomes --ifs/--ofs/--fs\n");
  925     fprintf(o, "    auto for XTAB format. (XTAB's default IFS/OFS are \"auto\".)\n");
  926     fprintf(o, "  * OFS must be single-character for PPRINT format. This is because it is used\n");
  927     fprintf(o, "    with repetition for alignment; multi-character separators would make\n");
  928     fprintf(o, "    alignment impossible.\n");
  929     fprintf(o, "  * OPS may be multi-character for XTAB format, in which case alignment is\n");
  930     fprintf(o, "    disabled.\n");
  931     fprintf(o, "  * TSV is simply CSV using tab as field separator (\"--fs tab\").\n");
  932     fprintf(o, "  * FS/PS are ignored for markdown format; RS is used.\n");
  933     fprintf(o, "  * All FS and PS options are ignored for JSON format, since they are not relevant\n");
  934     fprintf(o, "    to the JSON format.\n");
  935     fprintf(o, "  * You can specify separators in any of the following ways, shown by example:\n");
  936     fprintf(o, "    - Type them out, quoting as necessary for shell escapes, e.g.\n");
  937     fprintf(o, "      \"--fs '|' --ips :\"\n");
  938     fprintf(o, "    - C-style escape sequences, e.g. \"--rs '\\r\\n' --fs '\\t'\".\n");
  939     fprintf(o, "    - To avoid backslashing, you can use any of the following names:\n");
  940     fprintf(o, "     ");
  941     lhmss_t* pmap = get_desc_to_chars_map();
  942     for (lhmsse_t* pe = pmap->phead; pe != NULL; pe = pe->pnext) {
  943         fprintf(o, " %s", pe->key);
  944     }
  945     fprintf(o, "\n");
  946     fprintf(o, "  * Default separators by format:\n");
  947     fprintf(o, "      %-12s %-8s %-8s %s\n", "File format", "RS", "FS", "PS");
  948     lhmss_t* default_rses = get_default_rses();
  949     lhmss_t* default_fses = get_default_fses();
  950     lhmss_t* default_pses = get_default_pses();
  951     for (lhmsse_t* pe = default_rses->phead; pe != NULL; pe = pe->pnext) {
  952         char* filefmt = pe->key;
  953         char* rs = pe->value;
  954         char* fs = lhmss_get(default_fses, filefmt);
  955         char* ps = lhmss_get(default_pses, filefmt);
  956         fprintf(o, "      %-12s %-8s %-8s %s\n", filefmt, rebackslash(rs), rebackslash(fs), rebackslash(ps));
  957     }
  958 }
  959 
  960 static void main_usage_csv_options(FILE* o, char* argv0) {
  961     fprintf(o, "  --implicit-csv-header Use 1,2,3,... as field labels, rather than from line 1\n");
  962     fprintf(o, "                     of input files. Tip: combine with \"label\" to recreate\n");
  963     fprintf(o, "                     missing headers.\n");
  964     fprintf(o, "  --allow-ragged-csv-input|--ragged If a data line has fewer fields than the header line,\n");
  965     fprintf(o, "                     fill remaining keys with empty string. If a data line has more\n");
  966     fprintf(o, "                     fields than the header line, use integer field labels as in\n");
  967     fprintf(o, "                     the implicit-header case.\n");
  968     fprintf(o, "  --headerless-csv-output   Print only CSV data lines.\n");
  969     fprintf(o, "  -N                 Keystroke-saver for --implicit-csv-header --headerless-csv-output.\n");
  970 }
  971 
  972 static void main_usage_double_quoting(FILE* o, char* argv0) {
  973     fprintf(o, "  --quote-all        Wrap all fields in double quotes\n");
  974     fprintf(o, "  --quote-none       Do not wrap any fields in double quotes, even if they have\n");
  975     fprintf(o, "                     OFS or ORS in them\n");
  976     fprintf(o, "  --quote-minimal    Wrap fields in double quotes only if they have OFS or ORS\n");
  977     fprintf(o, "                     in them (default)\n");
  978     fprintf(o, "  --quote-numeric    Wrap fields in double quotes only if they have numbers\n");
  979     fprintf(o, "                     in them\n");
  980     fprintf(o, "  --quote-original   Wrap fields in double quotes if and only if they were\n");
  981     fprintf(o, "                     quoted on input. This isn't sticky for computed fields:\n");
  982     fprintf(o, "                     e.g. if fields a and b were quoted on input and you do\n");
  983     fprintf(o, "                     \"put '$c = $a . $b'\" then field c won't inherit a or b's\n");
  984     fprintf(o, "                     was-quoted-on-input flag.\n");
  985 }
  986 
  987 static void main_usage_numerical_formatting(FILE* o, char* argv0) {
  988     fprintf(o, "  --ofmt {format}    E.g. %%.18lf, %%.0lf. Please use sprintf-style codes for\n");
  989     fprintf(o, "                     double-precision. Applies to verbs which compute new\n");
  990     fprintf(o, "                     values, e.g. put, stats1, stats2. See also the fmtnum\n");
  991     fprintf(o, "                     function within mlr put (mlr --help-all-functions).\n");
  992     fprintf(o, "                     Defaults to %s.\n", DEFAULT_OFMT);
  993 }
  994 
  995 static void main_usage_other_options(FILE* o, char* argv0) {
  996     fprintf(o, "  --seed {n} with n of the form 12345678 or 0xcafefeed. For put/filter\n");
  997     fprintf(o, "                     urand()/urandint()/urand32().\n");
  998     fprintf(o, "  --nr-progress-mod {m}, with m a positive integer: print filename and record\n");
  999     fprintf(o, "                     count to stderr every m input records.\n");
 1000     fprintf(o, "  --from {filename}  Use this to specify an input file before the verb(s),\n");
 1001     fprintf(o, "                     rather than after. May be used more than once. Example:\n");
 1002     fprintf(o, "                     \"%s --from a.dat --from b.dat cat\" is the same as\n", argv0);
 1003     fprintf(o, "                     \"%s cat a.dat b.dat\".\n", argv0);
 1004     fprintf(o, "  -n                 Process no input files, nor standard input either. Useful\n");
 1005     fprintf(o, "                     for %s put with begin/end statements only. (Same as --from\n", argv0);
 1006     fprintf(o, "                     /dev/null.) Also useful in \"%s -n put -v '...'\" for\n", argv0);
 1007     fprintf(o, "                     analyzing abstract syntax trees (if that's your thing).\n");
 1008     fprintf(o, "  -I                 Process files in-place. For each file name on the command\n");
 1009     fprintf(o, "                     line, output is written to a temp file in the same\n");
 1010     fprintf(o, "                     directory, which is then renamed over the original. Each\n");
 1011     fprintf(o, "                     file is processed in isolation: if the output format is\n");
 1012     fprintf(o, "                     CSV, CSV headers will be present in each output file;\n");
 1013     fprintf(o, "                     statistics are only over each file's own records; and so on.\n");
 1014 }
 1015 
 1016 static void main_usage_then_chaining(FILE* o, char* argv0) {
 1017     fprintf(o, "Output of one verb may be chained as input to another using \"then\", e.g.\n");
 1018     fprintf(o, "  %s stats1 -a min,mean,max -f flag,u,v -g color then sort -f color\n", argv0);
 1019 }
 1020 
 1021 static void main_usage_auxents(FILE* o, char* argv0) {
 1022     fprintf(o, "Miller has a few otherwise-standalone executables packaged within it.\n");
 1023     fprintf(o, "They do not participate in any other parts of Miller.\n");
 1024     show_aux_entries(o);
 1025 }
 1026 
 1027 static void main_usage_see_also(FILE* o, char* argv0) {
 1028     fprintf(o, "For more information please see http://johnkerl.org/miller/doc and/or\n");
 1029     fprintf(o, "http://github.com/johnkerl/miller.");
 1030     fprintf(o, " This is Miller version %s.\n", VERSION_STRING);
 1031 }
 1032 
 1033 static void print_type_arithmetic_info(FILE* o, char* argv0) {
 1034     for (int i = -2; i < MT_DIM; i++) {
 1035         mv_t a = (mv_t) {.type = i, .free_flags = NO_FREE, .u.intv = 0};
 1036         if (i == -2)
 1037             printf("%-6s |", "(+)");
 1038         else if (i == -1)
 1039             printf("%-6s +", "------");
 1040         else
 1041             printf("%-6s |", mt_describe_type_simple(a.type));
 1042 
 1043         for (int j = 0; j < MT_DIM; j++) {
 1044             mv_t b = (mv_t) {.type = j, .free_flags = NO_FREE, .u.intv = 0};
 1045             if (i == -2) {
 1046                 printf(" %-6s", mt_describe_type_simple(b.type));
 1047             } else if (i == -1) {
 1048                 printf(" %-6s", "------");
 1049             } else {
 1050                 mv_t c = x_xx_plus_func(&a, &b);
 1051                 printf(" %-6s", mt_describe_type_simple(c.type));
 1052             }
 1053         }
 1054 
 1055         fprintf(o, "\n");
 1056     }
 1057 }
 1058 
 1059 // ----------------------------------------------------------------
 1060 static void usage_all_verbs(char* argv0) {
 1061     char* separator = "================================================================";
 1062 
 1063     for (int i = 0; i < mapper_lookup_table_length; i++) {
 1064         fprintf(stdout, "%s\n", separator);
 1065         mapper_lookup_table[i]->pusage_func(stdout, argv0, mapper_lookup_table[i]->verb);
 1066         fprintf(stdout, "\n");
 1067     }
 1068     fprintf(stdout, "%s\n", separator);
 1069     exit(0);
 1070 }
 1071 
 1072 static void usage_unrecognized_verb(char* argv0, char* arg) {
 1073     fprintf(stderr, "%s: option \"%s\" not recognized.\n", argv0, arg);
 1074     fprintf(stderr, "Please run \"%s --help\" for usage information.\n", argv0);
 1075     exit(1);
 1076 }
 1077 
 1078 static void check_arg_count(char** argv, int argi, int argc, int n) {
 1079     if ((argc - argi) < n) {
 1080         fprintf(stderr, "%s: option \"%s\" missing argument(s).\n", MLR_GLOBALS.bargv0, argv[argi]);
 1081         main_usage_short(stderr, MLR_GLOBALS.bargv0);
 1082         exit(1);
 1083     }
 1084 }
 1085 
 1086 static mapper_setup_t* look_up_mapper_setup(char* verb) {
 1087     mapper_setup_t* pmapper_setup = NULL;
 1088     for (int i = 0; i < mapper_lookup_table_length; i++) {
 1089         if (streq(mapper_lookup_table[i]->verb, verb))
 1090             return mapper_lookup_table[i];
 1091     }
 1092 
 1093     return pmapper_setup;
 1094 }
 1095 
 1096 // ----------------------------------------------------------------
 1097 void cli_opts_init(cli_opts_t* popts) {
 1098     memset(popts, 0, sizeof(*popts));
 1099 
 1100     cli_reader_opts_init(&popts->reader_opts);
 1101     cli_writer_opts_init(&popts->writer_opts);
 1102 
 1103     popts->mapper_argb     = 0;
 1104     popts->filenames       = slls_alloc();
 1105 
 1106     popts->ofmt            = NULL;
 1107     popts->nr_progress_mod = 0LL;
 1108 
 1109     popts->do_in_place     = FALSE;
 1110 
 1111     popts->no_input        = FALSE;
 1112     popts->have_rand_seed  = FALSE;
 1113     popts->rand_seed       = 0;
 1114 }
 1115 
 1116 // ----------------------------------------------------------------
 1117 // * If $MLRRC is set, use it and only it.
 1118 // * Otherwise try first $HOME/.mlrrc and then ./.mlrrc but let them
 1119 //   stack: e.g. $HOME/.mlrrc is lots of settings and maybe in one
 1120 //   subdir you want to override just a setting or two.
 1121 static void cli_load_mlrrc(cli_opts_t* popts) {
 1122     char* env_mlrrc = getenv("MLRRC");
 1123     if (env_mlrrc != NULL) {
 1124         if (streq(env_mlrrc, "__none__")) {
 1125             return;
 1126         }
 1127         cli_try_load_mlrrc(popts, env_mlrrc);
 1128         return;
 1129     }
 1130 
 1131     char* env_home = getenv("HOME");
 1132     if (env_home != NULL) {
 1133         char* path = mlr_paste_2_strings(env_home, "/.mlrrc");
 1134         cli_try_load_mlrrc(popts, path);
 1135         free(path);
 1136     }
 1137 
 1138     cli_try_load_mlrrc(popts, "./.mlrrc");
 1139 }
 1140 
 1141 static void cli_try_load_mlrrc(cli_opts_t* popts, char* path) {
 1142     FILE* fp = fopen(path, "r");
 1143     if (fp == NULL) {
 1144         return;
 1145     }
 1146 
 1147     char* line = NULL;
 1148     size_t linecap = 0;
 1149     int rc;
 1150     int lineno = 0;
 1151 
 1152     while ((rc = getline(&line, &linecap, fp)) != -1) {
 1153         lineno++;
 1154         char* line_to_destroy = strdup(line);
 1155         if (!handle_mlrrc_line_1(popts, line_to_destroy)) {
 1156             fprintf(stderr, "Parse error at file \"%s\" line %d: %s\n",
 1157                 path, lineno, line);
 1158             exit(1);
 1159         }
 1160         free(line_to_destroy);
 1161     }
 1162 
 1163     fclose(fp);
 1164     if (line != NULL) {
 1165         free(line);
 1166     }
 1167 }
 1168 
 1169 // Chomps trailing CR, LF, or CR/LF; comment-strips; left-right trims.
 1170 static int handle_mlrrc_line_1(cli_opts_t* popts, char* line) {
 1171     // chomp
 1172     size_t len = strlen(line);
 1173     if (len >= 2 && line[len-2] == '\r' && line[len-1] == '\n') {
 1174         line[len-2] = 0;
 1175     } else if (len >= 1 && (line[len-1] == '\r' || line[len-1] == '\n')) {
 1176         line[len-1] = 0;
 1177     }
 1178 
 1179     // comment-strip
 1180     char* pbang = strstr(line, "#");
 1181     if (pbang != NULL) {
 1182         *pbang = 0;
 1183     }
 1184 
 1185     // Left-trim
 1186     char* start = line;
 1187     while (*start == ' ' || *start == '\t') {
 1188         start++;
 1189     }
 1190 
 1191     // Right-trim
 1192     len = strlen(start);
 1193     char* end = &start[len-1];
 1194     while (end > start && (*end == ' ' || *end == '\t')) {
 1195         *end = 0;
 1196         end--;
 1197     }
 1198     if (end < start) { // line was whitespace-only
 1199         return TRUE;
 1200     } else {
 1201         return handle_mlrrc_line_2(popts, start);
 1202     }
 1203 }
 1204 
 1205 // Prepends initial "--" if it's not already there
 1206 static int handle_mlrrc_line_2(cli_opts_t* popts, char* line) {
 1207     size_t len = strlen(line);
 1208 
 1209     char* dashed_line = NULL;
 1210     if (len >= 2 && line[0] != '-' && line[1] != '-') {
 1211         dashed_line = mlr_paste_2_strings("--", line);
 1212     } else {
 1213         dashed_line = strdup(line);
 1214     }
 1215 
 1216     int rc = handle_mlrrc_line_3(popts, dashed_line);
 1217 
 1218     // Do not free these. The command-line parsers can retain pointers into argv strings (rather
 1219     // than copying), resulting in freed-memory reads later in the data-processing verbs.
 1220     //
 1221     // It would be possible to be diligent about making sure all current command-line-parsing
 1222     // callsites copy strings rather than pointing to them -- but it would be easy to miss some, and
 1223     // also any future codemods might make the same mistake as well.
 1224     //
 1225     // It's safer (and no big leak) to simply leave these parsed mlrrc lines unfreed.
 1226     //
 1227     // free(dashed_line);
 1228     return rc;
 1229 }
 1230 
 1231 // Splits line into argv array
 1232 static int handle_mlrrc_line_3(cli_opts_t* popts, char* line) {
 1233     char* argv[3];
 1234     int argc = 0;
 1235     char* split = strpbrk(line, " \t");
 1236     if (split == NULL) {
 1237         argv[0] = line;
 1238         argv[1] = NULL;
 1239         argc = 1;
 1240     } else {
 1241         *split = 0;
 1242         char* p = split + 1;
 1243         while (*p == ' ' || *p == '\t') {
 1244             p++;
 1245         }
 1246         argv[0] = line;
 1247         argv[1] = p;
 1248         argv[2] = NULL;
 1249         argc = 2;
 1250     }
 1251     return handle_mlrrc_line_4(popts, argv, argc);
 1252 }
 1253 
 1254 static int handle_mlrrc_line_4(cli_opts_t* popts, char** argv, int argc) {
 1255     int argi = 0;
 1256     if (streq(argv[0], "--prepipe")) {
 1257         // Don't allow code execution via .mlrrc
 1258         return FALSE;
 1259     }
 1260     if (cli_handle_reader_options(argv, argc, &argi, &popts->reader_opts)) {
 1261         // handled
 1262     } else if (cli_handle_writer_options(argv, argc, &argi, &popts->writer_opts)) {
 1263         // handled
 1264     } else if (cli_handle_reader_writer_options(argv, argc, &argi, &popts->reader_opts, &popts->writer_opts)) {
 1265         // handled
 1266     } else if (cli_handle_misc_options(argv, argc, &argi, popts)) {
 1267         // handled
 1268     } else {
 1269         // unhandled
 1270         return FALSE;
 1271     }
 1272 
 1273     return TRUE;
 1274 }
 1275 
 1276 // ----------------------------------------------------------------
 1277 void cli_reader_opts_init(cli_reader_opts_t* preader_opts) {
 1278     preader_opts->ifile_fmt                      = NULL;
 1279     preader_opts->irs                            = NULL;
 1280     preader_opts->ifs                            = NULL;
 1281     preader_opts->ips                            = NULL;
 1282     preader_opts->input_json_flatten_separator   = NULL;
 1283     preader_opts->json_array_ingest              = JSON_ARRAY_INGEST_UNSPECIFIED;
 1284 
 1285     preader_opts->allow_repeat_ifs               = NEITHER_TRUE_NOR_FALSE;
 1286     preader_opts->allow_repeat_ips               = NEITHER_TRUE_NOR_FALSE;
 1287     preader_opts->use_implicit_csv_header        = NEITHER_TRUE_NOR_FALSE;
 1288     preader_opts->allow_ragged_csv_input         = NEITHER_TRUE_NOR_FALSE;
 1289 
 1290     preader_opts->prepipe                        = NULL;
 1291     preader_opts->comment_handling               = COMMENTS_ARE_DATA;
 1292     preader_opts->comment_string                 = NULL;
 1293 
 1294     preader_opts->generator_opts.field_name     = "i";
 1295     preader_opts->generator_opts.start          = 0LL;
 1296     preader_opts->generator_opts.stop           = 100LL;
 1297     preader_opts->generator_opts.step           = 1LL;
 1298 }
 1299 
 1300 void cli_writer_opts_init(cli_writer_opts_t* pwriter_opts) {
 1301     pwriter_opts->ofile_fmt                      = NULL;
 1302     pwriter_opts->ors                            = NULL;
 1303     pwriter_opts->ofs                            = NULL;
 1304     pwriter_opts->ops                            = NULL;
 1305 
 1306     pwriter_opts->headerless_csv_output          = NEITHER_TRUE_NOR_FALSE;
 1307     pwriter_opts->right_justify_xtab_value       = NEITHER_TRUE_NOR_FALSE;
 1308     pwriter_opts->right_align_pprint             = NEITHER_TRUE_NOR_FALSE;
 1309     pwriter_opts->pprint_barred                  = NEITHER_TRUE_NOR_FALSE;
 1310     pwriter_opts->stack_json_output_vertically   = NEITHER_TRUE_NOR_FALSE;
 1311     pwriter_opts->wrap_json_output_in_outer_list = NEITHER_TRUE_NOR_FALSE;
 1312     pwriter_opts->json_quote_int_keys            = NEITHER_TRUE_NOR_FALSE;
 1313     pwriter_opts->json_quote_non_string_values   = NEITHER_TRUE_NOR_FALSE;
 1314 
 1315     pwriter_opts->output_json_flatten_separator  = NULL;
 1316     pwriter_opts->oosvar_flatten_separator       = NULL;
 1317 
 1318     pwriter_opts->oquoting                       = QUOTE_UNSPECIFIED;
 1319 }
 1320 
 1321 void cli_apply_defaults(cli_opts_t* popts) {
 1322 
 1323     cli_apply_reader_defaults(&popts->reader_opts);
 1324 
 1325     cli_apply_writer_defaults(&popts->writer_opts);
 1326 
 1327     if (popts->ofmt == NULL)
 1328         popts->ofmt = DEFAULT_OFMT;
 1329 }
 1330 
 1331 void cli_apply_reader_defaults(cli_reader_opts_t* preader_opts) {
 1332     if (preader_opts->ifile_fmt == NULL)
 1333         preader_opts->ifile_fmt = "dkvp";
 1334 
 1335     if (preader_opts->json_array_ingest == JSON_ARRAY_INGEST_UNSPECIFIED)
 1336         preader_opts->json_array_ingest = JSON_ARRAY_INGEST_AS_MAP;
 1337 
 1338     if (preader_opts->use_implicit_csv_header == NEITHER_TRUE_NOR_FALSE)
 1339         preader_opts->use_implicit_csv_header = FALSE;
 1340 
 1341     if (preader_opts->allow_ragged_csv_input == NEITHER_TRUE_NOR_FALSE)
 1342         preader_opts->allow_ragged_csv_input = FALSE;
 1343 
 1344     if (preader_opts->input_json_flatten_separator == NULL)
 1345         preader_opts->input_json_flatten_separator = DEFAULT_JSON_FLATTEN_SEPARATOR;
 1346 }
 1347 
 1348 void cli_apply_writer_defaults(cli_writer_opts_t* pwriter_opts) {
 1349     if (pwriter_opts->ofile_fmt == NULL)
 1350         pwriter_opts->ofile_fmt = "dkvp";
 1351 
 1352     if (pwriter_opts->headerless_csv_output == NEITHER_TRUE_NOR_FALSE)
 1353         pwriter_opts->headerless_csv_output = FALSE;
 1354 
 1355     if (pwriter_opts->right_justify_xtab_value == NEITHER_TRUE_NOR_FALSE)
 1356         pwriter_opts->right_justify_xtab_value = FALSE;
 1357 
 1358     if (pwriter_opts->right_align_pprint == NEITHER_TRUE_NOR_FALSE)
 1359         pwriter_opts->right_align_pprint = FALSE;
 1360 
 1361     if (pwriter_opts->pprint_barred == NEITHER_TRUE_NOR_FALSE)
 1362         pwriter_opts->pprint_barred = FALSE;
 1363 
 1364     if (pwriter_opts->stack_json_output_vertically == NEITHER_TRUE_NOR_FALSE)
 1365         pwriter_opts->stack_json_output_vertically = FALSE;
 1366 
 1367     if (pwriter_opts->wrap_json_output_in_outer_list == NEITHER_TRUE_NOR_FALSE)
 1368         pwriter_opts->wrap_json_output_in_outer_list = FALSE;
 1369 
 1370     if (pwriter_opts->json_quote_int_keys == NEITHER_TRUE_NOR_FALSE)
 1371         pwriter_opts->json_quote_int_keys = TRUE;
 1372 
 1373     if (pwriter_opts->json_quote_non_string_values == NEITHER_TRUE_NOR_FALSE)
 1374         pwriter_opts->json_quote_non_string_values = FALSE;
 1375 
 1376     if (pwriter_opts->output_json_flatten_separator == NULL)
 1377         pwriter_opts->output_json_flatten_separator = DEFAULT_JSON_FLATTEN_SEPARATOR;
 1378 
 1379     if (pwriter_opts->oosvar_flatten_separator == NULL)
 1380         pwriter_opts->oosvar_flatten_separator = DEFAULT_OOSVAR_FLATTEN_SEPARATOR;
 1381 
 1382     if (pwriter_opts->oquoting == QUOTE_UNSPECIFIED)
 1383         pwriter_opts->oquoting = DEFAULT_OQUOTING;
 1384 }
 1385 
 1386 // ----------------------------------------------------------------
 1387 // For mapper join which has its own input-format overrides.
 1388 //
 1389 // Mainly this just takes the main-opts flag whenever the join-opts flag was not
 1390 // specified by the user. But it's a bit more complex when main and join input
 1391 // formats are different. Example: main input format is CSV, for which IPS is
 1392 // "(N/A)", and join input format is DKVP. Then we should not use "(N/A)"
 1393 // for DKVP IPS. However if main input format were DKVP with IPS set to ":",
 1394 // then we should take that.
 1395 //
 1396 // The logic is:
 1397 //
 1398 // * If the join input format was unspecified, take all unspecified values from
 1399 //   main opts.
 1400 //
 1401 // * If the join input format was specified and is the same as main input
 1402 //   format, take unspecified values from main opts.
 1403 //
 1404 // * If the join input format was specified and is not the same as main input
 1405 //   format, take unspecified values from defaults for the join input format.
 1406 
 1407 void cli_merge_reader_opts(cli_reader_opts_t* pfunc_opts, cli_reader_opts_t* pmain_opts) {
 1408 
 1409     if (pfunc_opts->ifile_fmt == NULL) {
 1410         pfunc_opts->ifile_fmt = pmain_opts->ifile_fmt;
 1411     }
 1412 
 1413     if (streq(pfunc_opts->ifile_fmt, pmain_opts->ifile_fmt)) {
 1414 
 1415         if (pfunc_opts->irs == NULL)
 1416             pfunc_opts->irs = pmain_opts->irs;
 1417         if (pfunc_opts->ifs == NULL)
 1418             pfunc_opts->ifs = pmain_opts->ifs;
 1419         if (pfunc_opts->ips == NULL)
 1420             pfunc_opts->ips = pmain_opts->ips;
 1421         if (pfunc_opts->allow_repeat_ifs  == NEITHER_TRUE_NOR_FALSE)
 1422             pfunc_opts->allow_repeat_ifs = pmain_opts->allow_repeat_ifs;
 1423         if (pfunc_opts->allow_repeat_ips  == NEITHER_TRUE_NOR_FALSE)
 1424             pfunc_opts->allow_repeat_ips = pmain_opts->allow_repeat_ips;
 1425 
 1426     } else {
 1427 
 1428         if (pfunc_opts->irs == NULL)
 1429             pfunc_opts->irs = lhmss_get_or_die(get_default_rses(), pfunc_opts->ifile_fmt);
 1430         if (pfunc_opts->ifs == NULL)
 1431             pfunc_opts->ifs = lhmss_get_or_die(get_default_fses(), pfunc_opts->ifile_fmt);
 1432         if (pfunc_opts->ips == NULL)
 1433             pfunc_opts->ips = lhmss_get_or_die(get_default_pses(), pfunc_opts->ifile_fmt);
 1434         if (pfunc_opts->allow_repeat_ifs  == NEITHER_TRUE_NOR_FALSE)
 1435             pfunc_opts->allow_repeat_ifs = lhmsll_get_or_die(get_default_repeat_ifses(), pfunc_opts->ifile_fmt);
 1436         if (pfunc_opts->allow_repeat_ips  == NEITHER_TRUE_NOR_FALSE)
 1437             pfunc_opts->allow_repeat_ips = lhmsll_get_or_die(get_default_repeat_ipses(), pfunc_opts->ifile_fmt);
 1438 
 1439     }
 1440 
 1441     if (pfunc_opts->json_array_ingest == JSON_ARRAY_INGEST_UNSPECIFIED)
 1442         pfunc_opts->json_array_ingest = pmain_opts->json_array_ingest;
 1443 
 1444     if (pfunc_opts->use_implicit_csv_header == NEITHER_TRUE_NOR_FALSE)
 1445         pfunc_opts->use_implicit_csv_header = pmain_opts->use_implicit_csv_header;
 1446 
 1447     if (pfunc_opts->allow_ragged_csv_input == NEITHER_TRUE_NOR_FALSE)
 1448         pfunc_opts->allow_ragged_csv_input = pmain_opts->allow_ragged_csv_input;
 1449 
 1450     if (pfunc_opts->input_json_flatten_separator == NULL)
 1451         pfunc_opts->input_json_flatten_separator = pmain_opts->input_json_flatten_separator;
 1452 }
 1453 
 1454 // Similar to cli_merge_reader_opts but for mapper tee & mapper put which have their
 1455 // own output-format overrides.
 1456 void cli_merge_writer_opts(cli_writer_opts_t* pfunc_opts, cli_writer_opts_t* pmain_opts) {
 1457 
 1458     if (pfunc_opts->ofile_fmt == NULL) {
 1459         pfunc_opts->ofile_fmt = pmain_opts->ofile_fmt;
 1460     }
 1461 
 1462     if (streq(pfunc_opts->ofile_fmt, pmain_opts->ofile_fmt)) {
 1463         if (pfunc_opts->ors == NULL)
 1464             pfunc_opts->ors = pmain_opts->ors;
 1465         if (pfunc_opts->ofs == NULL)
 1466             pfunc_opts->ofs = pmain_opts->ofs;
 1467         if (pfunc_opts->ops == NULL)
 1468             pfunc_opts->ops = pmain_opts->ops;
 1469     } else {
 1470         if (pfunc_opts->ors == NULL)
 1471             pfunc_opts->ors = lhmss_get_or_die(get_default_rses(), pfunc_opts->ofile_fmt);
 1472         if (pfunc_opts->ofs == NULL)
 1473             pfunc_opts->ofs = lhmss_get_or_die(get_default_fses(), pfunc_opts->ofile_fmt);
 1474         if (pfunc_opts->ops == NULL)
 1475             pfunc_opts->ops = lhmss_get_or_die(get_default_pses(), pfunc_opts->ofile_fmt);
 1476     }
 1477 
 1478     if (pfunc_opts->headerless_csv_output == NEITHER_TRUE_NOR_FALSE)
 1479         pfunc_opts->headerless_csv_output = pmain_opts->headerless_csv_output;
 1480 
 1481     if (pfunc_opts->right_justify_xtab_value == NEITHER_TRUE_NOR_FALSE)
 1482         pfunc_opts->right_justify_xtab_value = pmain_opts->right_justify_xtab_value;
 1483 
 1484     if (pfunc_opts->right_align_pprint == NEITHER_TRUE_NOR_FALSE)
 1485         pfunc_opts->right_align_pprint = pmain_opts->right_align_pprint;
 1486 
 1487     if (pfunc_opts->pprint_barred == NEITHER_TRUE_NOR_FALSE)
 1488         pfunc_opts->pprint_barred = pmain_opts->pprint_barred;
 1489 
 1490     if (pfunc_opts->stack_json_output_vertically == NEITHER_TRUE_NOR_FALSE)
 1491         pfunc_opts->stack_json_output_vertically = pmain_opts->stack_json_output_vertically;
 1492 
 1493     if (pfunc_opts->wrap_json_output_in_outer_list == NEITHER_TRUE_NOR_FALSE)
 1494         pfunc_opts->wrap_json_output_in_outer_list = pmain_opts->wrap_json_output_in_outer_list;
 1495 
 1496     if (pfunc_opts->json_quote_int_keys == NEITHER_TRUE_NOR_FALSE)
 1497         pfunc_opts->json_quote_int_keys = pmain_opts->json_quote_int_keys;
 1498 
 1499     if (pfunc_opts->json_quote_non_string_values == NEITHER_TRUE_NOR_FALSE)
 1500         pfunc_opts->json_quote_non_string_values = pmain_opts->json_quote_non_string_values;
 1501 
 1502     if (pfunc_opts->output_json_flatten_separator == NULL)
 1503         pfunc_opts->output_json_flatten_separator = pmain_opts->output_json_flatten_separator;
 1504 
 1505     if (pfunc_opts->oosvar_flatten_separator == NULL)
 1506         pfunc_opts->oosvar_flatten_separator = pmain_opts->oosvar_flatten_separator;
 1507 
 1508     if (pfunc_opts->oquoting == QUOTE_UNSPECIFIED)
 1509         pfunc_opts->oquoting = pmain_opts->oquoting;
 1510 }
 1511 
 1512 // ----------------------------------------------------------------
 1513 static int handle_terminal_usage(char** argv, int argc, int argi) {
 1514     if (streq(argv[argi], "--version")) {
 1515         printf("Miller %s\n", VERSION_STRING);
 1516         return TRUE;
 1517     } else if (streq(argv[argi], "-h")) {
 1518         main_usage_long(stdout, MLR_GLOBALS.bargv0);
 1519         return TRUE;
 1520     } else if (streq(argv[argi], "--help")) {
 1521         main_usage_long(stdout, MLR_GLOBALS.bargv0);
 1522         return TRUE;
 1523     } else if (streq(argv[argi], "--print-type-arithmetic-info")) {
 1524         print_type_arithmetic_info(stdout, MLR_GLOBALS.bargv0);
 1525         return TRUE;
 1526 
 1527     } else if (streq(argv[argi], "--help-all-verbs")) {
 1528         usage_all_verbs(MLR_GLOBALS.bargv0);
 1529     } else if (streq(argv[argi], "--list-all-verbs") || streq(argv[argi], "-l")) {
 1530         list_all_verbs(stdout, "");
 1531         return TRUE;
 1532     } else if (streq(argv[argi], "--list-all-verbs-raw") || streq(argv[argi], "-L")) {
 1533         list_all_verbs_raw(stdout);
 1534         return TRUE;
 1535 
 1536     } else if (streq(argv[argi], "--list-all-functions-raw") || streq(argv[argi], "-F")) {
 1537         fmgr_t* pfmgr = fmgr_alloc();
 1538         fmgr_list_all_functions_raw(pfmgr, stdout);
 1539         fmgr_free(pfmgr, NULL);
 1540         return TRUE;
 1541     } else if (streq(argv[argi], "--list-all-functions-as-table")) {
 1542         fmgr_t* pfmgr = fmgr_alloc();
 1543         fmgr_list_all_functions_as_table(pfmgr, stdout);
 1544         fmgr_free(pfmgr, NULL);
 1545         return TRUE;
 1546     } else if (streq(argv[argi], "--help-all-functions") || streq(argv[argi], "-f")) {
 1547         fmgr_t* pfmgr = fmgr_alloc();
 1548         fmgr_function_usage(pfmgr, stdout, NULL);
 1549         fmgr_free(pfmgr, NULL);
 1550         return TRUE;
 1551     } else if (streq(argv[argi], "--help-function") || streq(argv[argi], "--hf")) {
 1552         check_arg_count(argv, argi, argc, 2);
 1553         fmgr_t* pfmgr = fmgr_alloc();
 1554         fmgr_function_usage(pfmgr, stdout, argv[argi+1]);
 1555         fmgr_free(pfmgr, NULL);
 1556         return TRUE;
 1557 
 1558     } else if (streq(argv[argi], "--list-all-keywords-raw") || streq(argv[argi], "-K")) {
 1559         mlr_dsl_list_all_keywords_raw(stdout);
 1560         return TRUE;
 1561     } else if (streq(argv[argi], "--help-all-keywords") || streq(argv[argi], "-k")) {
 1562         mlr_dsl_keyword_usage(stdout, NULL);
 1563         return TRUE;
 1564     } else if (streq(argv[argi], "--help-keyword") || streq(argv[argi], "--hk")) {
 1565         check_arg_count(argv, argi, argc, 2);
 1566         mlr_dsl_keyword_usage(stdout, argv[argi+1]);
 1567         return TRUE;
 1568 
 1569     // main-usage subsections, individually accessible for the benefit of
 1570     // the manpage-autogenerator
 1571     } else if (streq(argv[argi], "--usage-synopsis")) {
 1572         main_usage_synopsis(stdout, MLR_GLOBALS.bargv0);
 1573         return TRUE;
 1574     } else if (streq(argv[argi], "--usage-examples")) {
 1575         main_usage_examples(stdout, MLR_GLOBALS.bargv0, "");
 1576         return TRUE;
 1577     } else if (streq(argv[argi], "--usage-list-all-verbs")) {
 1578         list_all_verbs(stdout, "");
 1579         return TRUE;
 1580     } else if (streq(argv[argi], "--usage-help-options")) {
 1581         main_usage_help_options(stdout, MLR_GLOBALS.bargv0);
 1582         return TRUE;
 1583     } else if (streq(argv[argi], "--usage-mlrrc")) {
 1584         main_usage_mlrrc(stdout, MLR_GLOBALS.bargv0);
 1585         return TRUE;
 1586     } else if (streq(argv[argi], "--usage-functions")) {
 1587         main_usage_functions(stdout, MLR_GLOBALS.bargv0, "");
 1588         return TRUE;
 1589     } else if (streq(argv[argi], "--usage-data-format-examples")) {
 1590         main_usage_data_format_examples(stdout, MLR_GLOBALS.bargv0);
 1591         return TRUE;
 1592     } else if (streq(argv[argi], "--usage-data-format-options")) {
 1593         main_usage_data_format_options(stdout, MLR_GLOBALS.bargv0);
 1594         return TRUE;
 1595     } else if (streq(argv[argi], "--usage-comments-in-data")) {
 1596         main_usage_comments_in_data(stdout, MLR_GLOBALS.bargv0);
 1597         return TRUE;
 1598     } else if (streq(argv[argi], "--usage-format-conversion-keystroke-saver-options")) {
 1599         main_usage_format_conversion_keystroke_saver_options(stdout, MLR_GLOBALS.bargv0);
 1600         return TRUE;
 1601     } else if (streq(argv[argi], "--usage-compressed-data-options")) {
 1602         main_usage_compressed_data_options(stdout, MLR_GLOBALS.bargv0);
 1603         return TRUE;
 1604     } else if (streq(argv[argi], "--usage-separator-options")) {
 1605         main_usage_separator_options(stdout, MLR_GLOBALS.bargv0);
 1606         return TRUE;
 1607     } else if (streq(argv[argi], "--usage-csv-options")) {
 1608         main_usage_csv_options(stdout, MLR_GLOBALS.bargv0);
 1609         return TRUE;
 1610     } else if (streq(argv[argi], "--usage-double-quoting")) {
 1611         main_usage_double_quoting(stdout, MLR_GLOBALS.bargv0);
 1612         return TRUE;
 1613     } else if (streq(argv[argi], "--usage-numerical-formatting")) {
 1614         main_usage_numerical_formatting(stdout, MLR_GLOBALS.bargv0);
 1615         return TRUE;
 1616     } else if (streq(argv[argi], "--usage-other-options")) {
 1617         main_usage_other_options(stdout, MLR_GLOBALS.bargv0);
 1618         return TRUE;
 1619     } else if (streq(argv[argi], "--usage-then-chaining")) {
 1620         main_usage_then_chaining(stdout, MLR_GLOBALS.bargv0);
 1621         return TRUE;
 1622     } else if (streq(argv[argi], "--usage-auxents")) {
 1623         main_usage_auxents(stdout, MLR_GLOBALS.bargv0);
 1624         return TRUE;
 1625     } else if (streq(argv[argi], "--usage-see-also")) {
 1626         main_usage_see_also(stdout, MLR_GLOBALS.bargv0);
 1627         return TRUE;
 1628     }
 1629     return FALSE;
 1630 }
 1631 
 1632 // Returns TRUE if the current flag was handled.
 1633 int cli_handle_reader_options(char** argv, int argc, int *pargi, cli_reader_opts_t* preader_opts) {
 1634     int argi = *pargi;
 1635     int oargi = argi;
 1636 
 1637     if (streq(argv[argi], "--irs")) {
 1638         check_arg_count(argv, argi, argc, 2);
 1639         preader_opts->irs = cli_sep_from_arg(argv[argi+1]);
 1640         argi += 2;
 1641 
 1642     } else if (streq(argv[argi], "--ifs")) {
 1643         check_arg_count(argv, argi, argc, 2);
 1644         preader_opts->ifs = cli_sep_from_arg(argv[argi+1]);
 1645         argi += 2;
 1646 
 1647     } else if (streq(argv[argi], "--repifs")) {
 1648         preader_opts->allow_repeat_ifs = TRUE;
 1649         argi += 1;
 1650 
 1651     } else if (streq(argv[argi], "--json-fatal-arrays-on-input")) {
 1652         preader_opts->json_array_ingest = JSON_ARRAY_INGEST_FATAL;
 1653         argi += 1;
 1654     } else if (streq(argv[argi], "--json-skip-arrays-on-input")) {
 1655         preader_opts->json_array_ingest = JSON_ARRAY_INGEST_SKIP;
 1656         argi += 1;
 1657     } else if (streq(argv[argi], "--json-map-arrays-on-input")) {
 1658         preader_opts->json_array_ingest = JSON_ARRAY_INGEST_AS_MAP;
 1659         argi += 1;
 1660 
 1661     } else if (streq(argv[argi], "--implicit-csv-header")) {
 1662         preader_opts->use_implicit_csv_header = TRUE;
 1663         argi += 1;
 1664 
 1665     } else if (streq(argv[argi], "--no-implicit-csv-header")) {
 1666         preader_opts->use_implicit_csv_header = FALSE;
 1667         argi += 1;
 1668 
 1669     } else if (streq(argv[argi], "--allow-ragged-csv-input") || streq(argv[argi], "--ragged")) {
 1670         preader_opts->allow_ragged_csv_input = TRUE;
 1671         argi += 1;
 1672 
 1673     } else if (streq(argv[argi], "--ips")) {
 1674         check_arg_count(argv, argi, argc, 2);
 1675         preader_opts->ips = cli_sep_from_arg(argv[argi+1]);
 1676         argi += 2;
 1677 
 1678     } else if (streq(argv[argi], "-i")) {
 1679         check_arg_count(argv, argi, argc, 2);
 1680         if (!lhmss_has_key(get_default_rses(), argv[argi+1])) {
 1681             fprintf(stderr, "%s: unrecognized input format \"%s\".\n",
 1682                 MLR_GLOBALS.bargv0, argv[argi+1]);
 1683             exit(1);
 1684         }
 1685         preader_opts->ifile_fmt = argv[argi+1];
 1686         argi += 2;
 1687 
 1688     } else if (streq(argv[argi], "--igen")) {
 1689         preader_opts->ifile_fmt = "gen";
 1690         argi += 1;
 1691     } else if (streq(argv[argi], "--gen-start")) {
 1692         preader_opts->ifile_fmt = "gen";
 1693         check_arg_count(argv, argi, argc, 2);
 1694         if (sscanf(argv[argi+1], "%lld", &preader_opts->generator_opts.start) != 1) {
 1695             fprintf(stderr, "%s: could not scan \"%s\".\n",
 1696                 MLR_GLOBALS.bargv0, argv[argi+1]);
 1697         }
 1698         argi += 2;
 1699     } else if (streq(argv[argi], "--gen-stop")) {
 1700         preader_opts->ifile_fmt = "gen";
 1701         check_arg_count(argv, argi, argc, 2);
 1702         if (sscanf(argv[argi+1], "%lld", &preader_opts->generator_opts.stop) != 1) {
 1703             fprintf(stderr, "%s: could not scan \"%s\".\n",
 1704                 MLR_GLOBALS.bargv0, argv[argi+1]);
 1705         }
 1706         argi += 2;
 1707     } else if (streq(argv[argi], "--gen-step")) {
 1708         preader_opts->ifile_fmt = "gen";
 1709         check_arg_count(argv, argi, argc, 2);
 1710         if (sscanf(argv[argi+1], "%lld", &preader_opts->generator_opts.step) != 1) {
 1711             fprintf(stderr, "%s: could not scan \"%s\".\n",
 1712                 MLR_GLOBALS.bargv0, argv[argi+1]);
 1713         }
 1714         argi += 2;
 1715 
 1716     } else if (streq(argv[argi], "--icsv")) {
 1717         preader_opts->ifile_fmt = "csv";
 1718         argi += 1;
 1719 
 1720     } else if (streq(argv[argi], "--icsvlite")) {
 1721         preader_opts->ifile_fmt = "csvlite";
 1722         argi += 1;
 1723 
 1724     } else if (streq(argv[argi], "--itsv")) {
 1725         preader_opts->ifile_fmt = "csv";
 1726         preader_opts->ifs = "\t";
 1727         argi += 1;
 1728 
 1729     } else if (streq(argv[argi], "--itsvlite")) {
 1730         preader_opts->ifile_fmt = "csvlite";
 1731         preader_opts->ifs = "\t";
 1732         argi += 1;
 1733 
 1734     } else if (streq(argv[argi], "--iasv")) {
 1735         preader_opts->ifile_fmt = "csv";
 1736         preader_opts->ifs = ASV_FS;
 1737         preader_opts->irs = ASV_RS;
 1738         argi += 1;
 1739 
 1740     } else if (streq(argv[argi], "--iasvlite")) {
 1741         preader_opts->ifile_fmt = "csvlite";
 1742         preader_opts->ifs = ASV_FS;
 1743         preader_opts->irs = ASV_RS;
 1744         argi += 1;
 1745 
 1746     } else if (streq(argv[argi], "--iusv")) {
 1747         preader_opts->ifile_fmt = "csv";
 1748         preader_opts->ifs = USV_FS;
 1749         preader_opts->irs = USV_RS;
 1750         argi += 1;
 1751 
 1752     } else if (streq(argv[argi], "--iusvlite")) {
 1753         preader_opts->ifile_fmt = "csvlite";
 1754         preader_opts->ifs = USV_FS;
 1755         preader_opts->irs = USV_RS;
 1756         argi += 1;
 1757 
 1758     } else if (streq(argv[argi], "--idkvp")) {
 1759         preader_opts->ifile_fmt = "dkvp";
 1760         argi += 1;
 1761 
 1762     } else if (streq(argv[argi], "--ijson")) {
 1763         preader_opts->ifile_fmt = "json";
 1764         argi += 1;
 1765 
 1766     } else if (streq(argv[argi], "--inidx")) {
 1767         preader_opts->ifile_fmt = "nidx";
 1768         argi += 1;
 1769 
 1770     } else if (streq(argv[argi], "--ixtab")) {
 1771         preader_opts->ifile_fmt = "xtab";
 1772         argi += 1;
 1773 
 1774     } else if (streq(argv[argi], "--ipprint")) {
 1775         preader_opts->ifile_fmt        = "csvlite";
 1776         preader_opts->ifs              = " ";
 1777         preader_opts->allow_repeat_ifs = TRUE;
 1778         argi += 1;
 1779 
 1780     } else if (streq(argv[argi], "--mmap")) {
 1781         // No-op as of 5.6.3 (mmap is being abandoned) but don't break
 1782         // the command-line user experience.
 1783         argi += 1;
 1784 
 1785     } else if (streq(argv[argi], "--no-mmap")) {
 1786         // No-op as of 5.6.3 (mmap is being abandoned) but don't break
 1787         // the command-line user experience.
 1788         argi += 1;
 1789 
 1790     } else if (streq(argv[argi], "--prepipe")) {
 1791         check_arg_count(argv, argi, argc, 2);
 1792         preader_opts->prepipe = argv[argi+1];
 1793         argi += 2;
 1794 
 1795     } else if (streq(argv[argi], "--prepipe-gunzip")) {
 1796         preader_opts->prepipe = "gunzip";
 1797         argi += 1;
 1798 
 1799     } else if (streq(argv[argi], "--prepipe-zcat")) {
 1800         preader_opts->prepipe = "zcat";
 1801         argi += 1;
 1802 
 1803     } else if (streq(argv[argi], "--skip-comments")) {
 1804         preader_opts->comment_string = DEFAULT_COMMENT_STRING;
 1805         preader_opts->comment_handling = SKIP_COMMENTS;
 1806         argi += 1;
 1807 
 1808     } else if (streq(argv[argi], "--skip-comments-with")) {
 1809         check_arg_count(argv, argi, argc, 2);
 1810         preader_opts->comment_string = argv[argi+1];
 1811         preader_opts->comment_handling = SKIP_COMMENTS;
 1812         argi += 2;
 1813 
 1814     } else if (streq(argv[argi], "--pass-comments")) {
 1815         preader_opts->comment_string = DEFAULT_COMMENT_STRING;
 1816         preader_opts->comment_handling = PASS_COMMENTS;
 1817         argi += 1;
 1818 
 1819     } else if (streq(argv[argi], "--pass-comments-with")) {
 1820         check_arg_count(argv, argi, argc, 2);
 1821         preader_opts->comment_string = argv[argi+1];
 1822         preader_opts->comment_handling = PASS_COMMENTS;
 1823         argi += 2;
 1824 
 1825     }
 1826     *pargi = argi;
 1827     return argi != oargi;
 1828 }
 1829 
 1830 // Returns TRUE if the current flag was handled.
 1831 int cli_handle_writer_options(char** argv, int argc, int *pargi, cli_writer_opts_t* pwriter_opts) {
 1832     int argi = *pargi;
 1833     int oargi = argi;
 1834 
 1835     if (streq(argv[argi], "--ors")) {
 1836         check_arg_count(argv, argi, argc, 2);
 1837         pwriter_opts->ors = cli_sep_from_arg(argv[argi+1]);
 1838         argi += 2;
 1839 
 1840     } else if (streq(argv[argi], "--ofs")) {
 1841         check_arg_count(argv, argi, argc, 2);
 1842         pwriter_opts->ofs = cli_sep_from_arg(argv[argi+1]);
 1843         argi += 2;
 1844 
 1845     } else if (streq(argv[argi], "--headerless-csv-output")) {
 1846         pwriter_opts->headerless_csv_output = TRUE;
 1847         argi += 1;
 1848 
 1849     } else if (streq(argv[argi], "--ops")) {
 1850         check_arg_count(argv, argi, argc, 2);
 1851         pwriter_opts->ops = cli_sep_from_arg(argv[argi+1]);
 1852         argi += 2;
 1853 
 1854     } else if (streq(argv[argi], "--xvright")) {
 1855         pwriter_opts->right_justify_xtab_value = TRUE;
 1856         argi += 1;
 1857 
 1858     } else if (streq(argv[argi], "--jvstack")) {
 1859         pwriter_opts->stack_json_output_vertically = TRUE;
 1860         argi += 1;
 1861 
 1862     } else if (streq(argv[argi], "--jlistwrap")) {
 1863         pwriter_opts->wrap_json_output_in_outer_list = TRUE;
 1864         argi += 1;
 1865 
 1866     } else if (streq(argv[argi], "--jknquoteint")) {
 1867         pwriter_opts->json_quote_int_keys = FALSE;
 1868         argi += 1;
 1869     } else if (streq(argv[argi], "--jquoteall")) {
 1870         pwriter_opts->json_quote_non_string_values = TRUE;
 1871         argi += 1;
 1872     } else if (streq(argv[argi], "--jvquoteall")) {
 1873         pwriter_opts->json_quote_non_string_values = TRUE;
 1874         argi += 1;
 1875 
 1876     } else if (streq(argv[argi], "--vflatsep")) {
 1877         check_arg_count(argv, argi, argc, 2);
 1878         pwriter_opts->oosvar_flatten_separator = cli_sep_from_arg(argv[argi+1]);
 1879         argi += 2;
 1880 
 1881     } else if (streq(argv[argi], "-o")) {
 1882         check_arg_count(argv, argi, argc, 2);
 1883         if (!lhmss_has_key(get_default_rses(), argv[argi+1])) {
 1884             fprintf(stderr, "%s: unrecognized output format \"%s\".\n",
 1885                 MLR_GLOBALS.bargv0, argv[argi+1]);
 1886             exit(1);
 1887         }
 1888         pwriter_opts->ofile_fmt = argv[argi+1];
 1889         argi += 2;
 1890 
 1891     } else if (streq(argv[argi], "--ocsv")) {
 1892         pwriter_opts->ofile_fmt = "csv";
 1893         argi += 1;
 1894 
 1895     } else if (streq(argv[argi], "--ocsvlite")) {
 1896         pwriter_opts->ofile_fmt = "csvlite";
 1897         argi += 1;
 1898 
 1899     } else if (streq(argv[argi], "--otsv")) {
 1900         pwriter_opts->ofile_fmt = "csv";
 1901         pwriter_opts->ofs = "\t";
 1902         argi += 1;
 1903 
 1904     } else if (streq(argv[argi], "--otsvlite")) {
 1905         pwriter_opts->ofile_fmt = "csvlite";
 1906         pwriter_opts->ofs = "\t";
 1907         argi += 1;
 1908 
 1909     } else if (streq(argv[argi], "--oasv")) {
 1910         pwriter_opts->ofile_fmt = "csv";
 1911         pwriter_opts->ofs = ASV_FS;
 1912         pwriter_opts->ors = ASV_RS;
 1913         argi += 1;
 1914 
 1915     } else if (streq(argv[argi], "--oasvlite")) {
 1916         pwriter_opts->ofile_fmt = "csvlite";
 1917         pwriter_opts->ofs = ASV_FS;
 1918         pwriter_opts->ors = ASV_RS;
 1919         argi += 1;
 1920 
 1921     } else if (streq(argv[argi], "--ousv")) {
 1922         pwriter_opts->ofile_fmt = "csv";
 1923         pwriter_opts->ofs = USV_FS;
 1924         pwriter_opts->ors = USV_RS;
 1925         argi += 1;
 1926 
 1927     } else if (streq(argv[argi], "--ousvlite")) {
 1928         pwriter_opts->ofile_fmt = "csvlite";
 1929         pwriter_opts->ofs = USV_FS;
 1930         pwriter_opts->ors = USV_RS;
 1931         argi += 1;
 1932 
 1933     } else if (streq(argv[argi], "--omd")) {
 1934         pwriter_opts->ofile_fmt = "markdown";
 1935         argi += 1;
 1936 
 1937     } else if (streq(argv[argi], "--odkvp")) {
 1938         pwriter_opts->ofile_fmt = "dkvp";
 1939         argi += 1;
 1940 
 1941     } else if (streq(argv[argi], "--ojson")) {
 1942         pwriter_opts->ofile_fmt = "json";
 1943         argi += 1;
 1944     } else if (streq(argv[argi], "--ojsonx")) {
 1945         pwriter_opts->ofile_fmt = "json";
 1946         pwriter_opts->stack_json_output_vertically = TRUE;
 1947         argi += 1;
 1948 
 1949     } else if (streq(argv[argi], "--onidx")) {
 1950         pwriter_opts->ofile_fmt = "nidx";
 1951         argi += 1;
 1952 
 1953     } else if (streq(argv[argi], "--oxtab")) {
 1954         pwriter_opts->ofile_fmt = "xtab";
 1955         argi += 1;
 1956 
 1957     } else if (streq(argv[argi], "--opprint")) {
 1958         pwriter_opts->ofile_fmt = "pprint";
 1959         argi += 1;
 1960 
 1961     } else if (streq(argv[argi], "--right")) {
 1962         pwriter_opts->right_align_pprint = TRUE;
 1963         argi += 1;
 1964 
 1965     } else if (streq(argv[argi], "--barred")) {
 1966         pwriter_opts->pprint_barred = TRUE;
 1967         argi += 1;
 1968 
 1969     } else if (streq(argv[argi], "--quote-all")) {
 1970         pwriter_opts->oquoting = QUOTE_ALL;
 1971         argi += 1;
 1972 
 1973     } else if (streq(argv[argi], "--quote-none")) {
 1974         pwriter_opts->oquoting = QUOTE_NONE;
 1975         argi += 1;
 1976 
 1977     } else if (streq(argv[argi], "--quote-minimal")) {
 1978         pwriter_opts->oquoting = QUOTE_MINIMAL;
 1979         argi += 1;
 1980 
 1981     } else if (streq(argv[argi], "--quote-numeric")) {
 1982         pwriter_opts->oquoting = QUOTE_NUMERIC;
 1983         argi += 1;
 1984 
 1985     } else if (streq(argv[argi], "--quote-original")) {
 1986         pwriter_opts->oquoting = QUOTE_ORIGINAL;
 1987         argi += 1;
 1988 
 1989     }
 1990     *pargi = argi;
 1991     return argi != oargi;
 1992 }
 1993 
 1994 // Returns TRUE if the current flag was handled.
 1995 int cli_handle_reader_writer_options(char** argv, int argc, int *pargi,
 1996     cli_reader_opts_t* preader_opts, cli_writer_opts_t* pwriter_opts)
 1997 {
 1998     int argi = *pargi;
 1999     int oargi = argi;
 2000 
 2001     if (streq(argv[argi], "--rs")) {
 2002         check_arg_count(argv, argi, argc, 2);
 2003         preader_opts->irs = cli_sep_from_arg(argv[argi+1]);
 2004         pwriter_opts->ors = cli_sep_from_arg(argv[argi+1]);
 2005         argi += 2;
 2006 
 2007     } else if (streq(argv[argi], "--fs")) {
 2008         check_arg_count(argv, argi, argc, 2);
 2009         preader_opts->ifs = cli_sep_from_arg(argv[argi+1]);
 2010         pwriter_opts->ofs = cli_sep_from_arg(argv[argi+1]);
 2011         argi += 2;
 2012 
 2013     } else if (streq(argv[argi], "-p")) {
 2014         preader_opts->ifile_fmt = "nidx";
 2015         pwriter_opts->ofile_fmt = "nidx";
 2016         preader_opts->ifs = " ";
 2017         pwriter_opts->ofs = " ";
 2018         preader_opts->allow_repeat_ifs = TRUE;
 2019         argi += 1;
 2020 
 2021     } else if (streq(argv[argi], "--ps")) {
 2022         check_arg_count(argv, argi, argc, 2);
 2023         preader_opts->ips = cli_sep_from_arg(argv[argi+1]);
 2024         pwriter_opts->ops = cli_sep_from_arg(argv[argi+1]);
 2025         argi += 2;
 2026 
 2027     } else if (streq(argv[argi], "--jflatsep")) {
 2028         check_arg_count(argv, argi, argc, 2);
 2029         preader_opts->input_json_flatten_separator  = cli_sep_from_arg(argv[argi+1]);
 2030         pwriter_opts->output_json_flatten_separator = cli_sep_from_arg(argv[argi+1]);
 2031         argi += 2;
 2032 
 2033     } else if (streq(argv[argi], "--io")) {
 2034         check_arg_count(argv, argi, argc, 2);
 2035         if (!lhmss_has_key(get_default_rses(), argv[argi+1])) {
 2036             fprintf(stderr, "%s: unrecognized I/O format \"%s\".\n",
 2037                 MLR_GLOBALS.bargv0, argv[argi+1]);
 2038             exit(1);
 2039         }
 2040         preader_opts->ifile_fmt = argv[argi+1];
 2041         pwriter_opts->ofile_fmt = argv[argi+1];
 2042         argi += 2;
 2043 
 2044     } else if (streq(argv[argi], "--csv")) {
 2045         preader_opts->ifile_fmt = "csv";
 2046         pwriter_opts->ofile_fmt = "csv";
 2047         argi += 1;
 2048 
 2049     } else if (streq(argv[argi], "--csvlite")) {
 2050         preader_opts->ifile_fmt = "csvlite";
 2051         pwriter_opts->ofile_fmt = "csvlite";
 2052         argi += 1;
 2053 
 2054     } else if (streq(argv[argi], "--tsv")) {
 2055         preader_opts->ifile_fmt = pwriter_opts->ofile_fmt = "csv";
 2056         preader_opts->ifs = "\t";
 2057         pwriter_opts->ofs = "\t";
 2058         argi += 1;
 2059 
 2060     } else if (streq(argv[argi], "--tsvlite") || streq(argv[argi], "-t")) {
 2061         preader_opts->ifile_fmt = pwriter_opts->ofile_fmt = "csvlite";
 2062         preader_opts->ifs = "\t";
 2063         pwriter_opts->ofs = "\t";
 2064         argi += 1;
 2065 
 2066     } else if (streq(argv[argi], "--asv")) {
 2067         preader_opts->ifile_fmt = pwriter_opts->ofile_fmt = "csv";
 2068         preader_opts->ifs = ASV_FS;
 2069         pwriter_opts->ofs = ASV_FS;
 2070         preader_opts->irs = ASV_RS;
 2071         pwriter_opts->ors = ASV_RS;
 2072         argi += 1;
 2073 
 2074     } else if (streq(argv[argi], "--asvlite")) {
 2075         preader_opts->ifile_fmt = pwriter_opts->ofile_fmt = "csvlite";
 2076         preader_opts->ifs = ASV_FS;
 2077         pwriter_opts->ofs = ASV_FS;
 2078         preader_opts->irs = ASV_RS;
 2079         pwriter_opts->ors = ASV_RS;
 2080         argi += 1;
 2081 
 2082     } else if (streq(argv[argi], "--usv")) {
 2083         preader_opts->ifile_fmt = pwriter_opts->ofile_fmt = "csv";
 2084         preader_opts->ifs = USV_FS;
 2085         pwriter_opts->ofs = USV_FS;
 2086         preader_opts->irs = USV_RS;
 2087         pwriter_opts->ors = USV_RS;
 2088         argi += 1;
 2089 
 2090     } else if (streq(argv[argi], "--usvlite")) {
 2091         preader_opts->ifile_fmt = pwriter_opts->ofile_fmt = "csvlite";
 2092         preader_opts->ifs = USV_FS;
 2093         pwriter_opts->ofs = USV_FS;
 2094         preader_opts->irs = USV_RS;
 2095         pwriter_opts->ors = USV_RS;
 2096         argi += 1;
 2097 
 2098     } else if (streq(argv[argi], "--dkvp")) {
 2099         preader_opts->ifile_fmt = "dkvp";
 2100         pwriter_opts->ofile_fmt = "dkvp";
 2101         argi += 1;
 2102 
 2103     } else if (streq(argv[argi], "--json")) {
 2104         preader_opts->ifile_fmt = "json";
 2105         pwriter_opts->ofile_fmt = "json";
 2106         argi += 1;
 2107     } else if (streq(argv[argi], "--jsonx")) {
 2108         preader_opts->ifile_fmt = "json";
 2109         pwriter_opts->ofile_fmt = "json";
 2110         pwriter_opts->stack_json_output_vertically = TRUE;
 2111         argi += 1;
 2112 
 2113     } else if (streq(argv[argi], "--nidx")) {
 2114         preader_opts->ifile_fmt = "nidx";
 2115         pwriter_opts->ofile_fmt = "nidx";
 2116         argi += 1;
 2117 
 2118     } else if (streq(argv[argi], "-T")) {
 2119         preader_opts->ifile_fmt = "nidx";
 2120         pwriter_opts->ofile_fmt = "nidx";
 2121         preader_opts->ifs = "\t";
 2122         pwriter_opts->ofs = "\t";
 2123         argi += 1;
 2124 
 2125     } else if (streq(argv[argi], "--xtab")) {
 2126         preader_opts->ifile_fmt = "xtab";
 2127         pwriter_opts->ofile_fmt = "xtab";
 2128         argi += 1;
 2129 
 2130     } else if (streq(argv[argi], "--pprint")) {
 2131         preader_opts->ifile_fmt        = "csvlite";
 2132         preader_opts->ifs              = " ";
 2133         preader_opts->allow_repeat_ifs = TRUE;
 2134         pwriter_opts->ofile_fmt        = "pprint";
 2135         argi += 1;
 2136 
 2137     } else if (streq(argv[argi], "--c2t")) {
 2138         preader_opts->ifile_fmt = "csv";
 2139         preader_opts->irs       = "auto";
 2140         pwriter_opts->ofile_fmt = "csv";
 2141         pwriter_opts->ors       = "auto";
 2142         pwriter_opts->ofs       = "\t";
 2143         argi += 1;
 2144     } else if (streq(argv[argi], "--c2d")) {
 2145         preader_opts->ifile_fmt = "csv";
 2146         preader_opts->irs       = "auto";
 2147         pwriter_opts->ofile_fmt = "dkvp";
 2148         argi += 1;
 2149     } else if (streq(argv[argi], "--c2n")) {
 2150         preader_opts->ifile_fmt = "csv";
 2151         preader_opts->irs       = "auto";
 2152         pwriter_opts->ofile_fmt = "nidx";
 2153         argi += 1;
 2154     } else if (streq(argv[argi], "--c2j")) {
 2155         preader_opts->ifile_fmt = "csv";
 2156         preader_opts->irs       = "auto";
 2157         pwriter_opts->ofile_fmt = "json";
 2158         argi += 1;
 2159     } else if (streq(argv[argi], "--c2p")) {
 2160         preader_opts->ifile_fmt = "csv";
 2161         preader_opts->irs       = "auto";
 2162         pwriter_opts->ofile_fmt = "pprint";
 2163         argi += 1;
 2164     } else if (streq(argv[argi], "--c2x")) {
 2165         preader_opts->ifile_fmt = "csv";
 2166         preader_opts->irs       = "auto";
 2167         pwriter_opts->ofile_fmt = "xtab";
 2168         argi += 1;
 2169     } else if (streq(argv[argi], "--c2m")) {
 2170         preader_opts->ifile_fmt = "csv";
 2171         preader_opts->irs       = "auto";
 2172         pwriter_opts->ofile_fmt = "markdown";
 2173         argi += 1;
 2174 
 2175     } else if (streq(argv[argi], "--t2c")) {
 2176         preader_opts->ifile_fmt = "csv";
 2177         preader_opts->ifs       = "\t";
 2178         preader_opts->irs       = "auto";
 2179         pwriter_opts->ofile_fmt = "csv";
 2180         pwriter_opts->ors       = "auto";
 2181         argi += 1;
 2182     } else if (streq(argv[argi], "--t2d")) {
 2183         preader_opts->ifile_fmt = "csv";
 2184         preader_opts->ifs       = "\t";
 2185         preader_opts->irs       = "auto";
 2186         pwriter_opts->ofile_fmt = "dkvp";
 2187         argi += 1;
 2188     } else if (streq(argv[argi], "--t2n")) {
 2189         preader_opts->ifile_fmt = "csv";
 2190         preader_opts->ifs       = "\t";
 2191         preader_opts->irs       = "auto";
 2192         pwriter_opts->ofile_fmt = "nidx";
 2193         argi += 1;
 2194     } else if (streq(argv[argi], "--t2j")) {
 2195         preader_opts->ifile_fmt = "csv";
 2196         preader_opts->ifs       = "\t";
 2197         preader_opts->irs       = "auto";
 2198         pwriter_opts->ofile_fmt = "json";
 2199         argi += 1;
 2200     } else if (streq(argv[argi], "--t2p")) {
 2201         preader_opts->ifile_fmt = "csv";
 2202         preader_opts->ifs       = "\t";
 2203         preader_opts->irs       = "auto";
 2204         pwriter_opts->ofile_fmt = "pprint";
 2205         argi += 1;
 2206     } else if (streq(argv[argi], "--t2x")) {
 2207         preader_opts->ifile_fmt = "csv";
 2208         preader_opts->ifs       = "\t";
 2209         preader_opts->irs       = "auto";
 2210         pwriter_opts->ofile_fmt = "xtab";
 2211         argi += 1;
 2212     } else if (streq(argv[argi], "--t2m")) {
 2213         preader_opts->ifile_fmt = "csv";
 2214         preader_opts->ifs       = "\t";
 2215         preader_opts->irs       = "auto";
 2216         pwriter_opts->ofile_fmt = "markdown";
 2217         argi += 1;
 2218 
 2219     } else if (streq(argv[argi], "--d2c")) {
 2220         preader_opts->ifile_fmt = "dkvp";
 2221         pwriter_opts->ofile_fmt = "csv";
 2222         pwriter_opts->ors       = "auto";
 2223         argi += 1;
 2224     } else if (streq(argv[argi], "--d2t")) {
 2225         preader_opts->ifile_fmt = "dkvp";
 2226         pwriter_opts->ofile_fmt = "csv";
 2227         pwriter_opts->ors       = "auto";
 2228         pwriter_opts->ofs       = "\t";
 2229         argi += 1;
 2230     } else if (streq(argv[argi], "--d2n")) {
 2231         preader_opts->ifile_fmt = "dkvp";
 2232         pwriter_opts->ofile_fmt = "nidx";
 2233         argi += 1;
 2234     } else if (streq(argv[argi], "--d2j")) {
 2235         preader_opts->ifile_fmt = "dkvp";
 2236         pwriter_opts->ofile_fmt = "json";
 2237         argi += 1;
 2238     } else if (streq(argv[argi], "--d2p")) {
 2239         preader_opts->ifile_fmt = "dkvp";
 2240         pwriter_opts->ofile_fmt = "pprint";
 2241         argi += 1;
 2242     } else if (streq(argv[argi], "--d2x")) {
 2243         preader_opts->ifile_fmt = "dkvp";
 2244         pwriter_opts->ofile_fmt = "xtab";
 2245         argi += 1;
 2246     } else if (streq(argv[argi], "--d2m")) {
 2247         preader_opts->ifile_fmt = "dkvp";
 2248         pwriter_opts->ofile_fmt = "markdown";
 2249         argi += 1;
 2250 
 2251     } else if (streq(argv[argi], "--n2c")) {
 2252         preader_opts->ifile_fmt = "nidx";
 2253         pwriter_opts->ofile_fmt = "csv";
 2254         pwriter_opts->ors       = "auto";
 2255         argi += 1;
 2256     } else if (streq(argv[argi], "--n2t")) {
 2257         preader_opts->ifile_fmt = "nidx";
 2258         pwriter_opts->ofile_fmt = "csv";
 2259         pwriter_opts->ors       = "auto";
 2260         pwriter_opts->ofs       = "\t";
 2261         argi += 1;
 2262     } else if (streq(argv[argi], "--n2d")) {
 2263         preader_opts->ifile_fmt = "nidx";
 2264         pwriter_opts->ofile_fmt = "dkvp";
 2265         argi += 1;
 2266     } else if (streq(argv[argi], "--n2j")) {
 2267         preader_opts->ifile_fmt = "nidx";
 2268         pwriter_opts->ofile_fmt = "json";
 2269         argi += 1;
 2270     } else if (streq(argv[argi], "--n2p")) {
 2271         preader_opts->ifile_fmt = "nidx";
 2272         pwriter_opts->ofile_fmt = "pprint";
 2273         argi += 1;
 2274     } else if (streq(argv[argi], "--n2x")) {
 2275         preader_opts->ifile_fmt = "nidx";
 2276         pwriter_opts->ofile_fmt = "xtab";
 2277         argi += 1;
 2278     } else if (streq(argv[argi], "--n2m")) {
 2279         preader_opts->ifile_fmt = "nidx";
 2280         pwriter_opts->ofile_fmt = "markdown";
 2281         argi += 1;
 2282 
 2283     } else if (streq(argv[argi], "--j2c")) {
 2284         preader_opts->ifile_fmt = "json";
 2285         pwriter_opts->ofile_fmt = "csv";
 2286         pwriter_opts->ors       = "auto";
 2287         argi += 1;
 2288     } else if (streq(argv[argi], "--j2t")) {
 2289         preader_opts->ifile_fmt = "json";
 2290         pwriter_opts->ofile_fmt = "csv";
 2291         pwriter_opts->ors       = "auto";
 2292         pwriter_opts->ofs       = "\t";
 2293         argi += 1;
 2294     } else if (streq(argv[argi], "--j2d")) {
 2295         preader_opts->ifile_fmt = "json";
 2296         pwriter_opts->ofile_fmt = "dkvp";
 2297         argi += 1;
 2298     } else if (streq(argv[argi], "--j2n")) {
 2299         preader_opts->ifile_fmt = "json";
 2300         pwriter_opts->ofile_fmt = "nidx";
 2301         argi += 1;
 2302     } else if (streq(argv[argi], "--j2p")) {
 2303         preader_opts->ifile_fmt = "json";
 2304         pwriter_opts->ofile_fmt = "pprint";
 2305         argi += 1;
 2306     } else if (streq(argv[argi], "--j2x")) {
 2307         preader_opts->ifile_fmt = "json";
 2308         pwriter_opts->ofile_fmt = "xtab";
 2309         argi += 1;
 2310     } else if (streq(argv[argi], "--j2m")) {
 2311         preader_opts->ifile_fmt = "json";
 2312         pwriter_opts->ofile_fmt = "markdown";
 2313         argi += 1;
 2314 
 2315     } else if (streq(argv[argi], "--p2c")) {
 2316         preader_opts->ifile_fmt        = "csvlite";
 2317         preader_opts->ifs              = " ";
 2318         preader_opts->allow_repeat_ifs = TRUE;
 2319         pwriter_opts->ofile_fmt        = "csv";
 2320         pwriter_opts->ors              = "auto";
 2321         argi += 1;
 2322     } else if (streq(argv[argi], "--p2t")) {
 2323         preader_opts->ifile_fmt        = "csvlite";
 2324         preader_opts->ifs              = " ";
 2325         preader_opts->allow_repeat_ifs = TRUE;
 2326         pwriter_opts->ofile_fmt        = "csv";
 2327         pwriter_opts->ors              = "auto";
 2328         pwriter_opts->ofs              = "\t";
 2329         argi += 1;
 2330     } else if (streq(argv[argi], "--p2d")) {
 2331         preader_opts->ifile_fmt        = "csvlite";
 2332         preader_opts->ifs              = " ";
 2333         preader_opts->allow_repeat_ifs = TRUE;
 2334         pwriter_opts->ofile_fmt        = "dkvp";
 2335         argi += 1;
 2336     } else if (streq(argv[argi], "--p2n")) {
 2337         preader_opts->ifile_fmt        = "csvlite";
 2338         preader_opts->ifs              = " ";
 2339         preader_opts->allow_repeat_ifs = TRUE;
 2340         pwriter_opts->ofile_fmt        = "nidx";
 2341         argi += 1;
 2342     } else if (streq(argv[argi], "--p2j")) {
 2343         preader_opts->ifile_fmt        = "csvlite";
 2344         preader_opts->ifs              = " ";
 2345         preader_opts->allow_repeat_ifs = TRUE;
 2346         pwriter_opts->ofile_fmt = "json";
 2347         argi += 1;
 2348     } else if (streq(argv[argi], "--p2x")) {
 2349         preader_opts->ifile_fmt        = "csvlite";
 2350         preader_opts->ifs              = " ";
 2351         preader_opts->allow_repeat_ifs = TRUE;
 2352         pwriter_opts->ofile_fmt        = "xtab";
 2353         argi += 1;
 2354     } else if (streq(argv[argi], "--p2m")) {
 2355         preader_opts->ifile_fmt        = "csvlite";
 2356         preader_opts->ifs              = " ";
 2357         preader_opts->allow_repeat_ifs = TRUE;
 2358         pwriter_opts->ofile_fmt        = "markdown";
 2359         argi += 1;
 2360 
 2361     } else if (streq(argv[argi], "--x2c")) {
 2362         preader_opts->ifile_fmt = "xtab";
 2363         pwriter_opts->ofile_fmt = "csv";
 2364         pwriter_opts->ors       = "auto";
 2365         argi += 1;
 2366     } else if (streq(argv[argi], "--x2t")) {
 2367         preader_opts->ifile_fmt = "xtab";
 2368         pwriter_opts->ofile_fmt = "csv";
 2369         pwriter_opts->ors       = "auto";
 2370         pwriter_opts->ofs       = "\t";
 2371         argi += 1;
 2372     } else if (streq(argv[argi], "--x2d")) {
 2373         preader_opts->ifile_fmt = "xtab";
 2374         pwriter_opts->ofile_fmt = "dkvp";
 2375         argi += 1;
 2376     } else if (streq(argv[argi], "--x2n")) {
 2377         preader_opts->ifile_fmt = "xtab";
 2378         pwriter_opts->ofile_fmt = "nidx";
 2379         argi += 1;
 2380     } else if (streq(argv[argi], "--x2j")) {
 2381         preader_opts->ifile_fmt = "xtab";
 2382         pwriter_opts->ofile_fmt = "json";
 2383         argi += 1;
 2384     } else if (streq(argv[argi], "--x2p")) {
 2385         preader_opts->ifile_fmt = "xtab";
 2386         pwriter_opts->ofile_fmt = "pprint";
 2387         argi += 1;
 2388     } else if (streq(argv[argi], "--x2m")) {
 2389         preader_opts->ifile_fmt = "xtab";
 2390         pwriter_opts->ofile_fmt = "markdown";
 2391         argi += 1;
 2392 
 2393     } else if (streq(argv[argi], "-N")) {
 2394         preader_opts->use_implicit_csv_header = TRUE;
 2395         pwriter_opts->headerless_csv_output = TRUE;
 2396         argi += 1;
 2397     }
 2398     *pargi = argi;
 2399     return argi != oargi;
 2400 }
 2401 
 2402 // Returns TRUE if the current flag was handled.
 2403 static int cli_handle_misc_options(char** argv, int argc, int *pargi, cli_opts_t* popts) {
 2404     int argi = *pargi;
 2405     int oargi = argi;
 2406 
 2407     if (streq(argv[argi], "-I")) {
 2408         popts->do_in_place = TRUE;
 2409         argi += 1;
 2410 
 2411     } else if (streq(argv[argi], "-n")) {
 2412         popts->no_input = TRUE;
 2413         argi += 1;
 2414 
 2415     } else if (streq(argv[argi], "--from")) {
 2416         check_arg_count(argv, argi, argc, 2);
 2417         slls_append(popts->filenames, argv[argi+1], NO_FREE);
 2418         argi += 2;
 2419 
 2420     } else if (streq(argv[argi], "--ofmt")) {
 2421         check_arg_count(argv, argi, argc, 2);
 2422         popts->ofmt = argv[argi+1];
 2423         argi += 2;
 2424 
 2425     } else if (streq(argv[argi], "--nr-progress-mod")) {
 2426         check_arg_count(argv, argi, argc, 2);
 2427         if (sscanf(argv[argi+1], "%lld", &popts->nr_progress_mod) != 1) {
 2428             fprintf(stderr,
 2429                 "%s: --nr-progress-mod argument must be a positive integer; got \"%s\".\n",
 2430                 MLR_GLOBALS.bargv0, argv[argi+1]);
 2431             main_usage_short(stderr, MLR_GLOBALS.bargv0);
 2432             exit(1);
 2433         }
 2434         if (popts->nr_progress_mod <= 0) {
 2435             fprintf(stderr,
 2436                 "%s: --nr-progress-mod argument must be a positive integer; got \"%s\".\n",
 2437                 MLR_GLOBALS.bargv0, argv[argi+1]);
 2438             main_usage_short(stderr, MLR_GLOBALS.bargv0);
 2439             exit(1);
 2440         }
 2441         argi += 2;
 2442 
 2443     } else if (streq(argv[argi], "--seed")) {
 2444         check_arg_count(argv, argi, argc, 2);
 2445         if (sscanf(argv[argi+1], "0x%x", &popts->rand_seed) == 1) {
 2446             popts->have_rand_seed = TRUE;
 2447         } else if (sscanf(argv[argi+1], "%u", &popts->rand_seed) == 1) {
 2448             popts->have_rand_seed = TRUE;
 2449         } else {
 2450             fprintf(stderr,
 2451                 "%s: --seed argument must be a decimal or hexadecimal integer; got \"%s\".\n",
 2452                 MLR_GLOBALS.bargv0, argv[argi+1]);
 2453             main_usage_short(stderr, MLR_GLOBALS.bargv0);
 2454             exit(1);
 2455         }
 2456         argi += 2;
 2457 
 2458     }
 2459     *pargi = argi;
 2460     return argi != oargi;
 2461 }
 2462 
 2463 // ----------------------------------------------------------------
 2464 static char* lhmss_get_or_die(lhmss_t* pmap, char* key) {
 2465     char* value = lhmss_get(pmap, key);
 2466     MLR_INTERNAL_CODING_ERROR_IF(value == NULL);
 2467     return value;
 2468 }
 2469 
 2470 // ----------------------------------------------------------------
 2471 static int lhmsll_get_or_die(lhmsll_t* pmap, char* key) {
 2472     MLR_INTERNAL_CODING_ERROR_UNLESS(lhmsll_has_key(pmap, key));
 2473     return lhmsll_get(pmap, key);
 2474 }