1 /*
2  * fields.c
3  *
4  * Copyright (c) Chris Putnam 2003-2020
5  *
6  * Source code released under the GPL version 2
7  *
8  */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <string.h>
13 #include "fields.h"
14 
15 #define FIELDS_MIN_ALLOC (20)
16 
17 /* private helper macros to access fields
18  *
19  * These skip all of the error checking and used flag manipulation
20  * of the fields functions, so they are faster. However, they should
21  * only be used in internal code that knows that the index is valid.
22  */
23 #define _fields_tag(f,i)            &((f)->tag[(i)])
24 #define _fields_tag_char(f,i)       str_cstr( &((f)->tag[(i)]) )
25 #define _fields_tag_notempty(f,i)   str_has_value( &((f)->tag[(i)]) )
26 #define _fields_value(f,i)          &((f)->value[(i)])
27 #define _fields_value_char(f,i)     str_cstr( &((f)->value[(i)]) )
28 #define _fields_value_notempty(f,i) str_has_value( &((f)->value[(i)]) )
29 #define _fields_level(f,i)          (f)->level[(i)]
30 
31 fields*
fields_new(void)32 fields_new( void )
33 {
34 	fields *f = ( fields * ) malloc( sizeof( fields ) );
35 	if ( f ) fields_init( f );
36 	return f;
37 }
38 
39 void
fields_init(fields * f)40 fields_init( fields *f )
41 {
42 	f->used  = f->level = NULL;
43 	f->tag   = f->value = NULL;
44 	f->max   = f->n     = 0;
45 }
46 
47 void
fields_free(fields * f)48 fields_free( fields *f )
49 {
50 	int i;
51 
52 	for ( i=0; i<f->max; ++i ) {
53 		str_free( _fields_tag( f, i ) );
54 		str_free( _fields_value( f, i ) );
55 	}
56 	if ( f->tag )   free( f->tag );
57 	if ( f->value ) free( f->value );
58 	if ( f->used )  free( f->used );
59 	if ( f->level ) free( f->level );
60 
61 	fields_init( f );
62 }
63 
64 void
fields_delete(fields * f)65 fields_delete( fields *f )
66 {
67 	fields_free( f );
68 	free( f );
69 }
70 
71 int
fields_remove(fields * f,int n)72 fields_remove( fields *f, int n )
73 {
74 	int i;
75 	if ( n<0 || n>= f->n ) return FIELDS_ERR_MEMERR;
76 	for ( i=n+1; i<f->n; ++i ) {
77 		str_strcpy( _fields_tag  ( f, i-1 ), _fields_tag  ( f, i ) );
78 		str_strcpy( _fields_value( f, i-1 ), _fields_value( f, i ) );
79 		f->used[i-1]  = f->used[i];
80 		f->level[i-1] = f->level[i];
81 	}
82 	f->n -= 1;
83 	return FIELDS_OK;
84 }
85 
86 static void
initialize_new_tag_data_pairs(fields * f,int start,int end)87 initialize_new_tag_data_pairs( fields *f, int start, int end )
88 {
89 	int i;
90 	for ( i=start; i<end; ++i ) {
91 		str_init( _fields_tag( f, i ) );
92 		str_init( _fields_value( f, i ) );
93 	}
94 }
95 
96 static int
fields_alloc(fields * f,int alloc)97 fields_alloc( fields *f, int alloc )
98 {
99 	f->tag   = (str *) malloc( sizeof(str) * alloc );
100 	f->value = (str *) malloc( sizeof(str) * alloc );
101 	f->used  = (int *) calloc( alloc, sizeof(int) );
102 	f->level = (int *) calloc( alloc, sizeof(int) );
103 	if ( !f->tag || !f->value || !f->used || !f->level ){
104 		if ( f->tag )   free( f->tag );
105 		if ( f->value ) free( f->value );
106 		if ( f->used )  free( f->used );
107 		if ( f->level ) free( f->level );
108 		fields_init( f );
109 		return FIELDS_ERR_MEMERR;
110 	}
111 
112 	initialize_new_tag_data_pairs( f, 0, alloc );
113 
114 	f->max = alloc;
115 	f->n   = 0;
116 
117 	return FIELDS_OK;
118 }
119 
120 static int
fields_realloc(fields * f)121 fields_realloc( fields *f )
122 {
123 	int *newused, *newlevel;
124 	str *newtags, *newvalue;
125 	int alloc;
126 
127 	alloc = f->max * 2;
128 	if ( alloc < f->max ) return FIELDS_ERR_MEMERR; /* integer overflow */
129 
130 	newtags  = (str*) realloc( f->tag,   sizeof(str) * alloc );
131 	newvalue = (str*) realloc( f->value, sizeof(str) * alloc );
132 	newused  = (int*) realloc( f->used,  sizeof(int) * alloc );
133 	newlevel = (int*) realloc( f->level, sizeof(int) * alloc );
134 
135 	/*
136 	 * ensure struct fields is consistent by reassigning pointers for any successful
137 	 * reallocation prior to failing on memory error
138 	 */
139 	if ( newtags )  f->tag   = newtags;
140 	if ( newvalue ) f->value = newvalue;
141 	if ( newused )  f->used  = newused;
142 	if ( newlevel ) f->level = newlevel;
143 
144 	if ( !newtags || !newvalue || !newused || !newlevel )
145 		return FIELDS_ERR_MEMERR;
146 
147 	initialize_new_tag_data_pairs( f, f->n, alloc );
148 
149 	f->max = alloc;
150 
151 	return FIELDS_OK;
152 }
153 
154 static int
ensure_space(fields * f)155 ensure_space( fields *f )
156 {
157 	int status = FIELDS_OK;
158 	if ( f->max==0 )         status = fields_alloc( f, FIELDS_MIN_ALLOC );
159 	else if ( f->n==f->max ) status = fields_realloc( f );
160 	return status;
161 }
162 
163 static int
is_duplicate_entry(fields * f,const char * tag,const char * value,int level)164 is_duplicate_entry( fields *f, const char *tag, const char *value, int level )
165 {
166 	int i;
167 
168 	for ( i=0; i<f->n; i++ ) {
169 		if ( _fields_level( f, i ) != level ) continue;
170 		if ( strcasecmp( _fields_tag_char( f, i ),   tag   ) ) continue;
171 		if ( strcasecmp( _fields_value_char( f, i ), value ) ) continue;
172 		return 1;
173 	}
174 
175 	return 0;
176 }
177 
178 int
_fields_add(fields * f,const char * tag,const char * value,int level,int mode)179 _fields_add( fields *f, const char *tag, const char *value, int level, int mode )
180 {
181 	int n, status;
182 
183 	/* Don't add incomplete entry */
184 	if ( !tag || !value ) return FIELDS_OK;
185 
186 	/* Don't add duplicate entry if FIELDS_NO_DUPS */
187 	if ( mode == FIELDS_NO_DUPS && is_duplicate_entry( f, tag, value, level ) )
188 		return FIELDS_OK;
189 
190 	status = ensure_space( f );
191 	if ( status!=FIELDS_OK ) return status;
192 
193 	n = f->n;
194 	f->used[ n ]  = 0;
195 	f->level[ n ] = level;
196 	str_strcpyc( _fields_tag( f, n ),   tag   );
197 	str_strcpyc( _fields_value( f, n ), value );
198 
199 	if ( str_memerr( &(f->tag[n]) ) || str_memerr( &(f->value[n] ) ) )
200 		return FIELDS_ERR_MEMERR;
201 
202 	f->n++;
203 
204 	return FIELDS_OK;
205 }
206 
207 int
_fields_add_suffix(fields * f,const char * tag,const char * suffix,const char * value,int level,int mode)208 _fields_add_suffix( fields *f, const char *tag, const char *suffix, const char *value, int level, int mode )
209 {
210 	str newtag;
211 	int ret;
212 
213 	str_init( &newtag );
214 
215 	str_mergestrs( &newtag, tag, suffix, NULL );
216 	if ( str_memerr( &newtag ) )
217 		ret = FIELDS_ERR_MEMERR;
218 	else
219 		ret = _fields_add( f, str_cstr( &newtag ), value, level, mode );
220 
221 	str_free( &newtag );
222 
223 	return ret;
224 }
225 
226 static fields*
fields_new_size(int alloc)227 fields_new_size( int alloc )
228 {
229 	int status;
230 	fields *f;
231 
232 	f = ( fields * ) malloc( sizeof( fields ) );
233 	if ( f ) {
234 		fields_init( f );
235 		status = fields_alloc( f, alloc );
236 		if ( status!=FIELDS_OK ) {
237 			fields_delete( f );
238 			return NULL;
239 		}
240 	}
241 	return f;
242 }
243 
244 fields *
fields_dupl(fields * in)245 fields_dupl( fields *in )
246 {
247 	int i, level, status;
248 	char *tag, *value;
249 	fields *out;
250 
251 	out = fields_new_size( in->n );
252 	if ( !out ) return NULL;
253 
254 	for ( i=0; i<in->n; ++i ) {
255 		tag   = _fields_tag_char( in, i );
256 		value = _fields_value_char( in, i );
257 		level = _fields_level( in, i );
258 		if ( tag && value ) {
259 			status = fields_add_can_dup( out, tag, value, level );
260 			if ( status!=FIELDS_OK ) {
261 				fields_delete( out );
262 				return NULL;
263 			}
264 		}
265 	}
266 
267 	return out;
268 }
269 
270 /* fields_match_level()
271  *
272  * returns 1 if level matched, 0 if not
273  *
274  * level==LEVEL_ANY is a special flag meaning any level can match
275  */
276 int
fields_match_level(fields * f,int n,int level)277 fields_match_level( fields *f, int n, int level )
278 {
279 	if ( level==LEVEL_ANY ) return 1;
280 	if ( fields_level( f, n )==level ) return 1;
281 	return 0;
282 }
283 
284 /* fields_match_tag()
285  *
286  * returns 1 if tag matches, 0 if not
287  *
288  */
289 int
fields_match_tag(fields * f,int n,const char * tag)290 fields_match_tag( fields *f, int n, const char *tag )
291 {
292 	if ( !strcmp( _fields_tag_char( f, n ), tag ) ) return 1;
293 	return 0;
294 }
295 
296 int
fields_match_casetag(fields * f,int n,const char * tag)297 fields_match_casetag( fields *f, int n, const char *tag )
298 {
299 	if ( !strcasecmp( _fields_tag_char( f, n ), tag ) ) return 1;
300 	return 0;
301 }
302 
303 int
fields_match_tag_level(fields * f,int n,const char * tag,int level)304 fields_match_tag_level( fields *f, int n, const char *tag, int level )
305 {
306 	if ( !fields_match_level( f, n, level ) ) return 0;
307 	return fields_match_tag( f, n, tag );
308 }
309 
310 int
fields_match_casetag_level(fields * f,int n,const char * tag,int level)311 fields_match_casetag_level( fields *f, int n, const char *tag, int level )
312 {
313 	if ( !fields_match_level( f, n, level ) ) return 0;
314 	return fields_match_casetag( f, n, tag );
315 }
316 
317 /* fields_find()
318  *
319  * Return position [0,f->n) for first match of the tag.
320  * Return FIELDS_NOTFOUND if tag isn't found.
321  */
322 int
fields_find(fields * f,const char * tag,int level)323 fields_find( fields *f, const char *tag, int level )
324 {
325 	int i;
326 
327 	for ( i=0; i<f->n; ++i ) {
328 		if ( !fields_match_casetag_level( f, i, tag, level ) )
329 			continue;
330 		if ( str_has_value( _fields_value( f, i ) ) ) return i;
331 		else {
332 			/* if there is no data for the tag, don't "find" it */
333 			/* and set "used" so noise is suppressed */
334 			f->used[i] = 1;
335 		}
336 	}
337 
338 	return FIELDS_NOTFOUND;
339 }
340 
341 int
fields_maxlevel(fields * f)342 fields_maxlevel( fields *f )
343 {
344 	int i, max = 0;
345 
346 	if ( f->n ) {
347 		max = _fields_level( f, 0 );
348 		for ( i=1; i<f->n; ++i ) {
349 			if ( _fields_level( f, i ) > max )
350 				max = _fields_level( f, i );
351 		}
352 	}
353 
354 	return max;
355 }
356 
357 void
fields_clear_used(fields * f)358 fields_clear_used( fields *f )
359 {
360 	int i;
361 
362 	for ( i=0; i<f->n; ++i )
363 		f->used[i] = 0;
364 }
365 
366 void
fields_set_used(fields * f,int n)367 fields_set_used( fields *f, int n )
368 {
369 	if ( n >= 0 && n < f->n )
370 		f->used[n] = 1;
371 }
372 
373 /* fields_replace_or_add()
374  *
375  * return FIELDS_OK on success, else FIELDS_ERR_MEMERR
376  */
377 int
fields_replace_or_add(fields * f,const char * tag,const char * value,int level)378 fields_replace_or_add( fields *f, const char *tag, const char *value, int level )
379 {
380 	int n;
381 
382 	n = fields_find( f, tag, level );
383 	if ( n==FIELDS_NOTFOUND ) {
384 		return fields_add( f, tag, value, level );
385 	}
386 	else {
387 		str_strcpyc( _fields_value( f, n ), value );
388 		if ( str_memerr( _fields_value( f, n ) ) ) return FIELDS_ERR_MEMERR;
389 		return FIELDS_OK;
390 	}
391 }
392 
393 char *fields_null_value = "\0";
394 
395 int
fields_used(fields * f,int n)396 fields_used( fields *f, int n )
397 {
398 	if ( n >= 0 && n < f->n ) return f->used[n];
399 	else return 0;
400 }
401 
402 int
fields_no_tag(fields * f,int n)403 fields_no_tag( fields *f, int n )
404 {
405 	if ( n >= 0 && n < f->n ) {
406 		if ( _fields_tag_notempty( f, n ) ) return 0;
407 	}
408 	return 1;
409 }
410 
411 int
fields_no_value(fields * f,int n)412 fields_no_value( fields *f, int n )
413 {
414 	if ( n >= 0 && n < f->n ) {
415 		if ( _fields_value_notempty( f, n ) ) return 0;
416 	}
417 	return 1;
418 }
419 
420 int
fields_has_value(fields * f,int n)421 fields_has_value( fields *f, int n )
422 {
423 	if ( n >= 0 && n < f->n ) return _fields_value_notempty( f, n );
424 	return 0;
425 }
426 
427 int
fields_num(fields * f)428 fields_num( fields *f )
429 {
430 	return f->n;
431 }
432 
433 /*
434  * #define FIELDS_CHRP
435  * #define FIELDS_STRP
436  * #define FIELDS_CHRP_NOLEN
437  * #define FIELDS_STRP_NOLEN
438  *
439  * If the length of the tagged value is zero and the mode is
440  * FIELDS_STRP_NOLEN or FIELDS_CHRP_NOLEN, return a pointer to
441  * a static null string as the data field could be new due to
442  * the way str handles initialized strings with no data.
443  *
444  */
445 
446 void *
fields_value(fields * f,int n,int mode)447 fields_value( fields *f, int n, int mode )
448 {
449 	intptr_t retn;
450 
451 	if ( n<0 || n>= f->n ) return NULL;
452 
453 	if ( mode & FIELDS_SETUSE_FLAG )
454 		fields_set_used( f, n );
455 
456 	if ( mode & FIELDS_STRP_FLAG ) {
457 		return ( void * ) _fields_value( f, n );
458 	}
459 	else if ( mode & FIELDS_POSP_FLAG ) {
460 		retn = n;               /* avoid compiler warning */
461 		return ( void * ) retn; /* Rather pointless -- the user provided "n" */
462 	}
463 	else {
464 		if ( str_has_value( _fields_value( f, n ) ) )
465 			return _fields_value_char( f, n );
466 		else
467 			return fields_null_value;
468 	}
469 }
470 
471 void *
fields_tag(fields * f,int n,int mode)472 fields_tag( fields *f, int n, int mode )
473 {
474 	intptr_t retn;
475 
476 	if ( n<0 || n>= f->n ) return NULL;
477 
478 	if ( mode & FIELDS_STRP_FLAG ) {
479 		return ( void * ) _fields_tag( f, n );
480 	}
481 	else if ( mode & FIELDS_POSP_FLAG ) {
482 		retn = n;               /* avoid compiler warning */
483 		return ( void * ) retn; /* Rather pointless -- the user provided "n" */
484 	}
485 	else {
486 		if ( str_has_value( _fields_tag( f, n ) ) )
487 			return _fields_tag_char( f, n );
488 		else
489 			return fields_null_value;
490 	}
491 }
492 
493 int
fields_level(fields * f,int n)494 fields_level( fields *f, int n )
495 {
496 	if ( n<0 || n>= f->n ) return 0;
497 	return _fields_level( f, n );
498 }
499 
500 void *
fields_findv(fields * f,int level,int mode,const char * tag)501 fields_findv( fields *f, int level, int mode, const char *tag )
502 {
503 	int i, found = FIELDS_NOTFOUND;
504 
505 	for ( i=0; i<f->n; ++i ) {
506 
507 		if ( !fields_match_level( f, i, level ) ) continue;
508 		if ( !fields_match_casetag( f, i, tag ) ) continue;
509 
510 		if ( _fields_value_notempty( f, i ) ) {
511 			found = i;
512 			break;
513 		} else {
514 			if ( mode & FIELDS_NOLENOK_FLAG ) {
515 				return (void *) fields_null_value;
516 			} else if ( mode & FIELDS_SETUSE_FLAG ) {
517 				f->used[i] = 1; /* Suppress "noise" of unused */
518 			}
519 		}
520 	}
521 
522 	if ( found==FIELDS_NOTFOUND ) return NULL;
523 	else return fields_value( f, found, mode );
524 }
525 
526 void *
fields_findv_firstof(fields * f,int level,int mode,...)527 fields_findv_firstof( fields *f, int level, int mode, ... )
528 {
529 	char *tag, *value;
530 	va_list argp;
531 
532 	va_start( argp, mode );
533 	while ( ( tag = ( char * ) va_arg( argp, char * ) ) ) {
534 		value = fields_findv( f, level, mode, tag );
535 		if ( value ) {
536 			va_end( argp );
537 			return value;
538 		}
539 	}
540 	va_end( argp );
541 
542 	return NULL;
543 }
544 
545 static int
fields_findv_each_add(fields * f,int mode,int n,vplist * a)546 fields_findv_each_add( fields *f, int mode, int n, vplist *a )
547 {
548 	int status;
549 	void *v;
550 
551 	v = fields_value( f, n, mode );
552 	if ( !v ) return FIELDS_OK;
553 
554 	status = vplist_add( a, v );
555 
556 	if ( status==VPLIST_OK ) return FIELDS_OK;
557 	else return FIELDS_ERR_MEMERR;
558 }
559 
560 int
fields_findv_each(fields * f,int level,int mode,vplist * a,const char * tag)561 fields_findv_each( fields *f, int level, int mode, vplist *a, const char *tag )
562 {
563 	int i, status;
564 
565 	for ( i=0; i<f->n; ++i ) {
566 
567 		if ( !fields_match_level( f, i, level ) ) continue;
568 		if ( !fields_match_casetag( f, i, tag ) ) continue;
569 
570 		if ( _fields_value_notempty( f, i ) ) {
571 			status = fields_findv_each_add( f, mode, i, a );
572 			if ( status!=FIELDS_OK ) return status;
573 		} else {
574 			if ( mode & FIELDS_NOLENOK_FLAG ) {
575 				status = fields_findv_each_add( f, mode, i, a );
576 				if ( status!=FIELDS_OK ) return status;
577 			} else {
578 				f->used[i] = 1; /* Suppress "noise" of unused */
579 			}
580 		}
581 
582 	}
583 
584 	return FIELDS_OK;
585 }
586 
587 static int
fields_build_tags(va_list argp,vplist * tags)588 fields_build_tags( va_list argp, vplist *tags )
589 {
590 	int status;
591 	char *tag;
592 
593 	while ( ( tag = ( char * ) va_arg( argp, char * ) ) ) {
594 		status = vplist_add( tags, tag );
595 		if ( status!=VPLIST_OK ) return FIELDS_ERR_MEMERR;
596 	}
597 
598 	return FIELDS_OK;
599 }
600 
601 static int
fields_match_casetags(fields * f,int n,vplist * tags)602 fields_match_casetags( fields *f, int n, vplist *tags )
603 {
604 	int i;
605 
606 	for ( i=0; i<tags->n; ++i )
607 		if ( fields_match_casetag( f, n, vplist_get( tags, i ) ) ) return 1;
608 
609 	return 0;
610 }
611 
612 int
fields_findv_eachof(fields * f,int level,int mode,vplist * a,...)613 fields_findv_eachof( fields *f, int level, int mode, vplist *a, ... )
614 {
615 	int i, status;
616 	va_list argp;
617 	vplist tags;
618 
619 	vplist_init( &tags );
620 
621 	/* build list of tags to search for */
622 	va_start( argp, a );
623 	status = fields_build_tags( argp, &tags );
624 	va_end( argp );
625 	if ( status!=FIELDS_OK ) goto out;
626 
627 	/* search list */
628 	for ( i=0; i<f->n; ++i ) {
629 
630 		if ( !fields_match_level( f, i, level ) ) continue;
631 		if ( !fields_match_casetags( f, i, &tags ) ) continue;
632 
633 		if ( _fields_value_notempty( f, i ) || ( mode & FIELDS_NOLENOK_FLAG ) ) {
634 			status = fields_findv_each_add( f, mode, i, a );
635 			if ( status!=FIELDS_OK ) goto out;
636 		} else {
637 			f->used[i] = 1; /* Suppress "noise" of unused */
638 		}
639 
640 	}
641 
642 out:
643 	vplist_free( &tags );
644 	return status;
645 }
646 
647 void
fields_report(fields * f,FILE * fp)648 fields_report( fields *f, FILE *fp )
649 {
650 	int i, n;
651 	n = fields_num( f );
652 	// Patch: Disable output logging
653 }
654