rugged 1.3.2.3 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }