1 #include <stdlib.h>
2 #include <string.h>
3 #include "lib/mlrutil.h"
4 #include "containers/sllv.h"
5 #include "containers/slls.h"
6 #include "containers/mixutil.h"
7 #include "output/lrec_writers.h"
8 
9 typedef struct _lrec_writer_pprint_state_t {
10 	sllv_t*    precords;
11 	slls_t*    pprev_keys;
12 	int        right_align;
13 	int        barred;
14 	int        headerless_output;
15 	long long  num_blocks_written;
16 	char*      ors;
17 	char       ofs;
18 } lrec_writer_pprint_state_t;
19 
20 static void lrec_writer_pprint_free(lrec_writer_t* pwriter, context_t* pctx);
21 static void lrec_writer_pprint_process(void* pvstate, FILE* output_stream, lrec_t* prec, char* ors);
22 static void lrec_writer_pprint_process_auto_ors(void* pvstate, FILE* output_stream, lrec_t* prec, context_t* pctx);
23 static void lrec_writer_pprint_process_nonauto_ors(void* pvstate, FILE* output_stream, lrec_t* prec, context_t* pctx);
24 static void print_and_free_record_list(sllv_t* precords, FILE* output_stream, char* ors, char ofs,
25 	int right_align, int headerless_output);
26 static void print_and_free_record_list_barred(sllv_t* precords, FILE* output_stream, char* ors, char ofs,
27 	int right_align, int headerless_output);
28 
29 // ----------------------------------------------------------------
lrec_writer_pprint_alloc(char * ors,char ofs,int right_align,int barred,int headerless_output)30 lrec_writer_t* lrec_writer_pprint_alloc(char* ors, char ofs, int right_align, int barred, int headerless_output) {
31 	lrec_writer_t* plrec_writer = mlr_malloc_or_die(sizeof(lrec_writer_t));
32 
33 	lrec_writer_pprint_state_t* pstate = mlr_malloc_or_die(sizeof(lrec_writer_pprint_state_t));
34 	pstate->precords           = sllv_alloc();
35 	pstate->pprev_keys         = NULL;
36 	pstate->ors                = ors;
37 	pstate->ofs                = ofs;
38 	pstate->right_align        = right_align;
39 	pstate->barred             = barred;
40 	pstate->headerless_output  = headerless_output;
41 	pstate->num_blocks_written = 0LL;
42 
43 	plrec_writer->pvstate       = pstate;
44 	plrec_writer->pprocess_func = streq(ors, "auto")
45 		? lrec_writer_pprint_process_auto_ors
46 		: lrec_writer_pprint_process_nonauto_ors;
47 	plrec_writer->pfree_func    = lrec_writer_pprint_free;
48 
49 	return plrec_writer;
50 }
51 
lrec_writer_pprint_free(lrec_writer_t * pwriter,context_t * pctx)52 static void lrec_writer_pprint_free(lrec_writer_t* pwriter, context_t* pctx) {
53 	lrec_writer_pprint_state_t* pstate = pwriter->pvstate;
54 	if (pstate->precords != NULL) {
55 		sllv_free(pstate->precords);
56 		pstate->precords = NULL;
57 	}
58 	if (pstate->pprev_keys != NULL) {
59 		slls_free(pstate->pprev_keys);
60 		pstate->pprev_keys = NULL;
61 	}
62 	free(pstate);
63 	free(pwriter);
64 }
65 
66 // ----------------------------------------------------------------
lrec_writer_pprint_process_auto_ors(void * pvstate,FILE * output_stream,lrec_t * prec,context_t * pctx)67 static void lrec_writer_pprint_process_auto_ors(void* pvstate, FILE* output_stream, lrec_t* prec, context_t* pctx) {
68 	lrec_writer_pprint_process(pvstate, output_stream, prec, pctx->auto_line_term);
69 }
70 
lrec_writer_pprint_process_nonauto_ors(void * pvstate,FILE * output_stream,lrec_t * prec,context_t * pctx)71 static void lrec_writer_pprint_process_nonauto_ors(void* pvstate, FILE* output_stream, lrec_t* prec, context_t* pctx) {
72 	lrec_writer_pprint_state_t* pstate = pvstate;
73 	lrec_writer_pprint_process(pvstate, output_stream, prec, pstate->ors);
74 }
75 
lrec_writer_pprint_process(void * pvstate,FILE * output_stream,lrec_t * prec,char * ors)76 static void lrec_writer_pprint_process(void* pvstate, FILE* output_stream, lrec_t* prec, char* ors) {
77 	lrec_writer_pprint_state_t* pstate = pvstate;
78 
79 	int drain = FALSE;
80 
81 	if (prec == NULL) {
82 		drain = TRUE;
83 	} else {
84 		if (pstate->pprev_keys != NULL && !lrec_keys_equal_list(prec, pstate->pprev_keys)) {
85 			drain = TRUE;
86 		}
87 	}
88 
89 	if (drain) {
90 		if (pstate->num_blocks_written > 0LL) // separate blocks with empty line
91 			fputs(ors, output_stream);
92 		if (pstate->barred) {
93 			print_and_free_record_list_barred(pstate->precords, output_stream, ors, pstate->ofs,
94 				pstate->right_align, pstate->headerless_output);
95 		} else {
96 			print_and_free_record_list(pstate->precords, output_stream, ors, pstate->ofs,
97 				pstate->right_align, pstate->headerless_output);
98 		}
99 		if (pstate->pprev_keys != NULL) {
100 			slls_free(pstate->pprev_keys);
101 			pstate->pprev_keys = NULL;
102 		}
103 		pstate->precords = sllv_alloc();
104 		pstate->num_blocks_written++;
105 	}
106 	if (prec != NULL) {
107 		sllv_append(pstate->precords, prec);
108 		if (pstate->pprev_keys == NULL)
109 			pstate->pprev_keys = mlr_copy_keys_from_record(prec);
110 	}
111 }
112 
113 // ----------------------------------------------------------------
print_and_free_record_list(sllv_t * precords,FILE * output_stream,char * ors,char ofs,int right_align,int headerless_output)114 static void print_and_free_record_list(sllv_t* precords, FILE* output_stream, char* ors, char ofs,
115 	int right_align, int headerless_output)
116 {
117 	if (precords->length == 0) {
118 		sllv_free(precords);
119 		return;
120 	}
121 	lrec_t* prec1 = precords->phead->pvvalue;
122 
123 	int* max_widths = mlr_malloc_or_die(sizeof(int) * prec1->field_count);
124 	int j = 0;
125 	for (lrece_t* pe = prec1->phead; pe != NULL; pe = pe->pnext, j++) {
126 		if (headerless_output) {
127 			max_widths[j] = 1;
128 		} else {
129 			max_widths[j] = strlen_for_utf8_display(pe->key);
130 		}
131 	}
132 	for (sllve_t* pnode = precords->phead; pnode != NULL; pnode = pnode->pnext) {
133 		lrec_t* prec = pnode->pvvalue;
134 		j = 0;
135 		for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
136 			int width = strlen_for_utf8_display(pe->value);
137 			if (width > max_widths[j])
138 				max_widths[j] = width;
139 		}
140 	}
141 
142 	int onr = 0;
143 	for (sllve_t* pnode = precords->phead; pnode != NULL; pnode = pnode->pnext, onr++) {
144 		lrec_t* prec = pnode->pvvalue;
145 
146 		if (onr == 0 && !headerless_output) {
147 			j = 0;
148 			for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
149 				if (j > 0) {
150 					fputc(ofs, output_stream);
151 				}
152 				if (!right_align) {
153 					if (pe->pnext == NULL) {
154 						fprintf(output_stream, "%s", pe->key);
155 					} else {
156 						// "%-*s" fprintf format isn't correct for non-ASCII UTF-8
157 						fprintf(output_stream, "%s", pe->key);
158 						int d = max_widths[j] - strlen_for_utf8_display(pe->key);
159 						for (int i = 0; i < d; i++)
160 							fputc(ofs, output_stream);
161 					}
162 				} else {
163 					int d = max_widths[j] - strlen_for_utf8_display(pe->key);
164 					for (int i = 0; i < d; i++)
165 						fputc(ofs, output_stream);
166 					fprintf(output_stream, "%s", pe->key);
167 				}
168 			}
169 			fputs(ors, output_stream);
170 		}
171 
172 		j = 0;
173 		for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
174 			if (j > 0) {
175 				fputc(ofs, output_stream);
176 			}
177 			char* value = pe->value;
178 			if (*value == 0) // empty string
179 				value = "-";
180 			if (!right_align) {
181 				if (pe->pnext == NULL) {
182 					fprintf(output_stream, "%s", value);
183 				} else {
184 					fprintf(output_stream, "%s", value);
185 					int d = max_widths[j] - strlen_for_utf8_display(value);
186 					for (int i = 0; i < d; i++)
187 						fputc(ofs, output_stream);
188 				}
189 			} else {
190 				int d = max_widths[j] - strlen_for_utf8_display(value);
191 				for (int i = 0; i < d; i++)
192 					fputc(ofs, output_stream);
193 				fprintf(output_stream, "%s", value);
194 			}
195 		}
196 		fputs(ors, output_stream);
197 
198 		lrec_free(prec); // end of baton-pass
199 	}
200 
201 	free(max_widths);
202 	sllv_free(precords);
203 }
204 
205 // ----------------------------------------------------------------
print_and_free_record_list_barred(sllv_t * precords,FILE * output_stream,char * ors,char ofs,int right_align,int headerless_output)206 static void print_and_free_record_list_barred(sllv_t* precords, FILE* output_stream, char* ors, char ofs,
207 	int right_align, int headerless_output)
208 {
209 	if (precords->length == 0) {
210 		sllv_free(precords);
211 		return;
212 	}
213 	lrec_t* prec1 = precords->phead->pvvalue;
214 
215 	int* max_widths = mlr_malloc_or_die(sizeof(int) * prec1->field_count);
216 	int j = 0;
217 	for (lrece_t* pe = prec1->phead; pe != NULL; pe = pe->pnext, j++) {
218 		if (headerless_output) {
219 			max_widths[j] = 1;
220 		} else {
221 			max_widths[j] = strlen_for_utf8_display(pe->key);
222 		}
223 	}
224 	for (sllve_t* pnode = precords->phead; pnode != NULL; pnode = pnode->pnext) {
225 		lrec_t* prec = pnode->pvvalue;
226 		j = 0;
227 		for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
228 			int width = strlen_for_utf8_display(pe->value);
229 			if (width > max_widths[j])
230 				max_widths[j] = width;
231 		}
232 	}
233 
234 	int onr = 0;
235 	for (sllve_t* pnode = precords->phead; pnode != NULL; pnode = pnode->pnext, onr++) {
236 		lrec_t* prec = pnode->pvvalue;
237 
238 		if (onr == 0 && !headerless_output) {
239 
240 			j = 0;
241 			fputc('+', output_stream);
242 			fputc('-', output_stream);
243 			for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
244 				if (j > 0) {
245 					fputc('-', output_stream);
246 				}
247 				int d = max_widths[j];
248 				for (int i = 0; i < d; i++)
249 					fputc('-', output_stream);
250 				fputc('-', output_stream);
251 				fputc('+', output_stream);
252 			}
253 			fputs(ors, output_stream);
254 
255 			j = 0;
256 			fputc('|', output_stream);
257 			fputc(ofs, output_stream);
258 			for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
259 				if (j > 0) {
260 					fputc(ofs, output_stream);
261 				}
262 				if (!right_align) {
263 					// "%-*s" fprintf format isn't correct for non-ASCII UTF-8
264 					fprintf(output_stream, "%s", pe->key);
265 					int d = max_widths[j] - strlen_for_utf8_display(pe->key);
266 					for (int i = 0; i < d; i++)
267 						fputc(ofs, output_stream);
268 					fputc(ofs, output_stream);
269 					fputc('|', output_stream);
270 				} else {
271 					int d = max_widths[j] - strlen_for_utf8_display(pe->key);
272 					for (int i = 0; i < d; i++)
273 						fputc(ofs, output_stream);
274 					fprintf(output_stream, "%s", pe->key);
275 					fputc(ofs, output_stream);
276 					fputc('|', output_stream);
277 				}
278 			}
279 			fputs(ors, output_stream);
280 
281 			j = 0;
282 			fputc('+', output_stream);
283 			fputc('-', output_stream);
284 			for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
285 				if (j > 0) {
286 					fputc('-', output_stream);
287 				}
288 				int d = max_widths[j];
289 				for (int i = 0; i < d; i++)
290 					fputc('-', output_stream);
291 				fputc('-', output_stream);
292 				fputc('+', output_stream);
293 			}
294 			fputs(ors, output_stream);
295 
296 		}
297 
298 		j = 0;
299 		fputc('|', output_stream);
300 		fputc(ofs, output_stream);
301 		for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
302 			if (j > 0) {
303 				fputc(ofs, output_stream);
304 			}
305 			char* value = pe->value;
306 			if (*value == 0) // empty string
307 				value = "-";
308 			if (!right_align) {
309 				fprintf(output_stream, "%s", value);
310 				int d = max_widths[j] - strlen_for_utf8_display(value);
311 				for (int i = 0; i < d; i++)
312 					fputc(ofs, output_stream);
313 				fputc(ofs, output_stream);
314 				fputc('|', output_stream);
315 			} else {
316 				int d = max_widths[j] - strlen_for_utf8_display(value);
317 				for (int i = 0; i < d; i++)
318 					fputc(ofs, output_stream);
319 				fprintf(output_stream, "%s", value);
320 				fputc(ofs, output_stream);
321 				fputc('|', output_stream);
322 			}
323 		}
324 		fputs(ors, output_stream);
325 
326 		if (pnode->pnext == NULL) {
327 			j = 0;
328 			fputc('+', output_stream);
329 			fputc('-', output_stream);
330 			for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext, j++) {
331 				if (j > 0) {
332 					fputc('-', output_stream);
333 				}
334 				int d = max_widths[j];
335 				for (int i = 0; i < d; i++)
336 					fputc('-', output_stream);
337 				fputc('-', output_stream);
338 				fputc('+', output_stream);
339 			}
340 			fputs(ors, output_stream);
341 		}
342 
343 		lrec_free(prec); // end of baton-pass
344 	}
345 
346 	free(max_widths);
347 	sllv_free(precords);
348 }
349