0a186de2051398429c84937d89e3c7a57b7b71bb
[oonf.git] / src / libcommon / string.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #include <ctype.h>
47 #include <errno.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 #include <oonf/libcommon/string.h>
53
54 /**
55  * @param size minimum size of block
56  * @return rounded up block size of STRARRAY_BLOCKSIZE
57  */
58 static INLINE size_t
59 STRARRAY_MEMSIZE(const size_t size) {
60   return (size + STRARRAY_BLOCKSIZE - 1) & (~(STRARRAY_BLOCKSIZE - 1));
61 }
62
63 /**
64  * A safer version of strncpy that ensures that the
65  * destination string will be null-terminated if its
66  * length is greater than 0.
67  * @param dest target string buffer
68  * @param src source string buffer
69  * @param size size of target buffer
70  * @return pointer to target buffer
71  */
72 char *
73 strscpy(char *dest, const char *src, size_t size) {
74   if (dest == NULL || src == NULL || size == 0) {
75     return dest;
76   }
77
78   /* src does not need to be null terminated */
79   strncpy(dest, src, size - 1);
80   dest[size - 1] = 0;
81
82   return dest;
83 }
84
85 /**
86  * A safer version of strncat that ensures that
87  * the target buffer will be null-terminated if
88  * its size is greater than zero.
89  *
90  * If the target buffer is already full, it will
91  * not be changed.
92  * @param dest target string buffer
93  * @param src source string buffer
94  * @param size size of target buffer
95  * @return pointer to target buffer
96  */
97 char *
98 strscat(char *dest, const char *src, size_t size) {
99   size_t l;
100
101   if (dest == NULL || src == NULL || size == 0 || *src == 0) {
102     return dest;
103   }
104
105   l = strlen(dest);
106   if (l < size) {
107     strscpy(dest + l, src, size - l);
108   }
109   return dest;
110 }
111
112 /**
113  * Removes leading and trailing whitespaces from a string.
114  * @param ptr input string to be modified string-pointer
115  * @return pointer to first non-whitespace character in string
116  */
117 char *
118 str_trim(char *ptr) {
119   size_t len;
120
121   if (!ptr) {
122     return NULL;
123   }
124
125   /* skip leading whitespaces */
126   while (isspace(*ptr)) {
127     ptr++;
128   }
129
130   /* get end of string */
131   len = strlen(ptr);
132   if (len) {
133     /* remove trailing whitespaces */
134     while (--len > 0 && isspace(ptr[len])) {
135       ptr[len] = 0;
136     }
137   }
138   return ptr;
139 }
140
141 /**
142  * Check if a string starts with a certain word. The function
143  * is not case sensitive and does NOT modify the input strings.
144  * @param buffer pointer to string
145  * @param word pointer to the word
146  * @return pointer to the string behind the word, NULL if no match
147  */
148 const char *
149 str_hasnextword(const char *buffer, const char *word) {
150   /* sanity check */
151   if (buffer == NULL) {
152     return NULL;
153   }
154
155   /* skip whitespace prefix */
156   while (isblank(*buffer)) {
157     buffer++;
158   }
159
160   while (*word != 0 && *buffer != 0 && !isblank(*buffer) && tolower(*word) == tolower(*buffer)) {
161     word++;
162     buffer++;
163   }
164
165   /* complete match ? */
166   if (*word == 0 && (*buffer == 0 || isblank(*buffer))) {
167     while (isblank(*buffer)) {
168       buffer++;
169     }
170     return buffer;
171   }
172   return NULL;
173 }
174
175 /**
176  * Copies the next word of a constant stringbuffer into
177  * a second buffer.
178  * @param dst pointer to target buffer
179  * @param src constant source buffer
180  * @param len maximum length of copied data
181  * @return pointer to next word behind the copied word
182  */
183 const char *
184 str_cpynextword(char *dst, const char *src, size_t len) {
185   size_t i;
186
187   /* sanity check */
188   if (src == NULL) {
189     *dst = 0;
190     return NULL;
191   }
192
193   /* skip whitespace prefix */
194   while (isblank(*src)) {
195     src++;
196   }
197
198   /* copy next word */
199   i = 0;
200   while (*src != 0 && !isblank(*src) && i < len - 1) {
201     dst[i++] = *src++;
202   }
203
204   /* terminate */
205   dst[i] = 0;
206
207   /* skip ahead in src */
208   while (isblank(*src)) {
209     src++;
210   }
211
212   if (*src) {
213     /* return next word */
214     return src;
215   }
216
217   /* end of src */
218   return NULL;
219 }
220
221 /**
222  * Skips the next word of a constant stringbuffer.
223  * @param src constant source buffer
224  * @return pointer to next word behind the skipped word
225  */
226 const char *
227 str_skipnextword(const char *src) {
228   /* sanity check */
229   if (src == NULL) {
230     return NULL;
231   }
232
233   /* skip whitespace prefix */
234   while (isblank(*src)) {
235     src++;
236   }
237
238   /* copy next word */
239   while (*src != 0 && !isblank(*src)) {
240     src++;
241   }
242
243   /* skip ahead in src */
244   while (isblank(*src)) {
245     src++;
246   }
247
248   if (*src) {
249     /* return next word */
250     return src;
251   }
252
253   /* end of src */
254   return NULL;
255 }
256
257 /**
258  * @param src pointer to source string
259  * @return number of non-whitespace words separated by whitespaces in string
260  */
261 size_t
262 str_countwords(const char *src) {
263   size_t count = 0;
264
265   /* sanity check */
266   if (src != NULL) {
267     /* skip whitespace prefix */
268     while (isblank(*src)) {
269       src++;
270     }
271
272     while (src != NULL && *src != 0) {
273       count++;
274
275       src = str_skipnextword(src);
276     }
277   }
278   return count;
279 }
280
281 /**
282  * Printable is defined as all ascii characters >= 32 except
283  * 127 and 255.
284  * @param value stringpointer
285  * @return true if string only contains printable characters,
286  *   false otherwise
287  */
288 bool
289 str_is_printable(const char *value) {
290   const unsigned char *_value;
291
292   _value = (const unsigned char *)value;
293
294   while (*_value) {
295     if (!str_char_is_printable(*_value)) {
296       return false;
297     }
298     _value++;
299   }
300   return true;
301 }
302
303 /**
304  * Copy a string array into another array. This overwrites
305  * all data in the original array.
306  * @param dst destination array
307  * @param src source array
308  * @return 0 if array was copied, -1 if an error happened
309  */
310 int
311 strarray_copy(struct strarray *dst, const struct strarray *src) {
312   char *ptr;
313   size_t block;
314   if (src->value == NULL || src->length == 0) {
315     memset(dst, 0, sizeof(*dst));
316     return 0;
317   }
318
319   block = STRARRAY_MEMSIZE(src->length);
320   ptr = realloc(dst->value, block);
321   if (!ptr) {
322     return -1;
323   }
324
325   memcpy(ptr, src->value, src->length);
326   memset(ptr + src->length, 0, block - src->length);
327   dst->length = src->length;
328   dst->value = ptr;
329   return 0;
330 }
331
332 /**
333  * Appends a string to an existing string array. Only use this
334  * if the string-array value has been allocated with malloc/calloc.
335  * @param array pointer to string array object
336  * @param string pointer to string to append
337  * @return 0 if string was appended, -1 if an error happened
338  */
339 int
340 strarray_append(struct strarray *array, const char *string) {
341   size_t length, new_length;
342   char *ptr;
343
344   length = strlen(string) + 1;
345
346   new_length = array->length + length;
347   ptr = realloc(array->value, STRARRAY_MEMSIZE(new_length));
348   if (ptr == NULL) {
349     return -1;
350   }
351
352   memcpy(ptr + array->length, string, length);
353   array->value = ptr;
354   array->length = new_length;
355   return 0;
356 }
357
358 /**
359  * Put a string to in front of an existing string array. Only use this
360  * if the string-array value has been allocated with malloc/calloc.
361  * @param array pointer to string array object
362  * @param string pointer to string to append
363  * @return 0 if string was appended, -1 if an error happened
364  */
365 int
366 strarray_prepend(struct strarray *array, const char *string) {
367   size_t length, new_length;
368   char *ptr;
369
370   length = strlen(string) + 1;
371
372   new_length = array->length + length;
373   ptr = realloc(array->value, STRARRAY_MEMSIZE(new_length));
374   if (ptr == NULL) {
375     return -1;
376   }
377
378   memmove(ptr + length, ptr, array->length);
379   memcpy(ptr, string, length);
380   array->value = ptr;
381   array->length = new_length;
382   return 0;
383 }
384
385 /**
386  * Remove an element from a string array
387  * @param array pointer to string array object
388  * @param element an element to be removed from the array
389  * @param resize array afterwards
390  */
391 void
392 strarray_remove_ext(struct strarray *array, char *element, bool resize) {
393   char *ptr1;
394   size_t len;
395
396   /* get length of element to remove */
397   len = strlen(element) + 1;
398   if (len == array->length) {
399     strarray_free(array);
400     return;
401   }
402
403   /* adjust length */
404   array->length -= len;
405
406   /* remove element from memory */
407   if (element <= array->value + array->length) {
408     memmove(element, element + len, array->length - (element - array->value));
409   }
410
411   if (!resize) {
412     return;
413   }
414
415   /* adjust memory block */
416   ptr1 = realloc(array->value, STRARRAY_MEMSIZE(array->length));
417   if (ptr1 == NULL) {
418     /* just keep the current memory block */
419     return;
420   }
421
422   /* adjust value pointer to new memory block */
423   array->value = ptr1;
424 }
425
426 /**
427  * @param array pointer to strarray object
428  * @return number of strings in string array
429  */
430 size_t
431 strarray_get_count(const struct strarray *array) {
432   size_t count = 0;
433   char *ptr;
434
435   strarray_for_each_element(array, ptr) {
436     count++;
437   }
438   return count;
439 }
440
441 /**
442  * @param array pointer to strarray object
443  * @param idx position of the requested object inside the array
444  * @return string at the specified index, NULL if not found
445  */
446 char *
447 strarray_get(const struct strarray *array, size_t idx) {
448   size_t count = 0;
449   char *ptr;
450
451   strarray_for_each_element(array, ptr) {
452     if (count == idx) {
453       return ptr;
454     }
455     count++;
456   }
457   return NULL;
458 }
459
460 /**
461  * Compare to stringarrays
462  * @param a1 pointer to array 1
463  * @param a2 pointer to array 2
464  * @return <0 if a1 is 'smaller' than a2, >0 if a1 is 'larger' than a2,
465  *   0 if both are the same.
466  */
467 int
468 strarray_cmp(const struct strarray *a1, const struct strarray *a2) {
469   int result;
470   size_t min_len;
471
472   if (a1 == NULL || a1->value == NULL) {
473     return (a2 == NULL || a2->value == NULL) ? 0 : -1;
474   }
475   if (a2 == NULL || a2->value == NULL) {
476     return 1;
477   }
478
479   if (a1->length > a2->length) {
480     min_len = a2->length;
481   }
482   else {
483     min_len = a1->length;
484   }
485
486   result = memcmp(a1->value, a2->value, min_len);
487   if (result == 0) {
488     if (a1->length > a2->length) {
489       return 1;
490     }
491     if (a1->length < a2->length) {
492       return -1;
493     }
494   }
495   return result;
496 }