]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/lib/cvar.qh
Move expr_evaluate to cvar.qh in lib
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / cvar.qh
index e30de507ba2cdbea6148ed9d5093ed2d287c7129..d5b47ce43ee70bfdda9f758b3d2d36969bdd9ccb 100644 (file)
@@ -1,12 +1,26 @@
-#ifndef CVAR_H
-#define CVAR_H
+#pragma once
 
 #include "nil.qh"
+#include "progname.qh"
 #include "static.qh"
 
-void RegisterCvars(void(string name, string def, string desc, bool archive, string file) f) { }
+ERASEABLE
+void RegisterCvars(void(string name, string def, string desc, bool archive, string file) f) {}
+
+ERASEABLE
+bool cvar_value_issafe(string s)
+{
+       if (strstrofs(s, "\"", 0) >= 0) return false;
+       if (strstrofs(s, "\\", 0) >= 0) return false;
+       if (strstrofs(s, ";", 0) >= 0) return false;
+       if (strstrofs(s, "$", 0) >= 0) return false;
+       if (strstrofs(s, "\r", 0) >= 0) return false;
+       if (strstrofs(s, "\n", 0) >= 0) return false;
+       return true;
+}
 
 /** escape the string to make it safe for consoles */
+ERASEABLE
 string MakeConsoleSafe(string input)
 {
        input = strreplace("\n", "", input);
@@ -16,23 +30,102 @@ string MakeConsoleSafe(string input)
        return input;
 }
 
