Пример #1
0
// ----------------------------------------------------------------
ex_ast_node_t* ex_ast_node_alloc_binary(char* text, ex_ast_node_type_t type,
	ex_ast_node_t* pa, ex_ast_node_t* pb)
{
	ex_ast_node_t* pnode = ex_ast_node_alloc(text, type);
	pnode->pchildren = sllv_alloc();
	sllv_append(pnode->pchildren, pa);
	sllv_append(pnode->pchildren, pb);
	return pnode;
}
Пример #2
0
// ----------------------------------------------------------------
mlr_dsl_ast_node_t* mlr_dsl_ast_node_alloc_binary(char* text, int type,
	mlr_dsl_ast_node_t* pa, mlr_dsl_ast_node_t* pb)
{
	mlr_dsl_ast_node_t* pnode = mlr_dsl_ast_node_alloc(text, type);
	pnode->pchildren = sllv_alloc();
	sllv_append(pnode->pchildren, pa);
	sllv_append(pnode->pchildren, pb);
	return pnode;
}
Пример #3
0
// ----------------------------------------------------------------
static void lrec_writer_pprint_process(FILE* output_stream, lrec_t* prec, void* pvstate) {
	lrec_writer_pprint_state_t* pstate = pvstate;

	int drain = FALSE;

	if (prec == NULL) {
		drain = TRUE;
	} else {
		if (pstate->pprev_keys != NULL && !lrec_keys_equal_list(prec, pstate->pprev_keys)) {
			drain = TRUE;
		}
	}

	if (drain) {
		if (pstate->num_blocks_written > 0LL) // separate blocks with empty line
			fputs(pstate->ors, output_stream);
		print_and_free_record_list(pstate->precords, output_stream, pstate->ors, pstate->ofs, pstate->left_align);
		if (pstate->pprev_keys != NULL) {
			slls_free(pstate->pprev_keys);
			pstate->pprev_keys = NULL;
		}
		pstate->precords = sllv_alloc();
		pstate->num_blocks_written++;
	}
	if (prec != NULL) {
		sllv_append(pstate->precords, prec);
		if (pstate->pprev_keys == NULL)
			pstate->pprev_keys = mlr_copy_keys_from_record(prec);
	}
}
static mlr_dsl_cst_statement_t* alloc_if_item(mlr_dsl_cst_t* pcst,
	mlr_dsl_ast_node_t* pitemnode, mlr_dsl_ast_node_t* pexprnode, mlr_dsl_ast_node_t* plistnode,
	int type_inferencing, int context_flags)
{
	if_item_state_t* pstate = mlr_malloc_or_die(sizeof(if_item_state_t));

	MLR_INTERNAL_CODING_ERROR_IF(plistnode->subframe_var_count == MD_UNUSED_INDEX);
	cst_statement_block_t* pblock = cst_statement_block_alloc(plistnode->subframe_var_count);

	for (sllve_t* pe = plistnode->pchildren->phead; pe != NULL; pe = pe->pnext) {
		mlr_dsl_ast_node_t* pbody_ast_node = pe->pvvalue;
		mlr_dsl_cst_statement_t *pchild_statement = mlr_dsl_cst_alloc_statement(pcst, pbody_ast_node,
			type_inferencing, context_flags);
		sllv_append(pblock->pstatements, pchild_statement);
	}

	pstate->pexpression_evaluator = pexprnode != NULL
		? rval_evaluator_alloc_from_ast(pexprnode, pcst->pfmgr,
			type_inferencing, context_flags) // if-statement or elif-statement
		: rval_evaluator_alloc_from_boolean(TRUE); // else-statement

	return mlr_dsl_cst_statement_valloc_with_block(
		pitemnode,
		NULL, // handled by the containing if-head evaluator
		pblock,
		NULL, // handled by the containing if-head evaluator
		free_if_item,
		pstate);
}
// ----------------------------------------------------------------
mlr_dsl_cst_statement_t* alloc_do_while(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
	int type_inferencing, int context_flags)
{
	do_while_state_t* pstate = mlr_malloc_or_die(sizeof(do_while_state_t));

	pstate->pexpression_evaluator = NULL;

	// Left child node is the list of statements in the body.
	// Right child node is the AST for the boolean expression.
	mlr_dsl_ast_node_t* pleft  = pnode->pchildren->phead->pvvalue;
	mlr_dsl_ast_node_t* pright = pnode->pchildren->phead->pnext->pvvalue;

	MLR_INTERNAL_CODING_ERROR_IF(pleft->subframe_var_count == MD_UNUSED_INDEX);
	cst_statement_block_t* pblock = cst_statement_block_alloc(pright->subframe_var_count);

	for (sllve_t* pe = pleft->pchildren->phead; pe != NULL; pe = pe->pnext) {
		mlr_dsl_ast_node_t* pbody_ast_node = pe->pvvalue;
		mlr_dsl_cst_statement_t *pchild_statement = mlr_dsl_cst_alloc_statement(pcst, pbody_ast_node,
			type_inferencing, context_flags);
		sllv_append(pblock->pstatements, pchild_statement);
	}

	pstate->pexpression_evaluator = rval_evaluator_alloc_from_ast(
		pright, pcst->pfmgr, type_inferencing, context_flags);

	return mlr_dsl_cst_statement_valloc_with_block(
		pnode,
		handle_do_while,
		pblock,
		mlr_dsl_cst_handle_statement_block_with_break_continue,
		free_do_while,
		pstate);
}
Пример #6
0
// ----------------------------------------------------------------
mlr_dsl_ast_node_t* mlr_dsl_ast_node_append_arg(
	mlr_dsl_ast_node_t* pa, mlr_dsl_ast_node_t* pb)
{
	if (pa->pchildren == NULL)
		pa->pchildren = sllv_alloc();
	sllv_append(pa->pchildren, pb);
	return pa;
}
Пример #7
0
ex_ast_node_t* ex_ast_node_append_arg(
	ex_ast_node_t* pa, ex_ast_node_t* pb)
{
	if (pa->pchildren == NULL)
		pa->pchildren = sllv_alloc();
	sllv_append(pa->pchildren, pb);
	return pa;
}
Пример #8
0
// ----------------------------------------------------------------
static sllv_t* make_records_het() {
	sllv_t* precords = sllv_alloc();
	sllv_append(precords, lrec_literal_2("x","100", "b","10"));
	sllv_append(precords, lrec_literal_2("l","1",   "b","11"));
	sllv_append(precords, lrec_literal_2("l","1",   "b","12"));
	sllv_append(precords, lrec_literal_2("x","200", "b","13"));
	sllv_append(precords, lrec_literal_2("l","3",   "b","14"));
	sllv_append(precords, lrec_literal_2("l","3",   "b","15"));
	sllv_append(precords, lrec_literal_2("x","300", "b","16"));
	sllv_append(precords, lrec_literal_2("l","5",   "b","17"));
	sllv_append(precords, lrec_literal_2("l","5",   "b","18"));

	return precords;
}
Пример #9
0
// pnode is input; pkeylist_evaluators is appended to.
sllv_t* allocate_keylist_evaluators_from_ast_node(
	mlr_dsl_ast_node_t* pnode, fmgr_t* pfmgr, int type_inferencing, int context_flags)
{
	sllv_t* pkeylist_evaluators = sllv_alloc();

	if (pnode->pchildren != NULL) { // Non-indexed localvars have no child nodes in the AST.
		for (sllve_t* pe = pnode->pchildren->phead; pe != NULL; pe = pe->pnext) {
			mlr_dsl_ast_node_t* pkeynode = pe->pvvalue;
			if (pkeynode->type == MD_AST_NODE_TYPE_STRING_LITERAL) {
				sllv_append(pkeylist_evaluators, rval_evaluator_alloc_from_string(pkeynode->text));
			} else {
				sllv_append(pkeylist_evaluators, rval_evaluator_alloc_from_ast(pkeynode, pfmgr,
					type_inferencing, context_flags));
			}
		}
	}

	return pkeylist_evaluators;
}
Пример #10
0
// ----------------------------------------------------------------
ex_ast_node_t* ex_ast_tree_copy(ex_ast_node_t* pold) {
	ex_ast_node_t* pnew = ex_ast_node_copy(pold);
	if (pold->pchildren != NULL) {
		pnew->pchildren = sllv_alloc();
		for (sllve_t* pe = pold->pchildren->phead; pe != NULL; pe = pe->pnext) {
			ex_ast_node_t* pchild = pe->pvvalue;
			sllv_append(pnew->pchildren, ex_ast_tree_copy(pchild));
		}
	}
	return pnew;
}
Пример #11
0
// ----------------------------------------------------------------
static sllv_t* make_records_113335() {
	sllv_t* precords = sllv_alloc();
	sllv_append(precords, lrec_literal_2("l","1", "b","10"));
	sllv_append(precords, lrec_literal_2("l","1", "b","11"));
	sllv_append(precords, lrec_literal_2("l","3", "b","12"));
	sllv_append(precords, lrec_literal_2("l","3", "b","13"));
	sllv_append(precords, lrec_literal_2("l","3", "b","14"));
	sllv_append(precords, lrec_literal_2("l","5", "b","15"));
	return precords;
}
Пример #12
0
// ----------------------------------------------------------------
static char* test_sllv_append() {
	mu_assert_lf(0 == 0);

	sllv_t* pa = sllv_alloc();
	sllv_add(pa, "a");
	sllv_add(pa, "b");
	sllv_add(pa, "c");
	mu_assert_lf(pa->length == 3);

	sllve_t* pe = pa->phead;

	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "a")); pe = pe->pnext;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "b")); pe = pe->pnext;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "c")); pe = pe->pnext;
	mu_assert_lf(pe == NULL);

	sllv_t* pb = sllv_alloc();
	sllv_add(pb, "d");
	sllv_add(pb, "e");
	mu_assert_lf(pb->length == 2);

	pe = pb->phead;

	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "d")); pe = pe->pnext;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "e")); pe = pe->pnext;
	mu_assert_lf(pe == NULL);

	pa = sllv_append(pa, pb);

	mu_assert_lf(pa->length == 5);
	mu_assert_lf(pb->length == 2);

	pe = pa->phead;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "a")); pe = pe->pnext;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "b")); pe = pe->pnext;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "c")); pe = pe->pnext;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "d")); pe = pe->pnext;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "e")); pe = pe->pnext;
	mu_assert_lf(pe == NULL);

	pe = pb->phead;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "d")); pe = pe->pnext;
	mu_assert_lf(pe != NULL); mu_assert_lf(streq(pe->pvdata, "e")); pe = pe->pnext;
	mu_assert_lf(pe == NULL);

	return NULL;
}
Пример #13
0
static sllv_t* chain_map(lrec_t* pinrec, context_t* pctx, sllve_t* pmapper_list_head) {
	mapper_t* pmapper = pmapper_list_head->pvdata;
	sllv_t* outrecs = pmapper->pprocess_func(pinrec, pctx, pmapper->pvstate);
	if (pmapper_list_head->pnext == NULL) {
		return outrecs;
	} else if (outrecs == NULL) { // xxx cmt
		return NULL;
	} else {
		sllv_t* nextrecs = sllv_alloc();

		for (sllve_t* pe = outrecs->phead; pe != NULL; pe = pe->pnext) {
			lrec_t* poutrec = pe->pvdata;
			sllv_t* nextrecsi = chain_map(poutrec, pctx, pmapper_list_head->pnext);
			nextrecs = sllv_append(nextrecs, nextrecsi);
		}
		sllv_free(outrecs);

		return nextrecs;
	}
}
mlr_dsl_cst_statement_t* alloc_if_head(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
	int type_inferencing, int context_flags)
{
	if_head_state_t* pstate = mlr_malloc_or_die(sizeof(if_head_state_t));

	pstate->pif_chain_statements = sllv_alloc();

	for (sllve_t* pe = pnode->pchildren->phead; pe != NULL; pe = pe->pnext) {
		// For if and elif:
		// * Left subnode is the AST for the boolean expression.
		// * Right subnode is a list of statements to be executed if the left evaluates to true.
		// For else:
		// * Sole subnode is a list of statements to be executed.
		mlr_dsl_ast_node_t* pitemnode = pe->pvvalue;
		mlr_dsl_ast_node_t* pexprnode = NULL;
		mlr_dsl_ast_node_t* plistnode = NULL;
		if (pitemnode->pchildren->length == 2) {
			pexprnode = pitemnode->pchildren->phead->pvvalue;
			plistnode = pitemnode->pchildren->phead->pnext->pvvalue;
		} else {
			pexprnode = NULL;
			plistnode = pitemnode->pchildren->phead->pvvalue;
		}

		sllv_append(pstate->pif_chain_statements,
			alloc_if_item(pcst, pitemnode, pexprnode, plistnode, type_inferencing, context_flags)
		);
	}

	mlr_dsl_cst_block_handler_t* pblock_handler = (context_flags & IN_BREAKABLE)
		?  mlr_dsl_cst_handle_statement_block_with_break_continue
		: mlr_dsl_cst_handle_statement_block;

	return mlr_dsl_cst_statement_valloc_with_block(
		pnode,
		handle_if_head,
		NULL,
		pblock_handler,
		free_if_head,
		pstate);
}
// ----------------------------------------------------------------
mlr_dsl_cst_statement_t* alloc_conditional_block(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
	int type_inferencing, int context_flags)
{
	conditional_block_state_t* pstate = mlr_malloc_or_die(sizeof(conditional_block_state_t));

	pstate->pexpression_evaluator = NULL;

	// Right node is a list of statements to be executed if the left evaluates to true.
	mlr_dsl_ast_node_t* pleft  = pnode->pchildren->phead->pvvalue;
	mlr_dsl_ast_node_t* pright = pnode->pchildren->phead->pnext->pvvalue;

	pstate->pexpression_evaluator = rval_evaluator_alloc_from_ast(
		pleft, pcst->pfmgr, type_inferencing, context_flags);

	MLR_INTERNAL_CODING_ERROR_IF(pright->subframe_var_count == MD_UNUSED_INDEX);
	cst_statement_block_t* pblock = cst_statement_block_alloc(pright->subframe_var_count);

	for (sllve_t* pe = pright->pchildren->phead; pe != NULL; pe = pe->pnext) {
		mlr_dsl_ast_node_t* pbody_ast_node = pe->pvvalue;
		mlr_dsl_cst_statement_t *pchild_statement = mlr_dsl_cst_alloc_statement(pcst, pbody_ast_node,
			type_inferencing, context_flags);
		sllv_append(pblock->pstatements, pchild_statement);
	}

	mlr_dsl_cst_block_handler_t* pblock_handler = (context_flags & IN_BREAKABLE)
		? mlr_dsl_cst_handle_statement_block_with_break_continue
		: mlr_dsl_cst_handle_statement_block;

	return mlr_dsl_cst_statement_valloc_with_block(
		pnode,
		handle_conditional_block,
		pblock,
		pblock_handler,
		free_conditional_block,
		pstate);
}
Пример #16
0
// ----------------------------------------------------------------
mlr_dsl_cst_statement_t* alloc_unset(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
	int type_inferencing, int context_flags)
{
	unset_state_t* pstate = mlr_malloc_or_die(sizeof(unset_state_t));

	pstate->punset_items = sllv_alloc();

	mlr_dsl_cst_statement_handler_t* pstatement_handler = handle_unset;
	for (sllve_t* pe = pnode->pchildren->phead; pe != NULL; pe = pe->pnext) {
		mlr_dsl_ast_node_t* pchild = pe->pvvalue;

		if (pchild->type == MD_AST_NODE_TYPE_ALL || pchild->type == MD_AST_NODE_TYPE_FULL_OOSVAR) {
			// The grammar allows only 'unset all', not 'unset @x, all, $y'.
			// So if 'all' appears at all, it's the only name. Likewise with '@*'.
			pstatement_handler = handle_unset_all;

		} else if (pchild->type == MD_AST_NODE_TYPE_FULL_SREC) {
			if (context_flags & IN_BEGIN_OR_END) {
				fprintf(stderr, "%s: unset of $-variables is not valid within begin or end blocks.\n",
					MLR_GLOBALS.bargv0);
				exit(1);
			}
			unset_item_t* punset_item = alloc_blank_unset_item();
			punset_item->punset_item_handler = handle_unset_full_srec;
			sllv_append(pstate->punset_items, punset_item);

		} else if (pchild->type == MD_AST_NODE_TYPE_FIELD_NAME) {
			if (context_flags & IN_BEGIN_OR_END) {
				fprintf(stderr, "%s: unset of $-variables is not valid within begin or end blocks.\n",
					MLR_GLOBALS.bargv0);
				exit(1);
			}
			unset_item_t* punset_item = alloc_blank_unset_item();
			punset_item->punset_item_handler = handle_unset_srec_field_name;
			punset_item->srec_field_name = pchild->text;
			sllv_append(pstate->punset_items, punset_item);

		} else if (pchild->type == MD_AST_NODE_TYPE_INDIRECT_FIELD_NAME) {
			if (context_flags & IN_BEGIN_OR_END) {
				fprintf(stderr, "%s: unset of $-variables are not valid within begin or end blocks.\n",
					MLR_GLOBALS.bargv0);
				exit(1);
			}
			unset_item_t* punset_item = alloc_blank_unset_item();
			punset_item->punset_item_handler = handle_unset_indirect_srec_field_name;
			punset_item->psrec_field_name_evaluator = rval_evaluator_alloc_from_ast(
				pchild->pchildren->phead->pvvalue, pcst->pfmgr, type_inferencing, context_flags);
			sllv_append(pstate->punset_items, punset_item);

		} else if (pchild->type == MD_AST_NODE_TYPE_OOSVAR_KEYLIST) {
			unset_item_t* punset_item = alloc_blank_unset_item();
			punset_item->punset_item_handler = handle_unset_oosvar;
			punset_item->pkeylist_evaluators = allocate_keylist_evaluators_from_ast_node(
				pchild, pcst->pfmgr, type_inferencing, context_flags);
			sllv_append(pstate->punset_items, punset_item);

		} else if (pchild->type == MD_AST_NODE_TYPE_NONINDEXED_LOCAL_VARIABLE) {
			MLR_INTERNAL_CODING_ERROR_IF(pchild->vardef_frame_relative_index == MD_UNUSED_INDEX);
			unset_item_t* punset_item = alloc_blank_unset_item();
			punset_item->punset_item_handler = handle_unset_nonindexed_local_variable;
			punset_item->local_variable_frame_relative_index = pchild->vardef_frame_relative_index;
			sllv_append(pstate->punset_items, punset_item);

		} else if (pchild->type == MD_AST_NODE_TYPE_INDEXED_LOCAL_VARIABLE) {
			MLR_INTERNAL_CODING_ERROR_IF(pchild->vardef_frame_relative_index == MD_UNUSED_INDEX);
			unset_item_t* punset_item = alloc_blank_unset_item();
			punset_item->punset_item_handler = handle_unset_indexed_local_variable;
			punset_item->local_variable_frame_relative_index = pchild->vardef_frame_relative_index;
			punset_item->pkeylist_evaluators = allocate_keylist_evaluators_from_ast_node(
				pchild, pcst->pfmgr, type_inferencing, context_flags);
			sllv_append(pstate->punset_items, punset_item);

		} else {
			MLR_INTERNAL_CODING_ERROR();
		}
	}
	return mlr_dsl_cst_statement_valloc(
		pnode,
		pstatement_handler,
		free_unset,
		pstate);
}
Пример #17
0
// ----------------------------------------------------------------
cli_opts_t* parse_command_line(int argc, char** argv) {
	cli_opts_t* popts = mlr_malloc_or_die(sizeof(cli_opts_t));
	memset(popts, 0, sizeof(*popts));

	popts->irs               = NULL;
	popts->ifs               = NULL;
	popts->ips               = NULL;
	popts->allow_repeat_ifs  = NEITHER_TRUE_NOR_FALSE;
	popts->allow_repeat_ips  = NEITHER_TRUE_NOR_FALSE;
	popts->use_implicit_csv_header = FALSE;
	popts->headerless_csv_output   = FALSE;

	popts->ors               = NULL;
	popts->ofs               = NULL;
	popts->ops               = NULL;

	popts->right_justify_xtab_value       = FALSE;
	popts->stack_json_output_vertically   = FALSE;
	popts->wrap_json_output_in_outer_list = FALSE;
	popts->quote_json_values_always       = FALSE;
	popts->json_flatten_separator         = DEFAULT_JSON_FLATTEN_SEPARATOR;

	popts->ofmt              = DEFAULT_OFMT;
	popts->oquoting          = DEFAULT_OQUOTING;

	popts->plrec_reader      = NULL;
	popts->plrec_writer      = NULL;

	popts->prepipe           = NULL;
	popts->filenames         = NULL;

	popts->ifile_fmt         = "dkvp";
	popts->ofile_fmt         = "dkvp";

	popts->use_mmap_for_read = TRUE;
	int left_align_pprint    = TRUE;

	int have_rand_seed       = FALSE;
	unsigned rand_seed       = 0;

	int argi = 1;
	for (; argi < argc; argi++) {
		if (argv[argi][0] != '-') {
			break;

		} else if (streq(argv[argi], "--version")) {
#ifdef HAVE_CONFIG_H
			printf("Miller %s\n", PACKAGE_VERSION);
#else
			printf("Miller %s\n", MLR_VERSION);
#endif // HAVE_CONFIG_H
			exit(0);
		} else if (streq(argv[argi], "-h")) {
			main_usage(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--help")) {
			main_usage(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--help-all-verbs")) {
			usage_all_verbs(argv[0]);
		} else if (streq(argv[argi], "--list-all-verbs") || streq(argv[argi], "-l")) {
			list_all_verbs(stdout, "");
			exit(0);
		} else if (streq(argv[argi], "--list-all-verbs-raw")) {
			list_all_verbs_raw(stdout);
			exit(0);
		} else if (streq(argv[argi], "--list-all-functions-raw")) {
			lrec_evaluator_list_all_functions_raw(stdout);
			exit(0);
		} else if (streq(argv[argi], "--help-all-functions") || streq(argv[argi], "-f")) {
			lrec_evaluator_function_usage(stdout, NULL);
			exit(0);

		} else if (streq(argv[argi], "--help-function") || streq(argv[argi], "--hf")) {
			check_arg_count(argv, argi, argc, 2);
			lrec_evaluator_function_usage(stdout, argv[argi+1]);
			exit(0);

		// main-usage subsections, individually accessible for the benefit of
		// the manpage-autogenerator
		} else if (streq(argv[argi], "--usage-synopsis")) {
			main_usage_synopsis(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-examples")) {
			main_usage_examples(stdout, argv[0], "");
			exit(0);
		} else if (streq(argv[argi], "--usage-list-all-verbs")) {
			list_all_verbs(stdout, "");
			exit(0);
		} else if (streq(argv[argi], "--usage-help-options")) {
			main_usage_help_options(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-functions")) {
			main_usage_functions(stdout, argv[0], "");
			exit(0);
		} else if (streq(argv[argi], "--usage-data-format-examples")) {
			main_usage_data_format_examples(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-data-format-options")) {
			main_usage_data_format_options(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-compressed-data-options")) {
			main_usage_compressed_data_options(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-separator-options")) {
			main_usage_separator_options(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-csv-options")) {
			main_usage_csv_options(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-double-quoting")) {
			main_usage_double_quoting(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-numerical-formatting")) {
			main_usage_numerical_formatting(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-other-options")) {
			main_usage_other_options(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-then-chaining")) {
			main_usage_then_chaining(stdout, argv[0]);
			exit(0);
		} else if (streq(argv[argi], "--usage-see-also")) {
			main_usage_see_also(stdout, argv[0]);
			exit(0);

		} else if (streq(argv[argi], "--rs")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ors = sep_from_arg(argv[argi+1], argv[0]);
			popts->irs = sep_from_arg(argv[argi+1], argv[0]);
			argi++;
		} else if (streq(argv[argi], "--irs")) {
			check_arg_count(argv, argi, argc, 2);
			popts->irs = sep_from_arg(argv[argi+1], argv[0]);
			argi++;
		} else if (streq(argv[argi], "--ors")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ors = sep_from_arg(argv[argi+1], argv[0]);
			argi++;

		} else if (streq(argv[argi], "--fs")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ofs = sep_from_arg(argv[argi+1], argv[0]);
			popts->ifs = sep_from_arg(argv[argi+1], argv[0]);
			argi++;
		} else if (streq(argv[argi], "--ifs")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ifs = sep_from_arg(argv[argi+1], argv[0]);
			argi++;
		} else if (streq(argv[argi], "--ofs")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ofs = sep_from_arg(argv[argi+1], argv[0]);
			argi++;
		} else if (streq(argv[argi], "--repifs")) {
			popts->allow_repeat_ifs = TRUE;
		} else if (streq(argv[argi], "--implicit-csv-header")) {
			popts->use_implicit_csv_header = TRUE;
		} else if (streq(argv[argi], "--headerless-csv-output")) {
			popts->headerless_csv_output = TRUE;

		} else if (streq(argv[argi], "-p")) {
			popts->ifile_fmt = "nidx";
			popts->ofile_fmt = "nidx";
			popts->ifs = " ";
			popts->ofs = " ";
			popts->allow_repeat_ifs = TRUE;

		} else if (streq(argv[argi], "--ps")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ops = sep_from_arg(argv[argi+1], argv[0]);
			popts->ips = sep_from_arg(argv[argi+1], argv[0]);
			argi++;
		} else if (streq(argv[argi], "--ips")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ips = sep_from_arg(argv[argi+1], argv[0]);
			argi++;
		} else if (streq(argv[argi], "--ops")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ops = sep_from_arg(argv[argi+1], argv[0]);
			argi++;

		} else if (streq(argv[argi], "--xvright")) {
			popts->right_justify_xtab_value = TRUE;

		} else if (streq(argv[argi], "--jvstack")) {
			popts->stack_json_output_vertically = TRUE;
		} else if (streq(argv[argi], "--jlistwrap")) {
			popts->wrap_json_output_in_outer_list = TRUE;
		} else if (streq(argv[argi], "--jquoteall")) {
			popts->quote_json_values_always = TRUE;
		} else if (streq(argv[argi], "--jflatsep")) {
			check_arg_count(argv, argi, argc, 2);
			popts->json_flatten_separator = sep_from_arg(argv[argi+1], argv[0]);
			argi++;

		} else if (streq(argv[argi], "--csv"))      { popts->ifile_fmt = popts->ofile_fmt = "csv";
		} else if (streq(argv[argi], "--icsv"))     { popts->ifile_fmt = "csv";
		} else if (streq(argv[argi], "--ocsv"))     { popts->ofile_fmt = "csv";

		} else if (streq(argv[argi], "--csvlite"))  { popts->ifile_fmt = popts->ofile_fmt = "csvlite";
		} else if (streq(argv[argi], "--icsvlite")) { popts->ifile_fmt = "csvlite";
		} else if (streq(argv[argi], "--ocsvlite")) { popts->ofile_fmt = "csvlite";

		} else if (streq(argv[argi], "--dkvp"))     { popts->ifile_fmt = popts->ofile_fmt = "dkvp";
		} else if (streq(argv[argi], "--idkvp"))    { popts->ifile_fmt = "dkvp";
		} else if (streq(argv[argi], "--odkvp"))    { popts->ofile_fmt = "dkvp";

		} else if (streq(argv[argi], "--json"))     { popts->ifile_fmt = popts->ofile_fmt = "json";
		} else if (streq(argv[argi], "--ijson"))    { popts->ifile_fmt = "json";
		} else if (streq(argv[argi], "--ojson"))    { popts->ofile_fmt = "json";

		} else if (streq(argv[argi], "--nidx"))     { popts->ifile_fmt = popts->ofile_fmt = "nidx";
		} else if (streq(argv[argi], "--inidx"))    { popts->ifile_fmt = "nidx";
		} else if (streq(argv[argi], "--onidx"))    { popts->ofile_fmt = "nidx";

		} else if (streq(argv[argi], "--xtab"))     { popts->ifile_fmt = popts->ofile_fmt = "xtab";
		} else if (streq(argv[argi], "--ixtab"))    { popts->ifile_fmt = "xtab";
		} else if (streq(argv[argi], "--oxtab"))    { popts->ofile_fmt = "xtab";

		} else if (streq(argv[argi], "--ipprint")) {
			popts->ifile_fmt        = "csvlite";
			popts->ifs              = " ";
			popts->allow_repeat_ifs = TRUE;

		} else if (streq(argv[argi], "--opprint")) {
			popts->ofile_fmt = "pprint";
		} else if (streq(argv[argi], "--pprint")) {
			popts->ifile_fmt        = "csvlite";
			popts->ifs              = " ";
			popts->allow_repeat_ifs = TRUE;
			popts->ofile_fmt        = "pprint";
		} else if (streq(argv[argi], "--right"))   {
			left_align_pprint = FALSE;

		} else if (streq(argv[argi], "--ofmt")) {
			check_arg_count(argv, argi, argc, 2);
			popts->ofmt = argv[argi+1];
			argi++;

		} else if (streq(argv[argi], "--quote-all"))     { popts->oquoting = QUOTE_ALL;
		} else if (streq(argv[argi], "--quote-none"))    { popts->oquoting = QUOTE_NONE;
		} else if (streq(argv[argi], "--quote-minimal")) { popts->oquoting = QUOTE_MINIMAL;
		} else if (streq(argv[argi], "--quote-numeric")) { popts->oquoting = QUOTE_NUMERIC;

		} else if (streq(argv[argi], "--mmap")) {
			popts->use_mmap_for_read = TRUE;
		} else if (streq(argv[argi], "--no-mmap")) {
			popts->use_mmap_for_read = FALSE;

		} else if (streq(argv[argi], "--seed")) {
			check_arg_count(argv, argi, argc, 2);
			if (sscanf(argv[argi+1], "0x%x", &rand_seed) == 1) {
				have_rand_seed = TRUE;
			} else if (sscanf(argv[argi+1], "%u", &rand_seed) == 1) {
				have_rand_seed = TRUE;
			} else {
				main_usage(stderr, argv[0]);
				exit(1);
			}
			argi++;

		} else if (streq(argv[argi], "--prepipe")) {
			check_arg_count(argv, argi, argc, 2);
			popts->prepipe = argv[argi+1];
			popts->use_mmap_for_read = FALSE;
			argi++;

		} else {
			usage_unrecognized_verb(argv[0], argv[argi]);
		}
	}

	lhmss_t* default_rses = get_default_rses();
	lhmss_t* default_fses = get_default_fses();
	lhmss_t* default_pses = get_default_pses();
	lhmsi_t* default_repeat_ifses = get_default_repeat_ifses();
	lhmsi_t* default_repeat_ipses = get_default_repeat_ipses();

	if (popts->irs == NULL)
		popts->irs = lhmss_get(default_rses, popts->ifile_fmt);
	if (popts->ifs == NULL)
		popts->ifs = lhmss_get(default_fses, popts->ifile_fmt);
	if (popts->ips == NULL)
		popts->ips = lhmss_get(default_pses, popts->ifile_fmt);

	if (popts->allow_repeat_ifs == NEITHER_TRUE_NOR_FALSE)
		popts->allow_repeat_ifs = lhmsi_get(default_repeat_ifses, popts->ifile_fmt);
	if (popts->allow_repeat_ips == NEITHER_TRUE_NOR_FALSE)
		popts->allow_repeat_ips = lhmsi_get(default_repeat_ipses, popts->ifile_fmt);

	if (popts->ors == NULL)
		popts->ors = lhmss_get(default_rses, popts->ofile_fmt);
	if (popts->ofs == NULL)
		popts->ofs = lhmss_get(default_fses, popts->ofile_fmt);
	if (popts->ops == NULL)
		popts->ops = lhmss_get(default_pses, popts->ofile_fmt);

	if (popts->irs == NULL) {
		fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", argv[0], __FILE__, __LINE__);
		exit(1);
	}
	if (popts->ifs == NULL) {
		fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", argv[0], __FILE__, __LINE__);
		exit(1);
	}
	if (popts->ips == NULL) {
		fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", argv[0], __FILE__, __LINE__);
		exit(1);
	}

	if (popts->allow_repeat_ifs == NEITHER_TRUE_NOR_FALSE) {
		fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", argv[0], __FILE__, __LINE__);
		exit(1);
	}
	if (popts->allow_repeat_ips == NEITHER_TRUE_NOR_FALSE) {
		fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", argv[0], __FILE__, __LINE__);
		exit(1);
	}

	if (popts->ors == NULL) {
		fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", argv[0], __FILE__, __LINE__);
		exit(1);
	}
	if (popts->ofs == NULL) {
		fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", argv[0], __FILE__, __LINE__);
		exit(1);
	}
	if (popts->ops == NULL) {
		fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", argv[0], __FILE__, __LINE__);
		exit(1);
	}

	if (streq(popts->ofile_fmt, "pprint") && strlen(popts->ofs) != 1) {
		fprintf(stderr, "%s: OFS for PPRINT format must be single-character; got \"%s\".\n",
			argv[0], popts->ofs);
		return NULL;
	}
	if      (streq(popts->ofile_fmt, "dkvp"))
		popts->plrec_writer = lrec_writer_dkvp_alloc(popts->ors, popts->ofs, popts->ops);
	else if (streq(popts->ofile_fmt, "json"))
		popts->plrec_writer = lrec_writer_json_alloc(popts->stack_json_output_vertically,
			popts->wrap_json_output_in_outer_list, popts->quote_json_values_always, popts->json_flatten_separator);
	else if (streq(popts->ofile_fmt, "csv"))
		popts->plrec_writer = lrec_writer_csv_alloc(popts->ors, popts->ofs, popts->oquoting,
			popts->headerless_csv_output);
	else if (streq(popts->ofile_fmt, "csvlite"))
		popts->plrec_writer = lrec_writer_csvlite_alloc(popts->ors, popts->ofs, popts->headerless_csv_output);
	else if (streq(popts->ofile_fmt, "nidx"))
		popts->plrec_writer = lrec_writer_nidx_alloc(popts->ors, popts->ofs);
	else if (streq(popts->ofile_fmt, "xtab"))
		popts->plrec_writer = lrec_writer_xtab_alloc(popts->ofs, popts->ops, popts->right_justify_xtab_value);
	else if (streq(popts->ofile_fmt, "pprint"))
		popts->plrec_writer = lrec_writer_pprint_alloc(popts->ors, popts->ofs[0], left_align_pprint);
	else {
		main_usage(stderr, argv[0]);
		exit(1);
	}

	if ((argc - argi) < 1) {
		main_usage(stderr, argv[0]);
		exit(1);
	}

	popts->pmapper_list = sllv_alloc();
	while (TRUE) {
		check_arg_count(argv, argi, argc, 1);
		char* verb = argv[argi];

		mapper_setup_t* pmapper_setup = look_up_mapper_setup(verb);
		if (pmapper_setup == NULL) {
			fprintf(stderr, "%s: verb \"%s\" not found. Please use \"%s --help\" for a list.\n",
				argv[0], verb, argv[0]);
			exit(1);
		}

		if ((argc - argi) >= 2) {
			if (streq(argv[argi+1], "-h") || streq(argv[argi+1], "--help")) {
				pmapper_setup->pusage_func(stdout, argv[0], verb);
				exit(0);
			}
		}

		// It's up to the parse func to print its usage on CLI-parse failure.
		mapper_t* pmapper = pmapper_setup->pparse_func(&argi, argc, argv);
		if (pmapper == NULL) {
			exit(1);
		}
		sllv_append(popts->pmapper_list, pmapper);

		if (argi >= argc || !streq(argv[argi], "then"))
			break;
		argi++;
	}

	popts->filenames = &argv[argi];

	// No filenames means read from standard input, and standard input cannot be mmapped.
	if (argi == argc)
		popts->use_mmap_for_read = FALSE;

	popts->plrec_reader = lrec_reader_alloc(popts->ifile_fmt, popts->use_mmap_for_read,
		popts->irs, popts->ifs, popts->allow_repeat_ifs, popts->ips, popts->allow_repeat_ips,
		popts->use_implicit_csv_header, popts->json_flatten_separator);
	if (popts->plrec_reader == NULL) {
		main_usage(stderr, argv[0]);
		exit(1);
	}

	if (have_rand_seed) {
		mtrand_init(rand_seed);
	} else {
		mtrand_init_default();
	}

	return popts;
}