xref: /linux-6.15/tools/perf/util/string.c (revision 61e0a944)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a067558eSArnaldo Carvalho de Melo #include "string2.h"
3a067558eSArnaldo Carvalho de Melo #include <linux/kernel.h>
4a067558eSArnaldo Carvalho de Melo #include <linux/string.h>
5a067558eSArnaldo Carvalho de Melo #include <stdlib.h>
686470930SIngo Molnar 
73052ba56SArnaldo Carvalho de Melo #include <linux/ctype.h>
83d689ed6SArnaldo Carvalho de Melo 
96a9fa4e3SArnaldo Carvalho de Melo const char *graph_dotted_line =
106a9fa4e3SArnaldo Carvalho de Melo 	"---------------------------------------------------------------------"
116a9fa4e3SArnaldo Carvalho de Melo 	"---------------------------------------------------------------------"
126a9fa4e3SArnaldo Carvalho de Melo 	"---------------------------------------------------------------------";
136a9fa4e3SArnaldo Carvalho de Melo const char *dots =
146a9fa4e3SArnaldo Carvalho de Melo 	"....................................................................."
156a9fa4e3SArnaldo Carvalho de Melo 	"....................................................................."
166a9fa4e3SArnaldo Carvalho de Melo 	".....................................................................";
176a9fa4e3SArnaldo Carvalho de Melo 
18d2fb8b41SHitoshi Mitake /*
19d2fb8b41SHitoshi Mitake  * perf_atoll()
20d2fb8b41SHitoshi Mitake  * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB")
21d2fb8b41SHitoshi Mitake  * and return its numeric value
22d2fb8b41SHitoshi Mitake  */
perf_atoll(const char * str)23d2fb8b41SHitoshi Mitake s64 perf_atoll(const char *str)
24d2fb8b41SHitoshi Mitake {
258ba7f6c2SAl Viro 	s64 length;
268ba7f6c2SAl Viro 	char *p;
278ba7f6c2SAl Viro 	char c;
28d2fb8b41SHitoshi Mitake 
29d2fb8b41SHitoshi Mitake 	if (!isdigit(str[0]))
30d2fb8b41SHitoshi Mitake 		goto out_err;
31d2fb8b41SHitoshi Mitake 
328ba7f6c2SAl Viro 	length = strtoll(str, &p, 10);
338ba7f6c2SAl Viro 	switch (c = *p++) {
348ba7f6c2SAl Viro 		case 'b': case 'B':
358ba7f6c2SAl Viro 			if (*p)
36d2fb8b41SHitoshi Mitake 				goto out_err;
3794bdd5edSArnaldo Carvalho de Melo 
38f7a858bfSLiam Howlett 			fallthrough;
398ba7f6c2SAl Viro 		case '\0':
408ba7f6c2SAl Viro 			return length;
41d2fb8b41SHitoshi Mitake 		default:
42d2fb8b41SHitoshi Mitake 			goto out_err;
438ba7f6c2SAl Viro 		/* two-letter suffices */
448ba7f6c2SAl Viro 		case 'k': case 'K':
458ba7f6c2SAl Viro 			length <<= 10;
468ba7f6c2SAl Viro 			break;
478ba7f6c2SAl Viro 		case 'm': case 'M':
488ba7f6c2SAl Viro 			length <<= 20;
498ba7f6c2SAl Viro 			break;
508ba7f6c2SAl Viro 		case 'g': case 'G':
518ba7f6c2SAl Viro 			length <<= 30;
528ba7f6c2SAl Viro 			break;
538ba7f6c2SAl Viro 		case 't': case 'T':
548ba7f6c2SAl Viro 			length <<= 40;
55d2fb8b41SHitoshi Mitake 			break;
56d2fb8b41SHitoshi Mitake 	}
578ba7f6c2SAl Viro 	/* we want the cases to match */
588ba7f6c2SAl Viro 	if (islower(c)) {
598ba7f6c2SAl Viro 		if (strcmp(p, "b") != 0)
608ba7f6c2SAl Viro 			goto out_err;
618ba7f6c2SAl Viro 	} else {
628ba7f6c2SAl Viro 		if (strcmp(p, "B") != 0)
638ba7f6c2SAl Viro 			goto out_err;
64d2fb8b41SHitoshi Mitake 	}
658ba7f6c2SAl Viro 	return length;
66d2fb8b41SHitoshi Mitake 
67d2fb8b41SHitoshi Mitake out_err:
688ba7f6c2SAl Viro 	return -1;
69d2fb8b41SHitoshi Mitake }
70e1c01d61SMasami Hiramatsu 
716964cd2cSMasami Hiramatsu /* Character class matching */
__match_charclass(const char * pat,char c,const char ** npat)726964cd2cSMasami Hiramatsu static bool __match_charclass(const char *pat, char c, const char **npat)
736964cd2cSMasami Hiramatsu {
746964cd2cSMasami Hiramatsu 	bool complement = false, ret = true;
756964cd2cSMasami Hiramatsu 
766964cd2cSMasami Hiramatsu 	if (*pat == '!') {
776964cd2cSMasami Hiramatsu 		complement = true;
786964cd2cSMasami Hiramatsu 		pat++;
796964cd2cSMasami Hiramatsu 	}
806964cd2cSMasami Hiramatsu 	if (*pat++ == c)	/* First character is special */
816964cd2cSMasami Hiramatsu 		goto end;
826964cd2cSMasami Hiramatsu 
836964cd2cSMasami Hiramatsu 	while (*pat && *pat != ']') {	/* Matching */
846964cd2cSMasami Hiramatsu 		if (*pat == '-' && *(pat + 1) != ']') {	/* Range */
856964cd2cSMasami Hiramatsu 			if (*(pat - 1) <= c && c <= *(pat + 1))
866964cd2cSMasami Hiramatsu 				goto end;
876964cd2cSMasami Hiramatsu 			if (*(pat - 1) > *(pat + 1))
886964cd2cSMasami Hiramatsu 				goto error;
896964cd2cSMasami Hiramatsu 			pat += 2;
906964cd2cSMasami Hiramatsu 		} else if (*pat++ == c)
916964cd2cSMasami Hiramatsu 			goto end;
926964cd2cSMasami Hiramatsu 	}
936964cd2cSMasami Hiramatsu 	if (!*pat)
946964cd2cSMasami Hiramatsu 		goto error;
956964cd2cSMasami Hiramatsu 	ret = false;
966964cd2cSMasami Hiramatsu 
976964cd2cSMasami Hiramatsu end:
986964cd2cSMasami Hiramatsu 	while (*pat && *pat != ']')	/* Searching closing */
996964cd2cSMasami Hiramatsu 		pat++;
1006964cd2cSMasami Hiramatsu 	if (!*pat)
1016964cd2cSMasami Hiramatsu 		goto error;
1026964cd2cSMasami Hiramatsu 	*npat = pat + 1;
1036964cd2cSMasami Hiramatsu 	return complement ? !ret : ret;
1046964cd2cSMasami Hiramatsu 
1056964cd2cSMasami Hiramatsu error:
1066964cd2cSMasami Hiramatsu 	return false;
1076964cd2cSMasami Hiramatsu }
1086964cd2cSMasami Hiramatsu 
1092a9c8c36SMasami Hiramatsu /* Glob/lazy pattern matching */
__match_glob(const char * str,const char * pat,bool ignore_space,bool case_ins)11038d14f0cSAndi Kleen static bool __match_glob(const char *str, const char *pat, bool ignore_space,
11138d14f0cSAndi Kleen 			bool case_ins)
112bbbb521bSMasami Hiramatsu {
113bbbb521bSMasami Hiramatsu 	while (*str && *pat && *pat != '*') {
1142a9c8c36SMasami Hiramatsu 		if (ignore_space) {
1152a9c8c36SMasami Hiramatsu 			/* Ignore spaces for lazy matching */
1162a9c8c36SMasami Hiramatsu 			if (isspace(*str)) {
1172a9c8c36SMasami Hiramatsu 				str++;
1182a9c8c36SMasami Hiramatsu 				continue;
1192a9c8c36SMasami Hiramatsu 			}
1202a9c8c36SMasami Hiramatsu 			if (isspace(*pat)) {
1212a9c8c36SMasami Hiramatsu 				pat++;
1222a9c8c36SMasami Hiramatsu 				continue;
1232a9c8c36SMasami Hiramatsu 			}
1242a9c8c36SMasami Hiramatsu 		}
1256964cd2cSMasami Hiramatsu 		if (*pat == '?') {	/* Matches any single character */
126bbbb521bSMasami Hiramatsu 			str++;
127bbbb521bSMasami Hiramatsu 			pat++;
1286964cd2cSMasami Hiramatsu 			continue;
1296964cd2cSMasami Hiramatsu 		} else if (*pat == '[')	/* Character classes/Ranges */
1306964cd2cSMasami Hiramatsu 			if (__match_charclass(pat + 1, *str, &pat)) {
1316964cd2cSMasami Hiramatsu 				str++;
1326964cd2cSMasami Hiramatsu 				continue;
133bbbb521bSMasami Hiramatsu 			} else
1346964cd2cSMasami Hiramatsu 				return false;
1356964cd2cSMasami Hiramatsu 		else if (*pat == '\\') /* Escaped char match as normal char */
1366964cd2cSMasami Hiramatsu 			pat++;
13738d14f0cSAndi Kleen 		if (case_ins) {
13838d14f0cSAndi Kleen 			if (tolower(*str) != tolower(*pat))
139bbbb521bSMasami Hiramatsu 				return false;
14038d14f0cSAndi Kleen 		} else if (*str != *pat)
14138d14f0cSAndi Kleen 			return false;
14238d14f0cSAndi Kleen 		str++;
14338d14f0cSAndi Kleen 		pat++;
144bbbb521bSMasami Hiramatsu 	}
145bbbb521bSMasami Hiramatsu 	/* Check wild card */
146bbbb521bSMasami Hiramatsu 	if (*pat == '*') {
147bbbb521bSMasami Hiramatsu 		while (*pat == '*')
148bbbb521bSMasami Hiramatsu 			pat++;
149bbbb521bSMasami Hiramatsu 		if (!*pat)	/* Tail wild card matches all */
150bbbb521bSMasami Hiramatsu 			return true;
151bbbb521bSMasami Hiramatsu 		while (*str)
15238d14f0cSAndi Kleen 			if (__match_glob(str++, pat, ignore_space, case_ins))
153bbbb521bSMasami Hiramatsu 				return true;
154bbbb521bSMasami Hiramatsu 	}
155bbbb521bSMasami Hiramatsu 	return !*str && !*pat;
156bbbb521bSMasami Hiramatsu }
157bbbb521bSMasami Hiramatsu 
1582a9c8c36SMasami Hiramatsu /**
1592a9c8c36SMasami Hiramatsu  * strglobmatch - glob expression pattern matching
1602a9c8c36SMasami Hiramatsu  * @str: the target string to match
1612a9c8c36SMasami Hiramatsu  * @pat: the pattern string to match
1622a9c8c36SMasami Hiramatsu  *
1632a9c8c36SMasami Hiramatsu  * This returns true if the @str matches @pat. @pat can includes wildcards
1642a9c8c36SMasami Hiramatsu  * ('*','?') and character classes ([CHARS], complementation and ranges are
1652a9c8c36SMasami Hiramatsu  * also supported). Also, this supports escape character ('\') to use special
1662a9c8c36SMasami Hiramatsu  * characters as normal character.
1672a9c8c36SMasami Hiramatsu  *
1682a9c8c36SMasami Hiramatsu  * Note: if @pat syntax is broken, this always returns false.
1692a9c8c36SMasami Hiramatsu  */
strglobmatch(const char * str,const char * pat)1702a9c8c36SMasami Hiramatsu bool strglobmatch(const char *str, const char *pat)
1712a9c8c36SMasami Hiramatsu {
17238d14f0cSAndi Kleen 	return __match_glob(str, pat, false, false);
17338d14f0cSAndi Kleen }
17438d14f0cSAndi Kleen 
strglobmatch_nocase(const char * str,const char * pat)17538d14f0cSAndi Kleen bool strglobmatch_nocase(const char *str, const char *pat)
17638d14f0cSAndi Kleen {
17738d14f0cSAndi Kleen 	return __match_glob(str, pat, false, true);
1782a9c8c36SMasami Hiramatsu }
1792a9c8c36SMasami Hiramatsu 
1802a9c8c36SMasami Hiramatsu /**
1812a9c8c36SMasami Hiramatsu  * strlazymatch - matching pattern strings lazily with glob pattern
1822a9c8c36SMasami Hiramatsu  * @str: the target string to match
1832a9c8c36SMasami Hiramatsu  * @pat: the pattern string to match
1842a9c8c36SMasami Hiramatsu  *
1852a9c8c36SMasami Hiramatsu  * This is similar to strglobmatch, except this ignores spaces in
1862a9c8c36SMasami Hiramatsu  * the target string.
1872a9c8c36SMasami Hiramatsu  */
strlazymatch(const char * str,const char * pat)1882a9c8c36SMasami Hiramatsu bool strlazymatch(const char *str, const char *pat)
1892a9c8c36SMasami Hiramatsu {
19038d14f0cSAndi Kleen 	return __match_glob(str, pat, true, false);
1912a9c8c36SMasami Hiramatsu }
192bad03ae4SMasami Hiramatsu 
193bad03ae4SMasami Hiramatsu /**
194bad03ae4SMasami Hiramatsu  * strtailcmp - Compare the tail of two strings
195bad03ae4SMasami Hiramatsu  * @s1: 1st string to be compared
196bad03ae4SMasami Hiramatsu  * @s2: 2nd string to be compared
197bad03ae4SMasami Hiramatsu  *
198bad03ae4SMasami Hiramatsu  * Return 0 if whole of either string is same as another's tail part.
199bad03ae4SMasami Hiramatsu  */
strtailcmp(const char * s1,const char * s2)200bad03ae4SMasami Hiramatsu int strtailcmp(const char *s1, const char *s2)
201bad03ae4SMasami Hiramatsu {
202bad03ae4SMasami Hiramatsu 	int i1 = strlen(s1);
203bad03ae4SMasami Hiramatsu 	int i2 = strlen(s2);
204bad03ae4SMasami Hiramatsu 	while (--i1 >= 0 && --i2 >= 0) {
205bad03ae4SMasami Hiramatsu 		if (s1[i1] != s2[i2])
206bad03ae4SMasami Hiramatsu 			return s1[i1] - s2[i2];
207bad03ae4SMasami Hiramatsu 	}
208bad03ae4SMasami Hiramatsu 	return 0;
209bad03ae4SMasami Hiramatsu }
210bad03ae4SMasami Hiramatsu 
asprintf_expr_inout_ints(const char * var,bool in,size_t nints,int * ints)21193ec4ce7SArnaldo Carvalho de Melo char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
21293ec4ce7SArnaldo Carvalho de Melo {
21393ec4ce7SArnaldo Carvalho de Melo 	/*
21493ec4ce7SArnaldo Carvalho de Melo 	 * FIXME: replace this with an expression using log10() when we
21593ec4ce7SArnaldo Carvalho de Melo 	 * find a suitable implementation, maybe the one in the dvb drivers...
21693ec4ce7SArnaldo Carvalho de Melo 	 *
21793ec4ce7SArnaldo Carvalho de Melo 	 * "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
21893ec4ce7SArnaldo Carvalho de Melo 	 */
21993ec4ce7SArnaldo Carvalho de Melo 	size_t size = nints * 28 + 1; /* \0 */
22093ec4ce7SArnaldo Carvalho de Melo 	size_t i, printed = 0;
22193ec4ce7SArnaldo Carvalho de Melo 	char *expr = malloc(size);
22293ec4ce7SArnaldo Carvalho de Melo 
22393ec4ce7SArnaldo Carvalho de Melo 	if (expr) {
22493ec4ce7SArnaldo Carvalho de Melo 		const char *or_and = "||", *eq_neq = "==";
22593ec4ce7SArnaldo Carvalho de Melo 		char *e = expr;
22693ec4ce7SArnaldo Carvalho de Melo 
22793ec4ce7SArnaldo Carvalho de Melo 		if (!in) {
22893ec4ce7SArnaldo Carvalho de Melo 			or_and = "&&";
22993ec4ce7SArnaldo Carvalho de Melo 			eq_neq = "!=";
23093ec4ce7SArnaldo Carvalho de Melo 		}
23193ec4ce7SArnaldo Carvalho de Melo 
23293ec4ce7SArnaldo Carvalho de Melo 		for (i = 0; i < nints; ++i) {
23393ec4ce7SArnaldo Carvalho de Melo 			if (printed == size)
23493ec4ce7SArnaldo Carvalho de Melo 				goto out_err_overflow;
23593ec4ce7SArnaldo Carvalho de Melo 
23693ec4ce7SArnaldo Carvalho de Melo 			if (i > 0)
237a067558eSArnaldo Carvalho de Melo 				printed += scnprintf(e + printed, size - printed, " %s ", or_and);
23893ec4ce7SArnaldo Carvalho de Melo 			printed += scnprintf(e + printed, size - printed,
23993ec4ce7SArnaldo Carvalho de Melo 					     "%s %s %d", var, eq_neq, ints[i]);
24093ec4ce7SArnaldo Carvalho de Melo 		}
24193ec4ce7SArnaldo Carvalho de Melo 	}
24293ec4ce7SArnaldo Carvalho de Melo 
24393ec4ce7SArnaldo Carvalho de Melo 	return expr;
24493ec4ce7SArnaldo Carvalho de Melo 
24593ec4ce7SArnaldo Carvalho de Melo out_err_overflow:
24693ec4ce7SArnaldo Carvalho de Melo 	free(expr);
24793ec4ce7SArnaldo Carvalho de Melo 	return NULL;
24893ec4ce7SArnaldo Carvalho de Melo }
2491e9f9e8aSMasami Hiramatsu 
2501e9f9e8aSMasami Hiramatsu /* Like strpbrk(), but not break if it is right after a backslash (escaped) */
strpbrk_esc(char * str,const char * stopset)2511e9f9e8aSMasami Hiramatsu char *strpbrk_esc(char *str, const char *stopset)
2521e9f9e8aSMasami Hiramatsu {
2531e9f9e8aSMasami Hiramatsu 	char *ptr;
2541e9f9e8aSMasami Hiramatsu 
2551e9f9e8aSMasami Hiramatsu 	do {
2561e9f9e8aSMasami Hiramatsu 		ptr = strpbrk(str, stopset);
257*61e0a944SIan Rogers 		if (!ptr) {
258*61e0a944SIan Rogers 			/* stopset not in str. */
2591e9f9e8aSMasami Hiramatsu 			break;
260*61e0a944SIan Rogers 		}
261*61e0a944SIan Rogers 		if (ptr == str) {
262*61e0a944SIan Rogers 			/* stopset character is first in str. */
263*61e0a944SIan Rogers 			break;
264*61e0a944SIan Rogers 		}
265*61e0a944SIan Rogers 		if (ptr == str + 1 && str[0] != '\\') {
266*61e0a944SIan Rogers 			/* stopset chacter is second and wasn't preceded by a '\'. */
267*61e0a944SIan Rogers 			break;
268*61e0a944SIan Rogers 		}
2691e9f9e8aSMasami Hiramatsu 		str = ptr + 1;
270*61e0a944SIan Rogers 	} while (ptr[-1] == '\\' && ptr[-2] != '\\');
2711e9f9e8aSMasami Hiramatsu 
2721e9f9e8aSMasami Hiramatsu 	return ptr;
2731e9f9e8aSMasami Hiramatsu }
2741e9f9e8aSMasami Hiramatsu 
275313026f3SMasami Hiramatsu (Google) /* Like strpbrk_esc(), but not break if it is quoted with single/double quotes */
strpbrk_esq(char * str,const char * stopset)276313026f3SMasami Hiramatsu (Google) char *strpbrk_esq(char *str, const char *stopset)
277313026f3SMasami Hiramatsu (Google) {
278313026f3SMasami Hiramatsu (Google) 	char *_stopset = NULL;
279313026f3SMasami Hiramatsu (Google) 	char *ptr;
280313026f3SMasami Hiramatsu (Google) 	const char *squote = "'";
281313026f3SMasami Hiramatsu (Google) 	const char *dquote = "\"";
282313026f3SMasami Hiramatsu (Google) 
283313026f3SMasami Hiramatsu (Google) 	if (asprintf(&_stopset, "%s%c%c", stopset, *squote, *dquote) < 0)
284313026f3SMasami Hiramatsu (Google) 		return NULL;
285313026f3SMasami Hiramatsu (Google) 
286313026f3SMasami Hiramatsu (Google) 	do {
287313026f3SMasami Hiramatsu (Google) 		ptr = strpbrk_esc(str, _stopset);
288313026f3SMasami Hiramatsu (Google) 		if (!ptr)
289313026f3SMasami Hiramatsu (Google) 			break;
290313026f3SMasami Hiramatsu (Google) 		if (*ptr == *squote)
291313026f3SMasami Hiramatsu (Google) 			ptr = strpbrk_esc(ptr + 1, squote);
292313026f3SMasami Hiramatsu (Google) 		else if (*ptr == *dquote)
293313026f3SMasami Hiramatsu (Google) 			ptr = strpbrk_esc(ptr + 1, dquote);
294313026f3SMasami Hiramatsu (Google) 		else
295313026f3SMasami Hiramatsu (Google) 			break;
296313026f3SMasami Hiramatsu (Google) 		str = ptr + 1;
297313026f3SMasami Hiramatsu (Google) 	} while (ptr);
298313026f3SMasami Hiramatsu (Google) 
299313026f3SMasami Hiramatsu (Google) 	free(_stopset);
300313026f3SMasami Hiramatsu (Google) 	return ptr;
301313026f3SMasami Hiramatsu (Google) }
302313026f3SMasami Hiramatsu (Google) 
3031e9f9e8aSMasami Hiramatsu /* Like strdup, but do not copy a single backslash */
strdup_esc(const char * str)3041e9f9e8aSMasami Hiramatsu char *strdup_esc(const char *str)
3051e9f9e8aSMasami Hiramatsu {
3061e9f9e8aSMasami Hiramatsu 	char *s, *d, *p, *ret = strdup(str);
3071e9f9e8aSMasami Hiramatsu 
3081e9f9e8aSMasami Hiramatsu 	if (!ret)
3091e9f9e8aSMasami Hiramatsu 		return NULL;
3101e9f9e8aSMasami Hiramatsu 
3111e9f9e8aSMasami Hiramatsu 	d = strchr(ret, '\\');
3121e9f9e8aSMasami Hiramatsu 	if (!d)
3131e9f9e8aSMasami Hiramatsu 		return ret;
3141e9f9e8aSMasami Hiramatsu 
3151e9f9e8aSMasami Hiramatsu 	s = d + 1;
3161e9f9e8aSMasami Hiramatsu 	do {
3171e9f9e8aSMasami Hiramatsu 		if (*s == '\0') {
3181e9f9e8aSMasami Hiramatsu 			*d = '\0';
3191e9f9e8aSMasami Hiramatsu 			break;
3201e9f9e8aSMasami Hiramatsu 		}
3211e9f9e8aSMasami Hiramatsu 		p = strchr(s + 1, '\\');
3221e9f9e8aSMasami Hiramatsu 		if (p) {
3231e9f9e8aSMasami Hiramatsu 			memmove(d, s, p - s);
3241e9f9e8aSMasami Hiramatsu 			d += p - s;
3251e9f9e8aSMasami Hiramatsu 			s = p + 1;
3261e9f9e8aSMasami Hiramatsu 		} else
3271e9f9e8aSMasami Hiramatsu 			memmove(d, s, strlen(s) + 1);
3281e9f9e8aSMasami Hiramatsu 	} while (p);
3291e9f9e8aSMasami Hiramatsu 
3301e9f9e8aSMasami Hiramatsu 	return ret;
3311e9f9e8aSMasami Hiramatsu }
332cef7af25SFabian Hemmer 
333313026f3SMasami Hiramatsu (Google) /* Remove backslash right before quote and return next quote address. */
remove_consumed_esc(char * str,int len,int quote)334313026f3SMasami Hiramatsu (Google) static char *remove_consumed_esc(char *str, int len, int quote)
335313026f3SMasami Hiramatsu (Google) {
336313026f3SMasami Hiramatsu (Google) 	char *ptr = str, *end = str + len;
337313026f3SMasami Hiramatsu (Google) 
338313026f3SMasami Hiramatsu (Google) 	while (*ptr != quote && ptr < end) {
339313026f3SMasami Hiramatsu (Google) 		if (*ptr == '\\' && *(ptr + 1) == quote) {
340313026f3SMasami Hiramatsu (Google) 			memmove(ptr, ptr + 1, end - (ptr + 1));
341313026f3SMasami Hiramatsu (Google) 			/* now *ptr is `quote`. */
342313026f3SMasami Hiramatsu (Google) 			end--;
343313026f3SMasami Hiramatsu (Google) 		}
344313026f3SMasami Hiramatsu (Google) 		ptr++;
345313026f3SMasami Hiramatsu (Google) 	}
346313026f3SMasami Hiramatsu (Google) 
347313026f3SMasami Hiramatsu (Google) 	return *ptr == quote ? ptr : NULL;
348313026f3SMasami Hiramatsu (Google) }
349313026f3SMasami Hiramatsu (Google) 
350313026f3SMasami Hiramatsu (Google) /*
351313026f3SMasami Hiramatsu (Google)  * Like strdup_esc, but keep quoted string as it is (and single backslash
352313026f3SMasami Hiramatsu (Google)  * before quote is removed). If there is no closed quote, return NULL.
353313026f3SMasami Hiramatsu (Google)  */
strdup_esq(const char * str)354313026f3SMasami Hiramatsu (Google) char *strdup_esq(const char *str)
355313026f3SMasami Hiramatsu (Google) {
356313026f3SMasami Hiramatsu (Google) 	char *d, *ret;
357313026f3SMasami Hiramatsu (Google) 
358313026f3SMasami Hiramatsu (Google) 	/* If there is no quote, return normal strdup_esc() */
359313026f3SMasami Hiramatsu (Google) 	d = strpbrk_esc((char *)str, "\"'");
360313026f3SMasami Hiramatsu (Google) 	if (!d)
361313026f3SMasami Hiramatsu (Google) 		return strdup_esc(str);
362313026f3SMasami Hiramatsu (Google) 
363313026f3SMasami Hiramatsu (Google) 	ret = strdup(str);
364313026f3SMasami Hiramatsu (Google) 	if (!ret)
365313026f3SMasami Hiramatsu (Google) 		return NULL;
366313026f3SMasami Hiramatsu (Google) 
367313026f3SMasami Hiramatsu (Google) 	d = ret;
368313026f3SMasami Hiramatsu (Google) 	do {
369313026f3SMasami Hiramatsu (Google) 		d = strpbrk(d, "\\\"\'");
370313026f3SMasami Hiramatsu (Google) 		if (!d)
371313026f3SMasami Hiramatsu (Google) 			break;
372313026f3SMasami Hiramatsu (Google) 
373313026f3SMasami Hiramatsu (Google) 		if (*d == '"' || *d == '\'') {
374313026f3SMasami Hiramatsu (Google) 			/* This is non-escaped quote */
375313026f3SMasami Hiramatsu (Google) 			int quote = *d;
376313026f3SMasami Hiramatsu (Google) 			int len = strlen(d + 1) + 1;
377313026f3SMasami Hiramatsu (Google) 
378313026f3SMasami Hiramatsu (Google) 			/*
379313026f3SMasami Hiramatsu (Google) 			 * Remove the start quote and remove consumed escape (backslash
380313026f3SMasami Hiramatsu (Google) 			 * before quote) and remove the end quote. If there is no end
381313026f3SMasami Hiramatsu (Google) 			 * quote, it is the input error.
382313026f3SMasami Hiramatsu (Google) 			 */
383313026f3SMasami Hiramatsu (Google) 			memmove(d, d + 1, len);
384313026f3SMasami Hiramatsu (Google) 			d = remove_consumed_esc(d, len, quote);
385313026f3SMasami Hiramatsu (Google) 			if (!d)
386313026f3SMasami Hiramatsu (Google) 				goto error;
387313026f3SMasami Hiramatsu (Google) 			memmove(d, d + 1, strlen(d + 1) + 1);
388313026f3SMasami Hiramatsu (Google) 		}
389313026f3SMasami Hiramatsu (Google) 		if (*d == '\\') {
390313026f3SMasami Hiramatsu (Google) 			memmove(d, d + 1, strlen(d + 1) + 1);
391313026f3SMasami Hiramatsu (Google) 			if (*d == '\\') {
392313026f3SMasami Hiramatsu (Google) 				/* double backslash -- keep the second one. */
393313026f3SMasami Hiramatsu (Google) 				d++;
394313026f3SMasami Hiramatsu (Google) 			}
395313026f3SMasami Hiramatsu (Google) 		}
396313026f3SMasami Hiramatsu (Google) 	} while (*d != '\0');
397313026f3SMasami Hiramatsu (Google) 
398313026f3SMasami Hiramatsu (Google) 	return ret;
399313026f3SMasami Hiramatsu (Google) 
400313026f3SMasami Hiramatsu (Google) error:
401313026f3SMasami Hiramatsu (Google) 	free(ret);
402313026f3SMasami Hiramatsu (Google) 	return NULL;
403313026f3SMasami Hiramatsu (Google) }
404313026f3SMasami Hiramatsu (Google) 
hex(char c)405cef7af25SFabian Hemmer unsigned int hex(char c)
406cef7af25SFabian Hemmer {
407cef7af25SFabian Hemmer 	if (c >= '0' && c <= '9')
408cef7af25SFabian Hemmer 		return c - '0';
409cef7af25SFabian Hemmer 	if (c >= 'a' && c <= 'f')
410cef7af25SFabian Hemmer 		return c - 'a' + 10;
411cef7af25SFabian Hemmer 	return c - 'A' + 10;
412cef7af25SFabian Hemmer }
4138a55c1e2SJames Clark 
4148a55c1e2SJames Clark /*
4158a55c1e2SJames Clark  * Replace all occurrences of character 'needle' in string 'haystack' with
4168a55c1e2SJames Clark  * string 'replace'
4178a55c1e2SJames Clark  *
4188a55c1e2SJames Clark  * The new string could be longer so a new string is returned which must be
4198a55c1e2SJames Clark  * freed.
4208a55c1e2SJames Clark  */
strreplace_chars(char needle,const char * haystack,const char * replace)4218a55c1e2SJames Clark char *strreplace_chars(char needle, const char *haystack, const char *replace)
4228a55c1e2SJames Clark {
4238a55c1e2SJames Clark 	int replace_len = strlen(replace);
4248a55c1e2SJames Clark 	char *new_s, *to;
4258a55c1e2SJames Clark 	const char *loc = strchr(haystack, needle);
4268a55c1e2SJames Clark 	const char *from = haystack;
4278a55c1e2SJames Clark 	int num = 0;
4288a55c1e2SJames Clark 
4298a55c1e2SJames Clark 	/* Count occurrences */
4308a55c1e2SJames Clark 	while (loc) {
4318a55c1e2SJames Clark 		loc = strchr(loc + 1, needle);
4328a55c1e2SJames Clark 		num++;
4338a55c1e2SJames Clark 	}
4348a55c1e2SJames Clark 
4358a55c1e2SJames Clark 	/* Allocate enough space for replacements and reset first location */
4368a55c1e2SJames Clark 	new_s = malloc(strlen(haystack) + (num * (replace_len - 1) + 1));
4378a55c1e2SJames Clark 	if (!new_s)
4388a55c1e2SJames Clark 		return NULL;
4398a55c1e2SJames Clark 	loc = strchr(haystack, needle);
4408a55c1e2SJames Clark 	to = new_s;
4418a55c1e2SJames Clark 
4428a55c1e2SJames Clark 	while (loc) {
4438a55c1e2SJames Clark 		/* Copy original string up to found char and update positions */
4448a55c1e2SJames Clark 		memcpy(to, from, 1 + loc - from);
4458a55c1e2SJames Clark 		to += loc - from;
4468a55c1e2SJames Clark 		from = loc + 1;
4478a55c1e2SJames Clark 
4488a55c1e2SJames Clark 		/* Copy replacement string and update positions */
4498a55c1e2SJames Clark 		memcpy(to, replace, replace_len);
4508a55c1e2SJames Clark 		to += replace_len;
4518a55c1e2SJames Clark 
4528a55c1e2SJames Clark 		/* needle next occurrence or end of string */
4538a55c1e2SJames Clark 		loc = strchr(from, needle);
4548a55c1e2SJames Clark 	}
4558a55c1e2SJames Clark 
4568a55c1e2SJames Clark 	/* Copy any remaining chars + null */
4578a55c1e2SJames Clark 	strcpy(to, from);
4588a55c1e2SJames Clark 
4598a55c1e2SJames Clark 	return new_s;
4608a55c1e2SJames Clark }
461