PageRenderTime 34ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/win32/shellext/QueryDirstate.cpp

https://bitbucket.org/tortoisehg/hgtk/
C++ | 372 lines | 343 code | 13 blank | 16 comment | 8 complexity | 2a962b1053de06a420a46a9aa2ed9505 MD5 | raw file
Possible License(s): GPL-2.0
  1. // Copyright (C) 2009 Benjamin Pollack
  2. // Copyright (C) 2009 Adrian Buehlmann
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 2 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. #include "stdafx.h"
  17. #include "QueryDirstate.h"
  18. #include "dirstate.h"
  19. #include "DirectoryStatus.h"
  20. #include "Dirstatecache.h"
  21. #include "Winstat.h"
  22. #include "TortoiseUtils.h"
  23. #include "Thgstatus.h"
  24. #include <shlwapi.h>
  25. class QueryState
  26. {
  27. public:
  28. std::string path;
  29. bool isdir;
  30. std::string basedir;
  31. std::string hgroot;
  32. char status;
  33. unsigned tickcount;
  34. QueryState(): isdir(false), status('0'), tickcount(0) {}
  35. };
  36. bool hasHgDir(char cls, const std::string& path, unsigned& ticks)
  37. {
  38. ticks = 0;
  39. bool res = false;
  40. if (path.empty() || path == "\\")
  41. return res;
  42. const std::string p = path + "\\.hg";
  43. if (::PathIsUNCServerShare(p.c_str()))
  44. return res;
  45. unsigned tc0 = ::GetTickCount();
  46. res = ::PathIsDirectory(p.c_str()) != 0;
  47. unsigned tc1 = ::GetTickCount();
  48. ticks = tc1 - tc0;
  49. if (ticks > 5 /* ms */)
  50. {
  51. // trace slower PathIsDirectory calls (untypical on local discs)
  52. TDEBUG_TRACE(
  53. "[" << cls << "] hasHgDir: PathIsDirectory(\"" << p << "\")" <<
  54. " -> " << res << ", in " << ticks << " ticks"
  55. );
  56. }
  57. return res;
  58. }
  59. int findHgRoot(char cls, QueryState& cur, QueryState& last, bool outdated)
  60. {
  61. std::string dp = "["; dp += cls; dp += "] findHgRoot";
  62. {
  63. std::string p = cur.path;
  64. p.push_back('\\');
  65. if (p.find("\\.hg\\") != std::string::npos)
  66. {
  67. // ignore files and dirs named '.hg'
  68. last = cur;
  69. return 0;
  70. }
  71. }
  72. if (!outdated && !last.hgroot.empty()
  73. && cur.path.size() >= last.hgroot.size()
  74. && StartsWith(cur.path, last.hgroot + "\\"))
  75. {
  76. cur.hgroot = last.hgroot;
  77. return 1;
  78. }
  79. unsigned ticks = 0;
  80. bool file_access_is_unacceptably_slow = false;
  81. if (!PathIsNetworkPath(cur.path.c_str()))
  82. {
  83. // checking if we have a repo root, visible from its parent dir
  84. const bool has_hg = hasHgDir(cls, cur.path, ticks);
  85. if (ticks > 5000 /* ms */)
  86. {
  87. file_access_is_unacceptably_slow = true;
  88. goto exit;
  89. }
  90. if (has_hg)
  91. {
  92. cur.hgroot = cur.path;
  93. TDEBUG_TRACE(dp << "(" << cur.path << "): hgroot = cur.path");
  94. return 1;
  95. }
  96. }
  97. cur.basedir = DirName(cur.path);
  98. if (!outdated && !last.basedir.empty() && cur.basedir == last.basedir)
  99. {
  100. cur.hgroot = last.hgroot;
  101. return 1;
  102. }
  103. for (std::string p = cur.basedir;;)
  104. {
  105. bool has_hg = hasHgDir(cls, p, ticks);
  106. if (ticks > 5000 /* ms */)
  107. {
  108. const std::string reason = "ignoring slow \"" + p + "\"";
  109. Thgstatus::error(reason);
  110. file_access_is_unacceptably_slow = true;
  111. goto exit;
  112. }
  113. if (has_hg)
  114. {
  115. cur.hgroot = p;
  116. TDEBUG_TRACE(
  117. dp << "(" << cur.path << "): hgroot = '" << cur.hgroot
  118. << "' (found repo)"
  119. );
  120. return 1;
  121. }
  122. std::string p2 = DirName(p);
  123. if (p2.size() == p.size())
  124. break;
  125. p.swap(p2);
  126. }
  127. exit:
  128. if (file_access_is_unacceptably_slow)
  129. {
  130. TDEBUG_TRACE(
  131. "******" << dp << "(" << cur.path << "): ignored, "
  132. << "call took too long (" << ticks << " ticks)");
  133. }
  134. else
  135. {
  136. TDEBUG_TRACE(dp << "(" << cur.path << "): NO repo found");
  137. }
  138. last = cur;
  139. return 0;
  140. }
  141. int get_relpath(
  142. const std::string& hgroot,
  143. const std::string& path,
  144. std::string& res
  145. )
  146. {
  147. size_t offset = hgroot.size();
  148. if (offset == 0)
  149. return 0;
  150. if (offset > path.size())
  151. return 0;
  152. if (path[offset] == '\\')
  153. offset++;
  154. const char* relpathptr = path.c_str() + offset;
  155. res = relpathptr;
  156. return 1;
  157. }
  158. int HgQueryDirstate(
  159. const char cls,
  160. const std::string& path,
  161. const char& filterStatus,
  162. char& outStatus
  163. )
  164. {
  165. std::string dp = "["; dp += cls; dp += "] HgQueryDirstate: ";
  166. static QueryState last;
  167. if (path.empty())
  168. return 0;
  169. QueryState cur;
  170. cur.path = path;
  171. cur.tickcount = ::GetTickCount();
  172. const bool outdated = cur.tickcount - last.tickcount > 2000;
  173. if (!outdated && last.path == path)
  174. {
  175. outStatus = last.status;
  176. if (outStatus == 'P')
  177. outStatus = 'M';
  178. return 1;
  179. }
  180. if (::PathIsRoot(path.c_str()))
  181. {
  182. last = cur;
  183. return 0;
  184. }
  185. if (findHgRoot(cls, cur, last, outdated) == 0)
  186. return 0;
  187. size_t offset = cur.hgroot.length();
  188. if (offset == 0)
  189. {
  190. last = cur;
  191. return 0;
  192. }
  193. if (path[offset] == '\\')
  194. offset++;
  195. const char* relpathptr = path.c_str() + offset;
  196. std::string relpath = relpathptr;
  197. for (size_t i = 0; i < relpath.size(); ++i)
  198. {
  199. if (relpath[i] == '\\')
  200. relpath[i] = '/';
  201. }
  202. DirectoryStatus* pdirsta = DirectoryStatus::get(cur.hgroot, cur.basedir);
  203. if (pdirsta && pdirsta->noicons())
  204. {
  205. last = cur;
  206. return 0;
  207. }
  208. if (relpath.empty())
  209. {
  210. outStatus = (pdirsta ? pdirsta->status(relpath) : '?');
  211. cur.status = outStatus;
  212. cur.tickcount = ::GetTickCount();
  213. last = cur;
  214. return 1;
  215. }
  216. bool unset = false;
  217. Dirstate* pds = Dirstatecache::get(cur.hgroot, cur.basedir, unset);
  218. if (!pds)
  219. {
  220. TDEBUG_TRACE(
  221. dp << "Dirstatecache::get(" << cur.hgroot
  222. << ") returns no Dirstate"
  223. );
  224. last = cur;
  225. return 0;
  226. }
  227. Winstat stat;
  228. if (0 != stat.lstat(path.c_str()))
  229. {
  230. TDEBUG_TRACE(dp << "lstat(" << path << ") failed");
  231. last = cur;
  232. return 0;
  233. }
  234. cur.isdir = stat.isdir;
  235. #if 0
  236. TDEBUG_TRACE(
  237. dp << "stat.lstat(\"" << cur.path << "\") "
  238. << "-> stat.isdir is " << stat.isdir
  239. );
  240. #endif
  241. if (cur.isdir)
  242. {
  243. if (!relpath.empty() && !pds->root().getdir(relpath))
  244. {
  245. last = cur;
  246. return 0; // unknown dir -> no icon
  247. }
  248. outStatus = (pdirsta ? pdirsta->status(relpath) : '?');
  249. cur.status = outStatus;
  250. cur.tickcount = ::GetTickCount();
  251. last = cur;
  252. return 1;
  253. }
  254. const Direntry* const e = pds->root().get(relpath);
  255. if (!e)
  256. {
  257. last = cur;
  258. return 0;
  259. }
  260. outStatus = e->status(stat);
  261. if (unset)
  262. goto exit;
  263. bool update = false;
  264. if (outStatus == 'M')
  265. {
  266. std::string relbase;
  267. if (pdirsta && get_relpath(cur.hgroot, cur.basedir, relbase))
  268. {
  269. TDEBUG_TRACE(dp << "relbase = '" << relbase << "'");
  270. char basedir_status = pdirsta->status(relbase);
  271. TDEBUG_TRACE(dp << "basedir_status = " << basedir_status);
  272. if (basedir_status != 'M')
  273. update = true;
  274. }
  275. }
  276. else if (outStatus == 'P')
  277. {
  278. static unsigned lasttickcount;
  279. const unsigned tc = ::GetTickCount();
  280. const bool outdated = tc - lasttickcount > 6000;
  281. if (outdated) // protect against endless update loops
  282. {
  283. update = true;
  284. lasttickcount = tc;
  285. }
  286. TDEBUG_TRACE(dp << "outStatus is 'P'");
  287. }
  288. if (update)
  289. {
  290. TDEBUG_TRACE(dp << "calling Thgstatus::update");
  291. Thgstatus::update(path);
  292. }
  293. exit:
  294. cur.status = outStatus;
  295. if (outStatus == 'P')
  296. outStatus = 'M';
  297. cur.tickcount = ::GetTickCount();
  298. last = cur;
  299. return 1;
  300. }