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.

1349 lines
29 KiB

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