+static const char *Cmd_GetDirectCvarValue(const char *varname, cmdalias_t *alias, qboolean *is_multiple)
+{
+ cvar_t *cvar;
+ long argno;
+ char *endptr;
+ char vabuf[1024];
+
+ if(is_multiple)
+ *is_multiple = false;
+
+ if(!varname || !*varname)
+ return NULL;
+
+ if(alias)
+ {
+ if(!strcmp(varname, "*"))
+ {
+ if(is_multiple)
+ *is_multiple = true;
+ return Cmd_Args();
+ }
+ else if(!strcmp(varname, "#"))
+ {
+ return va(vabuf, sizeof(vabuf), "%d", Cmd_Argc());
+ }
+ else if(varname[strlen(varname) - 1] == '-')
+ {
+ argno = strtol(varname, &endptr, 10);
+ if(endptr == varname + strlen(varname) - 1)
+ {
+ // whole string is a number, apart from the -
+ const char *p = Cmd_Args();
+ for(; argno > 1; --argno)
+ if(!COM_ParseToken_Console(&p))
+ break;
+ if(p)
+ {
+ if(is_multiple)
+ *is_multiple = true;
+
+ // kill pre-argument whitespace
+ for (;*p && ISWHITESPACE(*p);p++)
+ ;
+
+ return p;
+ }
+ }
+ }
+ else
+ {
+ argno = strtol(varname, &endptr, 10);
+ if(*endptr == 0)
+ {
+ // whole string is a number
+ // NOTE: we already made sure we don't have an empty cvar name!
+ if(argno >= 0 && argno < Cmd_Argc())
+ return Cmd_Argv(argno);
+ }
+ }
+ }
+
+ if((cvar = Cvar_FindVar(varname)) && !(cvar->flags & CVAR_PRIVATE))
+ return cvar->string;
+
+ return NULL;
+}
+
+qboolean Cmd_QuoteString(char *out, size_t outlen, const char *in, const char *quoteset, qboolean putquotes)
+{
+ qboolean quote_quot = !!strchr(quoteset, '"');
+ qboolean quote_backslash = !!strchr(quoteset, '\\');
+ qboolean quote_dollar = !!strchr(quoteset, '$');
+
+ if(putquotes)
+ {
+ if(outlen <= 2)
+ {
+ *out++ = 0;
+ return false;
+ }
+ *out++ = '"'; --outlen;
+ --outlen;
+ }
+
+ while(*in)
+ {
+ if(*in == '"' && quote_quot)
+ {
+ if(outlen <= 2)
+ goto fail;
+ *out++ = '\\'; --outlen;
+ *out++ = '"'; --outlen;
+ }
+ else if(*in == '\\' && quote_backslash)
+ {
+ if(outlen <= 2)
+ goto fail;
+ *out++ = '\\'; --outlen;
+ *out++ = '\\'; --outlen;
+ }
+ else if(*in == '$' && quote_dollar)
+ {
+ if(outlen <= 2)
+ goto fail;
+ *out++ = '$'; --outlen;
+ *out++ = '$'; --outlen;
+ }
+ else
+ {
+ if(outlen <= 1)
+ goto fail;
+ *out++ = *in; --outlen;
+ }
+ ++in;
+ }
+ if(putquotes)
+ *out++ = '"';
+ *out++ = 0;
+ return true;
+fail:
+ if(putquotes)
+ *out++ = '"';
+ *out++ = 0;
+ return false;
+}
+
+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;
+}
+