1 /*
2  * slist.c
3  *
4  * version: 2019-01-14
5  *
6  * Copyright (c) Chris Putnam 2004-2020
7  *
8  * Source code released under the GPL version 2
9  *
10  * Implements a simple managed array of strs.
11  *
12  */
13 #include "slist.h"
14 
15 /* Do not use asserts in VPLIST_NOASSERT defined */
16 #ifdef VPLIST_NOASSERT
17 #define NDEBUG
18 #endif
19 #include <assert.h>
20 
21 #define SLIST_MINALLOC (20)
22 
23 #define SLIST_EXACT_SIZE  (0)
24 #define SLIST_DOUBLE_SIZE (1)
25 
26 /*
27  * returns 1 if n is valid string in slist
28  */
29 static inline int
slist_valid_num(slist * a,slist_index n)30 slist_valid_num( slist *a, slist_index n )
31 {
32 	if ( n < 0 || n >= a->n ) return 0;
33 	return 1;
34 }
35 
36 void
slist_init(slist * a)37 slist_init( slist *a  )
38 {
39 	assert( a );
40 
41 	a->strs = NULL;
42 	a->max = 0;
43 	a->n = 0;
44 	a->sorted = 1;
45 }
46 
47 int
slist_init_values(slist * a,...)48 slist_init_values( slist *a, ... )
49 {
50 	int status = SLIST_OK;
51 	va_list ap;
52 	str *s;
53 
54 	slist_init( a );
55 
56 	va_start( ap, a );
57 	do {
58 		s = va_arg( ap, str * );
59 		if ( s ) {
60 			status = slist_add( a, s );
61 			if ( status!=SLIST_OK ) goto out;
62 		}
63 	} while ( s );
64 out:
65 	va_end( ap );
66 
67 	return status;
68 }
69 
70 int
slist_init_valuesc(slist * a,...)71 slist_init_valuesc( slist *a, ... )
72 {
73 	int status = SLIST_OK;
74 	va_list ap;
75 	char *s;
76 
77 	slist_init( a );
78 
79 	va_start( ap, a );
80 	do {
81 		s = va_arg( ap, char * );
82 		if ( s ) {
83 			status = slist_addc( a, s );
84 			if ( status!=SLIST_OK ) goto out;
85 		}
86 	} while ( s );
87 out:
88 	va_end( ap );
89 
90 	return status;
91 }
92 
93 void
slist_empty(slist * a)94 slist_empty( slist *a )
95 {
96 	slist_index i;
97 
98 	assert( a );
99 
100 	for ( i=0; i<a->max; ++i )
101 		str_empty( &(a->strs[i]) );
102 
103 	a->n = 0;
104 	a->sorted = 1;
105 }
106 
107 void
slist_free(slist * a)108 slist_free( slist *a )
109 {
110 	slist_index i;
111 
112 	assert( a );
113 
114 	for ( i=0; i<a->max; ++i )
115 		str_free( &(a->strs[i]) );
116 
117 	free( a->strs );
118 	slist_init( a );
119 }
120 
121 slist *
slist_new(void)122 slist_new( void )
123 {
124 	slist *a;
125 
126 	a = ( slist * ) malloc( sizeof ( slist ) );
127 	if ( a ) slist_init( a );
128 
129 	return a;
130 }
131 
132 void
slist_delete(slist * a)133 slist_delete( slist *a )
134 {
135 	assert( a );
136 
137 	slist_free( a );
138 	free( a );
139 }
140 
141 void
slist_deletev(void * v)142 slist_deletev( void *v )
143 {
144 	slist_delete( (slist*) v );
145 }
146 
147 void
slist_swap(slist * a,slist_index n1,slist_index n2)148 slist_swap( slist *a, slist_index n1, slist_index n2 )
149 {
150 	assert( a );
151 
152 	if ( slist_valid_num( a, n1 ) && slist_valid_num( a, n2 ) )
153 		str_swapstrings( &(a->strs[n1]), &(a->strs[n2]) );
154 }
155 
156 static int
slist_revcomp(const void * v1,const void * v2)157 slist_revcomp( const void *v1, const void *v2 )
158 {
159 	str *s1 = ( str *) v1;
160 	str *s2 = ( str *) v2;
161 	int n;
162 
163 	if ( !s1->len && !s2->len ) return 0;
164 	else if ( !s1->len ) return 1;
165 	else if ( !s2->len ) return -1;
166 
167 	n = str_strcmp( s1, s2 );
168 	if ( n==0 ) return 0;
169 	else if ( n > 0 ) return -1;
170 	else return 1;
171 }
172 
173 static int
slist_comp(const void * v1,const void * v2)174 slist_comp( const void *v1, const void *v2 )
175 {
176 	str *s1 = ( str *) v1;
177 	str *s2 = ( str *) v2;
178 	if ( !s1->len && !s2->len ) return 0;
179 	else if ( !s1->len ) return -1;
180 	else if ( !s2->len ) return 1;
181 	else return str_strcmp( s1, s2 );
182 }
183 
184 static int
slist_comp_step(slist * a,slist_index n1,slist_index n2)185 slist_comp_step( slist *a, slist_index n1, slist_index n2 )
186 {
187 	return slist_comp( (const void*) &(a->strs[n1]), (const void*) &(a->strs[n2]) );
188 }
189 
190 static str *
slist_set_cleanup(slist * a,slist_index n)191 slist_set_cleanup( slist *a, slist_index n )
192 {
193 	if ( str_memerr( &(a->strs[n]) ) ) return NULL;
194 	if ( a->sorted ) {
195 		if ( n>0 && slist_comp_step( a, n-1, n )>0 )
196 			a->sorted = 0;
197 	}
198 	if ( a->sorted ) {
199 		if ( n<a->n-1 && slist_comp_step( a, n, n+1 )>0 )
200 			a->sorted = 0;
201 	}
202 	return &(a->strs[n]);
203 }
204 
205 str *
slist_setc(slist * a,slist_index n,const char * s)206 slist_setc( slist *a, slist_index n, const char *s )
207 {
208 	assert( a );
209 	assert( s );
210 
211 	if ( !slist_valid_num( a, n ) ) return NULL;
212 	str_strcpyc( &(a->strs[n]), s );
213 	return slist_set_cleanup( a, n );
214 }
215 
216 str *
slist_set(slist * a,slist_index n,str * s)217 slist_set( slist *a, slist_index n, str *s )
218 {
219 	assert( s );
220 
221 	return slist_setc( a, n, str_cstr( s ) );
222 }
223 
224 /*
225  * return pointer to str 'n'
226  */
227 str *
slist_str(slist * a,slist_index n)228 slist_str( slist *a, slist_index n )
229 {
230 	assert( a );
231 
232 	if ( !slist_valid_num( a, n ) ) return NULL;
233 	else return &(a->strs[n]);
234 }
235 
236 /*
237  * return pointer to C string 'n'
238  *
239  * So long as the index is a valid number ensure
240  * that a pointer is returned even if the newstr isn't
241  * allocated. Only return NULL if the index
242  * is invalid. Thus we can convert loops like:
243  *
244  * for ( i=0; i<a->n; ++i ) {
245  *      p = slist_cstr( a, i );
246  *      if ( p==NULL ) continue; // empty string
247  *      ...
248  * }
249  *
250  * to
251  *
252  * i = 0;
253  * while ( ( p = slist_cstr( a, i ) ) ) {
254  *      ...
255  *      i++;
256  * }
257  *
258  */
259 char *
slist_cstr(slist * a,slist_index n)260 slist_cstr( slist *a, slist_index n )
261 {
262 	static char empty[] = "";
263 	char *p;
264 
265 	assert( a );
266 
267 	if ( !slist_valid_num( a, n ) ) return NULL;
268 	p = str_cstr( &(a->strs[n]) );
269 	if ( p ) return p;
270 	else return empty;
271 }
272 
273 static inline int
slist_alloc(slist * a,slist_index alloc)274 slist_alloc( slist *a, slist_index alloc )
275 {
276 	slist_index i;
277 
278 	a->strs = ( str* ) malloc( sizeof( str ) * alloc );
279 	if ( !(a->strs) ) return SLIST_ERR_MEMERR;
280 
281 	a->max = alloc;
282 	a->n   = 0;
283 
284 	for ( i=0; i<alloc; ++i )
285 		str_init( &(a->strs[i]) );
286 
287 	return SLIST_OK;
288 }
289 
290 static inline int
slist_realloc(slist * a,slist_index alloc)291 slist_realloc( slist *a, slist_index alloc )
292 {
293 	slist_index i;
294 	str *more;
295 
296 	more = ( str* ) realloc( a->strs, sizeof( str ) * alloc );
297 	if ( !more ) return SLIST_ERR_MEMERR;
298 
299 	a->strs = more;
300 
301 	for ( i=a->max; i<alloc; ++i )
302 		str_init( &(a->strs[i]) );
303 
304 	a->max = alloc;
305 
306 	return SLIST_OK;
307 }
308 
309 #define SLIST_EXACT_SIZE  (0)
310 #define SLIST_DOUBLE_SIZE (1)
311 
312 static int
slist_ensure_space(slist * a,slist_index n,int mode)313 slist_ensure_space( slist *a, slist_index n, int mode )
314 {
315 	int status = SLIST_OK;
316 	int alloc = n;
317 
318 	if ( a->max==0 ) {
319 		if ( mode == SLIST_DOUBLE_SIZE && alloc < SLIST_MINALLOC ) alloc = SLIST_MINALLOC;
320 		status = slist_alloc( a, alloc );
321 	}
322 
323 	else if ( a->max < n ) {
324 		if ( mode == SLIST_DOUBLE_SIZE && alloc < a->max * 2 ) alloc = a->max * 2;
325 		status = slist_realloc( a, alloc );
326 	}
327 
328 	return status;
329 }
330 
331 int
slist_addvp(slist * a,int mode,void * vp)332 slist_addvp( slist *a, int mode, void *vp )
333 {
334 	str *s = NULL;
335 	int status;
336 
337 	status = slist_ensure_space( a, a->n+1, SLIST_DOUBLE_SIZE );
338 
339 	if ( status==SLIST_OK ) {
340 
341 		s = &( a->strs[a->n] );
342 
343 		if ( mode==SLIST_CHR )
344 			str_strcpyc( s, (const char*) vp );
345 		else
346 			str_strcpy( s, (str*) vp );
347 
348 		if ( str_memerr( s ) ) return SLIST_ERR_MEMERR;
349 		a->n++;
350 		if ( a->sorted && a->n > 1 ) {
351 			if ( slist_comp_step( a, a->n-2, a->n-1 ) > 0 )
352 				a->sorted = 0;
353 		}
354 
355 	}
356 
357 	return SLIST_OK;
358 }
359 int
slist_addc(slist * a,const char * s)360 slist_addc( slist *a, const char *s )
361 {
362 	return slist_addvp( a, SLIST_CHR, (void*)s );
363 }
364 int
slist_add(slist * a,str * s)365 slist_add( slist *a, str *s )
366 {
367 	return slist_addvp( a, SLIST_STR, (void*)s );
368 }
369 
370 int
slist_addvp_ret(slist * a,int mode,void * vp,int retok,int reterr)371 slist_addvp_ret( slist *a, int mode, void *vp, int retok, int reterr )
372 {
373 	int status = slist_addvp( a, mode, vp );
374 	if ( status==SLIST_OK ) return retok;
375 	else return reterr;
376 }
377 int
slist_addc_ret(slist * a,const char * value,int retok,int reterr)378 slist_addc_ret( slist *a, const char *value, int retok, int reterr )
379 {
380 	int status = slist_addc( a, value );
381 	if ( status==SLIST_OK ) return retok;
382 	else return reterr;
383 }
384 int
slist_add_ret(slist * a,str * value,int retok,int reterr)385 slist_add_ret( slist *a, str *value, int retok, int reterr )
386 {
387 	int status = slist_add( a, value );
388 	if ( status==SLIST_OK ) return retok;
389 	else return reterr;
390 }
391 
392 int
slist_addvp_unique(slist * a,int mode,void * vp)393 slist_addvp_unique( slist *a, int mode, void *vp )
394 {
395 	int n;
396 
397 	if ( mode==SLIST_CHR )
398 		n = slist_findc( a, (const char*) vp );
399 	else
400 		n = slist_find( a, (str*) vp );
401 
402 	if ( slist_wasfound( a, n ) )
403 		return SLIST_OK;
404 	else
405 		return slist_addvp( a, mode, vp );
406 }
407 int
slist_addc_unique(slist * a,const char * s)408 slist_addc_unique( slist *a, const char *s )
409 {
410 	return slist_addvp_unique( a, SLIST_CHR, (void*)s );
411 }
412 int
slist_add_unique(slist * a,str * s)413 slist_add_unique( slist *a, str *s )
414 {
415 	return slist_addvp_unique( a, SLIST_STR, (void*)s );
416 }
417 
418 int
slist_addvp_unique_ret(slist * a,int mode,void * vp,int retok,int reterr)419 slist_addvp_unique_ret( slist *a, int mode, void *vp, int retok, int reterr )
420 {
421 	int status = slist_addvp_unique( a, mode, vp );
422 	if ( status==SLIST_OK ) return retok;
423 	else return reterr;
424 }
425 int
slist_addc_unique_ret(slist * a,const char * s,int retok,int reterr)426 slist_addc_unique_ret( slist *a, const char *s, int retok, int reterr )
427 {
428 	int status = slist_addc_unique( a, s );
429 	if ( status==SLIST_OK ) return retok;
430 	else return reterr;
431 }
432 int
slist_add_unique_ret(slist * a,str * s,int retok,int reterr)433 slist_add_unique_ret( slist *a, str *s, int retok, int reterr )
434 {
435 	int status = slist_add_unique( a, s );
436 	if ( status==SLIST_OK ) return retok;
437 	else return reterr;
438 }
439 
440 int
slist_addvp_all(slist * a,int mode,...)441 slist_addvp_all( slist *a, int mode, ... )
442 {
443 	int status = SLIST_OK;
444 	va_list ap;
445 	void *v;
446 
447 	va_start( ap, mode );
448 
449 	do {
450 
451 		if ( mode==SLIST_CHR )
452 			v = va_arg( ap, char * );
453 		else
454 			v = va_arg( ap, str * );
455 
456 		if ( v ) {
457 			status = slist_addvp( a, mode, v );
458 			if ( status!=SLIST_OK ) goto out;
459 		}
460 
461 	} while ( v );
462 
463 out:
464 	va_end( ap );
465 	return status;
466 }
467 
468 int
slist_add_all(slist * a,...)469 slist_add_all( slist *a, ... )
470 {
471 	int status = SLIST_OK;
472 	va_list ap;
473 	str *v;
474 
475 	va_start( ap, a );
476 
477 	do {
478 		v = va_arg( ap, str * );
479 
480 		if ( v ) {
481 			status = slist_addvp( a, SLIST_STR, (void*)v );
482 			if ( status!=SLIST_OK ) goto out;
483 		}
484 
485 	} while ( v );
486 out:
487 	va_end( ap );
488 	return status;
489 }
490 
491 int
slist_addc_all(slist * a,...)492 slist_addc_all( slist *a, ... )
493 {
494 	int status = SLIST_OK;
495 	const char *v;
496 	va_list ap;
497 
498 	va_start( ap, a );
499 
500 	do {
501 
502 		v = va_arg( ap, const char * );
503 
504 		if ( v ) {
505 			status = slist_addvp( a, SLIST_CHR, (void*)v );
506 			if ( status!=SLIST_OK ) goto out;
507 		}
508 
509 	} while ( v );
510 out:
511 	va_end( ap );
512 	return status;
513 }
514 
515 int
slist_append(slist * a,slist * toadd)516 slist_append( slist *a, slist *toadd )
517 {
518 	int i, status;
519 
520 	assert( a );
521 	assert( toadd );
522 
523 	status = slist_ensure_space( a, a->n + toadd->n, SLIST_EXACT_SIZE );
524 
525 	if ( status == SLIST_OK ) {
526 
527 		for ( i=0; i<toadd->n; ++i ) {
528 			str_strcpy( &(a->strs[a->n+i]), &(toadd->strs[i]) );
529 			if ( str_memerr( &(a->strs[a->n+i]) ) ) return SLIST_ERR_MEMERR;
530 		}
531 
532 		if ( a->sorted && toadd->sorted == 0 ) a->sorted = 0;
533 		if ( a->sorted && a->n > 0 ) {
534 			if ( slist_comp_step( a, a->n-1, a->n ) > 0 ) {
535 				a->sorted = 0;
536 			}
537 		}
538 
539 		a->n += toadd->n;
540 
541 	}
542 
543 	return status;
544 }
545 
546 int
slist_append_unique(slist * a,slist * toadd)547 slist_append_unique( slist *a, slist *toadd )
548 {
549 	int i, status;
550 
551 	assert( a );
552 	assert( toadd );
553 
554 	for ( i=0; i<toadd->n; ++i ) {
555 		status = slist_add_unique( a, &(toadd->strs[i]) );
556 		if ( status!=SLIST_OK ) return status;
557 	}
558 
559 	return SLIST_OK;
560 }
561 
562 int
slist_append_ret(slist * a,slist * toadd,int retok,int reterr)563 slist_append_ret( slist *a, slist *toadd, int retok, int reterr )
564 {
565 	int status;
566 
567 	status = slist_append( a, toadd );
568 	if ( status==SLIST_OK ) return retok;
569 	else return reterr;
570 }
571 
572 int
slist_append_unique_ret(slist * a,slist * toadd,int retok,int reterr)573 slist_append_unique_ret( slist *a, slist *toadd, int retok, int reterr )
574 {
575 	int status;
576 
577 	status = slist_append_unique( a, toadd );
578 	if ( status==SLIST_OK ) return retok;
579 	else return reterr;
580 }
581 
582 int
slist_remove(slist * a,slist_index n)583 slist_remove( slist *a, slist_index n )
584 {
585 	int i;
586 
587 	assert( a );
588 
589 	if ( !slist_valid_num( a, n ) ) return SLIST_ERR_BADPARAM;
590 
591 	for ( i=n+1; i<a->n; ++i ) {
592 		str_strcpy( &(a->strs[i-1]), &(a->strs[i]) );
593 		if ( str_memerr( &(a->strs[i-1]) ) ) return SLIST_ERR_MEMERR;
594 	}
595 
596 	a->n--;
597 
598 	return SLIST_OK;
599 }
600 
601 void
slist_sort(slist * a)602 slist_sort( slist *a )
603 {
604 	qsort( a->strs, a->n, sizeof( str ), slist_comp );
605 	a->sorted = 1;
606 }
607 
608 void
slist_revsort(slist * a)609 slist_revsort( slist *a )
610 {
611 	qsort( a->strs, a->n, sizeof( str ), slist_revcomp );
612 	a->sorted = 0;
613 }
614 
615 static slist_index
slist_find_sorted(slist * a,const char * searchstr)616 slist_find_sorted( slist *a, const char *searchstr )
617 {
618 	slist_index min, max, mid;
619 	str s, *cs;
620 	int comp;
621 
622 	assert( a );
623 	assert( searchstr );
624 
625 	str_initstrc( &s, searchstr );
626 	min = 0;
627 	max = a->n - 1;
628 	while ( min <= max ) {
629 		mid = ( min + max ) / 2;
630 		cs = slist_str( a, mid );
631 		comp = slist_comp( (void*)cs, (void*) (&s) );
632 		if ( comp==0 ) {
633 			str_free( &s );
634 			return mid;
635 		}
636 		else if ( comp > 0 ) max = mid - 1;
637 		else if ( comp < 0 ) min = mid + 1;
638 	}
639 	str_free( &s );
640 	return -1;
641 }
642 
643 static slist_index
slist_find_simple(slist * a,const char * searchstr,int nocase)644 slist_find_simple( slist *a, const char *searchstr, int nocase )
645 {
646 	slist_index i;
647 
648 	assert( a );
649 	assert( searchstr );
650 
651 	if ( nocase ) {
652 		for ( i=0; i<a->n; ++i )
653 			if ( !str_strcasecmpc( &(a->strs[i]), searchstr ) )
654 				return i;
655 	} else {
656 		for ( i=0; i<a->n; ++i )
657 			if ( !str_strcmpc( &(a->strs[i]), searchstr ) )
658 				return i;
659 	}
660 	return -1;
661 }
662 
663 slist_index
slist_findc(slist * a,const char * searchstr)664 slist_findc( slist *a, const char *searchstr )
665 {
666 	assert( a );
667 
668 	if ( a->n==0 ) return -1;
669 	if ( a->sorted )
670 		return slist_find_sorted( a, searchstr );
671 	else
672 		return slist_find_simple( a, searchstr, 0 );
673 }
674 
675 slist_index
slist_find(slist * a,str * searchstr)676 slist_find( slist *a, str *searchstr )
677 {
678 	if ( searchstr->len==0 ) return -1;
679 	return slist_findc( a, str_cstr( searchstr ) );
680 }
681 
682 slist_index
slist_findnocasec(slist * a,const char * searchstr)683 slist_findnocasec( slist *a, const char *searchstr )
684 {
685 	assert( a );
686 
687 	return slist_find_simple( a, searchstr, 1 );
688 }
689 
690 slist_index
slist_findnocase(slist * a,str * searchstr)691 slist_findnocase( slist *a, str *searchstr )
692 {
693 	if ( searchstr->len==0 ) return -1;
694 	return slist_findnocasec( a, str_cstr( searchstr ) );
695 }
696 
697 int
slist_wasfound(slist * a,slist_index n)698 slist_wasfound( slist *a, slist_index n )
699 {
700 	return ( n!=-1 );
701 }
702 
703 int
slist_wasnotfound(slist * a,slist_index n)704 slist_wasnotfound( slist *a, slist_index n )
705 {
706 	return ( n==-1 );
707 }
708 
709 int
slist_fillfp(slist * a,FILE * fp,unsigned char skip_blank_lines)710 slist_fillfp( slist *a, FILE *fp, unsigned char skip_blank_lines )
711 {
712 	int status, ret = SLIST_OK;
713 	str line;
714 
715 	assert( a );
716 	assert( fp );
717 
718 	slist_empty( a );
719 	str_init( &line );
720 
721 	while ( str_fgetline( &line, fp ) ) {
722 		if ( skip_blank_lines && line.len==0 ) continue;
723 		status = slist_add( a, &line );
724 		if ( status!=SLIST_OK ) {
725 			ret = SLIST_ERR_MEMERR;
726 			goto out;
727 		}
728 	}
729 
730 out:
731 	str_free( &line );
732 	return ret;
733 }
734 
735 int
slist_fill(slist * a,const char * filename,unsigned char skip_blank_lines)736 slist_fill( slist *a, const char *filename, unsigned char skip_blank_lines )
737 {
738 	FILE *fp;
739 	int ret;
740 
741 	fp = fopen( filename, "r" );
742 	if ( !fp ) return SLIST_ERR_CANTOPEN;
743 
744 	ret = slist_fillfp( a, fp, skip_blank_lines );
745 
746 	fclose( fp );
747 
748 	return ret;
749 }
750 
751 int
slist_copy(slist * to,slist * from)752 slist_copy( slist *to, slist *from )
753 {
754 	slist_index i;
755 	int status;
756 
757 	assert( to );
758 	assert( from );
759 
760 	slist_free( to );
761 
762 	if ( from->n==0 ) return SLIST_OK;
763 
764 	status = slist_ensure_space( to, from->n, SLIST_EXACT_SIZE );
765 
766 	if ( status == SLIST_OK ) {
767 
768 		to->sorted = from->sorted;
769 		to->n      = from->n;
770 
771 		for ( i=0; i<from->n; i++ ) {
772 			str_strcpy( &(to->strs[i]), &(from->strs[i]) );
773 			if ( str_memerr( &(to->strs[i]) ) ) return SLIST_ERR_MEMERR;
774 		}
775 
776 	}
777 	return SLIST_OK;
778 }
779 
780 int
slist_copy_ret(slist * to,slist * from,int retok,int reterr)781 slist_copy_ret( slist *to, slist *from, int retok, int reterr )
782 {
783 	int status = slist_copy( to, from );
784 	if ( status==SLIST_OK ) return retok;
785 	else return reterr;
786 }
787 
788 slist *
slist_dup(slist * from)789 slist_dup( slist *from )
790 {
791 	int status;
792 	slist *to;
793 
794 	to = slist_new();
795 	if ( to ) {
796 		status = slist_copy( to, from );
797 		if ( status!=SLIST_OK ) {
798 			slist_delete( to );
799 			to = NULL;
800 		}
801 	}
802 
803 	return to;
804 }
805 
806 unsigned long
slist_get_maxlen(slist * a)807 slist_get_maxlen( slist *a )
808 {
809 	unsigned long max = 0;
810 	slist_index i;
811 	str *s;
812 
813 	assert( a );
814 
815 	for ( i=0; i<a->n; ++i ) {
816 		s = slist_str( a, i );
817 		if ( s->len > max ) max = s->len;
818 	}
819 
820 	return max;
821 }
822 
823 void
slist_dump(slist * a,FILE * fp,int newline)824 slist_dump( slist *a, FILE *fp, int newline )
825 {
826 	slist_index i;
827 
828 	assert( a );
829 	assert( fp );
830 
831 	if ( newline ) {
832 		for ( i=0; i<a->n; ++i )
833 			fprintf( fp, "%s\n", slist_cstr( a, i ) );
834 	}
835 
836 	else {
837 		for ( i=0; i<a->n; ++i )
838 			fprintf( fp, "%s", slist_cstr( a, i ) );
839 	}
840 }
841 
842 int
slist_match_entry(slist * a,int n,const char * s)843 slist_match_entry( slist *a, int n, const char *s )
844 {
845 	assert( a );
846 
847 	if ( !slist_valid_num( a, n ) ) return 0;
848 	if ( str_strcmpc( &(a->strs[n]), s ) ) return 0;
849 	return 1;
850 }
851 
852 void
slist_trimend(slist * a,int n)853 slist_trimend( slist *a, int n )
854 {
855 	slist_index i;
856 
857 	assert( a );
858 
859 	if ( a->n - n < 1 ) {
860 		slist_empty( a );
861 	} else {
862 		for ( i=a->n -n; i<a->n; ++i ) {
863 			str_empty( &(a->strs[i]) );
864 		}
865 		a->n -= n;
866 	}
867 }
868 
869 int
slist_tokenizec(slist * tokens,char * p,const char * delim,int merge_delim)870 slist_tokenizec( slist *tokens, char *p, const char *delim, int merge_delim )
871 {
872 	int status, ret = SLIST_OK;
873 	char *q;
874 	str s;
875 
876 	assert( tokens );
877 
878 	slist_empty( tokens );
879 	str_init( &s );
880 	while ( p && *p ) {
881 		q = p;
882 		while ( *q && !strchr( delim, *q ) ) q++;
883 		str_segcpy( &s, p, q );
884 		if ( str_memerr( &s ) ) { ret = SLIST_ERR_MEMERR; goto out; }
885 		if ( s.len ) {
886 			status = slist_addvp( tokens, SLIST_STR, (void*) &s );
887 			if ( status!=SLIST_OK ) { ret = SLIST_ERR_MEMERR; goto out; }
888 		} else if ( !merge_delim ) {
889 			status = slist_addvp( tokens, SLIST_CHR, (void*) "" );
890 			if ( status!=SLIST_OK ) { ret = SLIST_ERR_MEMERR; goto out; }
891 		}
892 		p = q;
893 		if ( *p ) p++;
894 	}
895 out:
896 	str_free( &s );
897 	return ret;
898 }
899 
900 int
slist_tokenize(slist * tokens,str * in,const char * delim,int merge_delim)901 slist_tokenize( slist *tokens, str *in, const char *delim, int merge_delim )
902 {
903 	return slist_tokenizec( tokens, str_cstr( in ), delim, merge_delim );
904 }
905 
906 void
slists_init(slist * a,...)907 slists_init( slist *a, ... )
908 {
909 	slist *a2;
910 	va_list ap;
911 	slist_init( a );
912 	va_start( ap, a );
913 	do {
914 		a2 = va_arg( ap, slist * );
915 		if ( a2 ) slist_init( a2 );
916 	} while ( a2 );
917 	va_end( ap );
918 }
919 
920 void
slists_free(slist * a,...)921 slists_free( slist *a, ... )
922 {
923 	slist *a2;
924 	va_list ap;
925 	slist_free( a );
926 	va_start( ap, a );
927 	do {
928 		a2 = va_arg( ap, slist * );
929 		if ( a2 ) slist_free( a2 );
930 	} while ( a2 );
931 	va_end( ap );
932 }
933 
934 void
slists_empty(slist * a,...)935 slists_empty( slist *a, ... )
936 {
937 	slist *a2;
938 	va_list ap;
939 	slist_empty( a );
940 	va_start( ap, a );
941 	do {
942 		a2 = va_arg( ap, slist * );
943 		if ( a2 ) slist_empty( a2 );
944 	} while ( a2 );
945 	va_end( ap );
946 }
947