Bug Summary

File:shared/entitiesdef.cpp
Location:line 561, column 2
Description:Memory is never released; potential leak of memory pointed to by 'newRange'

Annotated Source Code

1/**
2 * @file
3 * @brief Handles definition of entities, parsing them from entities.ufo
4 */
5
6/*
7All original material Copyright (C) 2002-2011 UFO: Alien Invasion.
8
9Copyright (C) 1997-2001 Id Software, Inc.
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26*/
27
28#include <string.h>
29#include <stdlib.h>
30#include <assert.h>
31#include <stdio.h>
32#include <math.h>
33
34#include "shared.h" /* needed for strdup() */
35#include "parse.h"
36#include "entitiesdef.h"
37
38#define ED_MAX_KEYS_PER_ENT32 32
39#define ED_MAX_TOKEN_LEN512 512
40#define ED_MAX_ERR_LEN512 512
41
42static char lastErr[ED_MAX_ERR_LEN512]; /**< for storing last error message */
43static char lastErrExtra[ED_MAX_ERR_LEN512]; /**< temporary storage for extra information to be added to lastErr */
44
45int numEntityDefs;
46entityDef_t entityDefs[ED_MAX_DEFS64 + 1];
47
48/**
49 * @brief write an error message and exit the current function returning ED_ERROR
50 * @sa ED_TEST_RETURN_ERROR
51 */
52#define ED_RETURN_ERROR(...){ snprintf(lastErr, sizeof(lastErr), ...); return -1; } \
53 { \
54 snprintf(lastErr, sizeof(lastErr), __VA_ARGS__); \
55 return ED_ERROR-1; \
56 }
57
58/**
59 * @brief test a condition, write an error
60 * message and exit the current function with ED_ERROR
61 */
62#define ED_TEST_RETURN_ERROR(condition,...)if (condition) { snprintf(lastErr, sizeof(lastErr), ...); return
-1; }
\
63 if (condition) { \
64 snprintf(lastErr, sizeof(lastErr), __VA_ARGS__); \
65 return ED_ERROR-1; \
66 }
67
68/**
69 * @brief if the function returns ED_ERROR, then the function that the
70 * macro is in also returns ED_ERROR. Note that the called function is expected
71 * to set lastErr, probably by using the ED_TEST_RETURN_ERROR macro
72 */
73#define ED_PASS_ERROR(function_call)if ((function_call) == -1) { return -1; } \
74 if ((function_call) == ED_ERROR-1) { \
75 return ED_ERROR-1; \
76 }
77
78/**
79 * @brief if the function returns ED_ERROR, then the function that the
80 * macro is in also returns ED_ERROR. Note that the called function is expected
81 * to set lastErr, probably by using the ED_TEST_RETURN_ERROR macro. this macro then
82 * appends extra detail to the message to give context
83 */
84#define ED_PASS_ERROR_EXTRAMSG(function_call,...)if ((function_call) == -1) { snprintf(lastErrExtra, sizeof(lastErr
), ...); strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen
(lastErr) -1); return -1; }
\
85 if ((function_call) == ED_ERROR-1) { \
86 snprintf(lastErrExtra, sizeof(lastErr), __VA_ARGS__); \
87 strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr) -1); \
88 return ED_ERROR-1; \
89 }
90
91/**
92 * @brief allocate space for key defs etc, pointers for which are stored in the entityDef_t
93 * @return ED_ERROR or ED_OK.
94 */
95static int ED_AllocEntityDef (entityKeyDef_t *newKeyDefs, int numKeyDefs, int entityIndex)
96{
97 entityDef_t *eDef = &entityDefs[entityIndex];
98
99 /* now we know how many there are in this entity, malloc */
100 eDef->keyDefs = (entityKeyDef_t *) malloc((numKeyDefs + 1) * sizeof(entityKeyDef_t));
101 ED_TEST_RETURN_ERROR(!eDef->keyDefs, "ED_AllocEntityDef: out of memory")if (!eDef->keyDefs) { snprintf(lastErr, sizeof(lastErr), "ED_AllocEntityDef: out of memory"
); return -1; }
;
102 eDef->numKeyDefs = numKeyDefs;
103
104 /* and copy from temp buffer */
105 memcpy(eDef->keyDefs, newKeyDefs, numKeyDefs * sizeof(entityKeyDef_t));
106
107 /* set NULLs at the end, to enable looping through using a pointer */
108 OBJZERO(eDef->keyDefs[numKeyDefs])(memset(&((eDef->keyDefs[numKeyDefs])), (0), sizeof((eDef
->keyDefs[numKeyDefs]))))
;
109
110 /* classname is commonly used, put it in its own field, copied from keyDefs[0] */
111 eDef->classname = strdup(eDef->keyDefs->desc);
112 ED_TEST_RETURN_ERROR(!eDef->classname, "ED_AllocEntityDef: out of memory")if (!eDef->classname) { snprintf(lastErr, sizeof(lastErr),
"ED_AllocEntityDef: out of memory"); return -1; }
;
113
114 return ED_OK1;
115}
116
117/**
118 * @brief search for an existing keyDef to add a new parsed pair info to.
119 * @return a pointer to the entity def or NULL if it is not found
120 */
121static entityKeyDef_t *ED_FindKeyDefInArray (entityKeyDef_t keyDefs[], int numDefs, const char *name, int parseMode)
122{
123 int i;
124 for (i = 0; i < numDefs; i++) {
125 const entityKeyDef_t *keyDef = &keyDefs[i];
126 /* names equal. both abstract or both not abstract */
127 if (Q_streq(keyDef->name, name)(strcmp(keyDef->name, name) == 0) && !((keyDef->flags ^ parseMode) & ED_ABSTRACT(1<<2))) {
128 return &keyDefs[i];
129 }
130 }
131 return NULL__null;
132}
133
134/**
135 * @brief converts a string representation of a type (eg V_FLOAT) to
136 * the appropriate internal constant integer
137 * @return the constant, or ED_ERROR if strType is not recognised
138 */
139static int ED_Type2Constant (const char *strType)
140{
141 if (Q_streq(strType, "V_FLOAT")(strcmp(strType, "V_FLOAT") == 0))
142 return ED_TYPE_FLOAT(1<<3);
143 else if (Q_streq(strType, "V_INT")(strcmp(strType, "V_INT") == 0))
144 return ED_TYPE_INT(1<<4);
145 else if (Q_streq(strType, "V_STRING")(strcmp(strType, "V_STRING") == 0))
146 return ED_TYPE_STRING(1<<5);
147
148 ED_RETURN_ERROR("ED_Type2Constant: type string not recognised: \"%s\"", strType){ snprintf(lastErr, sizeof(lastErr), "ED_Type2Constant: type string not recognised: \"%s\""
, strType); return -1; }
;
149}
150
151/**
152 * @brief converts an internal constant integer to a string
153 * representation of a type (eg V_FLOAT)
154 * @return the string, or NULL if the integer is not recognised.
155 */
156static const char *ED_Constant2Type (int constInt)
157{
158 switch (constInt) {
159 case ED_TYPE_FLOAT(1<<3):
160 return "V_FLOAT";
161 case ED_TYPE_INT(1<<4):
162 return "V_INT";
163 case ED_TYPE_STRING(1<<5):
164 return "V_STRING";
165 default:
166 snprintf(lastErr, sizeof(lastErr), "ED_Constant2Type: constant not recognised");
167 return NULL__null;
168 }
169}
170
171/**
172 * @brief parses an int array from a string
173 * @param str the string to parse
174 * @param[out] v the array of int to put the answer in
175 * it must have enough space for n elements.
176 * @param n the number of elements expected in the vector
177 * @return ED_ERROR or ED_OK
178 * @sa ED_GetLastError.
179 */
180static int ED_GetIntVectorFromString (const char *str, int v[], const int n)
181{
182 int i;
183 const char *buf_p = str;
184
185 for (i = 0; buf_p; i++) {
186 const char *tok = Com_Parse(&buf_p);
187 if (tok[0] == '\0')
188 break; /* previous tok was the last real one, don't waste time */
189 ED_TEST_RETURN_ERROR(i >= n, "ED_GetIntVectorFromString: v[%i] too small for ints from string \"%s\"", n, str)if (i >= n) { snprintf(lastErr, sizeof(lastErr), "ED_GetIntVectorFromString: v[%i] too small for ints from string \"%s\""
, n, str); return -1; }
;
190 v[i] = atoi(tok);
191 }
192 ED_TEST_RETURN_ERROR(i != n, "ED_GetIntVectorFromString: v[%i] wrong size for ints from string \"%s\"", n, str)if (i != n) { snprintf(lastErr, sizeof(lastErr), "ED_GetIntVectorFromString: v[%i] wrong size for ints from string \"%s\""
, n, str); return -1; }
;
193 return ED_OK1;
194}
195
196/**
197 * @brief parses an float array from a string
198 * @param str the string to parse
199 * @param[out] v the array of float to put the answer in
200 * it must have enough space for n elements.
201 * @param n the number of elements expected in the vector
202 * @return ED_ERROR or ED_OK
203 * @sa ED_GetLastError.
204 */
205static int ED_GetFloatVectorFromString (const char *str, float v[], const int n)
206{
207 int i;
208 const char *buf_p = str;
209
210 for (i = 0; buf_p; i++) {
211 const char *tok = Com_Parse(&buf_p);
212 if (tok[0] == '\0')
213 break; /* previous tok was the last real one, don't waste time */
214 ED_TEST_RETURN_ERROR(i >= n, "ED_GetFloatVectorFromString: v[%i] too small for floats from string \"%s\"", n, str)if (i >= n) { snprintf(lastErr, sizeof(lastErr), "ED_GetFloatVectorFromString: v[%i] too small for floats from string \"%s\""
, n, str); return -1; }
;
215 v[i] = atof(tok);
216 }
217 ED_TEST_RETURN_ERROR(i != n, "ED_GetFloatVectorFromString: v[%i] wrong size for floats from string \"%s\"", n, str)if (i != n) { snprintf(lastErr, sizeof(lastErr), "ED_GetFloatVectorFromString: v[%i] wrong size for floats from string \"%s\""
, n, str); return -1; }
;
218 return ED_OK1;
219}
220
221/**
222 * @brief gets the default value for the key
223 * @param[in] defaultBuf the calling function is responsible for ensuring this buffer
224 * has enough space
225 * @param n the number of floats the defaultBuf can hold
226 * @param kd the key definition to get the default for.
227 * @sa ED_GetKeyDef can be used to find kd.
228 * @return ED_OK or ED_ERROR (no default, or n is too small).
229 * @note the defualt int array is tested on parsing
230 */
231int ED_GetDefaultFloat (float *defaultBuf, const int n, const entityKeyDef_t *kd)
232{
233 ED_TEST_RETURN_ERROR(!kd->defaultVal, "ED_GetDefaultFloat: no default available")if (!kd->defaultVal) { snprintf(lastErr, sizeof(lastErr), "ED_GetDefaultFloat: no default available"
); return -1; }
;
234 ED_TEST_RETURN_ERROR(!(kd->flags & ED_TYPE_FLOAT), "ED_GetDefaultFloat: key does not define V_FLOAT")if (!(kd->flags & (1<<3))) { snprintf(lastErr, sizeof
(lastErr), "ED_GetDefaultFloat: key does not define V_FLOAT")
; return -1; }
;
235 ED_PASS_ERROR(ED_GetFloatVectorFromString(kd->defaultVal, defaultBuf, n))if ((ED_GetFloatVectorFromString(kd->defaultVal, defaultBuf
, n)) == -1) { return -1; }
;
236 return ED_OK1;
237}
238
239/**
240 * @brief gets the default value for the key
241 * @param[in] defaultBuf the calling function is responsible for ensuring this buffer
242 * has enough space
243 * @param n the number of ints the defaultBuf can hold
244 * @param kd the key definition to get the default for.
245 * @sa ED_GetKeyDef can be used to find kd.
246 * @return ED_OK or ED_ERROR (no default, or n is too small).
247 * @note the defualt int array is tested on parsing
248 */
249int ED_GetDefaultInt (int *defaultBuf, const int n, const entityKeyDef_t *kd)
250{
251 ED_TEST_RETURN_ERROR(!kd->defaultVal, "ED_GetDefaultInt: no default available")if (!kd->defaultVal) { snprintf(lastErr, sizeof(lastErr), "ED_GetDefaultInt: no default available"
); return -1; }
;
252 ED_TEST_RETURN_ERROR(!(kd->flags & ED_TYPE_INT), "ED_GetDefaultInt: key does not define V_INT")if (!(kd->flags & (1<<4))) { snprintf(lastErr, sizeof
(lastErr), "ED_GetDefaultInt: key does not define V_INT"); return
-1; }
;
253 ED_PASS_ERROR(ED_GetIntVectorFromString(kd->defaultVal, defaultBuf, n))if ((ED_GetIntVectorFromString(kd->defaultVal, defaultBuf,
n)) == -1) { return -1; }
;
254 return ED_OK1;
255}
256
257/**
258 * @brief parses a value from the definition
259 * @param kd the key definition to parse from
260 * @param[out] v the array of int to put the answer in
261 * it must have enough space for n elements.
262 * @param n the number of elements expected in the vector
263 * @return ED_ERROR or ED_OK
264 * @sa ED_GetLastError.
265 */
266int ED_GetIntVector (const entityKeyDef_t *kd, int v[], const int n)
267{
268 ED_PASS_ERROR(ED_GetIntVectorFromString(kd->desc, v, n))if ((ED_GetIntVectorFromString(kd->desc, v, n)) == -1) { return
-1; }
;
269 return ED_OK1;
270}
271
272/**
273 * @brief checks that a string represents a single number
274 * @param floatOrInt one of ED_TYPE_FLOAT or ED_TYPE_INT
275 * @param insistPositive if 1, then tests for the number being greater than or equal to zero.
276 * @sa ED_CheckNumericType
277 * @note disallows hex, inf, NaN, numbers with junk on the end (eg -0123junk)
278 * @return ED_OK or ED_ERROR
279 * @sa ED_GetLastError
280 * @note the parsed numbers are stored for later use in lastCheckedInt and lastCheckedFloat
281 */
282static int ED_CheckNumber (const char *value, const int floatOrInt, const int insistPositive, int_float_tu *parsedNumber)
283{
284 char *end_p;
285 /* V_INTs are protected from octal and hex as strtol with base 10 is used.
286 * this test is useful for V_INT, as it gives a specific error message.
287 * V_FLOATs are not protected from hex, inf, nan, so this check is here.
288 * strstr is used for hex, as 0x may not be the start of the string.
289 * eg -0x0.2 is a negative hex float */
290 ED_TEST_RETURN_ERROR(value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value[0] == 'N'if (value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value
[0] == 'N' || strstr(value, "0x") || strstr(value, "0X")) { snprintf
(lastErr, sizeof(lastErr), "infinity, NaN, hex (0x...) not allowed. found \"%s\""
, value); return -1; }
291 || strstr(value, "0x") || strstr(value, "0X"),if (value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value
[0] == 'N' || strstr(value, "0x") || strstr(value, "0X")) { snprintf
(lastErr, sizeof(lastErr), "infinity, NaN, hex (0x...) not allowed. found \"%s\""
, value); return -1; }
292 "infinity, NaN, hex (0x...) not allowed. found \"%s\"", value)if (value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value
[0] == 'N' || strstr(value, "0x") || strstr(value, "0X")) { snprintf
(lastErr, sizeof(lastErr), "infinity, NaN, hex (0x...) not allowed. found \"%s\""
, value); return -1; }
;
293 switch (floatOrInt) {
294 case ED_TYPE_FLOAT(1<<3):
295 parsedNumber->f = strtof(value, &end_p);
296 ED_TEST_RETURN_ERROR(insistPositive && parsedNumber->f < 0.0f, "ED_CheckNumber: not positive %s", value)if (insistPositive && parsedNumber->f < 0.0f) {
snprintf(lastErr, sizeof(lastErr), "ED_CheckNumber: not positive %s"
, value); return -1; }
;
297 break;
298 case ED_TYPE_INT(1<<4):
299 parsedNumber->i = (int)strtol(value, &end_p, 10);
300 ED_TEST_RETURN_ERROR(insistPositive && parsedNumber->i < 0, "ED_CheckNumber: not positive %s", value)if (insistPositive && parsedNumber->i < 0) { snprintf
(lastErr, sizeof(lastErr), "ED_CheckNumber: not positive %s",
value); return -1; }
;
301 break;
302 default:
303 ED_RETURN_ERROR("ED_CheckNumber: type to test against not recognised"){ snprintf(lastErr, sizeof(lastErr), "ED_CheckNumber: type to test against not recognised"
); return -1; }
;
304 }
305 /* if strto* did not use the whole token, then there is some non-number part to it */
306 ED_TEST_RETURN_ERROR(strlen(value) != (unsigned int)(end_p-value),if (strlen(value) != (unsigned int)(end_p-value)) { snprintf(
lastErr, sizeof(lastErr), "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)"
, value, ED_Constant2Type(floatOrInt)); return -1; }
307 "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)",if (strlen(value) != (unsigned int)(end_p-value)) { snprintf(
lastErr, sizeof(lastErr), "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)"
, value, ED_Constant2Type(floatOrInt)); return -1; }
308 value, ED_Constant2Type(floatOrInt))if (strlen(value) != (unsigned int)(end_p-value)) { snprintf(
lastErr, sizeof(lastErr), "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)"
, value, ED_Constant2Type(floatOrInt)); return -1; }
;
309 return ED_OK1;
310}
311
312/**
313 * @brief check a value against the range for the key
314 * @param floatOrInt either ED_TYPE_FLOAT or ED_TYPE_INT
315 * @param index the index of the number being checked in the value. eg angles "90 180", 90 is at 0, 180 is at 1.
316 * @note checks lastCheckedInt or lastCheckedFloat against the range in the supplied keyDef.
317 * @return ED_ERROR or ED_OK
318 */
319static int ED_CheckRange (const entityKeyDef_t *keyDef, const int floatOrInt, const int index, int_float_tu parsedNumber)
320{
321 /* there may be a range for each element of the value, or there may only be one */
322 const int useRangeIndex = (keyDef->numRanges == 1) ? 0 : index;
323 entityKeyRange_t *kr;
324 const float discreteFloatEpsilon = 0.0001f;
325 if (0 == keyDef->numRanges)
326 return ED_OK1; /* if no range defined, that is OK */
327 assert(useRangeIndex < keyDef->numRanges)(__builtin_expect(!(useRangeIndex < keyDef->numRanges),
0) ? __assert_rtn(__func__, "src/shared/entitiesdef.cpp", 327
, "useRangeIndex < keyDef->numRanges") : (void)0)
;
328 kr = keyDef->ranges[useRangeIndex];
329 switch (floatOrInt) {
330 case ED_TYPE_FLOAT(1<<3):
331 if (kr->continuous) {
332 ED_TEST_RETURN_ERROR(parsedNumber.f < kr->fArr[0] || parsedNumber.f > kr->fArr[1],if (parsedNumber.f < kr->fArr[0] || parsedNumber.f >
kr->fArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %.1f out of range, \"%s\" in %s"
, parsedNumber.f, kr->str, keyDef->name); return -1; }
333 "ED_CheckRange: %.1f out of range, \"%s\" in %s",if (parsedNumber.f < kr->fArr[0] || parsedNumber.f >
kr->fArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %.1f out of range, \"%s\" in %s"
, parsedNumber.f, kr->str, keyDef->name); return -1; }
334 parsedNumber.f, kr->str, keyDef->name)if (parsedNumber.f < kr->fArr[0] || parsedNumber.f >
kr->fArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %.1f out of range, \"%s\" in %s"
, parsedNumber.f, kr->str, keyDef->name); return -1; }
;
335 return ED_OK1;
336 } else {
337 int j;
338 for (j = 0; j < kr->numElements; j++)
339 if (fabs(parsedNumber.f - kr->fArr[j]) < discreteFloatEpsilon)
340 return ED_OK1;
341 }
342 break;
343 case ED_TYPE_INT(1<<4):
344 if (kr->continuous) {
345 ED_TEST_RETURN_ERROR(parsedNumber.i < kr->iArr[0] || parsedNumber.i > kr->iArr[1],if (parsedNumber.i < kr->iArr[0] || parsedNumber.i >
kr->iArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %i out of range, \"%s\" in %s"
, parsedNumber.i, kr->str, keyDef->name); return -1; }
346 "ED_CheckRange: %i out of range, \"%s\" in %s",if (parsedNumber.i < kr->iArr[0] || parsedNumber.i >
kr->iArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %i out of range, \"%s\" in %s"
, parsedNumber.i, kr->str, keyDef->name); return -1; }
347 parsedNumber.i, kr->str, keyDef->name)if (parsedNumber.i < kr->iArr[0] || parsedNumber.i >
kr->iArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %i out of range, \"%s\" in %s"
, parsedNumber.i, kr->str, keyDef->name); return -1; }
;
348 return ED_OK1;
349 } else {
350 int j;
351 for (j = 0; j < kr->numElements; j++)
352 if (kr->iArr[j] == parsedNumber.i)
353 return ED_OK1;
354 }
355 break;
356 default:
357 ED_RETURN_ERROR( "ED_CheckRange: type to test against not recognised in %s", keyDef->name){ snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: type to test against not recognised in %s"
, keyDef->name); return -1; }
;
358 }
359 ED_RETURN_ERROR("ED_CheckRange: value not specified in range definition, \"%s\" in %s",{ snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: value not specified in range definition, \"%s\" in %s"
, kr->str, keyDef->name); return -1; }
360 kr->str, keyDef->name){ snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: value not specified in range definition, \"%s\" in %s"
, kr->str, keyDef->name); return -1; }
;
361}
362
363/**
364 * @brief tests if a value string matches the type for this key. this includes
365 * each element of a numeric array. Also checks value against range def, if one exists.
366 * @param floatOrInt one of ED_TYPE_FLOAT or ED_TYPE_INT
367 * @return ED_OK or ED_ERROR (call ED_GetLastError)
368 */
369static int ED_CheckNumericType (const entityKeyDef_t *keyDef, const char *value, const int floatOrInt)
370{
371 int i = 0;
372 static char tokBuf[64];
373 const char *buf_p = tokBuf;
374
375 strncpy(tokBuf, value, sizeof(tokBuf));
376 assert(!(floatOrInt & ED_TYPE_INT) != !(floatOrInt & ED_TYPE_FLOAT))(__builtin_expect(!(!(floatOrInt & (1<<4)) != !(floatOrInt
& (1<<3))), 0) ? __assert_rtn(__func__, "src/shared/entitiesdef.cpp"
, 376, "!(floatOrInt & ED_TYPE_INT) != !(floatOrInt & ED_TYPE_FLOAT)"
) : (void)0)
;/* logical exclusive or */
377 while (buf_p) {
378 const char *tok = Com_Parse(&buf_p);
379 int_float_tu parsedNumber;
380 if (tok[0] == '\0')
381 break; /* previous tok was the last real one, don't waste time */
382
383 ED_PASS_ERROR_EXTRAMSG(ED_CheckNumber(tok, floatOrInt, keyDef->flags & ED_INSIST_POSITIVE, &parsedNumber),if ((ED_CheckNumber(tok, floatOrInt, keyDef->flags & (
1<<9), &parsedNumber)) == -1) { snprintf(lastErrExtra
, sizeof(lastErr), " in key \"%s\"", keyDef->name); strncat
(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr) -1)
; return -1; }
384 " in key \"%s\"", keyDef->name)if ((ED_CheckNumber(tok, floatOrInt, keyDef->flags & (
1<<9), &parsedNumber)) == -1) { snprintf(lastErrExtra
, sizeof(lastErr), " in key \"%s\"", keyDef->name); strncat
(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr) -1)
; return -1; }
;
385
386 ED_PASS_ERROR(ED_CheckRange(keyDef, floatOrInt, i, parsedNumber))if ((ED_CheckRange(keyDef, floatOrInt, i, parsedNumber)) == -
1) { return -1; }
;
387
388 i++;
389 }
390
391 ED_TEST_RETURN_ERROR(i != keyDef->vLen, "ED_CheckNumericType: %i elements in vector that should have %i for \"%s\" key",if (i != keyDef->vLen) { snprintf(lastErr, sizeof(lastErr)
, "ED_CheckNumericType: %i elements in vector that should have %i for \"%s\" key"
, i, keyDef->vLen, keyDef->name); return -1; }
392 i, keyDef->vLen, keyDef->name)if (i != keyDef->vLen) { snprintf(lastErr, sizeof(lastErr)
, "ED_CheckNumericType: %i elements in vector that should have %i for \"%s\" key"
, i, keyDef->vLen, keyDef->name); return -1; }
;
393
394 return ED_OK1;
395}
396
397/**
398 * @brief tests if a value string matches the type for this key. Also checks the value against the
399 * range, if one was defined.
400 * @return ED_OK or ED_ERROR
401 * @sa ED_GetLastError
402 * @note abstract (radiant) keys may not have types. keys used here must be declared in entities.ufo in
403 * an optional or mandatory block.
404 */
405int ED_Check (const char *classname, const char *key, const char *value)
406{
407 const entityKeyDef_t *kd = ED_GetKeyDef(classname, key, 0);
408 if (!kd)
409 return ED_ERROR-1;
410
411 return ED_CheckKey(kd, value);
412}
413
414/**
415 * @brief as ED_Check, but where the entity and key are known, so takes
416 * different arguments.
417 * @return ED_ERROR or ED_OK
418 * @sa ED_GetLastError
419 */
420int ED_CheckKey (const entityKeyDef_t *kd, const char *value)
421{
422 ED_TEST_RETURN_ERROR(!kd, "ED_CheckTypeEntityKey: null key def")if (!kd) { snprintf(lastErr, sizeof(lastErr), "ED_CheckTypeEntityKey: null key def"
); return -1; }
;
423 switch (kd->flags & ED_KEY_TYPE((1<<3) | (1<<4) | (1<<5))) {
424 case ED_TYPE_FLOAT(1<<3):
425 return ED_CheckNumericType(kd, value, ED_TYPE_FLOAT(1<<3));
426 case ED_TYPE_INT(1<<4):
427 return ED_CheckNumericType(kd, value, ED_TYPE_INT(1<<4));
428 case ED_TYPE_STRING(1<<5):
429 case 0: /* string is the default */
430 return ED_OK1; /* all strings are good */
431 default:
432 ED_RETURN_ERROR("ED_CheckTypeEntityKey: type not recognised in key def"){ snprintf(lastErr, sizeof(lastErr), "ED_CheckTypeEntityKey: type not recognised in key def"
); return -1; }
;
433 }
434}
435
436/**
437 * @brief gets the default value for the key
438 * @param[in] defaultBuf the calling function is responsible for ensuring this buffer
439 * has enough space
440 * @param n the number of chars the defualtBuf can hold
441 * @param kd the key definition to get the default for. E
442 * @sa D_GetKeyDef can be used to find kd.
443 * @return ED_OK or ED_ERROR (no default, or n is too small).
444 */
445int ED_GetDefaultString (char *defaultBuf, const size_t n, const entityKeyDef_t *kd)
446{
447 ED_TEST_RETURN_ERROR(n < 1, "ED_GetDefaultString: n is %lu", (unsigned long)n)if (n < 1) { snprintf(lastErr, sizeof(lastErr), "ED_GetDefaultString: n is %lu"
, (unsigned long)n); return -1; }
;
448 defaultBuf[0] = '\0'; /* be safe, in case an error is found, and we return early */
449 ED_TEST_RETURN_ERROR(!kd->defaultVal, "ED_GetDefaultString: no default available")if (!kd->defaultVal) { snprintf(lastErr, sizeof(lastErr), "ED_GetDefaultString: no default available"
); return -1; }
;
450 strncpy(defaultBuf, kd->defaultVal, n - 1);
451 ED_TEST_RETURN_ERROR(strlen(defaultBuf) < strlen(kd->defaultVal),if (strlen(defaultBuf) < strlen(kd->defaultVal)) { snprintf
(lastErr, sizeof(lastErr), "ED_GetDefaultString: target buffer supplied is too small"
); return -1; }
452 "ED_GetDefaultString: target buffer supplied is too small")if (strlen(defaultBuf) < strlen(kd->defaultVal)) { snprintf
(lastErr, sizeof(lastErr), "ED_GetDefaultString: target buffer supplied is too small"
); return -1; }
;
453 return ED_OK1;
454}
455
456/**
457 * @brief takes a type string (eg "V_FLOAT 6") and configures entity def
458 * @return ED_ERROR or ED_OK
459 */
460static int ED_ParseType (entityKeyDef_t *kd, const char *parsedToken)
461{
462 static char tokBuf[64];
463 const char *buf_p;
464 int type;
465 int vectorLen;
466 const char *partToken;
467 int_float_tu parsedNumber;
468
469 /* need a copy, as parsedToken is held in a static buffer in the
470 * Com_Parse function */
471 ED_TEST_RETURN_ERROR((strlen(parsedToken) + 1) > sizeof(tokBuf),if ((strlen(parsedToken) + 1) > sizeof(tokBuf)) { snprintf
(lastErr, sizeof(lastErr), "ED_ParseType: type string too long for buffer for key %s"
, kd->name); return -1; }
472 "ED_ParseType: type string too long for buffer for key %s", kd->name)if ((strlen(parsedToken) + 1) > sizeof(tokBuf)) { snprintf
(lastErr, sizeof(lastErr), "ED_ParseType: type string too long for buffer for key %s"
, kd->name); return -1; }
;
473 strncpy(tokBuf, parsedToken, sizeof(tokBuf));
474 tokBuf[sizeof(tokBuf) - 1] = '\0';
475 buf_p = tokBuf;
476
477 partToken = Com_Parse(&buf_p);
478
479 if (Q_streq("SIGNED", partToken)(strcmp("SIGNED", partToken) == 0)) {
480 partToken = Com_Parse(&buf_p);/* get next token */
481 } else if (Q_streq("UNSIGNED", partToken)(strcmp("UNSIGNED", partToken) == 0)) {
482 kd->flags |= ED_INSIST_POSITIVE(1<<9);
483 partToken = Com_Parse(&buf_p);
484 }
485
486 if (partToken[0] != '\0') {
487 type = ED_Type2Constant(partToken);
488 ED_PASS_ERROR(type)if ((type) == -1) { return -1; };
489 } else {/* default is string */
490 type = ED_TYPE_STRING(1<<5);
491 }
492
493 kd->flags |= type;
494 partToken = Com_Parse(&buf_p);
495 vectorLen = atoi(partToken);
496 if (vectorLen)
497 ED_TEST_RETURN_ERROR(ED_ERROR == ED_CheckNumber(partToken, ED_TYPE_INT, 1, &parsedNumber),if (-1 == ED_CheckNumber(partToken, (1<<4), 1, &parsedNumber
)) { snprintf(lastErr, sizeof(lastErr), "ED_ParseType: problem with vector length \"%s\" in key %s"
, partToken, kd->name); return -1; }
498 "ED_ParseType: problem with vector length \"%s\" in key %s",if (-1 == ED_CheckNumber(partToken, (1<<4), 1, &parsedNumber
)) { snprintf(lastErr, sizeof(lastErr), "ED_ParseType: problem with vector length \"%s\" in key %s"
, partToken, kd->name); return -1; }
499 partToken, kd->name)if (-1 == ED_CheckNumber(partToken, (1<<4), 1, &parsedNumber
)) { snprintf(lastErr, sizeof(lastErr), "ED_ParseType: problem with vector length \"%s\" in key %s"
, partToken, kd->name); return -1; }
;
500 kd->vLen = strlen(partToken) ? (vectorLen ? vectorLen : 1) : 1; /* default is 1 */
501 return ED_OK1;
502}
503
504/**
505 * @brief converts a block name (eg "optional") to an constant (eg ED_OPTIONAL).
506 * @return the parse mode or ED_ERROR
507 */
508static int ED_Block2Constant (const char *blockName)
509{
510 if (Q_streq("optional", blockName)(strcmp("optional", blockName) == 0))
511 return ED_OPTIONAL(1<<0);
512 else if (Q_streq("mandatory", blockName)(strcmp("mandatory", blockName) == 0))
513 return ED_MANDATORY(1<<1);
514 else if (Q_streq("abstract", blockName)(strcmp("abstract", blockName) == 0))
515 return ED_ABSTRACT(1<<2);
516 else if (Q_streq("default", blockName)(strcmp("default", blockName) == 0))
517 return ED_DEFAULT(1<<6);
518 else if (Q_streq("type", blockName)(strcmp("type", blockName) == 0))
519 return ED_MODE_TYPE(1<<7);
520 else if (Q_streq("range", blockName)(strcmp("range", blockName) == 0))
521 return ED_RANGE(1<<8);
522 else
523 ED_RETURN_ERROR("parse mode not recognised"){ snprintf(lastErr, sizeof(lastErr), "parse mode not recognised"
); return -1; }
;
524}
525
526/**
527 * @brief converts an internal constant integer to a string
528 * representation of a type (eg V_FLOAT)
529 * @return the string, or NULL if the integer is not recognised.
530 */
531static const char *ED_Constant2Block (int constInt)
532{
533 switch (constInt) {
534 case ED_OPTIONAL(1<<0):
535 return "optional";
536 case ED_MANDATORY(1<<1):
537 return "mandatory";
538 case ED_ABSTRACT(1<<2):
539 return "abstract";
540 case ED_DEFAULT(1<<6):
541 return "default";
542 case ED_MODE_TYPE(1<<7):
543 return "type";
544 case ED_RANGE(1<<8):
545 return "range";
546 default:
547 snprintf(lastErr, sizeof(lastErr), "ED_Constant2Block: constant not recognised");
548 return NULL__null;
549 }
550}
551
552static int ED_AllocRange (entityKeyDef_t *kd, const char *rangeStr)
553{
554 entityKeyRange_t **newRanges;
555 /* start a new range */
556 char *newStr = strdup(rangeStr);
557 entityKeyRange_t *newRange = (entityKeyRange_t *)malloc(sizeof(entityKeyRange_t));
31
Memory is allocated
558 OBJZERO(*newRange)(memset(&((*newRange)), (0), sizeof((*newRange))));
559 /* resize array of pointers */
560 newRanges = (entityKeyRange_t **)malloc((kd->numRanges + 1) * sizeof(entityKeyRange_t *));
561 ED_TEST_RETURN_ERROR(!newRanges || !newStr || !newRange, "ED_AllocRange: out of memory")if (!newRanges || !newStr || !newRange) { snprintf(lastErr, sizeof
(lastErr), "ED_AllocRange: out of memory"); return -1; }
;
32
Within the expansion of the macro 'ED_TEST_RETURN_ERROR':
a
Memory is never released; potential leak of memory pointed to by 'newRange'
562 newRange->str = newStr;
563 newRanges[kd->numRanges] = newRange;
564 if (kd->ranges) {
565 memcpy(newRanges, kd->ranges, kd->numRanges * sizeof(entityKeyRange_t *));
566 free(kd->ranges);
567 }
568 kd->numRanges++;
569 kd->ranges = newRanges;
570 return ED_OK1;
571}
572
573/**
574 * @return ED_ERROR or ED_OK.
575 */
576static int ED_PairParsed (entityKeyDef_t keyDefsBuf[], int *numKeyDefsSoFar_p,
577 const char *newName, const char *newVal, const int mode)
578{
579 /* check if there is already a key def */
580 entityKeyDef_t *keyDef = ED_FindKeyDefInArray(keyDefsBuf, *numKeyDefsSoFar_p, newName, mode);
581
582 /* create one if required */
583 if (!keyDef) {
28
Taking true branch
584 keyDef = &keyDefsBuf[(*numKeyDefsSoFar_p)++];
585 ED_TEST_RETURN_ERROR(*numKeyDefsSoFar_p >= ED_MAX_KEYS_PER_ENT, "ED_PairParsed: too many keys for buffer")if (*numKeyDefsSoFar_p >= 32) { snprintf(lastErr, sizeof(lastErr
), "ED_PairParsed: too many keys for buffer"); return -1; }
;
586 keyDef->name = strdup(newName);
587 ED_TEST_RETURN_ERROR(!keyDef->name, "ED_PairParsed: out of memory")if (!keyDef->name) { snprintf(lastErr, sizeof(lastErr), "ED_PairParsed: out of memory"
); return -1; }
;
588 }
589
590 /* multiple range defs are permitted, different elements can have different ranges */
591 ED_TEST_RETURN_ERROR(keyDef->flags & mode & ~ED_RANGE,if (keyDef->flags & mode & ~(1<<8)) { snprintf
(lastErr, sizeof(lastErr), "Duplicate %s for %s key. second value: %s"
, ED_Constant2Block(mode), newName, newVal); return -1; }
592 "Duplicate %s for %s key. second value: %s", ED_Constant2Block(mode), newName, newVal)if (keyDef->flags & mode & ~(1<<8)) { snprintf
(lastErr, sizeof(lastErr), "Duplicate %s for %s key. second value: %s"
, ED_Constant2Block(mode), newName, newVal); return -1; }
;
593
594 keyDef->flags |= mode;
595
596 /* store information */
597 switch (mode) {
29
Control jumps to 'case 256:' at line 613
598 case ED_MANDATORY(1<<1):
599 case ED_OPTIONAL(1<<0):
600 case ED_ABSTRACT(1<<2): /* intentional fall-through */
601 keyDef->desc = strdup(newVal);
602 ED_TEST_RETURN_ERROR(!keyDef->desc, "ED_PairParsed: out of memory while storing string.")if (!keyDef->desc) { snprintf(lastErr, sizeof(lastErr), "ED_PairParsed: out of memory while storing string."
); return -1; }
;
603 return ED_OK1;
604 case ED_DEFAULT(1<<6):
605 keyDef->defaultVal = strdup(newVal);
606 ED_TEST_RETURN_ERROR(!keyDef->defaultVal, "ED_PairParsed: out of memory while storing string.")if (!keyDef->defaultVal) { snprintf(lastErr, sizeof(lastErr
), "ED_PairParsed: out of memory while storing string."); return
-1; }
;
607 return ED_OK1;
608 case ED_MODE_TYPE(1<<7):
609 /* only optional or mandatory keys may have types, not possible to test for this here,
610 * as the type block may come before the optional or mandatory block */
611 ED_PASS_ERROR(ED_ParseType(keyDef, newVal))if ((ED_ParseType(keyDef, newVal)) == -1) { return -1; };
612 return ED_OK1;
613 case ED_RANGE(1<<8):
614 /* only typed keys may have ranges, this may only be tested after
615 * the whole ent has been parsed: the blocks may come in any order */
616 ED_PASS_ERROR(ED_AllocRange(keyDef, newVal))if ((ED_AllocRange(keyDef, newVal)) == -1) { return -1; };
30
Within the expansion of the macro 'ED_PASS_ERROR':
a
Calling 'ED_AllocRange'
617 return ED_OK1;
618 default:
619 ED_RETURN_ERROR("ED_PairParsed: parse mode not recognised"){ snprintf(lastErr, sizeof(lastErr), "ED_PairParsed: parse mode not recognised"
); return -1; }
;
620 }
621}
622
623/**
624 * @return ED_ERROR or ED_OK
625 */
626static int ED_ParseEntities (const char **data_p)
627{
628 int braceLevel = 0;
629 int tokensOnLevel0 = 0;
630 int mode = ED_ABSTRACT(1<<2);
631 entityKeyDef_t keyDefBuf[ED_MAX_KEYS_PER_ENT32];
632 char lastTokenBuf[ED_MAX_TOKEN_LEN512];
633 int keyIndex = 0;
634 int toggle = 0; /* many lines should have a pair of tokens on, this toggles 0, 1 to indicate progress */
635
636 while (data_p) {
3
Loop condition is true. Entering loop body
7
Loop condition is true. Entering loop body
15
Loop condition is true. Entering loop body
21
Loop condition is true. Entering loop body
637 const char *parsedToken = Com_Parse(data_p);
638 toggle ^= 1;
639
640 if (parsedToken[0] == '\0' && braceLevel == 0)
4
Taking false branch
8
Taking false branch
16
Taking false branch
22
Taking false branch
641 break;
642
643 if (parsedToken[0] == '{') {
5
Taking true branch
9
Taking false branch
17
Taking false branch
23
Taking false branch
644 braceLevel++;
645 ED_TEST_RETURN_ERROR(braceLevel > 2, "Too many open braces, nested %i deep", braceLevel)if (braceLevel > 2) { snprintf(lastErr, sizeof(lastErr), "Too many open braces, nested %i deep"
, braceLevel); return -1; }
;
646 ED_TEST_RETURN_ERROR(!toggle, "ED_ParseEntities: Incorrect number of tokens before '{'")if (!toggle) { snprintf(lastErr, sizeof(lastErr), "ED_ParseEntities: Incorrect number of tokens before '{'"
); return -1; }
;
647 toggle ^= 1; /* reset, as toggle is only for counting proper text tokens, not braces */
648 tokensOnLevel0 = 0;
649 continue;
6
Execution continues on line 636
650 }
651
652 if (parsedToken[0] == '}') {
10
Taking false branch
18
Taking false branch
24
Taking false branch
653 braceLevel--;
654 ED_TEST_RETURN_ERROR(braceLevel < 0, "Too many close braces. after %i entities", numEntityDefs)if (braceLevel < 0) { snprintf(lastErr, sizeof(lastErr), "Too many close braces. after %i entities"
, numEntityDefs); return -1; }
;
655 toggle ^= 1; /* reset, as toggle is only for counting proper text tokens, not braces */
656 if (braceLevel == 0) { /* finished parsing entity def and prepare for the next one */
657 ED_PASS_ERROR(ED_AllocEntityDef(keyDefBuf, keyIndex, numEntityDefs))if ((ED_AllocEntityDef(keyDefBuf, keyIndex, numEntityDefs)) ==
-1) { return -1; }
;
658 numEntityDefs++;
659 ED_TEST_RETURN_ERROR(numEntityDefs >= ED_MAX_DEFS, "ED_ParseEntities: too many entity defs for buffer")if (numEntityDefs >= 64) { snprintf(lastErr, sizeof(lastErr
), "ED_ParseEntities: too many entity defs for buffer"); return
-1; }
;
660 }
661 if (braceLevel == 1) /* ending a default, mandatory, etc block, go back to default parse mode */
662 mode = ED_ABSTRACT(1<<2);
663
664 continue;
665 }
666
667 if (braceLevel == 0) {
11
Taking false branch
19
Taking false branch
25
Taking false branch
668 if (tokensOnLevel0 == 0 && !Q_streq(parsedToken, "entity")(strcmp(parsedToken, "entity") == 0))
669 ED_RETURN_ERROR( "Next entity expected, found \"%s\"", parsedToken){ snprintf(lastErr, sizeof(lastErr), "Next entity expected, found \"%s\""
, parsedToken); return -1; }
;
670
671 if (tokensOnLevel0 == 1) {/* classname of entity, start parsing new entity */
672 const entityDef_t *prevED = ED_GetEntityDef(parsedToken);
673 ED_TEST_RETURN_ERROR(prevED, "ED_ParseEntities: duplicate entity definition \"%s\"", parsedToken)if (prevED) { snprintf(lastErr, sizeof(lastErr), "ED_ParseEntities: duplicate entity definition \"%s\""
, parsedToken); return -1; }
;
674 OBJZERO(keyDefBuf)(memset(&((keyDefBuf)), (0), sizeof((keyDefBuf)))); /* ensure pointers are not carried over from previous entity */
675 keyIndex = 0;
676 ED_PASS_ERROR(ED_PairParsed(keyDefBuf, &keyIndex, "classname", parsedToken, ED_MANDATORY))if ((ED_PairParsed(keyDefBuf, &keyIndex, "classname", parsedToken
, (1<<1))) == -1) { return -1; }
;
677 mode = ED_ABSTRACT(1<<2);
678 }
679
680 if (tokensOnLevel0 > 1)
681 ED_RETURN_ERROR( "Start of entity block expected found \"%s\"", parsedToken){ snprintf(lastErr, sizeof(lastErr), "Start of entity block expected found \"%s\""
, parsedToken); return -1; }
;
682
683 tokensOnLevel0++;
684 } else { /* braceLevel > 0 */
685 const int newMode = ED_Block2Constant(parsedToken);
686 if (ED_ERROR-1 == newMode) { /* must be a key name or value */
12
Taking true branch
20
Taking false branch
26
Taking true branch
687 if (toggle) { /* store key name til after next token is parsed */
13
Taking true branch
27
Taking false branch
688 if ('\0' == parsedToken[0])
14
Taking false branch
689 ED_RETURN_ERROR("key name null string, \"\", or missing closing brace"){ snprintf(lastErr, sizeof(lastErr), "key name null string, \"\", or missing closing brace"
); return -1; }
;
690 strncpy(lastTokenBuf, parsedToken, sizeof(lastTokenBuf));
691 lastTokenBuf[sizeof(lastTokenBuf) - 1] = '\0';
692 } else { /* store key-value pair in buffer until whole entity is parsed */
693 ED_PASS_ERROR(ED_PairParsed(keyDefBuf, &keyIndex, lastTokenBuf, parsedToken, mode))if ((ED_PairParsed(keyDefBuf, &keyIndex, lastTokenBuf, parsedToken
, mode)) == -1) { return -1; }
;
694 }
695 } else {
696 mode = newMode;
697 toggle ^= 1; /* start of a mode changing block is the only time that only one non-brace token is on a line */
698 }
699 }
700 }
701
702 return ED_OK1;
703}
704
705/**
706 * @brief checks if the default block entries meet the type and range definitions.
707 * @return ED_ERROR or ED_OK
708 * @sa CheckLastError
709 */
710static int ED_CheckDefaultTypes (void)
711{
712 const entityDef_t *ed;
713 const entityKeyDef_t *kd;
714 for (ed = entityDefs; ed->numKeyDefs; ed++)
715 for (kd = ed->keyDefs; kd->name; kd++)
716 if (kd->defaultVal)
717 ED_PASS_ERROR_EXTRAMSG(ED_CheckKey(kd, kd->defaultVal),if ((ED_CheckKey(kd, kd->defaultVal)) == -1) { snprintf(lastErrExtra
, sizeof(lastErr), " while checking default block entry agrees with type"
); strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr
) -1); return -1; }
718 " while checking default block entry agrees with type")if ((ED_CheckKey(kd, kd->defaultVal)) == -1) { snprintf(lastErrExtra
, sizeof(lastErr), " while checking default block entry agrees with type"
); strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr
) -1); return -1; }
719
720 return ED_OK1;
721}
722
723/**
724 * @brief finish parsing ranges. Could not be done earlier as would not have necessarily known
725 * types and defaults. parses values in ranges into ints or floats and tests ranges against types
726 * and defaults against ranges.
727 * @return ED_ERROR or ED_OK
728 */
729static int ED_ProcessRanges (void)
730{
731 static int ibuf[32];
732 static float fbuf[32];
733
734 entityDef_t *ed;
735 for (ed = entityDefs; ed->numKeyDefs; ed++) {
736 entityKeyDef_t *kd;
737 for (kd = ed->keyDefs; kd->name; kd++) {
738 const int keyType = kd->flags & ED_KEY_TYPE((1<<3) | (1<<4) | (1<<5));
739 int i;
740 for (i = 0; i < kd->numRanges ;i++) {
741 int numElements = 0;
742 entityKeyRange_t *kr = kd->ranges[i];
743 const char *tmpRange_p = kr->str;
744 ED_TEST_RETURN_ERROR(!keyType || (keyType & ED_TYPE_STRING), "ED_ProcessRanges: ranges may not be specified for strings. note that V_STRING is the default type. %s in %s",if (!keyType || (keyType & (1<<5))) { snprintf(lastErr
, sizeof(lastErr), "ED_ProcessRanges: ranges may not be specified for strings. note that V_STRING is the default type. %s in %s"
, kd->name, ed->classname); return -1; }
745 kd->name, ed->classname)if (!keyType || (keyType & (1<<5))) { snprintf(lastErr
, sizeof(lastErr), "ED_ProcessRanges: ranges may not be specified for strings. note that V_STRING is the default type. %s in %s"
, kd->name, ed->classname); return -1; }
;
746 while (tmpRange_p) {
747 int_float_tu parsedNumber;
748 const char *tok = Com_Parse(&tmpRange_p);
749 if (tok[0] == '\0')
750 break;
751 if (Q_streq("-", tok)(strcmp("-", tok) == 0)) {
752 kr->continuous = 1;
753 ED_TEST_RETURN_ERROR(numElements != 1, "ED_ProcessRanges: problem with continuous range, \"%s\" in %s in %s",if (numElements != 1) { snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: problem with continuous range, \"%s\" in %s in %s"
, kr->str, kd->name, ed->classname); return -1; }
754 kr->str, kd->name, ed->classname)if (numElements != 1) { snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: problem with continuous range, \"%s\" in %s in %s"
, kr->str, kd->name, ed->classname); return -1; }
;
755 continue;
756 }
757 ED_PASS_ERROR(ED_CheckNumber(tok, keyType, kd->flags & ED_INSIST_POSITIVE, &parsedNumber))if ((ED_CheckNumber(tok, keyType, kd->flags & (1<<
9), &parsedNumber)) == -1) { return -1; }
;
758 switch (keyType) {
759 case ED_TYPE_INT(1<<4):
760 ibuf[numElements++] = atoi(tok);
761 break;
762 case ED_TYPE_FLOAT(1<<3):
763 fbuf[numElements++] = atof(tok);
764 break;
765 default:
766 ED_RETURN_ERROR("ED_ProcessRanges: unexpected type"){ snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: unexpected type"
); return -1; }
;
767 }
768 }
769 kr->numElements = numElements;
770 ED_TEST_RETURN_ERROR(kr->continuous && numElements != 2,if (kr->continuous && numElements != 2) { snprintf
(lastErr, sizeof(lastErr), "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s"
, kr->str, kd->name, ed->classname); return -1; }
771 "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s",if (kr->continuous && numElements != 2) { snprintf
(lastErr, sizeof(lastErr), "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s"
, kr->str, kd->name, ed->classname); return -1; }
772 kr->str, kd->name, ed->classname)if (kr->continuous && numElements != 2) { snprintf
(lastErr, sizeof(lastErr), "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s"
, kr->str, kd->name, ed->classname); return -1; }
;
773 if (ED_TYPE_INT(1<<4) == keyType) {
774 const size_t size = numElements * sizeof(int);
775 kr->iArr = (int *)malloc(size);
776 ED_TEST_RETURN_ERROR(!kr->iArr, "ED_ProcessRanges: out of memory")if (!kr->iArr) { snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: out of memory"
); return -1; }
;
777 memcpy(kr->iArr, ibuf, size);
778 } else { /* ED_TYPE_FLOAT */
779 const size_t size = numElements * sizeof(float);
780 kr->fArr = (float *)malloc(size);
781 ED_TEST_RETURN_ERROR(!kr->fArr, "ED_ProcessRanges: out of memory")if (!kr->fArr) { snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: out of memory"
); return -1; }
;
782 memcpy(kr->fArr, fbuf, size);
783 }
784 }
785 ED_TEST_RETURN_ERROR(kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges,if (kd->numRanges && kd->numRanges != 1 &&
kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof(
lastErr), "ED_ProcessRanges: if range definitions are supplied, "
"there must be one (which is applied to each element of a vector), "
"or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions"
, ed->classname, kd->name, kd->vLen, kd->numRanges
); return -1; }
786 "ED_ProcessRanges: if range definitions are supplied, "if (kd->numRanges && kd->numRanges != 1 &&
kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof(
lastErr), "ED_ProcessRanges: if range definitions are supplied, "
"there must be one (which is applied to each element of a vector), "
"or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions"
, ed->classname, kd->name, kd->vLen, kd->numRanges
); return -1; }
787 "there must be one (which is applied to each element of a vector), "if (kd->numRanges && kd->numRanges != 1 &&
kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof(
lastErr), "ED_ProcessRanges: if range definitions are supplied, "
"there must be one (which is applied to each element of a vector), "
"or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions"
, ed->classname, kd->name, kd->vLen, kd->numRanges
); return -1; }
788 "or one for each element of the vector. "if (kd->numRanges && kd->numRanges != 1 &&
kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof(
lastErr), "ED_ProcessRanges: if range definitions are supplied, "
"there must be one (which is applied to each element of a vector), "
"or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions"
, ed->classname, kd->name, kd->vLen, kd->numRanges
); return -1; }
789 "%s in %s has %i elements in vector and %i range definitions",if (kd->numRanges && kd->numRanges != 1 &&
kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof(
lastErr), "ED_ProcessRanges: if range definitions are supplied, "
"there must be one (which is applied to each element of a vector), "
"or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions"
, ed->classname, kd->name, kd->vLen, kd->numRanges
); return -1; }
790 ed->classname, kd->name, kd->vLen, kd->numRanges)if (kd->numRanges && kd->numRanges != 1 &&
kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof(
lastErr), "ED_ProcessRanges: if range definitions are supplied, "
"there must be one (which is applied to each element of a vector), "
"or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions"
, ed->classname, kd->name, kd->vLen, kd->numRanges
); return -1; }
;
791 }
792 }
793 return ED_OK1;
794}
795
796/**
797 * parses entity definitions from entities.ufo
798 * @return ED_OK or ED_ERR
799 * @sa ED_GetLastErr
800 */
801int ED_Parse (const char *data_p)
802{
803 /* only do this once, repeat calls are OK */
804 static int done = 0;
805 if (done)
1
Taking false branch
806 return ED_OK1;
807
808 snprintf(lastErr, sizeof(lastErr), "no error");
809 /* Zero out so that looping through the ones that have already
810 * been parsed is possible while the rest are parsed */
811 OBJZERO(entityDefs)(memset(&((entityDefs)), (0), sizeof((entityDefs))));
812 numEntityDefs = 0;
813
814 ED_PASS_ERROR(ED_ParseEntities(&data_p))if ((ED_ParseEntities(&data_p)) == -1) { return -1; };
2
Within the expansion of the macro 'ED_PASS_ERROR':
a
Calling 'ED_ParseEntities'
815 ED_TEST_RETURN_ERROR(numEntityDefs == 0, "ED_Parse: Zero entity definitions found")if (numEntityDefs == 0) { snprintf(lastErr, sizeof(lastErr), "ED_Parse: Zero entity definitions found"
); return -1; }
;
816
817 ED_PASS_ERROR(ED_ProcessRanges())if ((ED_ProcessRanges()) == -1) { return -1; };
818
819 ED_PASS_ERROR(ED_CheckDefaultTypes())if ((ED_CheckDefaultTypes()) == -1) { return -1; };
820
821 done = 1; /* do not do it again */
822
823#ifdef DEBUG_ED
824 ED_Dump();
825#endif
826
827 return ED_OK1;
828}
829
830const char *ED_GetLastError (void)
831{
832 return lastErr;
833}
834
835/**
836 * @brief searches for the parsed key def
837 * @param abstract send abstract to find an abstract key with this name
838 * @return NULL if the entity def or key def is not found. call ED_GetLastError to get a relevant message.
839 */
840const entityKeyDef_t *ED_GetKeyDef (const char *classname, const char *keyname, const int abstract)
841{
842 const entityDef_t *ed = ED_GetEntityDef(classname);
843 return ED_GetKeyDefEntity(ed, keyname, abstract);
844}
845
846/**
847 * @brief searches for the parsed key def, when the entity def is known
848 * @param abstract send a nonzero value if the abstract (radiant - not in any block) version of the
849 * key is required
850 * @return NULL if the entity def or key def is not found. call ED_GetLastError to get a relevant message.
851 */
852const entityKeyDef_t *ED_GetKeyDefEntity (const entityDef_t *ed, const char *keyname, const int abstract)
853{
854 const entityKeyDef_t *kd;
855
856 if (!ed)
857 return NULL__null;
858
859 for (kd = ed->keyDefs; kd->name; kd++)
860 if (Q_streq(keyname, kd->name)(strcmp(keyname, kd->name) == 0)) {
861 if (abstract) {
862 if (kd->flags & ED_ABSTRACT(1<<2))
863 return kd;
864 } else {
865 if (!(kd->flags & ED_ABSTRACT(1<<2)))
866 return kd;
867 }
868 }
869
870 snprintf(lastErr, sizeof(lastErr), "ED_GetKeyDefEntity: no key definition for %s found in entity %s entities.ufo", keyname, ed->classname);
871 return NULL__null;
872}
873
874/**
875 * @brief searches for the parsed entity def by classname
876 * @return NULL if the entity def is not found. call ED_GetLastError to get a relevant message.
877 */
878const entityDef_t *ED_GetEntityDef (const char *classname)
879{
880 const entityDef_t *ed;
881
882 for (ed = entityDefs; ed->numKeyDefs; ed++)
883 if (Q_streq(classname, ed->classname)(strcmp(classname, ed->classname) == 0))
884 return ed;
885
886 snprintf(lastErr, sizeof(lastErr), "ED_GetEntityDef: no entity definition for %s found in entities.ufo", classname);
887 return NULL__null;
888}
889
890void ED_Free (void)
891{
892 if (numEntityDefs) {
893 for (entityDef_t* ed = entityDefs; ed->numKeyDefs; ++ed) {
894 free(ed->classname);
895 for (entityKeyDef_t* kd = ed->keyDefs; kd->name; ++kd) {
896 free(kd->name);
897 free(kd->desc);
898 free(kd->defaultVal);
899 if (kd->numRanges) {
900 int i;
901 for (i = 0; i < kd->numRanges ;i++) {
902 entityKeyRange_t *kr = kd->ranges[i];
903 free(kr->iArr);
904 free(kr->fArr);
905 free(kr->str);
906 free(kr);
907 }
908 free(kd->ranges);
909 }
910 }
911 free(ed->keyDefs);
912 }
913 }
914}
915
916#ifdef DEBUG_ED
917
918void ED_PrintRange (const entityKeyRange_t *kr)
919{
920 int i;
921 printf(" rge:%c:%c>", kr->continuous ? 'c' : 'd', kr->iArr ? 'i' : 'f');
922 for (i = 0; i < kr->numElements; i++) {
923 if (kr->iArr)
924 printf("%i ", kr->iArr[i]);
925 else
926 printf("%.1f ",kr->fArr[i]);
927 }
928 printf("\b<");
929}
930
931void ED_PrintParsedDefault (const entityKeyDef_t *kd)
932{
933 float fa[32];
934 int ia[32];
935 char ca[128];
936 int i;
937
938 if (!kd->defaultVal)
939 return;
940
941 printf(" parsed default>");
942
943 switch (kd->flags & ED_KEY_TYPE((1<<3) | (1<<4) | (1<<5))) {
944 case ED_TYPE_INT(1<<4):
945 ED_GetDefaultInt(ia, kd->vLen, kd);
946 for (i = 0; i < kd->vLen; i++)
947 printf("%i ", ia[i]);
948 break;
949 case ED_TYPE_FLOAT(1<<3):
950 ED_GetDefaultFloat(fa, kd->vLen, kd);
951 for (i = 0; i < kd->vLen; i++)
952 printf("%f ", fa[i]);
953 break;
954 case ED_TYPE_STRING(1<<5):
955 ED_GetDefaultString(ca, sizeof(ca) - 1, kd);
956 printf("%s", ca);
957 break;
958 }
959
960 printf("<");
961}
962
963void ED_PrintKeyDef (const entityKeyDef_t *kd)
964{
965 printf(" >%s< mandtry:%i opt:%i abst:%i type:%i", kd->name,
966 kd->flags & ED_MANDATORY(1<<1) ? 1 : 0,
967 kd->flags & ED_OPTIONAL(1<<0) ? 1 : 0,
968 kd->flags & ED_ABSTRACT(1<<2) ? 1 : 0,
969 kd->flags & ED_MODE_TYPE(1<<7) ? 1 : 0);
970 if (kd->flags & ED_MODE_TYPE(1<<7))
971 printf(" type:%s[%i]", ED_Constant2Type(kd->flags & ED_KEY_TYPE((1<<3) | (1<<4) | (1<<5))), kd->vLen);
972
973 if (kd->defaultVal)
974 printf(" default>%s<",kd->defaultVal);
975
976 if (kd->numRanges) {
977 int i;
978 for (i = 0; i < kd->numRanges; i++) {
979 ED_PrintRange(kd->ranges[i]);
980 }
981 }
982
983 ED_PrintParsedDefault(kd);
984
985 printf("\n");
986}
987
988/**
989 * @brief print all the parsed entity definitions
990 */
991void ED_Dump (void)
992{
993 const entityDef_t *ed;
994 printf("ED_Dump:\n");
995 for (ed = entityDefs; ed->numKeyDefs; ed++) {
996 const entityKeyDef_t *kd;
997 printf("entity def >%s<\n", ed->classname);
998 for (kd = &ed->keyDefs[0]; kd->name; kd++) {
999 ED_PrintKeyDef(kd);
1000 }
1001 }
1002}
1003#endif