util_path.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. /*
  2. * Copyright 2011-2013 Blender Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "util/util_md5.h"
  17. #include "util/util_path.h"
  18. #include "util/util_string.h"
  19. #include <OpenImageIO/filesystem.h>
  20. #include <OpenImageIO/strutil.h>
  21. #include <OpenImageIO/sysutil.h>
  22. OIIO_NAMESPACE_USING
  23. #include <stdio.h>
  24. #include <sys/stat.h>
  25. #if defined(_WIN32)
  26. # define DIR_SEP '\\'
  27. # define DIR_SEP_ALT '/'
  28. # include <direct.h>
  29. #else
  30. # define DIR_SEP '/'
  31. # include <dirent.h>
  32. # include <pwd.h>
  33. # include <unistd.h>
  34. # include <sys/types.h>
  35. #endif
  36. #ifdef HAVE_SHLWAPI_H
  37. # include <shlwapi.h>
  38. #endif
  39. #include "util/util_map.h"
  40. #include "util/util_windows.h"
  41. CCL_NAMESPACE_BEGIN
  42. #ifdef _WIN32
  43. # if defined(_MSC_VER) || defined(__MINGW64__)
  44. typedef struct _stat64 path_stat_t;
  45. # elif defined(__MINGW32__)
  46. typedef struct _stati64 path_stat_t;
  47. # else
  48. typedef struct _stat path_stat_t;
  49. # endif
  50. # ifndef S_ISDIR
  51. # define S_ISDIR(x) (((x)&_S_IFDIR) == _S_IFDIR)
  52. # endif
  53. #else
  54. typedef struct stat path_stat_t;
  55. #endif
  56. static string cached_path = "";
  57. static string cached_user_path = "";
  58. static string cached_xdg_cache_path = "";
  59. namespace {
  60. #ifdef _WIN32
  61. class directory_iterator {
  62. public:
  63. class path_info {
  64. public:
  65. path_info(const string &path, const WIN32_FIND_DATAW &find_data)
  66. : path_(path), find_data_(find_data)
  67. {
  68. }
  69. string path()
  70. {
  71. return path_join(path_, string_from_wstring(find_data_.cFileName));
  72. }
  73. protected:
  74. const string &path_;
  75. const WIN32_FIND_DATAW &find_data_;
  76. };
  77. directory_iterator() : path_info_("", find_data_), h_find_(INVALID_HANDLE_VALUE)
  78. {
  79. }
  80. explicit directory_iterator(const string &path) : path_(path), path_info_(path, find_data_)
  81. {
  82. string wildcard = path;
  83. if (wildcard[wildcard.size() - 1] != DIR_SEP) {
  84. wildcard += DIR_SEP;
  85. }
  86. wildcard += "*";
  87. h_find_ = FindFirstFileW(string_to_wstring(wildcard).c_str(), &find_data_);
  88. if (h_find_ != INVALID_HANDLE_VALUE) {
  89. skip_dots();
  90. }
  91. }
  92. ~directory_iterator()
  93. {
  94. if (h_find_ != INVALID_HANDLE_VALUE) {
  95. FindClose(h_find_);
  96. }
  97. }
  98. directory_iterator &operator++()
  99. {
  100. step();
  101. return *this;
  102. }
  103. path_info *operator->()
  104. {
  105. return &path_info_;
  106. }
  107. bool operator!=(const directory_iterator &other)
  108. {
  109. return h_find_ != other.h_find_;
  110. }
  111. protected:
  112. bool step()
  113. {
  114. if (do_step()) {
  115. return skip_dots();
  116. }
  117. return false;
  118. }
  119. bool do_step()
  120. {
  121. if (h_find_ != INVALID_HANDLE_VALUE) {
  122. bool result = FindNextFileW(h_find_, &find_data_) == TRUE;
  123. if (!result) {
  124. FindClose(h_find_);
  125. h_find_ = INVALID_HANDLE_VALUE;
  126. }
  127. return result;
  128. }
  129. return false;
  130. }
  131. bool skip_dots()
  132. {
  133. while (wcscmp(find_data_.cFileName, L".") == 0 || wcscmp(find_data_.cFileName, L"..") == 0) {
  134. if (!do_step()) {
  135. return false;
  136. }
  137. }
  138. return true;
  139. }
  140. string path_;
  141. path_info path_info_;
  142. WIN32_FIND_DATAW find_data_;
  143. HANDLE h_find_;
  144. };
  145. #else /* _WIN32 */
  146. class directory_iterator {
  147. public:
  148. class path_info {
  149. public:
  150. explicit path_info(const string &path) : path_(path), entry_(NULL)
  151. {
  152. }
  153. string path()
  154. {
  155. return path_join(path_, entry_->d_name);
  156. }
  157. void current_entry_set(const struct dirent *entry)
  158. {
  159. entry_ = entry;
  160. }
  161. protected:
  162. const string &path_;
  163. const struct dirent *entry_;
  164. };
  165. directory_iterator() : path_info_(""), name_list_(NULL), num_entries_(-1), cur_entry_(-1)
  166. {
  167. }
  168. explicit directory_iterator(const string &path) : path_(path), path_info_(path_), cur_entry_(0)
  169. {
  170. num_entries_ = scandir(path.c_str(), &name_list_, NULL, alphasort);
  171. if (num_entries_ < 0) {
  172. perror("scandir");
  173. }
  174. else {
  175. skip_dots();
  176. }
  177. }
  178. ~directory_iterator()
  179. {
  180. destroy_name_list();
  181. }
  182. directory_iterator &operator++()
  183. {
  184. step();
  185. return *this;
  186. }
  187. path_info *operator->()
  188. {
  189. path_info_.current_entry_set(name_list_[cur_entry_]);
  190. return &path_info_;
  191. }
  192. bool operator!=(const directory_iterator &other)
  193. {
  194. return name_list_ != other.name_list_;
  195. }
  196. protected:
  197. bool step()
  198. {
  199. if (do_step()) {
  200. return skip_dots();
  201. }
  202. return false;
  203. }
  204. bool do_step()
  205. {
  206. ++cur_entry_;
  207. if (cur_entry_ >= num_entries_) {
  208. destroy_name_list();
  209. return false;
  210. }
  211. return true;
  212. }
  213. /* Skip . and .. folders. */
  214. bool skip_dots()
  215. {
  216. while (strcmp(name_list_[cur_entry_]->d_name, ".") == 0 ||
  217. strcmp(name_list_[cur_entry_]->d_name, "..") == 0) {
  218. if (!step()) {
  219. return false;
  220. }
  221. }
  222. return true;
  223. }
  224. void destroy_name_list()
  225. {
  226. if (name_list_ == NULL) {
  227. return;
  228. }
  229. for (int i = 0; i < num_entries_; ++i) {
  230. free(name_list_[i]);
  231. }
  232. free(name_list_);
  233. name_list_ = NULL;
  234. }
  235. string path_;
  236. path_info path_info_;
  237. struct dirent **name_list_;
  238. int num_entries_, cur_entry_;
  239. };
  240. #endif /* _WIN32 */
  241. size_t find_last_slash(const string &path)
  242. {
  243. for (size_t i = 0; i < path.size(); ++i) {
  244. size_t index = path.size() - 1 - i;
  245. #ifdef _WIN32
  246. if (path[index] == DIR_SEP || path[index] == DIR_SEP_ALT)
  247. #else
  248. if (path[index] == DIR_SEP)
  249. #endif
  250. {
  251. return index;
  252. }
  253. }
  254. return string::npos;
  255. }
  256. } /* namespace */
  257. static char *path_specials(const string &sub)
  258. {
  259. static bool env_init = false;
  260. static char *env_shader_path;
  261. static char *env_source_path;
  262. if (!env_init) {
  263. env_shader_path = getenv("CYCLES_SHADER_PATH");
  264. /* NOTE: It is KERNEL in env variable for compatibility reasons. */
  265. env_source_path = getenv("CYCLES_KERNEL_PATH");
  266. env_init = true;
  267. }
  268. if (env_shader_path != NULL && sub == "shader") {
  269. return env_shader_path;
  270. }
  271. else if (env_shader_path != NULL && sub == "source") {
  272. return env_source_path;
  273. }
  274. return NULL;
  275. }
  276. #if defined(__linux__) || defined(__APPLE__)
  277. static string path_xdg_cache_get()
  278. {
  279. const char *home = getenv("XDG_CACHE_HOME");
  280. if (home) {
  281. return string(home);
  282. }
  283. else {
  284. home = getenv("HOME");
  285. if (home == NULL) {
  286. home = getpwuid(getuid())->pw_dir;
  287. }
  288. return path_join(string(home), ".cache");
  289. }
  290. }
  291. #endif
  292. void path_init(const string &path, const string &user_path)
  293. {
  294. cached_path = path;
  295. cached_user_path = user_path;
  296. #ifdef _MSC_VER
  297. // workaround for https://svn.boost.org/trac/boost/ticket/6320
  298. // indirectly init boost codec here since it's not thread safe, and can
  299. // cause crashes when it happens in multithreaded image load
  300. OIIO::Filesystem::exists(path);
  301. #endif
  302. }
  303. string path_get(const string &sub)
  304. {
  305. char *special = path_specials(sub);
  306. if (special != NULL)
  307. return special;
  308. if (cached_path == "")
  309. cached_path = path_dirname(Sysutil::this_program_path());
  310. return path_join(cached_path, sub);
  311. }
  312. string path_user_get(const string &sub)
  313. {
  314. if (cached_user_path == "")
  315. cached_user_path = path_dirname(Sysutil::this_program_path());
  316. return path_join(cached_user_path, sub);
  317. }
  318. string path_cache_get(const string &sub)
  319. {
  320. #if defined(__linux__) || defined(__APPLE__)
  321. if (cached_xdg_cache_path == "") {
  322. cached_xdg_cache_path = path_xdg_cache_get();
  323. }
  324. string result = path_join(cached_xdg_cache_path, "cycles");
  325. return path_join(result, sub);
  326. #else
  327. /* TODO(sergey): What that should be on Windows? */
  328. return path_user_get(path_join("cache", sub));
  329. #endif
  330. }
  331. #if defined(__linux__) || defined(__APPLE__)
  332. string path_xdg_home_get(const string &sub = "");
  333. #endif
  334. string path_filename(const string &path)
  335. {
  336. size_t index = find_last_slash(path);
  337. if (index != string::npos) {
  338. /* Corner cases to match boost behavior. */
  339. #ifndef _WIN32
  340. if (index == 0 && path.size() == 1) {
  341. return path;
  342. }
  343. #endif
  344. if (index == path.size() - 1) {
  345. #ifdef _WIN32
  346. if (index == 2) {
  347. return string(1, DIR_SEP);
  348. }
  349. #endif
  350. return ".";
  351. }
  352. return path.substr(index + 1, path.size() - index - 1);
  353. }
  354. return path;
  355. }
  356. string path_dirname(const string &path)
  357. {
  358. size_t index = find_last_slash(path);
  359. if (index != string::npos) {
  360. #ifndef _WIN32
  361. if (index == 0 && path.size() > 1) {
  362. return string(1, DIR_SEP);
  363. }
  364. #endif
  365. return path.substr(0, index);
  366. }
  367. return "";
  368. }
  369. string path_join(const string &dir, const string &file)
  370. {
  371. if (dir.size() == 0) {
  372. return file;
  373. }
  374. if (file.size() == 0) {
  375. return dir;
  376. }
  377. string result = dir;
  378. #ifndef _WIN32
  379. if (result[result.size() - 1] != DIR_SEP && file[0] != DIR_SEP)
  380. #else
  381. if (result[result.size() - 1] != DIR_SEP && result[result.size() - 1] != DIR_SEP_ALT &&
  382. file[0] != DIR_SEP && file[0] != DIR_SEP_ALT)
  383. #endif
  384. {
  385. result += DIR_SEP;
  386. }
  387. result += file;
  388. return result;
  389. }
  390. string path_escape(const string &path)
  391. {
  392. string result = path;
  393. string_replace(result, " ", "\\ ");
  394. return result;
  395. }
  396. bool path_is_relative(const string &path)
  397. {
  398. #ifdef _WIN32
  399. # ifdef HAVE_SHLWAPI_H
  400. return PathIsRelative(path.c_str());
  401. # else /* HAVE_SHLWAPI_H */
  402. if (path.size() >= 3) {
  403. return !(((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z')) &&
  404. path[1] == ':' && path[2] == DIR_SEP);
  405. }
  406. return true;
  407. # endif /* HAVE_SHLWAPI_H */
  408. #else /* _WIN32 */
  409. if (path.size() == 0) {
  410. return 1;
  411. }
  412. return path[0] != DIR_SEP;
  413. #endif /* _WIN32 */
  414. }
  415. #ifdef _WIN32
  416. /* Add a slash if the UNC path points to a share. */
  417. static string path_unc_add_slash_to_share(const string &path)
  418. {
  419. size_t slash_after_server = path.find(DIR_SEP, 2);
  420. if (slash_after_server != string::npos) {
  421. size_t slash_after_share = path.find(DIR_SEP, slash_after_server + 1);
  422. if (slash_after_share == string::npos) {
  423. return path + DIR_SEP;
  424. }
  425. }
  426. return path;
  427. }
  428. /* Convert:
  429. * \\?\UNC\server\share\folder\... to \\server\share\folder\...
  430. * \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
  431. */
  432. static string path_unc_to_short(const string &path)
  433. {
  434. size_t len = path.size();
  435. if ((len > 3) && (path[0] == DIR_SEP) && (path[1] == DIR_SEP) && (path[2] == '?') &&
  436. ((path[3] == DIR_SEP) || (path[3] == DIR_SEP_ALT))) {
  437. if ((len > 5) && (path[5] == ':')) {
  438. return path.substr(4, len - 4);
  439. }
  440. else if ((len > 7) && (path.substr(4, 3) == "UNC") &&
  441. ((path[7] == DIR_SEP) || (path[7] == DIR_SEP_ALT))) {
  442. return "\\\\" + path.substr(8, len - 8);
  443. }
  444. }
  445. return path;
  446. }
  447. static string path_cleanup_unc(const string &path)
  448. {
  449. string result = path_unc_to_short(path);
  450. if (path.size() > 2) {
  451. /* It's possible path is now a non-UNC. */
  452. if (result[0] == DIR_SEP && result[1] == DIR_SEP) {
  453. return path_unc_add_slash_to_share(result);
  454. }
  455. }
  456. return result;
  457. }
  458. /* Make path compatible for stat() functions. */
  459. static string path_make_compatible(const string &path)
  460. {
  461. string result = path;
  462. /* In Windows stat() doesn't recognize dir ending on a slash. */
  463. if (result.size() > 3 && result[result.size() - 1] == DIR_SEP) {
  464. result.resize(result.size() - 1);
  465. }
  466. /* Clean up UNC path. */
  467. if ((path.size() >= 3) && (path[0] == DIR_SEP) && (path[1] == DIR_SEP)) {
  468. result = path_cleanup_unc(result);
  469. }
  470. /* Make sure volume-only path ends up wit ha directory separator. */
  471. if (result.size() == 2 && result[1] == ':') {
  472. result += DIR_SEP;
  473. }
  474. return result;
  475. }
  476. static int path_wstat(const wstring &path_wc, path_stat_t *st)
  477. {
  478. # if defined(_MSC_VER) || defined(__MINGW64__)
  479. return _wstat64(path_wc.c_str(), st);
  480. # elif defined(__MINGW32__)
  481. return _wstati64(path_wc.c_str(), st);
  482. # else
  483. return _wstat(path_wc.c_str(), st);
  484. # endif
  485. }
  486. static int path_stat(const string &path, path_stat_t *st)
  487. {
  488. wstring path_wc = string_to_wstring(path);
  489. return path_wstat(path_wc, st);
  490. }
  491. #else /* _WIN32 */
  492. static int path_stat(const string &path, path_stat_t *st)
  493. {
  494. return stat(path.c_str(), st);
  495. }
  496. #endif /* _WIN32 */
  497. size_t path_file_size(const string &path)
  498. {
  499. path_stat_t st;
  500. if (path_stat(path, &st) != 0) {
  501. return -1;
  502. }
  503. return st.st_size;
  504. }
  505. bool path_exists(const string &path)
  506. {
  507. #ifdef _WIN32
  508. string fixed_path = path_make_compatible(path);
  509. wstring path_wc = string_to_wstring(fixed_path);
  510. path_stat_t st;
  511. if (path_wstat(path_wc, &st) != 0) {
  512. return false;
  513. }
  514. return st.st_mode != 0;
  515. #else /* _WIN32 */
  516. struct stat st;
  517. if (stat(path.c_str(), &st) != 0) {
  518. return 0;
  519. }
  520. return st.st_mode != 0;
  521. #endif /* _WIN32 */
  522. }
  523. bool path_is_directory(const string &path)
  524. {
  525. path_stat_t st;
  526. if (path_stat(path, &st) != 0) {
  527. return false;
  528. }
  529. return S_ISDIR(st.st_mode);
  530. }
  531. static void path_files_md5_hash_recursive(MD5Hash &hash, const string &dir)
  532. {
  533. if (path_exists(dir)) {
  534. directory_iterator it(dir), it_end;
  535. for (; it != it_end; ++it) {
  536. if (path_is_directory(it->path())) {
  537. path_files_md5_hash_recursive(hash, it->path());
  538. }
  539. else {
  540. string filepath = it->path();
  541. hash.append((const uint8_t *)filepath.c_str(), filepath.size());
  542. hash.append_file(filepath);
  543. }
  544. }
  545. }
  546. }
  547. string path_files_md5_hash(const string &dir)
  548. {
  549. /* computes md5 hash of all files in the directory */
  550. MD5Hash hash;
  551. path_files_md5_hash_recursive(hash, dir);
  552. return hash.get_hex();
  553. }
  554. static bool create_directories_recursivey(const string &path)
  555. {
  556. if (path_is_directory(path)) {
  557. /* Directory already exists, nothing to do. */
  558. return true;
  559. }
  560. if (path_exists(path)) {
  561. /* File exists and it's not a directory. */
  562. return false;
  563. }
  564. string parent = path_dirname(path);
  565. if (parent.size() > 0 && parent != path) {
  566. if (!create_directories_recursivey(parent)) {
  567. return false;
  568. }
  569. }
  570. #ifdef _WIN32
  571. wstring path_wc = string_to_wstring(path);
  572. return _wmkdir(path_wc.c_str()) == 0;
  573. #else
  574. return mkdir(path.c_str(), 0777) == 0;
  575. #endif
  576. }
  577. void path_create_directories(const string &filepath)
  578. {
  579. string path = path_dirname(filepath);
  580. create_directories_recursivey(path);
  581. }
  582. bool path_write_binary(const string &path, const vector<uint8_t> &binary)
  583. {
  584. path_create_directories(path);
  585. /* write binary file from memory */
  586. FILE *f = path_fopen(path, "wb");
  587. if (!f)
  588. return false;
  589. if (binary.size() > 0)
  590. fwrite(&binary[0], sizeof(uint8_t), binary.size(), f);
  591. fclose(f);
  592. return true;
  593. }
  594. bool path_write_text(const string &path, string &text)
  595. {
  596. vector<uint8_t> binary(text.length(), 0);
  597. std::copy(text.begin(), text.end(), binary.begin());
  598. return path_write_binary(path, binary);
  599. }
  600. bool path_read_binary(const string &path, vector<uint8_t> &binary)
  601. {
  602. /* read binary file into memory */
  603. FILE *f = path_fopen(path, "rb");
  604. if (!f) {
  605. binary.resize(0);
  606. return false;
  607. }
  608. binary.resize(path_file_size(path));
  609. if (binary.size() == 0) {
  610. fclose(f);
  611. return false;
  612. }
  613. if (fread(&binary[0], sizeof(uint8_t), binary.size(), f) != binary.size()) {
  614. fclose(f);
  615. return false;
  616. }
  617. fclose(f);
  618. return true;
  619. }
  620. bool path_read_text(const string &path, string &text)
  621. {
  622. vector<uint8_t> binary;
  623. if (!path_exists(path) || !path_read_binary(path, binary))
  624. return false;
  625. const char *str = (const char *)&binary[0];
  626. size_t size = binary.size();
  627. text = string(str, size);
  628. return true;
  629. }
  630. uint64_t path_modified_time(const string &path)
  631. {
  632. path_stat_t st;
  633. if (path_stat(path, &st) != 0) {
  634. return 0;
  635. }
  636. return st.st_mtime;
  637. }
  638. bool path_remove(const string &path)
  639. {
  640. return remove(path.c_str()) == 0;
  641. }
  642. struct SourceReplaceState {
  643. typedef map<string, string> ProcessedMapping;
  644. /* Base director for all relative include headers. */
  645. string base;
  646. /* Result of processed files. */
  647. ProcessedMapping processed_files;
  648. /* Set of files which are considered "precompiled" and which are replaced
  649. * with and empty string on a subsequent occurrence in include statement.
  650. */
  651. set<string> precompiled_headers;
  652. };
  653. static string path_source_replace_includes_recursive(const string &source,
  654. const string &source_filepath,
  655. SourceReplaceState *state);
  656. static string line_directive(const SourceReplaceState &state, const string &path, const int line)
  657. {
  658. string unescaped_path = path;
  659. /* First we make path relative. */
  660. if (string_startswith(unescaped_path, state.base.c_str())) {
  661. const string base_file = path_filename(state.base);
  662. const size_t base_len = state.base.length();
  663. unescaped_path = base_file +
  664. unescaped_path.substr(base_len, unescaped_path.length() - base_len);
  665. }
  666. /* Second, we replace all unsafe characters. */
  667. const size_t length = unescaped_path.length();
  668. string escaped_path = "";
  669. for (size_t i = 0; i < length; ++i) {
  670. const char ch = unescaped_path[i];
  671. if (strchr("\"\'\?\\", ch) != NULL) {
  672. escaped_path += "\\";
  673. }
  674. escaped_path += ch;
  675. }
  676. /* TODO(sergey): Check whether using std::to_string combined with several
  677. * concatenation operations is any faster.
  678. */
  679. return string_printf("#line %d \"%s\"", line, escaped_path.c_str());
  680. }
  681. static string path_source_handle_preprocessor(const string &preprocessor_line,
  682. const string &source_filepath,
  683. const size_t line_number,
  684. SourceReplaceState *state)
  685. {
  686. string result = preprocessor_line;
  687. string token = string_strip(preprocessor_line.substr(1, preprocessor_line.size() - 1));
  688. if (string_startswith(token, "include")) {
  689. token = string_strip(token.substr(7, token.size() - 7));
  690. if (token[0] == '"') {
  691. const size_t n_start = 1;
  692. const size_t n_end = token.find("\"", n_start);
  693. const string filename = token.substr(n_start, n_end - n_start);
  694. const bool is_precompiled = string_endswith(token, "// PRECOMPILED");
  695. string filepath = path_join(state->base, filename);
  696. if (!path_exists(filepath)) {
  697. filepath = path_join(path_dirname(source_filepath), filename);
  698. }
  699. if (is_precompiled) {
  700. state->precompiled_headers.insert(filepath);
  701. }
  702. string text;
  703. if (path_read_text(filepath, text)) {
  704. text = path_source_replace_includes_recursive(text, filepath, state);
  705. /* Use line directives for better error messages. */
  706. result = line_directive(*state, filepath, 1) + "\n" + text + "\n" +
  707. line_directive(*state, source_filepath, line_number + 1);
  708. }
  709. }
  710. }
  711. return result;
  712. }
  713. /* Our own little c preprocessor that replaces #includes with the file
  714. * contents, to work around issue of OpenCL drivers not supporting
  715. * include paths with spaces in them.
  716. */
  717. static string path_source_replace_includes_recursive(const string &source,
  718. const string &source_filepath,
  719. SourceReplaceState *state)
  720. {
  721. /* Try to re-use processed file without spending time on replacing all
  722. * include directives again.
  723. */
  724. SourceReplaceState::ProcessedMapping::iterator replaced_file = state->processed_files.find(
  725. source_filepath);
  726. if (replaced_file != state->processed_files.end()) {
  727. if (state->precompiled_headers.find(source_filepath) != state->precompiled_headers.end()) {
  728. return "";
  729. }
  730. return replaced_file->second;
  731. }
  732. /* Perform full file processing. */
  733. string result = "";
  734. const size_t source_length = source.length();
  735. size_t index = 0;
  736. /* Information about where we are in the source. */
  737. size_t line_number = 0, column_number = 1;
  738. /* Currently gathered non-preprocessor token.
  739. * Store as start/length rather than token itself to avoid overhead of
  740. * memory re-allocations on each character concatenation.
  741. */
  742. size_t token_start = 0, token_length = 0;
  743. /* Denotes whether we're inside of preprocessor line, together with
  744. * preprocessor line itself.
  745. *
  746. * TODO(sergey): Investigate whether using token start/end position
  747. * gives measurable speedup.
  748. */
  749. bool inside_preprocessor = false;
  750. string preprocessor_line = "";
  751. /* Actual loop over the whole source. */
  752. while (index < source_length) {
  753. const char ch = source[index];
  754. if (ch == '\n') {
  755. if (inside_preprocessor) {
  756. result += path_source_handle_preprocessor(
  757. preprocessor_line, source_filepath, line_number, state);
  758. /* Start gathering net part of the token. */
  759. token_start = index;
  760. token_length = 0;
  761. }
  762. inside_preprocessor = false;
  763. preprocessor_line = "";
  764. column_number = 0;
  765. ++line_number;
  766. }
  767. else if (ch == '#' && column_number == 1 && !inside_preprocessor) {
  768. /* Append all possible non-preprocessor token to the result. */
  769. if (token_length != 0) {
  770. result.append(source, token_start, token_length);
  771. token_start = index;
  772. token_length = 0;
  773. }
  774. inside_preprocessor = true;
  775. }
  776. if (inside_preprocessor) {
  777. preprocessor_line += ch;
  778. }
  779. else {
  780. ++token_length;
  781. }
  782. ++index;
  783. ++column_number;
  784. }
  785. /* Append possible tokens which happened before special events handled
  786. * above.
  787. */
  788. if (token_length != 0) {
  789. result.append(source, token_start, token_length);
  790. }
  791. if (inside_preprocessor) {
  792. result += path_source_handle_preprocessor(
  793. preprocessor_line, source_filepath, line_number, state);
  794. }
  795. /* Store result for further reuse. */
  796. state->processed_files[source_filepath] = result;
  797. return result;
  798. }
  799. string path_source_replace_includes(const string &source,
  800. const string &path,
  801. const string &source_filename)
  802. {
  803. SourceReplaceState state;
  804. state.base = path;
  805. return path_source_replace_includes_recursive(source, path_join(path, source_filename), &state);
  806. }
  807. FILE *path_fopen(const string &path, const string &mode)
  808. {
  809. #ifdef _WIN32
  810. wstring path_wc = string_to_wstring(path);
  811. wstring mode_wc = string_to_wstring(mode);
  812. return _wfopen(path_wc.c_str(), mode_wc.c_str());
  813. #else
  814. return fopen(path.c_str(), mode.c_str());
  815. #endif
  816. }
  817. void path_cache_clear_except(const string &name, const set<string> &except)
  818. {
  819. string dir = path_user_get("cache");
  820. if (path_exists(dir)) {
  821. directory_iterator it(dir), it_end;
  822. for (; it != it_end; ++it) {
  823. string filename = path_filename(it->path());
  824. if (string_startswith(filename, name.c_str()))
  825. if (except.find(filename) == except.end())
  826. path_remove(it->path());
  827. }
  828. }
  829. }
  830. CCL_NAMESPACE_END