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  *     & -> "&amp; "   must
365  *     < -> "&lt; "    must
366  *     > -> "&gt; "    must for backward compatibility
367  *     ' -> "&apos; "  may
368  *     ' -> "&quot; "  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("&amp;");
413 		break;
414 	    case '<':
415 		if (strncmp(&str[i], "<![CDATA[", strlen("<![CDATA[")) == 0){
416 		    len++;
417 		    cdata++;
418 		}
419 		else
420 		    len += strlen("&lt;");
421 		break;
422 	    case '>':
423 		len += strlen("&gt;");
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, "&amp;")) < 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, "&lt;")) < 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, "&gt;")) < 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, "&amp;");
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, "&lt;");
528 	    break;
529 	case '>':
530 	    cbuf_append_str(cb, "&gt;");
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