rugged 1.3.2.3 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (329) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rugged/extconf.rb +1 -1
  3. data/ext/rugged/rugged_config.c +7 -2
  4. data/ext/rugged/rugged_remote.c +17 -0
  5. data/lib/rugged/version.rb +1 -1
  6. data/vendor/libgit2/CMakeLists.txt +103 -276
  7. data/vendor/libgit2/COPYING +36 -19
  8. data/vendor/libgit2/cmake/AddCFlagIfSupported.cmake +21 -21
  9. data/vendor/libgit2/cmake/DefaultCFlags.cmake +154 -0
  10. data/vendor/libgit2/cmake/EnableWarnings.cmake +13 -13
  11. data/vendor/libgit2/cmake/FindCoreFoundation.cmake +13 -13
  12. data/vendor/libgit2/cmake/FindGSSAPI.cmake +171 -287
  13. data/vendor/libgit2/cmake/FindGSSFramework.cmake +13 -13
  14. data/vendor/libgit2/cmake/{FindHTTP_Parser.cmake → FindHTTPParser.cmake} +17 -17
  15. data/vendor/libgit2/cmake/FindIconv.cmake +27 -27
  16. data/vendor/libgit2/cmake/FindLibSSH2.cmake +5 -5
  17. data/vendor/libgit2/cmake/FindPCRE.cmake +13 -13
  18. data/vendor/libgit2/cmake/FindPCRE2.cmake +12 -12
  19. data/vendor/libgit2/cmake/FindPkgLibraries.cmake +19 -19
  20. data/vendor/libgit2/cmake/FindSecurity.cmake +14 -14
  21. data/vendor/libgit2/cmake/FindStatNsec.cmake +12 -18
  22. data/vendor/libgit2/cmake/Findfutimens.cmake +8 -8
  23. data/vendor/libgit2/cmake/FindmbedTLS.cmake +63 -70
  24. data/vendor/libgit2/cmake/IdeSplitSources.cmake +18 -18
  25. data/vendor/libgit2/cmake/PkgBuildConfig.cmake +60 -60
  26. data/vendor/libgit2/cmake/SanitizeBool.cmake +20 -20
  27. data/vendor/libgit2/cmake/SelectGSSAPI.cmake +37 -37
  28. data/vendor/libgit2/cmake/SelectHTTPParser.cmake +19 -0
  29. data/vendor/libgit2/cmake/SelectHTTPSBackend.cmake +100 -100
  30. data/vendor/libgit2/cmake/SelectHashes.cmake +39 -49
  31. data/vendor/libgit2/cmake/SelectRegex.cmake +51 -0
  32. data/vendor/libgit2/cmake/SelectSSH.cmake +41 -0
  33. data/vendor/libgit2/cmake/SelectWinHTTP.cmake +17 -0
  34. data/vendor/libgit2/cmake/SelectZlib.cmake +34 -0
  35. data/vendor/libgit2/deps/chromium-zlib/CMakeLists.txt +6 -6
  36. data/vendor/libgit2/deps/ntlmclient/CMakeLists.txt +31 -31
  37. data/vendor/libgit2/deps/ntlmclient/crypt_openssl.c +1 -1
  38. data/vendor/libgit2/deps/ntlmclient/ntlm.c +4 -4
  39. data/vendor/libgit2/deps/ntlmclient/ntlm.h +4 -4
  40. data/vendor/libgit2/deps/ntlmclient/ntlmclient.h +2 -2
  41. data/vendor/libgit2/deps/pcre/CMakeLists.txt +88 -88
  42. data/vendor/libgit2/deps/winhttp/CMakeLists.txt +14 -16
  43. data/vendor/libgit2/deps/zlib/adler32.c +0 -7
  44. data/vendor/libgit2/deps/zlib/crc32.c +288 -975
  45. data/vendor/libgit2/deps/zlib/crc32.h +436 -9441
  46. data/vendor/libgit2/deps/zlib/deflate.c +31 -83
  47. data/vendor/libgit2/deps/zlib/deflate.h +15 -12
  48. data/vendor/libgit2/deps/zlib/gzguts.h +2 -3
  49. data/vendor/libgit2/deps/zlib/infback.c +1 -2
  50. data/vendor/libgit2/deps/zlib/inffast.c +14 -14
  51. data/vendor/libgit2/deps/zlib/inflate.c +8 -39
  52. data/vendor/libgit2/deps/zlib/inflate.h +2 -3
  53. data/vendor/libgit2/deps/zlib/inftrees.c +3 -3
  54. data/vendor/libgit2/deps/zlib/trees.c +48 -27
  55. data/vendor/libgit2/deps/zlib/zlib.h +100 -126
  56. data/vendor/libgit2/deps/zlib/zutil.c +2 -2
  57. data/vendor/libgit2/deps/zlib/zutil.h +9 -12
  58. data/vendor/libgit2/include/git2/apply.h +16 -2
  59. data/vendor/libgit2/include/git2/attr.h +11 -2
  60. data/vendor/libgit2/include/git2/blame.h +4 -1
  61. data/vendor/libgit2/include/git2/blob.h +14 -1
  62. data/vendor/libgit2/include/git2/branch.h +2 -0
  63. data/vendor/libgit2/include/git2/buffer.h +18 -78
  64. data/vendor/libgit2/include/git2/cert.h +2 -2
  65. data/vendor/libgit2/include/git2/checkout.h +5 -2
  66. data/vendor/libgit2/include/git2/clone.h +3 -3
  67. data/vendor/libgit2/include/git2/commit.h +2 -0
  68. data/vendor/libgit2/include/git2/common.h +5 -12
  69. data/vendor/libgit2/include/git2/config.h +19 -3
  70. data/vendor/libgit2/include/git2/credential.h +2 -1
  71. data/vendor/libgit2/include/git2/credential_helpers.h +1 -0
  72. data/vendor/libgit2/include/git2/deprecated.h +1 -1
  73. data/vendor/libgit2/include/git2/describe.h +7 -2
  74. data/vendor/libgit2/include/git2/diff.h +17 -9
  75. data/vendor/libgit2/include/git2/email.h +1 -1
  76. data/vendor/libgit2/include/git2/errors.h +1 -2
  77. data/vendor/libgit2/include/git2/filter.h +7 -2
  78. data/vendor/libgit2/include/git2/graph.h +1 -0
  79. data/vendor/libgit2/include/git2/ignore.h +1 -1
  80. data/vendor/libgit2/include/git2/index.h +11 -5
  81. data/vendor/libgit2/include/git2/indexer.h +19 -0
  82. data/vendor/libgit2/include/git2/merge.h +23 -3
  83. data/vendor/libgit2/include/git2/message.h +2 -0
  84. data/vendor/libgit2/include/git2/object.h +23 -0
  85. data/vendor/libgit2/include/git2/odb.h +37 -7
  86. data/vendor/libgit2/include/git2/odb_backend.h +1 -1
  87. data/vendor/libgit2/include/git2/pack.h +24 -8
  88. data/vendor/libgit2/include/git2/patch.h +8 -0
  89. data/vendor/libgit2/include/git2/pathspec.h +1 -1
  90. data/vendor/libgit2/include/git2/proxy.h +1 -1
  91. data/vendor/libgit2/include/git2/rebase.h +9 -1
  92. data/vendor/libgit2/include/git2/refdb.h +3 -0
  93. data/vendor/libgit2/include/git2/reflog.h +1 -1
  94. data/vendor/libgit2/include/git2/refs.h +2 -2
  95. data/vendor/libgit2/include/git2/remote.h +184 -37
  96. data/vendor/libgit2/include/git2/repository.h +14 -9
  97. data/vendor/libgit2/include/git2/reset.h +2 -2
  98. data/vendor/libgit2/include/git2/revparse.h +1 -1
  99. data/vendor/libgit2/include/git2/revwalk.h +4 -1
  100. data/vendor/libgit2/include/git2/signature.h +1 -1
  101. data/vendor/libgit2/include/git2/stash.h +3 -3
  102. data/vendor/libgit2/include/git2/status.h +9 -3
  103. data/vendor/libgit2/include/git2/submodule.h +7 -2
  104. data/vendor/libgit2/include/git2/sys/commit_graph.h +1 -1
  105. data/vendor/libgit2/include/git2/sys/odb_backend.h +2 -5
  106. data/vendor/libgit2/include/git2/sys/remote.h +31 -0
  107. data/vendor/libgit2/include/git2/sys/stream.h +1 -1
  108. data/vendor/libgit2/include/git2/sys/transport.h +25 -34
  109. data/vendor/libgit2/include/git2/tag.h +1 -0
  110. data/vendor/libgit2/include/git2/tree.h +4 -3
  111. data/vendor/libgit2/include/git2/types.h +7 -7
  112. data/vendor/libgit2/include/git2/version.h +3 -3
  113. data/vendor/libgit2/include/git2/worktree.h +12 -2
  114. data/vendor/libgit2/src/CMakeLists.txt +189 -315
  115. data/vendor/libgit2/src/annotated_commit.h +1 -1
  116. data/vendor/libgit2/src/apply.c +18 -18
  117. data/vendor/libgit2/src/apply.h +2 -2
  118. data/vendor/libgit2/src/attr.c +18 -18
  119. data/vendor/libgit2/src/attr_file.c +17 -17
  120. data/vendor/libgit2/src/attr_file.h +4 -4
  121. data/vendor/libgit2/src/attrcache.c +17 -12
  122. data/vendor/libgit2/src/blame_git.c +1 -1
  123. data/vendor/libgit2/src/blob.c +33 -26
  124. data/vendor/libgit2/src/blob.h +1 -1
  125. data/vendor/libgit2/src/branch.c +150 -109
  126. data/vendor/libgit2/src/branch.h +15 -3
  127. data/vendor/libgit2/src/buf.c +126 -0
  128. data/vendor/libgit2/src/buf.h +50 -0
  129. data/vendor/libgit2/src/cc-compat.h +1 -1
  130. data/vendor/libgit2/src/checkout.c +74 -68
  131. data/vendor/libgit2/src/cherrypick.c +10 -10
  132. data/vendor/libgit2/src/clone.c +66 -66
  133. data/vendor/libgit2/src/commit.c +128 -58
  134. data/vendor/libgit2/src/commit.h +24 -1
  135. data/vendor/libgit2/src/commit_graph.c +68 -53
  136. data/vendor/libgit2/src/commit_graph.h +10 -3
  137. data/vendor/libgit2/src/commit_list.c +2 -3
  138. data/vendor/libgit2/src/common.h +10 -3
  139. data/vendor/libgit2/src/config.c +99 -77
  140. data/vendor/libgit2/src/config.h +15 -2
  141. data/vendor/libgit2/src/config_file.c +103 -91
  142. data/vendor/libgit2/src/config_mem.c +9 -9
  143. data/vendor/libgit2/src/config_parse.c +27 -23
  144. data/vendor/libgit2/src/crlf.c +24 -21
  145. data/vendor/libgit2/src/date.c +10 -17
  146. data/vendor/libgit2/src/date.h +33 -0
  147. data/vendor/libgit2/src/describe.c +27 -19
  148. data/vendor/libgit2/src/diff.c +25 -8
  149. data/vendor/libgit2/src/diff.h +2 -4
  150. data/vendor/libgit2/src/diff_driver.c +34 -36
  151. data/vendor/libgit2/src/diff_driver.h +3 -3
  152. data/vendor/libgit2/src/diff_file.c +29 -20
  153. data/vendor/libgit2/src/diff_generate.c +30 -6
  154. data/vendor/libgit2/src/diff_generate.h +5 -3
  155. data/vendor/libgit2/src/diff_print.c +102 -95
  156. data/vendor/libgit2/src/diff_stats.c +40 -29
  157. data/vendor/libgit2/src/{message.h → diff_stats.h} +7 -6
  158. data/vendor/libgit2/src/diff_tform.c +9 -8
  159. data/vendor/libgit2/src/diff_xdiff.c +3 -8
  160. data/vendor/libgit2/src/email.c +54 -38
  161. data/vendor/libgit2/src/email.h +1 -1
  162. data/vendor/libgit2/src/errors.c +18 -18
  163. data/vendor/libgit2/src/features.h.in +6 -1
  164. data/vendor/libgit2/src/fetch.c +69 -24
  165. data/vendor/libgit2/src/fetch.h +1 -1
  166. data/vendor/libgit2/src/fetchhead.c +19 -19
  167. data/vendor/libgit2/src/filebuf.c +28 -28
  168. data/vendor/libgit2/src/filebuf.h +1 -1
  169. data/vendor/libgit2/src/filter.c +96 -52
  170. data/vendor/libgit2/src/filter.h +26 -5
  171. data/vendor/libgit2/src/fs_path.c +1912 -0
  172. data/vendor/libgit2/src/fs_path.h +752 -0
  173. data/vendor/libgit2/src/futils.c +91 -85
  174. data/vendor/libgit2/src/futils.h +26 -14
  175. data/vendor/libgit2/src/hash/sha1/collisiondetect.c +2 -2
  176. data/vendor/libgit2/src/hash/sha1/common_crypto.c +2 -2
  177. data/vendor/libgit2/src/hash/sha1/generic.c +2 -2
  178. data/vendor/libgit2/src/hash/sha1/mbedtls.c +2 -2
  179. data/vendor/libgit2/src/hash/sha1/openssl.c +2 -2
  180. data/vendor/libgit2/src/hash/sha1/sha1dc/sha1.c +1 -1
  181. data/vendor/libgit2/src/hash/sha1/win32.c +6 -6
  182. data/vendor/libgit2/src/hash/sha1.h +3 -1
  183. data/vendor/libgit2/src/hash.c +67 -35
  184. data/vendor/libgit2/src/hash.h +12 -12
  185. data/vendor/libgit2/src/ident.c +18 -18
  186. data/vendor/libgit2/src/ignore.c +35 -34
  187. data/vendor/libgit2/src/ignore.h +2 -2
  188. data/vendor/libgit2/src/index.c +79 -80
  189. data/vendor/libgit2/src/index.h +6 -3
  190. data/vendor/libgit2/src/indexer.c +75 -57
  191. data/vendor/libgit2/src/iterator.c +64 -56
  192. data/vendor/libgit2/src/iterator.h +5 -5
  193. data/vendor/libgit2/src/khash.h +1 -1
  194. data/vendor/libgit2/src/libgit2.c +22 -19
  195. data/vendor/libgit2/src/mailmap.c +38 -36
  196. data/vendor/libgit2/src/merge.c +27 -27
  197. data/vendor/libgit2/src/merge.h +1 -14
  198. data/vendor/libgit2/src/merge_driver.c +2 -2
  199. data/vendor/libgit2/src/merge_file.c +13 -3
  200. data/vendor/libgit2/src/message.c +21 -10
  201. data/vendor/libgit2/src/midx.c +83 -66
  202. data/vendor/libgit2/src/midx.h +3 -3
  203. data/vendor/libgit2/src/mwindow.c +1 -1
  204. data/vendor/libgit2/src/net.c +278 -68
  205. data/vendor/libgit2/src/net.h +10 -3
  206. data/vendor/libgit2/src/netops.c +1 -1
  207. data/vendor/libgit2/src/netops.h +1 -1
  208. data/vendor/libgit2/src/notes.c +20 -29
  209. data/vendor/libgit2/src/object.c +49 -9
  210. data/vendor/libgit2/src/object.h +1 -1
  211. data/vendor/libgit2/src/odb.c +35 -32
  212. data/vendor/libgit2/src/odb.h +1 -1
  213. data/vendor/libgit2/src/odb_loose.c +68 -68
  214. data/vendor/libgit2/src/odb_mempack.c +18 -5
  215. data/vendor/libgit2/src/odb_pack.c +43 -43
  216. data/vendor/libgit2/src/oid.c +11 -4
  217. data/vendor/libgit2/src/oid.h +15 -0
  218. data/vendor/libgit2/src/pack-objects.c +41 -26
  219. data/vendor/libgit2/src/pack-objects.h +11 -6
  220. data/vendor/libgit2/src/pack.c +10 -10
  221. data/vendor/libgit2/src/patch.c +3 -3
  222. data/vendor/libgit2/src/patch.h +1 -0
  223. data/vendor/libgit2/src/patch_generate.c +27 -11
  224. data/vendor/libgit2/src/patch_generate.h +5 -5
  225. data/vendor/libgit2/src/patch_parse.c +24 -24
  226. data/vendor/libgit2/src/path.c +76 -1951
  227. data/vendor/libgit2/src/path.h +34 -741
  228. data/vendor/libgit2/src/pathspec.c +6 -6
  229. data/vendor/libgit2/src/pathspec.h +2 -2
  230. data/vendor/libgit2/src/posix.c +3 -3
  231. data/vendor/libgit2/src/posix.h +1 -0
  232. data/vendor/libgit2/src/pqueue.h +1 -1
  233. data/vendor/libgit2/src/proxy.c +4 -1
  234. data/vendor/libgit2/src/proxy.h +1 -1
  235. data/vendor/libgit2/src/push.c +30 -35
  236. data/vendor/libgit2/src/push.h +4 -16
  237. data/vendor/libgit2/src/rand.c +226 -0
  238. data/vendor/libgit2/src/rand.h +37 -0
  239. data/vendor/libgit2/src/reader.c +8 -8
  240. data/vendor/libgit2/src/reader.h +2 -2
  241. data/vendor/libgit2/src/rebase.c +89 -88
  242. data/vendor/libgit2/src/refdb_fs.c +447 -173
  243. data/vendor/libgit2/src/refs.c +32 -32
  244. data/vendor/libgit2/src/refs.h +2 -2
  245. data/vendor/libgit2/src/refspec.c +32 -37
  246. data/vendor/libgit2/src/refspec.h +5 -2
  247. data/vendor/libgit2/src/regexp.c +1 -1
  248. data/vendor/libgit2/src/remote.c +713 -419
  249. data/vendor/libgit2/src/remote.h +15 -10
  250. data/vendor/libgit2/src/repository.c +350 -467
  251. data/vendor/libgit2/src/repository.h +11 -10
  252. data/vendor/libgit2/src/reset.c +8 -5
  253. data/vendor/libgit2/src/revert.c +10 -10
  254. data/vendor/libgit2/src/revparse.c +48 -35
  255. data/vendor/libgit2/src/revwalk.c +7 -7
  256. data/vendor/libgit2/src/signature.c +12 -6
  257. data/vendor/libgit2/src/signature.h +1 -1
  258. data/vendor/libgit2/src/sortedcache.c +1 -1
  259. data/vendor/libgit2/src/sortedcache.h +1 -1
  260. data/vendor/libgit2/src/stash.c +36 -37
  261. data/vendor/libgit2/src/status.c +4 -1
  262. data/vendor/libgit2/src/{buffer.c → str.c} +157 -151
  263. data/vendor/libgit2/src/str.h +357 -0
  264. data/vendor/libgit2/src/streams/mbedtls.c +8 -6
  265. data/vendor/libgit2/src/streams/openssl_dynamic.h +3 -3
  266. data/vendor/libgit2/src/submodule.c +171 -159
  267. data/vendor/libgit2/src/submodule.h +1 -1
  268. data/vendor/libgit2/src/sysdir.c +68 -52
  269. data/vendor/libgit2/src/sysdir.h +15 -10
  270. data/vendor/libgit2/src/tag.c +29 -27
  271. data/vendor/libgit2/src/thread.h +3 -3
  272. data/vendor/libgit2/src/threadstate.c +3 -3
  273. data/vendor/libgit2/src/threadstate.h +1 -1
  274. data/vendor/libgit2/src/trace.c +1 -14
  275. data/vendor/libgit2/src/trace.h +5 -22
  276. data/vendor/libgit2/src/trailer.c +1 -1
  277. data/vendor/libgit2/src/transaction.c +1 -1
  278. data/vendor/libgit2/src/transport.c +10 -10
  279. data/vendor/libgit2/src/transports/auth.c +7 -9
  280. data/vendor/libgit2/src/transports/auth.h +2 -3
  281. data/vendor/libgit2/src/transports/auth_negotiate.c +12 -13
  282. data/vendor/libgit2/src/transports/auth_ntlm.c +10 -10
  283. data/vendor/libgit2/src/transports/auth_ntlm.h +0 -1
  284. data/vendor/libgit2/src/transports/git.c +9 -11
  285. data/vendor/libgit2/src/transports/http.c +37 -17
  286. data/vendor/libgit2/src/transports/http.h +2 -3
  287. data/vendor/libgit2/src/transports/httpclient.c +65 -65
  288. data/vendor/libgit2/src/transports/local.c +124 -116
  289. data/vendor/libgit2/src/transports/smart.c +51 -139
  290. data/vendor/libgit2/src/transports/smart.h +25 -31
  291. data/vendor/libgit2/src/transports/smart_pkt.c +33 -33
  292. data/vendor/libgit2/src/transports/smart_protocol.c +57 -39
  293. data/vendor/libgit2/src/transports/ssh.c +47 -112
  294. data/vendor/libgit2/src/transports/winhttp.c +50 -56
  295. data/vendor/libgit2/src/tree-cache.c +5 -5
  296. data/vendor/libgit2/src/tree-cache.h +2 -2
  297. data/vendor/libgit2/src/tree.c +59 -48
  298. data/vendor/libgit2/src/tree.h +1 -1
  299. data/vendor/libgit2/src/unix/map.c +0 -2
  300. data/vendor/libgit2/src/unix/posix.h +1 -4
  301. data/vendor/libgit2/src/unix/realpath.c +0 -2
  302. data/vendor/libgit2/src/util.c +14 -14
  303. data/vendor/libgit2/src/util.h +2 -28
  304. data/vendor/libgit2/src/vector.h +1 -1
  305. data/vendor/libgit2/src/win32/findfile.c +172 -116
  306. data/vendor/libgit2/src/win32/findfile.h +7 -4
  307. data/vendor/libgit2/src/win32/path_w32.c +140 -9
  308. data/vendor/libgit2/src/win32/path_w32.h +2 -0
  309. data/vendor/libgit2/src/win32/posix.h +0 -1
  310. data/vendor/libgit2/src/win32/posix_w32.c +11 -27
  311. data/vendor/libgit2/src/win32/w32_buffer.c +2 -3
  312. data/vendor/libgit2/src/win32/w32_buffer.h +2 -3
  313. data/vendor/libgit2/src/win32/w32_leakcheck.c +1 -1
  314. data/vendor/libgit2/src/worktree.c +116 -94
  315. data/vendor/libgit2/src/worktree.h +1 -1
  316. data/vendor/libgit2/src/xdiff/git-xdiff.h +53 -0
  317. data/vendor/libgit2/src/xdiff/xdiff.h +15 -15
  318. data/vendor/libgit2/src/xdiff/xdiffi.c +134 -108
  319. data/vendor/libgit2/src/xdiff/xemit.c +23 -7
  320. data/vendor/libgit2/src/xdiff/xhistogram.c +87 -78
  321. data/vendor/libgit2/src/xdiff/xinclude.h +1 -12
  322. data/vendor/libgit2/src/xdiff/xmerge.c +104 -117
  323. data/vendor/libgit2/src/xdiff/xpatience.c +6 -17
  324. data/vendor/libgit2/src/xdiff/xprepare.c +15 -20
  325. data/vendor/libgit2/src/xdiff/xutils.c +18 -7
  326. data/vendor/libgit2/src/zstream.c +5 -5
  327. data/vendor/libgit2/src/zstream.h +4 -4
  328. metadata +25 -10
  329. data/vendor/libgit2/src/buffer.h +0 -374
