rugged 0.1.2 → 0.16.0b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (277) hide show
  1. data/README.md +25 -36
  2. data/Rakefile +20 -98
  3. data/ext/rugged/extconf.rb +44 -4
  4. data/ext/rugged/remote.c +215 -0
  5. data/ext/rugged/rugged.c +171 -0
  6. data/ext/rugged/rugged.h +126 -0
  7. data/ext/rugged/rugged_blob.c +99 -0
  8. data/ext/rugged/rugged_commit.c +224 -0
  9. data/ext/rugged/rugged_config.c +238 -0
  10. data/ext/rugged/rugged_index.c +440 -0
  11. data/ext/rugged/rugged_object.c +203 -0
  12. data/ext/rugged/rugged_reference.c +401 -0
  13. data/ext/rugged/rugged_repo.c +482 -0
  14. data/ext/rugged/rugged_revwalk.c +138 -0
  15. data/ext/rugged/rugged_signature.c +80 -0
  16. data/ext/rugged/rugged_tag.c +216 -0
  17. data/ext/rugged/rugged_tree.c +322 -0
  18. data/ext/rugged/vendor/libgit2-dist.tar.gz +0 -0
  19. data/ext/rugged/vendor/libgit2-dist/deps/http-parser/http_parser.c +1778 -0
  20. data/ext/rugged/vendor/libgit2-dist/deps/http-parser/http_parser.h +267 -0
  21. data/ext/rugged/vendor/libgit2-dist/deps/zlib/adler32.c +169 -0
  22. data/ext/rugged/vendor/libgit2-dist/deps/zlib/crc32.c +442 -0
  23. data/ext/rugged/vendor/libgit2-dist/deps/zlib/crc32.h +441 -0
  24. data/ext/rugged/vendor/libgit2-dist/deps/zlib/deflate.c +1834 -0
  25. data/ext/rugged/vendor/libgit2-dist/deps/zlib/deflate.h +342 -0
  26. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inffast.c +340 -0
  27. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inffast.h +11 -0
  28. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inffixed.h +94 -0
  29. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inflate.c +1480 -0
  30. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inflate.h +122 -0
  31. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inftrees.c +330 -0
  32. data/ext/rugged/vendor/libgit2-dist/deps/zlib/inftrees.h +62 -0
  33. data/ext/rugged/vendor/libgit2-dist/deps/zlib/trees.c +1244 -0
  34. data/ext/rugged/vendor/libgit2-dist/deps/zlib/trees.h +128 -0
  35. data/ext/rugged/vendor/libgit2-dist/deps/zlib/zconf.h +54 -0
  36. data/ext/rugged/vendor/libgit2-dist/deps/zlib/zlib.h +1613 -0
  37. data/ext/rugged/vendor/libgit2-dist/deps/zlib/zutil.c +318 -0
  38. data/ext/rugged/vendor/libgit2-dist/deps/zlib/zutil.h +274 -0
  39. data/ext/rugged/vendor/libgit2-dist/examples/general.c +451 -0
  40. data/ext/rugged/vendor/libgit2-dist/examples/network/common.h +14 -0
  41. data/ext/rugged/vendor/libgit2-dist/examples/network/fetch.c +97 -0
  42. data/ext/rugged/vendor/libgit2-dist/examples/network/git2.c +58 -0
  43. data/ext/rugged/vendor/libgit2-dist/examples/network/index-pack.c +47 -0
  44. data/ext/rugged/vendor/libgit2-dist/examples/network/ls-remote.c +76 -0
  45. data/ext/rugged/vendor/libgit2-dist/examples/showindex.c +43 -0
  46. data/ext/rugged/vendor/libgit2-dist/include/git2.h +44 -0
  47. data/ext/rugged/vendor/libgit2-dist/include/git2/blob.h +120 -0
  48. data/ext/rugged/vendor/libgit2-dist/include/git2/branch.h +15 -0
  49. data/ext/rugged/vendor/libgit2-dist/include/git2/commit.h +263 -0
  50. data/ext/rugged/vendor/libgit2-dist/include/git2/common.h +103 -0
  51. data/ext/rugged/vendor/libgit2-dist/include/git2/config.h +278 -0
  52. data/ext/rugged/vendor/libgit2-dist/include/git2/errors.h +144 -0
  53. data/ext/rugged/vendor/libgit2-dist/include/git2/index.h +306 -0
  54. data/ext/rugged/vendor/libgit2-dist/include/git2/indexer.h +76 -0
  55. data/ext/rugged/vendor/libgit2-dist/include/git2/inttypes.h +305 -0
  56. data/ext/rugged/vendor/libgit2-dist/include/git2/net.h +51 -0
  57. data/ext/rugged/vendor/libgit2-dist/include/git2/object.h +173 -0
  58. data/ext/rugged/vendor/libgit2-dist/include/git2/odb.h +331 -0
  59. data/ext/rugged/vendor/libgit2-dist/include/git2/odb_backend.h +100 -0
  60. data/ext/rugged/vendor/libgit2-dist/include/git2/oid.h +218 -0
  61. data/ext/rugged/vendor/libgit2-dist/include/git2/reflog.h +128 -0
  62. data/ext/rugged/vendor/libgit2-dist/include/git2/refs.h +309 -0
  63. data/ext/rugged/vendor/libgit2-dist/include/git2/refspec.h +60 -0
  64. data/ext/rugged/vendor/libgit2-dist/include/git2/remote.h +176 -0
  65. data/ext/rugged/vendor/libgit2-dist/include/git2/repository.h +290 -0
  66. data/ext/rugged/vendor/libgit2-dist/include/git2/revwalk.h +169 -0
  67. data/ext/rugged/vendor/libgit2-dist/include/git2/signature.h +65 -0
  68. data/ext/rugged/vendor/libgit2-dist/include/git2/status.h +63 -0
  69. data/ext/rugged/vendor/libgit2-dist/include/git2/stdint.h +247 -0
  70. data/ext/rugged/vendor/libgit2-dist/include/git2/tag.h +279 -0
  71. data/ext/rugged/vendor/libgit2-dist/include/git2/threads.h +48 -0
  72. data/ext/rugged/vendor/libgit2-dist/include/git2/tree.h +318 -0
  73. data/ext/rugged/vendor/libgit2-dist/include/git2/types.h +169 -0
  74. data/ext/rugged/vendor/libgit2-dist/include/git2/version.h +15 -0
  75. data/ext/rugged/vendor/libgit2-dist/include/git2/windows.h +59 -0
  76. data/ext/rugged/vendor/libgit2-dist/include/git2/zlib.h +40 -0
  77. data/ext/rugged/vendor/libgit2-dist/src/blob.c +135 -0
  78. data/ext/rugged/vendor/libgit2-dist/src/blob.h +23 -0
  79. data/ext/rugged/vendor/libgit2-dist/src/bswap.h +97 -0
  80. data/ext/rugged/vendor/libgit2-dist/src/buffer.c +113 -0
  81. data/ext/rugged/vendor/libgit2-dist/src/buffer.h +32 -0
  82. data/ext/rugged/vendor/libgit2-dist/src/cache.c +111 -0
  83. data/ext/rugged/vendor/libgit2-dist/src/cache.h +64 -0
  84. data/ext/rugged/vendor/libgit2-dist/src/cc-compat.h +67 -0
  85. data/ext/rugged/vendor/libgit2-dist/src/commit.c +299 -0
  86. data/ext/rugged/vendor/libgit2-dist/src/commit.h +34 -0
  87. data/ext/rugged/vendor/libgit2-dist/src/common.h +64 -0
  88. data/ext/rugged/vendor/libgit2-dist/src/config.c +418 -0
  89. data/ext/rugged/vendor/libgit2-dist/src/config.h +24 -0
  90. data/ext/rugged/vendor/libgit2-dist/src/config_file.c +1210 -0
  91. data/ext/rugged/vendor/libgit2-dist/src/delta-apply.c +115 -0
  92. data/ext/rugged/vendor/libgit2-dist/src/delta-apply.h +33 -0
  93. data/ext/rugged/vendor/libgit2-dist/src/dir.h +47 -0
  94. data/ext/rugged/vendor/libgit2-dist/src/errors.c +104 -0
  95. data/ext/rugged/vendor/libgit2-dist/src/fetch.c +172 -0
  96. data/ext/rugged/vendor/libgit2-dist/src/fetch.h +18 -0
  97. data/ext/rugged/vendor/libgit2-dist/src/filebuf.c +400 -0
  98. data/ext/rugged/vendor/libgit2-dist/src/filebuf.h +72 -0
  99. data/ext/rugged/vendor/libgit2-dist/src/fileops.c +358 -0
  100. data/ext/rugged/vendor/libgit2-dist/src/fileops.h +151 -0
  101. data/ext/rugged/vendor/libgit2-dist/src/global.c +134 -0
  102. data/ext/rugged/vendor/libgit2-dist/src/global.h +24 -0
  103. data/ext/rugged/vendor/libgit2-dist/src/hash.c +74 -0
  104. data/ext/rugged/vendor/libgit2-dist/src/hash.h +29 -0
  105. data/ext/rugged/vendor/libgit2-dist/src/hashtable.c +243 -0
  106. data/ext/rugged/vendor/libgit2-dist/src/hashtable.h +80 -0
  107. data/ext/rugged/vendor/libgit2-dist/src/index.c +918 -0
  108. data/ext/rugged/vendor/libgit2-dist/src/index.h +34 -0
  109. data/ext/rugged/vendor/libgit2-dist/src/indexer.c +401 -0
  110. data/ext/rugged/vendor/libgit2-dist/src/map.h +37 -0
  111. data/ext/rugged/vendor/libgit2-dist/src/mwindow.c +272 -0
  112. data/ext/rugged/vendor/libgit2-dist/src/mwindow.h +45 -0
  113. data/ext/rugged/vendor/libgit2-dist/src/netops.c +198 -0
  114. data/ext/rugged/vendor/libgit2-dist/src/netops.h +36 -0
  115. data/ext/rugged/vendor/libgit2-dist/src/object.c +295 -0
  116. data/ext/rugged/vendor/libgit2-dist/src/odb.c +672 -0
  117. data/ext/rugged/vendor/libgit2-dist/src/odb.h +43 -0
  118. data/ext/rugged/vendor/libgit2-dist/src/odb_loose.c +855 -0
  119. data/ext/rugged/vendor/libgit2-dist/src/odb_pack.c +485 -0
  120. data/ext/rugged/vendor/libgit2-dist/src/oid.c +388 -0
  121. data/ext/rugged/vendor/libgit2-dist/src/pack.c +788 -0
  122. data/ext/rugged/vendor/libgit2-dist/src/pack.h +99 -0
  123. data/ext/rugged/vendor/libgit2-dist/src/path.c +270 -0
  124. data/ext/rugged/vendor/libgit2-dist/src/path.h +84 -0
  125. data/ext/rugged/vendor/libgit2-dist/src/pkt.c +426 -0
  126. data/ext/rugged/vendor/libgit2-dist/src/pkt.h +77 -0
  127. data/ext/rugged/vendor/libgit2-dist/src/posix.c +94 -0
  128. data/ext/rugged/vendor/libgit2-dist/src/posix.h +69 -0
  129. data/ext/rugged/vendor/libgit2-dist/src/ppc/sha1.c +70 -0
  130. data/ext/rugged/vendor/libgit2-dist/src/ppc/sha1.h +26 -0
  131. data/ext/rugged/vendor/libgit2-dist/src/pqueue.c +141 -0
  132. data/ext/rugged/vendor/libgit2-dist/src/pqueue.h +81 -0
  133. data/ext/rugged/vendor/libgit2-dist/src/protocol.c +50 -0
  134. data/ext/rugged/vendor/libgit2-dist/src/protocol.h +23 -0
  135. data/ext/rugged/vendor/libgit2-dist/src/reflog.c +318 -0
  136. data/ext/rugged/vendor/libgit2-dist/src/reflog.h +34 -0
  137. data/ext/rugged/vendor/libgit2-dist/src/refs.c +1693 -0
  138. data/ext/rugged/vendor/libgit2-dist/src/refs.h +58 -0
  139. data/ext/rugged/vendor/libgit2-dist/src/refspec.c +95 -0
  140. data/ext/rugged/vendor/libgit2-dist/src/refspec.h +23 -0
  141. data/ext/rugged/vendor/libgit2-dist/src/remote.c +339 -0
  142. data/ext/rugged/vendor/libgit2-dist/src/remote.h +25 -0
  143. data/ext/rugged/vendor/libgit2-dist/src/repository.c +849 -0
  144. data/ext/rugged/vendor/libgit2-dist/src/repository.h +60 -0
  145. data/ext/rugged/vendor/libgit2-dist/src/revwalk.c +569 -0
  146. data/ext/rugged/vendor/libgit2-dist/src/sha1.c +280 -0
  147. data/ext/rugged/vendor/libgit2-dist/src/sha1.h +21 -0
  148. data/ext/rugged/vendor/libgit2-dist/src/sha1_lookup.c +177 -0
  149. data/ext/rugged/vendor/libgit2-dist/src/sha1_lookup.h +18 -0
  150. data/ext/rugged/vendor/libgit2-dist/src/signature.c +335 -0
  151. data/ext/rugged/vendor/libgit2-dist/src/signature.h +18 -0
  152. data/ext/rugged/vendor/libgit2-dist/src/status.c +696 -0
  153. data/ext/rugged/vendor/libgit2-dist/src/tag.c +446 -0
  154. data/ext/rugged/vendor/libgit2-dist/src/tag.h +28 -0
  155. data/ext/rugged/vendor/libgit2-dist/src/thread-utils.c +55 -0
  156. data/ext/rugged/vendor/libgit2-dist/src/thread-utils.h +108 -0
  157. data/ext/rugged/vendor/libgit2-dist/src/transport.c +85 -0
  158. data/ext/rugged/vendor/libgit2-dist/src/transport.h +110 -0
  159. data/ext/rugged/vendor/libgit2-dist/src/transports/git.c +502 -0
  160. data/ext/rugged/vendor/libgit2-dist/src/transports/http.c +756 -0
  161. data/ext/rugged/vendor/libgit2-dist/src/transports/local.c +235 -0
  162. data/ext/rugged/vendor/libgit2-dist/src/tree-cache.c +201 -0
  163. data/ext/rugged/vendor/libgit2-dist/src/tree-cache.h +31 -0
  164. data/ext/rugged/vendor/libgit2-dist/src/tree.c +758 -0
  165. data/ext/rugged/vendor/libgit2-dist/src/tree.h +37 -0
  166. data/ext/rugged/vendor/libgit2-dist/src/tsort.c +365 -0
  167. data/ext/rugged/vendor/libgit2-dist/src/unix/map.c +70 -0
  168. data/ext/rugged/vendor/libgit2-dist/src/unix/posix.h +25 -0
  169. data/ext/rugged/vendor/libgit2-dist/src/util.c +381 -0
  170. data/ext/rugged/vendor/libgit2-dist/src/util.h +137 -0
  171. data/ext/rugged/vendor/libgit2-dist/src/vector.c +174 -0
  172. data/ext/rugged/vendor/libgit2-dist/src/vector.h +45 -0
  173. data/ext/rugged/vendor/libgit2-dist/src/win32/dir.c +115 -0
  174. data/ext/rugged/vendor/libgit2-dist/src/win32/fnmatch.c +180 -0
  175. data/ext/rugged/vendor/libgit2-dist/src/win32/fnmatch.h +27 -0
  176. data/ext/rugged/vendor/libgit2-dist/src/win32/map.c +131 -0
  177. data/ext/rugged/vendor/libgit2-dist/src/win32/mingw-compat.h +24 -0
  178. data/ext/rugged/vendor/libgit2-dist/src/win32/msvc-compat.h +38 -0
  179. data/ext/rugged/vendor/libgit2-dist/src/win32/posix.h +53 -0
  180. data/ext/rugged/vendor/libgit2-dist/src/win32/posix_w32.c +404 -0
  181. data/ext/rugged/vendor/libgit2-dist/src/win32/pthread.c +65 -0
  182. data/ext/rugged/vendor/libgit2-dist/src/win32/pthread.h +40 -0
  183. data/ext/rugged/vendor/libgit2-dist/src/win32/utf-conv.c +88 -0
  184. data/ext/rugged/vendor/libgit2-dist/src/win32/utf-conv.h +17 -0
  185. data/ext/rugged/vendor/libgit2-dist/tests-clay/buf/basic.c +29 -0
  186. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay.h +187 -0
  187. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay_libgit2.h +28 -0
  188. data/ext/rugged/vendor/libgit2-dist/tests-clay/clay_main.c +1073 -0
  189. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/add.c +37 -0
  190. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/new.c +36 -0
  191. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/read.c +209 -0
  192. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/stress.c +39 -0
  193. data/ext/rugged/vendor/libgit2-dist/tests-clay/config/write.c +77 -0
  194. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/dirent.c +222 -0
  195. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/filebuf.c +106 -0
  196. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/oid.c +18 -0
  197. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/path.c +139 -0
  198. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/rmdir.c +50 -0
  199. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/string.c +28 -0
  200. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/strtol.c +37 -0
  201. data/ext/rugged/vendor/libgit2-dist/tests-clay/core/vector.c +66 -0
  202. data/ext/rugged/vendor/libgit2-dist/tests-clay/index/rename.c +60 -0
  203. data/ext/rugged/vendor/libgit2-dist/tests-clay/network/remotes.c +50 -0
  204. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/chars.c +52 -0
  205. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/compare.c +124 -0
  206. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/convert.c +75 -0
  207. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/data.h +323 -0
  208. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/fromstr.c +30 -0
  209. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/hash.c +162 -0
  210. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/short.c +94 -0
  211. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/size.c +13 -0
  212. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/raw/type2string.c +54 -0
  213. data/ext/rugged/vendor/libgit2-dist/tests-clay/object/tree/frompath.c +75 -0
  214. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/loose.c +84 -0
  215. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/loose_data.h +522 -0
  216. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/pack_data.h +151 -0
  217. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/packed.c +78 -0
  218. data/ext/rugged/vendor/libgit2-dist/tests-clay/odb/sorting.c +71 -0
  219. data/ext/rugged/vendor/libgit2-dist/tests-clay/repo/getters.c +68 -0
  220. data/ext/rugged/vendor/libgit2-dist/tests-clay/repo/init.c +104 -0
  221. data/ext/rugged/vendor/libgit2-dist/tests-clay/repo/open.c +54 -0
  222. data/ext/rugged/vendor/libgit2-dist/tests-clay/status/single.c +38 -0
  223. data/ext/rugged/vendor/libgit2-dist/tests-clay/status/status_data.h +48 -0
  224. data/ext/rugged/vendor/libgit2-dist/tests-clay/status/worktree.c +124 -0
  225. data/ext/rugged/vendor/libgit2-dist/tests/t00-core.c +628 -0
  226. data/ext/rugged/vendor/libgit2-dist/tests/t01-data.h +322 -0
  227. data/ext/rugged/vendor/libgit2-dist/tests/t01-rawobj.c +638 -0
  228. data/ext/rugged/vendor/libgit2-dist/tests/t03-data.h +344 -0
  229. data/ext/rugged/vendor/libgit2-dist/tests/t03-objwrite.c +255 -0
  230. data/ext/rugged/vendor/libgit2-dist/tests/t04-commit.c +788 -0
  231. data/ext/rugged/vendor/libgit2-dist/tests/t05-revwalk.c +140 -0
  232. data/ext/rugged/vendor/libgit2-dist/tests/t06-index.c +219 -0
  233. data/ext/rugged/vendor/libgit2-dist/tests/t07-hashtable.c +192 -0
  234. data/ext/rugged/vendor/libgit2-dist/tests/t08-tag.c +357 -0
  235. data/ext/rugged/vendor/libgit2-dist/tests/t09-tree.c +221 -0
  236. data/ext/rugged/vendor/libgit2-dist/tests/t10-refs.c +1294 -0
  237. data/ext/rugged/vendor/libgit2-dist/tests/t12-repo.c +174 -0
  238. data/ext/rugged/vendor/libgit2-dist/tests/t13-threads.c +41 -0
  239. data/ext/rugged/vendor/libgit2-dist/tests/t17-bufs.c +61 -0
  240. data/ext/rugged/vendor/libgit2-dist/tests/t18-status.c +448 -0
  241. data/ext/rugged/vendor/libgit2-dist/tests/test_helpers.c +310 -0
  242. data/ext/rugged/vendor/libgit2-dist/tests/test_helpers.h +83 -0
  243. data/ext/rugged/vendor/libgit2-dist/tests/test_lib.c +198 -0
  244. data/ext/rugged/vendor/libgit2-dist/tests/test_lib.h +54 -0
  245. data/ext/rugged/vendor/libgit2-dist/tests/test_main.c +89 -0
  246. data/lib/rugged.rb +4 -3
  247. data/lib/rugged/index.rb +0 -8
  248. data/lib/rugged/objects.rb +45 -0
  249. data/lib/rugged/repository.rb +29 -0
  250. data/lib/rugged/tree.rb +16 -6
  251. data/lib/rugged/version.rb +1 -1
  252. data/lib/rugged/walker.rb +5 -0
  253. data/test/blob_test.rb +18 -14
  254. data/test/commit_test.rb +28 -26
  255. data/test/coverage/HEAD.json +1 -0
  256. data/test/coverage/cover.rb +106 -0
  257. data/test/fixtures/testrepo.git/refs/heads/new_name +1 -0
  258. data/test/index_test.rb +101 -78
  259. data/test/lib_test.rb +4 -4
  260. data/test/object_test.rb +3 -3
  261. data/test/reference_test.rb +75 -0
  262. data/test/remote_test.rb +19 -0
  263. data/test/repo_pack_test.rb +4 -4
  264. data/test/repo_test.rb +44 -15
  265. data/test/tag_test.rb +7 -17
  266. data/test/test_helper.rb +24 -5
  267. data/test/tree_test.rb +30 -12
  268. data/test/walker_test.rb +40 -31
  269. metadata +273 -50
  270. data/lib/rugged/person.rb +0 -20
  271. data/lib/rugged/tree_entry.rb +0 -9
  272. data/test/fixtures/testrepo.git/objects/1d/83f106355e4309a293e42ad2a2c4b8bdbe77ae +0 -0
  273. data/test/fixtures/testrepo.git/objects/2f/3321418db5b2a841375b8b70880a8ab5a4148f +0 -0
  274. data/test/fixtures/testrepo.git/objects/36/9b00a7700cca3a506d79e301d6ad8bf735d9ee +0 -3
  275. data/test/fixtures/testrepo.git/objects/3d/b1b5ceace59ff65279757003763046fd4cbbe6 +0 -0
  276. data/test/fixtures/testrepo.git/objects/4c/d1604907792e2c43e03dcec1216f99d63e68c4 +0 -3
  277. data/test/fixtures/testrepo.git/objects/e0/f46d77041c149296549b01ed4a18b02c4b7400 +0 -0
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright (C) 2009-2011 the libgit2 contributors
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
+ #ifndef INCLUDE_config_h__
8
+ #define INCLUDE_config_h__
9
+
10
+ #include "git2.h"
11
+ #include "git2/config.h"
12
+ #include "vector.h"
13
+ #include "repository.h"
14
+
15
+ #define GIT_CONFIG_FILENAME ".gitconfig"
16
+ #define GIT_CONFIG_FILENAME_INREPO "config"
17
+ #define GIT_CONFIG_FILE_MODE 0666
18
+
19
+ struct git_config {
20
+ git_refcount rc;
21
+ git_vector files;
22
+ };
23
+
24
+ #endif
@@ -0,0 +1,1210 @@
1
+ /*
2
+ * Copyright (C) 2009-2011 the libgit2 contributors
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 "common.h"
9
+ #include "config.h"
10
+ #include "fileops.h"
11
+ #include "filebuf.h"
12
+ #include "buffer.h"
13
+ #include "git2/config.h"
14
+ #include "git2/types.h"
15
+
16
+
17
+ #include <ctype.h>
18
+
19
+ typedef struct cvar_t {
20
+ struct cvar_t *next;
21
+ char *section;
22
+ char *name;
23
+ char *value;
24
+ } cvar_t;
25
+
26
+ typedef struct {
27
+ struct cvar_t *head;
28
+ struct cvar_t *tail;
29
+ } cvar_t_list;
30
+
31
+ #define CVAR_LIST_HEAD(list) ((list)->head)
32
+
33
+ #define CVAR_LIST_TAIL(list) ((list)->tail)
34
+
35
+ #define CVAR_LIST_NEXT(var) ((var)->next)
36
+
37
+ #define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
38
+
39
+ #define CVAR_LIST_APPEND(list, var) do {\
40
+ if (CVAR_LIST_EMPTY(list)) {\
41
+ CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
42
+ } else {\
43
+ CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
44
+ CVAR_LIST_TAIL(list) = var;\
45
+ }\
46
+ } while(0)
47
+
48
+ #define CVAR_LIST_REMOVE_HEAD(list) do {\
49
+ CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
50
+ } while(0)
51
+
52
+ #define CVAR_LIST_REMOVE_AFTER(var) do {\
53
+ CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
54
+ } while(0)
55
+
56
+ #define CVAR_LIST_FOREACH(list, iter)\
57
+ for ((iter) = CVAR_LIST_HEAD(list);\
58
+ (iter) != NULL;\
59
+ (iter) = CVAR_LIST_NEXT(iter))
60
+
61
+ /*
62
+ * Inspired by the FreeBSD functions
63
+ */
64
+ #define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
65
+ for ((iter) = CVAR_LIST_HEAD(vars);\
66
+ (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
67
+ (iter) = (tmp))
68
+
69
+ typedef struct {
70
+ git_config_file parent;
71
+
72
+ cvar_t_list var_list;
73
+
74
+ struct {
75
+ git_fbuffer buffer;
76
+ char *read_ptr;
77
+ int line_number;
78
+ int eof;
79
+ } reader;
80
+
81
+ char *file_path;
82
+ } diskfile_backend;
83
+
84
+ static int config_parse(diskfile_backend *cfg_file);
85
+ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
86
+ static int config_write(diskfile_backend *cfg, cvar_t *var);
87
+
88
+ static void cvar_free(cvar_t *var)
89
+ {
90
+ if (var == NULL)
91
+ return;
92
+
93
+ git__free(var->section);
94
+ git__free(var->name);
95
+ git__free(var->value);
96
+ git__free(var);
97
+ }
98
+
99
+ static void cvar_list_free(cvar_t_list *list)
100
+ {
101
+ cvar_t *cur;
102
+
103
+ while (!CVAR_LIST_EMPTY(list)) {
104
+ cur = CVAR_LIST_HEAD(list);
105
+ CVAR_LIST_REMOVE_HEAD(list);
106
+ cvar_free(cur);
107
+ }
108
+ }
109
+
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)
116
+ {
117
+ const char *sdot, *qdot, *qsub;
118
+ size_t section_len;
119
+
120
+ sdot = strchr(section, '.');
121
+
122
+ /* If the section doesn't have any dots, it's easy */
123
+ if (sdot == NULL)
124
+ return !strncasecmp(section, query, strlen(section));
125
+
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;
133
+
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))
138
+ return 0;
139
+
140
+ /* The subsection is case-sensitive */
141
+ return !strncmp(sdot + 1, qsub, strlen(sdot + 1));
142
+ }
143
+
144
+ static int cvar_match_name(const cvar_t *var, const char *str)
145
+ {
146
+ const char *name_start;
147
+
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;
155
+
156
+ return !strcasecmp(var->name, name_start);
157
+ }
158
+
159
+ static cvar_t *cvar_list_find(cvar_t_list *list, const char *name)
160
+ {
161
+ cvar_t *iter;
162
+
163
+ CVAR_LIST_FOREACH (list, iter) {
164
+ if (cvar_match_name(iter, name))
165
+ return iter;
166
+ }
167
+
168
+ return NULL;
169
+ }
170
+
171
+ static int cvar_normalize_name(cvar_t *var, char **output)
172
+ {
173
+ char *section_sp = strchr(var->section, ' ');
174
+ char *quote, *name;
175
+ size_t len;
176
+ int ret;
177
+
178
+ /*
179
+ * The final string is going to be at most one char longer than
180
+ * the input
181
+ */
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));
193
+ }
194
+
195
+ *output = name;
196
+ return GIT_SUCCESS;
197
+ }
198
+
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
+ }
219
+
220
+ static char *interiorize_section(const char *orig)
221
+ {
222
+ char *dot, *last_dot, *section, *ret;
223
+ size_t len;
224
+
225
+ dot = strchr(orig, '.');
226
+ last_dot = strrchr(orig, '.');
227
+ len = last_dot - orig;
228
+
229
+ /* No subsection, this is easy */
230
+ if (last_dot == dot)
231
+ return git__strndup(orig, dot - orig);
232
+
233
+ section = git__malloc(len + 4);
234
+ if (section == NULL)
235
+ return NULL;
236
+
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 = '"';
249
+
250
+ return ret;
251
+ }
252
+
253
+ static int config_open(git_config_file *cfg)
254
+ {
255
+ int error;
256
+ diskfile_backend *b = (diskfile_backend *)cfg;
257
+
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;
266
+
267
+ error = config_parse(b);
268
+ if (error < GIT_SUCCESS)
269
+ goto cleanup;
270
+
271
+ git_futils_freebuffer(&b->reader.buffer);
272
+
273
+ return GIT_SUCCESS;
274
+
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");
280
+ }
281
+
282
+ static void backend_free(git_config_file *_backend)
283
+ {
284
+ diskfile_backend *backend = (diskfile_backend *)_backend;
285
+
286
+ if (backend == NULL)
287
+ return;
288
+
289
+ git__free(backend->file_path);
290
+ cvar_list_free(&backend->var_list);
291
+
292
+ git__free(backend);
293
+ }
294
+
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;
300
+
301
+ CVAR_LIST_FOREACH(&b->var_list, var) {
302
+ char *normalized = NULL;
303
+
304
+ ret = cvar_normalize_name(var, &normalized);
305
+ if (ret < GIT_SUCCESS)
306
+ return ret;
307
+
308
+ ret = fn(normalized, var->value, data);
309
+ git__free(normalized);
310
+ if (ret)
311
+ break;
312
+ }
313
+
314
+ return ret;
315
+ }
316
+
317
+ static int config_set(git_config_file *cfg, const char *name, const char *value)
318
+ {
319
+ cvar_t *var = NULL;
320
+ cvar_t *existing = NULL;
321
+ int error = GIT_SUCCESS;
322
+ const char *last_dot;
323
+ diskfile_backend *b = (diskfile_backend *)cfg;
324
+
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;
333
+
334
+ git__free(existing->value);
335
+ existing->value = tmp;
336
+
337
+ return config_write(b, existing);
338
+ }
339
+
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");
348
+
349
+ last_dot = strrchr(name, '.');
350
+ if (last_dot == NULL) {
351
+ return git__throw(GIT_EINVALIDTYPE, "Variables without section aren't allowed");
352
+ }
353
+
354
+ var = git__malloc(sizeof(cvar_t));
355
+ if (var == NULL)
356
+ return GIT_ENOMEM;
357
+
358
+ memset(var, 0x0, sizeof(cvar_t));
359
+
360
+ var->section = interiorize_section(name);
361
+ if (var->section == NULL) {
362
+ error = GIT_ENOMEM;
363
+ goto out;
364
+ }
365
+
366
+ var->name = git__strdup(last_dot + 1);
367
+ if (var->name == NULL) {
368
+ error = GIT_ENOMEM;
369
+ goto out;
370
+ }
371
+
372
+ var->value = value ? git__strdup(value) : NULL;
373
+ if (var->value == NULL && value != NULL) {
374
+ error = GIT_ENOMEM;
375
+ goto out;
376
+ }
377
+
378
+ CVAR_LIST_APPEND(&b->var_list, var);
379
+ error = config_write(b, var);
380
+
381
+ out:
382
+ if (error < GIT_SUCCESS)
383
+ cvar_free(var);
384
+
385
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set config value");
386
+ }
387
+
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)
392
+ {
393
+ cvar_t *var;
394
+ int error = GIT_SUCCESS;
395
+ diskfile_backend *b = (diskfile_backend *)cfg;
396
+
397
+ var = cvar_list_find(&b->var_list, name);
398
+
399
+ if (var == NULL)
400
+ return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
401
+
402
+ *out = var->value;
403
+
404
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to get config value for %s", name);
405
+ }
406
+
407
+ int git_config_file__ondisk(git_config_file **out, const char *path)
408
+ {
409
+ diskfile_backend *backend;
410
+
411
+ backend = git__malloc(sizeof(diskfile_backend));
412
+ if (backend == NULL)
413
+ return GIT_ENOMEM;
414
+
415
+ memset(backend, 0x0, sizeof(diskfile_backend));
416
+
417
+ backend->file_path = git__strdup(path);
418
+ if (backend->file_path == NULL) {
419
+ git__free(backend);
420
+ return GIT_ENOMEM;
421
+ }
422
+
423
+ backend->parent.open = config_open;
424
+ backend->parent.get = config_get;
425
+ backend->parent.set = config_set;
426
+ backend->parent.foreach = file_foreach;
427
+ backend->parent.free = backend_free;
428
+
429
+ *out = (git_config_file *)backend;
430
+
431
+ return GIT_SUCCESS;
432
+ }
433
+
434
+ static int cfg_getchar_raw(diskfile_backend *cfg)
435
+ {
436
+ int c;
437
+
438
+ c = *cfg->reader.read_ptr++;
439
+
440
+ /*
441
+ Win 32 line breaks: if we find a \r\n sequence,
442
+ return only the \n as a newline
443
+ */
444
+ if (c == '\r' && *cfg->reader.read_ptr == '\n') {
445
+ cfg->reader.read_ptr++;
446
+ c = '\n';
447
+ }
448
+
449
+ if (c == '\n')
450
+ cfg->reader.line_number++;
451
+
452
+ if (c == 0) {
453
+ cfg->reader.eof = 1;
454
+ c = '\n';
455
+ }
456
+
457
+ return c;
458
+ }
459
+
460
+ #define SKIP_WHITESPACE (1 << 1)
461
+ #define SKIP_COMMENTS (1 << 2)
462
+
463
+ static int cfg_getchar(diskfile_backend *cfg_file, int flags)
464
+ {
465
+ const int skip_whitespace = (flags & SKIP_WHITESPACE);
466
+ const int skip_comments = (flags & SKIP_COMMENTS);
467
+ int c;
468
+
469
+ assert(cfg_file->reader.read_ptr);
470
+
471
+ do c = cfg_getchar_raw(cfg_file);
472
+ while (skip_whitespace && isspace(c));
473
+
474
+ if (skip_comments && (c == '#' || c == ';')) {
475
+ do c = cfg_getchar_raw(cfg_file);
476
+ while (c != '\n');
477
+ }
478
+
479
+ return c;
480
+ }
481
+
482
+ /*
483
+ * Read the next char, but don't move the reading pointer.
484
+ */
485
+ static int cfg_peek(diskfile_backend *cfg, int flags)
486
+ {
487
+ void *old_read_ptr;
488
+ int old_lineno, old_eof;
489
+ int ret;
490
+
491
+ assert(cfg->reader.read_ptr);
492
+
493
+ old_read_ptr = cfg->reader.read_ptr;
494
+ old_lineno = cfg->reader.line_number;
495
+ old_eof = cfg->reader.eof;
496
+
497
+ ret = cfg_getchar(cfg, flags);
498
+
499
+ cfg->reader.read_ptr = old_read_ptr;
500
+ cfg->reader.line_number = old_lineno;
501
+ cfg->reader.eof = old_eof;
502
+
503
+ return ret;
504
+ }
505
+
506
+ /*
507
+ * Read and consume a line, returning it in newly-allocated memory.
508
+ */
509
+ static char *cfg_readline(diskfile_backend *cfg)
510
+ {
511
+ char *line = NULL;
512
+ char *line_src, *line_end;
513
+ size_t line_len;
514
+
515
+ line_src = cfg->reader.read_ptr;
516
+
517
+ /* Skip empty empty lines */
518
+ while (isspace(*line_src))
519
+ ++line_src;
520
+
521
+ line_end = strchr(line_src, '\n');
522
+
523
+ /* no newline at EOF */
524
+ if (line_end == NULL)
525
+ line_end = strchr(line_src, 0);
526
+
527
+ line_len = line_end - line_src;
528
+
529
+ line = git__malloc(line_len + 1);
530
+ if (line == NULL)
531
+ return NULL;
532
+
533
+ memcpy(line, line_src, line_len);
534
+
535
+ do line[line_len] = '\0';
536
+ while (line_len-- > 0 && isspace(line[line_len]));
537
+
538
+ if (*line_end == '\n')
539
+ line_end++;
540
+
541
+ if (*line_end == '\0')
542
+ cfg->reader.eof = 1;
543
+
544
+ cfg->reader.line_number++;
545
+ cfg->reader.read_ptr = line_end;
546
+
547
+ return line;
548
+ }
549
+
550
+ /*
551
+ * Consume a line, without storing it anywhere
552
+ */
553
+ static void cfg_consume_line(diskfile_backend *cfg)
554
+ {
555
+ char *line_start, *line_end;
556
+
557
+ line_start = cfg->reader.read_ptr;
558
+ line_end = strchr(line_start, '\n');
559
+ /* No newline at EOF */
560
+ if(line_end == NULL){
561
+ line_end = strchr(line_start, '\0');
562
+ }
563
+
564
+ if (*line_end == '\n')
565
+ line_end++;
566
+
567
+ if (*line_end == '\0')
568
+ cfg->reader.eof = 1;
569
+
570
+ cfg->reader.line_number++;
571
+ cfg->reader.read_ptr = line_end;
572
+ }
573
+
574
+ GIT_INLINE(int) config_keychar(int c)
575
+ {
576
+ return isalnum(c) || c == '-';
577
+ }
578
+
579
+ static int parse_section_header_ext(const char *line, const char *base_name, char **section_name)
580
+ {
581
+ int c, rpos;
582
+ char *first_quote, *last_quote;
583
+ git_buf buf = GIT_BUF_INIT;
584
+ int error = GIT_SUCCESS;
585
+ int quote_marks;
586
+ /*
587
+ * base_name is what came before the space. We should be at the
588
+ * first quotation mark, except for now, line isn't being kept in
589
+ * sync so we only really use it to calculate the length.
590
+ */
591
+
592
+ first_quote = strchr(line, '"');
593
+ last_quote = strrchr(line, '"');
594
+
595
+ if (last_quote - first_quote == 0)
596
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. There is no final quotation mark");
597
+
598
+ git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
599
+ git_buf_printf(&buf, "%s.", base_name);
600
+
601
+ rpos = 0;
602
+ quote_marks = 0;
603
+
604
+ line = first_quote;
605
+ c = line[rpos++];
606
+
607
+ /*
608
+ * At the end of each iteration, whatever is stored in c will be
609
+ * added to the string. In case of error, jump to out
610
+ */
611
+ do {
612
+ if (quote_marks == 2) {
613
+ error = git__throw(GIT_EOBJCORRUPTED, "Falied to parse ext header. Text after closing quote");
614
+ goto out;
615
+
616
+ }
617
+
618
+ switch (c) {
619
+ case '"':
620
+ ++quote_marks;
621
+ continue;
622
+ case '\\':
623
+ c = line[rpos++];
624
+ switch (c) {
625
+ case '"':
626
+ case '\\':
627
+ break;
628
+ default:
629
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c);
630
+ goto out;
631
+ }
632
+ break;
633
+ default:
634
+ break;
635
+ }
636
+
637
+ git_buf_putc(&buf, c);
638
+ } while ((c = line[rpos++]) != ']');
639
+
640
+ *section_name = git__strdup(git_buf_cstr(&buf));
641
+ out:
642
+ git_buf_free(&buf);
643
+
644
+ return error;
645
+ }
646
+
647
+ static int parse_section_header(diskfile_backend *cfg, char **section_out)
648
+ {
649
+ char *name, *name_end;
650
+ int name_length, c, pos;
651
+ int error = GIT_SUCCESS;
652
+ char *line;
653
+
654
+ line = cfg_readline(cfg);
655
+ if (line == NULL)
656
+ return GIT_ENOMEM;
657
+
658
+ /* find the end of the variable's name */
659
+ name_end = strchr(line, ']');
660
+ if (name_end == NULL) {
661
+ git__free(line);
662
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Can't find header name end");
663
+ }
664
+
665
+ name = (char *)git__malloc((size_t)(name_end - line) + 1);
666
+ if (name == NULL) {
667
+ git__free(line);
668
+ return GIT_ENOMEM;
669
+ }
670
+
671
+ name_length = 0;
672
+ pos = 0;
673
+
674
+ /* Make sure we were given a section header */
675
+ 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
+ }
680
+
681
+ c = line[pos++];
682
+
683
+ do {
684
+ if (isspace(c)){
685
+ name[name_length] = '\0';
686
+ error = parse_section_header_ext(line, name, section_out);
687
+ git__free(line);
688
+ git__free(name);
689
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse header");
690
+ }
691
+
692
+ if (!config_keychar(c) && c != '.') {
693
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Wrong format on header");
694
+ goto error;
695
+ }
696
+
697
+ name[name_length++] = (char) tolower(c);
698
+
699
+ } while ((c = line[pos++]) != ']');
700
+
701
+ if (line[pos - 1] != ']') {
702
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Config file ended unexpectedly");
703
+ goto error;
704
+ }
705
+
706
+ name[name_length] = 0;
707
+ git__free(line);
708
+ git__strtolower(name);
709
+ *section_out = name;
710
+ return GIT_SUCCESS;
711
+
712
+ error:
713
+ git__free(line);
714
+ git__free(name);
715
+ return error;
716
+ }
717
+
718
+ static int skip_bom(diskfile_backend *cfg)
719
+ {
720
+ static const char *utf8_bom = "\xef\xbb\xbf";
721
+
722
+ if (cfg->reader.buffer.len < sizeof(utf8_bom))
723
+ return GIT_SUCCESS;
724
+
725
+ if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
726
+ cfg->reader.read_ptr += sizeof(utf8_bom);
727
+
728
+ /* TODO: the reference implementation does pretty stupid
729
+ shit with the BoM
730
+ */
731
+
732
+ return GIT_SUCCESS;
733
+ }
734
+
735
+ /*
736
+ (* basic types *)
737
+ digit = "0".."9"
738
+ integer = digit { digit }
739
+ alphabet = "a".."z" + "A" .. "Z"
740
+
741
+ section_char = alphabet | "." | "-"
742
+ extension_char = (* any character except newline *)
743
+ any_char = (* any character *)
744
+ variable_char = "alphabet" | "-"
745
+
746
+
747
+ (* actual grammar *)
748
+ config = { section }
749
+
750
+ section = header { definition }
751
+
752
+ header = "[" section [subsection | subsection_ext] "]"
753
+
754
+ subsection = "." section
755
+ subsection_ext = "\"" extension "\""
756
+
757
+ section = section_char { section_char }
758
+ extension = extension_char { extension_char }
759
+
760
+ definition = variable_name ["=" variable_value] "\n"
761
+
762
+ variable_name = variable_char { variable_char }
763
+ variable_value = string | boolean | integer
764
+
765
+ string = quoted_string | plain_string
766
+ quoted_string = "\"" plain_string "\""
767
+ plain_string = { any_char }
768
+
769
+ boolean = boolean_true | boolean_false
770
+ boolean_true = "yes" | "1" | "true" | "on"
771
+ boolean_false = "no" | "0" | "false" | "off"
772
+ */
773
+
774
+ static void strip_comments(char *line)
775
+ {
776
+ int quote_count = 0;
777
+ char *ptr;
778
+
779
+ for (ptr = line; *ptr; ++ptr) {
780
+ if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
781
+ quote_count++;
782
+
783
+ if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
784
+ ptr[0] = '\0';
785
+ break;
786
+ }
787
+ }
788
+
789
+ if (isspace(ptr[-1])) {
790
+ /* TODO skip whitespace */
791
+ }
792
+ }
793
+
794
+ static int config_parse(diskfile_backend *cfg_file)
795
+ {
796
+ int error = GIT_SUCCESS, c;
797
+ char *current_section = NULL;
798
+ char *var_name;
799
+ char *var_value;
800
+ cvar_t *var;
801
+
802
+ /* Initialize the reading position */
803
+ cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
804
+ cfg_file->reader.eof = 0;
805
+
806
+ /* If the file is empty, there's nothing for us to do */
807
+ if (*cfg_file->reader.read_ptr == '\0')
808
+ return GIT_SUCCESS;
809
+
810
+ skip_bom(cfg_file);
811
+
812
+ while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
813
+
814
+ c = cfg_peek(cfg_file, SKIP_WHITESPACE);
815
+
816
+ switch (c) {
817
+ case '\0': /* We've arrived at the end of the file */
818
+ break;
819
+
820
+ case '[': /* section header, new section begins */
821
+ git__free(current_section);
822
+ current_section = NULL;
823
+ error = parse_section_header(cfg_file, &current_section);
824
+ break;
825
+
826
+ case ';':
827
+ case '#':
828
+ cfg_consume_line(cfg_file);
829
+ break;
830
+
831
+ default: /* assume variable declaration */
832
+ error = parse_variable(cfg_file, &var_name, &var_value);
833
+
834
+ if (error < GIT_SUCCESS)
835
+ break;
836
+
837
+ var = git__malloc(sizeof(cvar_t));
838
+ if (var == NULL) {
839
+ error = GIT_ENOMEM;
840
+ break;
841
+ }
842
+
843
+ memset(var, 0x0, sizeof(cvar_t));
844
+
845
+ var->section = git__strdup(current_section);
846
+ if (var->section == NULL) {
847
+ error = GIT_ENOMEM;
848
+ git__free(var);
849
+ break;
850
+ }
851
+
852
+ var->name = var_name;
853
+ var->value = var_value;
854
+ git__strtolower(var->name);
855
+
856
+ CVAR_LIST_APPEND(&cfg_file->var_list, var);
857
+
858
+ break;
859
+ }
860
+ }
861
+
862
+ git__free(current_section);
863
+
864
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config");
865
+ }
866
+
867
+ static int write_section(git_filebuf *file, cvar_t *var)
868
+ {
869
+ int error;
870
+
871
+ error = git_filebuf_printf(file, "[%s]\n", var->section);
872
+ if (error < GIT_SUCCESS)
873
+ return error;
874
+
875
+ error = git_filebuf_printf(file, " %s = %s\n", var->name, var->value);
876
+ return error;
877
+ }
878
+
879
+ /*
880
+ * This is pretty much the parsing, except we write out anything we don't have
881
+ */
882
+ static int config_write(diskfile_backend *cfg, cvar_t *var)
883
+ {
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;
888
+ git_filebuf file = GIT_FILEBUF_INIT;
889
+ const char *pre_end = NULL, *post_start = NULL;
890
+
891
+ /* 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
+ }
896
+
897
+ /* Initialise the reading position */
898
+ if (error == GIT_ENOTFOUND) {
899
+ error = GIT_SUCCESS;
900
+ cfg->reader.read_ptr = NULL;
901
+ cfg->reader.eof = 1;
902
+ 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;
907
+ cfg->reader.eof = 0;
908
+ data_start = cfg->reader.read_ptr;
909
+ }
910
+
911
+ /* 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");
915
+
916
+ skip_bom(cfg);
917
+
918
+ while (error == GIT_SUCCESS && !cfg->reader.eof) {
919
+ c = cfg_peek(cfg, SKIP_WHITESPACE);
920
+
921
+ switch (c) {
922
+ case '\0': /* We've arrived at the end of the file */
923
+ break;
924
+
925
+ case '[': /* section header, new section begins */
926
+ /*
927
+ * We set both positions to the current one in case we
928
+ * need to add a variable to the end of a section. In that
929
+ * case, we want both variables to point just before the
930
+ * new section. If we actually want to replace it, the
931
+ * default case will take care of updating them.
932
+ */
933
+ 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;
939
+
940
+ /* Keep track of when it stops matching */
941
+ last_section_matched = section_matches;
942
+ section_matches = !strcmp(current_section, var->section);
943
+ break;
944
+
945
+ case ';':
946
+ case '#':
947
+ cfg_consume_line(cfg);
948
+ break;
949
+
950
+ default:
951
+ /*
952
+ * If the section doesn't match, but the last section did,
953
+ * it means we need to add a variable (so skip the line
954
+ * otherwise). If both the section and name match, we need
955
+ * to overwrite the variable (so skip the line
956
+ * otherwise). pre_end needs to be updated each time so we
957
+ * don't loose that information, but we only need to
958
+ * update post_start if we're going to use it in this
959
+ * iteration.
960
+ */
961
+ if (!section_matches) {
962
+ if (!last_section_matched) {
963
+ cfg_consume_line(cfg);
964
+ break;
965
+ }
966
+ } else {
967
+ int cmp = -1;
968
+
969
+ 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);
972
+
973
+ git__free(var_name);
974
+ git__free(var_value);
975
+
976
+ if (cmp != 0)
977
+ break;
978
+
979
+ post_start = cfg->reader.read_ptr;
980
+ }
981
+
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
+ }
991
+
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;
1004
+ }
1005
+
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;
1013
+ }
1014
+
1015
+ goto cleanup;
1016
+ }
1017
+ }
1018
+
1019
+ /*
1020
+ * Being here can mean that
1021
+ *
1022
+ * 1) our section is the last one in the file and we're
1023
+ * adding a variable
1024
+ *
1025
+ * 2) we didn't find a section for us so we need to create it
1026
+ * ourselves.
1027
+ *
1028
+ * Either way we need to write out the whole file.
1029
+ */
1030
+
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;
1035
+ }
1036
+
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
+ }
1042
+
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");
1047
+
1048
+ cleanup:
1049
+ git__free(current_section);
1050
+
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;
1058
+ }
1059
+
1060
+ static int is_multiline_var(const char *str)
1061
+ {
1062
+ char *end = strrchr(str, '\0') - 1;
1063
+
1064
+ while (isspace(*end))
1065
+ --end;
1066
+
1067
+ return *end == '\\';
1068
+ }
1069
+
1070
+ static int parse_multiline_variable(diskfile_backend *cfg, const char *first, char **out)
1071
+ {
1072
+ char *line = NULL, *end;
1073
+ int error = GIT_SUCCESS, ret;
1074
+ size_t len;
1075
+ char *buf;
1076
+
1077
+ /* Check that the next line exists */
1078
+ line = cfg_readline(cfg);
1079
+ if (line == NULL)
1080
+ return GIT_ENOMEM;
1081
+
1082
+ /* We've reached the end of the file, there is input missing */
1083
+ if (line[0] == '\0') {
1084
+ error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse multiline var. File ended unexpectedly");
1085
+ goto out;
1086
+ }
1087
+
1088
+ strip_comments(line);
1089
+
1090
+ /* If it was just a comment, pretend it didn't exist */
1091
+ if (line[0] == '\0') {
1092
+ error = parse_multiline_variable(cfg, first, out);
1093
+ goto out;
1094
+ }
1095
+
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
+ }
1109
+
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;
1115
+ }
1116
+
1117
+ /*
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.
1121
+ */
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;
1130
+
1131
+ out:
1132
+ git__free(line);
1133
+ return error;
1134
+ }
1135
+
1136
+ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
1137
+ {
1138
+ char *tmp;
1139
+ int error = GIT_SUCCESS;
1140
+ const char *var_end = NULL;
1141
+ const char *value_start = NULL;
1142
+ char *line;
1143
+
1144
+ line = cfg_readline(cfg);
1145
+ if (line == NULL)
1146
+ return GIT_ENOMEM;
1147
+
1148
+ strip_comments(line);
1149
+
1150
+ var_end = strchr(line, '=');
1151
+
1152
+ if (var_end == NULL)
1153
+ var_end = strchr(line, '\0');
1154
+ else
1155
+ value_start = var_end + 1;
1156
+
1157
+ if (isspace(var_end[-1])) {
1158
+ do var_end--;
1159
+ while (isspace(var_end[0]));
1160
+ }
1161
+
1162
+ tmp = git__strndup(line, var_end - line + 1);
1163
+ if (tmp == NULL) {
1164
+ error = GIT_ENOMEM;
1165
+ goto out;
1166
+ }
1167
+
1168
+ *var_name = tmp;
1169
+
1170
+ /*
1171
+ * Now, let's try to parse the value
1172
+ */
1173
+ if (value_start != NULL) {
1174
+
1175
+ while (isspace(value_start[0]))
1176
+ value_start++;
1177
+
1178
+ if (value_start[0] == '\0') {
1179
+ *var_value = NULL;
1180
+ goto out;
1181
+ }
1182
+
1183
+ 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;
1188
+ git__free(*var_name);
1189
+ }
1190
+ goto out;
1191
+ }
1192
+
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;
1199
+ }
1200
+
1201
+ *var_value = tmp;
1202
+ } else {
1203
+ /* If there is no value, boolean true is assumed */
1204
+ *var_value = NULL;
1205
+ }
1206
+
1207
+ out:
1208
+ git__free(line);
1209
+ return error;
1210
+ }