1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 #include "lib/mlr_globals.h"
6 #include "lib/mlrutil.h"
7 #include "lib/string_builder.h"
8 #include "containers/hss.h"
9 #include "containers/slls.h"
10 #include "containers/lrec.h"
11 
12 #define SB_ALLOC_LENGTH 256
13 
14 static lrece_t* lrec_find_entry(lrec_t* prec, char* key);
15 static void lrec_link_at_head(lrec_t* prec, lrece_t* pe);
16 static void lrec_link_at_tail(lrec_t* prec, lrece_t* pe);
17 
18 static void lrec_unbacked_free(lrec_t* prec);
19 static void lrec_free_single_line_backing(lrec_t* prec);
20 static void lrec_free_csv_backing(lrec_t* prec);
21 static void lrec_free_multiline_backing(lrec_t* prec);
22 
23 // ----------------------------------------------------------------
lrec_unbacked_alloc()24 lrec_t* lrec_unbacked_alloc() {
25 	lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
26 	memset(prec, 0, sizeof(lrec_t));
27 	prec->pfree_backing_func = lrec_unbacked_free;
28 	return prec;
29 }
30 
lrec_dkvp_alloc(char * line)31 lrec_t* lrec_dkvp_alloc(char* line) {
32 	lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
33 	memset(prec, 0, sizeof(lrec_t));
34 	prec->psingle_line = line;
35 	prec->pfree_backing_func = lrec_free_single_line_backing;
36 	return prec;
37 }
38 
lrec_nidx_alloc(char * line)39 lrec_t* lrec_nidx_alloc(char* line) {
40 	lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
41 	memset(prec, 0, sizeof(lrec_t));
42 	prec->psingle_line  = line;
43 	prec->pfree_backing_func = lrec_free_single_line_backing;
44 	return prec;
45 }
46 
lrec_csvlite_alloc(char * data_line)47 lrec_t* lrec_csvlite_alloc(char* data_line) {
48 	lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
49 	memset(prec, 0, sizeof(lrec_t));
50 	prec->psingle_line = data_line;
51 	prec->pfree_backing_func = lrec_free_csv_backing;
52 	return prec;
53 }
54 
lrec_csv_alloc(char * data_line)55 lrec_t* lrec_csv_alloc(char* data_line) {
56 	lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
57 	memset(prec, 0, sizeof(lrec_t));
58 	prec->psingle_line = data_line;
59 	prec->pfree_backing_func = lrec_free_csv_backing;
60 	return prec;
61 }
62 
lrec_xtab_alloc(slls_t * pxtab_lines)63 lrec_t* lrec_xtab_alloc(slls_t* pxtab_lines) {
64 	lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
65 	memset(prec, 0, sizeof(lrec_t));
66 	prec->pxtab_lines = pxtab_lines;
67 	prec->pfree_backing_func = lrec_free_multiline_backing;
68 	return prec;
69 }
70 
71 // ----------------------------------------------------------------
lrec_free_contents(lrec_t * prec)72 static void lrec_free_contents(lrec_t* prec) {
73 	for (lrece_t* pe = prec->phead; pe != NULL; /*pe = pe->pnext*/) {
74 		if (pe->free_flags & FREE_ENTRY_KEY)
75 			free(pe->key);
76 		if (pe->free_flags & FREE_ENTRY_VALUE)
77 			free(pe->value);
78 		lrece_t* ope = pe;
79 		pe = pe->pnext;
80 		free(ope);
81 	}
82 	prec->pfree_backing_func(prec);
83 }
84 
85 // ----------------------------------------------------------------
lrec_clear(lrec_t * prec)86 void lrec_clear(lrec_t* prec) {
87 	if (prec == NULL)
88 		return;
89 	lrec_free_contents(prec);
90 	memset(prec, 0, sizeof(lrec_t));
91 	prec->pfree_backing_func = lrec_unbacked_free;
92 }
93 
94 // ----------------------------------------------------------------
lrec_free(lrec_t * prec)95 void lrec_free(lrec_t* prec) {
96 	if (prec == NULL)
97 		return;
98 	lrec_free_contents(prec);
99 	free(prec);
100 }
101 
102 // ----------------------------------------------------------------
lrec_copy(lrec_t * pinrec)103 lrec_t* lrec_copy(lrec_t* pinrec) {
104 	lrec_t* poutrec = lrec_unbacked_alloc();
105 	for (lrece_t* pe = pinrec->phead; pe != NULL; pe = pe->pnext) {
106 		lrec_put(poutrec, mlr_strdup_or_die(pe->key), mlr_strdup_or_die(pe->value),
107 			FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
108 	}
109 	return poutrec;
110 }
111 
112 // ----------------------------------------------------------------
lrec_put(lrec_t * prec,char * key,char * value,char free_flags)113 void lrec_put(lrec_t* prec, char* key, char* value, char free_flags) {
114 	lrece_t* pe = lrec_find_entry(prec, key);
115 
116 	if (pe != NULL) {
117 		if (pe->free_flags & FREE_ENTRY_VALUE) {
118 			free(pe->value);
119 		}
120 		if (free_flags & FREE_ENTRY_KEY)
121 			free(key);
122 		pe->value = value;
123 		if (free_flags & FREE_ENTRY_VALUE)
124 			pe->free_flags |= FREE_ENTRY_VALUE;
125 		else
126 			pe->free_flags &= ~FREE_ENTRY_VALUE;
127 	} else {
128 		pe = mlr_malloc_or_die(sizeof(lrece_t));
129 		pe->key         = key;
130 		pe->value       = value;
131 		pe->free_flags  = free_flags;
132 		pe->quote_flags = 0;
133 
134 		if (prec->phead == NULL) {
135 			pe->pprev   = NULL;
136 			pe->pnext   = NULL;
137 			prec->phead = pe;
138 			prec->ptail = pe;
139 		} else {
140 			pe->pprev   = prec->ptail;
141 			pe->pnext   = NULL;
142 			prec->ptail->pnext = pe;
143 			prec->ptail = pe;
144 		}
145 		prec->field_count++;
146 	}
147 }
148 
lrec_put_ext(lrec_t * prec,char * key,char * value,char free_flags,char quote_flags)149 void lrec_put_ext(lrec_t* prec, char* key, char* value, char free_flags, char quote_flags) {
150 	lrece_t* pe = lrec_find_entry(prec, key);
151 
152 	if (pe != NULL) {
153 		if (pe->free_flags & FREE_ENTRY_VALUE) {
154 			free(pe->value);
155 		}
156 		if (free_flags & FREE_ENTRY_KEY)
157 			free(key);
158 		pe->value = value;
159 		if (free_flags & FREE_ENTRY_VALUE)
160 			pe->free_flags |= FREE_ENTRY_VALUE;
161 		else
162 			pe->free_flags &= ~FREE_ENTRY_VALUE;
163 	} else {
164 		pe = mlr_malloc_or_die(sizeof(lrece_t));
165 		pe->key         = key;
166 		pe->value       = value;
167 		pe->free_flags  = free_flags;
168 		pe->quote_flags = quote_flags;
169 
170 		if (prec->phead == NULL) {
171 			pe->pprev   = NULL;
172 			pe->pnext   = NULL;
173 			prec->phead = pe;
174 			prec->ptail = pe;
175 		} else {
176 			pe->pprev   = prec->ptail;
177 			pe->pnext   = NULL;
178 			prec->ptail->pnext = pe;
179 			prec->ptail = pe;
180 		}
181 		prec->field_count++;
182 	}
183 }
184 
lrec_prepend(lrec_t * prec,char * key,char * value,char free_flags)185 void lrec_prepend(lrec_t* prec, char* key, char* value, char free_flags) {
186 	lrece_t* pe = lrec_find_entry(prec, key);
187 
188 	if (pe != NULL) {
189 		if (pe->free_flags & FREE_ENTRY_VALUE) {
190 			free(pe->value);
191 		}
192 		pe->value = value;
193 		pe->free_flags &= ~FREE_ENTRY_VALUE;
194 		if (free_flags & FREE_ENTRY_VALUE)
195 			pe->free_flags |= FREE_ENTRY_VALUE;
196 	} else {
197 		pe = mlr_malloc_or_die(sizeof(lrece_t));
198 		pe->key         = key;
199 		pe->value       = value;
200 		pe->free_flags  = free_flags;
201 		pe->quote_flags = 0;
202 
203 		if (prec->phead == NULL) {
204 			pe->pprev   = NULL;
205 			pe->pnext   = NULL;
206 			prec->phead = pe;
207 			prec->ptail = pe;
208 		} else {
209 			pe->pnext   = prec->phead;
210 			pe->pprev   = NULL;
211 			prec->phead->pprev = pe;
212 			prec->phead = pe;
213 		}
214 		prec->field_count++;
215 	}
216 }
217 
lrec_put_after(lrec_t * prec,lrece_t * pd,char * key,char * value,char free_flags)218 lrece_t* lrec_put_after(lrec_t* prec, lrece_t* pd, char* key, char* value, char free_flags) {
219 	lrece_t* pe = lrec_find_entry(prec, key);
220 
221 	if (pe != NULL) { // Overwrite
222 		if (pe->free_flags & FREE_ENTRY_VALUE) {
223 			free(pe->value);
224 		}
225 		pe->value = value;
226 		pe->free_flags &= ~FREE_ENTRY_VALUE;
227 		if (free_flags & FREE_ENTRY_VALUE)
228 			pe->free_flags |= FREE_ENTRY_VALUE;
229 	} else { // Insert after specified entry
230 		pe = mlr_malloc_or_die(sizeof(lrece_t));
231 		pe->key         = key;
232 		pe->value       = value;
233 		pe->free_flags  = free_flags;
234 		pe->quote_flags = 0;
235 
236 		if (pd->pnext == NULL) { // Append at end of list
237 			pd->pnext = pe;
238 			pe->pprev = pd;
239 			pe->pnext = NULL;
240 			prec->ptail = pe;
241 
242 		} else {
243 			lrece_t* pf = pd->pnext;
244 			pd->pnext = pe;
245 			pf->pprev = pe;
246 			pe->pprev = pd;
247 			pe->pnext = pf;
248 		}
249 
250 		prec->field_count++;
251 	}
252 	return pe;
253 }
254 
255 // ----------------------------------------------------------------
lrec_get(lrec_t * prec,char * key)256 char* lrec_get(lrec_t* prec, char* key) {
257 	lrece_t* pe = lrec_find_entry(prec, key);
258 	if (pe != NULL) {
259 		return pe->value;
260 	} else {
261 		return NULL;
262 	}
263 }
264 
lrec_get_pff(lrec_t * prec,char * key,char ** ppfree_flags)265 char* lrec_get_pff(lrec_t* prec, char* key, char** ppfree_flags) {
266 	lrece_t* pe = lrec_find_entry(prec, key);
267 	if (pe != NULL) {
268 		*ppfree_flags = &pe->free_flags;
269 		return pe->value;
270 	} else {
271 		*ppfree_flags = NULL;
272 		return NULL;
273 	}
274 }
275 
lrec_get_ext(lrec_t * prec,char * key,lrece_t ** ppentry)276 char* lrec_get_ext(lrec_t* prec, char* key, lrece_t** ppentry) {
277 	lrece_t* pe = lrec_find_entry(prec, key);
278 	if (pe != NULL) {
279 		*ppentry = pe;
280 		return pe->value;
281 	} else {
282 		*ppentry = NULL;;
283 		return NULL;
284 	}
285 }
286 
287 // ----------------------------------------------------------------
lrec_get_pair_by_position(lrec_t * prec,int position)288 lrece_t* lrec_get_pair_by_position(lrec_t* prec, int position) { // 1-up not 0-up
289 	if (position <= 0 || position > prec->field_count) {
290 		return NULL;
291 	}
292 	int sought_index = position - 1;
293 	int found_index = 0;
294 	lrece_t* pe = NULL;
295 	for (
296 		found_index = 0, pe = prec->phead;
297 		pe != NULL;
298 		found_index++, pe = pe->pnext
299 	) {
300 		if (found_index == sought_index) {
301 			return pe;
302 		}
303 	}
304 	fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n",
305 		MLR_GLOBALS.bargv0, __FILE__, __LINE__);
306 	exit(1);
307 }
308 
lrec_get_key_by_position(lrec_t * prec,int position)309 char* lrec_get_key_by_position(lrec_t* prec, int position) { // 1-up not 0-up
310 	lrece_t* pe = lrec_get_pair_by_position(prec, position);
311 	if (pe == NULL) {
312 		return NULL;
313 	} else {
314 		return pe->key;
315 	}
316 }
317 
lrec_get_value_by_position(lrec_t * prec,int position)318 char* lrec_get_value_by_position(lrec_t* prec, int position) { // 1-up not 0-up
319 	lrece_t* pe = lrec_get_pair_by_position(prec, position);
320 	if (pe == NULL) {
321 		return NULL;
322 	} else {
323 		return pe->value;
324 	}
325 }
326 
327 // ----------------------------------------------------------------
lrec_remove(lrec_t * prec,char * key)328 void lrec_remove(lrec_t* prec, char* key) {
329 	lrece_t* pe = lrec_find_entry(prec, key);
330 	if (pe == NULL)
331 		return;
332 
333 	lrec_unlink(prec, pe);
334 
335 	if (pe->free_flags & FREE_ENTRY_KEY) {
336 		free(pe->key);
337 	}
338 	if (pe->free_flags & FREE_ENTRY_VALUE) {
339 		free(pe->value);
340 	}
341 
342 	free(pe);
343 }
344 
345 // ----------------------------------------------------------------
lrec_remove_by_position(lrec_t * prec,int position)346 void lrec_remove_by_position(lrec_t* prec, int position) { // 1-up not 0-up
347 	lrece_t* pe = lrec_get_pair_by_position(prec, position);
348 	if (pe == NULL)
349 		return;
350 
351 	lrec_unlink(prec, pe);
352 
353 	if (pe->free_flags & FREE_ENTRY_KEY) {
354 		free(pe->key);
355 	}
356 	if (pe->free_flags & FREE_ENTRY_VALUE) {
357 		free(pe->value);
358 	}
359 
360 	free(pe);
361 }
362 
363 // Before:
364 //   "x" => "3"
365 //   "y" => "4"  <-- pold
366 //   "z" => "5"  <-- pnew
367 //
368 // Rename y to z
369 //
370 // After:
371 //   "x" => "3"
372 //   "z" => "4"
373 //
lrec_rename(lrec_t * prec,char * old_key,char * new_key,int new_needs_freeing)374 void lrec_rename(lrec_t* prec, char* old_key, char* new_key, int new_needs_freeing) {
375 
376 	lrece_t* pold = lrec_find_entry(prec, old_key);
377 	if (pold != NULL) {
378 		lrece_t* pnew = lrec_find_entry(prec, new_key);
379 
380 		if (pnew == NULL) { // E.g. rename "x" to "y" when "y" is not present
381 			if (pold->free_flags & FREE_ENTRY_KEY) {
382 				free(pold->key);
383 				pold->key = new_key;
384 				if (!new_needs_freeing)
385 					pold->free_flags &= ~FREE_ENTRY_KEY;
386 			} else {
387 				pold->key = new_key;
388 				if (new_needs_freeing)
389 					pold->free_flags |=  FREE_ENTRY_KEY;
390 			}
391 
392 		} else { // E.g. rename "x" to "y" when "y" is already present
393 			if (pnew->free_flags & FREE_ENTRY_VALUE) {
394 				free(pnew->value);
395 			}
396 			if (pold->free_flags & FREE_ENTRY_KEY) {
397 				free(pold->key);
398 				pold->free_flags &= ~FREE_ENTRY_KEY;
399 			}
400 			pold->key = new_key;
401 			if (new_needs_freeing)
402 				pold->free_flags |=  FREE_ENTRY_KEY;
403 			else
404 				pold->free_flags &= ~FREE_ENTRY_KEY;
405 			lrec_unlink(prec, pnew);
406 			free(pnew);
407 		}
408 	}
409 }
410 
411 // Cases:
412 // 1. Rename field at position 3 from "x" to "y when "y" does not exist elsewhere in the srec
413 // 2. Rename field at position 3 from "x" to "y when "y" does     exist elsewhere in the srec
414 // Note: position is 1-up not 0-up
lrec_rename_at_position(lrec_t * prec,int position,char * new_key,int new_needs_freeing)415 void  lrec_rename_at_position(lrec_t* prec, int position, char* new_key, int new_needs_freeing){
416 	lrece_t* pe = lrec_get_pair_by_position(prec, position);
417 	if (pe == NULL) {
418 		if (new_needs_freeing) {
419 			free(new_key);
420 		}
421 		return;
422 	}
423 
424 	lrece_t* pother = lrec_find_entry(prec, new_key);
425 
426 	if (pe->free_flags & FREE_ENTRY_KEY) {
427 		free(pe->key);
428 	}
429 	pe->key = new_key;
430 	if (new_needs_freeing) {
431 		pe->free_flags |= FREE_ENTRY_KEY;
432 	} else {
433 		pe->free_flags &= ~FREE_ENTRY_KEY;
434 	}
435 	if (pother != NULL) {
436 		lrec_unlink(prec, pother);
437 		free(pother);
438 	}
439 }
440 
441 // ----------------------------------------------------------------
lrec_move_to_head(lrec_t * prec,char * key)442 void lrec_move_to_head(lrec_t* prec, char* key) {
443 	lrece_t* pe = lrec_find_entry(prec, key);
444 	if (pe == NULL)
445 		return;
446 
447 	lrec_unlink(prec, pe);
448 	lrec_link_at_head(prec, pe);
449 }
450 
lrec_move_to_tail(lrec_t * prec,char * key)451 void lrec_move_to_tail(lrec_t* prec, char* key) {
452 	lrece_t* pe = lrec_find_entry(prec, key);
453 	if (pe == NULL)
454 		return;
455 
456 	lrec_unlink(prec, pe);
457 	lrec_link_at_tail(prec, pe);
458 }
459 
460 // ----------------------------------------------------------------
461 // Simply rename the first (at most) n positions where n is the length of pnames.
462 //
463 // Possible complications:
464 //
465 // * pnames itself contains duplicates -- we require this as invariant-check from the caller since (for performance)
466 //   we don't want to check this on every record processed.
467 //
468 // * pnames has length less than the current record and one of the new names becomes a clash with an existing name.
469 //   Example:
470 //   - Input record has names "a,b,c,d,e".
471 //   - pnames is "d,x,f"
472 //   - We then construct the invalid "d,x,f,d,e" -- we need to detect and unset the second 'd' field.
473 
lrec_label(lrec_t * prec,slls_t * pnames_as_list,hss_t * pnames_as_set)474 void  lrec_label(lrec_t* prec, slls_t* pnames_as_list, hss_t* pnames_as_set) {
475 	lrece_t* pe = prec->phead;
476 	sllse_t* pn = pnames_as_list->phead;
477 
478 	// Process the labels list
479 	for ( ; pe != NULL && pn != NULL; pe = pe->pnext, pn = pn->pnext) {
480 		char* new_name = pn->value;
481 
482 		if (pe->free_flags & FREE_ENTRY_KEY) {
483 			free(pe->key);
484 		}
485 		pe->key = mlr_strdup_or_die(new_name);;
486 		pe->free_flags |= FREE_ENTRY_KEY;
487 	}
488 
489 	// Process the remaining fields in the record beyond those affected by the new-labels list
490 	for ( ; pe != NULL; ) {
491 		char* name = pe->key;
492 		if (hss_has(pnames_as_set, name)) {
493 			lrece_t* pnext = pe->pnext;
494 			if (pe->free_flags & FREE_ENTRY_KEY) {
495 				free(pe->key);
496 			}
497 			if (pe->free_flags & FREE_ENTRY_VALUE) {
498 				free(pe->value);
499 			}
500 			lrec_unlink(prec, pe);
501 			free(pe);
502 			pe = pnext;
503 		} else {
504 			pe = pe->pnext;
505 		}
506 	}
507 }
508 
509 // ----------------------------------------------------------------
lrece_update_value(lrece_t * pe,char * new_value,int new_needs_freeing)510 void lrece_update_value(lrece_t* pe, char* new_value, int new_needs_freeing) {
511 	if (pe == NULL) {
512 		return;
513 	}
514 	if (pe->free_flags & FREE_ENTRY_VALUE) {
515 		free(pe->value);
516 	}
517 	pe->value = new_value;
518 	if (new_needs_freeing)
519 		pe->free_flags |= FREE_ENTRY_VALUE;
520 	else
521 		pe->free_flags &= ~FREE_ENTRY_VALUE;
522 }
523 
524 // ----------------------------------------------------------------
lrec_unlink(lrec_t * prec,lrece_t * pe)525 void lrec_unlink(lrec_t* prec, lrece_t* pe) {
526 	if (pe == prec->phead) {
527 		if (pe == prec->ptail) {
528 			prec->phead = NULL;
529 			prec->ptail = NULL;
530 		} else {
531 			prec->phead = pe->pnext;
532 			pe->pnext->pprev = NULL;
533 		}
534 	} else {
535 		pe->pprev->pnext = pe->pnext;
536 		if (pe == prec->ptail) {
537 			prec->ptail = pe->pprev;
538 		} else {
539 			pe->pnext->pprev = pe->pprev;
540 		}
541 	}
542 	prec->field_count--;
543 }
544 
lrec_unlink_and_free(lrec_t * prec,lrece_t * pe)545 void lrec_unlink_and_free(lrec_t* prec, lrece_t* pe) {
546 	if (pe->free_flags & FREE_ENTRY_KEY)
547 		free(pe->key);
548 	if (pe->free_flags & FREE_ENTRY_VALUE)
549 		free(pe->value);
550 	lrec_unlink(prec, pe);
551 	free(pe);
552 }
553 
554 // ----------------------------------------------------------------
lrec_link_at_head(lrec_t * prec,lrece_t * pe)555 static void lrec_link_at_head(lrec_t* prec, lrece_t* pe) {
556 
557 	if (prec->phead == NULL) {
558 		pe->pprev   = NULL;
559 		pe->pnext   = NULL;
560 		prec->phead = pe;
561 		prec->ptail = pe;
562 	} else {
563 		// [b,c,d] + a
564 		pe->pprev   = NULL;
565 		pe->pnext   = prec->phead;
566 		prec->phead->pprev = pe;
567 		prec->phead = pe;
568 	}
569 	prec->field_count++;
570 }
571 
lrec_link_at_tail(lrec_t * prec,lrece_t * pe)572 static void lrec_link_at_tail(lrec_t* prec, lrece_t* pe) {
573 
574 	if (prec->phead == NULL) {
575 		pe->pprev   = NULL;
576 		pe->pnext   = NULL;
577 		prec->phead = pe;
578 		prec->ptail = pe;
579 	} else {
580 		pe->pprev   = prec->ptail;
581 		pe->pnext   = NULL;
582 		prec->ptail->pnext = pe;
583 		prec->ptail = pe;
584 	}
585 	prec->field_count++;
586 }
587 
588 // ----------------------------------------------------------------
lrec_dump(lrec_t * prec)589 void lrec_dump(lrec_t* prec) {
590 	lrec_dump_fp(prec, stdout);
591 }
lrec_dump_fp(lrec_t * prec,FILE * fp)592 void lrec_dump_fp(lrec_t* prec, FILE* fp) {
593 	if (prec == NULL) {
594 		fprintf(fp, "NULL\n");
595 		return;
596 	}
597 	fprintf(fp, "field_count = %d\n", prec->field_count);
598 	fprintf(fp, "| phead: %16p | ptail %16p\n", prec->phead, prec->ptail);
599 	for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
600 		const char* key_string = (pe == NULL) ? "none" :
601 			pe->key == NULL ? "null" :
602 			pe->key;
603 		const char* value_string = (pe == NULL) ? "none" :
604 			pe->value == NULL ? "null" :
605 			pe->value;
606 		fprintf(fp,
607 		"| prev: %16p curr: %16p next: %16p | key: %12s | value: %12s |\n",
608 			pe->pprev, pe, pe->pnext,
609 			key_string, value_string);
610 	}
611 }
612 
lrec_dump_titled(char * msg,lrec_t * prec)613 void lrec_dump_titled(char* msg, lrec_t* prec) {
614 	printf("%s:\n", msg);
615 	lrec_dump(prec);
616 	printf("\n");
617 }
618 
lrec_pointer_dump(lrec_t * prec)619 void lrec_pointer_dump(lrec_t* prec) {
620 	printf("prec %p\n", prec);
621 	for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
622 		printf("  pe %p k %p v %p\n", pe, pe->key, pe->value);
623 	}
624 }
625 
626 // ----------------------------------------------------------------
lrec_unbacked_free(lrec_t * prec)627 static void lrec_unbacked_free(lrec_t* prec) {
628 }
629 
lrec_free_single_line_backing(lrec_t * prec)630 static void lrec_free_single_line_backing(lrec_t* prec) {
631 	free(prec->psingle_line);
632 }
633 
lrec_free_csv_backing(lrec_t * prec)634 static void lrec_free_csv_backing(lrec_t* prec) {
635 	free(prec->psingle_line);
636 }
637 
lrec_free_multiline_backing(lrec_t * prec)638 static void lrec_free_multiline_backing(lrec_t* prec) {
639 	slls_free(prec->pxtab_lines);
640 }
641 
642 // ================================================================
643 
644 // ----------------------------------------------------------------
645 // Note on efficiency:
646 //
647 // I was imagining/hoping that strcmp has additional optimizations (e.g.
648 // hand-coded in assembly), so I don't *want* to re-implement it (i.e. I
649 // probably can't outperform it).
650 //
651 // But actual experiments show I get about a 1-2% performance gain doing it
652 // myself (on my particular system).
653 
lrec_find_entry(lrec_t * prec,char * key)654 static lrece_t* lrec_find_entry(lrec_t* prec, char* key) {
655 #if 1
656 	for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
657 		char* pa = pe->key;
658 		char* pb = key;
659 		while (*pa && *pb && (*pa == *pb)) {
660 			pa++;
661 			pb++;
662 		}
663 		if (*pa == 0 && *pb == 0)
664 			return pe;
665 	}
666 	return NULL;
667 #else
668 	for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext)
669 		if (streq(pe->key, key))
670 			return pe;
671 	return NULL;
672 #endif
673 }
674 
675 // ----------------------------------------------------------------
lrec_literal_1(char * k1,char * v1)676 lrec_t* lrec_literal_1(char* k1, char* v1) {
677 	lrec_t* prec = lrec_unbacked_alloc();
678 	lrec_put(prec, k1, v1, NO_FREE);
679 	return prec;
680 }
681 
lrec_literal_2(char * k1,char * v1,char * k2,char * v2)682 lrec_t* lrec_literal_2(char* k1, char* v1, char* k2, char* v2) {
683 	lrec_t* prec = lrec_unbacked_alloc();
684 	lrec_put(prec, k1, v1, NO_FREE);
685 	lrec_put(prec, k2, v2, NO_FREE);
686 	return prec;
687 }
688 
lrec_literal_3(char * k1,char * v1,char * k2,char * v2,char * k3,char * v3)689 lrec_t* lrec_literal_3(char* k1, char* v1, char* k2, char* v2, char* k3, char* v3) {
690 	lrec_t* prec = lrec_unbacked_alloc();
691 	lrec_put(prec, k1, v1, NO_FREE);
692 	lrec_put(prec, k2, v2, NO_FREE);
693 	lrec_put(prec, k3, v3, NO_FREE);
694 	return prec;
695 }
696 
lrec_literal_4(char * k1,char * v1,char * k2,char * v2,char * k3,char * v3,char * k4,char * v4)697 lrec_t* lrec_literal_4(char* k1, char* v1, char* k2, char* v2, char* k3, char* v3, char* k4, char* v4) {
698 	lrec_t* prec = lrec_unbacked_alloc();
699 	lrec_put(prec, k1, v1, NO_FREE);
700 	lrec_put(prec, k2, v2, NO_FREE);
701 	lrec_put(prec, k3, v3, NO_FREE);
702 	lrec_put(prec, k4, v4, NO_FREE);
703 	return prec;
704 }
705 
lrec_print(lrec_t * prec)706 void lrec_print(lrec_t* prec) {
707 	FILE* output_stream = stdout;
708 	char ors = '\n';
709 	char ofs = ',';
710 	char ops = '=';
711 	if (prec == NULL) {
712 		fputs("NULL", output_stream);
713 		fputc(ors, output_stream);
714 		return;
715 	}
716 	int nf = 0;
717 	for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
718 		if (nf > 0)
719 			fputc(ofs, output_stream);
720 		fputs(pe->key, output_stream);
721 		fputc(ops, output_stream);
722 		fputs(pe->value, output_stream);
723 		nf++;
724 	}
725 	fputc(ors, output_stream);
726 }
727 
lrec_sprint(lrec_t * prec,char * ors,char * ofs,char * ops)728 char* lrec_sprint(lrec_t* prec, char* ors, char* ofs, char* ops) {
729 	string_builder_t* psb = sb_alloc(SB_ALLOC_LENGTH);
730 	if (prec == NULL) {
731 		sb_append_string(psb, "NULL");
732 	} else {
733 		int nf = 0;
734 		for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
735 			if (nf > 0)
736 				sb_append_string(psb, ofs);
737 			sb_append_string(psb, pe->key);
738 			sb_append_string(psb, ops);
739 			sb_append_string(psb, pe->value);
740 			nf++;
741 		}
742 		sb_append_string(psb, ors);
743 	}
744 	char* rv = sb_finish(psb);
745 	sb_free(psb);
746 	return rv;
747 }
748