file.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. #ifdef _WIN32
  2. #include <direct.h>
  3. #define getcwd _getcwd
  4. #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
  5. #else
  6. #include <unistd.h>
  7. #endif
  8. #include <iostream>
  9. #include <fstream>
  10. #include <cctype>
  11. #include <algorithm>
  12. #include <sys/stat.h>
  13. #include "file.hpp"
  14. #include "context.hpp"
  15. #include "utf8_string.hpp"
  16. #include "sass2scss.h"
  17. #ifdef _WIN32
  18. #include <windows.h>
  19. #endif
  20. #ifndef FS_CASE_SENSITIVE
  21. #ifdef _WIN32
  22. #define FS_CASE_SENSITIVE 0
  23. #else
  24. #define FS_CASE_SENSITIVE 1
  25. #endif
  26. #endif
  27. namespace Sass {
  28. namespace File {
  29. using namespace std;
  30. string get_cwd()
  31. {
  32. const size_t wd_len = 1024;
  33. char wd[wd_len];
  34. string cwd = getcwd(wd, wd_len);
  35. #ifdef _WIN32
  36. //convert backslashes to forward slashes
  37. replace(cwd.begin(), cwd.end(), '\\', '/');
  38. #endif
  39. if (cwd[cwd.length() - 1] != '/') cwd += '/';
  40. return cwd;
  41. }
  42. // no physical check on filesystem
  43. // only a logical cleanup of a path
  44. string make_canonical_path (string path)
  45. {
  46. // declarations
  47. size_t pos;
  48. #ifdef _WIN32
  49. //convert backslashes to forward slashes
  50. replace(path.begin(), path.end(), '\\', '/');
  51. #endif
  52. pos = 0; // remove all self references inside the path string
  53. while((pos = path.find("/./", pos)) != string::npos) path.erase(pos, 2);
  54. pos = 0; // remove all leading and trailing self references
  55. while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2);
  56. while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2);
  57. pos = 0; // collapse multiple delimiters into a single one
  58. while((pos = path.find("//", pos)) != string::npos) path.erase(pos, 1);
  59. return path;
  60. }
  61. size_t find_last_folder_separator(const string& path, size_t limit = string::npos)
  62. {
  63. size_t pos = string::npos;
  64. size_t pos_p = path.find_last_of('/', limit);
  65. #ifdef _WIN32
  66. size_t pos_w = path.find_last_of('\\', limit);
  67. #else
  68. size_t pos_w = string::npos;
  69. #endif
  70. if (pos_p != string::npos && pos_w != string::npos) {
  71. pos = max(pos_p, pos_w);
  72. }
  73. else if (pos_p != string::npos) {
  74. pos = pos_p;
  75. }
  76. else {
  77. pos = pos_w;
  78. }
  79. return pos;
  80. }
  81. string base_name(string path)
  82. {
  83. size_t pos = find_last_folder_separator(path);
  84. if (pos == string::npos) return path;
  85. else return path.substr(pos+1);
  86. }
  87. string dir_name(string path)
  88. {
  89. size_t pos = find_last_folder_separator(path);
  90. if (pos == string::npos) return "";
  91. else return path.substr(0, pos+1);
  92. }
  93. string join_paths(string l, string r)
  94. {
  95. if (l.empty()) return r;
  96. if (r.empty()) return l;
  97. if (is_absolute_path(r)) return r;
  98. if (l[l.length()-1] != '/') l += '/';
  99. while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) {
  100. r = r.substr(3);
  101. size_t pos = find_last_folder_separator(l, l.length() - 2);
  102. l = l.substr(0, pos == string::npos ? pos : pos + 1);
  103. }
  104. return l + r;
  105. }
  106. bool is_absolute_path(const string& path)
  107. {
  108. if (path[0] == '/') return true;
  109. // TODO: UN-HACKIFY THIS
  110. #ifdef _WIN32
  111. if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true;
  112. #endif
  113. return false;
  114. }
  115. string make_absolute_path(const string& path, const string& cwd)
  116. {
  117. return make_canonical_path((is_absolute_path(path) ? path : join_paths(cwd, path)));
  118. }
  119. string resolve_relative_path(const string& uri, const string& base, const string& cwd)
  120. {
  121. string absolute_uri = make_absolute_path(uri, cwd);
  122. string absolute_base = make_absolute_path(base, cwd);
  123. string stripped_uri = "";
  124. string stripped_base = "";
  125. size_t index = 0;
  126. size_t minSize = min(absolute_uri.size(), absolute_base.size());
  127. for (size_t i = 0; i < minSize; ++i) {
  128. #ifdef FS_CASE_SENSITIVE
  129. if (absolute_uri[i] != absolute_base[i]) break;
  130. #else
  131. // compare the charactes in a case insensitive manner
  132. // windows fs is only case insensitive in ascii ranges
  133. if (tolower(absolute_uri[i]) != tolower(absolute_base[i])) break;
  134. #endif
  135. if (absolute_uri[i] == '/') index = i + 1;
  136. }
  137. for (size_t i = index; i < absolute_uri.size(); ++i) {
  138. stripped_uri += absolute_uri[i];
  139. }
  140. for (size_t i = index; i < absolute_base.size(); ++i) {
  141. stripped_base += absolute_base[i];
  142. }
  143. size_t left = 0;
  144. size_t directories = 0;
  145. for (size_t right = 0; right < stripped_base.size(); ++right) {
  146. if (stripped_base[right] == '/') {
  147. if (stripped_base.substr(left, 2) != "..") {
  148. ++directories;
  149. }
  150. else if (directories > 1) {
  151. --directories;
  152. }
  153. else {
  154. directories = 0;
  155. }
  156. left = right + 1;
  157. }
  158. }
  159. string result = "";
  160. for (size_t i = 0; i < directories; ++i) {
  161. result += "../";
  162. }
  163. result += stripped_uri;
  164. return result;
  165. }
  166. char* resolve_and_load(string path, string& real_path)
  167. {
  168. // Resolution order for ambiguous imports:
  169. // (1) filename as given
  170. // (2) underscore + given
  171. // (3) underscore + given + extension
  172. // (4) given + extension
  173. char* contents = 0;
  174. real_path = path;
  175. // if the file isn't found with the given filename ...
  176. if (!(contents = read_file(real_path))) {
  177. string dir(dir_name(path));
  178. string base(base_name(path));
  179. string _base("_" + base);
  180. real_path = dir + _base;
  181. // if the file isn't found with '_' + filename ...
  182. if (!(contents = read_file(real_path))) {
  183. string _base_scss(_base + ".scss");
  184. real_path = dir + _base_scss;
  185. // if the file isn't found with '_' + filename + ".scss" ...
  186. if (!(contents = read_file(real_path))) {
  187. string _base_sass(_base + ".sass");
  188. real_path = dir + _base_sass;
  189. // if the file isn't found with '_' + filename + ".sass" ...
  190. if (!(contents = read_file(real_path))) {
  191. string base_scss(base + ".scss");
  192. real_path = dir + base_scss;
  193. // if the file isn't found with filename + ".scss" ...
  194. if (!(contents = read_file(real_path))) {
  195. string base_sass(base + ".sass");
  196. real_path = dir + base_sass;
  197. // if the file isn't found with filename + ".sass" ...
  198. if (!(contents = read_file(real_path))) {
  199. // default back to scss version
  200. real_path = dir + base_scss;
  201. }
  202. }
  203. }
  204. }
  205. }
  206. }
  207. #ifdef _WIN32
  208. // convert Windows backslashes to URL forward slashes
  209. replace(real_path.begin(), real_path.end(), '\\', '/');
  210. #endif
  211. return contents;
  212. }
  213. char* read_file(string path)
  214. {
  215. #ifdef _WIN32
  216. BYTE* pBuffer;
  217. DWORD dwBytes;
  218. // windows unicode filepaths are encoded in utf16
  219. wstring wpath = UTF_8::convert_to_utf16(path);
  220. HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  221. if (hFile == INVALID_HANDLE_VALUE) return 0;
  222. DWORD dwFileLength = GetFileSize(hFile, NULL);
  223. if (dwFileLength == INVALID_FILE_SIZE) return 0;
  224. pBuffer = new BYTE[dwFileLength + 1];
  225. ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL);
  226. pBuffer[dwFileLength] = '\0';
  227. CloseHandle(hFile);
  228. // just convert from unsigned char*
  229. char* contents = (char*) pBuffer;
  230. #else
  231. struct stat st;
  232. if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0;
  233. ifstream file(path.c_str(), ios::in | ios::binary | ios::ate);
  234. char* contents = 0;
  235. if (file.is_open()) {
  236. size_t size = file.tellg();
  237. contents = new char[size + 1]; // extra byte for the null char
  238. file.seekg(0, ios::beg);
  239. file.read(contents, size);
  240. contents[size] = '\0';
  241. file.close();
  242. }
  243. #endif
  244. string extension;
  245. if (path.length() > 5) {
  246. extension = path.substr(path.length() - 5, 5);
  247. }
  248. for(size_t i=0; i<extension.size();++i)
  249. extension[i] = tolower(extension[i]);
  250. if (extension == ".sass" && contents != 0) {
  251. char * converted = sass2scss(contents, SASS2SCSS_PRETTIFY_1);
  252. delete[] contents; // free the indented contents
  253. return converted; // should be freed by caller
  254. } else {
  255. return contents;
  256. }
  257. }
  258. }
  259. }