Changed the signature of osrfStringArrayGetString():
[OpenSRF.git] / src / libopensrf / string_array.c
1 /**
2         @file string_array.c
3         @brief Implement osrfStringArray, a vector of character strings.
4
5         An osrfStringArray is implemented as a thin wrapper around an osrfList.  The latter is
6         incorporated bodily in the osrfStringArray structure, not as a pointer, so as to avoid
7         a layer of malloc() and free().
8
9         Operations on the osrfList are restricted so as not to leave NULL pointers in the middle.
10 */
11
12 #include <opensrf/string_array.h>
13
14 /**
15         @brief Create and initialize an osrfStringArray.
16         @param size How many strings to allow space for initially.
17         @return Pointer to a newly created osrfStringArray.
18
19         If the size parameter is zero, osrfNewStringArray uses a default value.  If the initial
20         allocation isn't big enough, more space will be added as needed.
21
22         The calling code is responsible for freeing the osrfStringArray by calling
23         osrfStringArrayFree().
24 */
25 osrfStringArray* osrfNewStringArray(int size) {
26         if(size > STRING_ARRAY_MAX_SIZE)
27                 osrfLogError( OSRF_LOG_MARK, "osrfNewStringArray size is too large");
28
29         osrfStringArray* arr;
30         OSRF_MALLOC(arr, sizeof(osrfStringArray));
31         arr->size = 0;
32
33         // Initialize list
34         arr->list.size = 0;
35         arr->list.freeItem = NULL;
36         if( size <= 0 )
37                 arr->list.arrsize = 16;
38         else
39                 arr->list.arrsize = size;
40         OSRF_MALLOC( arr->list.arrlist, arr->list.arrsize * sizeof(void*) );
41
42         // Nullify all pointers in the array
43
44         int i;
45         for( i = 0; i < arr->list.arrsize; ++i )
46                 arr->list.arrlist[ i ] = NULL;
47
48         osrfListSetDefaultFree(&arr->list);
49         return arr;
50 }
51
52 /**
53         @brief Add a string to the end of an osrfStringArray.
54         @param arr Pointer to the osrfStringArray to which the string will be added.
55         @param string Pointer to the character string to be added.
56
57         The string stored is a copy; the original is not affected.
58
59         If either parameter is NULL, nothing happens.
60 */
61 void osrfStringArrayAdd( osrfStringArray* arr, const char* string ) {
62         if(arr == NULL || string == NULL ) return;
63         if( arr->list.size > STRING_ARRAY_MAX_SIZE )
64                 osrfLogError( OSRF_LOG_MARK, "osrfStringArrayAdd size is too large" );
65     osrfListPush(&arr->list, strdup(string));
66     arr->size = arr->list.size;
67 }
68
69 /**
70         @brief Return a pointer to the string stored at a specified position in an osrfStringArray.
71         @param arr Pointer to the osrfStringArray.
72         @param index A zero-based index into the array
73         @return A pointer to the string stored at the specified position. if it exists;
74                 or else NULL.
75 */
76 const char* osrfStringArrayGetString( const osrfStringArray* arr, int index ) {
77     if(!arr) return NULL;
78         return OSRF_LIST_GET_INDEX(&arr->list, index);
79 }
80
81 /**
82         @brief Free an osrfStringArray, and all the strings inside it.
83         @param arr Pointer to the osrfStringArray to be freed.
84 */
85 void osrfStringArrayFree(osrfStringArray* arr) {
86
87         // This function is a sleazy hack designed to avoid the
88         // need to duplicate the code in osrfListFree().  It
89         // works because:
90         //
91         // 1. The osrfList is the first member of an
92         //    osrfStringArray.  C guarantees that a pointer
93         //    to the one is also a pointer to the other.
94         //
95         // 2. The rest of the osrfStringArray owns no memory
96         //    and requires no special attention when freeing.
97         //
98         // If these facts ever cease to be true, we'll have to
99         // revisit this function.
100
101         osrfListFree( (osrfList*) arr );
102 }
103
104 /**
105         @brief Determine whether an osrfStringArray contains a specified string.
106         @param arr Pointer to the osrfStringArray.
107         @param string Pointer to the string to be sought.
108         @return A boolean: 1 if the string is present in the osrfStringArray, or 0 if it isn't.
109
110         The search is case-sensitive.
111 */
112 int osrfStringArrayContains(
113         const osrfStringArray* arr, const char* string ) {
114         if(!(arr && string)) return 0;
115         int i;
116         for( i = 0; i < arr->size; i++ ) {
117         char* str = OSRF_LIST_GET_INDEX(&arr->list, i);
118                 if(str && !strcmp(str, string))
119             return 1;
120         }
121
122         return 0;
123 }
124
125 /**
126         @brief Remove the first occurrence, if any, of a specified string from an osrfStringArray.
127         @param arr Pointer to the osrfStringArray.
128         @param tstr Pointer to a string to be removed.
129 */
130 void osrfStringArrayRemove( osrfStringArray* arr, const char* tstr ) {
131         if(!(arr && tstr)) return;
132         int i;
133     char* str;
134         int removed = 0;  // boolean
135
136         for( i = 0; i < arr->size; i++ ) {
137                 /* find and remove the string */
138                 str = OSRF_LIST_GET_INDEX(&arr->list, i);
139                 if(str && !strcmp(str, tstr)) {
140                         osrfListRemove(&arr->list, i);
141                         removed = 1;
142                         break;
143                 }
144         }
145
146         if( ! removed )
147                 return;         // Nothing was removed
148
149     /* disable automatic item freeing on delete, and shift
150      * items down in the array to fill in the gap
151      */
152     arr->list.freeItem = NULL;
153         for( ; i < arr->size - 1; i++ )
154         osrfListSet(&arr->list, OSRF_LIST_GET_INDEX(&arr->list, i+1), i);
155
156     /* remove the last item since it was shifted up */
157     osrfListRemove(&arr->list, i);
158
159     /* re-enable automatic item freeing in delete */
160     osrfListSetDefaultFree(&arr->list);
161         arr->size--;
162 }
163
164 /**
165         @brief Tokenize a string into an array of substrings separated by a specified delimiter.
166         @param src A pointer to the string to be parsed.
167         @param delim The delimiter character.
168         @return Pointer to a newly constructed osrfStringArray containing the tokens.
169
170         A series of consecutive delimiter characters is treated as a single separator.
171
172         The input string is left unchanged.
173
174         The calling code is responsible for freeing the osrfStringArray by calling
175         osrfStringArrayFree().
176 */
177 osrfStringArray* osrfStringArrayTokenize( const char* src, char delim )
178 {
179         // Take the length so that we know how big a buffer we need,
180         // in the worst case.  Estimate the number of tokens, assuming
181         // 5 characters per token, and add a few for a pad.
182
183         if( NULL == src || '\0' == *src )               // Got nothing?
184                 return osrfNewStringArray( 1 );         // Return empty array
185
186         size_t src_len = strlen( src );
187         size_t est_count = src_len / 6 + 5;
188         int in_token = 0;     // boolean
189         char buf[ src_len + 1 ];
190         char* out = buf;
191         osrfStringArray* arr = osrfNewStringArray( est_count );
192
193         for( ;; ++src ) {
194                 if( in_token ) {        // We're building a token
195                         if( *src == delim ) {
196                                 *out = '\0';
197                                 osrfStringArrayAdd( arr, buf );
198                                 in_token = 0;
199                         }
200                         else if( '\0' == *src ) {
201                                 *out = '\0';
202                                 osrfStringArrayAdd( arr, buf );
203                                 break;
204                         }
205                         else {
206                                 *out++ = *src;
207                         }
208                 }
209                 else {                          // We're between tokens
210                         if( *src == delim ) {
211                                 ;                       // skip it
212                         }
213                         else if( '\0' == *src ) {
214                                 break;
215                         }
216                         else {
217                                 out = buf;              // Start the next one
218                                 *out++ = *src;
219                                 in_token = 1;
220                         }
221                 }
222         }
223
224         return arr;
225 }