+static const char *Cmd_GetCvarValue(const char *var, size_t varlen, cmdalias_t *alias)
+{
+ static char varname[MAX_INPUTLINE]; // cmd_mutex
+ static char varval[MAX_INPUTLINE]; // cmd_mutex
+ const char *varstr = NULL;
+ char *varfunc;
+ qboolean required = false;
+ qboolean optional = false;
+ static char asis[] = "asis"; // just to suppress const char warnings
+
+ if(varlen >= MAX_INPUTLINE)
+ varlen = MAX_INPUTLINE - 1;
+ memcpy(varname, var, varlen);
+ varname[varlen] = 0;
+ varfunc = strchr(varname, ' ');
+
+ if(varfunc)
+ {
+ *varfunc = 0;
+ ++varfunc;
+ }
+
+ if(*var == 0)
+ {
+ // empty cvar name?
+ if(alias)
+ Con_Printf("Warning: Could not expand $ in alias %s\n", alias->name);
+ else
+ Con_Printf("Warning: Could not expand $\n");
+ return "$";
+ }
+
+ if(varfunc)
+ {
+ char *p;
+ // ? means optional
+ while((p = strchr(varfunc, '?')))
+ {
+ optional = true;
+ memmove(p, p+1, strlen(p)); // with final NUL
+ }
+ // ! means required
+ while((p = strchr(varfunc, '!')))
+ {
+ required = true;
+ memmove(p, p+1, strlen(p)); // with final NUL
+ }
+ // kill spaces
+ while((p = strchr(varfunc, ' ')))
+ {
+ memmove(p, p+1, strlen(p)); // with final NUL
+ }
+ // if no function is left, NULL it
+ if(!*varfunc)
+ varfunc = NULL;
+ }
+
+ if(varname[0] == '$')
+ varstr = Cmd_GetDirectCvarValue(Cmd_GetDirectCvarValue(varname + 1, alias, NULL), alias, NULL);
+ else
+ {
+ qboolean is_multiple = false;
+ // Exception: $* and $n- don't use the quoted form by default
+ varstr = Cmd_GetDirectCvarValue(varname, alias, &is_multiple);
+ if(is_multiple)
+ if(!varfunc)
+ varfunc = asis;
+ }
+
+ if(!varstr)
+ {
+ if(required)
+ {
+ if(alias)
+ Con_Printf("Error: Could not expand $%s in alias %s\n", varname, alias->name);
+ else
+ Con_Printf("Error: Could not expand $%s\n", varname);
+ return NULL;
+ }
+ else if(optional)
+ {
+ return "";
+ }
+ else
+ {
+ if(alias)
+ Con_Printf("Warning: Could not expand $%s in alias %s\n", varname, alias->name);
+ else
+ Con_Printf("Warning: Could not expand $%s\n", varname);
+ dpsnprintf(varval, sizeof(varval), "$%s", varname);
+ return varval;
+ }
+ }
+
+ if(!varfunc || !strcmp(varfunc, "q")) // note: quoted form is default, use "asis" to override!
+ {
+ // quote it so it can be used inside double quotes
+ // we just need to replace " by \", and of course, double backslashes
+ Cmd_QuoteString(varval, sizeof(varval), varstr, "\"\\", false);
+ return varval;
+ }
+ else if(!strcmp(varfunc, "asis"))
+ {
+ return varstr;
+ }
+ else
+ Con_Printf("Unknown variable function %s\n", varfunc);
+
+ return varstr;
+}
+
+/*
+Cmd_PreprocessString
+
+Preprocesses strings and replaces $*, $param#, $cvar accordingly. Also strips comments.
+*/
+static qboolean Cmd_PreprocessString( const char *intext, char *outtext, unsigned maxoutlen, cmdalias_t *alias ) {
+ const char *in;
+ size_t eat, varlen;
+ unsigned outlen;
+ const char *val;
+
+ // don't crash if there's no room in the outtext buffer
+ if( maxoutlen == 0 ) {
+ return false;
+ }
+ maxoutlen--; // because of \0
+
+ in = intext;
+ outlen = 0;
+
+ while( *in && outlen < maxoutlen ) {
+ if( *in == '$' ) {
+ // this is some kind of expansion, see what comes after the $
+ in++;
+
+ // The console does the following preprocessing:
+ //
+ // - $$ is transformed to a single dollar sign.
+ // - $var or ${var} are expanded to the contents of the named cvar,
+ // with quotation marks and backslashes quoted so it can safely
+ // be used inside quotation marks (and it should always be used
+ // that way)
+ // - ${var asis} inserts the cvar value as is, without doing this
+ // quoting
+ // - ${var ?} silently expands to the empty string if
+ // $var does not exist
+ // - ${var !} fails expansion and executes nothing if
+ // $var does not exist
+ // - prefix the cvar name with a dollar sign to do indirection;
+ // for example, if $x has the value timelimit, ${$x} will return
+ // the value of $timelimit
+ // - when expanding an alias, the special variable name $* refers
+ // to all alias parameters, and a number refers to that numbered
+ // alias parameter, where the name of the alias is $0, the first
+ // parameter is $1 and so on; as a special case, $* inserts all
+ // parameters, without extra quoting, so one can use $* to just
+ // pass all parameters around. All parameters starting from $n
+ // can be referred to as $n- (so $* is equivalent to $1-).
+ // - ${* q} and ${n- q} force quoting anyway
+ //
+ // Note: when expanding an alias, cvar expansion is done in the SAME step
+ // as alias expansion so that alias parameters or cvar values containing
+ // dollar signs have no unwanted bad side effects. However, this needs to
+ // be accounted for when writing complex aliases. For example,
+ // alias foo "set x NEW; echo $x"
+ // actually expands to
+ // "set x NEW; echo OLD"
+ // and will print OLD! To work around this, use a second alias:
+ // alias foo "set x NEW; foo2"
+ // alias foo2 "echo $x"
+ //
+ // Also note: lines starting with alias are exempt from cvar expansion.
+ // If you want cvar expansion, write "alias" instead:
+ //
+ // set x 1
+ // alias foo "echo $x"
+ // "alias" bar "echo $x"
+ // set x 2
+ //
+ // foo will print 2, because the variable $x will be expanded when the alias
+ // gets expanded. bar will print 1, because the variable $x was expanded
+ // at definition time. foo can be equivalently defined as
+ //
+ // "alias" foo "echo $$x"
+ //
+ // because at definition time, $$ will get replaced to a single $.
+
+ if( *in == '$' ) {
+ val = "$";
+ eat = 1;
+ } else if(*in == '{') {
+ varlen = strcspn(in + 1, "}");
+ if(in[varlen + 1] == '}')
+ {
+ val = Cmd_GetCvarValue(in + 1, varlen, alias);
+ if(!val)
+ return false;
+ eat = varlen + 2;
+ }
+ else
+ {
+ // ran out of data?
+ val = NULL;
+ eat = varlen + 1;
+ }
+ } else {
+ varlen = strspn(in, "#*0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-");
+ val = Cmd_GetCvarValue(in, varlen, alias);
+ if(!val)
+ return false;
+ eat = varlen;