-void RegisterCvars_Set(string name, string def, string desc, bool archive, string file)
+/**
+ * Evaluate an expression of the form: [+ | -]? [var[op]val | [op]var | val | var] ...
+ * +: all must match. this is the default
+ * -: one must NOT match
+ *
+ * var>x
+ * var<x
+ * var>=x
+ * var<=x
+ * var==x
+ * var!=x
+ * var===x
+ * var!==x
+ */
+ERASEABLE
+bool expr_evaluate(string s)
 {
-    string val = string_null;
-    if (cvar_type(name) & CVAR_TYPEFLAG_EXISTS) {
-        val = cvar_string(name);
-        // Need to unset first to change the default
-        localcmd(sprintf("\nunset %s\n", name));
+    bool ret = false;
+    if (str2chr(s, 0) == '+') {
+        s = substring(s, 1, -1);
+    } else if (str2chr(s, 0) == '-') {
+        ret = true;
+        s = substring(s, 1, -1);
     }
-    localcmd(sprintf("\n%s %s \"%s\" \"%s\"\n", (archive ? "seta" : "set"), name, MakeConsoleSafe(def), MakeConsoleSafe(desc)));
-    if (val) {
-        localcmd(sprintf("\n%s \"%s\"\n", name, MakeConsoleSafe(val)));
+    bool expr_fail = false;
+    for (int i = 0, n = tokenize_console(s); i < n; ++i) {
+        int o;
+        string k, v;
+        s = argv(i);
+        #define X(expr) \
+            if (expr) \
+                continue; \
+            expr_fail = true; \
+            break;
+
+        #define BINOP(op, len, expr) \
+            if ((o = strstrofs(s, op, 0)) >= 0) { \
+                k = substring(s, 0, o); \
+                v = substring(s, o + len, -1); \
+                X(expr); \
+            }
+        BINOP(">=", 2, cvar(k) >= stof(v));
+        BINOP("<=", 2, cvar(k) <= stof(v));
+        BINOP(">",  1, cvar(k) >  stof(v));
+        BINOP("<",  1, cvar(k) <  stof(v));
+        BINOP("==", 2, cvar(k) == stof(v));
+        BINOP("!=", 2, cvar(k) != stof(v));
+        BINOP("===", 3, cvar_string(k) == v);
+        BINOP("!==", 3, cvar_string(k) != v);
+        {
+            k = s;
+            bool b = true;
+            if (str2chr(k, 0) == '!') {
+                k = substring(s, 1, -1);
+                b = false;
+            }
+            float f = stof(k);
+            bool isnum = ftos(f) == k;
+            X(boolean(isnum ? f : cvar(k)) == b);
+        }
+        #undef BINOP
+        #undef X
     }
+    if (!expr_fail) {
+        ret = !ret;
+    }
+    // now ret is true if we want to keep the item, and false if we want to get rid of it
+    return ret;
 }
 
-#ifndef SVQC
-STATIC_INIT_LATE(Cvars) { RegisterCvars(RegisterCvars_Set); }
-#endif
+ERASEABLE
+void RegisterCvars_Set(string name, string def, string desc, bool archive, string file)
+{
+       localcmd(sprintf("\nset %1$s \"$%1$s\" \"%2$s\"\n", name, MakeConsoleSafe(desc)));
+       if (archive)
+               localcmd(sprintf("\nseta %1$s \"$%1$s\"\n", name));
+}
+
+int RegisterCvars_Save_fd;
+ERASEABLE
+void RegisterCvars_Save(string name, string def, string desc, bool archive, string file)
+{
+       if (!archive) return;
+       fputs(RegisterCvars_Save_fd, sprintf("seta %s \"%s\"\n", name, def));
+}
+
+STATIC_INIT_LATE(Cvars)
+{
+       RegisterCvars(RegisterCvars_Set);
+       RegisterCvars_Save_fd = fopen(sprintf("default%s.cfg", PROGNAME), FILE_WRITE);
+       if (RegisterCvars_Save_fd >= 0)
+       {
+               RegisterCvars(RegisterCvars_Save);
+               fclose(RegisterCvars_Save_fd);
+       }
+}
 
 const noref bool default_bool = false;
 const noref int default_int = 0;
@@ -40,21 +133,29 @@ const noref float default_float = 0;
 const noref string default_string = "";
 const noref vector default_vector = '0 0 0';
 
-#define repr_cvar_bool(x)   ((x) ? "1" : "0")
-#define repr_cvar_int(x)    (ftos(x))
-#define repr_cvar_float(x)  (ftos(x))
+#define repr_cvar_bool(x) ((x) ? "1" : "0")
+#define repr_cvar_int(x) (ftos(x))
+#define repr_cvar_float(x) (ftos(x))
 #define repr_cvar_string(x) (x)
 #define repr_cvar_vector(x) (sprintf("%v", x))
 
+//pseudo prototypes:
+// void AUTOCVAR(<cvar_name>, <qc_var_type>, default_cvar_value, string desc)
+// void AUTOCVAR_SAVE(<cvar_name>, <qc_var_type>, default_cvar_value, string desc)
+//  where default_cvar_value has type <qc_var_type>
+//  e.g.: AUTOCVAR(mycvar, float, 2.5, "cvar description")
+
 #define __AUTOCVAR(file, archive, var, type, desc, default) \
-    [[accumulate]] void RegisterCvars(void(string, string, string, bool, string) f) { f(#var, repr_cvar_##type(default), desc, archive, file); } \
-    type autocvar_##var = default
+       ACCUMULATE void RegisterCvars(void(string, string, string, bool, string) f) \
+       { \
+               f( #var, repr_cvar_##type(default), desc, archive, file); \
+       } \
+       type autocvar_##var = default
 #define AUTOCVAR_5(file, archive, var, type, desc) \
-    __AUTOCVAR(file, archive, var, type, desc, default_##type)
+       __AUTOCVAR(file, archive, var, type, desc, default_##type)
 #define AUTOCVAR_6(file, archive, var, type, default, desc) \
-    __AUTOCVAR(file, archive, var, type, desc, default)
-#define _AUTOCVAR(...) EVAL(OVERLOAD(AUTOCVAR, __FILE__, __VA_ARGS__))
+       __AUTOCVAR(file, archive, var, type, desc, default)
+#define _AUTOCVAR(...) EVAL__AUTOCVAR(OVERLOAD(AUTOCVAR, __FILE__, __VA_ARGS__))
+#define EVAL__AUTOCVAR(...) __VA_ARGS__
 #define AUTOCVAR_SAVE(...) _AUTOCVAR(true, __VA_ARGS__)
 #define AUTOCVAR(...) _AUTOCVAR(false, __VA_ARGS__)
-
-#endif