script, ui: added "sv_min_startmap_health" cvar
[k8vavoom.git] / vccrun / convars.cpp
blob670d4647d2220449693822499178780aca2781f9
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 #include "convars.h"
29 // ////////////////////////////////////////////////////////////////////////// //
30 static char *ccmdBuf = nullptr;
31 static size_t ccmdBufSize = 0;
32 static size_t ccmdBufUsed = 0;
33 static char snbuf[32768];
35 #define CCMD_MAX_ARGS (256)
37 static VStr ccmdArgv[CCMD_MAX_ARGS];
38 static int ccmdArgc = 0; // current argc
41 void ccmdClearText () { ccmdBufUsed = 0; }
43 void ccmdClearCommand () {
44 for (int f = ccmdArgc-1; f >= 0; --f) ccmdArgv[f].clear();
45 ccmdArgc = 0;
49 int ccmdGetArgc () { return ccmdArgc; }
50 const VStr &ccmdGetArgv (int idx) { return (idx >= 0 && idx < ccmdArgc ? ccmdArgv[idx] : VStr::EmptyString); }
52 // return number of unparsed bytes left in
53 int ccmdTextSize () { return (int)ccmdBufUsed; }
56 // ////////////////////////////////////////////////////////////////////////// //
57 // parse one command
59 enum CCResult {
60 CCMD_EMPTY = -1, // no more commands (nothing was parsed)
61 CCMD_NORMAL = 0, // one command parsed, line is not complete
62 CCMD_EOL = 1, // one command parsed, line is complete
66 static inline void ccmdShrink (size_t cpos) {
67 if (cpos >= ccmdBufUsed) { ccmdBufUsed = 0; return; }
68 if (cpos > 0) {
69 memmove(ccmdBuf, ccmdBuf+cpos, ccmdBufUsed-cpos);
70 ccmdBufUsed -= cpos;
75 static inline int hdig (char ch) {
76 if (ch >= '0' && ch <= '9') return ch-'0';
77 if (ch >= 'A' && ch <= 'F') return ch-'A'+10;
78 if (ch >= 'a' && ch <= 'f') return ch-'a'+10;
79 return -1;
83 CCResult ccmdParseOne () {
84 ccmdClearCommand();
85 size_t cpos = 0;
86 // find command start
87 while (cpos < ccmdBufUsed) {
88 vuint8 ch = (vuint8)ccmdBuf[cpos];
89 if (ch == '\n') { ++cpos; ccmdShrink(cpos); return CCMD_EOL; }
90 if (ch <= ' ') { ++cpos; continue; }
91 if (ch == '#') {
92 // comment
93 while (cpos < ccmdBufUsed && ccmdBuf[cpos] != '\n') ++cpos;
94 ++cpos;
95 ccmdShrink(cpos);
96 return CCMD_EOL;
98 if (ch == ';') { ++cpos; continue; }
99 break;
101 if (cpos >= ccmdBufUsed) { ccmdBufUsed = 0; return CCMD_EMPTY; }
102 // found something; parse it
103 while (cpos < ccmdBufUsed) {
104 vuint8 ch = (vuint8)ccmdBuf[cpos];
105 if (ch == '\n' || ch == '#') break; // end-of-command
106 if (ch == ';') { ++cpos; break; }
107 if (ch <= ' ') { ++cpos; continue; }
108 VStr tk;
109 int n;
110 // found a token
111 if (ch == '\'' || ch == '"') {
112 // quoted token
113 vuint8 qch = ch;
114 ++cpos;
115 while (cpos < ccmdBufUsed) {
116 ch = (vuint8)ccmdBuf[cpos++];
117 if (ch == qch) break;
118 if (ch != '\\') { tk += (char)ch; continue; }
119 if (cpos >= ccmdBufUsed) break;
120 ch = (vuint8)ccmdBuf[cpos++];
121 switch (ch) {
122 case 't': tk += '\t'; break;
123 case 'n': tk += '\n'; break;
124 case 'r': tk += '\r'; break;
125 case 'e': tk += '\x1b'; break;
126 case 'x':
127 if (cpos >= ccmdBufUsed) break;
128 n = hdig(ccmdBuf[cpos]);
129 if (n < 0) break;
130 ++cpos;
131 if (cpos < ccmdBufUsed && hdig(ccmdBuf[cpos]) >= 0) n = n*16+hdig(ccmdBuf[cpos++]);
132 if (n == 0) n = 32;
133 tk += (char)n;
134 break;
135 default:
136 tk += (char)ch;
137 break;
140 } else {
141 // space-delimited
142 while (cpos < ccmdBufUsed) {
143 ch = (vuint8)ccmdBuf[cpos];
144 if (ch <= ' ' || ch == '#' || ch == ';') break;
145 tk += (char)ch;
146 ++cpos;
149 if (ccmdArgc < CCMD_MAX_ARGS) ccmdArgv[ccmdArgc++] = tk;
151 ccmdShrink(cpos);
152 return CCMD_NORMAL;
156 // ////////////////////////////////////////////////////////////////////////// //
157 static void ccmdPrependChar (char ch) {
158 if (!ch) return;
159 if (ccmdBufUsed >= 1024*1024*32) Sys_Error("Command buffer overflow!");
160 if (ccmdBufUsed+1 > ccmdBufSize) {
161 size_t newsize = ((ccmdBufUsed+1)|0xffffU)+1;
162 ccmdBuf = (char *)Z_Realloc(ccmdBuf, newsize);
163 ccmdBufSize = newsize;
165 if (ccmdBufUsed) memmove(ccmdBuf+1, ccmdBuf, ccmdBufUsed);
166 ccmdBuf[0] = ch;
167 ++ccmdBufUsed;
171 void ccmdPrepend (const char *str) {
172 if (!str || !str[0]) return;
173 size_t slen = strlen(str);
174 if (slen > 1024*1024*32 || ccmdBufUsed+slen > 1024*1024*32) Sys_Error("Command buffer overflow!");
175 if (ccmdBufUsed+slen > ccmdBufSize) {
176 size_t newsize = ((ccmdBufUsed+slen)|0xffffU)+1;
177 ccmdBuf = (char *)Z_Realloc(ccmdBuf, newsize);
178 if (!ccmdBuf) Sys_Error("Out of memory for command buffer!");
179 ccmdBufSize = newsize;
181 if (ccmdBufUsed) memmove(ccmdBuf+slen, ccmdBuf, ccmdBufUsed);
182 memcpy(ccmdBuf, str, slen);
183 ccmdBufUsed += slen;
187 void ccmdPrepend (const VStr &str) {
188 if (str.length()) ccmdPrepend(*str);
192 __attribute__((format(printf,1,2))) void ccmdPrependf (const char *fmt, ...) {
193 va_list ap;
194 va_start(ap, fmt);
195 snbuf[0] = 0;
196 snbuf[sizeof(snbuf)-1] = 0;
197 vsnprintf(snbuf, sizeof(snbuf), fmt, ap);
198 va_end(ap);
199 ccmdPrepend(snbuf);
203 void ccmdPrependQuoted (const char *str) {
204 if (!str || !str[0]) return;
205 bool needQuote = false;
206 for (const vuint8 *s = (const vuint8 *)str; *s; ++s) {
207 if (*s <= ' ' || *s == '\'' || *s == '"' || *s == '\\' || *s == 127) {
208 needQuote = true;
209 break;
212 if (!needQuote) { ccmdPrepend(str); return; }
213 for (const vuint8 *s = (const vuint8 *)str; *s; ++s) {
214 if (*s < ' ') {
215 char xbuf[6];
216 snprintf(xbuf, sizeof(xbuf), "\\x%02x", *s);
217 ccmdPrepend(xbuf);
218 continue;
220 if (*s == ' ' || *s == '\'' || *s == '"' || *s == '\\' || *s == 127) ccmdPrependChar('\\');
221 ccmdPrependChar((char)*s);
226 void ccmdPrependQuoted (const VStr &str) {
227 if (str.length()) ccmdPrependQuoted(*str);
230 __attribute__((format(printf,1,2))) void ccmdPrependQuotdedf (const char *fmt, ...) {
231 va_list ap;
232 va_start(ap, fmt);
233 snbuf[0] = 0;
234 snbuf[sizeof(snbuf)-1] = 0;
235 vsnprintf(snbuf, sizeof(snbuf), fmt, ap);
236 va_end(ap);
237 ccmdPrependQuoted(snbuf);
241 // ////////////////////////////////////////////////////////////////////////// //
242 static inline void ccmdAppendChar (char ch) {
243 if (!ch) return;
244 if (ccmdBufUsed >= 1024*1024*32) Sys_Error("Command buffer overflow!");
245 if (ccmdBufUsed+1 > ccmdBufSize) {
246 size_t newsize = ((ccmdBufUsed+1)|0xffffU)+1;
247 ccmdBuf = (char *)Z_Realloc(ccmdBuf, newsize);
248 ccmdBufSize = newsize;
250 ccmdBuf[ccmdBufUsed++] = ch;
254 void ccmdAppend (const char *str) {
255 if (!str || !str[0]) return;
256 size_t slen = strlen(str);
257 if (slen > 1024*1024*32 || ccmdBufUsed+slen > 1024*1024*32) Sys_Error("Command buffer overflow!");
258 if (ccmdBufUsed+slen > ccmdBufSize) {
259 size_t newsize = ((ccmdBufUsed+slen)|0xffffU)+1;
260 ccmdBuf = (char *)Z_Realloc(ccmdBuf, newsize);
261 ccmdBufSize = newsize;
263 memcpy(ccmdBuf+ccmdBufUsed, str, slen);
264 ccmdBufUsed += slen;
268 void ccmdAppend (const VStr &str) {
269 if (str.length() > 0) ccmdAppend(*str);
273 __attribute__((format(printf,1,2))) void ccmdAppendf (const char *fmt, ...) {
274 va_list ap;
275 va_start(ap, fmt);
276 snbuf[0] = 0;
277 snbuf[sizeof(snbuf)-1] = 0;
278 vsnprintf(snbuf, sizeof(snbuf), fmt, ap);
279 va_end(ap);
280 ccmdAppend(snbuf);
284 void ccmdAppendQuoted (const char *str) {
285 if (!str || !str[0]) return;
286 bool needQuote = false;
287 for (const vuint8 *s = (const vuint8 *)str; *s; ++s) {
288 if (*s < ' ' || *s == '"' || *s == '\\' || *s == 127) {
289 needQuote = true;
290 break;
293 if (!needQuote) { ccmdAppend(str); return; }
294 ccmdAppendChar('"');
295 for (const vuint8 *s = (const vuint8 *)str; *s; ++s) {
296 if (*s == '\t') { ccmdAppend("\\t"); continue; }
297 if (*s == '\n') { ccmdAppend("\\n"); continue; }
298 if (*s == '\r') { ccmdAppend("\\r"); continue; }
299 if (*s == 0x1b) { ccmdAppend("\\e"); continue; }
300 if (*s < ' ' || *s == 127) {
301 char xbuf[6];
302 snprintf(xbuf, sizeof(xbuf), "\\x%02x", *s);
303 ccmdAppend(xbuf);
304 continue;
306 if (*s == '"' || *s == '\\') ccmdAppendChar('\\');
307 ccmdAppendChar((char)*s);
309 ccmdAppendChar('"');
313 void ccmdAppendQuoted (const VStr &str) {
314 if (str.length()) ccmdAppendQuoted(*str);
317 __attribute__((format(printf,1,2))) void ccmdAppendQuotedf (const char *fmt, ...) {
318 va_list ap;
319 va_start(ap, fmt);
320 snbuf[0] = 0;
321 snbuf[sizeof(snbuf)-1] = 0;
322 vsnprintf(snbuf, sizeof(snbuf), fmt, ap);
323 va_end(ap);
324 ccmdAppendQuoted(snbuf);
328 // ////////////////////////////////////////////////////////////////////////// //
329 static char *cbuf = nullptr;
330 static vuint32 cbufHead = 0;
331 static vuint32 cbufTail = 0; // `cbuftail` points *at* last char
332 //static bool cbufLastWasCR = false;
333 static vuint32 cbufcursize = 0;
334 static vuint32 cbufmaxsize = 256*1024;
337 static inline vuint32 textsize () {
338 return
339 cbufTail == cbufHead ? cbufcursize :
340 cbufTail > cbufHead ? cbufTail-cbufHead :
341 cbufcursize-cbufHead+cbufTail;
345 static char *cbufPos (vuint32 pos) {
346 return
347 pos >= textsize() ? nullptr :
348 pos < cbufcursize-cbufHead ? cbuf+cbufHead+pos :
349 cbuf+(pos-(cbufcursize-cbufHead));
353 static char cbufAt (vuint32 pos) {
354 return
355 pos >= textsize() ? '\0' :
356 *(pos < cbufcursize-cbufHead ? cbuf+cbufHead+pos :
357 cbuf+(pos-(cbufcursize-cbufHead)));
360 static vuint32 getLastLineStart () {
361 vuint32 sz = textsize();
362 while (sz--) {
363 if (cbufAt(sz) == '\n') return sz+1;
365 return 0;
369 //==========================================================================
371 // PutCharInternal
373 //==========================================================================
374 static void PutCharInternal (char ch, bool doDump) {
375 if (ch == '\r') return;
377 if (cbufcursize != cbufmaxsize) {
378 cbuf = (char *)Z_Realloc(cbuf, cbufmaxsize);
379 //FIXME
380 cbufHead = 0;
381 cbufTail = 1;
382 cbuf[0] = '\n';
383 cbufcursize = cbufmaxsize;
386 if (ch == '\t') {
387 if (doDump) putc(ch, stdout);
388 auto lslen = (textsize()-getLastLineStart())%8;
389 for (; lslen < 8; ++lslen) PutCharInternal(' ', doDump);
390 return;
393 if (ch != '\n' && (vuint8)ch < 32) ch = ' ';
395 // has room?
396 if (cbufTail == cbufHead) {
397 // oops, no room; remove top line then
399 bool foundNL = false;
400 vuint32 cbsz = cbufcursize;
401 vuint32 cbh = cbufHead;
402 cbh = (cbh+cbsz-1)%cbsz; // one char back, unconditionally
403 for (vuint32 cnt = cbsz-1; cnt > 0; --cnt) {
404 cbh = (cbh+cbsz-1)%cbsz;
405 if (cncbuf[cbh] == '\n') {
406 cbufHead = cbh;
407 foundNL = true;
408 break;
411 if (!foundNL) {
412 //FIXME: no newline, just clear it all
413 cbufHead = 0;
414 cbufTail = 1;
415 cbuf[0] = ch;
416 return;
419 cbufHead = (cbufHead+1)%cbufcursize;
421 cbuf[cbufTail++] = ch;
422 cbufTail %= cbufcursize;
424 if (doDump) putc(ch, stdout);
428 //==========================================================================
430 // conWriteStr
432 //==========================================================================
433 void conWriteStr (const VStr &str) {
434 if (!str.isEmpty()) {
435 fprintf(stdout, "%.*s", (vuint32)str.length(), *str);
436 const char *strd = *str;
437 int slen = str.length();
438 while (slen--) PutCharInternal(*strd++, false);
443 //==========================================================================
445 // conWriteStr
447 //==========================================================================
448 void conWriteStr (const char *str, size_t strlen) {
449 if (strlen) fprintf(stdout, "%.*s", (vuint32)strlen, str);
450 while (strlen--) PutCharInternal(*str++, false);
454 //==========================================================================
456 // conPutChar
458 //==========================================================================
459 void conPutChar (char ch) {
460 PutCharInternal(ch, true);