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