Commit 313026f3 authored by Masami Hiramatsu (Google)'s avatar Masami Hiramatsu (Google) Committed by Arnaldo Carvalho de Melo
Browse files

perf string: Add strpbrk_esq() and strdup_esq() for escape and quote



strpbrk_esq() and strdup_esq() are new variants for strpbrk() and
strdup() which handles escaped characters and quoted strings.

- strpbrk_esq() searches specified set of characters but ignores the
  escaped characters and quoted strings.
  e.g. strpbrk_esq("'quote\d' \queue quiz", "qd") returns "quiz".

- strdup_esq() duplicates string but removes backslash and quotes which
  is used for quotation. It also keeps the string (including backslash)
  in the quoted part.
  e.g. strdup_esq("'quote\d' \queue quiz") returns "quote\d queue quiz".

The (single, double) quotes in the quoted part should be escaped by
backslash. In this case, strdup_esq() removes that backslash.
The same quotes must be paired. If you use double quotation, you need
to use the double quotation to close the quoted part.

Signed-off-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Cc: Alexander Lobakin <aleksander.lobakin@intel.com>
Cc: Dima Kogan <dima@secretsauce.net>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Link: https://lore.kernel.org/r/173099116045.2431889.15772916605719019533.stgit@mhiramat.roam.corp.google.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent b9e57722
Loading
Loading
Loading
Loading
+100 −0
Original line number Diff line number Diff line
@@ -263,6 +263,34 @@ char *strpbrk_esc(char *str, const char *stopset)
	return ptr;
}

/* Like strpbrk_esc(), but not break if it is quoted with single/double quotes */
char *strpbrk_esq(char *str, const char *stopset)
{
	char *_stopset = NULL;
	char *ptr;
	const char *squote = "'";
	const char *dquote = "\"";

	if (asprintf(&_stopset, "%s%c%c", stopset, *squote, *dquote) < 0)
		return NULL;

	do {
		ptr = strpbrk_esc(str, _stopset);
		if (!ptr)
			break;
		if (*ptr == *squote)
			ptr = strpbrk_esc(ptr + 1, squote);
		else if (*ptr == *dquote)
			ptr = strpbrk_esc(ptr + 1, dquote);
		else
			break;
		str = ptr + 1;
	} while (ptr);

	free(_stopset);
	return ptr;
}

/* Like strdup, but do not copy a single backslash */
char *strdup_esc(const char *str)
{
@@ -293,6 +321,78 @@ char *strdup_esc(const char *str)
	return ret;
}

/* Remove backslash right before quote and return next quote address. */
static char *remove_consumed_esc(char *str, int len, int quote)
{
	char *ptr = str, *end = str + len;

	while (*ptr != quote && ptr < end) {
		if (*ptr == '\\' && *(ptr + 1) == quote) {
			memmove(ptr, ptr + 1, end - (ptr + 1));
			/* now *ptr is `quote`. */
			end--;
		}
		ptr++;
	}

	return *ptr == quote ? ptr : NULL;
}

/*
 * Like strdup_esc, but keep quoted string as it is (and single backslash
 * before quote is removed). If there is no closed quote, return NULL.
 */
char *strdup_esq(const char *str)
{
	char *d, *ret;

	/* If there is no quote, return normal strdup_esc() */
	d = strpbrk_esc((char *)str, "\"'");
	if (!d)
		return strdup_esc(str);

	ret = strdup(str);
	if (!ret)
		return NULL;

	d = ret;
	do {
		d = strpbrk(d, "\\\"\'");
		if (!d)
			break;

		if (*d == '"' || *d == '\'') {
			/* This is non-escaped quote */
			int quote = *d;
			int len = strlen(d + 1) + 1;

			/*
			 * Remove the start quote and remove consumed escape (backslash
			 * before quote) and remove the end quote. If there is no end
			 * quote, it is the input error.
			 */
			memmove(d, d + 1, len);
			d = remove_consumed_esc(d, len, quote);
			if (!d)
				goto error;
			memmove(d, d + 1, strlen(d + 1) + 1);
		}
		if (*d == '\\') {
			memmove(d, d + 1, strlen(d + 1) + 1);
			if (*d == '\\') {
				/* double backslash -- keep the second one. */
				d++;
			}
		}
	} while (*d != '\0');

	return ret;

error:
	free(ret);
	return NULL;
}

unsigned int hex(char c)
{
	if (c >= '0' && c <= '9')
+2 −0
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ char *asprintf__tp_filter_pids(size_t npids, pid_t *pids);

char *strpbrk_esc(char *str, const char *stopset);
char *strdup_esc(const char *str);
char *strpbrk_esq(char *str, const char *stopset);
char *strdup_esq(const char *str);

unsigned int hex(char c);
char *strreplace_chars(char needle, const char *haystack, const char *replace);