PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/quakeforge/branches/release_0_5_2/libs/util/qfplist.c

#
C | 495 lines | 440 code | 29 blank | 26 comment | 66 complexity | 21fb7cdf34536e327521493847869a62 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-3.0, AGPL-1.0, Unlicense
  1. /*
  2. qfplist.c
  3. Property list management
  4. Copyright (C) 2000 Jeff Teunissen <deek@d2dc.net>
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. See the GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to:
  15. Free Software Foundation, Inc.
  16. 59 Temple Place - Suite 330
  17. Boston, MA 02111-1307, USA
  18. */
  19. static const char rcsid[] =
  20. "$Id: qfplist.c 7635 2002-05-14 06:12:29Z taniwha $";
  21. #ifdef HAVE_CONFIG_H
  22. # include "config.h"
  23. #endif
  24. #include <ctype.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include "QF/hash.h"
  28. #include "QF/qfplist.h"
  29. #include "QF/qtypes.h"
  30. #include "QF/sys.h"
  31. static plitem_t *PL_ParsePropertyListItem (pldata_t *);
  32. static qboolean PL_SkipSpace (pldata_t *);
  33. static char *PL_ParseQuotedString (pldata_t *);
  34. static char *PL_ParseUnquotedString (pldata_t *);
  35. void
  36. PL_FreeItem (plitem_t *item)
  37. {
  38. switch (item->type) {
  39. case QFDictionary:
  40. Hash_DelTable (item->data);
  41. break;
  42. case QFArray: {
  43. int i = ((plarray_t *) item->data)->numvals;
  44. while (--i) {
  45. PL_FreeItem (((plarray_t *) item->data)->values[i]);
  46. }
  47. free (item->data);
  48. }
  49. break;
  50. case QFBinary:
  51. free (((plbinary_t *) item->data)->data);
  52. free (item->data);
  53. break;
  54. case QFString:
  55. free (item->data);
  56. break;
  57. }
  58. free (item);
  59. }
  60. static const char *
  61. dict_get_key (void *i, void *unused)
  62. {
  63. dictkey_t *item = (dictkey_t *) i;
  64. return item->key;
  65. }
  66. static void
  67. dict_free (void *i, void *unused)
  68. {
  69. dictkey_t *item = (dictkey_t *) i;
  70. free (item->key);
  71. PL_FreeItem (item->value); // Make descended stuff get freed
  72. free (item);
  73. }
  74. plitem_t *
  75. PL_ObjectForKey (hashtab_t *table, const char *key)
  76. {
  77. dictkey_t *k = (dictkey_t *) Hash_Find (table, key);
  78. return k ? k->value : NULL;
  79. }
  80. static qboolean
  81. PL_SkipSpace (pldata_t *pl)
  82. {
  83. while (pl->pos < pl->end) {
  84. char c = pl->ptr[pl->pos];
  85. if (!isspace ((byte) c)) {
  86. if (c == '/' && pl->pos < pl->end - 1) { // check for comments
  87. if (pl->ptr[pl->pos + 1] == '/') {
  88. pl->pos += 2;
  89. while (pl->pos < pl->end) {
  90. c = pl->ptr[pl->pos];
  91. if (c == '\n')
  92. break;
  93. pl->pos++;
  94. }
  95. if (pl->pos >= pl->end) {
  96. pl->error = "Reached end of string in comment";
  97. return false;
  98. }
  99. } else if (pl->ptr[pl->pos + 1] == '*') { // "/*" comments
  100. pl->pos += 2;
  101. while (pl->pos < pl->end) {
  102. c = pl->ptr[pl->pos];
  103. if (c == '\n') {
  104. pl->line++;
  105. } else if (c == '*' && pl->pos < pl->end - 1
  106. && pl->ptr[pl->pos+1] == '/') {
  107. pl->pos++;
  108. break;
  109. }
  110. pl->pos++;
  111. }
  112. if (pl->pos >= pl->end) {
  113. pl->error = "Reached end of string in comment";
  114. return false;
  115. }
  116. } else {
  117. return true;
  118. }
  119. } else {
  120. return true;
  121. }
  122. }
  123. if (c == '\n') {
  124. pl->line++;
  125. }
  126. pl->pos++;
  127. }
  128. pl->error = "Reached end of string";
  129. return false;
  130. }
  131. static char *
  132. PL_ParseQuotedString (pldata_t *pl)
  133. {
  134. unsigned int start = ++pl->pos;
  135. unsigned int escaped = 0;
  136. unsigned int shrink = 0;
  137. qboolean hex = false;
  138. char *str;
  139. while (pl->pos < pl->end) {
  140. char c = pl->ptr[pl->pos];
  141. if (escaped) {
  142. if (escaped == 1 && c == '0') {
  143. escaped++;
  144. hex = false;
  145. } else if (escaped > 1) {
  146. if (escaped == 2 && c == 'x') {
  147. hex = true;
  148. shrink++;
  149. escaped++;
  150. } else if (hex && isxdigit ((byte) c)) {
  151. shrink++;
  152. escaped++;
  153. } else if (c >= '0' && c <= '7') {
  154. shrink++;
  155. escaped++;
  156. } else {
  157. pl->pos--;
  158. escaped = 0;
  159. }
  160. } else {
  161. escaped = 0;
  162. }
  163. } else {
  164. if (c == '\\') {
  165. escaped = 1;
  166. shrink++;
  167. } else if (c == '"') {
  168. break;
  169. }
  170. }
  171. if (c == '\n') {
  172. pl->line++;
  173. }
  174. pl->pos++;
  175. }
  176. if (pl->pos >= pl->end) {
  177. pl->error = "Reached end of string while parsing quoted string";
  178. return NULL;
  179. }
  180. if (pl->pos - start - shrink == 0) {
  181. str = "";
  182. } else {
  183. char chars[pl->pos - start - shrink];
  184. unsigned int j;
  185. unsigned int k;
  186. escaped = 0;
  187. hex = false;
  188. for (j = start, k = 0; j < pl->pos; j++) {
  189. char c = pl->ptr[j];
  190. if (escaped) {
  191. if (escaped == 1 && c == '0') {
  192. chars[k] = 0;
  193. hex = false;
  194. escaped++;
  195. } else if (escaped > 1) {
  196. if (escaped == 2 && c == 'x') {
  197. hex = true;
  198. escaped++;
  199. } else if (hex && isxdigit ((byte) c)) {
  200. chars[k] <<= 4;
  201. chars[k] |= char2num (c);
  202. escaped++;
  203. } else if (inrange (c, '0', '7')) {
  204. chars[k] <<= 3;
  205. chars[k] |= (c - '0');
  206. escaped++;
  207. } else {
  208. escaped = 0;
  209. j--;
  210. k++;
  211. }
  212. } else {
  213. escaped = 0;
  214. switch (c) {
  215. case 'a':
  216. chars[k] = '\a';
  217. break;
  218. case 'b':
  219. chars[k] = '\b';
  220. break;
  221. case 't':
  222. chars[k] = '\t';
  223. break;
  224. case 'r':
  225. chars[k] = '\r';
  226. break;
  227. case 'n':
  228. chars[k] = '\n';
  229. break;
  230. case 'v':
  231. chars[k] = '\v';
  232. break;
  233. case 'f':
  234. chars[k] = '\f';
  235. break;
  236. default:
  237. chars[k] = c;
  238. break;
  239. }
  240. k++;
  241. }
  242. } else {
  243. chars[k] = c;
  244. if (c == '\\') {
  245. escaped = 1;
  246. } else {
  247. k++;
  248. }
  249. }
  250. }
  251. str = strncat (calloc ((pl->pos - start - shrink) + 1, 1), chars,
  252. pl->pos - start - shrink);
  253. }
  254. pl->pos++;
  255. return str;
  256. }
  257. static char *
  258. PL_ParseUnquotedString (pldata_t *pl)
  259. {
  260. unsigned int start = pl->pos;
  261. char *str;
  262. while (pl->pos < pl->end) {
  263. if (!isalnum ((byte) pl->ptr[pl->pos]) && pl->ptr[pl->pos] != '_')
  264. break;
  265. pl->pos++;
  266. }
  267. str = strncat (calloc ((pl->pos - start) + 1, 1), &pl->ptr[start],
  268. pl->pos - start);
  269. return str;
  270. }
  271. static plitem_t *
  272. PL_ParsePropertyListItem (pldata_t *pl)
  273. {
  274. plitem_t *item = NULL;
  275. if (!PL_SkipSpace (pl))
  276. return NULL;
  277. switch (pl->ptr[pl->pos]) {
  278. case '{':
  279. {
  280. hashtab_t *dict = Hash_NewTable (1021, dict_get_key, dict_free, NULL);
  281. pl->pos++;
  282. while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != '}') {
  283. plitem_t *key;
  284. plitem_t *value;
  285. if (!(key = PL_ParsePropertyListItem (pl)))
  286. return NULL;
  287. if (!(PL_SkipSpace (pl))) {
  288. PL_FreeItem (key);
  289. return NULL;
  290. }
  291. if (key->type != QFString) {
  292. pl->error = "Key is not a string";
  293. PL_FreeItem (key);
  294. return NULL;
  295. }
  296. if (pl->ptr[pl->pos] != '=') {
  297. pl->error = "Unexpected character (expected '=')";
  298. PL_FreeItem (key);
  299. return NULL;
  300. }
  301. pl->pos++;
  302. // If there is no value, lose the key
  303. if (!(value = PL_ParsePropertyListItem (pl))) {
  304. PL_FreeItem (key);
  305. return NULL;
  306. }
  307. if (!(PL_SkipSpace (pl))) {
  308. PL_FreeItem (key);
  309. PL_FreeItem (value);
  310. return NULL;
  311. }
  312. if (pl->ptr[pl->pos] == ';') {
  313. pl->pos++;
  314. } else if (pl->ptr[pl->pos] != '}') {
  315. pl->error = "Unexpected character (wanted ';' or '}')";
  316. PL_FreeItem (key);
  317. PL_FreeItem (value);
  318. return NULL;
  319. }
  320. { // Add the key/value pair to the dictionary
  321. dictkey_t *k = calloc (1, sizeof (dictkey_t));
  322. if (!k) {
  323. PL_FreeItem (key);
  324. PL_FreeItem (value);
  325. return NULL;
  326. }
  327. k->key = (char *) key->data;
  328. k->value = value;
  329. Hash_Add (dict, k);
  330. }
  331. }
  332. if (pl->pos >= pl->end) { // Catch the error
  333. pl->error = "Unexpected end of string when parsing dictionary";
  334. Hash_DelTable (dict);
  335. return NULL;
  336. }
  337. pl->pos++;
  338. item = calloc (1, sizeof (plitem_t));
  339. item->type = QFDictionary;
  340. item->data = dict;
  341. return item;
  342. }
  343. case '(': {
  344. plarray_t *a = calloc (1, sizeof (plarray_t));
  345. pl->pos++;
  346. while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != ')') {
  347. plitem_t *value;
  348. if (!(value = PL_ParsePropertyListItem (pl)))
  349. return NULL;
  350. if (!(PL_SkipSpace (pl))) {
  351. free (value);
  352. return NULL;
  353. }
  354. if (pl->ptr[pl->pos] == ',') {
  355. pl->pos++;
  356. } else if (pl->ptr[pl->pos] != ')') {
  357. pl->error = "Unexpected character (wanted ',' or ')')";
  358. free (value);
  359. return NULL;
  360. }
  361. if (a->numvals == MAX_ARRAY_INDEX) {
  362. pl->error = "Unexpected character (too many items in array)";
  363. free (value);
  364. return NULL;
  365. }
  366. a->values[a->numvals++] = value;
  367. }
  368. pl->pos++;
  369. item = calloc (1, sizeof (plitem_t));
  370. item->type = QFArray;
  371. item->data = a;
  372. return item;
  373. }
  374. case '<':
  375. pl->error = "Unexpected character in string (binary data unsupported)";
  376. return NULL;
  377. case '"': {
  378. char *str = PL_ParseQuotedString (pl);
  379. if (!str) {
  380. return NULL;
  381. } else {
  382. item = calloc (1, sizeof (plitem_t));
  383. item->type = QFString;
  384. item->data = str;
  385. return item;
  386. }
  387. }
  388. default: {
  389. char *str = PL_ParseUnquotedString (pl);
  390. if (!str) {
  391. return NULL;
  392. } else {
  393. item = calloc (1, sizeof (plitem_t));
  394. item->type = QFString;
  395. item->data = str;
  396. return item;
  397. }
  398. }
  399. } // switch
  400. }
  401. plitem_t *
  402. PL_GetPropertyList (const char *string)
  403. {
  404. pldata_t *pl = calloc (1, sizeof (pldata_t));
  405. plitem_t *newpl = NULL;
  406. pl->ptr = string;
  407. pl->pos = 0;
  408. pl->end = strlen (string);
  409. pl->error = NULL;
  410. pl->line = 1;
  411. if ((newpl = PL_ParsePropertyListItem (pl))) {
  412. free (pl);
  413. return newpl;
  414. } else {
  415. if (pl && pl->error && pl->error[0])
  416. Sys_Error ("%d,%d: %s", pl->line, pl->pos, pl->error);
  417. free (pl);
  418. return NULL;
  419. }
  420. }