/* * slist.c * * version: 2017-07-04 * * Copyright (c) Chris Putnam 2004-2017 * * Source code released under the GPL version 2 * * Implements a simple managed array of strs. * */ #include "slist.h" /* Do not use asserts in VPLIST_NOASSERT defined */ #ifdef VPLIST_NOASSERT #define NDEBUG #endif #include #define SLIST_MINALLOC (20) #define SLIST_EXACT_SIZE (0) #define SLIST_DOUBLE_SIZE (1) /* * returns 1 if n is valid string in slist */ static inline int slist_valid_num( slist *a, slist_index n ) { if ( n < 0 || n >= a->n ) return 0; return 1; } void slist_init( slist *a ) { assert( a ); a->strs = NULL; a->max = 0; a->n = 0; a->sorted = 1; } int slist_init_values( slist *a, ... ) { int status = SLIST_OK; va_list ap; str *s, *t; slist_init( a ); va_start( ap, a ); do { s = va_arg( ap, str * ); if ( s ) { t = slist_add( a, s ); if ( !t ) { status = SLIST_ERR_MEMERR; goto out; } } } while ( s ); out: va_end( ap ); return status; } int slist_init_valuesc( slist *a, ... ) { int status = SLIST_OK; va_list ap; char *s; str *t; slist_init( a ); va_start( ap, a ); do { s = va_arg( ap, char * ); if ( s ) { t = slist_addc( a, s ); if ( !t ) { status = SLIST_ERR_MEMERR; goto out; } } } while ( s ); out: va_end( ap ); return status; } void slist_empty( slist *a ) { slist_index i; assert( a ); for ( i=0; imax; ++i ) str_empty( &(a->strs[i]) ); a->n = 0; a->sorted = 1; } void slist_free( slist *a ) { slist_index i; assert( a ); for ( i=0; imax; ++i ) str_free( &(a->strs[i]) ); free( a->strs ); slist_init( a ); } slist * slist_new( void ) { slist *a; a = ( slist * ) malloc( sizeof ( slist ) ); if ( a ) slist_init( a ); return a; } void slist_delete( slist *a ) { assert( a ); slist_free( a ); free( a ); } void slist_deletev( void *v ) { slist_delete( (slist*) v ); } void slist_swap( slist *a, slist_index n1, slist_index n2 ) { assert( a ); if ( slist_valid_num( a, n1 ) && slist_valid_num( a, n2 ) ) str_swapstrings( &(a->strs[n1]), &(a->strs[n2]) ); } static int slist_comp( const void *v1, const void *v2 ) { str *s1 = ( str *) v1; str *s2 = ( str *) v2; if ( !s1->len && !s2->len ) return 0; else if ( !s1->len ) return -1; else if ( !s2->len ) return 1; else return str_strcmp( s1, s2 ); } static int slist_comp_step( slist *a, slist_index n1, slist_index n2 ) { return slist_comp( (const void*) &(a->strs[n1]), (const void*) &(a->strs[n2]) ); } static str * slist_set_cleanup( slist *a, slist_index n ) { if ( str_memerr( &(a->strs[n]) ) ) return NULL; if ( a->sorted ) { if ( n>0 && slist_comp_step( a, n-1, n )>0 ) a->sorted = 0; } if ( a->sorted ) { if ( nn-1 && slist_comp_step( a, n, n+1 )>0 ) a->sorted = 0; } return &(a->strs[n]); } str * slist_setc( slist *a, slist_index n, const char *s ) { assert( a ); assert( s ); if ( !slist_valid_num( a, n ) ) return NULL; str_strcpyc( &(a->strs[n]), s ); return slist_set_cleanup( a, n ); } str * slist_set( slist *a, slist_index n, str *s ) { assert( s ); return slist_setc( a, n, str_cstr( s ) ); } /* * return pointer to str 'n' */ str * slist_str( slist *a, slist_index n ) { assert( a ); if ( !slist_valid_num( a, n ) ) return NULL; else return &(a->strs[n]); } /* * return pointer to C string 'n' * * So long as the index is a valid number ensure * that a pointer is returned even if the newstr isn't * allocated. Only return NULL if the index * is invalid. Thus we can convert loops like: * * for ( i=0; in; ++i ) { * p = slist_cstr( a, i ); * if ( p==NULL ) continue; // empty string * ... * } * * to * * i = 0; * while ( ( p = slist_cstr( a, i ) ) ) { * ... * i++; * } * */ char * slist_cstr( slist *a, slist_index n ) { static char empty[] = ""; char *p; assert( a ); if ( !slist_valid_num( a, n ) ) return NULL; p = str_cstr( &(a->strs[n]) ); if ( p ) return p; else return empty; } static inline int slist_alloc( slist *a, slist_index alloc ) { slist_index i; a->strs = ( str* ) malloc( sizeof( str ) * alloc ); if ( !(a->strs) ) return SLIST_ERR_MEMERR; a->max = alloc; a->n = 0; for ( i=0; istrs[i]) ); return SLIST_OK; } static inline int slist_realloc( slist *a, slist_index alloc ) { slist_index i; str *more; more = ( str* ) realloc( a->strs, sizeof( str ) * alloc ); if ( !more ) return SLIST_ERR_MEMERR; a->strs = more; for ( i=a->max; istrs[i]) ); a->max = alloc; return SLIST_OK; } #define SLIST_EXACT_SIZE (0) #define SLIST_DOUBLE_SIZE (1) static int slist_ensure_space( slist *a, slist_index n, int mode ) { int status = SLIST_OK; int alloc = n; if ( a->max==0 ) { if ( mode == SLIST_DOUBLE_SIZE && alloc < SLIST_MINALLOC ) alloc = SLIST_MINALLOC; status = slist_alloc( a, alloc ); } else if ( a->max < n ) { if ( mode == SLIST_DOUBLE_SIZE && alloc < a->max * 2 ) alloc = a->max * 2; status = slist_realloc( a, alloc ); } return status; } str * slist_addvp( slist *a, unsigned char mode, void *vp ) { str *s = NULL; int status; status = slist_ensure_space( a, a->n+1, SLIST_DOUBLE_SIZE ); if ( status==SLIST_OK ) { s = &( a->strs[a->n] ); if ( mode==SLIST_CHR ) str_strcpyc( s, (const char*) vp ); else str_strcpy( s, (str*) vp ); if ( str_memerr( s ) ) return NULL; a->n++; if ( a->sorted && a->n > 1 ) { if ( slist_comp_step( a, a->n-2, a->n-1 ) > 0 ) a->sorted = 0; } } return s; } str * slist_addc( slist *a, const char *s ) { return slist_addvp( a, SLIST_CHR, (void*)s ); } str * slist_add( slist *a, str *s ) { return slist_addvp( a, SLIST_STR, (void*)s ); } str * slist_addvp_unique( slist *a, unsigned char mode, void *vp ) { str *s; int n; if ( mode==SLIST_CHR ) n = slist_findc( a, (const char*) vp ); else n = slist_find( a, (str*) vp ); if ( slist_wasfound( a, n ) ) s = &( a->strs[n] ); else s = slist_addvp( a, mode, vp ); return s; } str * slist_addc_unique( slist *a, const char *s ) { return slist_addvp_unique( a, SLIST_CHR, (void*)s ); } str * slist_add_unique( slist *a, str *s ) { return slist_addvp_unique( a, SLIST_STR, (void*)s ); } int slist_addvp_all( slist *a, unsigned char mode, ... ) { int ret = SLIST_OK; va_list ap; void *v; str *s; va_start( ap, mode ); do { if ( mode==SLIST_CHR ) v = va_arg( ap, char * ); else v = va_arg( ap, str * ); if ( v ) { s = slist_addvp( a, mode, v ); if ( s==NULL ) { ret = SLIST_ERR_MEMERR; goto out; } } } while ( v ); out: va_end( ap ); return ret; } int slist_add_all( slist *a, ... ) { int ret = SLIST_OK; va_list ap; str *s, *v; va_start( ap, a ); do { v = va_arg( ap, str * ); if ( v ) { s = slist_addvp( a, SLIST_STR, (void*)v ); if ( s==NULL ) { ret = SLIST_ERR_MEMERR; goto out; } } } while ( v ); out: va_end( ap ); return ret; } int slist_addc_all( slist *a, ... ) { int ret = SLIST_OK; const char *v; va_list ap; str *s; va_start( ap, a ); do { v = va_arg( ap, const char * ); if ( v ) { s = slist_addvp( a, SLIST_CHR, (void*)v ); if ( s==NULL ) { ret = SLIST_ERR_MEMERR; goto out; } } } while ( v ); out: va_end( ap ); return ret; } int slist_append( slist *a, slist *toadd ) { int i, status; assert( a ); assert( toadd ); status = slist_ensure_space( a, a->n + toadd->n, SLIST_EXACT_SIZE ); if ( status == SLIST_OK ) { for ( i=0; in; ++i ) { str_strcpy( &(a->strs[a->n+i]), &(toadd->strs[i]) ); if ( str_memerr( &(a->strs[a->n+i]) ) ) return SLIST_ERR_MEMERR; } a->n += toadd->n; } return status; } int slist_append_unique( slist *a, slist *toadd ) { str *s; int i; assert( a ); assert( toadd ); for ( i=0; in; ++i ) { s = slist_add_unique( a, &(toadd->strs[i]) ); if ( !s ) return SLIST_ERR_MEMERR; } return SLIST_OK; } int slist_remove( slist *a, slist_index n ) { int i; assert( a ); if ( !slist_valid_num( a, n ) ) return SLIST_ERR_BADPARAM; for ( i=n+1; in; ++i ) { str_strcpy( &(a->strs[i-1]), &(a->strs[i]) ); if ( str_memerr( &(a->strs[i-1]) ) ) return SLIST_ERR_MEMERR; } a->n--; return SLIST_OK; } void slist_sort( slist *a ) { qsort( a->strs, a->n, sizeof( str ), slist_comp ); a->sorted = 1; } static slist_index slist_find_sorted( slist *a, const char *searchstr ) { slist_index min, max, mid; str s, *cs; int comp; assert( a ); assert( searchstr ); str_initstrc( &s, searchstr ); min = 0; max = a->n - 1; while ( min <= max ) { mid = ( min + max ) / 2; cs = slist_str( a, mid ); comp = slist_comp( (void*)cs, (void*) (&s) ); if ( comp==0 ) { str_free( &s ); return mid; } else if ( comp > 0 ) max = mid - 1; else if ( comp < 0 ) min = mid + 1; } str_free( &s ); return -1; } static slist_index slist_find_simple( slist *a, const char *searchstr, int nocase ) { slist_index i; assert( a ); assert( searchstr ); if ( nocase ) { for ( i=0; in; ++i ) if ( !str_strcasecmpc( &(a->strs[i]), searchstr ) ) return i; } else { for ( i=0; in; ++i ) if ( !str_strcmpc( &(a->strs[i]), searchstr ) ) return i; } return -1; } slist_index slist_findc( slist *a, const char *searchstr ) { assert( a ); if ( a->n==0 ) return -1; if ( a->sorted ) return slist_find_sorted( a, searchstr ); else return slist_find_simple( a, searchstr, 0 ); } slist_index slist_find( slist *a, str *searchstr ) { if ( searchstr->len==0 ) return -1; return slist_findc( a, str_cstr( searchstr ) ); } slist_index slist_findnocasec( slist *a, const char *searchstr ) { assert( a ); return slist_find_simple( a, searchstr, 1 ); } slist_index slist_findnocase( slist *a, str *searchstr ) { if ( searchstr->len==0 ) return -1; return slist_findnocasec( a, str_cstr( searchstr ) ); } int slist_wasfound( slist *a, slist_index n ) { return ( n!=-1 ); } int slist_wasnotfound( slist *a, slist_index n ) { return ( n==-1 ); } int slist_fillfp( slist *a, FILE *fp, unsigned char skip_blank_lines ) { int ret = SLIST_OK; str line, *s; assert( a ); assert( fp ); slist_empty( a ); str_init( &line ); while ( str_fgetline( &line, fp ) ) { if ( skip_blank_lines && line.len==0 ) continue; s = slist_add( a, &line ); if ( !s ) { ret = SLIST_ERR_MEMERR; goto out; } } out: str_free( &line ); return ret; } int slist_fill( slist *a, const char *filename, unsigned char skip_blank_lines ) { FILE *fp; int ret; fp = fopen( filename, "r" ); if ( !fp ) return SLIST_ERR_CANTOPEN; ret = slist_fillfp( a, fp, skip_blank_lines ); fclose( fp ); return ret; } int slist_copy( slist *to, slist *from ) { slist_index i; int status; assert( to ); assert( from ); slist_free( to ); if ( from->n==0 ) return SLIST_OK; status = slist_ensure_space( to, from->n, SLIST_EXACT_SIZE ); if ( status == SLIST_OK ) { to->sorted = from->sorted; to->n = from->n; for ( i=0; in; i++ ) { str_strcpy( &(to->strs[i]), &(from->strs[i]) ); if ( str_memerr( &(to->strs[i]) ) ) return SLIST_ERR_MEMERR; } } return SLIST_OK; } slist * slist_dup( slist *from ) { int status; slist *to; to = slist_new(); if ( to ) { status = slist_copy( to, from ); if ( status!=SLIST_OK ) { slist_delete( to ); to = NULL; } } return to; } unsigned long slist_get_maxlen( slist *a ) { unsigned long max = 0; slist_index i; str *s; assert( a ); for ( i=0; in; ++i ) { s = slist_str( a, i ); if ( s->len > max ) max = s->len; } return max; } void slist_dump( slist *a, FILE *fp, int newline ) { slist_index i; assert( a ); assert( fp ); if ( newline ) { for ( i=0; in; ++i ) fprintf( fp, "%s\n", slist_cstr( a, i ) ); } else { for ( i=0; in; ++i ) fprintf( fp, "%s", slist_cstr( a, i ) ); } } int slist_match_entry( slist *a, int n, const char *s ) { assert( a ); if ( !slist_valid_num( a, n ) ) return 0; if ( str_strcmpc( &(a->strs[n]), s ) ) return 0; return 1; } void slist_trimend( slist *a, int n ) { slist_index i; assert( a ); if ( a->n - n < 1 ) { slist_empty( a ); } else { for ( i=a->n -n; in; ++i ) { str_empty( &(a->strs[i]) ); } a->n -= n; } } int slist_tokenizec( slist *tokens, char *p, const char *delim, int merge_delim ) { int ret = SLIST_OK; str s, *t; char *q; assert( tokens ); slist_empty( tokens ); str_init( &s ); while ( p && *p ) { q = p; while ( *q && !strchr( delim, *q ) ) q++; str_segcpy( &s, p, q ); if ( str_memerr( &s ) ) { ret = SLIST_ERR_MEMERR; goto out; } if ( s.len ) { t = slist_addvp( tokens, SLIST_STR, (void*) &s ); if ( !t ) { ret = SLIST_ERR_MEMERR; goto out; } } else if ( !merge_delim ) { t = slist_addvp( tokens, SLIST_CHR, (void*) "" ); if ( !t ) { ret = SLIST_ERR_MEMERR; goto out; } } p = q; if ( *p ) p++; } out: str_free( &s ); return ret; } int slist_tokenize( slist *tokens, str *in, const char *delim, int merge_delim ) { return slist_tokenizec( tokens, str_cstr( in ), delim, merge_delim ); } void slists_init( slist *a, ... ) { slist *a2; va_list ap; slist_init( a ); va_start( ap, a ); do { a2 = va_arg( ap, slist * ); if ( a2 ) slist_init( a2 ); } while ( a2 ); va_end( ap ); } void slists_free( slist *a, ... ) { slist *a2; va_list ap; slist_free( a ); va_start( ap, a ); do { a2 = va_arg( ap, slist * ); if ( a2 ) slist_free( a2 ); } while ( a2 ); va_end( ap ); } void slists_empty( slist *a, ... ) { slist *a2; va_list ap; slist_empty( a ); va_start( ap, a ); do { a2 = va_arg( ap, slist * ); if ( a2 ) slist_empty( a2 ); } while ( a2 ); va_end( ap ); }