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