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