370 lines
8.7 KiB
C
370 lines
8.7 KiB
C
/*
|
|
*
|
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
|
|
|
This file is part of CLIXON.
|
|
|
|
CLIXON is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
CLIXON is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with CLIXON; see the file LICENSE. If not, see
|
|
<http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
/* Error handling: dont use clicon_err, treat as unix system calls. That is,
|
|
ensure errno is set and return -1/NULL */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "clixon_config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <regex.h>
|
|
#include <ctype.h>
|
|
|
|
/* clicon */
|
|
#include "clixon_queue.h"
|
|
#include "clixon_chunk.h"
|
|
#include "clixon_string.h"
|
|
#include "clixon_err.h"
|
|
|
|
/*! Split string into a vector based on character delimiters
|
|
*
|
|
* The given string is split into a vector where the delimiter can be
|
|
* any of the characters in the specified delimiter string.
|
|
*
|
|
* The vector returned is one single memory chunk that must be unchunked
|
|
* by the caller
|
|
*
|
|
* @param[in] string String to be split
|
|
* @param[in] delim String of delimiter characters
|
|
* @param[out] nvec Number of entries in returned vector
|
|
* @param[in] label Chunk label for returned vector
|
|
* @retval vec Vector of strings. Free with unchunk
|
|
* @retval NULL Error
|
|
* @see clicon_strsplit Operates on full string delimiters rather than
|
|
* individual character delimiters.
|
|
*/
|
|
char **
|
|
clicon_sepsplit (char *string,
|
|
char *delim,
|
|
int *nvec,
|
|
const char *label)
|
|
{
|
|
int idx;
|
|
size_t siz;
|
|
char *s, *s0;
|
|
char **vec, *vecp;
|
|
|
|
*nvec = 0;
|
|
s0 = s = chunkdup (string, strlen(string)+1, __FUNCTION__);
|
|
while (strsep(&s, delim))
|
|
(*nvec)++;
|
|
unchunk (s0);
|
|
|
|
siz = ((*nvec +1) * sizeof (char *)) + strlen(string) + 1;
|
|
vec = (char **) chunk (siz, label);
|
|
if (!vec) {
|
|
return NULL;
|
|
}
|
|
bzero (vec, siz);
|
|
|
|
vecp = (char *)&vec[*nvec +1];
|
|
bcopy (string, vecp, strlen (string));
|
|
|
|
for (idx = 0; idx < *nvec; idx++) {
|
|
vec[idx] = vecp;
|
|
strsep (&vecp, delim);
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
/*! Split string into a vector based on a string delimiter
|
|
*
|
|
* The given string is split into a vector where the delimited by the
|
|
* the full delimiter string. The matched delimiters are not part of the
|
|
* resulting vector.
|
|
*
|
|
* See also clicon_sepsplit() which is similar
|
|
*
|
|
* The vector returned is one single memory chunk that must be unchunked
|
|
* by the caller
|
|
*
|
|
* @param[in] string String to be split
|
|
* @param[in] delim String of delimiter characters
|
|
* @param[out] nvec Number of entries in returned vector
|
|
* @param[in] label Chunk label for returned vector
|
|
* @retval vec Vector of strings. Free with unchunk
|
|
* @retval NULL Error
|
|
* @see clicon_sepsplit Operates on individual character delimiters rather
|
|
* than full string delimiter.
|
|
*/
|
|
char **
|
|
clicon_strsplit (char *string,
|
|
char *delim,
|
|
int *nvec,
|
|
const char *label)
|
|
{
|
|
int idx;
|
|
size_t siz;
|
|
char *s;
|
|
char **vec, *vecp;
|
|
|
|
*nvec = 1;
|
|
s = string;
|
|
while ((s = strstr(s, delim))) {
|
|
s += strlen(delim);
|
|
(*nvec)++;
|
|
}
|
|
|
|
siz = ((*nvec +1) * sizeof (char *)) + strlen(string) + 1;
|
|
vec = (char **) chunk (siz, label);
|
|
if (!vec) {
|
|
return NULL;
|
|
}
|
|
bzero (vec, siz);
|
|
|
|
vecp = (char *)&vec[*nvec +1];
|
|
bcopy (string, vecp, strlen (string));
|
|
|
|
s = vecp;
|
|
for (idx = 0; idx < *nvec; idx++) {
|
|
vec[idx] = s;
|
|
if ((s = strstr(s, delim)) != NULL) {
|
|
*s = '\0';
|
|
s += strlen(delim);
|
|
}
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
/*! Concatenate elements of a string array into a string.
|
|
* An optional delimiter string can be specified which will be inserted betwen
|
|
* each element.
|
|
* @param[in] label Chunk label for returned vector
|
|
* @retval str Joined string. Free with unchunk()
|
|
* @retval NULL Failure
|
|
*/
|
|
char *
|
|
clicon_strjoin (int argc,
|
|
char **argv,
|
|
char *delim,
|
|
const char *label)
|
|
{
|
|
int i;
|
|
int len;
|
|
char *str;
|
|
|
|
len = 0;
|
|
for (i = 0; i < argc; i++)
|
|
len += strlen(argv[i]);
|
|
if (delim)
|
|
len += (strlen(delim) * argc);
|
|
len += 1; /* '\0' */
|
|
|
|
if ((str = chunk (len, label)) == NULL)
|
|
return NULL;
|
|
memset (str, '\0', len);
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
if (i != 0)
|
|
strncat (str, delim, len - strlen(str));
|
|
strncat (str, argv[i], len - strlen(str));
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/*! Trim whitespace in beginning and end of string.
|
|
*
|
|
* @param[in] label Chunk label for returned vector
|
|
* @retval str Trimmed string. Free with unchunk()
|
|
* @retval NULL Failure
|
|
*/
|
|
char *
|
|
clicon_strtrim(char *str,
|
|
const char *label)
|
|
{
|
|
char *start, *end, *new;
|
|
|
|
start = str;
|
|
while (*start != '\0' && isspace(*start))
|
|
start++;
|
|
if (!strlen(start))
|
|
return (char *)chunkdup("\0", 1, label);
|
|
|
|
end = str + strlen(str) ;
|
|
while (end > str && isspace(*(end-1)))
|
|
end--;
|
|
if((new = chunkdup (start, end-start+1, label)))
|
|
new[end-start] = '\0';
|
|
|
|
return new;
|
|
}
|
|
|
|
/*! Given a string s, on format: a[b], separate it into two parts: a and b
|
|
* [] are separators.
|
|
* alterative use:
|
|
* a/b -> a and b (where sep = "/")
|
|
* @param[in] label Chunk label for returned vector
|
|
*/
|
|
int
|
|
clicon_sep(char *s,
|
|
const char sep[2],
|
|
const char *label,
|
|
char **a0,
|
|
char **b0)
|
|
{
|
|
char *a = NULL;
|
|
char *b = NULL;
|
|
char *ptr;
|
|
int len;
|
|
int retval = -1;
|
|
|
|
ptr = s;
|
|
/* move forward to last char of element name */
|
|
while (*ptr && *ptr != sep[0] && *ptr != sep[1] )
|
|
ptr++;
|
|
/* Copy first element name */
|
|
len = ptr-s;
|
|
if ((a = chunkdup(s, len+1, label)) == NULL)
|
|
goto catch;
|
|
a[len] = '\0';
|
|
/* Do we have an extended format? */
|
|
if (*ptr == sep[0]) {
|
|
b = ++ptr;
|
|
/* move forward to end extension */
|
|
while (*ptr && *ptr != sep[1])
|
|
ptr++;
|
|
/* Copy extension */
|
|
len = ptr-b;
|
|
if ((b = chunkdup(b, len+1, label)) == NULL)
|
|
goto catch;
|
|
b[len] = '\0';
|
|
}
|
|
|
|
*a0 = a;
|
|
*b0 = b;
|
|
retval = 0;
|
|
catch:
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*! strndup() for systems without it, such as xBSD
|
|
*/
|
|
#ifndef HAVE_STRNDUP
|
|
char *
|
|
clicon_strndup (const char *str,
|
|
size_t len)
|
|
{
|
|
char *new;
|
|
size_t slen;
|
|
|
|
slen = strlen (str);
|
|
len = (len < slen ? len : slen);
|
|
|
|
new = malloc (len + 1);
|
|
if (new == NULL)
|
|
return NULL;
|
|
|
|
new[len] = '\0';
|
|
memcpy (new, str, len);
|
|
|
|
return new;
|
|
}
|
|
#endif /* ! HAVE_STRNDUP */
|
|
|
|
/*! Match string against regexp.
|
|
*
|
|
* If a match pointer is given, the matching substring
|
|
* will be allocated 'match' will be pointing to it. The match string must
|
|
* be free:ed by the application.
|
|
* @retval -1 Failure
|
|
* @retval 0 No match
|
|
* @retval >0 Match: Length of matching substring
|
|
*/
|
|
int
|
|
clicon_strmatch(const char *str,
|
|
const char *regexp,
|
|
char **match)
|
|
{
|
|
size_t len;
|
|
int status;
|
|
regex_t re;
|
|
char rxerr[128];
|
|
size_t nmatch = 1;
|
|
regmatch_t pmatch[1];
|
|
|
|
if (match)
|
|
*match = NULL;
|
|
|
|
if ((status = regcomp(&re, regexp, REG_EXTENDED)) != 0) {
|
|
regerror(status, &re, rxerr, sizeof(rxerr));
|
|
clicon_err(OE_REGEX, errno, "%s", rxerr);
|
|
return -1;
|
|
}
|
|
|
|
status = regexec(&re, str, nmatch, pmatch, 0);
|
|
regfree(&re);
|
|
if (status != 0)
|
|
return 0; /* No match */
|
|
|
|
len = pmatch[0].rm_eo - pmatch[0].rm_so;
|
|
/* If we've specified a match pointer, allocate and populate it. */
|
|
if (match) {
|
|
if ((*match = malloc(len + 1)) == NULL) {
|
|
clicon_err(OE_UNIX, errno, "Failed to allocate string");
|
|
return -1;
|
|
}
|
|
memset(*match, '\0', len + 1);
|
|
strncpy(*match, str + pmatch[0].rm_so, len);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/*! Substitute pattern in string.
|
|
* @retval str Malloc:ed string on success, use free to deallocate
|
|
* @retval NULL Failure.
|
|
*/
|
|
char *
|
|
clicon_strsub(char *str,
|
|
char *from,
|
|
char *to)
|
|
{
|
|
char **vec;
|
|
int nvec;
|
|
char *new;
|
|
char *retval = NULL;
|
|
|
|
if ((vec = clicon_strsplit(str, from, &nvec, __FUNCTION__)) == NULL) {
|
|
clicon_err(OE_UNIX, errno, "Failed to split string");
|
|
goto done;
|
|
}
|
|
|
|
if ((new = clicon_strjoin (nvec, vec, to, __FUNCTION__)) == NULL) {
|
|
clicon_err(OE_UNIX, errno, "Failed to split string");
|
|
goto done;
|
|
}
|
|
|
|
retval = strdup(new);
|
|
|
|
done:
|
|
unchunk_group(__FUNCTION__);
|
|
return retval;
|
|
}
|