dfdbfeba454a4e866365ec07a3cab9161a6a6d9a
[oonf.git] / src-api / common / string.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2013, 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 #include <assert.h>
43 #include <ctype.h>
44 #include <errno.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <stdio.h>
48
49 #include "common/string.h"
50
51 static const char *_get_human_readable_u64(char *out,
52     uint64_t number, const char *unit, int fraction,
53     bool binary, bool raw);
54
55 /**
56  * @param size minimum size of block
57  * @return rounded up block size of STRARRAY_BLOCKSIZE
58  */
59 static INLINE size_t STRARRAY_MEMSIZE(const size_t b) {
60   return (b + 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 {
75   if (dest == NULL || src == NULL || size == 0) {
76     return dest;
77   }
78
79   /* src does not need to be null terminated */
80   strncpy(dest, src, size-1);
81   dest[size-1] = 0;
82
83   return dest;
84 }
85
86 /**
87  * A safer version of strncat that ensures that
88  * the target buffer will be null-terminated if
89  * its size is greater than zero.
90  *
91  * If the target buffer is already full, it will
92  * not be changed.
93  * @param dest target string buffer
94  * @param src source string buffer
95  * @param size size of target buffer
96  * @return pointer to target buffer
97  */
98 char *
99 strscat(char *dest, const char *src, size_t size)
100 {
101   size_t l;
102
103   if (dest == NULL || src == NULL || size == 0 || *src == 0) {
104     return dest;
105   }
106
107   l = strlen(dest);
108   if (l < size) {
109     strscpy(dest + l, src, size - l);
110   }
111   return dest;
112 }
113
114 /**
115  * Removes leading and trailing whitespaces from a string.
116  * @param ptr input string to be modified string-pointer
117  * @return pointer to first non-whitespace character in string
118  */
119 char *
120 str_trim (char *ptr) {
121   char *end;
122
123   if (!ptr) {
124     return NULL;
125   }
126
127   /* skip leading whitespaces */
128   while (isspace(*ptr)) {
129     ptr++;
130   }
131
132   /* get end of string */
133   end = ptr + strlen(ptr) - 1;
134
135   /* remove trailing whitespaces */
136   while (end > ptr && isspace(*end)) {
137     *end-- = 0;
138   }
139   return ptr;
140 }
141
142 /**
143  * Check if a string starts with a certain word. The function
144  * is not case sensitive and does NOT modify the input strings.
145  * @param buffer pointer to string
146  * @param word pointer to the word
147  * @return pointer to the string behind the word, NULL if no match
148  */
149 const char *
150 str_hasnextword (const char *buffer, const char *word) {
151   /* sanity check */
152   if (buffer == NULL) {
153     return NULL;
154   }
155
156   /* skip whitespace prefix */
157   while (isblank(*buffer)) {
158     buffer++;
159   }
160
161   while (*word != 0 && *buffer != 0 && !isblank(*buffer) && tolower(*word) == tolower(*buffer)) {
162     word++;
163     buffer++;
164   }
165
166   /* complete match ? */
167   if (*word == 0 && (*buffer == 0 || isblank(*buffer))) {
168     while (isblank(*buffer)) {
169       buffer++;
170     }
171     return buffer;
172   }
173   return NULL;
174 }
175
176 /**
177  * Copies the next word of a constant stringbuffer into
178  * a second buffer.
179  * @param dst pointer to target buffer
180  * @param buffer constant source buffer
181  * @param len length of source buffer
182  * @return pointer to next word behind the copied word
183  */
184 const char *
185 str_cpynextword (char *dst, const char *buffer, size_t len) {
186   size_t i;
187
188   /* sanity check */
189   if (buffer == NULL) {
190     *dst = 0;
191     return NULL;
192   }
193
194   /* skip whitespace prefix */
195   while (isblank(*buffer)) {
196     buffer++;
197   }
198
199   /* copy next word */
200   i = 0;
201   while (*buffer != 0 && !isblank(*buffer) && i < len-1) {
202     dst[i++] = *buffer++;
203   }
204
205   /* terminate */
206   dst[i] = 0;
207
208   /* skip ahead in buffer */
209   while (isblank(*buffer)) {
210     buffer++;
211   }
212
213   if (*buffer) {
214     /* return next word */
215     return buffer;
216   }
217
218   /* end of buffer */
219   return NULL;
220 }
221
222 /**
223  * Printable is defined as all ascii characters >= 32 except
224  * 127 and 255.
225  * @param value stringpointer
226  * @return true if string only contains printable characters,
227  *   false otherwise
228  */
229 bool
230 str_is_printable(const char *value) {
231   const unsigned char *_value;
232
233   _value = (const unsigned char *)value;
234
235   while (*_value) {
236     if (!str_char_is_printable(*_value)) {
237       return false;
238     }
239     _value++;
240   }
241   return true;
242 }
243
244 /**
245  * Copy a string array into another array. This overwrites
246  * all data in the original array.
247  * @param dst destination array
248  * @param src source array
249  * @return 0 if array was copied, -1 if an error happened
250  */
251 int
252 strarray_copy(struct strarray *dst, const struct strarray *src) {
253   char *ptr;
254   size_t block;
255   if (src->value == NULL || src->length == 0) {
256     memset(dst, 0, sizeof(*dst));
257     return 0;
258   }
259
260   block = STRARRAY_MEMSIZE(src->length);
261   ptr = realloc(dst->value, block);
262   if (!ptr) {
263     return -1;
264   }
265
266   memcpy(ptr, src->value, src->length);
267   memset(ptr + src->length, 0, block - src->length);
268   dst->length = src->length;
269   dst->value = ptr;
270   return 0;
271 }
272
273 /**
274  * Appends a string to an existing string array. Only use this
275  * if the string-array value has been allocated with malloc/calloc.
276  * @param array pointer to string array object
277  * @param string pointer to string to append
278  * @return 0 if string was appended, -1 if an error happened
279  */
280 int
281 strarray_append(struct strarray *array, const char *string) {
282   size_t length, new_length;
283   char *ptr;
284
285   length = strlen(string) + 1;
286
287   new_length = array->length + length;
288   ptr = realloc(array->value, STRARRAY_MEMSIZE(new_length));
289   if (ptr == NULL) {
290     return -1;
291   }
292
293   memcpy(ptr + array->length, string, length);
294   array->value = ptr;
295   array->length = new_length;
296   return 0;
297 }
298
299 /**
300  * Put a string to in front of an existing string array. Only use this
301  * if the string-array value has been allocated with malloc/calloc.
302  * @param array pointer to string array object
303  * @param string pointer to string to append
304  * @return 0 if string was appended, -1 if an error happened
305  */
306 int
307 strarray_prepend(struct strarray *array, const char *string) {
308   size_t length, new_length;
309   char *ptr;
310
311   length = strlen(string) + 1;
312
313   new_length = array->length + length;
314   ptr = realloc(array->value, STRARRAY_MEMSIZE(new_length));
315   if (ptr == NULL) {
316     return -1;
317   }
318
319   memmove(ptr + length, ptr, array->length);
320   memcpy(ptr, string, length);
321   array->value = ptr;
322   array->length = new_length;
323   return 0;
324 }
325
326 /**
327  * Remove an element from a string array
328  * @param array pointer to string array object
329  * @param element an element to be removed from the array
330  * @param resize array afterwards
331  */
332 void
333 strarray_remove_ext(struct strarray *array,
334     char *element, bool resize) {
335   char *ptr1;
336   size_t len;
337
338   /* get length of element to remove */
339   len = strlen(element) + 1;
340   if (len == array->length) {
341     strarray_free(array);
342     return;
343   }
344
345   /* adjust length */
346   array->length -= len;
347
348   /* remove element from memory */
349   if (element <= array->value + array->length) {
350     memmove(element, element + len, array->length - (element - array->value));
351   }
352
353   if (!resize) {
354     return;
355   }
356
357   /* adjust memory block */
358   ptr1 = realloc(array->value, STRARRAY_MEMSIZE(array->length));
359   if (ptr1 == NULL) {
360     /* just keep the current memory block */
361     return;
362   }
363
364   /* adjust value pointer to new memory block */
365   array->value = ptr1;
366 }
367
368 /**
369  * @param array pointer to strarray object
370  * @return number of strings in string array
371  */
372 size_t
373 strarray_get_count(const struct strarray *array) {
374   size_t count = 0;
375   char *ptr;
376
377   FOR_ALL_STRINGS(array, ptr) {
378     count ++;
379   }
380   return count;
381 }
382
383 /**
384  * @param array pointer to strarray object
385  * @param idx position of the requested object inside the array
386  * @return string at the specified index, NULL if not found
387  */
388 char *
389 strarray_get(const struct strarray *array, size_t idx) {
390   size_t count = 0;
391   char *ptr;
392
393   FOR_ALL_STRINGS(array, ptr) {
394     if (count == idx) {
395       return ptr;
396     }
397     count ++;
398   }
399   return NULL;
400 }
401
402 /**
403  * Compare to stringarrays
404  * @param a1 pointer to array 1
405  * @param a2 pointer to array 2
406  * @return <0 if a1 is 'smaller' than a2, >0 if a1 is 'larger' than a2,
407  *   0 if both are the same.
408  */
409 int
410 strarray_cmp(const struct strarray *a1, const struct strarray *a2) {
411   int result;
412   size_t min_len;
413
414   if (a1 == NULL || a1->value == NULL) {
415     return (a2 == NULL || a2->value == NULL) ? 0 : -1;
416   }
417   if (a2 == NULL || a2->value == NULL) {
418     return 1;
419   }
420
421   if (a1->length > a2->length) {
422     min_len = a2->length;
423   }
424   else {
425     min_len = a1->length;
426   }
427
428   result = memcmp(a1->value, a2->value, min_len);
429   if (result == 0) {
430     if (a1->length > a2->length) {
431       return 1;
432     }
433     if (a1->length < a2->length) {
434       return -1;
435     }
436   }
437   return result;
438 }
439
440 /**
441  * Converts an unsigned 64 bit integer into a human readable number
442  * in string representation.
443  *
444  * '120000' will become '120 k' for example.
445  *
446  * @param out pointer to output buffer
447  * @param number number to convert.
448  * @param unit unit to be appended at the end, can be NULL
449  * @param maxfraction maximum number of fractional digits
450  * @param binary true if conversion should use 1024 as factor,
451  *   false for default 1000 conversion factor
452  * @param raw true if the whole text conversion should be bypassed
453  *   and only the raw number shall be written, false otherwise
454  * @return pointer to converted string
455  */
456 const char *
457 str_get_human_readable_u64(struct human_readable_str *out,
458     uint64_t number, const char *unit, int fraction,
459     bool binary, bool raw) {
460   return _get_human_readable_u64(
461       out->buf, number, unit, fraction, binary, raw);
462 }
463
464 /**
465  * Converts a signed 64 bit integer into a human readable number
466  * in string representation.
467  *
468  * '-120000' will become '-120 k' for example.
469  *
470  * @param out pointer to output buffer
471  * @param number number to convert.
472  * @param unit unit to be appended at the end, can be NULL
473  * @param fraction number of fractional digits of fractional digits
474  * @param binary true if conversion should use 1024 as factor,
475  *   false for default 1000 conversion factor
476  * @param raw true if the whole text conversion should be bypassed
477  *   and only the raw number shall be written, false otherwise
478  * @return pointer to converted string
479  */
480 const char *
481 str_get_human_readable_s64(struct human_readable_str *out,
482     int64_t number, const char *unit, int fraction,
483     bool binary, bool raw) {
484   char *outbuf = out->buf;
485   uint64_t num;
486
487   if (number == INT64_MIN) {
488     *outbuf++ = '-';
489     num = 1ull<<63;
490   }
491   else if (number < 0) {
492     num = (uint64_t)(-number);
493   }
494   else {
495     num = (uint64_t)number;
496   }
497
498   return _get_human_readable_u64(
499       outbuf, num, unit, fraction, binary, raw);
500 }
501
502 int
503 str_parse_human_readable_s64(int64_t *dst, const char *hrn, int fractions, bool binary) {
504   const char *ptr;
505   int result;
506   uint64_t u64;
507
508   ptr = hrn;
509   if (*hrn == '-') {
510     ptr++;
511   }
512
513   result = str_parse_human_readable_u64(&u64, ptr, fractions, binary);
514   if (!result) {
515     if (*hrn == '-') {
516       *dst = -((int64_t)u64);
517     }
518     else {
519       *dst = (int64_t)u64;
520     }
521   }
522   return result;
523 }
524
525 int
526 str_parse_human_readable_u64(uint64_t *dst, const char *hrn, int fraction, bool binary) {
527   uint64_t num;
528   uint64_t factor;
529   uint64_t multiplicator;
530   int frac;
531   char *next = NULL;
532
533   errno = 0;
534   num = strtoull(hrn, &next, 10);
535   if (errno) {
536     return -1;
537   }
538
539   if (*next == 0) {
540     *dst = num;
541     return 0;
542   }
543
544   /* Handle fractional part */
545   frac = 0;
546   if (*next == '.') {
547     next++;
548     while (*next >='0' && *next <='9' && frac < fraction) {
549       num *= 10;
550       num += (*next - '0');
551       frac++;
552       next++;
553     }
554   }
555   while (frac++ < fraction) {
556     num *= 10;
557   }
558
559   /* handle spaces */
560   while (*next == ' ') {
561     next++;
562   }
563
564   factor = 1;
565   if (*next) {
566     /* handle iso-prefix */
567     if (next[1] != 0) {
568       return -1;
569     }
570
571     multiplicator = binary ? 1024 : 1000;
572
573     switch (next[0]) {
574       case 'E':
575         factor *= multiplicator;
576         /* no break */
577       case 'P':
578         factor *= multiplicator;
579         /* no break */
580       case 'T':
581         factor *= multiplicator;
582         /* no break */
583       case 'G':
584         factor *= multiplicator;
585         /* no break */
586       case 'M':
587         factor *= multiplicator;
588         /* no break */
589       case 'k':
590         factor *= multiplicator;
591         /* no break */
592       case ' ':
593         break;
594       default:
595         return -1;
596     }
597   }
598
599   if (num > UINT64_MAX / factor) {
600     /* this would be an integer overflow */
601     return -1;
602   }
603
604   *dst = num * factor;
605   return 0;
606 }
607
608 static const char *
609 _get_human_readable_u64(char *out,
610     uint64_t number, const char *unit, int fraction,
611     bool binary, bool raw) {
612   static const char symbol[] = " kMGTPE";
613   uint64_t step, multiplier, print, n;
614   const char *unit_modifier;
615   size_t idx, len;
616
617   step = binary ? 1024 : 1000;
618   multiplier = 1;
619   unit_modifier = symbol;
620
621   while (fraction-- > 0) {
622     multiplier *= 10;
623   }
624
625   while (!raw && *unit_modifier != 0 && number >= multiplier * step) {
626     multiplier *= step;
627     unit_modifier++;
628   }
629
630   /* print whole */
631   idx = snprintf(out, sizeof(*out), "%"PRIu64, number / multiplier);
632   len = idx;
633
634   out[len++] = '.';
635   n = number;
636
637   if (*unit_modifier != ' ') {
638     fraction = 3;
639   }
640
641   while (true) {
642     n = n % multiplier;
643     if (n == 0 || fraction == 0) {
644       break;
645     }
646     fraction--;
647     multiplier /= 10;
648
649     print = n / multiplier;
650
651     assert (print < 10);
652     out[len++] = (char)'0' + (char)(print);
653     if (print) {
654       idx = len;
655     }
656   }
657
658   out[idx++] = ' ';
659   out[idx++] = *unit_modifier;
660   out[idx++] = 0;
661
662   if (unit) {
663     strscat(out, unit, sizeof(*out));
664   }
665
666   return out;
667 }