q_parse.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. // q_parse.c -- support for parsing text files
  19. #include "q_shared.h"
  20. /*
  21. ============================================================================
  22. PARSING
  23. ============================================================================
  24. */
  25. // multiple character punctuation tokens
  26. static const char *punctuation[] = {
  27. "+=", "-=", "*=", "/=", "&=", "|=", "++", "--",
  28. "&&", "||", "<=", ">=", "==", "!=",
  29. NULL
  30. };
  31. typedef struct {
  32. char token[MAX_TOKEN_CHARS];
  33. int lines;
  34. qboolean ungetToken;
  35. char parseFile[MAX_QPATH];
  36. } parseInfo_t;
  37. #define MAX_PARSE_INFO 16
  38. static parseInfo_t parseInfo[MAX_PARSE_INFO];
  39. static int parseInfoNum;
  40. static parseInfo_t *pi = &parseInfo[0];
  41. /*
  42. ===================
  43. Com_BeginParseSession
  44. ===================
  45. */
  46. void Com_BeginParseSession( const char *filename ) {
  47. if ( parseInfoNum == MAX_PARSE_INFO - 1 ) {
  48. Com_Error( ERR_FATAL, "Com_BeginParseSession: session overflow" );
  49. }
  50. parseInfoNum++;
  51. pi = &parseInfo[parseInfoNum];
  52. pi->lines = 1;
  53. Q_strncpyz( pi->parseFile, filename, sizeof( pi->parseFile ) );
  54. }
  55. /*
  56. ===================
  57. Com_EndParseSession
  58. ===================
  59. */
  60. void Com_EndParseSession( void ) {
  61. if ( parseInfoNum == 0 ) {
  62. Com_Error( ERR_FATAL, "Com_EndParseSession: session underflow" );
  63. }
  64. parseInfoNum--;
  65. pi = &parseInfo[parseInfoNum];
  66. }
  67. /*
  68. ===================
  69. Com_GetCurrentParseLine
  70. ===================
  71. */
  72. int Com_GetCurrentParseLine( void ) {
  73. return pi->lines;
  74. }
  75. /*
  76. ===================
  77. Com_ScriptError
  78. Prints the script name and line number in the message
  79. ===================
  80. */
  81. void Com_ScriptError( const char *msg, ... ) {
  82. va_list argptr;
  83. char string[32000];
  84. va_start( argptr, msg );
  85. vsprintf( string, msg,argptr );
  86. va_end( argptr );
  87. Com_Error( ERR_DROP, "File %s, line %i: %s", pi->parseFile, pi->lines, string );
  88. }
  89. void Com_ScriptWarning( const char *msg, ... ) {
  90. va_list argptr;
  91. char string[32000];
  92. va_start( argptr, msg );
  93. vsprintf( string, msg,argptr );
  94. va_end( argptr );
  95. Com_Printf( "File %s, line %i: %s", pi->parseFile, pi->lines, string );
  96. }
  97. /*
  98. ===================
  99. Com_UngetToken
  100. Calling this will make the next Com_Parse return
  101. the current token instead of advancing the pointer
  102. ===================
  103. */
  104. void Com_UngetToken( void ) {
  105. if ( pi->ungetToken ) {
  106. Com_ScriptError( "UngetToken called twice" );
  107. }
  108. pi->ungetToken = qtrue;
  109. }
  110. static const char *SkipWhitespace( const char (*data), qboolean *hasNewLines ) {
  111. int c;
  112. while( (c = *data) <= ' ') {
  113. if( !c ) {
  114. return NULL;
  115. }
  116. if( c == '\n' ) {
  117. pi->lines++;
  118. *hasNewLines = qtrue;
  119. }
  120. data++;
  121. }
  122. return data;
  123. }
  124. /*
  125. ==============
  126. Com_ParseExt
  127. Parse a token out of a string
  128. Will never return NULL, just empty strings.
  129. An empty string will only be returned at end of file.
  130. If "allowLineBreaks" is qtrue then an empty
  131. string will be returned if the next token is
  132. a newline.
  133. ==============
  134. */
  135. static char *Com_ParseExt( const char *(*data_p), qboolean allowLineBreaks ) {
  136. int c = 0, len;
  137. qboolean hasNewLines = qfalse;
  138. const char *data;
  139. const char **punc;
  140. if ( !data_p ) {
  141. Com_Error( ERR_FATAL, "Com_ParseExt: NULL data_p" );
  142. }
  143. data = *data_p;
  144. len = 0;
  145. pi->token[0] = 0;
  146. // make sure incoming data is valid
  147. if ( !data ) {
  148. *data_p = NULL;
  149. return pi->token;
  150. }
  151. // skip any leading whitespace
  152. while ( 1 ) {
  153. // skip whitespace
  154. data = SkipWhitespace( data, &hasNewLines );
  155. if ( !data ) {
  156. *data_p = NULL;
  157. return pi->token;
  158. }
  159. if ( hasNewLines && !allowLineBreaks ) {
  160. *data_p = data;
  161. return pi->token;
  162. }
  163. c = *data;
  164. // skip double slash comments
  165. if ( c == '/' && data[1] == '/' ) {
  166. while (*data && *data != '\n') {
  167. data++;
  168. }
  169. continue;
  170. }
  171. // skip /* */ comments
  172. if ( c=='/' && data[1] == '*' ) {
  173. while ( *data && ( *data != '*' || data[1] != '/' ) ) {
  174. if( *data == '\n' ) {
  175. pi->lines++;
  176. }
  177. data++;
  178. }
  179. if ( *data ) {
  180. data += 2;
  181. }
  182. continue;
  183. }
  184. // a real token to parse
  185. break;
  186. }
  187. // handle quoted strings
  188. if ( c == '\"' ) {
  189. data++;
  190. while( 1 ) {
  191. c = *data++;
  192. if ( ( c=='\\' ) && ( *data == '\"' ) ) {
  193. // allow quoted strings to use \" to indicate the " character
  194. data++;
  195. } else if ( c=='\"' || !c ) {
  196. pi->token[len] = 0;
  197. *data_p = ( char * ) data;
  198. return pi->token;
  199. } else if( *data == '\n' ) {
  200. pi->lines++;
  201. }
  202. if ( len < MAX_TOKEN_CHARS - 1 ) {
  203. pi->token[len] = c;
  204. len++;
  205. }
  206. }
  207. }
  208. // check for a number
  209. // is this parsing of negative numbers going to cause expression problems
  210. if ( ( c >= '0' && c <= '9' ) || ( c == '-' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ||
  211. ( c == '.' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ) {
  212. do {
  213. if (len < MAX_TOKEN_CHARS - 1) {
  214. pi->token[len] = c;
  215. len++;
  216. }
  217. data++;
  218. c = *data;
  219. } while ( ( c >= '0' && c <= '9' ) || c == '.' );
  220. // parse the exponent
  221. if ( c == 'e' || c == 'E' ) {
  222. if (len < MAX_TOKEN_CHARS - 1) {
  223. pi->token[len] = c;
  224. len++;
  225. }
  226. data++;
  227. c = *data;
  228. if ( c == '-' || c == '+' ) {
  229. if (len < MAX_TOKEN_CHARS - 1) {
  230. pi->token[len] = c;
  231. len++;
  232. }
  233. data++;
  234. c = *data;
  235. }
  236. do {
  237. if (len < MAX_TOKEN_CHARS - 1) {
  238. pi->token[len] = c;
  239. len++;
  240. }
  241. data++;
  242. c = *data;
  243. } while ( c >= '0' && c <= '9' );
  244. }
  245. if (len == MAX_TOKEN_CHARS) {
  246. len = 0;
  247. }
  248. pi->token[len] = 0;
  249. *data_p = ( char * ) data;
  250. return pi->token;
  251. }
  252. // check for a regular word
  253. // we still allow forward and back slashes in name tokens for pathnames
  254. // and also colons for drive letters
  255. if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' || c == '/' || c == '\\' ) {
  256. do {
  257. if (len < MAX_TOKEN_CHARS - 1) {
  258. pi->token[len] = c;
  259. len++;
  260. }
  261. data++;
  262. c = *data;
  263. } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_'
  264. || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' );
  265. if (len == MAX_TOKEN_CHARS) {
  266. len = 0;
  267. }
  268. pi->token[len] = 0;
  269. *data_p = ( char * ) data;
  270. return pi->token;
  271. }
  272. // check for multi-character punctuation token
  273. for ( punc = punctuation ; *punc ; punc++ ) {
  274. int l;
  275. int j;
  276. l = strlen( *punc );
  277. for ( j = 0 ; j < l ; j++ ) {
  278. if ( data[j] != (*punc)[j] ) {
  279. break;
  280. }
  281. }
  282. if ( j == l ) {
  283. // a valid multi-character punctuation
  284. memcpy( pi->token, *punc, l );
  285. pi->token[l] = 0;
  286. data += l;
  287. *data_p = (char *)data;
  288. return pi->token;
  289. }
  290. }
  291. // single character punctuation
  292. pi->token[0] = *data;
  293. pi->token[1] = 0;
  294. data++;
  295. *data_p = (char *)data;
  296. return pi->token;
  297. }
  298. /*
  299. ===================
  300. Com_Parse
  301. ===================
  302. */
  303. const char *Com_Parse( const char *(*data_p) ) {
  304. if ( pi->ungetToken ) {
  305. pi->ungetToken = qfalse;
  306. return pi->token;
  307. }
  308. return Com_ParseExt( data_p, qtrue );
  309. }
  310. /*
  311. ===================
  312. Com_ParseOnLine
  313. ===================
  314. */
  315. const char *Com_ParseOnLine( const char *(*data_p) ) {
  316. if ( pi->ungetToken ) {
  317. pi->ungetToken = qfalse;
  318. return pi->token;
  319. }
  320. return Com_ParseExt( data_p, qfalse );
  321. }
  322. /*
  323. ==================
  324. Com_MatchToken
  325. ==================
  326. */
  327. void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) {
  328. const char *token;
  329. token = Com_Parse( buf_p );
  330. if ( strcmp( token, match ) ) {
  331. if (warning) {
  332. Com_ScriptWarning( "MatchToken: %s != %s", token, match );
  333. } else {
  334. Com_ScriptError( "MatchToken: %s != %s", token, match );
  335. }
  336. }
  337. }
  338. /*
  339. =================
  340. Com_SkipBracedSection
  341. The next token should be an open brace.
  342. Skips until a matching close brace is found.
  343. Internal brace depths are properly skipped.
  344. =================
  345. */
  346. void Com_SkipBracedSection( const char *(*program) ) {
  347. const char *token;
  348. int depth;
  349. depth = 0;
  350. do {
  351. token = Com_Parse( program );
  352. if( token[1] == 0 ) {
  353. if( token[0] == '{' ) {
  354. depth++;
  355. }
  356. else if( token[0] == '}' ) {
  357. depth--;
  358. }
  359. }
  360. } while( depth && *program );
  361. }
  362. /*
  363. =================
  364. Com_SkipRestOfLine
  365. =================
  366. */
  367. void Com_SkipRestOfLine ( const char *(*data) ) {
  368. const char *p;
  369. int c;
  370. p = *data;
  371. while ( (c = *p++) != 0 ) {
  372. if ( c == '\n' ) {
  373. pi->lines++;
  374. break;
  375. }
  376. }
  377. *data = p;
  378. }
  379. /*
  380. ====================
  381. Com_ParseRestOfLine
  382. ====================
  383. */
  384. const char *Com_ParseRestOfLine( const char *(*data_p) ) {
  385. static char line[MAX_TOKEN_CHARS];
  386. const char *token;
  387. line[0] = 0;
  388. while( 1 ) {
  389. token = Com_ParseOnLine( data_p );
  390. if ( !token[0] ) {
  391. break;
  392. }
  393. if ( line[0] ) {
  394. Q_strcat( line, sizeof(line), " " );
  395. }
  396. Q_strcat( line, sizeof(line), token );
  397. }
  398. return line;
  399. }
  400. float Com_ParseFloat( const char *(*buf_p) ) {
  401. const char *token;
  402. token = Com_Parse( buf_p );
  403. if ( !token[0] ) {
  404. return 0;
  405. }
  406. return atof( token );
  407. }
  408. int Com_ParseInt( const char *(*buf_p) ) {
  409. const char *token;
  410. token = Com_Parse( buf_p );
  411. if ( !token[0] ) {
  412. return 0;
  413. }
  414. return atof( token );
  415. }
  416. void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) {
  417. const char *token;
  418. int i;
  419. Com_MatchToken( buf_p, "(" );
  420. for (i = 0 ; i < x ; i++) {
  421. token = Com_Parse(buf_p);
  422. m[i] = atof(token);
  423. }
  424. Com_MatchToken( buf_p, ")" );
  425. }
  426. void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) {
  427. int i;
  428. Com_MatchToken( buf_p, "(" );
  429. for (i = 0 ; i < y ; i++) {
  430. Com_Parse1DMatrix (buf_p, x, m + i * x);
  431. }
  432. Com_MatchToken( buf_p, ")" );
  433. }
  434. void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) {
  435. int i;
  436. Com_MatchToken( buf_p, "(" );
  437. for (i = 0 ; i < z ; i++) {
  438. Com_Parse2DMatrix (buf_p, y, x, m + i * x*y);
  439. }
  440. Com_MatchToken( buf_p, ")" );
  441. }