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.

1927 lines
53 KiB

  1. //========= Copyright 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A collection of utility classes to simplify file I/O, and
  4. // as much as possible contain portability problems. Here avoiding
  5. // including windows.h.
  6. //
  7. //=============================================================================
  8. #if defined(_WIN32)
  9. #undef _WIN32_WINNT
  10. #define _WIN32_WINNT 0x0502 // ReadDirectoryChangesW
  11. #endif
  12. #include <sys/stat.h>
  13. #if defined(OSX)
  14. #include <CoreServices/CoreServices.h>
  15. #include <sys/types.h>
  16. #include <dirent.h>
  17. #include <sys/time.h>
  18. #endif
  19. #define ASYNC_FILEIO
  20. #if defined( LINUX ) || defined ( OSX )
  21. // Linux hasn't got a good AIO library that we have found yet, so lets punt for now
  22. #undef ASYNC_FILEIO
  23. #endif
  24. #if defined(_WIN32)
  25. //#include <direct.h>
  26. #include <io.h>
  27. // unset to force to use stdio implementation
  28. #define WIN32_FILEIO
  29. #if defined(ASYNC_FILEIO)
  30. #if defined(_WIN32) && !defined(WIN32_FILEIO)
  31. #error "trying to use async io without win32 filesystem API usage, that isn't doable"
  32. #endif
  33. #endif
  34. #else /* not defined (_WIN32) */
  35. #include <utime.h>
  36. #include <dirent.h>
  37. #include <unistd.h> // for unlink
  38. #include <limits.h> // defines PATH_MAX
  39. #include <alloca.h> // 'cause we like smashing the stack
  40. #if defined( _PS3 )
  41. #include <fcntl.h>
  42. #else
  43. #include <sys/fcntl.h>
  44. #include <sys/statvfs.h>
  45. #endif
  46. #include <sched.h>
  47. #define int64 int64_t
  48. // On OSX the native API file offset is always 64-bit
  49. // and things like stat64 are deprecated.
  50. // PS3 doesn't have anything other than the native API.
  51. #if defined(OSX) || defined(_PS3)
  52. typedef off_t offBig_t;
  53. typedef struct stat statBig_t;
  54. typedef struct statvfs statvfsBig_t;
  55. typedef struct dirent direntBig_t;
  56. #define openBig open
  57. #define lseekBig lseek
  58. #define preadBig pread
  59. #define pwriteBig pwrite
  60. #define statBig stat
  61. #define lstatBig lstat
  62. #define readdirBig readdir
  63. #define scandirBig scandir
  64. #define alphasortBig alphasort
  65. #define fopenBig fopen
  66. #define fseekBig fseeko
  67. #define ftellBig ftello
  68. #define ftruncateBig ftruncate
  69. #define fstatBig fstat
  70. #define statvfsBig statvfs
  71. #define mmapBig mmap
  72. #else
  73. // Use the 64-bit file I/O API.
  74. typedef off64_t offBig_t;
  75. typedef struct stat64 statBig_t;
  76. typedef struct statvfs64 statvfsBig_t;
  77. typedef struct dirent64 direntBig_t;
  78. #define openBig open64
  79. #define lseekBig lseek64
  80. #define preadBig pread64
  81. #define pwriteBig pwrite64
  82. #define statBig stat64
  83. #define lstatBig lstat64
  84. #define readdirBig readdir64
  85. #define scandirBig scandir64
  86. #define alphasortBig alphasort64
  87. #define fopenBig fopen64
  88. #define fseekBig fseeko64
  89. #define ftellBig ftello64
  90. #define ftruncateBig ftruncate64
  91. #define fstatBig fstat64
  92. #define statvfsBig statvfs64
  93. #define mmapBig mmap64
  94. #endif
  95. struct _finddata_t
  96. {
  97. _finddata_t()
  98. {
  99. name[0] = '\0';
  100. dirBase[0] = '\0';
  101. curName = 0;
  102. numNames = 0;
  103. namelist = NULL;
  104. }
  105. // public data
  106. char name[PATH_MAX]; // the file name returned from the call
  107. char dirBase[PATH_MAX];
  108. offBig_t size;
  109. mode_t attrib;
  110. time_t time_write;
  111. time_t time_create;
  112. int curName;
  113. int numNames;
  114. direntBig_t **namelist;
  115. };
  116. #define _A_SUBDIR S_IFDIR
  117. // FUTURE map _A_HIDDEN via checking filename against .*
  118. #define _A_HIDDEN 0
  119. // FUTURE check 'read only' by checking mode against S_IRUSR
  120. #define _A_RDONLY 0
  121. // no files under posix are 'system' or 'archive'
  122. #define _A_SYSTEM 0
  123. #define _A_ARCH 0
  124. int _findfirst( const char *pchBasePath, struct _finddata_t *pFindData );
  125. int _findnext( const int64 hFind, struct _finddata_t *pFindData );
  126. bool _findclose( int64 hFind );
  127. static int FileSelect( const char *name, const char *mask );
  128. #endif
  129. #include "tier1/fileio.h"
  130. #include "tier1/utlbuffer.h"
  131. #include "tier1/strtools.h"
  132. #include <errno.h>
  133. #include "vstdlib/vstrtools.h"
  134. #if defined( WIN32_FILEIO )
  135. #include "winlite.h"
  136. #endif
  137. #if defined( ASYNC_FILEIO )
  138. #ifdef _WIN32
  139. #include "winlite.h"
  140. #elif defined(_PS3)
  141. // bugbug ps3 - see some aio files under libfs.. skipping for the moment
  142. #elif defined(POSIX)
  143. #include <aio.h>
  144. #else
  145. #error "aio please"
  146. #endif
  147. #endif
  148. #if defined ( POSIX )
  149. #define INVALID_HANDLE_VALUE NULL
  150. #define _rmdir rmdir
  151. #if !defined( _PS3 )
  152. #define _S_IREAD S_IREAD
  153. #define _S_IWRITE S_IWRITE
  154. #else
  155. #define _S_IREAD S_IRUSR
  156. #define _S_IWRITE S_IWUSR
  157. #endif
  158. #endif
  159. #define PvAlloc( cub ) malloc( cub )
  160. #define PvRealloc( pv, cub ) realloc( pv, cub )
  161. #define FreePv( pv ) free( pv )
  162. //-----------------------------------------------------------------------------
  163. // Purpose: Constructor from UTF8
  164. //-----------------------------------------------------------------------------
  165. CPathString::CPathString( const char *pchUTF8Path )
  166. {
  167. // Need to first turn into an absolute path, so \\?\ pre-pended paths will be ok
  168. m_pchUTF8Path = new char[ MAX_UNICODE_PATH_IN_UTF8 ];
  169. m_pwchWideCharPathPrepended = NULL;
  170. // First, convert to absolute path, which also does Q_FixSlashes for us.
  171. Q_MakeAbsolutePath( m_pchUTF8Path, MAX_UNICODE_PATH * 4, pchUTF8Path );
  172. // Second, fix any double slashes
  173. V_FixDoubleSlashes( m_pchUTF8Path );
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Purpose: Destructor
  177. //-----------------------------------------------------------------------------
  178. CPathString::~CPathString()
  179. {
  180. if ( m_pwchWideCharPathPrepended )
  181. {
  182. delete[] m_pwchWideCharPathPrepended;
  183. m_pwchWideCharPathPrepended = NULL;
  184. }
  185. if ( m_pchUTF8Path )
  186. {
  187. delete[] m_pchUTF8Path;
  188. m_pchUTF8Path = NULL;
  189. }
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose: Access UTF8 path
  193. //-----------------------------------------------------------------------------
  194. const char * CPathString::GetUTF8Path()
  195. {
  196. return m_pchUTF8Path;
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Purpose: Gets wchar_t based path, with \\?\ pre-pended (allowing long paths
  200. // on Win32, should only be used with unicode extended path aware filesystem calls)
  201. //-----------------------------------------------------------------------------
  202. const wchar_t *CPathString::GetWCharPathPrePended()
  203. {
  204. PopulateWCharPath();
  205. return m_pwchWideCharPathPrepended;
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Purpose: Builds wchar path string
  209. //-----------------------------------------------------------------------------
  210. void CPathString::PopulateWCharPath()
  211. {
  212. if ( m_pwchWideCharPathPrepended )
  213. return;
  214. // Check if the UTF8 path starts with \\, which on Win32 means it's a UNC path, and then needs a different prefix
  215. if ( m_pchUTF8Path[0] == '\\' && m_pchUTF8Path[1] == '\\' )
  216. {
  217. m_pwchWideCharPathPrepended = new wchar_t[MAX_UNICODE_PATH+8];
  218. Q_memcpy( m_pwchWideCharPathPrepended, L"\\\\?\\UNC\\", 8*sizeof(wchar_t) );
  219. #ifdef DBGFLAG_ASSERT
  220. int cchResult =
  221. #endif
  222. Q_UTF8ToUnicode( m_pchUTF8Path+2, m_pwchWideCharPathPrepended+8, MAX_UNICODE_PATH*sizeof(wchar_t) );
  223. Assert( cchResult );
  224. // Be sure we NULL terminate within our allocated region incase Q_UTF8ToUnicode failed, though we're already in bad shape then.
  225. m_pwchWideCharPathPrepended[MAX_UNICODE_PATH+7] = 0;
  226. }
  227. else
  228. {
  229. m_pwchWideCharPathPrepended = new wchar_t[MAX_UNICODE_PATH+4];
  230. Q_memcpy( m_pwchWideCharPathPrepended, L"\\\\?\\", 4*sizeof(wchar_t) );
  231. #ifdef DBGFLAG_ASSERT
  232. int cchResult =
  233. #endif
  234. Q_UTF8ToUnicode( m_pchUTF8Path, m_pwchWideCharPathPrepended+4, MAX_UNICODE_PATH*sizeof(wchar_t) );
  235. Assert( cchResult );
  236. // Be sure we NULL terminate within our allocated region incase Q_UTF8ToUnicode failed, though we're already in bad shape then.
  237. m_pwchWideCharPathPrepended[MAX_UNICODE_PATH+3] = 0;
  238. }
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose: Helper on PS3 to find next entry that matches the provided pattern
  242. //-----------------------------------------------------------------------------
  243. #if defined( _PS3 )
  244. bool CDirIterator::BFindNextPS3()
  245. {
  246. while (true)
  247. {
  248. uint32 unDataCount = 0;
  249. if (cellFsGetDirectoryEntries( m_hFind, m_pDirEntry, sizeof(CellFsDirectoryEntry), &unDataCount ) != CELL_FS_SUCCEEDED || unDataCount == 0)
  250. return false;
  251. // if we found a new file/directory, need to make sure it matches our desired pattern
  252. if (FileSelect( m_pDirEntry->entry_name.d_name, m_strPattern.String() ) != 0)
  253. return true;
  254. }
  255. }
  256. #endif
  257. //-----------------------------------------------------------------------------
  258. // Purpose: Constructor
  259. //-----------------------------------------------------------------------------
  260. #if defined( _PS3 )
  261. CDirIterator::CDirIterator( const char *pchPath, const char *pchPattern )
  262. {
  263. // init for failure
  264. m_bOpenHandle = false;
  265. m_bNoFiles = true;
  266. m_bUsedFirstFile = true;
  267. // always create a new entry.. matches win32/posix (guessing so BCurrent functions won't crash?)
  268. m_pDirEntry = new CellFsDirectoryEntry;
  269. memset( m_pDirEntry, 0, sizeof(CellFsDirectoryEntry) );
  270. if (!pchPath || !pchPattern)
  271. return;
  272. // fix up path
  273. CPathString strPath( pchPath );
  274. // save pattern
  275. m_strPattern = pchPattern;
  276. // we have a path.. init
  277. CellFsErrno e = cellFsOpendir( strPath.GetUTF8Path(), &m_hFind );
  278. if (e != CELL_FS_SUCCEEDED)
  279. return;
  280. m_bOpenHandle = true;
  281. // find first entry
  282. if (!BFindNextPS3())
  283. return;
  284. // found at least 1 file
  285. m_bNoFiles = false;
  286. // if we're pointing at . or .., set it as used
  287. // so we'll look for the next item when BNextFile() is called
  288. m_bUsedFirstFile = !BValidFilename();
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Purpose: Destructor
  292. //-----------------------------------------------------------------------------
  293. CDirIterator::~CDirIterator()
  294. {
  295. if (m_bOpenHandle)
  296. cellFsClosedir( m_hFind );
  297. if (m_pDirEntry)
  298. delete m_pDirEntry;
  299. }
  300. #else
  301. CDirIterator::CDirIterator( const char *pchPath, const char *pchPattern )
  302. {
  303. CPathString strPath( pchPath );
  304. m_pFindData = NULL;
  305. // +2 so we can potentially add path separator as well as null termination
  306. char *pchPathAndPattern = new char[Q_strlen( strPath.GetUTF8Path() ) + Q_strlen( pchPattern ) + 2];
  307. // be resilient about whether the caller passes us a path with a terminal path separator or not.
  308. // put in the path
  309. if (pchPath)
  310. {
  311. Q_strncpy( pchPathAndPattern, strPath.GetUTF8Path(), Q_strlen( strPath.GetUTF8Path() ) + 1 );
  312. // identify whether we've got a terminal separator. add one if not.
  313. char *pchRest = pchPathAndPattern + Q_strlen( pchPathAndPattern ) - 1;
  314. if (*pchRest != CORRECT_PATH_SEPARATOR)
  315. {
  316. *++pchRest = CORRECT_PATH_SEPARATOR;
  317. }
  318. pchRest++;
  319. // now put in the search pattern.
  320. Q_strncpy( pchRest, pchPattern, Q_strlen( pchPattern ) + 1 );
  321. Init( pchPathAndPattern );
  322. }
  323. else
  324. {
  325. pchPathAndPattern[0] = 0;
  326. m_bNoFiles = true;
  327. m_bUsedFirstFile = true;
  328. #if defined(_WIN32)
  329. m_hFind = INVALID_HANDLE_VALUE;
  330. m_pFindData = new WIN32_FIND_DATAW;
  331. m_rgchFileName[0] = 0;
  332. #else
  333. m_hFind = -1;
  334. m_pFindData = new _finddata_t;
  335. #endif
  336. memset( m_pFindData, 0, sizeof(*m_pFindData) );
  337. }
  338. delete[] pchPathAndPattern;
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Purpose: Constructor
  342. //-----------------------------------------------------------------------------
  343. CDirIterator::CDirIterator( const char *pchSearchPath )
  344. {
  345. Init( pchSearchPath );
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Purpose: Initialize iteration structure
  349. //-----------------------------------------------------------------------------
  350. void CDirIterator::Init( const char *pchSearchPath )
  351. {
  352. CPathString strBasePath( pchSearchPath );
  353. #if defined(_WIN32)
  354. m_pFindData = new WIN32_FIND_DATAW;
  355. memset( m_pFindData, 0, sizeof(*m_pFindData) );
  356. m_rgchFileName[0] = 0;
  357. m_hFind = FindFirstFileW( strBasePath.GetWCharPathPrePended(), m_pFindData );
  358. bool bSuccess = (m_hFind != INVALID_HANDLE_VALUE);
  359. // Conversion should never fail with valid filenames...
  360. if (bSuccess && !Q_UnicodeToUTF8( m_pFindData->cFileName, m_rgchFileName, sizeof(m_rgchFileName) ))
  361. {
  362. AssertMsg( false, "Q_UnicodeToUTF8 failed on m_pFindData->cFileName in CDirIterator" );
  363. bSuccess = false;
  364. }
  365. #else
  366. m_pFindData = new _finddata_t;
  367. memset( m_pFindData, 0, sizeof(*m_pFindData) );
  368. m_hFind = _findfirst( strBasePath.GetUTF8Path(), m_pFindData );
  369. bool bSuccess = (m_hFind != -1);
  370. #endif
  371. if (!bSuccess)
  372. {
  373. m_bNoFiles = true;
  374. m_bUsedFirstFile = true;
  375. }
  376. else
  377. {
  378. m_bNoFiles = false;
  379. // if we're pointing at . or .., set it as used
  380. // so we'll look for the next item when BNextFile() is called
  381. m_bUsedFirstFile = !BValidFilename();
  382. }
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: Destructor
  386. //-----------------------------------------------------------------------------
  387. CDirIterator::~CDirIterator()
  388. {
  389. #if defined(_WIN32)
  390. if (m_hFind != INVALID_HANDLE_VALUE)
  391. {
  392. FindClose( m_hFind );
  393. }
  394. delete m_pFindData;
  395. #else
  396. if (m_hFind != -1)
  397. {
  398. _findclose( m_hFind );
  399. }
  400. if (m_pFindData)
  401. {
  402. for (int i = 0; i < m_pFindData->numNames; i++)
  403. {
  404. // scandir allocates with malloc, so free with free
  405. free( m_pFindData->namelist[i] );
  406. }
  407. free( m_pFindData->namelist );
  408. delete m_pFindData;
  409. }
  410. #endif
  411. }
  412. #endif // _PS3
  413. //-----------------------------------------------------------------------------
  414. // Purpose: Check for successful construction
  415. //-----------------------------------------------------------------------------
  416. bool CDirIterator::IsValid() const
  417. {
  418. #if defined(_WIN32)
  419. return m_hFind != INVALID_HANDLE_VALUE;
  420. #elif defined(_PS3)
  421. return m_bOpenHandle;
  422. #else
  423. return m_hFind != -1;
  424. #endif
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Purpose: Filter out . and ..
  428. //-----------------------------------------------------------------------------
  429. bool CDirIterator::BValidFilename()
  430. {
  431. #if defined( _WIN32 )
  432. const char *pch = m_rgchFileName;
  433. #elif defined( _PS3 )
  434. const char *pch = m_pDirEntry->entry_name.d_name;
  435. #else
  436. const char *pch = m_pFindData->name;
  437. #endif
  438. if ((pch[0] == '.' && pch[1] == 0) ||
  439. (pch[0] == '.' && pch[1] == '.' && pch[2] == 0))
  440. return false;
  441. return true;
  442. }
  443. //-----------------------------------------------------------------------------
  444. // Purpose: returns true if there is a file to read
  445. //-----------------------------------------------------------------------------
  446. bool CDirIterator::BNextFile()
  447. {
  448. if (m_bNoFiles)
  449. return false;
  450. // use the first result
  451. if (!m_bUsedFirstFile)
  452. {
  453. m_bUsedFirstFile = true;
  454. return true;
  455. }
  456. // find the next item
  457. for (;;)
  458. {
  459. #if defined( _WIN32 )
  460. bool bFound = (FindNextFileW( m_hFind, m_pFindData ) != FALSE);
  461. // Conversion should never fail with valid filenames...
  462. if (bFound && !Q_UnicodeToUTF8( m_pFindData->cFileName, m_rgchFileName, sizeof(m_rgchFileName) ))
  463. {
  464. AssertMsg( false, "Q_UnicodeToUTF8 failed on m_pFindData->cFileName in CDirIterator" );
  465. bFound = false;
  466. }
  467. #elif defined( _PS3 )
  468. bool bFound = BFindNextPS3();
  469. #else
  470. bool bFound = (_findnext( m_hFind, m_pFindData ) == 0);
  471. #endif
  472. if (!bFound)
  473. {
  474. // done
  475. m_bNoFiles = true;
  476. return false;
  477. }
  478. // skip over the '.' and '..' paths
  479. if (!BValidFilename())
  480. continue;
  481. break;
  482. }
  483. // have one more file
  484. return true;
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose: returns name (filename portion only) of the current file.
  488. // Name is emitted in UTF-8 encoding.
  489. // NOTE: This method returns a pointer into a static buffer, either a member
  490. // or the buffer inside the _finddata_t.
  491. //-----------------------------------------------------------------------------
  492. const char *CDirIterator::CurrentFileName()
  493. {
  494. #if defined( _WIN32 )
  495. return m_rgchFileName;
  496. #elif defined( _PS3 )
  497. return m_pDirEntry->entry_name.d_name;
  498. #else
  499. return m_pFindData->name;
  500. #endif
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: returns size of the file
  504. //-----------------------------------------------------------------------------
  505. int64 CDirIterator::CurrentFileLength() const
  506. {
  507. #if defined( _WIN32 )
  508. LARGE_INTEGER li = { { m_pFindData->nFileSizeLow, m_pFindData->nFileSizeHigh } };
  509. return li.QuadPart;
  510. #elif defined( _PS3 )
  511. return m_pDirEntry->attribute.st_size;
  512. #else
  513. return (int64)m_pFindData->size;
  514. #endif
  515. }
  516. #if defined( _WIN32 )
  517. //-----------------------------------------------------------------------------
  518. // Purpose: utility for converting a system filetime to a regular time
  519. //-----------------------------------------------------------------------------
  520. time64_t FileTimeToUnixTime( FILETIME filetime )
  521. {
  522. long long int t = filetime.dwHighDateTime;
  523. t <<= 32;
  524. t += (unsigned long)filetime.dwLowDateTime;
  525. t -= 116444736000000000LL;
  526. return t / 10000000;
  527. }
  528. #endif
  529. //-----------------------------------------------------------------------------
  530. // Purpose: returns last write time of the file
  531. //-----------------------------------------------------------------------------
  532. time64_t CDirIterator::CurrentFileWriteTime() const
  533. {
  534. #if defined( _WIN32 )
  535. return FileTimeToUnixTime( m_pFindData->ftLastWriteTime );
  536. #elif defined( _PS3 )
  537. return m_pDirEntry->attribute.st_mtime;
  538. #else
  539. return m_pFindData->time_write;
  540. #endif
  541. }
  542. //-----------------------------------------------------------------------------
  543. // Purpose: returns the creation time of the file
  544. //-----------------------------------------------------------------------------
  545. time64_t CDirIterator::CurrentFileCreateTime() const
  546. {
  547. #if defined( _WIN32 )
  548. return FileTimeToUnixTime( m_pFindData->ftCreationTime );
  549. #elif defined( _PS3 )
  550. return m_pDirEntry->attribute.st_ctime;
  551. #else
  552. return m_pFindData->time_create;
  553. #endif
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Purpose: returns whether current item under examination is a directory
  557. //-----------------------------------------------------------------------------
  558. bool CDirIterator::BCurrentIsDir() const
  559. {
  560. #if defined( _WIN32 )
  561. return (m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  562. #elif defined( _PS3 )
  563. return (m_pDirEntry->attribute.st_mode & CELL_FS_S_IFDIR ? true : false);
  564. #else
  565. return (m_pFindData->attrib & _A_SUBDIR ? true : false);
  566. #endif
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose: returns whether current item under examination is a hidden file
  570. //-----------------------------------------------------------------------------
  571. bool CDirIterator::BCurrentIsHidden() const
  572. {
  573. #if defined( _WIN32 )
  574. return (m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
  575. #elif defined( _PS3 )
  576. return false;
  577. #else
  578. return (m_pFindData->attrib & _A_HIDDEN ? true : false);
  579. #endif
  580. }
  581. //-----------------------------------------------------------------------------
  582. // Purpose: returns whether current item under examination is read-only
  583. //-----------------------------------------------------------------------------
  584. bool CDirIterator::BCurrentIsReadOnly() const
  585. {
  586. #if defined( _WIN32 )
  587. return (m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0;
  588. #elif defined( _PS3 )
  589. // assume this is windows version of read only.. can execute. Is it writable?
  590. return (m_pDirEntry->attribute.st_mode & CELL_FS_S_IWUSR == 0);
  591. #else
  592. return (m_pFindData->attrib & _A_RDONLY ? true : false);
  593. #endif
  594. }
  595. //-----------------------------------------------------------------------------
  596. // Purpose: returns whether current item under examination is marked as a system file
  597. //-----------------------------------------------------------------------------
  598. bool CDirIterator::BCurrentIsSystem() const
  599. {
  600. #if defined( _WIN32 )
  601. return (m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0;
  602. #elif defined( _PS3 )
  603. return false;
  604. #else
  605. return (m_pFindData->attrib & _A_SYSTEM ? true : false);
  606. #endif
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose: returns whether current item under examination is marked for archiving
  610. //-----------------------------------------------------------------------------
  611. bool CDirIterator::BCurrentIsMarkedForArchive() const
  612. {
  613. #if defined( _WIN32 )
  614. return (m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) != 0;
  615. #elif defined( _PS3 )
  616. return false;
  617. #else
  618. return (m_pFindData->attrib & _A_ARCH ? true : false);
  619. #endif
  620. }
  621. //-----------------------------------------------------------------------------
  622. // Purpose: Constructor
  623. //-----------------------------------------------------------------------------
  624. CFileWriter::CFileWriter( bool bAsync )
  625. {
  626. #ifdef ASYNC_FILEIO
  627. m_bDefaultAsync = bAsync;
  628. #else
  629. m_bDefaultAsync = false;
  630. #endif
  631. m_hFileDest = INVALID_HANDLE_VALUE;
  632. m_bAsync = m_bDefaultAsync;
  633. m_cPendingCallbacksFromOtherThreads = 0;
  634. m_cubOutstanding = 0;
  635. m_cubWritten = 0;
  636. m_unThreadID = 0;
  637. }
  638. //-----------------------------------------------------------------------------
  639. // Purpose: Destructor
  640. //-----------------------------------------------------------------------------
  641. CFileWriter::~CFileWriter()
  642. {
  643. Close();
  644. }
  645. #ifdef ASYNC_FILEIO
  646. #ifdef _WIN32
  647. // our own version of overlapped structure passed through async writes
  648. struct FileWriterOverlapped_t : public OVERLAPPED
  649. {
  650. CFileWriter *m_pFileWriter;
  651. void *m_pvData;
  652. size_t m_cubData;
  653. };
  654. #elif defined(_PS3)
  655. // bugbug ps3 - impement?
  656. #elif defined(POSIX)
  657. // our own version of overlapped structure passed through async writes
  658. struct FileWriterOverlapped_t : public aiocb
  659. {
  660. CFileWriter *m_pFileWriter;
  661. void *m_pvData;
  662. size_t m_cubData;
  663. };
  664. #else
  665. #error "struct me"
  666. #endif
  667. #endif
  668. //-----------------------------------------------------------------------------
  669. // Purpose: sets which file to write to
  670. //-----------------------------------------------------------------------------
  671. bool CFileWriter::BSetFile( const char *pchFile, bool bAllowOpenExisting )
  672. {
  673. CPathString strPath( pchFile );
  674. // make sure the full path to file exists
  675. CUtlString strCopyUTF8 = strPath.GetUTF8Path();
  676. Q_StripFilename( const_cast<char*>(strCopyUTF8.Access()) );
  677. CreateDirRecursive( strCopyUTF8.Access() );
  678. Close();
  679. m_bAsync = m_bDefaultAsync;
  680. m_cubWritten = 0;
  681. m_cubOutstanding = 0;
  682. m_unThreadID = 0;
  683. m_cPendingCallbacksFromOtherThreads = 0;
  684. #ifdef _WIN32
  685. DWORD dwFlags = FILE_ATTRIBUTE_NORMAL;
  686. if ( m_bAsync )
  687. dwFlags |= FILE_FLAG_OVERLAPPED;
  688. // First try to open existing file, if specified that we should allow that
  689. if ( bAllowOpenExisting )
  690. {
  691. m_hFileDest = ::CreateFileW( strPath.GetWCharPathPrePended(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, dwFlags, NULL );
  692. if ( m_hFileDest == INVALID_HANDLE_VALUE )
  693. {
  694. // clear overlapped and try again
  695. dwFlags &= ~FILE_FLAG_OVERLAPPED;
  696. m_hFileDest = ::CreateFileW( strPath.GetWCharPathPrePended(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, dwFlags, NULL );
  697. if ( m_hFileDest != INVALID_HANDLE_VALUE )
  698. {
  699. m_bAsync = false;
  700. }
  701. }
  702. if ( m_hFileDest != INVALID_HANDLE_VALUE )
  703. {
  704. LARGE_INTEGER liOffset;
  705. liOffset.QuadPart = 0;
  706. LARGE_INTEGER liFilePtr;
  707. ::SetFilePointerEx( m_hFileDest, liOffset, &liFilePtr, FILE_END );
  708. m_cubWritten = liFilePtr.QuadPart;
  709. }
  710. }
  711. // If we didn't already open existing, then move on to creation
  712. if ( m_hFileDest == INVALID_HANDLE_VALUE )
  713. {
  714. // make sure the full path to file exists
  715. CUtlString strPathCopyUTF8 = strPath.GetUTF8Path();
  716. Q_StripFilename( const_cast<char*>(strPathCopyUTF8.Access()) );
  717. CreateDirRecursive( strPathCopyUTF8.Access() );
  718. // Reset back to try overlapped below incase we tried without above
  719. if ( m_bAsync )
  720. dwFlags |= FILE_FLAG_OVERLAPPED;
  721. m_hFileDest = ::CreateFileW( strPath.GetWCharPathPrePended(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlags, NULL );
  722. if ( m_hFileDest == INVALID_HANDLE_VALUE )
  723. {
  724. // clear overlapped and try again
  725. m_bAsync = false;
  726. dwFlags &= ~FILE_FLAG_OVERLAPPED;
  727. m_hFileDest = ::CreateFileW( strPath.GetWCharPathPrePended(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlags, NULL );
  728. if ( m_hFileDest == INVALID_HANDLE_VALUE )
  729. return false;
  730. }
  731. }
  732. #elif defined(POSIX)
  733. int flags = O_WRONLY;
  734. if ( bAllowOpenExisting )
  735. flags |= O_CREAT;
  736. else
  737. flags |= O_CREAT | O_TRUNC;
  738. m_hFileDest = (HANDLE)open( strPath.GetUTF8Path(), flags, S_IRWXU );
  739. if ( bAllowOpenExisting )
  740. {
  741. off_t offset = lseek( (intptr_t)m_hFileDest, 0, SEEK_END );
  742. m_cubWritten = offset;
  743. }
  744. #else
  745. #error
  746. #endif
  747. m_unThreadID = ThreadGetCurrentId();
  748. return ( m_hFileDest != INVALID_HANDLE_VALUE );
  749. }
  750. void CFileWriter::Sleep( uint nMSec )
  751. {
  752. #ifdef _WIN32
  753. ::SleepEx( nMSec, TRUE );
  754. #elif PLATFORM_PS3
  755. sys_timer_usleep( nMSec * 1000 );
  756. #elif defined(POSIX)
  757. if ( nMSec == 0 )
  758. sched_yield();
  759. else
  760. usleep( nMSec * 1000 );
  761. #else
  762. #error
  763. #endif
  764. }
  765. //-----------------------------------------------------------------------------
  766. // Purpose: Seeks to a specific location in the file
  767. //-----------------------------------------------------------------------------
  768. bool CFileWriter::Seek( uint64 offset, ESeekOrigin eOrigin )
  769. {
  770. if ( m_bAsync )
  771. {
  772. AssertMsg( false, "Seeking to a position not supported with async io" );
  773. return false;
  774. }
  775. bool bSuccess = false;
  776. #ifdef _WIN32
  777. DWORD dwMoveMethod = FILE_BEGIN;
  778. switch( eOrigin )
  779. {
  780. case k_ESeekCur:
  781. dwMoveMethod = FILE_CURRENT;
  782. break;
  783. case k_ESeekEnd:
  784. dwMoveMethod = FILE_END;
  785. break;
  786. default:
  787. dwMoveMethod = FILE_BEGIN;
  788. }
  789. LARGE_INTEGER largeIntOffset;
  790. largeIntOffset.QuadPart = offset;
  791. if ( ::SetFilePointerEx( m_hFileDest, largeIntOffset, NULL, dwMoveMethod ) )
  792. bSuccess = true;
  793. #elif defined(POSIX)
  794. int orgin = SEEK_SET;
  795. switch( eOrigin )
  796. {
  797. case k_ESeekCur:
  798. orgin = SEEK_CUR;
  799. break;
  800. case k_ESeekEnd:
  801. orgin = SEEK_END;
  802. break;
  803. default:
  804. orgin = SEEK_SET;
  805. }
  806. // fseeko will work on 64 bit file offsets if _FILE_OFFSET_BITS 64 is defined, is this the best way
  807. // to do this on posix builds?
  808. bSuccess = lseek( (intptr_t)m_hFileDest, (off_t)offset, orgin ) != -1;
  809. #else
  810. #error
  811. #endif
  812. return bSuccess;
  813. }
  814. //-----------------------------------------------------------------------------
  815. // Purpose: posts a buffer to be written to the file
  816. //-----------------------------------------------------------------------------
  817. bool CFileWriter::Write( const void *pvData, uint32 cubData )
  818. {
  819. if ( cubData == 0 )
  820. return true;
  821. #if defined( _PS3 )
  822. if ( write( (int)m_hFileDest, pvData, cubData ) == cubData )
  823. return true;
  824. return false;
  825. #else
  826. BOOL bRet = 0;
  827. #ifdef ASYNC_FILEIO
  828. if ( m_bAsync )
  829. {
  830. // get any outstanding write callbacks
  831. if ( m_cubOutstanding > 0 )
  832. {
  833. ::SleepEx( 0, TRUE );
  834. }
  835. // make sure we don't have too much data outstanding
  836. while ( m_cubOutstanding > (10*k_nMegabyte) )
  837. {
  838. ::SleepEx( 10, TRUE );
  839. }
  840. // build the overlapped info that will get passed through the write
  841. FileWriterOverlapped_t *pFileWriterOverlapped = new FileWriterOverlapped_t;
  842. memset( pFileWriterOverlapped, 0x0, sizeof(FileWriterOverlapped_t) );
  843. pFileWriterOverlapped->m_pFileWriter = this;
  844. pFileWriterOverlapped->m_pvData = PvAlloc( cubData );
  845. pFileWriterOverlapped->m_cubData = cubData;
  846. memcpy( pFileWriterOverlapped->m_pvData, pvData, cubData );
  847. // work out where to write to
  848. #ifdef _WIN32
  849. pFileWriterOverlapped->Offset = ( uint32 ) ( m_cubWritten & 0xffffffff );
  850. pFileWriterOverlapped->OffsetHigh = ( uint32 ) ( m_cubWritten >> 32 );
  851. #elif defined(POSIX)
  852. pFileWriterOverlapped->aio_offset = m_cubWritten;
  853. pFileWriterOverlapped->aio_buf = pFileWriterOverlapped->m_pvData;
  854. pFileWriterOverlapped->aio_nbytes = pFileWriterOverlapped->m_cubData;
  855. pFileWriterOverlapped->aio_fildes = (int)m_hFileDest;
  856. /* Link the AIO request with a thread callback */
  857. pFileWriterOverlapped->aio_sigevent.sigev_notify = SIGEV_THREAD;
  858. pFileWriterOverlapped->aio_sigevent.sigev_notify_function = &CFileWriter::ThreadedWriteFileCompletionFunc;
  859. pFileWriterOverlapped->aio_sigevent.sigev_notify_attributes = NULL;
  860. pFileWriterOverlapped->aio_sigevent.sigev_value.sival_ptr = pFileWriterOverlapped;
  861. #else
  862. #error
  863. #endif
  864. #ifdef _WIN32
  865. // post write
  866. bRet = ::WriteFileEx( m_hFileDest, pFileWriterOverlapped->m_pvData, cubData, pFileWriterOverlapped, &CFileWriter::ThreadedWriteFileCompletionFunc );
  867. #elif defined(POSIX)
  868. bRet = aio_write( pFileWriterOverlapped );
  869. bRet = !bRet; // aio_read returns 0 on success, this func returns success if bRet != 0
  870. #else
  871. #error
  872. #endif
  873. if ( bRet )
  874. {
  875. ThreadInterlockedExchangeAdd( &m_cubOutstanding, cubData );
  876. if ( ThreadGetCurrentId() != m_unThreadID )
  877. {
  878. // this is not the main thread so we have to wait here
  879. ThreadInterlockedIncrement( &m_cPendingCallbacksFromOtherThreads );
  880. while ( m_cPendingCallbacksFromOtherThreads )
  881. {
  882. // we have to wait here since the OS can signal us only
  883. // on this current thread
  884. ::SleepEx( 10, TRUE );
  885. }
  886. }
  887. }
  888. }
  889. else
  890. #endif // ASYNC_FILEIO
  891. {
  892. #ifdef _WIN32
  893. // normal write
  894. DWORD dwBytesWritten = 0;
  895. ::WriteFile( m_hFileDest, pvData, cubData, &dwBytesWritten, NULL );
  896. bRet = ( dwBytesWritten == cubData );
  897. #elif defined(POSIX)
  898. bRet = write( (intptr_t)m_hFileDest, pvData, cubData );
  899. #else
  900. #error
  901. #endif
  902. }
  903. // increment
  904. m_cubWritten += cubData;
  905. return ( bRet != 0 );
  906. #endif // _PS3
  907. }
  908. //-----------------------------------------------------------------------------
  909. // Purpose: Convenient printf with no dynamic memory allocation
  910. //-----------------------------------------------------------------------------
  911. int CFileWriter::Printf( char *pDest, int bufferLen, char const *pFormat, ... )
  912. {
  913. va_list marker;
  914. va_start( marker, pFormat );
  915. // _vsnprintf will not write a terminator if the output string uses the entire buffer you provide
  916. int len = _vsnprintf( pDest, bufferLen-1, pFormat, marker );
  917. va_end( marker );
  918. // Len < 0 represents an overflow on windows; len > buffer length on posix
  919. if (( len < 0 ) || (len >= bufferLen ) )
  920. {
  921. len = bufferLen-1;
  922. }
  923. pDest[len] = 0;
  924. if ( !Write( pDest, len ) )
  925. return 0;
  926. return len;
  927. }
  928. //-----------------------------------------------------------------------------
  929. // Purpose: ensures any writes have been completed
  930. //-----------------------------------------------------------------------------
  931. void CFileWriter::Flush()
  932. {
  933. #ifdef WIN32
  934. FlushFileBuffers( m_hFileDest );
  935. #endif
  936. if ( m_unThreadID == ThreadGetCurrentId() )
  937. {
  938. // wait for all writes to be complete
  939. int cWaits = 0;
  940. const int k_nMaxWaits = 60000; /* roughly one minute */
  941. while ( m_cubOutstanding && cWaits < k_nMaxWaits )
  942. {
  943. Sleep( 10 );
  944. cWaits++;
  945. }
  946. AssertMsg1( cWaits < k_nMaxWaits, "Waited 60k iterations in CFileWriter::Flush - m_cubOutstanding = %u", m_cubOutstanding );
  947. }
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Purpose: check if file is open
  951. //-----------------------------------------------------------------------------
  952. bool CFileWriter::BFileOpen()
  953. {
  954. if ( m_hFileDest != INVALID_HANDLE_VALUE )
  955. return true;
  956. return false;
  957. }
  958. //-----------------------------------------------------------------------------
  959. // Purpose: closes the file
  960. //-----------------------------------------------------------------------------
  961. void CFileWriter::Close()
  962. {
  963. if ( m_hFileDest != INVALID_HANDLE_VALUE )
  964. {
  965. Flush();
  966. // temp handle to avoid double close in threaded environment
  967. HANDLE hFileDest = m_hFileDest;
  968. m_hFileDest = INVALID_HANDLE_VALUE;
  969. #ifdef _WIN32
  970. ::CloseHandle( hFileDest );
  971. #elif defined(POSIX)
  972. close( (intptr_t)hFileDest );
  973. #else
  974. #error
  975. #endif
  976. }
  977. // Close has to be called from thread that called BSetFile
  978. Assert( m_cPendingCallbacksFromOtherThreads == 0 );
  979. }
  980. //-----------------------------------------------------------------------------
  981. // Purpose: async callback for when a file write has completed
  982. //-----------------------------------------------------------------------------
  983. #ifdef ASYNC_FILEIO
  984. #ifdef _WIN32
  985. void CFileWriter::ThreadedWriteFileCompletionFunc( unsigned long dwErrorCode, unsigned long dwBytesTransfered, struct _OVERLAPPED *pOverlapped )
  986. {
  987. FileWriterOverlapped_t *pFileWriterOverlapped = (FileWriterOverlapped_t *)pOverlapped;
  988. ThreadInterlockedExchangeAdd( &pFileWriterOverlapped->m_pFileWriter->m_cubOutstanding, (int)(0-pFileWriterOverlapped->m_cubData) );
  989. if ( pFileWriterOverlapped->m_pFileWriter->m_unThreadID != ThreadGetCurrentId() )
  990. {
  991. // this was not the main thread, reduce counter
  992. ThreadInterlockedDecrement( &pFileWriterOverlapped->m_pFileWriter->m_cPendingCallbacksFromOtherThreads );
  993. }
  994. FreePv( pFileWriterOverlapped->m_pvData );
  995. delete pFileWriterOverlapped;
  996. }
  997. #elif defined( _PS3 )
  998. // bugbug PS3
  999. #elif defined(POSIX)
  1000. void CFileWriter::ThreadedWriteFileCompletionFunc( sigval sigval )
  1001. {
  1002. FileWriterOverlapped_t *pFileWriterOverlapped = (FileWriterOverlapped_t *)sigval.sival_ptr;
  1003. if ( aio_error( pFileWriterOverlapped ) == 0 )
  1004. {
  1005. uint nBytesWrite = aio_return( pFileWriterOverlapped );
  1006. Assert( nBytesWrite == pFileWriterOverlapped->m_cubData );
  1007. pFileWriterOverlapped->m_pFileWriter->m_cOutstandingWrites--;
  1008. pFileWriterOverlapped->m_pFileWriter->m_cubOutstanding += ( 0 - pFileWriterOverlapped->m_cubData );
  1009. FreePv( pFileWriterOverlapped->m_pvData );
  1010. delete pFileWriterOverlapped;
  1011. }
  1012. }
  1013. #else
  1014. #error
  1015. #endif
  1016. #endif // ASYNC_FILEIO
  1017. #ifdef WIN32
  1018. struct DirWatcherOverlapped : public OVERLAPPED
  1019. {
  1020. CDirWatcher *m_pDirWatcher;
  1021. };
  1022. #endif
  1023. #if !defined(_PS3) && !defined(_X360)
  1024. // a buffer full of file names
  1025. static const int k_cubDirWatchBufferSize = 8 * 1024;
  1026. //-----------------------------------------------------------------------------
  1027. // Purpose: directory watching
  1028. //-----------------------------------------------------------------------------
  1029. CDirWatcher::CDirWatcher()
  1030. {
  1031. m_hFile = NULL;
  1032. m_pOverlapped = NULL;
  1033. m_pFileInfo = NULL;
  1034. #ifdef OSX
  1035. m_WatcherStream = 0;
  1036. #endif
  1037. }
  1038. //-----------------------------------------------------------------------------
  1039. // Purpose: directory watching
  1040. //-----------------------------------------------------------------------------
  1041. CDirWatcher::~CDirWatcher()
  1042. {
  1043. #ifdef WIN32
  1044. if ( m_pOverlapped )
  1045. {
  1046. // mark the overlapped structure as gone
  1047. DirWatcherOverlapped *pDirWatcherOverlapped = (DirWatcherOverlapped *)m_pOverlapped;
  1048. pDirWatcherOverlapped->m_pDirWatcher = NULL;
  1049. }
  1050. if ( m_hFile )
  1051. {
  1052. // make sure we flush any pending I/O's on the handle
  1053. ::CancelIo( m_hFile );
  1054. ::SleepEx( 0, TRUE );
  1055. // close the handle
  1056. ::CloseHandle( m_hFile );
  1057. }
  1058. #elif defined(OSX)
  1059. if ( m_WatcherStream )
  1060. {
  1061. FSEventStreamStop( (FSEventStreamRef)m_WatcherStream );
  1062. FSEventStreamInvalidate( (FSEventStreamRef)m_WatcherStream );
  1063. FSEventStreamRelease( (FSEventStreamRef)m_WatcherStream );
  1064. m_WatcherStream = 0;
  1065. }
  1066. #endif
  1067. if ( m_pFileInfo )
  1068. {
  1069. free( m_pFileInfo );
  1070. }
  1071. if ( m_pOverlapped )
  1072. {
  1073. free( m_pOverlapped );
  1074. }
  1075. }
  1076. #ifdef WIN32
  1077. //-----------------------------------------------------------------------------
  1078. // Purpose: callback watch
  1079. // gets called on the same thread whenever a SleepEx() occurs
  1080. //-----------------------------------------------------------------------------
  1081. class CDirWatcherFriend
  1082. {
  1083. public:
  1084. static void WINAPI DirWatchCallback( DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED *pOverlapped )
  1085. {
  1086. DirWatcherOverlapped *pDirWatcherOverlapped = (DirWatcherOverlapped *)pOverlapped;
  1087. // see if we've been cancelled
  1088. if ( !pDirWatcherOverlapped->m_pDirWatcher )
  1089. return;
  1090. // parse and pass back
  1091. if ( dwNumberOfBytesTransfered > sizeof(FILE_NOTIFY_INFORMATION) )
  1092. {
  1093. FILE_NOTIFY_INFORMATION *pFileNotifyInformation = (FILE_NOTIFY_INFORMATION *)pDirWatcherOverlapped->m_pDirWatcher->m_pFileInfo;
  1094. do
  1095. {
  1096. // null terminate the string and turn it to UTF-8
  1097. int cNumWChars = pFileNotifyInformation->FileNameLength / sizeof(wchar_t);
  1098. wchar_t *pwchT = new wchar_t[cNumWChars + 1];
  1099. memcpy( pwchT, pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength );
  1100. pwchT[cNumWChars] = 0;
  1101. CStrAutoEncode strAutoEncode( pwchT );
  1102. // add it to our list
  1103. pDirWatcherOverlapped->m_pDirWatcher->AddFileToChangeList( strAutoEncode.ToString() );
  1104. delete[] pwchT;
  1105. if ( pFileNotifyInformation->NextEntryOffset == 0 )
  1106. break;
  1107. // move to the next file
  1108. pFileNotifyInformation = (FILE_NOTIFY_INFORMATION *)(((byte*)pFileNotifyInformation) + pFileNotifyInformation->NextEntryOffset);
  1109. } while ( 1 );
  1110. }
  1111. // watch again
  1112. pDirWatcherOverlapped->m_pDirWatcher->PostDirWatch();
  1113. }
  1114. };
  1115. #elif defined(OSX)
  1116. void CheckDirectoryForChanges( const char *path_buff, CDirWatcher *pDirWatch, bool bRecurse )
  1117. {
  1118. DIR *dir = opendir(path_buff);
  1119. char fullpath[MAX_PATH];
  1120. struct dirent *dirent;
  1121. struct timespec ts = { 0, 0 };
  1122. bool bTimeSet = false;
  1123. while ( (dirent = readdir(dir)) != NULL )
  1124. {
  1125. if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
  1126. continue;
  1127. snprintf( fullpath, PATH_MAX, "%s/%s", path_buff, dirent->d_name );
  1128. struct stat st;
  1129. if (lstat(fullpath, &st) != 0)
  1130. continue;
  1131. if ( S_ISDIR(st.st_mode) && bRecurse )
  1132. {
  1133. CheckDirectoryForChanges( fullpath, pDirWatch, bRecurse );
  1134. }
  1135. else if ( st.st_mtimespec.tv_sec > pDirWatch->m_modTime.tv_sec ||
  1136. ( st.st_mtimespec.tv_sec == pDirWatch->m_modTime.tv_sec && st.st_mtimespec.tv_nsec > pDirWatch->m_modTime.tv_nsec ) )
  1137. {
  1138. ts = st.st_mtimespec;
  1139. bTimeSet = true;
  1140. // the win32 size only sends up the dir relative to the watching dir, so replicate that here
  1141. pDirWatch->AddFileToChangeList( fullpath + pDirWatch->m_BaseDir.Length() + 1 );
  1142. }
  1143. }
  1144. if ( bTimeSet )
  1145. pDirWatch->m_modTime = ts;
  1146. closedir(dir);
  1147. }
  1148. static void fsevents_callback( ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents,void *eventPaths,
  1149. const FSEventStreamEventFlags eventMasks[], const FSEventStreamEventId eventIDs[] )
  1150. {
  1151. char path_buff[PATH_MAX];
  1152. for (int i=0; i < numEvents; i++)
  1153. {
  1154. char **paths = (char **)eventPaths;
  1155. strcpy(path_buff, paths[i]);
  1156. int len = strlen(path_buff);
  1157. if (path_buff[len-1] == '/')
  1158. {
  1159. // chop off a trailing slash
  1160. path_buff[--len] = '\0';
  1161. }
  1162. bool bRecurse = false;
  1163. if (eventMasks[i] & kFSEventStreamEventFlagMustScanSubDirs
  1164. || eventMasks[i] & kFSEventStreamEventFlagUserDropped
  1165. || eventMasks[i] & kFSEventStreamEventFlagKernelDropped)
  1166. {
  1167. bRecurse = true;
  1168. }
  1169. CDirWatcher *pDirWatch = (CDirWatcher *)clientCallBackInfo;
  1170. // make sure its in our subdir
  1171. if ( !V_strnicmp( path_buff, pDirWatch->m_BaseDir.String(), pDirWatch->m_BaseDir.Length() ) )
  1172. CheckDirectoryForChanges( path_buff, pDirWatch, bRecurse );
  1173. }
  1174. }
  1175. #endif
  1176. //-----------------------------------------------------------------------------
  1177. // Purpose: only one directory can be watched at a time
  1178. //-----------------------------------------------------------------------------
  1179. void CDirWatcher::SetDirToWatch( const char *pchDir )
  1180. {
  1181. if ( !pchDir || !*pchDir )
  1182. return;
  1183. CPathString strPath( pchDir );
  1184. #ifdef WIN32
  1185. // open the directory
  1186. m_hFile = ::CreateFileW( strPath.GetWCharPathPrePended(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS, NULL );
  1187. // create our buffers
  1188. m_pFileInfo = malloc( k_cubDirWatchBufferSize );
  1189. m_pOverlapped = malloc( sizeof( DirWatcherOverlapped ) );
  1190. // post a watch
  1191. PostDirWatch();
  1192. #elif defined(OSX)
  1193. CFStringRef mypath = CFStringCreateWithCString( NULL, strPath.GetUTF8Path(), kCFStringEncodingMacRoman );
  1194. if ( !mypath )
  1195. {
  1196. Assert( !"Failed to CFStringCreateWithCString watcher path" );
  1197. return;
  1198. }
  1199. CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
  1200. FSEventStreamContext callbackInfo = {0, this, NULL, NULL, NULL};
  1201. CFAbsoluteTime latency = 1.0; // Latency in seconds
  1202. m_WatcherStream = (void *)FSEventStreamCreate(NULL,
  1203. &fsevents_callback,
  1204. &callbackInfo,
  1205. pathsToWatch,
  1206. kFSEventStreamEventIdSinceNow,
  1207. latency,
  1208. kFSEventStreamCreateFlagNoDefer
  1209. );
  1210. FSEventStreamScheduleWithRunLoop( (FSEventStreamRef)m_WatcherStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  1211. CFRelease(pathsToWatch );
  1212. CFRelease( mypath );
  1213. FSEventStreamStart( (FSEventStreamRef)m_WatcherStream );
  1214. char szFullPath[MAX_PATH];
  1215. Q_MakeAbsolutePath( szFullPath, sizeof(szFullPath), pchDir );
  1216. m_BaseDir = szFullPath;
  1217. struct timeval tv;
  1218. gettimeofday( &tv, NULL );
  1219. TIMEVAL_TO_TIMESPEC( &tv, &m_modTime );
  1220. #else
  1221. Assert( !"Impl me" );
  1222. #endif
  1223. }
  1224. #ifdef WIN32
  1225. //-----------------------------------------------------------------------------
  1226. // Purpose: used by callback functions to push a file onto the list
  1227. //-----------------------------------------------------------------------------
  1228. void CDirWatcher::PostDirWatch()
  1229. {
  1230. memset( m_pOverlapped, 0, sizeof(DirWatcherOverlapped) );
  1231. DirWatcherOverlapped *pDirWatcherOverlapped = (DirWatcherOverlapped *)m_pOverlapped;
  1232. pDirWatcherOverlapped->m_pDirWatcher = this;
  1233. DWORD dwBytes;
  1234. ::ReadDirectoryChangesW( m_hFile, m_pFileInfo, k_cubDirWatchBufferSize, TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME, &dwBytes, (OVERLAPPED *)m_pOverlapped, &CDirWatcherFriend::DirWatchCallback );
  1235. }
  1236. #endif
  1237. //-----------------------------------------------------------------------------
  1238. // Purpose: used by callback functions to push a file onto the list
  1239. //-----------------------------------------------------------------------------
  1240. void CDirWatcher::AddFileToChangeList( const char *pchFile )
  1241. {
  1242. // make sure it isn't already in the list
  1243. FOR_EACH_LL( m_listChangedFiles, i )
  1244. {
  1245. if ( !Q_stricmp( m_listChangedFiles[i], pchFile ) )
  1246. return;
  1247. }
  1248. m_listChangedFiles.AddToTail( pchFile );
  1249. }
  1250. //-----------------------------------------------------------------------------
  1251. // Purpose: retrieve any changes
  1252. //-----------------------------------------------------------------------------
  1253. bool CDirWatcher::GetChangedFile( CUtlString *psFile )
  1254. {
  1255. #ifdef WIN32
  1256. // this will trigger any pending directory reads
  1257. // this does get hit other places in the code; so the callback can happen at any time
  1258. ::SleepEx( 0, TRUE );
  1259. #endif
  1260. if ( !m_listChangedFiles.Count() )
  1261. return false;
  1262. *psFile = m_listChangedFiles[m_listChangedFiles.Head()];
  1263. m_listChangedFiles.Remove( m_listChangedFiles.Head() );
  1264. return true;
  1265. }
  1266. #ifdef DBGFLAG_VALIDATE
  1267. void CDirWatcher::Validate( CValidator &validator, const char *pchName )
  1268. {
  1269. VALIDATE_SCOPE();
  1270. validator.ClaimMemory( m_pOverlapped );
  1271. validator.ClaimMemory( m_pFileInfo );
  1272. ValidateObj( m_listChangedFiles );
  1273. FOR_EACH_LL( m_listChangedFiles, i )
  1274. {
  1275. ValidateObj( m_listChangedFiles[i] );
  1276. }
  1277. }
  1278. #endif
  1279. #endif // _PS3 || _X360
  1280. //-----------------------------------------------------------------------------
  1281. // Purpose: utility function to create dirs & subdirs
  1282. //-----------------------------------------------------------------------------
  1283. bool CreateDirRecursive( const char *pchPathIn )
  1284. {
  1285. CPathString strPath( pchPathIn );
  1286. // Cast away const, we're going to modify in place even though that's kind of evil
  1287. char *path = (char *)strPath.GetUTF8Path();
  1288. // Does it already exist?
  1289. if ( BFileExists( path ) )
  1290. return true;
  1291. // Walk backwards to first non-existing dir that we find
  1292. char *s = path + Q_strlen(path) - 1;
  1293. while ( s > path )
  1294. {
  1295. if ( *s == CORRECT_PATH_SEPARATOR )
  1296. {
  1297. *s = '\0';
  1298. bool bExists = BFileExists( path );
  1299. *s = CORRECT_PATH_SEPARATOR;
  1300. if ( bExists )
  1301. {
  1302. ++s;
  1303. break;
  1304. }
  1305. }
  1306. --s;
  1307. }
  1308. // and then move forwards from there
  1309. while ( *s )
  1310. {
  1311. if ( *s == CORRECT_PATH_SEPARATOR )
  1312. {
  1313. *s = '\0';
  1314. BCreateDirectory( path );
  1315. *s = CORRECT_PATH_SEPARATOR;
  1316. }
  1317. s++;
  1318. }
  1319. if ( !BCreateDirectory( path ) )
  1320. {
  1321. return false;
  1322. }
  1323. return true;
  1324. }
  1325. //-----------------------------------------------------------------------------
  1326. // Purpose: Creates the directory, returning true if it is created, or if it already existed
  1327. //-----------------------------------------------------------------------------
  1328. bool BCreateDirectory( const char *path )
  1329. {
  1330. CPathString pathStr( path );
  1331. #ifdef WIN32
  1332. if ( ::CreateDirectoryW( pathStr.GetWCharPathPrePended(), NULL ) )
  1333. return true;
  1334. if ( ::GetLastError() == ERROR_ALREADY_EXISTS )
  1335. return true;
  1336. return false;
  1337. #else
  1338. int i = mkdir( pathStr.GetUTF8Path(), S_IRWXU | S_IRWXG | S_IRWXO );
  1339. if ( i == 0 )
  1340. return true;
  1341. if ( errno == EEXIST )
  1342. return true;
  1343. return false;
  1344. #endif
  1345. }
  1346. //-----------------------------------------------------------------------------
  1347. // Purpose: make a file writable
  1348. //-----------------------------------------------------------------------------
  1349. bool MakeFileWriteable( const char *pszFileNameIn )
  1350. {
  1351. CPathString strPath( pszFileNameIn );
  1352. #if defined( WIN32_FILEIO )
  1353. DWORD dwFileAttributes = ::GetFileAttributesW( strPath.GetWCharPathPrePended() );
  1354. if (dwFileAttributes != INVALID_FILE_ATTRIBUTES)
  1355. {
  1356. // remove flags that make it read only, if necessary
  1357. if (dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY))
  1358. {
  1359. dwFileAttributes &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY);
  1360. ::SetFileAttributesW( strPath.GetWCharPathPrePended(), dwFileAttributes );
  1361. }
  1362. return true;
  1363. }
  1364. return false;
  1365. #else
  1366. statBig_t statBuf;
  1367. if (statBig( strPath.GetUTF8Path(), &statBuf ) != 0)
  1368. return false;
  1369. if (statBuf.st_mode & _S_IWRITE)
  1370. return true;
  1371. int ret = chmod( strPath.GetUTF8Path(), statBuf.st_mode | _S_IWRITE );
  1372. return (ret == 0);
  1373. #endif
  1374. }
  1375. //-----------------------------------------------------------------------------
  1376. // Purpose: deletes a file
  1377. //-----------------------------------------------------------------------------
  1378. bool UnlinkFile( const char *pchFileIn )
  1379. {
  1380. CPathString strPath( pchFileIn );
  1381. #ifdef _WIN32
  1382. if (::DeleteFileW( strPath.GetWCharPathPrePended() ))
  1383. return true;
  1384. return false;
  1385. #else
  1386. return (0 == _unlink( strPath.GetUTF8Path() ));
  1387. #endif
  1388. }
  1389. //-----------------------------------------------------------------------------
  1390. // Purpose: Checks if a file exists
  1391. // Input: pchFileName - file name to check existence of (UTF8 - unqualified, relative, or fully qualified)
  1392. // Output: true if successful (file did not exist, or it existed and was deleted);
  1393. // false if unsuccessful (file existed but could not be deleted)
  1394. //-----------------------------------------------------------------------------
  1395. bool BFileExists( const char *pchFileNameIn )
  1396. {
  1397. CPathString strPath( pchFileNameIn );
  1398. #if defined( WIN32_FILEIO )
  1399. // Checking file attributes is fastest way to determine existence
  1400. return (INVALID_FILE_ATTRIBUTES != ::GetFileAttributesW( strPath.GetWCharPathPrePended() ));
  1401. #else
  1402. statBig_t buf;
  1403. return (0 == statBig( strPath.GetUTF8Path(), &buf ));
  1404. #endif
  1405. }
  1406. //-----------------------------------------------------------------------------
  1407. // Purpose: Deletes a file if it exists. If the file is read-only, will attempt
  1408. // to change file attributes and delete it.
  1409. // Input: pchFileName - file name to delete (unqualified, relative, or fully qualified)
  1410. // Output: true if successful (file did not exist, or it existed and was deleted);
  1411. // false if unsuccessful (file existed but could not be deleted)
  1412. //-----------------------------------------------------------------------------
  1413. bool BDeleteFileIfExists( const char * pchFileName )
  1414. {
  1415. // vast majority don't need to be touched/tested to delete them, so don't
  1416. // take the penalty in the common case.
  1417. if (UnlinkFile( pchFileName ))
  1418. return true;
  1419. if (BFileExists( pchFileName ))
  1420. {
  1421. MakeFileWriteable( pchFileName );
  1422. return UnlinkFile( pchFileName );
  1423. }
  1424. else
  1425. {
  1426. return true; // doesn't exist
  1427. }
  1428. }
  1429. //-----------------------------------------------------------------------------
  1430. // Purpose: Removes an empty directory that works on multiple platforms.
  1431. //-----------------------------------------------------------------------------
  1432. bool BRemoveDirectory( const char *pchPathIn )
  1433. {
  1434. MakeFileWriteable( pchPathIn );
  1435. CPathString strPath( pchPathIn );
  1436. #if defined( WIN32_FILEIO )
  1437. if (::RemoveDirectoryW( strPath.GetWCharPathPrePended() ))
  1438. return true;
  1439. return false;
  1440. #else
  1441. return _rmdir( pchPathIn ) == 0;
  1442. #endif
  1443. }
  1444. //-----------------------------------------------------------------------------
  1445. // Purpose: Removes a directory and all subdirectories and all files in those directories
  1446. //-----------------------------------------------------------------------------
  1447. bool BRemoveDirectoryRecursive( const char *pchPathIn )
  1448. {
  1449. CDirIterator dirIter( pchPathIn, "*" );
  1450. while (dirIter.BNextFile())
  1451. {
  1452. uint32 unLenPath = Q_strlen( pchPathIn ) + Q_strlen( dirIter.CurrentFileName() ) + 2;
  1453. char *pchPath = new char[unLenPath];
  1454. Q_snprintf( pchPath, unLenPath, "%s%c%s", pchPathIn, CORRECT_PATH_SEPARATOR, dirIter.CurrentFileName() );
  1455. if (dirIter.BCurrentIsDir())
  1456. {
  1457. BRemoveDirectoryRecursive( pchPath );
  1458. }
  1459. else
  1460. {
  1461. // Shouldn't have files in the root dir, delete them if found
  1462. BDeleteFileIfExists( pchPath );
  1463. }
  1464. delete[] pchPath;
  1465. }
  1466. return BRemoveDirectory( pchPathIn );
  1467. }
  1468. #ifdef POSIX
  1469. // findfirst/findnext implementation from filesystem/linux_support.[h|cpp]
  1470. // modified a bit for PS3
  1471. #if !defined(_PS3)
  1472. static char selectBuf[PATH_MAX];
  1473. #if defined(OSX) && !defined(__MAC_10_8)
  1474. static int FileSelect( direntBig_t *ent )
  1475. #elif defined(LINUX) || defined(OSX)
  1476. static int FileSelect( const direntBig_t *ent )
  1477. #else
  1478. #error
  1479. #endif
  1480. {
  1481. const char *mask = selectBuf;
  1482. const char *name = ent->d_name;
  1483. return FileSelect( name, mask );
  1484. }
  1485. #endif // !_PS3
  1486. static int FileSelect( const char *name, const char *mask )
  1487. {
  1488. //printf("Test:%s %s\n",mask,name);
  1489. if (!strcmp( name, "." ) || !strcmp( name, ".." )) return 0;
  1490. if (!strcmp( mask, "*.*" ) || !strcmp( mask, "*" )) return 1;
  1491. while (*mask && *name)
  1492. {
  1493. if (*mask == '*')
  1494. {
  1495. mask++; // move to the next char in the mask
  1496. if (!*mask) // if this is the end of the mask its a match
  1497. {
  1498. return 1;
  1499. }
  1500. while (*name && toupper( *name ) != toupper( *mask ))
  1501. { // while the two don't meet up again
  1502. name++;
  1503. }
  1504. if (!*name)
  1505. { // end of the name
  1506. break;
  1507. }
  1508. }
  1509. else if (*mask != '?')
  1510. {
  1511. if (toupper( *mask ) != toupper( *name ))
  1512. { // mismatched!
  1513. return 0;
  1514. }
  1515. else
  1516. {
  1517. mask++;
  1518. name++;
  1519. if (!*mask && !*name)
  1520. { // if its at the end of the buffer
  1521. return 1;
  1522. }
  1523. }
  1524. }
  1525. else /* mask is "?", we don't care*/
  1526. {
  1527. mask++;
  1528. name++;
  1529. }
  1530. }
  1531. return(!*mask && !*name); // both of the strings are at the end
  1532. }
  1533. #if !defined(_PS3)
  1534. int FillDataStruct( _finddata_t *dat )
  1535. {
  1536. statBig_t fileStat;
  1537. if (dat->curName >= dat->numNames)
  1538. return -1;
  1539. Q_strncpy( dat->name, dat->namelist[dat->curName]->d_name, sizeof(dat->name) );
  1540. char szFullPath[MAX_PATH];
  1541. Q_snprintf( szFullPath, sizeof(szFullPath), "%s%c%s", dat->dirBase, CORRECT_PATH_SEPARATOR, dat->name );
  1542. if (statBig( szFullPath, &fileStat ) == 0)
  1543. {
  1544. dat->attrib = fileStat.st_mode;
  1545. dat->size = fileStat.st_size;
  1546. dat->time_write = fileStat.st_mtime;
  1547. dat->time_create = fileStat.st_ctime;
  1548. }
  1549. else
  1550. {
  1551. dat->attrib = 0;
  1552. dat->size = 0;
  1553. dat->time_write = 0;
  1554. dat->time_create = 0;
  1555. }
  1556. free( dat->namelist[dat->curName] );
  1557. dat->namelist[dat->curName] = NULL;
  1558. dat->curName++;
  1559. return 1;
  1560. }
  1561. int _findfirst( const char *fileName, _finddata_t *dat )
  1562. {
  1563. char nameStore[PATH_MAX];
  1564. char *dir = NULL;
  1565. int n, iret = -1;
  1566. Q_strncpy( nameStore, fileName, sizeof(nameStore) );
  1567. if (strrchr( nameStore, '/' ))
  1568. {
  1569. dir = nameStore;
  1570. while (strrchr( dir, '/' ))
  1571. {
  1572. statBig_t dirChk;
  1573. // zero this with the dir name
  1574. dir = strrchr( nameStore, '/' );
  1575. *dir = '\0';
  1576. if (dir == nameStore)
  1577. {
  1578. dir = "/";
  1579. }
  1580. else
  1581. {
  1582. dir = nameStore;
  1583. }
  1584. if (statBig( dir, &dirChk ) == 0 && S_ISDIR( dirChk.st_mode ))
  1585. {
  1586. break;
  1587. }
  1588. }
  1589. }
  1590. else
  1591. {
  1592. // couldn't find a dir separator...
  1593. return -1;
  1594. }
  1595. if (strlen( dir ) > 0)
  1596. {
  1597. if (strlen( dir ) == 1)
  1598. Q_strncpy( selectBuf, fileName + 1, sizeof(selectBuf) );
  1599. else
  1600. Q_strncpy( selectBuf, fileName + strlen( dir ) + 1, sizeof(selectBuf) );
  1601. n = scandirBig( dir, &dat->namelist, FileSelect, alphasortBig );
  1602. if (n < 0)
  1603. {
  1604. // silently return, nothing interesting
  1605. }
  1606. else
  1607. {
  1608. dat->curName = 0;
  1609. dat->numNames = n; // n is the number of matches
  1610. Q_strncpy( dat->dirBase, dir, sizeof(dat->dirBase) );
  1611. iret = FillDataStruct( dat );
  1612. if (iret < 0)
  1613. {
  1614. free( dat->namelist );
  1615. dat->namelist = NULL;
  1616. dat->curName = 0;
  1617. dat->numNames = 0;
  1618. }
  1619. }
  1620. }
  1621. // printf("Returning: %i \n",iret);
  1622. return iret;
  1623. }
  1624. int _findnext( int64 handle, _finddata_t *dat )
  1625. {
  1626. if (dat->curName >= dat->numNames)
  1627. {
  1628. free( dat->namelist );
  1629. dat->namelist = NULL;
  1630. dat->curName = 0;
  1631. dat->numNames = 0;
  1632. return -1; // no matches left
  1633. }
  1634. FillDataStruct( dat );
  1635. return 0;
  1636. }
  1637. bool _findclose( int64 handle )
  1638. {
  1639. return true;
  1640. }
  1641. #endif // !_PS3
  1642. #endif