ossp-pkg/var/expression.c
#include "internal.h"
int expression(const char* begin, const char* end, const var_config_t* config,
const char nameclass[256], var_cb_t lookup, void* lookup_context,
int force_expand, tokenbuf* result)
{
const char* p = begin;
const char* data;
size_t len, buffer_size;
int failed = 0;
int rc;
tokenbuf name;
tokenbuf tmp;
/* Clear the tokenbufs to make sure we have a defined state. */
init_tokenbuf(&name);
init_tokenbuf(&tmp);
init_tokenbuf(result);
/* Expect STARTDELIM. */
if (p == end || *p != config->startdelim)
return 0;
if (++p == end)
return VAR_INCOMPLETE_VARIABLE_SPEC;
/* Get the name of the variable to expand. The name may consist of
an arbitrary number of VARNAMEs and VARIABLEs. */
do
{
rc = varname(p, end, nameclass);
if (rc < 0)
goto error_return;
else if (rc > 0)
{
if (!append_to_tokenbuf(&name, p, rc))
{
rc = VAR_OUT_OF_MEMORY;
goto error_return;
}
else
p += rc;
}
rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp);
if (rc < 0)
goto error_return;
else if (rc > 0)
{
if (!append_to_tokenbuf(&name, tmp.begin, tmp.end - tmp.begin))
{
rc = VAR_OUT_OF_MEMORY;
goto error_return;
}
else
p += rc;
}
}
while (rc > 0);
/* We must have the complete variable name now, so make sure we
do. */
if (name.begin == name.end)
{
rc = VAR_INCOMPLETE_VARIABLE_SPEC;
goto error_return;
}
/* Now we have the name of the variable stored in "name". We
expect an ENDDELIM here. */
if (p == end || (*p != config->enddelim && *p != ':'))
{
rc = VAR_INCOMPLETE_VARIABLE_SPEC;
goto error_return;
}
else
++p;
/* Use the lookup callback to get the variable's contents. */
rc = (*lookup)(lookup_context, name.begin, name.end - name.begin, &data, &len, &buffer_size);
if (rc < 0)
goto error_return;
else if (rc == 0)
{
/* The variable is undefined. What we'll do now depends on the
force_expand flag. */
if (force_expand)
{
rc = VAR_UNDEFINED_VARIABLE;
goto error_return;
}
else
{
/* Initialize result to point back to the original text in
the buffer. */
result->begin = begin-1;
result->end = p;
result->buffer_size = 0;
failed = 1;
}
}
else
{
/* The preliminary result is the contents of the variable.
This may be modified by the commands that may follow. */
result->begin = data;
result->end = data + len;
result->buffer_size = buffer_size;
}
if (p[-1] == ':')
{
/* Parse and execute commands. */
free_tokenbuf(&tmp);
--p;
while (p != end && *p == ':')
{
++p;
if (!failed)
rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, result);
else
rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp);
if (rc < 0)
goto error_return;
p += rc;
if (failed)
result->end += rc;
}
if (p == end || *p != config->enddelim)
{
rc = VAR_INCOMPLETE_VARIABLE_SPEC;
goto error_return;
}
++p;
if (failed)
++result->end;
}
/* Exit gracefully. */
free_tokenbuf(&name);
free_tokenbuf(&tmp);
return p - begin;
/* Exit in case of an error. */
error_return:
free_tokenbuf(&name);
free_tokenbuf(&tmp);
free_tokenbuf(result);
return rc;
}