@@ -0,0 +1,1912 @@
1
+ /*
2
+ * Copyright (C) the libgit2 contributors. All rights reserved.
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+
8
+ #include "fs_path.h"
9
+
10
+ #include "posix.h"
11
+ #include "repository.h"
12
+ #ifdef GIT_WIN32
13
+ #include "win32/posix.h"
14
+ #include "win32/w32_buffer.h"
15
+ #include "win32/w32_util.h"
16
+ #include "win32/version.h"
17
+ #include <aclapi.h>
18
+ #else
19
+ #include <dirent.h>
20
+ #endif
21
+ #include <stdio.h>
22
+ #include <ctype.h>
23
+
24
+ static int dos_drive_prefix_length(const char *path)
25
+ {
26
+ int i;
27
+
28
+ /*
29
+ * Does it start with an ASCII letter (i.e. highest bit not set),
30
+ * followed by a colon?
31
+ */
32
+ if (!(0x80 & (unsigned char)*path))
33
+ return *path && path[1] == ':' ? 2 : 0;
34
+
35
+ /*
36
+ * While drive letters must be letters of the English alphabet, it is
37
+ * possible to assign virtually _any_ Unicode character via `subst` as
38
+ * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
39
+ * like this:
40
+ *
41
+ * subst ֍: %USERPROFILE%\Desktop
42
+ */
43
+ for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
44
+ ; /* skip first UTF-8 character */
45
+ return path[i] == ':' ? i + 1 : 0;
46
+ }
47
+
48
+ #ifdef GIT_WIN32
49
+ static bool looks_like_network_computer_name(const char *path, int pos)
50
+ {
51
+ if (pos < 3)
52
+ return false;
53
+
54
+ if (path[0] != '/' || path[1] != '/')
55
+ return false;
56
+
57
+ while (pos-- > 2) {
58
+ if (path[pos] == '/')
59
+ return false;
60
+ }
61
+
62
+ return true;
63
+ }
64
+ #endif
65
+
66
+ /*
67
+ * Based on the Android implementation, BSD licensed.
68
+ * http://android.git.kernel.org/
69
+ *
70
+ * Copyright (C) 2008 The Android Open Source Project
71
+ * All rights reserved.
72
+ *
73
+ * Redistribution and use in source and binary forms, with or without
74
+ * modification, are permitted provided that the following conditions
75
+ * are met:
76
+ * * Redistributions of source code must retain the above copyright
77
+ * notice, this list of conditions and the following disclaimer.
78
+ * * Redistributions in binary form must reproduce the above copyright
79
+ * notice, this list of conditions and the following disclaimer in
80
+ * the documentation and/or other materials provided with the
81
+ * distribution.
82
+ *
83
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
84
+ * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
85
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
86
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
87
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
88
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
89
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
90
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
91
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
92
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
93
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
94
+ * SUCH DAMAGE.
95
+ */
96
+ int git_fs_path_basename_r(git_str *buffer, const char *path)
97
+ {
98
+ const char *endp, *startp;
99
+ int len, result;
100
+
101
+ /* Empty or NULL string gets treated as "." */
102
+ if (path == NULL || *path == '\0') {
103
+ startp = ".";
104
+ len = 1;
105
+ goto Exit;
106
+ }
107
+
108
+ /* Strip trailing slashes */
109
+ endp = path + strlen(path) - 1;
110
+ while (endp > path && *endp == '/')
111
+ endp--;
112
+
113
+ /* All slashes becomes "/" */
114
+ if (endp == path && *endp == '/') {
115
+ startp = "/";
116
+ len = 1;
117
+ goto Exit;
118
+ }
119
+
120
+ /* Find the start of the base */
121
+ startp = endp;
122
+ while (startp > path && *(startp - 1) != '/')
123
+ startp--;
124
+
125
+ /* Cast is safe because max path < max int */
126
+ len = (int)(endp - startp + 1);
127
+
128
+ Exit:
129
+ result = len;
130
+
131
+ if (buffer != NULL && git_str_set(buffer, startp, len) < 0)
132
+ return -1;
133
+
134
+ return result;
135
+ }
136
+
137
+ /*
138
+ * Determine if the path is a Windows prefix and, if so, returns
139
+ * its actual length. If it is not a prefix, returns -1.
140
+ */
141
+ static int win32_prefix_length(const char *path, int len)
142
+ {
143
+ #ifndef GIT_WIN32
144
+ GIT_UNUSED(path);
145
+ GIT_UNUSED(len);
146
+ #else
147
+ /*
148
+ * Mimic unix behavior where '/.git' returns '/': 'C:/.git'
149
+ * will return 'C:/' here
150
+ */
151
+ if (dos_drive_prefix_length(path) == len)
152
+ return len;
153
+
154
+ /*
155
+ * Similarly checks if we're dealing with a network computer name
156
+ * '//computername/.git' will return '//computername/'
157
+ */
158
+ if (looks_like_network_computer_name(path, len))
159
+ return len;
160
+ #endif
161
+
162
+ return -1;
163
+ }
164
+
165
+ /*
166
+ * Based on the Android implementation, BSD licensed.
167
+ * Check http://android.git.kernel.org/
168
+ */
169
+ int git_fs_path_dirname_r(git_str *buffer, const char *path)
170
+ {
171
+ const char *endp;
172
+ int is_prefix = 0, len;
173
+
174
+ /* Empty or NULL string gets treated as "." */
175
+ if (path == NULL || *path == '\0') {
176
+ path = ".";
177
+ len = 1;
178
+ goto Exit;
179
+ }
180
+
181
+ /* Strip trailing slashes */
182
+ endp = path + strlen(path) - 1;
183
+ while (endp > path && *endp == '/')
184
+ endp--;
185
+
186
+ if (endp - path + 1 > INT_MAX) {
187
+ git_error_set(GIT_ERROR_INVALID, "path too long");
188
+ len = -1;
189
+ goto Exit;
190
+ }
191
+
192
+ if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
193
+ is_prefix = 1;
194
+ goto Exit;
195
+ }
196
+
197
+ /* Find the start of the dir */
198
+ while (endp > path && *endp != '/')
199
+ endp--;
200
+
201
+ /* Either the dir is "/" or there are no slashes */
202
+ if (endp == path) {
203
+ path = (*endp == '/') ? "/" : ".";
204
+ len = 1;
205
+ goto Exit;
206
+ }
207
+
208
+ do {
209
+ endp--;
210
+ } while (endp > path && *endp == '/');
211
+
212
+ if (endp - path + 1 > INT_MAX) {
213
+ git_error_set(GIT_ERROR_INVALID, "path too long");
214
+ len = -1;
215
+ goto Exit;
216
+ }
217
+
218
+ if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
219
+ is_prefix = 1;
220
+ goto Exit;
221
+ }
222
+
223
+ /* Cast is safe because max path < max int */
224
+ len = (int)(endp - path + 1);
225
+
226
+ Exit:
227
+ if (buffer) {
228
+ if (git_str_set(buffer, path, len) < 0)
229
+ return -1;
230
+ if (is_prefix && git_str_putc(buffer, '/') < 0)
231
+ return -1;
232
+ }
233
+
234
+ return len;
235
+ }
236
+
237
+
238
+ char *git_fs_path_dirname(const char *path)
239
+ {
240
+ git_str buf = GIT_STR_INIT;
241
+ char *dirname;
242
+
243
+ git_fs_path_dirname_r(&buf, path);
244
+ dirname = git_str_detach(&buf);
245
+ git_str_dispose(&buf); /* avoid memleak if error occurs */
246
+
247
+ return dirname;
248
+ }
249
+
250
+ char *git_fs_path_basename(const char *path)
251
+ {
252
+ git_str buf = GIT_STR_INIT;
253
+ char *basename;
254
+
255
+ git_fs_path_basename_r(&buf, path);
256
+ basename = git_str_detach(&buf);
257
+ git_str_dispose(&buf); /* avoid memleak if error occurs */
258
+
259
+ return basename;
260
+ }
261
+
262
+ size_t git_fs_path_basename_offset(git_str *buffer)
263
+ {
264
+ ssize_t slash;
265
+
266
+ if (!buffer || buffer->size <= 0)
267
+ return 0;
268
+
269
+ slash = git_str_rfind_next(buffer, '/');
270
+
271
+ if (slash >= 0 && buffer->ptr[slash] == '/')
272
+ return (size_t)(slash + 1);
273
+
274
+ return 0;
275
+ }
276
+
277
+ int git_fs_path_root(const char *path)
278
+ {
279
+ int offset = 0, prefix_len;
280
+
281
+ /* Does the root of the path look like a windows drive ? */
282
+ if ((prefix_len = dos_drive_prefix_length(path)))
283
+ offset += prefix_len;
284
+
285
+ #ifdef GIT_WIN32
286
+ /* Are we dealing with a windows network path? */
287
+ else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') ||
288
+ (path[0] == '\\' && path[1] == '\\' && path[2] != '\\'))
289
+ {
290
+ offset += 2;
291
+
292
+ /* Skip the computer name segment */
293
+ while (path[offset] && path[offset] != '/' && path[offset] != '\\')
294
+ offset++;
295
+ }
296
+
297
+ if (path[offset] == '\\')
298
+ return offset;
299
+ #endif
300
+
301
+ if (path[offset] == '/')
302
+ return offset;
303
+
304
+ return -1; /* Not a real error - signals that path is not rooted */
305
+ }
306
+
307
+ static void path_trim_slashes(git_str *path)
308
+ {
309
+ int ceiling = git_fs_path_root(path->ptr) + 1;
310
+
311
+ if (ceiling < 0)
312
+ return;
313
+
314
+ while (path->size > (size_t)ceiling) {
315
+ if (path->ptr[path->size-1] != '/')
316
+ break;
317
+
318
+ path->ptr[path->size-1] = '\0';
319
+ path->size--;
320
+ }
321
+ }
322
+
323
+ int git_fs_path_join_unrooted(
324
+ git_str *path_out, const char *path, const char *base, ssize_t *root_at)
325
+ {
326
+ ssize_t root;
327
+
328
+ GIT_ASSERT_ARG(path_out);
329
+ GIT_ASSERT_ARG(path);
330
+
331
+ root = (ssize_t)git_fs_path_root(path);
332
+
333
+ if (base != NULL && root < 0) {
334
+ if (git_str_joinpath(path_out, base, path) < 0)
335
+ return -1;
336
+
337
+ root = (ssize_t)strlen(base);
338
+ } else {
339
+ if (git_str_sets(path_out, path) < 0)
340
+ return -1;
341
+
342
+ if (root < 0)
343
+ root = 0;
344
+ else if (base)
345
+ git_fs_path_equal_or_prefixed(base, path, &root);
346
+ }
347
+
348
+ if (root_at)
349
+ *root_at = root;
350
+
351
+ return 0;
352
+ }
353
+
354
+ void git_fs_path_squash_slashes(git_str *path)
355
+ {
356
+ char *p, *q;
357
+
358
+ if (path->size == 0)
359
+ return;
360
+
361
+ for (p = path->ptr, q = path->ptr; *q; p++, q++) {
362
+ *p = *q;
363
+
364
+ while (*q == '/' && *(q+1) == '/') {
365
+ path->size--;
366
+ q++;
367
+ }
368
+ }
369
+
370
+ *p = '\0';
371
+ }
372
+
373
+ int git_fs_path_prettify(git_str *path_out, const char *path, const char *base)
374
+ {
375
+ char buf[GIT_PATH_MAX];
376
+
377
+ GIT_ASSERT_ARG(path_out);
378
+ GIT_ASSERT_ARG(path);
379
+
380
+ /* construct path if needed */
381
+ if (base != NULL && git_fs_path_root(path) < 0) {
382
+ if (git_str_joinpath(path_out, base, path) < 0)
383
+ return -1;
384
+ path = path_out->ptr;
385
+ }
386
+
387
+ if (p_realpath(path, buf) == NULL) {
388
+ /* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */
389
+ int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;
390
+ git_error_set(GIT_ERROR_OS, "failed to resolve path '%s'", path);
391
+
392
+ git_str_clear(path_out);
393
+
394
+ return error;
395
+ }
396
+
397
+ return git_str_sets(path_out, buf);
398
+ }
399
+
400
+ int git_fs_path_prettify_dir(git_str *path_out, const char *path, const char *base)
401
+ {
402
+ int error = git_fs_path_prettify(path_out, path, base);
403
+ return (error < 0) ? error : git_fs_path_to_dir(path_out);
404
+ }
405
+
406
+ int git_fs_path_to_dir(git_str *path)
407
+ {
408
+ if (path->asize > 0 &&
409
+ git_str_len(path) > 0 &&
410
+ path->ptr[git_str_len(path) - 1] != '/')
411
+ git_str_putc(path, '/');
412
+
413
+ return git_str_oom(path) ? -1 : 0;
414
+ }
415
+
416
+ void git_fs_path_string_to_dir(char *path, size_t size)
417
+ {
418
+ size_t end = strlen(path);
419
+
420
+ if (end && path[end - 1] != '/' && end < size) {
421
+ path[end] = '/';
422
+ path[end + 1] = '\0';
423
+ }
424
+ }
425
+
426
+ int git__percent_decode(git_str *decoded_out, const char *input)
427
+ {
428
+ int len, hi, lo, i;
429
+
430
+ GIT_ASSERT_ARG(decoded_out);
431
+ GIT_ASSERT_ARG(input);
432
+
433
+ len = (int)strlen(input);
434
+ git_str_clear(decoded_out);
435
+
436
+ for(i = 0; i < len; i++)
437
+ {
438
+ char c = input[i];
439
+
440
+ if (c != '%')
441
+ goto append;
442
+
443
+ if (i >= len - 2)
444
+ goto append;
445
+
446
+ hi = git__fromhex(input[i + 1]);
447
+ lo = git__fromhex(input[i + 2]);
448
+
449
+ if (hi < 0 || lo < 0)
450
+ goto append;
451
+
452
+ c = (char)(hi << 4 | lo);
453
+ i += 2;
454
+
455
+ append:
456
+ if (git_str_putc(decoded_out, c) < 0)
457
+ return -1;
458
+ }
459
+
460
+ return 0;
461
+ }
462
+
463
+ static int error_invalid_local_file_uri(const char *uri)
464
+ {
465
+ git_error_set(GIT_ERROR_CONFIG, "'%s' is not a valid local file URI", uri);
466
+ return -1;
467
+ }
468
+
469
+ static int local_file_url_prefixlen(const char *file_url)
470
+ {
471
+ int len = -1;
472
+
473
+ if (git__prefixcmp(file_url, "file://") == 0) {
474
+ if (file_url[7] == '/')
475
+ len = 8;
476
+ else if (git__prefixcmp(file_url + 7, "localhost/") == 0)
477
+ len = 17;
478
+ }
479
+
480
+ return len;
481
+ }
482
+
483
+ bool git_fs_path_is_local_file_url(const char *file_url)
484
+ {
485
+ return (local_file_url_prefixlen(file_url) > 0);
486
+ }
487
+
488
+ int git_fs_path_fromurl(git_str *local_path_out, const char *file_url)
489
+ {
490
+ int offset;
491
+
492
+ GIT_ASSERT_ARG(local_path_out);
493
+ GIT_ASSERT_ARG(file_url);
494
+
495
+ if ((offset = local_file_url_prefixlen(file_url)) < 0 ||
496
+ file_url[offset] == '\0' || file_url[offset] == '/')
497
+ return error_invalid_local_file_uri(file_url);
498
+
499
+ #ifndef GIT_WIN32
500
+ offset--; /* A *nix absolute path starts with a forward slash */
501
+ #endif
502
+
503
+ git_str_clear(local_path_out);
504
+ return git__percent_decode(local_path_out, file_url + offset);
505
+ }
506
+
507
+ int git_fs_path_walk_up(
508
+ git_str *path,
509
+ const char *ceiling,
510
+ int (*cb)(void *data, const char *),
511
+ void *data)
512
+ {
513
+ int error = 0;
514
+ git_str iter;
515
+ ssize_t stop = 0, scan;
516
+ char oldc = '\0';
517
+
518
+ GIT_ASSERT_ARG(path);
519
+ GIT_ASSERT_ARG(cb);
520
+
521
+ if (ceiling != NULL) {
522
+ if (git__prefixcmp(path->ptr, ceiling) == 0)
523
+ stop = (ssize_t)strlen(ceiling);
524
+ else
525
+ stop = git_str_len(path);
526
+ }
527
+ scan = git_str_len(path);
528
+
529
+ /* empty path: yield only once */
530
+ if (!scan) {
531
+ error = cb(data, "");
532
+ if (error)
533
+ git_error_set_after_callback(error);
534
+ return error;
535
+ }
536
+
537
+ iter.ptr = path->ptr;
538
+ iter.size = git_str_len(path);
539
+ iter.asize = path->asize;
540
+
541
+ while (scan >= stop) {
542
+ error = cb(data, iter.ptr);
543
+ iter.ptr[scan] = oldc;
544
+
545
+ if (error) {
546
+ git_error_set_after_callback(error);
547
+ break;
548
+ }
549
+
550
+ scan = git_str_rfind_next(&iter, '/');
551
+ if (scan >= 0) {
552
+ scan++;
553
+ oldc = iter.ptr[scan];
554
+ iter.size = scan;
555
+ iter.ptr[scan] = '\0';
556
+ }
557
+ }
558
+
559
+ if (scan >= 0)
560
+ iter.ptr[scan] = oldc;
561
+
562
+ /* relative path: yield for the last component */
563
+ if (!error && stop == 0 && iter.ptr[0] != '/') {
564
+ error = cb(data, "");
565
+ if (error)
566
+ git_error_set_after_callback(error);
567
+ }
568
+
569
+ return error;
570
+ }
571
+
572
+ bool git_fs_path_exists(const char *path)
573
+ {
574
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
575
+ return p_access(path, F_OK) == 0;
576
+ }
577
+
578
+ bool git_fs_path_isdir(const char *path)
579
+ {
580
+ struct stat st;
581
+ if (p_stat(path, &st) < 0)
582
+ return false;
583
+
584
+ return S_ISDIR(st.st_mode) != 0;
585
+ }
586
+
587
+ bool git_fs_path_isfile(const char *path)
588
+ {
589
+ struct stat st;
590
+
591
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
592
+ if (p_stat(path, &st) < 0)
593
+ return false;
594
+
595
+ return S_ISREG(st.st_mode) != 0;
596
+ }
597
+
598
+ bool git_fs_path_islink(const char *path)
599
+ {
600
+ struct stat st;
601
+
602
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
603
+ if (p_lstat(path, &st) < 0)
604
+ return false;
605
+
606
+ return S_ISLNK(st.st_mode) != 0;
607
+ }
608
+
609
+ #ifdef GIT_WIN32
610
+
611
+ bool git_fs_path_is_empty_dir(const char *path)
612
+ {
613
+ git_win32_path filter_w;
614
+ bool empty = false;
615
+
616
+ if (git_win32__findfirstfile_filter(filter_w, path)) {
617
+ WIN32_FIND_DATAW findData;
618
+ HANDLE hFind = FindFirstFileW(filter_w, &findData);
619
+
620
+ /* FindFirstFile will fail if there are no children to the given
621
+ * path, which can happen if the given path is a file (and obviously
622
+ * has no children) or if the given path is an empty mount point.
623
+ * (Most directories have at least directory entries '.' and '..',
624
+ * but ridiculously another volume mounted in another drive letter's
625
+ * path space do not, and thus have nothing to enumerate.) If
626
+ * FindFirstFile fails, check if this is a directory-like thing
627
+ * (a mount point).
628
+ */
629
+ if (hFind == INVALID_HANDLE_VALUE)
630
+ return git_fs_path_isdir(path);
631
+
632
+ /* If the find handle was created successfully, then it's a directory */
633
+ empty = true;
634
+
635
+ do {
636
+ /* Allow the enumeration to return . and .. and still be considered
637
+ * empty. In the special case of drive roots (i.e. C:\) where . and
638
+ * .. do not occur, we can still consider the path to be an empty
639
+ * directory if there's nothing there. */
640
+ if (!git_fs_path_is_dot_or_dotdotW(findData.cFileName)) {
641
+ empty = false;
642
+ break;
643
+ }
644
+ } while (FindNextFileW(hFind, &findData));
645
+
646
+ FindClose(hFind);
647
+ }
648
+
649
+ return empty;
650
+ }
651
+
652
+ #else
653
+
654
+ static int path_found_entry(void *payload, git_str *path)
655
+ {
656
+ GIT_UNUSED(payload);
657
+ return !git_fs_path_is_dot_or_dotdot(path->ptr);
658
+ }
659
+
660
+ bool git_fs_path_is_empty_dir(const char *path)
661
+ {
662
+ int error;
663
+ git_str dir = GIT_STR_INIT;
664
+
665
+ if (!git_fs_path_isdir(path))
666
+ return false;
667
+
668
+ if ((error = git_str_sets(&dir, path)) != 0)
669
+ git_error_clear();
670
+ else
671
+ error = git_fs_path_direach(&dir, 0, path_found_entry, NULL);
672
+
673
+ git_str_dispose(&dir);
674
+
675
+ return !error;
676
+ }
677
+
678
+ #endif
679
+
680
+ int git_fs_path_set_error(int errno_value, const char *path, const char *action)
681
+ {
682
+ switch (errno_value) {
683
+ case ENOENT:
684
+ case ENOTDIR:
685
+ git_error_set(GIT_ERROR_OS, "could not find '%s' to %s", path, action);
686
+ return GIT_ENOTFOUND;
687
+
688
+ case EINVAL:
689
+ case ENAMETOOLONG:
690
+ git_error_set(GIT_ERROR_OS, "invalid path for filesystem '%s'", path);
691
+ return GIT_EINVALIDSPEC;
692
+
693
+ case EEXIST:
694
+ git_error_set(GIT_ERROR_OS, "failed %s - '%s' already exists", action, path);
695
+ return GIT_EEXISTS;
696
+
697
+ case EACCES:
698
+ git_error_set(GIT_ERROR_OS, "failed %s - '%s' is locked", action, path);
699
+ return GIT_ELOCKED;
700
+
701
+ default:
702
+ git_error_set(GIT_ERROR_OS, "could not %s '%s'", action, path);
703
+ return -1;
704
+ }
705
+ }
706
+
707
+ int git_fs_path_lstat(const char *path, struct stat *st)
708
+ {
709
+ if (p_lstat(path, st) == 0)
710
+ return 0;
711
+
712
+ return git_fs_path_set_error(errno, path, "stat");
713
+ }
714
+
715
+ static bool _check_dir_contents(
716
+ git_str *dir,
717
+ const char *sub,
718
+ bool (*predicate)(const char *))
719
+ {
720
+ bool result;
721
+ size_t dir_size = git_str_len(dir);
722
+ size_t sub_size = strlen(sub);
723
+ size_t alloc_size;
724
+
725
+ /* leave base valid even if we could not make space for subdir */
726
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) ||
727
+ GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) ||
728
+ git_str_try_grow(dir, alloc_size, false) < 0)
729
+ return false;
730
+
731
+ /* save excursion */
732
+ if (git_str_joinpath(dir, dir->ptr, sub) < 0)
733
+ return false;
734
+
735
+ result = predicate(dir->ptr);
736
+
737
+ /* restore path */
738
+ git_str_truncate(dir, dir_size);
739
+ return result;
740
+ }
741
+
742
+ bool git_fs_path_contains(git_str *dir, const char *item)
743
+ {
744
+ return _check_dir_contents(dir, item, &git_fs_path_exists);
745
+ }
746
+
747
+ bool git_fs_path_contains_dir(git_str *base, const char *subdir)
748
+ {
749
+ return _check_dir_contents(base, subdir, &git_fs_path_isdir);
750
+ }
751
+
752
+ bool git_fs_path_contains_file(git_str *base, const char *file)
753
+ {
754
+ return _check_dir_contents(base, file, &git_fs_path_isfile);
755
+ }
756
+
757
+ int git_fs_path_find_dir(git_str *dir)
758
+ {
759
+ int error = 0;
760
+ char buf[GIT_PATH_MAX];
761
+
762
+ if (p_realpath(dir->ptr, buf) != NULL)
763
+ error = git_str_sets(dir, buf);
764
+
765
+ /* call dirname if this is not a directory */
766
+ if (!error) /* && git_fs_path_isdir(dir->ptr) == false) */
767
+ error = (git_fs_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0;
768
+
769
+ if (!error)
770
+ error = git_fs_path_to_dir(dir);
771
+
772
+ return error;
773
+ }
774
+
775
+ int git_fs_path_resolve_relative(git_str *path, size_t ceiling)
776
+ {
777
+ char *base, *to, *from, *next;
778
+ size_t len;
779
+
780
+ GIT_ERROR_CHECK_ALLOC_STR(path);
781
+
782
+ if (ceiling > path->size)
783
+ ceiling = path->size;
784
+
785
+ /* recognize drive prefixes, etc. that should not be backed over */
786
+ if (ceiling == 0)
787
+ ceiling = git_fs_path_root(path->ptr) + 1;
788
+
789
+ /* recognize URL prefixes that should not be backed over */
790
+ if (ceiling == 0) {
791
+ for (next = path->ptr; *next && git__isalpha(*next); ++next);
792
+ if (next[0] == ':' && next[1] == '/' && next[2] == '/')
793
+ ceiling = (next + 3) - path->ptr;
794
+ }
795
+
796
+ base = to = from = path->ptr + ceiling;
797
+
798
+ while (*from) {
799
+ for (next = from; *next && *next != '/'; ++next);
800
+
801
+ len = next - from;
802
+
803
+ if (len == 1 && from[0] == '.')
804
+ /* do nothing with singleton dot */;
805
+
806
+ else if (len == 2 && from[0] == '.' && from[1] == '.') {
807
+ /* error out if trying to up one from a hard base */
808
+ if (to == base && ceiling != 0) {
809
+ git_error_set(GIT_ERROR_INVALID,
810
+ "cannot strip root component off url");
811
+ return -1;
812
+ }
813
+
814
+ /* no more path segments to strip,
815
+ * use '../' as a new base path */
816
+ if (to == base) {
817
+ if (*next == '/')
818
+ len++;
819
+
820
+ if (to != from)
821
+ memmove(to, from, len);
822
+
823
+ to += len;
824
+ /* this is now the base, can't back up from a
825
+ * relative prefix */
826
+ base = to;
827
+ } else {
828
+ /* back up a path segment */
829
+ while (to > base && to[-1] == '/') to--;
830
+ while (to > base && to[-1] != '/') to--;
831
+ }
832
+ } else {
833
+ if (*next == '/' && *from != '/')
834
+ len++;
835
+
836
+ if (to != from)
837
+ memmove(to, from, len);
838
+
839
+ to += len;
840
+ }
841
+
842
+ from += len;
843
+
844
+ while (*from == '/') from++;
845
+ }
846
+
847
+ *to = '\0';
848
+
849
+ path->size = to - path->ptr;
850
+
851
+ return 0;
852
+ }
853
+
854
+ int git_fs_path_apply_relative(git_str *target, const char *relpath)
855
+ {
856
+ return git_str_joinpath(target, git_str_cstr(target), relpath) ||
857
+ git_fs_path_resolve_relative(target, 0);
858
+ }
859
+
860
+ int git_fs_path_cmp(
861
+ const char *name1, size_t len1, int isdir1,
862
+ const char *name2, size_t len2, int isdir2,
863
+ int (*compare)(const char *, const char *, size_t))
864
+ {
865
+ unsigned char c1, c2;
866
+ size_t len = len1 < len2 ? len1 : len2;
867
+ int cmp;
868
+
869
+ cmp = compare(name1, name2, len);
870
+ if (cmp)
871
+ return cmp;
872
+
873
+ c1 = name1[len];
874
+ c2 = name2[len];
875
+
876
+ if (c1 == '\0' && isdir1)
877
+ c1 = '/';
878
+
879
+ if (c2 == '\0' && isdir2)
880
+ c2 = '/';
881
+
882
+ return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
883
+ }
884
+
885
+ size_t git_fs_path_common_dirlen(const char *one, const char *two)
886
+ {
887
+ const char *p, *q, *dirsep = NULL;
888
+
889
+ for (p = one, q = two; *p && *q; p++, q++) {
890
+ if (*p == '/' && *q == '/')
891
+ dirsep = p;
892
+ else if (*p != *q)
893
+ break;
894
+ }
895
+
896
+ return dirsep ? (dirsep - one) + 1 : 0;
897
+ }
898
+
899
+ int git_fs_path_make_relative(git_str *path, const char *parent)
900
+ {
901
+ const char *p, *q, *p_dirsep, *q_dirsep;
902
+ size_t plen = path->size, newlen, alloclen, depth = 1, i, offset;
903
+
904
+ for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
905
+ if (*p == '/' && *q == '/') {
906
+ p_dirsep = p;
907
+ q_dirsep = q;
908
+ }
909
+ else if (*p != *q)
910
+ break;
911
+ }
912
+
913
+ /* need at least 1 common path segment */
914
+ if ((p_dirsep == path->ptr || q_dirsep == parent) &&
915
+ (*p_dirsep != '/' || *q_dirsep != '/')) {
916
+ git_error_set(GIT_ERROR_INVALID,
917
+ "%s is not a parent of %s", parent, path->ptr);
918
+ return GIT_ENOTFOUND;
919
+ }
920
+
921
+ if (*p == '/' && !*q)
922
+ p++;
923
+ else if (!*p && *q == '/')
924
+ q++;
925
+ else if (!*p && !*q)
926
+ return git_str_clear(path), 0;
927
+ else {
928
+ p = p_dirsep + 1;
929
+ q = q_dirsep + 1;
930
+ }
931
+
932
+ plen -= (p - path->ptr);
933
+
934
+ if (!*q)
935
+ return git_str_set(path, p, plen);
936
+
937
+ for (; (q = strchr(q, '/')) && *(q + 1); q++)
938
+ depth++;
939
+
940
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3);
941
+ GIT_ERROR_CHECK_ALLOC_ADD(&newlen, newlen, plen);
942
+
943
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, newlen, 1);
944
+
945
+ /* save the offset as we might realllocate the pointer */
946
+ offset = p - path->ptr;
947
+ if (git_str_try_grow(path, alloclen, 1) < 0)
948
+ return -1;
949
+ p = path->ptr + offset;
950
+
951
+ memmove(path->ptr + (depth * 3), p, plen + 1);
952
+
953
+ for (i = 0; i < depth; i++)
954
+ memcpy(path->ptr + (i * 3), "../", 3);
955
+
956
+ path->size = newlen;
957
+ return 0;
958
+ }
959
+
960
+ bool git_fs_path_has_non_ascii(const char *path, size_t pathlen)
961
+ {
962
+ const uint8_t *scan = (const uint8_t *)path, *end;
963
+
964
+ for (end = scan + pathlen; scan < end; ++scan)
965
+ if (*scan & 0x80)
966
+ return true;
967
+
968
+ return false;
969
+ }
970
+
971
+ #ifdef GIT_USE_ICONV
972
+
973
+ int git_fs_path_iconv_init_precompose(git_fs_path_iconv_t *ic)
974
+ {
975
+ git_str_init(&ic->buf, 0);
976
+ ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
977
+ return 0;
978
+ }
979
+
980
+ void git_fs_path_iconv_clear(git_fs_path_iconv_t *ic)
981
+ {
982
+ if (ic) {
983
+ if (ic->map != (iconv_t)-1)
984
+ iconv_close(ic->map);
985
+ git_str_dispose(&ic->buf);
986
+ }
987
+ }
988
+
989
+ int git_fs_path_iconv(git_fs_path_iconv_t *ic, const char **in, size_t *inlen)
990
+ {
991
+ char *nfd = (char*)*in, *nfc;
992
+ size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv;
993
+ int retry = 1;
994
+
995
+ if (!ic || ic->map == (iconv_t)-1 ||
996
+ !git_fs_path_has_non_ascii(*in, *inlen))
997
+ return 0;
998
+
999
+ git_str_clear(&ic->buf);
1000
+
1001
+ while (1) {
1002
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1);
1003
+ if (git_str_grow(&ic->buf, alloclen) < 0)
1004
+ return -1;
1005
+
1006
+ nfc = ic->buf.ptr + ic->buf.size;
1007
+ nfclen = ic->buf.asize - ic->buf.size;
1008
+
1009
+ rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
1010
+
1011
+ ic->buf.size = (nfc - ic->buf.ptr);
1012
+
1013
+ if (rv != (size_t)-1)
1014
+ break;
1015
+
1016
+ /* if we cannot convert the data (probably because iconv thinks
1017
+ * it is not valid UTF-8 source data), then use original data
1018
+ */
1019
+ if (errno != E2BIG)
1020
+ return 0;
1021
+
1022
+ /* make space for 2x the remaining data to be converted
1023
+ * (with per retry overhead to avoid infinite loops)
1024
+ */
1025
+ wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
1026
+
1027
+ if (retry++ > 4)
1028
+ goto fail;
1029
+ }
1030
+
1031
+ ic->buf.ptr[ic->buf.size] = '\0';
1032
+
1033
+ *in = ic->buf.ptr;
1034
+ *inlen = ic->buf.size;
1035
+
1036
+ return 0;
1037
+
1038
+ fail:
1039
+ git_error_set(GIT_ERROR_OS, "unable to convert unicode path data");
1040
+ return -1;
1041
+ }
1042
+
1043
+ static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
1044
+ static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
1045
+
1046
+ /* Check if the platform is decomposing unicode data for us. We will
1047
+ * emulate core Git and prefer to use precomposed unicode data internally
1048
+ * on these platforms, composing the decomposed unicode on the fly.
1049
+ *
1050
+ * This mainly happens on the Mac where HDFS stores filenames as
1051
+ * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
1052
+ * return decomposed unicode from readdir() even when the actual
1053
+ * filesystem is storing precomposed unicode.
1054
+ */
1055
+ bool git_fs_path_does_decompose_unicode(const char *root)
1056
+ {
1057
+ git_str nfc_path = GIT_STR_INIT;
1058
+ git_str nfd_path = GIT_STR_INIT;
1059
+ int fd;
1060
+ bool found_decomposed = false;
1061
+ size_t orig_len;
1062
+ const char *trailer;
1063
+
1064
+ /* Create a file using a precomposed path and then try to find it
1065
+ * using the decomposed name. If the lookup fails, then we will mark
1066
+ * that we should precompose unicode for this repository.
1067
+ */
1068
+ if (git_str_joinpath(&nfc_path, root, nfc_file) < 0)
1069
+ goto done;
1070
+
1071
+ /* record original path length before trailer */
1072
+ orig_len = nfc_path.size;
1073
+
1074
+ if ((fd = git_futils_mktmp(&nfc_path, nfc_path.ptr, 0666)) < 0)
1075
+ goto done;
1076
+ p_close(fd);
1077
+
1078
+ trailer = nfc_path.ptr + orig_len;
1079
+
1080
+ /* try to look up as NFD path */
1081
+ if (git_str_joinpath(&nfd_path, root, nfd_file) < 0 ||
1082
+ git_str_puts(&nfd_path, trailer) < 0)
1083
+ goto done;
1084
+
1085
+ found_decomposed = git_fs_path_exists(nfd_path.ptr);
1086
+
1087
+ /* remove temporary file (using original precomposed path) */
1088
+ (void)p_unlink(nfc_path.ptr);
1089
+
1090
+ done:
1091
+ git_str_dispose(&nfc_path);
1092
+ git_str_dispose(&nfd_path);
1093
+ return found_decomposed;
1094
+ }
1095
+
1096
+ #else
1097
+
1098
+ bool git_fs_path_does_decompose_unicode(const char *root)
1099
+ {
1100
+ GIT_UNUSED(root);
1101
+ return false;
1102
+ }
1103
+
1104
+ #endif
1105
+
1106
+ #if defined(__sun) || defined(__GNU__)
1107
+ typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
1108
+ #else
1109
+ typedef struct dirent path_dirent_data;
1110
+ #endif
1111
+
1112
+ int git_fs_path_direach(
1113
+ git_str *path,
1114
+ uint32_t flags,
1115
+ int (*fn)(void *, git_str *),
1116
+ void *arg)
1117
+ {
1118
+ int error = 0;
1119
+ ssize_t wd_len;
1120
+ DIR *dir;
1121
+ struct dirent *de;
1122
+
1123
+ #ifdef GIT_USE_ICONV
1124
+ git_fs_path_iconv_t ic = GIT_PATH_ICONV_INIT;
1125
+ #endif
1126
+
1127
+ GIT_UNUSED(flags);
1128
+
1129
+ if (git_fs_path_to_dir(path) < 0)
1130
+ return -1;
1131
+
1132
+ wd_len = git_str_len(path);
1133
+
1134
+ if ((dir = opendir(path->ptr)) == NULL) {
1135
+ git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path->ptr);
1136
+ if (errno == ENOENT)
1137
+ return GIT_ENOTFOUND;
1138
+
1139
+ return -1;
1140
+ }
1141
+
1142
+ #ifdef GIT_USE_ICONV
1143
+ if ((flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
1144
+ (void)git_fs_path_iconv_init_precompose(&ic);
1145
+ #endif
1146
+
1147
+ while ((de = readdir(dir)) != NULL) {
1148
+ const char *de_path = de->d_name;
1149
+ size_t de_len = strlen(de_path);
1150
+
1151
+ if (git_fs_path_is_dot_or_dotdot(de_path))
1152
+ continue;
1153
+
1154
+ #ifdef GIT_USE_ICONV
1155
+ if ((error = git_fs_path_iconv(&ic, &de_path, &de_len)) < 0)
1156
+ break;
1157
+ #endif
1158
+
1159
+ if ((error = git_str_put(path, de_path, de_len)) < 0)
1160
+ break;
1161
+
1162
+ git_error_clear();
1163
+ error = fn(arg, path);
1164
+
1165
+ git_str_truncate(path, wd_len); /* restore path */
1166
+
1167
+ /* Only set our own error if the callback did not set one already */
1168
+ if (error != 0) {
1169
+ if (!git_error_last())
1170
+ git_error_set_after_callback(error);
1171
+
1172
+ break;
1173
+ }
1174
+ }
1175
+
1176
+ closedir(dir);
1177
+
1178
+ #ifdef GIT_USE_ICONV
1179
+ git_fs_path_iconv_clear(&ic);
1180
+ #endif
1181
+
1182
+ return error;
1183
+ }
1184
+
1185
+ #if defined(GIT_WIN32) && !defined(__MINGW32__)
1186
+
1187
+ /* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
1188
+ * and better.
1189
+ */
1190
+ #ifndef FIND_FIRST_EX_LARGE_FETCH
1191
+ # define FIND_FIRST_EX_LARGE_FETCH 2
1192
+ #endif
1193
+
1194
+ int git_fs_path_diriter_init(
1195
+ git_fs_path_diriter *diriter,
1196
+ const char *path,
1197
+ unsigned int flags)
1198
+ {
1199
+ git_win32_path path_filter;
1200
+
1201
+ static int is_win7_or_later = -1;
1202
+ if (is_win7_or_later < 0)
1203
+ is_win7_or_later = git_has_win32_version(6, 1, 0);
1204
+
1205
+ GIT_ASSERT_ARG(diriter);
1206
+ GIT_ASSERT_ARG(path);
1207
+
1208
+ memset(diriter, 0, sizeof(git_fs_path_diriter));
1209
+ diriter->handle = INVALID_HANDLE_VALUE;
1210
+
1211
+ if (git_str_puts(&diriter->path_utf8, path) < 0)
1212
+ return -1;
1213
+
1214
+ path_trim_slashes(&diriter->path_utf8);
1215
+
1216
+ if (diriter->path_utf8.size == 0) {
1217
+ git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path);
1218
+ return -1;
1219
+ }
1220
+
1221
+ if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 ||
1222
+ !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) {
1223
+ git_error_set(GIT_ERROR_OS, "could not parse the directory path '%s'", path);
1224
+ return -1;
1225
+ }
1226
+
1227
+ diriter->handle = FindFirstFileExW(
1228
+ path_filter,
1229
+ is_win7_or_later ? FindExInfoBasic : FindExInfoStandard,
1230
+ &diriter->current,
1231
+ FindExSearchNameMatch,
1232
+ NULL,
1233
+ is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0);
1234
+
1235
+ if (diriter->handle == INVALID_HANDLE_VALUE) {
1236
+ git_error_set(GIT_ERROR_OS, "could not open directory '%s'", path);
1237
+ return -1;
1238
+ }
1239
+
1240
+ diriter->parent_utf8_len = diriter->path_utf8.size;
1241
+ diriter->flags = flags;
1242
+ return 0;
1243
+ }
1244
+
1245
+ static int diriter_update_paths(git_fs_path_diriter *diriter)
1246
+ {
1247
+ size_t filename_len, path_len;
1248
+
1249
+ filename_len = wcslen(diriter->current.cFileName);
1250
+
1251
+ if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) ||
1252
+ GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2))
1253
+ return -1;
1254
+
1255
+ if (path_len > GIT_WIN_PATH_UTF16) {
1256
+ git_error_set(GIT_ERROR_FILESYSTEM,
1257
+ "invalid path '%.*ls\\%ls' (path too long)",
1258
+ diriter->parent_len, diriter->path, diriter->current.cFileName);
1259
+ return -1;
1260
+ }
1261
+
1262
+ diriter->path[diriter->parent_len] = L'\\';
1263
+ memcpy(&diriter->path[diriter->parent_len+1],
1264
+ diriter->current.cFileName, filename_len * sizeof(wchar_t));
1265
+ diriter->path[path_len-1] = L'\0';
1266
+
1267
+ git_str_truncate(&diriter->path_utf8, diriter->parent_utf8_len);
1268
+
1269
+ if (diriter->parent_utf8_len > 0 &&
1270
+ diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/')
1271
+ git_str_putc(&diriter->path_utf8, '/');
1272
+
1273
+ git_str_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len);
1274
+
1275
+ if (git_str_oom(&diriter->path_utf8))
1276
+ return -1;
1277
+
1278
+ return 0;
1279
+ }
1280
+
1281
+ int git_fs_path_diriter_next(git_fs_path_diriter *diriter)
1282
+ {
1283
+ bool skip_dot = !(diriter->flags & GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
1284
+
1285
+ do {
1286
+ /* Our first time through, we already have the data from
1287
+ * FindFirstFileW. Use it, otherwise get the next file.
1288
+ */
1289
+ if (!diriter->needs_next)
1290
+ diriter->needs_next = 1;
1291
+ else if (!FindNextFileW(diriter->handle, &diriter->current))
1292
+ return GIT_ITEROVER;
1293
+ } while (skip_dot && git_fs_path_is_dot_or_dotdotW(diriter->current.cFileName));
1294
+
1295
+ if (diriter_update_paths(diriter) < 0)
1296
+ return -1;
1297
+
1298
+ return 0;
1299
+ }
1300
+
1301
+ int git_fs_path_diriter_filename(
1302
+ const char **out,
1303
+ size_t *out_len,
1304
+ git_fs_path_diriter *diriter)
1305
+ {
1306
+ GIT_ASSERT_ARG(out);
1307
+ GIT_ASSERT_ARG(out_len);
1308
+ GIT_ASSERT_ARG(diriter);
1309
+ GIT_ASSERT(diriter->path_utf8.size > diriter->parent_utf8_len);
1310
+
1311
+ *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1];
1312
+ *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1;
1313
+ return 0;
1314
+ }
1315
+
1316
+ int git_fs_path_diriter_fullpath(
1317
+ const char **out,
1318
+ size_t *out_len,
1319
+ git_fs_path_diriter *diriter)
1320
+ {
1321
+ GIT_ASSERT_ARG(out);
1322
+ GIT_ASSERT_ARG(out_len);
1323
+ GIT_ASSERT_ARG(diriter);
1324
+
1325
+ *out = diriter->path_utf8.ptr;
1326
+ *out_len = diriter->path_utf8.size;
1327
+ return 0;
1328
+ }
1329
+
1330
+ int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter)
1331
+ {
1332
+ GIT_ASSERT_ARG(out);
1333
+ GIT_ASSERT_ARG(diriter);
1334
+
1335
+ return git_win32__file_attribute_to_stat(out,
1336
+ (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current,
1337
+ diriter->path);
1338
+ }
1339
+
1340
+ void git_fs_path_diriter_free(git_fs_path_diriter *diriter)
1341
+ {
1342
+ if (diriter == NULL)
1343
+ return;
1344
+
1345
+ git_str_dispose(&diriter->path_utf8);
1346
+
1347
+ if (diriter->handle != INVALID_HANDLE_VALUE) {
1348
+ FindClose(diriter->handle);
1349
+ diriter->handle = INVALID_HANDLE_VALUE;
1350
+ }
1351
+ }
1352
+
1353
+ #else
1354
+
1355
+ int git_fs_path_diriter_init(
1356
+ git_fs_path_diriter *diriter,
1357
+ const char *path,
1358
+ unsigned int flags)
1359
+ {
1360
+ GIT_ASSERT_ARG(diriter);
1361
+ GIT_ASSERT_ARG(path);
1362
+
1363
+ memset(diriter, 0, sizeof(git_fs_path_diriter));
1364
+
1365
+ if (git_str_puts(&diriter->path, path) < 0)
1366
+ return -1;
1367
+
1368
+ path_trim_slashes(&diriter->path);
1369
+
1370
+ if (diriter->path.size == 0) {
1371
+ git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path);
1372
+ return -1;
1373
+ }
1374
+
1375
+ if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
1376
+ git_str_dispose(&diriter->path);
1377
+
1378
+ git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path);
1379
+ return -1;
1380
+ }
1381
+
1382
+ #ifdef GIT_USE_ICONV
1383
+ if ((flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
1384
+ (void)git_fs_path_iconv_init_precompose(&diriter->ic);
1385
+ #endif
1386
+
1387
+ diriter->parent_len = diriter->path.size;
1388
+ diriter->flags = flags;
1389
+
1390
+ return 0;
1391
+ }
1392
+
1393
+ int git_fs_path_diriter_next(git_fs_path_diriter *diriter)
1394
+ {
1395
+ struct dirent *de;
1396
+ const char *filename;
1397
+ size_t filename_len;
1398
+ bool skip_dot = !(diriter->flags & GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
1399
+ int error = 0;
1400
+
1401
+ GIT_ASSERT_ARG(diriter);
1402
+
1403
+ errno = 0;
1404
+
1405
+ do {
1406
+ if ((de = readdir(diriter->dir)) == NULL) {
1407
+ if (!errno)
1408
+ return GIT_ITEROVER;
1409
+
1410
+ git_error_set(GIT_ERROR_OS,
1411
+ "could not read directory '%s'", diriter->path.ptr);
1412
+ return -1;
1413
+ }
1414
+ } while (skip_dot && git_fs_path_is_dot_or_dotdot(de->d_name));
1415
+
1416
+ filename = de->d_name;
1417
+ filename_len = strlen(filename);
1418
+
1419
+ #ifdef GIT_USE_ICONV
1420
+ if ((diriter->flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0 &&
1421
+ (error = git_fs_path_iconv(&diriter->ic, &filename, &filename_len)) < 0)
1422
+ return error;
1423
+ #endif
1424
+
1425
+ git_str_truncate(&diriter->path, diriter->parent_len);
1426
+
1427
+ if (diriter->parent_len > 0 &&
1428
+ diriter->path.ptr[diriter->parent_len-1] != '/')
1429
+ git_str_putc(&diriter->path, '/');
1430
+
1431
+ git_str_put(&diriter->path, filename, filename_len);
1432
+
1433
+ if (git_str_oom(&diriter->path))
1434
+ return -1;
1435
+
1436
+ return error;
1437
+ }
1438
+
1439
+ int git_fs_path_diriter_filename(
1440
+ const char **out,
1441
+ size_t *out_len,
1442
+ git_fs_path_diriter *diriter)
1443
+ {
1444
+ GIT_ASSERT_ARG(out);
1445
+ GIT_ASSERT_ARG(out_len);
1446
+ GIT_ASSERT_ARG(diriter);
1447
+ GIT_ASSERT(diriter->path.size > diriter->parent_len);
1448
+
1449
+ *out = &diriter->path.ptr[diriter->parent_len+1];
1450
+ *out_len = diriter->path.size - diriter->parent_len - 1;
1451
+ return 0;
1452
+ }
1453
+
1454
+ int git_fs_path_diriter_fullpath(
1455
+ const char **out,
1456
+ size_t *out_len,
1457
+ git_fs_path_diriter *diriter)
1458
+ {
1459
+ GIT_ASSERT_ARG(out);
1460
+ GIT_ASSERT_ARG(out_len);
1461
+ GIT_ASSERT_ARG(diriter);
1462
+
1463
+ *out = diriter->path.ptr;
1464
+ *out_len = diriter->path.size;
1465
+ return 0;
1466
+ }
1467
+
1468
+ int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter)
1469
+ {
1470
+ GIT_ASSERT_ARG(out);
1471
+ GIT_ASSERT_ARG(diriter);
1472
+
1473
+ return git_fs_path_lstat(diriter->path.ptr, out);
1474
+ }
1475
+
1476
+ void git_fs_path_diriter_free(git_fs_path_diriter *diriter)
1477
+ {
1478
+ if (diriter == NULL)
1479
+ return;
1480
+
1481
+ if (diriter->dir) {
1482
+ closedir(diriter->dir);
1483
+ diriter->dir = NULL;
1484
+ }
1485
+
1486
+ #ifdef GIT_USE_ICONV
1487
+ git_fs_path_iconv_clear(&diriter->ic);
1488
+ #endif
1489
+
1490
+ git_str_dispose(&diriter->path);
1491
+ }
1492
+
1493
+ #endif
1494
+
1495
+ int git_fs_path_dirload(
1496
+ git_vector *contents,
1497
+ const char *path,
1498
+ size_t prefix_len,
1499
+ uint32_t flags)
1500
+ {
1501
+ git_fs_path_diriter iter = GIT_FS_PATH_DIRITER_INIT;
1502
+ const char *name;
1503
+ size_t name_len;
1504
+ char *dup;
1505
+ int error;
1506
+
1507
+ GIT_ASSERT_ARG(contents);
1508
+ GIT_ASSERT_ARG(path);
1509
+
1510
+ if ((error = git_fs_path_diriter_init(&iter, path, flags)) < 0)
1511
+ return error;
1512
+
1513
+ while ((error = git_fs_path_diriter_next(&iter)) == 0) {
1514
+ if ((error = git_fs_path_diriter_fullpath(&name, &name_len, &iter)) < 0)
1515
+ break;
1516
+
1517
+ GIT_ASSERT(name_len > prefix_len);
1518
+
1519
+ dup = git__strndup(name + prefix_len, name_len - prefix_len);
1520
+ GIT_ERROR_CHECK_ALLOC(dup);
1521
+
1522
+ if ((error = git_vector_insert(contents, dup)) < 0)
1523
+ break;
1524
+ }
1525
+
1526
+ if (error == GIT_ITEROVER)
1527
+ error = 0;
1528
+
1529
+ git_fs_path_diriter_free(&iter);
1530
+ return error;
1531
+ }
1532
+
1533
+ int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url_or_path)
1534
+ {
1535
+ if (git_fs_path_is_local_file_url(url_or_path))
1536
+ return git_fs_path_fromurl(local_path_out, url_or_path);
1537
+ else
1538
+ return git_str_sets(local_path_out, url_or_path);
1539
+ }
1540
+
1541
+ /* Reject paths like AUX or COM1, or those versions that end in a dot or
1542
+ * colon. ("AUX." or "AUX:")
1543
+ */
1544
+ GIT_INLINE(bool) validate_dospath(
1545
+ const char *component,
1546
+ size_t len,
1547
+ const char dospath[3],
1548
+ bool trailing_num)
1549
+ {
1550
+ size_t last = trailing_num ? 4 : 3;
1551
+
1552
+ if (len < last || git__strncasecmp(component, dospath, 3) != 0)
1553
+ return true;
1554
+
1555
+ if (trailing_num && (component[3] < '1' || component[3] > '9'))
1556
+ return true;
1557
+
1558
+ return (len > last &&
1559
+ component[last] != '.' &&
1560
+ component[last] != ':');
1561
+ }
1562
+
1563
+ GIT_INLINE(bool) validate_char(unsigned char c, unsigned int flags)
1564
+ {
1565
+ if ((flags & GIT_FS_PATH_REJECT_BACKSLASH) && c == '\\')
1566
+ return false;
1567
+
1568
+ if ((flags & GIT_FS_PATH_REJECT_SLASH) && c == '/')
1569
+ return false;
1570
+
1571
+ if (flags & GIT_FS_PATH_REJECT_NT_CHARS) {
1572
+ if (c < 32)
1573
+ return false;
1574
+
1575
+ switch (c) {
1576
+ case '<':
1577
+ case '>':
1578
+ case ':':
1579
+ case '"':
1580
+ case '|':
1581
+ case '?':
1582
+ case '*':
1583
+ return false;
1584
+ }
1585
+ }
1586
+
1587
+ return true;
1588
+ }
1589
+
1590
+ /*
1591
+ * We fundamentally don't like some paths when dealing with user-inputted
1592
+ * strings (to avoid escaping a sandbox): we don't want dot or dot-dot
1593
+ * anywhere, we want to avoid writing weird paths on Windows that can't
1594
+ * be handled by tools that use the non-\\?\ APIs, we don't want slashes
1595
+ * or double slashes at the end of paths that can make them ambiguous.
1596
+ *
1597
+ * For checkout, we don't want to recurse into ".git" either.
1598
+ */
1599
+ static bool validate_component(
1600
+ const char *component,
1601
+ size_t len,
1602
+ unsigned int flags)
1603
+ {
1604
+ if (len == 0)
1605
+ return !(flags & GIT_FS_PATH_REJECT_EMPTY_COMPONENT);
1606
+
1607
+ if ((flags & GIT_FS_PATH_REJECT_TRAVERSAL) &&
1608
+ len == 1 && component[0] == '.')
1609
+ return false;
1610
+
1611
+ if ((flags & GIT_FS_PATH_REJECT_TRAVERSAL) &&
1612
+ len == 2 && component[0] == '.' && component[1] == '.')
1613
+ return false;
1614
+
1615
+ if ((flags & GIT_FS_PATH_REJECT_TRAILING_DOT) &&
1616
+ component[len - 1] == '.')
1617
+ return false;
1618
+
1619
+ if ((flags & GIT_FS_PATH_REJECT_TRAILING_SPACE) &&
1620
+ component[len - 1] == ' ')
1621
+ return false;
1622
+
1623
+ if ((flags & GIT_FS_PATH_REJECT_TRAILING_COLON) &&
1624
+ component[len - 1] == ':')
1625
+ return false;
1626
+
1627
+ if (flags & GIT_FS_PATH_REJECT_DOS_PATHS) {
1628
+ if (!validate_dospath(component, len, "CON", false) ||
1629
+ !validate_dospath(component, len, "PRN", false) ||
1630
+ !validate_dospath(component, len, "AUX", false) ||
1631
+ !validate_dospath(component, len, "NUL", false) ||
1632
+ !validate_dospath(component, len, "COM", true) ||
1633
+ !validate_dospath(component, len, "LPT", true))
1634
+ return false;
1635
+ }
1636
+
1637
+ return true;
1638
+ }
1639
+
1640
+ #ifdef GIT_WIN32
1641
+ GIT_INLINE(bool) validate_length(
1642
+ const char *path,
1643
+ size_t len,
1644
+ size_t utf8_char_len)
1645
+ {
1646
+ GIT_UNUSED(path);
1647
+ GIT_UNUSED(len);
1648
+
1649
+ return (utf8_char_len <= MAX_PATH);
1650
+ }
1651
+ #endif
1652
+
1653
+ bool git_fs_path_str_is_valid_ext(
1654
+ const git_str *path,
1655
+ unsigned int flags,
1656
+ bool (*validate_char_cb)(char ch, void *payload),
1657
+ bool (*validate_component_cb)(const char *component, size_t len, void *payload),
1658
+ bool (*validate_length_cb)(const char *path, size_t len, size_t utf8_char_len),
1659
+ void *payload)
1660
+ {
1661
+ const char *start, *c;
1662
+ size_t len = 0;
1663
+
1664
+ if (!flags)
1665
+ return true;
1666
+
1667
+ for (start = c = path->ptr; *c && len < path->size; c++, len++) {
1668
+ if (!validate_char(*c, flags))
1669
+ return false;
1670
+
1671
+ if (validate_char_cb && !validate_char_cb(*c, payload))
1672
+ return false;
1673
+
1674
+ if (*c != '/')
1675
+ continue;
1676
+
1677
+ if (!validate_component(start, (c - start), flags))
1678
+ return false;
1679
+
1680
+ if (validate_component_cb &&
1681
+ !validate_component_cb(start, (c - start), payload))
1682
+ return false;
1683
+
1684
+ start = c + 1;
1685
+ }
1686
+
1687
+ /*
1688
+ * We want to support paths specified as either `const char *`
1689
+ * or `git_str *`; we pass size as `SIZE_MAX` when we use a
1690
+ * `const char *` to avoid a `strlen`. Ensure that we didn't
1691
+ * have a NUL in the buffer if there was a non-SIZE_MAX length.
1692
+ */
1693
+ if (path->size != SIZE_MAX && len != path->size)
1694
+ return false;
1695
+
1696
+ if (!validate_component(start, (c - start), flags))
1697
+ return false;
1698
+
1699
+ if (validate_component_cb &&
1700
+ !validate_component_cb(start, (c - start), payload))
1701
+ return false;
1702
+
1703
+ #ifdef GIT_WIN32
1704
+ if ((flags & GIT_FS_PATH_REJECT_LONG_PATHS) != 0) {
1705
+ size_t utf8_len = git_utf8_char_length(path->ptr, len);
1706
+
1707
+ if (!validate_length(path->ptr, len, utf8_len))
1708
+ return false;
1709
+
1710
+ if (validate_length_cb &&
1711
+ !validate_length_cb(path->ptr, len, utf8_len))
1712
+ return false;
1713
+ }
1714
+ #else
1715
+ GIT_UNUSED(validate_length_cb);
1716
+ #endif
1717
+
1718
+ return true;
1719
+ }
1720
+
1721
+ int git_fs_path_validate_str_length_with_suffix(
1722
+ git_str *path,
1723
+ size_t suffix_len)
1724
+ {
1725
+ #ifdef GIT_WIN32
1726
+ size_t utf8_len = git_utf8_char_length(path->ptr, path->size);
1727
+ size_t total_len;
1728
+
1729
+ if (GIT_ADD_SIZET_OVERFLOW(&total_len, utf8_len, suffix_len) ||
1730
+ total_len > MAX_PATH) {
1731
+
1732
+ git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%.*s'",
1733
+ (int)path->size, path->ptr);
1734
+ return -1;
1735
+ }
1736
+ #else
1737
+ GIT_UNUSED(path);
1738
+ GIT_UNUSED(suffix_len);
1739
+ #endif
1740
+
1741
+ return 0;
1742
+ }
1743
+
1744
+ int git_fs_path_normalize_slashes(git_str *out, const char *path)
1745
+ {
1746
+ int error;
1747
+ char *p;
1748
+
1749
+ if ((error = git_str_puts(out, path)) < 0)
1750
+ return error;
1751
+
1752
+ for (p = out->ptr; *p; p++) {
1753
+ if (*p == '\\')
1754
+ *p = '/';
1755
+ }
1756
+
1757
+ return 0;
1758
+ }
1759
+
1760
+ bool git_fs_path_supports_symlinks(const char *dir)
1761
+ {
1762
+ git_str path = GIT_STR_INIT;
1763
+ bool supported = false;
1764
+ struct stat st;
1765
+ int fd;
1766
+
1767
+ if ((fd = git_futils_mktmp(&path, dir, 0666)) < 0 ||
1768
+ p_close(fd) < 0 ||
1769
+ p_unlink(path.ptr) < 0 ||
1770
+ p_symlink("testing", path.ptr) < 0 ||
1771
+ p_lstat(path.ptr, &st) < 0)
1772
+ goto done;
1773
+
1774
+ supported = (S_ISLNK(st.st_mode) != 0);
1775
+ done:
1776
+ if (path.size)
1777
+ (void)p_unlink(path.ptr);
1778
+ git_str_dispose(&path);
1779
+ return supported;
1780
+ }
1781
+
1782
+ int git_fs_path_validate_system_file_ownership(const char *path)
1783
+ {
1784
+ #ifndef GIT_WIN32
1785
+ GIT_UNUSED(path);
1786
+ return GIT_OK;
1787
+ #else
1788
+ git_win32_path buf;
1789
+ PSID owner_sid;
1790
+ PSECURITY_DESCRIPTOR descriptor = NULL;
1791
+ HANDLE token;
1792
+ TOKEN_USER *info = NULL;
1793
+ DWORD err, len;
1794
+ int ret;
1795
+
1796
+ if (git_win32_path_from_utf8(buf, path) < 0)
1797
+ return -1;
1798
+
1799
+ err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT,
1800
+ OWNER_SECURITY_INFORMATION |
1801
+ DACL_SECURITY_INFORMATION,
1802
+ &owner_sid, NULL, NULL, NULL, &descriptor);
1803
+
1804
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
1805
+ ret = GIT_ENOTFOUND;
1806
+ goto cleanup;
1807
+ }
1808
+
1809
+ if (err != ERROR_SUCCESS) {
1810
+ git_error_set(GIT_ERROR_OS, "failed to get security information");
1811
+ ret = GIT_ERROR;
1812
+ goto cleanup;
1813
+ }
1814
+
1815
+ if (!IsValidSid(owner_sid)) {
1816
+ git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown");
1817
+ ret = GIT_ERROR;
1818
+ goto cleanup;
1819
+ }
1820
+
1821
+ if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
1822
+ IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
1823
+ ret = GIT_OK;
1824
+ goto cleanup;
1825
+ }
1826
+
1827
+ /* Obtain current user's SID */
1828
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) &&
1829
+ !GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
1830
+ info = git__malloc(len);
1831
+ GIT_ERROR_CHECK_ALLOC(info);
1832
+ if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
1833
+ git__free(info);
1834
+ info = NULL;
1835
+ }
1836
+ }
1837
+
1838
+ /*
1839
+ * If the file is owned by the same account that is running the current
1840
+ * process, it's okay to read from that file.
1841
+ */
1842
+ if (info && EqualSid(owner_sid, info->User.Sid))
1843
+ ret = GIT_OK;
1844
+ else {
1845
+ git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid");
1846
+ ret = GIT_ERROR;
1847
+ }
1848
+ git__free(info);
1849
+
1850
+ cleanup:
1851
+ if (descriptor)
1852
+ LocalFree(descriptor);
1853
+
1854
+ return ret;
1855
+ #endif
1856
+ }
1857
+
1858
+ int git_fs_path_find_executable(git_str *fullpath, const char *executable)
1859
+ {
1860
+ #ifdef GIT_WIN32
1861
+ git_win32_path fullpath_w, executable_w;
1862
+ int error;
1863
+
1864
+ if (git__utf8_to_16(executable_w, GIT_WIN_PATH_MAX, executable) < 0)
1865
+ return -1;
1866
+
1867
+ error = git_win32_path_find_executable(fullpath_w, executable_w);
1868
+
1869
+ if (error == 0)
1870
+ error = git_str_put_w(fullpath, fullpath_w, wcslen(fullpath_w));
1871
+
1872
+ return error;
1873
+ #else
1874
+ git_str path = GIT_STR_INIT;
1875
+ const char *current_dir, *term;
1876
+ bool found = false;
1877
+
1878
+ if (git__getenv(&path, "PATH") < 0)
1879
+ return -1;
1880
+
1881
+ current_dir = path.ptr;
1882
+
1883
+ while (*current_dir) {
1884
+ if (! (term = strchr(current_dir, GIT_PATH_LIST_SEPARATOR)))
1885
+ term = strchr(current_dir, '\0');
1886
+
1887
+ git_str_clear(fullpath);
1888
+ if (git_str_put(fullpath, current_dir, (term - current_dir)) < 0 ||
1889
+ git_str_putc(fullpath, '/') < 0 ||
1890
+ git_str_puts(fullpath, executable) < 0)
1891
+ return -1;
1892
+
1893
+ if (git_fs_path_isfile(fullpath->ptr)) {
1894
+ found = true;
1895
+ break;
1896
+ }
1897
+
1898
+ current_dir = term;
1899
+
1900
+ while (*current_dir == GIT_PATH_LIST_SEPARATOR)
1901
+ current_dir++;
1902
+ }
1903
+
1904
+ git_str_dispose(&path);
1905
+
1906
+ if (found)
1907
+ return 0;
1908
+
1909
+ git_str_clear(fullpath);
1910
+ return GIT_ENOTFOUND;
1911
+ #endif
1912
+ }