|
Packit |
6bd9ab |
/*
|
|
Packit |
6bd9ab |
expr.c - limited shell-like expression parsing functions
|
|
Packit |
6bd9ab |
This file is part of the nss-pam-ldapd library.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
Copyright (C) 2009-2016 Arthur de Jong
|
|
Packit |
6bd9ab |
Copyright (c) 2012 Thorsten Glaser <t.glaser@tarent.de>
|
|
Packit |
6bd9ab |
Copyright (c) 2016 Giovanni Mascellani <gio@debian.org>
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is free software; you can redistribute it and/or
|
|
Packit |
6bd9ab |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License as published by the Free Software Foundation; either
|
|
Packit |
6bd9ab |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is distributed in the hope that it will be useful,
|
|
Packit |
6bd9ab |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6bd9ab |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6bd9ab |
Lesser General Public License for more details.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License along with this library; if not, write to the Free Software
|
|
Packit |
6bd9ab |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6bd9ab |
02110-1301 USA
|
|
Packit |
6bd9ab |
*/
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "config.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include <stdlib.h>
|
|
Packit |
6bd9ab |
#include <string.h>
|
|
Packit |
6bd9ab |
#include <stdio.h>
|
|
Packit |
6bd9ab |
#include <errno.h>
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "expr.h"
|
|
Packit |
6bd9ab |
#include "compat/attrs.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* the maximum length of a variable name */
|
|
Packit |
6bd9ab |
#define MAXVARLENGTH 30
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
static inline int my_isalpha(const char c)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
static inline int my_isdigit(const char c)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
return (c >= '0') && (c <= '9');
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
static inline int my_isalphanum(const char c)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
return my_isalpha(c) || ((c >= '0') && (c <= '9'));
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* return the part of the string that is a valid name */
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_name(const char *str, int *ptr,
|
|
Packit |
6bd9ab |
char *buffer, size_t buflen)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int i = 0;
|
|
Packit |
6bd9ab |
/* clear the buffer */
|
|
Packit |
6bd9ab |
buffer[i] = '\0';
|
|
Packit |
6bd9ab |
/* look for an alpha + alphanumeric* string */
|
|
Packit |
6bd9ab |
if (!my_isalpha(str[*ptr]))
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
while (my_isalphanum(str[*ptr]) || (str[*ptr] == ';'))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if ((size_t)i >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
buffer[i++] = str[(*ptr)++];
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* NULL-terminate the string */
|
|
Packit |
6bd9ab |
if ((size_t)i >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
buffer[i++] = '\0';
|
|
Packit |
6bd9ab |
return buffer;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* dummy expander function to always return an empty string */
|
|
Packit |
6bd9ab |
static const char *empty_expander(const char UNUSED(*name),
|
|
Packit |
6bd9ab |
void UNUSED(*expander_arg))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
return "";
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* definition of the parse functions (they call eachother) */
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_dollar_expression(
|
|
Packit |
6bd9ab |
const char *str, int *ptr, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
expr_expander_func expander, void *expander_arg);
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_expression(
|
|
Packit |
6bd9ab |
const char *str, int *ptr, int endat, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
expr_expander_func expander, void *expander_arg);
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* handle ${attr:-word} expressions */
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_dollar_default(
|
|
Packit |
6bd9ab |
const char *str, int *ptr, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
expr_expander_func expander, void *expander_arg,
|
|
Packit |
6bd9ab |
const char *varvalue)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if ((varvalue != NULL) && (*varvalue != '\0'))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* value is set, skip rest of expression and use value */
|
|
Packit |
6bd9ab |
if (parse_expression(str, ptr, '}', buffer, buflen, empty_expander, NULL) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
if (strlen(varvalue) >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
strcpy(buffer, varvalue);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* value is not set, evaluate rest of expression */
|
|
Packit |
6bd9ab |
if (parse_expression(str, ptr, '}', buffer, buflen, expander, expander_arg) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return buffer;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* handle ${attr:+word} expressions */
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_dollar_alternative(
|
|
Packit |
6bd9ab |
const char *str, int *ptr, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
expr_expander_func expander, void *expander_arg,
|
|
Packit |
6bd9ab |
const char *varvalue)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if ((varvalue != NULL) && (*varvalue != '\0'))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* value is set, evaluate rest of expression */
|
|
Packit |
6bd9ab |
if (parse_expression(str, ptr, '}', buffer, buflen, expander, expander_arg) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* value is not set, skip rest of expression and blank */
|
|
Packit |
6bd9ab |
if (parse_expression(str, ptr, '}', buffer, buflen, empty_expander, NULL) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
buffer[0] = '\0';
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return buffer;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* handle ${attr:offset:length} expressions */
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_dollar_substring(
|
|
Packit |
6bd9ab |
const char *str, int *ptr, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
const char *varvalue)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
char *tmp;
|
|
Packit |
6bd9ab |
unsigned long int offset, length;
|
|
Packit |
6bd9ab |
size_t varlen;
|
|
Packit |
6bd9ab |
/* parse input */
|
|
Packit |
6bd9ab |
tmp = (char *)str + *ptr;
|
|
Packit |
6bd9ab |
if (!my_isdigit(*tmp))
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
errno = 0;
|
|
Packit |
6bd9ab |
offset = strtoul(tmp, &tmp, 10);
|
|
Packit |
6bd9ab |
if ((*tmp != ':') || (errno != 0))
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
tmp += 1;
|
|
Packit |
6bd9ab |
errno = 0;
|
|
Packit |
6bd9ab |
length = strtoul(tmp, &tmp, 10);
|
|
Packit |
6bd9ab |
if ((*tmp != '}') || (errno != 0))
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
/* don't skip closing '}' here, because it will be skipped later */
|
|
Packit |
6bd9ab |
*ptr += tmp - (str + *ptr);
|
|
Packit |
6bd9ab |
varlen = strlen(varvalue);
|
|
Packit |
6bd9ab |
if (offset > varlen)
|
|
Packit |
6bd9ab |
offset = varlen;
|
|
Packit |
6bd9ab |
if (offset + length > varlen)
|
|
Packit |
6bd9ab |
length = varlen - offset;
|
|
Packit |
6bd9ab |
if (length >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
/* everything's ok, copy data; we use memcpy instead of strncpy
|
|
Packit |
6bd9ab |
because we already know the exact lenght in play */
|
|
Packit |
6bd9ab |
memcpy(buffer, varvalue + offset, length);
|
|
Packit |
6bd9ab |
buffer[length] = '\0';
|
|
Packit |
6bd9ab |
return buffer;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* handle ${attr#word} expressions */
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_dollar_match(
|
|
Packit |
6bd9ab |
const char *str, int *ptr, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
const char *varvalue)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
char c;
|
|
Packit |
6bd9ab |
const char *cp, *vp;
|
|
Packit |
6bd9ab |
int ismatch;
|
|
Packit |
6bd9ab |
size_t vallen;
|
|
Packit |
6bd9ab |
cp = str + *ptr;
|
|
Packit |
6bd9ab |
vp = varvalue;
|
|
Packit |
6bd9ab |
ismatch = 1;
|
|
Packit |
6bd9ab |
while (((c = *cp++) != '\0') && (c != '}'))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (ismatch && (*vp =='\0'))
|
|
Packit |
6bd9ab |
ismatch = 0; /* varvalue shorter than trim string */
|
|
Packit |
6bd9ab |
if (c == '?')
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* match any one character */
|
|
Packit |
6bd9ab |
vp++;
|
|
Packit |
6bd9ab |
continue;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
if (c == '\\')
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if ((c = *cp++) == '\0')
|
|
Packit |
6bd9ab |
return NULL; /* end of input: syntax error */
|
|
Packit |
6bd9ab |
/* escape the next character c */
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
if (ismatch && (*vp != c))
|
|
Packit |
6bd9ab |
ismatch = 0; /* they differ */
|
|
Packit |
6bd9ab |
vp++;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
if (c == '\0')
|
|
Packit |
6bd9ab |
return NULL; /* end of input: syntax error */
|
|
Packit |
6bd9ab |
/* at this point, cp points to after the closing } */
|
|
Packit |
6bd9ab |
(*ptr) = cp - str - 1;
|
|
Packit |
6bd9ab |
/* if ismatch, vp points to the beginning of the
|
|
Packit |
6bd9ab |
data after trimming, otherwise vp is invalid */
|
|
Packit |
6bd9ab |
if (!ismatch)
|
|
Packit |
6bd9ab |
vp = varvalue;
|
|
Packit |
6bd9ab |
/* now copy the (trimmed or not) value to the buffer */
|
|
Packit |
6bd9ab |
if ((vallen = strlen(vp) + 1) > buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
memcpy(buffer, vp, vallen);
|
|
Packit |
6bd9ab |
return buffer;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_dollar_expression(
|
|
Packit |
6bd9ab |
const char *str, int *ptr, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
expr_expander_func expander, void *expander_arg)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
char varname[MAXVARLENGTH];
|
|
Packit |
6bd9ab |
const char *varvalue;
|
|
Packit |
6bd9ab |
if ((buflen <= 0) || (buffer == NULL) || (str == NULL) || (ptr == NULL))
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
if (str[*ptr] == '{')
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
(*ptr)++;
|
|
Packit |
6bd9ab |
/* the first part is always a variable name */
|
|
Packit |
6bd9ab |
if (parse_name(str, ptr, varname, sizeof(varname)) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
varvalue = expander(varname, expander_arg);
|
|
Packit |
6bd9ab |
if (varvalue == NULL)
|
|
Packit |
6bd9ab |
varvalue = "";
|
|
Packit |
6bd9ab |
if (str[*ptr] == '}')
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* simple substitute */
|
|
Packit |
6bd9ab |
if (strlen(varvalue) >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
strcpy(buffer, varvalue);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (strncmp(str + *ptr, ":-", 2) == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* if variable is not set or empty, substitute remainder */
|
|
Packit |
6bd9ab |
(*ptr) += 2;
|
|
Packit |
6bd9ab |
if (parse_dollar_default(str, ptr, buffer, buflen, expander, expander_arg, varvalue) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (strncmp(str + *ptr, ":+", 2) == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* if variable is set, substitute remainder */
|
|
Packit |
6bd9ab |
(*ptr) += 2;
|
|
Packit |
6bd9ab |
if (parse_dollar_alternative(str, ptr, buffer, buflen, expander, expander_arg, varvalue) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (str[*ptr] == ':')
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* substitute substring of variable */
|
|
Packit |
6bd9ab |
(*ptr) += 1;
|
|
Packit |
6bd9ab |
if (parse_dollar_substring(str, ptr, buffer, buflen, varvalue) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (str[*ptr] == '#')
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* try to strip the remainder value from variable beginning */
|
|
Packit |
6bd9ab |
(*ptr) += 1;
|
|
Packit |
6bd9ab |
if (parse_dollar_match(str, ptr, buffer, buflen, varvalue) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
(*ptr)++; /* skip closing } */
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* it is a simple reference to a variable, like $uidNumber */
|
|
Packit |
6bd9ab |
if (parse_name(str, ptr, varname, sizeof(varname)) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
varvalue = expander(varname, expander_arg);
|
|
Packit |
6bd9ab |
if (varvalue == NULL)
|
|
Packit |
6bd9ab |
varvalue = "";
|
|
Packit |
6bd9ab |
if (strlen(varvalue) >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
strcpy(buffer, varvalue);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return buffer;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
MUST_USE static const char *parse_expression(
|
|
Packit |
6bd9ab |
const char *str, int *ptr, int endat, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
expr_expander_func expander, void *expander_arg)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int j = 0;
|
|
Packit |
6bd9ab |
/* go over string */
|
|
Packit |
6bd9ab |
while ((str[*ptr] != endat) && (str[*ptr] != '\0'))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
switch (str[*ptr])
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
case '$': /* beginning of an expression */
|
|
Packit |
6bd9ab |
(*ptr)++;
|
|
Packit |
6bd9ab |
if ((size_t)j >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
if (parse_dollar_expression(str, ptr, buffer + j, buflen - j,
|
|
Packit |
6bd9ab |
expander, expander_arg) == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
j = strlen(buffer);
|
|
Packit |
6bd9ab |
break;
|
|
Packit |
6bd9ab |
case '\\': /* escaped character, unescape */
|
|
Packit |
6bd9ab |
(*ptr)++;
|
|
Packit |
6bd9ab |
FALLTHROUGH; /* no break needed here */
|
|
Packit |
6bd9ab |
default: /* just copy the text */
|
|
Packit |
6bd9ab |
if ((size_t)j >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
buffer[j++] = str[*ptr];
|
|
Packit |
6bd9ab |
(*ptr)++;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* NULL-terminate buffer */
|
|
Packit |
6bd9ab |
if ((size_t)j >= buflen)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
buffer[j++] = '\0';
|
|
Packit |
6bd9ab |
return buffer;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
MUST_USE const char *expr_parse(const char *str, char *buffer, size_t buflen,
|
|
Packit |
6bd9ab |
expr_expander_func expander, void *expander_arg)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int i = 0;
|
|
Packit |
6bd9ab |
return parse_expression(str, &i, '\0', buffer, buflen,
|
|
Packit |
6bd9ab |
expander, expander_arg);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
SET *expr_vars(const char *str, SET *set)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
char varname[MAXVARLENGTH];
|
|
Packit |
6bd9ab |
int i = 0;
|
|
Packit |
6bd9ab |
/* allocate set if needed */
|
|
Packit |
6bd9ab |
if (set == NULL)
|
|
Packit |
6bd9ab |
set = set_new();
|
|
Packit |
6bd9ab |
if (set == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
/* go over string */
|
|
Packit |
6bd9ab |
while (str[i] != '\0')
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
switch (str[i])
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
case '$': /* beginning of a $-expression */
|
|
Packit |
6bd9ab |
i++;
|
|
Packit |
6bd9ab |
if (str[i] == '{')
|
|
Packit |
6bd9ab |
i++;
|
|
Packit |
6bd9ab |
/* the rest should start with a variable name */
|
|
Packit |
6bd9ab |
if (parse_name(str, &i, varname, sizeof(varname)) != NULL)
|
|
Packit |
6bd9ab |
set_add(set, varname);
|
|
Packit |
6bd9ab |
break;
|
|
Packit |
6bd9ab |
case '\\': /* escaped character, unescape */
|
|
Packit |
6bd9ab |
i++;
|
|
Packit |
6bd9ab |
FALLTHROUGH; /* no break needed here */
|
|
Packit |
6bd9ab |
default: /* just skip */
|
|
Packit |
6bd9ab |
i++;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return set;
|
|
Packit |
6bd9ab |
}
|