/* strpool.c unique string support Copyright (C) 2002 Bill Currie Author: Bill Currie 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 #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include "QF/dstring.h" #include "QF/hash.h" #include "QF/msg.h" #include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/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, 0); 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, 0); 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; } int strpool_findstr (strpool_t *strpool, const char *str) { if (!str) return 0; return (intptr_t) Hash_Find (strpool->str_tab, str); } 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, 0); s = Hash_Find (saved_strings, str); if (s) return s; s = strdup (str); Hash_Add (saved_strings, s); return s; } const char * save_cwd (void) { char *cwd = getcwd (0, 0); const char *str = save_string (cwd); free (cwd); return str; } const char * make_string (char *token, char **end) { char s[7]; // utf8 needs 6 + nul sizebuf_t utf8str = { .maxsize = sizeof (s), .data = (byte *) s, }; int c; int i; int mask; int boldnext; int unicount; int quote; static dstring_t *str; if (!str) str = dstring_newstr (); dstring_clearstr (str); s[1] = 0; mask = 0x00; boldnext = 0; unicount = 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 '?': 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; // cancel mask below break; case 'U': unicount += 4; case 'u': unicount += 4; boldnext = 0; c = 0; while (unicount && *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++; --unicount; } if (!*token) { error (0, "EOF inside quote"); } else if (unicount) { error (0, "incomplete unicode sequence: %x %d", c, unicount); } unicount = 1; // signal need to encode to utf8 c ^= mask; // cancel mask below 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; if (unicount) { SZ_Clear (&utf8str); MSG_WriteUTF8 (&utf8str, c); MSG_WriteByte (&utf8str, 0); // nul-terminate string unicount = 0; } else { s[0] = c; s[1] = 0; } 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; }