rugged 0.16.0 → 0.17.0b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (392) hide show
  1. data/README.md +373 -243
  2. data/Rakefile +22 -0
  3. data/ext/rugged/extconf.rb +9 -6
  4. data/ext/rugged/rugged.c +123 -31
  5. data/ext/rugged/rugged.h +11 -10
  6. data/ext/rugged/rugged_blob.c +181 -18
  7. data/ext/rugged/rugged_commit.c +196 -18
  8. data/ext/rugged/rugged_config.c +94 -5
  9. data/ext/rugged/rugged_object.c +54 -1
  10. data/ext/rugged/rugged_reference.c +203 -15
  11. data/ext/rugged/{remote.c → rugged_remote.c} +35 -10
  12. data/ext/rugged/rugged_repo.c +323 -81
  13. data/ext/rugged/rugged_revwalk.c +57 -4
  14. data/ext/rugged/rugged_signature.c +3 -3
  15. data/ext/rugged/rugged_tag.c +72 -1
  16. data/ext/rugged/rugged_tree.c +70 -2
  17. data/ext/rugged/vendor/libgit2-dist/deps/regex/config.h +7 -0
  18. data/ext/rugged/vendor/libgit2-dist/deps/regex/regcomp.c +3856 -0
  19. data/ext/rugged/vendor/libgit2-dist/deps/regex/regex.c +85 -0
  20. data/ext/rugged/vendor/libgit2-dist/deps/regex/regex.h +582 -0
  21. data/ext/rugged/vendor/libgit2-dist/deps/regex/regex_internal.c +1744 -0
  22. data/ext/rugged/vendor/libgit2-dist/deps/regex/regex_internal.h +810 -0
  23. data/ext/rugged/vendor/libgit2-dist/deps/regex/regexec.c +4369 -0
  24. data/ext/rugged/vendor/libgit2-dist/examples/diff.c +238 -0
  25. data/ext/rugged/vendor/libgit2-dist/examples/general.c +4 -4
  26. data/ext/rugged/vendor/libgit2-dist/examples/network/fetch.c +101 -85
  27. data/ext/rugged/vendor/libgit2-dist/examples/network/git2.c +7 -3
  28. data/ext/rugged/vendor/libgit2-dist/examples/network/index-pack.c +80 -25
  29. data/ext/rugged/vendor/libgit2-dist/examples/network/ls-remote.c +6 -6
  30. data/ext/rugged/vendor/libgit2-dist/include/git2/attr.h +224 -0
  31. data/ext/rugged/vendor/libgit2-dist/include/git2/blob.h +59 -5
  32. data/ext/rugged/vendor/libgit2-dist/include/git2/branch.h +114 -7
  33. data/ext/rugged/vendor/libgit2-dist/include/git2/commit.h +14 -7
  34. data/ext/rugged/vendor/libgit2-dist/include/git2/common.h +4 -3
  35. data/ext/rugged/vendor/libgit2-dist/include/git2/config.h +105 -27
  36. data/ext/rugged/vendor/libgit2-dist/include/git2/diff.h +409 -0
  37. data/ext/rugged/vendor/libgit2-dist/include/git2/errors.h +47 -82
  38. data/ext/rugged/vendor/libgit2-dist/include/git2/index.h +25 -10
  39. data/ext/rugged/vendor/libgit2-dist/include/git2/indexer.h +46 -1
  40. data/ext/rugged/vendor/libgit2-dist/include/git2/merge.h +35 -0
  41. data/ext/rugged/vendor/libgit2-dist/include/git2/net.h +1 -1
  42. data/ext/rugged/vendor/libgit2-dist/include/git2/notes.h +139 -0
  43. data/ext/rugged/vendor/libgit2-dist/include/git2/object.h +5 -5
  44. data/ext/rugged/vendor/libgit2-dist/include/git2/odb.h +13 -13
  45. data/ext/rugged/vendor/libgit2-dist/include/git2/odb_backend.h +8 -8
  46. data/ext/rugged/vendor/libgit2-dist/include/git2/oid.h +14 -9
  47. data/ext/rugged/vendor/libgit2-dist/include/git2/reflog.h +5 -5
  48. data/ext/rugged/vendor/libgit2-dist/include/git2/refs.h +37 -17
  49. data/ext/rugged/vendor/libgit2-dist/include/git2/refspec.h +17 -9
  50. data/ext/rugged/vendor/libgit2-dist/include/git2/remote.h +83 -16
  51. data/ext/rugged/vendor/libgit2-dist/include/git2/repository.h +24 -10
  52. data/ext/rugged/vendor/libgit2-dist/include/git2/reset.h +44 -0
  53. data/ext/rugged/vendor/libgit2-dist/include/git2/revparse.h +36 -0
  54. data/ext/rugged/vendor/libgit2-dist/include/git2/revwalk.h +74 -6
  55. data/ext/rugged/vendor/libgit2-dist/include/git2/signature.h +3 -3
  56. data/ext/rugged/vendor/libgit2-dist/include/git2/status.h +120 -19
  57. data/ext/rugged/vendor/libgit2-dist/include/git2/submodule.h +103 -0
  58. data/ext/rugged/vendor/libgit2-dist/include/git2/tag.h +28 -10
  59. data/ext/rugged/vendor/libgit2-dist/include/git2/threads.h +1 -1
  60. data/ext/rugged/vendor/libgit2-dist/include/git2/tree.h +13 -13
  61. data/ext/rugged/vendor/libgit2-dist/include/git2/types.h +16 -2
  62. data/ext/rugged/vendor/libgit2-dist/include/git2/version.h +3 -3
  63. data/ext/rugged/vendor/libgit2-dist/include/git2/windows.h +1 -1
  64. data/ext/rugged/vendor/libgit2-dist/include/git2.h +7 -2
  65. data/ext/rugged/vendor/libgit2-dist/src/attr.c +677 -0
  66. data/ext/rugged/vendor/libgit2-dist/src/attr.h +56 -0
  67. data/ext/rugged/vendor/libgit2-dist/src/attr_file.c +609 -0
  68. data/ext/rugged/vendor/libgit2-dist/src/attr_file.h +145 -0
  69. data/ext/rugged/vendor/libgit2-dist/src/blob.c +213 -60
  70. data/ext/rugged/vendor/libgit2-dist/src/blob.h +2 -1
  71. data/ext/rugged/vendor/libgit2-dist/src/branch.c +208 -0
  72. data/ext/rugged/vendor/libgit2-dist/src/branch.h +17 -0
  73. data/ext/rugged/vendor/libgit2-dist/src/bswap.h +1 -1
  74. data/ext/rugged/vendor/libgit2-dist/src/buffer.c +395 -46
  75. data/ext/rugged/vendor/libgit2-dist/src/buffer.h +112 -9
  76. data/ext/rugged/vendor/libgit2-dist/src/cache.c +37 -49
  77. data/ext/rugged/vendor/libgit2-dist/src/cache.h +7 -17
  78. data/ext/rugged/vendor/libgit2-dist/src/cc-compat.h +18 -16
  79. data/ext/rugged/vendor/libgit2-dist/src/commit.c +56 -90
  80. data/ext/rugged/vendor/libgit2-dist/src/commit.h +1 -1
  81. data/ext/rugged/vendor/libgit2-dist/src/common.h +12 -5
  82. data/ext/rugged/vendor/libgit2-dist/src/{win32 → compat}/fnmatch.c +1 -1
  83. data/ext/rugged/vendor/libgit2-dist/src/{win32 → compat}/fnmatch.h +3 -3
  84. data/ext/rugged/vendor/libgit2-dist/src/config.c +247 -158
  85. data/ext/rugged/vendor/libgit2-dist/src/config.h +10 -1
  86. data/ext/rugged/vendor/libgit2-dist/src/config_cache.c +94 -0
  87. data/ext/rugged/vendor/libgit2-dist/src/config_file.c +606 -496
  88. data/ext/rugged/vendor/libgit2-dist/src/config_file.h +31 -0
  89. data/ext/rugged/vendor/libgit2-dist/src/crlf.c +228 -0
  90. data/ext/rugged/vendor/libgit2-dist/src/date.c +876 -0
  91. data/ext/rugged/vendor/libgit2-dist/src/delta-apply.c +15 -9
  92. data/ext/rugged/vendor/libgit2-dist/src/delta-apply.h +2 -2
  93. data/ext/rugged/vendor/libgit2-dist/src/diff.c +814 -0
  94. data/ext/rugged/vendor/libgit2-dist/src/diff.h +43 -0
  95. data/ext/rugged/vendor/libgit2-dist/src/diff_output.c +794 -0
  96. data/ext/rugged/vendor/libgit2-dist/src/errors.c +89 -74
  97. data/ext/rugged/vendor/libgit2-dist/src/fetch.c +94 -66
  98. data/ext/rugged/vendor/libgit2-dist/src/fetch.h +5 -4
  99. data/ext/rugged/vendor/libgit2-dist/src/filebuf.c +157 -100
  100. data/ext/rugged/vendor/libgit2-dist/src/filebuf.h +22 -8
  101. data/ext/rugged/vendor/libgit2-dist/src/fileops.c +330 -206
  102. data/ext/rugged/vendor/libgit2-dist/src/fileops.h +82 -51
  103. data/ext/rugged/vendor/libgit2-dist/src/filter.c +165 -0
  104. data/ext/rugged/vendor/libgit2-dist/src/filter.h +119 -0
  105. data/ext/rugged/vendor/libgit2-dist/src/global.c +4 -4
  106. data/ext/rugged/vendor/libgit2-dist/src/global.h +4 -1
  107. data/ext/rugged/vendor/libgit2-dist/src/hash.c +1 -1
  108. data/ext/rugged/vendor/libgit2-dist/src/hash.h +1 -1
  109. data/ext/rugged/vendor/libgit2-dist/src/ignore.c +203 -0
  110. data/ext/rugged/vendor/libgit2-dist/src/ignore.h +38 -0
  111. data/ext/rugged/vendor/libgit2-dist/src/index.c +220 -169
  112. data/ext/rugged/vendor/libgit2-dist/src/index.h +5 -1
  113. data/ext/rugged/vendor/libgit2-dist/src/indexer.c +601 -102
  114. data/ext/rugged/vendor/libgit2-dist/src/iterator.c +748 -0
  115. data/ext/rugged/vendor/libgit2-dist/src/iterator.h +151 -0
  116. data/ext/rugged/vendor/libgit2-dist/src/khash.h +608 -0
  117. data/ext/rugged/vendor/libgit2-dist/src/map.h +6 -1
  118. data/ext/rugged/vendor/libgit2-dist/src/message.c +61 -0
  119. data/ext/rugged/vendor/libgit2-dist/src/message.h +14 -0
  120. data/ext/rugged/vendor/libgit2-dist/src/mwindow.c +27 -29
  121. data/ext/rugged/vendor/libgit2-dist/src/mwindow.h +4 -4
  122. data/ext/rugged/vendor/libgit2-dist/src/netops.c +375 -56
  123. data/ext/rugged/vendor/libgit2-dist/src/netops.h +12 -9
  124. data/ext/rugged/vendor/libgit2-dist/src/notes.c +548 -0
  125. data/ext/rugged/vendor/libgit2-dist/src/notes.h +28 -0
  126. data/ext/rugged/vendor/libgit2-dist/src/object.c +59 -21
  127. data/ext/rugged/vendor/libgit2-dist/src/odb.c +212 -175
  128. data/ext/rugged/vendor/libgit2-dist/src/odb.h +39 -2
  129. data/ext/rugged/vendor/libgit2-dist/src/odb_loose.c +238 -241
  130. data/ext/rugged/vendor/libgit2-dist/src/odb_pack.c +94 -106
  131. data/ext/rugged/vendor/libgit2-dist/src/oid.c +59 -60
  132. data/ext/rugged/vendor/libgit2-dist/src/oidmap.h +42 -0
  133. data/ext/rugged/vendor/libgit2-dist/src/pack.c +198 -170
  134. data/ext/rugged/vendor/libgit2-dist/src/pack.h +16 -9
  135. data/ext/rugged/vendor/libgit2-dist/src/path.c +496 -106
  136. data/ext/rugged/vendor/libgit2-dist/src/path.h +214 -20
  137. data/ext/rugged/vendor/libgit2-dist/src/pkt.c +88 -159
  138. data/ext/rugged/vendor/libgit2-dist/src/pkt.h +9 -5
  139. data/ext/rugged/vendor/libgit2-dist/src/pool.c +294 -0
  140. data/ext/rugged/vendor/libgit2-dist/src/pool.h +125 -0
  141. data/ext/rugged/vendor/libgit2-dist/src/posix.c +38 -16
  142. data/ext/rugged/vendor/libgit2-dist/src/posix.h +20 -2
  143. data/ext/rugged/vendor/libgit2-dist/src/ppc/sha1.c +1 -1
  144. data/ext/rugged/vendor/libgit2-dist/src/ppc/sha1.h +1 -1
  145. data/ext/rugged/vendor/libgit2-dist/src/pqueue.c +7 -7
  146. data/ext/rugged/vendor/libgit2-dist/src/pqueue.h +1 -1
  147. data/ext/rugged/vendor/libgit2-dist/src/protocol.c +21 -13
  148. data/ext/rugged/vendor/libgit2-dist/src/protocol.h +1 -1
  149. data/ext/rugged/vendor/libgit2-dist/src/reflog.c +125 -103
  150. data/ext/rugged/vendor/libgit2-dist/src/reflog.h +1 -1
  151. data/ext/rugged/vendor/libgit2-dist/src/refs.c +713 -640
  152. data/ext/rugged/vendor/libgit2-dist/src/refs.h +27 -3
  153. data/ext/rugged/vendor/libgit2-dist/src/refspec.c +51 -17
  154. data/ext/rugged/vendor/libgit2-dist/src/refspec.h +13 -1
  155. data/ext/rugged/vendor/libgit2-dist/src/remote.c +307 -119
  156. data/ext/rugged/vendor/libgit2-dist/src/remote.h +3 -2
  157. data/ext/rugged/vendor/libgit2-dist/src/repository.c +593 -442
  158. data/ext/rugged/vendor/libgit2-dist/src/repository.h +80 -2
  159. data/ext/rugged/vendor/libgit2-dist/src/reset.c +103 -0
  160. data/ext/rugged/vendor/libgit2-dist/src/revparse.c +753 -0
  161. data/ext/rugged/vendor/libgit2-dist/src/revwalk.c +434 -158
  162. data/ext/rugged/vendor/libgit2-dist/src/sha1.c +3 -3
  163. data/ext/rugged/vendor/libgit2-dist/src/sha1.h +2 -2
  164. data/ext/rugged/vendor/libgit2-dist/src/sha1_lookup.c +3 -2
  165. data/ext/rugged/vendor/libgit2-dist/src/sha1_lookup.h +1 -1
  166. data/ext/rugged/vendor/libgit2-dist/src/signature.c +69 -80
  167. data/ext/rugged/vendor/libgit2-dist/src/signature.h +1 -1
  168. data/ext/rugged/vendor/libgit2-dist/src/status.c +184 -638
  169. data/ext/rugged/vendor/libgit2-dist/src/strmap.h +64 -0
  170. data/ext/rugged/vendor/libgit2-dist/src/submodule.c +387 -0
  171. data/ext/rugged/vendor/libgit2-dist/src/tag.c +162 -137
  172. data/ext/rugged/vendor/libgit2-dist/src/tag.h +2 -1
  173. data/ext/rugged/vendor/libgit2-dist/src/thread-utils.c +1 -1
  174. data/ext/rugged/vendor/libgit2-dist/src/thread-utils.h +8 -8
  175. data/ext/rugged/vendor/libgit2-dist/src/transport.c +31 -19
  176. data/ext/rugged/vendor/libgit2-dist/src/transport.h +31 -11
  177. data/ext/rugged/vendor/libgit2-dist/src/transports/git.c +168 -193
  178. data/ext/rugged/vendor/libgit2-dist/src/transports/http.c +192 -241
  179. data/ext/rugged/vendor/libgit2-dist/src/transports/local.c +92 -86
  180. data/ext/rugged/vendor/libgit2-dist/src/tree-cache.c +32 -49
  181. data/ext/rugged/vendor/libgit2-dist/src/tree-cache.h +1 -1
  182. data/ext/rugged/vendor/libgit2-dist/src/tree.c +172 -145
  183. data/ext/rugged/vendor/libgit2-dist/src/tree.h +16 -2
  184. data/ext/rugged/vendor/libgit2-dist/src/tsort.c +16 -14
  185. data/ext/rugged/vendor/libgit2-dist/src/unix/map.c +8 -24
  186. data/ext/rugged/vendor/libgit2-dist/src/unix/posix.h +9 -3
  187. data/ext/rugged/vendor/libgit2-dist/src/util.c +94 -38
  188. data/ext/rugged/vendor/libgit2-dist/src/util.h +119 -13
  189. data/ext/rugged/vendor/libgit2-dist/src/vector.c +84 -31
  190. data/ext/rugged/vendor/libgit2-dist/src/vector.h +37 -4
  191. data/ext/rugged/vendor/libgit2-dist/src/win32/dir.c +81 -41
  192. data/ext/rugged/vendor/libgit2-dist/src/{dir.h → win32/dir.h} +4 -9
  193. data/ext/rugged/vendor/libgit2-dist/src/win32/map.c +19 -35
  194. data/ext/rugged/vendor/libgit2-dist/src/win32/mingw-compat.h +1 -1
  195. data/ext/rugged/vendor/libgit2-dist/src/win32/msvc-compat.h +5 -1
  196. data/ext/rugged/vendor/libgit2-dist/src/win32/posix.h +10 -8
  197. data/ext/rugged/vendor/libgit2-dist/src/win32/posix_w32.c +262 -118
  198. data/ext/rugged/vendor/libgit2-dist/src/win32/pthread.c +12 -9
  199. data/ext/rugged/vendor/libgit2-dist/src/win32/pthread.h +1 -1
  200. data/ext/rugged/vendor/libgit2-dist/src/win32/utf-conv.c +30 -26
  201. data/ext/rugged/vendor/libgit2-dist/src/win32/utf-conv.h +2 -1
  202. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xdiff.h +135 -0
  203. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xdiffi.c +572 -0
  204. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xdiffi.h +63 -0
  205. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xemit.c +253 -0
  206. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xemit.h +36 -0
  207. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xhistogram.c +371 -0
  208. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xinclude.h +46 -0
  209. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xmacros.h +54 -0
  210. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xmerge.c +619 -0
  211. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xpatience.c +358 -0
  212. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xprepare.c +483 -0
  213. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xprepare.h +34 -0
  214. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xtypes.h +67 -0
  215. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xutils.c +419 -0
  216. data/ext/rugged/vendor/libgit2-dist/src/xdiff/xutils.h +49 -0
  217. data/ext/rugged/vendor/libgit2-dist/tests-clar/attr/attr_expect.h +43 -0
  218. data/ext/rugged/vendor/libgit2-dist/tests-clar/attr/file.c +226 -0
  219. data/ext/rugged/vendor/libgit2-dist/tests-clar/attr/flags.c +108 -0
  220. data/ext/rugged/vendor/libgit2-dist/tests-clar/attr/lookup.c +262 -0
  221. data/ext/rugged/vendor/libgit2-dist/tests-clar/attr/repo.c +273 -0
  222. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/buf/basic.c +5 -5
  223. data/ext/rugged/vendor/libgit2-dist/tests-clar/clar_helpers.c +181 -0
  224. data/ext/rugged/vendor/libgit2-dist/tests-clar/clar_libgit2.h +55 -0
  225. data/ext/rugged/vendor/libgit2-dist/tests-clar/commit/commit.c +44 -0
  226. data/ext/rugged/vendor/libgit2-dist/tests-clar/commit/parse.c +350 -0
  227. data/ext/rugged/vendor/libgit2-dist/tests-clar/commit/signature.c +65 -0
  228. data/ext/rugged/vendor/libgit2-dist/tests-clar/commit/write.c +140 -0
  229. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/config/add.c +3 -3
  230. data/ext/rugged/vendor/libgit2-dist/tests-clar/config/multivar.c +151 -0
  231. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/config/new.c +5 -5
  232. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/config/read.c +44 -32
  233. data/ext/rugged/vendor/libgit2-dist/tests-clar/config/stress.c +61 -0
  234. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/config/write.c +20 -5
  235. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/buffer.c +613 -0
  236. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/core/dirent.c +39 -26
  237. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/env.c +115 -0
  238. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/errors.c +60 -0
  239. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/core/filebuf.c +4 -18
  240. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/hex.c +22 -0
  241. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/core/oid.c +6 -6
  242. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/path.c +420 -0
  243. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/pool.c +85 -0
  244. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/rmdir.c +68 -0
  245. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/core/string.c +1 -1
  246. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/strmap.c +102 -0
  247. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/core/strtol.c +1 -1
  248. data/ext/rugged/vendor/libgit2-dist/tests-clar/core/vector.c +191 -0
  249. data/ext/rugged/vendor/libgit2-dist/tests-clar/date/date.c +15 -0
  250. data/ext/rugged/vendor/libgit2-dist/tests-clar/diff/blob.c +254 -0
  251. data/ext/rugged/vendor/libgit2-dist/tests-clar/diff/diff_helpers.c +104 -0
  252. data/ext/rugged/vendor/libgit2-dist/tests-clar/diff/diff_helpers.h +47 -0
  253. data/ext/rugged/vendor/libgit2-dist/tests-clar/diff/index.c +92 -0
  254. data/ext/rugged/vendor/libgit2-dist/tests-clar/diff/iterator.c +572 -0
  255. data/ext/rugged/vendor/libgit2-dist/tests-clar/diff/patch.c +99 -0
  256. data/ext/rugged/vendor/libgit2-dist/tests-clar/diff/tree.c +210 -0
  257. data/ext/rugged/vendor/libgit2-dist/tests-clar/diff/workdir.c +543 -0
  258. data/ext/rugged/vendor/libgit2-dist/tests-clar/index/read_tree.c +46 -0
  259. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/index/rename.c +2 -12
  260. data/ext/rugged/vendor/libgit2-dist/tests-clar/index/tests.c +246 -0
  261. data/ext/rugged/vendor/libgit2-dist/tests-clar/network/createremotethenload.c +33 -0
  262. data/ext/rugged/vendor/libgit2-dist/tests-clar/network/remotelocal.c +137 -0
  263. data/ext/rugged/vendor/libgit2-dist/tests-clar/network/remotes.c +183 -0
  264. data/ext/rugged/vendor/libgit2-dist/tests-clar/notes/notes.c +133 -0
  265. data/ext/rugged/vendor/libgit2-dist/tests-clar/notes/notesref.c +57 -0
  266. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/blob/filter.c +125 -0
  267. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/blob/fromchunks.c +87 -0
  268. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/blob/write.c +69 -0
  269. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/commit/commitstagedfile.c +126 -0
  270. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/lookup.c +63 -0
  271. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/message.c +171 -0
  272. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/chars.c +3 -14
  273. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/compare.c +4 -4
  274. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/convert.c +10 -10
  275. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/data.h +0 -0
  276. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/fromstr.c +1 -1
  277. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/hash.c +21 -17
  278. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/short.c +1 -1
  279. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/size.c +1 -1
  280. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/raw/type2string.c +14 -14
  281. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/raw/write.c +455 -0
  282. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/tag/peel.c +56 -0
  283. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/tag/read.c +130 -0
  284. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/tag/write.c +192 -0
  285. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/object/tree/frompath.c +22 -16
  286. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/tree/read.c +75 -0
  287. data/ext/rugged/vendor/libgit2-dist/tests-clar/object/tree/write.c +84 -0
  288. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/odb/loose.c +1 -1
  289. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/odb/loose_data.h +0 -0
  290. data/ext/rugged/vendor/libgit2-dist/tests-clar/odb/mixed.c +24 -0
  291. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/odb/pack_data.h +0 -0
  292. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/odb/packed.c +1 -1
  293. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/odb/sorting.c +1 -1
  294. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/branches/create.c +113 -0
  295. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/branches/delete.c +91 -0
  296. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/branches/listall.c +78 -0
  297. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/branches/move.c +72 -0
  298. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/crashes.c +17 -0
  299. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/create.c +149 -0
  300. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/delete.c +85 -0
  301. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/list.c +53 -0
  302. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/listall.c +36 -0
  303. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/lookup.c +42 -0
  304. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/normalize.c +200 -0
  305. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/overwrite.c +136 -0
  306. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/pack.c +67 -0
  307. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/read.c +194 -0
  308. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/reflog.c +123 -0
  309. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/rename.c +339 -0
  310. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/revparse.c +174 -0
  311. data/ext/rugged/vendor/libgit2-dist/tests-clar/refs/unicode.c +42 -0
  312. data/ext/rugged/vendor/libgit2-dist/tests-clar/repo/discover.c +142 -0
  313. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/repo/getters.c +19 -1
  314. data/ext/rugged/vendor/libgit2-dist/tests-clar/repo/init.c +235 -0
  315. data/ext/rugged/vendor/libgit2-dist/tests-clar/repo/open.c +282 -0
  316. data/ext/rugged/vendor/libgit2-dist/tests-clar/repo/setters.c +80 -0
  317. data/ext/rugged/vendor/libgit2-dist/tests-clar/reset/mixed.c +47 -0
  318. data/ext/rugged/vendor/libgit2-dist/tests-clar/reset/reset_helpers.c +10 -0
  319. data/ext/rugged/vendor/libgit2-dist/tests-clar/reset/reset_helpers.h +6 -0
  320. data/ext/rugged/vendor/libgit2-dist/tests-clar/reset/soft.c +102 -0
  321. data/ext/rugged/vendor/libgit2-dist/{tests/t05-revwalk.c → tests-clar/revwalk/basic.c} +85 -44
  322. data/ext/rugged/vendor/libgit2-dist/tests-clar/revwalk/mergebase.c +148 -0
  323. data/ext/rugged/vendor/libgit2-dist/tests-clar/status/ignore.c +133 -0
  324. data/ext/rugged/vendor/libgit2-dist/{tests-clay → tests-clar}/status/single.c +2 -11
  325. data/ext/rugged/vendor/libgit2-dist/tests-clar/status/status_data.h +202 -0
  326. data/ext/rugged/vendor/libgit2-dist/tests-clar/status/status_helpers.c +49 -0
  327. data/ext/rugged/vendor/libgit2-dist/tests-clar/status/status_helpers.h +33 -0
  328. data/ext/rugged/vendor/libgit2-dist/tests-clar/status/submodules.c +112 -0
  329. data/ext/rugged/vendor/libgit2-dist/tests-clar/status/worktree.c +649 -0
  330. data/ext/rugged/vendor/libgit2-dist/tests-clar/threads/basic.c +20 -0
  331. data/ext/rugged/vendor/libgit2-dist.tar.gz +0 -0
  332. data/lib/rugged/commit.rb +35 -0
  333. data/lib/rugged/object.rb +7 -0
  334. data/lib/rugged/reference.rb +9 -0
  335. data/lib/rugged/repository.rb +99 -3
  336. data/lib/rugged/tag.rb +22 -0
  337. data/lib/rugged/tree.rb +8 -0
  338. data/lib/rugged/version.rb +1 -1
  339. data/lib/rugged.rb +4 -1
  340. data/test/blob_test.rb +2 -2
  341. data/test/commit_test.rb +27 -13
  342. data/test/config_test.rb +44 -0
  343. data/test/coverage/HEAD.json +1 -1
  344. data/test/fixtures/testrepo.git/config +2 -0
  345. data/test/fixtures/testrepo.git/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 +0 -0
  346. data/test/lib_test.rb +7 -0
  347. data/test/object_test.rb +3 -3
  348. data/test/reference_test.rb +46 -8
  349. data/test/remote_test.rb +8 -2
  350. data/test/repo_pack_test.rb +3 -3
  351. data/test/repo_test.rb +79 -11
  352. data/test/tag_test.rb +9 -2
  353. data/test/test_helper.rb +21 -0
  354. data/test/tree_test.rb +18 -6
  355. metadata +205 -81
  356. data/ext/rugged/vendor/libgit2-dist/include/git2/zlib.h +0 -40
  357. data/ext/rugged/vendor/libgit2-dist/src/hashtable.c +0 -243
  358. data/ext/rugged/vendor/libgit2-dist/src/hashtable.h +0 -80
  359. data/ext/rugged/vendor/libgit2-dist/tests/t00-core.c +0 -628
  360. data/ext/rugged/vendor/libgit2-dist/tests/t01-data.h +0 -322
  361. data/ext/rugged/vendor/libgit2-dist/tests/t01-rawobj.c +0 -638
  362. data/ext/rugged/vendor/libgit2-dist/tests/t03-data.h +0 -344
  363. data/ext/rugged/vendor/libgit2-dist/tests/t03-objwrite.c +0 -255
  364. data/ext/rugged/vendor/libgit2-dist/tests/t04-commit.c +0 -788
  365. data/ext/rugged/vendor/libgit2-dist/tests/t06-index.c +0 -219
  366. data/ext/rugged/vendor/libgit2-dist/tests/t07-hashtable.c +0 -192
  367. data/ext/rugged/vendor/libgit2-dist/tests/t08-tag.c +0 -357
  368. data/ext/rugged/vendor/libgit2-dist/tests/t09-tree.c +0 -221
  369. data/ext/rugged/vendor/libgit2-dist/tests/t10-refs.c +0 -1294
  370. data/ext/rugged/vendor/libgit2-dist/tests/t12-repo.c +0 -174
  371. data/ext/rugged/vendor/libgit2-dist/tests/t13-threads.c +0 -41
  372. data/ext/rugged/vendor/libgit2-dist/tests/t17-bufs.c +0 -61
  373. data/ext/rugged/vendor/libgit2-dist/tests/t18-status.c +0 -448
  374. data/ext/rugged/vendor/libgit2-dist/tests/test_helpers.c +0 -310
  375. data/ext/rugged/vendor/libgit2-dist/tests/test_helpers.h +0 -83
  376. data/ext/rugged/vendor/libgit2-dist/tests/test_lib.c +0 -198
  377. data/ext/rugged/vendor/libgit2-dist/tests/test_lib.h +0 -54
  378. data/ext/rugged/vendor/libgit2-dist/tests/test_main.c +0 -89
  379. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay.h +0 -187
  380. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay_libgit2.h +0 -28
  381. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay_main.c +0 -1073
  382. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/stress.c +0 -39
  383. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/path.c +0 -139
  384. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/rmdir.c +0 -50
  385. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/vector.c +0 -66
  386. data/ext/rugged/vendor/libgit2-dist/tests-clay/network/remotes.c +0 -50
  387. data/ext/rugged/vendor/libgit2-dist/tests-clay/repo/init.c +0 -104
  388. data/ext/rugged/vendor/libgit2-dist/tests-clay/repo/open.c +0 -54
  389. data/ext/rugged/vendor/libgit2-dist/tests-clay/status/status_data.h +0 -48
  390. data/ext/rugged/vendor/libgit2-dist/tests-clay/status/worktree.c +0 -124
  391. data/lib/rugged/objects.rb +0 -45
  392. data/test/fixtures/testrepo.git/refs/heads/new_name +0 -1
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2009-2011 the libgit2 contributors
2
+ * Copyright (C) 2009-2012 the libgit2 contributors
3
3
  *
