mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-14 00:40:55 +00:00
77337e2402
It turns out this is required for compatibility with qcc (and C, really). Once string to boolean conversions are sorted out completely (not that simple as qcc is inconsistent with if (string) vs if (!string)), Qgets can be implemented :)
444 lines
8.6 KiB
C
444 lines
8.6 KiB
C
/*
|
|
strpool.c
|
|
|
|
unique string support
|
|
|
|
Copyright (C) 2002 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2002/7/5
|
|
|
|
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
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#include "QF/dstring.h"
|
|
#include "QF/hash.h"
|
|
|
|
#include "diagnostic.h"
|
|
#include "options.h"
|
|
#include "strpool.h"
|
|
|
|
static hashtab_t *saved_strings;
|
|
|
|
static const char *
|
|
strpool_get_key (const void *_str, void *_strpool)
|
|
{
|
|
long str = (intptr_t) _str;
|
|
strpool_t *strpool = (strpool_t *) _strpool;
|
|
|
|
return strpool->strings + str;
|
|
}
|
|
|
|
strpool_t *
|
|
strpool_new (void)
|
|
{
|
|
strpool_t *strpool = calloc (1, sizeof (strpool_t));
|
|
|
|
strpool->str_tab = Hash_NewTable (16381, strpool_get_key, 0, strpool);
|
|
strpool->size = 1;
|
|
strpool->max_size = 16384;
|
|
strpool->strings = malloc (strpool->max_size);
|
|
strpool->strings[0] = 0;
|
|
return strpool;
|
|
}
|
|
|
|
strpool_t *
|
|
strpool_build (const char *strings, int size)
|
|
{
|
|
intptr_t s;
|
|
|
|
strpool_t *strpool = malloc (sizeof (strpool_t));
|
|
strpool->str_tab = Hash_NewTable (16381, strpool_get_key, 0, strpool);
|
|
strpool->size = size + (*strings != 0);
|
|
strpool->max_size = (strpool->size + 16383) & ~16383;
|
|
strpool->strings = malloc (strpool->max_size);
|
|
memcpy (strpool->strings + (*strings != 0), strings, strpool->size);
|
|
strpool->strings[0] = 0;
|
|
for (s = 1; s < strpool->size; s += strlen (strpool->strings + s) + 1) {
|
|
Hash_Add (strpool->str_tab, (void *) s);
|
|
}
|
|
return strpool;
|
|
}
|
|
|
|
void
|
|
strpool_delete (strpool_t *strpool)
|
|
{
|
|
Hash_DelTable (strpool->str_tab);
|
|
free (strpool->strings);
|
|
free (strpool);
|
|
}
|
|
|
|
int
|
|
strpool_addstr (strpool_t *strpool, const char *str)
|
|
{
|
|
intptr_t s;
|
|
int len;
|
|
|
|
if (!str)
|
|
return 0;
|
|
s = (intptr_t) Hash_Find (strpool->str_tab, str);
|
|
if (s)
|
|
return s;
|
|
len = strlen (str) + 1;
|
|
if (strpool->size + len > strpool->max_size) {
|
|
strpool->max_size += (len + 16383) & ~16383;
|
|
strpool->strings = realloc (strpool->strings, strpool->max_size);
|
|
}
|
|
s = strpool->size;
|
|
strpool->size += len;
|
|
strcpy (strpool->strings + s, str);
|
|
Hash_Add (strpool->str_tab, (void *) s);
|
|
return s;
|
|
}
|
|
|
|
static const char *
|
|
ss_get_key (const void *s, void *unused)
|
|
{
|
|
return (const char *)s;
|
|
}
|
|
|
|
const char *
|
|
save_string (const char *str)
|
|
{
|
|
char *s;
|
|
if (!saved_strings)
|
|
saved_strings = Hash_NewTable (16381, ss_get_key, 0, 0);
|
|
s = Hash_Find (saved_strings, str);
|
|
if (s)
|
|
return s;
|
|
s = strdup (str);
|
|
Hash_Add (saved_strings, s);
|
|
return s;
|
|
}
|
|
|
|
const char *
|
|
make_string (char *token, char **end)
|
|
{
|
|
char s[2];
|
|
int c;
|
|
int i;
|
|
int mask;
|
|
int boldnext;
|
|
int quote;
|
|
static dstring_t *str;
|
|
|
|
if (!str)
|
|
str = dstring_newstr ();
|
|
dstring_clearstr (str);
|
|
|
|
s[1] = 0;
|
|
|
|
mask = 0x00;
|
|
boldnext = 0;
|
|
|
|
quote = *token++;
|
|
do {
|
|
c = *token++;
|
|
if (!c)
|
|
error (0, "EOF inside quote");
|
|
if (c == '\n')
|
|
error (0, "newline inside quote");
|
|
if (c == '\\') { // escape char
|
|
c = *token++;
|
|
if (!c)
|
|
error (0, "EOF inside quote");
|
|
switch (c) {
|
|
case '\\':
|
|
boldnext = 0;
|
|
c = '\\' ^ mask;
|
|
break;
|
|
case 'n':
|
|
boldnext = 0;
|
|
c = '\n' ^ mask;
|
|
break;
|
|
case '"':
|
|
boldnext = 0;
|
|
c = '\"' ^ mask;
|
|
break;
|
|
case '\'':
|
|
boldnext = 0;
|
|
c = '\'' ^ mask;
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
if (!options.qccx_escapes) {
|
|
for (i = c = 0; i < 3
|
|
&& *token >= '0'
|
|
&& *token <= '7'; i++, token++) {
|
|
c *= 8;
|
|
c += *token - '0';
|
|
}
|
|
if (!*token)
|
|
error (0, "EOF inside quote");
|
|
c ^= mask;
|
|
boldnext = 0;
|
|
break;
|
|
}
|
|
case '8':
|
|
case '9':
|
|
boldnext = 0;
|
|
c = 18 + c - '0';
|
|
c ^= mask;
|
|
break;
|
|
case 'x':
|
|
boldnext = 0;
|
|
c = 0;
|
|
while (*token && isxdigit ((unsigned char)*token)) {
|
|
c *= 16;
|
|
if (*token <= '9')
|
|
c += *token - '0';
|
|
else if (*token <= 'F')
|
|
c += *token - 'A' + 10;
|
|
else
|
|
c += *token - 'a' + 10;
|
|
token++;
|
|
}
|
|
if (!*token)
|
|
error (0, "EOF inside quote");
|
|
c ^= mask;
|
|
break;
|
|
case 'a':
|
|
boldnext = 0;
|
|
c = '\a' ^ mask;
|
|
break;
|
|
case 'b':
|
|
boldnext = 0;
|
|
if (options.qccx_escapes) {
|
|
mask ^= 0x80;
|
|
continue;
|
|
} else {
|
|
c = '\b' ^ mask;
|
|
break;
|
|
}
|
|
case 'e':
|
|
boldnext = 0;
|
|
c = '\033' ^ mask;
|
|
break;
|
|
case 'f':
|
|
boldnext = 0;
|
|
c = '\f' ^ mask;
|
|
break;
|
|
case 'r':
|
|
boldnext = 0;
|
|
c = '\r' ^ mask;
|
|
break;
|
|
case 's':
|
|
boldnext = 0;
|
|
mask ^= 0x80;
|
|
continue;
|
|
case 't':
|
|
boldnext = 0;
|
|
c = '\t' ^ mask;
|
|
break;
|
|
case 'v':
|
|
boldnext = 0;
|
|
c = '\v' ^ mask;
|
|
break;
|
|
case '^':
|
|
if (*token == '\"')
|
|
error (0, "Unexpected end of string after \\^");
|
|
boldnext = 1;
|
|
continue;
|
|
case '[':
|
|
boldnext = 0;
|
|
c = 0x90; // gold [
|
|
break;
|
|
case ']':
|
|
boldnext = 0;
|
|
c = 0x91; // gold ]
|
|
break;
|
|
case '.':
|
|
boldnext = 0;
|
|
c = 28; // center dot
|
|
break;
|
|
case '<':
|
|
boldnext = 0;
|
|
if (options.qccx_escapes) {
|
|
c = 29 ^ mask; // brown left end
|
|
break;
|
|
} else {
|
|
mask = 0x80;
|
|
continue;
|
|
}
|
|
case '-':
|
|
boldnext = 0;
|
|
c = 30; // brown center bit
|
|
break;
|
|
case '>':
|
|
boldnext = 0;
|
|
if (options.qccx_escapes) {
|
|
c = 31 ^ mask; // brown right end
|
|
break;
|
|
} else {
|
|
mask = 0x00;
|
|
continue;
|
|
}
|
|
case '(':
|
|
boldnext = 0;
|
|
c = 128 ^ mask; // left slider end
|
|
break;
|
|
case '=':
|
|
boldnext = 0;
|
|
c = 129 ^ mask; // slider center
|
|
break;
|
|
case ')':
|
|
boldnext = 0;
|
|
c = 130 ^ mask; // right slider end
|
|
break;
|
|
case '{':
|
|
boldnext = 0;
|
|
c = 0;
|
|
while (*token && *token != '}'
|
|
&& isdigit ((unsigned char)*token)) {
|
|
c *= 10;
|
|
c += *token++ - '0';
|
|
}
|
|
if (!*token)
|
|
error (0, "EOF inside quote");
|
|
if (*token != '}')
|
|
error (0, "non-digit inside \\{}");
|
|
else
|
|
token++;
|
|
if (c > 255)
|
|
warning (0, "\\{%d} > 255", c);
|
|
c ^= mask;
|
|
break;
|
|
default:
|
|
error (0, "Unknown escape char");
|
|
break;
|
|
}
|
|
} else if (c == quote) {
|
|
break;
|
|
}
|
|
if (boldnext)
|
|
c = c ^ 0x80;
|
|
boldnext = 0;
|
|
c = c ^ mask;
|
|
s[0] = c;
|
|
dstring_appendstr (str, s);
|
|
} while (1);
|
|
|
|
if (end)
|
|
*end = token;
|
|
|
|
return save_string (str->str);
|
|
}
|
|
|
|
const char *
|
|
html_string (const char *str)
|
|
{
|
|
static dstring_t *q;
|
|
char c[2] = {0, 0};
|
|
|
|
if (!str)
|
|
return "(null)";
|
|
if (!q)
|
|
q = dstring_new ();
|
|
dstring_clearstr (q);
|
|
while ((c[0] = *str++)) {
|
|
switch (c[0]) {
|
|
case '<':
|
|
dstring_appendstr (q, "<");
|
|
break;
|
|
case '>':
|
|
dstring_appendstr (q, ">");
|
|
break;
|
|
case '&':
|
|
dstring_appendstr (q, "&");
|
|
break;
|
|
case '"':
|
|
dstring_appendstr (q, """);
|
|
break;
|
|
default:
|
|
dstring_appendstr (q, c);
|
|
break;
|
|
}
|
|
}
|
|
return q->str;
|
|
}
|
|
|
|
const char *
|
|
quote_string (const char *str)
|
|
{
|
|
static dstring_t *q;
|
|
char c[2] = {0, 0};
|
|
|
|
if (!str)
|
|
return "(null)";
|
|
if (!q)
|
|
q = dstring_new ();
|
|
dstring_clearstr (q);
|
|
while ((c[0] = *str++)) {
|
|
switch (c[0]) {
|
|
case '\a':
|
|
dstring_appendstr (q, "\\a");
|
|
break;
|
|
case '\b':
|
|
dstring_appendstr (q, "\\b");
|
|
break;
|
|
case '\f':
|
|
dstring_appendstr (q, "\\f");
|
|
break;
|
|
case '\n':
|
|
dstring_appendstr (q, "\\n");
|
|
break;
|
|
case '\r':
|
|
dstring_appendstr (q, "\\r");
|
|
break;
|
|
case '\t':
|
|
dstring_appendstr (q, "\\t");
|
|
break;
|
|
case '\\':
|
|
dstring_appendstr (q, "\\\\");
|
|
break;
|
|
case '\'':
|
|
dstring_appendstr (q, "\\'");
|
|
break;
|
|
case '\"':
|
|
dstring_appendstr (q, "\\\"");
|
|
break;
|
|
default:
|
|
if (c[0] >= 127 || c[0] < 32)
|
|
dasprintf (q, "\\\\x%02d", (byte) c[0]);
|
|
else
|
|
dstring_appendstr (q, c);
|
|
break;
|
|
}
|
|
}
|
|
return q->str;
|
|
}
|