Counter Strike : Global Offensive Source Code
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.

1389 lines
30 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. // scriplib.c
  9. #include "tier1/strtools.h"
  10. #include "tier2/tier2.h"
  11. #include "cmdlib.h"
  12. #include "scriplib.h"
  13. #if defined( _X360 )
  14. #include "xbox\xbox_win32stubs.h"
  15. #endif
  16. #if defined(POSIX)
  17. #include "../../filesystem/linux_support.h"
  18. #include <sys/stat.h>
  19. #endif
  20. /*
  21. =============================================================================
  22. PARSING STUFF
  23. =============================================================================
  24. */
  25. typedef struct
  26. {
  27. char filename[1024];
  28. char *buffer,*script_p,*end_p;
  29. int line;
  30. char macrobuffer[4096];
  31. char *macroparam[64];
  32. char *macrovalue[64];
  33. int nummacroparams;
  34. } script_t;
  35. #define MAX_INCLUDES 16
  36. script_t scriptstack[MAX_INCLUDES];
  37. script_t *script = NULL;
  38. int scriptline;
  39. char token[MAXTOKEN];
  40. qboolean endofscript;
  41. qboolean tokenready; // only true if UnGetToken was just called
  42. typedef struct
  43. {
  44. char *param;
  45. char *value;
  46. char *param_lcase;
  47. } variable_t;
  48. CUtlVector<variable_t> g_definevariable;
  49. /*
  50. Callback stuff
  51. */
  52. void DefaultScriptLoadedCallback( char const *pFilenameLoaded, char const *pIncludedFromFileName, int nIncludeLineNumber )
  53. {
  54. NULL;
  55. }
  56. SCRIPT_LOADED_CALLBACK g_pfnCallback = DefaultScriptLoadedCallback;
  57. SCRIPT_LOADED_CALLBACK SetScriptLoadedCallback( SCRIPT_LOADED_CALLBACK pfnNewScriptLoadedCallback )
  58. {
  59. SCRIPT_LOADED_CALLBACK pfnCallback = g_pfnCallback;
  60. g_pfnCallback = pfnNewScriptLoadedCallback;
  61. return pfnCallback;
  62. }
  63. /*
  64. ==============
  65. AddScriptToStack
  66. ==============
  67. */
  68. void AddScriptToStack (char *filename, ScriptPathMode_t pathMode = SCRIPT_USE_ABSOLUTE_PATH)
  69. {
  70. int size;
  71. script++;
  72. if (script == &scriptstack[MAX_INCLUDES])
  73. Error ("script file exceeded MAX_INCLUDES");
  74. if ( pathMode == SCRIPT_USE_RELATIVE_PATH )
  75. Q_strncpy( script->filename, filename, sizeof( script->filename ) );
  76. else
  77. Q_strncpy (script->filename, ExpandPath (filename), sizeof( script->filename ) );
  78. size = LoadFile (script->filename, (void **)&script->buffer);
  79. // printf ("entering %s\n", script->filename);
  80. if ( g_pfnCallback )
  81. {
  82. if ( script == scriptstack + 1 )
  83. g_pfnCallback( script->filename, NULL, 0 );
  84. else
  85. g_pfnCallback( script->filename, script[-1].filename, script[-1].line );
  86. }
  87. script->line = 1;
  88. script->script_p = script->buffer;
  89. script->end_p = script->buffer + size;
  90. }
  91. /*
  92. ==============
  93. LoadScriptFile
  94. ==============
  95. */
  96. void LoadScriptFile (char *filename, ScriptPathMode_t pathMode)
  97. {
  98. script = scriptstack;
  99. AddScriptToStack (filename, pathMode);
  100. endofscript = false;
  101. tokenready = false;
  102. }
  103. /*
  104. ==============
  105. ==============
  106. */
  107. #define MAX_MACROS 128
  108. script_t *macrolist[MAX_MACROS];
  109. int nummacros;
  110. void DefineMacro( char *macroname )
  111. {
  112. script_t *pmacro = (script_t *)malloc( sizeof( script_t ) );
  113. strcpy( pmacro->filename, macroname );
  114. pmacro->line = script->line;
  115. pmacro->nummacroparams = 0;
  116. char *mp = pmacro->macrobuffer;
  117. char *cp = script->script_p;
  118. while (TokenAvailable( ))
  119. {
  120. GetToken( false );
  121. if (token[0] == '\\' && token[1] == '\\')
  122. {
  123. break;
  124. }
  125. cp = script->script_p;
  126. pmacro->macroparam[pmacro->nummacroparams++] = mp;
  127. strcpy( mp, token );
  128. mp += strlen( token ) + 1;
  129. if (mp >= pmacro->macrobuffer + sizeof( pmacro->macrobuffer ))
  130. Error("Macro buffer overflow\n");
  131. }
  132. // roll back script_p to previous valid location
  133. script->script_p = cp;
  134. // find end of macro def
  135. while (*cp && *cp != '\n')
  136. {
  137. //Msg("%d ", *cp );
  138. if (*cp == '\\' && *(cp+1) == '\\')
  139. {
  140. // skip till end of line
  141. while (*cp && *cp != '\n')
  142. {
  143. *cp = ' '; // replace with spaces
  144. cp++;
  145. }
  146. if (*cp)
  147. {
  148. cp++;
  149. }
  150. }
  151. else
  152. {
  153. cp++;
  154. }
  155. }
  156. int size = (cp - script->script_p);
  157. pmacro->buffer = (char *)malloc( size + 1);
  158. memcpy( pmacro->buffer, script->script_p, size );
  159. pmacro->buffer[size] = '\0';
  160. pmacro->end_p = &pmacro->buffer[size];
  161. macrolist[nummacros++] = pmacro;
  162. if ( nummacros == MAX_MACROS )
  163. Error ("script file exceeded MAX_MACROS");
  164. script->script_p = cp;
  165. }
  166. void DefineVariable( char *variablename )
  167. {
  168. variable_t v;
  169. v.param = strdup( variablename );
  170. GetToken( false );
  171. v.value = strdup( token );
  172. v.param_lcase = strlwr( strdup(v.param) );
  173. for ( int i=0; i<g_definevariable.Count(); i++ )
  174. {
  175. if ( !V_strcmp( g_definevariable[i].value, v.value ) )
  176. {
  177. Warning( "\"$definevariable %s %s\" already exists as \"%s\".", v.param_lcase, v.value, g_definevariable[i].param_lcase );
  178. }
  179. }
  180. g_definevariable.AddToTail( v );
  181. }
  182. void RedefineVariable( char *variablename )
  183. {
  184. variable_t v;
  185. v.param = strdup( variablename );
  186. GetToken( false );
  187. v.value = strdup( token );
  188. int nIdx = -1;
  189. for ( int i=0; i<g_definevariable.Count(); i++ )
  190. {
  191. if ( !V_strcmp( g_definevariable[i].param, v.param ) )
  192. {
  193. nIdx = i;
  194. break;
  195. }
  196. }
  197. if ( nIdx >= 0 )
  198. {
  199. g_definevariable[nIdx] = v;
  200. }
  201. else
  202. {
  203. Error("Cannot redefine undefined variable \"%s\". Use $definevariable instead.\n", v.param );
  204. }
  205. }
  206. /*
  207. ==============
  208. ==============
  209. */
  210. bool AddMacroToStack( char *macroname )
  211. {
  212. // lookup macro
  213. if (macroname[0] != '$')
  214. return false;
  215. int i;
  216. for (i = 0; i < nummacros; i++)
  217. {
  218. if (strcmpi( macrolist[i]->filename, &macroname[1] ) == 0)
  219. {
  220. break;
  221. }
  222. }
  223. if (i == nummacros)
  224. return false;
  225. script_t *pmacro = macrolist[i];
  226. // get tokens
  227. script_t *pnext = script + 1;
  228. pnext++;
  229. if (pnext == &scriptstack[MAX_INCLUDES])
  230. Error ("script file exceeded MAX_INCLUDES");
  231. // get tokens
  232. char *cp = pnext->macrobuffer;
  233. pnext->nummacroparams = pmacro->nummacroparams;
  234. for (i = 0; i < pnext->nummacroparams; i++)
  235. {
  236. GetToken(false);
  237. strcpy( cp, token );
  238. pnext->macroparam[i] = pmacro->macroparam[i];
  239. pnext->macrovalue[i] = cp;
  240. cp += strlen( token ) + 1;
  241. if (cp >= pnext->macrobuffer + sizeof( pnext->macrobuffer ))
  242. Error("Macro buffer overflow\n");
  243. }
  244. script = pnext;
  245. strcpy( script->filename, pmacro->filename );
  246. int size = pmacro->end_p - pmacro->buffer;
  247. script->buffer = (char *)malloc( size + 1 );
  248. memcpy( script->buffer, pmacro->buffer, size );
  249. pmacro->buffer[size] = '\0';
  250. script->script_p = script->buffer;
  251. script->end_p = script->buffer + size;
  252. script->line = pmacro->line;
  253. return true;
  254. }
  255. bool ExpandSubMacroToken( char *&token_p )
  256. {
  257. if ( *token_p == '^' )
  258. {
  259. token_p++;
  260. char * szPotentialVar = token_p;
  261. while ( *token_p != '^' )
  262. {
  263. token_p++;
  264. }
  265. *token_p = '\0';
  266. token_p = szPotentialVar;
  267. int index;
  268. for (index = 0; index < g_definevariable.Count(); index++)
  269. {
  270. if ( !Q_strcmp( g_definevariable[index].param, szPotentialVar ) )
  271. {
  272. strcpy( token, g_definevariable[index].value );
  273. return true;
  274. }
  275. }
  276. }
  277. return false;
  278. }
  279. bool ExpandMacroToken( char *&token_p )
  280. {
  281. if ( script->nummacroparams && *script->script_p == '$' )
  282. {
  283. char *cp = script->script_p + 1;
  284. while ( *cp > 32 && *cp != '$' )
  285. {
  286. cp++;
  287. }
  288. // found a word with $'s on either end?
  289. if (*cp != '$')
  290. return false;
  291. // get token pointer
  292. char *tp = script->script_p + 1;
  293. int len = (cp - tp);
  294. *(tp + len) = '\0';
  295. // lookup macro parameter
  296. int index = 0;
  297. for (index = 0; index < script->nummacroparams; index++)
  298. {
  299. if (stricmp( script->macroparam[index], tp ) == 0)
  300. break;
  301. }
  302. if (index >= script->nummacroparams)
  303. {
  304. Error("unknown macro token \"%s\" in %s\n", tp, script->filename );
  305. }
  306. // paste token into
  307. len = strlen( script->macrovalue[index] );
  308. strcpy( token_p, script->macrovalue[index] );
  309. token_p += len;
  310. script->script_p = cp + 1;
  311. if (script->script_p >= script->end_p)
  312. Error ("Macro expand overflow\n");
  313. if (token_p >= &token[MAXTOKEN])
  314. Error ("Token too large on line %i\n",scriptline);
  315. return true;
  316. }
  317. return false;
  318. }
  319. /*
  320. ==============
  321. ==============
  322. */
  323. // FIXME: this should create a new script context so the individual tokens in the variable can be parsed
  324. bool ExpandVariableToken( char *&token_p )
  325. {
  326. if ( *script->script_p == '$' )
  327. {
  328. char *cp = script->script_p + 1;
  329. while ( *cp > 32 && *cp != '$' )
  330. {
  331. cp++;
  332. }
  333. // found a word with $'s on either end?
  334. if (*cp != '$')
  335. return false;
  336. // get token pointer
  337. char *tp = script->script_p + 1;
  338. int len = (cp - tp);
  339. *(tp + len) = '\0';
  340. // lookup macro parameter
  341. int index;
  342. for (index = 0; index < g_definevariable.Count(); index++)
  343. {
  344. // [wills] just strcmp here, this was doing nearest partial comparison before which could result in a false positive variable name match. Bad!
  345. if ( !Q_strcmp( g_definevariable[index].param, tp ) )
  346. break;
  347. }
  348. // if we can't find the variable, try again without case sensitivity, then complain loudly if we find anything
  349. if (index >= g_definevariable.Count() )
  350. {
  351. for (index = 0; index < g_definevariable.Count(); index++)
  352. {
  353. char *tp_lower = strlwr(strdup(tp));
  354. if ( !Q_strcmp( g_definevariable[index].param_lcase, tp_lower ) )
  355. {
  356. Warning( "Unknown variable token fell back to case-insensitive match ( found: \"%s\", matched it to: \"%s\" ) in %s\n", tp, g_definevariable[index].param, script->filename );
  357. break;
  358. }
  359. }
  360. }
  361. if (index >= g_definevariable.Count() )
  362. {
  363. Error("unknown variable token \"%s\" in %s\n", tp, script->filename );
  364. }
  365. // paste token into
  366. len = strlen( g_definevariable[index].value );
  367. strcpy( token_p, g_definevariable[index].value );
  368. token_p += len;
  369. script->script_p = cp + 1;
  370. if (script->script_p >= script->end_p)
  371. Error ("Macro expand overflow\n");
  372. if (token_p >= &token[MAXTOKEN])
  373. Error ("Token too large on line %i\n",scriptline);
  374. return true;
  375. }
  376. return false;
  377. }
  378. /*
  379. ==============
  380. ParseFromMemory
  381. ==============
  382. */
  383. void ParseFromMemory (char *buffer, int size)
  384. {
  385. script = scriptstack;
  386. script++;
  387. if (script == &scriptstack[MAX_INCLUDES])
  388. Error ("script file exceeded MAX_INCLUDES");
  389. strcpy (script->filename, "memory buffer" );
  390. script->buffer = buffer;
  391. script->line = 1;
  392. script->script_p = script->buffer;
  393. script->end_p = script->buffer + size;
  394. endofscript = false;
  395. tokenready = false;
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Used instead of ParseFromMemory to temporarily add a memory buffer
  399. // to the script stack. ParseFromMemory just blows away the stack.
  400. //-----------------------------------------------------------------------------
  401. void PushMemoryScript( char *pszBuffer, const int nSize )
  402. {
  403. if ( script == NULL )
  404. {
  405. script = scriptstack;
  406. }
  407. script++;
  408. if ( script == &scriptstack[MAX_INCLUDES] )
  409. {
  410. Error ( "script file exceeded MAX_INCLUDES" );
  411. }
  412. strcpy (script->filename, "memory buffer" );
  413. script->buffer = pszBuffer;
  414. script->line = 1;
  415. script->script_p = script->buffer;
  416. script->end_p = script->buffer + nSize;
  417. endofscript = false;
  418. tokenready = false;
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Used after calling PushMemoryScript to clean up the memory buffer
  422. // added to the script stack. The normal end of script terminates
  423. // all parsing at the end of a memory buffer even if there are more scripts
  424. // remaining on the script stack
  425. //-----------------------------------------------------------------------------
  426. bool PopMemoryScript()
  427. {
  428. if ( V_stricmp( script->filename, "memory buffer" ) )
  429. return false;
  430. if ( script == scriptstack )
  431. {
  432. endofscript = true;
  433. return false;
  434. }
  435. script--;
  436. scriptline = script->line;
  437. endofscript = false;
  438. return true;
  439. }
  440. /*
  441. ==============
  442. UnGetToken
  443. Signals that the current token was not used, and should be reported
  444. for the next GetToken. Note that
  445. GetToken (true);
  446. UnGetToken ();
  447. GetToken (false);
  448. could cross a line boundary.
  449. ==============
  450. */
  451. void UnGetToken (void)
  452. {
  453. tokenready = true;
  454. }
  455. qboolean EndOfScript (qboolean crossline)
  456. {
  457. if (!crossline)
  458. Error ("Line %i is incomplete\n",scriptline);
  459. if (!strcmp (script->filename, "memory buffer"))
  460. {
  461. endofscript = true;
  462. return false;
  463. }
  464. free (script->buffer);
  465. script->buffer = NULL;
  466. if (script == scriptstack+1)
  467. {
  468. endofscript = true;
  469. return false;
  470. }
  471. script--;
  472. scriptline = script->line;
  473. // printf ("returning to %s\n", script->filename);
  474. return GetToken (crossline);
  475. }
  476. void AttemptConditionalInclude( void )
  477. {
  478. // Look for additional $include parameters, and only perform the include if the condition succeeds.
  479. // Right now, there's only a check for a file existing. This is a hacky way to get some logical
  480. // processing into qc, short of giving it full language-like arbitrary expression evaluation.
  481. GetToken (false);
  482. char szSavedPath[MAX_PATH];
  483. V_strcpy( szSavedPath, token );
  484. // check for a conditional flag
  485. bool bConditionSuccess = true;
  486. if ( TokenAvailable() )
  487. {
  488. GetToken (false);
  489. if ( !stricmp (token, "iffileexists") )
  490. {
  491. bConditionSuccess = false;
  492. if ( TokenAvailable() )
  493. {
  494. GetToken (false);
  495. bConditionSuccess = g_pFullFileSystem->FileExists( token ) != 0;
  496. }
  497. printf("Condition 'iffileexists' for input '%s' is %s.\n", token, bConditionSuccess ? "True" : "False" );
  498. }
  499. else
  500. {
  501. Error ("Unknown $include parameter %s\n",token);
  502. }
  503. }
  504. printf("%s: %s\n", bConditionSuccess ? "Including" : "SKIPPING", szSavedPath );
  505. if ( bConditionSuccess )
  506. AddScriptToStack( szSavedPath );
  507. }
  508. /*
  509. ==============
  510. GetToken
  511. ==============
  512. */
  513. qboolean GetToken (qboolean crossline)
  514. {
  515. char *token_p;
  516. if (tokenready) // is a token allready waiting?
  517. {
  518. tokenready = false;
  519. return true;
  520. }
  521. // printf("script_p %x (%x)\n", script->script_p, script->end_p ); fflush( stdout );
  522. if (script->script_p >= script->end_p)
  523. {
  524. return EndOfScript (crossline);
  525. }
  526. tokenready = false;
  527. // skip space, ctrl chars
  528. skipspace:
  529. while (*script->script_p <= 32)
  530. {
  531. if (script->script_p >= script->end_p)
  532. {
  533. return EndOfScript (crossline);
  534. }
  535. if (*(script->script_p++) == '\n')
  536. {
  537. if (!crossline)
  538. {
  539. Error ("Line %i is incomplete\n",scriptline);
  540. }
  541. scriptline = ++script->line;
  542. }
  543. }
  544. if (script->script_p >= script->end_p)
  545. {
  546. return EndOfScript (crossline);
  547. }
  548. // strip single line comments
  549. if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field
  550. (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field
  551. {
  552. if (!crossline)
  553. Error ("Line %i is incomplete\n",scriptline);
  554. while (*script->script_p++ != '\n')
  555. {
  556. if (script->script_p >= script->end_p)
  557. {
  558. return EndOfScript (crossline);
  559. }
  560. }
  561. scriptline = ++script->line;
  562. goto skipspace;
  563. }
  564. // strip out matching /* */ comments
  565. if (*script->script_p == '/' && *((script->script_p)+1) == '*')
  566. {
  567. script->script_p += 2;
  568. while (*script->script_p != '*' || *((script->script_p)+1) != '/')
  569. {
  570. if (*script->script_p++ != '\n')
  571. {
  572. if (script->script_p >= script->end_p)
  573. {
  574. return EndOfScript (crossline);
  575. }
  576. scriptline = ++script->line;
  577. }
  578. }
  579. script->script_p += 2;
  580. goto skipspace;
  581. }
  582. // copy token to buffer
  583. token_p = token;
  584. if (*script->script_p == '"')
  585. {
  586. // quoted token
  587. script->script_p++;
  588. while (*script->script_p != '"')
  589. {
  590. *token_p++ = *script->script_p++;
  591. if (script->script_p == script->end_p)
  592. break;
  593. if (token_p == &token[MAXTOKEN])
  594. Error ("Token too large on line %i\n",scriptline);
  595. }
  596. script->script_p++;
  597. }
  598. else // regular token
  599. while ( *script->script_p > 32 && *script->script_p != ';')
  600. {
  601. if ( !ExpandMacroToken( token_p ) )
  602. {
  603. if ( !ExpandVariableToken( token_p ) )
  604. {
  605. *token_p++ = *script->script_p++;
  606. if (script->script_p == script->end_p)
  607. break;
  608. if (token_p == &token[MAXTOKEN])
  609. Error ("Token too large on line %i\n",scriptline);
  610. }
  611. }
  612. }
  613. // add null to end of token
  614. *token_p = 0;
  615. // quick hack: check for submacro variables
  616. char *token_submacro = token;
  617. if ( ExpandSubMacroToken(token_submacro) )
  618. {
  619. return true;
  620. }
  621. // check for other commands
  622. if (!stricmp (token, "$include"))
  623. {
  624. AttemptConditionalInclude();
  625. return GetToken (crossline);
  626. }
  627. else if (!stricmp (token, "$definemacro"))
  628. {
  629. GetToken (false);
  630. DefineMacro(token);
  631. return GetToken (crossline);
  632. }
  633. else if (!stricmp (token, "$definevariable"))
  634. {
  635. GetToken (false);
  636. DefineVariable(token);
  637. return GetToken (crossline);
  638. }
  639. else if (!stricmp (token, "$redefinevariable"))
  640. {
  641. GetToken (false);
  642. RedefineVariable(token);
  643. return GetToken (crossline);
  644. }
  645. else if (AddMacroToStack( token ))
  646. {
  647. return GetToken (crossline);
  648. }
  649. return true;
  650. }
  651. /*
  652. ==============
  653. GetExprToken - use C mathematical operator parsing rules to split tokens instead of whitespace
  654. ==============
  655. */
  656. qboolean GetExprToken (qboolean crossline)
  657. {
  658. char *token_p;
  659. if (tokenready) // is a token allready waiting?
  660. {
  661. tokenready = false;
  662. return true;
  663. }
  664. if (script->script_p >= script->end_p)
  665. return EndOfScript (crossline);
  666. tokenready = false;
  667. //
  668. // skip space
  669. //
  670. skipspace:
  671. while (*script->script_p <= 32)
  672. {
  673. if (script->script_p >= script->end_p)
  674. return EndOfScript (crossline);
  675. if (*script->script_p++ == '\n')
  676. {
  677. if (!crossline)
  678. Error ("Line %i is incomplete\n",scriptline);
  679. scriptline = ++script->line;
  680. }
  681. }
  682. if (script->script_p >= script->end_p)
  683. return EndOfScript (crossline);
  684. if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field
  685. (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field
  686. {
  687. if (!crossline)
  688. Error ("Line %i is incomplete\n",scriptline);
  689. while (*script->script_p++ != '\n')
  690. if (script->script_p >= script->end_p)
  691. return EndOfScript (crossline);
  692. goto skipspace;
  693. }
  694. //
  695. // copy token
  696. //
  697. token_p = token;
  698. if (*script->script_p == '"')
  699. {
  700. // quoted token
  701. script->script_p++;
  702. while (*script->script_p != '"')
  703. {
  704. *token_p++ = *script->script_p++;
  705. if (script->script_p == script->end_p)
  706. break;
  707. if (token_p == &token[MAXTOKEN])
  708. Error ("Token too large on line %i\n",scriptline);
  709. }
  710. script->script_p++;
  711. }
  712. else
  713. {
  714. if ( V_isalpha( *script->script_p ) || *script->script_p == '_' )
  715. {
  716. // regular token
  717. while ( V_isalnum( *script->script_p ) || *script->script_p == '_' )
  718. {
  719. *token_p++ = *script->script_p++;
  720. if (script->script_p == script->end_p)
  721. break;
  722. if (token_p == &token[MAXTOKEN])
  723. Error ("Token too large on line %i\n",scriptline);
  724. }
  725. }
  726. else if ( V_isdigit( *script->script_p ) || *script->script_p == '.' )
  727. {
  728. // regular token
  729. while ( V_isdigit( *script->script_p ) || *script->script_p == '.' )
  730. {
  731. *token_p++ = *script->script_p++;
  732. if (script->script_p == script->end_p)
  733. break;
  734. if (token_p == &token[MAXTOKEN])
  735. Error ("Token too large on line %i\n",scriptline);
  736. }
  737. }
  738. else
  739. {
  740. // single char
  741. *token_p++ = *script->script_p++;
  742. }
  743. }
  744. *token_p = 0;
  745. if (!stricmp (token, "$include"))
  746. {
  747. AttemptConditionalInclude();
  748. return GetToken (crossline);
  749. }
  750. return true;
  751. }
  752. /*
  753. ==============
  754. TokenAvailable
  755. Returns true if there is another token on the line
  756. ==============
  757. */
  758. qboolean TokenAvailable (void)
  759. {
  760. char *search_p;
  761. if (tokenready) // is a token allready waiting?
  762. {
  763. return true;
  764. }
  765. search_p = script->script_p;
  766. if (search_p >= script->end_p)
  767. return false;
  768. while ( *search_p <= 32)
  769. {
  770. if (*search_p == '\n')
  771. return false;
  772. search_p++;
  773. if (search_p == script->end_p)
  774. return false;
  775. }
  776. if (*search_p == ';' || *search_p == '#' || // semicolon and # is comment field
  777. (*search_p == '/' && *((search_p)+1) == '/')) // also make // a comment field
  778. return false;
  779. return true;
  780. }
  781. qboolean GetTokenizerStatus( char **pFilename, int *pLine )
  782. {
  783. // is this the default state?
  784. if (!script)
  785. return false;
  786. if (script->script_p >= script->end_p)
  787. return false;
  788. if (pFilename)
  789. {
  790. *pFilename = script->filename;
  791. }
  792. if (pLine)
  793. {
  794. *pLine = script->line;
  795. }
  796. return true;
  797. }
  798. #include <stdio.h>
  799. #include <stdlib.h>
  800. #ifdef WIN32
  801. #include <direct.h>
  802. #include <io.h>
  803. #include <sys/utime.h>
  804. #endif
  805. #include <time.h>
  806. #include <fcntl.h>
  807. #include <sys/stat.h>
  808. #include <sys/types.h>
  809. #include "tier1/utlbuffer.h"
  810. class CScriptLib : public IScriptLib
  811. {
  812. public:
  813. virtual bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText = false, bool bNoOpenFailureWarning = false );
  814. virtual bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode );
  815. virtual int FindFiles( char* pFileMask, bool bRecurse, CUtlVector<fileList_t> &fileList );
  816. virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize );
  817. virtual void DeleteTemporaryFiles( const char *pFileMask );
  818. virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB );
  819. virtual bool DoesFileExist( const char *pFilename );
  820. private:
  821. int GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< fileList_t > &fileList );
  822. void RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList );
  823. };
  824. static CScriptLib g_ScriptLib;
  825. IScriptLib *scriptlib = &g_ScriptLib;
  826. IScriptLib *g_pScriptLib = &g_ScriptLib;
  827. //-----------------------------------------------------------------------------
  828. // Existence check
  829. //-----------------------------------------------------------------------------
  830. bool CScriptLib::DoesFileExist( const char *pFilename )
  831. {
  832. return g_pFullFileSystem->FileExists( pFilename );
  833. }
  834. //-----------------------------------------------------------------------------
  835. // Purpose: Helper utility, read file into buffer
  836. //-----------------------------------------------------------------------------
  837. bool CScriptLib::ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText, bool bNoOpenFailureWarning )
  838. {
  839. bool bSuccess = true;
  840. if ( !g_pFullFileSystem->ReadFile( pSourceName, NULL, buffer ) )
  841. {
  842. if ( !bNoOpenFailureWarning )
  843. {
  844. Msg( "ReadFileToBuffer(): Error opening %s: %s\n", pSourceName, strerror( errno ) );
  845. }
  846. return false;
  847. }
  848. if ( bText )
  849. {
  850. // force it into text mode
  851. buffer.SetBufferType( true, true );
  852. }
  853. else
  854. {
  855. buffer.SetBufferType( false, false );
  856. }
  857. return bSuccess;
  858. }
  859. //-----------------------------------------------------------------------------
  860. // Purpose: Helper utility, Write buffer to file
  861. //-----------------------------------------------------------------------------
  862. bool CScriptLib::WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode )
  863. {
  864. char* ptr;
  865. char dirPath[MAX_PATH];
  866. bool bSuccess = true;
  867. // create path
  868. // prime and skip to first seperator
  869. strcpy( dirPath, pTargetName );
  870. ptr = strchr( dirPath, '\\' );
  871. while ( ptr )
  872. {
  873. ptr = strchr( ptr+1, '\\' );
  874. if ( ptr )
  875. {
  876. *ptr = '\0';
  877. _mkdir( dirPath );
  878. *ptr = '\\';
  879. }
  880. }
  881. bool bDoWrite = false;
  882. if ( writeMode == WRITE_TO_DISK_ALWAYS )
  883. {
  884. bDoWrite = true;
  885. }
  886. else if ( writeMode == WRITE_TO_DISK_UPDATE )
  887. {
  888. if ( DoesFileExist( pTargetName ) )
  889. {
  890. bDoWrite = true;
  891. }
  892. }
  893. if ( bDoWrite )
  894. {
  895. bSuccess = g_pFullFileSystem->WriteFile( pTargetName, NULL, buffer );
  896. }
  897. return bSuccess;
  898. }
  899. //-----------------------------------------------------------------------------
  900. // Returns -1, 0, or 1.
  901. //-----------------------------------------------------------------------------
  902. int CScriptLib::CompareFileTime( const char *pFilenameA, const char *pFilenameB )
  903. {
  904. int timeA = g_pFullFileSystem->GetFileTime( (char *)pFilenameA );
  905. int timeB = g_pFullFileSystem->GetFileTime( (char *)pFilenameB );
  906. if ( timeA == -1)
  907. {
  908. // file a not exist
  909. timeA = 0;
  910. }
  911. if ( timeB == -1 )
  912. {
  913. // file b not exist
  914. timeB = 0;
  915. }
  916. if ( (unsigned int)timeA < (unsigned int)timeB )
  917. {
  918. return -1;
  919. }
  920. else if ( (unsigned int)timeA > (unsigned int)timeB )
  921. {
  922. return 1;
  923. }
  924. return 0;
  925. }
  926. //-----------------------------------------------------------------------------
  927. // Make a temporary filename
  928. //-----------------------------------------------------------------------------
  929. char *CScriptLib::MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize )
  930. {
  931. char *pBuffer = _tempnam( pchModPath, "mgd_" );
  932. if ( pBuffer[0] == '\\' )
  933. {
  934. pBuffer++;
  935. }
  936. if ( pBuffer[strlen( pBuffer )-1] == '.' )
  937. {
  938. pBuffer[strlen( pBuffer )-1] = '\0';
  939. }
  940. V_snprintf( pPath, pathSize, "%s.tmp", pBuffer );
  941. free( pBuffer );
  942. return pPath;
  943. }
  944. //-----------------------------------------------------------------------------
  945. // Delete temporary files
  946. //-----------------------------------------------------------------------------
  947. void CScriptLib::DeleteTemporaryFiles( const char *pFileMask )
  948. {
  949. #if !defined( _X360 )
  950. const char *pEnv = getenv( "temp" );
  951. if ( !pEnv )
  952. {
  953. pEnv = getenv( "tmp" );
  954. }
  955. if ( pEnv )
  956. {
  957. char tempPath[MAX_PATH];
  958. strcpy( tempPath, pEnv );
  959. V_AppendSlash( tempPath, sizeof( tempPath ) );
  960. strcat( tempPath, pFileMask );
  961. CUtlVector<fileList_t> fileList;
  962. FindFiles( tempPath, false, fileList );
  963. for ( int i=0; i<fileList.Count(); i++ )
  964. {
  965. _unlink( fileList[i].fileName.String() );
  966. }
  967. }
  968. #else
  969. AssertOnce( !"CScriptLib::DeleteTemporaryFiles: Not avail on 360\n" );
  970. #endif
  971. }
  972. //-----------------------------------------------------------------------------
  973. // Purpose: Get list of files from current path that match pattern
  974. //-----------------------------------------------------------------------------
  975. int CScriptLib::GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< fileList_t > &fileList )
  976. {
  977. char sourcePath[MAX_PATH];
  978. char fullPath[MAX_PATH];
  979. bool bFindDirs;
  980. fileList.Purge();
  981. strcpy( sourcePath, pDirPath );
  982. int len = (int)strlen( sourcePath );
  983. if ( !len )
  984. {
  985. strcpy( sourcePath, ".\\" );
  986. }
  987. else if ( sourcePath[len-1] != '\\' )
  988. {
  989. sourcePath[len] = '\\';
  990. sourcePath[len+1] = '\0';
  991. }
  992. strcpy( fullPath, sourcePath );
  993. if ( pPattern[0] == '\\' && pPattern[1] == '\0' )
  994. {
  995. // find directories only
  996. bFindDirs = true;
  997. strcat( fullPath, "*" );
  998. }
  999. else
  1000. {
  1001. // find files, use provided pattern
  1002. bFindDirs = false;
  1003. strcat( fullPath, pPattern );
  1004. }
  1005. #ifdef WIN32
  1006. struct _finddata_t findData;
  1007. intptr_t h = _findfirst( fullPath, &findData );
  1008. if ( h == -1 )
  1009. {
  1010. return 0;
  1011. }
  1012. do
  1013. {
  1014. // dos attribute complexities i.e. _A_NORMAL is 0
  1015. if ( bFindDirs )
  1016. {
  1017. // skip non dirs
  1018. if ( !( findData.attrib & _A_SUBDIR ) )
  1019. continue;
  1020. }
  1021. else
  1022. {
  1023. // skip dirs
  1024. if ( findData.attrib & _A_SUBDIR )
  1025. continue;
  1026. }
  1027. if ( !stricmp( findData.name, "." ) )
  1028. continue;
  1029. if ( !stricmp( findData.name, ".." ) )
  1030. continue;
  1031. char fileName[MAX_PATH];
  1032. strcpy( fileName, sourcePath );
  1033. strcat( fileName, findData.name );
  1034. int j = fileList.AddToTail();
  1035. fileList[j].fileName.Set( fileName );
  1036. fileList[j].timeWrite = findData.time_write;
  1037. }
  1038. while ( !_findnext( h, &findData ) );
  1039. _findclose( h );
  1040. #elif defined(POSIX)
  1041. FIND_DATA findData;
  1042. Q_FixSlashes( fullPath );
  1043. void *h = FindFirstFile( fullPath, &findData );
  1044. if ( (intp)h == -1 )
  1045. {
  1046. return 0;
  1047. }
  1048. do
  1049. {
  1050. // dos attribute complexities i.e. _A_NORMAL is 0
  1051. if ( bFindDirs )
  1052. {
  1053. // skip non dirs
  1054. if ( !( findData.dwFileAttributes & S_IFDIR ) )
  1055. continue;
  1056. }
  1057. else
  1058. {
  1059. // skip dirs
  1060. if ( findData.dwFileAttributes & S_IFDIR )
  1061. continue;
  1062. }
  1063. if ( !stricmp( findData.cFileName, "." ) )
  1064. continue;
  1065. if ( !stricmp( findData.cFileName, ".." ) )
  1066. continue;
  1067. char fileName[MAX_PATH];
  1068. strcpy( fileName, sourcePath );
  1069. strcat( fileName, findData.cFileName );
  1070. int j = fileList.AddToTail();
  1071. fileList[j].fileName.Set( fileName );
  1072. struct stat statbuf;
  1073. if ( stat( fileName, &statbuf ) )
  1074. #ifdef LINUX
  1075. fileList[j].timeWrite = statbuf.st_mtime;
  1076. #else
  1077. fileList[j].timeWrite = statbuf.st_mtimespec.tv_sec;
  1078. #endif
  1079. else
  1080. fileList[j].timeWrite = 0;
  1081. }
  1082. while ( !FindNextFile( h, &findData ) );
  1083. FindClose( h );
  1084. #else
  1085. #error
  1086. #endif
  1087. return fileList.Count();
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose: Recursively determine directory tree
  1091. //-----------------------------------------------------------------------------
  1092. void CScriptLib::RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList )
  1093. {
  1094. // recurse from source directory, get directories only
  1095. CUtlVector< fileList_t > fileList;
  1096. int dirCount = GetFileList( pDirPath, "\\", fileList );
  1097. if ( !dirCount )
  1098. {
  1099. // add directory name to search tree
  1100. int j = dirList.AddToTail();
  1101. dirList[j].Set( pDirPath );
  1102. return;
  1103. }
  1104. for ( int i=0; i<dirCount; i++ )
  1105. {
  1106. // form new path name, recurse into
  1107. RecurseFileTree_r( fileList[i].fileName.String(), depth+1, dirList );
  1108. }
  1109. int j = dirList.AddToTail();
  1110. dirList[j].Set( pDirPath );
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. // Purpose: Generate a list of file matching mask
  1114. //-----------------------------------------------------------------------------
  1115. int CScriptLib::FindFiles( char* pFileMask, bool bRecurse, CUtlVector<fileList_t> &fileList )
  1116. {
  1117. char dirPath[MAX_PATH];
  1118. char pattern[MAX_PATH];
  1119. char extension[MAX_PATH];
  1120. // get path only
  1121. strcpy( dirPath, pFileMask );
  1122. V_StripFilename( dirPath );
  1123. // get pattern only
  1124. V_FileBase( pFileMask, pattern, sizeof( pattern ) );
  1125. V_ExtractFileExtension( pFileMask, extension, sizeof( extension ) );
  1126. if ( extension[0] )
  1127. {
  1128. strcat( pattern, "." );
  1129. strcat( pattern, extension );
  1130. }
  1131. if ( !bRecurse )
  1132. {
  1133. GetFileList( dirPath, pattern, fileList );
  1134. }
  1135. else
  1136. {
  1137. // recurse and get the tree
  1138. CUtlVector< fileList_t > tempList;
  1139. CUtlVector< CUtlString > dirList;
  1140. RecurseFileTree_r( dirPath, 0, dirList );
  1141. for ( int i=0; i<dirList.Count(); i++ )
  1142. {
  1143. // iterate each directory found
  1144. tempList.Purge();
  1145. tempList.EnsureCapacity( dirList.Count() );
  1146. GetFileList( dirList[i].String(), pattern, tempList );
  1147. int start = fileList.AddMultipleToTail( tempList.Count() );
  1148. for ( int j=0; j<tempList.Count(); j++ )
  1149. {
  1150. fileList[start+j] = tempList[j];
  1151. }
  1152. }
  1153. }
  1154. return fileList.Count();
  1155. }