1 #include <stdlib.h>
2 #include "lib/mlr_globals.h"
3 #include "lib/mlrutil.h"
4 #include "keylist_evaluators.h"
5 #include "mlr_dsl_cst.h"
6 #include "context_flags.h"
7 
8 // ----------------------------------------------------------------
file_output_mode_from_ast_node_type(mlr_dsl_ast_node_type_t mlr_dsl_ast_node_type)9 static file_output_mode_t file_output_mode_from_ast_node_type(mlr_dsl_ast_node_type_t mlr_dsl_ast_node_type) {
10 	switch(mlr_dsl_ast_node_type) {
11 	case MD_AST_NODE_TYPE_FILE_APPEND:
12 		return MODE_APPEND;
13 	case MD_AST_NODE_TYPE_PIPE:
14 		return MODE_PIPE;
15 	case MD_AST_NODE_TYPE_FILE_WRITE:
16 		return MODE_WRITE;
17 	default:
18 		MLR_INTERNAL_CODING_ERROR();
19 		return MODE_WRITE; // not reached
20 	}
21 }
22 
23 // ================================================================
24 typedef struct _print_state_t {
25 	rxval_evaluator_t*   prhs_xevaluator;
26 	FILE*                stdfp;
27 	file_output_mode_t   file_output_mode;
28 	rval_evaluator_t*    poutput_filename_evaluator;
29 	int                  flush_every_record;
30 	multi_out_t*         pmulti_out;
31 	char*                print_terminator;
32 } print_state_t;
33 
34 static mlr_dsl_cst_statement_handler_t handle_print;
35 static mlr_dsl_cst_statement_freer_t free_print;
36 
37 // ----------------------------------------------------------------
alloc_print(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags,char * print_terminator)38 mlr_dsl_cst_statement_t* alloc_print(
39 	mlr_dsl_cst_t*      pcst,
40 	mlr_dsl_ast_node_t* pnode,
41 	int                 type_inferencing,
42 	int                 context_flags,
43 	char*               print_terminator)
44 {
45 	print_state_t* pstate = mlr_malloc_or_die(sizeof(print_state_t));
46 
47 	pstate->prhs_xevaluator            = NULL;
48 	pstate->stdfp                      = NULL;
49 	pstate->poutput_filename_evaluator = NULL;
50 	pstate->pmulti_out                 = NULL;
51 
52 	MLR_INTERNAL_CODING_ERROR_IF((pnode->pchildren == NULL) || (pnode->pchildren->length != 2));
53 	mlr_dsl_ast_node_t* pvalue_node = pnode->pchildren->phead->pvvalue;
54 	pstate->prhs_xevaluator = rxval_evaluator_alloc_from_ast(pvalue_node, pcst->pfmgr,
55 		type_inferencing, context_flags);
56 	pstate->print_terminator = print_terminator;
57 
58 	mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pnext->pvvalue;
59 	mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren->phead->pvvalue;
60 	if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) {
61 		pstate->stdfp = stdout;
62 	} else if (pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
63 		pstate->stdfp = stderr;
64 	} else {
65 		pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
66 			type_inferencing, context_flags);
67 		pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
68 		pstate->pmulti_out = multi_out_alloc();
69 	}
70 	pstate->flush_every_record = pcst->flush_every_record;
71 
72 	return mlr_dsl_cst_statement_valloc(
73 		pnode,
74 		handle_print,
75 		free_print,
76 		pstate);
77 }
78 
79 // ----------------------------------------------------------------
free_print(mlr_dsl_cst_statement_t * pstatement,context_t * _)80 static void free_print(mlr_dsl_cst_statement_t* pstatement, context_t* _) {
81 	print_state_t* pstate = pstatement->pvstate;
82 
83 	if (pstate->prhs_xevaluator != NULL) {
84 		pstate->prhs_xevaluator->pfree_func(pstate->prhs_xevaluator);
85 	}
86 
87 	if (pstate->poutput_filename_evaluator != NULL) {
88 		pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
89 	}
90 
91 	if (pstate->pmulti_out != NULL) {
92 		multi_out_close(pstate->pmulti_out);
93 		multi_out_free(pstate->pmulti_out);
94 	}
95 
96 	free(pstate);
97 }
98 
99 // ----------------------------------------------------------------
handle_print(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)100 static void handle_print(
101 	mlr_dsl_cst_statement_t* pstatement,
102 	variables_t*             pvars,
103 	cst_outputs_t*           pcst_outputs)
104 {
105 	print_state_t* pstate = pstatement->pvstate;
106 
107 	rxval_evaluator_t* prhs_xevaluator = pstate->prhs_xevaluator;
108 	boxed_xval_t bxval = prhs_xevaluator->pprocess_func(prhs_xevaluator->pvstate, pvars);
109 
110 	char sfree_flags = NO_FREE;
111 	char* sval = "{is-a-map}";
112 
113 	if (bxval.xval.is_terminal) {
114 		sval = mv_alloc_format_val(&bxval.xval.terminal_mlrval);
115 		sfree_flags = FREE_ENTRY_VALUE;
116 	}
117 
118 	rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
119 	if (poutput_filename_evaluator == NULL) {
120 		fprintf(pstate->stdfp, "%s%s", sval, pstate->print_terminator);
121 		if (pstate->flush_every_record)
122 			fflush(pstate->stdfp);
123 	} else {
124 		mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
125 
126 		char fn_free_flags;
127 		char* filename = mv_format_val(&filename_mv, &fn_free_flags);
128 
129 		FILE* outfp = multi_out_get(pstate->pmulti_out, filename, pstate->file_output_mode);
130 		fprintf(outfp, "%s%s", sval, pstate->print_terminator);
131 		if (pstate->flush_every_record)
132 			fflush(outfp);
133 
134 		if (fn_free_flags)
135 			free(filename);
136 		mv_free(&filename_mv);
137 	}
138 
139 	if (sfree_flags) {
140 		free(sval);
141 	}
142 	if (bxval.is_ephemeral) {
143 		mlhmmv_xvalue_free(&bxval.xval);
144 	}
145 }
146 
147 // ================================================================
148 typedef struct _tee_state_t {
149 	FILE*                stdfp;
150 	file_output_mode_t   file_output_mode;
151 	rval_evaluator_t*    poutput_filename_evaluator;
152 	int                  flush_every_record;
153 	lrec_writer_t*       psingle_lrec_writer;
154 	multi_lrec_writer_t* pmulti_lrec_writer;
155 } tee_state_t;
156 
157 static mlr_dsl_cst_statement_handler_t handle_tee_to_stdfp;
158 static mlr_dsl_cst_statement_handler_t handle_tee_to_file;
159 static mlr_dsl_cst_statement_freer_t free_tee;
160 
161 static lrec_t* handle_tee_common(
162 	tee_state_t*   pstate,
163 	variables_t*   pvars,
164 	cst_outputs_t* pcst_outputs);
165 
166 // ----------------------------------------------------------------
alloc_tee(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags)167 mlr_dsl_cst_statement_t* alloc_tee(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
168 	int type_inferencing, int context_flags)
169 {
170 	tee_state_t* pstate = mlr_malloc_or_die(sizeof(tee_state_t));
171 
172 	pstate->stdfp                      = NULL;
173 	pstate->poutput_filename_evaluator = NULL;
174 	pstate->psingle_lrec_writer        = NULL;
175 	pstate->pmulti_lrec_writer         = NULL;
176 
177 	mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pvvalue;
178 	mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren->phead->pvvalue;
179 
180 	pstate->flush_every_record = pcst->flush_every_record;
181 	if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT || pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
182 		pstate->stdfp = (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) ? stdout : stderr;
183 
184 		return mlr_dsl_cst_statement_valloc(
185 			pnode,
186 			handle_tee_to_stdfp,
187 			free_tee,
188 			pstate);
189 
190 	} else {
191 		pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
192 			type_inferencing, context_flags);
193 		pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
194 
195 		return mlr_dsl_cst_statement_valloc(
196 			pnode,
197 			handle_tee_to_file,
198 			free_tee,
199 			pstate);
200 	}
201 }
202 
203 // ----------------------------------------------------------------
free_tee(mlr_dsl_cst_statement_t * pstatement,context_t * pctx)204 static void free_tee(mlr_dsl_cst_statement_t* pstatement, context_t* pctx) {
205 	tee_state_t* pstate = pstatement->pvstate;
206 
207 	if (pstate->poutput_filename_evaluator != NULL) {
208 		pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
209 	}
210 
211 	if (pstate->psingle_lrec_writer != NULL) {
212 		pstate->psingle_lrec_writer->pfree_func(pstate->psingle_lrec_writer, pctx);
213 	}
214 
215 	if (pstate->pmulti_lrec_writer != NULL) {
216 		multi_lrec_writer_drain(pstate->pmulti_lrec_writer, pctx);
217 		multi_lrec_writer_free(pstate->pmulti_lrec_writer, pctx);
218 	}
219 
220 	free(pstate);
221 }
222 
223 // ----------------------------------------------------------------
handle_tee_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)224 static void handle_tee_to_stdfp(
225 	mlr_dsl_cst_statement_t* pstatement,
226 	variables_t*             pvars,
227 	cst_outputs_t*           pcst_outputs)
228 {
229 	tee_state_t* pstate = pstatement->pvstate;
230 
231 	// The opts aren't complete at alloc time so we need to handle them on first use.
232 	if (pstate->psingle_lrec_writer == NULL)
233 		pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
234 
235 	lrec_t* pcopy = handle_tee_common(pstate, pvars, pcst_outputs);
236 
237 	// The writer frees the lrec
238 	pstate->psingle_lrec_writer->pprocess_func(pstate->psingle_lrec_writer->pvstate,
239 		pstate->stdfp, pcopy, pvars->pctx);
240 	if (pstate->flush_every_record)
241 		fflush(pstate->stdfp);
242 }
243 
244 // ----------------------------------------------------------------
handle_tee_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)245 static void handle_tee_to_file(
246 	mlr_dsl_cst_statement_t* pstatement,
247 	variables_t*             pvars,
248 	cst_outputs_t*           pcst_outputs)
249 {
250 	tee_state_t* pstate = pstatement->pvstate;
251 
252 	// The opts aren't complete at alloc time so we need to handle them on first use.
253 	if (pstate->pmulti_lrec_writer == NULL)
254 		pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
255 
256 	rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
257 	mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
258 
259 	lrec_t* pcopy = handle_tee_common(pstate, pvars, pcst_outputs);
260 
261 	char fn_free_flags = 0;
262 	char* filename = mv_format_val(&filename_mv, &fn_free_flags);
263 	// The writer frees the lrec
264 	multi_lrec_writer_output_srec(pstate->pmulti_lrec_writer, pcopy, filename,
265 		pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
266 
267 	if (fn_free_flags)
268 		free(filename);
269 	mv_free(&filename_mv);
270 }
271 
272 // ----------------------------------------------------------------
handle_tee_common(tee_state_t * pstate,variables_t * pvars,cst_outputs_t * pcst_outputs)273 static lrec_t* handle_tee_common(
274 	tee_state_t*   pstate,
275 	variables_t*   pvars,
276 	cst_outputs_t* pcst_outputs)
277 {
278 	lrec_t* pcopy = lrec_copy(pvars->pinrec);
279 
280 	// Write the output fields from the typed overlay back to the lrec.
281 	for (lhmsmve_t* pe = pvars->ptyped_overlay->phead; pe != NULL; pe = pe->pnext) {
282 		char* output_field_name = pe->key;
283 		mv_t* pval = &pe->value;
284 
285 		// Ownership transfer from mv_t to lrec.
286 		if (pval->type == MT_STRING || pval->type == MT_EMPTY) {
287 			lrec_put(pcopy, output_field_name, mlr_strdup_or_die(pval->u.strv), FREE_ENTRY_VALUE);
288 		} else {
289 			char free_flags = NO_FREE;
290 			char* string = mv_format_val(pval, &free_flags);
291 			lrec_put(pcopy, output_field_name, string, free_flags);
292 		}
293 	}
294 	return pcopy;
295 }
296 
297 // ================================================================
298 // Most statements have one item, except emit and emitf.
299 struct _emitf_item_t;
300 typedef void emitf_item_handler_t(
301 	struct _emitf_item_t* pemitf_item,
302 	variables_t*          pvars,
303 	cst_outputs_t*        pcst_outputs);
304 
305 typedef struct _emitf_item_t {
306 	char*                 srec_field_name;
307 	rval_evaluator_t*     parg_evaluator;
308 } emitf_item_t;
309 
310 static emitf_item_t* alloc_emitf_item(char* srec_field_name, rval_evaluator_t* parg_evaluator);
311 static void free_emitf_item(emitf_item_t* pemitf_item);
312 
313 // ----------------------------------------------------------------
314 typedef struct _emitf_state_t {
315 	FILE*                stdfp;
316 	file_output_mode_t   file_output_mode;
317 	rval_evaluator_t*    poutput_filename_evaluator;
318 	int                  flush_every_record;
319 	lrec_writer_t*       psingle_lrec_writer;
320 	multi_lrec_writer_t* pmulti_lrec_writer;
321 	sllv_t*              pemitf_items;
322 } emitf_state_t;
323 
324 static mlr_dsl_cst_statement_handler_t handle_emitf;
325 static mlr_dsl_cst_statement_freer_t free_emitf;
326 
327 static void handle_emitf(
328 	mlr_dsl_cst_statement_t* pstatement,
329 	variables_t*             pvars,
330 	cst_outputs_t*           pcst_outputs);
331 
332 static void handle_emitf_to_stdfp(
333 	mlr_dsl_cst_statement_t* pstatement,
334 	variables_t*             pvars,
335 	cst_outputs_t*           pcst_outputs);
336 
337 static void handle_emitf_to_file(
338 	mlr_dsl_cst_statement_t* pstatement,
339 	variables_t*             pvars,
340 	cst_outputs_t*           pcst_outputs);
341 
342 static void handle_emitf_common(
343 	emitf_state_t* pstate,
344 	variables_t*   pvars,
345 	sllv_t*        poutrecs);
346 
347 // ----------------------------------------------------------------
alloc_emitf(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags)348 mlr_dsl_cst_statement_t* alloc_emitf(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
349 	int type_inferencing, int context_flags)
350 {
351 	emitf_state_t* pstate = mlr_malloc_or_die(sizeof(emitf_state_t));
352 
353 	pstate->stdfp                      = NULL;
354 	pstate->poutput_filename_evaluator = NULL;
355 	pstate->psingle_lrec_writer        = NULL;
356 	pstate->pmulti_lrec_writer         = NULL;
357 	pstate->pemitf_items               = NULL;
358 
359 	mlr_dsl_ast_node_t* pnamesnode = pnode->pchildren->phead->pvvalue;
360 
361 	// Loop over oosvar names to emit in e.g. 'emitf @a, @b, @c'.
362 	pstate->pemitf_items = sllv_alloc();
363 	for (sllve_t* pe = pnamesnode->pchildren->phead; pe != NULL; pe = pe->pnext) {
364 		mlr_dsl_ast_node_t* pwalker = pe->pvvalue;
365 
366 		char* name = NULL;
367 		switch(pwalker->type) {
368 		case MD_AST_NODE_TYPE_OOSVAR_KEYLIST:
369 			name = ((mlr_dsl_ast_node_t*)(pwalker->pchildren->phead->pvvalue))->text;
370 			break;
371 		case MD_AST_NODE_TYPE_NONINDEXED_LOCAL_VARIABLE:
372 			name = pwalker->text;
373 			break;
374 		case MD_AST_NODE_TYPE_INDEXED_LOCAL_VARIABLE:
375 			name = pwalker->text;
376 			break;
377 		default:
378 			MLR_INTERNAL_CODING_ERROR();
379 			break;
380 		}
381 		sllv_append(pstate->pemitf_items,
382 			alloc_emitf_item(
383 				name,
384 				rval_evaluator_alloc_from_ast(pwalker, pcst->pfmgr, type_inferencing, context_flags)));
385 	}
386 
387 	mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pnext->pvvalue;
388 	mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren == NULL
389 		? NULL
390 		: poutput_node->pchildren->phead == NULL
391 		? NULL
392 		: poutput_node->pchildren->phead->pvvalue;
393 	mlr_dsl_cst_statement_handler_t* phandler = NULL;
394 	if (poutput_node->type == MD_AST_NODE_TYPE_STREAM) {
395 		phandler = handle_emitf;
396 	} else if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT || pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
397 		pstate->stdfp = (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) ? stdout : stderr;
398 		phandler = handle_emitf_to_stdfp;
399 	} else {
400 		pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
401 			type_inferencing, context_flags);
402 		pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
403 		phandler = handle_emitf_to_file;
404 	}
405 	pstate->flush_every_record = pcst->flush_every_record;
406 
407 	return mlr_dsl_cst_statement_valloc(
408 		pnode,
409 		phandler,
410 		free_emitf,
411 		pstate);
412 }
413 
alloc_emitf_item(char * srec_field_name,rval_evaluator_t * parg_evaluator)414 static emitf_item_t* alloc_emitf_item(char* srec_field_name, rval_evaluator_t* parg_evaluator) {
415 	emitf_item_t* pemitf_item = mlr_malloc_or_die(sizeof(emitf_item_t));
416 	pemitf_item->srec_field_name = srec_field_name;
417 	pemitf_item->parg_evaluator  = parg_evaluator;
418 	return pemitf_item;
419 }
420 
free_emitf_item(emitf_item_t * pemitf_item)421 static void free_emitf_item(emitf_item_t* pemitf_item) {
422 	pemitf_item->parg_evaluator->pfree_func(pemitf_item->parg_evaluator);
423 	free(pemitf_item);
424 }
425 
free_emitf(mlr_dsl_cst_statement_t * pstatement,context_t * pctx)426 static void free_emitf(mlr_dsl_cst_statement_t* pstatement, context_t* pctx) {
427 	emitf_state_t* pstate = pstatement->pvstate;
428 
429 	if (pstate->poutput_filename_evaluator != NULL) {
430 		pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
431 	}
432 
433 	if (pstate->psingle_lrec_writer != NULL) {
434 		pstate->psingle_lrec_writer->pfree_func(pstate->psingle_lrec_writer, pctx);
435 	}
436 
437 	if (pstate->pmulti_lrec_writer != NULL) {
438 		multi_lrec_writer_drain(pstate->pmulti_lrec_writer, pctx);
439 		multi_lrec_writer_free(pstate->pmulti_lrec_writer, pctx);
440 	}
441 
442 	if (pstate->pemitf_items != NULL) {
443 		for (sllve_t* pe = pstate->pemitf_items->phead; pe != NULL; pe = pe->pnext)
444 			free_emitf_item(pe->pvvalue);
445 		sllv_free(pstate->pemitf_items);
446 	}
447 
448 	free(pstate);
449 }
450 
451 // ----------------------------------------------------------------
handle_emitf(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)452 static void handle_emitf(
453 	mlr_dsl_cst_statement_t* pstatement,
454 	variables_t*             pvars,
455 	cst_outputs_t*           pcst_outputs)
456 {
457 	emitf_state_t* pstate = pstatement->pvstate;
458 	handle_emitf_common(pstate, pvars, pcst_outputs->poutrecs);
459 }
460 
handle_emitf_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)461 static void handle_emitf_to_stdfp(
462 	mlr_dsl_cst_statement_t* pstatement,
463 	variables_t*             pvars,
464 	cst_outputs_t*           pcst_outputs)
465 {
466 	emitf_state_t* pstate = pstatement->pvstate;
467 
468 	// The opts aren't complete at alloc time so we need to handle them on first use.
469 	if (pstate->psingle_lrec_writer == NULL)
470 		pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
471 
472 	sllv_t* poutrecs = sllv_alloc();
473 
474 	handle_emitf_common(pstate, pvars, poutrecs);
475 
476 	lrec_writer_print_all(pstate->psingle_lrec_writer, pstate->stdfp, poutrecs, pvars->pctx);
477 	if (pstate->flush_every_record)
478 		fflush(pstate->stdfp);
479 	sllv_free(poutrecs);
480 }
481 
handle_emitf_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)482 static void handle_emitf_to_file(
483 	mlr_dsl_cst_statement_t* pstatement,
484 	variables_t*             pvars,
485 	cst_outputs_t*           pcst_outputs)
486 {
487 	emitf_state_t* pstate = pstatement->pvstate;
488 
489 	// The opts aren't complete at alloc time so we need to handle them on first use.
490 	if (pstate->pmulti_lrec_writer == NULL)
491 		pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
492 
493 	rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
494 	mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
495 
496 	sllv_t* poutrecs = sllv_alloc();
497 
498 	handle_emitf_common(pstate, pvars, poutrecs);
499 
500 	char fn_free_flags = 0;
501 	char* filename = mv_format_val(&filename_mv, &fn_free_flags);
502 	multi_lrec_writer_output_list(pstate->pmulti_lrec_writer, poutrecs, filename,
503 		pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
504 
505 	sllv_free(poutrecs);
506 	if (fn_free_flags)
507 		free(filename);
508 	mv_free(&filename_mv);
509 }
510 
handle_emitf_common(emitf_state_t * pstate,variables_t * pvars,sllv_t * poutrecs)511 static void handle_emitf_common(
512 	emitf_state_t* pstate,
513 	variables_t*   pvars,
514 	sllv_t*        poutrecs)
515 {
516 	lrec_t* prec_to_emit = lrec_unbacked_alloc();
517 	for (sllve_t* pf = pstate->pemitf_items->phead; pf != NULL; pf = pf->pnext) {
518 		emitf_item_t* pemitf_item = pf->pvvalue;
519 		char* srec_field_name = pemitf_item->srec_field_name;
520 		rval_evaluator_t* parg_evaluator = pemitf_item->parg_evaluator;
521 
522 		// This is overkill ... the grammar allows only for oosvar names as args to emit.  So we could bypass
523 		// that and just hashmap-get keyed by srec_field_name here.
524 		mv_t val = parg_evaluator->pprocess_func(parg_evaluator->pvstate, pvars);
525 
526 		if (val.type == MT_STRING) {
527 			// Ownership transfer from (newly created) mlrval to (newly created) lrec.
528 			lrec_put(prec_to_emit, srec_field_name, val.u.strv, val.free_flags);
529 		} else {
530 			char free_flags = NO_FREE;
531 			char* string = mv_format_val(&val, &free_flags);
532 			lrec_put(prec_to_emit, srec_field_name, string, free_flags);
533 		}
534 
535 	}
536 	sllv_append(poutrecs, prec_to_emit);
537 }
538 
539 // ================================================================
540 struct _emit_state_t; // Forward reference
541 typedef void record_emitter_t(
542 	struct _emit_state_t* pstate,
543 	variables_t*  pvars,
544 	sllv_t*       poutrecs,
545 	char*         oosvar_flatten_separator);
546 
547 typedef struct _emit_state_t {
548 	rval_evaluator_t*  poutput_filename_evaluator;
549 	FILE*              stdfp;
550 	file_output_mode_t file_output_mode;
551 	sllv_t*            pemit_namelist_evaluators;
552 	int                do_full_prefixing;
553 
554 	record_emitter_t*  precord_emitter;
555 
556 	// For map literals
557 	rxval_evaluator_t* prhs_xevaluator;
558 
559 	// For local variables
560 	char* localvar_name;
561 	int   localvar_frame_relative_index;
562 
563 	// For oosvars and localvars: indices ["a", 1, $2] in 'for (k,v in @a[1][$2]) {...}'.
564 	sllv_t* pemit_keylist_evaluators;
565 
566 	lrec_writer_t* psingle_lrec_writer; // emit/tee to stdout/stderr
567 	multi_lrec_writer_t* pmulti_lrec_writer; // emit-to-file
568 
569 	int flush_every_record;
570 } emit_state_t;
571 
572 static mlr_dsl_cst_statement_handler_t handle_emit;
573 static mlr_dsl_cst_statement_handler_t handle_emit_to_stdfp;
574 static mlr_dsl_cst_statement_handler_t handle_emit_to_file;
575 
576 static void record_emitter_from_oosvar(
577 	emit_state_t* pstate,
578 	variables_t*  pvars,
579 	sllv_t*       poutrecs,
580 	char*         oosvar_flatten_separator);
581 
582 static void record_emitter_from_local_variable(
583 	emit_state_t* pstate,
584 	variables_t*  pvars,
585 	sllv_t*       poutrecs,
586 	char*         oosvar_flatten_separator);
587 
588 static void record_emitter_from_full_srec(
589 	emit_state_t* pstate,
590 	variables_t*  pvars,
591 	sllv_t*       poutrecs,
592 	char*         oosvar_flatten_separator);
593 
594 static void record_emitter_from_ephemeral_map(
595 	emit_state_t* pstate,
596 	variables_t*  pvars,
597 	sllv_t*       poutrecs,
598 	char*         oosvar_flatten_separator);
599 
600 static mlr_dsl_cst_statement_handler_t handle_emit_all;
601 static mlr_dsl_cst_statement_handler_t handle_emit_all_to_stdfp;
602 static mlr_dsl_cst_statement_handler_t handle_emit_all_to_file;
603 
604 static mlr_dsl_cst_statement_freer_t free_emit;
605 
606 // ----------------------------------------------------------------
607 // $ mlr -n put -v 'emit @a[2][3], "x", "y", "z"'
608 // AST ROOT:
609 // text="list", type=statement_list:
610 //     text="emit", type=emit:
611 //         text="emit", type=emit:
612 //             text="oosvar_keylist", type=oosvar_keylist:
613 //                 text="a", type=string_literal.
614 //                 text="2", type=numeric_literal.
615 //                 text="3", type=numeric_literal.
616 //             text="emit_namelist", type=emit:
617 //                 text="x", type=numeric_literal.
618 //                 text="y", type=numeric_literal.
619 //                 text="z", type=numeric_literal.
620 //         text="stream", type=stream:
621 //
622 // $ mlr -n put -v 'emit all, "x", "y", "z"'
623 // AST ROOT:
624 // text="list", type=statement_list:
625 //     text="emit", type=emit:
626 //         text="emit", type=emit:
627 //             text="all", type=all.
628 //             text="emit_namelist", type=emit:
629 //                 text="x", type=numeric_literal.
630 //                 text="y", type=numeric_literal.
631 //                 text="z", type=numeric_literal.
632 //         text="stream", type=stream:
633 
alloc_emit(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags,int do_full_prefixing)634 mlr_dsl_cst_statement_t* alloc_emit(
635 	mlr_dsl_cst_t*      pcst,
636 	mlr_dsl_ast_node_t* pnode,
637 	int                 type_inferencing,
638 	int                 context_flags,
639 	int                 do_full_prefixing)
640 {
641 	emit_state_t* pstate = mlr_malloc_or_die(sizeof(emit_state_t));
642 
643 	pstate->poutput_filename_evaluator    = NULL;
644 	pstate->stdfp                         = NULL;
645 	pstate->precord_emitter               = NULL;
646 	pstate->prhs_xevaluator               = NULL;
647 	pstate->localvar_name                 = NULL;
648 	pstate->localvar_frame_relative_index = MD_UNUSED_INDEX;
649 	pstate->pemit_namelist_evaluators     = NULL;
650 	pstate->pemit_keylist_evaluators      = NULL;
651 	pstate->psingle_lrec_writer           = NULL;
652 	pstate->pmulti_lrec_writer            = NULL;
653 
654 	mlr_dsl_ast_node_t* pemit_node = pnode->pchildren->phead->pvvalue;
655 	mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pnext->pvvalue;
656 	mlr_dsl_ast_node_t* pkeylist_node = pemit_node->pchildren->phead->pvvalue;
657 
658 	// Name note: difference between keylist and namelist: in emit @a[$b]["c"], "d", @e,
659 	// the keylist is ["a", $b, "c"] and the namelist is ["d", @e].
660 
661 	// xxx why not rxval_evaluator here??
662 
663 	int output_all = FALSE;
664 	// The grammar allows only 'emit all', not 'emit @x, all, $y'.
665 	// So if 'all' appears at all, it's the only name.
666 	if (pkeylist_node->type == MD_AST_NODE_TYPE_ALL || pkeylist_node->type == MD_AST_NODE_TYPE_FULL_OOSVAR) {
667 		output_all = TRUE;
668 
669 		pstate->precord_emitter = record_emitter_from_oosvar;
670 
671 	} else if (pkeylist_node->type == MD_AST_NODE_TYPE_OOSVAR_KEYLIST) {
672 
673 		pstate->pemit_keylist_evaluators = allocate_keylist_evaluators_from_ast_node(
674 			pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
675 
676 		pstate->precord_emitter = record_emitter_from_oosvar;
677 
678 	} else if (pkeylist_node->type == MD_AST_NODE_TYPE_NONINDEXED_LOCAL_VARIABLE) {
679 		pstate->precord_emitter = record_emitter_from_local_variable;
680 
681 		MLR_INTERNAL_CODING_ERROR_IF(pkeylist_node->vardef_frame_relative_index == MD_UNUSED_INDEX);
682 		pstate->localvar_name = pkeylist_node->text;
683 		pstate->localvar_frame_relative_index = pkeylist_node->vardef_frame_relative_index;
684 		pstate->pemit_keylist_evaluators = sllv_alloc();
685 
686 	} else if (pkeylist_node->type == MD_AST_NODE_TYPE_INDEXED_LOCAL_VARIABLE) {
687 		pstate->precord_emitter = record_emitter_from_local_variable;
688 
689 		MLR_INTERNAL_CODING_ERROR_IF(pkeylist_node->vardef_frame_relative_index == MD_UNUSED_INDEX);
690 		pstate->localvar_name = pkeylist_node->text;
691 		pstate->localvar_frame_relative_index = pkeylist_node->vardef_frame_relative_index;
692 		pstate->pemit_keylist_evaluators = allocate_keylist_evaluators_from_ast_node(
693 			pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
694 
695 	} else if (pkeylist_node->type == MD_AST_NODE_TYPE_FULL_SREC) {
696 		pstate->precord_emitter = record_emitter_from_full_srec;
697 
698 	} else if (pkeylist_node->type == MD_AST_NODE_TYPE_FUNCTION_CALLSITE) {
699 		pstate->precord_emitter = record_emitter_from_ephemeral_map;
700 		pstate->prhs_xevaluator = rxval_evaluator_alloc_from_ast(
701 			pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
702 
703 	// xxx indexed function callsite -- ?
704 
705 	} else if (pkeylist_node->type == MD_AST_NODE_TYPE_MAP_LITERAL) {
706 		pstate->precord_emitter = record_emitter_from_ephemeral_map;
707 		pstate->prhs_xevaluator = rxval_evaluator_alloc_from_ast(
708 			pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
709 
710 	} else {
711 		MLR_INTERNAL_CODING_ERROR();
712 	}
713 
714 	pstate->pemit_namelist_evaluators = sllv_alloc();
715 	if (pemit_node->pchildren->length == 2) {
716 		mlr_dsl_ast_node_t* pnamelist_node = pemit_node->pchildren->phead->pnext->pvvalue;
717 		for (sllve_t* pe = pnamelist_node->pchildren->phead; pe != NULL; pe = pe->pnext) {
718 			mlr_dsl_ast_node_t* pkeynode = pe->pvvalue;
719 			sllv_append(pstate->pemit_namelist_evaluators,
720 				rval_evaluator_alloc_from_ast(pkeynode, pcst->pfmgr, type_inferencing, context_flags));
721 		}
722 	}
723 
724 	mlr_dsl_cst_statement_handler_t* phandler = NULL;
725 
726 	pstate->do_full_prefixing = do_full_prefixing;
727 	mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren == NULL
728 		? NULL
729 		: poutput_node->pchildren->phead == NULL
730 		? NULL
731 		: poutput_node->pchildren->phead->pvvalue;
732 	if (poutput_node->type == MD_AST_NODE_TYPE_STREAM) {
733 		phandler = output_all ? handle_emit_all : handle_emit;
734 	} else if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT || pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
735 		phandler = output_all ? handle_emit_all_to_stdfp : handle_emit_to_stdfp;
736 		pstate->stdfp = (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) ? stdout : stderr;
737 	} else {
738 		pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
739 			type_inferencing, context_flags);
740 		pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
741 		phandler = output_all ? handle_emit_all_to_file : handle_emit_to_file;
742 	}
743 	pstate->flush_every_record = pcst->flush_every_record;
744 
745 	return mlr_dsl_cst_statement_valloc(
746 		pnode,
747 		phandler,
748 		free_emit,
749 		pstate);
750 }
751 
752 // ----------------------------------------------------------------
free_emit(mlr_dsl_cst_statement_t * pstatement,context_t * pctx)753 static void free_emit(mlr_dsl_cst_statement_t* pstatement, context_t* pctx) {
754 	emit_state_t* pstate = pstatement->pvstate;
755 
756 	if (pstate->poutput_filename_evaluator != NULL) {
757 		pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
758 	}
759 
760 	if (pstate->prhs_xevaluator != NULL) {
761 		pstate->prhs_xevaluator->pfree_func(pstate->prhs_xevaluator);
762 	}
763 
764 	if (pstate->pemit_namelist_evaluators != NULL) {
765 		for (sllve_t* pe = pstate->pemit_namelist_evaluators->phead; pe != NULL; pe = pe->pnext) {
766 			rval_evaluator_t* phandler = pe->pvvalue;
767 			phandler->pfree_func(phandler);
768 		}
769 		sllv_free(pstate->pemit_namelist_evaluators);
770 	}
771 
772 	if (pstate->pemit_keylist_evaluators != NULL) {
773 		for (sllve_t* pe = pstate->pemit_keylist_evaluators->phead; pe != NULL; pe = pe->pnext) {
774 			rval_evaluator_t* phandler = pe->pvvalue;
775 			phandler->pfree_func(phandler);
776 		}
777 		sllv_free(pstate->pemit_keylist_evaluators);
778 	}
779 
780 	if (pstate->psingle_lrec_writer != NULL) {
781 		pstate->psingle_lrec_writer->pfree_func(pstate->psingle_lrec_writer, pctx);
782 	}
783 
784 	if (pstate->pmulti_lrec_writer != NULL) {
785 		multi_lrec_writer_drain(pstate->pmulti_lrec_writer, pctx);
786 		multi_lrec_writer_free(pstate->pmulti_lrec_writer, pctx);
787 	}
788 
789 	free(pstate);
790 }
791 
792 // ----------------------------------------------------------------
handle_emit(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)793 static void handle_emit(
794 	mlr_dsl_cst_statement_t* pstatement,
795 	variables_t*             pvars,
796 	cst_outputs_t*           pcst_outputs)
797 {
798 	emit_state_t* pstate = pstatement->pvstate;
799 	pstate->precord_emitter(pstate, pvars, pcst_outputs->poutrecs, pcst_outputs->oosvar_flatten_separator);
800 }
801 
802 // ----------------------------------------------------------------
handle_emit_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)803 static void handle_emit_to_stdfp(
804 	mlr_dsl_cst_statement_t* pstatement,
805 	variables_t*             pvars,
806 	cst_outputs_t*           pcst_outputs)
807 {
808 	emit_state_t* pstate = pstatement->pvstate;
809 	sllv_t* poutrecs = sllv_alloc();
810 
811 	pstate->precord_emitter(pstate, pvars, poutrecs, pcst_outputs->oosvar_flatten_separator);
812 
813 	// The opts aren't complete at alloc time so we need to handle them on first use.
814 	if (pstate->psingle_lrec_writer == NULL)
815 		pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
816 
817 	lrec_writer_print_all(pstate->psingle_lrec_writer, pstate->stdfp, poutrecs, pvars->pctx);
818 	if (pstate->flush_every_record)
819 		fflush(pstate->stdfp);
820 
821 	sllv_free(poutrecs);
822 }
823 
824 // ----------------------------------------------------------------
handle_emit_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)825 static void handle_emit_to_file(
826 	mlr_dsl_cst_statement_t* pstatement,
827 	variables_t*             pvars,
828 	cst_outputs_t*           pcst_outputs)
829 {
830 	emit_state_t* pstate = pstatement->pvstate;
831 
832 	// The opts aren't complete at alloc time so we need to handle them on first use.
833 	if (pstate->pmulti_lrec_writer == NULL)
834 		pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
835 
836 	sllv_t* poutrecs = sllv_alloc();
837 
838 	pstate->precord_emitter(pstate, pvars, poutrecs, pcst_outputs->oosvar_flatten_separator);
839 
840 	rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
841 	mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
842 	char fn_free_flags = 0;
843 	char* filename = mv_format_val(&filename_mv, &fn_free_flags);
844 
845 	multi_lrec_writer_output_list(pstate->pmulti_lrec_writer, poutrecs, filename,
846 		pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
847 	sllv_free(poutrecs);
848 
849 	if (fn_free_flags)
850 		free(filename);
851 	mv_free(&filename_mv);
852 }
853 
854 // ----------------------------------------------------------------
record_emitter_from_oosvar(emit_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)855 static void record_emitter_from_oosvar(
856 	emit_state_t* pstate,
857 	variables_t*  pvars,
858 	sllv_t*       poutrecs,
859 	char*         oosvar_flatten_separator)
860 {
861 	int keys_all_non_null_or_error = TRUE;
862 	sllmv_t* pmvkeys = evaluate_list(pstate->pemit_keylist_evaluators, pvars, &keys_all_non_null_or_error);
863 	if (keys_all_non_null_or_error) {
864 		int names_all_non_null_or_error = TRUE;
865 		sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars,
866 			&names_all_non_null_or_error);
867 		if (names_all_non_null_or_error) {
868 			mlhmmv_root_partial_to_lrecs(pvars->poosvars, pmvkeys, pmvnames, poutrecs,
869 				pstate->do_full_prefixing, oosvar_flatten_separator);
870 		}
871 		sllmv_free(pmvnames);
872 	}
873 	sllmv_free(pmvkeys);
874 }
875 
record_emitter_from_local_variable(emit_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)876 static void record_emitter_from_local_variable(
877 	emit_state_t* pstate,
878 	variables_t*  pvars,
879 	sllv_t*       poutrecs,
880 	char*         oosvar_flatten_separator)
881 {
882 	int keys_all_non_null_or_error = TRUE;
883 	sllmv_t* pmvkeys = evaluate_list(pstate->pemit_keylist_evaluators, pvars, &keys_all_non_null_or_error);
884 
885 	mv_t name = mv_from_string(pstate->localvar_name, NO_FREE);
886 	sllmv_prepend_no_free(pmvkeys, &name);
887 
888 	if (keys_all_non_null_or_error) {
889 		int names_all_non_null_or_error = TRUE;
890 		sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars,
891 			&names_all_non_null_or_error);
892 		if (names_all_non_null_or_error) {
893 
894 			local_stack_frame_t* pframe = local_stack_get_top_frame(pvars->plocal_stack);
895 			mlhmmv_xvalue_t* pmval = local_stack_frame_ref_extended_from_indexed(pframe,
896 				pstate->localvar_frame_relative_index, NULL);
897 			if (pmval != NULL) {
898 				// Temporarily wrap the localvar in a parent map whose single key is the variable name.
899 				mlhmmv_root_t* pmap = mlhmmv_wrap_name_and_xvalue(&name, pmval);
900 
901 				mlhmmv_root_partial_to_lrecs(pmap, pmvkeys, pmvnames, poutrecs,
902 					pstate->do_full_prefixing, oosvar_flatten_separator);
903 
904 				mlhmmv_unwrap_name_and_xvalue(pmap);
905 			}
906 		}
907 		sllmv_free(pmvnames);
908 	}
909 	sllmv_free(pmvkeys);
910 }
911 
record_emitter_from_full_srec(emit_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)912 static void record_emitter_from_full_srec(
913 	emit_state_t* pstate,
914 	variables_t*  pvars,
915 	sllv_t*       poutrecs,
916 	char*         oosvar_flatten_separator)
917 {
918 	sllv_append(poutrecs, lrec_copy(pvars->pinrec));
919 }
920 
record_emitter_from_ephemeral_map(emit_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)921 static void record_emitter_from_ephemeral_map(
922 	emit_state_t* pstate,
923 	variables_t*  pvars,
924 	sllv_t*       poutrecs,
925 	char*         oosvar_flatten_separator)
926 {
927 	rxval_evaluator_t* prhs_xevaluator = pstate->prhs_xevaluator;
928 	boxed_xval_t boxed_xval = prhs_xevaluator->pprocess_func(prhs_xevaluator->pvstate, pvars);
929 	sllmv_t* pmvkeys = sllmv_alloc();
930 
931 	if (!boxed_xval.xval.is_terminal) {
932 		int names_all_non_null_or_error = TRUE;
933 		sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars,
934 			&names_all_non_null_or_error);
935 		if (names_all_non_null_or_error) {
936 			mv_t name = mv_from_string("_", NO_FREE);
937 			sllmv_prepend_no_free(pmvkeys, &name);
938 
939 			// Temporarily wrap the localvar in a parent map whose single key is the variable name.
940 			mlhmmv_root_t* pmap = mlhmmv_wrap_name_and_xvalue(&name, &boxed_xval.xval);
941 
942 			mlhmmv_root_partial_to_lrecs(pmap, pmvkeys, pmvnames, poutrecs,
943 				pstate->do_full_prefixing, oosvar_flatten_separator);
944 
945 			mlhmmv_unwrap_name_and_xvalue(pmap);
946 		}
947 		sllmv_free(pmvnames);
948 	}
949 
950 	if (boxed_xval.is_ephemeral) {
951 		mlhmmv_xvalue_free(&boxed_xval.xval);
952 	}
953 
954 	sllmv_free(pmvkeys);
955 }
956 
957 // ----------------------------------------------------------------
handle_emit_all(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)958 static void handle_emit_all(
959 	mlr_dsl_cst_statement_t* pstatement,
960 	variables_t*             pvars,
961 	cst_outputs_t*           pcst_outputs)
962 {
963 	emit_state_t* pstate = pstatement->pvstate;
964 	int all_non_null_or_error = TRUE;
965 	sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars, &all_non_null_or_error);
966 	if (all_non_null_or_error) {
967 		mlhmmv_root_all_to_lrecs(pvars->poosvars, pmvnames, pcst_outputs->poutrecs,
968 			pstate->do_full_prefixing, pcst_outputs->oosvar_flatten_separator);
969 	}
970 	sllmv_free(pmvnames);
971 }
972 
973 // ----------------------------------------------------------------
handle_emit_all_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)974 static void handle_emit_all_to_stdfp(
975 	mlr_dsl_cst_statement_t* pstatement,
976 	variables_t*             pvars,
977 	cst_outputs_t*           pcst_outputs)
978 {
979 	emit_state_t* pstate = pstatement->pvstate;
980 
981 	// The opts aren't complete at alloc time so we need to handle them on first use.
982 	if (pstate->psingle_lrec_writer == NULL)
983 		pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
984 
985 	sllv_t* poutrecs = sllv_alloc();
986 	int all_non_null_or_error = TRUE;
987 	sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars, &all_non_null_or_error);
988 	if (all_non_null_or_error) {
989 		mlhmmv_root_all_to_lrecs(pvars->poosvars, pmvnames, poutrecs,
990 			pstate->do_full_prefixing, pcst_outputs->oosvar_flatten_separator);
991 	}
992 	sllmv_free(pmvnames);
993 
994 	lrec_writer_print_all(pstate->psingle_lrec_writer, pstate->stdfp, poutrecs, pvars->pctx);
995 	if (pstate->flush_every_record)
996 		fflush(pstate->stdfp);
997 	sllv_free(poutrecs);
998 }
999 
1000 // ----------------------------------------------------------------
handle_emit_all_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1001 static void handle_emit_all_to_file(
1002 	mlr_dsl_cst_statement_t* pstatement,
1003 	variables_t*             pvars,
1004 	cst_outputs_t*           pcst_outputs)
1005 {
1006 	emit_state_t* pstate = pstatement->pvstate;
1007 
1008 	// The opts aren't complete at alloc time so we need to handle them on first use.
1009 	if (pstate->pmulti_lrec_writer == NULL)
1010 		pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
1011 
1012 	sllv_t* poutrecs = sllv_alloc();
1013 	rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
1014 	mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
1015 	int all_non_null_or_error = TRUE;
1016 	sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars, &all_non_null_or_error);
1017 	if (all_non_null_or_error) {
1018 		mlhmmv_root_all_to_lrecs(pvars->poosvars, pmvnames, poutrecs,
1019 			pstate->do_full_prefixing, pcst_outputs->oosvar_flatten_separator);
1020 	}
1021 
1022 	char fn_free_flags = 0;
1023 	char* filename = mv_format_val(&filename_mv, &fn_free_flags);
1024 	multi_lrec_writer_output_list(pstate->pmulti_lrec_writer, poutrecs, filename,
1025 		pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
1026 	sllv_free(poutrecs);
1027 
1028 	if (fn_free_flags)
1029 		free(filename);
1030 	mv_free(&filename_mv);
1031 	sllmv_free(pmvnames);
1032 }
1033 
1034 // ================================================================
1035 struct _emit_lashed_item_t; // Forward reference
1036 
1037 typedef struct _emit_lashed_item_t {
1038 	rval_evaluator_t*  pbasename_evaluator;
1039 	rxval_evaluator_t* pitem_xevaluator;
1040 } emit_lashed_item_t;
1041 
emit_lashed_item_alloc(rval_evaluator_t * pbasename_evaluator,rxval_evaluator_t * pitem_xevaluator)1042 static emit_lashed_item_t* emit_lashed_item_alloc(
1043 	rval_evaluator_t*  pbasename_evaluator,
1044 	rxval_evaluator_t* pitem_xevaluator)
1045 {
1046 	emit_lashed_item_t* pitem = mlr_malloc_or_die(sizeof(emit_lashed_item_t));
1047 	pitem->pbasename_evaluator = pbasename_evaluator;
1048 	pitem->pitem_xevaluator = pitem_xevaluator;
1049 	return pitem;
1050 }
1051 
emit_lashed_item_free(emit_lashed_item_t * pitem)1052 static void emit_lashed_item_free(emit_lashed_item_t* pitem) {
1053 	pitem->pbasename_evaluator->pfree_func(pitem->pbasename_evaluator);
1054 	pitem->pitem_xevaluator->pfree_func(pitem->pitem_xevaluator);
1055 	free(pitem);
1056 }
1057 
1058 // ----------------------------------------------------------------
1059 typedef struct _emit_lashed_state_t {
1060 	int                  num_emit_lashed_items;
1061 	emit_lashed_item_t** ppitems;
1062 	sllv_t*              pemit_namelist_evaluators;
1063 
1064 	// Used per-call but allocated once in the constructor:
1065 	mv_t*                pbasenames;
1066 	boxed_xval_t*        pboxed_xvals;
1067 	mlhmmv_xvalue_t**    ptop_values;
1068 
1069 	rval_evaluator_t*    poutput_filename_evaluator;
1070 	FILE*                stdfp;
1071 	file_output_mode_t   file_output_mode;
1072 	lrec_writer_t*       psingle_lrec_writer; // emit/tee to stdout/stderr
1073 	multi_lrec_writer_t* pmulti_lrec_writer;  // emit-to-file
1074 
1075 	int                  do_full_prefixing;
1076 	int                  flush_every_record;
1077 } emit_lashed_state_t;
1078 
1079 static mlr_dsl_cst_statement_handler_t handle_emit_lashed;
1080 static mlr_dsl_cst_statement_handler_t handle_emit_lashed_to_stdfp;
1081 static mlr_dsl_cst_statement_handler_t handle_emit_lashed_to_file;
1082 static void handle_emit_lashed_common(
1083 	emit_lashed_state_t* pstate,
1084 	variables_t*         pvars,
1085 	sllv_t*              poutrecs,
1086 	char*                oosvar_flatten_separator);
1087 static void free_emit_lashed(mlr_dsl_cst_statement_t* pstatement, context_t* pctx);
1088 
1089 // ----------------------------------------------------------------
alloc_emit_lashed(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags,int do_full_prefixing)1090 mlr_dsl_cst_statement_t* alloc_emit_lashed(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
1091 	int type_inferencing, int context_flags, int do_full_prefixing)
1092 {
1093 	emit_lashed_state_t* pstate = mlr_malloc_or_die(sizeof(emit_lashed_state_t));
1094 
1095 	pstate->num_emit_lashed_items      = 0;
1096 	pstate->ppitems                    = NULL;
1097 	pstate->pemit_namelist_evaluators  = NULL;
1098 
1099 	pstate->poutput_filename_evaluator = NULL;
1100 	pstate->stdfp                      = NULL;
1101 	pstate->psingle_lrec_writer        = NULL;
1102 	pstate->pmulti_lrec_writer         = NULL;
1103 
1104 	mlr_dsl_ast_node_t* pemit_node = pnode->pchildren->phead->pvvalue;
1105 	mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pnext->pvvalue;
1106 	mlr_dsl_ast_node_t* pkeylists_node = pemit_node->pchildren->phead->pvvalue;
1107 
1108 	pstate->num_emit_lashed_items = pkeylists_node->pchildren->length;
1109 	pstate->ppitems = mlr_malloc_or_die(pstate->num_emit_lashed_items * sizeof(emit_lashed_item_t*));
1110 	int i = 0;
1111 	for (sllve_t* pe = pkeylists_node->pchildren->phead; pe != NULL; pe = pe->pnext, i++) {
1112 		mlr_dsl_ast_node_t* pkeylist_node = pe->pvvalue;
1113 
1114 		rval_evaluator_t* pbasename_evaluator = NULL;
1115 		switch (pkeylist_node->type) {
1116 		case MD_AST_NODE_TYPE_NONINDEXED_LOCAL_VARIABLE:
1117 		case MD_AST_NODE_TYPE_INDEXED_LOCAL_VARIABLE:
1118 			pbasename_evaluator = rval_evaluator_alloc_from_string(pkeylist_node->text);
1119 			break;
1120 		case MD_AST_NODE_TYPE_OOSVAR_KEYLIST:
1121 			pbasename_evaluator = rval_evaluator_alloc_from_string(
1122 				((mlr_dsl_ast_node_t*)pkeylist_node->pchildren->phead->pvvalue)->text);
1123 			break;
1124 		default:
1125 			pbasename_evaluator = rval_evaluator_alloc_from_string("_");
1126 			break;
1127 		}
1128 
1129 		rxval_evaluator_t* pitem_xevaluator = rxval_evaluator_alloc_from_ast(
1130 			pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
1131 
1132 		pstate->ppitems[i] = emit_lashed_item_alloc(pbasename_evaluator, pitem_xevaluator);
1133 	}
1134 
1135 	sllv_t* pemit_namelist_evaluators = sllv_alloc();
1136 	if (pemit_node->pchildren->length == 2) {
1137 		mlr_dsl_ast_node_t* pnamelist_node = pemit_node->pchildren->phead->pnext->pvvalue;
1138 		for (sllve_t* pe = pnamelist_node->pchildren->phead; pe != NULL; pe = pe->pnext) {
1139 			mlr_dsl_ast_node_t* pkeynode = pe->pvvalue;
1140 			sllv_append(pemit_namelist_evaluators,
1141 				rval_evaluator_alloc_from_ast(pkeynode, pcst->pfmgr, type_inferencing, context_flags));
1142 		}
1143 	}
1144 	pstate->pemit_namelist_evaluators = pemit_namelist_evaluators;
1145 
1146 	pstate->pbasenames   = mlr_malloc_or_die(pstate->num_emit_lashed_items * sizeof(mv_t));
1147 	pstate->pboxed_xvals = mlr_malloc_or_die(pstate->num_emit_lashed_items * sizeof(boxed_xval_t));
1148 	pstate->ptop_values  = mlr_malloc_or_die(pstate->num_emit_lashed_items * sizeof(mlhmmv_level_entry_t*));
1149 
1150 	mlr_dsl_cst_statement_handler_t* phandler = NULL;
1151 	pstate->do_full_prefixing = do_full_prefixing;
1152 	mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren == NULL
1153 		? NULL
1154 		: poutput_node->pchildren->phead == NULL
1155 		? NULL
1156 		: poutput_node->pchildren->phead->pvvalue;
1157 	if (poutput_node->type == MD_AST_NODE_TYPE_STREAM) {
1158 		phandler = handle_emit_lashed;
1159 	} else if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT || pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
1160 		phandler = handle_emit_lashed_to_stdfp;
1161 		pstate->stdfp = (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) ? stdout : stderr;
1162 	} else {
1163 		pstate->poutput_filename_evaluator = NULL;
1164 		pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
1165 			type_inferencing, context_flags);
1166 		pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
1167 		phandler = handle_emit_lashed_to_file;
1168 	}
1169 	pstate->flush_every_record = pcst->flush_every_record;
1170 
1171 	return mlr_dsl_cst_statement_valloc(
1172 		pnode,
1173 		phandler,
1174 		free_emit_lashed,
1175 		pstate);
1176 }
1177 
1178 // ----------------------------------------------------------------
free_emit_lashed(mlr_dsl_cst_statement_t * pstatement,context_t * pctx)1179 static void free_emit_lashed(mlr_dsl_cst_statement_t* pstatement, context_t* pctx) {
1180 	emit_lashed_state_t* pstate = pstatement->pvstate;
1181 
1182 	if (pstate->ppitems != NULL) {
1183 		for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1184 			emit_lashed_item_free(pstate->ppitems[i]);
1185 		}
1186 		free(pstate->ppitems);
1187 	}
1188 
1189 	if (pstate->poutput_filename_evaluator != NULL) {
1190 		pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
1191 	}
1192 
1193 	if (pstate->pemit_namelist_evaluators != NULL) {
1194 		for (sllve_t* pe = pstate->pemit_namelist_evaluators->phead; pe != NULL; pe = pe->pnext) {
1195 			rval_evaluator_t* phandler = pe->pvvalue;
1196 			phandler->pfree_func(phandler);
1197 		}
1198 		sllv_free(pstate->pemit_namelist_evaluators);
1199 	}
1200 
1201 	free(pstate->pbasenames);
1202 	free(pstate->pboxed_xvals);
1203 	free(pstate->ptop_values);
1204 
1205 	if (pstate->psingle_lrec_writer != NULL) {
1206 		pstate->psingle_lrec_writer->pfree_func(pstate->psingle_lrec_writer, pctx);
1207 	}
1208 
1209 	if (pstate->pmulti_lrec_writer != NULL) {
1210 		multi_lrec_writer_drain(pstate->pmulti_lrec_writer, pctx);
1211 		multi_lrec_writer_free(pstate->pmulti_lrec_writer, pctx);
1212 	}
1213 
1214 	free(pstate);
1215 }
1216 
1217 // ----------------------------------------------------------------
handle_emit_lashed(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1218 static void handle_emit_lashed(
1219 	mlr_dsl_cst_statement_t* pstatement,
1220 	variables_t*             pvars,
1221 	cst_outputs_t*           pcst_outputs)
1222 {
1223 	emit_lashed_state_t* pstate = pstatement->pvstate;
1224 	handle_emit_lashed_common(pstate, pvars, pcst_outputs->poutrecs, pcst_outputs->oosvar_flatten_separator);
1225 }
1226 
handle_emit_lashed_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1227 static void handle_emit_lashed_to_stdfp(
1228 	mlr_dsl_cst_statement_t* pstatement,
1229 	variables_t*             pvars,
1230 	cst_outputs_t*           pcst_outputs)
1231 {
1232 	emit_lashed_state_t* pstate = pstatement->pvstate;
1233 
1234 	// The opts aren't complete at alloc time so we need to handle them on first use.
1235 	if (pstate->psingle_lrec_writer == NULL)
1236 		pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
1237 
1238 	sllv_t* poutrecs = sllv_alloc();
1239 
1240 	handle_emit_lashed_common(pstate, pvars, poutrecs, pcst_outputs->oosvar_flatten_separator);
1241 
1242 	lrec_writer_print_all(pstate->psingle_lrec_writer, pstate->stdfp, poutrecs, pvars->pctx);
1243 	if (pstate->flush_every_record)
1244 		fflush(pstate->stdfp);
1245 
1246 	sllv_free(poutrecs);
1247 }
1248 
1249 // ----------------------------------------------------------------
handle_emit_lashed_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1250 static void handle_emit_lashed_to_file(
1251 	mlr_dsl_cst_statement_t* pstatement,
1252 	variables_t*             pvars,
1253 	cst_outputs_t*           pcst_outputs)
1254 {
1255 	emit_lashed_state_t* pstate = pstatement->pvstate;
1256 
1257 	// The opts aren't complete at alloc time so we need to handle them on first use.
1258 	if (pstate->pmulti_lrec_writer == NULL)
1259 		pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
1260 
1261 	sllv_t* poutrecs = sllv_alloc();
1262 
1263 	handle_emit_lashed_common(pstate, pvars, poutrecs, pcst_outputs->oosvar_flatten_separator);
1264 
1265 	rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
1266 	mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
1267 	char fn_free_flags = 0;
1268 	char* filename = mv_format_val(&filename_mv, &fn_free_flags);
1269 
1270 	multi_lrec_writer_output_list(pstate->pmulti_lrec_writer, poutrecs, filename,
1271 		pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
1272 
1273 	sllv_free(poutrecs);
1274 
1275 	if (fn_free_flags)
1276 		free(filename);
1277 	mv_free(&filename_mv);
1278 }
1279 
1280 // ----------------------------------------------------------------
handle_emit_lashed_common(emit_lashed_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)1281 static void handle_emit_lashed_common(
1282 	emit_lashed_state_t* pstate,
1283 	variables_t*         pvars,
1284 	sllv_t*              poutrecs,
1285 	char*                oosvar_flatten_separator)
1286 {
1287 	for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1288 		rval_evaluator_t* pev = pstate->ppitems[i]->pbasename_evaluator;
1289 		pstate->pbasenames[i] = pev->pprocess_func(pev->pvstate, pvars);
1290 	}
1291 
1292 	int names_all_non_null_or_error = TRUE;
1293 	sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars,
1294 		&names_all_non_null_or_error);
1295 	if (names_all_non_null_or_error) {
1296 
1297 		for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1298 			emit_lashed_item_t* pitem = pstate->ppitems[i];
1299 			pstate->pboxed_xvals[i] = pitem->pitem_xevaluator->pprocess_func(
1300 				pitem->pitem_xevaluator->pvstate, pvars);
1301 			pstate->ptop_values[i] = &pstate->pboxed_xvals[i].xval;
1302 		}
1303 
1304 		mlhmmv_xvalues_to_lrecs_lashed(pstate->ptop_values, pstate->num_emit_lashed_items,
1305 			pstate->pbasenames, pmvnames, poutrecs, pstate->do_full_prefixing, oosvar_flatten_separator);
1306 
1307 		for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1308 			if (pstate->pboxed_xvals[i].is_ephemeral) {
1309 				mlhmmv_xvalue_free(&pstate->pboxed_xvals[i].xval);
1310 			}
1311 		}
1312 	}
1313 	sllmv_free(pmvnames);
1314 
1315 	for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1316 		mv_free(&pstate->pbasenames[i]);
1317 	}
1318 }
1319 
1320 // ================================================================
1321 struct _dump_state_t; // forward reference
1322 
1323 typedef struct _dump_state_t {
1324 	rxval_evaluator_t*    ptarget_xevaluator;
1325 
1326 	FILE*                 stdfp;
1327 	file_output_mode_t    file_output_mode;
1328 	rval_evaluator_t*     poutput_filename_evaluator;
1329 	int                   flush_every_record;
1330 	multi_out_t*          pmulti_out;
1331 } dump_state_t;
1332 
1333 static mlr_dsl_cst_statement_handler_t handle_dump;
1334 static mlr_dsl_cst_statement_handler_t handle_dump_to_file;
1335 static mlr_dsl_cst_statement_freer_t   free_dump;
1336 
1337 // ----------------------------------------------------------------
alloc_dump(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags)1338 mlr_dsl_cst_statement_t* alloc_dump(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
1339 	int type_inferencing, int context_flags)
1340 {
1341 	dump_state_t* pstate = mlr_malloc_or_die(sizeof(dump_state_t));
1342 
1343 	pstate->ptarget_xevaluator         = NULL;
1344 	pstate->stdfp                      = NULL;
1345 	pstate->poutput_filename_evaluator = NULL;
1346 	pstate->pmulti_out                 = NULL;
1347 	pstate->flush_every_record         = pcst->flush_every_record;
1348 
1349 	mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pvvalue;
1350 	mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren->phead->pvvalue;
1351 	mlr_dsl_cst_statement_handler_t* phandler = NULL;
1352 
1353 	MLR_INTERNAL_CODING_ERROR_UNLESS(pnode->pchildren->length == 2);
1354 
1355 	mlr_dsl_ast_node_t* ptarget_node = pnode->pchildren->phead->pnext->pvvalue;
1356 
1357 	pstate->ptarget_xevaluator = rxval_evaluator_alloc_from_ast(
1358 		ptarget_node, pcst->pfmgr, type_inferencing, context_flags);
1359 
1360 	if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) {
1361 		phandler = handle_dump;
1362 		pstate->stdfp = stdout;
1363 	} else if (pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
1364 		phandler = handle_dump;
1365 		pstate->stdfp = stderr;
1366 	} else {
1367 		pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
1368 			type_inferencing, context_flags);
1369 		pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
1370 		pstate->pmulti_out = multi_out_alloc();
1371 		phandler = handle_dump_to_file;
1372 	}
1373 
1374 	return mlr_dsl_cst_statement_valloc(
1375 		pnode,
1376 		phandler,
1377 		free_dump,
1378 		pstate);
1379 }
1380 
1381 // ----------------------------------------------------------------
free_dump(mlr_dsl_cst_statement_t * pstatement,context_t * _)1382 static void free_dump(mlr_dsl_cst_statement_t* pstatement, context_t* _) {
1383 	dump_state_t* pstate = pstatement->pvstate;
1384 
1385 	if (pstate->ptarget_xevaluator != NULL) {
1386 		pstate->ptarget_xevaluator->pfree_func(pstate->ptarget_xevaluator);
1387 	}
1388 	if (pstate->poutput_filename_evaluator != NULL) {
1389 		pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
1390 	}
1391 	if (pstate->pmulti_out != NULL) {
1392 		multi_out_close(pstate->pmulti_out);
1393 		multi_out_free(pstate->pmulti_out);
1394 	}
1395 
1396 	free(pstate);
1397 }
1398 
1399 // ----------------------------------------------------------------
handle_dump(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1400 static void handle_dump(
1401 	mlr_dsl_cst_statement_t* pstatement,
1402 	variables_t*             pvars,
1403 	cst_outputs_t*           pcst_outputs)
1404 {
1405 	dump_state_t* pstate = pstatement->pvstate;
1406 
1407 	rxval_evaluator_t* ptarget_xevaluator = pstate->ptarget_xevaluator;
1408 	boxed_xval_t boxed_xval = ptarget_xevaluator->pprocess_func(ptarget_xevaluator->pvstate, pvars);
1409 
1410 	if (boxed_xval.xval.is_terminal) {
1411 		mlhmmv_print_terminal(&boxed_xval.xval.terminal_mlrval,
1412 			pvars->json_quote_int_keys, pvars->json_quote_non_string_values,
1413 			pstate->stdfp);
1414 		fprintf(pstate->stdfp, "\n");
1415 	} else {
1416 		mlhmmv_level_print_stacked(boxed_xval.xval.pnext_level, 0, FALSE,
1417 			pvars->json_quote_int_keys, pvars->json_quote_non_string_values,
1418 			"", pvars->pctx->auto_line_term,
1419 			pstate->stdfp);
1420 	}
1421 
1422 	if (boxed_xval.is_ephemeral) {
1423 		mlhmmv_xvalue_free(&boxed_xval.xval);
1424 	}
1425 }
1426 
1427 // ----------------------------------------------------------------
handle_dump_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1428 static void handle_dump_to_file(
1429 	mlr_dsl_cst_statement_t* pstatement,
1430 	variables_t*             pvars,
1431 	cst_outputs_t*           pcst_outputs)
1432 {
1433 	dump_state_t* pstate = pstatement->pvstate;
1434 	rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
1435 	mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
1436 	char fn_free_flags;
1437 	char* filename = mv_format_val(&filename_mv, &fn_free_flags);
1438 
1439 	FILE* outfp = multi_out_get(pstate->pmulti_out, filename, pstate->file_output_mode);
1440 
1441 	rxval_evaluator_t* ptarget_xevaluator = pstate->ptarget_xevaluator;
1442 	boxed_xval_t boxed_xval = ptarget_xevaluator->pprocess_func(ptarget_xevaluator->pvstate, pvars);
1443 
1444 	if (boxed_xval.xval.is_terminal) {
1445 		mlhmmv_print_terminal(&boxed_xval.xval.terminal_mlrval,
1446 			pvars->json_quote_int_keys, pvars->json_quote_non_string_values,
1447 			outfp);
1448 		fprintf(outfp, "\n");
1449 	} else if (boxed_xval.xval.pnext_level != NULL) {
1450 		mlhmmv_level_print_stacked(boxed_xval.xval.pnext_level, 0, FALSE,
1451 			pvars->json_quote_int_keys, pvars->json_quote_non_string_values,
1452 			"", pvars->pctx->auto_line_term,
1453 			outfp);
1454 	}
1455 
1456 	if (pstate->flush_every_record)
1457 		fflush(outfp);
1458 
1459 	if (fn_free_flags)
1460 		free(filename);
1461 	mv_free(&filename_mv);
1462 
1463 	if (boxed_xval.is_ephemeral) {
1464 		mlhmmv_xvalue_free(&boxed_xval.xval);
1465 	}
1466 }
1467