Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

480 lines
9.6 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include <ctype.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include "tokenreader.h"
  11. #include "tier0/platform.h"
  12. #include "tier1/strtools.h"
  13. #include "tier0/dbg.h"
  14. //-----------------------------------------------------------------------------
  15. // Purpose:
  16. //-----------------------------------------------------------------------------
  17. TokenReader::TokenReader(void)
  18. {
  19. m_szFilename[0] = '\0';
  20. m_nLine = 1;
  21. m_nErrorCount = 0;
  22. m_bStuffed = false;
  23. }
  24. //-----------------------------------------------------------------------------
  25. // Purpose:
  26. // Input : *pszFilename -
  27. // Output : Returns true on success, false on failure.
  28. //-----------------------------------------------------------------------------
  29. bool TokenReader::Open(const char *pszFilename)
  30. {
  31. open(pszFilename, std::ios::in | std::ios::binary );
  32. Q_strncpy(m_szFilename, pszFilename, sizeof( m_szFilename ) );
  33. m_nLine = 1;
  34. m_nErrorCount = 0;
  35. m_bStuffed = false;
  36. return(is_open() != 0);
  37. }
  38. //-----------------------------------------------------------------------------
  39. // Purpose:
  40. //-----------------------------------------------------------------------------
  41. void TokenReader::Close()
  42. {
  43. close();
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Purpose:
  47. // Input : *error -
  48. // Output : const char
  49. //-----------------------------------------------------------------------------
  50. const char *TokenReader::Error(char *error, ...)
  51. {
  52. static char szErrorBuf[256];
  53. Q_snprintf(szErrorBuf, sizeof( szErrorBuf ), "File %s, line %d: ", m_szFilename, m_nLine);
  54. Q_strncat(szErrorBuf, error, sizeof( szErrorBuf ), COPY_ALL_CHARACTERS );
  55. m_nErrorCount++;
  56. return(szErrorBuf);
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Purpose:
  60. // Input : pszStore -
  61. // nSize -
  62. // Output : Returns true on success, false on failure.
  63. //-----------------------------------------------------------------------------
  64. trtoken_t TokenReader::GetString(char *pszStore, int nSize)
  65. {
  66. if (nSize <= 0)
  67. {
  68. return TOKENERROR;
  69. }
  70. char szBuf[1024];
  71. //
  72. // Until we reach the end of this string or run out of room in
  73. // the destination buffer...
  74. //
  75. while (true)
  76. {
  77. //
  78. // Fetch the next batch of text from the file.
  79. //
  80. get(szBuf, sizeof(szBuf), '\"');
  81. if (eof())
  82. {
  83. return TOKENEOF;
  84. }
  85. if (fail())
  86. {
  87. // Just means nothing was read (empty string probably "")
  88. clear();
  89. }
  90. //
  91. // Transfer the text to the destination buffer.
  92. //
  93. char *pszSrc = szBuf;
  94. while ((*pszSrc != '\0') && (nSize > 1))
  95. {
  96. if (*pszSrc == 0x0d)
  97. {
  98. //
  99. // Newline encountered before closing quote -- unterminated string.
  100. //
  101. *pszStore = '\0';
  102. return TOKENSTRINGTOOLONG;
  103. }
  104. else if (*pszSrc != '\\')
  105. {
  106. *pszStore = *pszSrc;
  107. pszSrc++;
  108. }
  109. else
  110. {
  111. //
  112. // Backslash sequence - replace with the appropriate character.
  113. //
  114. pszSrc++;
  115. if (*pszSrc == 'n')
  116. {
  117. *pszStore = '\n';
  118. }
  119. pszSrc++;
  120. }
  121. pszStore++;
  122. nSize--;
  123. }
  124. if (*pszSrc != '\0')
  125. {
  126. //
  127. // Ran out of room in the destination buffer. Skip to the close-quote,
  128. // terminate the string, and exit.
  129. //
  130. ignore(1024, '\"');
  131. *pszStore = '\0';
  132. return TOKENSTRINGTOOLONG;
  133. }
  134. //
  135. // Check for closing quote.
  136. //
  137. if (peek() == '\"')
  138. {
  139. //
  140. // Eat the close quote and any whitespace.
  141. //
  142. get();
  143. bool bCombineStrings = SkipWhiteSpace();
  144. //
  145. // Combine consecutive quoted strings if the combine strings character was
  146. // encountered between the two strings.
  147. //
  148. if (bCombineStrings && (peek() == '\"'))
  149. {
  150. //
  151. // Eat the open quote and keep parsing this string.
  152. //
  153. get();
  154. }
  155. else
  156. {
  157. //
  158. // Done with this string, terminate the string and exit.
  159. //
  160. *pszStore = '\0';
  161. return STRING;
  162. }
  163. }
  164. }
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Returns the next token, allocating enough memory to store the token
  168. // plus a terminating NULL.
  169. // Input : pszStore - Pointer to a string that will be allocated.
  170. // Output : Returns the type of token that was read, or TOKENERROR.
  171. //-----------------------------------------------------------------------------
  172. trtoken_t TokenReader::NextTokenDynamic(char **ppszStore)
  173. {
  174. char szTempBuffer[8192];
  175. trtoken_t eType = NextToken(szTempBuffer, sizeof(szTempBuffer));
  176. int len = Q_strlen(szTempBuffer) + 1;
  177. *ppszStore = new char [len];
  178. Assert( *ppszStore );
  179. Q_strncpy(*ppszStore, szTempBuffer, len );
  180. return(eType);
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose: Returns the next token.
  184. // Input : pszStore - Pointer to a string that will receive the token.
  185. // Output : Returns the type of token that was read, or TOKENERROR.
  186. //-----------------------------------------------------------------------------
  187. trtoken_t TokenReader::NextToken(char *pszStore, int nSize)
  188. {
  189. char *pStart = pszStore;
  190. if (!is_open())
  191. {
  192. return TOKENEOF;
  193. }
  194. //
  195. // If they stuffed a token, return that token.
  196. //
  197. if (m_bStuffed)
  198. {
  199. m_bStuffed = false;
  200. Q_strncpy( pszStore, m_szStuffed, nSize );
  201. return m_eStuffed;
  202. }
  203. SkipWhiteSpace();
  204. if (eof())
  205. {
  206. return TOKENEOF;
  207. }
  208. if (fail())
  209. {
  210. return TOKENEOF;
  211. }
  212. char ch = get();
  213. //
  214. // Look for all the valid operators.
  215. //
  216. switch (ch)
  217. {
  218. case '@':
  219. case ',':
  220. case '!':
  221. case '+':
  222. case '&':
  223. case '*':
  224. case '$':
  225. case '.':
  226. case '=':
  227. case ':':
  228. case '[':
  229. case ']':
  230. case '(':
  231. case ')':
  232. case '{':
  233. case '}':
  234. case '\\':
  235. {
  236. pszStore[0] = ch;
  237. pszStore[1] = 0;
  238. return OPERATOR;
  239. }
  240. }
  241. //
  242. // Look for the start of a quoted string.
  243. //
  244. if (ch == '\"')
  245. {
  246. return GetString(pszStore, nSize);
  247. }
  248. //
  249. // Integers consist of numbers with an optional leading minus sign.
  250. //
  251. if (isdigit(ch) || (ch == '-'))
  252. {
  253. do
  254. {
  255. if ( (pszStore - pStart + 1) < nSize )
  256. {
  257. *pszStore = ch;
  258. pszStore++;
  259. }
  260. ch = get();
  261. if (ch == '-')
  262. {
  263. return TOKENERROR;
  264. }
  265. } while (isdigit(ch));
  266. //
  267. // No identifier characters are allowed contiguous with numbers.
  268. //
  269. if (isalpha(ch) || (ch == '_'))
  270. {
  271. return TOKENERROR;
  272. }
  273. //
  274. // Put back the non-numeric character for the next call.
  275. //
  276. putback(ch);
  277. *pszStore = '\0';
  278. return INTEGER;
  279. }
  280. //
  281. // Identifiers consist of a consecutive string of alphanumeric
  282. // characters and underscores.
  283. //
  284. while ( isalpha(ch) || isdigit(ch) || (ch == '_') )
  285. {
  286. if ( (pszStore - pStart + 1) < nSize )
  287. {
  288. *pszStore = ch;
  289. pszStore++;
  290. }
  291. ch = get();
  292. }
  293. //
  294. // Put back the non-identifier character for the next call.
  295. //
  296. putback(ch);
  297. *pszStore = '\0';
  298. return IDENT;
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose:
  302. // Input : ttype -
  303. // *pszToken -
  304. //-----------------------------------------------------------------------------
  305. void TokenReader::IgnoreTill(trtoken_t ttype, const char *pszToken)
  306. {
  307. trtoken_t _ttype;
  308. char szBuf[1024];
  309. while(1)
  310. {
  311. _ttype = NextToken(szBuf, sizeof(szBuf));
  312. if(_ttype == TOKENEOF)
  313. return;
  314. if(_ttype == ttype)
  315. {
  316. if(IsToken(pszToken, szBuf))
  317. {
  318. Stuff(ttype, pszToken);
  319. return;
  320. }
  321. }
  322. }
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose:
  326. // Input : ttype -
  327. // pszToken -
  328. //-----------------------------------------------------------------------------
  329. void TokenReader::Stuff(trtoken_t eType, const char *pszToken)
  330. {
  331. m_eStuffed = eType;
  332. Q_strncpy(m_szStuffed, pszToken, sizeof( m_szStuffed ) );
  333. m_bStuffed = true;
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose:
  337. // Input : ttype -
  338. // pszToken -
  339. // Output : Returns true on success, false on failure.
  340. //-----------------------------------------------------------------------------
  341. bool TokenReader::Expecting(trtoken_t ttype, const char *pszToken)
  342. {
  343. char szBuf[1024];
  344. if (NextToken(szBuf, sizeof(szBuf)) != ttype || !IsToken(pszToken, szBuf))
  345. {
  346. return false;
  347. }
  348. return true;
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Purpose:
  352. // Input : pszStore -
  353. // Output :
  354. //-----------------------------------------------------------------------------
  355. trtoken_t TokenReader::PeekTokenType(char *pszStore, int maxlen )
  356. {
  357. if (!m_bStuffed)
  358. {
  359. m_eStuffed = NextToken(m_szStuffed, sizeof(m_szStuffed));
  360. m_bStuffed = true;
  361. }
  362. if (pszStore)
  363. {
  364. Q_strncpy(pszStore, m_szStuffed, maxlen );
  365. }
  366. return(m_eStuffed);
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose: Gets the next non-whitespace character from the file.
  370. // Input : ch - Receives the character.
  371. // Output : Returns true if the whitespace contained the combine strings
  372. // character '\', which is used to merge consecutive quoted strings.
  373. //-----------------------------------------------------------------------------
  374. bool TokenReader::SkipWhiteSpace(void)
  375. {
  376. bool bCombineStrings = false;
  377. while (true)
  378. {
  379. char ch = get();
  380. if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == 0))
  381. {
  382. continue;
  383. }
  384. if (ch == '+')
  385. {
  386. bCombineStrings = true;
  387. continue;
  388. }
  389. if (ch == '\n')
  390. {
  391. m_nLine++;
  392. continue;
  393. }
  394. if (eof())
  395. {
  396. return(bCombineStrings);
  397. }
  398. //
  399. // Check for the start of a comment.
  400. //
  401. if (ch == '/')
  402. {
  403. if (peek() == '/')
  404. {
  405. ignore(1024, '\n');
  406. m_nLine++;
  407. }
  408. }
  409. else
  410. {
  411. //
  412. // It is a worthy character. Put it back.
  413. //
  414. putback(ch);
  415. return(bCombineStrings);
  416. }
  417. }
  418. }