1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
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.
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.
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/>.
25 //**************************************************************************
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();
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 // ////////////////////////////////////////////////////////////////////////// //
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; }
69 memmove(ccmdBuf
, ccmdBuf
+cpos
, 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;
83 CCResult
ccmdParseOne () {
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; }
93 while (cpos
< ccmdBufUsed
&& ccmdBuf
[cpos
] != '\n') ++cpos
;
98 if (ch
== ';') { ++cpos
; continue; }
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; }
111 if (ch
== '\'' || ch
== '"') {
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
++];
122 case 't': tk
+= '\t'; break;
123 case 'n': tk
+= '\n'; break;
124 case 'r': tk
+= '\r'; break;
125 case 'e': tk
+= '\x1b'; break;
127 if (cpos
>= ccmdBufUsed
) break;
128 n
= hdig(ccmdBuf
[cpos
]);
131 if (cpos
< ccmdBufUsed
&& hdig(ccmdBuf
[cpos
]) >= 0) n
= n
*16+hdig(ccmdBuf
[cpos
++]);
142 while (cpos
< ccmdBufUsed
) {
143 ch
= (vuint8
)ccmdBuf
[cpos
];
144 if (ch
<= ' ' || ch
== '#' || ch
== ';') break;
149 if (ccmdArgc
< CCMD_MAX_ARGS
) ccmdArgv
[ccmdArgc
++] = tk
;
156 // ////////////////////////////////////////////////////////////////////////// //
157 static void ccmdPrependChar (char ch
) {
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
);
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
);
187 void ccmdPrepend (const VStr
&str
) {
188 if (str
.length()) ccmdPrepend(*str
);
192 __attribute__((format(printf
,1,2))) void ccmdPrependf (const char *fmt
, ...) {
196 snbuf
[sizeof(snbuf
)-1] = 0;
197 vsnprintf(snbuf
, sizeof(snbuf
), fmt
, ap
);
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) {
212 if (!needQuote
) { ccmdPrepend(str
); return; }
213 for (const vuint8
*s
= (const vuint8
*)str
; *s
; ++s
) {
216 snprintf(xbuf
, sizeof(xbuf
), "\\x%02x", *s
);
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
, ...) {
234 snbuf
[sizeof(snbuf
)-1] = 0;
235 vsnprintf(snbuf
, sizeof(snbuf
), fmt
, ap
);
237 ccmdPrependQuoted(snbuf
);
241 // ////////////////////////////////////////////////////////////////////////// //
242 static inline void ccmdAppendChar (char ch
) {
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
);
268 void ccmdAppend (const VStr
&str
) {
269 if (str
.length() > 0) ccmdAppend(*str
);
273 __attribute__((format(printf
,1,2))) void ccmdAppendf (const char *fmt
, ...) {
277 snbuf
[sizeof(snbuf
)-1] = 0;
278 vsnprintf(snbuf
, sizeof(snbuf
), fmt
, ap
);
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) {
293 if (!needQuote
) { ccmdAppend(str
); return; }
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) {
302 snprintf(xbuf
, sizeof(xbuf
), "\\x%02x", *s
);
306 if (*s
== '"' || *s
== '\\') ccmdAppendChar('\\');
307 ccmdAppendChar((char)*s
);
313 void ccmdAppendQuoted (const VStr
&str
) {
314 if (str
.length()) ccmdAppendQuoted(*str
);
317 __attribute__((format(printf
,1,2))) void ccmdAppendQuotedf (const char *fmt
, ...) {
321 snbuf
[sizeof(snbuf
)-1] = 0;
322 vsnprintf(snbuf
, sizeof(snbuf
), fmt
, 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 () {
339 cbufTail
== cbufHead
? cbufcursize
:
340 cbufTail
> cbufHead
? cbufTail
-cbufHead
:
341 cbufcursize
-cbufHead
+cbufTail
;
345 static char *cbufPos (vuint32 pos) {
347 pos >= textsize() ? nullptr :
348 pos < cbufcursize-cbufHead ? cbuf+cbufHead+pos :
349 cbuf+(pos-(cbufcursize-cbufHead));
353 static char cbufAt (vuint32 pos
) {
355 pos
>= textsize() ? '\0' :
356 *(pos
< cbufcursize
-cbufHead
? cbuf
+cbufHead
+pos
:
357 cbuf
+(pos
-(cbufcursize
-cbufHead
)));
360 static vuint32
getLastLineStart () {
361 vuint32 sz
= textsize();
363 if (cbufAt(sz
) == '\n') return sz
+1;
369 //==========================================================================
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
);
383 cbufcursize
= cbufmaxsize
;
387 if (doDump
) putc(ch
, stdout
);
388 auto lslen
= (textsize()-getLastLineStart())%8;
389 for (; lslen
< 8; ++lslen
) PutCharInternal(' ', doDump
);
393 if (ch
!= '\n' && (vuint8
)ch
< 32) ch
= ' ';
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') {
412 //FIXME: no newline, just clear it all
419 cbufHead
= (cbufHead
+1)%cbufcursize
;
421 cbuf
[cbufTail
++] = ch
;
422 cbufTail
%= cbufcursize
;
424 if (doDump
) putc(ch
, stdout
);
428 //==========================================================================
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 //==========================================================================
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 //==========================================================================
458 //==========================================================================
459 void conPutChar (char ch
) {
460 PutCharInternal(ch
, true);