blob: 7c64c131d12296ad66e37b8291df88abbc1dd4a3 [file] [log] [blame]
Austin Schuh3333ec72022-12-29 16:21:06 -08001/* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2All rights reserved.
3This software was developed in the APRIL Robotics Lab under the
4direction of Edwin Olson, ebolson@umich.edu. This software may be
5available under alternative licensing terms; contact the address above.
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
81. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
102. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23The views and conclusions contained in the software and documentation are those
24of the authors and should not be interpreted as representing official policies,
25either expressed or implied, of the Regents of The University of Michigan.
26*/
27
28#include <assert.h>
29#include <ctype.h>
30#include <string.h>
31#include <stdarg.h>
32#include <stdlib.h>
33#include <stdio.h>
34
35#include "string_util.h"
36#include "zarray.h"
37
38struct string_buffer
39{
40 char *s;
41 int alloc;
42 size_t size; // as if strlen() was called; not counting terminating \0
43};
44
45#define MIN_PRINTF_ALLOC 16
46
47char *sprintf_alloc(const char *fmt, ...)
48{
49 assert(fmt != NULL);
50
51 va_list args;
52
53 va_start(args,fmt);
54 char *buf = vsprintf_alloc(fmt, args);
55 va_end(args);
56
57 return buf;
58}
59
60char *vsprintf_alloc(const char *fmt, va_list orig_args)
61{
62 assert(fmt != NULL);
63
64 int size = MIN_PRINTF_ALLOC;
65 char *buf = malloc(size * sizeof(char));
66
67 int returnsize;
68 va_list args;
69
70 va_copy(args, orig_args);
71 returnsize = vsnprintf(buf, size, fmt, args);
72 va_end(args);
73
74 // it was successful
75 if (returnsize < size) {
76 return buf;
77 }
78
79 // otherwise, we should try again
80 free(buf);
81 size = returnsize + 1;
82 buf = malloc(size * sizeof(char));
83
84 va_copy(args, orig_args);
85 returnsize = vsnprintf(buf, size, fmt, args);
86 va_end(args);
87
88 assert(returnsize <= size);
89 return buf;
90}
91
92char *_str_concat_private(const char *first, ...)
93{
94 size_t len = 0;
95
96 // get the total length (for the allocation)
97 {
98 va_list args;
99 va_start(args, first);
100 const char *arg = first;
101 while(arg != NULL) {
102 len += strlen(arg);
103 arg = va_arg(args, const char *);
104 }
105 va_end(args);
106 }
107
108 // write the string
109 char *str = malloc(len*sizeof(char) + 1);
110 char *ptr = str;
111 {
112 va_list args;
113 va_start(args, first);
114 const char *arg = first;
115 while(arg != NULL) {
116 while(*arg)
117 *ptr++ = *arg++;
118 arg = va_arg(args, const char *);
119 }
120 *ptr = '\0';
121 va_end(args);
122 }
123
124 return str;
125}
126
127// Returns the index of the first character that differs:
128int str_diff_idx(const char * a, const char * b)
129{
130 assert(a != NULL);
131 assert(b != NULL);
132
133 int i = 0;
134
135 size_t lena = strlen(a);
136 size_t lenb = strlen(b);
137
138 size_t minlen = lena < lenb ? lena : lenb;
139
140 for (; i < minlen; i++)
141 if (a[i] != b[i])
142 break;
143
144 return i;
145}
146
147
148zarray_t *str_split(const char *str, const char *delim)
149{
150 assert(str != NULL);
151 assert(delim != NULL);
152
153 zarray_t *parts = zarray_create(sizeof(char*));
154 string_buffer_t *sb = string_buffer_create();
155
156 size_t delim_len = strlen(delim);
157 size_t len = strlen(str);
158 size_t pos = 0;
159
160 while (pos < len) {
161 if (str_starts_with(&str[pos], delim) && delim_len > 0) {
162 pos += delim_len;
163 // never add empty strings (repeated tokens)
164 if (string_buffer_size(sb) > 0) {
165 char *part = string_buffer_to_string(sb);
166 zarray_add(parts, &part);
167 }
168 string_buffer_reset(sb);
169 } else {
170 string_buffer_append(sb, str[pos]);
171 pos++;
172 }
173 }
174
175 if (string_buffer_size(sb) > 0) {
176 char *part = string_buffer_to_string(sb);
177 zarray_add(parts, &part);
178 }
179
180 string_buffer_destroy(sb);
181 return parts;
182}
183
184// split on one or more spaces.
185zarray_t *str_split_spaces(const char *str)
186{
187 zarray_t *parts = zarray_create(sizeof(char*));
188 size_t len = strlen(str);
189 size_t pos = 0;
190
191 while (pos < len) {
192
193 while (pos < len && str[pos] == ' ')
194 pos++;
195
196 // produce a token?
197 if (pos < len) {
198 // yes!
199 size_t off0 = pos;
200 while (pos < len && str[pos] != ' ')
201 pos++;
202 size_t off1 = pos;
203
204 size_t len_off = off1 - off0;
205 char *tok = malloc(len_off + 1);
206 memcpy(tok, &str[off0], len_off);
207 tok[len_off] = 0;
208 zarray_add(parts, &tok);
209 }
210 }
211
212 return parts;
213}
214
215void str_split_destroy(zarray_t *za)
216{
217 if (!za)
218 return;
219
220 zarray_vmap(za, free);
221 zarray_destroy(za);
222}
223
224char *str_trim(char *str)
225{
226 assert(str != NULL);
227
228 return str_lstrip(str_rstrip(str));
229}
230
231char *str_lstrip(char *str)
232{
233 assert(str != NULL);
234
235 char *ptr = str;
236 char *end = str + strlen(str);
237 for(; ptr != end && isspace(*ptr); ptr++);
238 // shift the string to the left so the original pointer still works
239 memmove(str, ptr, strlen(ptr)+1);
240 return str;
241}
242
243char *str_rstrip(char *str)
244{
245 assert(str != NULL);
246
247 char *ptr = str + strlen(str) - 1;
248 for(; ptr+1 != str && isspace(*ptr); ptr--);
249 *(ptr+1) = '\0';
250 return str;
251}
252
253int str_indexof(const char *haystack, const char *needle)
254{
255 assert(haystack != NULL);
256 assert(needle != NULL);
257
258 // use signed types for hlen/nlen because hlen - nlen can be negative.
259 int hlen = (int) strlen(haystack);
260 int nlen = (int) strlen(needle);
261
262 if (nlen > hlen) return -1;
263
264 for (int i = 0; i <= hlen - nlen; i++) {
265 if (!strncmp(&haystack[i], needle, nlen))
266 return i;
267 }
268
269 return -1;
270}
271
272int str_last_indexof(const char *haystack, const char *needle)
273{
274 assert(haystack != NULL);
275 assert(needle != NULL);
276
277 // use signed types for hlen/nlen because hlen - nlen can be negative.
278 int hlen = (int) strlen(haystack);
279 int nlen = (int) strlen(needle);
280
281 int last_index = -1;
282 for (int i = 0; i <= hlen - nlen; i++) {
283 if (!strncmp(&haystack[i], needle, nlen))
284 last_index = i;
285 }
286
287 return last_index;
288}
289
290// in-place modification.
291char *str_tolowercase(char *s)
292{
293 assert(s != NULL);
294
295 size_t slen = strlen(s);
296 for (int i = 0; i < slen; i++) {
297 if (s[i] >= 'A' && s[i] <= 'Z')
298 s[i] = s[i] + 'a' - 'A';
299 }
300
301 return s;
302}
303
304char *str_touppercase(char *s)
305{
306 assert(s != NULL);
307
308 size_t slen = strlen(s);
309 for (int i = 0; i < slen; i++) {
310 if (s[i] >= 'a' && s[i] <= 'z')
311 s[i] = s[i] - ('a' - 'A');
312 }
313
314 return s;
315}
316
317string_buffer_t* string_buffer_create()
318{
319 string_buffer_t *sb = (string_buffer_t*) calloc(1, sizeof(string_buffer_t));
320 assert(sb != NULL);
321 sb->alloc = 32;
322 sb->s = calloc(sb->alloc, 1);
323 return sb;
324}
325
326void string_buffer_destroy(string_buffer_t *sb)
327{
328 if (sb == NULL)
329 return;
330
331 if (sb->s)
332 free(sb->s);
333
334 memset(sb, 0, sizeof(string_buffer_t));
335 free(sb);
336}
337
338void string_buffer_append(string_buffer_t *sb, char c)
339{
340 assert(sb != NULL);
341
342 if (sb->size+2 >= sb->alloc) {
343 sb->alloc *= 2;
344 sb->s = realloc(sb->s, sb->alloc);
345 }
346
347 sb->s[sb->size++] = c;
348 sb->s[sb->size] = 0;
349}
350
351char string_buffer_pop_back(string_buffer_t *sb) {
352 assert(sb != NULL);
353 if (sb->size == 0)
354 return 0;
355
356 char back = sb->s[--sb->size];
357 sb->s[sb->size] = 0;
358 return back;
359}
360
361void string_buffer_appendf(string_buffer_t *sb, const char *fmt, ...)
362{
363 assert(sb != NULL);
364 assert(fmt != NULL);
365
366 int size = MIN_PRINTF_ALLOC;
367 char *buf = malloc(size * sizeof(char));
368
369 int returnsize;
370 va_list args;
371
372 va_start(args,fmt);
373 returnsize = vsnprintf(buf, size, fmt, args);
374 va_end(args);
375
376 if (returnsize >= size) {
377 // otherwise, we should try again
378 free(buf);
379 size = returnsize + 1;
380 buf = malloc(size * sizeof(char));
381
382 va_start(args, fmt);
383 returnsize = vsnprintf(buf, size, fmt, args);
384 va_end(args);
385
386 assert(returnsize <= size);
387 }
388
389 string_buffer_append_string(sb, buf);
390 free(buf);
391}
392
393void string_buffer_append_string(string_buffer_t *sb, const char *str)
394{
395 assert(sb != NULL);
396 assert(str != NULL);
397
398 size_t len = strlen(str);
399
400 while (sb->size+len + 1 >= sb->alloc) {
401 sb->alloc *= 2;
402 sb->s = realloc(sb->s, sb->alloc);
403 }
404
405 memcpy(&sb->s[sb->size], str, len);
406 sb->size += len;
407 sb->s[sb->size] = 0;
408}
409
410bool string_buffer_ends_with(string_buffer_t *sb, const char *str)
411{
412 assert(sb != NULL);
413 assert(str != NULL);
414
415 return str_ends_with(sb->s, str);
416}
417
418char *string_buffer_to_string(string_buffer_t *sb)
419{
420 assert(sb != NULL);
421
422 return strdup(sb->s);
423}
424
425// returns length of string (not counting \0)
426size_t string_buffer_size(string_buffer_t *sb)
427{
428 assert(sb != NULL);
429
430 return sb->size;
431}
432
433void string_buffer_reset(string_buffer_t *sb)
434{
435 assert(sb != NULL);
436
437 sb->s[0] = 0;
438 sb->size = 0;
439}
440
441string_feeder_t *string_feeder_create(const char *str)
442{
443 assert(str != NULL);
444
445 string_feeder_t *sf = (string_feeder_t*) calloc(1, sizeof(string_feeder_t));
446 sf->s = strdup(str);
447 sf->len = strlen(sf->s);
448 sf->line = 1;
449 sf->col = 0;
450 sf->pos = 0;
451 return sf;
452}
453
454int string_feeder_get_line(string_feeder_t *sf)
455{
456 assert(sf != NULL);
457 return sf->line;
458}
459
460int string_feeder_get_column(string_feeder_t *sf)
461{
462 assert(sf != NULL);
463 return sf->col;
464}
465
466void string_feeder_destroy(string_feeder_t *sf)
467{
468 if (sf == NULL)
469 return;
470
471 free(sf->s);
472 memset(sf, 0, sizeof(string_feeder_t));
473 free(sf);
474}
475
476bool string_feeder_has_next(string_feeder_t *sf)
477{
478 assert(sf != NULL);
479
480 return sf->s[sf->pos] != 0 && sf->pos <= sf->len;
481}
482
483char string_feeder_next(string_feeder_t *sf)
484{
485 assert(sf != NULL);
486 assert(sf->pos <= sf->len);
487
488 char c = sf->s[sf->pos++];
489 if (c == '\n') {
490 sf->line++;
491 sf->col = 0;
492 } else {
493 sf->col++;
494 }
495
496 return c;
497}
498
499char *string_feeder_next_length(string_feeder_t *sf, size_t length)
500{
501 assert(sf != NULL);
502 assert(length >= 0);
503 assert(sf->pos <= sf->len);
504
505 if (sf->pos + length > sf->len)
506 length = sf->len - sf->pos;
507
508 char *substr = calloc(length+1, sizeof(char));
509 for (int i = 0 ; i < length ; i++)
510 substr[i] = string_feeder_next(sf);
511 return substr;
512}
513
514char string_feeder_peek(string_feeder_t *sf)
515{
516 assert(sf != NULL);
517 assert(sf->pos <= sf->len);
518
519 return sf->s[sf->pos];
520}
521
522char *string_feeder_peek_length(string_feeder_t *sf, size_t length)
523{
524 assert(sf != NULL);
525 assert(length >= 0);
526 assert(sf->pos <= sf->len);
527
528 if (sf->pos + length > sf->len)
529 length = sf->len - sf->pos;
530
531 char *substr = calloc(length+1, sizeof(char));
532 memcpy(substr, &sf->s[sf->pos], length*sizeof(char));
533 return substr;
534}
535
536bool string_feeder_starts_with(string_feeder_t *sf, const char *str)
537{
538 assert(sf != NULL);
539 assert(str != NULL);
540 assert(sf->pos <= sf->len);
541
542 return str_starts_with(&sf->s[sf->pos], str);
543}
544
545void string_feeder_require(string_feeder_t *sf, const char *str)
546{
547 assert(sf != NULL);
548 assert(str != NULL);
549 assert(sf->pos <= sf->len);
550
551 size_t len = strlen(str);
552
553 for (int i = 0; i < len; i++) {
554 char c = string_feeder_next(sf);
555 assert(c == str[i]);
556 }
557}
558
559////////////////////////////////////////////
560bool str_ends_with(const char *haystack, const char *needle)
561{
562 assert(haystack != NULL);
563 assert(needle != NULL);
564
565 size_t lens = strlen(haystack);
566 size_t lenneedle = strlen(needle);
567
568 if (lenneedle > lens)
569 return false;
570
571 return !strncmp(&haystack[lens - lenneedle], needle, lenneedle);
572}
573
574#ifndef _MSC_VER
575inline
576#endif
577bool str_starts_with(const char *haystack, const char *needle)
578{
579 assert(haystack != NULL);
580 assert(needle != NULL);
581
582 // haystack[pos] doesn't have to be compared to zero; if it were
583 // zero, it either doesn't match needle (in which case the loop
584 // terminates) or it matches needle[pos] (in which case the loop
585 // terminates).
586 int pos = 0;
587 while (haystack[pos] == needle[pos] && needle[pos] != 0)
588 pos++;
589
590 return (needle[pos] == 0);
591}
592
593bool str_starts_with_any(const char *haystack, const char **needles, int num_needles)
594{
595 assert(haystack != NULL);
596 assert(needles != NULL);
597 assert(num_needles >= 0);
598
599 for (int i = 0; i < num_needles; i++) {
600 assert(needles[i] != NULL);
601 if (str_starts_with(haystack, needles[i]))
602 return true;
603 }
604
605 return false;
606}
607
608bool str_matches_any(const char *haystack, const char **needles, int num_needles)
609{
610 assert(haystack != NULL);
611 assert(needles != NULL);
612 assert(num_needles >= 0);
613
614 for (int i = 0; i < num_needles; i++) {
615 assert(needles[i] != NULL);
616 if (!strcmp(haystack, needles[i]))
617 return true;
618 }
619
620 return false;
621}
622
623char *str_substring(const char *str, size_t startidx, long endidx)
624{
625 assert(str != NULL);
626 assert(startidx >= 0 && startidx <= strlen(str)+1);
627 assert(endidx < 0 || endidx >= startidx);
628 assert(endidx < 0 || endidx <= strlen(str)+1);
629
630 if (endidx < 0)
631 endidx = (long) strlen(str);
632
633 size_t blen = endidx - startidx; // not counting \0
634 char *b = malloc(blen + 1);
635 memcpy(b, &str[startidx], blen);
636 b[blen] = 0;
637 return b;
638}
639
640char *str_replace(const char *haystack, const char *needle, const char *replacement)
641{
642 assert(haystack != NULL);
643 assert(needle != NULL);
644 assert(replacement != NULL);
645
646 string_buffer_t *sb = string_buffer_create();
647 size_t haystack_len = strlen(haystack);
648 size_t needle_len = strlen(needle);
649
650 int pos = 0;
651 while (pos < haystack_len) {
652 if (needle_len > 0 && str_starts_with(&haystack[pos], needle)) {
653 string_buffer_append_string(sb, replacement);
654 pos += needle_len;
655 } else {
656 string_buffer_append(sb, haystack[pos]);
657 pos++;
658 }
659 }
660 if (needle_len == 0 && haystack_len == 0)
661 string_buffer_append_string(sb, replacement);
662
663 char *res = string_buffer_to_string(sb);
664 string_buffer_destroy(sb);
665 return res;
666}
667
668char *str_replace_many(const char *_haystack, ...)
669{
670 va_list ap;
671 va_start(ap, _haystack);
672
673 char *haystack = strdup(_haystack);
674
675 while (true) {
676 char *needle = va_arg(ap, char*);
677 if (!needle)
678 break;
679
680 char *replacement = va_arg(ap, char*);
681 char *tmp = str_replace(haystack, needle, replacement);
682 free(haystack);
683 haystack = tmp;
684 }
685
686 va_end(ap);
687
688 return haystack;
689}
690
691static void buffer_appendf(char **_buf, int *bufpos, void *fmt, ...)
692{
693 char *buf = *_buf;
694 va_list ap;
695
696 int salloc = 128;
697 char *s = malloc(salloc);
698
699 va_start(ap, fmt);
700 int slen = vsnprintf(s, salloc, fmt, ap);
701 va_end(ap);
702
703 if (slen >= salloc) {
704 s = realloc(s, slen + 1);
705 va_start(ap, fmt);
706 vsprintf((char*) s, fmt, ap);
707 va_end(ap);
708 }
709
710 buf = realloc(buf, *bufpos + slen + 1);
711 *_buf = buf;
712
713 memcpy(&buf[*bufpos], s, slen + 1); // get trailing \0
714 (*bufpos) += slen;
715
716 free(s);
717}
718
719static int is_variable_character(char c)
720{
721 if (c >= 'a' && c <= 'z')
722 return 1;
723
724 if (c >= 'A' && c <= 'Z')
725 return 1;
726
727 if (c >= '0' && c <= '9')
728 return 1;
729
730 if (c == '_')
731 return 1;
732
733 return 0;
734}
735
736char *str_expand_envs(const char *in)
737{
738 size_t inlen = strlen(in);
739 size_t inpos = 0;
740
741 char *out = NULL;
742 int outpos = 0;
743
744 while (inpos < inlen) {
745
746 if (in[inpos] != '$') {
747 buffer_appendf(&out, &outpos, "%c", in[inpos]);
748 inpos++;
749 continue;
750
751 } else {
752 inpos++; // consume '$'
753
754 char *varname = NULL;
755 int varnamepos = 0;
756
757 while (inpos < inlen && is_variable_character(in[inpos])) {
758 buffer_appendf(&varname, &varnamepos, "%c", in[inpos]);
759 inpos++;
760 }
761
762 char *env = getenv(varname);
763 if (env)
764 buffer_appendf(&out, &outpos, "%s", env);
765
766 free(varname);
767 }
768 }
769
770 return out;
771}