1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
6 Copyright (C) 2017-2019 Olof Hagsand
7 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
8
9 This file is part of CLIXON.
10
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22
23 Alternatively, the contents of this file may be used under the terms of
24 the GNU General Public License Version 3 or later (the "GPL"),
25 in which case the provisions of the GPL are applicable instead
26 of those above. If you wish to allow use of your version of this file only
27 under the terms of the GPL, and not to allow others to
28 use your version of this file under the terms of Apache License version 2,
29 indicate your decision by deleting the provisions above and replace them with
30 the notice and other provisions required by the GPL. If you do not delete
31 the provisions above, a recipient may use your version of this file under
32 the terms of any one of the Apache License version 2 or the GPL.
33
34 ***** END LICENSE BLOCK *****
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #include "clixon_config.h"
39 #endif
40
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdint.h>
44 #include <stdlib.h>
45 #include <errno.h>
46 #include <ctype.h>
47
48 #include <cligen/cligen.h>
49
50 /* clicon */
51 #include "clixon_queue.h"
52 #include "clixon_string.h"
53 #include "clixon_err.h"
54
55 /*! Split string into a vector based on character delimiters. Using malloc
56 *
57 * The given string is split into a vector where the delimiter can be
58 * _any_ of the characters in the specified delimiter string.
59 *
60 * The vector returned is one single memory block that must be freed
61 * by the caller
62 *
63 * @code
64 * char **vec = NULL;
65 * char *v;
66 * int nvec;
67 * if ((vec = clicon_strsep("/home/user/src/clixon", "/", &nvec)) == NULL)
68 * err;
69 * for (i=0; i<nvec; i++){
70 * v = vec[i];
71 * ...
72 * }
73 * free(vec);
74 * @endcode
75 * @param[in] string String to be split
76 * @param[in] delim String of delimiter characters
77 * @param[out] nvec Number of entries in returned vector
78 * @retval vec Vector of strings. NULL terminated. Free after use
79 * @retval NULL Error *
80 */
81 char **
clicon_strsep(char * string,char * delim,int * nvec0)82 clicon_strsep(char *string,
83 char *delim,
84 int *nvec0)
85 {
86 char **vec = NULL;
87 char *ptr;
88 char *p;
89 int nvec = 1;
90 int i;
91 size_t siz;
92 char *s;
93 char *d;
94
95 if ((s = string)==NULL)
96 goto done;
97 while (*s){
98 if ((d = index(delim, *s)) != NULL)
99 nvec++;
100 s++;
101 }
102 /* alloc vector and append copy of string */
103 siz = (nvec+1)* sizeof(char*) + strlen(string)+1;
104 if ((vec = (char**)malloc(siz)) == NULL){
105 clicon_err(OE_UNIX, errno, "malloc");
106 goto done;
107 }
108 memset(vec, 0, siz);
109 ptr = (char*)vec + (nvec+1)* sizeof(char*); /* this is where ptr starts */
110 strcpy(ptr, string);
111 i = 0;
112 while ((p = strsep(&ptr, delim)) != NULL)
113 vec[i++] = p;
114 *nvec0 = nvec;
115 done:
116 return vec;
117 }
118
119 /*! Concatenate elements of a string array into a string.
120 * An optional delimiter string can be specified which will be inserted betwen
121 * each element.
122 * @retval str Joined string. Free after use.
123 * @retval NULL Failure
124 */
125 char *
clicon_strjoin(int argc,char ** argv,char * delim)126 clicon_strjoin(int argc,
127 char **argv,
128 char *delim)
129 {
130 int i;
131 int len;
132 char *str;
133
134 len = 0;
135 for (i = 0; i < argc; i++)
136 len += strlen(argv[i]);
137 if (delim)
138 len += (strlen(delim) * argc);
139 len += 1; /* '\0' */
140 if ((str = malloc(len)) == NULL)
141 return NULL;
142 memset(str, '\0', len);
143 for (i = 0; i < argc; i++) {
144 if (i != 0)
145 strncat(str, delim, len - strlen(str));
146 strncat(str, argv[i], len - strlen(str));
147 }
148 return str;
149 }
150
151 /*! Split a string once into two parts: prefix and suffix
152 * @param[in] string
153 * @param[in] delim
154 * @param[out] prefix If non-NULL, return malloced string, or NULL.
155 * @param[out] suffix If non-NULL, return malloced identifier.
156 * @retval 0 OK
157 * @retval -1 Error
158 * @code
159 * char *a = NULL;
160 * char *b = NULL;
161 * if (clixon_strsplit(nodeid, ':', &a, &b) < 0)
162 * goto done;
163 * if (a)
164 * free(a);
165 * if (b)
166 * free(b);
167 * @note caller need to free prefix and suffix after use
168 * @see clicon_strsep not just single split
169 */
170 int
clixon_strsplit(char * string,const int delim,char ** prefix,char ** suffix)171 clixon_strsplit(char *string,
172 const int delim,
173 char **prefix,
174 char **suffix)
175 {
176 int retval = -1;
177 char *str;
178
179 if ((str = strchr(string, delim)) == NULL){
180 if (suffix && (*suffix = strdup(string)) == NULL){
181 clicon_err(OE_YANG, errno, "strdup");
182 goto done;
183 }
184 }
185 else {
186 if (prefix){
187 if ((*prefix = strdup(string)) == NULL){
188 clicon_err(OE_YANG, errno, "strdup");
189 goto done;
190 }
191 (*prefix)[str-string] = '\0';
192 }
193 str++;
194 if (suffix && (*suffix = strdup(str)) == NULL){
195 clicon_err(OE_YANG, errno, "strdup");
196 goto done;
197 }
198 }
199 retval = 0;
200 done:
201 return retval;
202 }
203
204 static int
uri_unreserved(unsigned char in)205 uri_unreserved(unsigned char in)
206 {
207 switch(in) {
208 case '0': case '1': case '2': case '3': case '4':
209 case '5': case '6': case '7': case '8': case '9':
210 case 'a': case 'b': case 'c': case 'd': case 'e':
211 case 'f': case 'g': case 'h': case 'i': case 'j':
212 case 'k': case 'l': case 'm': case 'n': case 'o':
213 case 'p': case 'q': case 'r': case 's': case 't':
214 case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
215 case 'A': case 'B': case 'C': case 'D': case 'E':
216 case 'F': case 'G': case 'H': case 'I': case 'J':
217 case 'K': case 'L': case 'M': case 'N': case 'O':
218 case 'P': case 'Q': case 'R': case 'S': case 'T':
219 case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
220 case '-': case '.': case '_': case '~':
221 return 1;
222 default:
223 break;
224 }
225 return 0;
226 }
227
228 /*! Percent encoding according to RFC 3986 URI Syntax
229 * @param[out] encp Encoded malloced output string
230 * @param[in] fmt Not-encoded input string (stdarg format string)
231 * @param[in] ... stdarg variable parameters
232 * @retval 0 OK
233 * @retval -1 Error
234 * @code
235 * char *enc;
236 * if (uri_percent_encode(&enc, "formatstr: <>= %s", "substr<>") < 0)
237 * err;
238 * if(enc)
239 * free(enc);
240 * @endcode
241 * @see RFC 3986 Uniform Resource Identifier (URI): Generic Syntax
242 * @see uri_percent_decode
243 * @see xml_chardata_encode
244 */
245 int
uri_percent_encode(char ** encp,const char * fmt,...)246 uri_percent_encode(char **encp,
247 const char *fmt, ...)
248 {
249 int retval = -1;
250 char *str = NULL; /* Expanded format string w stdarg */
251 char *enc = NULL;
252 int fmtlen;
253 int len;
254 int i, j;
255 va_list args;
256
257 /* Two steps: (1) read in the complete format string */
258 va_start(args, fmt); /* dryrun */
259 fmtlen = vsnprintf(NULL, 0, fmt, args) + 1;
260 va_end(args);
261 if ((str = malloc(fmtlen)) == NULL){
262 clicon_err(OE_UNIX, errno, "malloc");
263 goto done;
264 }
265 memset(str, 0, fmtlen);
266 va_start(args, fmt); /* real */
267 fmtlen = vsnprintf(str, fmtlen, fmt, args) + 1;
268 va_end(args);
269 /* Now str is the combined fmt + ... */
270
271 /* Step (2) encode and expand str --> enc */
272 /* This is max */
273 len = strlen(str)*3+1;
274 if ((enc = malloc(len)) == NULL){
275 clicon_err(OE_UNIX, errno, "malloc");
276 goto done;
277 }
278 memset(enc, 0, len);
279 j = 0;
280 for (i=0; i<strlen(str); i++){
281 if (uri_unreserved(str[i]))
282 enc[j++] = str[i];
283 else{
284 snprintf(&enc[j], 4, "%%%02X", str[i]&0xff);
285 j += 3;
286 }
287 }
288 *encp = enc;
289 retval = 0;
290 done:
291 if (str)
292 free(str);
293 if (retval < 0 && enc)
294 free(enc);
295 return retval;
296 }
297
298 /*! Percent decoding according to RFC 3986 URI Syntax
299 * @param[in] enc Encoded input string
300 * @param[out] strp Decoded malloced output string. Deallocate with free()
301 * @retval 0 OK
302 * @retval -1 Error
303 * @see RFC 3986 Uniform Resource Identifier (URI): Generic Syntax
304 * @see uri_percent_encode
305 */
306 int
uri_percent_decode(char * enc,char ** strp)307 uri_percent_decode(char *enc,
308 char **strp)
309 {
310 int retval = -1;
311 char *str = NULL;
312 int i, j;
313 char hstr[3];
314 int len;
315 char *ptr;
316
317 /* This is max */
318 len = strlen(enc)+1;
319 if ((str = malloc(len)) == NULL){
320 clicon_err(OE_UNIX, errno, "malloc");
321 goto done;
322 }
323 memset(str, 0, len);
324 j = 0;
325 for (i=0; i<strlen(enc); i++){
326 if (enc[i] == '%' && strlen(enc)-i > 2 &&
327 isxdigit(enc[i+1]) && isxdigit(enc[i+2])){
328 hstr[0] = enc[i+1];
329 hstr[1] = enc[i+2];
330 hstr[2] = 0;
331 str[j] = strtoul(hstr, &ptr, 16);
332 i += 2;
333 }
334 else
335 str[j] = enc[i];
336 j++;
337 }
338 str[j++] = '\0';
339 *strp = str;
340 retval = 0;
341 done:
342 if (retval < 0 && str)
343 free(str);
344 return retval;
345 }
346
347 /*! Escape characters according to XML definition
348 * @param[out] encp Encoded malloced output string
349 * @param[in] fmt Not-encoded input string (stdarg format string)
350 * @param[in] ... stdarg variable parameters
351 * @retval 0 OK
352 * @retval -1 Error
353 * @see https://www.w3.org/TR/2008/REC-xml-20081126/#syntax chapter 2.6
354 * @see uri_percent_encode
355 * @see AMPERSAND mode in clixon_xml_parse.l
356 * @code
357 * char *encstr = NULL;
358 * if (xml_chardata_encode(&encstr, "fmtstr<>& %s", "substr<>") < 0)
359 * err;
360 * if (encstr)
361 * free(encstr);
362 * @endcode
363 * Essentially encode as follows:
364 * & -> "& " must
365 * < -> "< " must
366 * > -> "> " must for backward compatibility
367 * ' -> "' " may
368 * ' -> "" " may
369 * Optionally >
370 * @see xml_chardata_cbuf_append for a specialized version
371 */
372 int
xml_chardata_encode(char ** escp,const char * fmt,...)373 xml_chardata_encode(char **escp,
374 const char *fmt,...)
375 {
376 int retval = -1;
377 char *str = NULL; /* Expanded format string w stdarg */
378 int fmtlen;
379 char *esc = NULL;
380 int l;
381 int len;
382 int i, j;
383 int cdata; /* when set, skip encoding */
384 va_list args;
385
386 /* Two steps: (1) read in the complete format string */
387 va_start(args, fmt); /* dryrun */
388 fmtlen = vsnprintf(NULL, 0, fmt, args) + 1;
389 va_end(args);
390 if ((str = malloc(fmtlen)) == NULL){
391 clicon_err(OE_UNIX, errno, "malloc");
392 goto done;
393 }
394 memset(str, 0, fmtlen);
395 va_start(args, fmt); /* real */
396 fmtlen = vsnprintf(str, fmtlen, fmt, args) + 1;
397 va_end(args);
398 /* Now str is the combined fmt + ... */
399
400 /* Step (2) encode and expand str --> enc */
401 /* First compute length (do nothing) */
402 len = 0; cdata = 0;
403 for (i=0; i<strlen(str); i++){
404 if (cdata){
405 if (strncmp(&str[i], "]]>", strlen("]]>")) == 0)
406 cdata = 0;
407 len++;
408 }
409 else
410 switch (str[i]){
411 case '&':
412 len += strlen("&");
413 break;
414 case '<':
415 if (strncmp(&str[i], "<![CDATA[", strlen("<![CDATA[")) == 0){
416 len++;
417 cdata++;
418 }
419 else
420 len += strlen("<");
421 break;
422 case '>':
423 len += strlen(">");
424 break;
425 default:
426 len++;
427 }
428 }
429 len++; /* trailing \0 */
430 /* We know length, allocate encoding buffer */
431 if ((esc = malloc(len)) == NULL){
432 clicon_err(OE_UNIX, errno, "malloc");
433 goto done;
434 }
435 memset(esc, 0, len);
436
437 /* Same code again, but now actually encode into output buffer */
438 j = 0; cdata = 0;
439 for (i=0; i<strlen(str); i++){
440 if (cdata){
441 if (strncmp(&str[i], "]]>", strlen("]]>")) == 0){
442 cdata = 0;
443 esc[j++] = str[i++];
444 esc[j++] = str[i++];
445 }
446 esc[j++] = str[i];
447 }
448 else
449 switch (str[i]){
450 case '&':
451 if ((l=snprintf(&esc[j], 6, "&")) < 0){
452 clicon_err(OE_UNIX, errno, "snprintf");
453 goto done;
454 }
455 j += l;
456 break;
457 case '<':
458 if (strncmp(&str[i], "<![CDATA[", strlen("<![CDATA[")) == 0){
459 esc[j++] = str[i];
460 cdata++;
461 break;
462 }
463 if ((l=snprintf(&esc[j], 5, "<")) < 0){
464 clicon_err(OE_UNIX, errno, "snprintf");
465 goto done;
466 }
467 j += l;
468 break;
469 case '>':
470 if ((l=snprintf(&esc[j], 5, ">")) < 0){
471 clicon_err(OE_UNIX, errno, "snprintf");
472 goto done;
473 }
474 j += l;
475 break;
476 default:
477 esc[j++] = str[i];
478 }
479 }
480 *escp = esc;
481 retval = 0;
482 done:
483 if (str)
484 free(str);
485 if (retval < 0 && esc)
486 free(esc);
487 return retval;
488 }
489
490 /*! Escape characters according to XML definition and append to cbuf
491 * @param[in] cb CLIgen buf
492 * @param[in] str Not-encoded input string
493 * @see xml_chardata_encode for the generic function
494 */
495 int
xml_chardata_cbuf_append(cbuf * cb,char * str)496 xml_chardata_cbuf_append(cbuf *cb,
497 char *str)
498 {
499 int retval = -1;
500 int i;
501 int cdata; /* when set, skip encoding */
502
503 /* The orignal of this code is in xml_chardata_encode */
504 /* Step: encode and expand str --> enc */
505 /* Same code again, but now actually encode into output buffer */
506 cdata = 0;
507 for (i=0; i<strlen(str); i++){
508 if (cdata){
509 if (strncmp(&str[i], "]]>", strlen("]]>")) == 0){
510 cdata = 0;
511 cbuf_append(cb, str[i++]);
512 cbuf_append(cb, str[i++]);
513 }
514 cbuf_append(cb, str[i]);
515 }
516 else
517 switch (str[i]){
518 case '&':
519 cbuf_append_str(cb, "&");
520 break;
521 case '<':
522 if (strncmp(&str[i], "<![CDATA[", strlen("<![CDATA[")) == 0){
523 cbuf_append(cb, str[i]);
524 cdata++;
525 break;
526 }
527 cbuf_append_str(cb, "<");
528 break;
529 case '>':
530 cbuf_append_str(cb, ">");
531 break;
532 default:
533 cbuf_append(cb, str[i]);
534 }
535 }
536 retval = 0;
537 // done:
538 return retval;
539 }
540
541 /*! Split a string into a cligen variable vector using 1st and 2nd delimiter
542 * Split a string first into elements delimited by delim1, then into
543 * pairs delimited by delim2.
544 * @param[in] string String to split
545 * @param[in] delim1 First delimiter char that delimits between elements
546 * @param[in] delim2 Second delimiter char for pairs within an element
547 * @param[out] cvp Created cligen variable vector, deallocate w cvec_free
548 * @retval 0 OK
549 * @retval -1 error
550 * @code
551 * cvec *cvv = NULL;
552 * if (str2cvec("a=b&c=d", ';', '=', &cvv) < 0)
553 * err;
554 * @endcode
555 *
556 * a=b&c=d -> [[a,"b"][c="d"]
557 * kalle&c=d -> [[c="d"]] # Discard elements with no delim2
558 * XXX differentiate between error and null cvec.
559 */
560 int
str2cvec(char * string,char delim1,char delim2,cvec ** cvp)561 str2cvec(char *string,
562 char delim1,
563 char delim2,
564 cvec **cvp)
565 {
566 int retval = -1;
567 char *s;
568 char *s0 = NULL;;
569 char *val; /* value */
570 char *valu; /* unescaped value */
571 char *snext; /* next element in string */
572 cvec *cvv = NULL;
573 cg_var *cv;
574
575 if ((s0 = strdup(string)) == NULL){
576 clicon_err(OE_UNIX, errno, "strdup");
577 goto err;
578 }
579 s = s0;
580 if ((cvv = cvec_new(0)) ==NULL){
581 clicon_err(OE_UNIX, errno, "cvec_new");
582 goto err;
583 }
584 while (s != NULL) {
585 /*
586 * In the pointer algorithm below:
587 * name1=val1; name2=val2;
588 * ^ ^ ^
589 * | | |
590 * s val snext
591 */
592 if ((snext = index(s, delim1)) != NULL)
593 *(snext++) = '\0';
594 if ((val = index(s, delim2)) != NULL){
595 *(val++) = '\0';
596 if (uri_percent_decode(val, &valu) < 0)
597 goto err;
598 if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
599 clicon_err(OE_UNIX, errno, "cvec_add");
600 goto err;
601 }
602 while ((strlen(s) > 0) && isblank(*s))
603 s++;
604 cv_name_set(cv, s);
605 cv_string_set(cv, valu);
606 free(valu); valu = NULL;
607 }
608 else{
609 if (strlen(s)){
610 if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
611 clicon_err(OE_UNIX, errno, "cvec_add");
612 goto err;
613 }
614 cv_name_set(cv, s);
615 cv_string_set(cv, "");
616 }
617 }
618 s = snext;
619 }
620 retval = 0;
621 done:
622 *cvp = cvv;
623 if (s0)
624 free(s0);
625 return retval;
626 err:
627 if (cvv){
628 cvec_free(cvv);
629 cvv = NULL;
630 }
631 goto done;
632 }
633
634 /*! Map from int to string using str2int map
635 * @param[in] ms String, integer map
636 * @param[in] i Input integer
637 * @retval str String value
638 * @retval NULL Error, not found
639 * @note linear search
640 */
641 const char *
clicon_int2str(const map_str2int * mstab,int i)642 clicon_int2str(const map_str2int *mstab,
643 int i)
644 {
645 const struct map_str2int *ms;
646
647 for (ms = &mstab[0]; ms->ms_str; ms++)
648 if (ms->ms_int == i)
649 return ms->ms_str;
650 return NULL;
651 }
652
653 /*! Map from string to int using str2int map
654 * @param[in] ms String, integer map
655 * @param[in] str Input string
656 * @retval int Value
657 * @retval -1 Error, not found
658 * @see clicon_str2int_search for optimized lookup, but strings must be sorted
659 */
660 int
clicon_str2int(const map_str2int * mstab,char * str)661 clicon_str2int(const map_str2int *mstab,
662 char *str)
663 {
664 const struct map_str2int *ms;
665
666 for (ms = &mstab[0]; ms->ms_str; ms++)
667 if (strcmp(ms->ms_str, str) == 0)
668 return ms->ms_int;
669 return -1;
670 }
671
672 /*! Map from string to int using binary (alphatical) search
673 * @param[in] ms String, integer map
674 * @param[in] str Input string
675 * @param[in] low Lower bound index
676 * @param[in] upper Upper bound index
677 * @param[in] len Length of array (max)
678 * @param[out] found Integer found (can also be negative)
679 * @retval 0 Not found
680 * @retval 1 Found with "found" value set.
681 * @note Assumes sorted strings, tree search
682 */
683 static int
str2int_search1(const map_str2int * mstab,char * str,int low,int upper,int len,int * found)684 str2int_search1(const map_str2int *mstab,
685 char *str,
686 int low,
687 int upper,
688 int len,
689 int *found)
690 {
691 const struct map_str2int *ms;
692 int mid;
693 int cmp;
694
695 if (upper < low)
696 return 0; /* not found */
697 mid = (low + upper) / 2;
698 if (mid >= len) /* beyond range */
699 return 0; /* not found */
700 ms = &mstab[mid];
701 if ((cmp = strcmp(str, ms->ms_str)) == 0){
702 *found = ms->ms_int;
703 return 1; /* found */
704 }
705 else if (cmp < 0)
706 return str2int_search1(mstab, str, low, mid-1, len, found);
707 else
708 return str2int_search1(mstab, str, mid+1, upper, len, found);
709 }
710
711 /*! Map from string to int using str2int map
712 * @param[in] ms String, integer map
713 * @param[in] str Input string
714 * @retval int Value
715 * @retval -1 Error, not found
716 * @note Assumes sorted strings, tree search
717 * @note -1 can not be value
718 */
719 int
clicon_str2int_search(const map_str2int * mstab,char * str,int len)720 clicon_str2int_search(const map_str2int *mstab,
721 char *str,
722 int len)
723 {
724 int found;
725
726 if (str2int_search1(mstab, str, 0, len, len, &found))
727 return found;
728 return -1; /* not found */
729 }
730
731 /*! Split colon-separated node identifier into prefix and name
732 * @param[in] node-id
733 * @param[out] prefix If non-NULL, return malloced string, or NULL.
734 * @param[out] id If non-NULL, return malloced identifier.
735 * @retval 0 OK
736 * @retval -1 Error
737 * @code
738 * char *prefix = NULL;
739 * char *id = NULL;
740 * if (nodeid_split(nodeid, &prefix, &id) < 0)
741 * goto done;
742 * if (prefix)
743 * free(prefix);
744 * if (id)
745 * free(id);
746 * @note caller need to free id and prefix after use
747 */
748 int
nodeid_split(char * nodeid,char ** prefix,char ** id)749 nodeid_split(char *nodeid,
750 char **prefix,
751 char **id)
752 {
753 return clixon_strsplit(nodeid, ':', prefix, id);
754 }
755
756 /*! Trim blanks from front and end of a string, return new string
757 * @param[in] str
758 * @retval s Pointer into existing str after trimming blanks
759 */
760 char *
clixon_trim(char * str)761 clixon_trim(char *str)
762 {
763 char *s = str;
764 int i;
765
766 while (strlen(s) && isblank(s[0])) /* trim from front */
767 s++;
768 for (i=strlen(s)-1; i>=0; i--){ /* trim from rear */
769 if (isblank(s[i]))
770 s[i] = '\0';
771 else
772 break;
773 }
774 return s;
775 }
776
777 /*! Trim blanks from front and end of a string, return new string
778 * @param[in] str
779 * @param[in] trims Characters to trim: a vector of characters
780 * @retval s Pointer into existing str after trimming blanks
781 */
782 char *
clixon_trim2(char * str,char * trims)783 clixon_trim2(char *str,
784 char *trims)
785 {
786 char *s = str;
787 int i;
788
789 while (strlen(s) && index(trims, s[0])) /* trim from front */
790 s++;
791 for (i=strlen(s)-1; i>=0; i--){ /* trim from rear */
792 if (index(trims, s[i]))
793 s[i] = '\0';
794 else
795 break;
796 }
797 return s;
798 }
799
800 /*! check string equals (NULL is equal)
801 * @param[in] s1 String 1
802 * @param[in] s2 String 2
803 * @retval 0 Equal
804 * @retval <0 s1 is less than s2
805 * @retval >0 s1 is greater than s2
806 */
807 int
clicon_strcmp(char * s1,char * s2)808 clicon_strcmp(char *s1,
809 char *s2)
810 {
811 if (s1 == NULL && s2 == NULL)
812 return 0;
813 if (s1 == NULL) /* empty string first */
814 return -1;
815 if (s2 == NULL)
816 return 1;
817 return strcmp(s1, s2);
818 }
819
820 /*! strndup() for systems without it, such as xBSD
821 */
822 #ifndef HAVE_STRNDUP
823 char *
clicon_strndup(const char * str,size_t len)824 clicon_strndup(const char *str,
825 size_t len)
826 {
827 char *new;
828 size_t slen;
829
830 slen = strlen(str);
831 len = (len < slen ? len : slen);
832
833 new = malloc(len + 1);
834 if (new == NULL)
835 return NULL;
836
837 new[len] = '\0';
838 memcpy(new, str, len);
839
840 return new;
841 }
842 #endif /* ! HAVE_STRNDUP */
843
844 /*
845 * Turn this on for uni-test programs
846 * Usage: clixon_string join
847 * Example compile:
848 gcc -g -o clixon_string -I. -I../clixon ./clixon_string.c -lclixon -lcligen
849 * Example run:
850 */
851 #if 0 /* Test program */
852
853 static int
854 usage(char *argv0)
855 {
856 fprintf(stderr, "usage:%s <string>\n", argv0);
857 exit(0);
858 }
859
860 int
861 main(int argc, char **argv)
862 {
863 int nvec;
864 char **vec;
865 char *str0;
866 char *str1;
867 int i;
868
869 if (argc != 2){
870 usage(argv[0]);
871 return 0;
872 }
873 str0 = argv[1];
874 if ((vec = clicon_strsep(str0, " \t", &nvec)) == NULL)
875 return -1;
876 fprintf(stderr, "nvec: %d\n", nvec);
877 for (i=0; i<nvec+1; i++)
878 fprintf(stderr, "vec[%d]: %s\n", i, vec[i]);
879 if ((str1 = clicon_strjoin(nvec, vec, " ")) == NULL)
880 return -1;
881 fprintf(stderr, "join: %s\n", str1);
882 free(vec);
883 free(str1);
884 return 0;
885 }
886
887 #endif /* Test program */
888
889