diff --git a/c/cli/argparse.c b/c/cli/argparse.c index e67bada41..74ac12f5b 100644 --- a/c/cli/argparse.c +++ b/c/cli/argparse.c @@ -9,6 +9,7 @@ typedef enum _ap_flag_t { AP_LONG_LONG_FLAG, AP_DOUBLE_FLAG, AP_STRING_FLAG, + AP_STRING_BUILD_LIST_FLAG, AP_STRING_LIST_FLAG, AP_STRING_ARRAY_FLAG } ap_flag_t; @@ -99,6 +100,10 @@ void ap_define_string_flag(ap_state_t* pstate, char* flag_name, char** pstring) sllv_append(pstate->pflag_defs, ap_flag_def_alloc(flag_name, AP_STRING_FLAG, 0, pstring, 2)); } +void ap_define_string_build_list_flag(ap_state_t* pstate, char* flag_name, slls_t** pplist) { + sllv_append(pstate->pflag_defs, ap_flag_def_alloc(flag_name, AP_STRING_BUILD_LIST_FLAG, 0, pplist, 2)); +} + void ap_define_string_list_flag(ap_state_t* pstate, char* flag_name, slls_t** pplist) { sllv_append(pstate->pflag_defs, ap_flag_def_alloc(flag_name, AP_STRING_LIST_FLAG, 0, pplist, 2)); } @@ -165,22 +170,34 @@ int ap_parse_aux(ap_state_t* pstate, char* verb, int* pargi, int argc, char** ar argv[0], verb, argv[argi+1], argv[argi]); fprintf(stderr, "\n"); } + } else if (pdef->type == AP_STRING_FLAG) { char** pstring = pdef->pval; *pstring = argv[argi+1]; pdef->pval = pstring; + + } else if (pdef->type == AP_STRING_BUILD_LIST_FLAG) { + slls_t** pplist = pdef->pval; + if (*pplist == NULL) { + *pplist = slls_alloc(); + } + slls_append_no_free(*pplist, argv[argi+1]); + pdef->pval = pplist; + } else if (pdef->type == AP_STRING_LIST_FLAG) { slls_t** pplist = pdef->pval; if (*pplist != NULL) slls_free(*pplist); *pplist = slls_from_line(argv[argi+1], ',', FALSE); pdef->pval = pplist; + } else if (pdef->type == AP_STRING_ARRAY_FLAG) { string_array_t** pparray = pdef->pval; if (*pparray != NULL) string_array_free(*pparray); *pparray = string_array_from_line(argv[argi+1], ','); pdef->pval = pparray; + } else { ok = FALSE; fprintf(stderr, "argparse.c: internal coding error: flag-def type %x not recognized.\n", pdef->type); diff --git a/c/cli/argparse.h b/c/cli/argparse.h index be5148850..0ad47144f 100644 --- a/c/cli/argparse.h +++ b/c/cli/argparse.h @@ -20,8 +20,9 @@ void ap_define_false_flag(ap_state_t* pstate, char* flag_name, int* pintv void ap_define_int_value_flag(ap_state_t* pstate, char* flag_name, int value, int* pintval); void ap_define_int_flag(ap_state_t* pstate, char* flag_name, int* pintval); void ap_define_long_long_flag(ap_state_t* pstate, char* flag_name, long long* pintval); -void ap_define_float_flag(ap_state_t* pstate, char* flag_name, double* pdoubleval); +void ap_define_float_flag(ap_state_t* pstate, char* flag_name, double* pdoubleval); void ap_define_string_flag(ap_state_t* pstate, char* flag_name, char** pstring); +void ap_define_string_build_list_flag(ap_state_t* pstate, char* flag_name, slls_t** pplist); void ap_define_string_list_flag(ap_state_t* pstate, char* flag_name, slls_t** pplist); void ap_define_string_array_flag(ap_state_t* pstate, char* flag_name, string_array_t** pparray); diff --git a/c/lib/string_builder.c b/c/lib/string_builder.c index d6fd1a4d4..8363c1358 100644 --- a/c/lib/string_builder.c +++ b/c/lib/string_builder.c @@ -17,7 +17,7 @@ string_builder_t* sb_alloc(int alloc_length) { } // ---------------------------------------------------------------- -void sb_free(string_builder_t* psb) { +void sb_free(string_builder_t* psb) { if (psb == NULL) return; free(psb->buffer); diff --git a/c/mapping/mapper_filter.c b/c/mapping/mapper_filter.c index a5fad889b..0f9ab54e8 100644 --- a/c/mapping/mapper_filter.c +++ b/c/mapping/mapper_filter.c @@ -1,5 +1,6 @@ #include "lib/mlr_globals.h" #include "lib/mlrutil.h" +#include "lib/string_builder.h" #include "containers/lrec.h" #include "containers/sllv.h" #include "mapping/context_flags.h" @@ -54,7 +55,8 @@ static void mapper_filter_usage(FILE* o, char* argv0, char* verb) { fprintf(o, "-x: Prints records for which {expression} evaluates to false.\n"); fprintf(o, "-f {filename}: the DSL expression is taken from the specified file rather\n"); fprintf(o, " than from the command line. Outer single quotes wrapping the expression\n"); - fprintf(o, " should not be placed in the file.\n"); + fprintf(o, " should not be placed in the file. If -f is specified more than once,\n"); + fprintf(o, " all input files specified using -f are concatenated to produce the expression.\n"); fprintf(o, "\n"); fprintf(o, "Please use a dollar sign for field names and double-quotes for string\n"); fprintf(o, "literals. If field names have special characters such as \".\" then you might\n"); @@ -89,19 +91,19 @@ static mapper_t* mapper_filter_parse_cli(int* pargi, int argc, char** argv, char* verb = argv[(*pargi)++]; char* mlr_dsl_expression = NULL; char* comment_stripped_mlr_dsl_expression = NULL; - char* expression_filename = NULL; + slls_t* expression_filenames = NULL; int print_ast = FALSE; int trace_parse = FALSE; int type_inferencing = TYPE_INFER_STRING_FLOAT_INT; int do_exclude = FALSE; ap_state_t* pstate = ap_alloc(); - ap_define_string_flag(pstate, "-f", &expression_filename); - ap_define_true_flag(pstate, "-v", &print_ast); - ap_define_true_flag(pstate, "-t", &trace_parse); - ap_define_int_value_flag(pstate, "-S", TYPE_INFER_STRING_ONLY, &type_inferencing); - ap_define_int_value_flag(pstate, "-F", TYPE_INFER_STRING_FLOAT, &type_inferencing); - ap_define_true_flag(pstate, "-x", &do_exclude); + ap_define_string_build_list_flag(pstate, "-f", &expression_filenames); + ap_define_true_flag(pstate, "-v", &print_ast); + ap_define_true_flag(pstate, "-t", &trace_parse); + ap_define_int_value_flag(pstate, "-S", TYPE_INFER_STRING_ONLY, &type_inferencing); + ap_define_int_value_flag(pstate, "-F", TYPE_INFER_STRING_FLOAT, &type_inferencing); + ap_define_true_flag(pstate, "-x", &do_exclude); // Pass error_on_unrecognized == FALSE to ap_parse so expressions starting // with a minus sign aren't treated as errors. Example: "mlr filter '-$x == @@ -111,14 +113,21 @@ static mapper_t* mapper_filter_parse_cli(int* pargi, int argc, char** argv, return NULL; } - if (expression_filename == NULL) { + if (expression_filenames == NULL) { if ((argc - *pargi) < 1) { mapper_filter_usage(stderr, argv[0], verb); return NULL; } mlr_dsl_expression = mlr_strdup_or_die(argv[(*pargi)++]); } else { - mlr_dsl_expression = read_file_into_memory(expression_filename, NULL); + string_builder_t *psb = sb_alloc(1024); + for (sllse_t* pe = expression_filenames->phead; pe != NULL; pe = pe->pnext) { + char* expression_filename = pe->value; + char* mlr_dsl_expression_piece = read_file_into_memory(expression_filename, NULL); + sb_append_string(psb, mlr_dsl_expression_piece); + } + mlr_dsl_expression = sb_finish(psb); + sb_free(psb); } comment_stripped_mlr_dsl_expression = alloc_comment_strip(mlr_dsl_expression); diff --git a/c/mapping/mapper_put.c b/c/mapping/mapper_put.c index eeb312617..9b37c6e44 100644 --- a/c/mapping/mapper_put.c +++ b/c/mapping/mapper_put.c @@ -1,5 +1,6 @@ #include "lib/mlr_globals.h" #include "lib/mlrutil.h" +#include "lib/string_builder.h" #include "cli/mlrcli.h" #include "containers/lrec.h" #include "containers/sllv.h" @@ -73,7 +74,10 @@ static void mapper_put_usage(FILE* o, char* argv0, char* verb) { fprintf(o, " to output records for emit. Default \"%s\".\n", DEFAULT_OOSVAR_FLATTEN_SEPARATOR); fprintf(o, "-f {filename}: the DSL expression is taken from the specified file rather\n"); fprintf(o, " than from the command line. Outer single quotes wrapping the expression\n"); - fprintf(o, " should not be placed in the file.\n"); + fprintf(o, " should not be placed in the file. If -f is specified more than once,\n"); + fprintf(o, " all input files specified using -f are concatenated to produce the expression.\n"); + fprintf(o, " (For example, you can define functions in one file and call them from another.)\n"); + fprintf(o, "\n"); fprintf(o, "--no-fflush: for emit, tee, print, and dump, don't call fflush() after every\n"); fprintf(o, " record.\n"); fprintf(o, "Any of the output-format command-line flags (see %s -h). Example: using\n", @@ -126,7 +130,7 @@ static mapper_t* mapper_put_parse_cli(int* pargi, int argc, char** argv, { char* mlr_dsl_expression = NULL; char* comment_stripped_mlr_dsl_expression = NULL; - char* expression_filename = NULL; + slls_t* expression_filenames = slls_alloc(); int outer_filter = TRUE; int type_inferencing = TYPE_INFER_STRING_FLOAT_INT; int print_ast = FALSE; @@ -158,7 +162,7 @@ static mapper_t* mapper_put_parse_cli(int* pargi, int argc, char** argv, mapper_put_usage(stderr, argv[0], verb); return NULL; } - expression_filename = argv[argi+1]; + slls_append_no_free(expression_filenames, argv[argi+1]); argi += 2; } else if (streq(argv[argi], "-v")) { print_ast = TRUE; @@ -192,14 +196,21 @@ static mapper_t* mapper_put_parse_cli(int* pargi, int argc, char** argv, } } - if (expression_filename == NULL) { + if (expression_filenames->length == 0) { if ((argc - argi) < 1) { mapper_put_usage(stderr, argv[0], verb); return NULL; } mlr_dsl_expression = mlr_strdup_or_die(argv[argi++]); } else { - mlr_dsl_expression = read_file_into_memory(expression_filename, NULL); + string_builder_t *psb = sb_alloc(1024); + for (sllse_t* pe = expression_filenames->phead; pe != NULL; pe = pe->pnext) { + char* expression_filename = pe->value; + char* mlr_dsl_expression_piece = read_file_into_memory(expression_filename, NULL); + sb_append_string(psb, mlr_dsl_expression_piece); + } + mlr_dsl_expression = sb_finish(psb); + sb_free(psb); } comment_stripped_mlr_dsl_expression = alloc_comment_strip(mlr_dsl_expression); diff --git a/c/todo.txt b/c/todo.txt index e70646848..2abe4ba77 100644 --- a/c/todo.txt +++ b/c/todo.txt @@ -9,7 +9,6 @@ TOP OF LIST: * 10min vers: - sql-schema example - publish - - to 4.n.0 release notes as well UDFs: * multiple put -f @@ -22,6 +21,8 @@ UDFs: ! try to get seqgen to force eof sooner. in particular, try to get 'mlr seqgen' w/o -n. if so, fix cookbook etc. * some help for keyword clashes? e.g. 'func E(x) { ... }' ? +* NR PI E etc. to keyword help ... + * indent by two more for jvstack ? rework comma placement for jvstack? * --c2p, --c2j etc ...