#include <ctype.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "defines.h"
#include "var.h"
#include "varname.h"
#include "error.h"
#include "cmd_exec.h"
#include "parsevar.h"
static const char *find_op1(const char *);
static bool parse_variable_assignment(const char *, int);
static const char *
find_op1(const char *p)
{
for(;; p++) {
if (ISSPACE(*p) || *p == '$' || *p == '\0')
break;
if (p[strspn(p, "?:!+")] == '=')
break;
if (p[0] == ':' && p[1] == 's' && p[2] == 'h')
break;
}
return p;
}
static bool
parse_variable_assignment(const char *line, int ctxt)
{
const char *arg;
char *res1 = NULL, *res2 = NULL;
#define VAR_INVALID -1
#define VAR_NORMAL 0
#define VAR_SUBST 1
#define VAR_APPEND 2
#define VAR_SHELL 4
#define VAR_OPT 8
#define VAR_LAZYSHELL 16
#define VAR_SUNSHELL 32
int type;
struct Name name;
arg = VarName_Get(line, &name, NULL, true, find_op1);
while (ISSPACE(*arg))
arg++;
type = VAR_NORMAL;
while (*arg != '=') {
switch (*arg++) {
case '+':
if (type & (VAR_OPT|VAR_LAZYSHELL|VAR_APPEND))
type = VAR_INVALID;
else
type |= VAR_APPEND;
break;
case '?':
if (type & (VAR_OPT|VAR_APPEND))
type = VAR_INVALID;
else
type |= VAR_OPT;
break;
case ':':
if (strncmp(arg, "sh", 2) == 0) {
type = VAR_SUNSHELL;
arg += 2;
while (*arg != '=' && *arg != '\0')
arg++;
} else {
if (type & VAR_SUBST)
type = VAR_INVALID;
else
type |= VAR_SUBST;
}
break;
case '!':
if (type & VAR_SHELL) {
if (type & (VAR_APPEND))
type = VAR_INVALID;
else
type = VAR_LAZYSHELL;
} else if (type & (VAR_LAZYSHELL|VAR_SUNSHELL))
type = VAR_INVALID;
else
type |= VAR_SHELL;
break;
default:
type = VAR_INVALID;
break;
}
if (type == VAR_INVALID) {
VarName_Free(&name);
return false;
}
}
arg++;
while (ISSPACE(*arg))
arg++;
if ((type & VAR_OPT) && Var_Definedi(name.s, name.e)) {
VarName_Free(&name);
return true;
}
if (type & (VAR_SHELL|VAR_SUNSHELL)) {
char *err;
if (strchr(arg, '$') != NULL) {
char *sub;
sub = Var_Subst(arg, NULL, true);
res1 = Cmd_Exec(sub, &err);
free(sub);
} else
res1 = Cmd_Exec(arg, &err);
if (err)
Parse_Error(PARSE_WARNING, err, arg);
arg = res1;
}
if (type & VAR_LAZYSHELL) {
if (strchr(arg, '$') != NULL) {
arg = Var_Subst(arg, NULL, true);
}
}
if (type & VAR_SUBST) {
bool saved = errorIsOkay;
errorIsOkay = false;
if (!Var_Definedi(name.s, name.e))
Var_Seti_with_ctxt(name.s, name.e, "", ctxt);
res2 = Var_Subst(arg, NULL, false);
errorIsOkay = saved;
arg = res2;
}
if (type & VAR_APPEND)
Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt);
else
Var_Seti_with_ctxt(name.s, name.e, arg, ctxt);
if (type & VAR_LAZYSHELL)
Var_Mark(name.s, name.e, VAR_EXEC_LATER);
VarName_Free(&name);
free(res2);
free(res1);
return true;
}
bool
Parse_As_Var_Assignment(const char *line)
{
return parse_variable_assignment(line, VAR_GLOBAL);
}
bool
Parse_CmdlineVar(const char *line)
{
bool result;
bool saved = errorIsOkay;
errorIsOkay = false;
result = parse_variable_assignment(line, VAR_CMD);
errorIsOkay = saved;
return result;
}