fix up index/position type for calls of various osrfList* functions
[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 Render an osrfStringArray empty.
83         @param arr Pointer to the osrfStringArray to be emptied.
84 */
85 void osrfStringArrayClear( osrfStringArray* arr ) {
86         if( arr ) {
87                 osrfListClear( &arr->list );
88                 arr->size = 0;
89         }
90 }
91
92 /**
93         @brief Exchange the contents of two osrfStringArrays.
94         @param one Pointer to the first osrfStringArray.
95         @param two Pointer to the second osrfStringArray.
96
97         After the swap, the first osrfStringArray contains what had been the contents of the
98         second, and vice versa.  The swap also works if both parameters point to the same
99         osrfStringArray; i.e. there is no net change.
100
101         If either parameter is NULL, nothing happens.
102 */
103 void osrfStringArraySwap( osrfStringArray* one, osrfStringArray* two ) {
104         if( one && two ) {
105                 osrfListSwap( &one->list, &two->list );
106                 int temp = one->size;
107                 one->size = two->size;
108                 two->size = temp;
109         }
110 }
111
112 /**
113         @brief Free an osrfStringArray, and all the strings inside it.
114         @param arr Pointer to the osrfStringArray to be freed.
115 */
116 void osrfStringArrayFree(osrfStringArray* arr) {
117
118         // This function is a sleazy hack designed to avoid the
119         // need to duplicate the code in osrfListFree().  It
120         // works because:
121         //
122         // 1. The osrfList is the first member of an
123         //    osrfStringArray.  C guarantees that a pointer
124         //    to the one is also a pointer to the other.
125         //
126         // 2. The rest of the osrfStringArray owns no memory
127         //    and requires no special attention when freeing.
128         //
129         // If these facts ever cease to be true, we'll have to
130         // revisit this function.
131
132         osrfListFree( (osrfList*) arr );
133 }
134
135 /**
136         @brief Determine whether an osrfStringArray contains a specified string.
137         @param arr Pointer to the osrfStringArray.
138         @param string Pointer to the string to be sought.
139         @return A boolean: 1 if the string is present in the osrfStringArray, or 0 if it isn't.
140
141         The search is case-sensitive.
142 */
143 int osrfStringArrayContains(
144         const osrfStringArray* arr, const char* string ) {
145         if(!(arr && string)) return 0;
146         int i;
147         for( i = 0; i < arr->size; i++ ) {
148         char* str = OSRF_LIST_GET_INDEX(&arr->list, i);
149                 if(str && !strcmp(str, string))
150             return 1;
151         }
152
153         return 0;
154 }
155
156 /**
157         @brief Remove the first occurrence, if any, of a specified string from an osrfStringArray.
158         @param arr Pointer to the osrfStringArray.
159         @param tstr Pointer to a string to be removed.
160 */
161 void osrfStringArrayRemove( osrfStringArray* arr, const char* tstr ) {
162         if(!(arr && tstr)) return;
163         unsigned int i;
164     char* str;
165         int removed = 0;  // boolean
166
167         for( i = 0; i < arr->size; i++ ) {
168                 /* find and remove the string */
169                 str = OSRF_LIST_GET_INDEX(&arr->list, i);
170                 if(str && !strcmp(str, tstr)) {
171                         osrfListRemove(&arr->list, i);
172                         removed = 1;
173                         break;
174                 }
175         }
176
177         if( ! removed )
178                 return;         // Nothing was removed
179
180     /* disable automatic item freeing on delete, and shift
181      * items down in the array to fill in the gap
182      */
183     arr->list.freeItem = NULL;
184         for( ; i < arr->size - 1; i++ )
185         osrfListSet(&arr->list, OSRF_LIST_GET_INDEX(&arr->list, i+1), i);
186
187     /* remove the last item since it was shifted up */
188     osrfListRemove(&arr->list, i);
189
190     /* re-enable automatic item freeing in delete */
191     osrfListSetDefaultFree(&arr->list);
192         arr->size--;
193 }
194
195 /**
196         @brief Tokenize a string into an array of substrings separated by a specified delimiter.
197         @param src A pointer to the string to be parsed.
198         @param delim The delimiter character.
199         @return Pointer to a newly constructed osrfStringArray containing the tokens.
200
201         A series of consecutive delimiter characters is treated as a single separator.
202
203         The input string is left unchanged.
204
205         The calling code is responsible for freeing the osrfStringArray by calling
206         osrfStringArrayFree().
207 */
208 osrfStringArray* osrfStringArrayTokenize( const char* src, char delim )
209 {
210         // Take the length so that we know how big a buffer we need,
211         // in the worst case.  Estimate the number of tokens, assuming
212         // 5 characters per token, and add a few for a pad.
213
214         if( NULL == src || '\0' == *src )               // Got nothing?
215                 return osrfNewStringArray( 1 );         // Return empty array
216
217         size_t src_len = strlen( src );
218         size_t est_count = src_len / 6 + 5;
219         int in_token = 0;     // boolean
220         char buf[ src_len + 1 ];
221         char* out = buf;
222         osrfStringArray* arr = osrfNewStringArray( est_count );
223
224         for( ;; ++src ) {
225                 if( in_token ) {        // We're building a token
226                         if( *src == delim ) {
227                                 *out = '\0';
228                                 osrfStringArrayAdd( arr, buf );
229                                 in_token = 0;
230                         }
231                         else if( '\0' == *src ) {
232                                 *out = '\0';
233                                 osrfStringArrayAdd( arr, buf );
234                                 break;
235                         }
236                         else {
237                                 *out++ = *src;
238                         }
239                 }
240                 else {                          // We're between tokens
241                         if( *src == delim ) {
242                                 ;                       // skip it
243                         }
244                         else if( '\0' == *src ) {
245                                 break;
246                         }
247                         else {
248                                 out = buf;              // Start the next one
249                                 *out++ = *src;
250                                 in_token = 1;
251                         }
252                 }
253         }
254
255         return arr;
256 }