From 557846b22057b7a4c232da3cfcb000ccf128c364 Mon Sep 17 00:00:00 2001 From: Brian Koropoff Date: Sun, 3 Mar 2002 03:36:27 +0000 Subject: [PATCH] This is the first commit to the newcmd branch, which is a mostly-rewritten console command parser. It will eventually include html-like tags for modifying text (gold numbers, brown characters, etc) and escaped characters. The major differences so far are that dynamic strings are now used instead of static buffers, and single quotes can be used to enclose tokens as well as double quotes. --- include/QF/cmd.h | 1 - include/QF/dstring.h | 20 +++ libs/util/Makefile.am | 2 +- libs/util/cmd.c | 349 +++++++++++++++++++----------------------- libs/util/dstring.c | 113 ++++++++++++++ 5 files changed, 289 insertions(+), 196 deletions(-) create mode 100644 include/QF/dstring.h create mode 100644 libs/util/dstring.c diff --git a/include/QF/cmd.h b/include/QF/cmd.h index ccf98f040..d898e40f0 100644 --- a/include/QF/cmd.h +++ b/include/QF/cmd.h @@ -45,7 +45,6 @@ The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute (); */ - void Cbuf_Init (void); // allocates an initial text buffer that will grow as needed diff --git a/include/QF/dstring.h b/include/QF/dstring.h new file mode 100644 index 000000000..91a7da5cc --- /dev/null +++ b/include/QF/dstring.h @@ -0,0 +1,20 @@ +typedef struct dstring_s { + unsigned long int size, truesize; + char *str; +} dstring_t; + + +// General buffer functions +dstring_t *dstring_new(void); +void dstring_delete (dstring_t *dstr); +void dstring_adjust(dstring_t *dstr); +void dstring_append (dstring_t *dstr, const char *data, unsigned int len); +void dstring_insert(dstring_t *dstr, const char *data, unsigned int len, unsigned int pos); +void dstring_snip (dstring_t *dstr, unsigned int pos, unsigned int len); +void dstring_clear (dstring_t *dstr); + +// String-specific functions +dstring_t *dstring_newstr (void); +void dstring_appendstr (dstring_t *dstr, const char *str); +void dstring_insertstr (dstring_t *dstr, const char *str, unsigned int pos); +void dstring_clearstr (dstring_t *dstr); diff --git a/libs/util/Makefile.am b/libs/util/Makefile.am index a56f9ad10..628e6e5ce 100644 --- a/libs/util/Makefile.am +++ b/libs/util/Makefile.am @@ -29,6 +29,6 @@ libQFutil_la_SOURCES= \ checksum.c cmd.c crc.c cvar.c fendian.c getopt.c getopt1.c hash.c \ info.c link.c mathlib.c mdfour.c msg.c pcx.c plugin.c qargs.c \ qendian.c qfplist.c quakefs.c quakeio.c sizebuf.c string.c sys.c \ - tga.c va.c ver_check.c wad.c zone.c $(fnmatch) + tga.c va.c ver_check.c wad.c zone.c dstring.c $(fnmatch) EXTRA_DIST= $(asm_src) $(fnmatch_src) diff --git a/libs/util/cmd.c b/libs/util/cmd.c index c2d1538f1..97221d1e7 100644 --- a/libs/util/cmd.c +++ b/libs/util/cmd.c @@ -48,8 +48,8 @@ static const char rcsid[] = #include "QF/sys.h" #include "QF/vfs.h" #include "QF/zone.h" - #include "compat.h" +#include "QF/dstring.h" typedef struct cmdalias_s { struct cmdalias_s *next; @@ -59,7 +59,13 @@ typedef struct cmdalias_s { cmdalias_t *cmd_alias; cmd_source_t cmd_source; -qboolean cmd_wait; +dstring_t *cmd_buffer; +qboolean cmd_wait = false; +int cmd_argc; +int cmd_maxargc = 0; +dstring_t **cmd_argv = 0; +const char **cmd_args = 0; + cvar_t *cmd_warncmd; cvar_t *cmd_highchars; @@ -86,139 +92,78 @@ Cmd_Wait_f (void) COMMAND BUFFER */ -sizebuf_t cmd_text; byte cmd_text_buf[8192]; -void -Cbuf_Init (void) -{ - cmd_text.data = cmd_text_buf; - cmd_text.maxsize = sizeof (cmd_text_buf); +void Cbuf_Init (void) { + + cmd_buffer = dstring_newstr (); } -/* - Cbuf_AddText - Adds command text at the end of the buffer -*/ -void -Cbuf_AddText (const char *text) -{ - int l; +void Cbuf_AddText (const char *text) { - l = strlen (text); - - if (cmd_text.cursize + l < cmd_text.maxsize) { - SZ_Write (&cmd_text, text, l); - return; - } - Sys_Printf ("Cbuf_AddText: overflow\n"); + dstring_appendstr (cmd_buffer, text); } -/* - Cbuf_InsertText - - Adds command text to the beginning of the buffer. - Adds a \n to the text -*/ -void -Cbuf_InsertText (const char *text) -{ - int textlen; - - textlen = strlen (text); - if (cmd_text.cursize + 1 + textlen >= cmd_text.maxsize) { - Sys_Printf ("Cbuf_InsertText: overflow\n"); - return; - } - - if (!cmd_text.cursize) { - memcpy (cmd_text.data, text, textlen); - cmd_text.cursize = textlen; - return; - } - // Move up to make room for inserted text - memmove (cmd_text.data + textlen + 1, cmd_text.data, cmd_text.cursize); - cmd_text.cursize += textlen + 1; - - // Insert new text - memcpy (cmd_text.data, text, textlen); - cmd_text.data[textlen] = '\n'; +void Cbuf_InsertText (const char *text) { + dstring_insertstr (cmd_buffer, "\n", 0); + dstring_insertstr (cmd_buffer, text, 0); } -static void -extract_line (char *line) -{ - int i; - char *text; - int quotes; - - // find a \n or ; line break - text = (char *) cmd_text.data; - quotes = 0; - for (i = 0; i < cmd_text.cursize; i++) { - if (text[i] == '"') - quotes++; - if (!(quotes & 1) && text[i] == ';') - break; // don't break if inside a quoted - // string +void extract_line (dstring_t *buffer) { + int i, squotes = 0, dquotes = 0; + char *text = cmd_buffer->str; + + for (i = 0; text[i]; i++) { + if (text[i] == '\'' && !dquotes) + squotes^=1; + if (text[i] == '"' && !squotes) + dquotes^=1; + if (text[i] == ';' && !squotes && !dquotes) + break; if (text[i] == '\n' || text[i] == '\r') break; } - - memcpy (line, text, i); - line[i] = '\0'; - // delete the text from the command buffer and move remaining commands - // down this is necessary because commands (exec, alias) can insert - // data at the beginning of the text buffer - - if (i == cmd_text.cursize) - cmd_text.cursize = 0; - else { - i++; - cmd_text.cursize -= i; - memcpy (text, text + i, cmd_text.cursize); - } + if (i) + dstring_insert (buffer, cmd_buffer->str, i, 0); + if (text[i]) + dstring_snip (cmd_buffer, 0, i + 1); + else // We've hit the end of the buffer, just clear it + dstring_clearstr (cmd_buffer); } -void -Cbuf_Execute (void) -{ - char line[1024]; - - while (cmd_text.cursize) { - extract_line (line); - // execute the command line - // Sys_DPrintf("+%s\n",line), - Cmd_ExecuteString (line, src_command); - - if (cmd_wait) { // skip out while text still remains - // in buffer, leaving it - // for next frame +void Cbuf_Execute (void) { + dstring_t *buf = dstring_newstr (); + + while (strlen(cmd_buffer->str)) { + extract_line (buf); + Cmd_ExecuteString(buf->str, cmd_source); + if (cmd_wait) { cmd_wait = false; break; } + dstring_clearstr(buf); } + dstring_delete (buf); } -void -Cbuf_Execute_Sets (void) +void Cbuf_Execute_Sets (void) { - char line[1024]; - - while (cmd_text.cursize) { - extract_line (line); - // execute the command line - if (strnequal (line, "set", 3) && isspace ((int) line[3])) { - // Sys_DPrintf ("+%s\n",line); - Cmd_ExecuteString (line, src_command); - } else if (strnequal (line, "setrom", 6) && isspace ((int) line[6])) { - // Sys_DPrintf ("+%s\n",line); - Cmd_ExecuteString (line, src_command); + dstring_t *buf = dstring_newstr (); + + while (strlen(cmd_buffer->str)) { + extract_line (buf); + if (!strncmp (buf->str, "set", 3) && isspace ((int) buf->str[3])) { + Cmd_ExecuteString (buf->str, cmd_source); + } else if (!strncmp (buf->str, "setrom", 6) && isspace ((int) buf->str[6])) { + Cmd_ExecuteString (buf->str, cmd_source); } + dstring_clearstr(buf); } + dstring_delete (buf); } + /* SCRIPT COMMANDS */ @@ -431,14 +376,6 @@ typedef struct cmd_function_s { const char *description; } cmd_function_t; - -#define MAX_ARGS 80 - -static int cmd_argc; -static char *cmd_argv[MAX_ARGS]; -static const char *cmd_null_string = ""; -static const char *cmd_args[MAX_ARGS]; - static cmd_function_t *cmd_functions; // possible commands to execute int @@ -451,8 +388,8 @@ const char * Cmd_Argv (int arg) { if (arg >= cmd_argc) - return cmd_null_string; - return cmd_argv[arg]; + return ""; + return cmd_argv[arg]->str; } /* @@ -468,66 +405,109 @@ Cmd_Args (int start) return cmd_args[start]; } -/* - Cmd_TokenizeString - - Parses the given string into command line tokens. -*/ -void -Cmd_TokenizeString (const char *text) -{ - static char *argv_buf; - static size_t argv_buf_size; - int argv_idx; - - if (!argv_buf_size) { - argv_buf_size = 1024; //FIXME dynamic - argv_buf = malloc (argv_buf_size); - if (!argv_buf) - Sys_Error ("Cmd_TokenizeString: could not allocate %ld bytes", - (long)argv_buf_size); - } - - argv_idx = 0; - - cmd_argc = 0; - memset (cmd_args, 0, sizeof (cmd_args)); - - while (1) { - // skip whitespace up to a \n - while (*text && *(unsigned char *) text <= ' ' && *text != '\n') { - text++; +int Cmd_GetToken (const char *str) { + int i, squote, dquote; + + for (i = 0, squote = 0, dquote = 0; i <= strlen(str); i++) { + if (str[i] == 0) { + if (dquote) { // We never found another quote, backtrack + for (; str[i] != '"'; i--); + dquote = 0; + continue; + } + else if (squote) { + for (; str[i] != '\''; i--); + squote = 0; + continue; + } + else // End of string, this token is done + break; } - - if (*text == '\n') { // a newline seperates commands in - // the buffer - text++; + else if (str[i] == '\'' && !dquote) { + if (i) // If not at start of string, we must be at the end of a token + break; + squote = 1; + } + else if (str[i] == '"' && !squote) { + if (i) // Start new token + break; + dquote = 1; + } + else if (isspace(str[i]) && !dquote && !squote) { break; } + } + return i; +} - if (!*text) - return; +int tag_gold = 0; +int tag_shift = 0; - if (cmd_argc < MAX_ARGS) - cmd_args[cmd_argc] = text; - - text = COM_Parse (text); - if (!text) - return; - - if (cmd_argc < MAX_ARGS) { - size_t len = strlen (com_token) + 1; - - if (argv_idx + len > argv_buf_size) { - Sys_Printf ("Cmd_TokenizeString: overflow\n"); - return; - } - cmd_argv[cmd_argc] = argv_buf + argv_idx; - strcpy (cmd_argv[cmd_argc], com_token); - argv_idx += len; - - cmd_argc++; +void Cmd_ProcessTags (dstring_t *dstr) { + int close = 0, i, n, c; + char *str = dstr->str; + + for (i = 0; i < strlen(str); i++) { + if (str[i] == '<') { + close = 0; + for (n = 0; str[i+n] != '>'; n++) + if (str[n] == 0) + return; + if (str[i+1] == '/') + close = 1; + if (!strncmp(str+i+close+1, "gold", 4)) + tag_gold = tag_gold > 0 && close ? tag_gold - 1 : tag_gold + 1; + if (!strncmp(str+i+close+1, "shift", 5)) + tag_shift = tag_shift > 0 && close ? tag_shift - 1 : tag_shift + 1; + dstring_snip(dstr, i, n+1); + i--; + continue; } + c = str[i]; + + if (tag_gold && c >='0' && c <= '9') + c = (str[i] += (146 - '0')); + if (tag_shift && c < 128) + c = (str[i] += 128); + } +} + +void Cmd_TokenizeString (const char *text) { + int i = 0, len = 0, quotes = 0; + const char *str = text; + + cmd_argc = 0; + tag_shift = 0; + tag_gold = 0; + while (strlen(str + i)) { + while (isspace(str[i])) + i++; + len = Cmd_GetToken (str + i); + if (!len) + return; + cmd_argc++; + if (cmd_argc > cmd_maxargc) { + cmd_argv = realloc(cmd_argv, sizeof(dstring_t *)*cmd_argc); + if (!cmd_argv) + Sys_Error ("Cmd_TokenizeString: Failed to reallocate memory.\n"); + cmd_args = realloc(cmd_args, sizeof(char *)*cmd_argc); + if (!cmd_args) + Sys_Error ("Cmd_TokenizeString: Failed to reallocate memory.\n"); + cmd_argv[cmd_argc-1] = dstring_newstr (); + cmd_maxargc++; + } + dstring_clearstr(cmd_argv[cmd_argc-1]); + /* Remove surrounding quotes or double quotes */ + cmd_args[cmd_argc-1] = str+i; + quotes = 0; + if ((str[i] == '\'' && str[i+len] == '\'') || (str[i] == '"' && str[i+len] == '"')) { + i++; + len-=1; + quotes = 1; + } + dstring_insert(cmd_argv[cmd_argc-1], str + i, len, 0); + Cmd_ProcessTags(cmd_argv[cmd_argc-1]); + i += len + quotes; /* If we ended on a quote, skip it */ } } @@ -834,35 +814,16 @@ Cmd_ExecuteString (const char *text, cmd_source_t src) { cmd_function_t *cmd; cmdalias_t *a; - char buf[1024]; - cmd_source = src; - -#if 0 Cmd_TokenizeString (text); -#else - if (cmd_highchars->value) { - char buf2[1024]; - - strncpy(buf, text, sizeof(buf) - 1); - buf[sizeof(buf) - 1] = 0; - Cmd_ParseSpecial (buf); - Cmd_ExpandVariables (buf, buf2); - Cmd_TokenizeString (buf2); - } - else { - Cmd_ExpandVariables (text, buf); - Cmd_TokenizeString (buf); - } -#endif // execute the command line if (!Cmd_Argc ()) return; // no tokens // check functions - cmd = (cmd_function_t*)Hash_Find (cmd_hash, cmd_argv[0]); + cmd = (cmd_function_t*)Hash_Find (cmd_hash, cmd_argv[0]->str); if (cmd) { if (cmd->function) cmd->function (); @@ -874,7 +835,7 @@ Cmd_ExecuteString (const char *text, cmd_source_t src) return; // check alias - a = (cmdalias_t*)Hash_Find (cmd_alias_hash, cmd_argv[0]); + a = (cmdalias_t*)Hash_Find (cmd_alias_hash, cmd_argv[0]->str); if (a) { Cbuf_InsertText ("\n"); Cbuf_InsertText (a->value); diff --git a/libs/util/dstring.c b/libs/util/dstring.c new file mode 100644 index 000000000..07df799c0 --- /dev/null +++ b/libs/util/dstring.c @@ -0,0 +1,113 @@ +/* + dstring.c + + dynamic string buffer functions + + Copyright (C) 2002 Brian Koropoff. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +static const char rcsid[] = + "$Id$"; + +#include +#include +#include "QF/sys.h" +#include "QF/dstring.h" + +dstring_t *dstring_new (void) { + dstring_t *new; + + new = calloc (1, sizeof(dstring_t)); + if (!new) + Sys_Error ("dstring_new: Failed to allocate memory.\n"); + return new; +} + +void dstring_delete (dstring_t *dstr) { + if (dstr->str) + free (dstr->str); + free (dstr); +} + +void dstring_adjust (dstring_t *dstr) { + if (dstr->size > dstr->truesize) { + dstr->str = realloc(dstr->str, dstr->size); + if (!dstr->str) + Sys_Error ("dstring_adjust: Failed to reallocate memory.\n"); + dstr->truesize = dstr->size; + } +} + +void dstring_append (dstring_t *dstr, const char *data, unsigned int len) { + unsigned int ins = dstr->size; // Save insertion point + + dstr->size += len; + dstring_adjust (dstr); + memcpy (dstr->str + ins, data, len); +} + +void dstring_insert (dstring_t *dstr, const char *data, unsigned int len, unsigned int pos) { + unsigned int oldsize = dstr->size; + + dstr->size += len; + dstring_adjust (dstr); + memmove (dstr->str+pos+len, dstr->str+pos, oldsize - pos); + memcpy (dstr->str+pos, data, len); +} + +void dstring_snip (dstring_t *dstr, unsigned int pos, unsigned int len) { + memmove (dstr->str+pos, dstr->str+pos+len, dstr->size-pos-len); + dstr->size -= len; + dstring_adjust (dstr); +} + +void dstring_clear (dstring_t *dstr) { + dstr->size = 0; + dstring_adjust (dstr); +} + +dstring_t *dstring_newstr (void) { + dstring_t *new; + + new = calloc(1, sizeof(dstring_t)); + if (!new) + Sys_Error ("dstring_newstr: Failed to allocate memory.\n"); + new->size = 1; + dstring_adjust(new); + new->str[0] = 0; + return new; +} +void dstring_appendstr (dstring_t *dstr, const char *str) { + dstr->size += strlen(str); + dstring_adjust(dstr); + strcat(dstr->str, str); +} + +void dstring_insertstr (dstring_t *dstr, const char *str, unsigned int pos) { + dstring_insert (dstr, str, strlen(str), pos); // Don't instert strlen + 1 to achieve concatenation +} + +void dstring_clearstr (dstring_t *dstr) { + dstr->size = 1; + dstring_adjust (dstr); + dstr->str[0] = 0; +}