4
4
  * This file is part of libgit2, distributed under the GNU GPL v2 with
5
5
  * a Linking Exception. For full terms see the included COPYING file.
@@ -12,14 +12,17 @@
12
12
  #include "buffer.h"
13
13
  #include "git2/config.h"
14
14
  #include "git2/types.h"
15
-
15
+ #include "strmap.h"
16
16
 
17
17
  #include <ctype.h>
18
+ #include <sys/types.h>
19
+ #include <regex.h>
20
+
21
+ GIT__USE_STRMAP;
18
22
 
19
23
  typedef struct cvar_t {
20
24
  struct cvar_t *next;
21
- char *section;
22
- char *name;
25
+ char *key; /* TODO: we might be able to get rid of this */
23
26
  char *value;
24
27
  } cvar_t;
25
28
 
@@ -69,10 +72,10 @@ typedef struct {
69
72
  typedef struct {
70
73
  git_config_file parent;
71
74
 
72
- cvar_t_list var_list;
75
+ git_strmap *values;
73
76
 
74
77
  struct {
75
- git_fbuffer buffer;
78
+ git_buf buffer;
76
79
  char *read_ptr;
77
80
  int line_number;
78
81
  int eof;
@@ -83,325 +86,381 @@ typedef struct {
83
86
 
84
87
  static int config_parse(diskfile_backend *cfg_file);
85
88
  static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
86
- static int config_write(diskfile_backend *cfg, cvar_t *var);
89
+ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
90
+
91
+ static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
92
+ {
93
+ giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
94
+ error_str, backend->file_path, backend->reader.line_number, col);
95
+ }
87
96
 
88
97
  static void cvar_free(cvar_t *var)
89
98
  {
90
99
  if (var == NULL)
91
100
  return;
92
101
 
93
- git__free(var->section);
94
- git__free(var->name);
102
+ git__free(var->key);
95
103
  git__free(var->value);
96
104
  git__free(var);
97
105
  }
98
106
 
99
- static void cvar_list_free(cvar_t_list *list)
107
+ /* Take something the user gave us and make it nice for our hash function */
108
+ static int normalize_name(const char *in, char **out)
100
109
  {
101
- cvar_t *cur;
110
+ char *name, *fdot, *ldot;
111
+
112
+ assert(in && out);
113
+
114
+ name = git__strdup(in);
115
+ GITERR_CHECK_ALLOC(name);
116
+
117
+ fdot = strchr(name, '.');
118
+ ldot = strrchr(name, '.');
102
119
 
103
- while (!CVAR_LIST_EMPTY(list)) {
104
- cur = CVAR_LIST_HEAD(list);
105
- CVAR_LIST_REMOVE_HEAD(list);
106
- cvar_free(cur);
120
+ if (fdot == NULL || ldot == NULL) {
121
+ git__free(name);
122
+ giterr_set(GITERR_CONFIG,
123
+ "Invalid variable name: '%s'", in);
124
+ return -1;
107
125
  }
126
+
127
+ /* Downcase up to the first dot and after the last one */
128
+ git__strntolower(name, fdot - name);
129
+ git__strtolower(ldot);
130
+
131
+ *out = name;
132
+ return 0;
108
133
  }
109
134
 
110
- /*
111
- * Compare according to the git rules. Section contains the section as
112
- * it's stored internally. query is the full name as would be given to
113
- * 'git config'.
114
- */
115
- static int cvar_match_section(const char *section, const char *query)
135
+ static void free_vars(git_strmap *values)
116
136
  {
117
- const char *sdot, *qdot, *qsub;
118
- size_t section_len;
137
+ cvar_t *var = NULL;
119
138
 
120
- sdot = strchr(section, '.');
139
+ if (values == NULL)
140
+ return;
121
141
 
122
- /* If the section doesn't have any dots, it's easy */
123
- if (sdot == NULL)
124
- return !strncasecmp(section, query, strlen(section));
142
+ git_strmap_foreach_value(values, var,
143
+ while (var != NULL) {
144
+ cvar_t *next = CVAR_LIST_NEXT(var);
145
+ cvar_free(var);
146
+ var = next;
147
+ });
125
148
 
126
- /*
127
- * If it does have dots, compare the sections
128
- * case-insensitively. The comparison includes the dots.
129
- */
130
- section_len = sdot - section + 1;
131
- if (strncasecmp(section, query, sdot - section))
132
- return 0;
149
+ git_strmap_free(values);
150
+ }
151
+
152
+ static int config_open(git_config_file *cfg)
153
+ {
154
+ int res;
155
+ diskfile_backend *b = (diskfile_backend *)cfg;
156
+
157
+ b->values = git_strmap_alloc();
158
+ GITERR_CHECK_ALLOC(b->values);
159
+
160
+ git_buf_init(&b->reader.buffer, 0);
161
+ res = git_futils_readbuffer(&b->reader.buffer, b->file_path);
133
162
 
134
- qsub = query + section_len;
135
- qdot = strchr(qsub, '.');
136
- /* Make sure the subsections are the same length */
137
- if (strlen(sdot + 1) != (size_t) (qdot - qsub))
163
+ /* It's fine if the file doesn't exist */
164
+ if (res == GIT_ENOTFOUND)
138
165
  return 0;
139
166
 
140
- /* The subsection is case-sensitive */
141
- return !strncmp(sdot + 1, qsub, strlen(sdot + 1));
167
+ if (res < 0 || config_parse(b) < 0) {
168
+ free_vars(b->values);
169
+ b->values = NULL;
170
+ git_buf_free(&b->reader.buffer);
171
+ return -1;
172
+ }
173
+
174
+ git_buf_free(&b->reader.buffer);
175
+ return 0;
142
176
  }
143
177
 
144
- static int cvar_match_name(const cvar_t *var, const char *str)
178
+ static void backend_free(git_config_file *_backend)
145
179
  {
146
- const char *name_start;
180
+ diskfile_backend *backend = (diskfile_backend *)_backend;
147
181
 
148
- if (!cvar_match_section(var->section, str)) {
149
- return 0;
150
- }
151
- /* Early exit if the lengths are different */
152
- name_start = strrchr(str, '.') + 1;
153
- if (strlen(var->name) != strlen(name_start))
154
- return 0;
182
+ if (backend == NULL)
183
+ return;
155
184
 
156
- return !strcasecmp(var->name, name_start);
185
+ git__free(backend->file_path);
186
+ free_vars(backend->values);
187
+ git__free(backend);
157
188
  }
158
189
 
159
- static cvar_t *cvar_list_find(cvar_t_list *list, const char *name)
190
+ static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)
160
191
  {
161
- cvar_t *iter;
192
+ diskfile_backend *b = (diskfile_backend *)backend;
193
+ cvar_t *var;
194
+ const char *key;
162
195
 
163
- CVAR_LIST_FOREACH (list, iter) {
164
- if (cvar_match_name(iter, name))
165
- return iter;
166
- }
196
+ if (!b->values)
197
+ return 0;
198
+
199
+ git_strmap_foreach(b->values, key, var,
200
+ do {
201
+ if (fn(key, var->value, data) < 0)
202
+ break;
167
203
 
168
- return NULL;
204
+ var = CVAR_LIST_NEXT(var);
205
+ } while (var != NULL);
206
+ );
207
+
208
+ return 0;
169
209
  }
170
210
 
171
- static int cvar_normalize_name(cvar_t *var, char **output)
211
+ static int config_set(git_config_file *cfg, const char *name, const char *value)
172
212
  {
173
- char *section_sp = strchr(var->section, ' ');
174
- char *quote, *name;
175
- size_t len;
176
- int ret;
213
+ cvar_t *var = NULL, *old_var;
214
+ diskfile_backend *b = (diskfile_backend *)cfg;
215
+ char *key;
216
+ khiter_t pos;
217
+ int rval;
218
+
219
+ if (normalize_name(name, &key) < 0)
220
+ return -1;
177
221
 
178
222
  /*
179
- * The final string is going to be at most one char longer than
180
- * the input
223
+ * Try to find it in the existing values and update it if it
224
+ * only has one value.
181
225
  */
182
- len = strlen(var->section) + strlen(var->name) + 1;
183
- name = git__malloc(len + 1);
184
- if (name == NULL)
185
- return GIT_ENOMEM;
186
-
187
- /* If there aren't any spaces in the section, it's easy */
188
- if (section_sp == NULL) {
189
- ret = p_snprintf(name, len + 1, "%s.%s", var->section, var->name);
190
- if (ret < 0) {
191
- git__free(name);
192
- return git__throw(GIT_EOSERR, "Failed to normalize name. OS err: %s", strerror(errno));
226
+ pos = git_strmap_lookup_index(b->values, key);
227
+ if (git_strmap_valid_index(b->values, pos)) {
228
+ cvar_t *existing = git_strmap_value_at(b->values, pos);
229
+ char *tmp = NULL;
230
+
231
+ git__free(key);
232
+ if (existing->next != NULL) {
233
+ giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
234
+ return -1;
235
+ }
236
+
237
+ if (value) {
238
+ tmp = git__strdup(value);
239
+ GITERR_CHECK_ALLOC(tmp);
193
240
  }
194
241
 
195
- *output = name;
196
- return GIT_SUCCESS;
242
+ git__free(existing->value);
243
+ existing->value = tmp;
244
+
245
+ return config_write(b, existing->key, NULL, value);
197
246
  }
198
247
 
199
- /*
200
- * If there are spaces, we replace the space by a dot, move
201
- * section name so it overwrites the first quotation mark and
202
- * replace the last quotation mark by a dot. We then append the
203
- * variable name.
204
- */
205
- strcpy(name, var->section);
206
- section_sp = strchr(name, ' ');
207
- *section_sp = '.';
208
- /* Remove first quote */
209
- quote = strchr(name, '"');
210
- memmove(quote, quote+1, strlen(quote+1));
211
- /* Remove second quote */
212
- quote = strchr(name, '"');
213
- *quote = '.';
214
- strcpy(quote+1, var->name);
215
-
216
- *output = name;
217
- return GIT_SUCCESS;
218
- }
248
+ var = git__malloc(sizeof(cvar_t));
249
+ GITERR_CHECK_ALLOC(var);
219
250
 
220
- static char *interiorize_section(const char *orig)
221
- {
222
- char *dot, *last_dot, *section, *ret;
223
- size_t len;
251
+ memset(var, 0x0, sizeof(cvar_t));
224
252
 
225
- dot = strchr(orig, '.');
226
- last_dot = strrchr(orig, '.');
227
- len = last_dot - orig;
253
+ var->key = key;
254
+ var->value = NULL;
228
255
 
229
- /* No subsection, this is easy */
230
- if (last_dot == dot)
231
- return git__strndup(orig, dot - orig);
256
+ if (value) {
257
+ var->value = git__strdup(value);
258
+ GITERR_CHECK_ALLOC(var->value);
259
+ }
232
260
 
233
- section = git__malloc(len + 4);
234
- if (section == NULL)
235
- return NULL;
261
+ if (config_write(b, key, NULL, value) < 0) {
262
+ cvar_free(var);
263
+ return -1;
264
+ }
236
265
 
237
- memset(section, 0x0, len + 4);
238
- ret = section;
239
- len = dot - orig;
240
- memcpy(section, orig, len);
241
- section += len;
242
- len = strlen(" \"");
243
- memcpy(section, " \"", len);
244
- section += len;
245
- len = last_dot - dot - 1;
246
- memcpy(section, dot + 1, len);
247
- section += len;
248
- *section = '"';
266
+ git_strmap_insert2(b->values, key, var, old_var, rval);
267
+ if (rval < 0)
268
+ return -1;
269
+ if (old_var != NULL)
270
+ cvar_free(old_var);
249
271
 
250
- return ret;
272
+ return 0;
251
273
  }
252
274
 
253
- static int config_open(git_config_file *cfg)
275
+ /*
276
+ * Internal function that actually gets the value in string form
277
+ */
278
+ static int config_get(git_config_file *cfg, const char *name, const char **out)
254
279
  {
255
- int error;
256
280
  diskfile_backend *b = (diskfile_backend *)cfg;
281
+ char *key;
282
+ khiter_t pos;
257
283
 
258
- error = git_futils_readbuffer(&b->reader.buffer, b->file_path);
259
-
260
- /* It's fine if the file doesn't exist */
261
- if (error == GIT_ENOTFOUND)
262
- return GIT_SUCCESS;
263
-
264
- if (error < GIT_SUCCESS)
265
- goto cleanup;
284
+ if (normalize_name(name, &key) < 0)
285
+ return -1;
266
286
 
267
- error = config_parse(b);
268
- if (error < GIT_SUCCESS)
269
- goto cleanup;
287
+ pos = git_strmap_lookup_index(b->values, key);
288
+ git__free(key);
270
289
 
271
- git_futils_freebuffer(&b->reader.buffer);
290
+ /* no error message; the config system will write one */
291
+ if (!git_strmap_valid_index(b->values, pos))
292
+ return GIT_ENOTFOUND;
272
293
 
273
- return GIT_SUCCESS;
294
+ *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value;
274
295
 
275
- cleanup:
276
- cvar_list_free(&b->var_list);
277
- git_futils_freebuffer(&b->reader.buffer);
278
-
279
- return git__rethrow(error, "Failed to open config");
296
+ return 0;
280
297
  }
281
298
 
282
- static void backend_free(git_config_file *_backend)
299
+ static int config_get_multivar(
300
+ git_config_file *cfg,
301
+ const char *name,
302
+ const char *regex_str,
303
+ int (*fn)(const char *, void *),
304
+ void *data)
283
305
  {
284
- diskfile_backend *backend = (diskfile_backend *)_backend;
306
+ cvar_t *var;
307
+ diskfile_backend *b = (diskfile_backend *)cfg;
308
+ char *key;
309
+ khiter_t pos;
285
310
 
286
- if (backend == NULL)
287
- return;
311
+ if (normalize_name(name, &key) < 0)
312
+ return -1;
288
313
 
289
- git__free(backend->file_path);
290
- cvar_list_free(&backend->var_list);
314
+ pos = git_strmap_lookup_index(b->values, key);
315
+ git__free(key);
291
316
 
292
- git__free(backend);
293
- }
317
+ if (!git_strmap_valid_index(b->values, pos))
318
+ return GIT_ENOTFOUND;
294
319
 
295
- static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)
296
- {
297
- int ret = GIT_SUCCESS;
298
- cvar_t *var;
299
- diskfile_backend *b = (diskfile_backend *)backend;
320
+ var = git_strmap_value_at(b->values, pos);
300
321
 
301
- CVAR_LIST_FOREACH(&b->var_list, var) {
302
- char *normalized = NULL;
322
+ if (regex_str != NULL) {
323
+ regex_t regex;
324
+ int result;
303
325
 
304
- ret = cvar_normalize_name(var, &normalized);
305
- if (ret < GIT_SUCCESS)
306
- return ret;
326
+ /* regex matching; build the regex */
327
+ result = regcomp(&regex, regex_str, REG_EXTENDED);
328
+ if (result < 0) {
329
+ giterr_set_regex(&regex, result);
330
+ return -1;
331
+ }
307
332
 
308
- ret = fn(normalized, var->value, data);
309
- git__free(normalized);
310
- if (ret)
311
- break;
333
+ /* and throw the callback only on the variables that
334
+ * match the regex */
335
+ do {
336
+ if (regexec(&regex, var->value, 0, NULL, 0) == 0) {
337
+ /* early termination by the user is not an error;
338
+ * just break and return successfully */
339
+ if (fn(var->value, data) < 0)
340
+ break;
341
+ }
342
+
343
+ var = var->next;
344
+ } while (var != NULL);
345
+ regfree(&regex);
346
+ } else {
347
+ /* no regex; go through all the variables */
348
+ do {
349
+ /* early termination by the user is not an error;
350
+ * just break and return successfully */
351
+ if (fn(var->value, data) < 0)
352
+ break;
353
+
354
+ var = var->next;
355
+ } while (var != NULL);
312
356
  }
313
357
 
314
- return ret;
358
+ return 0;
315
359
  }
316
360
 
317
- static int config_set(git_config_file *cfg, const char *name, const char *value)
361
+ static int config_set_multivar(
362
+ git_config_file *cfg, const char *name, const char *regexp, const char *value)
318
363
  {
319
- cvar_t *var = NULL;
320
- cvar_t *existing = NULL;
321
- int error = GIT_SUCCESS;
322
- const char *last_dot;
364
+ int replaced = 0;
365
+ cvar_t *var, *newvar;
323
366
  diskfile_backend *b = (diskfile_backend *)cfg;
367
+ char *key;
368
+ regex_t preg;
369
+ int result;
370
+ khiter_t pos;
324
371
 
325
- /*
326
- * If it already exists, we just need to update its value.
327
- */
328
- existing = cvar_list_find(&b->var_list, name);
329
- if (existing != NULL) {
330
- char *tmp = value ? git__strdup(value) : NULL;
331
- if (tmp == NULL && value != NULL)
332
- return GIT_ENOMEM;
372
+ assert(regexp);
333
373
 
334
- git__free(existing->value);
335
- existing->value = tmp;
374
+ if (normalize_name(name, &key) < 0)
375
+ return -1;
336
376
 
337
- return config_write(b, existing);
377
+ pos = git_strmap_lookup_index(b->values, key);
378
+ if (!git_strmap_valid_index(b->values, pos)) {
379
+ git__free(key);
380
+ return GIT_ENOTFOUND;
338
381
  }
339
382
 
340
- /*
341
- * Otherwise, create it and stick it at the end of the queue. If
342
- * value is NULL, we return an error, because you can't delete a
343
- * variable that doesn't exist.
344
- */
345
-
346
- if (value == NULL)
347
- return git__throw(GIT_ENOTFOUND, "Can't delete non-exitent variable");
383
+ var = git_strmap_value_at(b->values, pos);
348
384
 
349
- last_dot = strrchr(name, '.');
350
- if (last_dot == NULL) {
351
- return git__throw(GIT_EINVALIDTYPE, "Variables without section aren't allowed");
385
+ result = regcomp(&preg, regexp, REG_EXTENDED);
386
+ if (result < 0) {
387
+ git__free(key);
388
+ giterr_set_regex(&preg, result);
389
+ return -1;
352
390
  }
353
391
 
354
- var = git__malloc(sizeof(cvar_t));
355
- if (var == NULL)
356
- return GIT_ENOMEM;
392
+ for (;;) {
393
+ if (regexec(&preg, var->value, 0, NULL, 0) == 0) {
394
+ char *tmp = git__strdup(value);
395
+ GITERR_CHECK_ALLOC(tmp);
357
396
 
358
- memset(var, 0x0, sizeof(cvar_t));
397
+ git__free(var->value);
398
+ var->value = tmp;
399
+ replaced = 1;
400
+ }
359
401
 
360
- var->section = interiorize_section(name);
361
- if (var->section == NULL) {
362
- error = GIT_ENOMEM;
363
- goto out;
364
- }
402
+ if (var->next == NULL)
403
+ break;
365
404
 
366
- var->name = git__strdup(last_dot + 1);
367
- if (var->name == NULL) {
368
- error = GIT_ENOMEM;
369
- goto out;
405
+ var = var->next;
370
406
  }
371
407
 
372
- var->value = value ? git__strdup(value) : NULL;
373
- if (var->value == NULL && value != NULL) {
374
- error = GIT_ENOMEM;
375
- goto out;
408
+ /* If we've reached the end of the variables and we haven't found it yet, we need to append it */
409
+ if (!replaced) {
410
+ newvar = git__malloc(sizeof(cvar_t));
411
+ GITERR_CHECK_ALLOC(newvar);
412
+
413
+ memset(newvar, 0x0, sizeof(cvar_t));
414
+
415
+ newvar->key = git__strdup(var->key);
416
+ GITERR_CHECK_ALLOC(newvar->key);
417
+
418
+ newvar->value = git__strdup(value);
419
+ GITERR_CHECK_ALLOC(newvar->value);
420
+
421
+ var->next = newvar;
376
422
  }
377
423
 
378
- CVAR_LIST_APPEND(&b->var_list, var);
379
- error = config_write(b, var);
424
+ result = config_write(b, key, &preg, value);
380
425
 
381
- out:
382
- if (error < GIT_SUCCESS)
383
- cvar_free(var);
426
+ git__free(key);
427
+ regfree(&preg);
384
428
 
385
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set config value");
429
+ return result;
386
430
  }
387
431
 
388
- /*
389
- * Internal function that actually gets the value in string form
390
- */
391
- static int config_get(git_config_file *cfg, const char *name, const char **out)
432
+ static int config_delete(git_config_file *cfg, const char *name)
392
433
  {
393
434
  cvar_t *var;
394
- int error = GIT_SUCCESS;
395
435
  diskfile_backend *b = (diskfile_backend *)cfg;
436
+ char *key;
437
+ int result;
438
+ khiter_t pos;
396
439
 
397
- var = cvar_list_find(&b->var_list, name);
440
+ if (normalize_name(name, &key) < 0)
441
+ return -1;
398
442
 
399
- if (var == NULL)
400
- return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
443
+ pos = git_strmap_lookup_index(b->values, key);
444
+ git__free(key);
401
445
 
402
- *out = var->value;
446
+ if (!git_strmap_valid_index(b->values, pos)) {
447
+ giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
448
+ return GIT_ENOTFOUND;
449
+ }
450
+
451
+ var = git_strmap_value_at(b->values, pos);
452
+
453
+ if (var->next != NULL) {
454
+ giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
455
+ return -1;
456
+ }
403
457
 
404
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to get config value for %s", name);
458
+ git_strmap_delete_at(b->values, pos);
459
+
460
+ result = config_write(b, var->key, NULL, NULL);
461
+
462
+ cvar_free(var);
463
+ return result;
405
464
  }
406
465
 
407
466
  int git_config_file__ondisk(git_config_file **out, const char *path)
@@ -409,26 +468,25 @@ int git_config_file__ondisk(git_config_file **out, const char *path)
409
468
  diskfile_backend *backend;
410
469
 
411
470
  backend = git__malloc(sizeof(diskfile_backend));
412
- if (backend == NULL)
413
- return GIT_ENOMEM;
471
+ GITERR_CHECK_ALLOC(backend);
414
472
 
415
473
  memset(backend, 0x0, sizeof(diskfile_backend));
416
474
 
417
475
  backend->file_path = git__strdup(path);
418
- if (backend->file_path == NULL) {
419
- git__free(backend);
420
- return GIT_ENOMEM;
421
- }
476
+ GITERR_CHECK_ALLOC(backend->file_path);
422
477
 
423
478
  backend->parent.open = config_open;
424
479
  backend->parent.get = config_get;
480
+ backend->parent.get_multivar = config_get_multivar;
425
481
  backend->parent.set = config_set;
482
+ backend->parent.set_multivar = config_set_multivar;
483
+ backend->parent.del = config_delete;
426
484
  backend->parent.foreach = file_foreach;
427
485
  backend->parent.free = backend_free;
428
486
 
429
487
  *out = (git_config_file *)backend;
430
488
 
431
- return GIT_SUCCESS;
489
+ return 0;
432
490
  }
433
491
 
434
492
  static int cfg_getchar_raw(diskfile_backend *cfg)
@@ -469,7 +527,8 @@ static int cfg_getchar(diskfile_backend *cfg_file, int flags)
469
527
  assert(cfg_file->reader.read_ptr);
470
528
 
471
529
  do c = cfg_getchar_raw(cfg_file);
472
- while (skip_whitespace && isspace(c));
530
+ while (skip_whitespace && git__isspace(c) &&
531
+ !cfg_file->reader.eof);
473
532
 
474
533
  if (skip_comments && (c == '#' || c == ';')) {
475
534
  do c = cfg_getchar_raw(cfg_file);
@@ -506,7 +565,7 @@ static int cfg_peek(diskfile_backend *cfg, int flags)
506
565
  /*
507
566
  * Read and consume a line, returning it in newly-allocated memory.
508
567
  */
509
- static char *cfg_readline(diskfile_backend *cfg)
568
+ static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
510
569
  {
511
570
  char *line = NULL;
512
571
  char *line_src, *line_end;
@@ -514,9 +573,11 @@ static char *cfg_readline(diskfile_backend *cfg)
514
573
 
515
574
  line_src = cfg->reader.read_ptr;
516
575
 
517
- /* Skip empty empty lines */
518
- while (isspace(*line_src))
519
- ++line_src;
576
+ if (skip_whitespace) {
577
+ /* Skip empty empty lines */
578
+ while (git__isspace(*line_src))
579
+ ++line_src;
580
+ }
520
581
 
521
582
  line_end = strchr(line_src, '\n');
522
583
 
@@ -533,7 +594,7 @@ static char *cfg_readline(diskfile_backend *cfg)
533
594
  memcpy(line, line_src, line_len);
534
595
 
535
596
  do line[line_len] = '\0';
536
- while (line_len-- > 0 && isspace(line[line_len]));
597
+ while (line_len-- > 0 && git__isspace(line[line_len]));
537
598
 
538
599
  if (*line_end == '\n')
539
600
  line_end++;
@@ -576,12 +637,11 @@ GIT_INLINE(int) config_keychar(int c)
576
637
  return isalnum(c) || c == '-';
577
638
  }
578
639
 
579
- static int parse_section_header_ext(const char *line, const char *base_name, char **section_name)
640
+ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
580
641
  {
581
642
  int c, rpos;
582
643
  char *first_quote, *last_quote;
583
644
  git_buf buf = GIT_BUF_INIT;
584
- int error = GIT_SUCCESS;
585
645
  int quote_marks;
586
646
  /*
587
647
  * base_name is what came before the space. We should be at the
@@ -592,8 +652,10 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
592
652
  first_quote = strchr(line, '"');
593
653
  last_quote = strrchr(line, '"');
594
654
 
595
- if (last_quote - first_quote == 0)
596
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. There is no final quotation mark");
655
+ if (last_quote - first_quote == 0) {
656
+ set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
657
+ return -1;
658
+ }
597
659
 
598
660
  git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
599
661
  git_buf_printf(&buf, "%s.", base_name);
@@ -610,26 +672,30 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
610
672
  */
611
673
  do {
612
674
  if (quote_marks == 2) {
613
- error = git__throw(GIT_EOBJCORRUPTED, "Falied to parse ext header. Text after closing quote");
614
- goto out;
615
-
675
+ set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
676
+ git_buf_free(&buf);
677
+ return -1;
616
678
  }
617
679
 
618
680
  switch (c) {
619
681
  case '"':
620
682
  ++quote_marks;
621
683
  continue;
684
+
622
685
  case '\\':
623
686
  c = line[rpos++];
687
+
624
688
  switch (c) {
625
689
  case '"':
626
690
  case '\\':
627
691
  break;
692
+
628
693
  default:
629
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c);
630
- goto out;
694
+ set_parse_error(cfg, rpos, "Unsupported escape sequence");
695
+ git_buf_free(&buf);
696
+ return -1;
631
697
  }
632
- break;
698
+
633
699
  default:
634
700
  break;
635
701
  }
@@ -637,61 +703,53 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
637
703
  git_buf_putc(&buf, c);
638
704
  } while ((c = line[rpos++]) != ']');
639
705
 
640
- *section_name = git__strdup(git_buf_cstr(&buf));
641
- out:
642
- git_buf_free(&buf);
643
-
644
- return error;
706
+ *section_name = git_buf_detach(&buf);
707
+ return 0;
645
708
  }
646
709
 
647
710
  static int parse_section_header(diskfile_backend *cfg, char **section_out)
648
711
  {
649
712
  char *name, *name_end;
650
713
  int name_length, c, pos;
651
- int error = GIT_SUCCESS;
714
+ int result;
652
715
  char *line;
653
716
 
654
- line = cfg_readline(cfg);
717
+ line = cfg_readline(cfg, true);
655
718
  if (line == NULL)
656
- return GIT_ENOMEM;
719
+ return -1;
657
720
 
658
721
  /* find the end of the variable's name */
659
722
  name_end = strchr(line, ']');
660
723
  if (name_end == NULL) {
661
724
  git__free(line);
662
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Can't find header name end");
725
+ set_parse_error(cfg, 0, "Missing ']' in section header");
726
+ return -1;
663
727
  }
664
728
 
665
729
  name = (char *)git__malloc((size_t)(name_end - line) + 1);
666
- if (name == NULL) {
667
- git__free(line);
668
- return GIT_ENOMEM;
669
- }
730
+ GITERR_CHECK_ALLOC(name);
670
731
 
671
732
  name_length = 0;
672
733
  pos = 0;
673
734
 
674
735
  /* Make sure we were given a section header */
675
736
  c = line[pos++];
676
- if (c != '[') {
677
- error = git__throw(GIT_ERROR, "Failed to parse header. Didn't get section header. This is a bug");
678
- goto error;
679
- }
737
+ assert(c == '[');
680
738
 
681
739
  c = line[pos++];
682
740
 
683
741
  do {
684
- if (isspace(c)){
742
+ if (git__isspace(c)){
685
743
  name[name_length] = '\0';
686
- error = parse_section_header_ext(line, name, section_out);
744
+ result = parse_section_header_ext(cfg, line, name, section_out);
687
745
  git__free(line);
688
746
  git__free(name);
689
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse header");
747
+ return result;
690
748
  }
691
749
 
692
750
  if (!config_keychar(c) && c != '.') {
693
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Wrong format on header");
694
- goto error;
751
+ set_parse_error(cfg, pos, "Unexpected character in header");
752
+ goto fail_parse;
695
753
  }
696
754
 
697
755
  name[name_length++] = (char) tolower(c);
@@ -699,28 +757,29 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out)
699
757
  } while ((c = line[pos++]) != ']');
700
758
 
701
759
  if (line[pos - 1] != ']') {
702
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Config file ended unexpectedly");
703
- goto error;
760
+ set_parse_error(cfg, pos, "Unexpected end of file");
761
+ goto fail_parse;
704
762
  }
705
763
 
706
- name[name_length] = 0;
707
764
  git__free(line);
708
- git__strtolower(name);
765
+
766
+ name[name_length] = 0;
709
767
  *section_out = name;
710
- return GIT_SUCCESS;
711
768
 
712
- error:
769
+ return 0;
770
+
771
+ fail_parse:
713
772
  git__free(line);
714
773
  git__free(name);
715
- return error;
774
+ return -1;
716
775
  }
717
776
 
718
777
  static int skip_bom(diskfile_backend *cfg)
719
778
  {
720
- static const char *utf8_bom = "\xef\xbb\xbf";
779
+ static const char utf8_bom[] = "\xef\xbb\xbf";
721
780
 
722
- if (cfg->reader.buffer.len < sizeof(utf8_bom))
723
- return GIT_SUCCESS;
781
+ if (cfg->reader.buffer.size < sizeof(utf8_bom))
782
+ return 0;
724
783
 
725
784
  if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
726
785
  cfg->reader.read_ptr += sizeof(utf8_bom);
@@ -729,7 +788,7 @@ static int skip_bom(diskfile_backend *cfg)
729
788
  shit with the BoM
730
789
  */
731
790
 
732
- return GIT_SUCCESS;
791
+ return 0;
733
792
  }
734
793
 
735
794
  /*
@@ -771,9 +830,9 @@ static int skip_bom(diskfile_backend *cfg)
771
830
  boolean_false = "no" | "0" | "false" | "off"
772
831
  */
773
832
 
774
- static void strip_comments(char *line)
833
+ static int strip_comments(char *line, int in_quotes)
775
834
  {
776
- int quote_count = 0;
835
+ int quote_count = in_quotes;
777
836
  char *ptr;
778
837
 
779
838
  for (ptr = line; *ptr; ++ptr) {
@@ -786,41 +845,49 @@ static void strip_comments(char *line)
786
845
  }
787
846
  }
788
847
 
789
- if (isspace(ptr[-1])) {
790
- /* TODO skip whitespace */
848
+ /* skip any space at the end */
849
+ if (git__isspace(ptr[-1])) {
850
+ ptr--;
791
851
  }
852
+ ptr[0] = '\0';
853
+
854
+ return quote_count;
792
855
  }
793
856
 
794
857
  static int config_parse(diskfile_backend *cfg_file)
795
858
  {
796
- int error = GIT_SUCCESS, c;
859
+ int c;
797
860
  char *current_section = NULL;
798
861
  char *var_name;
799
862
  char *var_value;
800
- cvar_t *var;
863
+ cvar_t *var, *existing;
864
+ git_buf buf = GIT_BUF_INIT;
865
+ int result = 0;
866
+ khiter_t pos;
801
867
 
802
868
  /* Initialize the reading position */
803
- cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
869
+ cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
804
870
  cfg_file->reader.eof = 0;
805
871
 
806
872
  /* If the file is empty, there's nothing for us to do */
807
873
  if (*cfg_file->reader.read_ptr == '\0')
808
- return GIT_SUCCESS;
874
+ return 0;
809
875
 
810
876
  skip_bom(cfg_file);
811
877
 
812
- while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
878
+ while (result == 0 && !cfg_file->reader.eof) {
813
879
 
814
880
  c = cfg_peek(cfg_file, SKIP_WHITESPACE);
815
881
 
816
882
  switch (c) {
817
- case '\0': /* We've arrived at the end of the file */
883
+ case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
884
+ cfg_file->reader.eof = 1;
818
885
  break;
819
886
 
820
887
  case '[': /* section header, new section begins */
821
888
  git__free(current_section);
822
889
  current_section = NULL;
823
- error = parse_section_header(cfg_file, &current_section);
890
+ result = parse_section_header(cfg_file, &current_section);
824
891
  break;
825
892
 
826
893
  case ';':
@@ -829,100 +896,119 @@ static int config_parse(diskfile_backend *cfg_file)
829
896
  break;
830
897
 
831
898
  default: /* assume variable declaration */
832
- error = parse_variable(cfg_file, &var_name, &var_value);
833
-
834
- if (error < GIT_SUCCESS)
899
+ result = parse_variable(cfg_file, &var_name, &var_value);
900
+ if (result < 0)
835
901
  break;
836
902
 
837
903
  var = git__malloc(sizeof(cvar_t));
838
- if (var == NULL) {
839
- error = GIT_ENOMEM;
840
- break;
841
- }
904
+ GITERR_CHECK_ALLOC(var);
842
905
 
843
906
  memset(var, 0x0, sizeof(cvar_t));
844
907
 
845
- var->section = git__strdup(current_section);
846
- if (var->section == NULL) {
847
- error = GIT_ENOMEM;
848
- git__free(var);
849
- break;
850
- }
908
+ git__strtolower(var_name);
909
+ git_buf_printf(&buf, "%s.%s", current_section, var_name);
910
+ git__free(var_name);
851
911
 
852
- var->name = var_name;
912
+ if (git_buf_oom(&buf))
913
+ return -1;
914
+
915
+ var->key = git_buf_detach(&buf);
853
916
  var->value = var_value;
854
- git__strtolower(var->name);
855
917
 
856
- CVAR_LIST_APPEND(&cfg_file->var_list, var);
918
+ /* Add or append the new config option */
919
+ pos = git_strmap_lookup_index(cfg_file->values, var->key);
920
+ if (!git_strmap_valid_index(cfg_file->values, pos)) {
921
+ git_strmap_insert(cfg_file->values, var->key, var, result);
922
+ if (result < 0)
923
+ break;
924
+ result = 0;
925
+ } else {
926
+ existing = git_strmap_value_at(cfg_file->values, pos);
927
+ while (existing->next != NULL) {
928
+ existing = existing->next;
929
+ }
930
+ existing->next = var;
931
+ }
857
932
 
858
933
  break;
859
934
  }
860
935
  }
861
936
 
862
937
  git__free(current_section);
863
-
864
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config");
938
+ return result;
865
939
  }
866
940
 
867
- static int write_section(git_filebuf *file, cvar_t *var)
941
+ static int write_section(git_filebuf *file, const char *key)
868
942
  {
869
- int error;
943
+ int result;
944
+ const char *dot;
945
+ git_buf buf = GIT_BUF_INIT;
946
+
947
+ /* All of this just for [section "subsection"] */
948
+ dot = strchr(key, '.');
949
+ git_buf_putc(&buf, '[');
950
+ if (dot == NULL) {
951
+ git_buf_puts(&buf, key);
952
+ } else {
953
+ git_buf_put(&buf, key, dot - key);
954
+ /* TODO: escape */
955
+ git_buf_printf(&buf, " \"%s\"", dot + 1);
956
+ }
957
+ git_buf_puts(&buf, "]\n");
958
+
959
+ if (git_buf_oom(&buf))
960
+ return -1;
870
961
 
871
- error = git_filebuf_printf(file, "[%s]\n", var->section);
872
- if (error < GIT_SUCCESS)
873
- return error;
962
+ result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
963
+ git_buf_free(&buf);
874
964
 
875
- error = git_filebuf_printf(file, " %s = %s\n", var->name, var->value);
876
- return error;
965
+ return result;
877
966
  }
878
967
 
879
968
  /*
880
969
  * This is pretty much the parsing, except we write out anything we don't have
881
970
  */
882
- static int config_write(diskfile_backend *cfg, cvar_t *var)
971
+ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
883
972
  {
884
- int error = GIT_SUCCESS, c;
885
- int section_matches = 0, last_section_matched = 0;
886
- char *current_section = NULL;
887
- char *var_name, *var_value, *data_start;
973
+ int result, c;
974
+ int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
975
+ const char *pre_end = NULL, *post_start = NULL, *data_start;
976
+ char *current_section = NULL, *section, *name, *ldot;
888
977
  git_filebuf file = GIT_FILEBUF_INIT;
889
- const char *pre_end = NULL, *post_start = NULL;
890
978
 
891
979
  /* We need to read in our own config file */
892
- error = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
893
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND) {
894
- return git__rethrow(error, "Failed to read existing config file %s", cfg->file_path);
895
- }
980
+ result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
896
981
 
897
982
  /* Initialise the reading position */
898
- if (error == GIT_ENOTFOUND) {
899
- error = GIT_SUCCESS;
983
+ if (result == GIT_ENOTFOUND) {
900
984
  cfg->reader.read_ptr = NULL;
901
985
  cfg->reader.eof = 1;
902
986
  data_start = NULL;
903
- cfg->reader.buffer.len = 0;
904
- cfg->reader.buffer.data = NULL;
905
- } else {
906
- cfg->reader.read_ptr = cfg->reader.buffer.data;
987
+ git_buf_clear(&cfg->reader.buffer);
988
+ } else if (result == 0) {
989
+ cfg->reader.read_ptr = cfg->reader.buffer.ptr;
907
990
  cfg->reader.eof = 0;
908
991
  data_start = cfg->reader.read_ptr;
992
+ } else {
993
+ return -1; /* OS error when reading the file */
909
994
  }
910
995
 
911
996
  /* Lock the file */
912
- error = git_filebuf_open(&file, cfg->file_path, 0);
913
- if (error < GIT_SUCCESS)
914
- return git__rethrow(error, "Failed to lock config file");
997
+ if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
998
+ return -1;
915
999
 
916
1000
  skip_bom(cfg);
1001
+ ldot = strrchr(key, '.');
1002
+ name = ldot + 1;
1003
+ section = git__strndup(key, ldot - key);
917
1004
 
918
- while (error == GIT_SUCCESS && !cfg->reader.eof) {
1005
+ while (!cfg->reader.eof) {
919
1006
  c = cfg_peek(cfg, SKIP_WHITESPACE);
920
1007
 
921
- switch (c) {
922
- case '\0': /* We've arrived at the end of the file */
1008
+ if (c == '\0') { /* We've arrived at the end of the file */
923
1009
  break;
924
1010
 
925
- case '[': /* section header, new section begins */
1011
+ } else if (c == '[') { /* section header, new section begins */
926
1012
  /*
927
1013
  * We set both positions to the current one in case we
928
1014
  * need to add a variable to the end of a section. In that
@@ -931,23 +1017,22 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
931
1017
  * default case will take care of updating them.
932
1018
  */
933
1019
  pre_end = post_start = cfg->reader.read_ptr;
934
- if (current_section)
935
- git__free(current_section);
936
- error = parse_section_header(cfg, &current_section);
937
- if (error < GIT_SUCCESS)
938
- break;
1020
+
1021
+ git__free(current_section);
1022
+ current_section = NULL;
1023
+ if (parse_section_header(cfg, &current_section) < 0)
1024
+ goto rewrite_fail;
939
1025
 
940
1026
  /* Keep track of when it stops matching */
941
1027
  last_section_matched = section_matches;
942
- section_matches = !strcmp(current_section, var->section);
943
- break;
1028
+ section_matches = !strcmp(current_section, section);
1029
+ }
944
1030
 
945
- case ';':
946
- case '#':
1031
+ else if (c == ';' || c == '#') {
947
1032
  cfg_consume_line(cfg);
948
- break;
1033
+ }
949
1034
 
950
- default:
1035
+ else {
951
1036
  /*
952
1037
  * If the section doesn't match, but the last section did,
953
1038
  * it means we need to add a variable (so skip the line
@@ -961,58 +1046,54 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
961
1046
  if (!section_matches) {
962
1047
  if (!last_section_matched) {
963
1048
  cfg_consume_line(cfg);
964
- break;
1049
+ continue;
965
1050
  }
966
1051
  } else {
967
- int cmp = -1;
1052
+ int has_matched = 0;
1053
+ char *var_name, *var_value;
968
1054
 
969
1055
  pre_end = cfg->reader.read_ptr;
970
- if ((error = parse_variable(cfg, &var_name, &var_value)) == GIT_SUCCESS)
971
- cmp = strcasecmp(var->name, var_name);
1056
+ if (parse_variable(cfg, &var_name, &var_value) < 0)
1057
+ goto rewrite_fail;
1058
+
1059
+ /* First try to match the name of the variable */
1060
+ if (strcasecmp(name, var_name) == 0)
1061
+ has_matched = 1;
1062
+
1063
+ /* If the name matches, and we have a regex to match the
1064
+ * value, try to match it */
1065
+ if (has_matched && preg != NULL)
1066
+ has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
972
1067
 
973
1068
  git__free(var_name);
974
1069
  git__free(var_value);
975
1070
 
976
- if (cmp != 0)
977
- break;
1071
+ /* if there is no match, keep going */
1072
+ if (!has_matched)
1073
+ continue;
978
1074
 
979
1075
  post_start = cfg->reader.read_ptr;
980
1076
  }
981
1077
 
982
- /*
983
- * We've found the variable we wanted to change, so
984
- * write anything up to it
985
- */
986
- error = git_filebuf_write(&file, data_start, pre_end - data_start);
987
- if (error < GIT_SUCCESS) {
988
- git__rethrow(error, "Failed to write the first part of the file");
989
- break;
990
- }
1078
+ /* We've found the variable we wanted to change, so
1079
+ * write anything up to it */
1080
+ git_filebuf_write(&file, data_start, pre_end - data_start);
1081
+ preg_replaced = 1;
991
1082
 
992
- /*
993
- * Then replace the variable. If the value is NULL, it
994
- * means we want to delete it, so pretend everything went
995
- * fine
996
- */
997
- if (var->value == NULL)
998
- error = GIT_SUCCESS;
999
- else
1000
- error = git_filebuf_printf(&file, "\t%s = %s\n", var->name, var->value);
1001
- if (error < GIT_SUCCESS) {
1002
- git__rethrow(error, "Failed to overwrite the variable");
1003
- break;
1083
+ /* Then replace the variable. If the value is NULL, it
1084
+ * means we want to delete it, so don't write anything. */
1085
+ if (value != NULL) {
1086
+ git_filebuf_printf(&file, "\t%s = %s\n", name, value);
1004
1087
  }
1005
1088
 
1006
- /* And then the write out rest of the file */
1007
- error = git_filebuf_write(&file, post_start,
1008
- cfg->reader.buffer.len - (post_start - data_start));
1009
-
1010
- if (error < GIT_SUCCESS) {
1011
- git__rethrow(error, "Failed to write the rest of the file");
1012
- break;
1089
+ /* multiline variable? we need to keep reading lines to match */
1090
+ if (preg != NULL) {
1091
+ data_start = post_start;
1092
+ continue;
1013
1093
  }
1014
1094
 
1015
- goto cleanup;
1095
+ write_trailer = 1;
1096
+ break; /* break from the loop */
1016
1097
  }
1017
1098
  }
1018
1099
 
@@ -1025,127 +1106,166 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
1025
1106
  * 2) we didn't find a section for us so we need to create it
1026
1107
  * ourselves.
1027
1108
  *
1028
- * Either way we need to write out the whole file.
1109
+ * 3) we're setting a multivar with a regex, which means we
1110
+ * continue to search for matching values
1111
+ *
1112
+ * In the last case, if we've already replaced a value, we
1113
+ * want to write the rest of the file. Otherwise we need to write
1114
+ * out the whole file and then the new variable.
1029
1115
  */
1116
+ if (write_trailer) {
1117
+ /* Write out rest of the file */
1118
+ git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
1119
+ } else {
1120
+ if (preg_replaced) {
1121
+ git_filebuf_printf(&file, "\n%s", data_start);
1122
+ } else {
1123
+ git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
1124
+
1125
+ /* And now if we just need to add a variable */
1126
+ if (!section_matches && write_section(&file, section) < 0)
1127
+ goto rewrite_fail;
1128
+
1129
+ /* Sanity check: if we are here, and value is NULL, that means that somebody
1130
+ * touched the config file after our intial read. We should probably assert()
1131
+ * this, but instead we'll handle it gracefully with an error. */
1132
+ if (value == NULL) {
1133
+ giterr_set(GITERR_CONFIG,
1134
+ "Race condition when writing a config file (a cvar has been removed)");
1135
+ goto rewrite_fail;
1136
+ }
1030
1137
 
1031
- error = git_filebuf_write(&file, cfg->reader.buffer.data, cfg->reader.buffer.len);
1032
- if (error < GIT_SUCCESS) {
1033
- git__rethrow(error, "Failed to write original config content");
1034
- goto cleanup;
1138
+ git_filebuf_printf(&file, "\t%s = %s\n", name, value);
1139
+ }
1035
1140
  }
1036
1141
 
1037
- /* And now if we just need to add a variable */
1038
- if (section_matches) {
1039
- error = git_filebuf_printf(&file, "\t%s = %s\n", var->name, var->value);
1040
- goto cleanup;
1041
- }
1142
+ git__free(section);
1143
+ git__free(current_section);
1042
1144
 
1043
- /* Or maybe we need to write out a whole section */
1044
- error = write_section(&file, var);
1045
- if (error < GIT_SUCCESS)
1046
- git__rethrow(error, "Failed to write new section");
1145
+ result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
1146
+ git_buf_free(&cfg->reader.buffer);
1147
+ return result;
1047
1148
 
1048
- cleanup:
1149
+ rewrite_fail:
1150
+ git__free(section);
1049
1151
  git__free(current_section);
1050
1152
 
1051
- if (error < GIT_SUCCESS)
1052
- git_filebuf_cleanup(&file);
1053
- else
1054
- error = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
1055
-
1056
- git_futils_freebuffer(&cfg->reader.buffer);
1057
- return error;
1153
+ git_filebuf_cleanup(&file);
1154
+ git_buf_free(&cfg->reader.buffer);
1155
+ return -1;
1058
1156
  }
1059
1157
 
1060
- static int is_multiline_var(const char *str)
1158
+ /* '\"' -> '"' etc */
1159
+ static char *fixup_line(const char *ptr, int quote_count)
1061
1160
  {
1062
- char *end = strrchr(str, '\0') - 1;
1161
+ char *str = git__malloc(strlen(ptr) + 1);
1162
+ char *out = str, *esc;
1163
+ const char *escapes = "ntb\"\\";
1164
+ const char *escaped = "\n\t\b\"\\";
1165
+
1166
+ if (str == NULL)
1167
+ return NULL;
1168
+
1169
+ while (*ptr != '\0') {
1170
+ if (*ptr == '"') {
1171
+ quote_count++;
1172
+ } else if (*ptr != '\\') {
1173
+ *out++ = *ptr;
1174
+ } else {
1175
+ /* backslash, check the next char */
1176
+ ptr++;
1177
+ /* if we're at the end, it's a multiline, so keep the backslash */
1178
+ if (*ptr == '\0') {
1179
+ *out++ = '\\';
1180
+ goto out;
1181
+ }
1182
+ if ((esc = strchr(escapes, *ptr)) != NULL) {
1183
+ *out++ = escaped[esc - escapes];
1184
+ } else {
1185
+ git__free(str);
1186
+ giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
1187
+ return NULL;
1188
+ }
1189
+ }
1190
+ ptr++;
1191
+ }
1063
1192
 
1064
- while (isspace(*end))
1065
- --end;
1193
+ out:
1194
+ *out = '\0';
1066
1195
 
1067
- return *end == '\\';
1196
+ return str;
1068
1197
  }
1069
1198
 
1070
- static int parse_multiline_variable(diskfile_backend *cfg, const char *first, char **out)
1199
+ static int is_multiline_var(const char *str)
1071
1200
  {
1072
- char *line = NULL, *end;
1073
- int error = GIT_SUCCESS, ret;
1074
- size_t len;
1075
- char *buf;
1201
+ const char *end = str + strlen(str);
1202
+ return (end > str) && (end[-1] == '\\');
1203
+ }
1204
+
1205
+ static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
1206
+ {
1207
+ char *line = NULL, *proc_line = NULL;
1208
+ int quote_count;
1076
1209
 
1077
1210
  /* Check that the next line exists */
1078
- line = cfg_readline(cfg);
1211
+ line = cfg_readline(cfg, false);
1079
1212
  if (line == NULL)
1080
- return GIT_ENOMEM;
1213
+ return -1;
1081
1214
 
1082
1215
  /* We've reached the end of the file, there is input missing */
1083
1216
  if (line[0] == '\0') {
1084
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse multiline var. File ended unexpectedly");
1085
- goto out;
1217
+ set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
1218
+ git__free(line);
1219
+ return -1;
1086
1220
  }
1087
1221
 
1088
- strip_comments(line);
1222
+ quote_count = strip_comments(line, !!in_quotes);
1089
1223
 
1090
1224
  /* If it was just a comment, pretend it didn't exist */
1091
1225
  if (line[0] == '\0') {
1092
- error = parse_multiline_variable(cfg, first, out);
1093
- goto out;
1226
+ git__free(line);
1227
+ return parse_multiline_variable(cfg, value, quote_count);
1228
+ /* TODO: unbounded recursion. This **could** be exploitable */
1094
1229
  }
1095
1230
 
1096
- /* Find the continuation character '\' and strip the whitespace */
1097
- end = strrchr(first, '\\');
1098
- while (isspace(end[-1]))
1099
- --end;
1100
-
1101
- *end = '\0'; /* Terminate the string here */
1102
-
1103
- len = strlen(first) + strlen(line) + 2;
1104
- buf = git__malloc(len);
1105
- if (buf == NULL) {
1106
- error = GIT_ENOMEM;
1107
- goto out;
1108
- }
1231
+ /* Drop the continuation character '\': to closely follow the UNIX
1232
+ * standard, this character **has** to be last one in the buf, with
1233
+ * no whitespace after it */
1234
+ assert(is_multiline_var(value->ptr));
1235
+ git_buf_truncate(value, git_buf_len(value) - 1);
1109
1236
 
1110
- ret = p_snprintf(buf, len, "%s %s", first, line);
1111
- if (ret < 0) {
1112
- error = git__throw(GIT_EOSERR, "Failed to parse multiline var. Failed to put together two lines. OS err: %s", strerror(errno));
1113
- git__free(buf);
1114
- goto out;
1237
+ proc_line = fixup_line(line, in_quotes);
1238
+ if (proc_line == NULL) {
1239
+ git__free(line);
1240
+ return -1;
1115
1241
  }
1242
+ /* add this line to the multiline var */
1243
+ git_buf_puts(value, proc_line);
1244
+ git__free(line);
1245
+ git__free(proc_line);
1116
1246
 
1117
1247
  /*
1118
- * If we need to continue reading the next line, pretend
1119
- * everything we've read up to now was in one line and call
1120
- * ourselves.
1248
+ * If we need to continue reading the next line, let's just
1249
+ * keep putting stuff in the buffer
1121
1250
  */
1122
- if (is_multiline_var(buf)) {
1123
- char *final_val;
1124
- error = parse_multiline_variable(cfg, buf, &final_val);
1125
- git__free(buf);
1126
- buf = final_val;
1127
- }
1128
-
1129
- *out = buf;
1251
+ if (is_multiline_var(value->ptr))
1252
+ return parse_multiline_variable(cfg, value, quote_count);
1130
1253
 
1131
- out:
1132
- git__free(line);
1133
- return error;
1254
+ return 0;
1134
1255
  }
1135
1256
 
1136
1257
  static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
1137
1258
  {
1138
- char *tmp;
1139
- int error = GIT_SUCCESS;
1140
1259
  const char *var_end = NULL;
1141
1260
  const char *value_start = NULL;
1142
1261
  char *line;
1262
+ int quote_count;
1143
1263
 
1144
- line = cfg_readline(cfg);
1264
+ line = cfg_readline(cfg, true);
1145
1265
  if (line == NULL)
1146
- return GIT_ENOMEM;
1266
+ return -1;
1147
1267
 
1148
- strip_comments(line);
1268
+ quote_count = strip_comments(line, 0);
1149
1269
 
1150
1270
  var_end = strchr(line, '=');
1151
1271
 
@@ -1154,57 +1274,47 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val
1154
1274
  else
1155
1275
  value_start = var_end + 1;
1156
1276
 
1157
- if (isspace(var_end[-1])) {
1277
+ if (git__isspace(var_end[-1])) {
1158
1278
  do var_end--;
1159
- while (isspace(var_end[0]));
1279
+ while (git__isspace(var_end[0]));
1160
1280
  }
1161
1281
 
1162
- tmp = git__strndup(line, var_end - line + 1);
1163
- if (tmp == NULL) {
1164
- error = GIT_ENOMEM;
1165
- goto out;
1166
- }
1282
+ *var_name = git__strndup(line, var_end - line + 1);
1283
+ GITERR_CHECK_ALLOC(*var_name);
1167
1284
 
1168
- *var_name = tmp;
1285
+ /* If there is no value, boolean true is assumed */
1286
+ *var_value = NULL;
1169
1287
 
1170
1288
  /*
1171
1289
  * Now, let's try to parse the value
1172
1290
  */
1173
1291
  if (value_start != NULL) {
1174
-
1175
- while (isspace(value_start[0]))
1292
+ while (git__isspace(value_start[0]))
1176
1293
  value_start++;
1177
1294
 
1178
- if (value_start[0] == '\0') {
1179
- *var_value = NULL;
1180
- goto out;
1181
- }
1182
-
1183
1295
  if (is_multiline_var(value_start)) {
1184
- error = parse_multiline_variable(cfg, value_start, var_value);
1185
- if (error != GIT_SUCCESS)
1186
- {
1187
- *var_value = NULL;
1296
+ git_buf multi_value = GIT_BUF_INIT;
1297
+ char *proc_line = fixup_line(value_start, 0);
1298
+ GITERR_CHECK_ALLOC(proc_line);
1299
+ git_buf_puts(&multi_value, proc_line);
1300
+ git__free(proc_line);
1301
+ if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
1188
1302
  git__free(*var_name);
1303
+ git__free(line);
1304
+ git_buf_free(&multi_value);
1305
+ return -1;
1189
1306
  }
1190
- goto out;
1191
- }
1192
1307
 
1193
- tmp = git__strdup(value_start);
1194
- if (tmp == NULL) {
1195
- git__free(*var_name);
1196
- *var_value = NULL;
1197
- error = GIT_ENOMEM;
1198
- goto out;
1308
+ *var_value = git_buf_detach(&multi_value);
1309
+
1310
+ }
1311
+ else if (value_start[0] != '\0') {
1312
+ *var_value = fixup_line(value_start, 0);
1313
+ GITERR_CHECK_ALLOC(*var_value);
1199
1314
  }
1200
1315
 
1201
- *var_value = tmp;
1202
- } else {
1203
- /* If there is no value, boolean true is assumed */
1204
- *var_value = NULL;
1205
1316
  }
1206
1317
 
1207
- out:
1208
1318
  git__free(line);
1209
- return error;
1319
+ return 0;
1210
1320
  }