packages feed

hlibgit2 0.6.0 → 0.17.0.1

raw patch · 152 files changed

+38292/−37 lines, 152 filesdep +zlib

Dependencies added: zlib

Files

Bindings/Libgit2/Windows.hsc view
@@ -4,6 +4,8 @@ #strict_import  import Bindings.Libgit2.Common+#ifdef GIT_WIN32 #ccall gitwin_set_codepage , CUInt -> IO () #ccall gitwin_get_codepage , IO (CUInt) #ccall gitwin_set_utf8 , IO ()+#endif
hlibgit2.cabal view
@@ -1,45 +1,48 @@ Name:                hlibgit2-Version:             0.6.0+Version:             0.17.0.1 Synopsis:            Low-level bindings to libgit2.-Description:         Bindings to libgit2 v0.16.0-471-g5b9fac3+Description:         Bindings to libgit2 v0.17.0 License-file:        LICENSE License:             MIT-Author:              Sakari Jokinen, Jacob Stanley, John Wiegley+Author:              John Wiegley, Sakari Jokinen, Jacob Stanleyyeah, Maintainer:          johnw@newartisans.com Build-Type:          Simple Cabal-Version:       >=1.10 Category:            FFI  Extra-source-files:+  libgit2/deps/http-parser/*.h   libgit2/include/git2.h   libgit2/include/git2/*.h+  libgit2/src/*.h+  libgit2/src/unix/*.h+  libgit2/src/win32/*.h+  libgit2/src/xdiff/*.h  Source-repository head   type: git   location: git://github.com/jwiegley/hlibgit2.git -Source-repository this-  type: git-  location: git://github.com/jwiegley/hlibgit2.git-  tag: hlibgit2-0.1-17-gd7b22d6- Test-suite smoke   default-language: Haskell98   type: exitcode-stdio-1.0   main-is: Main.hs-  hs-source-dirs: tests+  hs-source-dirs: test   build-depends:       base >=3     , hlibgit2     , process  Library+  hs-source-dirs: .   default-language: Haskell98   default-extensions:     ForeignFunctionInterface   build-depends:       base            >= 3 && < 5     , bindings-DSL    >= 1.0.11+    , zlib            >= 0.5.4+   exposed-modules:     Bindings.Libgit2     Bindings.Libgit2.Attr@@ -74,6 +77,7 @@     Bindings.Libgit2.Types     Bindings.Libgit2.Version     Bindings.Libgit2.Windows+   c-sources:     Bindings/Libgit2/Attr.hsc.helper.c     Bindings/Libgit2/Blob.hsc.helper.c@@ -81,8 +85,96 @@     Bindings/Libgit2/Common.hsc.helper.c     Bindings/Libgit2/Tag.hsc.helper.c     Bindings/Libgit2/Tree.hsc.helper.c+    libgit2/src/attr.c+    libgit2/src/attr_file.c+    libgit2/src/blob.c+    libgit2/src/branch.c+    libgit2/src/buffer.c+    libgit2/src/cache.c+    libgit2/src/commit.c+    libgit2/src/config.c+    libgit2/src/config_cache.c+    libgit2/src/config_file.c+    libgit2/src/crlf.c+    libgit2/src/delta-apply.c+    libgit2/src/diff.c+    libgit2/src/diff_output.c+    libgit2/src/errors.c+    libgit2/src/fetch.c+    libgit2/src/filebuf.c+    libgit2/src/fileops.c+    libgit2/src/filter.c+    libgit2/src/global.c+    libgit2/src/hash.c+    libgit2/src/ignore.c+    libgit2/src/index.c+    libgit2/src/indexer.c+    libgit2/src/iterator.c+    libgit2/src/message.c+    libgit2/src/mwindow.c+    libgit2/src/netops.c+    libgit2/src/notes.c+    libgit2/src/object.c+    libgit2/src/odb.c+    libgit2/src/odb_loose.c+    libgit2/src/odb_pack.c+    libgit2/src/oid.c+    libgit2/src/pack.c+    libgit2/src/path.c+    libgit2/src/pkt.c+    libgit2/src/pool.c+    libgit2/src/posix.c+    libgit2/src/pqueue.c+    libgit2/src/protocol.c+    libgit2/src/reflog.c+    libgit2/src/refs.c+    libgit2/src/refspec.c+    libgit2/src/remote.c+    libgit2/src/repository.c+    libgit2/src/revwalk.c+    libgit2/src/sha1.c+    libgit2/src/sha1_lookup.c+    libgit2/src/signature.c+    libgit2/src/status.c+    libgit2/src/submodule.c+    libgit2/src/tag.c+    libgit2/src/thread-utils.c+    libgit2/src/transport.c+    libgit2/src/tree-cache.c+    libgit2/src/tree.c+    libgit2/src/tsort.c+    libgit2/src/util.c+    libgit2/src/vector.c+    libgit2/src/transports/git.c+    libgit2/src/transports/http.c+    libgit2/src/transports/local.c+    libgit2/src/xdiff/xdiffi.c+    libgit2/src/xdiff/xemit.c+    libgit2/src/xdiff/xhistogram.c+    libgit2/src/xdiff/xmerge.c+    libgit2/src/xdiff/xpatience.c+    libgit2/src/xdiff/xprepare.c+    libgit2/src/xdiff/xutils.c+    libgit2/deps/http-parser/http_parser.c -  include-dirs: libgit2/include-  extra-libraries: git2+  include-dirs:+    libgit2/include+    libgit2/src+    libgit2/deps/http-parser++  cc-options: -DGIT_THREADS -D_FILE_OFFSET_BITS=64   if os(windows)-    cc-options: -DGIT_WIN32 -DNO_VIZ+    cc-options: -DGIT_WIN32 -DWIN32 -D_DEBUG -D_WIN32_WINNT=0x0501+    c-sources:+      libgit2/src/win32/dir.c+      libgit2/src/win32/pthread.c+      libgit2/src/win32/utf-conv.c+      libgit2/src/win32/map.c+      libgit2/src/win32/posix_w32.c+    include-dirs:+      libgit2/src/win32+  else+    c-sources:+      libgit2/src/unix/map.c+    include-dirs:+      libgit2/src/unix
+ libgit2/deps/http-parser/http_parser.c view
@@ -0,0 +1,1778 @@+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev+ *+ * Additional changes are licensed under the same terms as NGINX and+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to+ * deal in the Software without restriction, including without limitation the+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or+ * sell copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS+ * IN THE SOFTWARE.+ */+#include <http_parser.h>+#include <assert.h>+#include <stddef.h>+++#ifndef MIN+# define MIN(a,b) ((a) < (b) ? (a) : (b))+#endif+++#if HTTP_PARSER_DEBUG+#define SET_ERRNO(e)                                                 \+do {                                                                 \+  parser->http_errno = (e);                                          \+  parser->error_lineno = __LINE__;                                   \+} while (0)+#else+#define SET_ERRNO(e)                                                 \+do {                                                                 \+  parser->http_errno = (e);                                          \+} while(0)+#endif+++#define CALLBACK2(FOR)                                               \+do {                                                                 \+  if (settings->on_##FOR) {                                          \+    if (0 != settings->on_##FOR(parser)) {                           \+      SET_ERRNO(HPE_CB_##FOR);                                       \+      return (p - data);                                             \+    }                                                                \+  }                                                                  \+} while (0)+++#define MARK(FOR)                                                    \+do {                                                                 \+  FOR##_mark = p;                                                    \+} while (0)++#define CALLBACK(FOR)                                                \+do {                                                                 \+  if (FOR##_mark) {                                                  \+    if (settings->on_##FOR) {                                        \+      if (0 != settings->on_##FOR(parser,                            \+                                 FOR##_mark,                         \+                                 p - FOR##_mark))                    \+      {                                                              \+        SET_ERRNO(HPE_CB_##FOR);                                     \+        return (p - data);                                           \+      }                                                              \+    }                                                                \+    FOR##_mark = NULL;                                               \+  }                                                                  \+} while (0)+++#define PROXY_CONNECTION "proxy-connection"+#define CONNECTION "connection"+#define CONTENT_LENGTH "content-length"+#define TRANSFER_ENCODING "transfer-encoding"+#define UPGRADE "upgrade"+#define CHUNKED "chunked"+#define KEEP_ALIVE "keep-alive"+#define CLOSE "close"+++static const char *method_strings[] =+  { "DELETE"+  , "GET"+  , "HEAD"+  , "POST"+  , "PUT"+  , "CONNECT"+  , "OPTIONS"+  , "TRACE"+  , "COPY"+  , "LOCK"+  , "MKCOL"+  , "MOVE"+  , "PROPFIND"+  , "PROPPATCH"+  , "UNLOCK"+  , "REPORT"+  , "MKACTIVITY"+  , "CHECKOUT"+  , "MERGE"+  , "M-SEARCH"+  , "NOTIFY"+  , "SUBSCRIBE"+  , "UNSUBSCRIBE"+  , "PATCH"+  };+++/* Tokens as defined by rfc 2616. Also lowercases them.+ *        token       = 1*<any CHAR except CTLs or separators>+ *     separators     = "(" | ")" | "<" | ">" | "@"+ *                    | "," | ";" | ":" | "\" | <">+ *                    | "/" | "[" | "]" | "?" | "="+ *                    | "{" | "}" | SP | HT+ */+static const char tokens[256] = {+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */+        0,       0,       0,       0,       0,       0,       0,       0,+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */+        0,       0,       0,       0,       0,       0,       0,       0,+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */+        0,       0,       0,       0,       0,       0,       0,       0,+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */+        0,       0,       0,       0,       0,       0,       0,       0,+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */+       ' ',      '!',     '"',     '#',     '$',     '%',     '&',    '\'',+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */+        0,       0,      '*',     '+',      0,      '-',     '.',     '/',+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */+       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */+       '8',     '9',      0,       0,       0,       0,       0,       0,+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */+        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */+       'x',     'y',     'z',      0,       0,       0,      '^',     '_',+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */+       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */+       'x',     'y',     'z',      0,      '|',     '}',     '~',       0 };+++static const int8_t unhex[256] =+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1+  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1+  };+++static const uint8_t normal_url_char[256] = {+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */+        0,       0,       0,       0,       0,       0,       0,       0,+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */+        0,       0,       0,       0,       0,       0,       0,       0,+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */+        0,       0,       0,       0,       0,       0,       0,       0,+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */+        0,       0,       0,       0,       0,       0,       0,       0,+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */+        0,       1,       1,       0,       1,       1,       1,       1,+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */+        1,       1,       1,       1,       1,       1,       1,       1,+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */+        1,       1,       1,       1,       1,       1,       1,       1,+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */+        1,       1,       1,       1,       1,       1,       1,       0,+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */+        1,       1,       1,       1,       1,       1,       1,       1,+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */+        1,       1,       1,       1,       1,       1,       1,       1,+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */+        1,       1,       1,       1,       1,       1,       1,       1,+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */+        1,       1,       1,       1,       1,       1,       1,       1,+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */+        1,       1,       1,       1,       1,       1,       1,       1,+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */+        1,       1,       1,       1,       1,       1,       1,       1,+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */+        1,       1,       1,       1,       1,       1,       1,       1,+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */+        1,       1,       1,       1,       1,       1,       1,       0, };+++enum state+  { s_dead = 1 /* important that this is > 0 */++  , s_start_req_or_res+  , s_res_or_resp_H+  , s_start_res+  , s_res_H+  , s_res_HT+  , s_res_HTT+  , s_res_HTTP+  , s_res_first_http_major+  , s_res_http_major+  , s_res_first_http_minor+  , s_res_http_minor+  , s_res_first_status_code+  , s_res_status_code+  , s_res_status+  , s_res_line_almost_done++  , s_start_req++  , s_req_method+  , s_req_spaces_before_url+  , s_req_schema+  , s_req_schema_slash+  , s_req_schema_slash_slash+  , s_req_host+  , s_req_port+  , s_req_path+  , s_req_query_string_start+  , s_req_query_string+  , s_req_fragment_start+  , s_req_fragment+  , s_req_http_start+  , s_req_http_H+  , s_req_http_HT+  , s_req_http_HTT+  , s_req_http_HTTP+  , s_req_first_http_major+  , s_req_http_major+  , s_req_first_http_minor+  , s_req_http_minor+  , s_req_line_almost_done++  , s_header_field_start+  , s_header_field+  , s_header_value_start+  , s_header_value+  , s_header_value_lws++  , s_header_almost_done++  , s_chunk_size_start+  , s_chunk_size+  , s_chunk_parameters+  , s_chunk_size_almost_done+  +  , s_headers_almost_done+  /* Important: 's_headers_almost_done' must be the last 'header' state. All+   * states beyond this must be 'body' states. It is used for overflow+   * checking. See the PARSING_HEADER() macro.+   */++  , s_chunk_data+  , s_chunk_data_almost_done+  , s_chunk_data_done++  , s_body_identity+  , s_body_identity_eof+  };+++#define PARSING_HEADER(state) (state <= s_headers_almost_done)+++enum header_states+  { h_general = 0+  , h_C+  , h_CO+  , h_CON++  , h_matching_connection+  , h_matching_proxy_connection+  , h_matching_content_length+  , h_matching_transfer_encoding+  , h_matching_upgrade++  , h_connection+  , h_content_length+  , h_transfer_encoding+  , h_upgrade++  , h_matching_transfer_encoding_chunked+  , h_matching_connection_keep_alive+  , h_matching_connection_close++  , h_transfer_encoding_chunked+  , h_connection_keep_alive+  , h_connection_close+  };+++/* Macros for character classes; depends on strict-mode  */+#define CR                  '\r'+#define LF                  '\n'+#define LOWER(c)            (unsigned char)(c | 0x20)+#define TOKEN(c)            (tokens[(unsigned char)c])+#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')+#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')+#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))++#if HTTP_PARSER_STRICT+#define IS_URL_CHAR(c)      (normal_url_char[(unsigned char) (c)])+#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')+#else+#define IS_URL_CHAR(c)                                                         \+  (normal_url_char[(unsigned char) (c)] || ((c) & 0x80))+#define IS_HOST_CHAR(c)                                                        \+  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')+#endif+++#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)+++#if HTTP_PARSER_STRICT+# define STRICT_CHECK(cond)                                          \+do {                                                                 \+  if (cond) {                                                        \+    SET_ERRNO(HPE_STRICT);                                           \+    goto error;                                                      \+  }                                                                  \+} while (0)+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)+#else+# define STRICT_CHECK(cond)+# define NEW_MESSAGE() start_state+#endif+++/* Map errno values to strings for human-readable output */+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },+static struct {+  const char *name;+  const char *description;+} http_strerror_tab[] = {+  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)+};+#undef HTTP_STRERROR_GEN+++size_t http_parser_execute (http_parser *parser,+                            const http_parser_settings *settings,+                            const char *data,+                            size_t len)+{+  char c, ch;+  int8_t unhex_val;+  const char *p = data, *pe;+  size_t to_read;+  enum state state;+  enum header_states header_state;+  size_t index = parser->index;+  size_t nread = parser->nread;+  const char *header_field_mark, *header_value_mark, *url_mark;+  const char *matcher;++  /* We're in an error state. Don't bother doing anything. */+  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {+    return 0;+  }++  state = (enum state) parser->state;+  header_state = (enum header_states) parser->header_state;++  if (len == 0) {+    switch (state) {+      case s_body_identity_eof:+        CALLBACK2(message_complete);+        return 0;++      case s_dead:+      case s_start_req_or_res:+      case s_start_res:+      case s_start_req:+        return 0;++      default:+        SET_ERRNO(HPE_INVALID_EOF_STATE);+        return 1;+    }+  }++  /* technically we could combine all of these (except for url_mark) into one+     variable, saving stack space, but it seems more clear to have them+     separated. */+  header_field_mark = 0;+  header_value_mark = 0;+  url_mark = 0;++  if (state == s_header_field)+    header_field_mark = data;+  if (state == s_header_value)+    header_value_mark = data;+  if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash+      || state == s_req_schema_slash_slash || state == s_req_port+      || state == s_req_query_string_start || state == s_req_query_string+      || state == s_req_host+      || state == s_req_fragment_start || state == s_req_fragment)+    url_mark = data;++  for (p=data, pe=data+len; p != pe; p++) {+    ch = *p;++    if (PARSING_HEADER(state)) {+      ++nread;+      /* Buffer overflow attack */+      if (nread > HTTP_MAX_HEADER_SIZE) {+        SET_ERRNO(HPE_HEADER_OVERFLOW);+        goto error;+      }+    }++    switch (state) {++      case s_dead:+        /* this state is used after a 'Connection: close' message+         * the parser will error out if it reads another message+         */+        SET_ERRNO(HPE_CLOSED_CONNECTION);+        goto error;++      case s_start_req_or_res:+      {+        if (ch == CR || ch == LF)+          break;+        parser->flags = 0;+        parser->content_length = -1;++        CALLBACK2(message_begin);++        if (ch == 'H')+          state = s_res_or_resp_H;+        else {+          parser->type = HTTP_REQUEST;+          goto start_req_method_assign;+        }+        break;+      }++      case s_res_or_resp_H:+        if (ch == 'T') {+          parser->type = HTTP_RESPONSE;+          state = s_res_HT;+        } else {+          if (ch != 'E') {+            SET_ERRNO(HPE_INVALID_CONSTANT);+            goto error;+          }++          parser->type = HTTP_REQUEST;+          parser->method = HTTP_HEAD;+          index = 2;+          state = s_req_method;+        }+        break;++      case s_start_res:+      {+        parser->flags = 0;+        parser->content_length = -1;++        CALLBACK2(message_begin);++        switch (ch) {+          case 'H':+            state = s_res_H;+            break;++          case CR:+          case LF:+            break;++          default:+            SET_ERRNO(HPE_INVALID_CONSTANT);+            goto error;+        }+        break;+      }++      case s_res_H:+        STRICT_CHECK(ch != 'T');+        state = s_res_HT;+        break;++      case s_res_HT:+        STRICT_CHECK(ch != 'T');+        state = s_res_HTT;+        break;++      case s_res_HTT:+        STRICT_CHECK(ch != 'P');+        state = s_res_HTTP;+        break;++      case s_res_HTTP:+        STRICT_CHECK(ch != '/');+        state = s_res_first_http_major;+        break;++      case s_res_first_http_major:+        if (ch < '1' || ch > '9') {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        parser->http_major = ch - '0';+        state = s_res_http_major;+        break;++      /* major HTTP version or dot */+      case s_res_http_major:+      {+        if (ch == '.') {+          state = s_res_first_http_minor;+          break;+        }++        if (!IS_NUM(ch)) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        parser->http_major *= 10;+        parser->http_major += ch - '0';++        if (parser->http_major > 999) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        break;+      }++      /* first digit of minor HTTP version */+      case s_res_first_http_minor:+        if (!IS_NUM(ch)) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        parser->http_minor = ch - '0';+        state = s_res_http_minor;+        break;++      /* minor HTTP version or end of request line */+      case s_res_http_minor:+      {+        if (ch == ' ') {+          state = s_res_first_status_code;+          break;+        }++        if (!IS_NUM(ch)) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        parser->http_minor *= 10;+        parser->http_minor += ch - '0';++        if (parser->http_minor > 999) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        break;+      }++      case s_res_first_status_code:+      {+        if (!IS_NUM(ch)) {+          if (ch == ' ') {+            break;+          }++          SET_ERRNO(HPE_INVALID_STATUS);+          goto error;+        }+        parser->status_code = ch - '0';+        state = s_res_status_code;+        break;+      }++      case s_res_status_code:+      {+        if (!IS_NUM(ch)) {+          switch (ch) {+            case ' ':+              state = s_res_status;+              break;+            case CR:+              state = s_res_line_almost_done;+              break;+            case LF:+              state = s_header_field_start;+              break;+            default:+              SET_ERRNO(HPE_INVALID_STATUS);+              goto error;+          }+          break;+        }++        parser->status_code *= 10;+        parser->status_code += ch - '0';++        if (parser->status_code > 999) {+          SET_ERRNO(HPE_INVALID_STATUS);+          goto error;+        }++        break;+      }++      case s_res_status:+        /* the human readable status. e.g. "NOT FOUND"+         * we are not humans so just ignore this */+        if (ch == CR) {+          state = s_res_line_almost_done;+          break;+        }++        if (ch == LF) {+          state = s_header_field_start;+          break;+        }+        break;++      case s_res_line_almost_done:+        STRICT_CHECK(ch != LF);+        state = s_header_field_start;+        break;++      case s_start_req:+      {+        if (ch == CR || ch == LF)+          break;+        parser->flags = 0;+        parser->content_length = -1;++        CALLBACK2(message_begin);++        if (!IS_ALPHA(ch)) {+          SET_ERRNO(HPE_INVALID_METHOD);+          goto error;+        }++      start_req_method_assign:+        parser->method = (enum http_method) 0;+        index = 1;+        switch (ch) {+          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;+          case 'D': parser->method = HTTP_DELETE; break;+          case 'G': parser->method = HTTP_GET; break;+          case 'H': parser->method = HTTP_HEAD; break;+          case 'L': parser->method = HTTP_LOCK; break;+          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;+          case 'N': parser->method = HTTP_NOTIFY; break;+          case 'O': parser->method = HTTP_OPTIONS; break;+          case 'P': parser->method = HTTP_POST;+            /* or PROPFIND or PROPPATCH or PUT or PATCH */+            break;+          case 'R': parser->method = HTTP_REPORT; break;+          case 'S': parser->method = HTTP_SUBSCRIBE; break;+          case 'T': parser->method = HTTP_TRACE; break;+          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;+          default:+            SET_ERRNO(HPE_INVALID_METHOD);+            goto error;+        }+        state = s_req_method;+        break;+      }++      case s_req_method:+      {+        if (ch == '\0') {+          SET_ERRNO(HPE_INVALID_METHOD);+          goto error;+        }++        matcher = method_strings[parser->method];+        if (ch == ' ' && matcher[index] == '\0') {+          state = s_req_spaces_before_url;+        } else if (ch == matcher[index]) {+          ; /* nada */+        } else if (parser->method == HTTP_CONNECT) {+          if (index == 1 && ch == 'H') {+            parser->method = HTTP_CHECKOUT;+          } else if (index == 2  && ch == 'P') {+            parser->method = HTTP_COPY;+          } else {+            goto error;+          }+        } else if (parser->method == HTTP_MKCOL) {+          if (index == 1 && ch == 'O') {+            parser->method = HTTP_MOVE;+          } else if (index == 1 && ch == 'E') {+            parser->method = HTTP_MERGE;+          } else if (index == 1 && ch == '-') {+            parser->method = HTTP_MSEARCH;+          } else if (index == 2 && ch == 'A') {+            parser->method = HTTP_MKACTIVITY;+          } else {+            goto error;+          }+        } else if (index == 1 && parser->method == HTTP_POST) {+          if (ch == 'R') {+            parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */+          } else if (ch == 'U') {+            parser->method = HTTP_PUT;+          } else if (ch == 'A') {+            parser->method = HTTP_PATCH;+          } else {+            goto error;+          }+        } else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {+          parser->method = HTTP_UNSUBSCRIBE;+        } else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {+          parser->method = HTTP_PROPPATCH;+        } else {+          SET_ERRNO(HPE_INVALID_METHOD);+          goto error;+        }++        ++index;+        break;+      }+      case s_req_spaces_before_url:+      {+        if (ch == ' ') break;++        if (ch == '/' || ch == '*') {+          MARK(url);+          state = s_req_path;+          break;+        }++        /* Proxied requests are followed by scheme of an absolute URI (alpha).+         * CONNECT is followed by a hostname, which begins with alphanum.+         * All other methods are followed by '/' or '*' (handled above).+         */+        if (IS_ALPHA(ch) || (parser->method == HTTP_CONNECT && IS_NUM(ch))) {+          MARK(url);+          state = (parser->method == HTTP_CONNECT) ? s_req_host : s_req_schema;+          break;+        }++        SET_ERRNO(HPE_INVALID_URL);+        goto error;+      }++      case s_req_schema:+      {+        if (IS_ALPHA(ch)) break;++        if (ch == ':') {+          state = s_req_schema_slash;+          break;+        }++        SET_ERRNO(HPE_INVALID_URL);+        goto error;+      }++      case s_req_schema_slash:+        STRICT_CHECK(ch != '/');+        state = s_req_schema_slash_slash;+        break;++      case s_req_schema_slash_slash:+        STRICT_CHECK(ch != '/');+        state = s_req_host;+        break;++      case s_req_host:+      {+        if (IS_HOST_CHAR(ch)) break;+        switch (ch) {+          case ':':+            state = s_req_port;+            break;+          case '/':+            state = s_req_path;+            break;+          case ' ':+            /* The request line looks like:+             *   "GET http://foo.bar.com HTTP/1.1"+             * That is, there is no path.+             */+            CALLBACK(url);+            state = s_req_http_start;+            break;+          case '?':+            state = s_req_query_string_start;+            break;+          default:+            SET_ERRNO(HPE_INVALID_HOST);+            goto error;+        }+        break;+      }++      case s_req_port:+      {+        if (IS_NUM(ch)) break;+        switch (ch) {+          case '/':+            state = s_req_path;+            break;+          case ' ':+            /* The request line looks like:+             *   "GET http://foo.bar.com:1234 HTTP/1.1"+             * That is, there is no path.+             */+            CALLBACK(url);+            state = s_req_http_start;+            break;+          case '?':+            state = s_req_query_string_start;+            break;+          default:+            SET_ERRNO(HPE_INVALID_PORT);+            goto error;+        }+        break;+      }++      case s_req_path:+      {+        if (IS_URL_CHAR(ch)) break;++        switch (ch) {+          case ' ':+            CALLBACK(url);+            state = s_req_http_start;+            break;+          case CR:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_req_line_almost_done;+            break;+          case LF:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_header_field_start;+            break;+          case '?':+            state = s_req_query_string_start;+            break;+          case '#':+            state = s_req_fragment_start;+            break;+          default:+            SET_ERRNO(HPE_INVALID_PATH);+            goto error;+        }+        break;+      }++      case s_req_query_string_start:+      {+        if (IS_URL_CHAR(ch)) {+          state = s_req_query_string;+          break;+        }++        switch (ch) {+          case '?':+            break; /* XXX ignore extra '?' ... is this right? */+          case ' ':+            CALLBACK(url);+            state = s_req_http_start;+            break;+          case CR:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_req_line_almost_done;+            break;+          case LF:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_header_field_start;+            break;+          case '#':+            state = s_req_fragment_start;+            break;+          default:+            SET_ERRNO(HPE_INVALID_QUERY_STRING);+            goto error;+        }+        break;+      }++      case s_req_query_string:+      {+        if (IS_URL_CHAR(ch)) break;++        switch (ch) {+          case '?':+            /* allow extra '?' in query string */+            break;+          case ' ':+            CALLBACK(url);+            state = s_req_http_start;+            break;+          case CR:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_req_line_almost_done;+            break;+          case LF:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_header_field_start;+            break;+          case '#':+            state = s_req_fragment_start;+            break;+          default:+            SET_ERRNO(HPE_INVALID_QUERY_STRING);+            goto error;+        }+        break;+      }++      case s_req_fragment_start:+      {+        if (IS_URL_CHAR(ch)) {+          state = s_req_fragment;+          break;+        }++        switch (ch) {+          case ' ':+            CALLBACK(url);+            state = s_req_http_start;+            break;+          case CR:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_req_line_almost_done;+            break;+          case LF:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_header_field_start;+            break;+          case '?':+            state = s_req_fragment;+            break;+          case '#':+            break;+          default:+            SET_ERRNO(HPE_INVALID_FRAGMENT);+            goto error;+        }+        break;+      }++      case s_req_fragment:+      {+        if (IS_URL_CHAR(ch)) break;++        switch (ch) {+          case ' ':+            CALLBACK(url);+            state = s_req_http_start;+            break;+          case CR:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_req_line_almost_done;+            break;+          case LF:+            CALLBACK(url);+            parser->http_major = 0;+            parser->http_minor = 9;+            state = s_header_field_start;+            break;+          case '?':+          case '#':+            break;+          default:+            SET_ERRNO(HPE_INVALID_FRAGMENT);+            goto error;+        }+        break;+      }++      case s_req_http_start:+        switch (ch) {+          case 'H':+            state = s_req_http_H;+            break;+          case ' ':+            break;+          default:+            SET_ERRNO(HPE_INVALID_CONSTANT);+            goto error;+        }+        break;++      case s_req_http_H:+        STRICT_CHECK(ch != 'T');+        state = s_req_http_HT;+        break;++      case s_req_http_HT:+        STRICT_CHECK(ch != 'T');+        state = s_req_http_HTT;+        break;++      case s_req_http_HTT:+        STRICT_CHECK(ch != 'P');+        state = s_req_http_HTTP;+        break;++      case s_req_http_HTTP:+        STRICT_CHECK(ch != '/');+        state = s_req_first_http_major;+        break;++      /* first digit of major HTTP version */+      case s_req_first_http_major:+        if (ch < '1' || ch > '9') {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        parser->http_major = ch - '0';+        state = s_req_http_major;+        break;++      /* major HTTP version or dot */+      case s_req_http_major:+      {+        if (ch == '.') {+          state = s_req_first_http_minor;+          break;+        }++        if (!IS_NUM(ch)) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        parser->http_major *= 10;+        parser->http_major += ch - '0';++        if (parser->http_major > 999) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        break;+      }++      /* first digit of minor HTTP version */+      case s_req_first_http_minor:+        if (!IS_NUM(ch)) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        parser->http_minor = ch - '0';+        state = s_req_http_minor;+        break;++      /* minor HTTP version or end of request line */+      case s_req_http_minor:+      {+        if (ch == CR) {+          state = s_req_line_almost_done;+          break;+        }++        if (ch == LF) {+          state = s_header_field_start;+          break;+        }++        /* XXX allow spaces after digit? */++        if (!IS_NUM(ch)) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        parser->http_minor *= 10;+        parser->http_minor += ch - '0';++        if (parser->http_minor > 999) {+          SET_ERRNO(HPE_INVALID_VERSION);+          goto error;+        }++        break;+      }++      /* end of request line */+      case s_req_line_almost_done:+      {+        if (ch != LF) {+          SET_ERRNO(HPE_LF_EXPECTED);+          goto error;+        }++        state = s_header_field_start;+        break;+      }++      case s_header_field_start:+      header_field_start:+      {+        if (ch == CR) {+          state = s_headers_almost_done;+          break;+        }++        if (ch == LF) {+          /* they might be just sending \n instead of \r\n so this would be+           * the second \n to denote the end of headers*/+          state = s_headers_almost_done;+          goto headers_almost_done;+        }++        c = TOKEN(ch);++        if (!c) {+          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);+          goto error;+        }++        MARK(header_field);++        index = 0;+        state = s_header_field;++        switch (c) {+          case 'c':+            header_state = h_C;+            break;++          case 'p':+            header_state = h_matching_proxy_connection;+            break;++          case 't':+            header_state = h_matching_transfer_encoding;+            break;++          case 'u':+            header_state = h_matching_upgrade;+            break;++          default:+            header_state = h_general;+            break;+        }+        break;+      }++      case s_header_field:+      {+        c = TOKEN(ch);++        if (c) {+          switch (header_state) {+            case h_general:+              break;++            case h_C:+              index++;+              header_state = (c == 'o' ? h_CO : h_general);+              break;++            case h_CO:+              index++;+              header_state = (c == 'n' ? h_CON : h_general);+              break;++            case h_CON:+              index++;+              switch (c) {+                case 'n':+                  header_state = h_matching_connection;+                  break;+                case 't':+                  header_state = h_matching_content_length;+                  break;+                default:+                  header_state = h_general;+                  break;+              }+              break;++            /* connection */++            case h_matching_connection:+              index++;+              if (index > sizeof(CONNECTION)-1+                  || c != CONNECTION[index]) {+                header_state = h_general;+              } else if (index == sizeof(CONNECTION)-2) {+                header_state = h_connection;+              }+              break;++            /* proxy-connection */++            case h_matching_proxy_connection:+              index++;+              if (index > sizeof(PROXY_CONNECTION)-1+                  || c != PROXY_CONNECTION[index]) {+                header_state = h_general;+              } else if (index == sizeof(PROXY_CONNECTION)-2) {+                header_state = h_connection;+              }+              break;++            /* content-length */++            case h_matching_content_length:+              index++;+              if (index > sizeof(CONTENT_LENGTH)-1+                  || c != CONTENT_LENGTH[index]) {+                header_state = h_general;+              } else if (index == sizeof(CONTENT_LENGTH)-2) {+                header_state = h_content_length;+              }+              break;++            /* transfer-encoding */++            case h_matching_transfer_encoding:+              index++;+              if (index > sizeof(TRANSFER_ENCODING)-1+                  || c != TRANSFER_ENCODING[index]) {+                header_state = h_general;+              } else if (index == sizeof(TRANSFER_ENCODING)-2) {+                header_state = h_transfer_encoding;+              }+              break;++            /* upgrade */++            case h_matching_upgrade:+              index++;+              if (index > sizeof(UPGRADE)-1+                  || c != UPGRADE[index]) {+                header_state = h_general;+              } else if (index == sizeof(UPGRADE)-2) {+                header_state = h_upgrade;+              }+              break;++            case h_connection:+            case h_content_length:+            case h_transfer_encoding:+            case h_upgrade:+              if (ch != ' ') header_state = h_general;+              break;++            default:+              assert(0 && "Unknown header_state");+              break;+          }+          break;+        }++        if (ch == ':') {+          CALLBACK(header_field);+          state = s_header_value_start;+          break;+        }++        if (ch == CR) {+          state = s_header_almost_done;+          CALLBACK(header_field);+          break;+        }++        if (ch == LF) {+          CALLBACK(header_field);+          state = s_header_field_start;+          break;+        }++        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);+        goto error;+      }++      case s_header_value_start:+      {+        if (ch == ' ' || ch == '\t') break;++        MARK(header_value);++        state = s_header_value;+        index = 0;++        if (ch == CR) {+          CALLBACK(header_value);+          header_state = h_general;+          state = s_header_almost_done;+          break;+        }++        if (ch == LF) {+          CALLBACK(header_value);+          state = s_header_field_start;+          break;+        }++        c = LOWER(ch);++        switch (header_state) {+          case h_upgrade:+            parser->flags |= F_UPGRADE;+            header_state = h_general;+            break;++          case h_transfer_encoding:+            /* looking for 'Transfer-Encoding: chunked' */+            if ('c' == c) {+              header_state = h_matching_transfer_encoding_chunked;+            } else {+              header_state = h_general;+            }+            break;++          case h_content_length:+            if (!IS_NUM(ch)) {+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);+              goto error;+            }++            parser->content_length = ch - '0';+            break;++          case h_connection:+            /* looking for 'Connection: keep-alive' */+            if (c == 'k') {+              header_state = h_matching_connection_keep_alive;+            /* looking for 'Connection: close' */+            } else if (c == 'c') {+              header_state = h_matching_connection_close;+            } else {+              header_state = h_general;+            }+            break;++          default:+            header_state = h_general;+            break;+        }+        break;+      }++      case s_header_value:+      {++        if (ch == CR) {+          CALLBACK(header_value);+          state = s_header_almost_done;+          break;+        }++        if (ch == LF) {+          CALLBACK(header_value);+          goto header_almost_done;+        }++        c = LOWER(ch);++        switch (header_state) {+          case h_general:+            break;++          case h_connection:+          case h_transfer_encoding:+            assert(0 && "Shouldn't get here.");+            break;++          case h_content_length:+            if (ch == ' ') break;+            if (!IS_NUM(ch)) {+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);+              goto error;+            }++            parser->content_length *= 10;+            parser->content_length += ch - '0';+            break;++          /* Transfer-Encoding: chunked */+          case h_matching_transfer_encoding_chunked:+            index++;+            if (index > sizeof(CHUNKED)-1+                || c != CHUNKED[index]) {+              header_state = h_general;+            } else if (index == sizeof(CHUNKED)-2) {+              header_state = h_transfer_encoding_chunked;+            }+            break;++          /* looking for 'Connection: keep-alive' */+          case h_matching_connection_keep_alive:+            index++;+            if (index > sizeof(KEEP_ALIVE)-1+                || c != KEEP_ALIVE[index]) {+              header_state = h_general;+            } else if (index == sizeof(KEEP_ALIVE)-2) {+              header_state = h_connection_keep_alive;+            }+            break;++          /* looking for 'Connection: close' */+          case h_matching_connection_close:+            index++;+            if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {+              header_state = h_general;+            } else if (index == sizeof(CLOSE)-2) {+              header_state = h_connection_close;+            }+            break;++          case h_transfer_encoding_chunked:+          case h_connection_keep_alive:+          case h_connection_close:+            if (ch != ' ') header_state = h_general;+            break;++          default:+            state = s_header_value;+            header_state = h_general;+            break;+        }+        break;+      }++      case s_header_almost_done:+      header_almost_done:+      {+        STRICT_CHECK(ch != LF);++        state = s_header_value_lws;++        switch (header_state) {+          case h_connection_keep_alive:+            parser->flags |= F_CONNECTION_KEEP_ALIVE;+            break;+          case h_connection_close:+            parser->flags |= F_CONNECTION_CLOSE;+            break;+          case h_transfer_encoding_chunked:+            parser->flags |= F_CHUNKED;+            break;+          default:+            break;+        }+        break;+      }++      case s_header_value_lws:+      {+        if (ch == ' ' || ch == '\t')+          state = s_header_value_start;+        else+        {+          state = s_header_field_start;+          goto header_field_start;+        }+        break;+      }++      case s_headers_almost_done:+      headers_almost_done:+      {+        STRICT_CHECK(ch != LF);++        if (parser->flags & F_TRAILING) {+          /* End of a chunked request */+          CALLBACK2(message_complete);+          state = NEW_MESSAGE();+          break;+        }++        nread = 0;++        if (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT) {+          parser->upgrade = 1;+        }++        /* Here we call the headers_complete callback. This is somewhat+         * different than other callbacks because if the user returns 1, we+         * will interpret that as saying that this message has no body. This+         * is needed for the annoying case of recieving a response to a HEAD+         * request.+         */+        if (settings->on_headers_complete) {+          switch (settings->on_headers_complete(parser)) {+            case 0:+              break;++            case 1:+              parser->flags |= F_SKIPBODY;+              break;++            default:+              parser->state = state;+              SET_ERRNO(HPE_CB_headers_complete);+              return p - data; /* Error */+          }+        }++        /* Exit, the rest of the connect is in a different protocol. */+        if (parser->upgrade) {+          CALLBACK2(message_complete);+          return (p - data) + 1;+        }++        if (parser->flags & F_SKIPBODY) {+          CALLBACK2(message_complete);+          state = NEW_MESSAGE();+        } else if (parser->flags & F_CHUNKED) {+          /* chunked encoding - ignore Content-Length header */+          state = s_chunk_size_start;+        } else {+          if (parser->content_length == 0) {+            /* Content-Length header given but zero: Content-Length: 0\r\n */+            CALLBACK2(message_complete);+            state = NEW_MESSAGE();+          } else if (parser->content_length > 0) {+            /* Content-Length header given and non-zero */+            state = s_body_identity;+          } else {+            if (parser->type == HTTP_REQUEST || http_should_keep_alive(parser)) {+              /* Assume content-length 0 - read the next */+              CALLBACK2(message_complete);+              state = NEW_MESSAGE();+            } else {+              /* Read body until EOF */+              state = s_body_identity_eof;+            }+          }+        }++        break;+      }++      case s_body_identity:+        to_read = (size_t)MIN(pe - p, parser->content_length);+        if (to_read > 0) {+          if (settings->on_body) settings->on_body(parser, p, to_read);+          p += to_read - 1;+          parser->content_length -= to_read;+          if (parser->content_length == 0) {+            CALLBACK2(message_complete);+            state = NEW_MESSAGE();+          }+        }+        break;++      /* read until EOF */+      case s_body_identity_eof:+        to_read = pe - p;+        if (to_read > 0) {+          if (settings->on_body) settings->on_body(parser, p, to_read);+          p += to_read - 1;+        }+        break;++      case s_chunk_size_start:+      {+        assert(nread == 1);+        assert(parser->flags & F_CHUNKED);++        unhex_val = unhex[(unsigned char)ch];+        if (unhex_val == -1) {+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);+          goto error;+        }++        parser->content_length = unhex_val;+        state = s_chunk_size;+        break;+      }++      case s_chunk_size:+      {+        assert(parser->flags & F_CHUNKED);++        if (ch == CR) {+          state = s_chunk_size_almost_done;+          break;+        }++        unhex_val = unhex[(unsigned char)ch];++        if (unhex_val == -1) {+          if (ch == ';' || ch == ' ') {+            state = s_chunk_parameters;+            break;+          }++          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);+          goto error;+        }++        parser->content_length *= 16;+        parser->content_length += unhex_val;+        break;+      }++      case s_chunk_parameters:+      {+        assert(parser->flags & F_CHUNKED);+        /* just ignore this shit. TODO check for overflow */+        if (ch == CR) {+          state = s_chunk_size_almost_done;+          break;+        }+        break;+      }++      case s_chunk_size_almost_done:+      {+        assert(parser->flags & F_CHUNKED);+        STRICT_CHECK(ch != LF);++        nread = 0;++        if (parser->content_length == 0) {+          parser->flags |= F_TRAILING;+          state = s_header_field_start;+        } else {+          state = s_chunk_data;+        }+        break;+      }++      case s_chunk_data:+      {+        assert(parser->flags & F_CHUNKED);++        to_read = (size_t)MIN(pe - p, parser->content_length);++        if (to_read > 0) {+          if (settings->on_body) settings->on_body(parser, p, to_read);+          p += to_read - 1;+        }++        if ((signed)to_read == parser->content_length) {+          state = s_chunk_data_almost_done;+        }++        parser->content_length -= to_read;+        break;+      }++      case s_chunk_data_almost_done:+        assert(parser->flags & F_CHUNKED);+        STRICT_CHECK(ch != CR);+        state = s_chunk_data_done;+        break;++      case s_chunk_data_done:+        assert(parser->flags & F_CHUNKED);+        STRICT_CHECK(ch != LF);+        state = s_chunk_size_start;+        break;++      default:+        assert(0 && "unhandled state");+        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);+        goto error;+    }+  }++  CALLBACK(header_field);+  CALLBACK(header_value);+  CALLBACK(url);++  parser->state = state;+  parser->header_state = header_state;+  parser->index = (unsigned char) index;+  parser->nread = nread;++  return len;++error:+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {+    SET_ERRNO(HPE_UNKNOWN);+  }++  return (p - data);+}+++int+http_should_keep_alive (http_parser *parser)+{+  if (parser->http_major > 0 && parser->http_minor > 0) {+    /* HTTP/1.1 */+    if (parser->flags & F_CONNECTION_CLOSE) {+      return 0;+    } else {+      return 1;+    }+  } else {+    /* HTTP/1.0 or earlier */+    if (parser->flags & F_CONNECTION_KEEP_ALIVE) {+      return 1;+    } else {+      return 0;+    }+  }+}+++const char * http_method_str (enum http_method m)+{+  return method_strings[m];+}+++void+http_parser_init (http_parser *parser, enum http_parser_type t)+{+  parser->type = t;+  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));+  parser->nread = 0;+  parser->upgrade = 0;+  parser->flags = 0;+  parser->method = 0;+  parser->http_errno = 0;+}++const char *+http_errno_name(enum http_errno err) {+  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));+  return http_strerror_tab[err].name;+}++const char *+http_errno_description(enum http_errno err) {+  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));+  return http_strerror_tab[err].description;+}
+ libgit2/deps/http-parser/http_parser.h view
@@ -0,0 +1,267 @@+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.+ *+ * Permission is hereby granted, free of charge, to any person obtaining a copy+ * of this software and associated documentation files (the "Software"), to+ * deal in the Software without restriction, including without limitation the+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or+ * sell copies of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be included in+ * all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS+ * IN THE SOFTWARE.+ */+#ifndef http_parser_h+#define http_parser_h+#ifdef __cplusplus+extern "C" {+#endif++#define HTTP_PARSER_VERSION_MAJOR 1+#define HTTP_PARSER_VERSION_MINOR 0++#ifdef _MSC_VER+	/* disable silly warnings */+#	pragma warning(disable: 4127 4214)+#endif++#include <sys/types.h>+#include "git2/common.h"++/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run+ * faster+ */+#ifndef HTTP_PARSER_STRICT+# define HTTP_PARSER_STRICT 1+#endif++/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to+ * the error reporting facility.+ */+#ifndef HTTP_PARSER_DEBUG+# define HTTP_PARSER_DEBUG 0+#endif+++/* Maximium header size allowed */+#define HTTP_MAX_HEADER_SIZE (80*1024)+++typedef struct http_parser http_parser;+typedef struct http_parser_settings http_parser_settings;+typedef struct http_parser_result http_parser_result;+++/* Callbacks should return non-zero to indicate an error. The parser will+ * then halt execution.+ *+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser+ * returning '1' from on_headers_complete will tell the parser that it+ * should not expect a body. This is used when receiving a response to a+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:+ * chunked' headers that indicate the presence of a body.+ *+ * http_data_cb does not return data chunks. It will be call arbitrarally+ * many times for each string. E.G. you might get 10 callbacks for "on_path"+ * each providing just a few characters more data.+ */+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);+typedef int (*http_cb) (http_parser*);+++/* Request Methods */+enum http_method+  { HTTP_DELETE    = 0+  , HTTP_GET+  , HTTP_HEAD+  , HTTP_POST+  , HTTP_PUT+  /* pathological */+  , HTTP_CONNECT+  , HTTP_OPTIONS+  , HTTP_TRACE+  /* webdav */+  , HTTP_COPY+  , HTTP_LOCK+  , HTTP_MKCOL+  , HTTP_MOVE+  , HTTP_PROPFIND+  , HTTP_PROPPATCH+  , HTTP_UNLOCK+  /* subversion */+  , HTTP_REPORT+  , HTTP_MKACTIVITY+  , HTTP_CHECKOUT+  , HTTP_MERGE+  /* upnp */+  , HTTP_MSEARCH+  , HTTP_NOTIFY+  , HTTP_SUBSCRIBE+  , HTTP_UNSUBSCRIBE+  /* RFC-5789 */+  , HTTP_PATCH+  };+++enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };+++/* Flag values for http_parser.flags field */+enum flags+  { F_CHUNKED               = 1 << 0+  , F_CONNECTION_KEEP_ALIVE = 1 << 1+  , F_CONNECTION_CLOSE      = 1 << 2+  , F_TRAILING              = 1 << 3+  , F_UPGRADE               = 1 << 4+  , F_SKIPBODY              = 1 << 5+  };+++/* Map for errno-related constants+ * + * The provided argument should be a macro that takes 2 arguments.+ */+#define HTTP_ERRNO_MAP(XX)                                           \+  /* No error */                                                     \+  XX(OK, "success")                                                  \+                                                                     \+  /* Callback-related errors */                                      \+  XX(CB_message_begin, "the on_message_begin callback failed")       \+  XX(CB_path, "the on_path callback failed")                         \+  XX(CB_query_string, "the on_query_string callback failed")         \+  XX(CB_url, "the on_url callback failed")                           \+  XX(CB_fragment, "the on_fragment callback failed")                 \+  XX(CB_header_field, "the on_header_field callback failed")         \+  XX(CB_header_value, "the on_header_value callback failed")         \+  XX(CB_headers_complete, "the on_headers_complete callback failed") \+  XX(CB_body, "the on_body callback failed")                         \+  XX(CB_message_complete, "the on_message_complete callback failed") \+                                                                     \+  /* Parsing-related errors */                                       \+  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \+  XX(HEADER_OVERFLOW,                                                \+     "too many header bytes seen; overflow detected")                \+  XX(CLOSED_CONNECTION,                                              \+     "data received after completed connection: close message")      \+  XX(INVALID_VERSION, "invalid HTTP version")                        \+  XX(INVALID_STATUS, "invalid HTTP status code")                     \+  XX(INVALID_METHOD, "invalid HTTP method")                          \+  XX(INVALID_URL, "invalid URL")                                     \+  XX(INVALID_HOST, "invalid host")                                   \+  XX(INVALID_PORT, "invalid port")                                   \+  XX(INVALID_PATH, "invalid path")                                   \+  XX(INVALID_QUERY_STRING, "invalid query string")                   \+  XX(INVALID_FRAGMENT, "invalid fragment")                           \+  XX(LF_EXPECTED, "LF character expected")                           \+  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \+  XX(INVALID_CONTENT_LENGTH,                                         \+     "invalid character in content-length header")                   \+  XX(INVALID_CHUNK_SIZE,                                             \+     "invalid character in chunk size header")                       \+  XX(INVALID_CONSTANT, "invalid constant string")                    \+  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\+  XX(STRICT, "strict mode assertion failed")                         \+  XX(UNKNOWN, "an unknown error occurred")+++/* Define HPE_* values for each errno value above */+#define HTTP_ERRNO_GEN(n, s) HPE_##n,+enum http_errno {+  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)+};+#undef HTTP_ERRNO_GEN+++/* Get an http_errno value from an http_parser */+#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)++/* Get the line number that generated the current error */+#if HTTP_PARSER_DEBUG+#define HTTP_PARSER_ERRNO_LINE(p)       ((p)->error_lineno)+#else+#define HTTP_PARSER_ERRNO_LINE(p)       0+#endif+++struct http_parser {+  /** PRIVATE **/+  unsigned char type : 2;+  unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */+  unsigned char state;+  unsigned char header_state;+  unsigned char index;++  size_t nread;+  int64_t content_length;++  /** READ-ONLY **/+  unsigned short http_major;+  unsigned short http_minor;+  unsigned short status_code; /* responses only */+  unsigned char method;    /* requests only */+  unsigned char http_errno : 7;++  /* 1 = Upgrade header was present and the parser has exited because of that.+   * 0 = No upgrade header present.+   * Should be checked when http_parser_execute() returns in addition to+   * error checking.+   */+  unsigned char upgrade : 1;++#if HTTP_PARSER_DEBUG+  uint32_t error_lineno;+#endif++  /** PUBLIC **/+  void *data; /* A pointer to get hook to the "connection" or "socket" object */+};+++struct http_parser_settings {+  http_cb      on_message_begin;+  http_data_cb on_url;+  http_data_cb on_header_field;+  http_data_cb on_header_value;+  http_cb      on_headers_complete;+  http_data_cb on_body;+  http_cb      on_message_complete;+};+++void http_parser_init(http_parser *parser, enum http_parser_type type);+++size_t http_parser_execute(http_parser *parser,+                           const http_parser_settings *settings,+                           const char *data,+                           size_t len);+++/* If http_should_keep_alive() in the on_headers_complete or+ * on_message_complete callback returns true, then this will be should be+ * the last message on the connection.+ * If you are the server, respond with the "Connection: close" header.+ * If you are the client, close the connection.+ */+int http_should_keep_alive(http_parser *parser);++/* Returns a string version of the HTTP method. */+const char *http_method_str(enum http_method m);++/* Return a string name of the given error */+const char *http_errno_name(enum http_errno err);++/* Return a string description of the given error */+const char *http_errno_description(enum http_errno err);++#ifdef __cplusplus+}+#endif+#endif
libgit2/include/git2/branch.h view
@@ -8,7 +8,6 @@ #define INCLUDE_git_branch_h__  #include "common.h"-#include "oid.h" #include "types.h"  /**
+ libgit2/src/attr.c view
@@ -0,0 +1,672 @@+#include "repository.h"+#include "fileops.h"+#include "config.h"+#include <ctype.h>++GIT__USE_STRMAP;++static int collect_attr_files(+	git_repository *repo,+	uint32_t flags,+	const char *path,+	git_vector *files);+++int git_attr_get(+	const char **value,+    git_repository *repo,+	uint32_t flags,+	const char *pathname,+	const char *name)+{+	int error;+	git_attr_path path;+	git_vector files = GIT_VECTOR_INIT;+	unsigned int i, j;+	git_attr_file *file;+	git_attr_name attr;+	git_attr_rule *rule;++	*value = NULL;++	if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)+		return -1;++	if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)+		goto cleanup;++	attr.name = name;+	attr.name_hash = git_attr_file__name_hash(name);++	git_vector_foreach(&files, i, file) {++		git_attr_file__foreach_matching_rule(file, &path, j, rule) {+			int pos = git_vector_bsearch(&rule->assigns, &attr);+			if (pos >= 0) {+				*value = ((git_attr_assignment *)git_vector_get(+							  &rule->assigns, pos))->value;+				goto cleanup;+			}+		}+	}++cleanup:+	git_vector_free(&files);+	git_attr_path__free(&path);++	return error;+}+++typedef struct {+	git_attr_name name;+	git_attr_assignment *found;+} attr_get_many_info;++int git_attr_get_many(+	const char **values,+    git_repository *repo,+	uint32_t flags,+	const char *pathname,+    size_t num_attr,+	const char **names)+{+	int error;+	git_attr_path path;+	git_vector files = GIT_VECTOR_INIT;+	unsigned int i, j, k;+	git_attr_file *file;+	git_attr_rule *rule;+	attr_get_many_info *info = NULL;+	size_t num_found = 0;++	memset((void *)values, 0, sizeof(const char *) * num_attr);++	if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)+		return -1;++	if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)+		goto cleanup;++	info = git__calloc(num_attr, sizeof(attr_get_many_info));+	GITERR_CHECK_ALLOC(info);++	git_vector_foreach(&files, i, file) {++		git_attr_file__foreach_matching_rule(file, &path, j, rule) {++			for (k = 0; k < num_attr; k++) {+				int pos;++				if (info[k].found != NULL) /* already found assignment */+					continue;++				if (!info[k].name.name) {+					info[k].name.name = names[k];+					info[k].name.name_hash = git_attr_file__name_hash(names[k]);+				}++				pos = git_vector_bsearch(&rule->assigns, &info[k].name);+				if (pos >= 0) {+					info[k].found = (git_attr_assignment *)+						git_vector_get(&rule->assigns, pos);+					values[k] = info[k].found->value;++					if (++num_found == num_attr)+						goto cleanup;+				}+			}+		}+	}++cleanup:+	git_vector_free(&files);+	git_attr_path__free(&path);+	git__free(info);++	return error;+}+++int git_attr_foreach(+    git_repository *repo,+	uint32_t flags,+	const char *pathname,+	int (*callback)(const char *name, const char *value, void *payload),+	void *payload)+{+	int error;+	git_attr_path path;+	git_vector files = GIT_VECTOR_INIT;+	unsigned int i, j, k;+	git_attr_file *file;+	git_attr_rule *rule;+	git_attr_assignment *assign;+	git_strmap *seen = NULL;++	if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)+		return -1;++	if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)+		goto cleanup;++	seen = git_strmap_alloc();+	GITERR_CHECK_ALLOC(seen);++	git_vector_foreach(&files, i, file) {++		git_attr_file__foreach_matching_rule(file, &path, j, rule) {++			git_vector_foreach(&rule->assigns, k, assign) {+				/* skip if higher priority assignment was already seen */+				if (git_strmap_exists(seen, assign->name))+					continue;++				git_strmap_insert(seen, assign->name, assign, error);+				if (error >= 0)+					error = callback(assign->name, assign->value, payload);++				if (error != 0)+					goto cleanup;+			}+		}+	}++cleanup:+	git_strmap_free(seen);+	git_vector_free(&files);+	git_attr_path__free(&path);++	return error;+}+++int git_attr_add_macro(+	git_repository *repo,+	const char *name,+	const char *values)+{+	int error;+	git_attr_rule *macro = NULL;+	git_pool *pool;++	if (git_attr_cache__init(repo) < 0)+		return -1;++	macro = git__calloc(1, sizeof(git_attr_rule));+	GITERR_CHECK_ALLOC(macro);++	pool = &git_repository_attr_cache(repo)->pool;++	macro->match.pattern = git_pool_strdup(pool, name);+	GITERR_CHECK_ALLOC(macro->match.pattern);++	macro->match.length = strlen(macro->match.pattern);+	macro->match.flags = GIT_ATTR_FNMATCH_MACRO;++	error = git_attr_assignment__parse(repo, pool, &macro->assigns, &values);++	if (!error)+		error = git_attr_cache__insert_macro(repo, macro);++	if (error < 0)+		git_attr_rule__free(macro);++	return error;+}++bool git_attr_cache__is_cached(+	git_repository *repo, git_attr_file_source source, const char *path)+{+	git_buf cache_key = GIT_BUF_INIT;+	git_strmap *files = git_repository_attr_cache(repo)->files;+	const char *workdir = git_repository_workdir(repo);+	bool rval;++	if (workdir && git__prefixcmp(path, workdir) == 0)+		path += strlen(workdir);+	if (git_buf_printf(&cache_key, "%d#%s", (int)source, path) < 0)+		return false;++	rval = git_strmap_exists(files, git_buf_cstr(&cache_key));++	git_buf_free(&cache_key);++	return rval;+}++static int load_attr_file(+	const char **data,+	git_attr_file_stat_sig *sig,+	const char *filename)+{+	int error;+	git_buf content = GIT_BUF_INIT;+	struct stat st;++	if (p_stat(filename, &st) < 0)+		return GIT_ENOTFOUND;++	if (sig != NULL &&+		(git_time_t)st.st_mtime == sig->seconds &&+		(git_off_t)st.st_size == sig->size &&+		(unsigned int)st.st_ino == sig->ino)+		return GIT_ENOTFOUND;++	error = git_futils_readbuffer_updated(&content, filename, NULL, NULL);+	if (error < 0)+		return error;++	if (sig != NULL) {+		sig->seconds = (git_time_t)st.st_mtime;+		sig->size    = (git_off_t)st.st_size;+		sig->ino     = (unsigned int)st.st_ino;+	}++	*data = git_buf_detach(&content);++	return 0;+}++static int load_attr_blob_from_index(+	const char **content,+	git_blob **blob,+	git_repository *repo,+	const git_oid *old_oid,+	const char *relfile)+{+	int error;+	git_index *index;+	git_index_entry *entry;++	if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||+		(error = git_index_find(index, relfile)) < 0)+		return error;++	entry = git_index_get(index, error);++	if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0)+		return GIT_ENOTFOUND;++	if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0)+		return error;++	*content = git_blob_rawcontent(*blob);+	return 0;+}++static int load_attr_from_cache(+	git_attr_file **file,+	git_attr_cache *cache,+	git_attr_file_source source,+	const char *relative_path)+{+	git_buf  cache_key = GIT_BUF_INIT;+	khiter_t cache_pos;++	*file = NULL;++	if (!cache || !cache->files)+		return 0;++	if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 0)+		return -1;++	cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr);++	git_buf_free(&cache_key);++	if (git_strmap_valid_index(cache->files, cache_pos))+		*file = git_strmap_value_at(cache->files, cache_pos);++	return 0;+}++int git_attr_cache__internal_file(+	git_repository *repo,+	const char *filename,+	git_attr_file **file)+{+	int error = 0;+	git_attr_cache *cache = git_repository_attr_cache(repo);+	khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename);++	if (git_strmap_valid_index(cache->files, cache_pos)) {+		*file = git_strmap_value_at(cache->files, cache_pos);+		return 0;+	}++	if (git_attr_file__new(file, 0, filename, &cache->pool) < 0)+		return -1;++	git_strmap_insert(cache->files, (*file)->key + 2, *file, error);+	if (error > 0)+		error = 0;++	return error;+}++int git_attr_cache__push_file(+	git_repository *repo,+	const char *base,+	const char *filename,+	git_attr_file_source source,+	git_attr_file_parser parse,+	git_vector *stack)+{+	int error = 0;+	git_buf path = GIT_BUF_INIT;+	const char *workdir = git_repository_workdir(repo);+	const char *relfile, *content = NULL;+	git_attr_cache *cache = git_repository_attr_cache(repo);+	git_attr_file *file = NULL;+	git_blob *blob = NULL;+	git_attr_file_stat_sig st;++	assert(filename && stack);++	/* join base and path as needed */+	if (base != NULL && git_path_root(filename) < 0) {+		if (git_buf_joinpath(&path, base, filename) < 0)+			return -1;+		filename = path.ptr;+	}++	relfile = filename;+	if (workdir && git__prefixcmp(relfile, workdir) == 0)+		relfile += strlen(workdir);++	/* check cache */+	if (load_attr_from_cache(&file, cache, source, relfile) < 0)+		return -1;++	/* if not in cache, load data, parse, and cache */++	if (source == GIT_ATTR_FILE_FROM_FILE) {+		if (file)+			memcpy(&st, &file->cache_data.st, sizeof(st));+		else+			memset(&st, 0, sizeof(st));++		error = load_attr_file(&content, &st, filename);+	} else {+		error = load_attr_blob_from_index(&content, &blob,+			repo, file ? &file->cache_data.oid : NULL, relfile);+	}++	if (error) {+		/* not finding a file is not an error for this function */+		if (error == GIT_ENOTFOUND) {+			giterr_clear();+			error = 0;+		}+		goto finish;+	}++	if (!file &&+		(error = git_attr_file__new(&file, source, relfile, &cache->pool)) < 0)+		goto finish;++	if (parse && (error = parse(repo, content, file)) < 0)+		goto finish;++	git_strmap_insert(cache->files, file->key, file, error);+	if (error > 0)+		error = 0;++	/* remember "cache buster" file signature */+	if (blob)+		git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));+	else+		memcpy(&file->cache_data.st, &st, sizeof(st));++finish:+	/* push file onto vector if we found one*/+	if (!error && file != NULL)+		error = git_vector_insert(stack, file);++	if (error != 0)+		git_attr_file__free(file);++	if (blob)+		git_blob_free(blob);+	else+		git__free((void *)content);++	git_buf_free(&path);++	return error;+}++#define push_attr_file(R,S,B,F) \+	git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S))++typedef struct {+	git_repository *repo;+	uint32_t flags;+	const char *workdir;+	git_index *index;+	git_vector *files;+} attr_walk_up_info;++int git_attr_cache__decide_sources(+	uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)+{+	int count = 0;++	switch (flags & 0x03) {+	case GIT_ATTR_CHECK_FILE_THEN_INDEX:+		if (has_wd)+			srcs[count++] = GIT_ATTR_FILE_FROM_FILE;+		if (has_index)+			srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;+		break;+	case GIT_ATTR_CHECK_INDEX_THEN_FILE:+		if (has_index)+			srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;+		if (has_wd)+			srcs[count++] = GIT_ATTR_FILE_FROM_FILE;+		break;+	case GIT_ATTR_CHECK_INDEX_ONLY:+		if (has_index)+			srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;+		break;+	}++	return count;+}++static int push_one_attr(void *ref, git_buf *path)+{+	int error = 0, n_src, i;+	attr_walk_up_info *info = (attr_walk_up_info *)ref;+	git_attr_file_source src[2];++	n_src = git_attr_cache__decide_sources(+		info->flags, info->workdir != NULL, info->index != NULL, src);++	for (i = 0; !error && i < n_src; ++i)+		error = git_attr_cache__push_file(+			info->repo, path->ptr, GIT_ATTR_FILE, src[i],+			git_attr_file__parse_buffer, info->files);++	return error;+}++static int collect_attr_files(+	git_repository *repo,+	uint32_t flags,+	const char *path,+	git_vector *files)+{+	int error;+	git_buf dir = GIT_BUF_INIT;+	const char *workdir = git_repository_workdir(repo);+	attr_walk_up_info info;++	if (git_attr_cache__init(repo) < 0 ||+		git_vector_init(files, 4, NULL) < 0)+		return -1;++	/* Resolve path in a non-bare repo */+	if (workdir != NULL)+		error = git_path_find_dir(&dir, path, workdir);+	else+		error = git_path_dirname_r(&dir, path);+	if (error < 0)+		goto cleanup;++	/* in precendence order highest to lowest:+	 * - $GIT_DIR/info/attributes+	 * - path components with .gitattributes+	 * - config core.attributesfile+	 * - $GIT_PREFIX/etc/gitattributes+	 */++	error = push_attr_file(+		repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);+	if (error < 0)+		goto cleanup;++	info.repo  = repo;+	info.flags = flags;+	info.workdir = workdir;+	if (git_repository_index__weakptr(&info.index, repo) < 0)+		giterr_clear(); /* no error even if there is no index */+	info.files = files;++	error = git_path_walk_up(&dir, workdir, push_one_attr, &info);+	if (error < 0)+		goto cleanup;++	if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {+		error = push_attr_file(+			repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);+		if (error < 0)+			goto cleanup;+	}++	if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {+		error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);+		if (!error)+			error = push_attr_file(repo, files, NULL, dir.ptr);+		else if (error == GIT_ENOTFOUND)+			error = 0;+	}++ cleanup:+	if (error < 0)+		git_vector_free(files);+	git_buf_free(&dir);++	return error;+}+++int git_attr_cache__init(git_repository *repo)+{+	int ret;+	git_attr_cache *cache = git_repository_attr_cache(repo);+	git_config *cfg;++	if (cache->initialized)+		return 0;++	/* cache config settings for attributes and ignores */+	if (git_repository_config__weakptr(&cfg, repo) < 0)+		return -1;++	ret = git_config_get_string(&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG);+	if (ret < 0 && ret != GIT_ENOTFOUND)+		return ret;++	ret = git_config_get_string(&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG);+	if (ret < 0 && ret != GIT_ENOTFOUND)+		return ret;++	giterr_clear();++	/* allocate hashtable for attribute and ignore file contents */+	if (cache->files == NULL) {+		cache->files = git_strmap_alloc();+		GITERR_CHECK_ALLOC(cache->files);+	}++	/* allocate hashtable for attribute macros */+	if (cache->macros == NULL) {+		cache->macros = git_strmap_alloc();+		GITERR_CHECK_ALLOC(cache->macros);+	}++	/* allocate string pool */+	if (git_pool_init(&cache->pool, 1, 0) < 0)+		return -1;++	cache->initialized = 1;++	/* insert default macros */+	return git_attr_add_macro(repo, "binary", "-diff -crlf -text");+}++void git_attr_cache_flush(+	git_repository *repo)+{+	git_attr_cache *cache;++	if (!repo)+		return;++	cache = git_repository_attr_cache(repo);++	if (cache->files != NULL) {+		git_attr_file *file;++		git_strmap_foreach_value(cache->files, file, {+			git_attr_file__free(file);+		});++		git_strmap_free(cache->files);+	}++	if (cache->macros != NULL) {+		git_attr_rule *rule;++		git_strmap_foreach_value(cache->macros, rule, {+			git_attr_rule__free(rule);+		});++		git_strmap_free(cache->macros);+	}++	git_pool_clear(&cache->pool);++	cache->initialized = 0;+}++int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)+{+	git_strmap *macros = git_repository_attr_cache(repo)->macros;+	int error;++	/* TODO: generate warning log if (macro->assigns.length == 0) */+	if (macro->assigns.length == 0)+		return 0;++	git_strmap_insert(macros, macro->match.pattern, macro, error);+	return (error < 0) ? -1 : 0;+}++git_attr_rule *git_attr_cache__lookup_macro(+	git_repository *repo, const char *name)+{+	git_strmap *macros = git_repository_attr_cache(repo)->macros;+	khiter_t pos;++	pos = git_strmap_lookup_index(macros, name);++	if (!git_strmap_valid_index(macros, pos))+		return NULL;++	return (git_attr_rule *)git_strmap_value_at(macros, pos);+}+
+ libgit2/src/attr.h view
@@ -0,0 +1,56 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_attr_h__+#define INCLUDE_attr_h__++#include "attr_file.h"+#include "strmap.h"++#define GIT_ATTR_CONFIG   "core.attributesfile"+#define GIT_IGNORE_CONFIG "core.excludesfile"++typedef struct {+	int initialized;+	git_pool pool;+	git_strmap *files;	/* hash path to git_attr_file of rules */+	git_strmap *macros;	/* hash name to vector<git_attr_assignment> */+	const char *cfg_attr_file; /* cached value of core.attributesfile */+	const char *cfg_excl_file; /* cached value of core.excludesfile */+} git_attr_cache;++typedef int (*git_attr_file_parser)(+	git_repository *, const char *, git_attr_file *);++extern int git_attr_cache__init(git_repository *repo);++extern int git_attr_cache__insert_macro(+	git_repository *repo, git_attr_rule *macro);++extern git_attr_rule *git_attr_cache__lookup_macro(+	git_repository *repo, const char *name);++extern int git_attr_cache__push_file(+	git_repository *repo,+	const char *base,+	const char *filename,+	git_attr_file_source source,+	git_attr_file_parser parse,+	git_vector *stack);++extern int git_attr_cache__internal_file(+	git_repository *repo,+	const char *key,+	git_attr_file **file_ptr);++/* returns true if path is in cache */+extern bool git_attr_cache__is_cached(+	git_repository *repo, git_attr_file_source source, const char *path);++extern int git_attr_cache__decide_sources(+	uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs);++#endif
+ libgit2/src/attr_file.c view
@@ -0,0 +1,601 @@+#include "common.h"+#include "repository.h"+#include "filebuf.h"+#include "git2/blob.h"+#include "git2/tree.h"+#include <ctype.h>++const char *git_attr__true  = "[internal]__TRUE__";+const char *git_attr__false = "[internal]__FALSE__";+const char *git_attr__unset = "[internal]__UNSET__";++static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);+static void git_attr_rule__clear(git_attr_rule *rule);++int git_attr_file__new(+	git_attr_file **attrs_ptr,+	git_attr_file_source from,+	const char *path,+	git_pool *pool)+{+	git_attr_file *attrs = NULL;++	attrs = git__calloc(1, sizeof(git_attr_file));+	GITERR_CHECK_ALLOC(attrs);++	if (pool)+		attrs->pool = pool;+	else {+		attrs->pool = git__calloc(1, sizeof(git_pool));+		if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)+			goto fail;+		attrs->pool_is_allocated = true;+	}++	if (path) {+		size_t len = strlen(path);++		attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);+		GITERR_CHECK_ALLOC(attrs->key);++		attrs->key[0] = '0' + from;+		attrs->key[1] = '#';+		memcpy(&attrs->key[2], path, len);+		attrs->key[len + 2] = '\0';+	}++	if (git_vector_init(&attrs->rules, 4, NULL) < 0)+		goto fail;++	*attrs_ptr = attrs;+	return 0;++fail:+	git_attr_file__free(attrs);+	attrs_ptr = NULL;+	return -1;+}++int git_attr_file__parse_buffer(+	git_repository *repo, const char *buffer, git_attr_file *attrs)+{+	int error = 0;+	const char *scan = NULL;+	char *context = NULL;+	git_attr_rule *rule = NULL;++	assert(buffer && attrs);++	scan = buffer;++	/* if subdir file path, convert context for file paths */+	if (attrs->key && git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) {+		context = attrs->key + 2;+		context[strlen(context) - strlen(GIT_ATTR_FILE)] = '\0';+	}++	while (!error && *scan) {+		/* allocate rule if needed */+		if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {+			error = -1;+			break;+		}++		/* parse the next "pattern attr attr attr" line */+		if (!(error = git_attr_fnmatch__parse(+				&rule->match, attrs->pool, context, &scan)) &&+			!(error = git_attr_assignment__parse(+				repo, attrs->pool, &rule->assigns, &scan)))+		{+			if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)+				/* should generate error/warning if this is coming from any+				 * file other than .gitattributes at repo root.+				 */+				error = git_attr_cache__insert_macro(repo, rule);+			else+				error = git_vector_insert(&attrs->rules, rule);+		}++		/* if the rule wasn't a pattern, on to the next */+		if (error < 0) {+			git_attr_rule__clear(rule); /* reset rule contents */+			if (error == GIT_ENOTFOUND)+				error = 0;+		} else {+			rule = NULL; /* vector now "owns" the rule */+		}+	}++	git_attr_rule__free(rule);++	/* restore file path used for context */+	if (context)+		context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */++	return error;+}++int git_attr_file__new_and_load(+	git_attr_file **attrs_ptr,+	const char *path)+{+	int error;+	git_buf content = GIT_BUF_INIT;++	if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)+		return error;++	if (!(error = git_futils_readbuffer(&content, path)))+		error = git_attr_file__parse_buffer(+			NULL, git_buf_cstr(&content), *attrs_ptr);++	git_buf_free(&content);++	if (error) {+		git_attr_file__free(*attrs_ptr);+		*attrs_ptr = NULL;+	}++	return error;+}++void git_attr_file__free(git_attr_file *file)+{+	unsigned int i;+	git_attr_rule *rule;++	if (!file)+		return;++	git_vector_foreach(&file->rules, i, rule)+		git_attr_rule__free(rule);++	git_vector_free(&file->rules);++	if (file->pool_is_allocated) {+		git_pool_clear(file->pool);+		git__free(file->pool);+	}+	file->pool = NULL;++	git__free(file);+}++uint32_t git_attr_file__name_hash(const char *name)+{+	uint32_t h = 5381;+	int c;+	assert(name);+	while ((c = (int)*name++) != 0)+		h = ((h << 5) + h) + c;+	return h;+}+++int git_attr_file__lookup_one(+	git_attr_file *file,+	const git_attr_path *path,+	const char *attr,+	const char **value)+{+	unsigned int i;+	git_attr_name name;+	git_attr_rule *rule;++	*value = NULL;++	name.name = attr;+	name.name_hash = git_attr_file__name_hash(attr);++	git_attr_file__foreach_matching_rule(file, path, i, rule) {+		int pos = git_vector_bsearch(&rule->assigns, &name);++		if (pos >= 0) {+			*value = ((git_attr_assignment *)+					  git_vector_get(&rule->assigns, pos))->value;+			break;+		}+	}++	return 0;+}+++bool git_attr_fnmatch__match(+	git_attr_fnmatch *match,+	const git_attr_path *path)+{+	int fnm;++	if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)+		return false;++	if (match->flags & GIT_ATTR_FNMATCH_FULLPATH)+		fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME);+	else if (path->is_dir)+		fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);+	else+		fnm = p_fnmatch(match->pattern, path->basename, 0);++	return (fnm == FNM_NOMATCH) ? false : true;+}++bool git_attr_rule__match(+	git_attr_rule *rule,+	const git_attr_path *path)+{+	bool matched = git_attr_fnmatch__match(&rule->match, path);++	if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE)+		matched = !matched;++	return matched;+}+++git_attr_assignment *git_attr_rule__lookup_assignment(+	git_attr_rule *rule, const char *name)+{+	int pos;+	git_attr_name key;+	key.name = name;+	key.name_hash = git_attr_file__name_hash(name);++	pos = git_vector_bsearch(&rule->assigns, &key);++	return (pos >= 0) ? git_vector_get(&rule->assigns, pos) : NULL;+}++int git_attr_path__init(+	git_attr_path *info, const char *path, const char *base)+{+	/* build full path as best we can */+	git_buf_init(&info->full, 0);++	if (base != NULL && git_path_root(path) < 0) {+		if (git_buf_joinpath(&info->full, base, path) < 0)+			return -1;+		info->path = info->full.ptr + strlen(base);+	} else {+		if (git_buf_sets(&info->full, path) < 0)+			return -1;+		info->path = info->full.ptr;+	}++	/* remove trailing slashes */+	while (info->full.size > 0) {+		if (info->full.ptr[info->full.size - 1] != '/')+			break;+		info->full.size--;+	}+	info->full.ptr[info->full.size] = '\0';++	/* skip leading slashes in path */+	while (*info->path == '/')+		info->path++;++	/* find trailing basename component */+	info->basename = strrchr(info->path, '/');+	if (info->basename)+		info->basename++;+	if (!info->basename || !*info->basename)+		info->basename = info->path;++	info->is_dir = (int)git_path_isdir(info->full.ptr);++	return 0;+}++void git_attr_path__free(git_attr_path *info)+{+	git_buf_free(&info->full);+	info->path = NULL;+	info->basename = NULL;+}+++/*+ * From gitattributes(5):+ *+ * Patterns have the following format:+ *+ * - A blank line matches no files, so it can serve as a separator for+ *   readability.+ *+ * - A line starting with # serves as a comment.+ *+ * - An optional prefix ! which negates the pattern; any matching file+ *   excluded by a previous pattern will become included again. If a negated+ *   pattern matches, this will override lower precedence patterns sources.+ *+ * - If the pattern ends with a slash, it is removed for the purpose of the+ *   following description, but it would only find a match with a directory. In+ *   other words, foo/ will match a directory foo and paths underneath it, but+ *   will not match a regular file or a symbolic link foo (this is consistent+ *   with the way how pathspec works in general in git).+ *+ * - If the pattern does not contain a slash /, git treats it as a shell glob+ *   pattern and checks for a match against the pathname without leading+ *   directories.+ *+ * - Otherwise, git treats the pattern as a shell glob suitable for consumption+ *   by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will+ *   not match a / in the pathname. For example, "Documentation/\*.html" matches+ *   "Documentation/git.html" but not "Documentation/ppc/ppc.html". A leading+ *   slash matches the beginning of the pathname; for example, "/\*.c" matches+ *   "cat-file.c" but not "mozilla-sha1/sha1.c".+ */++/*+ * This will return 0 if the spec was filled out,+ * GIT_ENOTFOUND if the fnmatch does not require matching, or+ * another error code there was an actual problem.+ */+int git_attr_fnmatch__parse(+	git_attr_fnmatch *spec,+	git_pool *pool,+	const char *source,+	const char **base)+{+	const char *pattern, *scan;+	int slash_count;++	assert(spec && base && *base);++	pattern = *base;++	while (git__isspace(*pattern)) pattern++;+	if (!*pattern || *pattern == '#') {+		*base = git__next_line(pattern);+		return GIT_ENOTFOUND;+	}++	spec->flags = 0;++	if (*pattern == '[') {+		if (strncmp(pattern, "[attr]", 6) == 0) {+			spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;+			pattern += 6;+		}+		/* else a character range like [a-e]* which is accepted */+	}++	if (*pattern == '!') {+		spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;+		pattern++;+	}++	slash_count = 0;+	for (scan = pattern; *scan != '\0'; ++scan) {+		/* scan until (non-escaped) white space */+		if (git__isspace(*scan) && *(scan - 1) != '\\')+			break;++		if (*scan == '/') {+			spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;+			slash_count++;+			if (pattern == scan)+				pattern++;+		}+		/* remember if we see an unescaped wildcard in pattern */+		else if (git__iswildcard(*scan) &&+			(scan == pattern || (*(scan - 1) != '\\')))+			spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;+	}++	*base = scan;++	spec->length = scan - pattern;++	if (pattern[spec->length - 1] == '/') {+		spec->length--;+		spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY;+		if (--slash_count <= 0)+			spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;+	}++	if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&+		source != NULL && git_path_root(pattern) < 0)+	{+		size_t sourcelen = strlen(source);+		/* given an unrooted fullpath match from a file inside a repo,+		 * prefix the pattern with the relative directory of the source file+		 */+		spec->pattern = git_pool_malloc(+			pool, (uint32_t)(sourcelen + spec->length + 1));+		if (spec->pattern) {+			memcpy(spec->pattern, source, sourcelen);+			memcpy(spec->pattern + sourcelen, pattern, spec->length);+			spec->length += sourcelen;+			spec->pattern[spec->length] = '\0';+		}+	} else {+		spec->pattern = git_pool_strndup(pool, pattern, spec->length);+	}++	if (!spec->pattern) {+		*base = git__next_line(pattern);+		return -1;+	} else {+		/* strip '\' that might have be used for internal whitespace */+		char *to = spec->pattern;+		for (scan = spec->pattern; *scan; to++, scan++) {+			if (*scan == '\\')+				scan++; /* skip '\' but include next char */+			if (to != scan)+				*to = *scan;+		}+		if (to != scan) {+			*to = '\0';+			spec->length = (to - spec->pattern);+		}+	}++	return 0;+}++static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)+{+	const git_attr_name *a = a_raw;+	const git_attr_name *b = b_raw;++	if (b->name_hash < a->name_hash)+		return 1;+	else if (b->name_hash > a->name_hash)+		return -1;+	else+		return strcmp(b->name, a->name);+}++static void git_attr_assignment__free(git_attr_assignment *assign)+{+	/* name and value are stored in a git_pool associated with the+	 * git_attr_file, so they do not need to be freed here+	 */+	assign->name = NULL;+	assign->value = NULL;+	git__free(assign);+}++static int merge_assignments(void **old_raw, void *new_raw)+{+	git_attr_assignment **old = (git_attr_assignment **)old_raw;+	git_attr_assignment *new = (git_attr_assignment *)new_raw;++	GIT_REFCOUNT_DEC(*old, git_attr_assignment__free);+	*old = new;+	return GIT_EEXISTS;+}++int git_attr_assignment__parse(+	git_repository *repo,+	git_pool *pool,+	git_vector *assigns,+	const char **base)+{+	int error;+	const char *scan = *base;+	git_attr_assignment *assign = NULL;++	assert(assigns && !assigns->length);++	assigns->_cmp = sort_by_hash_and_name;++	while (*scan && *scan != '\n') {+		const char *name_start, *value_start;++		/* skip leading blanks */+		while (git__isspace(*scan) && *scan != '\n') scan++;++		/* allocate assign if needed */+		if (!assign) {+			assign = git__calloc(1, sizeof(git_attr_assignment));+			GITERR_CHECK_ALLOC(assign);+			GIT_REFCOUNT_INC(assign);+		}++		assign->name_hash = 5381;+		assign->value = git_attr__true;++		/* look for magic name prefixes */+		if (*scan == '-') {+			assign->value = git_attr__false;+			scan++;+		} else if (*scan == '!') {+			assign->value = git_attr__unset; /* explicit unspecified state */+			scan++;+		} else if (*scan == '#') /* comment rest of line */+			break;++		/* find the name */+		name_start = scan;+		while (*scan && !git__isspace(*scan) && *scan != '=') {+			assign->name_hash =+				((assign->name_hash << 5) + assign->name_hash) + *scan;+			scan++;+		}+		if (scan == name_start) {+			/* must have found lone prefix (" - ") or leading = ("=foo")+			 * or end of buffer -- advance until whitespace and continue+			 */+			while (*scan && !git__isspace(*scan)) scan++;+			continue;+		}++		/* allocate permanent storage for name */+		assign->name = git_pool_strndup(pool, name_start, scan - name_start);+		GITERR_CHECK_ALLOC(assign->name);++		/* if there is an equals sign, find the value */+		if (*scan == '=') {+			for (value_start = ++scan; *scan && !git__isspace(*scan); ++scan);++			/* if we found a value, allocate permanent storage for it */+			if (scan > value_start) {+				assign->value = git_pool_strndup(pool, value_start, scan - value_start);+				GITERR_CHECK_ALLOC(assign->value);+			}+		}++		/* expand macros (if given a repo with a macro cache) */+		if (repo != NULL && assign->value == git_attr__true) {+			git_attr_rule *macro =+				git_attr_cache__lookup_macro(repo, assign->name);++			if (macro != NULL) {+				unsigned int i;+				git_attr_assignment *massign;++				git_vector_foreach(&macro->assigns, i, massign) {+					GIT_REFCOUNT_INC(massign);++					error = git_vector_insert_sorted(+						assigns, massign, &merge_assignments);+					if (error < 0 && error != GIT_EEXISTS)+						return error;+				}+			}+		}++		/* insert allocated assign into vector */+		error = git_vector_insert_sorted(assigns, assign, &merge_assignments);+		if (error < 0 && error != GIT_EEXISTS)+			return error;++		/* clear assign since it is now "owned" by the vector */+		assign = NULL;+	}++	if (assign != NULL)+		git_attr_assignment__free(assign);++	*base = git__next_line(scan);++	return (assigns->length == 0) ? GIT_ENOTFOUND : 0;+}++static void git_attr_rule__clear(git_attr_rule *rule)+{+	unsigned int i;+	git_attr_assignment *assign;++	if (!rule)+		return;++	if (!(rule->match.flags & GIT_ATTR_FNMATCH_IGNORE)) {+		git_vector_foreach(&rule->assigns, i, assign)+			GIT_REFCOUNT_DEC(assign, git_attr_assignment__free);+		git_vector_free(&rule->assigns);+	}++	/* match.pattern is stored in a git_pool, so no need to free */+	rule->match.pattern = NULL;+	rule->match.length = 0;+}++void git_attr_rule__free(git_attr_rule *rule)+{+	git_attr_rule__clear(rule);+	git__free(rule);+}+
+ libgit2/src/attr_file.h view
@@ -0,0 +1,142 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_attr_file_h__+#define INCLUDE_attr_file_h__++#include "git2/attr.h"+#include "vector.h"+#include "pool.h"+#include "buffer.h"++#define GIT_ATTR_FILE			".gitattributes"+#define GIT_ATTR_FILE_INREPO	"info/attributes"+#define GIT_ATTR_FILE_SYSTEM	"gitattributes"++#define GIT_ATTR_FNMATCH_NEGATIVE	(1U << 0)+#define GIT_ATTR_FNMATCH_DIRECTORY	(1U << 1)+#define GIT_ATTR_FNMATCH_FULLPATH	(1U << 2)+#define GIT_ATTR_FNMATCH_MACRO		(1U << 3)+#define GIT_ATTR_FNMATCH_IGNORE		(1U << 4)+#define GIT_ATTR_FNMATCH_HASWILD	(1U << 5)++typedef struct {+	char *pattern;+	size_t length;+	unsigned int flags;+} git_attr_fnmatch;++typedef struct {+	git_attr_fnmatch match;+	git_vector assigns;		/* vector of <git_attr_assignment*> */+} git_attr_rule;++typedef struct {+	git_refcount unused;+	const char *name;+    uint32_t name_hash;+} git_attr_name;++typedef struct {+	git_refcount rc;		/* for macros */+	char *name;+    uint32_t name_hash;+    const char *value;+} git_attr_assignment;++typedef struct {+	git_time_t seconds;+	git_off_t  size;+	unsigned int ino;+} git_attr_file_stat_sig;++typedef struct {+	char *key;				/* cache "source#path" this was loaded from */+	git_vector rules;		/* vector of <rule*> or <fnmatch*> */+	git_pool *pool;+	bool pool_is_allocated;+	union {+		git_oid oid;+		git_attr_file_stat_sig st;+	} cache_data;+} git_attr_file;++typedef struct {+	git_buf     full;+	const char *path;+	const char *basename;+	int         is_dir;+} git_attr_path;++typedef enum {+	GIT_ATTR_FILE_FROM_FILE = 0,+	GIT_ATTR_FILE_FROM_INDEX = 1+} git_attr_file_source;++/*+ * git_attr_file API+ */++extern int git_attr_file__new(+	git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool);++extern int git_attr_file__new_and_load(+	git_attr_file **attrs_ptr, const char *path);++extern void git_attr_file__free(git_attr_file *file);++extern int git_attr_file__parse_buffer(+	git_repository *repo, const char *buf, git_attr_file *file);++extern int git_attr_file__lookup_one(+	git_attr_file *file,+	const git_attr_path *path,+	const char *attr,+	const char **value);++/* loop over rules in file from bottom to top */+#define git_attr_file__foreach_matching_rule(file, path, iter, rule)	\+	git_vector_rforeach(&(file)->rules, (iter), (rule)) \+		if (git_attr_rule__match((rule), (path)))++extern uint32_t git_attr_file__name_hash(const char *name);+++/*+ * other utilities+ */++extern int git_attr_fnmatch__parse(+	git_attr_fnmatch *spec,+	git_pool *pool,+	const char *source,+	const char **base);++extern bool git_attr_fnmatch__match(+	git_attr_fnmatch *rule,+	const git_attr_path *path);++extern void git_attr_rule__free(git_attr_rule *rule);++extern bool git_attr_rule__match(+	git_attr_rule *rule,+	const git_attr_path *path);++extern git_attr_assignment *git_attr_rule__lookup_assignment(+	git_attr_rule *rule, const char *name);++extern int git_attr_path__init(+	git_attr_path *info, const char *path, const char *base);++extern void git_attr_path__free(git_attr_path *info);++extern int git_attr_assignment__parse(+	git_repository *repo, /* needed to expand macros */+	git_pool *pool,+	git_vector *assigns,+	const char **scan);++#endif
+ libgit2/src/blob.c view
@@ -0,0 +1,239 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "git2/common.h"+#include "git2/object.h"+#include "git2/repository.h"++#include "common.h"+#include "blob.h"+#include "filter.h"++const void *git_blob_rawcontent(git_blob *blob)+{+	assert(blob);+	return blob->odb_object->raw.data;+}++size_t git_blob_rawsize(git_blob *blob)+{+	assert(blob);+	return blob->odb_object->raw.len;+}++int git_blob__getbuf(git_buf *buffer, git_blob *blob)+{+	return git_buf_set(+		buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);+}++void git_blob__free(git_blob *blob)+{+	git_odb_object_free(blob->odb_object);+	git__free(blob);+}++int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)+{+	assert(blob);+	git_cached_obj_incref((git_cached_obj *)odb_obj);+	blob->odb_object = odb_obj;+	return 0;+}++int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)+{+	int error;+	git_odb *odb;+	git_odb_stream *stream;++	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||+		(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)+		return error;++	if ((error = stream->write(stream, buffer, len)) == 0)+		error = stream->finalize_write(oid, stream);++	stream->free(stream);+	return error;+}++static int write_file_stream(+	git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)+{+	int fd, error;+	char buffer[4096];+	git_odb_stream *stream = NULL;++	if ((error = git_odb_open_wstream(+			&stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0)+		return error;++	if ((fd = git_futils_open_ro(path)) < 0) {+		stream->free(stream);+		return -1;+	}++	while (!error && file_size > 0) {+		ssize_t read_len = p_read(fd, buffer, sizeof(buffer));++		if (read_len < 0) {+			giterr_set(+				GITERR_OS, "Failed to create blob. Can't read whole file");+			error = -1;+		}+		else if (!(error = stream->write(stream, buffer, read_len)))+			file_size -= read_len;+	}++	p_close(fd);++	if (!error)+		error = stream->finalize_write(oid, stream);++	stream->free(stream);+	return error;+}++static int write_file_filtered(+	git_oid *oid,+	git_odb *odb,+	const char *full_path,+	git_vector *filters)+{+	int error;+	git_buf source = GIT_BUF_INIT;+	git_buf dest = GIT_BUF_INIT;++	if ((error = git_futils_readbuffer(&source, full_path)) < 0)+		return error;++	error = git_filters_apply(&dest, &source, filters);++	/* Free the source as soon as possible. This can be big in memory,+	 * and we don't want to ODB write to choke */+	git_buf_free(&source);++	/* Write the file to disk if it was properly filtered */+	if (!error)+		error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);++	git_buf_free(&dest);+	return error;+}++static int write_symlink(+	git_oid *oid, git_odb *odb, const char *path, size_t link_size)+{+	char *link_data;+	ssize_t read_len;+	int error;++	link_data = git__malloc(link_size);+	GITERR_CHECK_ALLOC(link_data);++	read_len = p_readlink(path, link_data, link_size);+	if (read_len != (ssize_t)link_size) {+		giterr_set(GITERR_OS, "Failed to create blob.  Can't read symlink '%s'", path);+		git__free(link_data);+		return -1;+	}++	error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);+	git__free(link_data);+	return error;+}++static int blob_create_internal(git_oid *oid, git_repository *repo, const char *path)+{+	int error;+	struct stat st;+	git_odb *odb = NULL;+	git_off_t size;++	if ((error = git_path_lstat(path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)+		return error;++	size = st.st_size;++	if (S_ISLNK(st.st_mode)) {+		error = write_symlink(oid, odb, path, (size_t)size);+	} else {+		git_vector write_filters = GIT_VECTOR_INIT;+		int filter_count;++		/* Load the filters for writing this file to the ODB */+		filter_count = git_filters_load(+			&write_filters, repo, path, GIT_FILTER_TO_ODB);++		if (filter_count < 0) {+			/* Negative value means there was a critical error */+			error = filter_count;+		} else if (filter_count == 0) {+			/* No filters need to be applied to the document: we can stream+			 * directly from disk */+			error = write_file_stream(oid, odb, path, size);+		} else {+			/* We need to apply one or more filters */+			error = write_file_filtered(oid, odb, path, &write_filters);+		}++		git_filters_free(&write_filters);++		/*+		 * TODO: eventually support streaming filtered files, for files+		 * which are bigger than a given threshold. This is not a priority+		 * because applying a filter in streaming mode changes the final+		 * size of the blob, and without knowing its final size, the blob+		 * cannot be written in stream mode to the ODB.+		 *+		 * The plan is to do streaming writes to a tempfile on disk and then+		 * opening streaming that file to the ODB, using+		 * `write_file_stream`.+		 *+		 * CAREFULLY DESIGNED APIS YO+		 */+	}++	return error;+}++int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)+{+	git_buf full_path = GIT_BUF_INIT;+	const char *workdir;+	int error;++	workdir = git_repository_workdir(repo);+	assert(workdir); /* error to call this on bare repo */++	if (git_buf_joinpath(&full_path, workdir, path) < 0) {+		git_buf_free(&full_path);+		return -1;+	}++	error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));++	git_buf_free(&full_path);+	return error;+}++int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)+{+	int error;+	git_buf full_path = GIT_BUF_INIT;++	if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {+		git_buf_free(&full_path);+		return error;+	}++	error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));++	git_buf_free(&full_path);+	return error;+}
+ libgit2/src/blob.h view
@@ -0,0 +1,24 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_blob_h__+#define INCLUDE_blob_h__++#include "git2/blob.h"+#include "repository.h"+#include "odb.h"+#include "fileops.h"++struct git_blob {+	git_object object;+	git_odb_object *odb_object;+};++void git_blob__free(git_blob *blob);+int git_blob__parse(git_blob *blob, git_odb_object *obj);+int git_blob__getbuf(git_buf *buffer, git_blob *blob);++#endif
+ libgit2/src/branch.c view
@@ -0,0 +1,208 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "commit.h"+#include "branch.h"+#include "tag.h"++static int retrieve_branch_reference(+	git_reference **branch_reference_out,+	git_repository *repo,+	const char *branch_name,+	int is_remote)+{+	git_reference *branch;+	int error = -1;+	char *prefix;+	git_buf ref_name = GIT_BUF_INIT;++	*branch_reference_out = NULL;++	prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;++	if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)+		goto cleanup;++	if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {+		giterr_set(GITERR_REFERENCE,+			"Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);+		goto cleanup;+	}++	*branch_reference_out = branch;++cleanup:+	git_buf_free(&ref_name);+	return error;+}++static int create_error_invalid(const char *msg)+{+	giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);+	return -1;+}++int git_branch_create(+		git_oid *oid_out,+		git_repository *repo,+		const char *branch_name,+		const git_object *target,+		int force)+{+	git_otype target_type = GIT_OBJ_BAD;+	git_object *commit = NULL;+	git_reference *branch = NULL;+	git_buf canonical_branch_name = GIT_BUF_INIT;+	int error = -1;++	assert(repo && branch_name && target && oid_out);++	if (git_object_owner(target) != repo)+		return create_error_invalid("The given target does not belong to this repository");++	target_type = git_object_type(target);++	switch (target_type)+	{+	case GIT_OBJ_TAG:+		if (git_tag_peel(&commit, (git_tag *)target) < 0)+			goto cleanup;++		if (git_object_type(commit) != GIT_OBJ_COMMIT) {+			create_error_invalid("The given target does not resolve to a commit");+			goto cleanup;+		}+		break;++	case GIT_OBJ_COMMIT:+		commit = (git_object *)target;+		break;++	default:+		return create_error_invalid("Only git_tag and git_commit objects are valid targets.");+	}++	if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)+		goto cleanup;++	if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)+		goto cleanup;++	git_oid_cpy(oid_out, git_reference_oid(branch));+	error = 0;++cleanup:+	if (target_type == GIT_OBJ_TAG)+		git_object_free(commit);++	git_reference_free(branch);+	git_buf_free(&canonical_branch_name);+	return error;+}++int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_t branch_type)+{+	git_reference *branch = NULL;+	git_reference *head = NULL;+	int error;++	assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));++	if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)+		return error;++	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {+		giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");+		goto on_error;+	}++	if ((git_reference_type(head) == GIT_REF_SYMBOLIC)+		&& (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) {+			giterr_set(GITERR_REFERENCE,+					"Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name);+			goto on_error;+	}++	if (git_reference_delete(branch) < 0)+		goto on_error;++	git_reference_free(head);+	return 0;++on_error:+	git_reference_free(head);+	git_reference_free(branch);+	return -1;+}++typedef struct {+	git_vector *branchlist;+	unsigned int branch_type;+} branch_filter_data;++static int branch_list_cb(const char *branch_name, void *payload)+{+	branch_filter_data *filter = (branch_filter_data *)payload;++	if ((filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)+		|| (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0))+		return git_vector_insert(filter->branchlist, git__strdup(branch_name));++	return 0;+}++int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags)+{+	int error;+	branch_filter_data filter;+	git_vector branchlist;++	assert(branch_names && repo);++	if (git_vector_init(&branchlist, 8, NULL) < 0)+		return -1;++	filter.branchlist = &branchlist;+	filter.branch_type = list_flags;++	error = git_reference_foreach(repo, GIT_REF_LISTALL, &branch_list_cb, (void *)&filter);+	if (error < 0) {+		git_vector_free(&branchlist);+		return -1;+	}++	branch_names->strings = (char **)branchlist.contents;+	branch_names->count = branchlist.length;+	return 0;+}++int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force)+{+	git_reference *reference = NULL;+	git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT;+	int error = 0;++	if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0)+		goto cleanup;++	/* We need to be able to return GIT_ENOTFOUND */+	if ((error = git_reference_lookup(&reference, repo, git_buf_cstr(&old_reference_name))) < 0)+		goto cleanup;++	if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)+		goto cleanup;++	error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force);++cleanup:+	git_reference_free(reference);+	git_buf_free(&old_reference_name);+	git_buf_free(&new_reference_name);++	return error;+}
+ libgit2/src/branch.h view
@@ -0,0 +1,17 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_branch_h__+#define INCLUDE_branch_h__++#include "git2/branch.h"++struct git_branch {+	char *remote; /* TODO: Make this a git_remote */+	char *merge;+};++#endif
+ libgit2/src/bswap.h view
@@ -0,0 +1,97 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"++/*+ * Default version that the compiler ought to optimize properly with+ * constant values.+ */+GIT_INLINE(uint32_t) default_swab32(uint32_t val)+{+	return (((val & 0xff000000) >> 24) |+		((val & 0x00ff0000) >> 8) |+		((val & 0x0000ff00) << 8) |+		((val & 0x000000ff) << 24));+}++#undef bswap32++GIT_INLINE(uint16_t) default_swab16(uint16_t val)+{+	return (((val & 0xff00) >> 8) |+		((val & 0x00ff) << 8));+}++#undef bswap16++#if defined(__GNUC__) && defined(__i386__)++#define bswap32(x) ({ \+	uint32_t __res; \+	if (__builtin_constant_p(x)) { \+		__res = default_swab32(x); \+	} else { \+		__asm__("bswap %0" : "=r" (__res) : "0" ((uint32_t)(x))); \+	} \+	__res; })++#define bswap16(x) ({ \+	uint16_t __res; \+	if (__builtin_constant_p(x)) { \+		__res = default_swab16(x); \+	} else { \+		__asm__("xchgb %b0,%h0" : "=q" (__res) : "0" ((uint16_t)(x))); \+	} \+	__res; })++#elif defined(__GNUC__) && defined(__x86_64__)++#define bswap32(x) ({ \+	uint32_t __res; \+	if (__builtin_constant_p(x)) { \+		__res = default_swab32(x); \+	} else { \+		__asm__("bswapl %0" : "=r" (__res) : "0" ((uint32_t)(x))); \+	} \+	__res; })++#define bswap16(x) ({ \+	uint16_t __res; \+	if (__builtin_constant_p(x)) { \+		__res = default_swab16(x); \+	} else { \+		__asm__("xchgb %b0,%h0" : "=Q" (__res) : "0" ((uint16_t)(x))); \+	} \+	__res; })++#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))++#include <stdlib.h>++#define bswap32(x) _byteswap_ulong(x)+#define bswap16(x) _byteswap_ushort(x)++#endif++#ifdef bswap32++#undef ntohl+#undef htonl+#define ntohl(x) bswap32(x)+#define htonl(x) bswap32(x)++#endif++#ifdef bswap16++#undef ntohs+#undef htons+#define ntohs(x) bswap16(x)+#define htons(x) bswap16(x)++#endif
+ libgit2/src/buffer.c view
@@ -0,0 +1,461 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "buffer.h"+#include "posix.h"+#include <stdarg.h>+#include <ctype.h>++/* Used as default value for git_buf->ptr so that people can always+ * assume ptr is non-NULL and zero terminated even for new git_bufs.+ */+char git_buf__initbuf[1];++char git_buf__oom[1];++#define ENSURE_SIZE(b, d) \+	if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\+		return -1;+++void git_buf_init(git_buf *buf, size_t initial_size)+{+	buf->asize = 0;+	buf->size = 0;+	buf->ptr = git_buf__initbuf;++	if (initial_size)+		git_buf_grow(buf, initial_size);+}++int git_buf_grow(git_buf *buf, size_t target_size)+{+	int error = git_buf_try_grow(buf, target_size);+	if (error != 0)+		buf->ptr = git_buf__oom;+	return error;+}++int git_buf_try_grow(git_buf *buf, size_t target_size)+{+	char *new_ptr;+	size_t new_size;++	if (buf->ptr == git_buf__oom)+		return -1;++	if (target_size <= buf->asize)+		return 0;++	if (buf->asize == 0) {+		new_size = target_size;+		new_ptr = NULL;+	} else {+		new_size = buf->asize;+		new_ptr = buf->ptr;+	}++	/* grow the buffer size by 1.5, until it's big enough+	 * to fit our target size */+	while (new_size < target_size)+		new_size = (new_size << 1) - (new_size >> 1);++	/* round allocation up to multiple of 8 */+	new_size = (new_size + 7) & ~7;++	new_ptr = git__realloc(new_ptr, new_size);+	if (!new_ptr)+		return -1;++	buf->asize = new_size;+	buf->ptr   = new_ptr;++	/* truncate the existing buffer size if necessary */+	if (buf->size >= buf->asize)+		buf->size = buf->asize - 1;+	buf->ptr[buf->size] = '\0';++	return 0;+}++void git_buf_free(git_buf *buf)+{+	if (!buf) return;++	if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)+		git__free(buf->ptr);++	git_buf_init(buf, 0);+}++void git_buf_clear(git_buf *buf)+{+	buf->size = 0;+	if (buf->asize > 0)+		buf->ptr[0] = '\0';+}++int git_buf_set(git_buf *buf, const char *data, size_t len)+{+	if (len == 0 || data == NULL) {+		git_buf_clear(buf);+	} else {+		if (data != buf->ptr) {+			ENSURE_SIZE(buf, len + 1);+			memmove(buf->ptr, data, len);+		}+		buf->size = len;+		buf->ptr[buf->size] = '\0';+	}+	return 0;+}++int git_buf_sets(git_buf *buf, const char *string)+{+	return git_buf_set(buf, string, string ? strlen(string) : 0);+}++int git_buf_putc(git_buf *buf, char c)+{+	ENSURE_SIZE(buf, buf->size + 2);+	buf->ptr[buf->size++] = c;+	buf->ptr[buf->size] = '\0';+	return 0;+}++int git_buf_put(git_buf *buf, const char *data, size_t len)+{+	ENSURE_SIZE(buf, buf->size + len + 1);+	memmove(buf->ptr + buf->size, data, len);+	buf->size += len;+	buf->ptr[buf->size] = '\0';+	return 0;+}++int git_buf_puts(git_buf *buf, const char *string)+{+	assert(string);+	return git_buf_put(buf, string, strlen(string));+}++int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)+{+	int len;++	ENSURE_SIZE(buf, buf->size + (strlen(format) * 2));++	while (1) {+		va_list args;+		va_copy(args, ap);++		len = p_vsnprintf(+			buf->ptr + buf->size,+			buf->asize - buf->size,+			format, args+		);++		if (len < 0) {+			git__free(buf->ptr);+			buf->ptr = git_buf__oom;+			return -1;+		}++		if ((size_t)len + 1 <= buf->asize - buf->size) {+			buf->size += len;+			break;+		}++		ENSURE_SIZE(buf, buf->size + len + 1);+	}++	return 0;+}++int git_buf_printf(git_buf *buf, const char *format, ...)+{+	int r;+	va_list ap;++	va_start(ap, format);+	r = git_buf_vprintf(buf, format, ap);+	va_end(ap);++	return r;+}++void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)+{+	size_t copylen;++	assert(data && datasize && buf);++	data[0] = '\0';++	if (buf->size == 0 || buf->asize <= 0)+		return;++	copylen = buf->size;+	if (copylen > datasize - 1)+		copylen = datasize - 1;+	memmove(data, buf->ptr, copylen);+	data[copylen] = '\0';+}++void git_buf_consume(git_buf *buf, const char *end)+{+	if (end > buf->ptr && end <= buf->ptr + buf->size) {+		size_t consumed = end - buf->ptr;+		memmove(buf->ptr, end, buf->size - consumed);+		buf->size -= consumed;+		buf->ptr[buf->size] = '\0';+	}+}++void git_buf_truncate(git_buf *buf, size_t len)+{+	if (len < buf->size) {+		buf->size = len;+		buf->ptr[buf->size] = '\0';+	}+}++void git_buf_rtruncate_at_char(git_buf *buf, char separator)+{+	ssize_t idx = git_buf_rfind_next(buf, separator);+	git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);+}++void git_buf_swap(git_buf *buf_a, git_buf *buf_b)+{+	git_buf t = *buf_a;+	*buf_a = *buf_b;+	*buf_b = t;+}++char *git_buf_detach(git_buf *buf)+{+	char *data = buf->ptr;++	if (buf->asize == 0 || buf->ptr == git_buf__oom)+		return NULL;++	git_buf_init(buf, 0);++	return data;+}++void git_buf_attach(git_buf *buf, char *ptr, size_t asize)+{+	git_buf_free(buf);++	if (ptr) {+		buf->ptr = ptr;+		buf->size = strlen(ptr);+		if (asize)+			buf->asize = (asize < buf->size) ? buf->size + 1 : asize;+		else /* pass 0 to fall back on strlen + 1 */+			buf->asize = buf->size + 1;+	} else {+		git_buf_grow(buf, asize);+	}+}++int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)+{+	va_list ap;+	int i;+	size_t total_size = 0, original_size = buf->size;+	char *out, *original = buf->ptr;++	if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)+		++total_size; /* space for initial separator */++	/* Make two passes to avoid multiple reallocation */++	va_start(ap, nbuf);+	for (i = 0; i < nbuf; ++i) {+		const char* segment;+		size_t segment_len;++		segment = va_arg(ap, const char *);+		if (!segment)+			continue;++		segment_len = strlen(segment);+		total_size += segment_len;+		if (segment_len == 0 || segment[segment_len - 1] != separator)+			++total_size; /* space for separator */+	}+	va_end(ap);++	/* expand buffer if needed */+	if (total_size == 0)+		return 0;+	if (git_buf_grow(buf, buf->size + total_size + 1) < 0)+		return -1;++	out = buf->ptr + buf->size;++	/* append separator to existing buf if needed */+	if (buf->size > 0 && out[-1] != separator)+		*out++ = separator;++	va_start(ap, nbuf);+	for (i = 0; i < nbuf; ++i) {+		const char* segment;+		size_t segment_len;++		segment = va_arg(ap, const char *);+		if (!segment)+			continue;++		/* deal with join that references buffer's original content */+		if (segment >= original && segment < original + original_size) {+			size_t offset = (segment - original);+			segment = buf->ptr + offset;+			segment_len = original_size - offset;+		} else {+			segment_len = strlen(segment);+		}++		/* skip leading separators */+		if (out > buf->ptr && out[-1] == separator)+			while (segment_len > 0 && *segment == separator) {+				segment++;+				segment_len--;+			}++		/* copy over next buffer */+		if (segment_len > 0) {+			memmove(out, segment, segment_len);+			out += segment_len;+		}++		/* append trailing separator (except for last item) */+		if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)+			*out++ = separator;+	}+	va_end(ap);++	/* set size based on num characters actually written */+	buf->size = out - buf->ptr;+	buf->ptr[buf->size] = '\0';++	return 0;+}++int git_buf_join(+	git_buf *buf,+	char separator,+	const char *str_a,+	const char *str_b)+{+	size_t strlen_a = str_a ? strlen(str_a) : 0;+	size_t strlen_b = strlen(str_b);+	int need_sep = 0;+	ssize_t offset_a = -1;++	/* not safe to have str_b point internally to the buffer */+	assert(str_b < buf->ptr || str_b > buf->ptr + buf->size);++	/* figure out if we need to insert a separator */+	if (separator && strlen_a) {+		while (*str_b == separator) { str_b++; strlen_b--; }+		if (str_a[strlen_a - 1] != separator)+			need_sep = 1;+	}++	/* str_a could be part of the buffer */+	if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)+		offset_a = str_a - buf->ptr;++	if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)+		return -1;++	/* fix up internal pointers */+	if (offset_a >= 0)+		str_a = buf->ptr + offset_a;++	/* do the actual copying */+	if (offset_a != 0)+		memmove(buf->ptr, str_a, strlen_a);+	if (need_sep)+		buf->ptr[strlen_a] = separator;+	memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);++	buf->size = strlen_a + strlen_b + need_sep;+	buf->ptr[buf->size] = '\0';++	return 0;+}++void git_buf_rtrim(git_buf *buf)+{+	while (buf->size > 0) {+		if (!git__isspace(buf->ptr[buf->size - 1]))+			break;++		buf->size--;+	}++	buf->ptr[buf->size] = '\0';+}++int git_buf_cmp(const git_buf *a, const git_buf *b)+{+	int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));+	return (result != 0) ? result :+		(a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;+}++int git_buf_common_prefix(git_buf *buf, const git_strarray *strings)+{+	size_t i;+	const char *str, *pfx;++	git_buf_clear(buf);++	if (!strings || !strings->count)+		return 0;++	/* initialize common prefix to first string */+	if (git_buf_sets(buf, strings->strings[0]) < 0)+		return -1;++	/* go through the rest of the strings, truncating to shared prefix */+	for (i = 1; i < strings->count; ++i) {++		for (str = strings->strings[i], pfx = buf->ptr;+			 *str && *str == *pfx; str++, pfx++)+			/* scanning */;++		git_buf_truncate(buf, pfx - buf->ptr);++		if (!buf->size)+			break;+	}++	return 0;+}++bool git_buf_is_binary(const git_buf *buf)+{+	size_t i;+	int printable = 0, nonprintable = 0;++	for (i = 0; i < buf->size; i++) {+		unsigned char c = buf->ptr[i];+		if (c > 0x1F && c < 0x7F)+			printable++;+		else if (c == '\0')+			return true;+		else if (!git__isspace(c))+			nonprintable++;+	}++	return ((printable >> 7) < nonprintable);+}+
+ libgit2/src/buffer.h view
@@ -0,0 +1,135 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_buffer_h__+#define INCLUDE_buffer_h__++#include "common.h"+#include <stdarg.h>++typedef struct {+	char *ptr;+	size_t asize, size;+} git_buf;++extern char git_buf__initbuf[];+extern char git_buf__oom[];++#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }++/**+ * Initialize a git_buf structure.+ *+ * For the cases where GIT_BUF_INIT cannot be used to do static+ * initialization.+ */+void git_buf_init(git_buf *buf, size_t initial_size);++/**+ * Grow the buffer to hold at least `target_size` bytes.+ *+ * If the allocation fails, this will return an error and the buffer+ * will be marked as invalid for future operations.  The existing+ * contents of the buffer will be preserved however.+ * @return 0 on success or -1 on failure+ */+int git_buf_grow(git_buf *buf, size_t target_size);++/**+ * Attempt to grow the buffer to hold at least `target_size` bytes.+ *+ * This is just like `git_buf_grow` except that even if the allocation+ * fails, the git_buf will still be left in a valid state.+ */+int git_buf_try_grow(git_buf *buf, size_t target_size);++void git_buf_free(git_buf *buf);+void git_buf_swap(git_buf *buf_a, git_buf *buf_b);+char *git_buf_detach(git_buf *buf);+void git_buf_attach(git_buf *buf, char *ptr, size_t asize);++/**+ * Test if there have been any reallocation failures with this git_buf.+ *+ * Any function that writes to a git_buf can fail due to memory allocation+ * issues.  If one fails, the git_buf will be marked with an OOM error and+ * further calls to modify the buffer will fail.  Check git_buf_oom() at the+ * end of your sequence and it will be true if you ran out of memory at any+ * point with that buffer.+ *+ * @return false if no error, true if allocation error+ */+GIT_INLINE(bool) git_buf_oom(const git_buf *buf)+{+	return (buf->ptr == git_buf__oom);+}++/*+ * Functions below that return int value error codes will return 0 on+ * success or -1 on failure (which generally means an allocation failed).+ * Using a git_buf where the allocation has failed with result in -1 from+ * all further calls using that buffer.  As a result, you can ignore the+ * return code of these functions and call them in a series then just call+ * git_buf_oom at the end.+ */+int git_buf_set(git_buf *buf, const char *data, size_t len);+int git_buf_sets(git_buf *buf, const char *string);+int git_buf_putc(git_buf *buf, char c);+int git_buf_put(git_buf *buf, const char *data, size_t len);+int git_buf_puts(git_buf *buf, const char *string);+int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);+int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);+void git_buf_clear(git_buf *buf);+void git_buf_consume(git_buf *buf, const char *end);+void git_buf_truncate(git_buf *buf, size_t len);+void git_buf_rtruncate_at_char(git_buf *path, char separator);++int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);+int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);++/**+ * Join two strings as paths, inserting a slash between as needed.+ * @return 0 on success, -1 on failure+ */+GIT_INLINE(int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)+{+	return git_buf_join(buf, '/', a, b);+}++GIT_INLINE(const char *) git_buf_cstr(const git_buf *buf)+{+	return buf->ptr;+}++GIT_INLINE(size_t) git_buf_len(const git_buf *buf)+{+	return buf->size;+}++void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);++#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)++GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)+{+	ssize_t idx = (ssize_t)buf->size - 1;+	while (idx >= 0 && buf->ptr[idx] == ch) idx--;+	while (idx >= 0 && buf->ptr[idx] != ch) idx--;+	return idx;+}++/* Remove whitespace from the end of the buffer */+void git_buf_rtrim(git_buf *buf);++int git_buf_cmp(const git_buf *a, const git_buf *b);++/* Fill buf with the common prefix of a array of strings */+int git_buf_common_prefix(git_buf *buf, const git_strarray *strings);++/* Check if buffer looks like it contains binary data */+bool git_buf_is_binary(const git_buf *buf);++#endif
+ libgit2/src/cache.c view
@@ -0,0 +1,99 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "repository.h"+#include "commit.h"+#include "thread-utils.h"+#include "util.h"+#include "cache.h"++int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)+{+	if (size < 8)+		size = 8;+	size = git__size_t_powerof2(size);++	cache->size_mask = size - 1;+	cache->lru_count = 0;+	cache->free_obj = free_ptr;++	git_mutex_init(&cache->lock);++	cache->nodes = git__malloc(size * sizeof(git_cached_obj *));+	GITERR_CHECK_ALLOC(cache->nodes);++	memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));+	return 0;+}++void git_cache_free(git_cache *cache)+{+	size_t i;++	for (i = 0; i < (cache->size_mask + 1); ++i) {+		if (cache->nodes[i] != NULL)+			git_cached_obj_decref(cache->nodes[i], cache->free_obj);+	}++	git__free(cache->nodes);+}++void *git_cache_get(git_cache *cache, const git_oid *oid)+{+	uint32_t hash;+	git_cached_obj *node = NULL, *result = NULL;++	memcpy(&hash, oid->id, sizeof(hash));++	git_mutex_lock(&cache->lock);+	{+		node = cache->nodes[hash & cache->size_mask];++		if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) {+			git_cached_obj_incref(node);+			result = node;+		}+	}+	git_mutex_unlock(&cache->lock);++	return result;+}++void *git_cache_try_store(git_cache *cache, void *_entry)+{+	git_cached_obj *entry = _entry;+	uint32_t hash;++	memcpy(&hash, &entry->oid, sizeof(hash));++	/* increase the refcount on this object, because+	 * the cache now owns it */+	git_cached_obj_incref(entry);++	git_mutex_lock(&cache->lock);+	{+		git_cached_obj *node = cache->nodes[hash & cache->size_mask];++		if (node == NULL) {+			cache->nodes[hash & cache->size_mask] = entry;+		} else if (git_oid_cmp(&node->oid, &entry->oid) == 0) {+			git_cached_obj_decref(entry, cache->free_obj);+			entry = node;+		} else {+			git_cached_obj_decref(node, cache->free_obj);+			cache->nodes[hash & cache->size_mask] = entry;+		}+	}+	git_mutex_unlock(&cache->lock);++	/* increase the refcount again, because we are+	 * returning it to the user */+	git_cached_obj_incref(entry);++	return entry;+}
+ libgit2/src/cache.h view
@@ -0,0 +1,54 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_cache_h__+#define INCLUDE_cache_h__++#include "git2/common.h"+#include "git2/oid.h"+#include "git2/odb.h"++#include "thread-utils.h"++#define GIT_DEFAULT_CACHE_SIZE 128++typedef void (*git_cached_obj_freeptr)(void *);++typedef struct {+	git_oid oid;+	git_atomic refcount;+} git_cached_obj;++typedef struct {+	git_cached_obj **nodes;+	git_mutex lock;++	unsigned int lru_count;+	size_t size_mask;+	git_cached_obj_freeptr free_obj;+} git_cache;++int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr);+void git_cache_free(git_cache *cache);++void *git_cache_try_store(git_cache *cache, void *entry);+void *git_cache_get(git_cache *cache, const git_oid *oid);++GIT_INLINE(void) git_cached_obj_incref(void *_obj)+{+	git_cached_obj *obj = _obj;+	git_atomic_inc(&obj->refcount);+}++GIT_INLINE(void) git_cached_obj_decref(void *_obj, git_cached_obj_freeptr free_obj)+{+	git_cached_obj *obj = _obj;++	if (git_atomic_dec(&obj->refcount) == 0)+		free_obj(obj);+}++#endif
+ libgit2/src/cc-compat.h view
@@ -0,0 +1,69 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_compat_h__+#define INCLUDE_compat_h__++/*+ * See if our compiler is known to support flexible array members.+ */+#ifndef GIT_FLEX_ARRAY+#	if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)+#		define GIT_FLEX_ARRAY /* empty */+#	elif defined(__GNUC__)+#		if (__GNUC__ >= 3)+#			define GIT_FLEX_ARRAY /* empty */+#		else+#			define GIT_FLEX_ARRAY 0 /* older GNU extension */+#		endif+#	endif++/* Default to safer but a bit wasteful traditional style */+#	ifndef GIT_FLEX_ARRAY+#		define GIT_FLEX_ARRAY 1+#	endif+#endif++#ifdef __GNUC__+#	define GIT_TYPEOF(x) (__typeof__(x))+#else+#	define GIT_TYPEOF(x)+#endif++#define GIT_UNUSED(x) ((void)(x))++/* Define the printf format specifer to use for size_t output */+#if defined(_MSC_VER) || defined(__MINGW32__)+#	define PRIuZ "Iu"+#else+#	define PRIuZ "zu"+#endif++/* Micosoft Visual C/C++ */+#if defined(_MSC_VER)+/* disable "deprecated function" warnings */+#	pragma warning ( disable : 4996 )+/* disable "conditional expression is constant" level 4 warnings */+#	pragma warning ( disable : 4127 )+#endif++#if defined (_MSC_VER)+	typedef unsigned char bool;+#	define true 1+#	define false 0+#else+#	include <stdbool.h>+#endif++#ifndef va_copy+#	ifdef __va_copy+#		define va_copy(dst, src) __va_copy(dst, src)+#	else+#		define va_copy(dst, src) ((dst) = (src))+#	endif+#endif++#endif /* INCLUDE_compat_h__ */
+ libgit2/src/commit.c view
@@ -0,0 +1,334 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "git2/common.h"+#include "git2/object.h"+#include "git2/repository.h"+#include "git2/signature.h"++#include "common.h"+#include "odb.h"+#include "commit.h"+#include "signature.h"+#include "message.h"++#include <stdarg.h>++#define COMMIT_BASIC_PARSE 0x0+#define COMMIT_FULL_PARSE 0x1++#define COMMIT_PRINT(commit) {\+	char oid[41]; oid[40] = 0;\+	git_oid_fmt(oid, &commit->object.id);\+	printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\+}++static void clear_parents(git_commit *commit)+{+	unsigned int i;++	for (i = 0; i < commit->parent_oids.length; ++i) {+		git_oid *parent = git_vector_get(&commit->parent_oids, i);+		git__free(parent);+	}++	git_vector_clear(&commit->parent_oids);+}++void git_commit__free(git_commit *commit)+{+	clear_parents(commit);+	git_vector_free(&commit->parent_oids);++	git_signature_free(commit->author);+	git_signature_free(commit->committer);++	git__free(commit->message);+	git__free(commit->message_encoding);+	git__free(commit);+}++const git_oid *git_commit_id(git_commit *c)+{+	return git_object_id((git_object *)c);+}++int git_commit_create_v(+		git_oid *oid,+		git_repository *repo,+		const char *update_ref,+		const git_signature *author,+		const git_signature *committer,+		const char *message_encoding,+		const char *message,+		const git_tree *tree,+		int parent_count,+		...)+{+	va_list ap;+	int i, res;+	const git_commit **parents;++	parents = git__malloc(parent_count * sizeof(git_commit *));+	GITERR_CHECK_ALLOC(parents);++	va_start(ap, parent_count);+	for (i = 0; i < parent_count; ++i)+		parents[i] = va_arg(ap, const git_commit *);+	va_end(ap);++	res = git_commit_create(+		oid, repo, update_ref, author, committer,+		message_encoding, message,+		tree, parent_count, parents);++	git__free((void *)parents);+	return res;+}++/* Update the reference named `ref_name` so it points to `oid` */+static int update_reference(git_repository *repo, git_oid *oid, const char *ref_name)+{+	git_reference *ref;+	int res;++	res = git_reference_lookup(&ref, repo, ref_name);++	/* If we haven't found the reference at all, we assume we need to create+	 * a new reference and that's it */+	if (res == GIT_ENOTFOUND) {+		giterr_clear();+		return git_reference_create_oid(NULL, repo, ref_name, oid, 1);+	}++	if (res < 0)+		return -1;++	/* If we have found a reference, but it's symbolic, we need to update+	 * the direct reference it points to */+	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {+		git_reference *aux;+		const char *sym_target;++		/* The target pointed at by this reference */+		sym_target = git_reference_target(ref);++		/* resolve the reference to the target it points to */+		res = git_reference_resolve(&aux, ref);++		/*+		 * if the symbolic reference pointed to an inexisting ref,+		 * this is means we're creating a new branch, for example.+		 * We need to create a new direct reference with that name+		 */+		if (res == GIT_ENOTFOUND) {+			giterr_clear();+			res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);+			git_reference_free(ref);+			return res;+		}++		/* free the original symbolic reference now; not before because+		 * we're using the `sym_target` pointer */+		git_reference_free(ref);++		if (res < 0)+			return -1;++		/* store the newly found direct reference in its place */+		ref = aux;+	}++	/* ref is made to point to `oid`: ref is either the original reference,+	 * or the target of the symbolic reference we've looked up */+	res = git_reference_set_oid(ref, oid);+	git_reference_free(ref);+	return res;+}++int git_commit_create(+		git_oid *oid,+		git_repository *repo,+		const char *update_ref,+		const git_signature *author,+		const git_signature *committer,+		const char *message_encoding,+		const char *message,+		const git_tree *tree,+		int parent_count,+		const git_commit *parents[])+{+	git_buf commit = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;+	int i;+	git_odb *odb;++	assert(git_object_owner((const git_object *)tree) == repo);++	git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));++	for (i = 0; i < parent_count; ++i) {+		assert(git_object_owner((const git_object *)parents[i]) == repo);+		git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));+	}++	git_signature__writebuf(&commit, "author ", author);+	git_signature__writebuf(&commit, "committer ", committer);++	if (message_encoding != NULL)+		git_buf_printf(&commit, "encoding %s\n", message_encoding);++	git_buf_putc(&commit, '\n');++	/* Remove comments by default */+	if (git_message_prettify(&cleaned_message, message, 1) < 0)+		goto on_error;++	if (git_buf_puts(&commit, git_buf_cstr(&cleaned_message)) < 0)+		goto on_error;++	git_buf_free(&cleaned_message);++	if (git_repository_odb__weakptr(&odb, repo) < 0)+		goto on_error;++	if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)+		goto on_error;++	git_buf_free(&commit);++	if (update_ref != NULL)+		return update_reference(repo, oid, update_ref);++	return 0;++on_error:+	git_buf_free(&commit);+	git_buf_free(&cleaned_message);+	giterr_set(GITERR_OBJECT, "Failed to create commit.");+	return -1;+}++int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)+{+	const char *buffer = data;+	const char *buffer_end = (const char *)data + len;++	git_oid parent_oid;++	git_vector_init(&commit->parent_oids, 4, NULL);++	if (git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ") < 0)+		goto bad_buffer;++	/*+	 * TODO: commit grafts!+	 */++	while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == 0) {+		git_oid *new_oid;++		new_oid = git__malloc(sizeof(git_oid));+		GITERR_CHECK_ALLOC(new_oid);++		git_oid_cpy(new_oid, &parent_oid);++		if (git_vector_insert(&commit->parent_oids, new_oid) < 0)+			return -1;+	}++	commit->author = git__malloc(sizeof(git_signature));+	GITERR_CHECK_ALLOC(commit->author);++	if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)+		return -1;++	/* Always parse the committer; we need the commit time */+	commit->committer = git__malloc(sizeof(git_signature));+	GITERR_CHECK_ALLOC(commit->committer);++	if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)+		return -1;++	if (git__prefixcmp(buffer, "encoding ") == 0) {+		const char *encoding_end;+		buffer += strlen("encoding ");++		encoding_end = buffer;+		while (encoding_end < buffer_end && *encoding_end != '\n')+			encoding_end++;++		commit->message_encoding = git__strndup(buffer, encoding_end - buffer);+		GITERR_CHECK_ALLOC(commit->message_encoding);++		buffer = encoding_end;+	}++	/* parse commit message */+	while (buffer < buffer_end - 1 && *buffer == '\n')+		buffer++;++	if (buffer <= buffer_end) {+		commit->message = git__strndup(buffer, buffer_end - buffer);+		GITERR_CHECK_ALLOC(commit->message);+	}++	return 0;++bad_buffer:+	giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");+	return -1;+}++int git_commit__parse(git_commit *commit, git_odb_object *obj)+{+	assert(commit);+	return git_commit__parse_buffer(commit, obj->raw.data, obj->raw.len);+}++#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \+	_rvalue git_commit_##_name(git_commit *commit) \+	{\+		assert(commit); \+		return _return; \+	}++GIT_COMMIT_GETTER(const git_signature *, author, commit->author)+GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)+GIT_COMMIT_GETTER(const char *, message, commit->message)+GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)+GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)+GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)+GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length)+GIT_COMMIT_GETTER(const git_oid *, tree_oid, &commit->tree_oid);+++int git_commit_tree(git_tree **tree_out, git_commit *commit)+{+	assert(commit);+	return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_oid);+}++int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)+{+	git_oid *parent_oid;+	assert(commit);++	parent_oid = git_vector_get(&commit->parent_oids, n);+	if (parent_oid == NULL) {+		giterr_set(GITERR_INVALID, "Parent %u does not exist", n);+		return GIT_ENOTFOUND;+	}++	return git_commit_lookup(parent, commit->object.repo, parent_oid);+}++const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n)+{+	assert(commit);++	return git_vector_get(&commit->parent_oids, n);+}
+ libgit2/src/commit.h view
@@ -0,0 +1,34 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_commit_h__+#define INCLUDE_commit_h__++#include "git2/commit.h"+#include "tree.h"+#include "repository.h"+#include "vector.h"++#include <time.h>++struct git_commit {+	git_object object;++	git_vector parent_oids;+	git_oid tree_oid;++	git_signature *author;+	git_signature *committer;++	char *message_encoding;+	char *message;+};++void git_commit__free(git_commit *c);+int git_commit__parse(git_commit *commit, git_odb_object *obj);++int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len);+#endif
+ libgit2/src/common.h view
@@ -0,0 +1,72 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_common_h__+#define INCLUDE_common_h__++#include "git2/common.h"+#include "cc-compat.h"++#include <assert.h>+#include <errno.h>+#include <limits.h>+#include <stdlib.h>+#include <stdio.h>+#include <string.h>++#include <sys/types.h>+#include <sys/stat.h>++#ifdef GIT_WIN32++# include <io.h>+# include <direct.h>+# include <windows.h>+# include "win32/msvc-compat.h"+# include "win32/mingw-compat.h"+# ifdef GIT_THREADS+#	include "win32/pthread.h"+#endif++# define snprintf _snprintf++#else+# include <unistd.h>++# ifdef GIT_THREADS+#	include <pthread.h>+# endif+#endif++#include "git2/types.h"+#include "git2/errors.h"+#include "thread-utils.h"+#include "bswap.h"++#include <regex.h>++extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);+#define git__throw(error, ...) \+	(git___throw(__VA_ARGS__), error)++extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);+#define git__rethrow(error, ...) \+	(git___rethrow(__VA_ARGS__), error)+++#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }++void giterr_set_oom(void);+void giterr_set(int error_class, const char *string, ...);+void giterr_clear(void);+void giterr_set_str(int error_class, const char *string);+void giterr_set_regex(const regex_t *regex, int error_code);+++#include "util.h"+++#endif /* INCLUDE_common_h__ */
+ libgit2/src/config.c view
@@ -0,0 +1,508 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "fileops.h"+#include "config.h"+#include "git2/config.h"+#include "vector.h"+#if GIT_WIN32+# include <windows.h>+#endif++#include <ctype.h>++typedef struct {+	git_config_file *file;+	int priority;+} file_internal;++static void config_free(git_config *cfg)+{+	unsigned int i;+	git_config_file *file;+	file_internal *internal;++	for(i = 0; i < cfg->files.length; ++i){+		internal = git_vector_get(&cfg->files, i);+		file = internal->file;+		file->free(file);+		git__free(internal);+	}++	git_vector_free(&cfg->files);+	git__free(cfg);+}++void git_config_free(git_config *cfg)+{+	if (cfg == NULL)+		return;++	GIT_REFCOUNT_DEC(cfg, config_free);+}++static int config_backend_cmp(const void *a, const void *b)+{+	const file_internal *bk_a = (const file_internal *)(a);+	const file_internal *bk_b = (const file_internal *)(b);++	return bk_b->priority - bk_a->priority;+}++int git_config_new(git_config **out)+{+	git_config *cfg;++	cfg = git__malloc(sizeof(git_config));+	GITERR_CHECK_ALLOC(cfg);++	memset(cfg, 0x0, sizeof(git_config));++	if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {+		git__free(cfg);+		return -1;+	}++	*out = cfg;+	GIT_REFCOUNT_INC(cfg);+	return 0;+}++int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)+{+	git_config_file *file = NULL;++	if (git_config_file__ondisk(&file, path) < 0)+		return -1;++	if (git_config_add_file(cfg, file, priority) < 0) {+		/*+		 * free manually; the file is not owned by the config+		 * instance yet and will not be freed on cleanup+		 */+		file->free(file);+		return -1;+	}++	return 0;+}++int git_config_open_ondisk(git_config **cfg, const char *path)+{+	if (git_config_new(cfg) < 0)+		return -1;++	if (git_config_add_file_ondisk(*cfg, path, 1) < 0) {+		git_config_free(*cfg);+		return -1;+	}++	return 0;+}++int git_config_add_file(git_config *cfg, git_config_file *file, int priority)+{+	file_internal *internal;+	int result;++	assert(cfg && file);++	if ((result = file->open(file)) < 0)+		return result;++	internal = git__malloc(sizeof(file_internal));+	GITERR_CHECK_ALLOC(internal);++	internal->file = file;+	internal->priority = priority;++	if (git_vector_insert(&cfg->files, internal) < 0) {+		git__free(internal);+		return -1;+	}++	git_vector_sort(&cfg->files);+	internal->file->cfg = cfg;++	return 0;+}++/*+ * Loop over all the variables+ */++int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)+{+	int ret = 0;+	unsigned int i;+	file_internal *internal;+	git_config_file *file;++	for(i = 0; i < cfg->files.length && ret == 0; ++i) {+		internal = git_vector_get(&cfg->files, i);+		file = internal->file;+		ret = file->foreach(file, fn, data);+	}++	return ret;+}++int git_config_delete(git_config *cfg, const char *name)+{+	file_internal *internal;+	git_config_file *file;++	assert(cfg->files.length);++	internal = git_vector_get(&cfg->files, 0);+	file = internal->file;++	return file->del(file, name);+}++/**************+ * Setters+ **************/++int git_config_set_int64(git_config *cfg, const char *name, int64_t value)+{+	char str_value[32]; /* All numbers should fit in here */+	p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);+	return git_config_set_string(cfg, name, str_value);+}++int git_config_set_int32(git_config *cfg, const char *name, int32_t value)+{+	return git_config_set_int64(cfg, name, (int64_t)value);+}++int git_config_set_bool(git_config *cfg, const char *name, int value)+{+	return git_config_set_string(cfg, name, value ? "true" : "false");+}++int git_config_set_string(git_config *cfg, const char *name, const char *value)+{+	file_internal *internal;+	git_config_file *file;++	assert(cfg->files.length);++	internal = git_vector_get(&cfg->files, 0);+	file = internal->file;++	return file->set(file, name, value);+}++static int parse_int64(int64_t *out, const char *value)+{+	const char *num_end;+	int64_t num;++	if (git__strtol64(&num, value, &num_end, 0) < 0)+		return -1;++	switch (*num_end) {+	case 'g':+	case 'G':+		num *= 1024;+		/* fallthrough */++	case 'm':+	case 'M':+		num *= 1024;+		/* fallthrough */++	case 'k':+	case 'K':+		num *= 1024;++		/* check that that there are no more characters after the+		 * given modifier suffix */+		if (num_end[1] != '\0')+			return -1;++		/* fallthrough */++	case '\0':+		*out = num;+		return 0;++	default:+		return -1;+	}+}++static int parse_int32(int32_t *out, const char *value)+{+	int64_t tmp;+	int32_t truncate;++	if (parse_int64(&tmp, value) < 0)+		return -1;++	truncate = tmp & 0xFFFFFFFF;+	if (truncate != tmp)+		return -1;++	*out = truncate;+	return 0;+}++/***********+ * Getters+ ***********/+int git_config_lookup_map_value(+	git_cvar_map *maps, size_t map_n, const char *value, int *out)+{+	size_t i;++	if (!value)+		return GIT_ENOTFOUND;++	for (i = 0; i < map_n; ++i) {+		git_cvar_map *m = maps + i;++		switch (m->cvar_type) {+		case GIT_CVAR_FALSE:+		case GIT_CVAR_TRUE: {+			int bool_val;++			if (git__parse_bool(&bool_val, value) == 0 &&+				bool_val == (int)m->cvar_type) {+				*out = m->map_value;+				return 0;+			}+			break;+		}++		case GIT_CVAR_INT32:+			if (parse_int32(out, value) == 0)+				return 0;+			break;++		case GIT_CVAR_STRING:+			if (strcasecmp(value, m->str_match) == 0) {+				*out = m->map_value;+				return 0;+			}+			break;+		}+	}++	return GIT_ENOTFOUND;+}++int git_config_get_mapped(+	int *out,+	git_config *cfg,+	const char *name,+	git_cvar_map *maps,+	size_t map_n)+{+	const char *value;+	int ret;++	ret = git_config_get_string(&value, cfg, name);+	if (ret < 0)+		return ret;++	if (!git_config_lookup_map_value(maps, map_n, value, out))+		return 0;++	giterr_set(GITERR_CONFIG,+		"Failed to map the '%s' config variable with a valid value", name);+	return -1;+}++int git_config_get_int64(int64_t *out, git_config *cfg, const char *name)+{+	const char *value;+	int ret;++	ret = git_config_get_string(&value, cfg, name);+	if (ret < 0)+		return ret;++	if (parse_int64(out, value) < 0) {+		giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);+		return -1;+	}++	return 0;+}++int git_config_get_int32(int32_t *out, git_config *cfg, const char *name)+{+	const char *value;+	int ret;++	ret = git_config_get_string(&value, cfg, name);+	if (ret < 0)+		return ret;++	if (parse_int32(out, value) < 0) {+		giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);+		return -1;+	}++	return 0;+}++int git_config_get_bool(int *out, git_config *cfg, const char *name)+{+	const char *value;+	int ret;++	ret = git_config_get_string(&value, cfg, name);+	if (ret < 0)+		return ret;++	if (git__parse_bool(out, value) == 0)+		return 0;++	if (parse_int32(out, value) == 0) {+		*out = !!(*out);+		return 0;+	}++	giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);+	return -1;+}++int git_config_get_string(const char **out, git_config *cfg, const char *name)+{+	file_internal *internal;+	unsigned int i;++	assert(cfg->files.length);++	*out = NULL;++	git_vector_foreach(&cfg->files, i, internal) {+		git_config_file *file = internal->file;+		int ret = file->get(file, name, out);+		if (ret != GIT_ENOTFOUND)+			return ret;+	}++	giterr_set(GITERR_CONFIG, "Config variable '%s' not found", name);+	return GIT_ENOTFOUND;+}++int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,+			    int (*fn)(const char *value, void *data), void *data)+{+	file_internal *internal;+	git_config_file *file;+	int ret = GIT_ENOTFOUND;+	unsigned int i;++	assert(cfg->files.length);++	/*+	 * This loop runs the "wrong" way 'round because we need to+	 * look at every value from the most general to most specific+	 */+	for (i = cfg->files.length; i > 0; --i) {+		internal = git_vector_get(&cfg->files, i - 1);+		file = internal->file;+		ret = file->get_multivar(file, name, regexp, fn, data);+		if (ret < 0 && ret != GIT_ENOTFOUND)+			return ret;+	}++	return 0;+}++int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)+{+	file_internal *internal;+	git_config_file *file;+	int ret = GIT_ENOTFOUND;+	unsigned int i;++	for (i = cfg->files.length; i > 0; --i) {+		internal = git_vector_get(&cfg->files, i - 1);+		file = internal->file;+		ret = file->set_multivar(file, name, regexp, value);+		if (ret < 0 && ret != GIT_ENOTFOUND)+			return ret;+	}++	return 0;+}++int git_config_find_global_r(git_buf *path)+{+	return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);+}++int git_config_find_global(char *global_config_path, size_t length)+{+	git_buf path  = GIT_BUF_INIT;+	int     ret = git_config_find_global_r(&path);++	if (ret < 0) {+		git_buf_free(&path);+		return ret;+	}++	if (path.size >= length) {+		git_buf_free(&path);+		giterr_set(GITERR_NOMEMORY,+			"Path is to long to fit on the given buffer");+		return -1;+	}++	git_buf_copy_cstr(global_config_path, length, &path);+	git_buf_free(&path);+	return 0;+}++int git_config_find_system_r(git_buf *path)+{+	return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);+}++int git_config_find_system(char *system_config_path, size_t length)+{+	git_buf path  = GIT_BUF_INIT;+	int     ret = git_config_find_system_r(&path);++	if (ret < 0) {+		git_buf_free(&path);+		return ret;+	}++	if (path.size >= length) {+		git_buf_free(&path);+		giterr_set(GITERR_NOMEMORY,+			"Path is to long to fit on the given buffer");+		return -1;+	}++	git_buf_copy_cstr(system_config_path, length, &path);+	git_buf_free(&path);+	return 0;+}++int git_config_open_global(git_config **out)+{+	int error;+	git_buf path = GIT_BUF_INIT;++	if ((error = git_config_find_global_r(&path)) < 0)+		return error;++	error = git_config_open_ondisk(out, git_buf_cstr(&path));+	git_buf_free(&path);++	return error;+}+
+ libgit2/src/config.h view
@@ -0,0 +1,33 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_config_h__+#define INCLUDE_config_h__++#include "git2.h"+#include "git2/config.h"+#include "vector.h"+#include "repository.h"++#define GIT_CONFIG_FILENAME ".gitconfig"+#define GIT_CONFIG_FILENAME_INREPO "config"+#define GIT_CONFIG_FILENAME_SYSTEM "gitconfig"+#define GIT_CONFIG_FILE_MODE 0666++struct git_config {+	git_refcount rc;+	git_vector files;+};++extern int git_config_find_global_r(git_buf *global_config_path);+extern int git_config_find_system_r(git_buf *system_config_path);++extern int git_config_parse_bool(int *out, const char *bool_string);++extern int git_config_lookup_map_value(+	git_cvar_map *maps, size_t map_n, const char *value, int *out);++#endif
+ libgit2/src/config_cache.c view
@@ -0,0 +1,94 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "fileops.h"+#include "config.h"+#include "git2/config.h"+#include "vector.h"+#include "filter.h"+#include "repository.h"++struct map_data {+	const char *cvar_name;+	git_cvar_map *maps;+	size_t map_count;+	int default_value;+};++/*+ *	core.eol+ *		Sets the line ending type to use in the working directory for+ *	files that have the text property set. Alternatives are lf, crlf+ *	and native, which uses the platform’s native line ending. The default+ *	value is native. See gitattributes(5) for more information on+ *	end-of-line conversion. + */+static git_cvar_map _cvar_map_eol[] = {+	{GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},+	{GIT_CVAR_STRING, "lf", GIT_EOL_LF},+	{GIT_CVAR_STRING, "crlf", GIT_EOL_CRLF},+	{GIT_CVAR_STRING, "native", GIT_EOL_NATIVE}+};++/*+ *	core.autocrlf+ *		Setting this variable to "true" is almost the same as setting + *	the text attribute to "auto" on all files except that text files are+ *	not guaranteed to be normalized: files that contain CRLF in the+ *	repository will not be touched. Use this setting if you want to have+ *	CRLF line endings in your working directory even though the repository+ *	does not have normalized line endings. This variable can be set to input,+ *	in which case no output conversion is performed.+ */+static git_cvar_map _cvar_map_autocrlf[] = {+	{GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},+	{GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},+	{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}+};++static struct map_data _cvar_maps[] = {+	{"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},+	{"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}+};++int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)+{+	*out = repo->cvar_cache[(int)cvar];++	if (*out == GIT_CVAR_NOT_CACHED) {+		struct map_data *data = &_cvar_maps[(int)cvar];+		git_config *config;+		int error;++		error = git_repository_config__weakptr(&config, repo);+		if (error < 0)+			return error;++		error = git_config_get_mapped(out,+			config, data->cvar_name, data->maps, data->map_count);++		if (error == GIT_ENOTFOUND)+			*out = data->default_value;++		else if (error < 0)+			return error;++		repo->cvar_cache[(int)cvar] = *out;+	}++	return 0;+}++void git_repository__cvar_cache_clear(git_repository *repo)+{+	int i;++	for (i = 0; i < GIT_CVAR_CACHE_MAX; ++i)+		repo->cvar_cache[i] = GIT_CVAR_NOT_CACHED;+}+
+ libgit2/src/config_file.c view
@@ -0,0 +1,1318 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "config.h"+#include "fileops.h"+#include "filebuf.h"+#include "buffer.h"+#include "git2/config.h"+#include "git2/types.h"+#include "strmap.h"++#include <ctype.h>+#include <sys/types.h>+#include <regex.h>++GIT__USE_STRMAP;++typedef struct cvar_t {+	struct cvar_t *next;+	char *key; /* TODO: we might be able to get rid of this */+	char *value;+} cvar_t;++typedef struct {+	struct cvar_t *head;+	struct cvar_t *tail;+} cvar_t_list;++#define CVAR_LIST_HEAD(list) ((list)->head)++#define CVAR_LIST_TAIL(list) ((list)->tail)++#define CVAR_LIST_NEXT(var) ((var)->next)++#define CVAR_LIST_EMPTY(list) ((list)->head == NULL)++#define CVAR_LIST_APPEND(list, var) do {\+	if (CVAR_LIST_EMPTY(list)) {\+		CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\+	} else {\+		CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\+		CVAR_LIST_TAIL(list) = var;\+	}\+} while(0)++#define CVAR_LIST_REMOVE_HEAD(list) do {\+	CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\+} while(0)++#define CVAR_LIST_REMOVE_AFTER(var) do {\+	CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\+} while(0)++#define CVAR_LIST_FOREACH(list, iter)\+	for ((iter) = CVAR_LIST_HEAD(list);\+		 (iter) != NULL;\+		 (iter) = CVAR_LIST_NEXT(iter))++/*+ * Inspired by the FreeBSD functions+ */+#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\+	for ((iter) = CVAR_LIST_HEAD(vars);\+		 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\+		 (iter) = (tmp))++typedef struct {+	git_config_file parent;++	git_strmap *values;++	struct {+		git_buf buffer;+		char *read_ptr;+		int line_number;+		int eof;+	} reader;++	char *file_path;+} diskfile_backend;++static int config_parse(diskfile_backend *cfg_file);+static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);+static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);++static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)+{+	giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",+		error_str, backend->file_path, backend->reader.line_number, col);+}++static void cvar_free(cvar_t *var)+{+	if (var == NULL)+		return;++	git__free(var->key);+	git__free(var->value);+	git__free(var);+}++/* Take something the user gave us and make it nice for our hash function */+static int normalize_name(const char *in, char **out)+{+	char *name, *fdot, *ldot;++	assert(in && out);++	name = git__strdup(in);+	GITERR_CHECK_ALLOC(name);++	fdot = strchr(name, '.');+	ldot = strrchr(name, '.');++	if (fdot == NULL || ldot == NULL) {+		git__free(name);+		giterr_set(GITERR_CONFIG,+			"Invalid variable name: '%s'", in);+		return -1;+	}++	/* Downcase up to the first dot and after the last one */+	git__strntolower(name, fdot - name);+	git__strtolower(ldot);++	*out = name;+	return 0;+}++static void free_vars(git_strmap *values)+{+	cvar_t *var = NULL;++	if (values == NULL)+		return;++	git_strmap_foreach_value(values, var,+		while (var != NULL) {+			cvar_t *next = CVAR_LIST_NEXT(var);+			cvar_free(var);+			var = next;+		});++	git_strmap_free(values);+}++static int config_open(git_config_file *cfg)+{+	int res;+	diskfile_backend *b = (diskfile_backend *)cfg;++	b->values = git_strmap_alloc();+	GITERR_CHECK_ALLOC(b->values);++	git_buf_init(&b->reader.buffer, 0);+	res = git_futils_readbuffer(&b->reader.buffer, b->file_path);++	/* It's fine if the file doesn't exist */+	if (res == GIT_ENOTFOUND)+		return 0;++	if (res < 0 || config_parse(b) <  0) {+		free_vars(b->values);+		b->values = NULL;+		git_buf_free(&b->reader.buffer);+		return -1;+	}++	git_buf_free(&b->reader.buffer);+	return 0;+}++static void backend_free(git_config_file *_backend)+{+	diskfile_backend *backend = (diskfile_backend *)_backend;++	if (backend == NULL)+		return;++	git__free(backend->file_path);+	free_vars(backend->values);+	git__free(backend);+}++static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)+{+	diskfile_backend *b = (diskfile_backend *)backend;+	cvar_t *var;+	const char *key;++	if (!b->values)+		return 0;++	git_strmap_foreach(b->values, key, var,+		do {+			if (fn(key, var->value, data) < 0)+				break;++			var = CVAR_LIST_NEXT(var);+		} while (var != NULL);+	);++	return 0;+}++static int config_set(git_config_file *cfg, const char *name, const char *value)+{+	cvar_t *var = NULL, *old_var;+	diskfile_backend *b = (diskfile_backend *)cfg;+	char *key;+	khiter_t pos;+	int rval;++	if (normalize_name(name, &key) < 0)+		return -1;++	/*+	 * Try to find it in the existing values and update it if it+	 * only has one value.+	 */+	pos = git_strmap_lookup_index(b->values, key);+	if (git_strmap_valid_index(b->values, pos)) {+		cvar_t *existing = git_strmap_value_at(b->values, pos);+		char *tmp = NULL;++		git__free(key);+		if (existing->next != NULL) {+			giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");+			return -1;+		}++		if (value) {+			tmp = git__strdup(value);+			GITERR_CHECK_ALLOC(tmp);+		}++		git__free(existing->value);+		existing->value = tmp;++		return config_write(b, existing->key, NULL, value);+	}++	var = git__malloc(sizeof(cvar_t));+	GITERR_CHECK_ALLOC(var);++	memset(var, 0x0, sizeof(cvar_t));++	var->key = key;+	var->value = NULL;++	if (value) {+		var->value = git__strdup(value);+		GITERR_CHECK_ALLOC(var->value);+	}++	if (config_write(b, key, NULL, value) < 0) {+		cvar_free(var);+		return -1;+	}++	git_strmap_insert2(b->values, key, var, old_var, rval);+	if (rval < 0)+		return -1;+	if (old_var != NULL)+		cvar_free(old_var);++	return 0;+}++/*+ * Internal function that actually gets the value in string form+ */+static int config_get(git_config_file *cfg, const char *name, const char **out)+{+	diskfile_backend *b = (diskfile_backend *)cfg;+	char *key;+	khiter_t pos;++	if (normalize_name(name, &key) < 0)+		return -1;++	pos = git_strmap_lookup_index(b->values, key);+	git__free(key);++	/* no error message; the config system will write one */+	if (!git_strmap_valid_index(b->values, pos))+		return GIT_ENOTFOUND;++	*out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value;++	return 0;+}++static int config_get_multivar(+	git_config_file *cfg,+	const char *name,+	const char *regex_str,+	int (*fn)(const char *, void *),+	void *data)+{+	cvar_t *var;+	diskfile_backend *b = (diskfile_backend *)cfg;+	char *key;+	khiter_t pos;++	if (normalize_name(name, &key) < 0)+		return -1;++	pos = git_strmap_lookup_index(b->values, key);+	git__free(key);++	if (!git_strmap_valid_index(b->values, pos))+		return GIT_ENOTFOUND;++	var = git_strmap_value_at(b->values, pos);++	if (regex_str != NULL) {+		regex_t regex;+		int result;++		/* regex matching; build the regex */+		result = regcomp(&regex, regex_str, REG_EXTENDED);+		if (result < 0) {+			giterr_set_regex(&regex, result);+			return -1;+		}++		/* and throw the callback only on the variables that+		 * match the regex */+		do {+			if (regexec(&regex, var->value, 0, NULL, 0) == 0) {+				/* early termination by the user is not an error;+				 * just break and return successfully */+				if (fn(var->value, data) < 0)+					break;+			}++			var = var->next;+		} while (var != NULL);+		regfree(&regex);+	} else {+		/* no regex; go through all the variables */+		do {+			/* early termination by the user is not an error;+			 * just break and return successfully */+			if (fn(var->value, data) < 0)+				break;++			var = var->next;+		} while (var != NULL);+	}++	return 0;+}++static int config_set_multivar(+	git_config_file *cfg, const char *name, const char *regexp, const char *value)+{+	int replaced = 0;+	cvar_t *var, *newvar;+	diskfile_backend *b = (diskfile_backend *)cfg;+	char *key;+	regex_t preg;+	int result;+	khiter_t pos;++	assert(regexp);++	if (normalize_name(name, &key) < 0)+		return -1;++	pos = git_strmap_lookup_index(b->values, key);+	if (!git_strmap_valid_index(b->values, pos)) {+		git__free(key);+		return GIT_ENOTFOUND;+	}++	var = git_strmap_value_at(b->values, pos);++	result = regcomp(&preg, regexp, REG_EXTENDED);+	if (result < 0) {+		git__free(key);+		giterr_set_regex(&preg, result);+		return -1;+	}++	for (;;) {+		if (regexec(&preg, var->value, 0, NULL, 0) == 0) {+			char *tmp = git__strdup(value);+			GITERR_CHECK_ALLOC(tmp);++			git__free(var->value);+			var->value = tmp;+			replaced = 1;+		}++		if (var->next == NULL)+			break;++		var = var->next;+	}++	/* If we've reached the end of the variables and we haven't found it yet, we need to append it */+	if (!replaced) {+		newvar = git__malloc(sizeof(cvar_t));+		GITERR_CHECK_ALLOC(newvar);++		memset(newvar, 0x0, sizeof(cvar_t));++		newvar->key = git__strdup(var->key);+		GITERR_CHECK_ALLOC(newvar->key);++		newvar->value = git__strdup(value);+		GITERR_CHECK_ALLOC(newvar->value);++		var->next = newvar;+	}++	result = config_write(b, key, &preg, value);++	git__free(key);+	regfree(&preg);++	return result;+}++static int config_delete(git_config_file *cfg, const char *name)+{+	cvar_t *var;+	diskfile_backend *b = (diskfile_backend *)cfg;+	char *key;+	int result;+	khiter_t pos;++	if (normalize_name(name, &key) < 0)+		return -1;++	pos = git_strmap_lookup_index(b->values, key);+	git__free(key);++	if (!git_strmap_valid_index(b->values, pos))+		return GIT_ENOTFOUND;++	var = git_strmap_value_at(b->values, pos);++	if (var->next != NULL) {+		giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");+		return -1;+	}++	git_strmap_delete_at(b->values, pos);++	result = config_write(b, var->key, NULL, NULL);++	cvar_free(var);+	return result;+}++int git_config_file__ondisk(git_config_file **out, const char *path)+{+	diskfile_backend *backend;++	backend = git__malloc(sizeof(diskfile_backend));+	GITERR_CHECK_ALLOC(backend);++	memset(backend, 0x0, sizeof(diskfile_backend));++	backend->file_path = git__strdup(path);+	GITERR_CHECK_ALLOC(backend->file_path);++	backend->parent.open = config_open;+	backend->parent.get = config_get;+	backend->parent.get_multivar = config_get_multivar;+	backend->parent.set = config_set;+	backend->parent.set_multivar = config_set_multivar;+	backend->parent.del = config_delete;+	backend->parent.foreach = file_foreach;+	backend->parent.free = backend_free;++	*out = (git_config_file *)backend;++	return 0;+}++static int cfg_getchar_raw(diskfile_backend *cfg)+{+	int c;++	c = *cfg->reader.read_ptr++;++	/*+	Win 32 line breaks: if we find a \r\n sequence,+	return only the \n as a newline+	*/+	if (c == '\r' && *cfg->reader.read_ptr == '\n') {+		cfg->reader.read_ptr++;+		c = '\n';+	}++	if (c == '\n')+		cfg->reader.line_number++;++	if (c == 0) {+		cfg->reader.eof = 1;+		c = '\n';+	}++	return c;+}++#define SKIP_WHITESPACE (1 << 1)+#define SKIP_COMMENTS (1 << 2)++static int cfg_getchar(diskfile_backend *cfg_file, int flags)+{+	const int skip_whitespace = (flags & SKIP_WHITESPACE);+	const int skip_comments = (flags & SKIP_COMMENTS);+	int c;++	assert(cfg_file->reader.read_ptr);++	do c = cfg_getchar_raw(cfg_file);+	while (skip_whitespace && git__isspace(c) &&+	       !cfg_file->reader.eof);++	if (skip_comments && (c == '#' || c == ';')) {+		do c = cfg_getchar_raw(cfg_file);+		while (c != '\n');+	}++	return c;+}++/*+ * Read the next char, but don't move the reading pointer.+ */+static int cfg_peek(diskfile_backend *cfg, int flags)+{+	void *old_read_ptr;+	int old_lineno, old_eof;+	int ret;++	assert(cfg->reader.read_ptr);++	old_read_ptr = cfg->reader.read_ptr;+	old_lineno = cfg->reader.line_number;+	old_eof = cfg->reader.eof;++	ret = cfg_getchar(cfg, flags);++	cfg->reader.read_ptr = old_read_ptr;+	cfg->reader.line_number = old_lineno;+	cfg->reader.eof = old_eof;++	return ret;+}++/*+ * Read and consume a line, returning it in newly-allocated memory.+ */+static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)+{+	char *line = NULL;+	char *line_src, *line_end;+	size_t line_len;++	line_src = cfg->reader.read_ptr;++	if (skip_whitespace) {+		/* Skip empty empty lines */+		while (git__isspace(*line_src))+			++line_src;+	}++	line_end = strchr(line_src, '\n');++	/* no newline at EOF */+	if (line_end == NULL)+		line_end = strchr(line_src, 0);++	line_len = line_end - line_src;++	line = git__malloc(line_len + 1);+	if (line == NULL)+		return NULL;++	memcpy(line, line_src, line_len);++	do line[line_len] = '\0';+	while (line_len-- > 0 && git__isspace(line[line_len]));++	if (*line_end == '\n')+		line_end++;++	if (*line_end == '\0')+		cfg->reader.eof = 1;++	cfg->reader.line_number++;+	cfg->reader.read_ptr = line_end;++	return line;+}++/*+ * Consume a line, without storing it anywhere+ */+static void cfg_consume_line(diskfile_backend *cfg)+{+	char *line_start, *line_end;++	line_start = cfg->reader.read_ptr;+	line_end = strchr(line_start, '\n');+	/* No newline at EOF */+	if(line_end == NULL){+		line_end = strchr(line_start, '\0');+	}++	if (*line_end == '\n')+		line_end++;++	if (*line_end == '\0')+		cfg->reader.eof = 1;++	cfg->reader.line_number++;+	cfg->reader.read_ptr = line_end;+}++GIT_INLINE(int) config_keychar(int c)+{+	return isalnum(c) || c == '-';+}++static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)+{+	int c, rpos;+	char *first_quote, *last_quote;+	git_buf buf = GIT_BUF_INIT;+	int quote_marks;+	/*+	 * base_name is what came before the space. We should be at the+	 * first quotation mark, except for now, line isn't being kept in+	 * sync so we only really use it to calculate the length.+	 */++	first_quote = strchr(line, '"');+	last_quote = strrchr(line, '"');++	if (last_quote - first_quote == 0) {+		set_parse_error(cfg, 0, "Missing closing quotation mark in section header");+		return -1;+	}++	git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);+	git_buf_printf(&buf, "%s.", base_name);++	rpos = 0;+	quote_marks = 0;++	line = first_quote;+	c = line[rpos++];++	/*+	 * At the end of each iteration, whatever is stored in c will be+	 * added to the string. In case of error, jump to out+	 */+	do {+		if (quote_marks == 2) {+			set_parse_error(cfg, rpos, "Unexpected text after closing quotes");+			git_buf_free(&buf);+			return -1;+		}++		switch (c) {+		case '"':+			++quote_marks;+			continue;++		case '\\':+			c = line[rpos++];++			switch (c) {+			case '"':+			case '\\':+				break;++			default:+				set_parse_error(cfg, rpos, "Unsupported escape sequence");+				git_buf_free(&buf);+				return -1;+			}++		default:+			break;+		}++		git_buf_putc(&buf, c);+	} while ((c = line[rpos++]) != ']');++	*section_name = git_buf_detach(&buf);+	return 0;+}++static int parse_section_header(diskfile_backend *cfg, char **section_out)+{+	char *name, *name_end;+	int name_length, c, pos;+	int result;+	char *line;++	line = cfg_readline(cfg, true);+	if (line == NULL)+		return -1;++	/* find the end of the variable's name */+	name_end = strchr(line, ']');+	if (name_end == NULL) {+		git__free(line);+		set_parse_error(cfg, 0, "Missing ']' in section header");+		return -1;+	}++	name = (char *)git__malloc((size_t)(name_end - line) + 1);+	GITERR_CHECK_ALLOC(name);++	name_length = 0;+	pos = 0;++	/* Make sure we were given a section header */+	c = line[pos++];+	assert(c == '[');++	c = line[pos++];++	do {+		if (git__isspace(c)){+			name[name_length] = '\0';+			result = parse_section_header_ext(cfg, line, name, section_out);+			git__free(line);+			git__free(name);+			return result;+		}++		if (!config_keychar(c) && c != '.') {+			set_parse_error(cfg, pos, "Unexpected character in header");+			goto fail_parse;+		}++		name[name_length++] = (char) tolower(c);++	} while ((c = line[pos++]) != ']');++	if (line[pos - 1] != ']') {+		set_parse_error(cfg, pos, "Unexpected end of file");+		goto fail_parse;+	}++	git__free(line);++	name[name_length] = 0;+	*section_out = name;++	return 0;++fail_parse:+	git__free(line);+	git__free(name);+	return -1;+}++static int skip_bom(diskfile_backend *cfg)+{+	static const char utf8_bom[] = "\xef\xbb\xbf";++	if (cfg->reader.buffer.size < sizeof(utf8_bom))+		return 0;++	if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)+		cfg->reader.read_ptr += sizeof(utf8_bom);++	/* TODO: the reference implementation does pretty stupid+		shit with the BoM+	*/++	return 0;+}++/*+	(* basic types *)+	digit = "0".."9"+	integer = digit { digit }+	alphabet = "a".."z" + "A" .. "Z"++	section_char = alphabet | "." | "-"+	extension_char = (* any character except newline *)+	any_char = (* any character *)+	variable_char = "alphabet" | "-"+++	(* actual grammar *)+	config = { section }++	section = header { definition }++	header = "[" section [subsection | subsection_ext] "]"++	subsection = "." section+	subsection_ext = "\"" extension "\""++	section = section_char { section_char }+	extension = extension_char { extension_char }++	definition = variable_name ["=" variable_value] "\n"++	variable_name = variable_char { variable_char }+	variable_value = string | boolean | integer++	string = quoted_string | plain_string+	quoted_string = "\"" plain_string "\""+	plain_string = { any_char }++	boolean = boolean_true | boolean_false+	boolean_true = "yes" | "1" | "true" | "on"+	boolean_false = "no" | "0" | "false" | "off"+*/++static int strip_comments(char *line, int in_quotes)+{+	int quote_count = in_quotes;+	char *ptr;++	for (ptr = line; *ptr; ++ptr) {+		if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')+			quote_count++;++		if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {+			ptr[0] = '\0';+			break;+		}+	}++	/* skip any space at the end */+	if (git__isspace(ptr[-1])) {+		ptr--;+	}+	ptr[0] = '\0';++	return quote_count;+}++static int config_parse(diskfile_backend *cfg_file)+{+	int c;+	char *current_section = NULL;+	char *var_name;+	char *var_value;+	cvar_t *var, *existing;+	git_buf buf = GIT_BUF_INIT;+	int result = 0;+	khiter_t pos;++	/* Initialize the reading position */+	cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;+	cfg_file->reader.eof = 0;++	/* If the file is empty, there's nothing for us to do */+	if (*cfg_file->reader.read_ptr == '\0')+		return 0;++	skip_bom(cfg_file);++	while (result == 0 && !cfg_file->reader.eof) {++		c = cfg_peek(cfg_file, SKIP_WHITESPACE);++		switch (c) {+		case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */+			cfg_file->reader.eof = 1;+			break;++		case '[': /* section header, new section begins */+			git__free(current_section);+			current_section = NULL;+			result = parse_section_header(cfg_file, &current_section);+			break;++		case ';':+		case '#':+			cfg_consume_line(cfg_file);+			break;++		default: /* assume variable declaration */+			result = parse_variable(cfg_file, &var_name, &var_value);+			if (result < 0)+				break;++			var = git__malloc(sizeof(cvar_t));+			GITERR_CHECK_ALLOC(var);++			memset(var, 0x0, sizeof(cvar_t));++			git__strtolower(var_name);+			git_buf_printf(&buf, "%s.%s", current_section, var_name);+			git__free(var_name);++			if (git_buf_oom(&buf))+				return -1;++			var->key = git_buf_detach(&buf);+			var->value = var_value;++			/* Add or append the new config option */+			pos = git_strmap_lookup_index(cfg_file->values, var->key);+			if (!git_strmap_valid_index(cfg_file->values, pos)) {+				git_strmap_insert(cfg_file->values, var->key, var, result);+				if (result < 0)+					break;+				result = 0;+			} else {+				existing = git_strmap_value_at(cfg_file->values, pos);+				while (existing->next != NULL) {+					existing = existing->next;+				}+				existing->next = var;+			}++			break;+		}+	}++	git__free(current_section);+	return result;+}++static int write_section(git_filebuf *file, const char *key)+{+	int result;+	const char *dot;+	git_buf buf = GIT_BUF_INIT;++	/* All of this just for [section "subsection"] */+	dot = strchr(key, '.');+	git_buf_putc(&buf, '[');+	if (dot == NULL) {+		git_buf_puts(&buf, key);+	} else {+		git_buf_put(&buf, key, dot - key);+		/* TODO: escape  */+		git_buf_printf(&buf, " \"%s\"", dot + 1);+	}+	git_buf_puts(&buf, "]\n");++	if (git_buf_oom(&buf))+		return -1;++	result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);+	git_buf_free(&buf);++	return result;+}++/*+ * This is pretty much the parsing, except we write out anything we don't have+ */+static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)+{+	int result, c;+	int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;+	const char *pre_end = NULL, *post_start = NULL, *data_start;+	char *current_section = NULL, *section, *name, *ldot;+	git_filebuf file = GIT_FILEBUF_INIT;++	/* We need to read in our own config file */+	result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);++	/* Initialise the reading position */+	if (result == GIT_ENOTFOUND) {+		cfg->reader.read_ptr = NULL;+		cfg->reader.eof = 1;+		data_start = NULL;+		git_buf_clear(&cfg->reader.buffer);+	} else if (result == 0) {+		cfg->reader.read_ptr = cfg->reader.buffer.ptr;+		cfg->reader.eof = 0;+		data_start = cfg->reader.read_ptr;+	} else {+		return -1; /* OS error when reading the file */+	}++	/* Lock the file */+	if (git_filebuf_open(&file, cfg->file_path, 0) < 0)+		return -1;++	skip_bom(cfg);+	ldot = strrchr(key, '.');+	name = ldot + 1;+	section = git__strndup(key, ldot - key);++	while (!cfg->reader.eof) {+		c = cfg_peek(cfg, SKIP_WHITESPACE);++		if (c == '\0') { /* We've arrived at the end of the file */+			break;++		} else if (c == '[') { /* section header, new section begins */+			/*+			 * We set both positions to the current one in case we+			 * need to add a variable to the end of a section. In that+			 * case, we want both variables to point just before the+			 * new section. If we actually want to replace it, the+			 * default case will take care of updating them.+			 */+			pre_end = post_start = cfg->reader.read_ptr;++			git__free(current_section);+			current_section = NULL;+			if (parse_section_header(cfg, &current_section) < 0)+				goto rewrite_fail;++			/* Keep track of when it stops matching */+			last_section_matched = section_matches;+			section_matches = !strcmp(current_section, section);+		}++		else if (c == ';' || c == '#') {+			cfg_consume_line(cfg);+		}++		else {+			/*+			 * If the section doesn't match, but the last section did,+			 * it means we need to add a variable (so skip the line+			 * otherwise). If both the section and name match, we need+			 * to overwrite the variable (so skip the line+			 * otherwise). pre_end needs to be updated each time so we+			 * don't loose that information, but we only need to+			 * update post_start if we're going to use it in this+			 * iteration.+			 */+			if (!section_matches) {+				if (!last_section_matched) {+					cfg_consume_line(cfg);+					continue;+				}+			} else {+				int has_matched = 0;+				char *var_name, *var_value;++				pre_end = cfg->reader.read_ptr;+				if (parse_variable(cfg, &var_name, &var_value) < 0)+					goto rewrite_fail;++				/* First try to match the name of the variable */+				if (strcasecmp(name, var_name) == 0)+					has_matched = 1;++				/* If the name matches, and we have a regex to match the+				 * value, try to match it */+				if (has_matched && preg != NULL)+					has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);++				git__free(var_name);+				git__free(var_value);++				/* if there is no match, keep going */+				if (!has_matched)+					continue;++				post_start = cfg->reader.read_ptr;+			}++			/* We've found the variable we wanted to change, so+			 * write anything up to it */+			git_filebuf_write(&file, data_start, pre_end - data_start);+			preg_replaced = 1;++			/* Then replace the variable. If the value is NULL, it+			 * means we want to delete it, so don't write anything. */+			if (value != NULL) {+				git_filebuf_printf(&file, "\t%s = %s\n", name, value);+			}++			/* multiline variable? we need to keep reading lines to match */+			if (preg != NULL) {+				data_start = post_start;+				continue;+			}++			write_trailer = 1;+			break; /* break from the loop */+		}+	}++	/*+	 * Being here can mean that+	 *+	 * 1) our section is the last one in the file and we're+	 * adding a variable+	 *+	 * 2) we didn't find a section for us so we need to create it+	 * ourselves.+	 *+	 * 3) we're setting a multivar with a regex, which means we+	 * continue to search for matching values+	 *+	 * In the last case, if we've already replaced a value, we+	 * want to write the rest of the file. Otherwise we need to write+	 * out the whole file and then the new variable.+	 */+	if (write_trailer) {+		/* Write out rest of the file */+		git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));+	} else {+		if (preg_replaced) {+			git_filebuf_printf(&file, "\n%s", data_start);+		} else {+			git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);++			/* And now if we just need to add a variable */+			if (!section_matches && write_section(&file, section) < 0)+				goto rewrite_fail;++			/* Sanity check: if we are here, and value is NULL, that means that somebody+			 * touched the config file after our intial read. We should probably assert()+			 * this, but instead we'll handle it gracefully with an error. */+			if (value == NULL) {+				giterr_set(GITERR_CONFIG,+					"Race condition when writing a config file (a cvar has been removed)");+				goto rewrite_fail;+			}++			git_filebuf_printf(&file, "\t%s = %s\n", name, value);+		}+	}++	git__free(section);+	git__free(current_section);++	result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);+	git_buf_free(&cfg->reader.buffer);+	return result;++rewrite_fail:+	git__free(section);+	git__free(current_section);++	git_filebuf_cleanup(&file);+	git_buf_free(&cfg->reader.buffer);+	return -1;+}++/* '\"' -> '"' etc */+static char *fixup_line(const char *ptr, int quote_count)+{+	char *str = git__malloc(strlen(ptr) + 1);+	char *out = str, *esc;+	const char *escapes = "ntb\"\\";+	const char *escaped = "\n\t\b\"\\";++	if (str == NULL)+		return NULL;++	while (*ptr != '\0') {+		if (*ptr == '"') {+			quote_count++;+		} else if (*ptr != '\\') {+			*out++ = *ptr;+		} else {+			/* backslash, check the next char */+			ptr++;+			/* if we're at the end, it's a multiline, so keep the backslash */+			if (*ptr == '\0') {+				*out++ = '\\';+				goto out;+			}+			if ((esc = strchr(escapes, *ptr)) != NULL) {+				*out++ = escaped[esc - escapes];+			} else {+				git__free(str);+				giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);+				return NULL;+			}+		}+		ptr++;+	}++out:+	*out = '\0';++	return str;+}++static int is_multiline_var(const char *str)+{+	const char *end = str + strlen(str);+	return (end > str) && (end[-1] == '\\');+}++static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)+{+	char *line = NULL, *proc_line = NULL;+	int quote_count;++	/* Check that the next line exists */+	line = cfg_readline(cfg, false);+	if (line == NULL)+		return -1;++	/* We've reached the end of the file, there is input missing */+	if (line[0] == '\0') {+		set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");+		git__free(line);+		return -1;+	}++	quote_count = strip_comments(line, !!in_quotes);++	/* If it was just a comment, pretend it didn't exist */+	if (line[0] == '\0') {+		git__free(line);+		return parse_multiline_variable(cfg, value, quote_count);+		/* TODO: unbounded recursion. This **could** be exploitable */+	}++	/* Drop the continuation character '\': to closely follow the UNIX+	 * standard, this character **has** to be last one in the buf, with+	 * no whitespace after it */+	assert(is_multiline_var(value->ptr));+	git_buf_truncate(value, git_buf_len(value) - 1);++	proc_line = fixup_line(line, in_quotes);+	if (proc_line == NULL) {+		git__free(line);+		return -1;+	}+	/* add this line to the multiline var */+	git_buf_puts(value, proc_line);+	git__free(line);+	git__free(proc_line);++	/*+	 * If we need to continue reading the next line, let's just+	 * keep putting stuff in the buffer+	 */+	if (is_multiline_var(value->ptr))+		return parse_multiline_variable(cfg, value, quote_count);++	return 0;+}++static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)+{+	const char *var_end = NULL;+	const char *value_start = NULL;+	char *line;+	int quote_count;++	line = cfg_readline(cfg, true);+	if (line == NULL)+		return -1;++	quote_count = strip_comments(line, 0);++	var_end = strchr(line, '=');++	if (var_end == NULL)+		var_end = strchr(line, '\0');+	else+		value_start = var_end + 1;++	if (git__isspace(var_end[-1])) {+		do var_end--;+		while (git__isspace(var_end[0]));+	}++	*var_name = git__strndup(line, var_end - line + 1);+	GITERR_CHECK_ALLOC(*var_name);++	/* If there is no value, boolean true is assumed */+	*var_value = NULL;++	/*+	 * Now, let's try to parse the value+	 */+	if (value_start != NULL) {+		while (git__isspace(value_start[0]))+			value_start++;++		if (is_multiline_var(value_start)) {+			git_buf multi_value = GIT_BUF_INIT;+			char *proc_line = fixup_line(value_start, 0);+			GITERR_CHECK_ALLOC(proc_line);+			git_buf_puts(&multi_value, proc_line);+			git__free(proc_line);+			if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {+				git__free(*var_name);+				git__free(line);+				git_buf_free(&multi_value);+				return -1;+			}++			*var_value = git_buf_detach(&multi_value);++		}+		else if (value_start[0] != '\0') {+			*var_value = fixup_line(value_start, 0);+			GITERR_CHECK_ALLOC(*var_value);+		}++	}++	git__free(line);+	return 0;+}
+ libgit2/src/config_file.h view
@@ -0,0 +1,31 @@+/*+ * Copyright (C) 2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_config_file_h__+#define INCLUDE_config_file_h__++#include "git2/config.h"++GIT_INLINE(int) git_config_file_open(git_config_file *cfg)+{+	return cfg->open(cfg);+}++GIT_INLINE(void) git_config_file_free(git_config_file *cfg)+{+	cfg->free(cfg);+}++GIT_INLINE(int) git_config_file_foreach(+	git_config_file *cfg,+	int (*fn)(const char *key, const char *value, void *data),+	void *data)+{+	return cfg->foreach(cfg, fn, data);+}++#endif+
+ libgit2/src/crlf.c view
@@ -0,0 +1,228 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "fileops.h"+#include "hash.h"+#include "filter.h"+#include "repository.h"++#include "git2/attr.h"++struct crlf_attrs {+	int crlf_action;+	int eol;+};++struct crlf_filter {+	git_filter f;+	struct crlf_attrs attrs;+};++static int check_crlf(const char *value)+{+	if (GIT_ATTR_TRUE(value))+		return GIT_CRLF_TEXT;++	if (GIT_ATTR_FALSE(value))+		return GIT_CRLF_BINARY;++	if (GIT_ATTR_UNSPECIFIED(value))+		return GIT_CRLF_GUESS;++	if (strcmp(value, "input") == 0)+		return GIT_CRLF_INPUT;++	if (strcmp(value, "auto") == 0)+		return GIT_CRLF_AUTO;++	return GIT_CRLF_GUESS;+}++static int check_eol(const char *value)+{+	if (GIT_ATTR_UNSPECIFIED(value))+		return GIT_EOL_UNSET;++	if (strcmp(value, "lf") == 0)+		return GIT_EOL_LF;++	if (strcmp(value, "crlf") == 0)+		return GIT_EOL_CRLF;++	return GIT_EOL_UNSET;+}++static int crlf_input_action(struct crlf_attrs *ca)+{+	if (ca->crlf_action == GIT_CRLF_BINARY)+		return GIT_CRLF_BINARY;++	if (ca->eol == GIT_EOL_LF)+		return GIT_CRLF_INPUT;++	if (ca->eol == GIT_EOL_CRLF)+		return GIT_CRLF_CRLF;++	return ca->crlf_action;+}++static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)+{+#define NUM_CONV_ATTRS 3++	static const char *attr_names[NUM_CONV_ATTRS] = {+		"crlf", "eol", "text",+	};++	const char *attr_vals[NUM_CONV_ATTRS];+	int error;++	error = git_attr_get_many(attr_vals,+		repo, 0, path, NUM_CONV_ATTRS, attr_names);++	if (error == GIT_ENOTFOUND) {+		ca->crlf_action = GIT_CRLF_GUESS;+		ca->eol = GIT_EOL_UNSET;+		return 0;+	}++	if (error == 0) {+		ca->crlf_action = check_crlf(attr_vals[2]); /* text */+		if (ca->crlf_action == GIT_CRLF_GUESS)+			ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */++		ca->eol = check_eol(attr_vals[1]); /* eol */+		return 0;+	}++	return -1;+}++static int drop_crlf(git_buf *dest, const git_buf *source)+{+	const char *scan = source->ptr, *next;+	const char *scan_end = git_buf_cstr(source) + git_buf_len(source);++	/* Main scan loop.  Find the next carriage return and copy the+	 * whole chunk up to that point to the destination buffer.+	 */+	while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {+		/* copy input up to \r */+		if (next > scan)+			git_buf_put(dest, scan, next - scan);++		/* Do not drop \r unless it is followed by \n */+		if (*(next + 1) != '\n')+			git_buf_putc(dest, '\r');++		scan = next + 1;+	}++	/* If there was no \r, then tell the library to skip this filter */+	if (scan == source->ptr)+		return -1;++	/* Copy remaining input into dest */+	git_buf_put(dest, scan, scan_end - scan);+	return 0;+}++static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)+{+	struct crlf_filter *filter = (struct crlf_filter *)self;++	assert(self && dest && source);++	/* Empty file? Nothing to do */+	if (git_buf_len(source) == 0)+		return 0;++	/* Heuristics to see if we can skip the conversion.+	 * Straight from Core Git.+	 */+	if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||+		filter->attrs.crlf_action == GIT_CRLF_GUESS) {++		git_text_stats stats;+		git_text_gather_stats(&stats, source);++		/*+		 * We're currently not going to even try to convert stuff+		 * that has bare CR characters. Does anybody do that crazy+		 * stuff?+		 */+		if (stats.cr != stats.crlf)+			return -1;++		/*+		 * And add some heuristics for binary vs text, of course...+		 */+		if (git_text_is_binary(&stats))+			return -1;++#if 0+		if (crlf_action == CRLF_GUESS) {+			/*+			 * If the file in the index has any CR in it, do not convert.+			 * This is the new safer autocrlf handling.+			 */+			if (has_cr_in_index(path))+				return 0;+		}+#endif++		if (!stats.cr)+			return -1;+	}++	/* Actually drop the carriage returns */+	return drop_crlf(dest, source);+}++int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)+{+	struct crlf_attrs ca;+	struct crlf_filter *filter;+	int error;++	/* Load gitattributes for the path */+	if ((error = crlf_load_attributes(&ca, repo, path)) < 0)+		return error;++	/*+	 * Use the core Git logic to see if we should perform CRLF for this file+	 * based on its attributes & the value of `core.auto_crlf`+	 */+	ca.crlf_action = crlf_input_action(&ca);++	if (ca.crlf_action == GIT_CRLF_BINARY)+		return 0;++	if (ca.crlf_action == GIT_CRLF_GUESS) {+		int auto_crlf;++		if ((error = git_repository__cvar(+			&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)+			return error;++		if (auto_crlf == GIT_AUTO_CRLF_FALSE)+			return 0;+	}++	/* If we're good, we create a new filter object and push it+	 * into the filters array */+	filter = git__malloc(sizeof(struct crlf_filter));+	GITERR_CHECK_ALLOC(filter);++	filter->f.apply = &crlf_apply_to_odb;+	filter->f.do_free = NULL;+	memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));++	return git_vector_insert(filters, filter);+}+
+ libgit2/src/delta-apply.c view
@@ -0,0 +1,121 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "git2/odb.h"+#include "delta-apply.h"++/*+ * This file was heavily cribbed from BinaryDelta.java in JGit, which+ * itself was heavily cribbed from <code>patch-delta.c</code> in the+ * GIT project.	The original delta patching code was written by+ * Nicolas Pitre <nico@cam.org>.+ */++static int hdr_sz(+	size_t *size,+	const unsigned char **delta,+	const unsigned char *end)+{+	const unsigned char *d = *delta;+	size_t r = 0;+	unsigned int c, shift = 0;++	do {+		if (d == end)+			return -1;+		c = *d++;+		r |= (c & 0x7f) << shift;+		shift += 7;+	} while (c & 0x80);+	*delta = d;+	*size = r;+	return 0;+}++int git__delta_apply(+	git_rawobj *out,+	const unsigned char *base,+	size_t base_len,+	const unsigned char *delta,+	size_t delta_len)+{+	const unsigned char *delta_end = delta + delta_len;+	size_t base_sz, res_sz;+	unsigned char *res_dp;++	/* Check that the base size matches the data we were given;+	 * if not we would underflow while accessing data from the+	 * base object, resulting in data corruption or segfault.+	 */+	if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {+		giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");+		return -1;+	}++	if (hdr_sz(&res_sz, &delta, delta_end) < 0) {+		giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");+		return -1;+	}++	res_dp = git__malloc(res_sz + 1);+	GITERR_CHECK_ALLOC(res_dp);++	res_dp[res_sz] = '\0';+	out->data = res_dp;+	out->len = res_sz;++	while (delta < delta_end) {+		unsigned char cmd = *delta++;+		if (cmd & 0x80) {+			/* cmd is a copy instruction; copy from the base.+			 */+			size_t off = 0, len = 0;++			if (cmd & 0x01) off = *delta++;+			if (cmd & 0x02) off |= *delta++ << 8;+			if (cmd & 0x04) off |= *delta++ << 16;+			if (cmd & 0x08) off |= *delta++ << 24;++			if (cmd & 0x10) len = *delta++;+			if (cmd & 0x20) len |= *delta++ << 8;+			if (cmd & 0x40) len |= *delta++ << 16;+			if (!len)		len = 0x10000;++			if (base_len < off + len || res_sz < len)+				goto fail;+			memcpy(res_dp, base + off, len);+			res_dp += len;+			res_sz -= len;++		} else if (cmd) {+			/* cmd is a literal insert instruction; copy from+			 * the delta stream itself.+			 */+			if (delta_end - delta < cmd || res_sz < cmd)+				goto fail;+			memcpy(res_dp, delta, cmd);+			delta += cmd;+			res_dp += cmd;+			res_sz -= cmd;++		} else {+			/* cmd == 0 is reserved for future encodings.+			 */+			goto fail;+		}+	}++	if (delta != delta_end || res_sz)+		goto fail;+	return 0;++fail:+	git__free(out->data);+	out->data = NULL;+	giterr_set(GITERR_INVALID, "Failed to apply delta");+	return -1;+}
+ libgit2/src/delta-apply.h view
@@ -0,0 +1,33 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_delta_apply_h__+#define INCLUDE_delta_apply_h__++#include "odb.h"++/**+ * Apply a git binary delta to recover the original content.+ *+ * @param out the output buffer to receive the original data.+ *		Only out->data and out->len are populated, as this is+ *		the only information available in the delta.+ * @param base the base to copy from during copy instructions.+ * @param base_len number of bytes available at base.+ * @param delta the delta to execute copy/insert instructions from.+ * @param delta_len total number of bytes in the delta.+ * @return+ * - 0 on a successful delta unpack.+ * - GIT_ERROR if the delta is corrupt or doesn't match the base.+ */+extern int git__delta_apply(+	git_rawobj *out,+	const unsigned char *base,+	size_t base_len,+	const unsigned char *delta,+	size_t delta_len);++#endif
+ libgit2/src/diff.c view
@@ -0,0 +1,783 @@+/*+ * Copyright (C) 2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "git2/diff.h"+#include "diff.h"+#include "fileops.h"+#include "config.h"+#include "attr_file.h"++static char *diff_prefix_from_pathspec(const git_strarray *pathspec)+{+	git_buf prefix = GIT_BUF_INIT;+	const char *scan;++	if (git_buf_common_prefix(&prefix, pathspec) < 0)+		return NULL;++	/* diff prefix will only be leading non-wildcards */+	for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);+	git_buf_truncate(&prefix, scan - prefix.ptr);++	if (prefix.size > 0)+		return git_buf_detach(&prefix);++	git_buf_free(&prefix);+	return NULL;+}++static bool diff_pathspec_is_interesting(const git_strarray *pathspec)+{+	const char *str;++	if (pathspec == NULL || pathspec->count == 0)+		return false;+	if (pathspec->count > 1)+		return true;++	str = pathspec->strings[0];+	if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))+		return false;+	return true;+}++static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)+{+	unsigned int i;+	git_attr_fnmatch *match;++	if (!diff->pathspec.length)+		return true;++	git_vector_foreach(&diff->pathspec, i, match) {+		int result = p_fnmatch(match->pattern, path, 0);++		/* if we didn't match, look for exact dirname prefix match */+		if (result == FNM_NOMATCH &&+			(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&+			strncmp(path, match->pattern, match->length) == 0 &&+			path[match->length] == '/')+			result = 0;++		if (result == 0)+			return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;+	}++	return false;+}++static git_diff_delta *diff_delta__alloc(+	git_diff_list *diff,+	git_delta_t status,+	const char *path)+{+	git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));+	if (!delta)+		return NULL;++	delta->old_file.path = git_pool_strdup(&diff->pool, path);+	if (delta->old_file.path == NULL) {+		git__free(delta);+		return NULL;+	}++	delta->new_file.path = delta->old_file.path;++	if (diff->opts.flags & GIT_DIFF_REVERSE) {+		switch (status) {+		case GIT_DELTA_ADDED:   status = GIT_DELTA_DELETED; break;+		case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;+		default: break; /* leave other status values alone */+		}+	}+	delta->status = status;++	return delta;+}++static git_diff_delta *diff_delta__dup(+	const git_diff_delta *d, git_pool *pool)+{+	git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));+	if (!delta)+		return NULL;++	memcpy(delta, d, sizeof(git_diff_delta));++	delta->old_file.path = git_pool_strdup(pool, d->old_file.path);+	if (delta->old_file.path == NULL)+		goto fail;++	if (d->new_file.path != d->old_file.path) {+		delta->new_file.path = git_pool_strdup(pool, d->new_file.path);+		if (delta->new_file.path == NULL)+			goto fail;+	} else {+		delta->new_file.path = delta->old_file.path;+	}++	return delta;++fail:+	git__free(delta);+	return NULL;+}++static git_diff_delta *diff_delta__merge_like_cgit(+	const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)+{+	git_diff_delta *dup = diff_delta__dup(a, pool);+	if (!dup)+		return NULL;++	if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)+		return dup;++	git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);++	dup->new_file.mode = b->new_file.mode;+	dup->new_file.size = b->new_file.size;+	dup->new_file.flags = b->new_file.flags;++	/* Emulate C git for merging two diffs (a la 'git diff <sha>').+	 *+	 * When C git does a diff between the work dir and a tree, it actually+	 * diffs with the index but uses the workdir contents.  This emulates+	 * those choices so we can emulate the type of diff.+	 */+	if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {+		if (dup->status == GIT_DELTA_DELETED)+			/* preserve pending delete info */;+		else if (b->status == GIT_DELTA_UNTRACKED ||+				 b->status == GIT_DELTA_IGNORED)+			dup->status = b->status;+		else+			dup->status = GIT_DELTA_UNMODIFIED;+	}+	else if (dup->status == GIT_DELTA_UNMODIFIED ||+			 b->status == GIT_DELTA_DELETED)+		dup->status = b->status;++	return dup;+}++static int diff_delta__from_one(+	git_diff_list *diff,+	git_delta_t   status,+	const git_index_entry *entry)+{+	git_diff_delta *delta;++	if (status == GIT_DELTA_IGNORED &&+		(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)+		return 0;++	if (status == GIT_DELTA_UNTRACKED &&+		(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)+		return 0;++	if (!diff_path_matches_pathspec(diff, entry->path))+		return 0;++	delta = diff_delta__alloc(diff, status, entry->path);+	GITERR_CHECK_ALLOC(delta);++	/* This fn is just for single-sided diffs */+	assert(status != GIT_DELTA_MODIFIED);++	if (delta->status == GIT_DELTA_DELETED) {+		delta->old_file.mode = entry->mode;+		delta->old_file.size = entry->file_size;+		git_oid_cpy(&delta->old_file.oid, &entry->oid);+	} else /* ADDED, IGNORED, UNTRACKED */ {+		delta->new_file.mode = entry->mode;+		delta->new_file.size = entry->file_size;+		git_oid_cpy(&delta->new_file.oid, &entry->oid);+	}++	delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;+	delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;++	if (git_vector_insert(&diff->deltas, delta) < 0) {+		git__free(delta);+		return -1;+	}++	return 0;+}++static int diff_delta__from_two(+	git_diff_list *diff,+	git_delta_t   status,+	const git_index_entry *old_entry,+	const git_index_entry *new_entry,+	git_oid *new_oid)+{+	git_diff_delta *delta;++	if (status == GIT_DELTA_UNMODIFIED &&+		(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)+		return 0;++	if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {+		const git_index_entry *temp = old_entry;+		old_entry = new_entry;+		new_entry = temp;+	}++	delta = diff_delta__alloc(diff, status, old_entry->path);+	GITERR_CHECK_ALLOC(delta);++	delta->old_file.mode = old_entry->mode;+	git_oid_cpy(&delta->old_file.oid, &old_entry->oid);+	delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;++	delta->new_file.mode = new_entry->mode;+	git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);+	if (new_oid || !git_oid_iszero(&new_entry->oid))+		delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;++	if (git_vector_insert(&diff->deltas, delta) < 0) {+		git__free(delta);+		return -1;+	}++	return 0;+}++static char *diff_strdup_prefix(git_pool *pool, const char *prefix)+{+	size_t len = strlen(prefix);++	/* append '/' at end if needed */+	if (len > 0 && prefix[len - 1] != '/')+		return git_pool_strcat(pool, prefix, "/");+	else+		return git_pool_strndup(pool, prefix, len + 1);+}++static int diff_delta__cmp(const void *a, const void *b)+{+	const git_diff_delta *da = a, *db = b;+	int val = strcmp(da->old_file.path, db->old_file.path);+	return val ? val : ((int)da->status - (int)db->status);+}++static int config_bool(git_config *cfg, const char *name, int defvalue)+{+	int val = defvalue;++	if (git_config_get_bool(&val, cfg, name) < 0)+		giterr_clear();++	return val;+}++static git_diff_list *git_diff_list_alloc(+	git_repository *repo, const git_diff_options *opts)+{+	git_config *cfg;+	size_t i;+	git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));+	if (diff == NULL)+		return NULL;++	diff->repo = repo;++	if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||+		git_pool_init(&diff->pool, 1, 0) < 0)+		goto fail;++	/* load config values that affect diff behavior */+	if (git_repository_config__weakptr(&cfg, repo) < 0)+		goto fail;+	if (config_bool(cfg, "core.symlinks", 1))+		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;+	if (config_bool(cfg, "core.ignorestat", 0))+		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;+	if (config_bool(cfg, "core.filemode", 1))+		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;+	if (config_bool(cfg, "core.trustctime", 1))+		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;+	/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */++	if (opts == NULL)+		return diff;++	memcpy(&diff->opts, opts, sizeof(git_diff_options));+	memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));++	diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,+		opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);+	diff->opts.new_prefix = diff_strdup_prefix(&diff->pool,+		opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);++	if (!diff->opts.old_prefix || !diff->opts.new_prefix)+		goto fail;++	if (diff->opts.flags & GIT_DIFF_REVERSE) {+		char *swap = diff->opts.old_prefix;+		diff->opts.old_prefix = diff->opts.new_prefix;+		diff->opts.new_prefix = swap;+	}++	/* only copy pathspec if it is "interesting" so we can test+	 * diff->pathspec.length > 0 to know if it is worth calling+	 * fnmatch as we iterate.+	 */+	if (!diff_pathspec_is_interesting(&opts->pathspec))+		return diff;++	if (git_vector_init(+		&diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0)+		goto fail;++	for (i = 0; i < opts->pathspec.count; ++i) {+		int ret;+		const char *pattern = opts->pathspec.strings[i];+		git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));+		if (!match)+			goto fail;+		ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);+		if (ret == GIT_ENOTFOUND) {+			git__free(match);+			continue;+		} else if (ret < 0)+			goto fail;++		if (git_vector_insert(&diff->pathspec, match) < 0)+			goto fail;+	}++	return diff;++fail:+	git_diff_list_free(diff);+	return NULL;+}++void git_diff_list_free(git_diff_list *diff)+{+	git_diff_delta *delta;+	git_attr_fnmatch *match;+	unsigned int i;++	if (!diff)+		return;++	git_vector_foreach(&diff->deltas, i, delta) {+		git__free(delta);+		diff->deltas.contents[i] = NULL;+	}+	git_vector_free(&diff->deltas);++	git_vector_foreach(&diff->pathspec, i, match) {+		git__free(match);+		diff->pathspec.contents[i] = NULL;+	}+	git_vector_free(&diff->pathspec);++	git_pool_clear(&diff->pool);+	git__free(diff);+}++static int oid_for_workdir_item(+	git_repository *repo,+	const git_index_entry *item,+	git_oid *oid)+{+	int result;+	git_buf full_path = GIT_BUF_INIT;++	if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)+		return -1;++	/* calculate OID for file if possible*/+	if (S_ISLNK(item->mode))+		result = git_odb__hashlink(oid, full_path.ptr);+	else if (!git__is_sizet(item->file_size)) {+		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");+		result = -1;+	} else {+		int fd = git_futils_open_ro(full_path.ptr);+		if (fd < 0)+			result = fd;+		else {+			result = git_odb__hashfd(+				oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);+			p_close(fd);+		}+	}++	git_buf_free(&full_path);++	return result;+}++#define EXEC_BIT_MASK 0000111++static int maybe_modified(+	git_iterator *old_iter,+	const git_index_entry *oitem,+	git_iterator *new_iter,+	const git_index_entry *nitem,+	git_diff_list *diff)+{+	git_oid noid, *use_noid = NULL;+	git_delta_t status = GIT_DELTA_MODIFIED;+	unsigned int omode = oitem->mode;+	unsigned int nmode = nitem->mode;++	GIT_UNUSED(old_iter);++	if (!diff_path_matches_pathspec(diff, oitem->path))+		return 0;++	/* on platforms with no symlinks, promote plain files to symlinks */+	if (S_ISLNK(omode) && S_ISREG(nmode) &&+		!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))+		nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);++	/* on platforms with no execmode, clear exec bit from comparisons */+	if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {+		omode = omode & ~EXEC_BIT_MASK;+		nmode = nmode & ~EXEC_BIT_MASK;+	}++	/* support "assume unchanged" (badly, b/c we still stat everything) */+	if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)+		status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?+			GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;++	/* support "skip worktree" index bit */+	else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)+		status = GIT_DELTA_UNMODIFIED;++	/* if basic type of file changed, then split into delete and add */+	else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {+		if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||+			diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)+			return -1;+		return 0;+	}++	/* if oids and modes match, then file is unmodified */+	else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&+			 omode == nmode)+		status = GIT_DELTA_UNMODIFIED;++	/* if we have a workdir item with an unknown oid, check deeper */+	else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {+		/* TODO: add check against index file st_mtime to avoid racy-git */++		/* if they files look exactly alike, then we'll assume the same */+		if (oitem->file_size == nitem->file_size &&+			(!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||+			 (oitem->ctime.seconds == nitem->ctime.seconds)) &&+			oitem->mtime.seconds == nitem->mtime.seconds &&+			(!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||+			 (oitem->dev == nitem->dev)) &&+			oitem->ino == nitem->ino &&+			oitem->uid == nitem->uid &&+			oitem->gid == nitem->gid)+			status = GIT_DELTA_UNMODIFIED;++		else if (S_ISGITLINK(nmode)) {+			git_submodule *sub;++			if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)+				status = GIT_DELTA_UNMODIFIED;+			else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)+				return -1;+			else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)+				status = GIT_DELTA_UNMODIFIED;+			else {+				/* TODO: support other GIT_SUBMODULE_IGNORE values */+				status = GIT_DELTA_UNMODIFIED;+			}+		}++		/* TODO: check git attributes so we will not have to read the file+		 * in if it is marked binary.+		 */++		else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)+			return -1;++		else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&+				 omode == nmode)+			status = GIT_DELTA_UNMODIFIED;++		/* store calculated oid so we don't have to recalc later */+		use_noid = &noid;+	}++	return diff_delta__from_two(diff, status, oitem, nitem, use_noid);+}++static int diff_from_iterators(+	git_repository *repo,+	const git_diff_options *opts, /**< can be NULL for defaults */+	git_iterator *old_iter,+	git_iterator *new_iter,+	git_diff_list **diff_ptr)+{+	const git_index_entry *oitem, *nitem;+	git_buf ignore_prefix = GIT_BUF_INIT;+	git_diff_list *diff = git_diff_list_alloc(repo, opts);+	if (!diff)+		goto fail;++	diff->old_src = old_iter->type;+	diff->new_src = new_iter->type;++	if (git_iterator_current(old_iter, &oitem) < 0 ||+		git_iterator_current(new_iter, &nitem) < 0)+		goto fail;++	/* run iterators building diffs */+	while (oitem || nitem) {++		/* create DELETED records for old items not matched in new */+		if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {+			if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||+				git_iterator_advance(old_iter, &oitem) < 0)+				goto fail;+		}++		/* create ADDED, TRACKED, or IGNORED records for new items not+		 * matched in old (and/or descend into directories as needed)+		 */+		else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {+			git_delta_t delta_type = GIT_DELTA_UNTRACKED;++			/* check if contained in ignored parent directory */+			if (git_buf_len(&ignore_prefix) &&+				git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)+				delta_type = GIT_DELTA_IGNORED;++			if (S_ISDIR(nitem->mode)) {+				/* recurse into directory only if there are tracked items in+				 * it or if the user requested the contents of untracked+				 * directories and it is not under an ignored directory.+				 */+				if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) ||+					(delta_type == GIT_DELTA_UNTRACKED &&+					 (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))+				{+					/* if this directory is ignored, remember it as the+					 * "ignore_prefix" for processing contained items+					 */+					if (delta_type == GIT_DELTA_UNTRACKED &&+						git_iterator_current_is_ignored(new_iter))+						git_buf_sets(&ignore_prefix, nitem->path);++					if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)+						goto fail;++					continue;+				}+			}++			/* In core git, the next two "else if" clauses are effectively+			 * reversed -- i.e. when an untracked file contained in an+			 * ignored directory is individually ignored, it shows up as an+			 * ignored file in the diff list, even though other untracked+			 * files in the same directory are skipped completely.+			 *+			 * To me, this is odd.  If the directory is ignored and the file+			 * is untracked, we should skip it consistently, regardless of+			 * whether it happens to match a pattern in the ignore file.+			 *+			 * To match the core git behavior, just reverse the following+			 * two "else if" cases so that individual file ignores are+			 * checked before container directory exclusions are used to+			 * skip the file.+			 */+			else if (delta_type == GIT_DELTA_IGNORED) {+				if (git_iterator_advance(new_iter, &nitem) < 0)+					goto fail;+				continue; /* ignored parent directory, so skip completely */+			}++			else if (git_iterator_current_is_ignored(new_iter))+				delta_type = GIT_DELTA_IGNORED;++			else if (new_iter->type != GIT_ITERATOR_WORKDIR)+				delta_type = GIT_DELTA_ADDED;++			if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||+				git_iterator_advance(new_iter, &nitem) < 0)+				goto fail;+		}++		/* otherwise item paths match, so create MODIFIED record+		 * (or ADDED and DELETED pair if type changed)+		 */+		else {+			assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);++			if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||+				git_iterator_advance(old_iter, &oitem) < 0 ||+				git_iterator_advance(new_iter, &nitem) < 0)+				goto fail;+		}+	}++	git_iterator_free(old_iter);+	git_iterator_free(new_iter);+	git_buf_free(&ignore_prefix);++	*diff_ptr = diff;+	return 0;++fail:+	git_iterator_free(old_iter);+	git_iterator_free(new_iter);+	git_buf_free(&ignore_prefix);++	git_diff_list_free(diff);+	*diff_ptr = NULL;+	return -1;+}+++int git_diff_tree_to_tree(+	git_repository *repo,+	const git_diff_options *opts, /**< can be NULL for defaults */+	git_tree *old_tree,+	git_tree *new_tree,+	git_diff_list **diff)+{+	git_iterator *a = NULL, *b = NULL;+	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;++	assert(repo && old_tree && new_tree && diff);++	if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||+		git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)+		return -1;++	git__free(prefix);++	return diff_from_iterators(repo, opts, a, b, diff);+}++int git_diff_index_to_tree(+	git_repository *repo,+	const git_diff_options *opts,+	git_tree *old_tree,+	git_diff_list **diff)+{+	git_iterator *a = NULL, *b = NULL;+	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;++	assert(repo && diff);++	if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||+		git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)+		return -1;++	git__free(prefix);++	return diff_from_iterators(repo, opts, a, b, diff);+}++int git_diff_workdir_to_index(+	git_repository *repo,+	const git_diff_options *opts,+	git_diff_list **diff)+{+	git_iterator *a = NULL, *b = NULL;+	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;++	assert(repo && diff);++	if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||+		git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)+		return -1;++	git__free(prefix);++	return diff_from_iterators(repo, opts, a, b, diff);+}+++int git_diff_workdir_to_tree(+	git_repository *repo,+	const git_diff_options *opts,+	git_tree *old_tree,+	git_diff_list **diff)+{+	git_iterator *a = NULL, *b = NULL;+	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;++	assert(repo && old_tree && diff);++	if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||+		git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)+		return -1;++	git__free(prefix);++	return diff_from_iterators(repo, opts, a, b, diff);+}++int git_diff_merge(+	git_diff_list *onto,+	const git_diff_list *from)+{+	int error = 0;+	git_pool onto_pool;+	git_vector onto_new;+	git_diff_delta *delta;+	unsigned int i, j;++	assert(onto && from);++	if (!from->deltas.length)+		return 0;++	if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||+		git_pool_init(&onto_pool, 1, 0) < 0)+		return -1;++	for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {+		git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);+		const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);+		int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path);++		if (cmp < 0) {+			delta = diff_delta__dup(o, &onto_pool);+			i++;+		} else if (cmp > 0) {+			delta = diff_delta__dup(f, &onto_pool);+			j++;+		} else {+			delta = diff_delta__merge_like_cgit(o, f, &onto_pool);+			i++;+			j++;+		}++		if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)+			break;+	}++	if (!error) {+		git_vector_swap(&onto->deltas, &onto_new);+		git_pool_swap(&onto->pool, &onto_pool);+		onto->new_src = from->new_src;+	}++	git_vector_foreach(&onto_new, i, delta)+		git__free(delta);+	git_vector_free(&onto_new);+	git_pool_clear(&onto_pool);++	return error;+}+
+ libgit2/src/diff.h view
@@ -0,0 +1,40 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_diff_h__+#define INCLUDE_diff_h__++#include <stdio.h>+#include "vector.h"+#include "buffer.h"+#include "iterator.h"+#include "repository.h"+#include "pool.h"++#define DIFF_OLD_PREFIX_DEFAULT "a/"+#define DIFF_NEW_PREFIX_DEFAULT "b/"++enum {+	GIT_DIFFCAPS_HAS_SYMLINKS     = (1 << 0), /* symlinks on platform? */+	GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */+	GIT_DIFFCAPS_TRUST_EXEC_BIT   = (1 << 2), /* use st_mode exec bit? */+	GIT_DIFFCAPS_TRUST_CTIME      = (1 << 3), /* use st_ctime? */+	GIT_DIFFCAPS_USE_DEV          = (1 << 4), /* use st_dev? */+};++struct git_diff_list {+	git_repository   *repo;+	git_diff_options opts;+	git_vector       pathspec;+	git_vector       deltas;    /* vector of git_diff_file_delta */+	git_pool pool;+	git_iterator_type_t old_src;+	git_iterator_type_t new_src;+	uint32_t diffcaps;+};++#endif+
+ libgit2/src/diff_output.c view
@@ -0,0 +1,786 @@+/*+ * Copyright (C) 2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "git2/diff.h"+#include "git2/attr.h"+#include "git2/blob.h"+#include "xdiff/xdiff.h"+#include <ctype.h>+#include "diff.h"+#include "map.h"+#include "fileops.h"+#include "filter.h"++typedef struct {+	git_diff_list *diff;+	void *cb_data;+	git_diff_hunk_fn hunk_cb;+	git_diff_data_fn line_cb;+	unsigned int index;+	git_diff_delta *delta;+	git_diff_range range;+} diff_output_info;++static int read_next_int(const char **str, int *value)+{+	const char *scan = *str;+	int v = 0, digits = 0;+	/* find next digit */+	for (scan = *str; *scan && !isdigit(*scan); scan++);+	/* parse next number */+	for (; isdigit(*scan); scan++, digits++)+		v = (v * 10) + (*scan - '0');+	*str = scan;+	*value = v;+	return (digits > 0) ? 0 : -1;+}++static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)+{+	diff_output_info *info = priv;++	if (len == 1 && info->hunk_cb) {+		git_diff_range range = { -1, 0, -1, 0 };+		const char *scan = bufs[0].ptr;++		/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */+		if (*scan != '@')+			return -1;++		if (read_next_int(&scan, &range.old_start) < 0)+			return -1;+		if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0)+			return -1;++		if (read_next_int(&scan, &range.new_start) < 0)+			return -1;+		if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)+			return -1;++		if (range.old_start < 0 || range.new_start < 0)+			return -1;++		memcpy(&info->range, &range, sizeof(git_diff_range));++		return info->hunk_cb(+			info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size);+	}++	if ((len == 2 || len == 3) && info->line_cb) {+		int origin;++		/* expect " "/"-"/"+", then data, then maybe newline */+		origin =+			(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :+			(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :+			GIT_DIFF_LINE_CONTEXT;++		if (info->line_cb(+			info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0)+			return -1;++		/* deal with adding and removing newline at EOF */+		if (len == 3) {+			if (origin == GIT_DIFF_LINE_ADDITION)+				origin = GIT_DIFF_LINE_ADD_EOFNL;+			else+				origin = GIT_DIFF_LINE_DEL_EOFNL;++			return info->line_cb(+				info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size);+		}+	}++	return 0;+}++#define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)++static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)+{+	const char *value;+	if (git_attr_get(&value, repo, 0, file->path, "diff") < 0)+		return -1;++	if (GIT_ATTR_FALSE(value))+		file->flags |= GIT_DIFF_FILE_BINARY;+	else if (GIT_ATTR_TRUE(value))+		file->flags |= GIT_DIFF_FILE_NOT_BINARY;+	/* otherwise leave file->flags alone */++	return 0;+}++static void update_delta_is_binary(git_diff_delta *delta)+{+	if ((delta->old_file.flags & GIT_DIFF_FILE_BINARY) != 0 ||+		(delta->new_file.flags & GIT_DIFF_FILE_BINARY) != 0)+		delta->binary = 1;+	else if ((delta->old_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0 ||+			 (delta->new_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0)+		delta->binary = 0;+	/* otherwise leave delta->binary value untouched */+}++static int file_is_binary_by_attr(+	git_diff_list *diff,+	git_diff_delta *delta)+{+	int error = 0, mirror_new;++	delta->binary = -1;++	/* make sure files are conceivably mmap-able */+	if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size ||+		(git_off_t)((size_t)delta->new_file.size) != delta->new_file.size)+	{+		delta->old_file.flags |= GIT_DIFF_FILE_BINARY;+		delta->new_file.flags |= GIT_DIFF_FILE_BINARY;+		delta->binary = 1;+		return 0;+	}++	/* check if user is forcing us to text diff these files */+	if (diff->opts.flags & GIT_DIFF_FORCE_TEXT) {+		delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;+		delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;+		delta->binary = 0;+		return 0;+	}++	/* check diff attribute +, -, or 0 */+	if (update_file_is_binary_by_attr(diff->repo, &delta->old_file) < 0)+		return -1;++	mirror_new = (delta->new_file.path == delta->old_file.path ||+				  strcmp(delta->new_file.path, delta->old_file.path) == 0);+	if (mirror_new)+		delta->new_file.flags &= (delta->old_file.flags & BINARY_DIFF_FLAGS);+	else+		error = update_file_is_binary_by_attr(diff->repo, &delta->new_file);++	update_delta_is_binary(delta);++	return error;+}++static int file_is_binary_by_content(+	git_diff_delta *delta,+	git_map *old_data,+	git_map *new_data)+{+	git_buf search;++	if ((delta->old_file.flags & BINARY_DIFF_FLAGS) == 0) {+		search.ptr  = old_data->data;+		search.size = min(old_data->len, 4000);++		if (git_buf_is_binary(&search))+			delta->old_file.flags |= GIT_DIFF_FILE_BINARY;+		else+			delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;+	}++	if ((delta->new_file.flags & BINARY_DIFF_FLAGS) == 0) {+		search.ptr  = new_data->data;+		search.size = min(new_data->len, 4000);++		if (git_buf_is_binary(&search))+			delta->new_file.flags |= GIT_DIFF_FILE_BINARY;+		else+			delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;+	}++	update_delta_is_binary(delta);++	/* TODO: if value != NULL, implement diff drivers */++	return 0;+}++static void setup_xdiff_options(+	git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param)+{+	memset(cfg, 0, sizeof(xdemitconf_t));+	memset(param, 0, sizeof(xpparam_t));++	cfg->ctxlen =+		(!opts || !opts->context_lines) ? 3 : opts->context_lines;+	cfg->interhunkctxlen =+		(!opts || !opts->interhunk_lines) ? 3 : opts->interhunk_lines;++	if (!opts)+		return;++	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE)+		param->flags |= XDF_WHITESPACE_FLAGS;+	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)+		param->flags |= XDF_IGNORE_WHITESPACE_CHANGE;+	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)+		param->flags |= XDF_IGNORE_WHITESPACE_AT_EOL;+}++static int get_blob_content(+	git_repository *repo,+	const git_oid *oid,+	git_map *map,+	git_blob **blob)+{+	if (git_oid_iszero(oid))+		return 0;++	if (git_blob_lookup(blob, repo, oid) < 0)+		return -1;++	map->data = (void *)git_blob_rawcontent(*blob);+	map->len  = git_blob_rawsize(*blob);+	return 0;+}++static int get_workdir_content(+	git_repository *repo,+	git_diff_file *file,+	git_map *map)+{+	int error = 0;+	git_buf path = GIT_BUF_INIT;++	if (git_buf_joinpath(&path, git_repository_workdir(repo), file->path) < 0)+		return -1;++	if (S_ISLNK(file->mode)) {+		ssize_t read_len;++		file->flags |= GIT_DIFF_FILE_FREE_DATA;+		file->flags |= GIT_DIFF_FILE_BINARY;++		map->data = git__malloc((size_t)file->size + 1);+		GITERR_CHECK_ALLOC(map->data);++		read_len = p_readlink(path.ptr, map->data, (size_t)file->size + 1);+		if (read_len != (ssize_t)file->size) {+			giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);+			error = -1;+		} else+			map->len = read_len;+	}+	else {+		error = git_futils_mmap_ro_file(map, path.ptr);+		file->flags |= GIT_DIFF_FILE_UNMAP_DATA;+	}+	git_buf_free(&path);+	return error;+}++static void release_content(git_diff_file *file, git_map *map, git_blob *blob)+{+	if (blob != NULL)+		git_blob_free(blob);++	if (file->flags & GIT_DIFF_FILE_FREE_DATA) {+		git__free(map->data);+		map->data = NULL;+		file->flags &= ~GIT_DIFF_FILE_FREE_DATA;+	}+	else if (file->flags & GIT_DIFF_FILE_UNMAP_DATA) {+		git_futils_mmap_free(map);+		map->data = NULL;+		file->flags &= ~GIT_DIFF_FILE_UNMAP_DATA;+	}+}++static void fill_map_from_mmfile(git_map *dst, mmfile_t *src) {+	assert(dst && src);++	dst->data = src->ptr;+	dst->len = src->size;+#ifdef GIT_WIN32+	dst->fmh = NULL;+#endif+}++int git_diff_foreach(+	git_diff_list *diff,+	void *data,+	git_diff_file_fn file_cb,+	git_diff_hunk_fn hunk_cb,+	git_diff_data_fn line_cb)+{+	int error = 0;+	diff_output_info info;+	git_diff_delta *delta;+	xpparam_t    xdiff_params;+	xdemitconf_t xdiff_config;+	xdemitcb_t   xdiff_callback;++	info.diff    = diff;+	info.cb_data = data;+	info.hunk_cb = hunk_cb;+	info.line_cb = line_cb;++	setup_xdiff_options(&diff->opts, &xdiff_config, &xdiff_params);+	memset(&xdiff_callback, 0, sizeof(xdiff_callback));+	xdiff_callback.outf = diff_output_cb;+	xdiff_callback.priv = &info;++	git_vector_foreach(&diff->deltas, info.index, delta) {+		git_blob *old_blob = NULL, *new_blob = NULL;+		git_map old_data, new_data;+		mmfile_t old_xdiff_data, new_xdiff_data;++		if (delta->status == GIT_DELTA_UNMODIFIED &&+			(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)+			continue;++		if (delta->status == GIT_DELTA_IGNORED &&+			(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)+			continue;++		if (delta->status == GIT_DELTA_UNTRACKED &&+			(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)+			continue;++		if ((error = file_is_binary_by_attr(diff, delta)) < 0)+			goto cleanup;++		old_data.data = "";+		old_data.len = 0;+		new_data.data = "";+		new_data.len  = 0;++		/* TODO: Partial blob reading to defer loading whole blob.+		 * I.e. I want a blob with just the first 4kb loaded, then+		 * later on I will read the rest of the blob if needed.+		 */++		/* map files */+		if (delta->binary != 1 &&+			(hunk_cb || line_cb) &&+			(delta->status == GIT_DELTA_DELETED ||+			 delta->status == GIT_DELTA_MODIFIED))+		{+			if (diff->old_src == GIT_ITERATOR_WORKDIR)+				error = get_workdir_content(diff->repo, &delta->old_file, &old_data);+			else+				error = get_blob_content(+					diff->repo, &delta->old_file.oid, &old_data, &old_blob);++			if (error < 0)+				goto cleanup;+		}++		if (delta->binary != 1 &&+			(hunk_cb || line_cb || git_oid_iszero(&delta->new_file.oid)) &&+			(delta->status == GIT_DELTA_ADDED ||+			 delta->status == GIT_DELTA_MODIFIED))+		{+			if (diff->new_src == GIT_ITERATOR_WORKDIR)+				error = get_workdir_content(diff->repo, &delta->new_file, &new_data);+			else+				error = get_blob_content(+					diff->repo, &delta->new_file.oid, &new_data, &new_blob);++			if (error < 0)+				goto cleanup;++			if ((delta->new_file.flags | GIT_DIFF_FILE_VALID_OID) == 0) {+				error = git_odb_hash(+					&delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB);++				if (error < 0)+					goto cleanup;++				/* since we did not have the definitive oid, we may have+				 * incorrect status and need to skip this item.+				 */+				if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) {+					delta->status = GIT_DELTA_UNMODIFIED;+					if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)+						goto cleanup;+				}+			}+		}++		/* if we have not already decided whether file is binary,+		 * check the first 4K for nul bytes to decide...+		 */+		if (delta->binary == -1) {+			error = file_is_binary_by_content(+				delta, &old_data, &new_data);+			if (error < 0)+				goto cleanup;+		}++		/* TODO: if ignore_whitespace is set, then we *must* do text+		 * diffs to tell if a file has really been changed.+		 */++		if (file_cb != NULL) {+			error = file_cb(data, delta, (float)info.index / diff->deltas.length);+			if (error < 0)+				goto cleanup;+		}++		/* don't do hunk and line diffs if file is binary */+		if (delta->binary == 1)+			goto cleanup;++		/* nothing to do if we did not get data */+		if (!old_data.len && !new_data.len)+			goto cleanup;++		assert(hunk_cb || line_cb);++		info.delta = delta;+		old_xdiff_data.ptr = old_data.data;+		old_xdiff_data.size = old_data.len;+		new_xdiff_data.ptr = new_data.data;+		new_xdiff_data.size = new_data.len;++		xdl_diff(&old_xdiff_data, &new_xdiff_data,+			&xdiff_params, &xdiff_config, &xdiff_callback);++cleanup:+		release_content(&delta->old_file, &old_data, old_blob);+		release_content(&delta->new_file, &new_data, new_blob);++		if (error < 0)+			break;+	}++	return error;+}+++typedef struct {+	git_diff_list *diff;+	git_diff_data_fn print_cb;+	void *cb_data;+	git_buf *buf;+} diff_print_info;++static char pick_suffix(int mode)+{+	if (S_ISDIR(mode))+		return '/';+	else if (mode & 0100)+		/* in git, modes are very regular, so we must have 0100755 mode */+		return '*';+	else+		return ' ';+}++static int print_compact(void *data, git_diff_delta *delta, float progress)+{+	diff_print_info *pi = data;+	char code, old_suffix, new_suffix;++	GIT_UNUSED(progress);++	switch (delta->status) {+	case GIT_DELTA_ADDED: code = 'A'; break;+	case GIT_DELTA_DELETED: code = 'D'; break;+	case GIT_DELTA_MODIFIED: code = 'M'; break;+	case GIT_DELTA_RENAMED: code = 'R'; break;+	case GIT_DELTA_COPIED: code = 'C'; break;+	case GIT_DELTA_IGNORED: code = 'I'; break;+	case GIT_DELTA_UNTRACKED: code = '?'; break;+	default: code = 0;+	}++	if (!code)+		return 0;++	old_suffix = pick_suffix(delta->old_file.mode);+	new_suffix = pick_suffix(delta->new_file.mode);++	git_buf_clear(pi->buf);++	if (delta->old_file.path != delta->new_file.path &&+		strcmp(delta->old_file.path,delta->new_file.path) != 0)+		git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,+			delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);+	else if (delta->old_file.mode != delta->new_file.mode &&+		delta->old_file.mode != 0 && delta->new_file.mode != 0)+		git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,+			delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);+	else if (old_suffix != ' ')+		git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);+	else+		git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);++	if (git_buf_oom(pi->buf))+		return -1;++	return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));+}++int git_diff_print_compact(+	git_diff_list *diff,+	void *cb_data,+	git_diff_data_fn print_cb)+{+	int error;+	git_buf buf = GIT_BUF_INIT;+	diff_print_info pi;++	pi.diff     = diff;+	pi.print_cb = print_cb;+	pi.cb_data  = cb_data;+	pi.buf      = &buf;++	error = git_diff_foreach(diff, &pi, print_compact, NULL, NULL);++	git_buf_free(&buf);++	return error;+}+++static int print_oid_range(diff_print_info *pi, git_diff_delta *delta)+{+	char start_oid[8], end_oid[8];++	/* TODO: Determine a good actual OID range to print */+	git_oid_tostr(start_oid, sizeof(start_oid), &delta->old_file.oid);+	git_oid_tostr(end_oid, sizeof(end_oid), &delta->new_file.oid);++	/* TODO: Match git diff more closely */+	if (delta->old_file.mode == delta->new_file.mode) {+		git_buf_printf(pi->buf, "index %s..%s %o\n",+			start_oid, end_oid, delta->old_file.mode);+	} else {+		if (delta->old_file.mode == 0) {+			git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);+		} else if (delta->new_file.mode == 0) {+			git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);+		} else {+			git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);+			git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);+		}+		git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);+	}++	if (git_buf_oom(pi->buf))+		return -1;++	return 0;+}++static int print_patch_file(void *data, git_diff_delta *delta, float progress)+{+	diff_print_info *pi = data;+	const char *oldpfx = pi->diff->opts.old_prefix;+	const char *oldpath = delta->old_file.path;+	const char *newpfx = pi->diff->opts.new_prefix;+	const char *newpath = delta->new_file.path;+	int result;++	GIT_UNUSED(progress);++	if (!oldpfx)+		oldpfx = DIFF_OLD_PREFIX_DEFAULT;++	if (!newpfx)+		newpfx = DIFF_NEW_PREFIX_DEFAULT;++	git_buf_clear(pi->buf);+	git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);++	if (print_oid_range(pi, delta) < 0)+		return -1;++	if (git_oid_iszero(&delta->old_file.oid)) {+		oldpfx = "";+		oldpath = "/dev/null";+	}+	if (git_oid_iszero(&delta->new_file.oid)) {+		newpfx = "";+		newpath = "/dev/null";+	}++	if (delta->binary != 1) {+		git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);+		git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);+	}++	if (git_buf_oom(pi->buf))+		return -1;++    result = pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));+    if (result < 0)+        return result;++    if (delta->binary != 1)+        return 0;++	git_buf_clear(pi->buf);+	git_buf_printf(+		pi->buf, "Binary files %s%s and %s%s differ\n",+		oldpfx, oldpath, newpfx, newpath);+	if (git_buf_oom(pi->buf))+		return -1;++	return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf));+}++static int print_patch_hunk(+	void *data,+	git_diff_delta *d,+	git_diff_range *r,+	const char *header,+	size_t header_len)+{+	diff_print_info *pi = data;++	git_buf_clear(pi->buf);+	if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)+		return -1;++	return pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));+}++static int print_patch_line(+	void *data,+	git_diff_delta *delta,+	git_diff_range *range,+	char line_origin, /* GIT_DIFF_LINE value from above */+	const char *content,+	size_t content_len)+{+	diff_print_info *pi = data;++	git_buf_clear(pi->buf);++	if (line_origin == GIT_DIFF_LINE_ADDITION ||+		line_origin == GIT_DIFF_LINE_DELETION ||+		line_origin == GIT_DIFF_LINE_CONTEXT)+		git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);+	else if (content_len > 0)+		git_buf_printf(pi->buf, "%.*s", (int)content_len, content);++	if (git_buf_oom(pi->buf))+		return -1;++	return pi->print_cb(pi->cb_data, delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf));+}++int git_diff_print_patch(+	git_diff_list *diff,+	void *cb_data,+	git_diff_data_fn print_cb)+{+	int error;+	git_buf buf = GIT_BUF_INIT;+	diff_print_info pi;++	pi.diff     = diff;+	pi.print_cb = print_cb;+	pi.cb_data  = cb_data;+	pi.buf      = &buf;++	error = git_diff_foreach(+		diff, &pi, print_patch_file, print_patch_hunk, print_patch_line);++	git_buf_free(&buf);++	return error;+}++int git_diff_blobs(+	git_blob *old_blob,+	git_blob *new_blob,+	git_diff_options *options,+	void *cb_data,+	git_diff_file_fn file_cb,+	git_diff_hunk_fn hunk_cb,+	git_diff_data_fn line_cb)+{+	diff_output_info info;+	git_diff_delta delta;+	mmfile_t old_data, new_data;+	git_map old_map, new_map;+	xpparam_t xdiff_params;+	xdemitconf_t xdiff_config;+	xdemitcb_t xdiff_callback;+	git_blob *new, *old;++	memset(&delta, 0, sizeof(delta));++	new = new_blob;+	old = old_blob;++	if (options && (options->flags & GIT_DIFF_REVERSE)) {+		git_blob *swap = old;+		old = new;+		new = swap;+	}++	if (old) {+		old_data.ptr  = (char *)git_blob_rawcontent(old);+		old_data.size = git_blob_rawsize(old);+		git_oid_cpy(&delta.old_file.oid, git_object_id((const git_object *)old));+	} else {+		old_data.ptr  = "";+		old_data.size = 0;+	}++	if (new) {+		new_data.ptr  = (char *)git_blob_rawcontent(new);+		new_data.size = git_blob_rawsize(new);+		git_oid_cpy(&delta.new_file.oid, git_object_id((const git_object *)new));+	} else {+		new_data.ptr  = "";+		new_data.size = 0;+	}++	/* populate a "fake" delta record */+	delta.status = new ?+		(old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :+		(old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);++	if (git_oid_cmp(&delta.new_file.oid, &delta.old_file.oid) == 0)+		delta.status = GIT_DELTA_UNMODIFIED;++	delta.old_file.size = old_data.size;+	delta.new_file.size = new_data.size;++	fill_map_from_mmfile(&old_map, &old_data);+	fill_map_from_mmfile(&new_map, &new_data);++	if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0)+		return -1;++	if (file_cb != NULL) {+		int error = file_cb(cb_data, &delta, 1);+		if (error < 0)+			return error;+	}++	/* don't do hunk and line diffs if the two blobs are identical */+	if (delta.status == GIT_DELTA_UNMODIFIED)+		return 0;++	/* don't do hunk and line diffs if file is binary */+	if (delta.binary == 1)+		return 0;++	info.diff    = NULL;+	info.delta   = &delta;+	info.cb_data = cb_data;+	info.hunk_cb = hunk_cb;+	info.line_cb = line_cb;++	setup_xdiff_options(options, &xdiff_config, &xdiff_params);+	memset(&xdiff_callback, 0, sizeof(xdiff_callback));+	xdiff_callback.outf = diff_output_cb;+	xdiff_callback.priv = &info;++	xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);++	return 0;+}
+ libgit2/src/errors.c view
@@ -0,0 +1,119 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "global.h"+#include "posix.h"+#include "buffer.h"+#include <stdarg.h>++/********************************************+ * New error handling+ ********************************************/++static git_error g_git_oom_error = {+	"Out of memory",+	GITERR_NOMEMORY+};++static void set_error(int error_class, char *string)+{+	git_error *error = &GIT_GLOBAL->error_t;++	git__free(error->message);++	error->message = string;+	error->klass = error_class;++	GIT_GLOBAL->last_error = error;+}++void giterr_set_oom(void)+{+	GIT_GLOBAL->last_error = &g_git_oom_error;+}++void giterr_set(int error_class, const char *string, ...)+{+	git_buf buf = GIT_BUF_INIT;+	va_list arglist;++	int unix_error_code = 0;++#ifdef GIT_WIN32+	DWORD win32_error_code = 0;+#endif++	if (error_class == GITERR_OS) {+		unix_error_code = errno;+		errno = 0;++#ifdef GIT_WIN32+		win32_error_code = GetLastError();+		SetLastError(0);+#endif+	}++	va_start(arglist, string);+	git_buf_vprintf(&buf, string, arglist);+	va_end(arglist);++	/* automatically suffix strerror(errno) for GITERR_OS errors */+	if (error_class == GITERR_OS) {++		if (unix_error_code != 0) {+			git_buf_PUTS(&buf, ": ");+			git_buf_puts(&buf, strerror(unix_error_code));+		}++#ifdef GIT_WIN32+		else if (win32_error_code != 0) {+			LPVOID lpMsgBuf = NULL;++			FormatMessage(+				FORMAT_MESSAGE_ALLOCATE_BUFFER | +				FORMAT_MESSAGE_FROM_SYSTEM |+				FORMAT_MESSAGE_IGNORE_INSERTS,+				NULL, win32_error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);++			if (lpMsgBuf) {+				git_buf_PUTS(&buf, ": ");+				git_buf_puts(&buf, lpMsgBuf);+				LocalFree(lpMsgBuf);+			}+		}+#endif+	}++	if (!git_buf_oom(&buf))+		set_error(error_class, git_buf_detach(&buf));+}++void giterr_set_str(int error_class, const char *string)+{+	char *message = git__strdup(string);++	if (message)+		set_error(error_class, message);+}++void giterr_set_regex(const regex_t *regex, int error_code)+{+	char error_buf[1024];+	regerror(error_code, regex, error_buf, sizeof(error_buf));+	giterr_set_str(GITERR_REGEX, error_buf);+}++void giterr_clear(void)+{+	GIT_GLOBAL->last_error = NULL;+}++const git_error *giterr_last(void)+{+	return GIT_GLOBAL->last_error;+}+
+ libgit2/src/fetch.c view
@@ -0,0 +1,200 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "git2/remote.h"+#include "git2/oid.h"+#include "git2/refs.h"+#include "git2/revwalk.h"+#include "git2/indexer.h"++#include "common.h"+#include "transport.h"+#include "remote.h"+#include "refspec.h"+#include "pack.h"+#include "fetch.h"+#include "netops.h"++struct filter_payload {+	git_remote *remote;+	const git_refspec *spec;+	git_odb *odb;+	int found_head;+};++static int filter_ref__cb(git_remote_head *head, void *payload)+{+	struct filter_payload *p = payload;++	if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {+		p->found_head = 1;+	} else {+		/* If it doesn't match the refpec, we don't want it */+		if (!git_refspec_src_matches(p->spec, head->name))+			return 0;++		/* Don't even try to ask for the annotation target */+		if (!git__suffixcmp(head->name, "^{}"))+			return 0;+	}++	/* If we have the object, mark it so we don't ask for it */+	if (git_odb_exists(p->odb, &head->oid))+		head->local = 1;+	else+		p->remote->need_pack = 1;++	return git_vector_insert(&p->remote->refs, head);+}++static int filter_wants(git_remote *remote)+{+	struct filter_payload p;++	git_vector_clear(&remote->refs);++	/*+	 * The fetch refspec can be NULL, and what this means is that the+	 * user didn't specify one. This is fine, as it means that we're+	 * not interested in any particular branch but just the remote's+	 * HEAD, which will be stored in FETCH_HEAD after the fetch.+	 */+	p.spec = git_remote_fetchspec(remote);+	p.found_head = 0;+	p.remote = remote;++	if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)+		return -1;++	return remote->transport->ls(remote->transport, &filter_ref__cb, &p);+}++/*+ * In this first version, we push all our refs in and start sending+ * them out. When we get an ACK we hide that commit and continue+ * traversing until we're done+ */+int git_fetch_negotiate(git_remote *remote)+{+	git_transport *t = remote->transport;++	if (filter_wants(remote) < 0) {+		giterr_set(GITERR_NET, "Failed to filter the reference list for wants");+		return -1;+	}++	/* Don't try to negotiate when we don't want anything */+	if (remote->refs.length == 0 || !remote->need_pack)+		return 0;++	/*+	 * Now we have everything set up so we can start tell the server+	 * what we want and what we have.+	 */+	return t->negotiate_fetch(t, remote->repo, &remote->refs);+}++int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)+{+	if(!remote->need_pack)+		return 0;++	return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats);+}++/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */+int git_fetch__download_pack(+	const char *buffered,+	size_t buffered_size,+	GIT_SOCKET fd,+	git_repository *repo,+	git_off_t *bytes,+	git_indexer_stats *stats)+{+	int recvd;+	char buff[1024];+	gitno_buffer buf;+	git_indexer_stream *idx;++	gitno_buffer_setup(&buf, buff, sizeof(buff), fd);++	if (memcmp(buffered, "PACK", strlen("PACK"))) {+		giterr_set(GITERR_NET, "The pack doesn't start with the signature");+		return -1;+	}++	if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)+		return -1;++	memset(stats, 0, sizeof(git_indexer_stats));+	if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0)+		goto on_error;++	*bytes = buffered_size;++	do {+		if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0)+			goto on_error;++		gitno_consume_n(&buf, buf.offset);+		if ((recvd = gitno_recv(&buf)) < 0)+			goto on_error;++		*bytes += recvd;+	} while(recvd > 0);++	if (git_indexer_stream_finalize(idx, stats))+		goto on_error;++	git_indexer_stream_free(idx);+	return 0;++on_error:+	git_indexer_stream_free(idx);+	return -1;+}++int git_fetch_setup_walk(git_revwalk **out, git_repository *repo)+{+	git_revwalk *walk;+	git_strarray refs;+	unsigned int i;+	git_reference *ref;++	if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0)+		return -1;++	if (git_revwalk_new(&walk, repo) < 0)+		return -1;++	git_revwalk_sorting(walk, GIT_SORT_TIME);++	for (i = 0; i < refs.count; ++i) {+		/* No tags */+		if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))+			continue;++		if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)+			goto on_error;++		if (git_reference_type(ref) == GIT_REF_SYMBOLIC)+			continue;+		if (git_revwalk_push(walk, git_reference_oid(ref)) < 0)+			goto on_error;++		git_reference_free(ref);+	}++	git_strarray_free(&refs);+	*out = walk;+	return 0;++on_error:+	git_reference_free(ref);+	git_strarray_free(&refs);+	return -1;+}
+ libgit2/src/fetch.h view
@@ -0,0 +1,19 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_fetch_h__+#define INCLUDE_fetch_h__++#include "netops.h"++int git_fetch_negotiate(git_remote *remote);+int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);++int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,+			     git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);+int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);++#endif
+ libgit2/src/filebuf.c view
@@ -0,0 +1,452 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include <stdarg.h>++#include "common.h"+#include "filebuf.h"+#include "fileops.h"++#define GIT_LOCK_FILE_MODE 0644++static const size_t WRITE_BUFFER_SIZE = (4096 * 2);++enum buferr_t {+	BUFERR_OK = 0,+	BUFERR_WRITE,+	BUFERR_ZLIB,+	BUFERR_MEM+};++#define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; }++static int verify_last_error(git_filebuf *file)+{+	switch (file->last_error) {+	case BUFERR_WRITE:+		giterr_set(GITERR_OS, "Failed to write out file");+		return -1;++	case BUFERR_MEM:+		giterr_set_oom();+		return -1;++	case BUFERR_ZLIB:+		giterr_set(GITERR_ZLIB,+			"Buffer error when writing out ZLib data");+		return -1;++	default:+		return 0;+	}+}++static int lock_file(git_filebuf *file, int flags)+{+	if (git_path_exists(file->path_lock) == true) {+		if (flags & GIT_FILEBUF_FORCE)+			p_unlink(file->path_lock);+		else {+			giterr_set(GITERR_OS,+				"Failed to lock file '%s' for writing", file->path_lock);+			return -1;+		}+	}++	/* create path to the file buffer is required */+	if (flags & GIT_FILEBUF_FORCE) {+		/* XXX: Should dirmode here be configurable? Or is 0777 always fine? */+		file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, GIT_LOCK_FILE_MODE);+	} else {+		file->fd = git_futils_creat_locked(file->path_lock, GIT_LOCK_FILE_MODE);+	}++	if (file->fd < 0)+		return -1;++	file->fd_is_open = true;++	if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {+		git_file source;+		char buffer[2048];+		size_t read_bytes;++		source = p_open(file->path_original, O_RDONLY);+		if (source < 0) {+			giterr_set(GITERR_OS,+				"Failed to open file '%s' for reading",+				file->path_original);+			return -1;+		}++		while ((read_bytes = p_read(source, buffer, 2048)) > 0) {+			p_write(file->fd, buffer, read_bytes);+			if (file->digest)+				git_hash_update(file->digest, buffer, read_bytes);+		}++		p_close(source);+	}++	return 0;+}++void git_filebuf_cleanup(git_filebuf *file)+{+	if (file->fd_is_open && file->fd >= 0)+		p_close(file->fd);++	if (file->fd_is_open && file->path_lock && git_path_exists(file->path_lock))+		p_unlink(file->path_lock);++	if (file->digest)+		git_hash_free_ctx(file->digest);++	if (file->buffer)+		git__free(file->buffer);++	/* use the presence of z_buf to decide if we need to deflateEnd */+	if (file->z_buf) {+		git__free(file->z_buf);+		deflateEnd(&file->zs);+	}++	if (file->path_original)+		git__free(file->path_original);+	if (file->path_lock)+		git__free(file->path_lock);++	memset(file, 0x0, sizeof(git_filebuf));+	file->fd = -1;+}++GIT_INLINE(int) flush_buffer(git_filebuf *file)+{+	int result = file->write(file, file->buffer, file->buf_pos);+	file->buf_pos = 0;+	return result;+}++static int write_normal(git_filebuf *file, void *source, size_t len)+{+	if (len > 0) {+		if (p_write(file->fd, (void *)source, len) < 0) {+			file->last_error = BUFERR_WRITE;+			return -1;+		}++		if (file->digest)+			git_hash_update(file->digest, source, len);+	}++	return 0;+}++static int write_deflate(git_filebuf *file, void *source, size_t len)+{+	z_stream *zs = &file->zs;++	if (len > 0 || file->flush_mode == Z_FINISH) {+		zs->next_in = source;+		zs->avail_in = (uInt)len;++		do {+			size_t have;++			zs->next_out = file->z_buf;+			zs->avail_out = (uInt)file->buf_size;++			if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) {+				file->last_error = BUFERR_ZLIB;+				return -1;+			}++			have = file->buf_size - (size_t)zs->avail_out;++			if (p_write(file->fd, file->z_buf, have) < 0) {+				file->last_error = BUFERR_WRITE;+				return -1;+			}++		} while (zs->avail_out == 0);++		assert(zs->avail_in == 0);++		if (file->digest)+			git_hash_update(file->digest, source, len);+	}++	return 0;+}++int git_filebuf_open(git_filebuf *file, const char *path, int flags)+{+	int compression;+	size_t path_len;++	/* opening an already open buffer is a programming error;+	 * assert that this never happens instead of returning+	 * an error code */+	assert(file && path && file->buffer == NULL);++	memset(file, 0x0, sizeof(git_filebuf));++	if (flags & GIT_FILEBUF_DO_NOT_BUFFER)+		file->do_not_buffer = true;++	file->buf_size = WRITE_BUFFER_SIZE;+	file->buf_pos = 0;+	file->fd = -1;+	file->last_error = BUFERR_OK;++	/* Allocate the main cache buffer */+	if (!file->do_not_buffer) {+		file->buffer = git__malloc(file->buf_size);+		GITERR_CHECK_ALLOC(file->buffer);+	}++	/* If we are hashing on-write, allocate a new hash context */+	if (flags & GIT_FILEBUF_HASH_CONTENTS) {+		file->digest = git_hash_new_ctx();+		GITERR_CHECK_ALLOC(file->digest);+	}++	compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;++	/* If we are deflating on-write, */+	if (compression != 0) {+		/* Initialize the ZLib stream */+		if (deflateInit(&file->zs, compression) != Z_OK) {+			giterr_set(GITERR_ZLIB, "Failed to initialize zlib");+			goto cleanup;+		}++		/* Allocate the Zlib cache buffer */+		file->z_buf = git__malloc(file->buf_size);+		GITERR_CHECK_ALLOC(file->z_buf);++		/* Never flush */+		file->flush_mode = Z_NO_FLUSH;+		file->write = &write_deflate;+	} else {+		file->write = &write_normal;+	}++	/* If we are writing to a temp file */+	if (flags & GIT_FILEBUF_TEMPORARY) {+		git_buf tmp_path = GIT_BUF_INIT;++		/* Open the file as temporary for locking */+		file->fd = git_futils_mktmp(&tmp_path, path);++		if (file->fd < 0) {+			git_buf_free(&tmp_path);+			goto cleanup;+		}+		file->fd_is_open = true;++		/* No original path */+		file->path_original = NULL;+		file->path_lock = git_buf_detach(&tmp_path);+		GITERR_CHECK_ALLOC(file->path_lock);+	} else {+		path_len = strlen(path);++		/* Save the original path of the file */+		file->path_original = git__strdup(path);+		GITERR_CHECK_ALLOC(file->path_original);++		/* create the locking path by appending ".lock" to the original */+		file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH);+		GITERR_CHECK_ALLOC(file->path_lock);++		memcpy(file->path_lock, file->path_original, path_len);+		memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);++		/* open the file for locking */+		if (lock_file(file, flags) < 0)+			goto cleanup;+	}++	return 0;++cleanup:+	git_filebuf_cleanup(file);+	return -1;+}++int git_filebuf_hash(git_oid *oid, git_filebuf *file)+{+	assert(oid && file && file->digest);++	flush_buffer(file);++	if (verify_last_error(file) < 0)+		return -1;++	git_hash_final(oid, file->digest);+	git_hash_free_ctx(file->digest);+	file->digest = NULL;++	return 0;+}++int git_filebuf_commit_at(git_filebuf *file, const char *path, mode_t mode)+{+	git__free(file->path_original);+	file->path_original = git__strdup(path);+	GITERR_CHECK_ALLOC(file->path_original);++	return git_filebuf_commit(file, mode);+}++int git_filebuf_commit(git_filebuf *file, mode_t mode)+{+	/* temporary files cannot be committed */+	assert(file && file->path_original);++	file->flush_mode = Z_FINISH;+	flush_buffer(file);++	if (verify_last_error(file) < 0)+		goto on_error;++	p_close(file->fd);+	file->fd = -1;+	file->fd_is_open = false;++	if (p_chmod(file->path_lock, mode)) {+		giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock);+		goto on_error;+	}++	p_unlink(file->path_original);++	if (p_rename(file->path_lock, file->path_original) < 0) {+		giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);+		goto on_error;+	}++	git_filebuf_cleanup(file);+	return 0;++on_error:+	git_filebuf_cleanup(file);+	return -1;+}++GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)+{+	memcpy(file->buffer + file->buf_pos, buf, len);+	file->buf_pos += len;+}++int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)+{+	const unsigned char *buf = buff;++	ENSURE_BUF_OK(file);++	if (file->do_not_buffer)+		return file->write(file, (void *)buff, len);++	for (;;) {+		size_t space_left = file->buf_size - file->buf_pos;++		/* cache if it's small */+		if (space_left > len) {+			add_to_cache(file, buf, len);+			return 0;+		}++		add_to_cache(file, buf, space_left);+		if (flush_buffer(file) < 0)+			return -1;++		len -= space_left;+		buf += space_left;+	}+}++int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len)+{+	size_t space_left = file->buf_size - file->buf_pos;++	*buffer = NULL;++	ENSURE_BUF_OK(file);++	if (len > file->buf_size) {+		file->last_error = BUFERR_MEM;+		return -1;+	}++	if (space_left <= len) {+		if (flush_buffer(file) < 0)+			return -1;+	}++	*buffer = (file->buffer + file->buf_pos);+	file->buf_pos += len;++	return 0;+}++int git_filebuf_printf(git_filebuf *file, const char *format, ...)+{+	va_list arglist;+	size_t space_left;+	int len, res;+	char *tmp_buffer;++	ENSURE_BUF_OK(file);++	space_left = file->buf_size - file->buf_pos;++	do {+		va_start(arglist, format);+		len = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);+		va_end(arglist);++		if (len < 0) {+			file->last_error = BUFERR_MEM;+			return -1;+		}++		if ((size_t)len + 1 <= space_left) {+			file->buf_pos += len;+			return 0;+		}++		if (flush_buffer(file) < 0)+			return -1;++		space_left = file->buf_size - file->buf_pos;++	} while ((size_t)len + 1 <= space_left);++	tmp_buffer = git__malloc(len + 1);+	if (!tmp_buffer) {+		file->last_error = BUFERR_MEM;+		return -1;+	}++	va_start(arglist, format);+	len = p_vsnprintf(tmp_buffer, len + 1, format, arglist);+	va_end(arglist);++	if (len < 0) {+		git__free(tmp_buffer);+		file->last_error = BUFERR_MEM;+		return -1;+	}++	res = git_filebuf_write(file, tmp_buffer, len);+	git__free(tmp_buffer);++	return res;+}+
+ libgit2/src/filebuf.h view
@@ -0,0 +1,85 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_filebuf_h__+#define INCLUDE_filebuf_h__++#include "fileops.h"+#include "hash.h"+#include <zlib.h>++#ifdef GIT_THREADS+#	define GIT_FILEBUF_THREADS+#endif++#define GIT_FILEBUF_HASH_CONTENTS		(1 << 0)+#define GIT_FILEBUF_APPEND				(1 << 2)+#define GIT_FILEBUF_FORCE				(1 << 3)+#define GIT_FILEBUF_TEMPORARY			(1 << 4)+#define GIT_FILEBUF_DO_NOT_BUFFER		(1 << 5)+#define GIT_FILEBUF_DEFLATE_SHIFT		(6)++#define GIT_FILELOCK_EXTENSION ".lock\0"+#define GIT_FILELOCK_EXTLENGTH 6++struct git_filebuf {+	char *path_original;+	char *path_lock;++	int (*write)(struct git_filebuf *file, void *source, size_t len);++	git_hash_ctx *digest;++	unsigned char *buffer;+	unsigned char *z_buf;++	z_stream zs;+	int flush_mode;++	size_t buf_size, buf_pos;+	git_file fd;+	bool fd_is_open;+	bool do_not_buffer;+	int last_error;+};++typedef struct git_filebuf git_filebuf;++#define GIT_FILEBUF_INIT {0}++/*+ * The git_filebuf object lifecycle is:+ * - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT.+ *+ * - Call git_filebuf_open() to initialize the filebuf for use.+ *+ * - Make as many calls to git_filebuf_write(), git_filebuf_printf(),+ *   git_filebuf_reserve() as you like. The error codes for these+ *   functions don't need to be checked. They are stored internally+ *   by the file buffer.+ *+ * - While you are writing, you may call git_filebuf_hash() to get+ *   the hash of all you have written so far. This function will+ *   fail if any of the previous writes to the buffer failed.+ *+ * - To close the git_filebuf, you may call git_filebuf_commit() or+ *   git_filebuf_commit_at() to save the file, or+ *   git_filebuf_cleanup() to abandon the file.  All of these will+ *   free the git_filebuf object. Likewise, all of these will fail+ *   if any of the previous writes to the buffer failed, and set+ *   an error code accordingly.+ */+int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);+int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);+int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);++int git_filebuf_open(git_filebuf *lock, const char *path, int flags);+int git_filebuf_commit(git_filebuf *lock, mode_t mode);+int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode);+void git_filebuf_cleanup(git_filebuf *lock);+int git_filebuf_hash(git_oid *oid, git_filebuf *file);++#endif
+ libgit2/src/fileops.c view
@@ -0,0 +1,475 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "fileops.h"+#include <ctype.h>++int git_futils_mkpath2file(const char *file_path, const mode_t mode)+{+	int result = 0;+	git_buf target_folder = GIT_BUF_INIT;++	if (git_path_dirname_r(&target_folder, file_path) < 0)+		return -1;++	/* Does the containing folder exist? */+	if (git_path_isdir(target_folder.ptr) == false)+		/* Let's create the tree structure */+		result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);++	git_buf_free(&target_folder);+	return result;+}++int git_futils_mktmp(git_buf *path_out, const char *filename)+{+	int fd;++	git_buf_sets(path_out, filename);+	git_buf_puts(path_out, "_git2_XXXXXX");++	if (git_buf_oom(path_out))+		return -1;++	if ((fd = p_mkstemp(path_out->ptr)) < 0) {+		giterr_set(GITERR_OS,+			"Failed to create temporary file '%s'", path_out->ptr);+		return -1;+	}++	return fd;+}++int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)+{+	int fd;++	if (git_futils_mkpath2file(path, dirmode) < 0)+		return -1;++	fd = p_creat(path, mode);+	if (fd < 0) {+		giterr_set(GITERR_OS, "Failed to create file '%s'", path);+		return -1;+	}++	return fd;+}++int git_futils_creat_locked(const char *path, const mode_t mode)+{+	int fd;++#ifdef GIT_WIN32+	wchar_t* buf;++	buf = gitwin_to_utf16(path);+	fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);+	git__free(buf);+#else+	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);+#endif++	if (fd < 0) {+		giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);+		return -1;+	}++	return fd;+}++int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)+{+	if (git_futils_mkpath2file(path, dirmode) < 0)+		return -1;++	return git_futils_creat_locked(path, mode);+}++int git_futils_open_ro(const char *path)+{+	int fd = p_open(path, O_RDONLY);+	if (fd < 0) {+		if (errno == ENOENT)+			fd = GIT_ENOTFOUND;+		giterr_set(GITERR_OS, "Failed to open '%s'", path);+	}+	return fd;+}++git_off_t git_futils_filesize(git_file fd)+{+	struct stat sb;++	if (p_fstat(fd, &sb)) {+		giterr_set(GITERR_OS, "Failed to stat file descriptor");+		return -1;+	}++	return sb.st_size;+}++mode_t git_futils_canonical_mode(mode_t raw_mode)+{+	if (S_ISREG(raw_mode))+		return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);+	else if (S_ISLNK(raw_mode))+		return S_IFLNK;+	else if (S_ISGITLINK(raw_mode))+		return S_IFGITLINK;+	else if (S_ISDIR(raw_mode))+		return S_IFDIR;+	else+		return 0;+}++int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)+{+	git_file fd;+	size_t len;+	struct stat st;++	assert(buf && path && *path);++	if (updated != NULL)+		*updated = 0;++	if ((fd = git_futils_open_ro(path)) < 0)+		return fd;++	if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {+		p_close(fd);+		giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);+		return -1;+	}++	/*+	 * If we were given a time, we only want to read the file if it+	 * has been modified.+	 */+	if (mtime != NULL && *mtime >= st.st_mtime) {+		p_close(fd);+		return 0;+	}++	if (mtime != NULL)+		*mtime = st.st_mtime;++	len = (size_t) st.st_size;++	git_buf_clear(buf);++	if (git_buf_grow(buf, len + 1) < 0) {+		p_close(fd);+		return -1;+	}++	buf->ptr[len] = '\0';++	while (len > 0) {+		ssize_t read_size = p_read(fd, buf->ptr, len);++		if (read_size < 0) {+			p_close(fd);+			giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path);+			return -1;+		}++		len -= read_size;+		buf->size += read_size;+	}++	p_close(fd);++	if (updated != NULL)+		*updated = 1;++	return 0;+}++int git_futils_readbuffer(git_buf *buf, const char *path)+{+	return git_futils_readbuffer_updated(buf, path, NULL, NULL);+}++int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)+{+	if (git_futils_mkpath2file(to, dirmode) < 0)+		return -1;++	if (p_rename(from, to) < 0) {+		giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);+		return -1;+	}++	return 0;+}++int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)+{+	return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);+}++int git_futils_mmap_ro_file(git_map *out, const char *path)+{+	git_file fd = git_futils_open_ro(path);+	git_off_t len;+	int result;++	if (fd < 0)+		return fd;++	len = git_futils_filesize(fd);+	if (!git__is_sizet(len)) {+		giterr_set(GITERR_OS, "File `%s` too large to mmap", path);+		return -1;+	}++	result = git_futils_mmap_ro(out, fd, 0, (size_t)len);+	p_close(fd);+	return result;+}++void git_futils_mmap_free(git_map *out)+{+	p_munmap(out);+}++int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)+{+	int root_path_offset;+	git_buf make_path = GIT_BUF_INIT;+	size_t start;+	char *pp, *sp;+	bool failed = false;++	if (base != NULL) {+		start = strlen(base);+		if (git_buf_joinpath(&make_path, base, path) < 0)+			return -1;+	} else {+		start = 0;+		if (git_buf_puts(&make_path, path) < 0)+			return -1;+	}++	pp = make_path.ptr + start;++	root_path_offset = git_path_root(make_path.ptr);+	if (root_path_offset > 0)+		pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */++	while (!failed && (sp = strchr(pp, '/')) != NULL) {+		if (sp != pp && git_path_isdir(make_path.ptr) == false) {+			*sp = 0;++			/* Do not choke while trying to recreate an existing directory */+			if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)+				failed = true;++			*sp = '/';+		}++		pp = sp + 1;+	}++	if (*pp != '\0' && !failed) {+		if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)+			failed = true;+	}++	git_buf_free(&make_path);++	if (failed) {+		giterr_set(GITERR_OS,+			"Failed to create directory structure at '%s'", path);+		return -1;+	}++	return 0;+}++static int _rmdir_recurs_foreach(void *opaque, git_buf *path)+{+	git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;++	assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY+		|| removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS+		|| removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);++	if (git_path_isdir(path->ptr) == true) {+		if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)+			return -1;++		if (p_rmdir(path->ptr) < 0) {+			if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))+				return 0;++			giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);+			return -1;+		}++		return 0;+	}++	if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {+		if (p_unlink(path->ptr) < 0) {+			giterr_set(GITERR_OS, "Could not remove directory.  File '%s' cannot be removed", path->ptr);+			return -1;+		}++		return 0;+	}++	if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {+		giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);+		return -1;+	}++	return 0;+}++int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type)+{+	int error;+	git_buf p = GIT_BUF_INIT;++	error = git_buf_sets(&p, path);+	if (!error)+		error = _rmdir_recurs_foreach(&removal_type, &p);+	git_buf_free(&p);+	return error;+}++int git_futils_find_global_file(git_buf *path, const char *filename)+{+	const char *home = getenv("HOME");++#ifdef GIT_WIN32+	if (home == NULL)+		home = getenv("USERPROFILE");+#endif++	if (home == NULL) {+		giterr_set(GITERR_OS, "Global file lookup failed. "+			"Cannot locate the user's home directory");+		return -1;+	}++	if (git_buf_joinpath(path, home, filename) < 0)+		return -1;++	if (git_path_exists(path->ptr) == false) {+		git_buf_clear(path);+		return GIT_ENOTFOUND;+	}++	return 0;+}++#ifdef GIT_WIN32+typedef struct {+	wchar_t *path;+	DWORD len;+} win32_path;++static const win32_path *win32_system_root(void)+{+	static win32_path s_root = { 0, 0 };++	if (s_root.path == NULL) {+		const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\";++		s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0);+		if (s_root.len <= 0) {+			giterr_set(GITERR_OS, "Failed to expand environment strings");+			return NULL;+		}++		s_root.path = git__calloc(s_root.len, sizeof(wchar_t));+		if (s_root.path == NULL)+			return NULL;++		if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) {+			giterr_set(GITERR_OS, "Failed to expand environment strings");+			git__free(s_root.path);+			s_root.path = NULL;+			return NULL;+		}+	}++	return &s_root;+}++static int win32_find_system_file(git_buf *path, const char *filename)+{+	int error = 0;+	const win32_path *root = win32_system_root();+	size_t len;+	wchar_t *file_utf16 = NULL, *scan;+	char *file_utf8 = NULL;++	if (!root || !filename || (len = strlen(filename)) == 0)+		return GIT_ENOTFOUND;++	/* allocate space for wchar_t path to file */+	file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));+	GITERR_CHECK_ALLOC(file_utf16);++	/* append root + '\\' + filename as wchar_t */+	memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));++	if (*filename == '/' || *filename == '\\')+		filename++;++	if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=+		(int)len + 1) {+		error = -1;+		goto cleanup;+	}++	for (scan = file_utf16; *scan; scan++)+		if (*scan == L'/')+			*scan = L'\\';++	/* check access */+	if (_waccess(file_utf16, F_OK) < 0) {+		error = GIT_ENOTFOUND;+		goto cleanup;+	}++	/* convert to utf8 */+	if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)+		error = -1;+	else {+		git_path_mkposix(file_utf8);+		git_buf_attach(path, file_utf8, 0);+	}++cleanup:+	git__free(file_utf16);++	return error;+}+#endif++int git_futils_find_system_file(git_buf *path, const char *filename)+{+	if (git_buf_joinpath(path, "/etc", filename) < 0)+		return -1;++	if (git_path_exists(path->ptr) == true)+		return 0;++	git_buf_clear(path);++#ifdef GIT_WIN32+	return win32_find_system_file(path, filename);+#else+	return GIT_ENOTFOUND;+#endif+}
+ libgit2/src/fileops.h view
@@ -0,0 +1,179 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_fileops_h__+#define INCLUDE_fileops_h__++#include "common.h"+#include "map.h"+#include "posix.h"+#include "path.h"++/**+ * Filebuffer methods+ *+ * Read whole files into an in-memory buffer for processing+ */+extern int git_futils_readbuffer(git_buf *obj, const char *path);+extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t *mtime, int *updated);++/**+ * File utils+ *+ * These are custom filesystem-related helper methods. They are+ * rather high level, and wrap the underlying POSIX methods+ *+ * All these methods return 0 on success,+ * or an error code on failure and an error message is set.+ */++/**+ * Create and open a file, while also+ * creating all the folders in its path+ */+extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode);++/**+ * Create an open a process-locked file+ */+extern int git_futils_creat_locked(const char *path, const mode_t mode);++/**+ * Create an open a process-locked file, while+ * also creating all the folders in its path+ */+extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode);++/**+ * Create a path recursively+ */+extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);++/**+ * Create all the folders required to contain+ * the full path of a file+ */+extern int git_futils_mkpath2file(const char *path, const mode_t mode);++typedef enum {+	GIT_DIRREMOVAL_EMPTY_HIERARCHY = 0,+	GIT_DIRREMOVAL_FILES_AND_DIRS = 1,+	GIT_DIRREMOVAL_ONLY_EMPTY_DIRS = 2,+} git_directory_removal_type;++/**+ * Remove path and any files and directories beneath it.+ *+ * @param path Path to to top level directory to process.+ *+ * @param removal_type GIT_DIRREMOVAL_EMPTY_HIERARCHY to remove a hierarchy+ * of empty directories (will fail if any file is found), GIT_DIRREMOVAL_FILES_AND_DIRS+ * to remove a hierarchy of files and folders, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove+ * empty directories (no failure on file encounter).+ *+ * @return 0 on success; -1 on error.+ */+extern int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type);++/**+ * Create and open a temporary file with a `_git2_` suffix.+ * Writes the filename into path_out.+ * @return On success, an open file descriptor, else an error code < 0.+ */+extern int git_futils_mktmp(git_buf *path_out, const char *filename);++/**+ * Move a file on the filesystem, create the+ * destination path if it doesn't exist+ */+extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);++/**+ * Open a file readonly and set error if needed.+ */+extern int git_futils_open_ro(const char *path);++/**+ * Get the filesize in bytes of a file+ */+extern git_off_t git_futils_filesize(git_file fd);++#define GIT_MODE_PERMS_MASK			0777+#define GIT_CANONICAL_PERMS(MODE)	(((MODE) & 0100) ? 0755 : 0644)+#define GIT_MODE_TYPE(MODE)			((MODE) & ~GIT_MODE_PERMS_MASK)++/**+ * Convert a mode_t from the OS to a legal git mode_t value.+ */+extern mode_t git_futils_canonical_mode(mode_t raw_mode);+++/**+ * Read-only map all or part of a file into memory.+ * When possible this function should favor a virtual memory+ * style mapping over some form of malloc()+read(), as the+ * data access will be random and is not likely to touch the+ * majority of the region requested.+ *+ * @param out buffer to populate with the mapping information.+ * @param fd open descriptor to configure the mapping from.+ * @param begin first byte to map, this should be page aligned.+ * @param end number of bytes to map.+ * @return+ * - 0 on success;+ * - -1 on error.+ */+extern int git_futils_mmap_ro(+	git_map *out,+	git_file fd,+	git_off_t begin,+	size_t len);++/**+ * Read-only map an entire file.+ *+ * @param out buffer to populate with the mapping information.+ * @param path path to file to be opened.+ * @return+ * - 0 on success;+ * - GIT_ENOTFOUND if not found;+ * - -1 on an unspecified OS related error.+ */+extern int git_futils_mmap_ro_file(+	git_map *out,+	const char *path);++/**+ * Release the memory associated with a previous memory mapping.+ * @param map the mapping description previously configured.+ */+extern void git_futils_mmap_free(git_map *map);++/**+ * Find a "global" file (i.e. one in a user's home directory).+ *+ * @param pathbuf buffer to write the full path into+ * @param filename name of file to find in the home directory+ * @return+ * - 0 if found;+ * - GIT_ENOTFOUND if not found;+ * - -1 on an unspecified OS related error.+ */+extern int git_futils_find_global_file(git_buf *path, const char *filename);++/**+ * Find a "system" file (i.e. one shared for all users of the system).+ *+ * @param pathbuf buffer to write the full path into+ * @param filename name of file to find in the home directory+ * @return+ * - 0 if found;+ * - GIT_ENOTFOUND if not found;+ * - -1 on an unspecified OS related error.+ */+extern int git_futils_find_system_file(git_buf *path, const char *filename);++#endif /* INCLUDE_fileops_h__ */
+ libgit2/src/filter.c view
@@ -0,0 +1,165 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "fileops.h"+#include "hash.h"+#include "filter.h"+#include "repository.h"+#include "git2/config.h"++/* Tweaked from Core Git. I wonder what we could use this for... */+void git_text_gather_stats(git_text_stats *stats, const git_buf *text)+{+	size_t i;++	memset(stats, 0, sizeof(*stats));++	for (i = 0; i < git_buf_len(text); i++) {+		unsigned char c = text->ptr[i];++		if (c == '\r') {+			stats->cr++;++			if (i + 1 < git_buf_len(text) && text->ptr[i + 1] == '\n')+				stats->crlf++;+		}++		else if (c == '\n')+			stats->lf++;++		else if (c == 0x85)+			/* Unicode CR+LF */+			stats->crlf++;++		else if (c == 127)+			/* DEL */+			stats->nonprintable++;++		else if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {+			switch (c) {+				/* BS, HT, ESC and FF */+			case '\b': case '\t': case '\033': case '\014':+				stats->printable++;+				break;+			case 0:+				stats->nul++;+				/* fall through */+			default:+				stats->nonprintable++;+			}+		}++		else+			stats->printable++;+	}++	/* If file ends with EOF then don't count this EOF as non-printable. */+	if (git_buf_len(text) >= 1 && text->ptr[text->size - 1] == '\032')+		stats->nonprintable--;+}++/*+ * Fresh from Core Git+ */+int git_text_is_binary(git_text_stats *stats)+{+	if (stats->nul)+		return 1;++	if ((stats->printable >> 7) < stats->nonprintable)+		return 1;+	/*+	 * Other heuristics? Average line length might be relevant,+	 * as might LF vs CR vs CRLF counts..+	 *+	 * NOTE! It might be normal to have a low ratio of CRLF to LF+	 * (somebody starts with a LF-only file and edits it with an editor+	 * that adds CRLF only to lines that are added..). But do  we+	 * want to support CR-only? Probably not.+	 */+	return 0;+}++int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)+{+	int error;++	if (mode == GIT_FILTER_TO_ODB) {+		/* Load the CRLF cleanup filter when writing to the ODB */+		error = git_filter_add__crlf_to_odb(filters, repo, path);+		if (error < 0)+			return error;+	} else {+		giterr_set(GITERR_INVALID, "Worktree filters are not implemented yet");+		return -1;+	}++	return (int)filters->length;+}++void git_filters_free(git_vector *filters)+{+	size_t i;+	git_filter *filter;++	git_vector_foreach(filters, i, filter) {+		if (filter->do_free != NULL)+			filter->do_free(filter);+		else+			git__free(filter);+	}++	git_vector_free(filters);+}++int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)+{+	unsigned int i, src;+	git_buf *dbuffer[2];++	dbuffer[0] = source;+	dbuffer[1] = dest;++	src = 0;++	if (git_buf_len(source) == 0) {+		git_buf_clear(dest);+		return 0;+	}++	/* Pre-grow the destination buffer to more or less the size+	 * we expect it to have */+	if (git_buf_grow(dest, git_buf_len(source)) < 0)+		return -1;++	for (i = 0; i < filters->length; ++i) {+		git_filter *filter = git_vector_get(filters, i);+		unsigned int dst = 1 - src;++		git_buf_clear(dbuffer[dst]);++		/* Apply the filter from dbuffer[src] to the other buffer;+		 * if the filtering is canceled by the user mid-filter,+		 * we skip to the next filter without changing the source+		 * of the double buffering (so that the text goes through+		 * cleanly).+		 */+		if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)+			src = dst;++		if (git_buf_oom(dbuffer[dst]))+			return -1;+	}++	/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */+	if (src != 1)+		git_buf_swap(dest, source);++	return 0;+}+
+ libgit2/src/filter.h view
@@ -0,0 +1,119 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_filter_h__+#define INCLUDE_filter_h__++#include "common.h"+#include "buffer.h"+#include "git2/odb.h"+#include "git2/repository.h"++typedef struct git_filter {+	int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);+	void (*do_free)(struct git_filter *self);+} git_filter;++typedef enum {+	GIT_FILTER_TO_WORKTREE,+	GIT_FILTER_TO_ODB+} git_filter_mode;++typedef enum {+	GIT_CRLF_GUESS = -1,+	GIT_CRLF_BINARY = 0,+	GIT_CRLF_TEXT,+	GIT_CRLF_INPUT,+	GIT_CRLF_CRLF,+	GIT_CRLF_AUTO,+} git_crlf_t;++typedef struct {+	/* NUL, CR, LF and CRLF counts */+	unsigned int nul, cr, lf, crlf;++	/* These are just approximations! */+	unsigned int printable, nonprintable;+} git_text_stats;++/*+ * FILTER API+ */++/*+ * For any given path in the working directory, fill the `filters`+ * array with the relevant filters that need to be applied.+ *+ * Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the+ * filters that will be used when checking out a file to the working+ * directory, or `GIT_FILTER_TO_ODB` for the filters used when writing+ * a file to the ODB.+ *+ * @param filters Vector where to store all the loaded filters+ * @param repo Repository object that contains `path`+ * @param path Relative path of the file to be filtered+ * @param mode Filtering direction (WT->ODB or ODB->WT)+ * @return the number of filters loaded for the file (0 if the file+ *	doesn't need filtering), or a negative error code+ */+extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);++/*+ * Apply one or more filters to a file.+ *+ * The file must have been loaded as a `git_buf` object. Both the `source`+ * and `dest` buffers are owned by the caller and must be freed once+ * they are no longer needed.+ *+ * NOTE: Because of the double-buffering schema, the `source` buffer that contains+ * the original file may be tampered once the filtering is complete. Regardless, + * the `dest` buffer will always contain the final result of the filtering+ *+ * @param dest Buffer to store the result of the filtering+ * @param source Buffer containing the document to filter+ * @param filters A non-empty vector of filters as supplied by `git_filters_load`+ * @return 0 on success, an error code otherwise+ */+extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);++/*+ * Free the `filters` array generated by `git_filters_load`.+ *+ * Note that this frees both the array and its contents. The array will+ * be clean/reusable after this call.+ *+ * @param filters A filters array as supplied by `git_filters_load`+ */+extern void git_filters_free(git_vector *filters);++/*+ * Available filters+ */++/* Strip CRLF, from Worktree to ODB */+extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);+++/*+ * PLAINTEXT API+ */++/*+ * Gather stats for a piece of text+ *+ * Fill the `stats` structure with information on the number of+ * unreadable characters, carriage returns, etc, so it can be+ * used in heuristics.+ */+extern void git_text_gather_stats(git_text_stats *stats, const git_buf *text);++/*+ * Process `git_text_stats` data generated by `git_text_stat` to see+ * if it qualifies as a binary file+ */+extern int git_text_is_binary(git_text_stats *stats);++#endif
+ libgit2/src/global.c view
@@ -0,0 +1,134 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "global.h"+#include "git2/threads.h" +#include "thread-utils.h"++/**+ * Handle the global state with TLS+ *+ * If libgit2 is built with GIT_THREADS enabled,+ * the `git_threads_init()` function must be called+ * before calling any other function of the library.+ *+ * This function allocates a TLS index (using pthreads+ * or the native Win32 API) to store the global state+ * on a per-thread basis.+ *+ * Any internal method that requires global state will+ * then call `git__global_state()` which returns a pointer+ * to the global state structure; this pointer is lazily+ * allocated on each thread.+ *+ * Before shutting down the library, the+ * `git_threads_shutdown` method must be called to free+ * the previously reserved TLS index.+ *+ * If libgit2 is built without threading support, the+ * `git__global_statestate()` call returns a pointer to a single,+ * statically allocated global state. The `git_thread_`+ * functions are not available in that case.+ */++#if defined(GIT_THREADS) && defined(GIT_WIN32)++static DWORD _tls_index;+static int _tls_init = 0;++void git_threads_init(void)+{+	if (_tls_init)+		return;++	_tls_index = TlsAlloc();+	_tls_init = 1;+}++void git_threads_shutdown(void)+{+	TlsFree(_tls_index);+	_tls_init = 0;+}++git_global_st *git__global_state(void)+{+	void *ptr;++	if ((ptr = TlsGetValue(_tls_index)) != NULL)+		return ptr;++	ptr = git__malloc(sizeof(git_global_st));+	if (!ptr)+		return NULL;++	memset(ptr, 0x0, sizeof(git_global_st));+	TlsSetValue(_tls_index, ptr);+	return ptr;+}++#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)++static pthread_key_t _tls_key;+static int _tls_init = 0;++static void cb__free_status(void *st)+{+	git__free(st);+}++void git_threads_init(void)+{+	if (_tls_init)+		return;++	pthread_key_create(&_tls_key, &cb__free_status);+	_tls_init = 1;+}++void git_threads_shutdown(void)+{+	pthread_key_delete(_tls_key);+	_tls_init = 0;+}++git_global_st *git__global_state(void)+{+	void *ptr;++	if ((ptr = pthread_getspecific(_tls_key)) != NULL)+		return ptr;++	ptr = git__malloc(sizeof(git_global_st));+	if (!ptr)+		return NULL;++	memset(ptr, 0x0, sizeof(git_global_st));+	pthread_setspecific(_tls_key, ptr);+	return ptr;+}++#else++static git_global_st __state;++void git_threads_init(void)+{+	/* noop */ +}++void git_threads_shutdown(void)+{+	/* noop */+}++git_global_st *git__global_state(void)+{+	return &__state;+}++#endif /* GIT_THREADS */
+ libgit2/src/global.h view
@@ -0,0 +1,27 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_global_h__+#define INCLUDE_global_h__++#include "mwindow.h"++typedef struct {+	struct {+		char last[1024];+	} error;++	git_error *last_error;+	git_error error_t;++	git_mwindow_ctl mem_ctl;+} git_global_st;++git_global_st *git__global_state(void);++#define GIT_GLOBAL (git__global_state())++#endif
+ libgit2/src/hash.c view
@@ -0,0 +1,74 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "hash.h"++#if defined(PPC_SHA1)+# include "ppc/sha1.h"+#else+# include "sha1.h"+#endif++struct git_hash_ctx {+	SHA_CTX c;+};++git_hash_ctx *git_hash_new_ctx(void)+{+	git_hash_ctx *ctx = git__malloc(sizeof(*ctx));++	if (!ctx)+		return NULL;++	SHA1_Init(&ctx->c);++	return ctx;+}++void git_hash_free_ctx(git_hash_ctx *ctx)+{+	git__free(ctx);+}++void git_hash_init(git_hash_ctx *ctx)+{+	assert(ctx);+	SHA1_Init(&ctx->c);+}++void git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)+{+	assert(ctx);+	SHA1_Update(&ctx->c, data, len);+}++void git_hash_final(git_oid *out, git_hash_ctx *ctx)+{+	assert(ctx);+	SHA1_Final(out->id, &ctx->c);+}++void git_hash_buf(git_oid *out, const void *data, size_t len)+{+	SHA_CTX c;++	SHA1_Init(&c);+	SHA1_Update(&c, data, len);+	SHA1_Final(out->id, &c);+}++void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n)+{+	SHA_CTX c;+	size_t i;++	SHA1_Init(&c);+	for (i = 0; i < n; i++)+		SHA1_Update(&c, vec[i].data, vec[i].len);+	SHA1_Final(out->id, &c);+}
+ libgit2/src/hash.h view
@@ -0,0 +1,29 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_hash_h__+#define INCLUDE_hash_h__++#include "git2/oid.h"++typedef struct git_hash_ctx git_hash_ctx;++typedef struct {+	void *data;+	size_t len;+} git_buf_vec;++git_hash_ctx *git_hash_new_ctx(void);+void git_hash_free_ctx(git_hash_ctx *ctx);++void git_hash_init(git_hash_ctx *c);+void git_hash_update(git_hash_ctx *c, const void *data, size_t len);+void git_hash_final(git_oid *out, git_hash_ctx *c);++void git_hash_buf(git_oid *out, const void *data, size_t len);+void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n);++#endif /* INCLUDE_hash_h__ */
+ libgit2/src/ignore.c view
@@ -0,0 +1,203 @@+#include "ignore.h"+#include "path.h"++#define GIT_IGNORE_INTERNAL		"[internal]exclude"+#define GIT_IGNORE_FILE_INREPO	"info/exclude"+#define GIT_IGNORE_FILE			".gitignore"++static int parse_ignore_file(+	git_repository *repo, const char *buffer, git_attr_file *ignores)+{+	int error = 0;+	git_attr_fnmatch *match = NULL;+	const char *scan = NULL;+	char *context = NULL;++	GIT_UNUSED(repo);++	if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) {+		context = ignores->key + 2;+		context[strlen(context) - strlen(GIT_IGNORE_FILE)] = '\0';+	}++	scan = buffer;++	while (!error && *scan) {+		if (!match) {+			match = git__calloc(1, sizeof(*match));+			GITERR_CHECK_ALLOC(match);+		}++		if (!(error = git_attr_fnmatch__parse(+			match, ignores->pool, context, &scan)))+		{+			match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE;+			scan = git__next_line(scan);+			error = git_vector_insert(&ignores->rules, match);+		}++		if (error != 0) {+			git__free(match->pattern);+			match->pattern = NULL;++			if (error == GIT_ENOTFOUND)+				error = 0;+		} else {+			match = NULL; /* vector now "owns" the match */+		}+	}++	git__free(match);+	/* restore file path used for context */+	if (context)+		context[strlen(context)] = '.'; /* first char of GIT_IGNORE_FILE */++	return error;+}++#define push_ignore_file(R,S,B,F) \+	git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(S))++static int push_one_ignore(void *ref, git_buf *path)+{+	git_ignores *ign = (git_ignores *)ref;+	return push_ignore_file(ign->repo, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);+}++int git_ignore__for_path(+	git_repository *repo,+	const char *path,+	git_ignores *ignores)+{+	int error = 0;+	const char *workdir = git_repository_workdir(repo);++	assert(ignores);++	ignores->repo = repo;+	git_buf_init(&ignores->dir, 0);+	ignores->ign_internal = NULL;++	if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 ||+		(error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 ||+		(error = git_attr_cache__init(repo)) < 0)+		goto cleanup;++	/* given a unrooted path in a non-bare repo, resolve it */+	if (workdir && git_path_root(path) < 0)+		error = git_path_find_dir(&ignores->dir, path, workdir);+	else+		error = git_buf_sets(&ignores->dir, path);+	if (error < 0)+		goto cleanup;++	/* set up internals */+	error = git_attr_cache__internal_file(+		repo, GIT_IGNORE_INTERNAL, &ignores->ign_internal);+	if (error < 0)+		goto cleanup;++	/* load .gitignore up the path */+	if (workdir != NULL) {+		error = git_path_walk_up(+			&ignores->dir, workdir, push_one_ignore, ignores);+		if (error < 0)+			goto cleanup;+	}++	/* load .git/info/exclude */+	error = push_ignore_file(repo, &ignores->ign_global,+		git_repository_path(repo), GIT_IGNORE_FILE_INREPO);+	if (error < 0)+		goto cleanup;++	/* load core.excludesfile */+	if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)+		error = push_ignore_file(repo, &ignores->ign_global, NULL,+			git_repository_attr_cache(repo)->cfg_excl_file);++cleanup:+	if (error < 0)+		git_ignore__free(ignores);++	return error;+}++int git_ignore__push_dir(git_ignores *ign, const char *dir)+{+	if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)+		return -1;+	else+		return push_ignore_file(+			ign->repo, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);+}++int git_ignore__pop_dir(git_ignores *ign)+{+	if (ign->ign_path.length > 0) {+		git_attr_file *file = git_vector_last(&ign->ign_path);+		if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)+			git_vector_pop(&ign->ign_path);+		git_buf_rtruncate_at_char(&ign->dir, '/');+	}+	return 0;+}++void git_ignore__free(git_ignores *ignores)+{+	/* don't need to free ignores->ign_internal since it is in cache */+	git_vector_free(&ignores->ign_path);+	git_vector_free(&ignores->ign_global);+	git_buf_free(&ignores->dir);+}++static bool ignore_lookup_in_rules(+	git_vector *rules, git_attr_path *path, int *ignored)+{+	unsigned int j;+	git_attr_fnmatch *match;++	git_vector_rforeach(rules, j, match) {+		if (git_attr_fnmatch__match(match, path)) {+			*ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);+			return true;+		}+	}++	return false;+}++int git_ignore__lookup(+	git_ignores *ignores, const char *pathname, int *ignored)+{+	unsigned int i;+	git_attr_file *file;+	git_attr_path path;++	if (git_attr_path__init(+		&path, pathname, git_repository_workdir(ignores->repo)) < 0)+		return -1;++	/* first process builtins - success means path was found */+	if (ignore_lookup_in_rules(+			&ignores->ign_internal->rules, &path, ignored))+		goto cleanup;++	/* next process files in the path */+	git_vector_foreach(&ignores->ign_path, i, file) {+		if (ignore_lookup_in_rules(&file->rules, &path, ignored))+			goto cleanup;+	}++	/* last process global ignores */+	git_vector_foreach(&ignores->ign_global, i, file) {+		if (ignore_lookup_in_rules(&file->rules, &path, ignored))+			goto cleanup;+	}++	*ignored = 0;++cleanup:+	git_attr_path__free(&path);+	return 0;+}
+ libgit2/src/ignore.h view
@@ -0,0 +1,38 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_ignore_h__+#define INCLUDE_ignore_h__++#include "repository.h"+#include "vector.h"++/* The git_ignores structure maintains three sets of ignores:+ * - internal ignores+ * - per directory ignores+ * - global ignores (at lower priority than the others)+ * As you traverse from one directory to another, you can push and pop+ * directories onto git_ignores list efficiently.+ */+typedef struct {+	git_repository *repo;+	git_buf dir;+	git_attr_file *ign_internal;+	git_vector ign_path;+	git_vector ign_global;+} git_ignores;++extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);++extern int git_ignore__push_dir(git_ignores *ign, const char *dir);++extern int git_ignore__pop_dir(git_ignores *ign);++extern void git_ignore__free(git_ignores *ign);++extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored);++#endif
+ libgit2/src/index.c view
@@ -0,0 +1,969 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include <stddef.h>++#include "common.h"+#include "repository.h"+#include "index.h"+#include "tree.h"+#include "tree-cache.h"+#include "hash.h"+#include "git2/odb.h"+#include "git2/blob.h"++#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)+#define short_entry_size(len) entry_size(struct entry_short, len)+#define long_entry_size(len) entry_size(struct entry_long, len)++#define minimal_entry_size (offsetof(struct entry_short, path))++static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;+static const size_t INDEX_HEADER_SIZE = 12;++static const unsigned int INDEX_VERSION_NUMBER = 2;+static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;++static const unsigned int INDEX_HEADER_SIG = 0x44495243;+static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};+static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};++#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx)))++struct index_header {+	uint32_t signature;+	uint32_t version;+	uint32_t entry_count;+};++struct index_extension {+	char signature[4];+	uint32_t extension_size;+};++struct entry_time {+	uint32_t seconds;+	uint32_t nanoseconds;+};++struct entry_short {+	struct entry_time ctime;+	struct entry_time mtime;+	uint32_t dev;+	uint32_t ino;+	uint32_t mode;+	uint32_t uid;+	uint32_t gid;+	uint32_t file_size;+	git_oid oid;+	uint16_t flags;+	char path[1]; /* arbitrary length */+};++struct entry_long {+	struct entry_time ctime;+	struct entry_time mtime;+	uint32_t dev;+	uint32_t ino;+	uint32_t mode;+	uint32_t uid;+	uint32_t gid;+	uint32_t file_size;+	git_oid oid;+	uint16_t flags;+	uint16_t flags_extended;+	char path[1]; /* arbitrary length */+};++/* local declarations */+static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);+static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);+static int read_header(struct index_header *dest, const void *buffer);++static int parse_index(git_index *index, const char *buffer, size_t buffer_size);+static int is_index_extended(git_index *index);+static int write_index(git_index *index, git_filebuf *file);++static void index_entry_free(git_index_entry *entry);++static int index_srch(const void *key, const void *array_member)+{+	const git_index_entry *entry = array_member;++	return strcmp(key, entry->path);+}++static int index_cmp(const void *a, const void *b)+{+	const git_index_entry *entry_a = a;+	const git_index_entry *entry_b = b;++	return strcmp(entry_a->path, entry_b->path);+}++static int unmerged_srch(const void *key, const void *array_member)+{+	const git_index_entry_unmerged *entry = array_member;++	return strcmp(key, entry->path);+}++static int unmerged_cmp(const void *a, const void *b)+{+	const git_index_entry_unmerged *info_a = a;+	const git_index_entry_unmerged *info_b = b;++	return strcmp(info_a->path, info_b->path);+}++static unsigned int index_create_mode(unsigned int mode)+{+	if (S_ISLNK(mode))+		return S_IFLNK;+	if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))+		return (S_IFLNK | S_IFDIR);+	return S_IFREG | ((mode & 0100) ? 0755 : 0644);+}++int git_index_open(git_index **index_out, const char *index_path)+{+	git_index *index;++	assert(index_out && index_path);++	index = git__calloc(1, sizeof(git_index));+	GITERR_CHECK_ALLOC(index);++	index->index_file_path = git__strdup(index_path);+	GITERR_CHECK_ALLOC(index->index_file_path);++	if (git_vector_init(&index->entries, 32, index_cmp) < 0)+		return -1;++	/* Check if index file is stored on disk already */+	if (git_path_exists(index->index_file_path) == true)+		index->on_disk = 1;++	*index_out = index;+	GIT_REFCOUNT_INC(index);+	return git_index_read(index);+}++static void index_free(git_index *index)+{+	git_index_entry *e;+	unsigned int i;++	git_index_clear(index);+	git_vector_foreach(&index->entries, i, e) {+		index_entry_free(e);+	}+	git_vector_free(&index->entries);+	git_vector_foreach(&index->unmerged, i, e) {+		index_entry_free(e);+	}+	git_vector_free(&index->unmerged);++	git__free(index->index_file_path);+	git__free(index);+}++void git_index_free(git_index *index)+{+	if (index == NULL)+		return;++	GIT_REFCOUNT_DEC(index, index_free);+}++void git_index_clear(git_index *index)+{+	unsigned int i;++	assert(index);++	for (i = 0; i < index->entries.length; ++i) {+		git_index_entry *e;+		e = git_vector_get(&index->entries, i);+		git__free(e->path);+		git__free(e);+	}++	for (i = 0; i < index->unmerged.length; ++i) {+		git_index_entry_unmerged *e;+		e = git_vector_get(&index->unmerged, i);+		git__free(e->path);+		git__free(e);+	}++	git_vector_clear(&index->entries);+	git_vector_clear(&index->unmerged);+	index->last_modified = 0;++	git_tree_cache_free(index->tree);+	index->tree = NULL;+}++int git_index_read(git_index *index)+{+	int error, updated;+	git_buf buffer = GIT_BUF_INIT;+	time_t mtime;++	assert(index->index_file_path);++	if (!index->on_disk || git_path_exists(index->index_file_path) == false) {+		git_index_clear(index);+		index->on_disk = 0;+		return 0;+	}++	/* We don't want to update the mtime if we fail to parse the index */+	mtime = index->last_modified;+	error = git_futils_readbuffer_updated(+		&buffer, index->index_file_path, &mtime, &updated);+	if (error < 0)+		return error;++	if (updated) {+		git_index_clear(index);+		error = parse_index(index, buffer.ptr, buffer.size);++		if (!error)+			index->last_modified = mtime;++		git_buf_free(&buffer);+	}++	return error;+}++int git_index_write(git_index *index)+{+	git_filebuf file = GIT_FILEBUF_INIT;+	struct stat indexst;+	int error;++	git_vector_sort(&index->entries);++	if ((error = git_filebuf_open(+			 &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)+		return error;++	if ((error = write_index(index, &file)) < 0) {+		git_filebuf_cleanup(&file);+		return error;+	}++	if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < 0)+		return error;++	if (p_stat(index->index_file_path, &indexst) == 0) {+		index->last_modified = indexst.st_mtime;+		index->on_disk = 1;+	}++	return 0;+}++unsigned int git_index_entrycount(git_index *index)+{+	assert(index);+	return index->entries.length;+}++unsigned int git_index_entrycount_unmerged(git_index *index)+{+	assert(index);+	return index->unmerged.length;+}++git_index_entry *git_index_get(git_index *index, unsigned int n)+{+	git_vector_sort(&index->entries);+	return git_vector_get(&index->entries, n);+}++void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)+{+	entry->ctime.seconds = (git_time_t)st->st_ctime;+	entry->mtime.seconds = (git_time_t)st->st_mtime;+	/* entry->mtime.nanoseconds = st->st_mtimensec; */+	/* entry->ctime.nanoseconds = st->st_ctimensec; */+	entry->dev  = st->st_rdev;+	entry->ino  = st->st_ino;+	entry->mode = index_create_mode(st->st_mode);+	entry->uid  = st->st_uid;+	entry->gid  = st->st_gid;+	entry->file_size = st->st_size;+}++static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path, int stage)+{+	git_index_entry *entry = NULL;+	struct stat st;+	git_oid oid;+	const char *workdir;+	git_buf full_path = GIT_BUF_INIT;+	int error;++	assert(stage >= 0 && stage <= 3);++	if (INDEX_OWNER(index) == NULL ||+		(workdir = git_repository_workdir(INDEX_OWNER(index))) == NULL)+	{+		giterr_set(GITERR_INDEX,+			"Could not initialize index entry. Repository is bare");+		return -1;+	}++	if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)+		return error;++	if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {+		git_buf_free(&full_path);+		return error;+	}++	git_buf_free(&full_path); /* done with full path */++	/* There is no need to validate the rel_path here, since it will be+	 * immediately validated by the call to git_blob_create_fromfile.+	 */++	/* write the blob to disk and get the oid */+	if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < 0)+		return error;++	entry = git__calloc(1, sizeof(git_index_entry));+	GITERR_CHECK_ALLOC(entry);++	git_index__init_entry_from_stat(&st, entry);++	entry->oid = oid;+	entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT);+	entry->path = git__strdup(rel_path);+	GITERR_CHECK_ALLOC(entry->path);++	*entry_out = entry;+	return 0;+}++static git_index_entry *index_entry_dup(const git_index_entry *source_entry)+{+	git_index_entry *entry;++	entry = git__malloc(sizeof(git_index_entry));+	if (!entry)+		return NULL;++	memcpy(entry, source_entry, sizeof(git_index_entry));++	/* duplicate the path string so we own it */+	entry->path = git__strdup(entry->path);+	if (!entry->path)+		return NULL;++	return entry;+}++static void index_entry_free(git_index_entry *entry)+{+	if (!entry)+		return;+	git__free(entry->path);+	git__free(entry);+}++static int index_insert(git_index *index, git_index_entry *entry, int replace)+{+	size_t path_length;+	int position;+	git_index_entry **entry_array;++	assert(index && entry && entry->path != NULL);++	/* make sure that the path length flag is correct */+	path_length = strlen(entry->path);++	entry->flags &= ~GIT_IDXENTRY_NAMEMASK;++	if (path_length < GIT_IDXENTRY_NAMEMASK)+		entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;+	else+		entry->flags |= GIT_IDXENTRY_NAMEMASK;;++	/*+	 * replacing is not requested: just insert entry at the end;+	 * the index is no longer sorted+	 */+	if (!replace)+		return git_vector_insert(&index->entries, entry);++	/* look if an entry with this path already exists */+	position = git_index_find(index, entry->path);++	/*+	 * if no entry exists add the entry at the end;+	 * the index is no longer sorted+	 */+	if (position == GIT_ENOTFOUND)+		return git_vector_insert(&index->entries, entry);++	/* exists, replace it */+	entry_array = (git_index_entry **) index->entries.contents;+	git__free(entry_array[position]->path);+	git__free(entry_array[position]);+	entry_array[position] = entry;++	return 0;+}++static int index_add(git_index *index, const char *path, int stage, int replace)+{+	git_index_entry *entry = NULL;+	int ret;++	if ((ret = index_entry_init(&entry, index, path, stage)) < 0 ||+		(ret = index_insert(index, entry, replace)) < 0)+	{+		index_entry_free(entry);+		return ret;+	}++	git_tree_cache_invalidate_path(index->tree, entry->path);+	return 0;+}++int git_index_add(git_index *index, const char *path, int stage)+{+	return index_add(index, path, stage, 1);+}++int git_index_append(git_index *index, const char *path, int stage)+{+	return index_add(index, path, stage, 0);+}++static int index_add2(+	git_index *index, const git_index_entry *source_entry, int replace)+{+	git_index_entry *entry = NULL;+	int ret;++	entry = index_entry_dup(source_entry);+	if (entry == NULL)+		return -1;++	if ((ret = index_insert(index, entry, replace)) < 0) {+		index_entry_free(entry);+		return ret;+	}++	git_tree_cache_invalidate_path(index->tree, entry->path);+	return 0;+}++int git_index_add2(git_index *index, const git_index_entry *source_entry)+{+	return index_add2(index, source_entry, 1);+}++int git_index_append2(git_index *index, const git_index_entry *source_entry)+{+	return index_add2(index, source_entry, 1);+}++int git_index_remove(git_index *index, int position)+{+	int error;+	git_index_entry *entry;++	git_vector_sort(&index->entries);++	entry = git_vector_get(&index->entries, position);+	if (entry != NULL)+		git_tree_cache_invalidate_path(index->tree, entry->path);++	error = git_vector_remove(&index->entries, (unsigned int)position);++	if (!error)+		index_entry_free(entry);++	return error;+}++int git_index_find(git_index *index, const char *path)+{+	return git_vector_bsearch2(&index->entries, index_srch, path);+}++unsigned int git_index__prefix_position(git_index *index, const char *path)+{+	unsigned int pos;++	git_vector_bsearch3(&pos, &index->entries, index_srch, path);++	return pos;+}++void git_index_uniq(git_index *index)+{+	git_vector_uniq(&index->entries);+}++const git_index_entry_unmerged *git_index_get_unmerged_bypath(+	git_index *index, const char *path)+{+	int pos;+	assert(index && path);++	if (!index->unmerged.length)+		return NULL;++	if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < 0)+		return NULL;++	return git_vector_get(&index->unmerged, pos);+}++const git_index_entry_unmerged *git_index_get_unmerged_byindex(+	git_index *index, unsigned int n)+{+	assert(index);+	return git_vector_get(&index->unmerged, n);+}++static int index_error_invalid(const char *message)+{+	giterr_set(GITERR_INDEX, "Invalid data in index - %s", message);+	return -1;+}++static int read_unmerged(git_index *index, const char *buffer, size_t size)+{+	const char *endptr;+	size_t len;+	int i;++	if (git_vector_init(&index->unmerged, 16, unmerged_cmp) < 0)+		return -1;++	while (size) {+		git_index_entry_unmerged *lost;++		len = strlen(buffer) + 1;+		if (size <= len)+			return index_error_invalid("reading unmerged entries");++		lost = git__malloc(sizeof(git_index_entry_unmerged));+		GITERR_CHECK_ALLOC(lost);++		if (git_vector_insert(&index->unmerged, lost) < 0)+			return -1;++		/* read NUL-terminated pathname for entry */+		lost->path = git__strdup(buffer);+		GITERR_CHECK_ALLOC(lost->path);++		size -= len;+		buffer += len;++		/* read 3 ASCII octal numbers for stage entries */+		for (i = 0; i < 3; i++) {+			int tmp;++			if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||+				!endptr || endptr == buffer || *endptr ||+				(unsigned)tmp > UINT_MAX)+				return index_error_invalid("reading unmerged entry stage");++			lost->mode[i] = tmp;++			len = (endptr + 1) - buffer;+			if (size <= len)+				return index_error_invalid("reading unmerged entry stage");++			size -= len;+			buffer += len;+		}++		/* read up to 3 OIDs for stage entries */+		for (i = 0; i < 3; i++) {+			if (!lost->mode[i])+				continue;+			if (size < 20)+				return index_error_invalid("reading unmerged entry oid");++			git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);+			size -= 20;+			buffer += 20;+		}+	}++	return 0;+}++static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)+{+	size_t path_length, entry_size;+	uint16_t flags_raw;+	const char *path_ptr;+	const struct entry_short *source = buffer;++	if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)+		return 0;++	memset(dest, 0x0, sizeof(git_index_entry));++	dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);+	dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds);+	dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);+	dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds);+	dest->dev = ntohl(source->dev);+	dest->ino = ntohl(source->ino);+	dest->mode = ntohl(source->mode);+	dest->uid = ntohl(source->uid);+	dest->gid = ntohl(source->gid);+	dest->file_size = ntohl(source->file_size);+	git_oid_cpy(&dest->oid, &source->oid);+	dest->flags = ntohs(source->flags);++	if (dest->flags & GIT_IDXENTRY_EXTENDED) {+		const struct entry_long *source_l = (const struct entry_long *)source;+		path_ptr = source_l->path;++		flags_raw = ntohs(source_l->flags_extended);+		memcpy(&dest->flags_extended, &flags_raw, 2);+	} else+		path_ptr = source->path;++	path_length = dest->flags & GIT_IDXENTRY_NAMEMASK;++	/* if this is a very long string, we must find its+	 * real length without overflowing */+	if (path_length == 0xFFF) {+		const char *path_end;++		path_end = memchr(path_ptr, '\0', buffer_size);+		if (path_end == NULL)+			return 0;++		path_length = path_end - path_ptr;+	}++	if (dest->flags & GIT_IDXENTRY_EXTENDED)+		entry_size = long_entry_size(path_length);+	else+		entry_size = short_entry_size(path_length);++	if (INDEX_FOOTER_SIZE + entry_size > buffer_size)+		return 0;++	dest->path = git__strdup(path_ptr);+	assert(dest->path);++	return entry_size;+}++static int read_header(struct index_header *dest, const void *buffer)+{+	const struct index_header *source = buffer;++	dest->signature = ntohl(source->signature);+	if (dest->signature != INDEX_HEADER_SIG)+		return index_error_invalid("incorrect header signature");++	dest->version = ntohl(source->version);+	if (dest->version != INDEX_VERSION_NUMBER_EXT &&+		dest->version != INDEX_VERSION_NUMBER)+		return index_error_invalid("incorrect header version");++	dest->entry_count = ntohl(source->entry_count);+	return 0;+}++static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)+{+	const struct index_extension *source;+	struct index_extension dest;+	size_t total_size;++	source = (const struct index_extension *)(buffer);++	memcpy(dest.signature, source->signature, 4);+	dest.extension_size = ntohl(source->extension_size);++	total_size = dest.extension_size + sizeof(struct index_extension);++	if (buffer_size - total_size < INDEX_FOOTER_SIZE)+		return 0;++	/* optional extension */+	if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {+		/* tree cache */+		if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {+			if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < 0)+				return 0;+		} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {+			if (read_unmerged(index, buffer + 8, dest.extension_size) < 0)+				return 0;+		}+		/* else, unsupported extension. We cannot parse this, but we can skip+		 * it by returning `total_size */+	} else {+		/* we cannot handle non-ignorable extensions;+		 * in fact they aren't even defined in the standard */+		return 0;+	}++	return total_size;+}++static int parse_index(git_index *index, const char *buffer, size_t buffer_size)+{+	unsigned int i;+	struct index_header header;+	git_oid checksum_calculated, checksum_expected;++#define seek_forward(_increase) { \+	if (_increase >= buffer_size) \+		return index_error_invalid("ran out of data while parsing"); \+	buffer += _increase; \+	buffer_size -= _increase;\+}++	if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)+		return index_error_invalid("insufficient buffer space");++	/* Precalculate the SHA1 of the files's contents -- we'll match it to+	 * the provided SHA1 in the footer */+	git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE);++	/* Parse header */+	if (read_header(&header, buffer) < 0)+		return -1;++	seek_forward(INDEX_HEADER_SIZE);++	git_vector_clear(&index->entries);++	/* Parse all the entries */+	for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {+		size_t entry_size;+		git_index_entry *entry;++		entry = git__malloc(sizeof(git_index_entry));+		GITERR_CHECK_ALLOC(entry);++		entry_size = read_entry(entry, buffer, buffer_size);++		/* 0 bytes read means an object corruption */+		if (entry_size == 0)+			return index_error_invalid("invalid entry");++		if (git_vector_insert(&index->entries, entry) < 0)+			return -1;++		seek_forward(entry_size);+	}++	if (i != header.entry_count)+		return index_error_invalid("header entries changed while parsing");++	/* There's still space for some extensions! */+	while (buffer_size > INDEX_FOOTER_SIZE) {+		size_t extension_size;++		extension_size = read_extension(index, buffer, buffer_size);++		/* see if we have read any bytes from the extension */+		if (extension_size == 0)+			return index_error_invalid("extension size is zero");++		seek_forward(extension_size);+	}++	if (buffer_size != INDEX_FOOTER_SIZE)+		return index_error_invalid("buffer size does not match index footer size");++	/* 160-bit SHA-1 over the content of the index file before this checksum. */+	git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);++	if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)+		return index_error_invalid("calculated checksum does not match expected");++#undef seek_forward++	/* force sorting in the vector: the entries are+	 * assured to be sorted on the index */+	index->entries.sorted = 1;+	return 0;+}++static int is_index_extended(git_index *index)+{+	unsigned int i, extended;+	git_index_entry *entry;++	extended = 0;++	git_vector_foreach(&index->entries, i, entry) {+		entry->flags &= ~GIT_IDXENTRY_EXTENDED;+		if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {+			extended++;+			entry->flags |= GIT_IDXENTRY_EXTENDED;+		}+	}++	return extended;+}++static int write_disk_entry(git_filebuf *file, git_index_entry *entry)+{+	void *mem = NULL;+	struct entry_short *ondisk;+	size_t path_len, disk_size;+	char *path;++	path_len = strlen(entry->path);++	if (entry->flags & GIT_IDXENTRY_EXTENDED)+		disk_size = long_entry_size(path_len);+	else+		disk_size = short_entry_size(path_len);++	if (git_filebuf_reserve(file, &mem, disk_size) < 0)+		return -1;++	ondisk = (struct entry_short *)mem;++	memset(ondisk, 0x0, disk_size);++	/**+	 * Yes, we have to truncate.+	 *+	 * The on-disk format for Index entries clearly defines+	 * the time and size fields to be 4 bytes each -- so even if+	 * we store these values with 8 bytes on-memory, they must+	 * be truncated to 4 bytes before writing to disk.+	 *+	 * In 2038 I will be either too dead or too rich to care about this+	 */+	ondisk->ctime.seconds = htonl((uint32_t)entry->ctime.seconds);+	ondisk->mtime.seconds = htonl((uint32_t)entry->mtime.seconds);+	ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);+	ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);+	ondisk->dev = htonl(entry->dev);+	ondisk->ino = htonl(entry->ino);+	ondisk->mode = htonl(entry->mode);+	ondisk->uid = htonl(entry->uid);+	ondisk->gid = htonl(entry->gid);+	ondisk->file_size = htonl((uint32_t)entry->file_size);++	git_oid_cpy(&ondisk->oid, &entry->oid);++	ondisk->flags = htons(entry->flags);++	if (entry->flags & GIT_IDXENTRY_EXTENDED) {+		struct entry_long *ondisk_ext;+		ondisk_ext = (struct entry_long *)ondisk;+		ondisk_ext->flags_extended = htons(entry->flags_extended);+		path = ondisk_ext->path;+	}+	else+		path = ondisk->path;++	memcpy(path, entry->path, path_len);++	return 0;+}++static int write_entries(git_index *index, git_filebuf *file)+{+	unsigned int i;++	for (i = 0; i < index->entries.length; ++i) {+		git_index_entry *entry;+		entry = git_vector_get(&index->entries, i);+		if (write_disk_entry(file, entry) < 0)+			return -1;+	}++	return 0;+}++static int write_index(git_index *index, git_filebuf *file)+{+	git_oid hash_final;++	struct index_header header;++	int is_extended;++	assert(index && file);++	is_extended = is_index_extended(index);++	header.signature = htonl(INDEX_HEADER_SIG);+	header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER);+	header.entry_count = htonl(index->entries.length);++	if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0)+		return -1;++	if (write_entries(index, file) < 0)+		return -1;++	/* TODO: write extensions (tree cache) */++	/* get out the hash for all the contents we've appended to the file */+	git_filebuf_hash(&hash_final, file);++	/* write it at the end of the file */+	return git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);+}++int git_index_entry_stage(const git_index_entry *entry)+{+	return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;+}++static int read_tree_cb(const char *root, git_tree_entry *tentry, void *data)+{+	git_index *index = data;+	git_index_entry *entry = NULL;+	git_buf path = GIT_BUF_INIT;++	if (git_tree_entry__is_tree(tentry))+		return 0;++	if (git_buf_joinpath(&path, root, tentry->filename) < 0)+		return -1;++	entry = git__calloc(1, sizeof(git_index_entry));+	GITERR_CHECK_ALLOC(entry);++	entry->mode = tentry->attr;+	entry->oid = tentry->oid;+	entry->path = git_buf_detach(&path);+	git_buf_free(&path);++	if (index_insert(index, entry, 0) < 0) {+		index_entry_free(entry);+		return -1;+	}++	return 0;+}++int git_index_read_tree(git_index *index, git_tree *tree)+{+	git_index_clear(index);++	return git_tree_walk(tree, read_tree_cb, GIT_TREEWALK_POST, index);+}
+ libgit2/src/index.h view
@@ -0,0 +1,38 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_index_h__+#define INCLUDE_index_h__++#include "fileops.h"+#include "filebuf.h"+#include "vector.h"+#include "tree-cache.h"+#include "git2/odb.h"+#include "git2/index.h"++#define GIT_INDEX_FILE "index"+#define GIT_INDEX_FILE_MODE 0666++struct git_index {+	git_refcount rc;++	char *index_file_path;++	time_t last_modified;+	git_vector entries;++	unsigned int on_disk:1;+	git_tree_cache *tree;++	git_vector unmerged;+};++extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);++extern unsigned int git_index__prefix_position(git_index *index, const char *path);++#endif
+ libgit2/src/indexer.c view
@@ -0,0 +1,899 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include <zlib.h>++#include "git2/indexer.h"+#include "git2/object.h"+#include "git2/oid.h"++#include "common.h"+#include "pack.h"+#include "mwindow.h"+#include "posix.h"+#include "pack.h"+#include "filebuf.h"+#include "sha1.h"++#define UINT31_MAX (0x7FFFFFFF)++struct entry {+	git_oid oid;+	uint32_t crc;+	uint32_t offset;+	uint64_t offset_long;+};++struct git_indexer {+	struct git_pack_file *pack;+	size_t nr_objects;+	git_vector objects;+	git_filebuf file;+	unsigned int fanout[256];+	git_oid hash;+};++struct git_indexer_stream {+	unsigned int parsed_header :1,+		opened_pack;+	struct git_pack_file *pack;+	git_filebuf pack_file;+	git_filebuf index_file;+	git_off_t off;+	size_t nr_objects;+	git_vector objects;+	git_vector deltas;+	unsigned int fanout[256];+	git_oid hash;+};++struct delta_info {+	git_off_t delta_off;+};++const git_oid *git_indexer_hash(git_indexer *idx)+{+	return &idx->hash;+}++const git_oid *git_indexer_stream_hash(git_indexer_stream *idx)+{+	return &idx->hash;+}++static int open_pack(struct git_pack_file **out, const char *filename)+{+	size_t namelen;+	struct git_pack_file *pack;+	struct stat st;+	int fd;++	namelen = strlen(filename);+	pack = git__calloc(1, sizeof(struct git_pack_file) + namelen + 1);+	GITERR_CHECK_ALLOC(pack);++	memcpy(pack->pack_name, filename, namelen + 1);++	if (p_stat(filename, &st) < 0) {+		giterr_set(GITERR_OS, "Failed to stat packfile.");+		goto cleanup;+	}++	if ((fd = p_open(pack->pack_name, O_RDONLY)) < 0) {+		giterr_set(GITERR_OS, "Failed to open packfile.");+		goto cleanup;+	}++	pack->mwf.fd = fd;+	pack->mwf.size = (git_off_t)st.st_size;++	*out = pack;+	return 0;++cleanup:+	git__free(pack);+	return -1;+}++static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)+{+	int error;++	/* Verify we recognize this pack file format. */+	if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) {+		giterr_set(GITERR_OS, "Failed to read in pack header");+		return error;+	}++	if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {+		giterr_set(GITERR_INDEXER, "Wrong pack signature");+		return -1;+	}++	if (!pack_version_ok(hdr->hdr_version)) {+		giterr_set(GITERR_INDEXER, "Wrong pack version");+		return -1;+	}++	return 0;+}++static int objects_cmp(const void *a, const void *b)+{+	const struct entry *entrya = a;+	const struct entry *entryb = b;++	return git_oid_cmp(&entrya->oid, &entryb->oid);+}++static int cache_cmp(const void *a, const void *b)+{+	const struct git_pack_entry *ea = a;+	const struct git_pack_entry *eb = b;++	return git_oid_cmp(&ea->sha1, &eb->sha1);+}++int git_indexer_stream_new(git_indexer_stream **out, const char *prefix)+{+	git_indexer_stream *idx;+	git_buf path = GIT_BUF_INIT;+	static const char suff[] = "/objects/pack/pack-received";+	int error;++	idx = git__calloc(1, sizeof(git_indexer_stream));+	GITERR_CHECK_ALLOC(idx);++	error = git_buf_joinpath(&path, prefix, suff);+	if (error < 0)+		goto cleanup;++	error = git_filebuf_open(&idx->pack_file, path.ptr,+				 GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER);+	git_buf_free(&path);+	if (error < 0)+		goto cleanup;++	*out = idx;+	return 0;++cleanup:+	git_buf_free(&path);+	git_filebuf_cleanup(&idx->pack_file);+	git__free(idx);+	return -1;+}++/* Try to store the delta so we can try to resolve it later */+static int store_delta(git_indexer_stream *idx)+{+	git_otype type;+	git_mwindow *w = NULL;+	git_mwindow_file *mwf = &idx->pack->mwf;+	git_off_t entry_start = idx->off;+	struct delta_info *delta;+	size_t entry_size;+	git_rawobj obj;+	int error;++	/*+	 * ref-delta objects can refer to object that we haven't+	 * found yet, so give it another opportunity+	 */+	if (git_packfile_unpack_header(&entry_size, &type, mwf, &w, &idx->off) < 0)+		return -1;++	git_mwindow_close(&w);++	/* If it's not a delta, mark it as failure, we can't do anything with it */+	if (type != GIT_OBJ_REF_DELTA && type != GIT_OBJ_OFS_DELTA)+		return -1;++	if (type == GIT_OBJ_REF_DELTA) {+		idx->off += GIT_OID_RAWSZ;+	} else {+		git_off_t base_off;++		base_off = get_delta_base(idx->pack, &w, &idx->off, type, entry_start);+		git_mwindow_close(&w);+		if (base_off < 0)+			return (int)base_off;+	}++	error = packfile_unpack_compressed(&obj, idx->pack, &w, &idx->off, entry_size, type);+	if (error == GIT_EBUFS) {+		idx->off = entry_start;+		return GIT_EBUFS;+	} else if (error < 0){+		return -1;+	}++	delta = git__calloc(1, sizeof(struct delta_info));+	GITERR_CHECK_ALLOC(delta);+	delta->delta_off = entry_start;++	git__free(obj.data);++	if (git_vector_insert(&idx->deltas, delta) < 0)+		return -1;++	return 0;+}++static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)+{+	int i;+	git_oid oid;+	void *packed;+	size_t entry_size;+	unsigned int left;+	struct entry *entry;+	git_mwindow *w = NULL;+	git_mwindow_file *mwf = &idx->pack->mwf;+	struct git_pack_entry *pentry;++	entry = git__calloc(1, sizeof(*entry));+	GITERR_CHECK_ALLOC(entry);++	if (entry_start > UINT31_MAX) {+		entry->offset = UINT32_MAX;+		entry->offset_long = entry_start;+	} else {+		entry->offset = (uint32_t)entry_start;+	}++	/* FIXME: Parse the object instead of hashing it */+	if (git_odb__hashobj(&oid, obj) < 0) {+		giterr_set(GITERR_INDEXER, "Failed to hash object");+		return -1;+	}++	pentry = git__malloc(sizeof(struct git_pack_entry));+	GITERR_CHECK_ALLOC(pentry);++	git_oid_cpy(&pentry->sha1, &oid);+	pentry->offset = entry_start;+	if (git_vector_insert(&idx->pack->cache, pentry) < 0)+		goto on_error;++	git_oid_cpy(&entry->oid, &oid);+	entry->crc = crc32(0L, Z_NULL, 0);++	entry_size = (size_t)(idx->off - entry_start);+	packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left);+	if (packed == NULL)+		goto on_error;++	entry->crc = htonl(crc32(entry->crc, packed, (uInt)entry_size));+	git_mwindow_close(&w);++	/* Add the object to the list */+	if (git_vector_insert(&idx->objects, entry) < 0)+		goto on_error;++	for (i = oid.id[0]; i < 256; ++i) {+		idx->fanout[i]++;+	}++	return 0;++on_error:+	git__free(entry);+	git__free(pentry);+	git__free(obj->data);+	return -1;+}++int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats)+{+	int error;+	struct git_pack_header hdr;+	size_t processed = stats->processed;+	git_mwindow_file *mwf = &idx->pack->mwf;++	assert(idx && data && stats);++	if (git_filebuf_write(&idx->pack_file, data, size) < 0)+		return -1;++	/* Make sure we set the new size of the pack */+	if (idx->opened_pack) {+		idx->pack->mwf.size += size;+		//printf("\nadding %zu for %zu\n", size, idx->pack->mwf.size);+	} else {+		if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0)+			return -1;+		idx->opened_pack = 1;+		mwf = &idx->pack->mwf;+		if (git_mwindow_file_register(&idx->pack->mwf) < 0)+			return -1;++		return 0;+	}++	if (!idx->parsed_header) {+		if ((unsigned)idx->pack->mwf.size < sizeof(hdr))+			return 0;++		if (parse_header(&hdr, idx->pack) < 0)+			return -1;++		idx->parsed_header = 1;+		idx->nr_objects = ntohl(hdr.hdr_entries);+		idx->off = sizeof(struct git_pack_header);++		/* for now, limit to 2^32 objects */+		assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));++		if (git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp) < 0)+			return -1;++		idx->pack->has_cache = 1;+		if (git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp) < 0)+			return -1;++		if (git_vector_init(&idx->deltas, (unsigned int)(idx->nr_objects / 2), NULL) < 0)+			return -1;++		stats->total = (unsigned int)idx->nr_objects;+		stats->processed = 0;+	}++	/* Now that we have data in the pack, let's try to parse it */++	/* As the file grows any windows we try to use will be out of date */+	git_mwindow_free_all(mwf);+	while (processed < idx->nr_objects) {+		git_rawobj obj;+		git_off_t entry_start = idx->off;++		if (idx->pack->mwf.size <= idx->off + 20)+			return 0;++		error = git_packfile_unpack(&obj, idx->pack, &idx->off);+		if (error == GIT_EBUFS) {+			idx->off = entry_start;+			return 0;+		}++		if (error < 0) {+			idx->off = entry_start;+			error = store_delta(idx);+			if (error == GIT_EBUFS)+				return 0;+			if (error < 0)+				return error;++			continue;+		}++		if (hash_and_save(idx, &obj, entry_start) < 0)+			goto on_error;++		git__free(obj.data);++		stats->processed = (unsigned int)++processed;+	}++	return 0;++on_error:+	git_mwindow_free_all(mwf);+	return -1;+}++static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char *suffix)+{+	const char prefix[] = "pack-";+	size_t slash = (size_t)path->size;++	/* search backwards for '/' */+	while (slash > 0 && path->ptr[slash - 1] != '/')+		slash--;++	if (git_buf_grow(path, slash + 1 + strlen(prefix) ++					 GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)+		return -1;++	git_buf_truncate(path, slash);+	git_buf_puts(path, prefix);+	git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);+	path->size += GIT_OID_HEXSZ;+	git_buf_puts(path, suffix);++	return git_buf_oom(path) ? -1 : 0;+}++static int resolve_deltas(git_indexer_stream *idx, git_indexer_stats *stats)+{+	unsigned int i;+	struct delta_info *delta;++	git_vector_foreach(&idx->deltas, i, delta) {+		git_rawobj obj;++		idx->off = delta->delta_off;+		if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)+			return -1;++		if (hash_and_save(idx, &obj, delta->delta_off) < 0)+			return -1;++		git__free(obj.data);+		stats->processed++;+	}++	return 0;+}++int git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats)+{+	git_mwindow *w = NULL;+	unsigned int i, long_offsets = 0, left;+	struct git_pack_idx_header hdr;+	git_buf filename = GIT_BUF_INIT;+	struct entry *entry;+	void *packfile_hash;+	git_oid file_hash;+	SHA_CTX ctx;++	/* Test for this before resolve_deltas(), as it plays with idx->off */+	if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) {+		giterr_set(GITERR_INDEXER, "Indexing error: junk at the end of the pack");+		return -1;+	}++	if (idx->deltas.length > 0)+		if (resolve_deltas(idx, stats) < 0)+			return -1;++	if (stats->processed != stats->total) {+		giterr_set(GITERR_INDEXER, "Indexing error: early EOF");+		return -1;+	}++	git_vector_sort(&idx->objects);++	git_buf_sets(&filename, idx->pack->pack_name);+	git_buf_truncate(&filename, filename.size - strlen("pack"));+	git_buf_puts(&filename, "idx");+	if (git_buf_oom(&filename))+		return -1;++	if (git_filebuf_open(&idx->index_file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS) < 0)+		goto on_error;++	/* Write out the header */+	hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);+	hdr.idx_version = htonl(2);+	git_filebuf_write(&idx->index_file, &hdr, sizeof(hdr));++	/* Write out the fanout table */+	for (i = 0; i < 256; ++i) {+		uint32_t n = htonl(idx->fanout[i]);+		git_filebuf_write(&idx->index_file, &n, sizeof(n));+	}++	/* Write out the object names (SHA-1 hashes) */+	SHA1_Init(&ctx);+	git_vector_foreach(&idx->objects, i, entry) {+		git_filebuf_write(&idx->index_file, &entry->oid, sizeof(git_oid));+		SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ);+	}+	SHA1_Final(idx->hash.id, &ctx);++	/* Write out the CRC32 values */+	git_vector_foreach(&idx->objects, i, entry) {+		git_filebuf_write(&idx->index_file, &entry->crc, sizeof(uint32_t));+	}++	/* Write out the offsets */+	git_vector_foreach(&idx->objects, i, entry) {+		uint32_t n;++		if (entry->offset == UINT32_MAX)+			n = htonl(0x80000000 | long_offsets++);+		else+			n = htonl(entry->offset);++		git_filebuf_write(&idx->index_file, &n, sizeof(uint32_t));+	}++	/* Write out the long offsets */+	git_vector_foreach(&idx->objects, i, entry) {+		uint32_t split[2];++		if (entry->offset != UINT32_MAX)+			continue;++		split[0] = htonl(entry->offset_long >> 32);+		split[1] = htonl(entry->offset_long & 0xffffffff);++		git_filebuf_write(&idx->index_file, &split, sizeof(uint32_t) * 2);+	}++	/* Write out the packfile trailer */+	packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);+	if (packfile_hash == NULL) {+		git_mwindow_close(&w);+		goto on_error;+	}++	memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);+	git_mwindow_close(&w);++	git_filebuf_write(&idx->index_file, &file_hash, sizeof(git_oid));++	/* Write out the packfile trailer to the idx file as well */+	if (git_filebuf_hash(&file_hash, &idx->index_file) < 0)+		goto on_error;++	git_filebuf_write(&idx->index_file, &file_hash, sizeof(git_oid));++	/* Figure out what the final name should be */+	if (index_path_stream(&filename, idx, ".idx") < 0)+		goto on_error;++	/* Commit file */+	if (git_filebuf_commit_at(&idx->index_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)+		goto on_error;++	git_mwindow_free_all(&idx->pack->mwf);+	p_close(idx->pack->mwf.fd);++	if (index_path_stream(&filename, idx, ".pack") < 0)+		goto on_error;+	/* And don't forget to rename the packfile to its new place. */+	if (git_filebuf_commit_at(&idx->pack_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)+		return -1;++	git_buf_free(&filename);+	return 0;++on_error:+	git_mwindow_free_all(&idx->pack->mwf);+	p_close(idx->pack->mwf.fd);+	git_filebuf_cleanup(&idx->index_file);+	git_buf_free(&filename);+	return -1;+}++void git_indexer_stream_free(git_indexer_stream *idx)+{+	unsigned int i;+	struct entry *e;+	struct git_pack_entry *pe;+	struct delta_info *delta;++	if (idx == NULL)+		return;++	git_vector_foreach(&idx->objects, i, e)+		git__free(e);+	git_vector_free(&idx->objects);+	git_vector_foreach(&idx->pack->cache, i, pe)+		git__free(pe);+	git_vector_free(&idx->pack->cache);+	git_vector_foreach(&idx->deltas, i, delta)+		git__free(delta);+	git_vector_free(&idx->deltas);+	git__free(idx->pack);+	git__free(idx);+}++int git_indexer_new(git_indexer **out, const char *packname)+{+	git_indexer *idx;+	struct git_pack_header hdr;+	int error;++	assert(out && packname);++	if (git_path_root(packname) < 0) {+		giterr_set(GITERR_INDEXER, "Path is not absolute");+		return -1;+	}++	idx = git__calloc(1, sizeof(git_indexer));+	GITERR_CHECK_ALLOC(idx);++	open_pack(&idx->pack, packname);++	if ((error = parse_header(&hdr, idx->pack)) < 0)+		goto cleanup;++	idx->nr_objects = ntohl(hdr.hdr_entries);++	/* for now, limit to 2^32 objects */+	assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));++	error = git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp);+	if (error < 0)+		goto cleanup;++	idx->pack->has_cache = 1;+	error = git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp);+	if (error < 0)+		goto cleanup;++	*out = idx;++	return 0;++cleanup:+	git_indexer_free(idx);++	return -1;+}++static int index_path(git_buf *path, git_indexer *idx)+{+	const char prefix[] = "pack-", suffix[] = ".idx";+	size_t slash = (size_t)path->size;++	/* search backwards for '/' */+	while (slash > 0 && path->ptr[slash - 1] != '/')+		slash--;++	if (git_buf_grow(path, slash + 1 + strlen(prefix) ++					 GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)+		return -1;++	git_buf_truncate(path, slash);+	git_buf_puts(path, prefix);+	git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);+	path->size += GIT_OID_HEXSZ;+	git_buf_puts(path, suffix);++	return git_buf_oom(path) ? -1 : 0;+}++int git_indexer_write(git_indexer *idx)+{+	git_mwindow *w = NULL;+	int error;+	unsigned int i, long_offsets = 0, left;+	struct git_pack_idx_header hdr;+	git_buf filename = GIT_BUF_INIT;+	struct entry *entry;+	void *packfile_hash;+	git_oid file_hash;+	SHA_CTX ctx;++	git_vector_sort(&idx->objects);++	git_buf_sets(&filename, idx->pack->pack_name);+	git_buf_truncate(&filename, filename.size - strlen("pack"));+	git_buf_puts(&filename, "idx");+	if (git_buf_oom(&filename))+		return -1;++	error = git_filebuf_open(&idx->file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS);+	if (error < 0)+		goto cleanup;++	/* Write out the header */+	hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);+	hdr.idx_version = htonl(2);+	error = git_filebuf_write(&idx->file, &hdr, sizeof(hdr));+	if (error < 0)+		goto cleanup;++	/* Write out the fanout table */+	for (i = 0; i < 256; ++i) {+		uint32_t n = htonl(idx->fanout[i]);+		error = git_filebuf_write(&idx->file, &n, sizeof(n));+		if (error < 0)+			goto cleanup;+	}++	/* Write out the object names (SHA-1 hashes) */+	SHA1_Init(&ctx);+	git_vector_foreach(&idx->objects, i, entry) {+		error = git_filebuf_write(&idx->file, &entry->oid, sizeof(git_oid));+		SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ);+		if (error < 0)+			goto cleanup;+	}+	SHA1_Final(idx->hash.id, &ctx);++	/* Write out the CRC32 values */+	git_vector_foreach(&idx->objects, i, entry) {+		error = git_filebuf_write(&idx->file, &entry->crc, sizeof(uint32_t));+		if (error < 0)+			goto cleanup;+	}++	/* Write out the offsets */+	git_vector_foreach(&idx->objects, i, entry) {+		uint32_t n;++		if (entry->offset == UINT32_MAX)+			n = htonl(0x80000000 | long_offsets++);+		else+			n = htonl(entry->offset);++		error = git_filebuf_write(&idx->file, &n, sizeof(uint32_t));+		if (error < 0)+			goto cleanup;+	}++	/* Write out the long offsets */+	git_vector_foreach(&idx->objects, i, entry) {+		uint32_t split[2];++		if (entry->offset != UINT32_MAX)+			continue;++		split[0] = htonl(entry->offset_long >> 32);+		split[1] = htonl(entry->offset_long & 0xffffffff);++		error = git_filebuf_write(&idx->file, &split, sizeof(uint32_t) * 2);+		if (error < 0)+			goto cleanup;+	}++	/* Write out the packfile trailer */++	packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);+	git_mwindow_close(&w);+	if (packfile_hash == NULL) {+		error = -1;+		goto cleanup;+	}++	memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);++	git_mwindow_close(&w);++	error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid));+	if (error < 0)+		goto cleanup;++	/* Write out the index sha */+	error = git_filebuf_hash(&file_hash, &idx->file);+	if (error < 0)+		goto cleanup;++	error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid));+	if (error < 0)+		goto cleanup;++	/* Figure out what the final name should be */+	error = index_path(&filename, idx);+	if (error < 0)+		goto cleanup;++	/* Commit file */+	error = git_filebuf_commit_at(&idx->file, filename.ptr, GIT_PACK_FILE_MODE);++cleanup:+	git_mwindow_free_all(&idx->pack->mwf);+	if (error < 0)+		git_filebuf_cleanup(&idx->file);+	git_buf_free(&filename);++	return error;+}++int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)+{+	git_mwindow_file *mwf;+	git_off_t off = sizeof(struct git_pack_header);+	int error;+	struct entry *entry;+	unsigned int left, processed;++	assert(idx && stats);++	mwf = &idx->pack->mwf;+	error = git_mwindow_file_register(mwf);+	if (error < 0)+		return error;++	stats->total = (unsigned int)idx->nr_objects;+	stats->processed = processed = 0;++	while (processed < idx->nr_objects) {+		git_rawobj obj;+		git_oid oid;+		struct git_pack_entry *pentry;+		git_mwindow *w = NULL;+		int i;+		git_off_t entry_start = off;+		void *packed;+		size_t entry_size;+		char fmt[GIT_OID_HEXSZ] = {0};++		entry = git__calloc(1, sizeof(*entry));+		GITERR_CHECK_ALLOC(entry);++		if (off > UINT31_MAX) {+			entry->offset = UINT32_MAX;+			entry->offset_long = off;+		} else {+			entry->offset = (uint32_t)off;+		}++		error = git_packfile_unpack(&obj, idx->pack, &off);+		if (error < 0)+			goto cleanup;++		/* FIXME: Parse the object instead of hashing it */+		error = git_odb__hashobj(&oid, &obj);+		if (error < 0) {+			giterr_set(GITERR_INDEXER, "Failed to hash object");+			goto cleanup;+		}++		pentry = git__malloc(sizeof(struct git_pack_entry));+		if (pentry == NULL) {+			error = -1;+			goto cleanup;+		}++		git_oid_cpy(&pentry->sha1, &oid);+		pentry->offset = entry_start;+		git_oid_fmt(fmt, &oid);+		printf("adding %s to cache\n", fmt);+		error = git_vector_insert(&idx->pack->cache, pentry);+		if (error < 0)+			goto cleanup;++		git_oid_cpy(&entry->oid, &oid);+		entry->crc = crc32(0L, Z_NULL, 0);++		entry_size = (size_t)(off - entry_start);+		packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left);+		if (packed == NULL) {+			error = -1;+			goto cleanup;+		}+		entry->crc = htonl(crc32(entry->crc, packed, (uInt)entry_size));+		git_mwindow_close(&w);++		/* Add the object to the list */+		error = git_vector_insert(&idx->objects, entry);+		if (error < 0)+			goto cleanup;++		for (i = oid.id[0]; i < 256; ++i) {+			idx->fanout[i]++;+		}++		git__free(obj.data);++		stats->processed = ++processed;+	}++cleanup:+	git_mwindow_free_all(mwf);++	return error;++}++void git_indexer_free(git_indexer *idx)+{+	unsigned int i;+	struct entry *e;+	struct git_pack_entry *pe;++	if (idx == NULL)+		return;++	p_close(idx->pack->mwf.fd);+	git_vector_foreach(&idx->objects, i, e)+		git__free(e);+	git_vector_free(&idx->objects);+	git_vector_foreach(&idx->pack->cache, i, pe)+		git__free(pe);+	git_vector_free(&idx->pack->cache);+	git__free(idx->pack);+	git__free(idx);+}+
+ libgit2/src/iterator.c view
@@ -0,0 +1,748 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "iterator.h"+#include "tree.h"+#include "ignore.h"+#include "buffer.h"+#include "git2/submodule.h"++#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \+	(P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \+	GITERR_CHECK_ALLOC(P); \+	(P)->base.type    = GIT_ITERATOR_ ## NAME_UC; \+	(P)->base.start   = start ? git__strdup(start) : NULL; \+	(P)->base.end     = end ? git__strdup(end) : NULL; \+	(P)->base.current = NAME_LC ## _iterator__current; \+	(P)->base.at_end  = NAME_LC ## _iterator__at_end; \+	(P)->base.advance = NAME_LC ## _iterator__advance; \+	(P)->base.seek    = NAME_LC ## _iterator__seek; \+	(P)->base.reset   = NAME_LC ## _iterator__reset; \+	(P)->base.free    = NAME_LC ## _iterator__free; \+	if ((start && !(P)->base.start) || (end && !(P)->base.end)) \+		return -1; \+	} while (0)+++static int empty_iterator__no_item(+	git_iterator *iter, const git_index_entry **entry)+{+	GIT_UNUSED(iter);+	*entry = NULL;+	return 0;+}++static int empty_iterator__at_end(git_iterator *iter)+{+	GIT_UNUSED(iter);+	return 1;+}++static int empty_iterator__noop(git_iterator *iter)+{+	GIT_UNUSED(iter);+	return 0;+}++static int empty_iterator__seek(git_iterator *iter, const char *prefix)+{+	GIT_UNUSED(iter);+	GIT_UNUSED(prefix);+	return -1;+}++static void empty_iterator__free(git_iterator *iter)+{+	GIT_UNUSED(iter);+}++int git_iterator_for_nothing(git_iterator **iter)+{+	git_iterator *i = git__calloc(1, sizeof(git_iterator));+	GITERR_CHECK_ALLOC(i);++	i->type    = GIT_ITERATOR_EMPTY;+	i->current = empty_iterator__no_item;+	i->at_end  = empty_iterator__at_end;+	i->advance = empty_iterator__no_item;+	i->seek    = empty_iterator__seek;+	i->reset   = empty_iterator__noop;+	i->free    = empty_iterator__free;++	*iter = i;++	return 0;+}+++typedef struct tree_iterator_frame tree_iterator_frame;+struct tree_iterator_frame {+	tree_iterator_frame *next;+	git_tree *tree;+	char *start;+	unsigned int index;+};++typedef struct {+	git_iterator base;+	git_repository *repo;+	tree_iterator_frame *stack;+	git_index_entry entry;+	git_buf path;+	bool path_has_filename;+} tree_iterator;++static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)+{+	return (ti->stack == NULL) ? NULL :+		git_tree_entry_byindex(ti->stack->tree, ti->stack->index);+}++static char *tree_iterator__current_filename(+	tree_iterator *ti, const git_tree_entry *te)+{+	if (!ti->path_has_filename) {+		if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)+			return NULL;+		ti->path_has_filename = true;+	}++	return ti->path.ptr;+}++static void tree_iterator__pop_frame(tree_iterator *ti)+{+	tree_iterator_frame *tf = ti->stack;+	ti->stack = tf->next;+	if (ti->stack != NULL) /* don't free the initial tree */+		git_tree_free(tf->tree);+	git__free(tf);+}++static int tree_iterator__to_end(tree_iterator *ti)+{+	while (ti->stack && ti->stack->next)+		tree_iterator__pop_frame(ti);++	if (ti->stack)+		ti->stack->index = git_tree_entrycount(ti->stack->tree);++	return 0;+}++static int tree_iterator__current(+	git_iterator *self, const git_index_entry **entry)+{+	tree_iterator *ti = (tree_iterator *)self;+	const git_tree_entry *te = tree_iterator__tree_entry(ti);++	if (entry)+		*entry = NULL;++	if (te == NULL)+		return 0;++	ti->entry.mode = te->attr;+	git_oid_cpy(&ti->entry.oid, &te->oid);++	ti->entry.path = tree_iterator__current_filename(ti, te);+	if (ti->entry.path == NULL)+		return -1;++	if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0)+		return tree_iterator__to_end(ti);++	if (entry)+		*entry = &ti->entry;++	return 0;+}++static int tree_iterator__at_end(git_iterator *self)+{+	return (tree_iterator__tree_entry((tree_iterator *)self) == NULL);+}++static tree_iterator_frame *tree_iterator__alloc_frame(+	git_tree *tree, char *start)+{+	tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame));+	if (!tf)+		return NULL;++	tf->tree = tree;++	if (start && *start) {+		tf->start = start;+		tf->index = git_tree__prefix_position(tree, start);+	}++	return tf;+}++static int tree_iterator__expand_tree(tree_iterator *ti)+{+	int error;+	git_tree *subtree;+	const git_tree_entry *te = tree_iterator__tree_entry(ti);+	tree_iterator_frame *tf;+	char *relpath;++	while (te != NULL && git_tree_entry__is_tree(te)) {+		if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)+			return -1;++		/* check that we have not passed the range end */+		if (ti->base.end != NULL &&+			git__prefixcmp(ti->path.ptr, ti->base.end) > 0)+			return tree_iterator__to_end(ti);++		if ((error = git_tree_lookup(&subtree, ti->repo, &te->oid)) < 0)+			return error;++		relpath = NULL;++		/* apply range start to new frame if relevant */+		if (ti->stack->start &&+			git__prefixcmp(ti->stack->start, te->filename) == 0)+		{+			size_t namelen = strlen(te->filename);+			if (ti->stack->start[namelen] == '/')+				relpath = ti->stack->start + namelen + 1;+		}++		if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL)+			return -1;++		tf->next  = ti->stack;+		ti->stack = tf;++		te = tree_iterator__tree_entry(ti);+	}++	return 0;+}++static int tree_iterator__advance(+	git_iterator *self, const git_index_entry **entry)+{+	int error = 0;+	tree_iterator *ti = (tree_iterator *)self;+	const git_tree_entry *te = NULL;++	if (entry != NULL)+		*entry = NULL;++	if (ti->path_has_filename) {+		git_buf_rtruncate_at_char(&ti->path, '/');+		ti->path_has_filename = false;+	}++	while (ti->stack != NULL) {+		te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);+		if (te != NULL)+			break;++		tree_iterator__pop_frame(ti);++		git_buf_rtruncate_at_char(&ti->path, '/');+	}++	if (te && git_tree_entry__is_tree(te))+		error = tree_iterator__expand_tree(ti);++	if (!error)+		error = tree_iterator__current(self, entry);++	return error;+}++static int tree_iterator__seek(git_iterator *self, const char *prefix)+{+	GIT_UNUSED(self);+	GIT_UNUSED(prefix);+	/* pop stack until matches prefix */+	/* seek item in current frame matching prefix */+	/* push stack which matches prefix */+	return -1;+}++static void tree_iterator__free(git_iterator *self)+{+	tree_iterator *ti = (tree_iterator *)self;+	while (ti->stack != NULL)+		tree_iterator__pop_frame(ti);+	git_buf_free(&ti->path);+}++static int tree_iterator__reset(git_iterator *self)+{+	tree_iterator *ti = (tree_iterator *)self;++	while (ti->stack && ti->stack->next)+		tree_iterator__pop_frame(ti);++	if (ti->stack)+		ti->stack->index =+			git_tree__prefix_position(ti->stack->tree, ti->base.start);++	git_buf_clear(&ti->path);++	return tree_iterator__expand_tree(ti);+}++int git_iterator_for_tree_range(+	git_iterator **iter,+	git_repository *repo,+	git_tree *tree,+	const char *start,+	const char *end)+{+	int error;+	tree_iterator *ti;++	if (tree == NULL)+		return git_iterator_for_nothing(iter);++	ITERATOR_BASE_INIT(ti, tree, TREE);++	ti->repo  = repo;+	ti->stack = tree_iterator__alloc_frame(tree, ti->base.start);++	if ((error = tree_iterator__expand_tree(ti)) < 0)+		git_iterator_free((git_iterator *)ti);+	else+		*iter = (git_iterator *)ti;++	return error;+}+++typedef struct {+	git_iterator base;+	git_index *index;+	unsigned int current;+} index_iterator;++static int index_iterator__current(+	git_iterator *self, const git_index_entry **entry)+{+	index_iterator *ii = (index_iterator *)self;+	git_index_entry *ie = git_index_get(ii->index, ii->current);++	if (ie != NULL &&+		ii->base.end != NULL &&+		git__prefixcmp(ie->path, ii->base.end) > 0)+	{+		ii->current = git_index_entrycount(ii->index);+		ie = NULL;+	}++	if (entry)+		*entry = ie;++	return 0;+}++static int index_iterator__at_end(git_iterator *self)+{+	index_iterator *ii = (index_iterator *)self;+	return (ii->current >= git_index_entrycount(ii->index));+}++static int index_iterator__advance(+	git_iterator *self, const git_index_entry **entry)+{+	index_iterator *ii = (index_iterator *)self;++	if (ii->current < git_index_entrycount(ii->index))+		ii->current++;++	return index_iterator__current(self, entry);+}++static int index_iterator__seek(git_iterator *self, const char *prefix)+{+	GIT_UNUSED(self);+	GIT_UNUSED(prefix);+	/* find last item before prefix */+	return -1;+}++static int index_iterator__reset(git_iterator *self)+{+	index_iterator *ii = (index_iterator *)self;+	ii->current = 0;+	return 0;+}++static void index_iterator__free(git_iterator *self)+{+	index_iterator *ii = (index_iterator *)self;+	git_index_free(ii->index);+	ii->index = NULL;+}++int git_iterator_for_index_range(+	git_iterator **iter,+	git_repository *repo,+	const char *start,+	const char *end)+{+	int error;+	index_iterator *ii;++	ITERATOR_BASE_INIT(ii, index, INDEX);++	if ((error = git_repository_index(&ii->index, repo)) < 0)+		git__free(ii);+	else {+		ii->current = start ? git_index__prefix_position(ii->index, start) : 0;+		*iter = (git_iterator *)ii;+	}++	return error;+}+++typedef struct workdir_iterator_frame workdir_iterator_frame;+struct workdir_iterator_frame {+	workdir_iterator_frame *next;+	git_vector entries;+	unsigned int index;+	char *start;+};++typedef struct {+	git_iterator base;+	git_repository *repo;+	size_t root_len;+	workdir_iterator_frame *stack;+	git_ignores ignores;+	git_index_entry entry;+	git_buf path;+	int is_ignored;+} workdir_iterator;++static workdir_iterator_frame *workdir_iterator__alloc_frame(void)+{+	workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));+	if (wf == NULL)+		return NULL;+	if (git_vector_init(&wf->entries, 0, git_path_with_stat_cmp) != 0) {+		git__free(wf);+		return NULL;+	}+	return wf;+}++static void workdir_iterator__free_frame(workdir_iterator_frame *wf)+{+	unsigned int i;+	git_path_with_stat *path;++	git_vector_foreach(&wf->entries, i, path)+		git__free(path);+	git_vector_free(&wf->entries);+	git__free(wf);+}++static int workdir_iterator__update_entry(workdir_iterator *wi);++static int workdir_iterator__entry_cmp(const void *prefix, const void *item)+{+	const git_path_with_stat *ps = item;+	return git__prefixcmp((const char *)prefix, ps->path);+}++static int workdir_iterator__expand_dir(workdir_iterator *wi)+{+	int error;+	workdir_iterator_frame *wf = workdir_iterator__alloc_frame();+	GITERR_CHECK_ALLOC(wf);++	error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);+	if (error < 0 || wf->entries.length == 0) {+		workdir_iterator__free_frame(wf);+		return GIT_ENOTFOUND;+	}++	git_vector_sort(&wf->entries);++	if (!wi->stack)+		wf->start = wi->base.start;+	else if (wi->stack->start &&+		git__prefixcmp(wi->stack->start, wi->path.ptr + wi->root_len) == 0)+		wf->start = wi->stack->start;++	if (wf->start)+		git_vector_bsearch3(+			&wf->index, &wf->entries, workdir_iterator__entry_cmp, wf->start);++	wf->next  = wi->stack;+	wi->stack = wf;++	/* only push new ignores if this is not top level directory */+	if (wi->stack->next != NULL) {+		ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');+		(void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);+	}++	return workdir_iterator__update_entry(wi);+}++static int workdir_iterator__current(+	git_iterator *self, const git_index_entry **entry)+{+	workdir_iterator *wi = (workdir_iterator *)self;+	*entry = (wi->entry.path == NULL) ? NULL : &wi->entry;+	return 0;+}++static int workdir_iterator__at_end(git_iterator *self)+{+	return (((workdir_iterator *)self)->entry.path == NULL);+}++static int workdir_iterator__advance(+	git_iterator *self, const git_index_entry **entry)+{+	int error;+	workdir_iterator *wi = (workdir_iterator *)self;+	workdir_iterator_frame *wf;+	git_path_with_stat *next;++	if (entry != NULL)+		*entry = NULL;++	if (wi->entry.path == NULL)+		return 0;++	while ((wf = wi->stack) != NULL) {+		next = git_vector_get(&wf->entries, ++wf->index);+		if (next != NULL) {+			if (strcmp(next->path, DOT_GIT "/") == 0)+				continue;+			/* else found a good entry */+			break;+		}++		/* pop workdir directory stack */+		wi->stack = wf->next;+		workdir_iterator__free_frame(wf);+		git_ignore__pop_dir(&wi->ignores);++		if (wi->stack == NULL) {+			memset(&wi->entry, 0, sizeof(wi->entry));+			return 0;+		}+	}++	error = workdir_iterator__update_entry(wi);++	if (!error && entry != NULL)+		error = workdir_iterator__current(self, entry);++	return error;+}++static int workdir_iterator__seek(git_iterator *self, const char *prefix)+{+	GIT_UNUSED(self);+	GIT_UNUSED(prefix);+	/* pop stack until matching prefix */+	/* find prefix item in current frame */+	/* push subdirectories as deep as possible while matching */+	return 0;+}++static int workdir_iterator__reset(git_iterator *self)+{+	workdir_iterator *wi = (workdir_iterator *)self;+	while (wi->stack != NULL && wi->stack->next != NULL) {+		workdir_iterator_frame *wf = wi->stack;+		wi->stack = wf->next;+		workdir_iterator__free_frame(wf);+		git_ignore__pop_dir(&wi->ignores);+	}+	if (wi->stack)+		wi->stack->index = 0;+	return 0;+}++static void workdir_iterator__free(git_iterator *self)+{+	workdir_iterator *wi = (workdir_iterator *)self;++	while (wi->stack != NULL) {+		workdir_iterator_frame *wf = wi->stack;+		wi->stack = wf->next;+		workdir_iterator__free_frame(wf);+	}++	git_ignore__free(&wi->ignores);+	git_buf_free(&wi->path);+}++static int workdir_iterator__update_entry(workdir_iterator *wi)+{+	git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);++	git_buf_truncate(&wi->path, wi->root_len);+	memset(&wi->entry, 0, sizeof(wi->entry));++	if (!ps)+		return 0;++	if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)+		return -1;++	if (wi->base.end &&+		git__prefixcmp(wi->path.ptr + wi->root_len, wi->base.end) > 0)+		return 0;++	wi->entry.path = ps->path;++	/* skip over .git directory */+	if (strcmp(ps->path, DOT_GIT "/") == 0)+		return workdir_iterator__advance((git_iterator *)wi, NULL);++	/* if there is an error processing the entry, treat as ignored */+	wi->is_ignored = 1;++	git_index__init_entry_from_stat(&ps->st, &wi->entry);++	/* need different mode here to keep directories during iteration */+	wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);++	/* if this is a file type we don't handle, treat as ignored */+	if (wi->entry.mode == 0)+		return 0;++	/* okay, we are far enough along to look up real ignore rule */+	if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)+		return 0; /* if error, ignore it and ignore file */++	/* detect submodules */+	if (S_ISDIR(wi->entry.mode)) {+		bool is_submodule = git_path_contains(&wi->path, DOT_GIT);++		/* if there is no .git, still check submodules data */+		if (!is_submodule) {+			int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);+			is_submodule = (res == 0);+			if (res == GIT_ENOTFOUND)+				giterr_clear();+		}++		/* if submodule, mark as GITLINK and remove trailing slash */+		if (is_submodule) {+			size_t len = strlen(wi->entry.path);+			assert(wi->entry.path[len - 1] == '/');+			wi->entry.path[len - 1] = '\0';+			wi->entry.mode = S_IFGITLINK;+		}+	}++	return 0;+}++int git_iterator_for_workdir_range(+	git_iterator **iter,+	git_repository *repo,+	const char *start,+	const char *end)+{+	int error;+	workdir_iterator *wi;++	assert(iter && repo);++	if (git_repository_is_bare(repo)) {+		giterr_set(GITERR_INVALID,+			"Cannot scan working directory for bare repo");+		return -1;+	}++	ITERATOR_BASE_INIT(wi, workdir, WORKDIR);++	wi->repo = repo;++	if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||+		git_path_to_dir(&wi->path) < 0 ||+		git_ignore__for_path(repo, "", &wi->ignores) < 0)+	{+		git__free(wi);+		return -1;+	}++	wi->root_len = wi->path.size;++	if ((error = workdir_iterator__expand_dir(wi)) < 0) {+		if (error == GIT_ENOTFOUND)+			error = 0;+		else {+			git_iterator_free((git_iterator *)wi);+			wi = NULL;+		}+	}++	*iter = (git_iterator *)wi;++	return error;+}+++int git_iterator_current_tree_entry(+	git_iterator *iter, const git_tree_entry **tree_entry)+{+	*tree_entry = (iter->type != GIT_ITERATOR_TREE) ? NULL :+		tree_iterator__tree_entry((tree_iterator *)iter);+	return 0;+}++int git_iterator_current_is_ignored(git_iterator *iter)+{+	return (iter->type != GIT_ITERATOR_WORKDIR) ? 0 :+		((workdir_iterator *)iter)->is_ignored;+}++int git_iterator_advance_into_directory(+	git_iterator *iter, const git_index_entry **entry)+{+	workdir_iterator *wi = (workdir_iterator *)iter;++	if (iter->type == GIT_ITERATOR_WORKDIR &&+		wi->entry.path &&+		S_ISDIR(wi->entry.mode) &&+		!S_ISGITLINK(wi->entry.mode))+	{+		if (workdir_iterator__expand_dir(wi) < 0)+			/* if error loading or if empty, skip the directory. */+			return workdir_iterator__advance(iter, entry);+	}++	return entry ? git_iterator_current(iter, entry) : 0;+}++int git_iterator_cmp(+	git_iterator *iter, const char *path_prefix)+{+	const git_index_entry *entry;++	/* a "done" iterator is after every prefix */+	if (git_iterator_current(iter, &entry) < 0 ||+		entry == NULL)+		return 1;++	/* a NULL prefix is after any valid iterator */+	if (!path_prefix)+		return -1;++	return git__prefixcmp(entry->path, path_prefix);+}+
+ libgit2/src/iterator.h view
@@ -0,0 +1,151 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_iterator_h__+#define INCLUDE_iterator_h__++#include "common.h"+#include "git2/index.h"++typedef struct git_iterator git_iterator;++typedef enum {+	GIT_ITERATOR_EMPTY = 0,+	GIT_ITERATOR_TREE = 1,+	GIT_ITERATOR_INDEX = 2,+	GIT_ITERATOR_WORKDIR = 3+} git_iterator_type_t;++struct git_iterator {+	git_iterator_type_t type;+	char *start;+	char *end;+	int (*current)(git_iterator *, const git_index_entry **);+	int (*at_end)(git_iterator *);+	int (*advance)(git_iterator *, const git_index_entry **);+	int (*seek)(git_iterator *, const char *prefix);+	int (*reset)(git_iterator *);+	void (*free)(git_iterator *);+};++extern int git_iterator_for_nothing(git_iterator **iter);++extern int git_iterator_for_tree_range(+	git_iterator **iter, git_repository *repo, git_tree *tree,+	const char *start, const char *end);++GIT_INLINE(int) git_iterator_for_tree(+	git_iterator **iter, git_repository *repo, git_tree *tree)+{+	return git_iterator_for_tree_range(iter, repo, tree, NULL, NULL);+}++extern int git_iterator_for_index_range(+	git_iterator **iter, git_repository *repo,+	const char *start, const char *end);++GIT_INLINE(int) git_iterator_for_index(+	git_iterator **iter, git_repository *repo)+{+	return git_iterator_for_index_range(iter, repo, NULL, NULL);+}++extern int git_iterator_for_workdir_range(+	git_iterator **iter, git_repository *repo,+	const char *start, const char *end);++GIT_INLINE(int) git_iterator_for_workdir(+	git_iterator **iter, git_repository *repo)+{+	return git_iterator_for_workdir_range(iter, repo, NULL, NULL);+}+++/* Entry is not guaranteed to be fully populated.  For a tree iterator,+ * we will only populate the mode, oid and path, for example.  For a workdir+ * iterator, we will not populate the oid.+ *+ * You do not need to free the entry.  It is still "owned" by the iterator.+ * Once you call `git_iterator_advance`, then content of the old entry is+ * no longer guaranteed to be valid.+ */+GIT_INLINE(int) git_iterator_current(+	git_iterator *iter, const git_index_entry **entry)+{+	return iter->current(iter, entry);+}++GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)+{+	return iter->at_end(iter);+}++GIT_INLINE(int) git_iterator_advance(+	git_iterator *iter, const git_index_entry **entry)+{+	return iter->advance(iter, entry);+}++GIT_INLINE(int) git_iterator_seek(+	git_iterator *iter, const char *prefix)+{+	return iter->seek(iter, prefix);+}++GIT_INLINE(int) git_iterator_reset(git_iterator *iter)+{+	return iter->reset(iter);+}++GIT_INLINE(void) git_iterator_free(git_iterator *iter)+{+	if (iter == NULL)+		return;++	iter->free(iter);++	git__free(iter->start);+	git__free(iter->end);++	memset(iter, 0, sizeof(*iter));++	git__free(iter);+}++GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)+{+	return iter->type;+}++extern int git_iterator_current_tree_entry(+	git_iterator *iter, const git_tree_entry **tree_entry);++extern int git_iterator_current_is_ignored(git_iterator *iter);++/**+ * Iterate into a workdir directory.+ *+ * Workdir iterators do not automatically descend into directories (so that+ * when comparing two iterator entries you can detect a newly created+ * directory in the workdir).  As a result, you may get S_ISDIR items from+ * a workdir iterator.  If you wish to iterate over the contents of the+ * directories you encounter, then call this function when you encounter+ * a directory.+ *+ * If there are no files in the directory, this will end up acting like a+ * regular advance and will skip past the directory, so you should be+ * prepared for that case.+ *+ * On non-workdir iterators or if not pointing at a directory, this is a+ * no-op and will not advance the iterator.+ */+extern int git_iterator_advance_into_directory(+	git_iterator *iter, const git_index_entry **entry);++extern int git_iterator_cmp(+	git_iterator *iter, const char *path_prefix);++#endif
+ libgit2/src/khash.h view
@@ -0,0 +1,608 @@+/* The MIT License++   Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>++   Permission is hereby granted, free of charge, to any person obtaining+   a copy of this software and associated documentation files (the+   "Software"), to deal in the Software without restriction, including+   without limitation the rights to use, copy, modify, merge, publish,+   distribute, sublicense, and/or sell copies of the Software, and to+   permit persons to whom the Software is furnished to do so, subject to+   the following conditions:++   The above copyright notice and this permission notice shall be+   included in all copies or substantial portions of the Software.++   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+   SOFTWARE.+*/++/*+  An example:++#include "khash.h"+KHASH_MAP_INIT_INT(32, char)+int main() {+	int ret, is_missing;+	khiter_t k;+	khash_t(32) *h = kh_init(32);+	k = kh_put(32, h, 5, &ret);+	kh_value(h, k) = 10;+	k = kh_get(32, h, 10);+	is_missing = (k == kh_end(h));+	k = kh_get(32, h, 5);+	kh_del(32, h, k);+	for (k = kh_begin(h); k != kh_end(h); ++k)+		if (kh_exist(h, k)) kh_value(h, k) = 1;+	kh_destroy(32, h);+	return 0;+}+*/++/*+  2011-12-29 (0.2.7):++    * Minor code clean up; no actual effect.++  2011-09-16 (0.2.6):++	* The capacity is a power of 2. This seems to dramatically improve the+	  speed for simple keys. Thank Zilong Tan for the suggestion. Reference:++	   - http://code.google.com/p/ulib/+	   - http://nothings.org/computer/judy/++	* Allow to optionally use linear probing which usually has better+	  performance for random input. Double hashing is still the default as it+	  is more robust to certain non-random input.++	* Added Wang's integer hash function (not used by default). This hash+	  function is more robust to certain non-random input.++  2011-02-14 (0.2.5):++    * Allow to declare global functions.++  2009-09-26 (0.2.4):++    * Improve portability++  2008-09-19 (0.2.3):++	* Corrected the example+	* Improved interfaces++  2008-09-11 (0.2.2):++	* Improved speed a little in kh_put()++  2008-09-10 (0.2.1):++	* Added kh_clear()+	* Fixed a compiling error++  2008-09-02 (0.2.0):++	* Changed to token concatenation which increases flexibility.++  2008-08-31 (0.1.2):++	* Fixed a bug in kh_get(), which has not been tested previously.++  2008-08-31 (0.1.1):++	* Added destructor+*/+++#ifndef __AC_KHASH_H+#define __AC_KHASH_H++/*!+  @header++  Generic hash table library.+ */++#define AC_VERSION_KHASH_H "0.2.6"++#include <stdlib.h>+#include <string.h>+#include <limits.h>++/* compipler specific configuration */++#if UINT_MAX == 0xffffffffu+typedef unsigned int khint32_t;+#elif ULONG_MAX == 0xffffffffu+typedef unsigned long khint32_t;+#endif++#if ULONG_MAX == ULLONG_MAX+typedef unsigned long khint64_t;+#else+typedef unsigned long long khint64_t;+#endif++#ifdef _MSC_VER+#define inline __inline+#endif++typedef khint32_t khint_t;+typedef khint_t khiter_t;++#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))++#ifdef KHASH_LINEAR+#define __ac_inc(k, m) 1+#else+#define __ac_inc(k, m) (((k)>>3 ^ (k)<<3) | 1) & (m)+#endif++#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)++#ifndef kroundup32+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))+#endif++#ifndef kcalloc+#define kcalloc(N,Z) calloc(N,Z)+#endif+#ifndef kmalloc+#define kmalloc(Z) malloc(Z)+#endif+#ifndef krealloc+#define krealloc(P,Z) realloc(P,Z)+#endif+#ifndef kfree+#define kfree(P) free(P)+#endif++static const double __ac_HASH_UPPER = 0.77;++#define __KHASH_TYPE(name, khkey_t, khval_t) \+	typedef struct { \+		khint_t n_buckets, size, n_occupied, upper_bound; \+		khint32_t *flags; \+		khkey_t *keys; \+		khval_t *vals; \+	} kh_##name##_t;++#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)	 					\+	extern kh_##name##_t *kh_init_##name(void);							\+	extern void kh_destroy_##name(kh_##name##_t *h);					\+	extern void kh_clear_##name(kh_##name##_t *h);						\+	extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); 	\+	extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \+	extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \+	extern void kh_del_##name(kh_##name##_t *h, khint_t x);++#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \+	SCOPE kh_##name##_t *kh_init_##name(void) {							\+		return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t));		\+	}																	\+	SCOPE void kh_destroy_##name(kh_##name##_t *h)						\+	{																	\+		if (h) {														\+			kfree((void *)h->keys); kfree(h->flags);					\+			kfree((void *)h->vals);										\+			kfree(h);													\+		}																\+	}																	\+	SCOPE void kh_clear_##name(kh_##name##_t *h)						\+	{																	\+		if (h && h->flags) {											\+			memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \+			h->size = h->n_occupied = 0;								\+		}																\+	}																	\+	SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) 	\+	{																	\+		if (h->n_buckets) {												\+			khint_t inc, k, i, last, mask;								\+			mask = h->n_buckets - 1;									\+			k = __hash_func(key); i = k & mask;							\+			inc = __ac_inc(k, mask); last = i; /* inc==1 for linear probing */ \+			while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \+				i = (i + inc) & mask; 									\+				if (i == last) return h->n_buckets;						\+			}															\+			return __ac_iseither(h->flags, i)? h->n_buckets : i;		\+		} else return 0;												\+	}																	\+	SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \+	{ /* This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \+		khint32_t *new_flags = 0;										\+		khint_t j = 1;													\+		{																\+			kroundup32(new_n_buckets); 									\+			if (new_n_buckets < 4) new_n_buckets = 4;					\+			if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0;	/* requested size is too small */ \+			else { /* hash table size to be changed (shrink or expand); rehash */ \+				new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t));	\+				if (!new_flags) return -1;								\+				memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \+				if (h->n_buckets < new_n_buckets) {	/* expand */		\+					khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \+					if (!new_keys) return -1;							\+					h->keys = new_keys;									\+					if (kh_is_map) {									\+						khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \+						if (!new_vals) return -1;						\+						h->vals = new_vals;								\+					}													\+				} /* otherwise shrink */								\+			}															\+		}																\+		if (j) { /* rehashing is needed */								\+			for (j = 0; j != h->n_buckets; ++j) {						\+				if (__ac_iseither(h->flags, j) == 0) {					\+					khkey_t key = h->keys[j];							\+					khval_t val;										\+					khint_t new_mask;									\+					new_mask = new_n_buckets - 1; 						\+					if (kh_is_map) val = h->vals[j];					\+					__ac_set_isdel_true(h->flags, j);					\+					while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \+						khint_t inc, k, i;								\+						k = __hash_func(key);							\+						i = k & new_mask;								\+						inc = __ac_inc(k, new_mask);					\+						while (!__ac_isempty(new_flags, i)) i = (i + inc) & new_mask; \+						__ac_set_isempty_false(new_flags, i);			\+						if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \+							{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \+							if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \+							__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \+						} else { /* write the element and jump out of the loop */ \+							h->keys[i] = key;							\+							if (kh_is_map) h->vals[i] = val;			\+							break;										\+						}												\+					}													\+				}														\+			}															\+			if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \+				h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \+				if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \+			}															\+			kfree(h->flags); /* free the working space */				\+			h->flags = new_flags;										\+			h->n_buckets = new_n_buckets;								\+			h->n_occupied = h->size;									\+			h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \+		}																\+		return 0;														\+	}																	\+	SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \+	{																	\+		khint_t x;														\+		if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \+			if (h->n_buckets > (h->size<<1)) {							\+				if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \+					*ret = -1; return h->n_buckets;						\+				}														\+			} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \+				*ret = -1; return h->n_buckets;							\+			}															\+		} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \+		{																\+			khint_t inc, k, i, site, last, mask = h->n_buckets - 1;		\+			x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \+			if (__ac_isempty(h->flags, i)) x = i; /* for speed up */	\+			else {														\+				inc = __ac_inc(k, mask); last = i;						\+				while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \+					if (__ac_isdel(h->flags, i)) site = i;				\+					i = (i + inc) & mask; 								\+					if (i == last) { x = site; break; }					\+				}														\+				if (x == h->n_buckets) {								\+					if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \+					else x = i;											\+				}														\+			}															\+		}																\+		if (__ac_isempty(h->flags, x)) { /* not present at all */		\+			h->keys[x] = key;											\+			__ac_set_isboth_false(h->flags, x);							\+			++h->size; ++h->n_occupied;									\+			*ret = 1;													\+		} else if (__ac_isdel(h->flags, x)) { /* deleted */				\+			h->keys[x] = key;											\+			__ac_set_isboth_false(h->flags, x);							\+			++h->size;													\+			*ret = 2;													\+		} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \+		return x;														\+	}																	\+	SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)				\+	{																	\+		if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {			\+			__ac_set_isdel_true(h->flags, x);							\+			--h->size;													\+		}																\+	}++#define KHASH_DECLARE(name, khkey_t, khval_t)		 					\+	__KHASH_TYPE(name, khkey_t, khval_t) 								\+	__KHASH_PROTOTYPES(name, khkey_t, khval_t)++#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \+	__KHASH_TYPE(name, khkey_t, khval_t) 								\+	__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)++#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \+	KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)++/* --- BEGIN OF HASH FUNCTIONS --- */++/*! @function+  @abstract     Integer hash function+  @param  key   The integer [khint32_t]+  @return       The hash value [khint_t]+ */+#define kh_int_hash_func(key) (khint32_t)(key)+/*! @function+  @abstract     Integer comparison function+ */+#define kh_int_hash_equal(a, b) ((a) == (b))+/*! @function+  @abstract     64-bit integer hash function+  @param  key   The integer [khint64_t]+  @return       The hash value [khint_t]+ */+#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)+/*! @function+  @abstract     64-bit integer comparison function+ */+#define kh_int64_hash_equal(a, b) ((a) == (b))+/*! @function+  @abstract     const char* hash function+  @param  s     Pointer to a null terminated string+  @return       The hash value+ */+static inline khint_t __ac_X31_hash_string(const char *s)+{+	khint_t h = (khint_t)*s;+	if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;+	return h;+}+/*! @function+  @abstract     Another interface to const char* hash function+  @param  key   Pointer to a null terminated string [const char*]+  @return       The hash value [khint_t]+ */+#define kh_str_hash_func(key) __ac_X31_hash_string(key)+/*! @function+  @abstract     Const char* comparison function+ */+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)++static inline khint_t __ac_Wang_hash(khint_t key)+{+    key += ~(key << 15);+    key ^=  (key >> 10);+    key +=  (key << 3);+    key ^=  (key >> 6);+    key += ~(key << 11);+    key ^=  (key >> 16);+    return key;+}+#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)++/* --- END OF HASH FUNCTIONS --- */++/* Other convenient macros... */++/*!+  @abstract Type of the hash table.+  @param  name  Name of the hash table [symbol]+ */+#define khash_t(name) kh_##name##_t++/*! @function+  @abstract     Initiate a hash table.+  @param  name  Name of the hash table [symbol]+  @return       Pointer to the hash table [khash_t(name)*]+ */+#define kh_init(name) kh_init_##name()++/*! @function+  @abstract     Destroy a hash table.+  @param  name  Name of the hash table [symbol]+  @param  h     Pointer to the hash table [khash_t(name)*]+ */+#define kh_destroy(name, h) kh_destroy_##name(h)++/*! @function+  @abstract     Reset a hash table without deallocating memory.+  @param  name  Name of the hash table [symbol]+  @param  h     Pointer to the hash table [khash_t(name)*]+ */+#define kh_clear(name, h) kh_clear_##name(h)++/*! @function+  @abstract     Resize a hash table.+  @param  name  Name of the hash table [symbol]+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  s     New size [khint_t]+ */+#define kh_resize(name, h, s) kh_resize_##name(h, s)++/*! @function+  @abstract     Insert a key to the hash table.+  @param  name  Name of the hash table [symbol]+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  k     Key [type of keys]+  @param  r     Extra return code: 0 if the key is present in the hash table;+                1 if the bucket is empty (never used); 2 if the element in+				the bucket has been deleted [int*]+  @return       Iterator to the inserted element [khint_t]+ */+#define kh_put(name, h, k, r) kh_put_##name(h, k, r)++/*! @function+  @abstract     Retrieve a key from the hash table.+  @param  name  Name of the hash table [symbol]+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  k     Key [type of keys]+  @return       Iterator to the found element, or kh_end(h) is the element is absent [khint_t]+ */+#define kh_get(name, h, k) kh_get_##name(h, k)++/*! @function+  @abstract     Remove a key from the hash table.+  @param  name  Name of the hash table [symbol]+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  k     Iterator to the element to be deleted [khint_t]+ */+#define kh_del(name, h, k) kh_del_##name(h, k)++/*! @function+  @abstract     Test whether a bucket contains data.+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  x     Iterator to the bucket [khint_t]+  @return       1 if containing data; 0 otherwise [int]+ */+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))++/*! @function+  @abstract     Get key given an iterator+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  x     Iterator to the bucket [khint_t]+  @return       Key [type of keys]+ */+#define kh_key(h, x) ((h)->keys[x])++/*! @function+  @abstract     Get value given an iterator+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  x     Iterator to the bucket [khint_t]+  @return       Value [type of values]+  @discussion   For hash sets, calling this results in segfault.+ */+#define kh_val(h, x) ((h)->vals[x])++/*! @function+  @abstract     Alias of kh_val()+ */+#define kh_value(h, x) ((h)->vals[x])++/*! @function+  @abstract     Get the start iterator+  @param  h     Pointer to the hash table [khash_t(name)*]+  @return       The start iterator [khint_t]+ */+#define kh_begin(h) (khint_t)(0)++/*! @function+  @abstract     Get the end iterator+  @param  h     Pointer to the hash table [khash_t(name)*]+  @return       The end iterator [khint_t]+ */+#define kh_end(h) ((h)->n_buckets)++/*! @function+  @abstract     Get the number of elements in the hash table+  @param  h     Pointer to the hash table [khash_t(name)*]+  @return       Number of elements in the hash table [khint_t]+ */+#define kh_size(h) ((h)->size)++/*! @function+  @abstract     Get the number of buckets in the hash table+  @param  h     Pointer to the hash table [khash_t(name)*]+  @return       Number of buckets in the hash table [khint_t]+ */+#define kh_n_buckets(h) ((h)->n_buckets)++/*! @function+  @abstract     Iterate over the entries in the hash table+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  kvar  Variable to which key will be assigned+  @param  vvar  Variable to which value will be assigned+  @param  code  Block of code to execute+ */+#define kh_foreach(h, kvar, vvar, code) { khint_t __i;		\+	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\+		if (!kh_exist(h,__i)) continue;						\+		(kvar) = kh_key(h,__i);								\+		(vvar) = kh_val(h,__i);								\+		code;												\+	} }++/*! @function+  @abstract     Iterate over the values in the hash table+  @param  h     Pointer to the hash table [khash_t(name)*]+  @param  vvar  Variable to which value will be assigned+  @param  code  Block of code to execute+ */+#define kh_foreach_value(h, vvar, code) { khint_t __i;		\+	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\+		if (!kh_exist(h,__i)) continue;						\+		(vvar) = kh_val(h,__i);								\+		code;												\+	} }++/* More conenient interfaces */++/*! @function+  @abstract     Instantiate a hash set containing integer keys+  @param  name  Name of the hash table [symbol]+ */+#define KHASH_SET_INIT_INT(name)										\+	KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)++/*! @function+  @abstract     Instantiate a hash map containing integer keys+  @param  name  Name of the hash table [symbol]+  @param  khval_t  Type of values [type]+ */+#define KHASH_MAP_INIT_INT(name, khval_t)								\+	KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)++/*! @function+  @abstract     Instantiate a hash map containing 64-bit integer keys+  @param  name  Name of the hash table [symbol]+ */+#define KHASH_SET_INIT_INT64(name)										\+	KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)++/*! @function+  @abstract     Instantiate a hash map containing 64-bit integer keys+  @param  name  Name of the hash table [symbol]+  @param  khval_t  Type of values [type]+ */+#define KHASH_MAP_INIT_INT64(name, khval_t)								\+	KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)++typedef const char *kh_cstr_t;+/*! @function+  @abstract     Instantiate a hash map containing const char* keys+  @param  name  Name of the hash table [symbol]+ */+#define KHASH_SET_INIT_STR(name)										\+	KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)++/*! @function+  @abstract     Instantiate a hash map containing const char* keys+  @param  name  Name of the hash table [symbol]+  @param  khval_t  Type of values [type]+ */+#define KHASH_MAP_INIT_STR(name, khval_t)								\+	KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)++#endif /* __AC_KHASH_H */
+ libgit2/src/map.h view
@@ -0,0 +1,42 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_map_h__+#define INCLUDE_map_h__++#include "common.h"+++/* p_mmap() prot values */+#define GIT_PROT_NONE 0x0+#define GIT_PROT_READ 0x1+#define GIT_PROT_WRITE 0x2+#define GIT_PROT_EXEC 0x4++/* git__mmmap() flags values */+#define GIT_MAP_FILE	0+#define GIT_MAP_SHARED 1+#define GIT_MAP_PRIVATE 2+#define GIT_MAP_TYPE	0xf+#define GIT_MAP_FIXED	0x10++typedef struct { /* memory mapped buffer	*/+	void *data; /* data bytes			*/+	size_t len; /* data length			*/+#ifdef GIT_WIN32+	HANDLE fmh; /* file mapping handle */+#endif+} git_map;++#define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \+	assert(out != NULL && len > 0); \+	assert((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \+	assert((flags & GIT_MAP_FIXED) == 0); } while (0)++extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);+extern int p_munmap(git_map *map);++#endif /* INCLUDE_map_h__ */
+ libgit2/src/message.c view
@@ -0,0 +1,61 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "message.h"+#include <ctype.h>++static size_t line_length_without_trailing_spaces(const char *line, size_t len)+{+	while (len) {+		unsigned char c = line[len - 1];+		if (!git__isspace(c))+			break;+		len--;+	}++	return len;+}++/* Greatly inspired from git.git "stripspace" */+/* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */+int git_message_prettify(git_buf *message_out, const char *message, int strip_comments)+{+	const size_t message_len = strlen(message);++	int consecutive_empty_lines = 0;+	size_t i, line_length, rtrimmed_line_length;+	char *next_newline;++	for (i = 0; i < strlen(message); i += line_length) {+		next_newline = memchr(message + i, '\n', message_len - i);++		if (next_newline != NULL) {+			line_length = next_newline - (message + i) + 1;+		} else {+			line_length = message_len - i;+		}++		if (strip_comments && line_length && message[i] == '#')+			continue;++		rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length);++		if (!rtrimmed_line_length) {+			consecutive_empty_lines++;+			continue;+		}++		if (consecutive_empty_lines > 0 && message_out->size > 0)+			git_buf_putc(message_out, '\n');++		consecutive_empty_lines = 0;+		git_buf_put(message_out, message + i, rtrimmed_line_length);+		git_buf_putc(message_out, '\n');+	}++	return git_buf_oom(message_out) ? -1 : 0;+}
+ libgit2/src/message.h view
@@ -0,0 +1,14 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_message_h__+#define INCLUDE_message_h__++#include "buffer.h"++int git_message_prettify(git_buf *message_out, const char *message, int strip_comments);++#endif /* INCLUDE_message_h__ */
+ libgit2/src/mwindow.c view
@@ -0,0 +1,272 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "mwindow.h"+#include "vector.h"+#include "fileops.h"+#include "map.h"+#include "global.h"++#define DEFAULT_WINDOW_SIZE \+	(sizeof(void*) >= 8 \+		? 1 * 1024 * 1024 * 1024 \+		: 32 * 1024 * 1024)++#define DEFAULT_MAPPED_LIMIT \+	((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))++/*+ * These are the global options for mmmap limits.+ * TODO: allow the user to change these+ */+static struct {+	size_t window_size;+	size_t mapped_limit;+} _mw_options = {+	DEFAULT_WINDOW_SIZE,+	DEFAULT_MAPPED_LIMIT,+};++/*+ * Free all the windows in a sequence, typically because we're done+ * with the file+ */+void git_mwindow_free_all(git_mwindow_file *mwf)+{+	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;+	unsigned int i;+	/*+	 * Remove these windows from the global list+	 */+	for (i = 0; i < ctl->windowfiles.length; ++i){+		if (git_vector_get(&ctl->windowfiles, i) == mwf) {+			git_vector_remove(&ctl->windowfiles, i);+			break;+		}+	}++	if (ctl->windowfiles.length == 0) {+		git_vector_free(&ctl->windowfiles);+		ctl->windowfiles.contents = NULL;+	}++	while (mwf->windows) {+		git_mwindow *w = mwf->windows;+		assert(w->inuse_cnt == 0);++		ctl->mapped -= w->window_map.len;+		ctl->open_windows--;++		git_futils_mmap_free(&w->window_map);++		mwf->windows = w->next;+		git__free(w);+	}+}++/*+ * Check if a window 'win' contains the address 'offset'+ */+int git_mwindow_contains(git_mwindow *win, git_off_t offset)+{+	git_off_t win_off = win->offset;+	return win_off <= offset+		&& offset <= (git_off_t)(win_off + win->window_map.len);+}++/*+ * Find the least-recently-used window in a file+ */+void git_mwindow_scan_lru(+	git_mwindow_file *mwf,+	git_mwindow **lru_w,+	git_mwindow **lru_l)+{+	git_mwindow *w, *w_l;++	puts("LRU");+	for (w_l = NULL, w = mwf->windows; w; w = w->next) {+		if (!w->inuse_cnt) {+			/*+			 * If the current one is more recent than the last one,+			 * store it in the output parameter. If lru_w is NULL,+			 * it's the first loop, so store it as well.+			 */+			if (!*lru_w || w->last_used < (*lru_w)->last_used) {+				*lru_w = w;+				*lru_l = w_l;+			}+		}+		w_l = w;+	}+}++/*+ * Close the least recently used window. You should check to see if+ * the file descriptors need closing from time to time.+ */+static int git_mwindow_close_lru(git_mwindow_file *mwf)+{+	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;+	unsigned int i;+	git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;++	/* FIXME: Does this give us any advantage? */+	if(mwf->windows)+		git_mwindow_scan_lru(mwf, &lru_w, &lru_l);++	for (i = 0; i < ctl->windowfiles.length; ++i) {+		git_mwindow *last = lru_w;+		git_mwindow_file *cur = git_vector_get(&ctl->windowfiles, i);+		git_mwindow_scan_lru(cur, &lru_w, &lru_l);+		if (lru_w != last)+			list = &cur->windows;+	}++	if (!lru_w) {+		giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU");+		return -1;+	}++	ctl->mapped -= lru_w->window_map.len;+	git_futils_mmap_free(&lru_w->window_map);++	if (lru_l)+		lru_l->next = lru_w->next;+	else+		*list = lru_w->next;++	git__free(lru_w);+	ctl->open_windows--;++	return 0;+}++static git_mwindow *new_window(+	git_mwindow_file *mwf,+	git_file fd,+	git_off_t size,+	git_off_t offset)+{+	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;+	size_t walign = _mw_options.window_size / 2;+	git_off_t len;+	git_mwindow *w;++	w = git__malloc(sizeof(*w));+	if (w == NULL)+		return NULL;++	memset(w, 0x0, sizeof(*w));+	w->offset = (offset / walign) * walign;++	len = size - w->offset;+	if (len > (git_off_t)_mw_options.window_size)+		len = (git_off_t)_mw_options.window_size;++	ctl->mapped += (size_t)len;++	while (_mw_options.mapped_limit < ctl->mapped &&+			git_mwindow_close_lru(mwf) == 0) /* nop */;++	/*+	 * We treat _mw_options.mapped_limit as a soft limit. If we can't find a+	 * window to close and are above the limit, we still mmap the new+	 * window.+	 */++	if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {+		git__free(w);+		return NULL;+	}++	ctl->mmap_calls++;+	ctl->open_windows++;++	if (ctl->mapped > ctl->peak_mapped)+		ctl->peak_mapped = ctl->mapped;++	if (ctl->open_windows > ctl->peak_open_windows)+		ctl->peak_open_windows = ctl->open_windows;++	return w;+}++/*+ * Open a new window, closing the least recenty used until we have+ * enough space. Don't forget to add it to your list+ */+unsigned char *git_mwindow_open(+	git_mwindow_file *mwf,+	git_mwindow **cursor,+	git_off_t offset,+	size_t extra,+	unsigned int *left)+{+	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;+	git_mwindow *w = *cursor;++	if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {+		if (w) {+			w->inuse_cnt--;+		}++		for (w = mwf->windows; w; w = w->next) {+			if (git_mwindow_contains(w, offset) &&+				git_mwindow_contains(w, offset + extra))+				break;+		}++		/*+		 * If there isn't a suitable window, we need to create a new+		 * one.+		 */+		if (!w) {+			w = new_window(mwf, mwf->fd, mwf->size, offset);+			if (w == NULL)+				return NULL;+			w->next = mwf->windows;+			mwf->windows = w;+		}+	}++	/* If we changed w, store it in the cursor */+	if (w != *cursor) {+		w->last_used = ctl->used_ctr++;+		w->inuse_cnt++;+		*cursor = w;+	}++	offset -= w->offset;++	if (left)+		*left = (unsigned int)(w->window_map.len - offset);++	fflush(stdout);+	return (unsigned char *) w->window_map.data + offset;+}++int git_mwindow_file_register(git_mwindow_file *mwf)+{+	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;++	if (ctl->windowfiles.length == 0 &&+		git_vector_init(&ctl->windowfiles, 8, NULL) < 0)+		return -1;++	return git_vector_insert(&ctl->windowfiles, mwf);+}++void git_mwindow_close(git_mwindow **window)+{+	git_mwindow *w = *window;+	if (w) {+		w->inuse_cnt--;+		*window = NULL;+	}+}
+ libgit2/src/mwindow.h view
@@ -0,0 +1,45 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#ifndef INCLUDE_mwindow__+#define INCLUDE_mwindow__++#include "map.h"+#include "vector.h"++typedef struct git_mwindow {+	struct git_mwindow *next;+	git_map window_map;+	git_off_t offset;+	size_t last_used;+	size_t inuse_cnt;+} git_mwindow;++typedef struct git_mwindow_file {+	git_mwindow *windows;+	int fd;+	git_off_t size;+} git_mwindow_file;++typedef struct git_mwindow_ctl {+	size_t mapped;+	unsigned int open_windows;+	unsigned int mmap_calls;+	unsigned int peak_open_windows;+	size_t peak_mapped;+	size_t used_ctr;+	git_vector windowfiles;+} git_mwindow_ctl;++int git_mwindow_contains(git_mwindow *win, git_off_t offset);+void git_mwindow_free_all(git_mwindow_file *mwf);+unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left);+void git_mwindow_scan_lru(git_mwindow_file *mwf, git_mwindow **lru_w, git_mwindow **lru_l);+int git_mwindow_file_register(git_mwindow_file *mwf);+void git_mwindow_close(git_mwindow **w_cursor);++#endif
+ libgit2/src/netops.c view
@@ -0,0 +1,209 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef _WIN32+#	include <sys/types.h>+#	include <sys/socket.h>+#	include <sys/select.h>+#	include <sys/time.h>+#	include <netdb.h>+#else+#	include <winsock2.h>+#	include <Ws2tcpip.h>+#	ifdef _MSC_VER+#		pragma comment(lib, "Ws2_32.lib")+#	endif+#endif+++#include "git2/errors.h"++#include "common.h"+#include "netops.h"+#include "posix.h"+#include "buffer.h"++#ifdef GIT_WIN32+static void net_set_error(const char *str)+{+	int size, error = WSAGetLastError();+	LPSTR err_str = NULL;++	size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,+			     0, error, 0, (LPSTR)&err_str, 0, 0);++	giterr_set(GITERR_NET, "%s: %s", str, err_str);+	LocalFree(err_str);+}+#else+static void net_set_error(const char *str)+{+	giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));+}+#endif++void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd)+{+	memset(buf, 0x0, sizeof(gitno_buffer));+	memset(data, 0x0, len);+	buf->data = data;+	buf->len = len;+	buf->offset = 0;+	buf->fd = fd;+}++int gitno_recv(gitno_buffer *buf)+{+	int ret;++	ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);+	if (ret < 0) {+		net_set_error("Error receiving socket data");+		return -1;+	}++	buf->offset += ret;+	return ret;+}++/* Consume up to ptr and move the rest of the buffer to the beginning */+void gitno_consume(gitno_buffer *buf, const char *ptr)+{+	size_t consumed;++	assert(ptr - buf->data >= 0);+	assert(ptr - buf->data <= (int) buf->len);++	consumed = ptr - buf->data;++	memmove(buf->data, ptr, buf->offset - consumed);+	memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);+	buf->offset -= consumed;+}++/* Consume const bytes and move the rest of the buffer to the beginning */+void gitno_consume_n(gitno_buffer *buf, size_t cons)+{+	memmove(buf->data, buf->data + cons, buf->len - buf->offset);+	memset(buf->data + cons, 0x0, buf->len - buf->offset);+	buf->offset -= cons;+}++int gitno_connect(GIT_SOCKET *sock, const char *host, const char *port)+{+	struct addrinfo *info = NULL, *p;+	struct addrinfo hints;+	int ret;+	GIT_SOCKET s = INVALID_SOCKET;++	memset(&hints, 0x0, sizeof(struct addrinfo));+	hints.ai_family = AF_UNSPEC;+	hints.ai_socktype = SOCK_STREAM;++	if ((ret = getaddrinfo(host, port, &hints, &info)) < 0) {+		giterr_set(GITERR_NET, "Failed to resolve address for %s: %s", host, gai_strerror(ret));+		return -1;+	}++	for (p = info; p != NULL; p = p->ai_next) {+		s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);+		if (s == INVALID_SOCKET) {+			net_set_error("error creating socket");+			break;+		}++		if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)+			break;++		/* If we can't connect, try the next one */+		gitno_close(s);+		s = INVALID_SOCKET;+	}++	/* Oops, we couldn't connect to any address */+	if (s == INVALID_SOCKET && p == NULL) {+		giterr_set(GITERR_OS, "Failed to connect to %s", host);+		return -1;+	}++	freeaddrinfo(info);+	*sock = s;+	return 0;+}++int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)+{+	int ret;+	size_t off = 0;++	while (off < len) {+		errno = 0;++		ret = p_send(s, msg + off, len - off, flags);+		if (ret < 0) {+			net_set_error("Error sending data");+			return -1;+		}++		off += ret;+	}++	return (int)off;+}+++#ifdef GIT_WIN32+int gitno_close(GIT_SOCKET s)+{+	return closesocket(s) == SOCKET_ERROR ? -1 : 0;+}+#else+int gitno_close(GIT_SOCKET s)+{+	return close(s);+}+#endif++int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)+{+	fd_set fds;+	struct timeval tv;++	tv.tv_sec = sec;+	tv.tv_usec = usec;++	FD_ZERO(&fds);+	FD_SET(buf->fd, &fds);++	/* The select(2) interface is silly */+	return select((int)buf->fd + 1, &fds, NULL, NULL, &tv);+}++int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port)+{+	char *colon, *slash, *delim;++	colon = strchr(url, ':');+	slash = strchr(url, '/');++	if (slash == NULL) {+		giterr_set(GITERR_NET, "Malformed URL: missing /");+		return -1;+	}++	if (colon == NULL) {+		*port = git__strdup(default_port);+	} else {+		*port = git__strndup(colon + 1, slash - colon - 1);+	}+	GITERR_CHECK_ALLOC(*port);++	delim = colon == NULL ? slash : colon;+	*host = git__strndup(url, delim - url);+	GITERR_CHECK_ALLOC(*host);++	return 0;+}
+ libgit2/src/netops.h view
@@ -0,0 +1,32 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_netops_h__+#define INCLUDE_netops_h__++#include "posix.h"++typedef struct gitno_buffer {+	char *data;+	size_t len;+	size_t offset;+	GIT_SOCKET fd;+} gitno_buffer;++void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd);+int gitno_recv(gitno_buffer *buf);+void gitno_consume(gitno_buffer *buf, const char *ptr);+void gitno_consume_n(gitno_buffer *buf, size_t cons);++int gitno_connect(GIT_SOCKET *s, const char *host, const char *port);+int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags);+int gitno_close(GIT_SOCKET s);+int gitno_send_chunk_size(int s, size_t len);+int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);++int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port);++#endif
+ libgit2/src/notes.c view
@@ -0,0 +1,548 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "notes.h"++#include "git2.h"+#include "refs.h"+#include "config.h"+#include "iterator.h"++static int find_subtree(git_tree **subtree, const git_oid *root,+			git_repository *repo, const char *target, int *fanout)+{+	int error;+	unsigned int i;+	git_tree *tree;+	const git_tree_entry *entry;++	*subtree = NULL;++	error = git_tree_lookup(&tree, repo, root);+	if (error < 0)+		return error;++	for (i=0; i<git_tree_entrycount(tree); i++) {+		entry = git_tree_entry_byindex(tree, i);++		if (!git__ishex(git_tree_entry_name(entry)))+			continue;++		/*+		 * A notes tree follows a strict byte-based progressive fanout+		 * (i.e. using 2/38, 2/2/36, etc. fanouts, not e.g. 4/36 fanout)+		 */++		if (S_ISDIR(git_tree_entry_attributes(entry))+			&& strlen(git_tree_entry_name(entry)) == 2+			&& !strncmp(git_tree_entry_name(entry), target + *fanout, 2)) {++			/* found matching subtree - unpack and resume lookup */++			git_oid subtree_sha;+			git_oid_cpy(&subtree_sha, git_tree_entry_id(entry));+			git_tree_free(tree);++			*fanout += 2;++			return find_subtree(subtree, &subtree_sha, repo,+					    target, fanout);+		}+	}++	*subtree = tree;+	return 0;+}++static int find_blob(git_oid *blob, git_tree *tree, const char *target)+{+	unsigned int i;+	const git_tree_entry *entry;++	for (i=0; i<git_tree_entrycount(tree); i++) {+		entry = git_tree_entry_byindex(tree, i);++		if (!strcmp(git_tree_entry_name(entry), target)) {+			/* found matching note object - return */++			git_oid_cpy(blob, git_tree_entry_id(entry));+			return 0;+		}+	}+	return GIT_ENOTFOUND;+}++static int note_write(git_oid *out, git_repository *repo,+		      git_signature *author, git_signature *committer,+		      const char *notes_ref, const char *note,+		      const git_oid *tree_sha, const char *target,+		      int nparents, git_commit **parents)+{+	int error, fanout = 0;+	git_oid oid;+	git_tree *tree = NULL;+	git_tree_entry *entry;+	git_treebuilder *tb;++	/* check for existing notes tree */++	if (tree_sha) {+		error = find_subtree(&tree, tree_sha, repo, target, &fanout);+		if (error < 0)+			return error;++		error = find_blob(&oid, tree, target + fanout);+		if (error != GIT_ENOTFOUND) {+			git_tree_free(tree);+			if (!error) {+				giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", target);+				error = GIT_EEXISTS;+			}+			return error;+		}+	}++	/* no matching tree entry - add note object to target tree */++	error = git_treebuilder_create(&tb, tree);+	git_tree_free(tree);++	if (error < 0)+		return error;++	if (!tree_sha)+		/* no notes tree yet - create fanout */+		fanout += 2;++	/* create note object */+	error = git_blob_create_frombuffer(&oid, repo, note, strlen(note));+	if (error < 0) {+		git_treebuilder_free(tb);+		return error;+	}++	error = git_treebuilder_insert(&entry, tb, target + fanout, &oid, 0100644);+	if (error < 0) {+		/* libgit2 doesn't support object removal (gc) yet */+		/* we leave an orphaned blob object behind - TODO */++		git_treebuilder_free(tb);+		return error;+	}++	if (out)+		git_oid_cpy(out, git_tree_entry_id(entry));++	error = git_treebuilder_write(&oid, repo, tb);+	git_treebuilder_free(tb);++	if (error < 0)+		return 0;++	if (!tree_sha) {+		/* create fanout subtree */++		char subtree[3];+		strncpy(subtree, target, 2);+		subtree[2] = '\0';++		error = git_treebuilder_create(&tb, NULL);+		if (error < 0)+			return error;++		error = git_treebuilder_insert(NULL, tb, subtree, &oid, 0040000);+		if (error < 0) {+			git_treebuilder_free(tb);+			return error;+		}++		error = git_treebuilder_write(&oid, repo, tb);++		git_treebuilder_free(tb);++		if (error < 0)+			return error;+	}++	/* create new notes commit */++	error = git_tree_lookup(&tree, repo, &oid);+	if (error < 0)+		return error;++	error = git_commit_create(&oid, repo, notes_ref, author, committer,+				  NULL, GIT_NOTES_DEFAULT_MSG_ADD,+				  tree, nparents, (const git_commit **) parents);++	git_tree_free(tree);++	return error;+}++static int note_lookup(git_note **out, git_repository *repo,+		       const git_oid *tree_sha, const char *target)+{+	int error, fanout = 0;+	git_oid oid;+	git_blob *blob;+	git_tree *tree;+	git_note *note;++	error = find_subtree(&tree, tree_sha, repo, target, &fanout);+	if (error < 0)+		return error;++	error = find_blob(&oid, tree, target + fanout);++	git_tree_free(tree);+	if (error < 0)+		return error;++	error = git_blob_lookup(&blob, repo, &oid);+	if (error < 0)+		return error;++	note = git__malloc(sizeof(git_note));+	GITERR_CHECK_ALLOC(note);++	git_oid_cpy(&note->oid, &oid);+	note->message = git__strdup(git_blob_rawcontent(blob));+	GITERR_CHECK_ALLOC(note->message);++	*out = note;++	git_blob_free(blob);+	return error;+}++static int note_remove(git_repository *repo,+		       git_signature *author, git_signature *committer,+		       const char *notes_ref, const git_oid *tree_sha,+		       const char *target, int nparents, git_commit **parents)+{+	int error, fanout = 0;+	git_oid oid;+	git_tree *tree;+	git_treebuilder *tb;++	error = find_subtree(&tree, tree_sha, repo, target, &fanout);+	if (error < 0)+		return error;++	error = find_blob(&oid, tree, target + fanout);+	if (!error)+		error = git_treebuilder_create(&tb, tree);++	git_tree_free(tree);+	if (error < 0)+		return error;++	error = git_treebuilder_remove(tb, target + fanout);+	if (!error)+		error = git_treebuilder_write(&oid, repo, tb);++	git_treebuilder_free(tb);+	if (error < 0)+		return error;++	/* create new notes commit */++	error = git_tree_lookup(&tree, repo, &oid);+	if (error < 0)+		return error;++	error = git_commit_create(&oid, repo, notes_ref, author, committer,+				  NULL, GIT_NOTES_DEFAULT_MSG_RM,+				  tree, nparents, (const git_commit **) parents);++	git_tree_free(tree);++	return error;+}++static int note_get_default_ref(const char **out, git_repository *repo)+{+	int ret;+	git_config *cfg;++	*out = NULL;++	if (git_repository_config__weakptr(&cfg, repo) < 0)+		return -1;++	ret = git_config_get_string(out, cfg, "core.notesRef");+	if (ret == GIT_ENOTFOUND) {+		*out = GIT_NOTES_DEFAULT_REF;+		return 0;+	}++	return ret;+}++static int normalize_namespace(const char **notes_ref, git_repository *repo)+{+	if (*notes_ref)+		return 0;++	return note_get_default_ref(notes_ref, repo);+}++static int retrieve_note_tree_oid(git_oid *tree_oid_out, git_repository *repo, const char *notes_ref)+{+	int error = -1;+	git_commit *commit = NULL;+	git_oid oid;++	if ((error = git_reference_name_to_oid(&oid, repo, notes_ref)) < 0)+		goto cleanup;++	if (git_commit_lookup(&commit, repo, &oid) < 0)+		goto cleanup;++	git_oid_cpy(tree_oid_out, git_commit_tree_oid(commit));++	error = 0;++cleanup:+	git_commit_free(commit);+	return error;+}++int git_note_read(git_note **out, git_repository *repo,+		  const char *notes_ref, const git_oid *oid)+{+	int error;+	char *target;+	git_oid sha;++	*out = NULL;++	if (normalize_namespace(&notes_ref, repo) < 0)+		return -1;++	if ((error = retrieve_note_tree_oid(&sha, repo, notes_ref)) < 0)+		return error;++	target = git_oid_allocfmt(oid);+	GITERR_CHECK_ALLOC(target);++	error = note_lookup(out, repo, &sha, target);++	git__free(target);+	return error;+}++int git_note_create(+	git_oid *out, git_repository *repo,+	git_signature *author, git_signature *committer,+	const char *notes_ref, const git_oid *oid,+	const char *note)+{+	int error, nparents = 0;+	char *target;+	git_oid sha;+	git_commit *commit = NULL;+	git_reference *ref;++	if (normalize_namespace(&notes_ref, repo) < 0)+		return -1;++	error = git_reference_lookup(&ref, repo, notes_ref);+	if (error < 0 && error != GIT_ENOTFOUND)+		return error;++	if (!error) {+		assert(git_reference_type(ref) == GIT_REF_OID);++		/* lookup existing notes tree oid */++		git_oid_cpy(&sha, git_reference_oid(ref));+		git_reference_free(ref);++		error = git_commit_lookup(&commit, repo, &sha);+		if (error < 0)+			return error;++		git_oid_cpy(&sha, git_commit_tree_oid(commit));+		nparents++;+	}++	target = git_oid_allocfmt(oid);+	GITERR_CHECK_ALLOC(target);++	error = note_write(out, repo, author, committer, notes_ref,+			   note, nparents ? &sha : NULL, target,+			   nparents, &commit);++	git__free(target);+	git_commit_free(commit);+	return error;+}++int git_note_remove(git_repository *repo, const char *notes_ref,+		    git_signature *author, git_signature *committer,+		    const git_oid *oid)+{+	int error;+	char *target;+	git_oid sha;+	git_commit *commit;+	git_reference *ref;++	if (normalize_namespace(&notes_ref, repo) < 0)+		return -1;++	error = git_reference_lookup(&ref, repo, notes_ref);+	if (error < 0)+		return error;++	assert(git_reference_type(ref) == GIT_REF_OID);++	git_oid_cpy(&sha, git_reference_oid(ref));+	git_reference_free(ref);++	error = git_commit_lookup(&commit, repo, &sha);+	if (error < 0)+		return error;++	git_oid_cpy(&sha, git_commit_tree_oid(commit));++	target = git_oid_allocfmt(oid);+	GITERR_CHECK_ALLOC(target);++	error = note_remove(repo, author, committer, notes_ref,+			    &sha, target, 1, &commit);++	git__free(target);+	git_commit_free(commit);+	return error;+}++int git_note_default_ref(const char **out, git_repository *repo)+{+	assert(repo);+	return note_get_default_ref(out, repo);+}++const char * git_note_message(git_note *note)+{+	assert(note);+	return note->message;+}++const git_oid * git_note_oid(git_note *note)+{+	assert(note);+	return &note->oid;+}++void git_note_free(git_note *note)+{+	if (note == NULL)+		return;++	git__free(note->message);+	git__free(note);+}++static int process_entry_path(+	const char* entry_path,+	const git_oid *note_oid,+	int (*note_cb)(git_note_data *note_data, void *payload),+	void *payload)+{+	int i = 0, j = 0, error = -1, len;+	git_buf buf = GIT_BUF_INIT;+	git_note_data note_data;++	if (git_buf_puts(&buf, entry_path) < 0)+		goto cleanup;+	+	len = git_buf_len(&buf);++	while (i < len) {+		if (buf.ptr[i] == '/') {+			i++;+			continue;+		}+		+		if (git__fromhex(buf.ptr[i]) < 0) {+			/* This is not a note entry */+			error = 0;+			goto cleanup;+		}++		if (i != j)+			buf.ptr[j] = buf.ptr[i];++		i++;+		j++;+	}++	buf.ptr[j] = '\0';+	buf.size = j;++	if (j != GIT_OID_HEXSZ) {+		/* This is not a note entry */+		error = 0;+		goto cleanup;+	}++	if (git_oid_fromstr(&note_data.annotated_object_oid, buf.ptr) < 0)+		return -1;++	git_oid_cpy(&note_data.blob_oid, note_oid);++	error = note_cb(&note_data, payload);++cleanup:+	git_buf_free(&buf);+	return error;+}++int git_note_foreach(+	git_repository *repo,+	const char *notes_ref,+	int (*note_cb)(git_note_data *note_data, void *payload),+	void *payload)+{+	int error = -1;+	git_oid tree_oid;+	git_iterator *iter = NULL;+	git_tree *tree = NULL;+	const git_index_entry *item;++	if (normalize_namespace(&notes_ref, repo) < 0)+		return -1;++	if ((error = retrieve_note_tree_oid(&tree_oid, repo, notes_ref)) < 0)+		goto cleanup;++	if (git_tree_lookup(&tree, repo, &tree_oid) < 0)+		goto cleanup;++	if (git_iterator_for_tree(&iter, repo, tree) < 0)+		goto cleanup;++	if (git_iterator_current(iter, &item) < 0)+		goto cleanup;++	while (item) {+		if (process_entry_path(item->path, &item->oid, note_cb, payload) < 0)+			goto cleanup;++		if (git_iterator_advance(iter, &item) < 0)+			goto cleanup;+	}++	error = 0;++cleanup:+	git_iterator_free(iter);+	git_tree_free(tree);+	return error;+}
+ libgit2/src/notes.h view
@@ -0,0 +1,28 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_note_h__+#define INCLUDE_note_h__++#include "common.h"++#include "git2/oid.h"++#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"++#define GIT_NOTES_DEFAULT_MSG_ADD \+	"Notes added by 'git_note_create' from libgit2"++#define GIT_NOTES_DEFAULT_MSG_RM \+	"Notes removed by 'git_note_remove' from libgit2"++struct git_note {+	git_oid oid;++	char *message;+};++#endif /* INCLUDE_notes_h__ */
+ libgit2/src/object.c view
@@ -0,0 +1,333 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include <stdarg.h>++#include "git2/object.h"++#include "common.h"+#include "repository.h"++#include "commit.h"+#include "tree.h"+#include "blob.h"+#include "tag.h"++static const int OBJECT_BASE_SIZE = 4096;++static struct {+	const char	*str;	/* type name string */+	int			loose; /* valid loose object type flag */+	size_t		size;	/* size in bytes of the object structure */+} git_objects_table[] = {+	/* 0 = GIT_OBJ__EXT1 */+	{ "", 0, 0},++	/* 1 = GIT_OBJ_COMMIT */+	{ "commit", 1, sizeof(struct git_commit)},++	/* 2 = GIT_OBJ_TREE */+	{ "tree", 1, sizeof(struct git_tree) },++	/* 3 = GIT_OBJ_BLOB */+	{ "blob", 1, sizeof(struct git_blob) },++	/* 4 = GIT_OBJ_TAG */+	{ "tag", 1, sizeof(struct git_tag) },++	/* 5 = GIT_OBJ__EXT2 */+	{ "", 0, 0 },++	/* 6 = GIT_OBJ_OFS_DELTA */+	{ "OFS_DELTA", 0, 0 },++	/* 7 = GIT_OBJ_REF_DELTA */+	{ "REF_DELTA", 0, 0	}+};++static int create_object(git_object **object_out, git_otype type)+{+	git_object *object = NULL;++	assert(object_out);++	*object_out = NULL;++	switch (type) {+	case GIT_OBJ_COMMIT:+	case GIT_OBJ_TAG:+	case GIT_OBJ_BLOB:+	case GIT_OBJ_TREE:+		object = git__malloc(git_object__size(type));+		GITERR_CHECK_ALLOC(object);+		memset(object, 0x0, git_object__size(type));+		break;++	default:+		giterr_set(GITERR_INVALID, "The given type is invalid");+		return -1;+	}++	object->type = type;++	*object_out = object;+	return 0;+}++int git_object_lookup_prefix(+	git_object **object_out,+	git_repository *repo,+	const git_oid *id,+	unsigned int len,+	git_otype type)+{+	git_object *object = NULL;+	git_odb *odb = NULL;+	git_odb_object *odb_obj;+	int error = 0;++	assert(repo && object_out && id);++	if (len < GIT_OID_MINPREFIXLEN)+		return GIT_EAMBIGUOUS;++	error = git_repository_odb__weakptr(&odb, repo);+	if (error < 0)+		return error;++	if (len > GIT_OID_HEXSZ)+		len = GIT_OID_HEXSZ;++	if (len == GIT_OID_HEXSZ) {+		/* We want to match the full id : we can first look up in the cache,+		 * since there is no need to check for non ambiguousity+		 */+		object = git_cache_get(&repo->objects, id);+		if (object != NULL) {+			if (type != GIT_OBJ_ANY && type != object->type) {+				git_object_free(object);+				giterr_set(GITERR_ODB, "The given type does not match the type in ODB");+				return GIT_ENOTFOUND;+			}++			*object_out = object;+			return 0;+		}++		/* Object was not found in the cache, let's explore the backends.+		 * We could just use git_odb_read_unique_short_oid,+		 * it is the same cost for packed and loose object backends,+		 * but it may be much more costly for sqlite and hiredis.+		 */+		error = git_odb_read(&odb_obj, odb, id);+	} else {+		git_oid short_oid;++		/* We copy the first len*4 bits from id and fill the remaining with 0s */+		memcpy(short_oid.id, id->id, (len + 1) / 2);+		if (len % 2)+			short_oid.id[len / 2] &= 0xF0;+		memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2);++		/* If len < GIT_OID_HEXSZ (a strict short oid was given), we have+		 * 2 options :+		 * - We always search in the cache first. If we find that short oid is+		 *	ambiguous, we can stop. But in all the other cases, we must then+		 *	explore all the backends (to find an object if there was match,+		 *	or to check that oid is not ambiguous if we have found 1 match in+		 *	the cache)+		 * - We never explore the cache, go right to exploring the backends+		 * We chose the latter : we explore directly the backends.+		 */+		error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);+	}++	if (error < 0)+		return error;++	if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {+		git_odb_object_free(odb_obj);+		giterr_set(GITERR_ODB, "The given type does not match the type on the ODB");+		return GIT_ENOTFOUND;+	}++	type = odb_obj->raw.type;++	if (create_object(&object, type) < 0)+		return -1;++	/* Initialize parent object */+	git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);+	object->repo = repo;++	switch (type) {+	case GIT_OBJ_COMMIT:+		error = git_commit__parse((git_commit *)object, odb_obj);+		break;++	case GIT_OBJ_TREE:+		error = git_tree__parse((git_tree *)object, odb_obj);+		break;++	case GIT_OBJ_TAG:+		error = git_tag__parse((git_tag *)object, odb_obj);+		break;++	case GIT_OBJ_BLOB:+		error = git_blob__parse((git_blob *)object, odb_obj);+		break;++	default:+		break;+	}++	git_odb_object_free(odb_obj);++	if (error < 0) {+		git_object__free(object);+		return -1;+	}++	*object_out = git_cache_try_store(&repo->objects, object);+	return 0;+}++int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {+	return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);+}++void git_object__free(void *_obj)+{+	git_object *object = (git_object *)_obj;++	assert(object);++	switch (object->type) {+	case GIT_OBJ_COMMIT:+		git_commit__free((git_commit *)object);+		break;++	case GIT_OBJ_TREE:+		git_tree__free((git_tree *)object);+		break;++	case GIT_OBJ_TAG:+		git_tag__free((git_tag *)object);+		break;++	case GIT_OBJ_BLOB:+		git_blob__free((git_blob *)object);+		break;++	default:+		git__free(object);+		break;+	}+}++void git_object_free(git_object *object)+{+	if (object == NULL)+		return;++	git_cached_obj_decref((git_cached_obj *)object, git_object__free);+}++const git_oid *git_object_id(const git_object *obj)+{+	assert(obj);+	return &obj->cached.oid;+}++git_otype git_object_type(const git_object *obj)+{+	assert(obj);+	return obj->type;+}++git_repository *git_object_owner(const git_object *obj)+{+	assert(obj);+	return obj->repo;+}++const char *git_object_type2string(git_otype type)+{+	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))+		return "";++	return git_objects_table[type].str;+}++git_otype git_object_string2type(const char *str)+{+	size_t i;++	if (!str || !*str)+		return GIT_OBJ_BAD;++	for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)+		if (!strcmp(str, git_objects_table[i].str))+			return (git_otype)i;++	return GIT_OBJ_BAD;+}++int git_object_typeisloose(git_otype type)+{+	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))+		return 0;++	return git_objects_table[type].loose;+}++size_t git_object__size(git_otype type)+{+	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))+		return 0;++	return git_objects_table[type].size;+}++int git_object__resolve_to_type(git_object **obj, git_otype type)+{+	int error = 0;+	git_object *scan, *next;++	if (type == GIT_OBJ_ANY)+		return 0;++	scan = *obj;++	while (!error && scan && git_object_type(scan) != type) {++		switch (git_object_type(scan)) {+		case GIT_OBJ_COMMIT:+		{+			git_tree *tree = NULL;+			error = git_commit_tree(&tree, (git_commit *)scan);+			next = (git_object *)tree;+			break;+		}++		case GIT_OBJ_TAG:+			error = git_tag_target(&next, (git_tag *)scan);+			break;++		default:+			giterr_set(GITERR_REFERENCE, "Object does not resolve to type");+			error = -1;+			next = NULL;+			break;+		}++		git_object_free(scan);+		scan = next;+	}++	*obj = scan;+	return error;+}
+ libgit2/src/odb.c view
@@ -0,0 +1,709 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include <zlib.h>+#include "git2/object.h"+#include "fileops.h"+#include "hash.h"+#include "odb.h"+#include "delta-apply.h"++#include "git2/odb_backend.h"++#define GIT_ALTERNATES_FILE "info/alternates"++/* TODO: is this correct? */+#define GIT_LOOSE_PRIORITY 2+#define GIT_PACKED_PRIORITY 1++typedef struct+{+	git_odb_backend *backend;+	int priority;+	int is_alternate;+} backend_internal;++static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)+{+	const char *type_str = git_object_type2string(obj_type);+	int len = p_snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);+	assert(len > 0 && len <= (int)n);+	return len+1;+}++int git_odb__hashobj(git_oid *id, git_rawobj *obj)+{+	git_buf_vec vec[2];+	char header[64];+	int hdrlen;++	assert(id && obj);++	if (!git_object_typeisloose(obj->type))+		return -1;+	if (!obj->data && obj->len != 0)+		return -1;++	hdrlen = format_object_header(header, sizeof(header), obj->len, obj->type);++	vec[0].data = header;+	vec[0].len = hdrlen;+	vec[1].data = obj->data;+	vec[1].len = obj->len;++	git_hash_vec(id, vec, 2);++	return 0;+}+++static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source)+{+	git_odb_object *object = git__malloc(sizeof(git_odb_object));+	memset(object, 0x0, sizeof(git_odb_object));++	git_oid_cpy(&object->cached.oid, oid);+	memcpy(&object->raw, source, sizeof(git_rawobj));++	return object;+}++static void free_odb_object(void *o)+{+	git_odb_object *object = (git_odb_object *)o;++	if (object != NULL) {+		git__free(object->raw.data);+		git__free(object);+	}+}++const git_oid *git_odb_object_id(git_odb_object *object)+{+	return &object->cached.oid;+}++const void *git_odb_object_data(git_odb_object *object)+{+	return object->raw.data;+}++size_t git_odb_object_size(git_odb_object *object)+{+	return object->raw.len;+}++git_otype git_odb_object_type(git_odb_object *object)+{+	return object->raw.type;+}++void git_odb_object_free(git_odb_object *object)+{+	git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);+}++int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)+{+	int hdr_len;+	char hdr[64], buffer[2048];+	git_hash_ctx *ctx;++	hdr_len = format_object_header(hdr, sizeof(hdr), size, type);++	ctx = git_hash_new_ctx();++	git_hash_update(ctx, hdr, hdr_len);++	while (size > 0) {+		ssize_t read_len = read(fd, buffer, sizeof(buffer));++		if (read_len < 0) {+			git_hash_free_ctx(ctx);+			giterr_set(GITERR_OS, "Error reading file");+			return -1;+		}++		git_hash_update(ctx, buffer, read_len);+		size -= read_len;+	}++	git_hash_final(out, ctx);+	git_hash_free_ctx(ctx);++	return 0;+}++int git_odb__hashlink(git_oid *out, const char *path)+{+	struct stat st;+	git_off_t size;+	int result;++	if (git_path_lstat(path, &st) < 0)+		return -1;++	size = st.st_size;++	if (!git__is_sizet(size)) {+		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");+		return -1;+	}++	if (S_ISLNK(st.st_mode)) {+		char *link_data;+		ssize_t read_len;++		link_data = git__malloc((size_t)size);+		GITERR_CHECK_ALLOC(link_data);++		read_len = p_readlink(path, link_data, (size_t)(size + 1));+		if (read_len != (ssize_t)size) {+			giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);+			return -1;+		}++		result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);+		git__free(link_data);+	} else { +		int fd = git_futils_open_ro(path);+		if (fd < 0)+			return -1;+		result = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB);+		p_close(fd);+	}++	return result;+}++int git_odb_hashfile(git_oid *out, const char *path, git_otype type)+{+	git_off_t size;+	int result, fd = git_futils_open_ro(path);+	if (fd < 0)+		return fd;++	if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) {+		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");+		p_close(fd);+		return -1;+	}++	result = git_odb__hashfd(out, fd, (size_t)size, type);+	p_close(fd);+	return result;+}++int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type)+{+	git_rawobj raw;++	assert(id);++	raw.data = (void *)data;+	raw.len = len;+	raw.type = type;++	return git_odb__hashobj(id, &raw);+}++/**+ * FAKE WSTREAM+ */++typedef struct {+	git_odb_stream stream;+	char *buffer;+	size_t size, written;+	git_otype type;+} fake_wstream;++static int fake_wstream__fwrite(git_oid *oid, git_odb_stream *_stream)+{+	fake_wstream *stream = (fake_wstream *)_stream;+	return _stream->backend->write(oid, _stream->backend, stream->buffer, stream->size, stream->type);+}++static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len)+{+	fake_wstream *stream = (fake_wstream *)_stream;++	if (stream->written + len > stream->size)+		return -1;++	memcpy(stream->buffer + stream->written, data, len);+	stream->written += len;+	return 0;+}++static void fake_wstream__free(git_odb_stream *_stream)+{+	fake_wstream *stream = (fake_wstream *)_stream;++	git__free(stream->buffer);+	git__free(stream);+}++static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, size_t size, git_otype type)+{+	fake_wstream *stream;++	stream = git__calloc(1, sizeof(fake_wstream));+	GITERR_CHECK_ALLOC(stream);++	stream->size = size;+	stream->type = type;+	stream->buffer = git__malloc(size);+	if (stream->buffer == NULL) {+		git__free(stream);+		return -1;+	}++	stream->stream.backend = backend;+	stream->stream.read = NULL; /* read only */+	stream->stream.write = &fake_wstream__write;+	stream->stream.finalize_write = &fake_wstream__fwrite;+	stream->stream.free = &fake_wstream__free;+	stream->stream.mode = GIT_STREAM_WRONLY;++	*stream_p = (git_odb_stream *)stream;+	return 0;+}++/***********************************************************+ *+ * OBJECT DATABASE PUBLIC API+ *+ * Public calls for the ODB functionality+ *+ ***********************************************************/++static int backend_sort_cmp(const void *a, const void *b)+{+	const backend_internal *backend_a = (const backend_internal *)(a);+	const backend_internal *backend_b = (const backend_internal *)(b);++	if (backend_a->is_alternate == backend_b->is_alternate)+		return (backend_b->priority - backend_a->priority);++	return backend_a->is_alternate ? 1 : -1;+}++int git_odb_new(git_odb **out)+{+	git_odb *db = git__calloc(1, sizeof(*db));+	GITERR_CHECK_ALLOC(db);++	if (git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object) < 0 ||+		git_vector_init(&db->backends, 4, backend_sort_cmp) < 0)+	{+		git__free(db);+		return -1;+	}++	*out = db;+	GIT_REFCOUNT_INC(db);+	return 0;+}++static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate)+{+	backend_internal *internal;++	assert(odb && backend);++	/* Check if the backend is already owned by another ODB */+	assert(!backend->odb || backend->odb == odb);++	internal = git__malloc(sizeof(backend_internal));+	GITERR_CHECK_ALLOC(internal);++	internal->backend = backend;+	internal->priority = priority;+	internal->is_alternate = is_alternate;++	if (git_vector_insert(&odb->backends, internal) < 0) {+		git__free(internal);+		return -1;+	}++	git_vector_sort(&odb->backends);+	internal->backend->odb = odb;+	return 0;+}++int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)+{+	return add_backend_internal(odb, backend, priority, 0);+}++int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)+{+	return add_backend_internal(odb, backend, priority, 1);+}++static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates)+{+	git_odb_backend *loose, *packed;++	/* add the loose object backend */+	if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||+		add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0)+		return -1;++	/* add the packed file backend */+	if (git_odb_backend_pack(&packed, objects_dir) < 0 ||+		add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)+		return -1;++	return 0;+}++static int load_alternates(git_odb *odb, const char *objects_dir)+{+	git_buf alternates_path = GIT_BUF_INIT;+	git_buf alternates_buf = GIT_BUF_INIT;+	char *buffer;+	const char *alternate;+	int result = 0;++	if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)+		return -1;++	if (git_path_exists(alternates_path.ptr) == false) {+		git_buf_free(&alternates_path);+		return 0;+	}++	if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) {+		git_buf_free(&alternates_path);+		return -1;+	}++	buffer = (char *)alternates_buf.ptr;++	/* add each alternate as a new backend; one alternate per line */+	while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {+		if (*alternate == '\0' || *alternate == '#')+			continue;++		/* relative path: build based on the current `objects` folder */+		if (*alternate == '.') {+			if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0)+				break;+			alternate = git_buf_cstr(&alternates_path);+		}++		if ((result = add_default_backends(odb, alternate, 1)) < 0)+			break;+	}++	git_buf_free(&alternates_path);+	git_buf_free(&alternates_buf);++	return result;+}++int git_odb_open(git_odb **out, const char *objects_dir)+{+	git_odb *db;++	assert(out && objects_dir);++	*out = NULL;++	if (git_odb_new(&db) < 0)+		return -1;++	if (add_default_backends(db, objects_dir, 0) < 0 ||+		load_alternates(db, objects_dir) < 0)+	{+		git_odb_free(db);+		return -1;+	}++	*out = db;+	return 0;+}++static void odb_free(git_odb *db)+{+	unsigned int i;++	for (i = 0; i < db->backends.length; ++i) {+		backend_internal *internal = git_vector_get(&db->backends, i);+		git_odb_backend *backend = internal->backend;++		if (backend->free) backend->free(backend);+		else git__free(backend);++		git__free(internal);+	}++	git_vector_free(&db->backends);+	git_cache_free(&db->cache);+	git__free(db);+}++void git_odb_free(git_odb *db)+{+	if (db == NULL)+		return;++	GIT_REFCOUNT_DEC(db, odb_free);+}++int git_odb_exists(git_odb *db, const git_oid *id)+{+	git_odb_object *object;+	unsigned int i;+	bool found = false;++	assert(db && id);++	if ((object = git_cache_get(&db->cache, id)) != NULL) {+		git_odb_object_free(object);+		return (int)true;+	}++	for (i = 0; i < db->backends.length && !found; ++i) {+		backend_internal *internal = git_vector_get(&db->backends, i);+		git_odb_backend *b = internal->backend;++		if (b->exists != NULL)+			found = b->exists(b, id);+	}++	return (int)found;+}++int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)+{+	unsigned int i;+	int error = GIT_ENOTFOUND;+	git_odb_object *object;++	assert(db && id);++	if ((object = git_cache_get(&db->cache, id)) != NULL) {+		*len_p = object->raw.len;+		*type_p = object->raw.type;+		git_odb_object_free(object);+		return 0;+	}++	for (i = 0; i < db->backends.length && error < 0; ++i) {+		backend_internal *internal = git_vector_get(&db->backends, i);+		git_odb_backend *b = internal->backend;++		if (b->read_header != NULL)+			error = b->read_header(len_p, type_p, b, id);+	}++	if (!error || error == GIT_PASSTHROUGH)+		return 0;++	/*+	 * no backend could read only the header.+	 * try reading the whole object and freeing the contents+	 */+	if ((error = git_odb_read(&object, db, id)) < 0)+		return error; /* error already set - pass along */++	*len_p = object->raw.len;+	*type_p = object->raw.type;+	git_odb_object_free(object);+	return 0;+}++int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)+{+	unsigned int i;+	int error = GIT_ENOTFOUND;+	git_rawobj raw;++	assert(out && db && id);++	*out = git_cache_get(&db->cache, id);+	if (*out != NULL)+		return 0;++	for (i = 0; i < db->backends.length && error < 0; ++i) {+		backend_internal *internal = git_vector_get(&db->backends, i);+		git_odb_backend *b = internal->backend;++		if (b->read != NULL)+			error = b->read(&raw.data, &raw.len, &raw.type, b, id);+	}++	/* TODO: If no backends are configured, this returns GIT_ENOTFOUND but+	 * will never have called giterr_set().+	 */++	if (error && error != GIT_PASSTHROUGH)+		return error;++	*out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));+	return 0;+}++int git_odb_read_prefix(+	git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)+{+	unsigned int i;+	int error = GIT_ENOTFOUND;+	git_oid found_full_oid = {{0}};+	git_rawobj raw;+	bool found = false;++	assert(out && db);++	if (len < GIT_OID_MINPREFIXLEN)+		return git_odb__error_ambiguous("prefix length too short");++	if (len > GIT_OID_HEXSZ)+		len = GIT_OID_HEXSZ;++	if (len == GIT_OID_HEXSZ) {+		*out = git_cache_get(&db->cache, short_id);+		if (*out != NULL)+			return 0;+	}++	for (i = 0; i < db->backends.length; ++i) {+		backend_internal *internal = git_vector_get(&db->backends, i);+		git_odb_backend *b = internal->backend;++		if (b->read != NULL) {+			git_oid full_oid;+			error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);+			if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)+				continue;++			if (error)+				return error;++			if (found && git_oid_cmp(&full_oid, &found_full_oid))+				return git_odb__error_ambiguous("multiple matches for prefix");+			found_full_oid = full_oid;+			found = true;+		}+	}++	if (!found)+		return git_odb__error_notfound("no match for prefix", short_id);++	*out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw));+	return 0;+}++int git_odb_write(+	git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)+{+	unsigned int i;+	int error = GIT_ERROR;+	git_odb_stream *stream;++	assert(oid && db);++	for (i = 0; i < db->backends.length && error < 0; ++i) {+		backend_internal *internal = git_vector_get(&db->backends, i);+		git_odb_backend *b = internal->backend;++		/* we don't write in alternates! */+		if (internal->is_alternate)+			continue;++		if (b->write != NULL)+			error = b->write(oid, b, data, len, type);+	}++	if (!error || error == GIT_PASSTHROUGH)+		return 0;++	/* if no backends were able to write the object directly, we try a streaming+	 * write to the backends; just write the whole object into the stream in one+	 * push */++	if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0)+		return error;++	stream->write(stream, data, len);+	error = stream->finalize_write(oid, stream);+	stream->free(stream);++	return error;+}++int git_odb_open_wstream(+	git_odb_stream **stream, git_odb *db, size_t size, git_otype type)+{+	unsigned int i;+	int error = GIT_ERROR;++	assert(stream && db);++	for (i = 0; i < db->backends.length && error < 0; ++i) {+		backend_internal *internal = git_vector_get(&db->backends, i);+		git_odb_backend *b = internal->backend;++		/* we don't write in alternates! */+		if (internal->is_alternate)+			continue;++		if (b->writestream != NULL)+			error = b->writestream(stream, b, size, type);+		else if (b->write != NULL)+			error = init_fake_wstream(stream, b, size, type);+	}++	if (error == GIT_PASSTHROUGH)+		error = 0;++	return error;+}++int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)+{+	unsigned int i;+	int error = GIT_ERROR;++	assert(stream && db);++	for (i = 0; i < db->backends.length && error < 0; ++i) {+		backend_internal *internal = git_vector_get(&db->backends, i);+		git_odb_backend *b = internal->backend;++		if (b->readstream != NULL)+			error = b->readstream(stream, b, oid);+	}++	if (error == GIT_PASSTHROUGH)+		error = 0;++	return error;+}++int git_odb__error_notfound(const char *message, const git_oid *oid)+{+	if (oid != NULL) {+		char oid_str[GIT_OID_HEXSZ + 1];+		git_oid_tostr(oid_str, sizeof(oid_str), oid);+		giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str);+	} else+		giterr_set(GITERR_ODB, "Object not found - %s", message);++	return GIT_ENOTFOUND;+}++int git_odb__error_ambiguous(const char *message)+{+	giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message);+	return GIT_EAMBIGUOUS;+}+
+ libgit2/src/odb.h view
@@ -0,0 +1,80 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_odb_h__+#define INCLUDE_odb_h__++#include "git2/odb.h"+#include "git2/oid.h"+#include "git2/types.h"++#include "vector.h"+#include "cache.h"+#include "posix.h"++#define GIT_OBJECTS_DIR "objects/"+#define GIT_OBJECT_DIR_MODE 0777+#define GIT_OBJECT_FILE_MODE 0444++/* DO NOT EXPORT */+typedef struct {+	void *data;			/**< Raw, decompressed object data. */+	size_t len;			/**< Total number of bytes in data. */+	git_otype type;		/**< Type of this object. */+} git_rawobj;++/* EXPORT */+struct git_odb_object {+	git_cached_obj cached;+	git_rawobj raw;+};++/* EXPORT */+struct git_odb {+	git_refcount rc;+	git_vector backends;+	git_cache cache;+};++/*+ * Hash a git_rawobj internally.+ * The `git_rawobj` is supposed to be previously initialized+ */+int git_odb__hashobj(git_oid *id, git_rawobj *obj);++/*+ * Hash an open file descriptor.+ * This is a performance call when the contents of a fd need to be hashed,+ * but the fd is already open and we have the size of the contents.+ *+ * Saves us some `stat` calls.+ *+ * The fd is never closed, not even on error. It must be opened and closed+ * by the caller+ */+int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);++/*+ * Hash a `path`, assuming it could be a POSIX symlink: if the path is a symlink,+ * then the raw contents of the symlink will be hashed. Otherwise, this will+ * fallback to `git_odb__hashfd`.+ *+ * The hash type for this call is always `GIT_OBJ_BLOB` because symlinks may only+ * point to blobs.+ */+int git_odb__hashlink(git_oid *out, const char *path);++/*+ * Generate a GIT_ENOTFOUND error for the ODB.+ */+int git_odb__error_notfound(const char *message, const git_oid *oid);++/*+ * Generate a GIT_EAMBIGUOUS error for the ODB.+ */+int git_odb__error_ambiguous(const char *message);++#endif
+ libgit2/src/odb_loose.c view
@@ -0,0 +1,852 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include <zlib.h>+#include "git2/object.h"+#include "git2/oid.h"+#include "fileops.h"+#include "hash.h"+#include "odb.h"+#include "delta-apply.h"+#include "filebuf.h"++#include "git2/odb_backend.h"+#include "git2/types.h"++typedef struct { /* object header data */+	git_otype type; /* object type */+	size_t	size; /* object size */+} obj_hdr;++typedef struct {+	git_odb_stream stream;+	git_filebuf fbuf;+} loose_writestream;++typedef struct loose_backend {+	git_odb_backend parent;++	int object_zlib_level; /** loose object zlib compression level. */+	int fsync_object_files; /** loose object file fsync flag. */+	char *objects_dir;+} loose_backend;++/* State structure for exploring directories,+ * in order to locate objects matching a short oid.+ */+typedef struct {+	size_t dir_len;+	unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */+	unsigned int short_oid_len;+	int found;				/* number of matching+						 * objects already found */+	unsigned char res_oid[GIT_OID_HEXSZ];	/* hex formatted oid of+						 * the object found */+} loose_locate_object_state;+++/***********************************************************+ *+ * MISCELANEOUS HELPER FUNCTIONS+ *+ ***********************************************************/++static int object_file_name(git_buf *name, const char *dir, const git_oid *id)+{+	git_buf_sets(name, dir);++	/* expand length for 40 hex sha1 chars + 2 * '/' + '\0' */+	if (git_buf_grow(name, git_buf_len(name) + GIT_OID_HEXSZ + 3) < 0)+		return -1;++	git_path_to_dir(name);++	/* loose object filename: aa/aaa... (41 bytes) */+	git_oid_pathfmt(name->ptr + git_buf_len(name), id);+	name->size += GIT_OID_HEXSZ + 1;+	name->ptr[name->size] = '\0';++	return 0;+}+++static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)+{+	unsigned char c;+	unsigned char *data = (unsigned char *)obj->ptr;+	size_t shift, size, used = 0;++	if (git_buf_len(obj) == 0)+		return 0;++	c = data[used++];+	hdr->type = (c >> 4) & 7;++	size = c & 15;+	shift = 4;+	while (c & 0x80) {+		if (git_buf_len(obj) <= used)+			return 0;+		if (sizeof(size_t) * 8 <= shift)+			return 0;+		c = data[used++];+		size += (c & 0x7f) << shift;+		shift += 7;+	}+	hdr->size = size;++	return used;+}++static size_t get_object_header(obj_hdr *hdr, unsigned char *data)+{+	char c, typename[10];+	size_t size, used = 0;++	/*+	 * type name string followed by space.+	 */+	while ((c = data[used]) != ' ') {+		typename[used++] = c;+		if (used >= sizeof(typename))+			return 0;+	}+	typename[used] = 0;+	if (used == 0)+		return 0;+	hdr->type = git_object_string2type(typename);+	used++; /* consume the space */++	/*+	 * length follows immediately in decimal (without+	 * leading zeros).+	 */+	size = data[used++] - '0';+	if (size > 9)+		return 0;+	if (size) {+		while ((c = data[used]) != '\0') {+			size_t d = c - '0';+			if (d > 9)+				break;+			used++;+			size = size * 10 + d;+		}+	}+	hdr->size = size;++	/*+	 * the length must be followed by a zero byte+	 */+	if (data[used++] != '\0')+		return 0;++	return used;+}++++/***********************************************************+ *+ * ZLIB RELATED FUNCTIONS+ *+ ***********************************************************/++static void init_stream(z_stream *s, void *out, size_t len)+{+	memset(s, 0, sizeof(*s));+	s->next_out = out;+	s->avail_out = (uInt)len;+}++static void set_stream_input(z_stream *s, void *in, size_t len)+{+	s->next_in = in;+	s->avail_in = (uInt)len;+}++static void set_stream_output(z_stream *s, void *out, size_t len)+{+	s->next_out = out;+	s->avail_out = (uInt)len;+}+++static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)+{+	int status;++	init_stream(s, out, len);+	set_stream_input(s, obj->ptr, git_buf_len(obj));++	if ((status = inflateInit(s)) < Z_OK)+		return status;++	return inflate(s, 0);+}++static int finish_inflate(z_stream *s)+{+	int status = Z_OK;++	while (status == Z_OK)+		status = inflate(s, Z_FINISH);++	inflateEnd(s);++	if ((status != Z_STREAM_END) || (s->avail_in != 0)) {+		giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely");+		return -1;+	}++	return 0;+}++static int is_zlib_compressed_data(unsigned char *data)+{+	unsigned int w;++	w = ((unsigned int)(data[0]) << 8) + data[1];+	return (data[0] & 0x8F) == 0x08 && !(w % 31);+}++static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)+{+	z_stream zs;+	int status = Z_OK;++	memset(&zs, 0x0, sizeof(zs));++	zs.next_out = out;+	zs.avail_out = (uInt)outlen;++	zs.next_in = in;+	zs.avail_in = (uInt)inlen;++	if (inflateInit(&zs) < Z_OK) {+		giterr_set(GITERR_ZLIB, "Failed to inflate buffer");+		return -1;+	}++	while (status == Z_OK)+		status = inflate(&zs, Z_FINISH);++	inflateEnd(&zs);++	if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||+		zs.total_out != outlen)+	{+		giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely");+		return -1;+	}++	return 0;+}++static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)+{+	unsigned char *buf, *head = hb;+	size_t tail;++	/*+	 * allocate a buffer to hold the inflated data and copy the+	 * initial sequence of inflated data from the tail of the+	 * head buffer, if any.+	 */+	if ((buf = git__malloc(hdr->size + 1)) == NULL) {+		inflateEnd(s);+		return NULL;+	}+	tail = s->total_out - used;+	if (used > 0 && tail > 0) {+		if (tail > hdr->size)+			tail = hdr->size;+		memcpy(buf, head + used, tail);+	}+	used = tail;++	/*+	 * inflate the remainder of the object data, if any+	 */+	if (hdr->size < used)+		inflateEnd(s);+	else {+		set_stream_output(s, buf + used, hdr->size - used);+		if (finish_inflate(s)) {+			git__free(buf);+			return NULL;+		}+	}++	return buf;+}++/*+ * At one point, there was a loose object format that was intended to+ * mimic the format used in pack-files. This was to allow easy copying+ * of loose object data into packs. This format is no longer used, but+ * we must still read it.+ */+static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)+{+	unsigned char *in, *buf;+	obj_hdr hdr;+	size_t len, used;++	/*+	 * read the object header, which is an (uncompressed)+	 * binary encoding of the object type and size.+	 */+	if ((used = get_binary_object_header(&hdr, obj)) == 0 ||+		!git_object_typeisloose(hdr.type)) {+		giterr_set(GITERR_ODB, "Failed to inflate loose object.");+		return -1;+	}++	/*+	 * allocate a buffer and inflate the data into it+	 */+	buf = git__malloc(hdr.size + 1);+	GITERR_CHECK_ALLOC(buf);++	in = ((unsigned char *)obj->ptr) + used;+	len = obj->size - used;+	if (inflate_buffer(in, len, buf, hdr.size) < 0) {+		git__free(buf);+		return -1;+	}+	buf[hdr.size] = '\0';++	out->data = buf;+	out->len = hdr.size;+	out->type = hdr.type;++	return 0;+}++static int inflate_disk_obj(git_rawobj *out, git_buf *obj)+{+	unsigned char head[64], *buf;+	z_stream zs;+	obj_hdr hdr;+	size_t used;++	/*+	 * check for a pack-like loose object+	 */+	if (!is_zlib_compressed_data((unsigned char *)obj->ptr))+		return inflate_packlike_loose_disk_obj(out, obj);++	/*+	 * inflate the initial part of the io buffer in order+	 * to parse the object header (type and size).+	 */+	if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||+		(used = get_object_header(&hdr, head)) == 0 ||+		!git_object_typeisloose(hdr.type))+	{+		giterr_set(GITERR_ODB, "Failed to inflate disk object.");+		return -1;+	}++	/*+	 * allocate a buffer and inflate the object data into it+	 * (including the initial sequence in the head buffer).+	 */+	if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)+		return -1;+	buf[hdr.size] = '\0';++	out->data = buf;+	out->len = hdr.size;+	out->type = hdr.type;++	return 0;+}+++++++/***********************************************************+ *+ * ODB OBJECT READING & WRITING+ *+ * Backend for the public API; read headers and full objects+ * from the ODB. Write raw data to the ODB.+ *+ ***********************************************************/++static int read_loose(git_rawobj *out, git_buf *loc)+{+	int error;+	git_buf obj = GIT_BUF_INIT;++	assert(out && loc);++	if (git_buf_oom(loc))+		return -1;++	out->data = NULL;+	out->len = 0;+	out->type = GIT_OBJ_BAD;++	if (!(error = git_futils_readbuffer(&obj, loc->ptr)))+		error = inflate_disk_obj(out, &obj);++	git_buf_free(&obj);++	return error;+}++static int read_header_loose(git_rawobj *out, git_buf *loc)+{+	int error = 0, z_return = Z_ERRNO, read_bytes;+	git_file fd;+	z_stream zs;+	obj_hdr header_obj;+	unsigned char raw_buffer[16], inflated_buffer[64];++	assert(out && loc);++	if (git_buf_oom(loc))+		return -1;++	out->data = NULL;++	if ((fd = git_futils_open_ro(loc->ptr)) < 0)+		return fd;++	init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));++	z_return = inflateInit(&zs);++	while (z_return == Z_OK) {+		if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {+			set_stream_input(&zs, raw_buffer, read_bytes);+			z_return = inflate(&zs, 0);+		} else+			z_return = Z_STREAM_END;+	}++	if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)+		|| get_object_header(&header_obj, inflated_buffer) == 0+		|| git_object_typeisloose(header_obj.type) == 0)+	{+		giterr_set(GITERR_ZLIB, "Failed to read loose object header");+		error = -1;+	} else {+		out->len = header_obj.size;+		out->type = header_obj.type;+	}++	finish_inflate(&zs);+	p_close(fd);++	return error;+}++static int locate_object(+	git_buf *object_location,+	loose_backend *backend,+	const git_oid *oid)+{+	int error = object_file_name(object_location, backend->objects_dir, oid);++	if (!error && !git_path_exists(object_location->ptr))+		return GIT_ENOTFOUND;++	return error;+}++/* Explore an entry of a directory and see if it matches a short oid */+static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {+	loose_locate_object_state *sstate = (loose_locate_object_state *)state;++	if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {+		/* Entry cannot be an object. Continue to next entry */+		return 0;+	}++	if (git_path_isdir(pathbuf->ptr) == false) {+		/* We are already in the directory matching the 2 first hex characters,+		 * compare the first ncmp characters of the oids */+		if (!memcmp(sstate->short_oid + 2,+			(unsigned char *)pathbuf->ptr + sstate->dir_len,+			sstate->short_oid_len - 2)) {++			if (!sstate->found) {+				sstate->res_oid[0] = sstate->short_oid[0];+				sstate->res_oid[1] = sstate->short_oid[1];+				memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);+			}+			sstate->found++;+		}+	}++	if (sstate->found > 1)+		return git_odb__error_ambiguous("multiple matches in loose objects");++	return 0;+}++/* Locate an object matching a given short oid */+static int locate_object_short_oid(+	git_buf *object_location,+	git_oid *res_oid,+	loose_backend *backend,+	const git_oid *short_oid,+	unsigned int len)+{+	char *objects_dir = backend->objects_dir;+	size_t dir_len = strlen(objects_dir);+	loose_locate_object_state state;+	int error;++	/* prealloc memory for OBJ_DIR/xx/ */+	if (git_buf_grow(object_location, dir_len + 5) < 0)+		return -1;++	git_buf_sets(object_location, objects_dir);+	git_path_to_dir(object_location);++	/* save adjusted position at end of dir so it can be restored later */+	dir_len = git_buf_len(object_location);++	/* Convert raw oid to hex formatted oid */+	git_oid_fmt((char *)state.short_oid, short_oid);++	/* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */+	if (git_buf_printf(object_location, "%.2s/", state.short_oid) < 0)+		return -1;++	/* Check that directory exists */+	if (git_path_isdir(object_location->ptr) == false)+		return git_odb__error_notfound("no matching loose object for prefix", short_oid);++	state.dir_len = git_buf_len(object_location);+	state.short_oid_len = len;+	state.found = 0;++	/* Explore directory to find a unique object matching short_oid */+	error = git_path_direach(+		object_location, fn_locate_object_short_oid, &state);+	if (error)+		return error;++	if (!state.found)+		return git_odb__error_notfound("no matching loose object for prefix", short_oid);++	/* Convert obtained hex formatted oid to raw */+	error = git_oid_fromstr(res_oid, (char *)state.res_oid);+	if (error)+		return error;++	/* Update the location according to the oid obtained */++	git_buf_truncate(object_location, dir_len);+	if (git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2) < 0)+		return -1;++	git_oid_pathfmt(object_location->ptr + dir_len, res_oid);++	object_location->size += GIT_OID_HEXSZ + 1;+	object_location->ptr[object_location->size] = '\0';++	return 0;+}++++++++++/***********************************************************+ *+ * LOOSE BACKEND PUBLIC API+ *+ * Implement the git_odb_backend API calls+ *+ ***********************************************************/++static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)+{+	git_buf object_path = GIT_BUF_INIT;+	git_rawobj raw;+	int error;++	assert(backend && oid);++	raw.len = 0;+	raw.type = GIT_OBJ_BAD;++	if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)+		error = git_odb__error_notfound("no matching loose object", oid);+	else if ((error = read_header_loose(&raw, &object_path)) == 0) {+		*len_p = raw.len;+		*type_p = raw.type;+	}++	git_buf_free(&object_path);++	return error;+}++static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)+{+	git_buf object_path = GIT_BUF_INIT;+	git_rawobj raw;+	int error = 0;++	assert(backend && oid);++	if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)+		error = git_odb__error_notfound("no matching loose object", oid);+	else if ((error = read_loose(&raw, &object_path)) == 0) {+		*buffer_p = raw.data;+		*len_p = raw.len;+		*type_p = raw.type;+	}++	git_buf_free(&object_path);++	return error;+}++static int loose_backend__read_prefix(+	git_oid *out_oid,+	void **buffer_p,+	size_t *len_p,+	git_otype *type_p,+	git_odb_backend *backend,+	const git_oid *short_oid,+	unsigned int len)+{+	int error = 0;++	if (len < GIT_OID_MINPREFIXLEN)+		error = git_odb__error_ambiguous("prefix length too short");++	else if (len >= GIT_OID_HEXSZ) {+		/* We can fall back to regular read method */+		error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);+		if (!error)+			git_oid_cpy(out_oid, short_oid);+	} else {+		git_buf object_path = GIT_BUF_INIT;+		git_rawobj raw;++		assert(backend && short_oid);++		if ((error = locate_object_short_oid(&object_path, out_oid,+				(loose_backend *)backend, short_oid, len)) == 0 &&+			(error = read_loose(&raw, &object_path)) == 0)+		{+			*buffer_p = raw.data;+			*len_p = raw.len;+			*type_p = raw.type;+		}++		git_buf_free(&object_path);+	}++	return error;+}++static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)+{+	git_buf object_path = GIT_BUF_INIT;+	int error;++	assert(backend && oid);++	error = locate_object(&object_path, (loose_backend *)backend, oid);++	git_buf_free(&object_path);++	return !error;+}++static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)+{+	loose_writestream *stream = (loose_writestream *)_stream;+	loose_backend *backend = (loose_backend *)_stream->backend;+	git_buf final_path = GIT_BUF_INIT;+	int error = 0;++	if (git_filebuf_hash(oid, &stream->fbuf) < 0 ||+		object_file_name(&final_path, backend->objects_dir, oid) < 0 ||+		git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0)+		error = -1;+	/*+	 * Don't try to add an existing object to the repository. This+	 * is what git does and allows us to sidestep the fact that+	 * we're not allowed to overwrite a read-only file on Windows.+	 */+	else if (git_path_exists(final_path.ptr) == true)+		git_filebuf_cleanup(&stream->fbuf);+	else+		error = git_filebuf_commit_at(+			&stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);++	git_buf_free(&final_path);++	return error;+}++static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)+{+	loose_writestream *stream = (loose_writestream *)_stream;+	return git_filebuf_write(&stream->fbuf, data, len);+}++static void loose_backend__stream_free(git_odb_stream *_stream)+{+	loose_writestream *stream = (loose_writestream *)_stream;++	git_filebuf_cleanup(&stream->fbuf);+	git__free(stream);+}++static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)+{+	const char *type_str = git_object_type2string(obj_type);+	int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);++	assert(len > 0);				/* otherwise snprintf() is broken */+	assert(((size_t)len) < n);		/* otherwise the caller is broken! */++	return len+1;+}++static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)+{+	loose_backend *backend;+	loose_writestream *stream = NULL;+	char hdr[64];+	git_buf tmp_path = GIT_BUF_INIT;+	int hdrlen;++	assert(_backend);++	backend = (loose_backend *)_backend;+	*stream_out = NULL;++	hdrlen = format_object_header(hdr, sizeof(hdr), length, type);++	stream = git__calloc(1, sizeof(loose_writestream));+	GITERR_CHECK_ALLOC(stream);++	stream->stream.backend = _backend;+	stream->stream.read = NULL; /* read only */+	stream->stream.write = &loose_backend__stream_write;+	stream->stream.finalize_write = &loose_backend__stream_fwrite;+	stream->stream.free = &loose_backend__stream_free;+	stream->stream.mode = GIT_STREAM_WRONLY;++	if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||+		git_filebuf_open(&stream->fbuf, tmp_path.ptr,+			GIT_FILEBUF_HASH_CONTENTS |+			GIT_FILEBUF_TEMPORARY |+			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0 ||+		stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)+	{+		git_filebuf_cleanup(&stream->fbuf);+		git__free(stream);+		stream = NULL;+	}+	git_buf_free(&tmp_path);+	*stream_out = (git_odb_stream *)stream;++	return !stream ? -1 : 0;+}++static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)+{+	int error = 0, header_len;+	git_buf final_path = GIT_BUF_INIT;+	char header[64];+	git_filebuf fbuf = GIT_FILEBUF_INIT;+	loose_backend *backend;++	backend = (loose_backend *)_backend;++	/* prepare the header for the file */+	header_len = format_object_header(header, sizeof(header), len, type);++	if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||+		git_filebuf_open(&fbuf, final_path.ptr,+			GIT_FILEBUF_HASH_CONTENTS |+			GIT_FILEBUF_TEMPORARY |+			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0)+	{+		error = -1;+		goto cleanup;+	}++	git_filebuf_write(&fbuf, header, header_len);+	git_filebuf_write(&fbuf, data, len);+	git_filebuf_hash(oid, &fbuf);++	if (object_file_name(&final_path, backend->objects_dir, oid) < 0 ||+		git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0 ||+		git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE) < 0)+		error = -1;++cleanup:+	if (error < 0)+		git_filebuf_cleanup(&fbuf);+	git_buf_free(&final_path);+	return error;+}++static void loose_backend__free(git_odb_backend *_backend)+{+	loose_backend *backend;+	assert(_backend);+	backend = (loose_backend *)_backend;++	git__free(backend->objects_dir);+	git__free(backend);+}++int git_odb_backend_loose(+	git_odb_backend **backend_out,+	const char *objects_dir,+	int compression_level,+	int do_fsync)+{+	loose_backend *backend;++	backend = git__calloc(1, sizeof(loose_backend));+	GITERR_CHECK_ALLOC(backend);++	backend->objects_dir = git__strdup(objects_dir);+	GITERR_CHECK_ALLOC(backend->objects_dir);++	if (compression_level < 0)+		compression_level = Z_BEST_SPEED;++	backend->object_zlib_level = compression_level;+	backend->fsync_object_files = do_fsync;++	backend->parent.read = &loose_backend__read;+	backend->parent.write = &loose_backend__write;+	backend->parent.read_prefix = &loose_backend__read_prefix;+	backend->parent.read_header = &loose_backend__read_header;+	backend->parent.writestream = &loose_backend__stream;+	backend->parent.exists = &loose_backend__exists;+	backend->parent.free = &loose_backend__free;++	*backend_out = (git_odb_backend *)backend;+	return 0;+}
+ libgit2/src/odb_pack.c view
@@ -0,0 +1,473 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include <zlib.h>+#include "git2/repository.h"+#include "git2/oid.h"+#include "fileops.h"+#include "hash.h"+#include "odb.h"+#include "delta-apply.h"+#include "sha1_lookup.h"+#include "mwindow.h"+#include "pack.h"++#include "git2/odb_backend.h"++struct pack_backend {+	git_odb_backend parent;+	git_vector packs;+	struct git_pack_file *last_found;+	char *pack_folder;+	time_t pack_folder_mtime;+};++/**+ * The wonderful tale of a Packed Object lookup query+ * ===================================================+ *	A riveting and epic story of epicness and ASCII+ *			art, presented by yours truly,+ *				Sir Vicent of Marti+ *+ *+ *	Chapter 1: Once upon a time...+ *	Initialization of the Pack Backend+ *	--------------------------------------------------+ *+ *	# git_odb_backend_pack+ *	| Creates the pack backend structure, initializes the+ *	| callback pointers to our default read() and exist() methods,+ *	| and tries to preload all the known packfiles in the ODB.+ * |+ *	|-# packfile_load_all+ *	 | Tries to find the `pack` folder, if it exists. ODBs without+ *	 | a pack folder are ignored altogether. If there's a `pack` folder+ *	 | we run a `dirent` callback through every file in the pack folder+ *	 | to find our packfiles. The packfiles are then sorted according+ *	 | to a sorting callback.+ * 	 |+ *	 |-# packfile_load__cb+ *	 | | This callback is called from `dirent` with every single file+ *	 | | inside the pack folder. We find the packs by actually locating+ *	 | | their index (ends in ".idx"). From that index, we verify that+ *	 | | the corresponding packfile exists and is valid, and if so, we+ *	| | add it to the pack list.+ *	 | |+ *	 | |-# packfile_check+ *	 |		Make sure that there's a packfile to back this index, and store+ *	 |		some very basic information regarding the packfile itself,+ *	 |		such as the full path, the size, and the modification time.+ *	 |		We don't actually open the packfile to check for internal consistency.+ *	|+ *	|-# packfile_sort__cb+ *		Sort all the preloaded packs according to some specific criteria:+ *		we prioritize the "newer" packs because it's more likely they+ *		contain the objects we are looking for, and we prioritize local+ *		packs over remote ones.+ *+ *+ *+ *	Chapter 2: To be, or not to be...+ *	A standard packed `exist` query for an OID+ *	--------------------------------------------------+ *+ * # pack_backend__exists+ * | Check if the given SHA1 oid exists in any of the packs+ * | that have been loaded for our ODB.+ * |+ * |-# pack_entry_find+ *	| Iterate through all the packs that have been preloaded+ *	| (starting by the pack where the latest object was found)+ *	| to try to find the OID in one of them.+ *	|+ *	|-# pack_entry_find1+ *		| Check the index of an individual pack to see if the SHA1+ *		| OID can be found. If we can find the offset to that SHA1+ *		| inside of the index, that means the object is contained+ *		| inside of the packfile and we can stop searching.+ *		| Before returning, we verify that the packfile behing the+ *		| index we are searching still exists on disk.+ *		|+ *		|-# pack_entry_find_offset+ *		| | Mmap the actual index file to disk if it hasn't been opened+ *		| | yet, and run a binary search through it to find the OID.+ *		| | See <http://book.git-scm.com/7_the_packfile.html> for specifics+ *		| | on the Packfile Index format and how do we find entries in it.+ *		| |+ *		| |-# pack_index_open+ *		|	| Guess the name of the index based on the full path to the+ *		|	| packfile, open it and verify its contents. Only if the index+ *		|	| has not been opened already.+ *		|	|+ *		|	|-# pack_index_check+ *		|		Mmap the index file and do a quick run through the header+ *		|		to guess the index version (right now we support v1 and v2),+ *		|		and to verify that the size of the index makes sense.+ *		|+ *		|-# packfile_open+ *			See `packfile_open` in Chapter 3+ *+ *+ *+ *	Chapter 3: The neverending story...+ *	A standard packed `lookup` query for an OID+ *	--------------------------------------------------+ *	TODO+ *+ */+++/***********************************************************+ *+ * FORWARD DECLARATIONS+ *+ ***********************************************************/++static void pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p);+static int pack_window_contains(git_mwindow *win, off_t offset);++static int packfile_sort__cb(const void *a_, const void *b_);++static int packfile_load__cb(void *_data, git_buf *path);+static int packfile_refresh_all(struct pack_backend *backend);++static int pack_entry_find(struct git_pack_entry *e,+	struct pack_backend *backend, const git_oid *oid);++/* Can find the offset of an object given+ * a prefix of an identifier.+ * Sets GIT_EAMBIGUOUS if short oid is ambiguous.+ * This method assumes that len is between+ * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.+ */+static int pack_entry_find_prefix(+	struct git_pack_entry *e,+	struct pack_backend *backend,+	const git_oid *short_oid,+	unsigned int len);++++/***********************************************************+ *+ * PACK WINDOW MANAGEMENT+ *+ ***********************************************************/++GIT_INLINE(void) pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p)+{+	GIT_UNUSED(backend);+	git_mwindow_free_all(&p->mwf);+}++GIT_INLINE(int) pack_window_contains(git_mwindow *win, off_t offset)+{+	/* We must promise at least 20 bytes (one hash) after the+	 * offset is available from this window, otherwise the offset+	 * is not actually in this window and a different window (which+	 * has that one hash excess) must be used. This is to support+	 * the object header and delta base parsing routines below.+	 */+	return git_mwindow_contains(win, offset + 20);+}++static int packfile_sort__cb(const void *a_, const void *b_)+{+	const struct git_pack_file *a = a_;+	const struct git_pack_file *b = b_;+	int st;++	/*+	 * Local packs tend to contain objects specific to our+	 * variant of the project than remote ones. In addition,+	 * remote ones could be on a network mounted filesystem.+	 * Favor local ones for these reasons.+	 */+	st = a->pack_local - b->pack_local;+	if (st)+		return -st;++	/*+	 * Younger packs tend to contain more recent objects,+	 * and more recent objects tend to get accessed more+	 * often.+	 */+	if (a->mtime < b->mtime)+		return 1;+	else if (a->mtime == b->mtime)+		return 0;++	return -1;+}++++static int packfile_load__cb(void *_data, git_buf *path)+{+	struct pack_backend *backend = (struct pack_backend *)_data;+	struct git_pack_file *pack;+	int error;+	unsigned int i;++	if (git__suffixcmp(path->ptr, ".idx") != 0)+		return 0; /* not an index */++	for (i = 0; i < backend->packs.length; ++i) {+		struct git_pack_file *p = git_vector_get(&backend->packs, i);+		if (memcmp(p->pack_name, git_buf_cstr(path), git_buf_len(path) - strlen(".idx")) == 0)+			return 0;+	}++	error = git_packfile_check(&pack, path->ptr);+	if (error == GIT_ENOTFOUND)+		/* ignore missing .pack file as git does */+		return 0;+	else if (error < 0)+		return error;++	return git_vector_insert(&backend->packs, pack);+}++static int packfile_refresh_all(struct pack_backend *backend)+{+	int error;+	struct stat st;++	if (backend->pack_folder == NULL)+		return 0;++	if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))+		return git_odb__error_notfound("failed to refresh packfiles", NULL);++	if (st.st_mtime != backend->pack_folder_mtime) {+		git_buf path = GIT_BUF_INIT;+		git_buf_sets(&path, backend->pack_folder);++		/* reload all packs */+		error = git_path_direach(&path, packfile_load__cb, (void *)backend);++		git_buf_free(&path);++		if (error < 0)+			return error;++		git_vector_sort(&backend->packs);+		backend->pack_folder_mtime = st.st_mtime;+	}++	return 0;+}++static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)+{+	int error;+	unsigned int i;++	if ((error = packfile_refresh_all(backend)) < 0)+		return error;++	if (backend->last_found &&+		git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)+		return 0;++	for (i = 0; i < backend->packs.length; ++i) {+		struct git_pack_file *p;++		p = git_vector_get(&backend->packs, i);+		if (p == backend->last_found)+			continue;++		if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {+			backend->last_found = p;+			return 0;+		}+	}++	return git_odb__error_notfound("failed to find pack entry", oid);+}++static int pack_entry_find_prefix(+	struct git_pack_entry *e,+	struct pack_backend *backend,+	const git_oid *short_oid,+	unsigned int len)+{+	int error;+	unsigned int i;+	unsigned found = 0;++	if ((error = packfile_refresh_all(backend)) < 0)+		return error;++	if (backend->last_found) {+		error = git_pack_entry_find(e, backend->last_found, short_oid, len);+		if (error == GIT_EAMBIGUOUS)+			return error;+		if (!error)+			found = 1;+	}++	for (i = 0; i < backend->packs.length; ++i) {+		struct git_pack_file *p;++		p = git_vector_get(&backend->packs, i);+		if (p == backend->last_found)+			continue;++		error = git_pack_entry_find(e, p, short_oid, len);+		if (error == GIT_EAMBIGUOUS)+			return error;+		if (!error) {+			if (++found > 1)+				break;+			backend->last_found = p;+		}+	}++	if (!found)+		return git_odb__error_notfound("no matching pack entry for prefix", short_oid);+	else if (found > 1)+		return git_odb__error_ambiguous("found multiple pack entries");+	else+		return 0;+}+++/***********************************************************+ *+ * PACKED BACKEND PUBLIC API+ *+ * Implement the git_odb_backend API calls+ *+ ***********************************************************/++/*+int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid)+{+	pack_location location;++	assert(obj && backend && oid);++	if (locate_packfile(&location, (struct pack_backend *)backend, oid) < 0)+		return GIT_ENOTFOUND;++	return read_header_packed(obj, &location);+}+*/++static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)+{+	struct git_pack_entry e;+	git_rawobj raw;+	int error;++	if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||+		(error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)+		return error;++	*buffer_p = raw.data;+	*len_p = raw.len;+	*type_p = raw.type;++	return 0;+}++static int pack_backend__read_prefix(+	git_oid *out_oid,+	void **buffer_p,+	size_t *len_p,+	git_otype *type_p,+	git_odb_backend *backend,+	const git_oid *short_oid,+	unsigned int len)+{+	int error = 0;++	if (len < GIT_OID_MINPREFIXLEN)+		error = git_odb__error_ambiguous("prefix length too short");++	else if (len >= GIT_OID_HEXSZ) {+		/* We can fall back to regular read method */+		error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);+		if (!error)+			git_oid_cpy(out_oid, short_oid);+	} else {+		struct git_pack_entry e;+		git_rawobj raw;++		if ((error = pack_entry_find_prefix(+				&e, (struct pack_backend *)backend, short_oid, len)) == 0 &&+			(error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)+		{+			*buffer_p = raw.data;+			*len_p = raw.len;+			*type_p = raw.type;+			git_oid_cpy(out_oid, &e.sha1);+		}+	}++	return error;+}++static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)+{+	struct git_pack_entry e;+	return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;+}++static void pack_backend__free(git_odb_backend *_backend)+{+	struct pack_backend *backend;+	unsigned int i;++	assert(_backend);++	backend = (struct pack_backend *)_backend;++	for (i = 0; i < backend->packs.length; ++i) {+		struct git_pack_file *p = git_vector_get(&backend->packs, i);+		packfile_free(p);+	}++	git_vector_free(&backend->packs);+	git__free(backend->pack_folder);+	git__free(backend);+}++int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)+{+	struct pack_backend *backend = NULL;+	git_buf path = GIT_BUF_INIT;++	backend = git__calloc(1, sizeof(struct pack_backend));+	GITERR_CHECK_ALLOC(backend);++	if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 ||+		git_buf_joinpath(&path, objects_dir, "pack") < 0)+	{+		git__free(backend);+		return -1;+	}++	if (git_path_isdir(git_buf_cstr(&path)) == true) {+		backend->pack_folder = git_buf_detach(&path);+		backend->pack_folder_mtime = 0;+	}++	backend->parent.read = &pack_backend__read;+	backend->parent.read_prefix = &pack_backend__read_prefix;+	backend->parent.read_header = NULL;+	backend->parent.exists = &pack_backend__exists;+	backend->parent.free = &pack_backend__free;++	*backend_out = (git_odb_backend *)backend;++	git_buf_free(&path);++	return 0;+}
+ libgit2/src/oid.c view
@@ -0,0 +1,387 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "git2/oid.h"+#include "repository.h"+#include <string.h>+#include <limits.h>++static char to_hex[] = "0123456789abcdef";++static int oid_error_invalid(const char *msg)+{+	giterr_set(GITERR_INVALID, "Unable to parse OID - %s", msg);+	return -1;+}++int git_oid_fromstrn(git_oid *out, const char *str, size_t length)+{+	size_t p;+	int v;++	if (length < 4)+		return oid_error_invalid("input too short");++	if (length > GIT_OID_HEXSZ)+		length = GIT_OID_HEXSZ;++	for (p = 0; p < length - 1; p += 2) {+		v = (git__fromhex(str[p + 0]) << 4)+				| git__fromhex(str[p + 1]);++		if (v < 0)+			return oid_error_invalid("contains invalid characters");++		out->id[p / 2] = (unsigned char)v;+	}++	if (length % 2) {+		v = (git__fromhex(str[p + 0]) << 4);+		if (v < 0)+			return oid_error_invalid("contains invalid characters");++		out->id[p / 2] = (unsigned char)v;+		p += 2;+	}++	memset(out->id + p / 2, 0, (GIT_OID_HEXSZ - p) / 2);++	return 0;+}++int git_oid_fromstr(git_oid *out, const char *str)+{+	return git_oid_fromstrn(out, str, GIT_OID_HEXSZ);+}++GIT_INLINE(char) *fmt_one(char *str, unsigned int val)+{+	*str++ = to_hex[val >> 4];+	*str++ = to_hex[val & 0xf];+	return str;+}++void git_oid_fmt(char *str, const git_oid *oid)+{+	size_t i;++	for (i = 0; i < sizeof(oid->id); i++)+		str = fmt_one(str, oid->id[i]);+}++void git_oid_pathfmt(char *str, const git_oid *oid)+{+	size_t i;++	str = fmt_one(str, oid->id[0]);+	*str++ = '/';+	for (i = 1; i < sizeof(oid->id); i++)+		str = fmt_one(str, oid->id[i]);+}++char *git_oid_allocfmt(const git_oid *oid)+{+	char *str = git__malloc(GIT_OID_HEXSZ + 1);+	if (!str)+		return NULL;+	git_oid_fmt(str, oid);+	str[GIT_OID_HEXSZ] = '\0';+	return str;+}++char *git_oid_tostr(char *out, size_t n, const git_oid *oid)+{+	char str[GIT_OID_HEXSZ];++	if (!out || n == 0 || !oid)+		return "";++	n--; /* allow room for terminating NUL */++	if (n > 0) {+		git_oid_fmt(str, oid);+		if (n > GIT_OID_HEXSZ)+			n = GIT_OID_HEXSZ;+		memcpy(out, str, n);+	}++	out[n] = '\0';++	return out;+}++int git_oid__parse(+	git_oid *oid, const char **buffer_out,+	const char *buffer_end, const char *header)+{+	const size_t sha_len = GIT_OID_HEXSZ;+	const size_t header_len = strlen(header);++	const char *buffer = *buffer_out;++	if (buffer + (header_len + sha_len + 1) > buffer_end)+		return -1;++	if (memcmp(buffer, header, header_len) != 0)+		return -1;++	if (buffer[header_len + sha_len] != '\n')+		return -1;++	if (git_oid_fromstr(oid, buffer + header_len) < 0)+		return -1;++	*buffer_out = buffer + (header_len + sha_len + 1);++	return 0;+}++void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid)+{+	char hex_oid[GIT_OID_HEXSZ];++	git_oid_fmt(hex_oid, oid);+	git_buf_puts(buf, header);+	git_buf_put(buf, hex_oid, GIT_OID_HEXSZ);+	git_buf_putc(buf, '\n');+}++void git_oid_fromraw(git_oid *out, const unsigned char *raw)+{+	memcpy(out->id, raw, sizeof(out->id));+}++void git_oid_cpy(git_oid *out, const git_oid *src)+{+	memcpy(out->id, src->id, sizeof(out->id));+}++int git_oid_cmp(const git_oid *a, const git_oid *b)+{+	return memcmp(a->id, b->id, sizeof(a->id));+}++int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, unsigned int len)+{+	const unsigned char *a = oid_a->id;+	const unsigned char *b = oid_b->id;++	do {+		if (*a != *b)+			return 1;+		a++;+		b++;+		len -= 2;+	} while (len > 1);++	if (len)+		if ((*a ^ *b) & 0xf0)+			return 1;++	return 0;+}++int git_oid_streq(const git_oid *a, const char *str)+{+	git_oid id;++	if (git_oid_fromstr(&id, str) < 0)+		return -1;++	return git_oid_cmp(a, &id) == 0 ? 0 : -1;+}++int git_oid_iszero(const git_oid *oid_a)+{+	const unsigned char *a = oid_a->id;+	unsigned int i;+	for (i = 0; i < GIT_OID_RAWSZ; ++i, ++a)+		if (*a != 0)+			return 0;+	return 1;+}++typedef short node_index;++typedef union {+	const char *tail;+	node_index children[16];+} trie_node;++struct git_oid_shorten {+	trie_node *nodes;+	size_t node_count, size;+	int min_length, full;+};++static int resize_trie(git_oid_shorten *self, size_t new_size)+{+	self->nodes = git__realloc(self->nodes, new_size * sizeof(trie_node));+	GITERR_CHECK_ALLOC(self->nodes);++	if (new_size > self->size) {+		memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node));+	}++	self->size = new_size;+	return 0;+}++static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid)+{+	trie_node *node, *leaf;+	node_index idx_leaf;++	if (os->node_count >= os->size) {+		if (resize_trie(os, os->size * 2) < 0)+			return NULL;+	}++	idx_leaf = (node_index)os->node_count++;++	if (os->node_count == SHRT_MAX)+		os->full = 1;++	node = &os->nodes[idx];+	node->children[push_at] = -idx_leaf;++	leaf = &os->nodes[idx_leaf];+	leaf->tail = oid;++	return node;+}++git_oid_shorten *git_oid_shorten_new(size_t min_length)+{+	git_oid_shorten *os;++	assert((size_t)((int)min_length) == min_length);++	os = git__calloc(1, sizeof(git_oid_shorten));+	if (os == NULL)+		return NULL;++	if (resize_trie(os, 16) < 0) {+		git__free(os);+		return NULL;+	}++	os->node_count = 1;+	os->min_length = (int)min_length;++	return os;+}++void git_oid_shorten_free(git_oid_shorten *os)+{+	git__free(os->nodes);+	git__free(os);+}+++/*+ * What wizardry is this?+ *+ * This is just a memory-optimized trie: basically a very fancy+ * 16-ary tree, which is used to store the prefixes of the OID+ * strings.+ *+ * Read more: http://en.wikipedia.org/wiki/Trie+ *+ * Magic that happens in this method:+ *+ *	- Each node in the trie is an union, so it can work both as+ *	a normal node, or as a leaf.+ *+ *	- Each normal node points to 16 children (one for each possible+ *	character in the oid). This is *not* stored in an array of+ *	pointers, because in a 64-bit arch this would be sucking+ *	16*sizeof(void*) = 128 bytes of memory per node, which is fucking+ *	insane. What we do is store Node Indexes, and use these indexes+ *	to look up each node in the om->index array. These indexes are+ *	signed shorts, so this limits the amount of unique OIDs that+ *	fit in the structure to about 20000 (assuming a more or less uniform+ *	distribution).+ *+ *	- All the nodes in om->index array are stored contiguously in+ *	memory, and each of them is 32 bytes, so we fit 2x nodes per+ *	cache line. Convenient for speed.+ *+ *	- To differentiate the leafs from the normal nodes, we store all+ *	the indexes towards a leaf as a negative index (indexes to normal+ *	nodes are positives). When we find that one of the children for+ *	a node has a negative value, that means it's going to be a leaf.+ *	This reduces the amount of indexes we have by two, but also reduces+ *	the size of each node by 1-4 bytes (the amount we would need to+ *	add a `is_leaf` field): this is good because it allows the nodes+ *	to fit cleanly in cache lines.+ *+ *	- Once we reach an empty children, instead of continuing to insert+ *	new nodes for each remaining character of the OID, we store a pointer+ *	to the tail in the leaf; if the leaf is reached again, we turn it+ *	into a normal node and use the tail to create a new leaf.+ *+ *	This is a pretty good balance between performance and memory usage.+ */+int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)+{+	int i;+	bool is_leaf;+	node_index idx;++	if (os->full)+		return -1;++	if (text_oid == NULL)+		return os->min_length;++	idx = 0;+	is_leaf = false;++	for (i = 0; i < GIT_OID_HEXSZ; ++i) {+		int c = git__fromhex(text_oid[i]);+		trie_node *node;++		if (c == -1) {+			giterr_set(GITERR_INVALID, "Unable to shorten OID - invalid hex value");+			return -1;+		}++		node = &os->nodes[idx];++		if (is_leaf) {+			const char *tail;++			tail = node->tail;+			node->tail = NULL;++			node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);+			GITERR_CHECK_ALLOC(node);+		}++		if (node->children[c] == 0) {+			if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL)+				return -1;+			break;+		}++		idx = node->children[c];+		is_leaf = false;++		if (idx < 0) {+			node->children[c] = idx = -idx;+			is_leaf = true;+		}+	}++	if (++i > os->min_length)+		os->min_length = i;++	return os->min_length;+}+
+ libgit2/src/oidmap.h view
@@ -0,0 +1,42 @@+/*+ * Copyright (C) 2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_oidmap_h__+#define INCLUDE_oidmap_h__++#include "common.h"+#include "git2/oid.h"++#define kmalloc git__malloc+#define kcalloc git__calloc+#define krealloc git__realloc+#define kfree git__free+#include "khash.h"++__KHASH_TYPE(oid, const git_oid *, void *);+typedef khash_t(oid) git_oidmap;++GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid)+{+	int i;+	khint_t h = 0;+	for (i = 0; i < 20; ++i)+		h = (h << 5) - h + oid->id[i];+	return h;+}++GIT_INLINE(int) hash_git_oid_equal(const git_oid *a, const git_oid *b)+{+	return (memcmp(a->id, b->id, sizeof(a->id)) == 0);+}++#define GIT__USE_OIDMAP \+	__KHASH_IMPL(oid, static inline, const git_oid *, void *, 1, hash_git_oid, hash_git_oid_equal)++#define git_oidmap_alloc() kh_init(oid)+#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL++#endif
+ libgit2/src/pack.c view
@@ -0,0 +1,816 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "odb.h"+#include "pack.h"+#include "delta-apply.h"+#include "sha1_lookup.h"+#include "mwindow.h"+#include "fileops.h"++#include "git2/oid.h"+#include <zlib.h>++static int packfile_open(struct git_pack_file *p);+static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);+int packfile_unpack_compressed(+		git_rawobj *obj,+		struct git_pack_file *p,+		git_mwindow **w_curs,+		git_off_t *curpos,+		size_t size,+		git_otype type);++/* Can find the offset of an object given+ * a prefix of an identifier.+ * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid+ * is ambiguous within the pack.+ * This method assumes that len is between+ * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.+ */+static int pack_entry_find_offset(+		git_off_t *offset_out,+		git_oid *found_oid,+		struct git_pack_file *p,+		const git_oid *short_oid,+		unsigned int len);++static int packfile_error(const char *message)+{+	giterr_set(GITERR_ODB, "Invalid pack file - %s", message);+	return -1;+}++/***********************************************************+ *+ * PACK INDEX METHODS+ *+ ***********************************************************/++static void pack_index_free(struct git_pack_file *p)+{+	if (p->index_map.data) {+		git_futils_mmap_free(&p->index_map);+		p->index_map.data = NULL;+	}+}++static int pack_index_check(const char *path, struct git_pack_file *p)+{+	struct git_pack_idx_header *hdr;+	uint32_t version, nr, i, *index;+	void *idx_map;+	size_t idx_size;+	struct stat st;+	int error;+	/* TODO: properly open the file without access time using O_NOATIME */+	git_file fd = git_futils_open_ro(path);+	if (fd < 0)+		return fd;++	if (p_fstat(fd, &st) < 0 ||+		!S_ISREG(st.st_mode) ||+		!git__is_sizet(st.st_size) ||+		(idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)+	{+		p_close(fd);+		giterr_set(GITERR_OS, "Failed to check pack index.");+		return -1;+	}++	error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size);++	p_close(fd);++	if (error < 0)+		return error;++	hdr = idx_map = p->index_map.data;++	if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {+		version = ntohl(hdr->idx_version);++		if (version < 2 || version > 2) {+			git_futils_mmap_free(&p->index_map);+			return packfile_error("unsupported index version");+		}++	} else+		version = 1;++	nr = 0;+	index = idx_map;++	if (version > 1)+		index += 2; /* skip index header */++	for (i = 0; i < 256; i++) {+		uint32_t n = ntohl(index[i]);+		if (n < nr) {+			git_futils_mmap_free(&p->index_map);+			return packfile_error("index is non-monotonic");+		}+		nr = n;+	}++	if (version == 1) {+		/*+		 * Total size:+		 * - 256 index entries 4 bytes each+		 * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)+		 * - 20-byte SHA1 of the packfile+		 * - 20-byte SHA1 file checksum+		 */+		if (idx_size != 4*256 + nr * 24 + 20 + 20) {+			git_futils_mmap_free(&p->index_map);+			return packfile_error("index is corrupted");+		}+	} else if (version == 2) {+		/*+		 * Minimum size:+		 * - 8 bytes of header+		 * - 256 index entries 4 bytes each+		 * - 20-byte sha1 entry * nr+		 * - 4-byte crc entry * nr+		 * - 4-byte offset entry * nr+		 * - 20-byte SHA1 of the packfile+		 * - 20-byte SHA1 file checksum+		 * And after the 4-byte offset table might be a+		 * variable sized table containing 8-byte entries+		 * for offsets larger than 2^31.+		 */+		unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;+		unsigned long max_size = min_size;++		if (nr)+			max_size += (nr - 1)*8;++		if (idx_size < min_size || idx_size > max_size) {+			git_futils_mmap_free(&p->index_map);+			return packfile_error("wrong index size");+		}+	}++	p->index_version = version;+	p->num_objects = nr;+	return 0;+}++static int pack_index_open(struct git_pack_file *p)+{+	char *idx_name;+	int error;+	size_t name_len, offset;++	if (p->index_map.data)+		return 0;++	idx_name = git__strdup(p->pack_name);+	GITERR_CHECK_ALLOC(idx_name);++	name_len = strlen(idx_name);+	offset = name_len - strlen(".pack");+	assert(offset < name_len); /* make sure no underflow */++	strncpy(idx_name + offset, ".idx", name_len - offset);++	error = pack_index_check(idx_name, p);+	git__free(idx_name);++	return error;+}++static unsigned char *pack_window_open(+		struct git_pack_file *p,+		git_mwindow **w_cursor,+		git_off_t offset,+		unsigned int *left)+{+	if (p->mwf.fd == -1 && packfile_open(p) < 0)+		return NULL;++	/* Since packfiles end in a hash of their content and it's+	 * pointless to ask for an offset into the middle of that+	 * hash, and the pack_window_contains function above wouldn't match+	 * don't allow an offset too close to the end of the file.+	 */+	if (offset > (p->mwf.size - 20))+		return NULL;++	return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);+ }++static int packfile_unpack_header1(+		unsigned long *usedp,+		size_t *sizep,+		git_otype *type,+		const unsigned char *buf,+		unsigned long len)+{+	unsigned shift;+	unsigned long size, c;+	unsigned long used = 0;++	c = buf[used++];+	*type = (c >> 4) & 7;+	size = c & 15;+	shift = 4;+	while (c & 0x80) {+		if (len <= used)+			return GIT_EBUFS;++		if (bitsizeof(long) <= shift) {+			*usedp = 0;+			return -1;+		}++		c = buf[used++];+		size += (c & 0x7f) << shift;+		shift += 7;+	}++	*sizep = (size_t)size;+	*usedp = used;+	return 0;+}++int git_packfile_unpack_header(+		size_t *size_p,+		git_otype *type_p,+		git_mwindow_file *mwf,+		git_mwindow **w_curs,+		git_off_t *curpos)+{+	unsigned char *base;+	unsigned int left;+	unsigned long used;+	int ret;++	/* pack_window_open() assures us we have [base, base + 20) available+	 * as a range that we can look at at. (Its actually the hash+	 * size that is assured.) With our object header encoding+	 * the maximum deflated object size is 2^137, which is just+	 * insane, so we know won't exceed what we have been given.+	 */+//	base = pack_window_open(p, w_curs, *curpos, &left);+	base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left);+	if (base == NULL)+		return GIT_EBUFS;++	ret = packfile_unpack_header1(&used, size_p, type_p, base, left);+	git_mwindow_close(w_curs);+	if (ret == GIT_EBUFS)+		return ret;+	else if (ret < 0)+		return packfile_error("header length is zero");++	*curpos += used;+	return 0;+}++static int packfile_unpack_delta(+		git_rawobj *obj,+		struct git_pack_file *p,+		git_mwindow **w_curs,+		git_off_t *curpos,+		size_t delta_size,+		git_otype delta_type,+		git_off_t obj_offset)+{+	git_off_t base_offset;+	git_rawobj base, delta;+	int error;++	base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset);+	git_mwindow_close(w_curs);+	if (base_offset == 0)+		return packfile_error("delta offset is zero");+	if (base_offset < 0) /* must actually be an error code */+		return (int)base_offset;++	error = git_packfile_unpack(&base, p, &base_offset);++	/*+	 * TODO: git.git tries to load the base from other packfiles+	 * or loose objects.+	 *+	 * We'll need to do this in order to support thin packs.+	 */+	if (error < 0)+		return error;++	error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);+	git_mwindow_close(w_curs);+	if (error < 0) {+		git__free(base.data);+		return error;+	}++	obj->type = base.type;+	error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);++	git__free(base.data);+	git__free(delta.data);++	/* TODO: we might want to cache this shit. eventually */+	//add_delta_base_cache(p, base_offset, base, base_size, *type);++	return error; /* error set by git__delta_apply */+}++int git_packfile_unpack(+	git_rawobj *obj,+	struct git_pack_file *p,+	git_off_t *obj_offset)+{+	git_mwindow *w_curs = NULL;+	git_off_t curpos = *obj_offset;+	int error;++	size_t size = 0;+	git_otype type;++	/*+	 * TODO: optionally check the CRC on the packfile+	 */++	obj->data = NULL;+	obj->len = 0;+	obj->type = GIT_OBJ_BAD;++	error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);+	git_mwindow_close(&w_curs);++	if (error < 0)+		return error;++	switch (type) {+	case GIT_OBJ_OFS_DELTA:+	case GIT_OBJ_REF_DELTA:+		error = packfile_unpack_delta(+				obj, p, &w_curs, &curpos,+				size, type, *obj_offset);+		break;++	case GIT_OBJ_COMMIT:+	case GIT_OBJ_TREE:+	case GIT_OBJ_BLOB:+	case GIT_OBJ_TAG:+		error = packfile_unpack_compressed(+				obj, p, &w_curs, &curpos,+				size, type);+		break;++	default:+		error = packfile_error("invalid packfile type in header");;+		break;+	}++	*obj_offset = curpos;+	return error;+}++static void *use_git_alloc(void *opaq, unsigned int count, unsigned int size)+{+	GIT_UNUSED(opaq);+	return git__calloc(count, size);+}++static void use_git_free(void *opaq, void *ptr)+{+	GIT_UNUSED(opaq);+	git__free(ptr);+}++int packfile_unpack_compressed(+	git_rawobj *obj,+	struct git_pack_file *p,+	git_mwindow **w_curs,+	git_off_t *curpos,+	size_t size,+	git_otype type)+{+	int st;+	z_stream stream;+	unsigned char *buffer, *in;++	buffer = git__calloc(1, size + 1);+	GITERR_CHECK_ALLOC(buffer);++	memset(&stream, 0, sizeof(stream));+	stream.next_out = buffer;+	stream.avail_out = (uInt)size + 1;+	stream.zalloc = use_git_alloc;+	stream.zfree = use_git_free;++	st = inflateInit(&stream);+	if (st != Z_OK) {+		git__free(buffer);+		giterr_set(GITERR_ZLIB, "Failed to inflate packfile");++		return -1;+	}++	do {+		in = pack_window_open(p, w_curs, *curpos, &stream.avail_in);+		stream.next_in = in;+		st = inflate(&stream, Z_FINISH);+		git_mwindow_close(w_curs);++		if (!stream.avail_out)+			break; /* the payload is larger than it should be */++		if (st == Z_BUF_ERROR && in == NULL) {+			inflateEnd(&stream);+			git__free(buffer);+			return GIT_EBUFS;+		}++		*curpos += stream.next_in - in;+	} while (st == Z_OK || st == Z_BUF_ERROR);++	inflateEnd(&stream);++	if ((st != Z_STREAM_END) || stream.total_out != size) {+		git__free(buffer);+		giterr_set(GITERR_ZLIB, "Failed to inflate packfile");+		return -1;+	}++	obj->type = type;+	obj->len = size;+	obj->data = buffer;+	return 0;+}++/*+ * curpos is where the data starts, delta_obj_offset is the where the+ * header starts+ */+git_off_t get_delta_base(+	struct git_pack_file *p,+	git_mwindow **w_curs,+	git_off_t *curpos,+	git_otype type,+	git_off_t delta_obj_offset)+{+	unsigned int left = 0;+	unsigned char *base_info;+	git_off_t base_offset;+	git_oid unused;++	base_info = pack_window_open(p, w_curs, *curpos, &left);+	/* Assumption: the only reason this would fail is because the file is too small */+	if (base_info == NULL)+		return GIT_EBUFS;+	/* pack_window_open() assured us we have [base_info, base_info + 20)+	 * as a range that we can look at without walking off the+	 * end of the mapped window. Its actually the hash size+	 * that is assured. An OFS_DELTA longer than the hash size+	 * is stupid, as then a REF_DELTA would be smaller to store.+	 */+	if (type == GIT_OBJ_OFS_DELTA) {+		unsigned used = 0;+		unsigned char c = base_info[used++];+		base_offset = c & 127;+		while (c & 128) {+			if (left <= used)+				return GIT_EBUFS;+			base_offset += 1;+			if (!base_offset || MSB(base_offset, 7))+				return 0; /* overflow */+			c = base_info[used++];+			base_offset = (base_offset << 7) + (c & 127);+		}+		base_offset = delta_obj_offset - base_offset;+		if (base_offset <= 0 || base_offset >= delta_obj_offset)+			return 0; /* out of bound */+		*curpos += used;+	} else if (type == GIT_OBJ_REF_DELTA) {+		/* If we have the cooperative cache, search in it first */+		if (p->has_cache) {+			int pos;+			struct git_pack_entry key;++			git_oid_fromraw(&key.sha1, base_info);+			pos = git_vector_bsearch(&p->cache, &key);+			if (pos >= 0) {+				*curpos += 20;+				return ((struct git_pack_entry *)git_vector_get(&p->cache, pos))->offset;+			}+		}+		/* The base entry _must_ be in the same pack */+		if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < 0)+			return packfile_error("base entry delta is not in the same pack");+		*curpos += 20;+	} else+		return 0;++	return base_offset;+}++/***********************************************************+ *+ * PACKFILE METHODS+ *+ ***********************************************************/++static struct git_pack_file *packfile_alloc(size_t extra)+{+	struct git_pack_file *p = git__calloc(1, sizeof(*p) + extra);+	if (p != NULL)+		p->mwf.fd = -1;+	return p;+}+++void packfile_free(struct git_pack_file *p)+{+	assert(p);++	/* clear_delta_base_cache(); */+	git_mwindow_free_all(&p->mwf);++	if (p->mwf.fd != -1)+		p_close(p->mwf.fd);++	pack_index_free(p);++	git__free(p->bad_object_sha1);+	git__free(p);+}++static int packfile_open(struct git_pack_file *p)+{+	struct stat st;+	struct git_pack_header hdr;+	git_oid sha1;+	unsigned char *idx_sha1;++	assert(p->index_map.data);++	if (!p->index_map.data && pack_index_open(p) < 0)+		return git_odb__error_notfound("failed to open packfile", NULL);++	/* TODO: open with noatime */+	p->mwf.fd = git_futils_open_ro(p->pack_name);+	if (p->mwf.fd < 0)+		return p->mwf.fd;++	if (p_fstat(p->mwf.fd, &st) < 0 ||+		git_mwindow_file_register(&p->mwf) < 0)+		goto cleanup;++	/* If we created the struct before we had the pack we lack size. */+	if (!p->mwf.size) {+		if (!S_ISREG(st.st_mode))+			goto cleanup;+		p->mwf.size = (git_off_t)st.st_size;+	} else if (p->mwf.size != st.st_size)+		goto cleanup;++#if 0+	/* We leave these file descriptors open with sliding mmap;+	 * there is no point keeping them open across exec(), though.+	 */+	fd_flag = fcntl(p->mwf.fd, F_GETFD, 0);+	if (fd_flag < 0)+		goto cleanup;++	fd_flag |= FD_CLOEXEC;+	if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)+		goto cleanup;+#endif++	/* Verify we recognize this pack file format. */+	if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < 0 ||+		hdr.hdr_signature != htonl(PACK_SIGNATURE) ||+		!pack_version_ok(hdr.hdr_version))+		goto cleanup;++	/* Verify the pack matches its index. */+	if (p->num_objects != ntohl(hdr.hdr_entries) ||+		p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1 ||+		p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < 0)+		goto cleanup;++	idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;++	if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) == 0)+		return 0;++cleanup:+	giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);+	p_close(p->mwf.fd);+	p->mwf.fd = -1;+	return -1;+}++int git_packfile_check(struct git_pack_file **pack_out, const char *path)+{+	struct stat st;+	struct git_pack_file *p;+	size_t path_len;++	*pack_out = NULL;+	path_len = strlen(path);+	p = packfile_alloc(path_len + 2);+	GITERR_CHECK_ALLOC(p);++	/*+	 * Make sure a corresponding .pack file exists and that+	 * the index looks sane.+	 */+	path_len -= strlen(".idx");+	if (path_len < 1) {+		git__free(p);+		return git_odb__error_notfound("invalid packfile path", NULL);+	}++	memcpy(p->pack_name, path, path_len);++	strcpy(p->pack_name + path_len, ".keep");+	if (git_path_exists(p->pack_name) == true)+		p->pack_keep = 1;++	strcpy(p->pack_name + path_len, ".pack");+	if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {+		git__free(p);+		return git_odb__error_notfound("packfile not found", NULL);+	}++	/* ok, it looks sane as far as we can check without+	 * actually mapping the pack file.+	 */+	p->mwf.size = st.st_size;+	p->pack_local = 1;+	p->mtime = (git_time_t)st.st_mtime;++	/* see if we can parse the sha1 oid in the packfile name */+	if (path_len < 40 ||+		git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < 0)+		memset(&p->sha1, 0x0, GIT_OID_RAWSZ);++	*pack_out = p;++	return 0;+}++/***********************************************************+ *+ * PACKFILE ENTRY SEARCH INTERNALS+ *+ ***********************************************************/++static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)+{+	const unsigned char *index = p->index_map.data;+	index += 4 * 256;+	if (p->index_version == 1) {+		return ntohl(*((uint32_t *)(index + 24 * n)));+	} else {+		uint32_t off;+		index += 8 + p->num_objects * (20 + 4);+		off = ntohl(*((uint32_t *)(index + 4 * n)));+		if (!(off & 0x80000000))+			return off;+		index += p->num_objects * 4 + (off & 0x7fffffff) * 8;+		return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |+					ntohl(*((uint32_t *)(index + 4)));+	}+}++static int pack_entry_find_offset(+	git_off_t *offset_out,+	git_oid *found_oid,+	struct git_pack_file *p,+	const git_oid *short_oid,+	unsigned int len)+{+	const uint32_t *level1_ofs = p->index_map.data;+	const unsigned char *index = p->index_map.data;+	unsigned hi, lo, stride;+	int pos, found = 0;+	const unsigned char *current = 0;++	*offset_out = 0;++	if (index == NULL) {+		int error;++		if ((error = pack_index_open(p)) < 0)+			return error;++		assert(p->index_map.data);++		index = p->index_map.data;+		level1_ofs = p->index_map.data;+	}++	if (p->index_version > 1) {+		level1_ofs += 2;+		index += 8;+	}++	index += 4 * 256;+	hi = ntohl(level1_ofs[(int)short_oid->id[0]]);+	lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1]));++	if (p->index_version > 1) {+		stride = 20;+	} else {+		stride = 24;+		index += 4;+	}++#ifdef INDEX_DEBUG_LOOKUP+	printf("%02x%02x%02x... lo %u hi %u nr %d\n",+		short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);+#endif++	/* Use git.git lookup code */+	pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id);++	if (pos >= 0) {+		/* An object matching exactly the oid was found */+		found = 1;+		current = index + pos * stride;+	} else {+		/* No object was found */+		/* pos refers to the object with the "closest" oid to short_oid */+		pos = - 1 - pos;+		if (pos < (int)p->num_objects) {+			current = index + pos * stride;++			if (!git_oid_ncmp(short_oid, (const git_oid *)current, len))+				found = 1;+		}+	}++	if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)p->num_objects) {+		/* Check for ambiguousity */+		const unsigned char *next = current + stride;++		if (!git_oid_ncmp(short_oid, (const git_oid *)next, len)) {+			found = 2;+		}+	}++	if (!found)+		return git_odb__error_notfound("failed to find offset for pack entry", short_oid);+	if (found > 1)+		return git_odb__error_ambiguous("found multiple offsets for pack entry");+	*offset_out = nth_packed_object_offset(p, pos);+	git_oid_fromraw(found_oid, current);++#ifdef INDEX_DEBUG_LOOKUP+	{+		unsigned char hex_sha1[GIT_OID_HEXSZ + 1];+		git_oid_fmt(hex_sha1, found_oid);+		hex_sha1[GIT_OID_HEXSZ] = '\0';+		printf("found lo=%d %s\n", lo, hex_sha1);+	}+#endif+	return 0;+}++int git_pack_entry_find(+		struct git_pack_entry *e,+		struct git_pack_file *p,+		const git_oid *short_oid,+		unsigned int len)+{+	git_off_t offset;+	git_oid found_oid;+	int error;++	assert(p);++	if (len == GIT_OID_HEXSZ && p->num_bad_objects) {+		unsigned i;+		for (i = 0; i < p->num_bad_objects; i++)+			if (git_oid_cmp(short_oid, &p->bad_object_sha1[i]) == 0)+				return packfile_error("bad object found in packfile");+	}++	error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len);+	if (error < 0)+		return error;++	/* we found a unique entry in the index;+	 * make sure the packfile backing the index+	 * still exists on disk */+	if (p->mwf.fd == -1 && (error = packfile_open(p)) < 0)+		return error;++	e->offset = offset;+	e->p = p;++	git_oid_cpy(&e->sha1, &found_oid);+	return 0;+}
+ libgit2/src/pack.h view
@@ -0,0 +1,106 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#ifndef INCLUDE_pack_h__+#define INCLUDE_pack_h__++#include "git2/oid.h"++#include "common.h"+#include "map.h"+#include "mwindow.h"+#include "odb.h"++#define GIT_PACK_FILE_MODE 0444++#define PACK_SIGNATURE 0x5041434b	/* "PACK" */+#define PACK_VERSION 2+#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))+struct git_pack_header {+	uint32_t hdr_signature;+	uint32_t hdr_version;+	uint32_t hdr_entries;+};++/*+ * The first four bytes of index formats later than version 1 should+ * start with this signature, as all older git binaries would find this+ * value illegal and abort reading the file.+ *+ * This is the case because the number of objects in a packfile+ * cannot exceed 1,431,660,000 as every object would need at least+ * 3 bytes of data and the overall packfile cannot exceed 4 GiB with+ * version 1 of the index file due to the offsets limited to 32 bits.+ * Clearly the signature exceeds this maximum.+ *+ * Very old git binaries will also compare the first 4 bytes to the+ * next 4 bytes in the index and abort with a "non-monotonic index"+ * error if the second 4 byte word is smaller than the first 4+ * byte word. This would be true in the proposed future index+ * format as idx_signature would be greater than idx_version.+ */++#define PACK_IDX_SIGNATURE 0xff744f63	/* "\377tOc" */++struct git_pack_idx_header {+	uint32_t idx_signature;+	uint32_t idx_version;+};++struct git_pack_file {+	git_mwindow_file mwf;+	git_map index_map;++	uint32_t num_objects;+	uint32_t num_bad_objects;+	git_oid *bad_object_sha1; /* array of git_oid */++	int index_version;+	git_time_t mtime;+	unsigned pack_local:1, pack_keep:1, has_cache:1;+	git_oid sha1;+	git_vector cache;++	/* something like ".git/objects/pack/xxxxx.pack" */+	char pack_name[GIT_FLEX_ARRAY]; /* more */+};++struct git_pack_entry {+	git_off_t offset;+	git_oid sha1;+	struct git_pack_file *p;+};++int git_packfile_unpack_header(+		size_t *size_p,+		git_otype *type_p,+		git_mwindow_file *mwf,+		git_mwindow **w_curs,+		git_off_t *curpos);++int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset);+int packfile_unpack_compressed(+	git_rawobj *obj,+	struct git_pack_file *p,+	git_mwindow **w_curs,+	git_off_t *curpos,+	size_t size,+	git_otype type);++git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,+		git_off_t *curpos, git_otype type,+		git_off_t delta_obj_offset);++void packfile_free(struct git_pack_file *p);+int git_packfile_check(struct git_pack_file **pack_out, const char *path);+int git_pack_entry_find(+		struct git_pack_entry *e,+		struct git_pack_file *p,+		const git_oid *short_oid,+		unsigned int len);++#endif
+ libgit2/src/path.c view
@@ -0,0 +1,656 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "path.h"+#include "posix.h"+#ifdef GIT_WIN32+#include "win32/dir.h"+#include "win32/posix.h"+#else+#include <dirent.h>+#endif+#include <stdarg.h>+#include <stdio.h>+#include <ctype.h>++/*+ * Based on the Android implementation, BSD licensed.+ * Check http://android.git.kernel.org/+ */+int git_path_basename_r(git_buf *buffer, const char *path)+{+	const char *endp, *startp;+	int len, result;++	/* Empty or NULL string gets treated as "." */+	if (path == NULL || *path == '\0') {+		startp = ".";+		len		= 1;+		goto Exit;+	}++	/* Strip trailing slashes */+	endp = path + strlen(path) - 1;+	while (endp > path && *endp == '/')+		endp--;++	/* All slashes becomes "/" */+	if (endp == path && *endp == '/') {+		startp = "/";+		len	= 1;+		goto Exit;+	}++	/* Find the start of the base */+	startp = endp;+	while (startp > path && *(startp - 1) != '/')+		startp--;++	/* Cast is safe because max path < max int */+	len = (int)(endp - startp + 1);++Exit:+	result = len;++	if (buffer != NULL && git_buf_set(buffer, startp, len) < 0)+		return -1;++	return result;+}++/*+ * Based on the Android implementation, BSD licensed.+ * Check http://android.git.kernel.org/+ */+int git_path_dirname_r(git_buf *buffer, const char *path)+{+	const char *endp;+	int result, len;++	/* Empty or NULL string gets treated as "." */+	if (path == NULL || *path == '\0') {+		path = ".";+		len = 1;+		goto Exit;+	}++	/* Strip trailing slashes */+	endp = path + strlen(path) - 1;+	while (endp > path && *endp == '/')+		endp--;++	/* Find the start of the dir */+	while (endp > path && *endp != '/')+		endp--;++	/* Either the dir is "/" or there are no slashes */+	if (endp == path) {+		path = (*endp == '/') ? "/" : ".";+		len = 1;+		goto Exit;+	}++	do {+		endp--;+	} while (endp > path && *endp == '/');++	/* Cast is safe because max path < max int */+	len = (int)(endp - path + 1);++#ifdef GIT_WIN32+	/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return+		'C:/' here */++	if (len == 2 && isalpha(path[0]) && path[1] == ':') {+		len = 3;+		goto Exit;+	}+#endif++Exit:+	result = len;++	if (buffer != NULL && git_buf_set(buffer, path, len) < 0)+		return -1;++	return result;+}+++char *git_path_dirname(const char *path)+{+	git_buf buf = GIT_BUF_INIT;+	char *dirname;++	git_path_dirname_r(&buf, path);+	dirname = git_buf_detach(&buf);+	git_buf_free(&buf); /* avoid memleak if error occurs */++	return dirname;+}++char *git_path_basename(const char *path)+{+	git_buf buf = GIT_BUF_INIT;+	char *basename;++	git_path_basename_r(&buf, path);+	basename = git_buf_detach(&buf);+	git_buf_free(&buf); /* avoid memleak if error occurs */++	return basename;+}+++const char *git_path_topdir(const char *path)+{+	size_t len;+	ssize_t i;++	assert(path);+	len = strlen(path);++	if (!len || path[len - 1] != '/')+		return NULL;++	for (i = (ssize_t)len - 2; i >= 0; --i)+		if (path[i] == '/')+			break;++	return &path[i + 1];+}++int git_path_root(const char *path)+{+	int offset = 0;++#ifdef GIT_WIN32+	/* Does the root of the path look like a windows drive ? */+	if (isalpha(path[0]) && (path[1] == ':'))+		offset += 2;++	/* Are we dealing with a windows network path? */+	else if ((path[0] == '/' && path[1] == '/') ||+		(path[0] == '\\' && path[1] == '\\'))+	{+		offset += 2;++		/* Skip the computer name segment */+		while (path[offset] && path[offset] != '/' && path[offset] != '\\')+			offset++;+	}+#endif++	if (path[offset] == '/' || path[offset] == '\\')+		return offset;++	return -1;	/* Not a real error - signals that path is not rooted */+}++int git_path_prettify(git_buf *path_out, const char *path, const char *base)+{+	char buf[GIT_PATH_MAX];++	assert(path && path_out);++	/* construct path if needed */+	if (base != NULL && git_path_root(path) < 0) {+		if (git_buf_joinpath(path_out, base, path) < 0)+			return -1;+		path = path_out->ptr;+	}++	if (p_realpath(path, buf) == NULL) {+		/* giterr_set resets the errno when dealing with a GITERR_OS kind of error */+		int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;+		giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);++		git_buf_clear(path_out);+		+		return error;+	}++	return git_buf_sets(path_out, buf);+}++int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)+{+	int error = git_path_prettify(path_out, path, base);+	return (error < 0) ? error : git_path_to_dir(path_out);+}++int git_path_to_dir(git_buf *path)+{+	if (path->asize > 0 &&+		git_buf_len(path) > 0 &&+		path->ptr[git_buf_len(path) - 1] != '/')+		git_buf_putc(path, '/');++	return git_buf_oom(path) ? -1 : 0;+}++void git_path_string_to_dir(char* path, size_t size)+{+	size_t end = strlen(path);++	if (end && path[end - 1] != '/' && end < size) {+		path[end] = '/';+		path[end + 1] = '\0';+	}+}++int git__percent_decode(git_buf *decoded_out, const char *input)+{+	int len, hi, lo, i;+	assert(decoded_out && input);++	len = (int)strlen(input);+	git_buf_clear(decoded_out);++	for(i = 0; i < len; i++)+	{+		char c = input[i];++		if (c != '%')+			goto append;++		if (i >= len - 2)+			goto append;++		hi = git__fromhex(input[i + 1]);+		lo = git__fromhex(input[i + 2]);++		if (hi < 0 || lo < 0)+			goto append;++		c = (char)(hi << 4 | lo);+		i += 2;++append:+		if (git_buf_putc(decoded_out, c) < 0)+			return -1;+	}++	return 0;+}++static int error_invalid_local_file_uri(const char *uri)+{+	giterr_set(GITERR_CONFIG, "'%s' is not a valid local file URI", uri);+	return -1;+}++int git_path_fromurl(git_buf *local_path_out, const char *file_url)+{+	int offset = 0, len;++	assert(local_path_out && file_url);++	if (git__prefixcmp(file_url, "file://") != 0)+		return error_invalid_local_file_uri(file_url);++	offset += 7;+	len = (int)strlen(file_url);++	if (offset < len && file_url[offset] == '/')+		offset++;+	else if (offset < len && git__prefixcmp(file_url + offset, "localhost/") == 0)+		offset += 10;+	else+		return error_invalid_local_file_uri(file_url);++	if (offset >= len || file_url[offset] == '/')+		return error_invalid_local_file_uri(file_url);++#ifndef _MSC_VER+	offset--;	/* A *nix absolute path starts with a forward slash */+#endif++	git_buf_clear(local_path_out);++	return git__percent_decode(local_path_out, file_url + offset);+}++int git_path_walk_up(+	git_buf *path,+	const char *ceiling,+	int (*cb)(void *data, git_buf *),+	void *data)+{+	int error = 0;+	git_buf iter;+	ssize_t stop = 0, scan;+	char oldc = '\0';++	assert(path && cb);++	if (ceiling != NULL) {+		if (git__prefixcmp(path->ptr, ceiling) == 0)+			stop = (ssize_t)strlen(ceiling);+		else+			stop = git_buf_len(path);+	}+	scan = git_buf_len(path);++	iter.ptr = path->ptr;+	iter.size = git_buf_len(path);+	iter.asize = path->asize;++	while (scan >= stop) {+		if ((error = cb(data, &iter)) < 0)+			break;+		iter.ptr[scan] = oldc;+		scan = git_buf_rfind_next(&iter, '/');+		if (scan >= 0) {+			scan++;+			oldc = iter.ptr[scan];+			iter.size = scan;+			iter.ptr[scan] = '\0';+		}+	}++	if (scan >= 0)+		iter.ptr[scan] = oldc;++	return error;+}++bool git_path_exists(const char *path)+{+	assert(path);+	return p_access(path, F_OK) == 0;+}++bool git_path_isdir(const char *path)+{+	struct stat st;+	if (p_stat(path, &st) < 0)+		return false;++	return S_ISDIR(st.st_mode) != 0;+}++bool git_path_isfile(const char *path)+{+	struct stat st;++	assert(path);+	if (p_stat(path, &st) < 0)+		return false;++	return S_ISREG(st.st_mode) != 0;+}++int git_path_lstat(const char *path, struct stat *st)+{+	int err = 0;++	if (p_lstat(path, st) < 0) {+		err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;+		giterr_set(GITERR_OS, "Failed to stat file '%s'", path);+	}++	return err;+}++static bool _check_dir_contents(+	git_buf *dir,+	const char *sub,+	bool (*predicate)(const char *))+{+	bool result;+	size_t dir_size = git_buf_len(dir);+	size_t sub_size = strlen(sub);++	/* leave base valid even if we could not make space for subdir */+	if (git_buf_try_grow(dir, dir_size + sub_size + 2) < 0)+		return false;++	/* save excursion */+	git_buf_joinpath(dir, dir->ptr, sub);++	result = predicate(dir->ptr);++	/* restore path */+	git_buf_truncate(dir, dir_size);+	return result;+}++bool git_path_contains(git_buf *dir, const char *item)+{+	return _check_dir_contents(dir, item, &git_path_exists);+}++bool git_path_contains_dir(git_buf *base, const char *subdir)+{+	return _check_dir_contents(base, subdir, &git_path_isdir);+}++bool git_path_contains_file(git_buf *base, const char *file)+{+	return _check_dir_contents(base, file, &git_path_isfile);+}++int git_path_find_dir(git_buf *dir, const char *path, const char *base)+{+	int error;++	if (base != NULL && git_path_root(path) < 0)+		error = git_buf_joinpath(dir, base, path);+	else+		error = git_buf_sets(dir, path);++	if (!error) {+		char buf[GIT_PATH_MAX];+		if (p_realpath(dir->ptr, buf) != NULL)+			error = git_buf_sets(dir, buf);+	}++	/* call dirname if this is not a directory */+	if (!error && git_path_isdir(dir->ptr) == false)+		error = git_path_dirname_r(dir, dir->ptr);++	if (!error)+		error = git_path_to_dir(dir);++	return error;+}++int git_path_cmp(+	const char *name1, size_t len1, int isdir1,+	const char *name2, size_t len2, int isdir2)+{+	size_t len = len1 < len2 ? len1 : len2;+	int cmp;++	cmp = memcmp(name1, name2, len);+	if (cmp)+		return cmp;+	if (len1 < len2)+		return (!isdir1 && !isdir2) ? -1 :+			(isdir1 ? '/' - name2[len1] : name2[len1] - '/');+	if (len1 > len2)+		return (!isdir1 && !isdir2) ? 1 :+			(isdir2 ? name1[len2] - '/' : '/' - name1[len2]);+	return 0;+}++/* Taken from git.git */+GIT_INLINE(int) is_dot_or_dotdot(const char *name)+{+	return (name[0] == '.' &&+		(name[1] == '\0' ||+		 (name[1] == '.' && name[2] == '\0')));+}++int git_path_direach(+	git_buf *path,+	int (*fn)(void *, git_buf *),+	void *arg)+{+	ssize_t wd_len;+	DIR *dir;+	struct dirent *de, *de_buf;++	if (git_path_to_dir(path) < 0)+		return -1;++	wd_len = git_buf_len(path);++	if ((dir = opendir(path->ptr)) == NULL) {+		giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr);+		return -1;+	}++#ifdef __sun+	de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);+#else+	de_buf = git__malloc(sizeof(struct dirent));+#endif++	while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {+		int result;++		if (is_dot_or_dotdot(de->d_name))+			continue;++		if (git_buf_puts(path, de->d_name) < 0) {+			closedir(dir);+			git__free(de_buf);+			return -1;+		}++		result = fn(arg, path);++		git_buf_truncate(path, wd_len); /* restore path */++		if (result < 0) {+			closedir(dir);+			git__free(de_buf);+			return -1;+		}+	}++	closedir(dir);+	git__free(de_buf);+	return 0;+}++int git_path_dirload(+	const char *path,+	size_t prefix_len,+	size_t alloc_extra,+	git_vector *contents)+{+	int error, need_slash;+	DIR *dir;+	struct dirent *de, *de_buf;+	size_t path_len;++	assert(path != NULL && contents != NULL);+	path_len = strlen(path);+	assert(path_len > 0 && path_len >= prefix_len);++	if ((dir = opendir(path)) == NULL) {+		giterr_set(GITERR_OS, "Failed to open directory '%s'", path);+		return -1;+	}++#ifdef __sun+	de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);+#else+	de_buf = git__malloc(sizeof(struct dirent));+#endif++	path += prefix_len;+	path_len -= prefix_len;+	need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;++	while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {+		char *entry_path;+		size_t entry_len;++		if (is_dot_or_dotdot(de->d_name))+			continue;++		entry_len = strlen(de->d_name);++		entry_path = git__malloc(+			path_len + need_slash + entry_len + 1 + alloc_extra);+		GITERR_CHECK_ALLOC(entry_path);++		if (path_len)+			memcpy(entry_path, path, path_len);+		if (need_slash)+			entry_path[path_len] = '/';+		memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);+		entry_path[path_len + need_slash + entry_len] = '\0';++		if (git_vector_insert(contents, entry_path) < 0) {+			closedir(dir);+			git__free(de_buf);+			return -1;+		}+	}++	closedir(dir);+	git__free(de_buf);++	if (error != 0)+		giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);++	return error;+}++int git_path_with_stat_cmp(const void *a, const void *b)+{+	const git_path_with_stat *psa = a, *psb = b;+	return git__strcmp_cb(psa->path, psb->path);+}++int git_path_dirload_with_stat(+	const char *path,+	size_t prefix_len,+	git_vector *contents)+{+	int error;+	unsigned int i;+	git_path_with_stat *ps;+	git_buf full = GIT_BUF_INIT;++	if (git_buf_set(&full, path, prefix_len) < 0)+		return -1;++	error = git_path_dirload(+		path, prefix_len, sizeof(git_path_with_stat) + 1, contents);+	if (error < 0) {+		git_buf_free(&full);+		return error;+	}++	git_vector_foreach(contents, i, ps) {+		size_t path_len = strlen((char *)ps);++		memmove(ps->path, ps, path_len + 1);+		ps->path_len = path_len;++		if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||+			(error = git_path_lstat(full.ptr, &ps->st)) < 0)+			break;++		git_buf_truncate(&full, prefix_len);++		if (S_ISDIR(ps->st.st_mode)) {+			ps->path[path_len] = '/';+			ps->path[path_len + 1] = '\0';+		}+	}++	git_buf_free(&full);++	return error;+}
+ libgit2/src/path.h view
@@ -0,0 +1,278 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_path_h__+#define INCLUDE_path_h__++#include "common.h"+#include "buffer.h"+#include "vector.h"++/**+ * Path manipulation utils+ *+ * These are path utilities that munge paths without actually+ * looking at the real filesystem.+ */++/*+ * The dirname() function shall take a pointer to a character string+ * that contains a pathname, and return a pointer to a string that is a+ * pathname of the parent directory of that file. Trailing '/' characters+ * in the path are not counted as part of the path.+ *+ * If path does not contain a '/', then dirname() shall return a pointer to+ * the string ".". If path is a null pointer or points to an empty string,+ * dirname() shall return a pointer to the string "." .+ *+ * The `git_path_dirname` implementation is thread safe. The returned+ * string must be manually free'd.+ *+ * The `git_path_dirname_r` implementation writes the dirname to a `git_buf`+ * if the buffer pointer is not NULL.+ * It returns an error code < 0 if there is an allocation error, otherwise+ * the length of the dirname (which will be > 0).+ */+extern char *git_path_dirname(const char *path);+extern int git_path_dirname_r(git_buf *buffer, const char *path);++/*+ * This function returns the basename of the file, which is the last+ * part of its full name given by fname, with the drive letter and+ * leading directories stripped off. For example, the basename of+ * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo.+ *+ * Trailing slashes and backslashes are significant: the basename of+ * c:/foo/bar/ is an empty string after the rightmost slash.+ *+ * The `git_path_basename` implementation is thread safe. The returned+ * string must be manually free'd.+ *+ * The `git_path_basename_r` implementation writes the basename to a `git_buf`.+ * It returns an error code < 0 if there is an allocation error, otherwise+ * the length of the basename (which will be >= 0).+ */+extern char *git_path_basename(const char *path);+extern int git_path_basename_r(git_buf *buffer, const char *path);++extern const char *git_path_topdir(const char *path);++/**+ * Find offset to root of path if path has one.+ *+ * This will return a number >= 0 which is the offset to the start of the+ * path, if the path is rooted (i.e. "/rooted/path" returns 0 and+ * "c:/windows/rooted/path" returns 2).  If the path is not rooted, this+ * returns < 0.+ */+extern int git_path_root(const char *path);++/**+ * Ensure path has a trailing '/'.+ */+extern int git_path_to_dir(git_buf *path);++/**+ * Ensure string has a trailing '/' if there is space for it.+ */+extern void git_path_string_to_dir(char* path, size_t size);++#ifdef GIT_WIN32+/**+ * Convert backslashes in path to forward slashes.+ */+GIT_INLINE(void) git_path_mkposix(char *path)+{+	while (*path) {+		if (*path == '\\')+			*path = '/';++		path++;+	}+}+#else+#	define git_path_mkposix(p) /* blank */+#endif++extern int git__percent_decode(git_buf *decoded_out, const char *input);++/**+ * Extract path from file:// URL.+ */+extern int git_path_fromurl(git_buf *local_path_out, const char *file_url);+++/**+ * Path filesystem utils+ *+ * These are path utilities that actually access the filesystem.+ */++/**+ * Check if a file exists and can be accessed.+ * @return true or false+ */+extern bool git_path_exists(const char *path);++/**+ * Check if the given path points to a directory.+ * @return true or false+ */+extern bool git_path_isdir(const char *path);++/**+ * Check if the given path points to a regular file.+ * @return true or false+ */+extern bool git_path_isfile(const char *path);++/**+ * Stat a file and/or link and set error if needed.+ */+extern int git_path_lstat(const char *path, struct stat *st);++/**+ * Check if the parent directory contains the item.+ *+ * @param dir Directory to check.+ * @param item Item that might be in the directory.+ * @return 0 if item exists in directory, <0 otherwise.+ */+extern bool git_path_contains(git_buf *dir, const char *item);++/**+ * Check if the given path contains the given subdirectory.+ *+ * @param parent Directory path that might contain subdir+ * @param subdir Subdirectory name to look for in parent+ * @param append_if_exists If true, then subdir will be appended to the parent path if it does exist+ * @return true if subdirectory exists, false otherwise.+ */+extern bool git_path_contains_dir(git_buf *parent, const char *subdir);++/**+ * Check if the given path contains the given file.+ *+ * @param dir Directory path that might contain file+ * @param file File name to look for in parent+ * @param append_if_exists If true, then file will be appended to the path if it does exist+ * @return true if file exists, false otherwise.+ */+extern bool git_path_contains_file(git_buf *dir, const char *file);++/**+ * Clean up path, prepending base if it is not already rooted.+ */+extern int git_path_prettify(git_buf *path_out, const char *path, const char *base);++/**+ * Clean up path, prepending base if it is not already rooted and+ * appending a slash.+ */+extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base);++/**+ * Get a directory from a path.+ *+ * If path is a directory, this acts like `git_path_prettify_dir`+ * (cleaning up path and appending a '/').  If path is a normal file,+ * this prettifies it, then removed the filename a la dirname and+ * appends the trailing '/'.  If the path does not exist, it is+ * treated like a regular filename.+ */+extern int git_path_find_dir(git_buf *dir, const char *path, const char *base);++/**+ * Walk each directory entry, except '.' and '..', calling fn(state).+ *+ * @param pathbuf buffer the function reads the initial directory+ * 		path from, and updates with each successive entry's name.+ * @param fn function to invoke with each entry. The first arg is+ *		the input state and the second arg is pathbuf. The function+ *		may modify the pathbuf, but only by appending new text.+ * @param state to pass to fn as the first arg.+ */+extern int git_path_direach(+	git_buf *pathbuf,+	int (*fn)(void *, git_buf *),+	void *state);++/**+ * Sort function to order two paths.+ */+extern int git_path_cmp(+	const char *name1, size_t len1, int isdir1,+	const char *name2, size_t len2, int isdir2);++/**+ * Invoke callback up path directory by directory until the ceiling is+ * reached (inclusive of a final call at the root_path).+ *+ * Returning anything other than 0 from the callback function+ * will stop the iteration and propogate the error to the caller.+ *+ * @param pathbuf Buffer the function reads the directory from and+ *		and updates with each successive name.+ * @param ceiling Prefix of path at which to stop walking up.  If NULL,+ *      this will walk all the way up to the root.  If not a prefix of+ *      pathbuf, the callback will be invoked a single time on the+ *      original input path.+ * @param fn Function to invoke on each path.  The first arg is the+ *		input satte and the second arg is the pathbuf.  The function+ *		should not modify the pathbuf.+ * @param state Passed to fn as the first ath.+ */+extern int git_path_walk_up(+	git_buf *pathbuf,+	const char *ceiling,+	int (*fn)(void *state, git_buf *),+	void *state);++/**+ * Load all directory entries (except '.' and '..') into a vector.+ *+ * For cases where `git_path_direach()` is not appropriate, this+ * allows you to load the filenames in a directory into a vector+ * of strings. That vector can then be sorted, iterated, or whatever.+ * Remember to free alloc of the allocated strings when you are done.+ *+ * @param path The directory to read from.+ * @param prefix_len When inserting entries, the trailing part of path+ * 		will be prefixed after this length.  I.e. given path "/a/b" and+ * 		prefix_len 3, the entries will look like "b/e1", "b/e2", etc.+ * @param alloc_extra Extra bytes to add to each string allocation in+ * 		case you want to append anything funny.+ * @param contents Vector to fill with directory entry names.+ */+extern int git_path_dirload(+	const char *path,+	size_t prefix_len,+	size_t alloc_extra,+	git_vector *contents);+++typedef struct {+	struct stat st;+	size_t      path_len;+	char        path[GIT_FLEX_ARRAY];+} git_path_with_stat;++extern int git_path_with_stat_cmp(const void *a, const void *b);++/**+ * Load all directory entries along with stat info into a vector.+ *+ * This is just like git_path_dirload except that each entry in the+ * vector is a git_path_with_stat structure that contains both the+ * path and the stat info, plus directories will have a / suffixed+ * to their path name.+ */+extern int git_path_dirload_with_stat(+	const char *path,+	size_t prefix_len,+	git_vector *contents);++#endif
+ libgit2/src/pkt.c view
@@ -0,0 +1,361 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"++#include "git2/types.h"+#include "git2/errors.h"+#include "git2/refs.h"+#include "git2/revwalk.h"++#include "pkt.h"+#include "util.h"+#include "netops.h"+#include "posix.h"+#include "buffer.h"++#include <ctype.h>++#define PKT_LEN_SIZE 4+static const char pkt_done_str[] = "0009done\n";+static const char pkt_flush_str[] = "0000";+static const char pkt_have_prefix[] = "0032have ";+static const char pkt_want_prefix[] = "0032want ";++static int flush_pkt(git_pkt **out)+{+	git_pkt *pkt;++	pkt = git__malloc(sizeof(git_pkt));+	GITERR_CHECK_ALLOC(pkt);++	pkt->type = GIT_PKT_FLUSH;+	*out = pkt;++	return 0;+}++/* the rest of the line will be useful for multi_ack */+static int ack_pkt(git_pkt **out, const char *line, size_t len)+{+	git_pkt *pkt;+	GIT_UNUSED(line);+	GIT_UNUSED(len);++	pkt = git__malloc(sizeof(git_pkt));+	GITERR_CHECK_ALLOC(pkt);++	pkt->type = GIT_PKT_ACK;+	*out = pkt;++	return 0;+}++static int nak_pkt(git_pkt **out)+{+	git_pkt *pkt;++	pkt = git__malloc(sizeof(git_pkt));+	GITERR_CHECK_ALLOC(pkt);++	pkt->type = GIT_PKT_NAK;+	*out = pkt;++	return 0;+}++static int pack_pkt(git_pkt **out)+{+	git_pkt *pkt;++	pkt = git__malloc(sizeof(git_pkt));+	GITERR_CHECK_ALLOC(pkt);++	pkt->type = GIT_PKT_PACK;+	*out = pkt;++	return 0;+}++static int comment_pkt(git_pkt **out, const char *line, size_t len)+{+	git_pkt_comment *pkt;++	pkt = git__malloc(sizeof(git_pkt_comment) + len + 1);+	GITERR_CHECK_ALLOC(pkt);++	pkt->type = GIT_PKT_COMMENT;+	memcpy(pkt->comment, line, len);+	pkt->comment[len] = '\0';++	*out = (git_pkt *) pkt;++	return 0;+}++static int err_pkt(git_pkt **out, const char *line, size_t len)+{+	git_pkt_err *pkt;++	/* Remove "ERR " from the line */+	line += 4;+	len -= 4;+	pkt = git__malloc(sizeof(git_pkt_err) + len + 1);+	GITERR_CHECK_ALLOC(pkt);++	pkt->type = GIT_PKT_ERR;+	memcpy(pkt->error, line, len);+	pkt->error[len] = '\0';++	*out = (git_pkt *) pkt;++	return 0;+}++/*+ * Parse an other-ref line.+ */+static int ref_pkt(git_pkt **out, const char *line, size_t len)+{+	int error;+	git_pkt_ref *pkt;++	pkt = git__malloc(sizeof(git_pkt_ref));+	GITERR_CHECK_ALLOC(pkt);++	memset(pkt, 0x0, sizeof(git_pkt_ref));+	pkt->type = GIT_PKT_REF;+	if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)+		goto error_out;++	/* Check for a bit of consistency */+	if (line[GIT_OID_HEXSZ] != ' ') {+		giterr_set(GITERR_NET, "Error parsing pkt-line");+		error = -1;+		goto error_out;+	}++	/* Jump from the name */+	line += GIT_OID_HEXSZ + 1;+	len -= (GIT_OID_HEXSZ + 1);++	if (line[len - 1] == '\n')+		--len;++	pkt->head.name = git__malloc(len + 1);+	GITERR_CHECK_ALLOC(pkt->head.name);++	memcpy(pkt->head.name, line, len);+	pkt->head.name[len] = '\0';++	if (strlen(pkt->head.name) < len) {+		pkt->capabilities = strchr(pkt->head.name, '\0') + 1;+	}++	*out = (git_pkt *)pkt;+	return 0;++error_out:+	git__free(pkt);+	return error;+}++static int32_t parse_len(const char *line)+{+	char num[PKT_LEN_SIZE + 1];+	int i, error;+	int32_t len;+	const char *num_end;++	memcpy(num, line, PKT_LEN_SIZE);+	num[PKT_LEN_SIZE] = '\0';++	for (i = 0; i < PKT_LEN_SIZE; ++i) {+		if (!isxdigit(num[i])) {+			giterr_set(GITERR_NET, "Found invalid hex digit in length");+			return -1;+		}+	}++	if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)+		return error;++	return len;+}++/*+ * As per the documentation, the syntax is:+ *+ * pkt-line	= data-pkt / flush-pkt+ * data-pkt	= pkt-len pkt-payload+ * pkt-len		= 4*(HEXDIG)+ * pkt-payload = (pkt-len -4)*(OCTET)+ * flush-pkt	= "0000"+ *+ * Which means that the first four bytes are the length of the line,+ * in ASCII hexadecimal (including itself)+ */++int git_pkt_parse_line(+	git_pkt **head, const char *line, const char **out, size_t bufflen)+{+	int ret;+	int32_t len;++	/* Not even enough for the length */+	if (bufflen > 0 && bufflen < PKT_LEN_SIZE)+		return GIT_EBUFS;++	len = parse_len(line);+	if (len < 0) {+		/*+		 * If we fail to parse the length, it might be because the+		 * server is trying to send us the packfile already.+		 */+		if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) {+			giterr_clear();+			*out = line;+			return pack_pkt(head);+		}++		return (int)len;+	}++	/*+	 * If we were given a buffer length, then make sure there is+	 * enough in the buffer to satisfy this line+	 */+	if (bufflen > 0 && bufflen < (size_t)len)+		return GIT_EBUFS;++	line += PKT_LEN_SIZE;+	/*+	 * TODO: How do we deal with empty lines? Try again? with the next+	 * line?+	 */+	if (len == PKT_LEN_SIZE) {+		*out = line;+		return 0;+	}++	if (len == 0) { /* Flush pkt */+		*out = line;+		return flush_pkt(head);+	}++	len -= PKT_LEN_SIZE; /* the encoded length includes its own size */++	/* Assming the minimal size is actually 4 */+	if (!git__prefixcmp(line, "ACK"))+		ret = ack_pkt(head, line, len);+	else if (!git__prefixcmp(line, "NAK"))+		ret = nak_pkt(head);+	else if (!git__prefixcmp(line, "ERR "))+		ret = err_pkt(head, line, len);+	else if (*line == '#')+		ret = comment_pkt(head, line, len);+	else+		ret = ref_pkt(head, line, len);++	*out = line + len;++	return ret;+}++void git_pkt_free(git_pkt *pkt)+{+	if (pkt->type == GIT_PKT_REF) {+		git_pkt_ref *p = (git_pkt_ref *) pkt;+		git__free(p->head.name);+	}++	git__free(pkt);+}++int git_pkt_buffer_flush(git_buf *buf)+{+	return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));+}++int git_pkt_send_flush(GIT_SOCKET s)+{++	return gitno_send(s, pkt_flush_str, strlen(pkt_flush_str), 0);+}++static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps, git_buf *buf)+{+	char capstr[20];+	char oid[GIT_OID_HEXSZ +1] = {0};+	unsigned int len;++	if (caps->ofs_delta)+		strncpy(capstr, GIT_CAP_OFS_DELTA, sizeof(capstr));++	len = (unsigned int)+		(strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ ++		 strlen(capstr) + 1 /* LF */);+	git_buf_grow(buf, git_buf_len(buf) + len);++	git_oid_fmt(oid, &head->oid);+	return git_buf_printf(buf, "%04xwant %s%c%s\n", len, oid, 0, capstr);+}++/*+ * All "want" packets have the same length and format, so what we do+ * is overwrite the OID each time.+ */++int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf)+{+	unsigned int i = 0;+	git_remote_head *head;++	if (caps->common) {+		for (; i < refs->length; ++i) {+			head = refs->contents[i];+			if (!head->local)+				break;+		}++		if (buffer_want_with_caps(refs->contents[i], caps, buf) < 0)+			return -1;++		i++;+	}++	for (; i < refs->length; ++i) {+		char oid[GIT_OID_HEXSZ];++		head = refs->contents[i];+		if (head->local)+			continue;++		git_oid_fmt(oid, &head->oid);+		git_buf_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));+		git_buf_put(buf, oid, GIT_OID_HEXSZ);+		git_buf_putc(buf, '\n');+		if (git_buf_oom(buf))+			return -1;+	}++	return git_pkt_buffer_flush(buf);+}++int git_pkt_buffer_have(git_oid *oid, git_buf *buf)+{+	char oidhex[GIT_OID_HEXSZ + 1];++	memset(oidhex, 0x0, sizeof(oidhex));+	git_oid_fmt(oidhex, oid);+	return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);+}++int git_pkt_buffer_done(git_buf *buf)+{+	return git_buf_puts(buf, pkt_done_str);+}
+ libgit2/src/pkt.h view
@@ -0,0 +1,81 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#ifndef INCLUDE_pkt_h__+#define INCLUDE_pkt_h__++#include "common.h"+#include "transport.h"+#include "buffer.h"+#include "posix.h"+#include "git2/net.h"++enum git_pkt_type {+	GIT_PKT_CMD,+	GIT_PKT_FLUSH,+	GIT_PKT_REF,+	GIT_PKT_HAVE,+	GIT_PKT_ACK,+	GIT_PKT_NAK,+	GIT_PKT_PACK,+	GIT_PKT_COMMENT,+	GIT_PKT_ERR,+};++/* Used for multi-ack */+enum git_ack_status {+	GIT_ACK_NONE,+	GIT_ACK_CONTINUE,+	GIT_ACK_COMMON,+	GIT_ACK_READY+};++/* This would be a flush pkt */+typedef struct {+	enum git_pkt_type type;+} git_pkt;++struct git_pkt_cmd {+	enum git_pkt_type type;+	char *cmd;+	char *path;+	char *host;+};++/* This is a pkt-line with some info in it */+typedef struct {+	enum git_pkt_type type;+	git_remote_head head;+	char *capabilities;+} git_pkt_ref;++/* Useful later */+typedef struct {+	enum git_pkt_type type;+	git_oid oid;+	enum git_ack_status status;+} git_pkt_ack;++typedef struct {+	enum git_pkt_type type;+	char comment[GIT_FLEX_ARRAY];+} git_pkt_comment;++typedef struct {+	enum git_pkt_type type;+	char error[GIT_FLEX_ARRAY];+} git_pkt_err;++int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);+int git_pkt_buffer_flush(git_buf *buf);+int git_pkt_send_flush(GIT_SOCKET s);+int git_pkt_buffer_done(git_buf *buf);+int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf);+int git_pkt_buffer_have(git_oid *oid, git_buf *buf);+void git_pkt_free(git_pkt *pkt);++#endif
+ libgit2/src/pool.c view
@@ -0,0 +1,294 @@+#include "pool.h"+#ifndef GIT_WIN32+#include <unistd.h>+#endif++struct git_pool_page {+	git_pool_page *next;+	uint32_t size;+	uint32_t avail;+	char data[GIT_FLEX_ARRAY];+};++#define GIT_POOL_MIN_USABLE	4+#define GIT_POOL_MIN_PAGESZ	2 * sizeof(void*)++static void *pool_alloc_page(git_pool *pool, uint32_t size);+static void pool_insert_page(git_pool *pool, git_pool_page *page);++int git_pool_init(+	git_pool *pool, uint32_t item_size, uint32_t items_per_page)+{+	assert(pool);++	if (!item_size)+		item_size = 1;+	/* round up item_size for decent object alignment */+	if (item_size > 4)+		item_size = (item_size + 7) & ~7;+	else if (item_size == 3)+		item_size = 4;++	if (!items_per_page)+		items_per_page = git_pool__suggest_items_per_page(item_size);+	if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ)+		items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size;++	memset(pool, 0, sizeof(git_pool));+	pool->item_size = item_size;+	pool->page_size = item_size * items_per_page;++	return 0;+}++void git_pool_clear(git_pool *pool)+{+	git_pool_page *scan, *next;++	for (scan = pool->open; scan != NULL; scan = next) {+		next = scan->next;+		git__free(scan);+	}+	pool->open = NULL;++	for (scan = pool->full; scan != NULL; scan = next) {+		next = scan->next;+		git__free(scan);+	}+	pool->full = NULL;++	pool->free_list = NULL;++	pool->items = 0;++	pool->has_string_alloc     = 0;+	pool->has_multi_item_alloc = 0;+	pool->has_large_page_alloc = 0;+}++void git_pool_swap(git_pool *a, git_pool *b)+{+	git_pool temp;++	if (a == b)+		return;++	memcpy(&temp, a, sizeof(temp));+	memcpy(a, b, sizeof(temp));+	memcpy(b, &temp, sizeof(temp));+}++static void pool_insert_page(git_pool *pool, git_pool_page *page)+{+	git_pool_page *scan;++	/* If there are no open pages or this page has the most open space,+	 * insert it at the beginning of the list.  This is the common case.+	 */+	if (pool->open == NULL || pool->open->avail < page->avail) {+		page->next = pool->open;+		pool->open = page;+		return;+	}++	/* Otherwise insert into sorted position. */+	for (scan = pool->open;+		 scan->next && scan->next->avail > page->avail;+		 scan = scan->next);+	page->next = scan->next;+	scan->next = page;+}++static void *pool_alloc_page(git_pool *pool, uint32_t size)+{+	git_pool_page *page;+	uint32_t alloc_size;++	if (size <= pool->page_size)+		alloc_size = pool->page_size;+	else {+		alloc_size = size;+		pool->has_large_page_alloc = 1;+	}++	page = git__calloc(1, alloc_size + sizeof(git_pool_page));+	if (!page)+		return NULL;++	page->size  = alloc_size;+	page->avail = alloc_size - size;++	if (page->avail > 0)+		pool_insert_page(pool, page);+	else {+		page->next = pool->full;+		pool->full = page;+	}++	pool->items++;++	return page->data;+}++GIT_INLINE(void) pool_remove_page(+	git_pool *pool, git_pool_page *page, git_pool_page *prev)+{+	if (prev == NULL)+		pool->open = page->next;+	else+		prev->next = page->next;+}++void *git_pool_malloc(git_pool *pool, uint32_t items)+{+	git_pool_page *scan = pool->open, *prev;+	uint32_t size = items * pool->item_size;+	void *ptr = NULL;++	pool->has_string_alloc = 0;+	if (items > 1)+		pool->has_multi_item_alloc = 1;+	else if (pool->free_list != NULL) {+		ptr = pool->free_list;+		pool->free_list = *((void **)pool->free_list);+		return ptr;+	}++	/* just add a block if there is no open one to accomodate this */+	if (size >= pool->page_size || !scan || scan->avail < size)+		return pool_alloc_page(pool, size);++	pool->items++;++	/* find smallest block in free list with space */+	for (scan = pool->open, prev = NULL;+		 scan->next && scan->next->avail >= size;+		 prev = scan, scan = scan->next);++	/* allocate space from the block */+	ptr = &scan->data[scan->size - scan->avail];+	scan->avail -= size;++	/* move to full list if there is almost no space left */+	if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) {+		pool_remove_page(pool, scan, prev);+		scan->next = pool->full;+		pool->full = scan;+	}+	/* reorder list if block is now smaller than the one after it */+	else if (scan->next != NULL && scan->next->avail > scan->avail) {+		pool_remove_page(pool, scan, prev);+		pool_insert_page(pool, scan);+	}++	return ptr;+}++char *git_pool_strndup(git_pool *pool, const char *str, size_t n)+{+	void *ptr = NULL;++	assert(pool && str && pool->item_size == sizeof(char));++	if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) {+		memcpy(ptr, str, n);+		*(((char *)ptr) + n) = '\0';+	}+	pool->has_string_alloc = 1;++	return ptr;+}++char *git_pool_strdup(git_pool *pool, const char *str)+{+	assert(pool && str && pool->item_size == sizeof(char));++	return git_pool_strndup(pool, str, strlen(str));+}++char *git_pool_strcat(git_pool *pool, const char *a, const char *b)+{+	void *ptr;+	size_t len_a, len_b;++	assert(pool && a && b && pool->item_size == sizeof(char));++	len_a = a ? strlen(a) : 0;+	len_b = b ? strlen(b) : 0;++	if ((ptr = git_pool_malloc(pool, (uint32_t)(len_a + len_b + 1))) != NULL) {+		if (len_a)+			memcpy(ptr, a, len_a);+		if (len_b)+			memcpy(((char *)ptr) + len_a, b, len_b);+		*(((char *)ptr) + len_a + len_b) = '\0';+	}+	pool->has_string_alloc = 1;++	return ptr;+}++void git_pool_free(git_pool *pool, void *ptr)+{+	assert(pool && ptr && pool->item_size >= sizeof(void*));++	*((void **)ptr) = pool->free_list;+	pool->free_list = ptr;+}++uint32_t git_pool__open_pages(git_pool *pool)+{+	uint32_t ct = 0;+	git_pool_page *scan;+	for (scan = pool->open; scan != NULL; scan = scan->next) ct++;+	return ct;+}++uint32_t git_pool__full_pages(git_pool *pool)+{+	uint32_t ct = 0;+	git_pool_page *scan;+	for (scan = pool->full; scan != NULL; scan = scan->next) ct++;+	return ct;+}++bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)+{+	git_pool_page *scan;+	for (scan = pool->open; scan != NULL; scan = scan->next)+		if ((void *)scan->data <= ptr &&+			(void *)(((char *)scan->data) + scan->size) > ptr)+			return true;+	for (scan = pool->full; scan != NULL; scan = scan->next)+		if ((void *)scan->data <= ptr &&+			(void *)(((char *)scan->data) + scan->size) > ptr)+			return true;+	return false;+}++uint32_t git_pool__system_page_size(void)+{+	static uint32_t size = 0;++	if (!size) {+#ifdef GIT_WIN32+		SYSTEM_INFO info;+		GetSystemInfo(&info);+		size = (uint32_t)info.dwPageSize;+#else+		size = (uint32_t)sysconf(_SC_PAGE_SIZE);+#endif++		size -= 2 * sizeof(void *); /* allow space for malloc overhead */+	}++	return size;+}++uint32_t git_pool__suggest_items_per_page(uint32_t item_size)+{+	uint32_t page_bytes =+		git_pool__system_page_size() - sizeof(git_pool_page);+	return page_bytes / item_size;+}+
+ libgit2/src/pool.h view
@@ -0,0 +1,125 @@+/*+ * Copyright (C) 2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_pool_h__+#define INCLUDE_pool_h__++#include "common.h"++typedef struct git_pool_page git_pool_page;++/**+ * Chunked allocator.+ *+ * A `git_pool` can be used when you want to cheaply allocate+ * multiple items of the same type and are willing to free them+ * all together with a single call.  The two most common cases+ * are a set of fixed size items (such as lots of OIDs) or a+ * bunch of strings.+ *+ * Internally, a `git_pool` allocates pages of memory and then+ * deals out blocks from the trailing unused portion of each page.+ * The pages guarantee that the number of actual allocations done+ * will be much smaller than the number of items needed.+ *+ * For examples of how to set up a `git_pool` see `git_pool_init`.+ */+typedef struct {+	git_pool_page *open; /* pages with space left */+	git_pool_page *full; /* pages with no space left */+	void *free_list;     /* optional: list of freed blocks */+	uint32_t item_size;  /* size of single alloc unit in bytes */+	uint32_t page_size;  /* size of page in bytes */+	uint32_t items;+	unsigned has_string_alloc : 1; /* was the strdup function used */+	unsigned has_multi_item_alloc : 1; /* was items ever > 1 in malloc */+	unsigned has_large_page_alloc : 1; /* are any pages > page_size */+} git_pool;++#define GIT_POOL_INIT_STRINGPOOL { 0, 0, 0, 1, 4000, 0, 0, 0, 0 }++/**+ * Initialize a pool.+ *+ * To allocation strings, use like this:+ *+ *     git_pool_init(&string_pool, 1, 0);+ *     my_string = git_pool_strdup(&string_pool, your_string);+ *+ * To allocate items of fixed size, use like this:+ *+ *     git_pool_init(&pool, sizeof(item), 0);+ *     my_item = git_pool_malloc(&pool, 1);+ *+ * Of course, you can use this in other ways, but those are the+ * two most common patterns.+ */+extern int git_pool_init(+	git_pool *pool, uint32_t item_size, uint32_t items_per_page);++/**+ * Free all items in pool+ */+extern void git_pool_clear(git_pool *pool);++/**+ * Swap two pools with one another+ */+extern void git_pool_swap(git_pool *a, git_pool *b);++/**+ * Allocate space for one or more items from a pool.+ */+extern void *git_pool_malloc(git_pool *pool, uint32_t items);++/**+ * Allocate space and duplicate string data into it.+ *+ * This is allowed only for pools with item_size == sizeof(char)+ */+extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n);++/**+ * Allocate space and duplicate a string into it.+ *+ * This is allowed only for pools with item_size == sizeof(char)+ */+extern char *git_pool_strdup(git_pool *pool, const char *str);++/**+ * Allocate space for the concatenation of two strings.+ *+ * This is allowed only for pools with item_size == sizeof(char)+ */+extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);++/**+ * Push a block back onto the free list for the pool.+ *+ * This is allowed only if the item_size is >= sizeof(void*).+ *+ * In some cases, it is helpful to "release" an allocated block+ * for reuse.  Pools don't support a general purpose free, but+ * they will keep a simple free blocks linked list provided the+ * native block size is large enough to hold a void pointer+ */+extern void git_pool_free(git_pool *pool, void *ptr);++/*+ * Misc utilities+ */++extern uint32_t git_pool__open_pages(git_pool *pool);++extern uint32_t git_pool__full_pages(git_pool *pool);++extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr);++extern uint32_t git_pool__system_page_size(void);++extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size);++#endif
+ libgit2/src/posix.c view
@@ -0,0 +1,116 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "posix.h"+#include "path.h"+#include <stdio.h>+#include <ctype.h>++#ifndef GIT_WIN32++int p_open(const char *path, int flags, ...)+{+	mode_t mode = 0;++	if (flags & O_CREAT)+	{+		va_list arg_list;++		va_start(arg_list, flags);+		mode = (mode_t)va_arg(arg_list, int);+		va_end(arg_list);+	}++	return open(path, flags | O_BINARY, mode);+}++int p_creat(const char *path, mode_t mode)+{+	return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);+}++int p_getcwd(char *buffer_out, size_t size)+{+	char *cwd_buffer;++	assert(buffer_out && size > 0);++	cwd_buffer = getcwd(buffer_out, size);++	if (cwd_buffer == NULL)+		return -1;++	git_path_mkposix(buffer_out);+	git_path_string_to_dir(buffer_out, size); /* append trailing slash */++	return 0;+}++int p_rename(const char *from, const char *to)+{+	if (!link(from, to)) {+		p_unlink(from);+		return 0;+	}++	if (!rename(from, to))+		return 0;++	return -1;+}++#endif++int p_read(git_file fd, void *buf, size_t cnt)+{+	char *b = buf;+	while (cnt) {+		ssize_t r;+#ifdef GIT_WIN32+		assert((size_t)((unsigned int)cnt) == cnt);+		r = read(fd, b, (unsigned int)cnt);+#else+		r = read(fd, b, cnt);+#endif+		if (r < 0) {+			if (errno == EINTR || errno == EAGAIN)+				continue;+			return -1;+		}+		if (!r)+			break;+		cnt -= r;+		b += r;+	}+	return (int)(b - (char *)buf);+}++int p_write(git_file fd, const void *buf, size_t cnt)+{+	const char *b = buf;+	while (cnt) {+		ssize_t r;+#ifdef GIT_WIN32+		assert((size_t)((unsigned int)cnt) == cnt);+		r = write(fd, b, (unsigned int)cnt);+#else+		r = write(fd, b, cnt);+#endif+		if (r < 0) {+			if (errno == EINTR || errno == EAGAIN)+				continue;+			return -1;+		}+		if (!r) {+			errno = EPIPE;+			return -1;+		}+		cnt -= r;+		b += r;+	}+	return 0;+}
+ libgit2/src/posix.h view
@@ -0,0 +1,79 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_posix_h__+#define INCLUDE_posix_h__++#include "common.h"+#include <fcntl.h>+#include <time.h>++#define S_IFGITLINK 0160000+#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)++#if !defined(O_BINARY)+#define O_BINARY 0+#endif++typedef int git_file;++/**+ * Standard POSIX Methods+ *+ * All the methods starting with the `p_` prefix are+ * direct ports of the standard POSIX methods. + *+ * Some of the methods are slightly wrapped to provide+ * saner defaults. Some of these methods are emulated+ * in Windows platforns.+ *+ * Use your manpages to check the docs on these.+ * Straightforward + */++extern int p_read(git_file fd, void *buf, size_t cnt);+extern int p_write(git_file fd, const void *buf, size_t cnt);++#define p_fstat(f,b) fstat(f, b)+#define p_lseek(f,n,w) lseek(f, n, w)+#define p_close(fd) close(fd)+#define p_umask(m) umask(m)++extern int p_open(const char *path, int flags, ...);+extern int p_creat(const char *path, mode_t mode);+extern int p_getcwd(char *buffer_out, size_t size);+extern int p_rename(const char *from, const char *to);++#ifndef GIT_WIN32++#define p_stat(p,b) stat(p, b)+#define p_chdir(p) chdir(p)+#define p_rmdir(p) rmdir(p)+#define p_chmod(p,m) chmod(p, m)+#define p_access(p,m) access(p,m)+#define p_recv(s,b,l,f) recv(s,b,l,f)+#define p_send(s,b,l,f) send(s,b,l,f)+typedef int GIT_SOCKET;+#define INVALID_SOCKET -1++#else++typedef SOCKET GIT_SOCKET;++#endif++/**+ * Platform-dependent methods+ */+#ifdef GIT_WIN32+#	include "win32/posix.h"+#else+#	include "unix/posix.h"+#endif++#define p_readdir_r(d,e,r) readdir_r(d,e,r)++#endif
+ libgit2/src/pqueue.c view
@@ -0,0 +1,141 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "pqueue.h"++#define left(i)	((i) << 1)+#define right(i) (((i) << 1) + 1)+#define parent(i) ((i) >> 1)++int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri)+{+	assert(q);++	/* Need to allocate n+1 elements since element 0 isn't used. */+	q->d = git__malloc((n + 1) * sizeof(void *));+	GITERR_CHECK_ALLOC(q->d);++	q->size = 1;+	q->avail = q->step = (n + 1); /* see comment above about n+1 */+	q->cmppri = cmppri;++	return 0;+}+++void git_pqueue_free(git_pqueue *q)+{+	git__free(q->d);+	q->d = NULL;+}++void git_pqueue_clear(git_pqueue *q)+{+	q->size = 1;+}++size_t git_pqueue_size(git_pqueue *q)+{+	/* queue element 0 exists but doesn't count since it isn't used. */+	return (q->size - 1);+}+++static void bubble_up(git_pqueue *q, size_t i)+{+	size_t parent_node;+	void *moving_node = q->d[i];++	for (parent_node = parent(i);+			((i > 1) && q->cmppri(q->d[parent_node], moving_node));+			i = parent_node, parent_node = parent(i)) {+		q->d[i] = q->d[parent_node];+	}++	q->d[i] = moving_node;+}+++static size_t maxchild(git_pqueue *q, size_t i)+{+	size_t child_node = left(i);++	if (child_node >= q->size)+		return 0;++	if ((child_node + 1) < q->size &&+		q->cmppri(q->d[child_node], q->d[child_node + 1]))+		child_node++; /* use right child instead of left */++	return child_node;+}+++static void percolate_down(git_pqueue *q, size_t i)+{+	size_t child_node;+	void *moving_node = q->d[i];++	while ((child_node = maxchild(q, i)) != 0 &&+			q->cmppri(moving_node, q->d[child_node])) {+		q->d[i] = q->d[child_node];+		i = child_node;+	}++	q->d[i] = moving_node;+}+++int git_pqueue_insert(git_pqueue *q, void *d)+{+	void *tmp;+	size_t i;+	size_t newsize;++	if (!q) return 1;++	/* allocate more memory if necessary */+	if (q->size >= q->avail) {+		newsize = q->size + q->step;+		tmp = git__realloc(q->d, sizeof(void *) * newsize);+		GITERR_CHECK_ALLOC(tmp);++		q->d = tmp;+		q->avail = newsize;+	}++	/* insert item */+	i = q->size++;+	q->d[i] = d;+	bubble_up(q, i);++	return 0;+}+++void *git_pqueue_pop(git_pqueue *q)+{+	void *head;++	if (!q || q->size == 1)+		return NULL;++	head = q->d[1];+	q->d[1] = q->d[--q->size];+	percolate_down(q, 1);++	return head;+}+++void *git_pqueue_peek(git_pqueue *q)+{+	if (!q || q->size == 1)+		return NULL;+	return q->d[1];+}
+ libgit2/src/pqueue.h view
@@ -0,0 +1,81 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#ifndef INCLUDE_pqueue_h__+#define INCLUDE_pqueue_h__++/** callback functions to get/set/compare the priority of an element */+typedef int (*git_pqueue_cmp)(void *a, void *b);++/** the priority queue handle */+typedef struct {+	size_t size, avail, step;+	git_pqueue_cmp cmppri;+	void **d;+} git_pqueue;+++/**+ * initialize the queue+ *+ * @param n the initial estimate of the number of queue items for which memory+ *			should be preallocated+ * @param cmppri the callback function to compare two nodes of the queue+ *+ * @Return the handle or NULL for insufficent memory+ */+int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri);+++/**+ * free all memory used by the queue+ * @param q the queue+ */+void git_pqueue_free(git_pqueue *q);++/**+ * clear all the elements in the queue+ * @param q the queue+ */+void git_pqueue_clear(git_pqueue *q);++/**+ * return the size of the queue.+ * @param q the queue+ */+size_t git_pqueue_size(git_pqueue *q);+++/**+ * insert an item into the queue.+ * @param q the queue+ * @param d the item+ * @return 0 on success+ */+int git_pqueue_insert(git_pqueue *q, void *d);+++/**+ * pop the highest-ranking item from the queue.+ * @param p the queue+ * @param d where to copy the entry to+ * @return NULL on error, otherwise the entry+ */+void *git_pqueue_pop(git_pqueue *q);+++/**+ * access highest-ranking item without removing it.+ * @param q the queue+ * @param d the entry+ * @return NULL on error, otherwise the entry+ */+void *git_pqueue_peek(git_pqueue *q);++#endif /* PQUEUE_H */+/** @} */+
+ libgit2/src/protocol.c view
@@ -0,0 +1,58 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "protocol.h"+#include "pkt.h"+#include "buffer.h"++int git_protocol_store_refs(git_protocol *p, const char *data, size_t len)+{+	git_buf *buf = &p->buf;+	git_vector *refs = p->refs;+	int error;+	const char *line_end, *ptr;++	if (len == 0) { /* EOF */+		if (git_buf_len(buf) != 0) {+			giterr_set(GITERR_NET, "Unexpected EOF");+			return p->error = -1;+		} else {+			return 0;+		}+	}++	git_buf_put(buf, data, len);+	ptr = buf->ptr;+	while (1) {+		git_pkt *pkt;++		if (git_buf_len(buf) == 0)+			return 0;++		error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf));+		if (error == GIT_EBUFS)+			return 0; /* Ask for more */+		if (error < 0)+			return p->error = -1;++		git_buf_consume(buf, line_end);++		if (pkt->type == GIT_PKT_ERR) {+			giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);+			git__free(pkt);+			return -1;+		}++		if (git_vector_insert(refs, pkt) < 0)+			return p->error = -1;++		if (pkt->type == GIT_PKT_FLUSH)+			p->flush = 1;+	}++	return 0;+}
+ libgit2/src/protocol.h view
@@ -0,0 +1,23 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_protocol_h__+#define INCLUDE_protocol_h__++#include "transport.h"+#include "buffer.h"++typedef struct {+	git_transport *transport;+	git_vector *refs;+	git_buf buf;+	int error;+	unsigned int flush :1;+} git_protocol;++int git_protocol_store_refs(git_protocol *p, const char *data, size_t len);++#endif
+ libgit2/src/reflog.c view
@@ -0,0 +1,340 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "reflog.h"+#include "repository.h"+#include "filebuf.h"+#include "signature.h"++static int reflog_init(git_reflog **reflog, git_reference *ref)+{+	git_reflog *log;++	*reflog = NULL;++	log = git__calloc(1, sizeof(git_reflog));+	GITERR_CHECK_ALLOC(log);++	log->ref_name = git__strdup(ref->name);+	GITERR_CHECK_ALLOC(log->ref_name);++	if (git_vector_init(&log->entries, 0, NULL) < 0) {+		git__free(log->ref_name);+		git__free(log);+		return -1;+	}++	*reflog = log;++	return 0;+}++static int reflog_write(const char *log_path, const char *oid_old,+			const char *oid_new, const git_signature *committer,+			const char *msg)+{+	int error;+	git_buf log = GIT_BUF_INIT;+	git_filebuf fbuf = GIT_FILEBUF_INIT;+	bool trailing_newline = false;++	assert(log_path && oid_old && oid_new && committer);++	if (msg) {+		const char *newline = strchr(msg, '\n');+		if (newline) {+			if (*(newline + 1) == '\0')+				trailing_newline = true;+			else {+				giterr_set(GITERR_INVALID, "Reflog message cannot contain newline");+				return -1;+			}+		}+	}++	git_buf_puts(&log, oid_old);+	git_buf_putc(&log, ' ');++	git_buf_puts(&log, oid_new);++	git_signature__writebuf(&log, " ", committer);+	git_buf_truncate(&log, log.size - 1); /* drop LF */++	if (msg) {+		git_buf_putc(&log, '\t');+		git_buf_puts(&log, msg);+	}++	if (!trailing_newline)+		git_buf_putc(&log, '\n');++	if (git_buf_oom(&log)) {+		git_buf_free(&log);+		return -1;+	}++	error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND);+	if (!error) {+		if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)+			git_filebuf_cleanup(&fbuf);+		else+			error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);+	}++	git_buf_free(&log);++	return error;+}++static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)+{+	const char *ptr;+	git_reflog_entry *entry;++#define seek_forward(_increase) do { \+	if (_increase >= buf_size) { \+		giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \+		goto fail; \+	} \+	buf += _increase; \+	buf_size -= _increase; \+	} while (0)++	while (buf_size > GIT_REFLOG_SIZE_MIN) {+		entry = git__malloc(sizeof(git_reflog_entry));+		GITERR_CHECK_ALLOC(entry);++		entry->committer = git__malloc(sizeof(git_signature));+		GITERR_CHECK_ALLOC(entry->committer);++		if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)+			goto fail;+		seek_forward(GIT_OID_HEXSZ + 1);++		if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)+			goto fail;+		seek_forward(GIT_OID_HEXSZ + 1);++		ptr = buf;++		/* Seek forward to the end of the signature. */+		while (*buf && *buf != '\t' && *buf != '\n')+			seek_forward(1);++		if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)+			goto fail;++		if (*buf == '\t') {+			/* We got a message. Read everything till we reach LF. */+			seek_forward(1);+			ptr = buf;++			while (*buf && *buf != '\n')+				seek_forward(1);++			entry->msg = git__strndup(ptr, buf - ptr);+			GITERR_CHECK_ALLOC(entry->msg);+		} else+			entry->msg = NULL;++		while (*buf && *buf == '\n' && buf_size > 1)+			seek_forward(1);++		if (git_vector_insert(&log->entries, entry) < 0)+			goto fail;+	}++	return 0;++#undef seek_forward++fail:+	if (entry) {+		git__free(entry->committer);+		git__free(entry);+	}+	return -1;+}++void git_reflog_free(git_reflog *reflog)+{+	unsigned int i;+	git_reflog_entry *entry;++	for (i=0; i < reflog->entries.length; i++) {+		entry = git_vector_get(&reflog->entries, i);++		git_signature_free(entry->committer);++		git__free(entry->msg);+		git__free(entry);+	}++	git_vector_free(&reflog->entries);+	git__free(reflog->ref_name);+	git__free(reflog);+}++int git_reflog_read(git_reflog **reflog, git_reference *ref)+{+	int error;+	git_buf log_path = GIT_BUF_INIT;+	git_buf log_file = GIT_BUF_INIT;+	git_reflog *log = NULL;++	*reflog = NULL;++	if (reflog_init(&log, ref) < 0)+		return -1;++	error = git_buf_join_n(&log_path, '/', 3,+		ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);++	if (!error)+		error = git_futils_readbuffer(&log_file, log_path.ptr);++	if (!error)+		error = reflog_parse(log, log_file.ptr, log_file.size);++	if (!error)+		*reflog = log;+	else+		git_reflog_free(log);++	git_buf_free(&log_file);+	git_buf_free(&log_path);++	return error;+}++int git_reflog_write(git_reference *ref, const git_oid *oid_old,+				const git_signature *committer, const char *msg)+{+	int error;+	char old[GIT_OID_HEXSZ+1];+	char new[GIT_OID_HEXSZ+1];+	git_buf log_path = GIT_BUF_INIT;+	git_reference *r;+	const git_oid *oid;++	if ((error = git_reference_resolve(&r, ref)) < 0)+		return error;++	oid = git_reference_oid(r);+	if (oid == NULL) {+		giterr_set(GITERR_REFERENCE,+			"Failed to write reflog. Cannot resolve reference `%s`", r->name);+		git_reference_free(r);+		return -1;+	}++	git_oid_tostr(new, GIT_OID_HEXSZ+1, oid);++	git_reference_free(r);++	error = git_buf_join_n(&log_path, '/', 3,+		ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);+	if (error < 0)+		goto cleanup;++	if (git_path_exists(log_path.ptr) == false) {+		error = git_futils_mkpath2file(log_path.ptr, GIT_REFLOG_DIR_MODE);+	} else if (git_path_isfile(log_path.ptr) == false) {+		giterr_set(GITERR_REFERENCE,+			"Failed to write reflog. `%s` is directory", log_path.ptr);+		error = -1;+	} else if (oid_old == NULL) {+		giterr_set(GITERR_REFERENCE,+			"Failed to write reflog. Old OID cannot be NULL for existing reference");+		error = -1;+	}+	if (error < 0)+		goto cleanup;++	if (oid_old)+		git_oid_tostr(old, sizeof(old), oid_old);+	else+		p_snprintf(old, sizeof(old), "%0*d", GIT_OID_HEXSZ, 0);++	error = reflog_write(log_path.ptr, old, new, committer, msg);++cleanup:+	git_buf_free(&log_path);+	return error;+}++int git_reflog_rename(git_reference *ref, const char *new_name)+{+	int error;+	git_buf old_path = GIT_BUF_INIT;+	git_buf new_path = GIT_BUF_INIT;++	if (!git_buf_join_n(&old_path, '/', 3, ref->owner->path_repository,+			GIT_REFLOG_DIR, ref->name) &&+		!git_buf_join_n(&new_path, '/', 3, ref->owner->path_repository,+			GIT_REFLOG_DIR, new_name))+		error = p_rename(git_buf_cstr(&old_path), git_buf_cstr(&new_path));+	else+		error = -1;++	git_buf_free(&old_path);+	git_buf_free(&new_path);++	return error;+}++int git_reflog_delete(git_reference *ref)+{+	int error;+	git_buf path = GIT_BUF_INIT;++	error = git_buf_join_n(+		&path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);++	if (!error && git_path_exists(path.ptr))+		error = p_unlink(path.ptr);++	git_buf_free(&path);++	return error;+}++unsigned int git_reflog_entrycount(git_reflog *reflog)+{+	assert(reflog);+	return reflog->entries.length;+}++const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx)+{+	assert(reflog);+	return git_vector_get(&reflog->entries, idx);+}++const git_oid * git_reflog_entry_oidold(const git_reflog_entry *entry)+{+	assert(entry);+	return &entry->oid_old;+}++const git_oid * git_reflog_entry_oidnew(const git_reflog_entry *entry)+{+	assert(entry);+	return &entry->oid_cur;+}++git_signature * git_reflog_entry_committer(const git_reflog_entry *entry)+{+	assert(entry);+	return entry->committer;+}++char * git_reflog_entry_msg(const git_reflog_entry *entry)+{+	assert(entry);+	return entry->msg;+}
+ libgit2/src/reflog.h view
@@ -0,0 +1,34 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_reflog_h__+#define INCLUDE_reflog_h__++#include "common.h"+#include "git2/reflog.h"+#include "vector.h"++#define GIT_REFLOG_DIR "logs/"+#define GIT_REFLOG_DIR_MODE 0777+#define GIT_REFLOG_FILE_MODE 0666++#define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17)++struct git_reflog_entry {+	git_oid oid_old;+	git_oid oid_cur;++	git_signature *committer;++	char *msg;+};++struct git_reflog {+	char *ref_name;+	git_vector entries;+};++#endif /* INCLUDE_reflog_h__ */
+ libgit2/src/refs.c view
@@ -0,0 +1,1707 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "refs.h"+#include "hash.h"+#include "repository.h"+#include "fileops.h"+#include "pack.h"+#include "reflog.h"++#include <git2/tag.h>+#include <git2/object.h>++GIT__USE_STRMAP;++#define DEFAULT_NESTING_LEVEL	5+#define MAX_NESTING_LEVEL		10++enum {+	GIT_PACKREF_HAS_PEEL = 1,+	GIT_PACKREF_WAS_LOOSE = 2+};++struct packref {+	git_oid oid;+	git_oid peel;+	char flags;+	char name[GIT_FLEX_ARRAY];+};++static int reference_read(+	git_buf *file_content,+	time_t *mtime,+	const char *repo_path,+	const char *ref_name,+	int *updated);++/* loose refs */+static int loose_parse_symbolic(git_reference *ref, git_buf *file_content);+static int loose_parse_oid(git_oid *ref, git_buf *file_content);+static int loose_lookup(git_reference *ref);+static int loose_lookup_to_packfile(struct packref **ref_out,+	git_repository *repo, const char *name);+static int loose_write(git_reference *ref);++/* packed refs */+static int packed_parse_peel(struct packref *tag_ref,+	const char **buffer_out, const char *buffer_end);+static int packed_parse_oid(struct packref **ref_out,+	const char **buffer_out, const char *buffer_end);+static int packed_load(git_repository *repo);+static int packed_loadloose(git_repository *repository);+static int packed_write_ref(struct packref *ref, git_filebuf *file);+static int packed_find_peel(git_repository *repo, struct packref *ref);+static int packed_remove_loose(git_repository *repo, git_vector *packing_list);+static int packed_sort(const void *a, const void *b);+static int packed_lookup(git_reference *ref);+static int packed_write(git_repository *repo);++/* internal helpers */+static int reference_path_available(git_repository *repo,+	const char *ref, const char *old_ref);+static int reference_delete(git_reference *ref);+static int reference_lookup(git_reference *ref);++/* name normalization */+static int normalize_name(char *buffer_out, size_t out_size,+	const char *name, int is_oid_ref);+++void git_reference_free(git_reference *reference)+{+	if (reference == NULL)+		return;++	git__free(reference->name);+	reference->name = NULL;++	if (reference->flags & GIT_REF_SYMBOLIC) {+		git__free(reference->target.symbolic);+		reference->target.symbolic = NULL;+	}++	git__free(reference);+}++static int reference_alloc(+	git_reference **ref_out,+	git_repository *repo,+	const char *name)+{+	git_reference *reference = NULL;++	assert(ref_out && repo && name);++	reference = git__malloc(sizeof(git_reference));+	GITERR_CHECK_ALLOC(reference);++	memset(reference, 0x0, sizeof(git_reference));+	reference->owner = repo;++	reference->name = git__strdup(name);+	GITERR_CHECK_ALLOC(reference->name);++	*ref_out = reference;+	return 0;+}++static int reference_read(+	git_buf *file_content,+	time_t *mtime,+	const char *repo_path,+	const char *ref_name,+	int *updated)+{+	git_buf path = GIT_BUF_INIT;+	int result;++	assert(file_content && repo_path && ref_name);++	/* Determine the full path of the file */+	if (git_buf_joinpath(&path, repo_path, ref_name) < 0)+		return -1;++	result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);+	git_buf_free(&path);+	return result;+}++static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)+{+	const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);+	const char *refname_start;+	char *eol;++	refname_start = (const char *)file_content->ptr;++	if (git_buf_len(file_content) < header_len + 1)+		goto corrupt;++	/*+	 * Assume we have already checked for the header+	 * before calling this function+	 */+	refname_start += header_len;++	ref->target.symbolic = git__strdup(refname_start);+	GITERR_CHECK_ALLOC(ref->target.symbolic);++	/* remove newline at the end of file */+	eol = strchr(ref->target.symbolic, '\n');+	if (eol == NULL)+		goto corrupt;++	*eol = '\0';+	if (eol[-1] == '\r')+		eol[-1] = '\0';++	return 0;++corrupt:+	giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");+	return -1;+}++static int loose_parse_oid(git_oid *oid, git_buf *file_content)+{+	char *buffer;++	buffer = (char *)file_content->ptr;++	/* File format: 40 chars (OID) + newline */+	if (git_buf_len(file_content) < GIT_OID_HEXSZ + 1)+		goto corrupt;++	if (git_oid_fromstr(oid, buffer) < 0)+		goto corrupt;++	buffer = buffer + GIT_OID_HEXSZ;+	if (*buffer == '\r')+		buffer++;++	if (*buffer != '\n')+		goto corrupt;++	return 0;++corrupt:+	giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");+	return -1;+}++static git_ref_t loose_guess_rtype(const git_buf *full_path)+{+	git_buf ref_file = GIT_BUF_INIT;+	git_ref_t type;++	type = GIT_REF_INVALID;++	if (git_futils_readbuffer(&ref_file, full_path->ptr) == 0) {+		if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)+			type = GIT_REF_SYMBOLIC;+		else+			type = GIT_REF_OID;+	}++	git_buf_free(&ref_file);+	return type;+}++static int loose_lookup(git_reference *ref)+{+	int result, updated;+	git_buf ref_file = GIT_BUF_INIT;++	result = reference_read(&ref_file, &ref->mtime,+		ref->owner->path_repository, ref->name, &updated);++	if (result < 0)+		return result;++	if (!updated)+		return 0;++	if (ref->flags & GIT_REF_SYMBOLIC) {+		git__free(ref->target.symbolic);+		ref->target.symbolic = NULL;+	}++	ref->flags = 0;++	if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {+		ref->flags |= GIT_REF_SYMBOLIC;+		result = loose_parse_symbolic(ref, &ref_file);+	} else {+		ref->flags |= GIT_REF_OID;+		result = loose_parse_oid(&ref->target.oid, &ref_file);+	}++	git_buf_free(&ref_file);+	return result;+}++static int loose_lookup_to_packfile(+		struct packref **ref_out,+		git_repository *repo,+		const char *name)+{+	git_buf ref_file = GIT_BUF_INIT;+	struct packref *ref = NULL;+	size_t name_len;++	*ref_out = NULL;++	if (reference_read(&ref_file, NULL, repo->path_repository, name, NULL) < 0)+		return -1;++	name_len = strlen(name);+	ref = git__malloc(sizeof(struct packref) + name_len + 1);+	GITERR_CHECK_ALLOC(ref);++	memcpy(ref->name, name, name_len);+	ref->name[name_len] = 0;++	if (loose_parse_oid(&ref->oid, &ref_file) < 0) {+		git_buf_free(&ref_file);+		git__free(ref);+		return -1;+	}++	ref->flags = GIT_PACKREF_WAS_LOOSE;++	*ref_out = ref;+	git_buf_free(&ref_file);+	return 0;+}++static int loose_write(git_reference *ref)+{+	git_filebuf file = GIT_FILEBUF_INIT;+	git_buf ref_path = GIT_BUF_INIT;+	struct stat st;++	if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)+		return -1;++	/* Remove a possibly existing empty directory hierarchy +	 * which name would collide with the reference name+	 */+	if (git_path_isdir(git_buf_cstr(&ref_path)) && +		(git_futils_rmdir_r(git_buf_cstr(&ref_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) {+			git_buf_free(&ref_path);+			return -1;+		}++	if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {+		git_buf_free(&ref_path);+		return -1;+	}++	git_buf_free(&ref_path);++	if (ref->flags & GIT_REF_OID) {+		char oid[GIT_OID_HEXSZ + 1];++		git_oid_fmt(oid, &ref->target.oid);+		oid[GIT_OID_HEXSZ] = '\0';++		git_filebuf_printf(&file, "%s\n", oid);++	} else if (ref->flags & GIT_REF_SYMBOLIC) {+		git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);+	} else {+		assert(0); /* don't let this happen */+	}++	if (p_stat(ref_path.ptr, &st) == 0)+		ref->mtime = st.st_mtime;++	return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);+}++static int packed_parse_peel(+		struct packref *tag_ref,+		const char **buffer_out,+		const char *buffer_end)+{+	const char *buffer = *buffer_out + 1;++	assert(buffer[-1] == '^');++	/* Ensure it's not the first entry of the file */+	if (tag_ref == NULL)+		goto corrupt;++	/* Ensure reference is a tag */+	if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0)+		goto corrupt;++	if (buffer + GIT_OID_HEXSZ >= buffer_end)+		goto corrupt;++	/* Is this a valid object id? */+	if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)+		goto corrupt;++	buffer = buffer + GIT_OID_HEXSZ;+	if (*buffer == '\r')+		buffer++;++	if (*buffer != '\n')+		goto corrupt;++	*buffer_out = buffer + 1;+	return 0;++corrupt:+	giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");+	return -1;+}++static int packed_parse_oid(+		struct packref **ref_out,+		const char **buffer_out,+		const char *buffer_end)+{+	struct packref *ref = NULL;++	const char *buffer = *buffer_out;+	const char *refname_begin, *refname_end;++	size_t refname_len;+	git_oid id;++	refname_begin = (buffer + GIT_OID_HEXSZ + 1);+	if (refname_begin >= buffer_end || refname_begin[-1] != ' ')+		goto corrupt;++	/* Is this a valid object id? */+	if (git_oid_fromstr(&id, buffer) < 0)+		goto corrupt;++	refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);+	if (refname_end == NULL)+		goto corrupt;++	if (refname_end[-1] == '\r')+		refname_end--;++	refname_len = refname_end - refname_begin;++	ref = git__malloc(sizeof(struct packref) + refname_len + 1);+	GITERR_CHECK_ALLOC(ref);++	memcpy(ref->name, refname_begin, refname_len);+	ref->name[refname_len] = 0;++	git_oid_cpy(&ref->oid, &id);++	ref->flags = 0;++	*ref_out = ref;+	*buffer_out = refname_end + 1;++	return 0;++corrupt:+	git__free(ref);+	giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");+	return -1;+}++static int packed_load(git_repository *repo)+{+	int result, updated;+	git_buf packfile = GIT_BUF_INIT;+	const char *buffer_start, *buffer_end;+	git_refcache *ref_cache = &repo->references;++	/* First we make sure we have allocated the hash table */+	if (ref_cache->packfile == NULL) {+		ref_cache->packfile = git_strmap_alloc();+		GITERR_CHECK_ALLOC(ref_cache->packfile);+	}++	result = reference_read(&packfile, &ref_cache->packfile_time,+		repo->path_repository, GIT_PACKEDREFS_FILE, &updated);++	/*+	 * If we couldn't find the file, we need to clear the table and+	 * return. On any other error, we return that error. If everything+	 * went fine and the file wasn't updated, then there's nothing new+	 * for us here, so just return. Anything else means we need to+	 * refresh the packed refs.+	 */+	if (result == GIT_ENOTFOUND) {+		git_strmap_clear(ref_cache->packfile);+		return 0;+	}++	if (result < 0)+		return -1;++	if (!updated)+		return 0;++	/*+	 * At this point, we want to refresh the packed refs. We already+	 * have the contents in our buffer.+	 */+	git_strmap_clear(ref_cache->packfile);++	buffer_start = (const char *)packfile.ptr;+	buffer_end = (const char *)(buffer_start) + packfile.size;++	while (buffer_start < buffer_end && buffer_start[0] == '#') {+		buffer_start = strchr(buffer_start, '\n');+		if (buffer_start == NULL)+			goto parse_failed;++		buffer_start++;+	}++	while (buffer_start < buffer_end) {+		int err;+		struct packref *ref = NULL;++		if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)+			goto parse_failed;++		if (buffer_start[0] == '^') {+			if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)+				goto parse_failed;+		}++		git_strmap_insert(ref_cache->packfile, ref->name, ref, err);+		if (err < 0)+			goto parse_failed;+	}++	git_buf_free(&packfile);+	return 0;++parse_failed:+	git_strmap_free(ref_cache->packfile);+	ref_cache->packfile = NULL;+	git_buf_free(&packfile);+	return -1;+}+++struct dirent_list_data {+	git_repository *repo;+	size_t repo_path_len;+	unsigned int list_flags;++	int (*callback)(const char *, void *);+	void *callback_payload;+};++static int _dirent_loose_listall(void *_data, git_buf *full_path)+{+	struct dirent_list_data *data = (struct dirent_list_data *)_data;+	const char *file_path = full_path->ptr + data->repo_path_len;++	if (git_path_isdir(full_path->ptr) == true)+		return git_path_direach(full_path, _dirent_loose_listall, _data);++	/* do not add twice a reference that exists already in the packfile */+	if ((data->list_flags & GIT_REF_PACKED) != 0 &&+		git_strmap_exists(data->repo->references.packfile, file_path))+		return 0;++	if (data->list_flags != GIT_REF_LISTALL) {+		if ((data->list_flags & loose_guess_rtype(full_path)) == 0)+			return 0; /* we are filtering out this reference */+	}++	return data->callback(file_path, data->callback_payload);+}++static int _dirent_loose_load(void *data, git_buf *full_path)+{+	git_repository *repository = (git_repository *)data;+	void *old_ref = NULL;+	struct packref *ref;+	const char *file_path;+	int err;++	if (git_path_isdir(full_path->ptr) == true)+		return git_path_direach(full_path, _dirent_loose_load, repository);++	file_path = full_path->ptr + strlen(repository->path_repository);++	if (loose_lookup_to_packfile(&ref, repository, file_path) < 0)+		return -1;++	git_strmap_insert2(+		repository->references.packfile, ref->name, ref, old_ref, err);+	if (err < 0) {+		git__free(ref);+		return -1;+	}++	git__free(old_ref);+	return 0;+}++/*+ * Load all the loose references from the repository+ * into the in-memory Packfile, and build a vector with+ * all the references so it can be written back to+ * disk.+ */+static int packed_loadloose(git_repository *repository)+{+	git_buf refs_path = GIT_BUF_INIT;+	int result;++	/* the packfile must have been previously loaded! */+	assert(repository->references.packfile);++	if (git_buf_joinpath(&refs_path, repository->path_repository, GIT_REFS_DIR) < 0)+		return -1;++	/*+	 * Load all the loose files from disk into the Packfile table.+	 * This will overwrite any old packed entries with their+	 * updated loose versions+	 */+	result = git_path_direach(&refs_path, _dirent_loose_load, repository);+	git_buf_free(&refs_path);++	return result;+}++/*+ * Write a single reference into a packfile+ */+static int packed_write_ref(struct packref *ref, git_filebuf *file)+{+	char oid[GIT_OID_HEXSZ + 1];++	git_oid_fmt(oid, &ref->oid);+	oid[GIT_OID_HEXSZ] = 0;++	/*+	 * For references that peel to an object in the repo, we must+	 * write the resulting peel on a separate line, e.g.+	 *+	 *	6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4+	 *	^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100+	 *+	 * This obviously only applies to tags.+	 * The required peels have already been loaded into `ref->peel_target`.+	 */+	if (ref->flags & GIT_PACKREF_HAS_PEEL) {+		char peel[GIT_OID_HEXSZ + 1];+		git_oid_fmt(peel, &ref->peel);+		peel[GIT_OID_HEXSZ] = 0;++		if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)+			return -1;+	} else {+		if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)+			return -1;+	}++	return 0;+}++/*+ * Find out what object this reference resolves to.+ *+ * For references that point to a 'big' tag (e.g. an+ * actual tag object on the repository), we need to+ * cache on the packfile the OID of the object to+ * which that 'big tag' is pointing to.+ */+static int packed_find_peel(git_repository *repo, struct packref *ref)+{+	git_object *object;++	if (ref->flags & GIT_PACKREF_HAS_PEEL)+		return 0;++	/*+	 * Only applies to tags, i.e. references+	 * in the /refs/tags folder+	 */+	if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0)+		return 0;++	/*+	 * Find the tagged object in the repository+	 */+	if (git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY) < 0)+		return -1;++	/*+	 * If the tagged object is a Tag object, we need to resolve it;+	 * if the ref is actually a 'weak' ref, we don't need to resolve+	 * anything.+	 */+	if (git_object_type(object) == GIT_OBJ_TAG) {+		git_tag *tag = (git_tag *)object;++		/*+		 * Find the object pointed at by this tag+		 */+		git_oid_cpy(&ref->peel, git_tag_target_oid(tag));+		ref->flags |= GIT_PACKREF_HAS_PEEL;++		/*+		 * The reference has now cached the resolved OID, and is+		 * marked at such. When written to the packfile, it'll be+		 * accompanied by this resolved oid+		 */+	}++	git_object_free(object);+	return 0;+}++/*+ * Remove all loose references+ *+ * Once we have successfully written a packfile,+ * all the loose references that were packed must be+ * removed from disk.+ *+ * This is a dangerous method; make sure the packfile+ * is well-written, because we are destructing references+ * here otherwise.+ */+static int packed_remove_loose(git_repository *repo, git_vector *packing_list)+{+	unsigned int i;+	git_buf full_path = GIT_BUF_INIT;+	int failed = 0;++	for (i = 0; i < packing_list->length; ++i) {+		struct packref *ref = git_vector_get(packing_list, i);++		if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)+			continue;++		if (git_buf_joinpath(&full_path, repo->path_repository, ref->name) < 0)+			return -1; /* critical; do not try to recover on oom */++		if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) {+			if (failed)+				continue;++			giterr_set(GITERR_REFERENCE,+				"Failed to remove loose reference '%s' after packing: %s",+				full_path.ptr, strerror(errno));++			failed = 1;+		}++		/*+		 * if we fail to remove a single file, this is *not* good,+		 * but we should keep going and remove as many as possible.+		 * After we've removed as many files as possible, we return+		 * the error code anyway.+		 */+	}++	git_buf_free(&full_path);+	return failed ? -1 : 0;+}++static int packed_sort(const void *a, const void *b)+{+	const struct packref *ref_a = (const struct packref *)a;+	const struct packref *ref_b = (const struct packref *)b;++	return strcmp(ref_a->name, ref_b->name);+}++/*+ * Write all the contents in the in-memory packfile to disk.+ */+static int packed_write(git_repository *repo)+{+	git_filebuf pack_file = GIT_FILEBUF_INIT;+	unsigned int i;+	git_buf pack_file_path = GIT_BUF_INIT;+	git_vector packing_list;+	unsigned int total_refs;++	assert(repo && repo->references.packfile);++	total_refs =+		(unsigned int)git_strmap_num_entries(repo->references.packfile);++	if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)+		return -1;++	/* Load all the packfile into a vector */+	{+		struct packref *reference;++		/* cannot fail: vector already has the right size */+		git_strmap_foreach_value(repo->references.packfile, reference, {+			git_vector_insert(&packing_list, reference);+		});+	}++	/* sort the vector so the entries appear sorted on the packfile */+	git_vector_sort(&packing_list);++	/* Now we can open the file! */+	if (git_buf_joinpath(&pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE) < 0)+		goto cleanup_memory;++	if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)+		goto cleanup_packfile;++	/* Packfiles have a header... apparently+	 * This is in fact not required, but we might as well print it+	 * just for kicks */+	if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)+		goto cleanup_packfile;++	for (i = 0; i < packing_list.length; ++i) {+		struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);++		if (packed_find_peel(repo, ref) < 0)+			goto cleanup_packfile;++		if (packed_write_ref(ref, &pack_file) < 0)+			goto cleanup_packfile;+	}++	/* if we've written all the references properly, we can commit+	 * the packfile to make the changes effective */+	if (git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE) < 0)+		goto cleanup_memory;++	/* when and only when the packfile has been properly written,+	 * we can go ahead and remove the loose refs */+	 if (packed_remove_loose(repo, &packing_list) < 0)+		 goto cleanup_memory;++	 {+		struct stat st;+		if (p_stat(pack_file_path.ptr, &st) == 0)+			repo->references.packfile_time = st.st_mtime;+	 }++	git_vector_free(&packing_list);+	git_buf_free(&pack_file_path);++	/* we're good now */+	return 0;++cleanup_packfile:+	git_filebuf_cleanup(&pack_file);++cleanup_memory:+	git_vector_free(&packing_list);+	git_buf_free(&pack_file_path);++	return -1;+}++struct reference_available_t {+	const char *new_ref;+	const char *old_ref;+	int available;+};++static int _reference_available_cb(const char *ref, void *data)+{+	struct reference_available_t *d;++	assert(ref && data);+	d = (struct reference_available_t *)data;++	if (!d->old_ref || strcmp(d->old_ref, ref)) {+		size_t reflen = strlen(ref);+		size_t newlen = strlen(d->new_ref);+		size_t cmplen = reflen < newlen ? reflen : newlen;+		const char *lead = reflen < newlen ? d->new_ref : ref;++		if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') {+			d->available = 0;+			return -1;+		}+	}++	return 0;+}++static int reference_path_available(+	git_repository *repo,+	const char *ref,+	const char* old_ref)+{+	struct reference_available_t data;++	data.new_ref = ref;+	data.old_ref = old_ref;+	data.available = 1;++	if (git_reference_foreach(repo, GIT_REF_LISTALL,+		_reference_available_cb, (void *)&data) < 0)+		return -1;++	if (!data.available) {+		giterr_set(GITERR_REFERENCE,+			"The path to reference '%s' collides with an existing one", ref);+		return -1;+	}++	return 0;+}++static int reference_exists(int *exists, git_repository *repo, const char *ref_name)+{+	git_buf ref_path = GIT_BUF_INIT;++	if (packed_load(repo) < 0)+		return -1;++	if (git_buf_joinpath(&ref_path, repo->path_repository, ref_name) < 0)+		return -1;++	if (git_path_isfile(ref_path.ptr) == true ||+		git_strmap_exists(repo->references.packfile, ref_path.ptr))+	{+		*exists = 1;+	} else {+		*exists = 0;+	}++	git_buf_free(&ref_path);+	return 0;+}++/*+ * Check if a reference could be written to disk, based on:+ *+ *	- Whether a reference with the same name already exists,+ *	and we are allowing or disallowing overwrites+ *+ *	- Whether the name of the reference would collide with+ *	an existing path+ */+static int reference_can_write(+	git_repository *repo,+	const char *refname,+	const char *previous_name,+	int force)+{+	/* see if the reference shares a path with an existing reference;+	 * if a path is shared, we cannot create the reference, even when forcing */+	if (reference_path_available(repo, refname, previous_name) < 0)+		return -1;++	/* check if the reference actually exists, but only if we are not forcing+	 * the rename. If we are forcing, it's OK to overwrite */+	if (!force) {+		int exists;++		if (reference_exists(&exists, repo, refname) < 0)+			return -1;++		/* We cannot proceed if the reference already exists and we're not forcing+		 * the rename; the existing one would be overwritten */+		if (exists) {+			giterr_set(GITERR_REFERENCE,+				"A reference with that name (%s) already exists", refname);+			return GIT_EEXISTS;+		}+	}++	/* FIXME: if the reference exists and we are forcing, do we really need to+	 * remove the reference first?+	 *+	 * Two cases:+	 *+	 *	- the reference already exists and is loose: not a problem, the file+	 *	gets overwritten on disk+	 *+	 *	- the reference already exists and is packed: we write a new one as+	 *	loose, which by all means renders the packed one useless+	 */++	return 0;+}+++static int packed_lookup(git_reference *ref)+{+	struct packref *pack_ref = NULL;+	git_strmap *packfile_refs;+	khiter_t pos;++	if (packed_load(ref->owner) < 0)+		return -1;++	/* maybe the packfile hasn't changed at all, so we don't+	 * have to re-lookup the reference */+	if ((ref->flags & GIT_REF_PACKED) &&+		ref->mtime == ref->owner->references.packfile_time)+		return 0;++	if (ref->flags & GIT_REF_SYMBOLIC) {+		git__free(ref->target.symbolic);+		ref->target.symbolic = NULL;+	}++	/* Look up on the packfile */+	packfile_refs = ref->owner->references.packfile;+	pos = git_strmap_lookup_index(packfile_refs, ref->name);+	if (!git_strmap_valid_index(packfile_refs, pos)) {+		giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref->name);+		return GIT_ENOTFOUND;+	}++	pack_ref = git_strmap_value_at(packfile_refs, pos);++	ref->flags = GIT_REF_OID | GIT_REF_PACKED;+	ref->mtime = ref->owner->references.packfile_time;+	git_oid_cpy(&ref->target.oid, &pack_ref->oid);++	return 0;+}++static int reference_lookup(git_reference *ref)+{+	int result;++	result = loose_lookup(ref);+	if (result == 0)+		return 0;++	/* only try to lookup this reference on the packfile if it+	 * wasn't found on the loose refs; not if there was a critical error */+	if (result == GIT_ENOTFOUND) {+		giterr_clear();+		result = packed_lookup(ref);+		if (result == 0)+			return 0;+	}++	/* unexpected error; free the reference */+	git_reference_free(ref);+	return result;+}++/*+ * Delete a reference.+ * This is an internal method; the reference is removed+ * from disk or the packfile, but the pointer is not freed+ */+static int reference_delete(git_reference *ref)+{+	int result;++	assert(ref);++	/* If the reference is packed, this is an expensive operation.+	 * We need to reload the packfile, remove the reference from the+	 * packing list, and repack */+	if (ref->flags & GIT_REF_PACKED) {+		git_strmap *packfile_refs;+		struct packref *packref;+		khiter_t pos;++		/* load the existing packfile */+		if (packed_load(ref->owner) < 0)+			return -1;++		packfile_refs = ref->owner->references.packfile;+		pos = git_strmap_lookup_index(packfile_refs, ref->name);+		if (!git_strmap_valid_index(packfile_refs, pos)) {+			giterr_set(GITERR_REFERENCE,+				"Reference %s stopped existing in the packfile", ref->name);+			return -1;+		}++		packref = git_strmap_value_at(packfile_refs, pos);+		git_strmap_delete_at(packfile_refs, pos);++		git__free(packref);+		if (packed_write(ref->owner) < 0)+			return -1;++	/* If the reference is loose, we can just remove the reference+	 * from the filesystem */+	} else {+		git_reference *ref_in_pack;+		git_buf full_path = GIT_BUF_INIT;++		if (git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name) < 0)+			return -1;++		result = p_unlink(full_path.ptr);+		git_buf_free(&full_path); /* done with path at this point */++		if (result < 0) {+			giterr_set(GITERR_OS, "Failed to unlink '%s'", full_path.ptr);+			return -1;+		}++		/* When deleting a loose reference, we have to ensure that an older+		 * packed version of it doesn't exist */+		if (git_reference_lookup(&ref_in_pack, ref->owner, ref->name) == 0) {+			assert((ref_in_pack->flags & GIT_REF_PACKED) != 0);+			return git_reference_delete(ref_in_pack);+		}++		giterr_clear();+	}++	return 0;+}++int git_reference_delete(git_reference *ref)+{+	int result = reference_delete(ref);+	git_reference_free(ref);+	return result;+}++int git_reference_lookup(git_reference **ref_out,+	git_repository *repo, const char *name)+{+	return git_reference_lookup_resolved(ref_out, repo, name, 0);+}++int git_reference_name_to_oid(+	git_oid *out, git_repository *repo, const char *name)+{+	int error;+	git_reference *ref;++	if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)+		return error;++	git_oid_cpy(out, git_reference_oid(ref));+	git_reference_free(ref);+	return 0;+}++int git_reference_lookup_resolved(+	git_reference **ref_out,+	git_repository *repo,+	const char *name,+	int max_nesting)+{+	git_reference *scan;+	int result, nesting;++	assert(ref_out && repo && name);++	*ref_out = NULL;++	if (max_nesting > MAX_NESTING_LEVEL)+		max_nesting = MAX_NESTING_LEVEL;+	else if (max_nesting < 0)+		max_nesting = DEFAULT_NESTING_LEVEL;++	scan = git__calloc(1, sizeof(git_reference));+	GITERR_CHECK_ALLOC(scan);++	scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char));+	GITERR_CHECK_ALLOC(scan->name);++	if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) {+		git_reference_free(scan);+		return result;+	}++	scan->target.symbolic = git__strdup(scan->name);+	GITERR_CHECK_ALLOC(scan->target.symbolic);++	scan->owner = repo;+	scan->flags = GIT_REF_SYMBOLIC;++	for (nesting = max_nesting;+		 nesting >= 0 && (scan->flags & GIT_REF_SYMBOLIC) != 0;+		 nesting--)+	{+		if (nesting != max_nesting)+			strncpy(scan->name, scan->target.symbolic, GIT_REFNAME_MAX);++		scan->mtime = 0;++		if ((result = reference_lookup(scan)) < 0)+			return result; /* lookup git_reference_free on scan already */+	}++	if ((scan->flags & GIT_REF_OID) == 0 && max_nesting != 0) {+		giterr_set(GITERR_REFERENCE,+			"Cannot resolve reference (>%u levels deep)", max_nesting);+		git_reference_free(scan);+		return -1;+	}++	*ref_out = scan;+	return 0;+}++/**+ * Getters+ */+git_ref_t git_reference_type(git_reference *ref)+{+	assert(ref);++	if (ref->flags & GIT_REF_OID)+		return GIT_REF_OID;++	if (ref->flags & GIT_REF_SYMBOLIC)+		return GIT_REF_SYMBOLIC;++	return GIT_REF_INVALID;+}++int git_reference_is_packed(git_reference *ref)+{+	assert(ref);+	return !!(ref->flags & GIT_REF_PACKED);+}++const char *git_reference_name(git_reference *ref)+{+	assert(ref);+	return ref->name;+}++git_repository *git_reference_owner(git_reference *ref)+{+	assert(ref);+	return ref->owner;+}++const git_oid *git_reference_oid(git_reference *ref)+{+	assert(ref);++	if ((ref->flags & GIT_REF_OID) == 0)+		return NULL;++	return &ref->target.oid;+}++const char *git_reference_target(git_reference *ref)+{+	assert(ref);++	if ((ref->flags & GIT_REF_SYMBOLIC) == 0)+		return NULL;++	return ref->target.symbolic;+}++int git_reference_create_symbolic(+	git_reference **ref_out,+	git_repository *repo,+	const char *name,+	const char *target,+	int force)+{+	char normalized[GIT_REFNAME_MAX];+	git_reference *ref = NULL;++	if (normalize_name(normalized, sizeof(normalized), name, 0) < 0)+		return -1;++	if (reference_can_write(repo, normalized, NULL, force) < 0)+		return -1;++	if (reference_alloc(&ref, repo, normalized) < 0)+		return -1;++	ref->flags |= GIT_REF_SYMBOLIC;++	/* set the target; this will normalize the name automatically+	 * and write the reference on disk */+	if (git_reference_set_target(ref, target) < 0) {+		git_reference_free(ref);+		return -1;+	}+	if (ref_out == NULL) {+		git_reference_free(ref);+	} else {+		*ref_out = ref;+	}++	return 0;+}++int git_reference_create_oid(+	git_reference **ref_out,+	git_repository *repo,+	const char *name,+	const git_oid *id,+	int force)+{+	git_reference *ref = NULL;+	char normalized[GIT_REFNAME_MAX];++	if (normalize_name(normalized, sizeof(normalized), name, 1) < 0)+		return -1;++	if (reference_can_write(repo, normalized, NULL, force) < 0)+		return -1;++	if (reference_alloc(&ref, repo, name) < 0)+		return -1;++	ref->flags |= GIT_REF_OID;++	/* set the oid; this will write the reference on disk */+	if (git_reference_set_oid(ref, id) < 0) {+		git_reference_free(ref);+		return -1;+	}++	if (ref_out == NULL) {+		git_reference_free(ref);+	} else {+		*ref_out = ref;+	}++	return 0;+}+/*+ * Change the OID target of a reference.+ *+ * For both loose and packed references, just change+ * the oid in memory and (over)write the file in disk.+ *+ * We do not repack packed references because of performance+ * reasons.+ */+int git_reference_set_oid(git_reference *ref, const git_oid *id)+{+	git_odb *odb = NULL;++	if ((ref->flags & GIT_REF_OID) == 0) {+		giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");+		return -1;+	}++	assert(ref->owner);++	if (git_repository_odb__weakptr(&odb, ref->owner) < 0)+		return -1;++	/* Don't let the user create references to OIDs that+	 * don't exist in the ODB */+	if (!git_odb_exists(odb, id)) {+		giterr_set(GITERR_REFERENCE,+			"Target OID for the reference doesn't exist on the repository");+		return -1;+	}++	/* Update the OID value on `ref` */+	git_oid_cpy(&ref->target.oid, id);++	/* Write back to disk */+	return loose_write(ref);+}++/*+ * Change the target of a symbolic reference.+ *+ * This is easy because symrefs cannot be inside+ * a pack. We just change the target in memory+ * and overwrite the file on disk.+ */+int git_reference_set_target(git_reference *ref, const char *target)+{+	char normalized[GIT_REFNAME_MAX];++	if ((ref->flags & GIT_REF_SYMBOLIC) == 0) {+		giterr_set(GITERR_REFERENCE,+			"Cannot set symbolic target on a direct reference");+		return -1;+	}++	if (normalize_name(normalized, sizeof(normalized), target, 0))+		return -1;++	git__free(ref->target.symbolic);+	ref->target.symbolic = git__strdup(normalized);+	GITERR_CHECK_ALLOC(ref->target.symbolic);++	return loose_write(ref);+}++int git_reference_rename(git_reference *ref, const char *new_name, int force)+{+	int result;+	git_buf aux_path = GIT_BUF_INIT;+	char normalized[GIT_REFNAME_MAX];++	const char *head_target = NULL;+	git_reference *head = NULL;++	if (normalize_name(normalized, sizeof(normalized),+		new_name, ref->flags & GIT_REF_OID) < 0)+		return -1;++	if (reference_can_write(ref->owner, normalized, ref->name, force) < 0)+		return -1;++	/* Initialize path now so we won't get an allocation failure once+	 * we actually start removing things. */+	if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0)+		return -1;++	/*+	 * Now delete the old ref and remove an possibly existing directory+	 * named `new_name`. Note that using the internal `reference_delete`+	 * method deletes the ref from disk but doesn't free the pointer, so+	 * we can still access the ref's attributes for creating the new one+	 */+	if (reference_delete(ref) < 0)+		goto cleanup;++	/*+	 * Finally we can create the new reference.+	 */+	if (ref->flags & GIT_REF_SYMBOLIC) {+		result = git_reference_create_symbolic(+			NULL, ref->owner, new_name, ref->target.symbolic, force);+	} else {+		result = git_reference_create_oid(+			NULL, ref->owner, new_name, &ref->target.oid, force);+	}++	if (result < 0)+		goto rollback;++	/*+	 * Check if we have to update HEAD.+	 */+	if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE) < 0) {+		giterr_set(GITERR_REFERENCE,+			"Failed to update HEAD after renaming reference");+		goto cleanup;+	}++	head_target = git_reference_target(head);++	if (head_target && !strcmp(head_target, ref->name)) {+		if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1) < 0) {+			giterr_set(GITERR_REFERENCE,+				"Failed to update HEAD after renaming reference");+			goto cleanup;+		}+	}++	/*+	 * Rename the reflog file.+	 */+	if (git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name) < 0)+		goto cleanup;++	if (git_path_exists(aux_path.ptr) == true) {+		if (git_reflog_rename(ref, new_name) < 0)+			goto cleanup;+	} else {+		giterr_clear();+	}++	/*+	 * Change the name of the reference given by the user.+	 */+	git__free(ref->name);+	ref->name = git__strdup(new_name);++	/* The reference is no longer packed */+	ref->flags &= ~GIT_REF_PACKED;++	git_reference_free(head);+	git_buf_free(&aux_path);+	return 0;++cleanup:+	git_reference_free(head);+	git_buf_free(&aux_path);+	return -1;++rollback:+	/*+	 * Try to create the old reference again, ignore failures+	 */+	if (ref->flags & GIT_REF_SYMBOLIC)+		git_reference_create_symbolic(+			NULL, ref->owner, ref->name, ref->target.symbolic, 0);+	else+		git_reference_create_oid(+			NULL, ref->owner, ref->name, &ref->target.oid, 0);++	/* The reference is no longer packed */+	ref->flags &= ~GIT_REF_PACKED;++	git_buf_free(&aux_path);+	return -1;+}++int git_reference_resolve(git_reference **ref_out, git_reference *ref)+{+	if (ref->flags & GIT_REF_OID)+		return git_reference_lookup(ref_out, ref->owner, ref->name);+	else+		return git_reference_lookup_resolved(ref_out, ref->owner, ref->target.symbolic, -1);+}++int git_reference_packall(git_repository *repo)+{+	if (packed_load(repo) < 0 || /* load the existing packfile */+		packed_loadloose(repo) < 0 || /* add all the loose refs */+		packed_write(repo) < 0) /* write back to disk */+		return -1;++	return 0;+}++int git_reference_foreach(+	git_repository *repo,+	unsigned int list_flags,+	int (*callback)(const char *, void *),+	void *payload)+{+	int result;+	struct dirent_list_data data;+	git_buf refs_path = GIT_BUF_INIT;++	/* list all the packed references first */+	if (list_flags & GIT_REF_PACKED) {+		const char *ref_name;+		void *ref;+		GIT_UNUSED(ref);++		if (packed_load(repo) < 0)+			return -1;++		git_strmap_foreach(repo->references.packfile, ref_name, ref, {+			if (callback(ref_name, payload) < 0)+				return 0;+		});+	}++	/* now list the loose references, trying not to+	 * duplicate the ref names already in the packed-refs file */++	data.repo_path_len = strlen(repo->path_repository);+	data.list_flags = list_flags;+	data.repo = repo;+	data.callback = callback;+	data.callback_payload = payload;++	if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0)+		return -1;++	result = git_path_direach(&refs_path, _dirent_loose_listall, &data);+	git_buf_free(&refs_path);++	return result;+}++static int cb__reflist_add(const char *ref, void *data)+{+	return git_vector_insert((git_vector *)data, git__strdup(ref));+}++int git_reference_list(+	git_strarray *array,+	git_repository *repo,+	unsigned int list_flags)+{+	git_vector ref_list;++	assert(array && repo);++	array->strings = NULL;+	array->count = 0;++	if (git_vector_init(&ref_list, 8, NULL) < 0)+		return -1;++	if (git_reference_foreach(+			repo, list_flags, &cb__reflist_add, (void *)&ref_list) < 0) {+		git_vector_free(&ref_list);+		return -1;+	}++	array->strings = (char **)ref_list.contents;+	array->count = ref_list.length;+	return 0;+}++int git_reference_reload(git_reference *ref)+{+	return reference_lookup(ref);+}++void git_repository__refcache_free(git_refcache *refs)+{+	assert(refs);++	if (refs->packfile) {+		struct packref *reference;++		git_strmap_foreach_value(refs->packfile, reference, {+			git__free(reference);+		});++		git_strmap_free(refs->packfile);+	}+}++static int is_valid_ref_char(char ch)+{+	if ((unsigned) ch <= ' ')+		return 0;++	switch (ch) {+	case '~':+	case '^':+	case ':':+	case '\\':+	case '?':+	case '[':+	case '*':+		return 0;+	default:+		return 1;+	}+}++static int normalize_name(+	char *buffer_out,+	size_t out_size,+	const char *name,+	int is_oid_ref)+{+	const char *name_end, *buffer_out_start;+	const char *current;+	int contains_a_slash = 0;++	assert(name && buffer_out);++	buffer_out_start = buffer_out;+	current = name;+	name_end = name + strlen(name);++	/* Terminating null byte */+	out_size--;++	/* A refname can not be empty */+	if (name_end == name)+		goto invalid_name;++	/* A refname can not end with a dot or a slash */+	if (*(name_end - 1) == '.' || *(name_end - 1) == '/')+		goto invalid_name;++	while (current < name_end && out_size) {+		if (!is_valid_ref_char(*current))+			goto invalid_name;++		if (buffer_out > buffer_out_start) {+			char prev = *(buffer_out - 1);++			/* A refname can not start with a dot nor contain a double dot */+			if (*current == '.' && ((prev == '.') || (prev == '/')))+				goto invalid_name;++			/* '@{' is forbidden within a refname */+			if (*current == '{' && prev == '@')+				goto invalid_name;++			/* Prevent multiple slashes from being added to the output */+			if (*current == '/' && prev == '/') {+				current++;+				continue;+			}+		}++		if (*current == '/')+			contains_a_slash = 1;++		*buffer_out++ = *current++;+		out_size--;+	}++	if (!out_size)+		goto invalid_name;++	/* Object id refname have to contain at least one slash, except+	 * for HEAD in a detached state or MERGE_HEAD if we're in the+	 * middle of a merge */+	if (is_oid_ref &&+		!contains_a_slash &&+		strcmp(name, GIT_HEAD_FILE) != 0 &&+		strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&+		strcmp(name, GIT_FETCH_HEAD_FILE) != 0)+		goto invalid_name;++	/* A refname can not end with ".lock" */+	if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))+		goto invalid_name;++	*buffer_out = '\0';++	/*+	 * For object id references, name has to start with refs/. Again,+	 * we need to allow HEAD to be in a detached state.+	 */+	if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||+		strcmp(buffer_out_start, GIT_HEAD_FILE)))+		goto invalid_name;++	return 0;++invalid_name:+	giterr_set(GITERR_REFERENCE, "The given reference name is not valid");+	return -1;+}++int git_reference__normalize_name(+	char *buffer_out,+	size_t out_size,+	const char *name)+{+	return normalize_name(buffer_out, out_size, name, 0);+}++int git_reference__normalize_name_oid(+	char *buffer_out,+	size_t out_size,+	const char *name)+{+	return normalize_name(buffer_out, out_size, name, 1);+}++#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)++int git_reference_cmp(git_reference *ref1, git_reference *ref2)+{+	assert(ref1 && ref2);++	/* let's put symbolic refs before OIDs */+	if ((ref1->flags & GIT_REF_TYPEMASK) != (ref2->flags & GIT_REF_TYPEMASK))+		return (ref1->flags & GIT_REF_SYMBOLIC) ? -1 : 1;++	if (ref1->flags & GIT_REF_SYMBOLIC)+		return strcmp(ref1->target.symbolic, ref2->target.symbolic);++	return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);+}+
+ libgit2/src/refs.h view
@@ -0,0 +1,81 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_refs_h__+#define INCLUDE_refs_h__++#include "common.h"+#include "git2/oid.h"+#include "git2/refs.h"+#include "strmap.h"++#define GIT_REFS_DIR "refs/"+#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"+#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"+#define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/"+#define GIT_REFS_DIR_MODE 0777+#define GIT_REFS_FILE_MODE 0666++#define GIT_RENAMED_REF_FILE GIT_REFS_DIR "RENAMED-REF"++#define GIT_SYMREF "ref: "+#define GIT_PACKEDREFS_FILE "packed-refs"+#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled "+#define GIT_PACKEDREFS_FILE_MODE 0666++#define GIT_HEAD_FILE "HEAD"+#define GIT_FETCH_HEAD_FILE "FETCH_HEAD"+#define GIT_MERGE_HEAD_FILE "MERGE_HEAD"+#define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"++#define GIT_REFNAME_MAX 1024++struct git_reference {+	unsigned int flags;+	git_repository *owner;+	char *name;+	time_t mtime;++	union {+		git_oid oid;+		char *symbolic;+	} target;+};++typedef struct {+	git_strmap *packfile;+	time_t packfile_time;+} git_refcache;++void git_repository__refcache_free(git_refcache *refs);++int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);+int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);++/**+ * Lookup a reference by name and try to resolve to an OID.+ *+ * You can control how many dereferences this will attempt to resolve the+ * reference with the `max_deref` parameter, or pass -1 to use a sane+ * default.  If you pass 0 for `max_deref`, this will not attempt to resolve+ * the reference.  For any value of `max_deref` other than 0, not+ * successfully resolving the reference will be reported as an error.++ * The generated reference must be freed by the user.+ *+ * @param reference_out Pointer to the looked-up reference+ * @param repo The repository to look up the reference+ * @param name The long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)+ * @param max_deref Maximum number of dereferences to make of symbolic refs, 0 means simple lookup, < 0 means use default reasonable value+ * @return 0 on success or < 0 on error; not being able to resolve the reference is an error unless 0 was passed for max_deref+ */+int git_reference_lookup_resolved(+	git_reference **reference_out,+	git_repository *repo,+	const char *name,+	int max_deref);++#endif
+ libgit2/src/refspec.c view
@@ -0,0 +1,122 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "git2/errors.h"++#include "common.h"+#include "refspec.h"+#include "util.h"+#include "posix.h"++int git_refspec_parse(git_refspec *refspec, const char *str)+{+	char *delim;++	memset(refspec, 0x0, sizeof(git_refspec));++	if (*str == '+') {+		refspec->force = 1;+		str++;+	}++	delim = strchr(str, ':');+	if (delim == NULL) {+		refspec->src = git__strdup(str);+		GITERR_CHECK_ALLOC(refspec->src);+		return 0;+	}++	refspec->src = git__strndup(str, delim - str);+	GITERR_CHECK_ALLOC(refspec->src);++	refspec->dst = git__strdup(delim + 1);+	if (refspec->dst == NULL) {+		git__free(refspec->src);+		refspec->src = NULL;+		return -1;+	}++	return 0;+}++const char *git_refspec_src(const git_refspec *refspec)+{+	return refspec == NULL ? NULL : refspec->src;+}++const char *git_refspec_dst(const git_refspec *refspec)+{+	return refspec == NULL ? NULL : refspec->dst;+}++int git_refspec_src_matches(const git_refspec *refspec, const char *refname)+{+	if (refspec == NULL || refspec->src == NULL)+		return false;++	return (p_fnmatch(refspec->src, refname, 0) == 0);+}++int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)+{+	size_t baselen, namelen;++	baselen = strlen(spec->dst);+	if (outlen <= baselen) {+		giterr_set(GITERR_INVALID, "Reference name too long");+		return GIT_EBUFS;+	}++	/*+	 * No '*' at the end means that it's mapped to one specific local+	 * branch, so no actual transformation is needed.+	 */+	if (spec->dst[baselen - 1] != '*') {+		memcpy(out, spec->dst, baselen + 1); /* include '\0' */+		return 0;+	}++	/* There's a '*' at the end, so remove its length */+	baselen--;++	/* skip the prefix, -1 is for the '*' */+	name += strlen(spec->src) - 1;++	namelen = strlen(name);++	if (outlen <= baselen + namelen) {+		giterr_set(GITERR_INVALID, "Reference name too long");+		return GIT_EBUFS;+	}++	memcpy(out, spec->dst, baselen);+	memcpy(out + baselen, name, namelen + 1);++	return 0;+}++int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)+{+	if (git_buf_sets(out, spec->dst) < 0)+		return -1;++	/*+	 * No '*' at the end means that it's mapped to one specific local+	 * branch, so no actual transformation is needed.+	 */+	if (git_buf_len(out) > 0 && out->ptr[git_buf_len(out) - 1] != '*')+		return 0;++	git_buf_truncate(out, git_buf_len(out) - 1); /* remove trailing '*' */+	git_buf_puts(out, name + strlen(spec->src) - 1);++	if (git_buf_oom(out))+		return -1;++	return 0;+}+
+ libgit2/src/refspec.h view
@@ -0,0 +1,35 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_refspec_h__+#define INCLUDE_refspec_h__++#include "git2/refspec.h"+#include "buffer.h"++struct git_refspec {+	struct git_refspec *next;+	char *src;+	char *dst;+	unsigned int force :1,+		pattern :1,+		matching :1;+};++int git_refspec_parse(struct git_refspec *refspec, const char *str);++/**+ * Transform a reference to its target following the refspec's rules,+ * and writes the results into a git_buf.+ *+ * @param out where to store the target name+ * @param spec the refspec+ * @param name the name of the reference to transform+ * @return 0 or error if buffer allocation fails+ */+int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name);++#endif
+ libgit2/src/remote.c view
@@ -0,0 +1,510 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "git2/remote.h"+#include "git2/config.h"+#include "git2/types.h"++#include "config.h"+#include "repository.h"+#include "remote.h"+#include "fetch.h"+#include "refs.h"++#include <regex.h>++static int refspec_parse(git_refspec *refspec, const char *str)+{+	char *delim;++	memset(refspec, 0x0, sizeof(git_refspec));++	if (*str == '+') {+		refspec->force = 1;+		str++;+	}++	delim = strchr(str, ':');+	if (delim == NULL) {+		giterr_set(GITERR_NET, "Invalid refspec, missing ':'");+		return -1;+	}++	refspec->src = git__strndup(str, delim - str);+	GITERR_CHECK_ALLOC(refspec->src);++	refspec->dst = git__strdup(delim + 1);+	GITERR_CHECK_ALLOC(refspec->dst);++	return 0;+}++static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var)+{+	int error;+	const char *val;++	if ((error = git_config_get_string(&val, cfg, var)) < 0)+		return error;++	return refspec_parse(refspec, val);+}++int git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)+{+	git_remote *remote;++	/* name is optional */+	assert(out && repo && url);++	remote = git__malloc(sizeof(git_remote));+	GITERR_CHECK_ALLOC(remote);++	memset(remote, 0x0, sizeof(git_remote));+	remote->repo = repo;++	if (git_vector_init(&remote->refs, 32, NULL) < 0)+		return -1;++	remote->url = git__strdup(url);+	GITERR_CHECK_ALLOC(remote->url);++	if (name != NULL) {+		remote->name = git__strdup(name);+		GITERR_CHECK_ALLOC(remote->name);+	}++	if (fetch != NULL) {+		if (refspec_parse(&remote->fetch, fetch) < 0)+			goto on_error;+	}++	*out = remote;+	return 0;++on_error:+	git_remote_free(remote);+	return -1;+}++int git_remote_load(git_remote **out, git_repository *repo, const char *name)+{+	git_remote *remote;+	git_buf buf = GIT_BUF_INIT;+	const char *val;+	int error = 0;+	git_config *config;++	assert(out && repo && name);++	if (git_repository_config__weakptr(&config, repo) < 0)+		return -1;++	remote = git__malloc(sizeof(git_remote));+	GITERR_CHECK_ALLOC(remote);++	memset(remote, 0x0, sizeof(git_remote));+	remote->name = git__strdup(name);+	GITERR_CHECK_ALLOC(remote->name);++	if (git_vector_init(&remote->refs, 32, NULL) < 0) {+		error = -1;+		goto cleanup;+	}++	if (git_buf_printf(&buf, "remote.%s.url", name) < 0) {+		error = -1;+		goto cleanup;+	}++	if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0)+		goto cleanup;++	remote->repo = repo;+	remote->url = git__strdup(val);+	GITERR_CHECK_ALLOC(remote->url);++	git_buf_clear(&buf);+	if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) {+		error = -1;+		goto cleanup;+	}++	error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf));+	if (error == GIT_ENOTFOUND)+		error = 0;++	if (error < 0) {+		error = -1;+		goto cleanup;+	}++	git_buf_clear(&buf);+	if (git_buf_printf(&buf, "remote.%s.push", name) < 0) {+		error = -1;+		goto cleanup;+	}++	error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf));+	if (error == GIT_ENOTFOUND)+		error = 0;++	if (error < 0) {+		error = -1;+		goto cleanup;+	}++	*out = remote;++cleanup:+	git_buf_free(&buf);++	if (error < 0)+		git_remote_free(remote);++	return error;+}++int git_remote_save(const git_remote *remote)+{+	git_config *config;+	git_buf buf = GIT_BUF_INIT, value = GIT_BUF_INIT;++	if (git_repository_config__weakptr(&config, remote->repo) < 0)+		return -1;++	if (git_buf_printf(&buf, "remote.%s.%s", remote->name, "url") < 0)+		return -1;++	if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {+		git_buf_free(&buf);+		return -1;+	}++	if (remote->fetch.src != NULL && remote->fetch.dst != NULL) {+		git_buf_clear(&buf);+		git_buf_clear(&value);+		git_buf_printf(&buf, "remote.%s.fetch", remote->name);+		git_buf_printf(&value, "%s:%s", remote->fetch.src, remote->fetch.dst);+		if (git_buf_oom(&buf) || git_buf_oom(&value))+			return -1;++		if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)+			goto on_error;+	}++	if (remote->push.src != NULL && remote->push.dst != NULL) {+		git_buf_clear(&buf);+		git_buf_clear(&value);+		git_buf_printf(&buf, "remote.%s.push", remote->name);+		git_buf_printf(&value, "%s:%s", remote->push.src, remote->push.dst);+		if (git_buf_oom(&buf) || git_buf_oom(&value))+			return -1;++		if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)+			goto on_error;+	}++	git_buf_free(&buf);+	git_buf_free(&value);++	return 0;++on_error:+	git_buf_free(&buf);+	git_buf_free(&value);+	return -1;+}++const char *git_remote_name(git_remote *remote)+{+	assert(remote);+	return remote->name;+}++const char *git_remote_url(git_remote *remote)+{+	assert(remote);+	return remote->url;+}++int git_remote_set_fetchspec(git_remote *remote, const char *spec)+{+	git_refspec refspec;++	assert(remote && spec);++	if (refspec_parse(&refspec, spec) < 0)+		return -1;++	git__free(remote->fetch.src);+	git__free(remote->fetch.dst);+	remote->fetch.src = refspec.src;+	remote->fetch.dst = refspec.dst;++	return 0;+}++const git_refspec *git_remote_fetchspec(git_remote *remote)+{+	assert(remote);+	return &remote->fetch;+}++int git_remote_set_pushspec(git_remote *remote, const char *spec)+{+	git_refspec refspec;++	assert(remote && spec);++	if (refspec_parse(&refspec, spec) < 0)+		return -1;++	git__free(remote->push.src);+	git__free(remote->push.dst);+	remote->push.src = refspec.src;+	remote->push.dst = refspec.dst;++	return 0;+}++const git_refspec *git_remote_pushspec(git_remote *remote)+{+	assert(remote);+	return &remote->push;+}++int git_remote_connect(git_remote *remote, int direction)+{+	git_transport *t;++	assert(remote);++	if (git_transport_new(&t, remote->url) < 0)+		return -1;++	if (t->connect(t, direction) < 0) {+		goto on_error;+	}++	remote->transport = t;++	return 0;++on_error:+	t->free(t);+	return -1;+}++int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)+{+	assert(remote);++	if (!remote->transport || !remote->transport->connected) {+		giterr_set(GITERR_NET, "The remote is not connected");+		return -1;+	}++	return remote->transport->ls(remote->transport, list_cb, payload);+}++int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)+{+	int error;++	assert(remote && bytes && stats);++	if ((error = git_fetch_negotiate(remote)) < 0)+		return error;++	return git_fetch_download_pack(remote, bytes, stats);+}++int git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b))+{+	int error = 0;+	unsigned int i = 0;+	git_buf refname = GIT_BUF_INIT;+	git_oid old;+	git_vector *refs = &remote->refs;+	git_remote_head *head;+	git_reference *ref;+	struct git_refspec *spec = &remote->fetch;++	assert(remote);++	if (refs->length == 0)+		return 0;++	/* HEAD is only allowed to be the first in the list */+	head = refs->contents[0];+	if (!strcmp(head->name, GIT_HEAD_FILE)) {+		if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)+			return -1;++		i = 1;+		git_reference_free(ref);+	}++	for (; i < refs->length; ++i) {+		head = refs->contents[i];++		if (git_refspec_transform_r(&refname, spec, head->name) < 0)+			goto on_error;++		error = git_reference_name_to_oid(&old, remote->repo, refname.ptr);+		if (error < 0 && error != GIT_ENOTFOUND)+			goto on_error;++		if (error == GIT_ENOTFOUND)+			memset(&old, 0, GIT_OID_RAWSZ);++		if (!git_oid_cmp(&old, &head->oid))+			continue;++		if (git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1) < 0)+			break;++		git_reference_free(ref);++		if (cb != NULL) {+			if (cb(refname.ptr, &old, &head->oid) < 0)+				goto on_error;+		}+	}++	git_buf_free(&refname);+	return 0;++on_error:+	git_buf_free(&refname);+	return -1;++}++int git_remote_connected(git_remote *remote)+{+	assert(remote);+	return remote->transport == NULL ? 0 : remote->transport->connected;+}++void git_remote_disconnect(git_remote *remote)+{+	assert(remote);++	if (remote->transport != NULL && remote->transport->connected)+			remote->transport->close(remote->transport);+}++void git_remote_free(git_remote *remote)+{+	if (remote == NULL)+		return;++	if (remote->transport != NULL) {+		git_remote_disconnect(remote);++		remote->transport->free(remote->transport);+		remote->transport = NULL;+	}++	git_vector_free(&remote->refs);++	git__free(remote->fetch.src);+	git__free(remote->fetch.dst);+	git__free(remote->push.src);+	git__free(remote->push.dst);+	git__free(remote->url);+	git__free(remote->name);+	git__free(remote);+}++struct cb_data {+	git_vector *list;+	regex_t *preg;+};++static int remote_list_cb(const char *name, const char *value, void *data_)+{+	struct cb_data *data = (struct cb_data *)data_;+	size_t nmatch = 2;+	regmatch_t pmatch[2];+	GIT_UNUSED(value);++	if (!regexec(data->preg, name, nmatch, pmatch, 0)) {+		char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);+		GITERR_CHECK_ALLOC(remote_name);++		if (git_vector_insert(data->list, remote_name) < 0)+			return -1;+	}++	return 0;+}++int git_remote_list(git_strarray *remotes_list, git_repository *repo)+{+	git_config *cfg;+	git_vector list;+	regex_t preg;+	struct cb_data data;+	int error;++	if (git_repository_config__weakptr(&cfg, repo) < 0)+		return -1;++	if (git_vector_init(&list, 4, NULL) < 0)+		return -1;++	if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) {+		giterr_set(GITERR_OS, "Remote catch regex failed to compile");+		return -1;+	}++	data.list = &list;+	data.preg = &preg;+	error = git_config_foreach(cfg, remote_list_cb, &data);+	regfree(&preg);+	if (error < 0) {+		size_t i;+		char *elem;+		git_vector_foreach(&list, i, elem) {+			git__free(elem);+		}++		git_vector_free(&list);+		return error;+	}++	remotes_list->strings = (char **)list.contents;+	remotes_list->count = list.length;++	return 0;+}++int git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url)+{+	git_buf buf = GIT_BUF_INIT;++	if (git_buf_printf(&buf, "refs/heads/*:refs/remotes/%s/*", name) < 0)+		return -1;++	if (git_remote_new(out, repo, name, url, git_buf_cstr(&buf)) < 0)+		goto on_error;++	git_buf_free(&buf);++	if (git_remote_save(*out) < 0)+		goto on_error;++	return 0;++on_error:+	git_buf_free(&buf);+	git_remote_free(*out);+	return -1;+}
+ libgit2/src/remote.h view
@@ -0,0 +1,25 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_remote_h__+#define INCLUDE_remote_h__++#include "refspec.h"+#include "transport.h"+#include "repository.h"++struct git_remote {+	char *name;+	char *url;+	git_vector refs;+	struct git_refspec fetch;+	struct git_refspec push;+	git_transport *transport;+	git_repository *repo;+	unsigned int need_pack:1;+};++#endif
+ libgit2/src/repository.c view
@@ -0,0 +1,951 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include <stdarg.h>+#include <ctype.h>++#include "git2/object.h"++#include "common.h"+#include "repository.h"+#include "commit.h"+#include "tag.h"+#include "blob.h"+#include "fileops.h"+#include "config.h"+#include "refs.h"++#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"+#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"++#define GIT_FILE_CONTENT_PREFIX "gitdir:"++#define GIT_BRANCH_MASTER "master"++#define GIT_REPO_VERSION 0++static void drop_odb(git_repository *repo)+{+	if (repo->_odb != NULL) {+		GIT_REFCOUNT_OWN(repo->_odb, NULL);+		git_odb_free(repo->_odb);+		repo->_odb = NULL;+	}+}++static void drop_config(git_repository *repo)+{+	if (repo->_config != NULL) {+		GIT_REFCOUNT_OWN(repo->_config, NULL);+		git_config_free(repo->_config);+		repo->_config = NULL;+	}++	git_repository__cvar_cache_clear(repo);+}++static void drop_index(git_repository *repo)+{+	if (repo->_index != NULL) {+		GIT_REFCOUNT_OWN(repo->_index, NULL);+		git_index_free(repo->_index);+		repo->_index = NULL;+	}+}++void git_repository_free(git_repository *repo)+{+	if (repo == NULL)+		return;++	git_cache_free(&repo->objects);+	git_repository__refcache_free(&repo->references);+	git_attr_cache_flush(repo);+	git_submodule_config_free(repo);++	git__free(repo->path_repository);+	git__free(repo->workdir);++	drop_config(repo);+	drop_index(repo);+	drop_odb(repo);++	git__free(repo);+}++/*+ * Git repository open methods+ *+ * Open a repository object from its path+ */+static bool valid_repository_path(git_buf *repository_path)+{+	/* Check OBJECTS_DIR first, since it will generate the longest path name */+	if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)+		return false;++	/* Ensure HEAD file exists */+	if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)+		return false;++	if (git_path_contains_dir(repository_path, GIT_REFS_DIR)  == false)+		return false;++	return true;+}++static git_repository *repository_alloc(void)+{+	git_repository *repo = git__malloc(sizeof(git_repository));+	if (!repo)+		return NULL;++	memset(repo, 0x0, sizeof(git_repository));++	if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) {+		git__free(repo);+		return NULL;+	}++	/* set all the entries in the cvar cache to `unset` */+	git_repository__cvar_cache_clear(repo);++	return repo;+}++static int load_config_data(git_repository *repo)+{+	int is_bare;+	git_config *config;++	if (git_repository_config__weakptr(&config, repo) < 0)+		return -1;++	if (git_config_get_bool(&is_bare, config, "core.bare") < 0)+		return -1; /* FIXME: We assume that a missing core.bare+					  variable is an error. Is this right? */++	repo->is_bare = is_bare;+	return 0;+}++static int load_workdir(git_repository *repo, git_buf *parent_path)+{+	int         error;+	git_config *config;+	const char *worktree;+	git_buf     worktree_buf = GIT_BUF_INIT;++	if (repo->is_bare)+		return 0;++	if (git_repository_config__weakptr(&config, repo) < 0)+		return -1;++	error = git_config_get_string(&worktree, config, "core.worktree");+	if (!error && worktree != NULL)+		repo->workdir = git__strdup(worktree);+	else if (error != GIT_ENOTFOUND)+		return error;+	else {+		giterr_clear();++		if (parent_path && git_path_isdir(parent_path->ptr))+			repo->workdir = git_buf_detach(parent_path);+		else {+			git_path_dirname_r(&worktree_buf, repo->path_repository);+			git_path_to_dir(&worktree_buf);+			repo->workdir = git_buf_detach(&worktree_buf);+		}+	}++	GITERR_CHECK_ALLOC(repo->workdir);++	return 0;+}++/*+ * This function returns furthest offset into path where a ceiling dir+ * is found, so we can stop processing the path at that point.+ *+ * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on+ * the stack could remove directories name limits, but at the cost of doing+ * repeated malloc/frees inside the loop below, so let's not do it now.+ */+static int find_ceiling_dir_offset(+	const char *path,+	const char *ceiling_directories)+{+	char buf[GIT_PATH_MAX + 1];+	char buf2[GIT_PATH_MAX + 1];+	const char *ceil, *sep;+	size_t len, max_len = 0, min_len;++	assert(path);++	min_len = (size_t)(git_path_root(path) + 1);++	if (ceiling_directories == NULL || min_len == 0)+		return (int)min_len;++	for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {+		for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);+		len = sep - ceil;++		if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)+			continue;++		strncpy(buf, ceil, len);+		buf[len] = '\0';++		if (p_realpath(buf, buf2) == NULL)+			continue;++		len = strlen(buf2);+		if (len > 0 && buf2[len-1] == '/')+			buf[--len] = '\0';++		if (!strncmp(path, buf2, len) &&+			path[len] == '/' &&+			len > max_len)+		{+			max_len = len;+		}+	}++	return (int)(max_len <= min_len ? min_len : max_len);+}++/*+ * Read the contents of `file_path` and set `path_out` to the repo dir that+ * it points to.  Before calling, set `path_out` to the base directory that+ * should be used if the contents of `file_path` are a relative path.+ */+static int read_gitfile(git_buf *path_out, const char *file_path)+{+	int     error = 0;+	git_buf file = GIT_BUF_INIT;+	size_t  prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);++	assert(path_out && file_path);++	if (git_futils_readbuffer(&file, file_path) < 0)+		return -1;++	git_buf_rtrim(&file);++	if (file.size <= prefix_len ||+		memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)+	{+		giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);+		error = -1;+	}+	else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {+		const char *gitlink = ((const char *)file.ptr) + prefix_len;+		while (*gitlink && git__isspace(*gitlink)) gitlink++;+		error = git_path_prettify_dir(path_out, gitlink, path_out->ptr);+	}++	git_buf_free(&file);+	return error;+}++static int find_repo(+	git_buf *repo_path,+	git_buf *parent_path,+	const char *start_path,+	uint32_t flags,+	const char *ceiling_dirs)+{+	int error;+	git_buf path = GIT_BUF_INIT;+	struct stat st;+	dev_t initial_device = 0;+	bool try_with_dot_git = false;+	int ceiling_offset;++	git_buf_free(repo_path);++	if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0)+		return error;++	ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);++	if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)+		return error;++	while (!error && !git_buf_len(repo_path)) {+		if (p_stat(path.ptr, &st) == 0) {+			/* check that we have not crossed device boundaries */+			if (initial_device == 0)+				initial_device = st.st_dev;+			else if (st.st_dev != initial_device &&+				(flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0)+				break;++			if (S_ISDIR(st.st_mode)) {+				if (valid_repository_path(&path)) {+					git_path_to_dir(&path);+					git_buf_set(repo_path, path.ptr, path.size);+					break;+				}+			}+			else if (S_ISREG(st.st_mode)) {+				git_buf repo_link = GIT_BUF_INIT;++				if (!(error = read_gitfile(&repo_link, path.ptr))) {+					if (valid_repository_path(&repo_link))+						git_buf_swap(repo_path, &repo_link);++					git_buf_free(&repo_link);+					break;+				}+				git_buf_free(&repo_link);+			}+		}++		/* move up one directory level */+		if (git_path_dirname_r(&path, path.ptr) < 0) {+			error = -1;+			break;+		}++		if (try_with_dot_git) {+			/* if we tried original dir with and without .git AND either hit+			 * directory ceiling or NO_SEARCH was requested, then be done.+			 */+			if (path.ptr[ceiling_offset] == '\0' ||+				(flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)+				break;+			/* otherwise look first for .git item */+			error = git_buf_joinpath(&path, path.ptr, DOT_GIT);+		}+		try_with_dot_git = !try_with_dot_git;+	}++	if (!error && parent_path != NULL) {+		if (!git_buf_len(repo_path))+			git_buf_clear(parent_path);+		else {+			git_path_dirname_r(parent_path, path.ptr);+			git_path_to_dir(parent_path);+		}+		if (git_buf_oom(parent_path))+			return -1;+	}++	git_buf_free(&path);++	if (!git_buf_len(repo_path) && !error) {+		giterr_set(GITERR_REPOSITORY,+			"Could not find repository from '%s'", start_path);+		error = GIT_ENOTFOUND;+	}++	return error;+}++int git_repository_open_ext(+	git_repository **repo_ptr,+	const char *start_path,+	uint32_t flags,+	const char *ceiling_dirs)+{+	int error;+	git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;+	git_repository *repo;++	*repo_ptr = NULL;++	if ((error = find_repo(&path, &parent, start_path, flags, ceiling_dirs)) < 0)+		return error;++	repo = repository_alloc();+	GITERR_CHECK_ALLOC(repo);++	repo->path_repository = git_buf_detach(&path);+	GITERR_CHECK_ALLOC(repo->path_repository);++	if ((error = load_config_data(repo)) < 0 ||+		(error = load_workdir(repo, &parent)) < 0)+	{+		git_repository_free(repo);+		return error;+	}++	git_buf_free(&parent);+	*repo_ptr = repo;+	return 0;+}++int git_repository_open(git_repository **repo_out, const char *path)+{+	return git_repository_open_ext(+		repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);+}++int git_repository_discover(+	char *repository_path,+	size_t size,+	const char *start_path,+	int across_fs,+	const char *ceiling_dirs)+{+	git_buf path = GIT_BUF_INIT;+	uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;+	int error;++	assert(start_path && repository_path && size > 0);++	*repository_path = '\0';++	if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0)+		return error != GIT_ENOTFOUND ? -1 : error;++	if (size < (size_t)(path.size + 1)) {+		giterr_set(GITERR_REPOSITORY,+			"The given buffer is too long to store the discovered path");+		git_buf_free(&path);+		return -1;+	}++	/* success: we discovered a repository */+	git_buf_copy_cstr(repository_path, size, &path);+	git_buf_free(&path);+	return 0;+}++static int load_config(+	git_config **out,+	git_repository *repo,+	const char *global_config_path,+	const char *system_config_path)+{+	git_buf config_path = GIT_BUF_INIT;+	git_config *cfg = NULL;++	assert(repo && out);++	if (git_config_new(&cfg) < 0)+		return -1;++	if (git_buf_joinpath(+		&config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO) < 0)+		goto on_error;++	if (git_config_add_file_ondisk(cfg, config_path.ptr, 3) < 0)+		goto on_error;++	git_buf_free(&config_path);++	if (global_config_path != NULL) {+		if (git_config_add_file_ondisk(cfg, global_config_path, 2) < 0)+			goto on_error;+	}++	if (system_config_path != NULL) {+		if (git_config_add_file_ondisk(cfg, system_config_path, 1) < 0)+			goto on_error;+	}++	*out = cfg;+	return 0;++on_error:+	git_buf_free(&config_path);+	git_config_free(cfg);+	*out = NULL;+	return -1;+}++int git_repository_config__weakptr(git_config **out, git_repository *repo)+{+	if (repo->_config == NULL) {+		git_buf global_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;+		int res;++		const char *global_config_path = NULL;+		const char *system_config_path = NULL;++		if (git_config_find_global_r(&global_buf) == 0)+			global_config_path = global_buf.ptr;++		if (git_config_find_system_r(&system_buf) == 0)+			system_config_path = system_buf.ptr;++		res = load_config(&repo->_config, repo, global_config_path, system_config_path);++		git_buf_free(&global_buf);+		git_buf_free(&system_buf);++		if (res < 0)+			return -1;++		GIT_REFCOUNT_OWN(repo->_config, repo);+	}++	*out = repo->_config;+	return 0;+}++int git_repository_config(git_config **out, git_repository *repo)+{+	if (git_repository_config__weakptr(out, repo) < 0)+		return -1;++	GIT_REFCOUNT_INC(*out);+	return 0;+}++void git_repository_set_config(git_repository *repo, git_config *config)+{+	assert(repo && config);++	drop_config(repo);++	repo->_config = config;+	GIT_REFCOUNT_OWN(repo->_config, repo);+}++int git_repository_odb__weakptr(git_odb **out, git_repository *repo)+{+	assert(repo && out);++	if (repo->_odb == NULL) {+		git_buf odb_path = GIT_BUF_INIT;+		int res;++		if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0)+			return -1;++		res = git_odb_open(&repo->_odb, odb_path.ptr);+		git_buf_free(&odb_path); /* done with path */++		if (res < 0)+			return -1;++		GIT_REFCOUNT_OWN(repo->_odb, repo);+	}++	*out = repo->_odb;+	return 0;+}++int git_repository_odb(git_odb **out, git_repository *repo)+{+	if (git_repository_odb__weakptr(out, repo) < 0)+		return -1;++	GIT_REFCOUNT_INC(*out);+	return 0;+}++void git_repository_set_odb(git_repository *repo, git_odb *odb)+{+	assert(repo && odb);++	drop_odb(repo);++	repo->_odb = odb;+	GIT_REFCOUNT_OWN(repo->_odb, repo);+	GIT_REFCOUNT_INC(odb);+}++int git_repository_index__weakptr(git_index **out, git_repository *repo)+{+	assert(out && repo);++	if (repo->_index == NULL) {+		int res;+		git_buf index_path = GIT_BUF_INIT;++		if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0)+			return -1;++		res = git_index_open(&repo->_index, index_path.ptr);+		git_buf_free(&index_path); /* done with path */++		if (res < 0)+			return -1;++		GIT_REFCOUNT_OWN(repo->_index, repo);+	}++	*out = repo->_index;+	return 0;+}++int git_repository_index(git_index **out, git_repository *repo)+{+	if (git_repository_index__weakptr(out, repo) < 0)+		return -1;++	GIT_REFCOUNT_INC(*out);+	return 0;+}++void git_repository_set_index(git_repository *repo, git_index *index)+{+	assert(repo && index);++	drop_index(repo);++	repo->_index = index;+	GIT_REFCOUNT_OWN(repo->_index, repo);+	GIT_REFCOUNT_INC(index);+}++static int check_repositoryformatversion(git_repository *repo)+{+	git_config *config;+	int version;++	if (git_repository_config__weakptr(&config, repo) < 0)+		return -1;++	if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0)+		return -1;++	if (GIT_REPO_VERSION < version) {+		giterr_set(GITERR_REPOSITORY,+			"Unsupported repository version %d. Only versions up to %d are supported.",+			version, GIT_REPO_VERSION);+		return -1;+	}++	return 0;+}++static int repo_init_reinit(git_repository **repo_out, const char *repository_path, int is_bare)+{+	git_repository *repo = NULL;++	GIT_UNUSED(is_bare);++	if (git_repository_open(&repo, repository_path) < 0)+		return -1;++	if (check_repositoryformatversion(repo) < 0) {+		git_repository_free(repo);+		return -1;+	}++	/* TODO: reinitialize the templates */++	*repo_out = repo;+	return 0;+}++static int repo_init_createhead(const char *git_dir)+{+	git_buf ref_path = GIT_BUF_INIT;+	git_filebuf ref = GIT_FILEBUF_INIT;++	if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||+		git_filebuf_open(&ref, ref_path.ptr, 0) < 0 ||+		git_filebuf_printf(&ref, "ref: refs/heads/master\n") < 0 ||+		git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)+		return -1;++	git_buf_free(&ref_path);+	return 0;+}++static int repo_init_config(const char *git_dir, int is_bare)+{+	git_buf cfg_path = GIT_BUF_INIT;+	git_config *config = NULL;++#define SET_REPO_CONFIG(type, name, val) {\+	if (git_config_set_##type(config, name, val) < 0) { \+		git_buf_free(&cfg_path); \+		git_config_free(config); \+		return -1; } \+}++	if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)+		return -1;++	if (git_config_open_ondisk(&config, cfg_path.ptr) < 0) {+		git_buf_free(&cfg_path);+		return -1;+	}++	SET_REPO_CONFIG(bool, "core.bare", is_bare);+	SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);+	/* TODO: what other defaults? */++	git_buf_free(&cfg_path);+	git_config_free(config);+	return 0;+}++#define GIT_HOOKS_DIR "hooks/"+#define GIT_HOOKS_DIR_MODE 0755++#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"+#define GIT_HOOKS_README_MODE 0755+#define GIT_HOOKS_README_CONTENT \+"#!/bin/sh\n"\+"#\n"\+"# Place appropriately named executable hook scripts into this directory\n"\+"# to intercept various actions that git takes.  See `git help hooks` for\n"\+"# more information.\n"++#define GIT_INFO_DIR "info/"+#define GIT_INFO_DIR_MODE 0755++#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"+#define GIT_INFO_EXCLUDE_MODE 0644+#define GIT_INFO_EXCLUDE_CONTENT \+"# File patterns to ignore; see `git help ignore` for more information.\n"\+"# Lines that start with '#' are comments.\n"++#define GIT_DESC_FILE "description"+#define GIT_DESC_MODE 0644+#define GIT_DESC_CONTENT "Unnamed repository; edit this file 'description' to name the repository.\n"++static int repo_write_template(+	const char *git_dir, const char *file, mode_t mode, const char *content)+{+	git_buf path = GIT_BUF_INIT;+	int fd, error = 0;++	if (git_buf_joinpath(&path, git_dir, file) < 0)+		return -1;++	fd = p_open(git_buf_cstr(&path), O_WRONLY | O_CREAT | O_EXCL, mode);++	if (fd >= 0) {+		error = p_write(fd, content, strlen(content));++		p_close(fd);+	}+	else if (errno != EEXIST)+		error = fd;++	git_buf_free(&path);++	if (error)+		giterr_set(GITERR_OS,+			"Failed to initialize repository with template '%s'", file);++	return error;+}++static int repo_init_structure(const char *git_dir, int is_bare)+{+	int i;+	struct { const char *dir; mode_t mode; } dirs[] = {+		{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */+		{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */+		{ GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE },     /* '/refs/heads/' */+		{ GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE },      /* '/refs/tags/' */+		{ GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE },         /* '/hooks/' */+		{ GIT_INFO_DIR, GIT_INFO_DIR_MODE },           /* '/info/' */+		{ NULL, 0 }+	};+	struct { const char *file; mode_t mode; const char *content; } tmpl[] = {+		{ GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },+		{ GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },+		{ GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },+		{ NULL, 0, NULL }+	};++	/* Make the base directory */+	if (git_futils_mkdir_r(git_dir, NULL, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE) < 0)+		return -1;++	/* Hides the ".git" directory */+	if (!is_bare) {+#ifdef GIT_WIN32+		if (p_hide_directory__w32(git_dir) < 0) {+			giterr_set(GITERR_REPOSITORY,+				"Failed to mark Git repository folder as hidden");+			return -1;+		}+#endif+	}++	/* Make subdirectories as needed */+	for (i = 0; dirs[i].dir != NULL; ++i) {+		if (git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode) < 0)+			return -1;+	}++	/* Make template files as needed */+	for (i = 0; tmpl[i].file != NULL; ++i) {+		if (repo_write_template(+				git_dir, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0)+			return -1;+	}++	return 0;+}++int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)+{+	git_buf repository_path = GIT_BUF_INIT;++	assert(repo_out && path);++	if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)+		return -1;++	if (git_path_isdir(repository_path.ptr) == true) {+		if (valid_repository_path(&repository_path) == true) {+			int res = repo_init_reinit(repo_out, repository_path.ptr, is_bare);+			git_buf_free(&repository_path);+			return res;+		}+	}++	if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||+		repo_init_config(repository_path.ptr, is_bare) < 0 || +		repo_init_createhead(repository_path.ptr) < 0 ||+		git_repository_open(repo_out, repository_path.ptr) < 0) {+		git_buf_free(&repository_path);+		return -1;+	}++	git_buf_free(&repository_path);+	return 0;+}++int git_repository_head_detached(git_repository *repo)+{+	git_reference *ref;+	git_odb *odb = NULL;+	int exists;++	if (git_repository_odb__weakptr(&odb, repo) < 0)+		return -1;++	if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)+		return -1;++	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {+		git_reference_free(ref);+		return 0;+	}++	exists = git_odb_exists(odb, git_reference_oid(ref));++	git_reference_free(ref);+	return exists;+}++int git_repository_head(git_reference **head_out, git_repository *repo)+{+	return git_reference_lookup_resolved(head_out, repo, GIT_HEAD_FILE, -1);+}++int git_repository_head_orphan(git_repository *repo)+{+	git_reference *ref = NULL;+	int error;++	error = git_repository_head(&ref, repo);+	git_reference_free(ref);++	if (error == GIT_ENOTFOUND)+		return 1;++	if (error < 0)+		return -1;++	return 0;+}++int git_repository_is_empty(git_repository *repo)+{+	git_reference *head = NULL, *branch = NULL;+	int error;++	if (git_reference_lookup(&head, repo, "HEAD") < 0)+		return -1;++	if (git_reference_type(head) != GIT_REF_SYMBOLIC) {+		git_reference_free(head);+		return 0;+	}++	if (strcmp(git_reference_target(head), "refs/heads/master") != 0) {+		git_reference_free(head);+		return 0;+	}++	error = git_reference_resolve(&branch, head);++	git_reference_free(head);+	git_reference_free(branch);++	if (error == GIT_ENOTFOUND)+		return 1;++	if (error < 0)+		return -1;++	return 0;+}++const char *git_repository_path(git_repository *repo)+{+	assert(repo);+	return repo->path_repository;+}++const char *git_repository_workdir(git_repository *repo)+{+	assert(repo);++	if (repo->is_bare)+		return NULL;++	return repo->workdir;+}++int git_repository_set_workdir(git_repository *repo, const char *workdir)+{+	git_buf path = GIT_BUF_INIT;++	assert(repo && workdir);++	if (git_path_prettify_dir(&path, workdir, NULL) < 0)+		return -1;++	git__free(repo->workdir);++	repo->workdir = git_buf_detach(&path);+	repo->is_bare = 0;+	return 0;+}++int git_repository_is_bare(git_repository *repo)+{+	assert(repo);+	return repo->is_bare;+}++int git_repository_head_tree(git_tree **tree, git_repository *repo)+{+	git_oid head_oid;+	git_object *obj = NULL;++	if (git_reference_name_to_oid(&head_oid, repo, GIT_HEAD_FILE) < 0) {+		/* cannot resolve HEAD - probably brand new repo */+		giterr_clear();+		*tree = NULL;+		return 0;+	}++	if (git_object_lookup(&obj, repo, &head_oid, GIT_OBJ_ANY) < 0 ||+		git_object__resolve_to_type(&obj, GIT_OBJ_TREE) < 0)+		return -1;++	*tree = (git_tree *)obj;+	return 0;+}
+ libgit2/src/repository.h view
@@ -0,0 +1,138 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_repository_h__+#define INCLUDE_repository_h__++#include "git2/common.h"+#include "git2/oid.h"+#include "git2/odb.h"+#include "git2/repository.h"+#include "git2/object.h"++#include "index.h"+#include "cache.h"+#include "refs.h"+#include "buffer.h"+#include "odb.h"+#include "attr.h"+#include "strmap.h"++#define DOT_GIT ".git"+#define GIT_DIR DOT_GIT "/"+#define GIT_DIR_MODE 0755+#define GIT_BARE_DIR_MODE 0777++/** Cvar cache identifiers */+typedef enum {+	GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */+	GIT_CVAR_EOL, /* core.eol */+	GIT_CVAR_CACHE_MAX+} git_cvar_cached;++/**+ * CVAR value enumerations+ *+ * These are the values that are actually stored in the cvar cache, instead+ * of their string equivalents. These values are internal and symbolic;+ * make sure that none of them is set to `-1`, since that is the unique+ * identifier for "not cached"+ */+typedef enum {+	/* The value hasn't been loaded from the cache yet */+	GIT_CVAR_NOT_CACHED = -1,++	/* core.safecrlf: false, 'fail', 'warn' */+	GIT_SAFE_CRLF_FALSE = 0,+	GIT_SAFE_CRLF_FAIL = 1,+	GIT_SAFE_CRLF_WARN = 2,++	/* core.autocrlf: false, true, 'input; */+	GIT_AUTO_CRLF_FALSE = 0,+	GIT_AUTO_CRLF_TRUE = 1,+	GIT_AUTO_CRLF_INPUT = 2,+	GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE,++	/* core.eol: unset, 'crlf', 'lf', 'native' */+	GIT_EOL_UNSET = 0,+	GIT_EOL_CRLF = 1,+	GIT_EOL_LF = 2,+#ifdef GIT_WIN32+	GIT_EOL_NATIVE = GIT_EOL_CRLF,+#else+	GIT_EOL_NATIVE = GIT_EOL_LF,+#endif+	GIT_EOL_DEFAULT = GIT_EOL_NATIVE+} git_cvar_value;++/** Base git object for inheritance */+struct git_object {+	git_cached_obj cached;+	git_repository *repo;+	git_otype type;+};++struct git_repository {+	git_odb *_odb;+	git_config *_config;+	git_index *_index;++	git_cache objects;+	git_refcache references;+	git_attr_cache attrcache;+	git_strmap *submodules;++	char *path_repository;+	char *workdir;++	unsigned is_bare:1;+	unsigned int lru_counter;++	git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];+};++/* fully free the object; internal method, do not+ * export */+void git_object__free(void *object);++int git_object__resolve_to_type(git_object **obj, git_otype type);++int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);+void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);++GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)+{+	return &repo->attrcache;+}++int git_repository_head_tree(git_tree **tree, git_repository *repo);++/*+ * Weak pointers to repository internals.+ *+ * The returned pointers do not need to be freed. Do not keep+ * permanent references to these (i.e. between API calls), since they may+ * become invalidated if the user replaces a repository internal.+ */+int git_repository_config__weakptr(git_config **out, git_repository *repo);+int git_repository_odb__weakptr(git_odb **out, git_repository *repo);+int git_repository_index__weakptr(git_index **out, git_repository *repo);++/*+ * CVAR cache + *+ * Efficient access to the most used config variables of a repository.+ * The cache is cleared everytime the config backend is replaced.+ */+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);+void git_repository__cvar_cache_clear(git_repository *repo);++/*+ * Submodule cache+ */+extern void git_submodule_config_free(git_repository *repo);++#endif
+ libgit2/src/revwalk.c view
@@ -0,0 +1,845 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "commit.h"+#include "odb.h"+#include "pqueue.h"+#include "pool.h"+#include "oidmap.h"++#include "git2/revwalk.h"+#include "git2/merge.h"++#include <regex.h>++GIT__USE_OIDMAP;++#define PARENT1  (1 << 0)+#define PARENT2  (1 << 1)+#define RESULT   (1 << 2)+#define STALE    (1 << 3)++typedef struct commit_object {+	git_oid oid;+	uint32_t time;+	unsigned int seen:1,+			 uninteresting:1,+			 topo_delay:1,+			 parsed:1,+			 flags : 4;++	unsigned short in_degree;+	unsigned short out_degree;++	struct commit_object **parents;+} commit_object;++typedef struct commit_list {+	commit_object *item;+	struct commit_list *next;+} commit_list;++struct git_revwalk {+	git_repository *repo;+	git_odb *odb;++	git_oidmap *commits;+	git_pool commit_pool;++	commit_list *iterator_topo;+	commit_list *iterator_rand;+	commit_list *iterator_reverse;+	git_pqueue iterator_time;++	int (*get_next)(commit_object **, git_revwalk *);+	int (*enqueue)(git_revwalk *, commit_object *);++	unsigned walking:1;+	unsigned int sorting;++	/* merge base calculation */+	commit_object *one;+	git_vector twos;+};++static int commit_time_cmp(void *a, void *b)+{+	commit_object *commit_a = (commit_object *)a;+	commit_object *commit_b = (commit_object *)b;++	return (commit_a->time < commit_b->time);+}++static commit_list *commit_list_insert(commit_object *item, commit_list **list_p)+{+	commit_list *new_list = git__malloc(sizeof(commit_list));+	if (new_list != NULL) {+		new_list->item = item;+		new_list->next = *list_p;+	}+	*list_p = new_list;+	return new_list;+}++static commit_list *commit_list_insert_by_date(commit_object *item, commit_list **list_p)+{+	commit_list **pp = list_p;+	commit_list *p;++	while ((p = *pp) != NULL) {+		if (commit_time_cmp(p->item, item) < 0)+			break;++		pp = &p->next;+	}++	return commit_list_insert(item, pp);+}+static void commit_list_free(commit_list **list_p)+{+	commit_list *list = *list_p;++	while (list) {+		commit_list *temp = list;+		list = temp->next;+		git__free(temp);+	}++	*list_p = NULL;+}++static commit_object *commit_list_pop(commit_list **stack)+{+	commit_list *top = *stack;+	commit_object *item = top ? top->item : NULL;++	if (top) {+		*stack = top->next;+		git__free(top);+	}+	return item;+}++#define PARENTS_PER_COMMIT	2+#define COMMIT_ALLOC \+	(sizeof(commit_object) + PARENTS_PER_COMMIT * sizeof(commit_object *))++static commit_object *alloc_commit(git_revwalk *walk)+{+	return (commit_object *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);+}++static commit_object **alloc_parents(+	git_revwalk *walk, commit_object *commit, size_t n_parents)+{+	if (n_parents <= PARENTS_PER_COMMIT)+		return (commit_object **)((char *)commit + sizeof(commit_object));++	return (commit_object **)git_pool_malloc(+		&walk->commit_pool, (uint32_t)(n_parents * sizeof(commit_object *)));+}+++static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)+{+	commit_object *commit;+	khiter_t pos;+	int ret;++	/* lookup and reserve space if not already present */+	pos = kh_get(oid, walk->commits, oid);+	if (pos != kh_end(walk->commits))+		return kh_value(walk->commits, pos);++	commit = alloc_commit(walk);+	if (commit == NULL)+		return NULL;++	git_oid_cpy(&commit->oid, oid);++	pos = kh_put(oid, walk->commits, &commit->oid, &ret);+	assert(ret != 0);+	kh_value(walk->commits, pos) = commit;++	return commit;+}++static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawobj *raw)+{+	const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;++	unsigned char *buffer = raw->data;+	unsigned char *buffer_end = buffer + raw->len;+	unsigned char *parents_start;++	int i, parents = 0;+	int commit_time;++	buffer += strlen("tree ") + GIT_OID_HEXSZ + 1;++	parents_start = buffer;+	while (buffer + parent_len < buffer_end && memcmp(buffer, "parent ", strlen("parent ")) == 0) {+		parents++;+		buffer += parent_len;+	}++	commit->parents = alloc_parents(walk, commit, parents);+	GITERR_CHECK_ALLOC(commit->parents);++	buffer = parents_start;+	for (i = 0; i < parents; ++i) {+		git_oid oid;++		if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < 0)+			return -1;++		commit->parents[i] = commit_lookup(walk, &oid);+		if (commit->parents[i] == NULL)+			return -1;++		buffer += parent_len;+	}++	commit->out_degree = (unsigned short)parents;++	if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) {+		giterr_set(GITERR_ODB, "Failed to parse commit. Object is corrupted");+		return -1;+	}++	buffer = memchr(buffer, '>', buffer_end - buffer);+	if (buffer == NULL) {+		giterr_set(GITERR_ODB, "Failed to parse commit. Can't find author");+		return -1;+	}++	if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < 0) {+		giterr_set(GITERR_ODB, "Failed to parse commit. Can't parse commit time");+		return -1;+	}++	commit->time = (time_t)commit_time;+	commit->parsed = 1;+	return 0;+}++static int commit_parse(git_revwalk *walk, commit_object *commit)+{+	git_odb_object *obj;+	int error;++	if (commit->parsed)+		return 0;++	if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)+		return error;++	if (obj->raw.type != GIT_OBJ_COMMIT) {+		git_odb_object_free(obj);+		giterr_set(GITERR_INVALID, "Failed to parse commit. Object is no commit object");+		return -1;+	}++	error = commit_quick_parse(walk, commit, &obj->raw);+	git_odb_object_free(obj);+	return error;+}++static int interesting(git_pqueue *list)+{+	unsigned int i;+	for (i = 1; i < git_pqueue_size(list); i++) {+		commit_object *commit = list->d[i];+		if ((commit->flags & STALE) == 0)+			return 1;+	}++	return 0;+}++static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object *one, git_vector *twos)+{+	int error;+	unsigned int i;+	commit_object *two;+	commit_list *result = NULL, *tmp = NULL;+	git_pqueue list;++	/* if the commit is repeated, we have a our merge base already */+	git_vector_foreach(twos, i, two) {+		if (one == two)+			return commit_list_insert(one, out) ? 0 : -1;+	}++	if (git_pqueue_init(&list, twos->length * 2, commit_time_cmp) < 0)+		return -1;++	if (commit_parse(walk, one) < 0)+	    return -1;++	one->flags |= PARENT1;+	if (git_pqueue_insert(&list, one) < 0)+		return -1;++	git_vector_foreach(twos, i, two) {+		commit_parse(walk, two);+		two->flags |= PARENT2;+		if (git_pqueue_insert(&list, two) < 0)+			return -1;+	}++	/* as long as there are non-STALE commits */+	while (interesting(&list)) {+		commit_object *commit;+		int flags;++		commit = git_pqueue_pop(&list);++		flags = commit->flags & (PARENT1 | PARENT2 | STALE);+		if (flags == (PARENT1 | PARENT2)) {+			if (!(commit->flags & RESULT)) {+				commit->flags |= RESULT;+				if (commit_list_insert(commit, &result) == NULL)+					return -1;+			}+			/* we mark the parents of a merge stale */+			flags |= STALE;+		}++		for (i = 0; i < commit->out_degree; i++) {+			commit_object *p = commit->parents[i];+			if ((p->flags & flags) == flags)+				continue;++			if ((error = commit_parse(walk, p)) < 0)+				return error;++			p->flags |= flags;+			if (git_pqueue_insert(&list, p) < 0)+				return -1;+		}+	}++	git_pqueue_free(&list);++	/* filter out any stale commits in the results */+	tmp = result;+	result = NULL;++	while (tmp) {+		struct commit_list *next = tmp->next;+		if (!(tmp->item->flags & STALE))+			if (commit_list_insert_by_date(tmp->item, &result) == NULL)+				return -1;++		git__free(tmp);+		tmp = next;+	}++	*out = result;+	return 0;+}++int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two)+{+	git_revwalk *walk;+	git_vector list;+	commit_list *result = NULL;+	commit_object *commit;+	void *contents[1];++	if (git_revwalk_new(&walk, repo) < 0)+		return -1;++	commit = commit_lookup(walk, two);+	if (commit == NULL)+		goto on_error;++	/* This is just one value, so we can do it on the stack */+	memset(&list, 0x0, sizeof(git_vector));+	contents[0] = commit;+	list.length = 1;+	list.contents = contents;++	commit = commit_lookup(walk, one);+	if (commit == NULL)+		goto on_error;++	if (merge_bases_many(&result, walk, commit, &list) < 0)+		goto on_error;++	if (!result) {+		git_revwalk_free(walk);+		return GIT_ENOTFOUND;+	}++	git_oid_cpy(out, &result->item->oid);+	commit_list_free(&result);+	git_revwalk_free(walk);++	return 0;++on_error:+	git_revwalk_free(walk);+	return -1;+}++static void mark_uninteresting(commit_object *commit)+{+	unsigned short i;+	assert(commit);++	commit->uninteresting = 1;++	/* This means we've reached a merge base, so there's no need to walk any more */+	if ((commit->flags & (RESULT | STALE)) == RESULT)+		return;++	for (i = 0; i < commit->out_degree; ++i)+		if (!commit->parents[i]->uninteresting)+			mark_uninteresting(commit->parents[i]);+}++static int process_commit(git_revwalk *walk, commit_object *commit, int hide)+{+	int error;++	if (hide)+		mark_uninteresting(commit);++	if (commit->seen)+		return 0;++	commit->seen = 1;++	if ((error = commit_parse(walk, commit)) < 0)+		return error;++	return walk->enqueue(walk, commit);+}++static int process_commit_parents(git_revwalk *walk, commit_object *commit)+{+	unsigned short i;+	int error = 0;++	for (i = 0; i < commit->out_degree && !error; ++i)+		error = process_commit(walk, commit->parents[i], commit->uninteresting);++	return error;+}++static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting)+{+	commit_object *commit;++	commit = commit_lookup(walk, oid);+	if (commit == NULL)+		return -1; /* error already reported by failed lookup */++	commit->uninteresting = uninteresting;+	if (walk->one == NULL && !uninteresting) {+		walk->one = commit;+	} else {+		if (git_vector_insert(&walk->twos, commit) < 0)+			return -1;+	}++	return 0;+}++int git_revwalk_push(git_revwalk *walk, const git_oid *oid)+{+	assert(walk && oid);+	return push_commit(walk, oid, 0);+}+++int git_revwalk_hide(git_revwalk *walk, const git_oid *oid)+{+	assert(walk && oid);+	return push_commit(walk, oid, 1);+}++static int push_ref(git_revwalk *walk, const char *refname, int hide)+{+	git_oid oid;++	if (git_reference_name_to_oid(&oid, walk->repo, refname) < 0)+		return -1;++	return push_commit(walk, &oid, hide);+}++struct push_cb_data {+	git_revwalk *walk;+	const char *glob;+	int hide;+};++static int push_glob_cb(const char *refname, void *data_)+{+	struct push_cb_data *data = (struct push_cb_data *)data_;++	if (!p_fnmatch(data->glob, refname, 0))+		return push_ref(data->walk, refname, data->hide);++	return 0;+}++static int push_glob(git_revwalk *walk, const char *glob, int hide)+{+	git_buf buf = GIT_BUF_INIT;+	struct push_cb_data data;+	regex_t preg;++	assert(walk && glob);++	/* refs/ is implied if not given in the glob */+	if (strncmp(glob, GIT_REFS_DIR, strlen(GIT_REFS_DIR))) {+		git_buf_printf(&buf, GIT_REFS_DIR "%s", glob);+	} else {+		git_buf_puts(&buf, glob);+	}++	/* If no '?', '*' or '[' exist, we append '/ *' to the glob */+	memset(&preg, 0x0, sizeof(regex_t));+	if (regcomp(&preg, "[?*[]", REG_EXTENDED)) {+		giterr_set(GITERR_OS, "Regex failed to compile");+		git_buf_free(&buf);+		return -1;+	}++	if (regexec(&preg, glob, 0, NULL, 0))+		git_buf_puts(&buf, "/*");++	if (git_buf_oom(&buf))+		goto on_error;++	data.walk = walk;+	data.glob = git_buf_cstr(&buf);+	data.hide = hide;++	if (git_reference_foreach(+			walk->repo, GIT_REF_LISTALL, push_glob_cb, &data) < 0)+		goto on_error;++	regfree(&preg);+	git_buf_free(&buf);+	return 0;++on_error:+	regfree(&preg);+	git_buf_free(&buf);+	return -1;+}++int git_revwalk_push_glob(git_revwalk *walk, const char *glob)+{+	assert(walk && glob);+	return push_glob(walk, glob, 0);+}++int git_revwalk_hide_glob(git_revwalk *walk, const char *glob)+{+	assert(walk && glob);+	return push_glob(walk, glob, 1);+}++int git_revwalk_push_head(git_revwalk *walk)+{+	assert(walk);+	return push_ref(walk, GIT_HEAD_FILE, 0);+}++int git_revwalk_hide_head(git_revwalk *walk)+{+	assert(walk);+	return push_ref(walk, GIT_HEAD_FILE, 1);+}++int git_revwalk_push_ref(git_revwalk *walk, const char *refname)+{+	assert(walk && refname);+	return push_ref(walk, refname, 0);+}++int git_revwalk_hide_ref(git_revwalk *walk, const char *refname)+{+	assert(walk && refname);+	return push_ref(walk, refname, 1);+}++static int revwalk_enqueue_timesort(git_revwalk *walk, commit_object *commit)+{+	return git_pqueue_insert(&walk->iterator_time, commit);+}++static int revwalk_enqueue_unsorted(git_revwalk *walk, commit_object *commit)+{+	return commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1;+}++static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk)+{+	int error;+	commit_object *next;++	while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {+		if ((error = process_commit_parents(walk, next)) < 0)+			return error;++		if (!next->uninteresting) {+			*object_out = next;+			return 0;+		}+	}++	return GIT_REVWALKOVER;+}++static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)+{+	int error;+	commit_object *next;++	while ((next = commit_list_pop(&walk->iterator_rand)) != NULL) {+		if ((error = process_commit_parents(walk, next)) < 0)+			return error;++		if (!next->uninteresting) {+			*object_out = next;+			return 0;+		}+	}++	return GIT_REVWALKOVER;+}++static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)+{+	commit_object *next;+	unsigned short i;++	for (;;) {+		next = commit_list_pop(&walk->iterator_topo);+		if (next == NULL)+			return GIT_REVWALKOVER;++		if (next->in_degree > 0) {+			next->topo_delay = 1;+			continue;+		}++		for (i = 0; i < next->out_degree; ++i) {+			commit_object *parent = next->parents[i];++			if (--parent->in_degree == 0 && parent->topo_delay) {+				parent->topo_delay = 0;+				if (commit_list_insert(parent, &walk->iterator_topo) == NULL)+					return -1;+			}+		}++		*object_out = next;+		return 0;+	}+}++static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk)+{+	*object_out = commit_list_pop(&walk->iterator_reverse);+	return *object_out ? 0 : GIT_REVWALKOVER;+}+++static int prepare_walk(git_revwalk *walk)+{+	int error;+	unsigned int i;+	commit_object *next, *two;+	commit_list *bases = NULL;++	/*+	 * If walk->one is NULL, there were no positive references,+	 * so we know that the walk is already over.+	 */+	if (walk->one == NULL)+		return GIT_REVWALKOVER;++	/* first figure out what the merge bases are */+	if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0)+		return -1;++	commit_list_free(&bases);+	if (process_commit(walk, walk->one, walk->one->uninteresting) < 0)+		return -1;++	git_vector_foreach(&walk->twos, i, two) {+		if (process_commit(walk, two, two->uninteresting) < 0)+			return -1;+	}++	if (walk->sorting & GIT_SORT_TOPOLOGICAL) {+		unsigned short i;++		while ((error = walk->get_next(&next, walk)) == 0) {+			for (i = 0; i < next->out_degree; ++i) {+				commit_object *parent = next->parents[i];+				parent->in_degree++;+			}++			if (commit_list_insert(next, &walk->iterator_topo) == NULL)+				return -1;+		}++		if (error != GIT_REVWALKOVER)+			return error;++		walk->get_next = &revwalk_next_toposort;+	}++	if (walk->sorting & GIT_SORT_REVERSE) {++		while ((error = walk->get_next(&next, walk)) == 0)+			if (commit_list_insert(next, &walk->iterator_reverse) == NULL)+				return -1;++		if (error != GIT_REVWALKOVER)+			return error;++		walk->get_next = &revwalk_next_reverse;+	}++	walk->walking = 1;+	return 0;+}++++++int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)+{+	git_revwalk *walk;++	walk = git__malloc(sizeof(git_revwalk));+	GITERR_CHECK_ALLOC(walk);++	memset(walk, 0x0, sizeof(git_revwalk));++	walk->commits = git_oidmap_alloc();+	GITERR_CHECK_ALLOC(walk->commits);++	if (git_pqueue_init(&walk->iterator_time, 8, commit_time_cmp) < 0 ||+		git_vector_init(&walk->twos, 4, NULL) < 0 ||+		git_pool_init(&walk->commit_pool, 1,+			git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0)+		return -1;++	walk->get_next = &revwalk_next_unsorted;+	walk->enqueue = &revwalk_enqueue_unsorted;++	walk->repo = repo;++	if (git_repository_odb(&walk->odb, repo) < 0) {+		git_revwalk_free(walk);+		return -1;+	}++	*revwalk_out = walk;+	return 0;+}++void git_revwalk_free(git_revwalk *walk)+{+	if (walk == NULL)+		return;++	git_revwalk_reset(walk);+	git_odb_free(walk->odb);++	git_oidmap_free(walk->commits);+	git_pool_clear(&walk->commit_pool);+	git_pqueue_free(&walk->iterator_time);+	git_vector_free(&walk->twos);+	git__free(walk);+}++git_repository *git_revwalk_repository(git_revwalk *walk)+{+	assert(walk);+	return walk->repo;+}++void git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode)+{+	assert(walk);++	if (walk->walking)+		git_revwalk_reset(walk);++	walk->sorting = sort_mode;++	if (walk->sorting & GIT_SORT_TIME) {+		walk->get_next = &revwalk_next_timesort;+		walk->enqueue = &revwalk_enqueue_timesort;+	} else {+		walk->get_next = &revwalk_next_unsorted;+		walk->enqueue = &revwalk_enqueue_unsorted;+	}+}++int git_revwalk_next(git_oid *oid, git_revwalk *walk)+{+	int error;+	commit_object *next;++	assert(walk && oid);++	if (!walk->walking) {+		if ((error = prepare_walk(walk)) < 0)+			return error;+	}++	error = walk->get_next(&next, walk);++	if (error == GIT_REVWALKOVER) {+		git_revwalk_reset(walk);+		return GIT_REVWALKOVER;+	}++	if (!error)+		git_oid_cpy(oid, &next->oid);++	return error;+}++void git_revwalk_reset(git_revwalk *walk)+{+	commit_object *commit;++	assert(walk);++	kh_foreach_value(walk->commits, commit, {+		commit->seen = 0;+		commit->in_degree = 0;+		commit->topo_delay = 0;+		commit->uninteresting = 0;+		});++	git_pqueue_clear(&walk->iterator_time);+	commit_list_free(&walk->iterator_topo);+	commit_list_free(&walk->iterator_rand);+	commit_list_free(&walk->iterator_reverse);+	walk->walking = 0;++	walk->one = NULL;+	git_vector_clear(&walk->twos);+}+
+ libgit2/src/sha1.c view
@@ -0,0 +1,280 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "sha1.h"++#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))++/*+ * Force usage of rol or ror by selecting the one with the smaller constant.+ * It _can_ generate slightly smaller code (a constant of 1 is special), but+ * perhaps more importantly it's possibly faster on any uarch that does a+ * rotate with a loop.+ */++#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })+#define SHA_ROL(x,n)	SHA_ASM("rol", x, n)+#define SHA_ROR(x,n)	SHA_ASM("ror", x, n)++#else++#define SHA_ROT(X,l,r)	(((X) << (l)) | ((X) >> (r)))+#define SHA_ROL(X,n)	SHA_ROT(X,n,32-(n))+#define SHA_ROR(X,n)	SHA_ROT(X,32-(n),n)++#endif++/*+ * If you have 32 registers or more, the compiler can (and should)+ * try to change the array[] accesses into registers. However, on+ * machines with less than ~25 registers, that won't really work,+ * and at least gcc will make an unholy mess of it.+ *+ * So to avoid that mess which just slows things down, we force+ * the stores to memory to actually happen (we might be better off+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as+ * suggested by Artur Skawina - that will also make gcc unable to+ * try to do the silly "optimize away loads" part because it won't+ * see what the value will be).+ *+ * Ben Herrenschmidt reports that on PPC, the C version comes close+ * to the optimized asm with this (ie on PPC you don't want that+ * 'volatile', since there are lots of registers).+ *+ * On ARM we get the best code generation by forcing a full memory barrier+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and+ * the stack frame size simply explode and performance goes down the drain.+ */++#if defined(__i386__) || defined(__x86_64__)+ #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))+#elif defined(__GNUC__) && defined(__arm__)+ #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)+#else+ #define setW(x, val) (W(x) = (val))+#endif++/*+ * Performance might be improved if the CPU architecture is OK with+ * unaligned 32-bit loads and a fast ntohl() is available.+ * Otherwise fall back to byte loads and shifts which is portable,+ * and is faster on architectures with memory alignment issues.+ */++#if defined(__i386__) || defined(__x86_64__) || \+	defined(_M_IX86) || defined(_M_X64) || \+	defined(__ppc__) || defined(__ppc64__) || \+	defined(__powerpc__) || defined(__powerpc64__) || \+	defined(__s390__) || defined(__s390x__)++#define get_be32(p)	ntohl(*(const unsigned int *)(p))+#define put_be32(p, v)	do { *(unsigned int *)(p) = htonl(v); } while (0)++#else++#define get_be32(p)	( \+	(*((const unsigned char *)(p) + 0) << 24) | \+	(*((const unsigned char *)(p) + 1) << 16) | \+	(*((const unsigned char *)(p) + 2) << 8) | \+	(*((const unsigned char *)(p) + 3) << 0) )+#define put_be32(p, v)	do { \+	unsigned int __v = (v); \+	*((unsigned char *)(p) + 0) = __v >> 24; \+	*((unsigned char *)(p) + 1) = __v >> 16; \+	*((unsigned char *)(p) + 2) = __v >> 8; \+	*((unsigned char *)(p) + 3) = __v >> 0; } while (0)++#endif++/* This "rolls" over the 512-bit array */+#define W(x) (array[(x)&15])++/*+ * Where do we get the source from? The first 16 iterations get it from+ * the input data, the next mix it from the 512-bit array.+ */+#define SHA_SRC(t) get_be32(data + t)+#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)++#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \+	unsigned int TEMP = input(t); setW(t, TEMP); \+	E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \+	B = SHA_ROR(B, 2); } while (0)++#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )++static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)+{+	unsigned int A,B,C,D,E;+	unsigned int array[16];++	A = ctx->H[0];+	B = ctx->H[1];+	C = ctx->H[2];+	D = ctx->H[3];+	E = ctx->H[4];++	/* Round 1 - iterations 0-16 take their input from 'data' */+	T_0_15( 0, A, B, C, D, E);+	T_0_15( 1, E, A, B, C, D);+	T_0_15( 2, D, E, A, B, C);+	T_0_15( 3, C, D, E, A, B);+	T_0_15( 4, B, C, D, E, A);+	T_0_15( 5, A, B, C, D, E);+	T_0_15( 6, E, A, B, C, D);+	T_0_15( 7, D, E, A, B, C);+	T_0_15( 8, C, D, E, A, B);+	T_0_15( 9, B, C, D, E, A);+	T_0_15(10, A, B, C, D, E);+	T_0_15(11, E, A, B, C, D);+	T_0_15(12, D, E, A, B, C);+	T_0_15(13, C, D, E, A, B);+	T_0_15(14, B, C, D, E, A);+	T_0_15(15, A, B, C, D, E);++	/* Round 1 - tail. Input from 512-bit mixing array */+	T_16_19(16, E, A, B, C, D);+	T_16_19(17, D, E, A, B, C);+	T_16_19(18, C, D, E, A, B);+	T_16_19(19, B, C, D, E, A);++	/* Round 2 */+	T_20_39(20, A, B, C, D, E);+	T_20_39(21, E, A, B, C, D);+	T_20_39(22, D, E, A, B, C);+	T_20_39(23, C, D, E, A, B);+	T_20_39(24, B, C, D, E, A);+	T_20_39(25, A, B, C, D, E);+	T_20_39(26, E, A, B, C, D);+	T_20_39(27, D, E, A, B, C);+	T_20_39(28, C, D, E, A, B);+	T_20_39(29, B, C, D, E, A);+	T_20_39(30, A, B, C, D, E);+	T_20_39(31, E, A, B, C, D);+	T_20_39(32, D, E, A, B, C);+	T_20_39(33, C, D, E, A, B);+	T_20_39(34, B, C, D, E, A);+	T_20_39(35, A, B, C, D, E);+	T_20_39(36, E, A, B, C, D);+	T_20_39(37, D, E, A, B, C);+	T_20_39(38, C, D, E, A, B);+	T_20_39(39, B, C, D, E, A);++	/* Round 3 */+	T_40_59(40, A, B, C, D, E);+	T_40_59(41, E, A, B, C, D);+	T_40_59(42, D, E, A, B, C);+	T_40_59(43, C, D, E, A, B);+	T_40_59(44, B, C, D, E, A);+	T_40_59(45, A, B, C, D, E);+	T_40_59(46, E, A, B, C, D);+	T_40_59(47, D, E, A, B, C);+	T_40_59(48, C, D, E, A, B);+	T_40_59(49, B, C, D, E, A);+	T_40_59(50, A, B, C, D, E);+	T_40_59(51, E, A, B, C, D);+	T_40_59(52, D, E, A, B, C);+	T_40_59(53, C, D, E, A, B);+	T_40_59(54, B, C, D, E, A);+	T_40_59(55, A, B, C, D, E);+	T_40_59(56, E, A, B, C, D);+	T_40_59(57, D, E, A, B, C);+	T_40_59(58, C, D, E, A, B);+	T_40_59(59, B, C, D, E, A);++	/* Round 4 */+	T_60_79(60, A, B, C, D, E);+	T_60_79(61, E, A, B, C, D);+	T_60_79(62, D, E, A, B, C);+	T_60_79(63, C, D, E, A, B);+	T_60_79(64, B, C, D, E, A);+	T_60_79(65, A, B, C, D, E);+	T_60_79(66, E, A, B, C, D);+	T_60_79(67, D, E, A, B, C);+	T_60_79(68, C, D, E, A, B);+	T_60_79(69, B, C, D, E, A);+	T_60_79(70, A, B, C, D, E);+	T_60_79(71, E, A, B, C, D);+	T_60_79(72, D, E, A, B, C);+	T_60_79(73, C, D, E, A, B);+	T_60_79(74, B, C, D, E, A);+	T_60_79(75, A, B, C, D, E);+	T_60_79(76, E, A, B, C, D);+	T_60_79(77, D, E, A, B, C);+	T_60_79(78, C, D, E, A, B);+	T_60_79(79, B, C, D, E, A);++	ctx->H[0] += A;+	ctx->H[1] += B;+	ctx->H[2] += C;+	ctx->H[3] += D;+	ctx->H[4] += E;+}++void git__blk_SHA1_Init(blk_SHA_CTX *ctx)+{+	ctx->size = 0;++	/* Initialize H with the magic constants (see FIPS180 for constants) */+	ctx->H[0] = 0x67452301;+	ctx->H[1] = 0xefcdab89;+	ctx->H[2] = 0x98badcfe;+	ctx->H[3] = 0x10325476;+	ctx->H[4] = 0xc3d2e1f0;+}++void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, size_t len)+{+	unsigned int lenW = ctx->size & 63;++	ctx->size += len;++	/* Read the data into W and process blocks as they get full */+	if (lenW) {+		unsigned int left = 64 - lenW;+		if (len < left)+			left = (unsigned int)len;+		memcpy(lenW + (char *)ctx->W, data, left);+		lenW = (lenW + left) & 63;+		len -= left;+		data = ((const char *)data + left);+		if (lenW)+			return;+		blk_SHA1_Block(ctx, ctx->W);+	}+	while (len >= 64) {+		blk_SHA1_Block(ctx, data);+		data = ((const char *)data + 64);+		len -= 64;+	}+	if (len)+		memcpy(ctx->W, data, len);+}++void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)+{+	static const unsigned char pad[64] = { 0x80 };+	unsigned int padlen[2];+	int i;++	/* Pad with a binary 1 (ie 0x80), then zeroes, then length */+	padlen[0] = htonl((uint32_t)(ctx->size >> 29));+	padlen[1] = htonl((uint32_t)(ctx->size << 3));++	i = ctx->size & 63;+	git__blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));+	git__blk_SHA1_Update(ctx, padlen, 8);++	/* Output hash */+	for (i = 0; i < 5; i++)+		put_be32(hashout + i*4, ctx->H[i]);+}
+ libgit2/src/sha1.h view
@@ -0,0 +1,21 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++typedef struct {+	unsigned long long size;+	unsigned int H[5];+	unsigned int W[16];+} blk_SHA_CTX;++void git__blk_SHA1_Init(blk_SHA_CTX *ctx);+void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, size_t len);+void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);++#define SHA_CTX		blk_SHA_CTX+#define SHA1_Init	git__blk_SHA1_Init+#define SHA1_Update	git__blk_SHA1_Update+#define SHA1_Final	git__blk_SHA1_Final
+ libgit2/src/sha1_lookup.c view
@@ -0,0 +1,178 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include <stdio.h>++#include "sha1_lookup.h"+#include "common.h"++/*+ * Conventional binary search loop looks like this:+ *+ *	unsigned lo, hi;+ *		do {+ *				unsigned mi = (lo + hi) / 2;+ *				int cmp = "entry pointed at by mi" minus "target";+ *				if (!cmp)+ *						return (mi is the wanted one)+ *				if (cmp > 0)+ *						hi = mi; "mi is larger than target"+ *				else+ *						lo = mi+1; "mi is smaller than target"+ *		} while (lo < hi);+ *+ * The invariants are:+ *+ * - When entering the loop, lo points at a slot that is never+ *	above the target (it could be at the target), hi points at a+ *	slot that is guaranteed to be above the target (it can never+ *	be at the target).+ *+ * - We find a point 'mi' between lo and hi (mi could be the same+ *	as lo, but never can be as same as hi), and check if it hits+ *	the target. There are three cases:+ *+ *	- if it is a hit, we are happy.+ *+ *	- if it is strictly higher than the target, we set it to hi,+ *		and repeat the search.+ *+ *	- if it is strictly lower than the target, we update lo to+ *		one slot after it, because we allow lo to be at the target.+ *+ *	If the loop exits, there is no matching entry.+ *+ * When choosing 'mi', we do not have to take the "middle" but+ * anywhere in between lo and hi, as long as lo <= mi < hi is+ * satisfied. When we somehow know that the distance between the+ * target and lo is much shorter than the target and hi, we could+ * pick mi that is much closer to lo than the midway.+ *+ * Now, we can take advantage of the fact that SHA-1 is a good hash+ * function, and as long as there are enough entries in the table, we+ * can expect uniform distribution. An entry that begins with for+ * example "deadbeef..." is much likely to appear much later than in+ * the midway of the table. It can reasonably be expected to be near+ * 87% (222/256) from the top of the table.+ *+ * However, we do not want to pick "mi" too precisely. If the entry at+ * the 87% in the above example turns out to be higher than the target+ * we are looking for, we would end up narrowing the search space down+ * only by 13%, instead of 50% we would get if we did a simple binary+ * search. So we would want to hedge our bets by being less aggressive.+ *+ * The table at "table" holds at least "nr" entries of "elem_size"+ * bytes each. Each entry has the SHA-1 key at "key_offset". The+ * table is sorted by the SHA-1 key of the entries. The caller wants+ * to find the entry with "key", and knows that the entry at "lo" is+ * not higher than the entry it is looking for, and that the entry at+ * "hi" is higher than the entry it is looking for.+ */+int sha1_entry_pos(const void *table,+			size_t elem_size,+			size_t key_offset,+			unsigned lo, unsigned hi, unsigned nr,+			const unsigned char *key)+{+	const unsigned char *base = (const unsigned char*)table;+	const unsigned char *hi_key, *lo_key;+	unsigned ofs_0;++	if (!nr || lo >= hi)+		return -1;++	if (nr == hi)+		hi_key = NULL;+	else+		hi_key = base + elem_size * hi + key_offset;+	lo_key = base + elem_size * lo + key_offset;++	ofs_0 = 0;+	do {+		int cmp;+		unsigned ofs, mi, range;+		unsigned lov, hiv, kyv;+		const unsigned char *mi_key;++		range = hi - lo;+		if (hi_key) {+			for (ofs = ofs_0; ofs < 20; ofs++)+				if (lo_key[ofs] != hi_key[ofs])+					break;+			ofs_0 = ofs;+			/*+			 * byte 0 thru (ofs-1) are the same between+			 * lo and hi; ofs is the first byte that is+			 * different.+			 */+			hiv = hi_key[ofs_0];+			if (ofs_0 < 19)+				hiv = (hiv << 8) | hi_key[ofs_0+1];+		} else {+			hiv = 256;+			if (ofs_0 < 19)+				hiv <<= 8;+		}+		lov = lo_key[ofs_0];+		kyv = key[ofs_0];+		if (ofs_0 < 19) {+			lov = (lov << 8) | lo_key[ofs_0+1];+			kyv = (kyv << 8) | key[ofs_0+1];+		}+		assert(lov < hiv);++		if (kyv < lov)+			return -1 - lo;+		if (hiv < kyv)+			return -1 - hi;++		/*+		 * Even if we know the target is much closer to 'hi'+		 * than 'lo', if we pick too precisely and overshoot+		 * (e.g. when we know 'mi' is closer to 'hi' than to+		 * 'lo', pick 'mi' that is higher than the target), we+		 * end up narrowing the search space by a smaller+		 * amount (i.e. the distance between 'mi' and 'hi')+		 * than what we would have (i.e. about half of 'lo'+		 * and 'hi'). Hedge our bets to pick 'mi' less+		 * aggressively, i.e. make 'mi' a bit closer to the+		 * middle than we would otherwise pick.+		 */+		kyv = (kyv * 6 + lov + hiv) / 8;+		if (lov < hiv - 1) {+			if (kyv == lov)+				kyv++;+			else if (kyv == hiv)+				kyv--;+		}+		mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;++#ifdef INDEX_DEBUG_LOOKUP+		printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);+		printf("ofs %u lov %x, hiv %x, kyv %x\n",+				ofs_0, lov, hiv, kyv);+#endif++		if (!(lo <= mi && mi < hi)) {+			giterr_set(GITERR_INVALID, "Assertion failure. Binary search invariant is false");+			return -1;+		}++		mi_key = base + elem_size * mi + key_offset;+		cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);+		if (!cmp)+			return mi;+		if (cmp > 0) {+			hi = mi;+			hi_key = mi_key;+		} else {+			lo = mi + 1;+			lo_key = mi_key + elem_size;+		}+	} while (lo < hi);+	return -((int)lo)-1;+}
+ libgit2/src/sha1_lookup.h view
@@ -0,0 +1,18 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_sha1_lookup_h__+#define INCLUDE_sha1_lookup_h__++#include <stdlib.h>++int sha1_entry_pos(const void *table,+			size_t elem_size,+			size_t key_offset,+			unsigned lo, unsigned hi, unsigned nr,+			const unsigned char *key);++#endif
+ libgit2/src/signature.c view
@@ -0,0 +1,336 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "signature.h"+#include "repository.h"+#include "git2/common.h"++void git_signature_free(git_signature *sig)+{+	if (sig == NULL)+		return;++	git__free(sig->name);+	sig->name = NULL;+	git__free(sig->email);+	sig->email = NULL;+	git__free(sig);+}++static const char *skip_leading_spaces(const char *buffer, const char *buffer_end)+{+	while (*buffer == ' ' && buffer < buffer_end)+		buffer++;++	return buffer;+}++static const char *skip_trailing_spaces(const char *buffer_start, const char *buffer_end)+{+	while (*buffer_end == ' ' && buffer_end > buffer_start)+		buffer_end--;++	return buffer_end;+}++static int signature_error(const char *msg)+{+	giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg);+	return -1;+}++static int process_trimming(const char *input, char **storage, const char *input_end, int fail_when_empty)+{+	const char *left, *right;+	size_t trimmed_input_length;++	assert(storage);++	left = skip_leading_spaces(input, input_end);+	right = skip_trailing_spaces(input, input_end - 1);++	if (right < left) {+		if (fail_when_empty)+			return signature_error("input is either empty of contains only spaces");++		right = left - 1;+	}++	trimmed_input_length = right - left + 1;++	*storage = git__malloc(trimmed_input_length + 1);+	GITERR_CHECK_ALLOC(*storage);++	memcpy(*storage, left, trimmed_input_length);+	(*storage)[trimmed_input_length] = 0;++	return 0;+}++int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)+{+	int error;+	git_signature *p = NULL;++	assert(name && email);++	*sig_out = NULL;++	p = git__calloc(1, sizeof(git_signature));+	GITERR_CHECK_ALLOC(p);++	if ((error = process_trimming(name, &p->name, name + strlen(name), 1)) < 0 ||+		(error = process_trimming(email, &p->email, email + strlen(email), 1)) < 0)+	{+		git_signature_free(p);+		return error;+	}++	p->when.time = time;+	p->when.offset = offset;++	*sig_out = p;++	return 0;+}++git_signature *git_signature_dup(const git_signature *sig)+{+	git_signature *new;+	if (git_signature_new(&new, sig->name, sig->email, sig->when.time, sig->when.offset) < 0)+		return NULL;+	return new;+}++int git_signature_now(git_signature **sig_out, const char *name, const char *email)+{+	time_t now;+	time_t offset;+	struct tm *utc_tm, *local_tm;+	git_signature *sig;++#ifndef GIT_WIN32+	struct tm _utc, _local;+#endif++	*sig_out = NULL;++	time(&now);++	/**+	 * On Win32, `gmtime_r` doesn't exist but+	 * `gmtime` is threadsafe, so we can use that+	 */+#ifdef GIT_WIN32+	utc_tm = gmtime(&now);+	local_tm = localtime(&now);+#else+	utc_tm = gmtime_r(&now, &_utc);+	local_tm = localtime_r(&now, &_local);+#endif++	offset = mktime(local_tm) - mktime(utc_tm);+	offset /= 60;++	/* mktime takes care of setting tm_isdst correctly */+	if (local_tm->tm_isdst)+		offset += 60;++	if (git_signature_new(&sig, name, email, now, (int)offset) < 0)+		return -1;++	*sig_out = sig;++	return 0;+}++static int timezone_error(const char *msg)+{+	giterr_set(GITERR_INVALID, "Failed to parse TZ offset - %s", msg);+	return -1;+}++static int parse_timezone_offset(const char *buffer, int *offset_out)+{+	int dec_offset;+	int mins, hours, offset;++	const char *offset_start;+	const char *offset_end;++	offset_start = buffer;++	if (*offset_start == '\n') {+		*offset_out = 0;+		return 0;+	}++	if (offset_start[0] != '-' && offset_start[0] != '+')+		return timezone_error("does not start with '+' or '-'");++	if (offset_start[1] < '0' || offset_start[1] > '9')+		return timezone_error("expected initial digit");++	if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < 0)+		return timezone_error("not a valid number");++	if (offset_end - offset_start != 5)+		return timezone_error("invalid length");++	if (dec_offset > 1400)+		return timezone_error("value too large");++	hours = dec_offset / 100;+	mins = dec_offset % 100;++	if (hours > 14)	// see http://www.worldtimezone.com/faq.html+		return timezone_error("hour value too large");++	if (mins > 59)+		return timezone_error("minutes value too large");++	offset = (hours * 60) + mins;++	if (offset_start[0] == '-')+		offset *= -1;++	*offset_out = offset;++	return 0;+}++static int process_next_token(const char **buffer_out, char **storage,+	const char *token_end, const char *right_boundary)+{+	int error = process_trimming(*buffer_out, storage, token_end, 0);+	if (error < 0)+		return error;++	*buffer_out = token_end + 1;++	if (*buffer_out > right_boundary)+		return signature_error("signature is too short");++	return 0;+}++static const char *scan_for_previous_token(const char *buffer, const char *left_boundary)+{+	const char *start;++	if (buffer <= left_boundary)+		return NULL;++	start = skip_trailing_spaces(left_boundary, buffer);++	/* Search for previous occurence of space */+	while (start[-1] != ' ' && start > left_boundary)+		start--;++	return start;+}++static int parse_time(git_time_t *time_out, const char *buffer)+{+	int time;+	int error;++	if (*buffer == '+' || *buffer == '-') {+		giterr_set(GITERR_INVALID, "Failed while parsing time. '%s' actually looks like a timezone offset.", buffer);+		return -1;+	}++	error = git__strtol32(&time, buffer, &buffer, 10);++	if (!error)+		*time_out = (git_time_t)time;++	return error;+}++int git_signature__parse(git_signature *sig, const char **buffer_out,+		const char *buffer_end, const char *header, char ender)+{+	const char *buffer = *buffer_out;+	const char *line_end, *name_end, *email_end, *tz_start, *time_start;+	int error = 0;++	memset(sig, 0x0, sizeof(git_signature));++	if ((line_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)+		return signature_error("no newline given");++	if (header) {+		const size_t header_len = strlen(header);++		if (memcmp(buffer, header, header_len) != 0)+			return signature_error("expected prefix doesn't match actual");++		buffer += header_len;+	}++	if (buffer > line_end)+		return signature_error("signature too short");++	if ((name_end = strchr(buffer, '<')) == NULL)+		return signature_error("character '<' not allowed in signature");++	if ((email_end = strchr(name_end, '>')) == NULL)+		return signature_error("character '>' not allowed in signature");++	if (email_end < name_end)+		return signature_error("malformed e-mail");++	error = process_next_token(&buffer, &sig->name, name_end, line_end);+	if (error < 0)+		return error;++	error = process_next_token(&buffer, &sig->email, email_end, line_end);+	if (error < 0)+		return error;++	tz_start = scan_for_previous_token(line_end - 1, buffer);++	if (tz_start == NULL)+		goto clean_exit;	/* No timezone nor date */++	time_start = scan_for_previous_token(tz_start - 1, buffer);+	if (time_start == NULL || parse_time(&sig->when.time, time_start) < 0) {+		/* The tz_start might point at the time */+		parse_time(&sig->when.time, tz_start);+		goto clean_exit;+	}++	if (parse_timezone_offset(tz_start, &sig->when.offset) < 0) {+		sig->when.time = 0; /* Bogus timezone, we reset the time */+	}++clean_exit:+	*buffer_out = line_end + 1;+	return 0;+}++void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig)+{+	int offset, hours, mins;+	char sign;++	offset = sig->when.offset;+	sign = (sig->when.offset < 0) ? '-' : '+';++	if (offset < 0)+		offset = -offset;++	hours = offset / 60;+	mins = offset % 60;++	git_buf_printf(buf, "%s%s <%s> %u %c%02d%02d\n",+			header ? header : "", sig->name, sig->email,+			(unsigned)sig->when.time, sign, hours, mins);+}+
+ libgit2/src/signature.h view
@@ -0,0 +1,18 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_signature_h__+#define INCLUDE_signature_h__++#include "git2/common.h"+#include "git2/signature.h"+#include "repository.h"+#include <time.h>++int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender);+void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig);++#endif
+ libgit2/src/status.c view
@@ -0,0 +1,242 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "git2.h"+#include "fileops.h"+#include "hash.h"+#include "vector.h"+#include "tree.h"+#include "git2/status.h"+#include "repository.h"+#include "ignore.h"++#include "git2/diff.h"+#include "diff.h"++static unsigned int index_delta2status(git_delta_t index_status)+{+	unsigned int st = GIT_STATUS_CURRENT;++	switch (index_status) {+	case GIT_DELTA_ADDED:+	case GIT_DELTA_COPIED:+	case GIT_DELTA_RENAMED:+		st = GIT_STATUS_INDEX_NEW;+		break;+	case GIT_DELTA_DELETED:+		st = GIT_STATUS_INDEX_DELETED;+		break;+	case GIT_DELTA_MODIFIED:+		st = GIT_STATUS_INDEX_MODIFIED;+		break;+	default:+		break;+	}++	return st;+}++static unsigned int workdir_delta2status(git_delta_t workdir_status)+{+	unsigned int st = GIT_STATUS_CURRENT;++	switch (workdir_status) {+	case GIT_DELTA_ADDED:+	case GIT_DELTA_COPIED:+	case GIT_DELTA_RENAMED:+	case GIT_DELTA_UNTRACKED:+		st = GIT_STATUS_WT_NEW;+		break;+	case GIT_DELTA_DELETED:+		st = GIT_STATUS_WT_DELETED;+		break;+	case GIT_DELTA_MODIFIED:+		st = GIT_STATUS_WT_MODIFIED;+		break;+	case GIT_DELTA_IGNORED:+		st = GIT_STATUS_IGNORED;+		break;+	default:+		break;+	}++	return st;+}++int git_status_foreach_ext(+	git_repository *repo,+	const git_status_options *opts,+	int (*cb)(const char *, unsigned int, void *),+	void *cbdata)+{+	int err = 0, cmp;+	git_diff_options diffopt;+	git_diff_list *idx2head = NULL, *wd2idx = NULL;+	git_tree *head = NULL;+	git_status_show_t show =+		opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;+	git_diff_delta *i2h, *w2i;+	unsigned int i, j, i_max, j_max;++	assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);++	if ((err = git_repository_head_tree(&head, repo)) < 0)+		return err;++	memset(&diffopt, 0, sizeof(diffopt));+	memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));++	if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)+		diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;+	if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)+		diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;+	if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)+		diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;+	if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)+		diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;+	/* TODO: support EXCLUDE_SUBMODULES flag */++	if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&+		(err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)+		goto cleanup;++	if (show != GIT_STATUS_SHOW_INDEX_ONLY &&+		(err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)+		goto cleanup;++	if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {+		for (i = 0; !err && i < idx2head->deltas.length; i++) {+			i2h = GIT_VECTOR_GET(&idx2head->deltas, i);+			err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);+		}+		git_diff_list_free(idx2head);+		idx2head = NULL;+	}++	i_max = idx2head ? idx2head->deltas.length : 0;+	j_max = wd2idx   ? wd2idx->deltas.length   : 0;++	for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {+		i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;+		w2i = wd2idx   ? GIT_VECTOR_GET(&wd2idx->deltas,j)   : NULL;++		cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path);++		if (cmp < 0) {+			err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);+			i++;+		} else if (cmp > 0) {+			err = cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata);+			j++;+		} else {+			err = cb(i2h->old_file.path, index_delta2status(i2h->status) |+					 workdir_delta2status(w2i->status), cbdata);+			i++; j++;+		}+	}++cleanup:+	git_tree_free(head);+	git_diff_list_free(idx2head);+	git_diff_list_free(wd2idx);+	return err;+}++int git_status_foreach(+	git_repository *repo,+	int (*callback)(const char *, unsigned int, void *),+	void *payload)+{+	git_status_options opts;++	memset(&opts, 0, sizeof(opts));+	opts.show  = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;+	opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |+		GIT_STATUS_OPT_INCLUDE_UNTRACKED |+		GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;++	return git_status_foreach_ext(repo, &opts, callback, payload);+}++struct status_file_info {+	unsigned int count;+	unsigned int status;+	char *expected;+};++static int get_one_status(const char *path, unsigned int status, void *data)+{+	struct status_file_info *sfi = data;++	sfi->count++;+	sfi->status = status;++	if (sfi->count > 1 || strcmp(sfi->expected, path) != 0) {+		giterr_set(GITERR_INVALID,+			"Ambiguous path '%s' given to git_status_file", sfi->expected);+		return -1;+	}++	return 0;+}++int git_status_file(+	unsigned int *status_flags,+	git_repository *repo,+	const char *path)+{+	int error;+	git_status_options opts;+	struct status_file_info sfi;++	assert(status_flags && repo && path);++	memset(&sfi, 0, sizeof(sfi));+	if ((sfi.expected = git__strdup(path)) == NULL)+		return -1;++	memset(&opts, 0, sizeof(opts));+	opts.show  = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;+	opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |+		GIT_STATUS_OPT_INCLUDE_UNTRACKED |+		GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |+		GIT_STATUS_OPT_INCLUDE_UNMODIFIED;+	opts.pathspec.count = 1;+	opts.pathspec.strings = &sfi.expected;++	error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);++	if (!error && !sfi.count) {+		giterr_set(GITERR_INVALID,+			"Attempt to get status of nonexistent file '%s'", path);+		error = GIT_ENOTFOUND;+	}++	*status_flags = sfi.status;++	git__free(sfi.expected);++	return error;+}++int git_status_should_ignore(+	int *ignored,+	git_repository *repo,+	const char *path)+{+	int error;+	git_ignores ignores;++	if (git_ignore__for_path(repo, path, &ignores) < 0)+		return -1;++	error = git_ignore__lookup(&ignores, path, ignored);+	git_ignore__free(&ignores);+	return error;+}+
+ libgit2/src/strmap.h view
@@ -0,0 +1,64 @@+/*+ * Copyright (C) 2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_strmap_h__+#define INCLUDE_strmap_h__++#include "common.h"++#define kmalloc git__malloc+#define kcalloc git__calloc+#define krealloc git__realloc+#define kfree git__free+#include "khash.h"++__KHASH_TYPE(str, const char *, void *);+typedef khash_t(str) git_strmap;++#define GIT__USE_STRMAP \+	__KHASH_IMPL(str, static inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)++#define git_strmap_alloc()  kh_init(str)+#define git_strmap_free(h)  kh_destroy(str, h), h = NULL+#define git_strmap_clear(h) kh_clear(str, h)++#define git_strmap_num_entries(h) kh_size(h)++#define git_strmap_lookup_index(h, k)  kh_get(str, h, k)+#define git_strmap_valid_index(h, idx) (idx != kh_end(h))++#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))++#define git_strmap_value_at(h, idx)        kh_val(h, idx)+#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v+#define git_strmap_delete_at(h, idx)       kh_del(str, h, idx)++#define git_strmap_insert(h, key, val, rval) do { \+	khiter_t __pos = kh_put(str, h, key, &rval); \+	if (rval >= 0) { \+		if (rval == 0) kh_key(h, __pos) = key; \+		kh_val(h, __pos) = val; \+	} } while (0)++#define git_strmap_insert2(h, key, val, oldv, rval) do { \+	khiter_t __pos = kh_put(str, h, key, &rval); \+	if (rval >= 0) { \+		if (rval == 0) { \+			oldv = kh_val(h, __pos); \+			kh_key(h, __pos) = key; \+		} else { oldv = NULL; } \+		kh_val(h, __pos) = val; \+	} } while (0)++#define git_strmap_delete(h, key) do { \+	khiter_t __pos = git_strmap_lookup_index(h, key); \+	if (git_strmap_valid_index(h, __pos)) \+		git_strmap_delete_at(h, __pos); } while (0)++#define git_strmap_foreach		kh_foreach+#define git_strmap_foreach_value	kh_foreach_value++#endif
+ libgit2/src/submodule.c view
@@ -0,0 +1,387 @@+/*+ * Copyright (C) 2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "git2/config.h"+#include "git2/types.h"+#include "git2/repository.h"+#include "git2/index.h"+#include "git2/submodule.h"+#include "buffer.h"+#include "vector.h"+#include "posix.h"+#include "config_file.h"+#include "config.h"+#include "repository.h"++static git_cvar_map _sm_update_map[] = {+	{GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},+	{GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},+	{GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE}+};++static git_cvar_map _sm_ignore_map[] = {+	{GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},+	{GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},+	{GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},+	{GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}+};++static inline khint_t str_hash_no_trailing_slash(const char *s)+{+	khint_t h;++	for (h = 0; *s; ++s)+		if (s[1] || *s != '/')+			h = (h << 5) - h + *s;++	return h;+}++static inline int str_equal_no_trailing_slash(const char *a, const char *b)+{+	size_t alen = a ? strlen(a) : 0;+	size_t blen = b ? strlen(b) : 0;++	if (alen && a[alen] == '/')+		alen--;+	if (blen && b[blen] == '/')+		blen--;++	return (alen == blen && strncmp(a, b, alen) == 0);+}++__KHASH_IMPL(str, static inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash);++static git_submodule *submodule_alloc(const char *name)+{+	git_submodule *sm = git__calloc(1, sizeof(git_submodule));+	if (sm == NULL)+		return sm;++	sm->path = sm->name = git__strdup(name);+	if (!sm->name) {+		git__free(sm);+		return NULL;+	}++	return sm;+}++static void submodule_release(git_submodule *sm, int decr)+{+	if (!sm)+		return;++	sm->refcount -= decr;++	if (sm->refcount == 0) {+		if (sm->name != sm->path)+			git__free(sm->path);+		git__free(sm->name);+		git__free(sm->url);+		git__free(sm);+	}+}++static int submodule_from_entry(+	git_strmap *smcfg, git_index_entry *entry)+{+	git_submodule *sm;+	void *old_sm;+	khiter_t pos;+	int error;++	pos = git_strmap_lookup_index(smcfg, entry->path);++	if (git_strmap_valid_index(smcfg, pos))+		sm = git_strmap_value_at(smcfg, pos);+	else+		sm = submodule_alloc(entry->path);++	git_oid_cpy(&sm->oid, &entry->oid);++	if (strcmp(sm->path, entry->path) != 0) {+		if (sm->path != sm->name) {+			git__free(sm->path);+			sm->path = sm->name;+		}+		sm->path = git__strdup(entry->path);+		if (!sm->path)+			goto fail;+	}++	git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);+	if (error < 0)+		goto fail;+	sm->refcount++;++	if (old_sm && ((git_submodule *)old_sm) != sm) {+		/* TODO: log warning about multiple entrys for same submodule path */+		submodule_release(old_sm, 1);+	}++	return 0;++fail:+	submodule_release(sm, 0);+	return -1;+}++static int submodule_from_config(+	const char *key, const char *value, void *data)+{+	git_strmap *smcfg = data;+	const char *namestart;+	const char *property;+	git_buf name = GIT_BUF_INIT;+	git_submodule *sm;+	void *old_sm = NULL;+	bool is_path;+	khiter_t pos;+	int error;++	if (git__prefixcmp(key, "submodule.") != 0)+		return 0;++	namestart = key + strlen("submodule.");+	property  = strrchr(namestart, '.');+	if (property == NULL)+		return 0;+	property++;+	is_path = (strcmp(property, "path") == 0);++	if (git_buf_set(&name, namestart, property - namestart - 1) < 0)+		return -1;++	pos = git_strmap_lookup_index(smcfg, name.ptr);+	if (!git_strmap_valid_index(smcfg, pos) && is_path)+		pos = git_strmap_lookup_index(smcfg, value);+	if (!git_strmap_valid_index(smcfg, pos))+		sm = submodule_alloc(name.ptr);+	else+		sm = git_strmap_value_at(smcfg, pos);+	if (!sm)+		goto fail;++	if (strcmp(sm->name, name.ptr) != 0) {+		assert(sm->path == sm->name);+		sm->name = git_buf_detach(&name);++		git_strmap_insert2(smcfg, sm->name, sm, old_sm, error);+		if (error < 0)+			goto fail;+		sm->refcount++;+	}+	else if (is_path && strcmp(sm->path, value) != 0) {+		assert(sm->path == sm->name);+		sm->path = git__strdup(value);+		if (sm->path == NULL)+			goto fail;++		git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);+		if (error < 0)+			goto fail;+		sm->refcount++;+	}+	git_buf_free(&name);++	if (old_sm && ((git_submodule *)old_sm) != sm) {+		/* TODO: log warning about multiple submodules with same path */+		submodule_release(old_sm, 1);+	}++	if (is_path)+		return 0;++	/* copy other properties into submodule entry */+	if (strcmp(property, "url") == 0) {+		if (sm->url) {+			git__free(sm->url);+			sm->url = NULL;+		}+		if ((sm->url = git__strdup(value)) == NULL)+			goto fail;+	}+	else if (strcmp(property, "update") == 0) {+		int val;+		if (git_config_lookup_map_value(+			_sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0) {+			giterr_set(GITERR_INVALID,+				"Invalid value for submodule update property: '%s'", value);+			goto fail;+		}+		sm->update = (git_submodule_update_t)val;+	}+	else if (strcmp(property, "fetchRecurseSubmodules") == 0) {+		if (git__parse_bool(&sm->fetch_recurse, value) < 0) {+			giterr_set(GITERR_INVALID,+				"Invalid value for submodule 'fetchRecurseSubmodules' property: '%s'", value);+			goto fail;+		}+	}+	else if (strcmp(property, "ignore") == 0) {+		int val;+		if (git_config_lookup_map_value(+			_sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0) {+			giterr_set(GITERR_INVALID,+				"Invalid value for submodule ignore property: '%s'", value);+			goto fail;+		}+		sm->ignore = (git_submodule_ignore_t)val;+	}+	/* ignore other unknown submodule properties */++	return 0;++fail:+	submodule_release(sm, 0);+	git_buf_free(&name);+	return -1;+}++static int load_submodule_config(git_repository *repo)+{+	int error;+	git_index *index;+	unsigned int i, max_i;+	git_oid gitmodules_oid;+	git_strmap *smcfg;+	struct git_config_file *mods = NULL;++	if (repo->submodules)+		return 0;++	/* submodule data is kept in a hashtable with each submodule stored+	 * under both its name and its path.  These are usually the same, but+	 * that is not guaranteed.+	 */+	smcfg = git_strmap_alloc();+	GITERR_CHECK_ALLOC(smcfg);++	/* scan index for gitmodules (and .gitmodules entry) */+	if ((error = git_repository_index__weakptr(&index, repo)) < 0)+		goto cleanup;+	memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));+	max_i = git_index_entrycount(index);++	for (i = 0; i < max_i; i++) {+		git_index_entry *entry = git_index_get(index, i);+		if (S_ISGITLINK(entry->mode)) {+			if ((error = submodule_from_entry(smcfg, entry)) < 0)+				goto cleanup;+		}+		else if (strcmp(entry->path, ".gitmodules") == 0)+			git_oid_cpy(&gitmodules_oid, &entry->oid);+	}++	/* load .gitmodules from workdir if it exists */+	if (git_repository_workdir(repo) != NULL) {+		/* look in workdir for .gitmodules */+		git_buf path = GIT_BUF_INIT;+		if (!git_buf_joinpath(+				&path, git_repository_workdir(repo), ".gitmodules") &&+			git_path_isfile(path.ptr))+		{+			if (!(error = git_config_file__ondisk(&mods, path.ptr)))+				error = git_config_file_open(mods);+		}+		git_buf_free(&path);+	}++	/* load .gitmodules from object cache if not in workdir */+	if (!error && mods == NULL && !git_oid_iszero(&gitmodules_oid)) {+		/* TODO: is it worth loading gitmodules from object cache? */+	}++	/* process .gitmodules info */+	if (!error && mods != NULL)+		error = git_config_file_foreach(mods, submodule_from_config, smcfg);++	/* store submodule config in repo */+	if (!error)+		repo->submodules = smcfg;++cleanup:+	if (mods != NULL)+		git_config_file_free(mods);+	if (error)+		git_strmap_free(smcfg);+	return error;+}++void git_submodule_config_free(git_repository *repo)+{+	git_strmap *smcfg = repo->submodules;+	git_submodule *sm;++	repo->submodules = NULL;++	if (smcfg == NULL)+		return;++	git_strmap_foreach_value(smcfg, sm, {+		submodule_release(sm,1);+	});+	git_strmap_free(smcfg);+}++static int submodule_cmp(const void *a, const void *b)+{+	return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);+}++int git_submodule_foreach(+	git_repository *repo,+	int (*callback)(const char *name, void *payload),+	void *payload)+{+	int error;+	git_submodule *sm;+	git_vector seen = GIT_VECTOR_INIT;+	seen._cmp = submodule_cmp;++	if ((error = load_submodule_config(repo)) < 0)+		return error;++	git_strmap_foreach_value(repo->submodules, sm, {+		/* usually the following will not come into play */+		if (sm->refcount > 1) {+			if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)+				continue;+			if ((error = git_vector_insert(&seen, sm)) < 0)+				break;+		}++		if ((error = callback(sm->name, payload)) < 0)+			break;+	});++	git_vector_free(&seen);++	return error;+}++int git_submodule_lookup(+	git_submodule **sm_ptr, /* NULL allowed if user only wants to test */+	git_repository *repo,+	const char *name)       /* trailing slash is allowed */+{+	khiter_t pos;++	if (load_submodule_config(repo) < 0)+		return -1;++	pos = git_strmap_lookup_index(repo->submodules, name);+	if (!git_strmap_valid_index(repo->submodules, pos))+		return GIT_ENOTFOUND;++	if (sm_ptr)+		*sm_ptr = git_strmap_value_at(repo->submodules, pos);++	return 0;+}
+ libgit2/src/tag.c view
@@ -0,0 +1,471 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "commit.h"+#include "tag.h"+#include "signature.h"+#include "message.h"+#include "git2/object.h"+#include "git2/repository.h"+#include "git2/signature.h"++void git_tag__free(git_tag *tag)+{+	git_signature_free(tag->tagger);+	git__free(tag->message);+	git__free(tag->tag_name);+	git__free(tag);+}++const git_oid *git_tag_id(git_tag *c)+{+	return git_object_id((git_object *)c);+}++int git_tag_target(git_object **target, git_tag *t)+{+	assert(t);+	return git_object_lookup(target, t->object.repo, &t->target, t->type);+}++const git_oid *git_tag_target_oid(git_tag *t)+{+	assert(t);+	return &t->target;+}++git_otype git_tag_type(git_tag *t)+{+	assert(t);+	return t->type;+}++const char *git_tag_name(git_tag *t)+{+	assert(t);+	return t->tag_name;+}++const git_signature *git_tag_tagger(git_tag *t)+{+	return t->tagger;+}++const char *git_tag_message(git_tag *t)+{+	assert(t);+	return t->message;+}++static int tag_error(const char *str)+{+	giterr_set(GITERR_TAG, "Failed to parse tag. %s", str);+	return -1;+}++int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)+{+	static const char *tag_types[] = {+		NULL, "commit\n", "tree\n", "blob\n", "tag\n"+	};++	unsigned int i;+	size_t text_len;+	char *search;++	const char *buffer_end = buffer + length;++	if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)+		return tag_error("Object field invalid");++	if (buffer + 5 >= buffer_end)+		return tag_error("Object too short");++	if (memcmp(buffer, "type ", 5) != 0)+		return tag_error("Type field not found");+	buffer += 5;++	tag->type = GIT_OBJ_BAD;++	for (i = 1; i < ARRAY_SIZE(tag_types); ++i) {+		size_t type_length = strlen(tag_types[i]);++		if (buffer + type_length >= buffer_end)+			return tag_error("Object too short");++		if (memcmp(buffer, tag_types[i], type_length) == 0) {+			tag->type = i;+			buffer += type_length;+			break;+		}+	}++	if (tag->type == GIT_OBJ_BAD)+		return tag_error("Invalid object type");++	if (buffer + 4 >= buffer_end)+		return tag_error("Object too short");++	if (memcmp(buffer, "tag ", 4) != 0)+		return tag_error("Tag field not found");++	buffer += 4;++	search = memchr(buffer, '\n', buffer_end - buffer);+	if (search == NULL)+		return tag_error("Object too short");++	text_len = search - buffer;++	tag->tag_name = git__malloc(text_len + 1);+	GITERR_CHECK_ALLOC(tag->tag_name);++	memcpy(tag->tag_name, buffer, text_len);+	tag->tag_name[text_len] = '\0';++	buffer = search + 1;++	tag->tagger = NULL;+	if (*buffer != '\n') {+		tag->tagger = git__malloc(sizeof(git_signature));+		GITERR_CHECK_ALLOC(tag->tagger);++		if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)+			return -1;+	}++	if( *buffer != '\n' )+		return tag_error("No new line before message");++	text_len = buffer_end - ++buffer;++	tag->message = git__malloc(text_len + 1);+	GITERR_CHECK_ALLOC(tag->message);++	memcpy(tag->message, buffer, text_len);+	tag->message[text_len] = '\0';++	return 0;+}++static int retrieve_tag_reference(+	git_reference **tag_reference_out,+	git_buf *ref_name_out,+	git_repository *repo,+	const char *tag_name)+{+	git_reference *tag_ref;+	int error;++	*tag_reference_out = NULL;++	if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)+		return -1;++	error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);+	if (error < 0)+		return error; /* Be it not foundo or corrupted */++	*tag_reference_out = tag_ref;++	return 0;+}++static int retrieve_tag_reference_oid(+	git_oid *oid,+	git_buf *ref_name_out,+	git_repository *repo,+	const char *tag_name)+{+	if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)+		return -1;++	return git_reference_name_to_oid(oid, repo, ref_name_out->ptr);+}++static int write_tag_annotation(+		git_oid *oid,+		git_repository *repo,+		const char *tag_name,+		const git_object *target,+		const git_signature *tagger,+		const char *message)+{+	git_buf tag = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;+	git_odb *odb;++	git_oid__writebuf(&tag, "object ", git_object_id(target));+	git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));+	git_buf_printf(&tag, "tag %s\n", tag_name);+	git_signature__writebuf(&tag, "tagger ", tagger);+	git_buf_putc(&tag, '\n');++	/* Remove comments by default */+	if (git_message_prettify(&cleaned_message, message, 1) < 0)+		goto on_error;++	if (git_buf_puts(&tag, git_buf_cstr(&cleaned_message)) < 0)+		goto on_error;++	git_buf_free(&cleaned_message);++	if (git_repository_odb__weakptr(&odb, repo) < 0)+		goto on_error;++	if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG) < 0)+		goto on_error;++	git_buf_free(&tag);+	return 0;++on_error:+	git_buf_free(&tag);+	git_buf_free(&cleaned_message);+	giterr_set(GITERR_OBJECT, "Failed to create tag annotation.");+	return -1;+}++static int git_tag_create__internal(+		git_oid *oid,+		git_repository *repo,+		const char *tag_name,+		const git_object *target,+		const git_signature *tagger,+		const char *message,+		int allow_ref_overwrite,+		int create_tag_annotation)+{+	git_reference *new_ref = NULL;+	git_buf ref_name = GIT_BUF_INIT;++	int error;++	assert(repo && tag_name && target);+	assert(!create_tag_annotation || (tagger && message));++	if (git_object_owner(target) != repo) {+		giterr_set(GITERR_INVALID, "The given target does not belong to this repository");+		return -1;+	}++	error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);+	if (error < 0 && error != GIT_ENOTFOUND)+		return -1;++	/** Ensure the tag name doesn't conflict with an already existing+	 *	reference unless overwriting has explictly been requested **/+	if (error == 0 && !allow_ref_overwrite) {+		git_buf_free(&ref_name);+		giterr_set(GITERR_TAG, "Tag already exists");+		return GIT_EEXISTS;+	}++	if (create_tag_annotation) {+		if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)+			return -1;+	} else+		git_oid_cpy(oid, git_object_id(target));++	error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);++	git_reference_free(new_ref);+	git_buf_free(&ref_name);+	return error;+}++int git_tag_create(+		git_oid *oid,+		git_repository *repo,+		const char *tag_name,+		const git_object *target,+		const git_signature *tagger,+		const char *message,+		int allow_ref_overwrite)+{+	return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);+}++int git_tag_create_lightweight(+		git_oid *oid,+		git_repository *repo,+		const char *tag_name,+		const git_object *target,+		int allow_ref_overwrite)+{+	return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);+}++int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)+{+	git_tag tag;+	int error;+	git_odb *odb;+	git_odb_stream *stream;+	git_odb_object *target_obj;++	git_reference *new_ref = NULL;+	git_buf ref_name = GIT_BUF_INIT;++	assert(oid && buffer);++	memset(&tag, 0, sizeof(tag));++	if (git_repository_odb__weakptr(&odb, repo) < 0)+		return -1;++	/* validate the buffer */+	if (git_tag__parse_buffer(&tag, buffer, strlen(buffer)) < 0)+		return -1;++	/* validate the target */+	if (git_odb_read(&target_obj, odb, &tag.target) < 0)+		goto on_error;++	if (tag.type != target_obj->raw.type) {+		giterr_set(GITERR_TAG, "The type for the given target is invalid");+		goto on_error;+	}++	error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);+	if (error < 0 && error != GIT_ENOTFOUND)+		goto on_error;++	/* We don't need these objects after this */+	git_signature_free(tag.tagger);+	git__free(tag.tag_name);+	git__free(tag.message);+	git_odb_object_free(target_obj);++	/** Ensure the tag name doesn't conflict with an already existing+	 *	reference unless overwriting has explictly been requested **/+	if (error == 0 && !allow_ref_overwrite) {+		giterr_set(GITERR_TAG, "Tag already exists");+		return GIT_EEXISTS;+	}++	/* write the buffer */+	if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)+		return -1;++	stream->write(stream, buffer, strlen(buffer));++	error = stream->finalize_write(oid, stream);+	stream->free(stream);++	if (error < 0) {+		git_buf_free(&ref_name);+		return -1;+	}++	error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);++	git_reference_free(new_ref);+	git_buf_free(&ref_name);++	return error;++on_error:+	git_signature_free(tag.tagger);+	git__free(tag.tag_name);+	git__free(tag.message);+	git_odb_object_free(target_obj);+	return -1;+}++int git_tag_delete(git_repository *repo, const char *tag_name)+{+	int error;+	git_reference *tag_ref;+	git_buf ref_name = GIT_BUF_INIT;++	error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);++	git_buf_free(&ref_name);++	if (error < 0)+		return -1;++	return git_reference_delete(tag_ref);+}++int git_tag__parse(git_tag *tag, git_odb_object *obj)+{+	assert(tag);+	return git_tag__parse_buffer(tag, obj->raw.data, obj->raw.len);+}++typedef struct {+ git_vector *taglist;+ const char *pattern;+} tag_filter_data;++#define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)++static int tag_list_cb(const char *tag_name, void *payload)+{+	tag_filter_data *filter;++	if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) != 0)+		return 0;++	filter = (tag_filter_data *)payload;+	if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)+		return git_vector_insert(filter->taglist, git__strdup(tag_name));++	return 0;+}++int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)+{+	int error;+	tag_filter_data filter;+	git_vector taglist;++	assert(tag_names && repo && pattern);++	if (git_vector_init(&taglist, 8, NULL) < 0)+		return -1;++	filter.taglist = &taglist;+	filter.pattern = pattern;++	error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&filter);+	if (error < 0) {+		git_vector_free(&taglist);+		return -1;+	}++	tag_names->strings = (char **)taglist.contents;+	tag_names->count = taglist.length;+	return 0;+}++int git_tag_list(git_strarray *tag_names, git_repository *repo)+{+	return git_tag_list_match(tag_names, "", repo);+}++int git_tag_peel(git_object **tag_target, git_tag *tag)+{+	int error;+	git_object *target;++	assert(tag_target && tag);++	if (git_tag_target(&target, tag) < 0)+		return -1;++	if (git_object_type(target) == GIT_OBJ_TAG) {+		error = git_tag_peel(tag_target, (git_tag *)target);+		git_object_free(target);+		return error;+	}++	*tag_target = target;+	return 0;+}
+ libgit2/src/tag.h view
@@ -0,0 +1,29 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_tag_h__+#define INCLUDE_tag_h__++#include "git2/tag.h"+#include "repository.h"+#include "odb.h"++struct git_tag {+	git_object object;++	git_oid target;+	git_otype type;++	char *tag_name;+	git_signature *tagger;+	char *message;+};++void git_tag__free(git_tag *tag);+int git_tag__parse(git_tag *tag, git_odb_object *obj);+int git_tag__parse_buffer(git_tag *tag, const char *data, size_t len);++#endif
+ libgit2/src/thread-utils.c view
@@ -0,0 +1,55 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "thread-utils.h"++#ifdef _WIN32+#	define WIN32_LEAN_AND_MEAN+#	include <windows.h>+#elif defined(hpux) || defined(__hpux) || defined(_hpux)+#	include <sys/pstat.h>+#endif++/*+ * By doing this in two steps we can at least get+ * the function to be somewhat coherent, even+ * with this disgusting nest of #ifdefs.+ */+#ifndef _SC_NPROCESSORS_ONLN+#	ifdef _SC_NPROC_ONLN+#		define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN+#	elif defined _SC_CRAY_NCPU+#		define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU+#	endif+#endif++int git_online_cpus(void)+{+#ifdef _SC_NPROCESSORS_ONLN+	long ncpus;+#endif++#ifdef _WIN32+	SYSTEM_INFO info;+	GetSystemInfo(&info);++	if ((int)info.dwNumberOfProcessors > 0)+		return (int)info.dwNumberOfProcessors;+#elif defined(hpux) || defined(__hpux) || defined(_hpux)+	struct pst_dynamic psd;++	if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))+		return (int)psd.psd_proc_cnt;+#endif++#ifdef _SC_NPROCESSORS_ONLN+	if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)+		return (int)ncpus;+#endif++	return 1;+}
+ libgit2/src/thread-utils.h view
@@ -0,0 +1,108 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_thread_utils_h__+#define INCLUDE_thread_utils_h__++#include "common.h"++/* Common operations even if threading has been disabled */+typedef struct {+#if defined(GIT_WIN32)+	volatile long val;+#else+	volatile int val;+#endif+} git_atomic;++GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)+{+	a->val = val;+}++#ifdef GIT_THREADS++#define git_thread pthread_t+#define git_thread_create(thread, attr, start_routine, arg) pthread_create(thread, attr, start_routine, arg)+#define git_thread_kill(thread) pthread_cancel(thread)+#define git_thread_exit(status)	pthread_exit(status)+#define git_thread_join(id, status) pthread_join(id, status)++/* Pthreads Mutex */+#define git_mutex pthread_mutex_t+#define git_mutex_init(a)	pthread_mutex_init(a, NULL)+#define git_mutex_lock(a)	pthread_mutex_lock(a)+#define git_mutex_unlock(a) pthread_mutex_unlock(a)+#define git_mutex_free(a)	pthread_mutex_destroy(a)++/* Pthreads condition vars -- disabled by now */+#define git_cond unsigned int //pthread_cond_t+#define git_cond_init(c, a)	(void)0		//pthread_cond_init(c, a)+#define git_cond_free(c) (void)0		//pthread_cond_destroy(c)+#define git_cond_wait(c, l)	(void)0		//pthread_cond_wait(c, l)+#define git_cond_signal(c)	(void)0		//pthread_cond_signal(c)+#define git_cond_broadcast(c) (void)0	//pthread_cond_broadcast(c)++GIT_INLINE(int) git_atomic_inc(git_atomic *a)+{+#if defined(GIT_WIN32)+	return InterlockedIncrement(&a->val);+#elif defined(__GNUC__)+	return __sync_add_and_fetch(&a->val, 1);+#else+#	error "Unsupported architecture for atomic operations"+#endif+}++GIT_INLINE(int) git_atomic_dec(git_atomic *a)+{+#if defined(GIT_WIN32)+	return InterlockedDecrement(&a->val);+#elif defined(__GNUC__)+	return __sync_sub_and_fetch(&a->val, 1);+#else+#	error "Unsupported architecture for atomic operations"+#endif+}++#else++#define git_thread unsigned int+#define git_thread_create(thread, attr, start_routine, arg) (void)0+#define git_thread_kill(thread) (void)0+#define git_thread_exit(status) (void)0+#define git_thread_join(id, status) (void)0++/* Pthreads Mutex */+#define git_mutex unsigned int+#define git_mutex_init(a) (void)0+#define git_mutex_lock(a) (void)0+#define git_mutex_unlock(a) (void)0+#define git_mutex_free(a) (void)0++/* Pthreads condition vars */+#define git_cond unsigned int+#define git_cond_init(c, a)	(void)0+#define git_cond_free(c) (void)0+#define git_cond_wait(c, l)	(void)0+#define git_cond_signal(c) (void)0+#define git_cond_broadcast(c) (void)0++GIT_INLINE(int) git_atomic_inc(git_atomic *a)+{+	return ++a->val;+}++GIT_INLINE(int) git_atomic_dec(git_atomic *a)+{+	return --a->val;+}++#endif++extern int git_online_cpus(void);++#endif /* INCLUDE_thread_utils_h__ */
+ libgit2/src/transport.c view
@@ -0,0 +1,97 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "git2/types.h"+#include "git2/remote.h"+#include "git2/net.h"+#include "transport.h"+#include "path.h"++static struct {+	char *prefix;+	git_transport_cb fn;+} transports[] = {+	{"git://", git_transport_git},+	{"http://", git_transport_http},+	{"https://", git_transport_dummy},+	{"file://", git_transport_local},+	{"git+ssh://", git_transport_dummy},+	{"ssh+git://", git_transport_dummy},+	{NULL, 0}+};++#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1++static git_transport_cb transport_find_fn(const char *url)+{+	size_t i = 0;++	// First, check to see if it's an obvious URL, which a URL scheme+	for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {+		if (!strncasecmp(url, transports[i].prefix, strlen(transports[i].prefix)))+			return transports[i].fn;+	}++	/* still here? Check to see if the path points to a file on the local file system */+	if ((git_path_exists(url) == 0) && git_path_isdir(url))+		return &git_transport_local;++	/* It could be a SSH remote path. Check to see if there's a : */+	if (strrchr(url, ':'))+		return &git_transport_dummy;	/* SSH is an unsupported transport mechanism in this version of libgit2 */++	return NULL;+}++/**************+ * Public API *+ **************/++int git_transport_dummy(git_transport **transport)+{+	GIT_UNUSED(transport);+	giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");+	return -1;+}++int git_transport_new(git_transport **out, const char *url)+{+	git_transport_cb fn;+	git_transport *transport;+	int error;++	fn = transport_find_fn(url);++	if (fn == NULL) {+		giterr_set(GITERR_NET, "Unsupported URL protocol");+		return -1;+	}++	error = fn(&transport);+	if (error < 0)+		return error;++	transport->url = git__strdup(url);+	GITERR_CHECK_ALLOC(transport->url);++	*out = transport;++	return 0;+}++/* from remote.h */+int git_remote_valid_url(const char *url)+{+	return transport_find_fn(url) != NULL;+}++int git_remote_supported_url(const char* url)+{+	git_transport_cb transport_fn = transport_find_fn(url);++	return ((transport_fn != NULL) && (transport_fn != &git_transport_dummy));+}
+ libgit2/src/transport.h view
@@ -0,0 +1,108 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_transport_h__+#define INCLUDE_transport_h__++#include "git2/net.h"+#include "git2/indexer.h"+#include "vector.h"++#define GIT_CAP_OFS_DELTA "ofs-delta"++typedef struct git_transport_caps {+	int common:1,+		ofs_delta:1;+} git_transport_caps;++/*+ * A day in the life of a network operation+ * ========================================+ *+ * The library gets told to ls-remote/push/fetch on/to/from some+ * remote. We look at the URL of the remote and fill the function+ * table with whatever is appropriate (the remote may be git over git,+ * ssh or http(s). It may even be an hg or svn repository, the library+ * at this level doesn't care, it just calls the helpers.+ *+ * The first call is to ->connect() which connects to the remote,+ * making use of the direction if necessary. This function must also+ * store the remote heads and any other information it needs.+ *+ * The next useful step is to call ->ls() to get the list of+ * references available to the remote. These references may have been+ * collected on connect, or we may build them now. For ls-remote,+ * nothing else is needed other than closing the connection.+ * Otherwise, the higher leves decide which objects we want to+ * have. ->send_have() is used to tell the other end what we have. If+ * we do need to download a pack, ->download_pack() is called.+ *+ * When we're done, we call ->close() to close the+ * connection. ->free() takes care of freeing all the resources.+ */++struct git_transport {+	/**+	 * Where the repo lives+	 */+	char *url;+	/**+	 * Whether we want to push or fetch+	 */+	int direction : 1, /* 0 fetch, 1 push */+		connected : 1;+	/**+	 * Connect and store the remote heads+	 */+	int (*connect)(struct git_transport *transport, int dir);+	/**+	 * Give a list of references, useful for ls-remote+	 */+	int (*ls)(struct git_transport *transport, git_headlist_cb list_cb, void *opaque);+	/**+	 * Push the changes over+	 */+	int (*push)(struct git_transport *transport);+	/**+	 * Negotiate the minimal amount of objects that need to be+	 * retrieved+	 */+	int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, const git_vector *wants);+	/**+	 * Download the packfile+	 */+	int (*download_pack)(struct git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);+	/**+	 * Fetch the changes+	 */+	int (*fetch)(struct git_transport *transport);+	/**+	 * Close the connection+	 */+	int (*close)(struct git_transport *transport);+	/**+	 * Free the associated resources+	 */+	void (*free)(struct git_transport *transport);+};+++int git_transport_new(struct git_transport **transport, const char *url);+int git_transport_local(struct git_transport **transport);+int git_transport_git(struct git_transport **transport);+int git_transport_http(struct git_transport **transport);+int git_transport_dummy(struct git_transport **transport);++/**+  Returns true if the passed URL is valid (a URL with a Git supported scheme,+  or pointing to an existing path)+*/+int git_transport_valid_url(const char *url);++typedef struct git_transport git_transport;+typedef int (*git_transport_cb)(git_transport **transport);++#endif
+ libgit2/src/transports/git.c view
@@ -0,0 +1,478 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "git2/net.h"+#include "git2/common.h"+#include "git2/types.h"+#include "git2/errors.h"+#include "git2/net.h"+#include "git2/revwalk.h"++#include "vector.h"+#include "transport.h"+#include "pkt.h"+#include "common.h"+#include "netops.h"+#include "filebuf.h"+#include "repository.h"+#include "fetch.h"+#include "protocol.h"++typedef struct {+	git_transport parent;+	git_protocol proto;+	GIT_SOCKET socket;+	git_vector refs;+	git_remote_head **heads;+	git_transport_caps caps;+	char buff[1024];+	gitno_buffer buf;+#ifdef GIT_WIN32+	WSADATA wsd;+#endif+} transport_git;++/*+ * Create a git procol request.+ *+ * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0+ */+static int gen_proto(git_buf *request, const char *cmd, const char *url)+{+	char *delim, *repo;+	char default_command[] = "git-upload-pack";+	char host[] = "host=";+	size_t len;++	delim = strchr(url, '/');+	if (delim == NULL) {+		giterr_set(GITERR_NET, "Malformed URL");+		return -1;+	}++	repo = delim;++	delim = strchr(url, ':');+	if (delim == NULL)+		delim = strchr(url, '/');++	if (cmd == NULL)+		cmd = default_command;++	len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;++	git_buf_grow(request, len);+	git_buf_printf(request, "%04x%s %s%c%s",+		(unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);+	git_buf_put(request, url, delim - url);+	git_buf_putc(request, '\0');++	if (git_buf_oom(request))+		return -1;++	return 0;+}++static int send_request(GIT_SOCKET s, const char *cmd, const char *url)+{+	int error;+	git_buf request = GIT_BUF_INIT;++	error = gen_proto(&request, cmd, url);+	if (error < 0)+		goto cleanup;++	error = gitno_send(s, request.ptr, request.size, 0);++cleanup:+	git_buf_free(&request);+	return error;+}++/*+ * Parse the URL and connect to a server, storing the socket in+ * out. For convenience this also takes care of asking for the remote+ * refs+ */+static int do_connect(transport_git *t, const char *url)+{+	char *host, *port;+	const char prefix[] = "git://";+	int error;++	t->socket = INVALID_SOCKET;++	if (!git__prefixcmp(url, prefix))+		url += strlen(prefix);++	if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)+		return -1;++	if ((error = gitno_connect(&t->socket, host, port)) == 0) {+		error = send_request(t->socket, NULL, url);+	}++	git__free(host);+	git__free(port);++	if (error < 0 && t->socket != INVALID_SOCKET) {+		gitno_close(t->socket);+		t->socket = INVALID_SOCKET;+	}++	if (t->socket == INVALID_SOCKET) {+		giterr_set(GITERR_NET, "Failed to connect to the host");+		return -1;+	}++	return 0;+}++/*+ * Read from the socket and store the references in the vector+ */+static int store_refs(transport_git *t)+{+	gitno_buffer *buf = &t->buf;+	int ret = 0;++	while (1) {+		if ((ret = gitno_recv(buf)) < 0)+			return -1;+		if (ret == 0) /* Orderly shutdown, so exit */+			return 0;++		ret = git_protocol_store_refs(&t->proto, buf->data, buf->offset);+		if (ret == GIT_EBUFS) {+			gitno_consume_n(buf, buf->len);+			continue;+		}++		if (ret < 0)+			return ret;++		gitno_consume_n(buf, buf->offset);++		if (t->proto.flush) { /* No more refs */+			t->proto.flush = 0;+			return 0;+		}+	}+}++static int detect_caps(transport_git *t)+{+	git_vector *refs = &t->refs;+	git_pkt_ref *pkt;+	git_transport_caps *caps = &t->caps;+	const char *ptr;++	pkt = git_vector_get(refs, 0);+	/* No refs or capabilites, odd but not a problem */+	if (pkt == NULL || pkt->capabilities == NULL)+		return 0;++	ptr = pkt->capabilities;+	while (ptr != NULL && *ptr != '\0') {+		if (*ptr == ' ')+			ptr++;++		if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {+			caps->common = caps->ofs_delta = 1;+			ptr += strlen(GIT_CAP_OFS_DELTA);+			continue;+		}++		/* We don't know this capability, so skip it */+		ptr = strchr(ptr, ' ');+	}++	return 0;+}++/*+ * Since this is a network connection, we need to parse and store the+ * pkt-lines at this stage and keep them there.+ */+static int git_connect(git_transport *transport, int direction)+{+	transport_git *t = (transport_git *) transport;++	if (direction == GIT_DIR_PUSH) {+		giterr_set(GITERR_NET, "Pushing over git:// is not supported");+		return -1;+	}++	t->parent.direction = direction;+	if (git_vector_init(&t->refs, 16, NULL) < 0)+		return -1;++	/* Connect and ask for the refs */+	if (do_connect(t, transport->url) < 0)+		goto cleanup;++	gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);++	t->parent.connected = 1;+	if (store_refs(t) < 0)+		goto cleanup;++	if (detect_caps(t) < 0)+		goto cleanup;++	return 0;+cleanup:+	git_vector_free(&t->refs);+	return -1;+}++static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)+{+	transport_git *t = (transport_git *) transport;+	git_vector *refs = &t->refs;+	unsigned int i;+	git_pkt *p = NULL;++	git_vector_foreach(refs, i, p) {+		git_pkt_ref *pkt = NULL;++		if (p->type != GIT_PKT_REF)+			continue;++		pkt = (git_pkt_ref *)p;++		if (list_cb(&pkt->head, opaque) < 0) {+			giterr_set(GITERR_NET, "User callback returned error");+			return -1;+		}+	}++	return 0;+}++/* Wait until we get an ack from the */+static int recv_pkt(gitno_buffer *buf)+{+	const char *ptr = buf->data, *line_end;+	git_pkt *pkt;+	int pkt_type, error;++	do {+		/* Wait for max. 1 second */+		if ((error = gitno_select_in(buf, 1, 0)) < 0) {+			return -1;+		} else if (error == 0) {+			/*+			 * Some servers don't respond immediately, so if this+			 * happens, we keep sending information until it+			 * answers. Pretend we received a NAK to convince higher+			 * layers to do so.+			 */+			return GIT_PKT_NAK;+		}++		if ((error = gitno_recv(buf)) < 0)+			return -1;++		error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);+		if (error == GIT_EBUFS)+			continue;+		if (error < 0)+			return -1;+	} while (error);++	gitno_consume(buf, line_end);+	pkt_type = pkt->type;+	git__free(pkt);++	return pkt_type;+}++static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)+{+	transport_git *t = (transport_git *) transport;+	git_revwalk *walk;+	git_oid oid;+	int error;+	unsigned int i;+	git_buf data = GIT_BUF_INIT;+	gitno_buffer *buf = &t->buf;++	if (git_pkt_buffer_wants(wants, &t->caps, &data) < 0)+		return -1;++	if (git_fetch_setup_walk(&walk, repo) < 0)+		goto on_error;++	if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)+		goto on_error;++	git_buf_clear(&data);+	/*+	 * We don't support any kind of ACK extensions, so the negotiation+	 * boils down to sending what we have and listening for an ACK+	 * every once in a while.+	 */+	i = 0;+	while ((error = git_revwalk_next(&oid, walk)) == 0) {+		git_pkt_buffer_have(&oid, &data);+		i++;+		if (i % 20 == 0) {+			int pkt_type;++			git_pkt_buffer_flush(&data);+			if (git_buf_oom(&data))+				goto on_error;++			if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)+				goto on_error;++			pkt_type = recv_pkt(buf);++			if (pkt_type == GIT_PKT_ACK) {+				break;+			} else if (pkt_type == GIT_PKT_NAK) {+				continue;+			} else {+				giterr_set(GITERR_NET, "Unexpected pkt type");+				goto on_error;+			}++		}+	}+	if (error < 0 && error != GIT_REVWALKOVER)+		goto on_error;++	/* Tell the other end that we're done negotiating */+	git_buf_clear(&data);+	git_pkt_buffer_flush(&data);+	git_pkt_buffer_done(&data);+	if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)+		goto on_error;++	git_buf_free(&data);+	git_revwalk_free(walk);+	return 0;++on_error:+	git_buf_free(&data);+	git_revwalk_free(walk);+	return -1;+}++static int git_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)+{+	transport_git *t = (transport_git *) transport;+	int error = 0, read_bytes;+	gitno_buffer *buf = &t->buf;+	git_pkt *pkt;+	const char *line_end, *ptr;++	/*+	 * For now, we ignore everything and wait for the pack+	 */+	do {+		ptr = buf->data;+		/* Whilst we're searching for the pack */+		while (1) {+			if (buf->offset == 0) {+				break;+			}++			error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);+			if (error == GIT_EBUFS)+				break;++			if (error < 0)+				return error;++			if (pkt->type == GIT_PKT_PACK) {+				git__free(pkt);+				return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);+			}++			/* For now we don't care about anything */+			git__free(pkt);+			gitno_consume(buf, line_end);+		}++		read_bytes = gitno_recv(buf);+	} while (read_bytes);++	return read_bytes;+}++static int git_close(git_transport *transport)+{+	transport_git *t = (transport_git*) transport;++	/* Can't do anything if there's an error, so don't bother checking  */+	git_pkt_send_flush(t->socket);+	if (gitno_close(t->socket) < 0) {+		giterr_set(GITERR_NET, "Failed to close socket");+		return -1;+	}++#ifdef GIT_WIN32+	WSACleanup();+#endif++	return 0;+}++static void git_free(git_transport *transport)+{+	transport_git *t = (transport_git *) transport;+	git_vector *refs = &t->refs;+	unsigned int i;++	for (i = 0; i < refs->length; ++i) {+		git_pkt *p = git_vector_get(refs, i);+		git_pkt_free(p);+	}++	git_vector_free(refs);+	git__free(t->heads);+	git_buf_free(&t->proto.buf);+	git__free(t->parent.url);+	git__free(t);+}++int git_transport_git(git_transport **out)+{+	transport_git *t;+#ifdef GIT_WIN32+	int ret;+#endif++	t = git__malloc(sizeof(transport_git));+	GITERR_CHECK_ALLOC(t);++	memset(t, 0x0, sizeof(transport_git));++	t->parent.connect = git_connect;+	t->parent.ls = git_ls;+	t->parent.negotiate_fetch = git_negotiate_fetch;+	t->parent.download_pack = git_download_pack;+	t->parent.close = git_close;+	t->parent.free = git_free;+	t->proto.refs = &t->refs;+	t->proto.transport = (git_transport *) t;++	*out = (git_transport *) t;++#ifdef GIT_WIN32+	ret = WSAStartup(MAKEWORD(2,2), &t->wsd);+	if (ret != 0) {+		git_free(*out);+		giterr_set(GITERR_NET, "Winsock init failed");+		return -1;+	}+#endif++	return 0;+}
+ libgit2/src/transports/http.c view
@@ -0,0 +1,682 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include <stdlib.h>+#include "git2.h"+#include "http_parser.h"++#include "transport.h"+#include "common.h"+#include "netops.h"+#include "buffer.h"+#include "pkt.h"+#include "refs.h"+#include "pack.h"+#include "fetch.h"+#include "filebuf.h"+#include "repository.h"+#include "protocol.h"++enum last_cb {+	NONE,+	FIELD,+	VALUE+};++typedef struct {+	git_transport parent;+	git_protocol proto;+	git_vector refs;+	git_vector common;+	GIT_SOCKET socket;+	git_buf buf;+	git_remote_head **heads;+	int error;+	int transfer_finished :1,+		ct_found :1,+		ct_finished :1,+		pack_ready :1;+	enum last_cb last_cb;+	http_parser parser;+	char *content_type;+	char *host;+	char *port;+	char *service;+	git_transport_caps caps;+#ifdef GIT_WIN32+	WSADATA wsd;+#endif+} transport_http;++static int gen_request(git_buf *buf, const char *url, const char *host, const char *op,+                       const char *service, ssize_t content_length, int ls)+{+	const char *path = url;++	path = strchr(path, '/');+	if (path == NULL) /* Is 'git fetch http://host.com/' valid? */+		path = "/";++	if (ls) {+		git_buf_printf(buf, "%s %s/info/refs?service=git-%s HTTP/1.1\r\n", op, path, service);+	} else {+		git_buf_printf(buf, "%s %s/git-%s HTTP/1.1\r\n", op, path, service);+	}+	git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");+	git_buf_printf(buf, "Host: %s\r\n", host);+	if (content_length > 0) {+		git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", service);+		git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", service);+		git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);+	} else {+		git_buf_puts(buf, "Accept: */*\r\n");+	}+	git_buf_puts(buf, "\r\n");++	if (git_buf_oom(buf))+		return -1;++	return 0;+}++static int do_connect(transport_http *t, const char *host, const char *port)+{+	GIT_SOCKET s;++	if (t->parent.connected && http_should_keep_alive(&t->parser))+		return 0;++	if (gitno_connect(&s, host, port) < 0)+		return -1;++	t->socket = s;+	t->parent.connected = 1;++	return 0;+}++/*+ * The HTTP parser is streaming, so we need to wait until we're in the+ * field handler before we can be sure that we can store the previous+ * value. Right now, we only care about the+ * Content-Type. on_header_{field,value} should be kept generic enough+ * to work for any request.+ */++static const char *typestr = "Content-Type";++static int on_header_field(http_parser *parser, const char *str, size_t len)+{+	transport_http *t = (transport_http *) parser->data;+	git_buf *buf = &t->buf;++	if (t->last_cb == VALUE && t->ct_found) {+		t->ct_finished = 1;+		t->ct_found = 0;+		t->content_type = git__strdup(git_buf_cstr(buf));+		GITERR_CHECK_ALLOC(t->content_type);+		git_buf_clear(buf);+	}++	if (t->ct_found) {+		t->last_cb = FIELD;+		return 0;+	}++	if (t->last_cb != FIELD)+		git_buf_clear(buf);++	git_buf_put(buf, str, len);+	t->last_cb = FIELD;++	return git_buf_oom(buf);+}++static int on_header_value(http_parser *parser, const char *str, size_t len)+{+	transport_http *t = (transport_http *) parser->data;+	git_buf *buf = &t->buf;++	if (t->ct_finished) {+		t->last_cb = VALUE;+		return 0;+	}++	if (t->last_cb == VALUE)+		git_buf_put(buf, str, len);++	if (t->last_cb == FIELD && !strcmp(git_buf_cstr(buf), typestr)) {+		t->ct_found = 1;+		git_buf_clear(buf);+		git_buf_put(buf, str, len);+	}++	t->last_cb = VALUE;++	return git_buf_oom(buf);+}++static int on_headers_complete(http_parser *parser)+{+	transport_http *t = (transport_http *) parser->data;+	git_buf *buf = &t->buf;++	/* The content-type is text/plain for 404, so don't validate */+	if (parser->status_code == 404) {+		git_buf_clear(buf);+		return 0;+	}++	if (t->content_type == NULL) {+		t->content_type = git__strdup(git_buf_cstr(buf));+		if (t->content_type == NULL)+			return t->error = -1;+	}++	git_buf_clear(buf);+	git_buf_printf(buf, "application/x-git-%s-advertisement", t->service);+	if (git_buf_oom(buf))+		return t->error = -1;++	if (strcmp(t->content_type, git_buf_cstr(buf)))+		return t->error = -1;++	git_buf_clear(buf);+	return 0;+}++static int on_body_store_refs(http_parser *parser, const char *str, size_t len)+{+	transport_http *t = (transport_http *) parser->data;++	if (parser->status_code == 404) {+		return git_buf_put(&t->buf, str, len);+	}++	return git_protocol_store_refs(&t->proto, str, len);+}++static int on_message_complete(http_parser *parser)+{+	transport_http *t = (transport_http *) parser->data;++	t->transfer_finished = 1;++	if (parser->status_code == 404) {+		giterr_set(GITERR_NET, "Remote error: %s", git_buf_cstr(&t->buf));+		t->error = -1;+	}++	return 0;+}++static int store_refs(transport_http *t)+{+	http_parser_settings settings;+	char buffer[1024];+	gitno_buffer buf;+	git_pkt *pkt;+	int ret;++	http_parser_init(&t->parser, HTTP_RESPONSE);+	t->parser.data = t;+	memset(&settings, 0x0, sizeof(http_parser_settings));+	settings.on_header_field = on_header_field;+	settings.on_header_value = on_header_value;+	settings.on_headers_complete = on_headers_complete;+	settings.on_body = on_body_store_refs;+	settings.on_message_complete = on_message_complete;++	gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);++	while(1) {+		size_t parsed;++		if ((ret = gitno_recv(&buf)) < 0)+			return -1;++		parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);+		/* Both should happen at the same time */+		if (parsed != buf.offset || t->error < 0)+			return t->error;++		gitno_consume_n(&buf, parsed);++		if (ret == 0 || t->transfer_finished)+			return 0;+	}++	pkt = git_vector_get(&t->refs, 0);+	if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) {+		giterr_set(GITERR_NET, "Invalid HTTP response");+		return t->error = -1;+	} else {+		git_vector_remove(&t->refs, 0);+	}++	return 0;+}++static int http_connect(git_transport *transport, int direction)+{+	transport_http *t = (transport_http *) transport;+	int ret;+	git_buf request = GIT_BUF_INIT;+	const char *service = "upload-pack";+	const char *url = t->parent.url, *prefix = "http://";++	if (direction == GIT_DIR_PUSH) {+		giterr_set(GITERR_NET, "Pushing over HTTP is not implemented");+		return -1;+	}++	t->parent.direction = direction;+	if (git_vector_init(&t->refs, 16, NULL) < 0)+		return -1;++	if (!git__prefixcmp(url, prefix))+		url += strlen(prefix);++	if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, "80")) < 0)+		goto cleanup;++	t->service = git__strdup(service);+	GITERR_CHECK_ALLOC(t->service);++	if ((ret = do_connect(t, t->host, t->port)) < 0)+		goto cleanup;++	/* Generate and send the HTTP request */+	if ((ret = gen_request(&request, url, t->host, "GET", service, 0, 1)) < 0) {+		giterr_set(GITERR_NET, "Failed to generate request");+		goto cleanup;+	}++	if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)+		goto cleanup;++	ret = store_refs(t);++cleanup:+	git_buf_free(&request);+	git_buf_clear(&t->buf);++	return ret;+}++static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)+{+	transport_http *t = (transport_http *) transport;+	git_vector *refs = &t->refs;+	unsigned int i;+	git_pkt_ref *p;++	git_vector_foreach(refs, i, p) {+		if (p->type != GIT_PKT_REF)+			continue;++		if (list_cb(&p->head, opaque) < 0) {+			giterr_set(GITERR_NET, "The user callback returned error");+			return -1;+		}+	}++	return 0;+}++static int on_body_parse_response(http_parser *parser, const char *str, size_t len)+{+	transport_http *t = (transport_http *) parser->data;+	git_buf *buf = &t->buf;+	git_vector *common = &t->common;+	int error;+	const char *line_end, *ptr;++	if (len == 0) { /* EOF */+		if (git_buf_len(buf) != 0) {+			giterr_set(GITERR_NET, "Unexpected EOF");+			return t->error = -1;+		} else {+			return 0;+		}+	}++	git_buf_put(buf, str, len);+	ptr = buf->ptr;+	while (1) {+		git_pkt *pkt;++		if (git_buf_len(buf) == 0)+			return 0;++		error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf));+		if (error == GIT_EBUFS) {+			return 0; /* Ask for more */+		}+		if (error < 0)+			return t->error = -1;++		git_buf_consume(buf, line_end);++		if (pkt->type == GIT_PKT_PACK) {+			git__free(pkt);+			t->pack_ready = 1;+			return 0;+		}++		if (pkt->type == GIT_PKT_NAK) {+			git__free(pkt);+			return 0;+		}++		if (pkt->type != GIT_PKT_ACK) {+			git__free(pkt);+			continue;+		}++		if (git_vector_insert(common, pkt) < 0)+			return -1;+	}++	return error;++}++static int parse_response(transport_http *t)+{+	int ret = 0;+	http_parser_settings settings;+	char buffer[1024];+	gitno_buffer buf;++	http_parser_init(&t->parser, HTTP_RESPONSE);+	t->parser.data = t;+	t->transfer_finished = 0;+	memset(&settings, 0x0, sizeof(http_parser_settings));+	settings.on_header_field = on_header_field;+	settings.on_header_value = on_header_value;+	settings.on_headers_complete = on_headers_complete;+	settings.on_body = on_body_parse_response;+	settings.on_message_complete = on_message_complete;++	gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);++	while(1) {+		size_t parsed;++		if ((ret = gitno_recv(&buf)) < 0)+			return -1;++		parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);+		/* Both should happen at the same time */+		if (parsed != buf.offset || t->error < 0)+			return t->error;++		gitno_consume_n(&buf, parsed);++		if (ret == 0 || t->transfer_finished || t->pack_ready) {+			return 0;+		}+	}++	return ret;+}++static int http_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)+{+	transport_http *t = (transport_http *) transport;+	int ret;+	unsigned int i;+	char buff[128];+	gitno_buffer buf;+	git_revwalk *walk = NULL;+	git_oid oid;+	git_pkt_ack *pkt;+	git_vector *common = &t->common;+	const char *prefix = "http://", *url = t->parent.url;+	git_buf request = GIT_BUF_INIT, data = GIT_BUF_INIT;+	gitno_buffer_setup(&buf, buff, sizeof(buff), t->socket);++	/* TODO: Store url in the transport */+	if (!git__prefixcmp(url, prefix))+		url += strlen(prefix);++	if (git_vector_init(common, 16, NULL) < 0)+		return -1;++	if (git_fetch_setup_walk(&walk, repo) < 0)+		return -1;++	do {+		if ((ret = do_connect(t, t->host, t->port)) < 0)+			goto cleanup;++		if ((ret = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)+			goto cleanup;++		/* We need to send these on each connection */+		git_vector_foreach (common, i, pkt) {+			if ((ret = git_pkt_buffer_have(&pkt->oid, &data)) < 0)+				goto cleanup;+		}++		i = 0;+		while ((i < 20) && ((ret = git_revwalk_next(&oid, walk)) == 0)) {+			if ((ret = git_pkt_buffer_have(&oid, &data)) < 0)+				goto cleanup;++			i++;+		}++		git_pkt_buffer_done(&data);++		if ((ret = gen_request(&request, url, t->host, "POST", "upload-pack", data.size, 0)) < 0)+			goto cleanup;++		if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)+			goto cleanup;++		if ((ret = gitno_send(t->socket, data.ptr, data.size, 0)) < 0)+			goto cleanup;++		git_buf_clear(&request);+		git_buf_clear(&data);++		if (ret < 0 || i >= 256)+			break;++		if ((ret = parse_response(t)) < 0)+			goto cleanup;++		if (t->pack_ready) {+			ret = 0;+			goto cleanup;+		}++	} while(1);++cleanup:+	git_buf_free(&request);+	git_buf_free(&data);+	git_revwalk_free(walk);+	return ret;+}++typedef struct {+	git_indexer_stream *idx;+	git_indexer_stats *stats;+	transport_http *transport;+} download_pack_cbdata;++static int on_message_complete_download_pack(http_parser *parser)+{+	download_pack_cbdata *data = (download_pack_cbdata *) parser->data;++	data->transport->transfer_finished = 1;++	return 0;+}+static int on_body_download_pack(http_parser *parser, const char *str, size_t len)+{+	download_pack_cbdata *data = (download_pack_cbdata *) parser->data;+	transport_http *t = data->transport;+	git_indexer_stream *idx = data->idx;+	git_indexer_stats *stats = data->stats;++	return t->error = git_indexer_stream_add(idx, str, len, stats);+}++/*+ * As the server is probably using Transfer-Encoding: chunked, we have+ * to use the HTTP parser to download the pack instead of giving it to+ * the simple downloader. Furthermore, we're using keep-alive+ * connections, so the simple downloader would just hang.+ */+static int http_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)+{+	transport_http *t = (transport_http *) transport;+	git_buf *oldbuf = &t->buf;+	int recvd;+	http_parser_settings settings;+	char buffer[1024];+	gitno_buffer buf;+	git_indexer_stream *idx = NULL;+	download_pack_cbdata data;++	gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);++	if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {+		giterr_set(GITERR_NET, "The pack doesn't start with a pack signature");+		return -1;+	}++	if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)+		return -1;+++	/*+	 * This is part of the previous response, so we don't want to+	 * re-init the parser, just set these two callbacks.+	 */+	memset(stats, 0, sizeof(git_indexer_stats));+	data.stats = stats;+	data.idx = idx;+	data.transport = t;+	t->parser.data = &data;+	t->transfer_finished = 0;+	memset(&settings, 0x0, sizeof(settings));+	settings.on_message_complete = on_message_complete_download_pack;+	settings.on_body = on_body_download_pack;+	*bytes = git_buf_len(oldbuf);++	if (git_indexer_stream_add(idx, git_buf_cstr(oldbuf), git_buf_len(oldbuf), stats) < 0)+		goto on_error;++	do {+		size_t parsed;++		if ((recvd = gitno_recv(&buf)) < 0)+			goto on_error;++		parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);+		if (parsed != buf.offset || t->error < 0)+			goto on_error;++		*bytes += recvd;+		gitno_consume_n(&buf, parsed);+	} while (recvd > 0 && !t->transfer_finished);++	if (git_indexer_stream_finalize(idx, stats) < 0)+		goto on_error;++	git_indexer_stream_free(idx);+	return 0;++on_error:+	git_indexer_stream_free(idx);+	return -1;+}++static int http_close(git_transport *transport)+{+	transport_http *t = (transport_http *) transport;++	if (gitno_close(t->socket) < 0) {+		giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));+		return -1;+	}++	return 0;+}+++static void http_free(git_transport *transport)+{+	transport_http *t = (transport_http *) transport;+	git_vector *refs = &t->refs;+	git_vector *common = &t->common;+	unsigned int i;+	git_pkt *p;++#ifdef GIT_WIN32+	/* cleanup the WSA context. note that this context+	 * can be initialized more than once with WSAStartup(),+	 * and needs to be cleaned one time for each init call+	 */+	WSACleanup();+#endif++	git_vector_foreach(refs, i, p) {+		git_pkt_free(p);+	}+	git_vector_free(refs);+	git_vector_foreach(common, i, p) {+		git_pkt_free(p);+	}+	git_vector_free(common);+	git_buf_free(&t->buf);+	git_buf_free(&t->proto.buf);+	git__free(t->heads);+	git__free(t->content_type);+	git__free(t->host);+	git__free(t->port);+	git__free(t->service);+	git__free(t->parent.url);+	git__free(t);+}++int git_transport_http(git_transport **out)+{+	transport_http *t;++	t = git__malloc(sizeof(transport_http));+	GITERR_CHECK_ALLOC(t);++	memset(t, 0x0, sizeof(transport_http));++	t->parent.connect = http_connect;+	t->parent.ls = http_ls;+	t->parent.negotiate_fetch = http_negotiate_fetch;+	t->parent.download_pack = http_download_pack;+	t->parent.close = http_close;+	t->parent.free = http_free;+	t->proto.refs = &t->refs;+	t->proto.transport = (git_transport *) t;++#ifdef GIT_WIN32+	/* on win32, the WSA context needs to be initialized+	 * before any socket calls can be performed */+	if (WSAStartup(MAKEWORD(2,2), &t->wsd) != 0) {+		http_free((git_transport *) t);+		giterr_set(GITERR_OS, "Winsock init failed");+		return -1;+	}+#endif++	*out = (git_transport *) t;+	return 0;+}
+ libgit2/src/transports/local.c view
@@ -0,0 +1,240 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "common.h"+#include "git2/types.h"+#include "git2/net.h"+#include "git2/repository.h"+#include "git2/object.h"+#include "git2/tag.h"+#include "refs.h"+#include "transport.h"+#include "posix.h"+#include "path.h"+#include "buffer.h"++typedef struct {+	git_transport parent;+	git_repository *repo;+	git_vector refs;+} transport_local;++static int add_ref(transport_local *t, const char *name)+{+	const char peeled[] = "^{}";+	git_remote_head *head;+	git_object *obj = NULL, *target = NULL;+	git_buf buf = GIT_BUF_INIT;++	head = git__malloc(sizeof(git_remote_head));+	GITERR_CHECK_ALLOC(head);++	head->name = git__strdup(name);+	GITERR_CHECK_ALLOC(head->name);++	if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0 ||+		git_vector_insert(&t->refs, head) < 0)+	{+		git__free(head->name);+		git__free(head);+		return -1;+	}++	/* If it's not a tag, we don't need to try to peel it */+	if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))+		return 0;++	if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0)+		return -1;++	head = NULL;++	/* If it's not an annotated tag, just get out */+	if (git_object_type(obj) != GIT_OBJ_TAG) {+		git_object_free(obj);+		return 0;+	}++	/* And if it's a tag, peel it, and add it to the list */+	head = git__malloc(sizeof(git_remote_head));+	GITERR_CHECK_ALLOC(head);+	if (git_buf_join(&buf, 0, name, peeled) < 0)+		return -1;++	head->name = git_buf_detach(&buf);++	if (git_tag_peel(&target, (git_tag *) obj) < 0)+		goto on_error;++	git_oid_cpy(&head->oid, git_object_id(target));+	git_object_free(obj);+	git_object_free(target);++	if (git_vector_insert(&t->refs, head) < 0)+		return -1;++	return 0;++on_error:+	git_object_free(obj);+	git_object_free(target);+	return -1;+}++static int store_refs(transport_local *t)+{+	unsigned int i;+	git_strarray ref_names = {0};++	assert(t);++	if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 ||+		git_vector_init(&t->refs, (unsigned int)ref_names.count, NULL) < 0)+		goto on_error;++	/* Sort the references first */+	git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);++	/* Add HEAD */+	if (add_ref(t, GIT_HEAD_FILE) < 0)+		goto on_error;++	for (i = 0; i < ref_names.count; ++i) {+		if (add_ref(t, ref_names.strings[i]) < 0)+			goto on_error;+	}++	git_strarray_free(&ref_names);+	return 0;++on_error:+	git_vector_free(&t->refs);+	git_strarray_free(&ref_names);+	return -1;+}++static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)+{+	transport_local  *t = (transport_local *) transport;+	git_vector *refs = &t->refs;+	unsigned int i;+	git_remote_head *h;++	assert(transport && transport->connected);++	git_vector_foreach(refs, i, h) {+		if (list_cb(h, payload) < 0)+			return -1;+	}++	return 0;+}++/*+ * Try to open the url as a git directory. The direction doesn't+ * matter in this case because we're calulating the heads ourselves.+ */+static int local_connect(git_transport *transport, int direction)+{+	git_repository *repo;+	int error;+	transport_local *t = (transport_local *) transport;+	const char *path;+	git_buf buf = GIT_BUF_INIT;++	GIT_UNUSED(direction);++	/* The repo layer doesn't want the prefix */+	if (!git__prefixcmp(transport->url, "file://")) {+		if (git_path_fromurl(&buf, transport->url) < 0) {+			git_buf_free(&buf);+			return -1;+		}+		path = git_buf_cstr(&buf);++	} else { /* We assume transport->url is already a path */+		path = transport->url;+	}++	error = git_repository_open(&repo, path);++	git_buf_free(&buf);++	if (error < 0)+		return -1;++	t->repo = repo;++	if (store_refs(t) < 0)+		return -1;++	t->parent.connected = 1;++	return 0;+}++static int local_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)+{+	GIT_UNUSED(transport);+	GIT_UNUSED(repo);+	GIT_UNUSED(wants);++	giterr_set(GITERR_NET, "Fetch via local transport isn't implemented. Sorry");+	return -1;+}++static int local_close(git_transport *transport)+{+	transport_local *t = (transport_local *)transport;++	git_repository_free(t->repo);+	t->repo = NULL;++	return 0;+}++static void local_free(git_transport *transport)+{+	unsigned int i;+	transport_local *t = (transport_local *) transport;+	git_vector *vec = &t->refs;+	git_remote_head *h;++	assert(transport);++	git_vector_foreach (vec, i, h) {+		git__free(h->name);+		git__free(h);+	}+	git_vector_free(vec);++	git__free(t->parent.url);+	git__free(t);+}++/**************+ * Public API *+ **************/++int git_transport_local(git_transport **out)+{+	transport_local *t;++	t = git__malloc(sizeof(transport_local));+	GITERR_CHECK_ALLOC(t);++	memset(t, 0x0, sizeof(transport_local));++	t->parent.connect = local_connect;+	t->parent.ls = local_ls;+	t->parent.negotiate_fetch = local_negotiate_fetch;+	t->parent.close = local_close;+	t->parent.free = local_free;++	*out = (git_transport *) t;++	return 0;+}
+ libgit2/src/tree-cache.c view
@@ -0,0 +1,184 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "tree-cache.h"++static git_tree_cache *find_child(const git_tree_cache *tree, const char *path)+{+	size_t i, dirlen;+	const char *end;++	end = strchr(path, '/');+	if (end == NULL) {+		end = strrchr(path, '\0');+	}++	dirlen = end - path;++	for (i = 0; i < tree->children_count; ++i) {+		const char *childname = tree->children[i]->name;++		if (strlen(childname) == dirlen && !memcmp(path, childname, dirlen))+			return tree->children[i];+	}++	return NULL;+}++void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path)+{+	const char *ptr = path, *end;++	if (tree == NULL)+		return;++	tree->entries = -1;++	while (ptr != NULL) {+		end = strchr(ptr, '/');++		if (end == NULL) /* End of path */+			break;++		tree = find_child(tree, ptr);+		if (tree == NULL) /* We don't have that tree */+			return;++		tree->entries = -1;+		ptr = end + 1;+	}+}++const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path)+{+	const char *ptr = path, *end;++	if (tree == NULL) {+		return NULL;+	}++	while (1) {+		end = strchr(ptr, '/');++		tree = find_child(tree, ptr);+		if (tree == NULL) { /* Can't find it */+			return NULL;+		}++		if (end == NULL || end + 1 == '\0')+			return tree;++		ptr = end + 1;+	}+}++static int read_tree_internal(git_tree_cache **out,+		const char **buffer_in, const char *buffer_end, git_tree_cache *parent)+{+	git_tree_cache *tree = NULL;+	const char *name_start, *buffer;+	int count;+	size_t name_len;++	buffer = name_start = *buffer_in;++	if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)+		goto corrupted;++	if (++buffer >= buffer_end)+		goto corrupted;++	name_len = strlen(name_start);+	tree = git__malloc(sizeof(git_tree_cache) + name_len + 1);+	GITERR_CHECK_ALLOC(tree);++	memset(tree, 0x0, sizeof(git_tree_cache));+	tree->parent = parent;++	/* NUL-terminated tree name */+	memcpy(tree->name, name_start, name_len);+	tree->name[name_len] = '\0';++	/* Blank-terminated ASCII decimal number of entries in this tree */+	if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < -1)+		goto corrupted;++	tree->entries = count;++	if (*buffer != ' ' || ++buffer >= buffer_end)+		goto corrupted;++	 /* Number of children of the tree, newline-terminated */+	if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < 0)+		goto corrupted;++	tree->children_count = count;++	if (*buffer != '\n' || ++buffer > buffer_end)+		goto corrupted;++	/* The SHA1 is only there if it's not invalidated */+	if (tree->entries >= 0) {+		/* 160-bit SHA-1 for this tree and it's children */+		if (buffer + GIT_OID_RAWSZ > buffer_end)+			goto corrupted;++		git_oid_fromraw(&tree->oid, (const unsigned char *)buffer);+		buffer += GIT_OID_RAWSZ;+	}++	/* Parse children: */+	if (tree->children_count > 0) {+		unsigned int i;++		tree->children = git__malloc(tree->children_count * sizeof(git_tree_cache *));+		GITERR_CHECK_ALLOC(tree->children);++		for (i = 0; i < tree->children_count; ++i) {+			if (read_tree_internal(&tree->children[i], &buffer, buffer_end, tree) < 0)+				return -1;+		}+	}++	*buffer_in = buffer;+	*out = tree;+	return 0;++ corrupted:+	git_tree_cache_free(tree);+	giterr_set(GITERR_INDEX, "Corruped TREE extension in index");+	return -1;+}++int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size)+{+	const char *buffer_end = buffer + buffer_size;++	if (read_tree_internal(tree, &buffer, buffer_end, NULL) < 0)+		return -1;++	if (buffer < buffer_end) {+		giterr_set(GITERR_INDEX, "Corruped TREE extension in index (unexpected trailing data)");+		return -1;+	}++	return 0;+}++void git_tree_cache_free(git_tree_cache *tree)+{+	unsigned int i;++	if (tree == NULL)+		return;++	for (i = 0; i < tree->children_count; ++i)+		git_tree_cache_free(tree->children[i]);++	git__free(tree->children);+	git__free(tree);+}
+ libgit2/src/tree-cache.h view
@@ -0,0 +1,31 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#ifndef INCLUDE_tree_cache_h__+#define INCLUDE_tree_cache_h__++#include "common.h"+#include "git2/oid.h"++struct git_tree_cache {+	struct git_tree_cache *parent;+	struct git_tree_cache **children;+	size_t children_count;++	ssize_t entries;+	git_oid oid;+	char name[GIT_FLEX_ARRAY];+};++typedef struct git_tree_cache git_tree_cache;++int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size);+void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path);+const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path);+void git_tree_cache_free(git_tree_cache *tree);++#endif
+ libgit2/src/tree.c view
@@ -0,0 +1,785 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "commit.h"+#include "tree.h"+#include "git2/repository.h"+#include "git2/object.h"++#define DEFAULT_TREE_SIZE 16+#define MAX_FILEMODE 0777777+#define MAX_FILEMODE_BYTES 6++static int valid_attributes(const int attributes)+{+	return attributes >= 0 && attributes <= MAX_FILEMODE;+}++static int valid_entry_name(const char *filename)+{+	return strlen(filename) > 0 && strchr(filename, '/') == NULL;+}++static int entry_sort_cmp(const void *a, const void *b)+{+	const git_tree_entry *entry_a = (const git_tree_entry *)(a);+	const git_tree_entry *entry_b = (const git_tree_entry *)(b);++	return git_path_cmp(+		entry_a->filename, entry_a->filename_len, git_tree_entry__is_tree(entry_a),+		entry_b->filename, entry_b->filename_len, git_tree_entry__is_tree(entry_b));+}+++struct tree_key_search {+	const char *filename;+	size_t filename_len;+};++static int homing_search_cmp(const void *key, const void *array_member)+{+	const struct tree_key_search *ksearch = key;+	const git_tree_entry *entry = array_member;++	const size_t len1 = ksearch->filename_len;+	const size_t len2 = entry->filename_len;++	return memcmp(+		ksearch->filename,+		entry->filename,+		len1 < len2 ? len1 : len2+	);+}++/*+ * Search for an entry in a given tree.+ *+ * Note that this search is performed in two steps because+ * of the way tree entries are sorted internally in git:+ *+ * Entries in a tree are not sorted alphabetically; two entries+ * with the same root prefix will have different positions+ * depending on whether they are folders (subtrees) or normal files.+ *+ * Consequently, it is not possible to find an entry on the tree+ * with a binary search if you don't know whether the filename+ * you're looking for is a folder or a normal file.+ *+ * To work around this, we first perform a homing binary search+ * on the tree, using the minimal length root prefix of our filename.+ * Once the comparisons for this homing search start becoming+ * ambiguous because of folder vs file sorting, we look linearly+ * around the area for our target file.+ */+static int tree_key_search(git_vector *entries, const char *filename)+{+	struct tree_key_search ksearch;+	const git_tree_entry *entry;++	int homing, i;++	ksearch.filename = filename;+	ksearch.filename_len = strlen(filename);++	/* Initial homing search; find an entry on the tree with+	 * the same prefix as the filename we're looking for */+	homing = git_vector_bsearch2(entries, &homing_search_cmp, &ksearch);+	if (homing < 0)+		return homing;++	/* We found a common prefix. Look forward as long as+	 * there are entries that share the common prefix */+	for (i = homing; i < (int)entries->length; ++i) {+		entry = entries->contents[i];++		if (homing_search_cmp(&ksearch, entry) < 0)+			break;++		if (strcmp(filename, entry->filename) == 0)+			return i;+	}++	/* If we haven't found our filename yet, look backwards+	 * too as long as we have entries with the same prefix */+	for (i = homing - 1; i >= 0; --i) {+		entry = entries->contents[i];++		if (homing_search_cmp(&ksearch, entry) > 0)+			break;++		if (strcmp(filename, entry->filename) == 0)+			return i;+	}++	/* The filename doesn't exist at all */+	return GIT_ENOTFOUND;+}++void git_tree__free(git_tree *tree)+{+	unsigned int i;++	for (i = 0; i < tree->entries.length; ++i) {+		git_tree_entry *e;+		e = git_vector_get(&tree->entries, i);++		git__free(e->filename);+		git__free(e);+	}++	git_vector_free(&tree->entries);+	git__free(tree);+}++const git_oid *git_tree_id(git_tree *c)+{+	return git_object_id((git_object *)c);+}++unsigned int git_tree_entry_attributes(const git_tree_entry *entry)+{+	return entry->attr;+}++const char *git_tree_entry_name(const git_tree_entry *entry)+{+	assert(entry);+	return entry->filename;+}++const git_oid *git_tree_entry_id(const git_tree_entry *entry)+{+	assert(entry);+	return &entry->oid;+}++git_otype git_tree_entry_type(const git_tree_entry *entry)+{+	assert(entry);++	if (S_ISGITLINK(entry->attr))+		return GIT_OBJ_COMMIT;+	else if (S_ISDIR(entry->attr))+		return GIT_OBJ_TREE;+	else+		return GIT_OBJ_BLOB;+}++int git_tree_entry_to_object(+	git_object **object_out,+	git_repository *repo,+	const git_tree_entry *entry)+{+	assert(entry && object_out);+	return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY);+}++const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)+{+	int idx;++	assert(tree && filename);++	idx = tree_key_search(&tree->entries, filename);+	if (idx == GIT_ENOTFOUND)+		return NULL;++	return git_vector_get(&tree->entries, idx);+}++const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx)+{+	assert(tree);+	return git_vector_get(&tree->entries, idx);+}++int git_tree__prefix_position(git_tree *tree, const char *path)+{+	git_vector *entries = &tree->entries;+	struct tree_key_search ksearch;+	unsigned int at_pos;++	ksearch.filename = path;+	ksearch.filename_len = strlen(path);++	/* Find tree entry with appropriate prefix */+	git_vector_bsearch3(&at_pos, entries, &homing_search_cmp, &ksearch);++	for (; at_pos < entries->length; ++at_pos) {+		const git_tree_entry *entry = entries->contents[at_pos];+		if (homing_search_cmp(&ksearch, entry) < 0)+			break;+	}++	for (; at_pos > 0; --at_pos) {+		const git_tree_entry *entry = entries->contents[at_pos - 1];+		if (homing_search_cmp(&ksearch, entry) > 0)+			break;+	}++	return at_pos;+}++unsigned int git_tree_entrycount(git_tree *tree)+{+	assert(tree);+	return tree->entries.length;+}++static int tree_error(const char *str)+{+	giterr_set(GITERR_TREE, "%s", str);+	return -1;+}++static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end)+{+	if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0)+		return -1;++	while (buffer < buffer_end) {+		git_tree_entry *entry;+		int tmp;++		entry = git__calloc(1, sizeof(git_tree_entry));+		GITERR_CHECK_ALLOC(entry);++		if (git_vector_insert(&tree->entries, entry) < 0)+			return -1;++		if (git__strtol32(&tmp, buffer, &buffer, 8) < 0 ||+			!buffer || !valid_attributes(tmp))+			return tree_error("Failed to parse tree. Can't parse attributes");++		entry->attr = tmp;++		if (*buffer++ != ' ')+			return tree_error("Failed to parse tree. Object is corrupted");++		if (memchr(buffer, 0, buffer_end - buffer) == NULL)+			return tree_error("Failed to parse tree. Object is corrupted");++		entry->filename = git__strdup(buffer);+		entry->filename_len = strlen(buffer);++		while (buffer < buffer_end && *buffer != 0)+			buffer++;++		buffer++;++		git_oid_fromraw(&entry->oid, (const unsigned char *)buffer);+		buffer += GIT_OID_RAWSZ;+	}++	return 0;+}++int git_tree__parse(git_tree *tree, git_odb_object *obj)+{+	assert(tree);+	return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len);+}++static unsigned int find_next_dir(const char *dirname, git_index *index, unsigned int start)+{+	unsigned int i, entries = git_index_entrycount(index);+	size_t dirlen;++	dirlen = strlen(dirname);+	for (i = start; i < entries; ++i) {+		git_index_entry *entry = git_index_get(index, i);+		if (strlen(entry->path) < dirlen ||+		    memcmp(entry->path, dirname, dirlen) ||+			(dirlen > 0 && entry->path[dirlen] != '/')) {+			break;+		}+	}++	return i;+}++static int append_entry(git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)+{+	git_tree_entry *entry;++	entry = git__calloc(1, sizeof(git_tree_entry));+	GITERR_CHECK_ALLOC(entry);++	entry->filename = git__strdup(filename);+	entry->filename_len = strlen(entry->filename);++	git_oid_cpy(&entry->oid, id);+	entry->attr = attributes;++	if (git_vector_insert(&bld->entries, entry) < 0)+		return -1;++	return 0;+}++static int write_tree(+	git_oid *oid,+	git_repository *repo,+	git_index *index,+	const char *dirname,+	unsigned int start)+{+	git_treebuilder *bld = NULL;++	unsigned int i, entries = git_index_entrycount(index);+	int error;+	size_t dirname_len = strlen(dirname);+	const git_tree_cache *cache;++	cache = git_tree_cache_get(index->tree, dirname);+	if (cache != NULL && cache->entries >= 0){+		git_oid_cpy(oid, &cache->oid);+		return find_next_dir(dirname, index, start);+	}++	error = git_treebuilder_create(&bld, NULL);+	if (bld == NULL) {+		return -1;+	}++	/*+	 * This loop is unfortunate, but necessary. The index doesn't have+	 * any directores, so we need to handle that manually, and we+	 * need to keep track of the current position.+	 */+	for (i = start; i < entries; ++i) {+		git_index_entry *entry = git_index_get(index, i);+		char *filename, *next_slash;++	/*+	 * If we've left our (sub)tree, exit the loop and return. The+	 * first check is an early out (and security for the+	 * third). The second check is a simple prefix comparison. The+	 * third check catches situations where there is a directory+	 * win32/sys and a file win32mmap.c. Without it, the following+	 * code believes there is a file win32/mmap.c+	 */+		if (strlen(entry->path) < dirname_len ||+		    memcmp(entry->path, dirname, dirname_len) ||+		    (dirname_len > 0 && entry->path[dirname_len] != '/')) {+			break;+		}++		filename = entry->path + dirname_len;+		if (*filename == '/')+			filename++;+		next_slash = strchr(filename, '/');+		if (next_slash) {+			git_oid sub_oid;+			int written;+			char *subdir, *last_comp;++			subdir = git__strndup(entry->path, next_slash - entry->path);+			GITERR_CHECK_ALLOC(subdir);++			/* Write out the subtree */+			written = write_tree(&sub_oid, repo, index, subdir, i);+			if (written < 0) {+				tree_error("Failed to write subtree");+				goto on_error;+			} else {+				i = written - 1; /* -1 because of the loop increment */+			}++			/*+			 * We need to figure out what we want toinsert+			 * into this tree. If we're traversing+			 * deps/zlib/, then we only want to write+			 * 'zlib' into the tree.+			 */+			last_comp = strrchr(subdir, '/');+			if (last_comp) {+				last_comp++; /* Get rid of the '/' */+			} else {+				last_comp = subdir;+			}+			error = append_entry(bld, last_comp, &sub_oid, S_IFDIR);+			git__free(subdir);+			if (error < 0) {+				tree_error("Failed to insert dir");+				goto on_error;+			}+		} else {+			error = append_entry(bld, filename, &entry->oid, entry->mode);+			if (error < 0) {+				tree_error("Failed to insert file");+				goto on_error;+			}+		}+	}++	if (git_treebuilder_write(oid, repo, bld) < 0)+		goto on_error;++	git_treebuilder_free(bld);+	return i;++on_error:+	git_treebuilder_free(bld);+	return -1;+}++int git_tree_create_fromindex(git_oid *oid, git_index *index)+{+	int ret;+	git_repository *repo;++	repo = (git_repository *)GIT_REFCOUNT_OWNER(index);++	if (repo == NULL)+		return tree_error("Failed to create tree. "+		  "The index file is not backed up by an existing repository");++	if (index->tree != NULL && index->tree->entries >= 0) {+		git_oid_cpy(oid, &index->tree->oid);+		return 0;+	}++	/* The tree cache didn't help us */+	ret = write_tree(oid, repo, index, "", 0);+	return ret < 0 ? ret : 0;+}++static void sort_entries(git_treebuilder *bld)+{+	git_vector_sort(&bld->entries);+}++int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)+{+	git_treebuilder *bld;+	unsigned int i, source_entries = DEFAULT_TREE_SIZE;++	assert(builder_p);++	bld = git__calloc(1, sizeof(git_treebuilder));+	GITERR_CHECK_ALLOC(bld);++	if (source != NULL)+		source_entries = source->entries.length;++	if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0)+		goto on_error;++	if (source != NULL) {+		for (i = 0; i < source->entries.length; ++i) {+			git_tree_entry *entry_src = source->entries.contents[i];++			if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0)+				goto on_error;+		}+	}++	*builder_p = bld;+	return 0;++on_error:+	git_treebuilder_free(bld);+	return -1;+}++int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)+{+	git_tree_entry *entry;+	int pos;++	assert(bld && id && filename);++	if (!valid_attributes(attributes))+		return tree_error("Failed to insert entry. Invalid attributes");++	if (!valid_entry_name(filename))+		return tree_error("Failed to insert entry. Invalid name for a tree entry");++	pos = tree_key_search(&bld->entries, filename);++	if (pos >= 0) {+		entry = git_vector_get(&bld->entries, pos);+		if (entry->removed)+			entry->removed = 0;+	} else {+		entry = git__calloc(1, sizeof(git_tree_entry));+		GITERR_CHECK_ALLOC(entry);++		entry->filename = git__strdup(filename);+		entry->filename_len = strlen(entry->filename);+	}++	git_oid_cpy(&entry->oid, id);+	entry->attr = attributes;++	if (pos == GIT_ENOTFOUND) {+		if (git_vector_insert(&bld->entries, entry) < 0)+			return -1;+	}++	if (entry_out != NULL)+		*entry_out = entry;++	return 0;+}++static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)+{+	int idx;+	git_tree_entry *entry;++	assert(bld && filename);++	idx = tree_key_search(&bld->entries, filename);+	if (idx < 0)+		return NULL;++	entry = git_vector_get(&bld->entries, idx);+	if (entry->removed)+		return NULL;++	return entry;+}++const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename)+{+	return treebuilder_get(bld, filename);+}++int git_treebuilder_remove(git_treebuilder *bld, const char *filename)+{+	git_tree_entry *remove_ptr = treebuilder_get(bld, filename);++	if (remove_ptr == NULL || remove_ptr->removed)+		return tree_error("Failed to remove entry. File isn't in the tree");++	remove_ptr->removed = 1;+	return 0;+}++int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)+{+	unsigned int i;+	git_buf tree = GIT_BUF_INIT;+	git_odb *odb;++	assert(bld);++	sort_entries(bld);++	/* Grow the buffer beforehand to an estimated size */+	git_buf_grow(&tree, bld->entries.length * 72);++	for (i = 0; i < bld->entries.length; ++i) {+		git_tree_entry *entry = bld->entries.contents[i];++		if (entry->removed)+			continue;++		git_buf_printf(&tree, "%o ", entry->attr);+		git_buf_put(&tree, entry->filename, entry->filename_len + 1);+		git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);+	}++	if (git_buf_oom(&tree))+		goto on_error;++	if (git_repository_odb__weakptr(&odb, repo) < 0)+		goto on_error;+++	if (git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE) < 0)+		goto on_error;++	git_buf_free(&tree);+	return 0;++on_error:+	git_buf_free(&tree);+	return -1;+}++void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)+{+	unsigned int i;++	assert(bld && filter);++	for (i = 0; i < bld->entries.length; ++i) {+		git_tree_entry *entry = bld->entries.contents[i];+		if (!entry->removed && filter(entry, payload))+			entry->removed = 1;+	}+}++void git_treebuilder_clear(git_treebuilder *bld)+{+	unsigned int i;+	assert(bld);++	for (i = 0; i < bld->entries.length; ++i) {+		git_tree_entry *e = bld->entries.contents[i];+		git__free(e->filename);+		git__free(e);+	}++	git_vector_clear(&bld->entries);+}++void git_treebuilder_free(git_treebuilder *bld)+{+	git_treebuilder_clear(bld);+	git_vector_free(&bld->entries);+	git__free(bld);+}++static int tree_frompath(+	git_tree **parent_out,+	git_tree *root,+	git_buf *treeentry_path,+	size_t offset)+{+	char *slash_pos = NULL;+	const git_tree_entry* entry;+	int error = 0;+	git_tree *subtree;++	if (!*(treeentry_path->ptr + offset)) {+		giterr_set(GITERR_INVALID,+			"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);+		return -1;+	}++	slash_pos = (char *)strchr(treeentry_path->ptr + offset, '/');++	if (slash_pos == NULL)+		return git_tree_lookup(+			parent_out,+			root->object.repo,+			git_object_id((const git_object *)root)+		);++	if (slash_pos == treeentry_path->ptr + offset) {+		giterr_set(GITERR_INVALID,+			"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);+		return -1;+	}++	*slash_pos = '\0';++	entry = git_tree_entry_byname(root, treeentry_path->ptr + offset);++	if (slash_pos != NULL)+		*slash_pos = '/';++	if (entry == NULL) {+		giterr_set(GITERR_TREE,+			"No tree entry can be found from "+			"the given tree and relative path '%s'.", treeentry_path->ptr);+		return GIT_ENOTFOUND;+	}+++	if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)+		return error;++	error = tree_frompath(+		parent_out,+		subtree,+		treeentry_path,+		(slash_pos - treeentry_path->ptr) + 1+	);++	git_tree_free(subtree);+	return error;+}++int git_tree_get_subtree(+	git_tree **subtree,+	git_tree *root,+	const char *subtree_path)+{+	int error;+	git_buf buffer = GIT_BUF_INIT;++	assert(subtree && root && subtree_path);++	if ((error = git_buf_sets(&buffer, subtree_path)) == 0)+		error = tree_frompath(subtree, root, &buffer, 0);++	git_buf_free(&buffer);++	return error;+}++static int tree_walk_post(+	git_tree *tree,+	git_treewalk_cb callback,+	git_buf *path,+	void *payload)+{+	int error = 0;+	unsigned int i;++	for (i = 0; i < tree->entries.length; ++i) {+		git_tree_entry *entry = tree->entries.contents[i];++		if (callback(path->ptr, entry, payload) < 0)+			continue;++		if (git_tree_entry__is_tree(entry)) {+			git_tree *subtree;+			size_t path_len = git_buf_len(path);++			if ((error = git_tree_lookup(+				&subtree, tree->object.repo, &entry->oid)) < 0)+				break;++			/* append the next entry to the path */+			git_buf_puts(path, entry->filename);+			git_buf_putc(path, '/');++			if (git_buf_oom(path))+				return -1;++			if (tree_walk_post(subtree, callback, path, payload) < 0)+				return -1;++			git_buf_truncate(path, path_len);+			git_tree_free(subtree);+		}+	}++	return 0;+}++int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload)+{+	int error = 0;+	git_buf root_path = GIT_BUF_INIT;++	switch (mode) {+		case GIT_TREEWALK_POST:+			error = tree_walk_post(tree, callback, &root_path, payload);+			break;++		case GIT_TREEWALK_PRE:+			tree_error("Preorder tree walking is still not implemented");+			return -1;++		default:+			giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");+			return -1;+	}++	git_buf_free(&root_path);++	return error;+}+
+ libgit2/src/tree.h view
@@ -0,0 +1,51 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_tree_h__+#define INCLUDE_tree_h__++#include "git2/tree.h"+#include "repository.h"+#include "odb.h"+#include "vector.h"++struct git_tree_entry {+	unsigned int attr;+	char *filename;+	git_oid oid;+	size_t filename_len;+	int removed;+};++struct git_tree {+	git_object object;+	git_vector entries;+};++struct git_treebuilder {+	git_vector entries;+};+++GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)+{+	return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));+}++void git_tree__free(git_tree *tree);+int git_tree__parse(git_tree *tree, git_odb_object *obj);++/**+ * Lookup the first position in the tree with a given prefix.+ *+ * @param tree a previously loaded tree.+ * @param prefix the beginning of a path to find in the tree.+ * @return index of the first item at or after the given prefix.+ */+int git_tree__prefix_position(git_tree *tree, const char *prefix);+++#endif
+ libgit2/src/tsort.c view
@@ -0,0 +1,367 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"++/**+ * An array-of-pointers implementation of Python's Timsort+ * Based on code by Christopher Swenson under the MIT license+ *+ * Copyright (c) 2010 Christopher Swenson+ * Copyright (c) 2011 Vicent Marti+ */++#ifndef MAX+#	define MAX(x,y) (((x) > (y) ? (x) : (y)))+#endif++#ifndef MIN+#	define MIN(x,y) (((x) < (y) ? (x) : (y)))+#endif++typedef int (*cmp_ptr_t)(const void *, const void *);++static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)+{+	int l, c, r;+	void *lx, *cx;++	assert(size > 0);++	l = 0;+	r = (int)size - 1;+	c = r >> 1;+	lx = dst[l];++	/* check for beginning conditions */+	if (cmp(x, lx) < 0)+		return 0;++	else if (cmp(x, lx) == 0) {+		int i = 1;+		while (cmp(x, dst[i]) == 0)+			i++;+		return i;+	}++	/* guaranteed not to be >= rx */+	cx = dst[c];+	while (1) {+		const int val = cmp(x, cx);+		if (val < 0) {+			if (c - l <= 1) return c;+			r = c;+		} else if (val > 0) {+			if (r - c <= 1) return c + 1;+			l = c;+			lx = cx;+		} else {+			do {+				cx = dst[++c];+			} while (cmp(x, cx) == 0);+			return c;+		}+		c = l + ((r - l) >> 1);+		cx = dst[c];+	}+}++/* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */+static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)+{+	size_t i;+	void *x;+	int location;++	for (i = start; i < size; i++) {+		int j;+		/* If this entry is already correct, just move along */+		if (cmp(dst[i - 1], dst[i]) <= 0)+			continue;++		/* Else we need to find the right place, shift everything over, and squeeze in */+		x = dst[i];+		location = binsearch(dst, x, i, cmp);+		for (j = (int)i - 1; j >= location; j--) {+			dst[j + 1] = dst[j];+		}+		dst[location] = x;+	}+}+++/* timsort implementation, based on timsort.txt */+struct tsort_run {+	ssize_t start;+	ssize_t length;+};++struct tsort_store {+	size_t alloc;+	cmp_ptr_t cmp;+	void **storage;+};++static void reverse_elements(void **dst, ssize_t start, ssize_t end)+{+	while (start < end) {+		void *tmp = dst[start];+		dst[start] = dst[end];+		dst[end] = tmp;++		start++;+		end--;+	}+}++static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)+{+	ssize_t curr = start + 2;++	if (size - start == 1)+		return 1;++	if (start >= size - 2) {+		if (store->cmp(dst[size - 2], dst[size - 1]) > 0) {+			void *tmp = dst[size - 1];+			dst[size - 1] = dst[size - 2];+			dst[size - 2] = tmp;+		}++		return 2;+	}++	if (store->cmp(dst[start], dst[start + 1]) <= 0) {+		while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) <= 0)+			curr++;++		return curr - start;+	} else {+		while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) > 0)+			curr++;++		/* reverse in-place */+		reverse_elements(dst, start, curr - 1);+		return curr - start;+	}+}++static size_t compute_minrun(size_t n)+{+	int r = 0;+	while (n >= 64) {+		r |= n & 1;+		n >>= 1;+	}+	return n + r;+}++static int check_invariant(struct tsort_run *stack, ssize_t stack_curr)+{+	if (stack_curr < 2)+		return 1;++	else if (stack_curr == 2) {+		const ssize_t A = stack[stack_curr - 2].length;+		const ssize_t B = stack[stack_curr - 1].length;+		return (A > B);+	} else {+		const ssize_t A = stack[stack_curr - 3].length;+		const ssize_t B = stack[stack_curr - 2].length;+		const ssize_t C = stack[stack_curr - 1].length;+		return !((A <= B + C) || (B <= C));+	}+}++static int resize(struct tsort_store *store, size_t new_size)+{+	if (store->alloc < new_size) {+		void **tempstore = git__realloc(store->storage, new_size * sizeof(void *));++		/**+		 * Do not propagate on OOM; this will abort the sort and+		 * leave the array unsorted, but no error code will be+		 * raised+		 */+		if (tempstore == NULL)+			return -1;++		store->storage = tempstore;+		store->alloc = new_size;+	}++	return 0;+}++static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store)+{+	const ssize_t A = stack[stack_curr - 2].length;+	const ssize_t B = stack[stack_curr - 1].length;+	const ssize_t curr = stack[stack_curr - 2].start;++	void **storage;+	ssize_t i, j, k;++	if (resize(store, MIN(A, B)) < 0)+		return;++	storage = store->storage;++	/* left merge */+	if (A < B) {+		memcpy(storage, &dst[curr], A * sizeof(void *));+		i = 0;+		j = curr + A;++		for (k = curr; k < curr + A + B; k++) {+			if ((i < A) && (j < curr + A + B)) {+				if (store->cmp(storage[i], dst[j]) <= 0)+					dst[k] = storage[i++];+				else+					dst[k] = dst[j++];+			} else if (i < A) {+				dst[k] = storage[i++];+			} else+				dst[k] = dst[j++];+		}+	} else {+		memcpy(storage, &dst[curr + A], B * sizeof(void *));+		i = B - 1;+		j = curr + A - 1;++		for (k = curr + A + B - 1; k >= curr; k--) {+			if ((i >= 0) && (j >= curr)) {+				if (store->cmp(dst[j], storage[i]) > 0)+					dst[k] = dst[j--];+				else+					dst[k] = storage[i--];+			} else if (i >= 0)+				dst[k] = storage[i--];+			else+				dst[k] = dst[j--];+		}+	}+}++static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store, ssize_t size)+{+	ssize_t A, B, C;++	while (1) {+		/* if the stack only has one thing on it, we are done with the collapse */+		if (stack_curr <= 1)+			break;++		/* if this is the last merge, just do it */+		if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) {+			merge(dst, stack, stack_curr, store);+			stack[0].length += stack[1].length;+			stack_curr--;+			break;+		}++		/* check if the invariant is off for a stack of 2 elements */+		else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) {+			merge(dst, stack, stack_curr, store);+			stack[0].length += stack[1].length;+			stack_curr--;+			break;+		}+		else if (stack_curr == 2)+			break;++		A = stack[stack_curr - 3].length;+		B = stack[stack_curr - 2].length;+		C = stack[stack_curr - 1].length;++		/* check first invariant */+		if (A <= B + C) {+			if (A < C) {+				merge(dst, stack, stack_curr - 1, store);+				stack[stack_curr - 3].length += stack[stack_curr - 2].length;+				stack[stack_curr - 2] = stack[stack_curr - 1];+				stack_curr--;+			} else {+				merge(dst, stack, stack_curr, store);+				stack[stack_curr - 2].length += stack[stack_curr - 1].length;+				stack_curr--;+			}+		} else if (B <= C) {+			merge(dst, stack, stack_curr, store);+			stack[stack_curr - 2].length += stack[stack_curr - 1].length;+			stack_curr--;+		} else+			break;+	}++	return stack_curr;+}++#define PUSH_NEXT() do {\+	len = count_run(dst, curr, size, store);\+	run = minrun;\+	if (run < minrun) run = minrun;\+	if (run > (ssize_t)size - curr) run = size - curr;\+	if (run > len) {\+		bisort(&dst[curr], len, run, cmp);\+		len = run;\+	}\+	run_stack[stack_curr].start = curr;\+	run_stack[stack_curr++].length = len;\+	curr += len;\+	if (curr == (ssize_t)size) {\+		/* finish up */ \+		while (stack_curr > 1) { \+			merge(dst, run_stack, stack_curr, store); \+			run_stack[stack_curr - 2].length += run_stack[stack_curr - 1].length; \+			stack_curr--; \+		} \+		if (store->storage != NULL) {\+			git__free(store->storage);\+			store->storage = NULL;\+		}\+		return;\+	}\+}\+while (0)++void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)+{+	struct tsort_store _store, *store = &_store;+	struct tsort_run run_stack[128];++	ssize_t stack_curr = 0;+	ssize_t len, run;+	ssize_t curr = 0;+	ssize_t minrun;++	if (size < 64) {+		bisort(dst, 1, size, cmp);+		return;+	}++	/* compute the minimum run length */+	minrun = (ssize_t)compute_minrun(size);++	/* temporary storage for merges */+	store->alloc = 0;+	store->storage = NULL;+	store->cmp = cmp;++	PUSH_NEXT();+	PUSH_NEXT();+	PUSH_NEXT();++	while (1) {+		if (!check_invariant(run_stack, stack_curr)) {+			stack_curr = collapse(dst, run_stack, stack_curr, store, size);+			continue;+		}++		PUSH_NEXT();+	}+}
+ libgit2/src/unix/map.c view
@@ -0,0 +1,54 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include <git2/common.h>++#ifndef GIT_WIN32++#include "map.h"+#include <sys/mman.h>+#include <errno.h>++int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)+{+	int mprot = 0;+	int mflag = 0;++	GIT_MMAP_VALIDATE(out, len, prot, flags);++	out->data = NULL;+	out->len = 0;++	if (prot & GIT_PROT_WRITE)+		mprot = PROT_WRITE;+	else if (prot & GIT_PROT_READ)+		mprot = PROT_READ;++	if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)+		mflag = MAP_SHARED;+	else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE)+		mflag = MAP_PRIVATE;++	out->data = mmap(NULL, len, mprot, mflag, fd, offset);+	if (!out->data || out->data == MAP_FAILED) {+		giterr_set(GITERR_OS, "Failed to mmap. Could not write data");+		return -1;+	}++	out->len = len;++	return 0;+}++int p_munmap(git_map *map)+{+	assert(map != NULL);+	munmap(map->data, map->len);+	return 0;+}++#endif+
+ libgit2/src/unix/posix.h view
@@ -0,0 +1,31 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_posix__w32_h__+#define INCLUDE_posix__w32_h__++#ifndef __sun+#	include <fnmatch.h>+#	define p_fnmatch(p, s, f) fnmatch(p, s, f)+#else+#	include "compat/fnmatch.h"+#endif++#include <stdio.h>++#define p_lstat(p,b) lstat(p,b)+#define p_readlink(a, b, c) readlink(a, b, c)+#define p_link(o,n) link(o, n)+#define p_unlink(p) unlink(p)+#define p_mkdir(p,m) mkdir(p, m)+#define p_fsync(fd) fsync(fd)+#define p_realpath(p, po) realpath(p, po)+#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)+#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)+#define p_mkstemp(p) mkstemp(p)+#define p_setenv(n,v,o) setenv(n,v,o)++#endif
+ libgit2/src/util.c view
@@ -0,0 +1,437 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include <git2.h>+#include "common.h"+#include <stdarg.h>+#include <stdio.h>+#include <ctype.h>+#include "posix.h"++#ifdef _MSC_VER+# include <Shlwapi.h>+#endif++void git_libgit2_version(int *major, int *minor, int *rev)+{+	*major = LIBGIT2_VER_MAJOR;+	*minor = LIBGIT2_VER_MINOR;+	*rev = LIBGIT2_VER_REVISION;+}++void git_strarray_free(git_strarray *array)+{+	size_t i;+	for (i = 0; i < array->count; ++i)+		git__free(array->strings[i]);++	git__free(array->strings);+}++int git_strarray_copy(git_strarray *tgt, const git_strarray *src)+{+	size_t i;++	assert(tgt && src);++	memset(tgt, 0, sizeof(*tgt));++	if (!src->count)+		return 0;++	tgt->strings = git__calloc(src->count, sizeof(char *));+	GITERR_CHECK_ALLOC(tgt->strings);++	for (i = 0; i < src->count; ++i) {+		tgt->strings[tgt->count] = git__strdup(src->strings[i]);++		if (!tgt->strings[tgt->count]) {+			git_strarray_free(tgt);+			memset(tgt, 0, sizeof(*tgt));+			return -1;+		}++		tgt->count++;+	}++	return 0;+}++int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int base)+{+	const char *p;+	int64_t n, nn;+	int c, ovfl, v, neg, ndig;++	p = nptr;+	neg = 0;+	n = 0;+	ndig = 0;+	ovfl = 0;++	/*+	 * White space+	 */+	while (git__isspace(*p))+		p++;++	/*+	 * Sign+	 */+	if (*p == '-' || *p == '+')+		if (*p++ == '-')+			neg = 1;++	/*+	 * Base+	 */+	if (base == 0) {+		if (*p != '0')+			base = 10;+		else {+			base = 8;+			if (p[1] == 'x' || p[1] == 'X') {+				p += 2;+				base = 16;+			}+		}+	} else if (base == 16 && *p == '0') {+		if (p[1] == 'x' || p[1] == 'X')+			p += 2;+	} else if (base < 0 || 36 < base)+		goto Return;++	/*+	 * Non-empty sequence of digits+	 */+	for (;; p++,ndig++) {+		c = *p;+		v = base;+		if ('0'<=c && c<='9')+			v = c - '0';+		else if ('a'<=c && c<='z')+			v = c - 'a' + 10;+		else if ('A'<=c && c<='Z')+			v = c - 'A' + 10;+		if (v >= base)+			break;+		nn = n*base + v;+		if (nn < n)+			ovfl = 1;+		n = nn;+	}++Return:+	if (ndig == 0) {+		giterr_set(GITERR_INVALID, "Failed to convert string to long. Not a number");+		return -1;+	}++	if (endptr)+		*endptr = p;++	if (ovfl) {+		giterr_set(GITERR_INVALID, "Failed to convert string to long. Overflow error");+		return -1;+	}++	*result = neg ? -n : n;+	return 0;+}++int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base)+{+	int error;+	int32_t tmp_int;+	int64_t tmp_long;++	if ((error = git__strtol64(&tmp_long, nptr, endptr, base)) < 0)+		return error;++	tmp_int = tmp_long & 0xFFFFFFFF;+	if (tmp_int != tmp_long) {+		giterr_set(GITERR_INVALID, "Failed to convert. '%s' is too large", nptr);+		return -1;+	}++	*result = tmp_int;++	return error;+}++void git__strntolower(char *str, size_t len)+{+	size_t i;++	for (i = 0; i < len; ++i) {+		str[i] = (char) tolower(str[i]);+	}+}++void git__strtolower(char *str)+{+	git__strntolower(str, strlen(str));+}++int git__prefixcmp(const char *str, const char *prefix)+{+	for (;;) {+		char p = *(prefix++), s;+		if (!p)+			return 0;+		if ((s = *(str++)) != p)+			return s - p;+	}+}++int git__suffixcmp(const char *str, const char *suffix)+{+	size_t a = strlen(str);+	size_t b = strlen(suffix);+	if (a < b)+		return -1;+	return strcmp(str + (a - b), suffix);+}++char *git__strtok(char **end, const char *sep)+{+	char *ptr = *end;++	while (*ptr && strchr(sep, *ptr))+		++ptr;++	if (*ptr) {+		char *start = ptr;+		*end = start + 1;++		while (**end && !strchr(sep, **end))+			++*end;++		if (**end) {+			**end = '\0';+			++*end;+		}++		return start;+	}++	return NULL;+}++void git__hexdump(const char *buffer, size_t len)+{+	static const size_t LINE_WIDTH = 16;++	size_t line_count, last_line, i, j;+	const char *line;++	line_count = (len / LINE_WIDTH);+	last_line = (len % LINE_WIDTH);++	for (i = 0; i < line_count; ++i) {+		line = buffer + (i * LINE_WIDTH);+		for (j = 0; j < LINE_WIDTH; ++j, ++line)+			printf("%02X ", (unsigned char)*line & 0xFF);++		printf("| ");++		line = buffer + (i * LINE_WIDTH);+		for (j = 0; j < LINE_WIDTH; ++j, ++line)+			printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');++		printf("\n");+	}++	if (last_line > 0) {++		line = buffer + (line_count * LINE_WIDTH);+		for (j = 0; j < last_line; ++j, ++line)+			printf("%02X ", (unsigned char)*line & 0xFF);++		for (j = 0; j < (LINE_WIDTH - last_line); ++j)+			printf("	");++		printf("| ");++		line = buffer + (line_count * LINE_WIDTH);+		for (j = 0; j < last_line; ++j, ++line)+			printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');++		printf("\n");+	}++	printf("\n");+}++#ifdef GIT_LEGACY_HASH+uint32_t git__hash(const void *key, int len, unsigned int seed)+{+	const uint32_t m = 0x5bd1e995;+	const int r = 24;+	uint32_t h = seed ^ len;++	const unsigned char *data = (const unsigned char *)key;++	while(len >= 4) {+		uint32_t k = *(uint32_t *)data;++		k *= m;+		k ^= k >> r;+		k *= m;++		h *= m;+		h ^= k;++		data += 4;+		len -= 4;+	}++	switch(len) {+	case 3: h ^= data[2] << 16;+	case 2: h ^= data[1] << 8;+	case 1: h ^= data[0];+			h *= m;+	};++	h ^= h >> 13;+	h *= m;+	h ^= h >> 15;++	return h;+}+#else+/*+	Cross-platform version of Murmurhash3+	http://code.google.com/p/smhasher/wiki/MurmurHash3+	by Austin Appleby (aappleby@gmail.com)++	This code is on the public domain.+*/+uint32_t git__hash(const void *key, int len, uint32_t seed)+{++#define MURMUR_BLOCK() {\+	k1 *= c1; \+	k1 = git__rotl(k1,11);\+	k1 *= c2;\+	h1 ^= k1;\+	h1 = h1*3 + 0x52dce729;\+	c1 = c1*5 + 0x7b7d159c;\+	c2 = c2*5 + 0x6bce6396;\+}++	const uint8_t *data = (const uint8_t*)key;+	const int nblocks = len / 4;++	const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);+	const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);++	uint32_t h1 = 0x971e137b ^ seed;+	uint32_t k1;++	uint32_t c1 = 0x95543787;+	uint32_t c2 = 0x2ad7eb25;++	int i;++	for (i = -nblocks; i; i++) {+		k1 = blocks[i];+		MURMUR_BLOCK();+	}++	k1 = 0;++	switch(len & 3) {+	case 3: k1 ^= tail[2] << 16;+	case 2: k1 ^= tail[1] << 8;+	case 1: k1 ^= tail[0];+			MURMUR_BLOCK();+	}++	h1 ^= len;+	h1 ^= h1 >> 16;+	h1 *= 0x85ebca6b;+	h1 ^= h1 >> 13;+	h1 *= 0xc2b2ae35;+	h1 ^= h1 >> 16;++	return h1;+}+#endif++/**+ * A modified `bsearch` from the BSD glibc.+ *+ * Copyright (c) 1990 Regents of the University of California.+ * All rights reserved.+ */+int git__bsearch(+	void **array,+	size_t array_len,+	const void *key,+	int (*compare)(const void *, const void *),+	size_t *position)+{+	unsigned int lim;+	int cmp = -1;+	void **part, **base = array;++	for (lim = (unsigned int)array_len; lim != 0; lim >>= 1) {+		part = base + (lim >> 1);+		cmp = (*compare)(key, *part);+		if (cmp == 0) {+			base = part;+			break;+		}+		if (cmp > 0) { /* key > p; take right partition */+			base = part + 1;+			lim--;+		} /* else take left partition */+	}++	if (position)+		*position = (base - array);++	return (cmp == 0) ? 0 : -1;+}++/**+ * A strcmp wrapper+ * + * We don't want direct pointers to the CRT on Windows, we may+ * get stdcall conflicts.+ */+int git__strcmp_cb(const void *a, const void *b)+{+	const char *stra = (const char *)a;+	const char *strb = (const char *)b;++	return strcmp(stra, strb);+}++int git__parse_bool(int *out, const char *value)+{+	/* A missing value means true */+	if (value == NULL) {+		*out = 1;+		return 0;+	}++	if (!strcasecmp(value, "true") ||+		!strcasecmp(value, "yes") ||+		!strcasecmp(value, "on")) {+		*out = 1;+		return 0;+	}+	if (!strcasecmp(value, "false") ||+		!strcasecmp(value, "no") ||+		!strcasecmp(value, "off")) {+		*out = 0;+		return 0;+	}++	return -1;+}
+ libgit2/src/util.h view
@@ -0,0 +1,226 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_util_h__+#define INCLUDE_util_h__++#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))+#define bitsizeof(x) (CHAR_BIT * sizeof(x))+#define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits))))+#ifndef min+# define min(a,b) ((a) < (b) ? (a) : (b))+#endif++/*+ * Custom memory allocation wrappers+ * that set error code and error message+ * on allocation failure+ */+GIT_INLINE(void *) git__malloc(size_t len)+{+	void *ptr = malloc(len);+	if (!ptr) giterr_set_oom();+	return ptr;+}++GIT_INLINE(void *) git__calloc(size_t nelem, size_t elsize)+{+	void *ptr = calloc(nelem, elsize);+	if (!ptr) giterr_set_oom();+	return ptr;+}++GIT_INLINE(char *) git__strdup(const char *str)+{+	char *ptr = strdup(str);+	if (!ptr) giterr_set_oom();+	return ptr;+}++GIT_INLINE(char *) git__strndup(const char *str, size_t n)+{+	size_t length;+	char *ptr;++	length = strlen(str);+	if (n < length)+		length = n;++	ptr = (char*)malloc(length + 1);+	if (!ptr) {+		giterr_set_oom();+		return NULL;+	}++	memcpy(ptr, str, length);+	ptr[length] = '\0';++	return ptr;+}++GIT_INLINE(void *) git__realloc(void *ptr, size_t size)+{+	void *new_ptr = realloc(ptr, size);+	if (!new_ptr) giterr_set_oom();+	return new_ptr;+}++#define git__free(ptr) free(ptr)++extern int git__prefixcmp(const char *str, const char *prefix);+extern int git__suffixcmp(const char *str, const char *suffix);++extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);+extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base);++extern void git__hexdump(const char *buffer, size_t n);+extern uint32_t git__hash(const void *key, int len, uint32_t seed);++/** @return true if p fits into the range of a size_t */+GIT_INLINE(int) git__is_sizet(git_off_t p)+{+	size_t r = (size_t)p;+	return p == (git_off_t)r;+}++/* 32-bit cross-platform rotl */+#ifdef _MSC_VER /* use built-in method in MSVC */+#	define git__rotl(v, s) (uint32_t)_rotl(v, s)+#else /* use bitops in GCC; with o2 this gets optimized to a rotl instruction */+#	define git__rotl(v, s) (uint32_t)(((uint32_t)(v) << (s)) | ((uint32_t)(v) >> (32 - (s))))+#endif++extern char *git__strtok(char **end, const char *sep);++extern void git__strntolower(char *str, size_t len);+extern void git__strtolower(char *str);++GIT_INLINE(const char *) git__next_line(const char *s)+{+	while (*s && *s != '\n') s++;+	while (*s == '\n' || *s == '\r') s++;+	return s;+}++extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *));++/**+ * @param position If non-NULL, this will be set to the position where the+ * 		element is or would be inserted if not found.+ * @return pos (>=0) if found or -1 if not found+ */+extern int git__bsearch(+	void **array,+	size_t array_len,+	const void *key,+	int (*compare)(const void *, const void *),+	size_t *position);++extern int git__strcmp_cb(const void *a, const void *b);++typedef struct {+	short refcount;+	void *owner;+} git_refcount;++typedef void (*git_refcount_freeptr)(void *r);++#define GIT_REFCOUNT_INC(r) { \+	((git_refcount *)(r))->refcount++; \+}++#define GIT_REFCOUNT_DEC(_r, do_free) { \+	git_refcount *r = (git_refcount *)(_r); \+	r->refcount--; \+	if (r->refcount <= 0 && r->owner == NULL) { do_free(_r); } \+}++#define GIT_REFCOUNT_OWN(r, o) { \+	((git_refcount *)(r))->owner = o; \+}++#define GIT_REFCOUNT_OWNER(r) (((git_refcount *)(r))->owner)++static signed char from_hex[] = {+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20 */+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 30 */+-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 40 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 */+-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a0 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* b0 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* c0 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* d0 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e0 */+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* f0 */+};++GIT_INLINE(int) git__fromhex(char h)+{+	return from_hex[(unsigned char) h];+}++GIT_INLINE(int) git__ishex(const char *str)+{+	unsigned i;+	for (i=0; i<strlen(str); i++)+		if (git__fromhex(str[i]) < 0)+			return 0;+	return 1;+}++GIT_INLINE(size_t) git__size_t_bitmask(size_t v)+{+	v--;+	v |= v >> 1;+	v |= v >> 2;+	v |= v >> 4;+	v |= v >> 8;+	v |= v >> 16;++	return v;+}++GIT_INLINE(size_t) git__size_t_powerof2(size_t v)+{+	return git__size_t_bitmask(v) + 1;+}++GIT_INLINE(bool) git__isupper(int c)+{+    return (c >= 'A' && c <= 'Z');+}++GIT_INLINE(bool) git__isalpha(int c)+{+    return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));+}++GIT_INLINE(bool) git__isspace(int c)+{+    return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');+}++GIT_INLINE(bool) git__iswildcard(int c)+{+	return (c == '*' || c == '?' || c == '[');+}++/*+ * Parse a string value as a boolean, just like Core Git+ * does.+ *+ * Valid values for true are: 'true', 'yes', 'on'+ * Valid values for false are: 'false', 'no', 'off'+ */+extern int git__parse_bool(int *out, const char *value);++#endif /* INCLUDE_util_h__ */
+ libgit2/src/vector.c view
@@ -0,0 +1,227 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "repository.h"+#include "vector.h"++static const double resize_factor = 1.75;+static const unsigned int minimum_size = 8;++static int resize_vector(git_vector *v)+{+	v->_alloc_size = ((unsigned int)(v->_alloc_size * resize_factor)) + 1;+	if (v->_alloc_size < minimum_size)+		v->_alloc_size = minimum_size;++	v->contents = git__realloc(v->contents, v->_alloc_size * sizeof(void *));+	GITERR_CHECK_ALLOC(v->contents);++	return 0;+}++void git_vector_free(git_vector *v)+{+	assert(v);++	git__free(v->contents);+	v->contents = NULL;++	v->length = 0;+	v->_alloc_size = 0;+}++int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp)+{+	assert(v);++	memset(v, 0x0, sizeof(git_vector));++	if (initial_size == 0)+		initial_size = minimum_size;++	v->_alloc_size = initial_size;+	v->_cmp = cmp;++	v->length = 0;+	v->sorted = 1;++	v->contents = git__malloc(v->_alloc_size * sizeof(void *));+	GITERR_CHECK_ALLOC(v->contents);++	return 0;+}++int git_vector_insert(git_vector *v, void *element)+{+	assert(v);++	if (v->length >= v->_alloc_size &&+		resize_vector(v) < 0)+		return -1;++	v->contents[v->length++] = element;+	v->sorted = 0;++	return 0;+}++int git_vector_insert_sorted(+	git_vector *v, void *element, int (*on_dup)(void **old, void *new))+{+	int result;+	size_t pos;++	assert(v && v->_cmp);++	if (!v->sorted)+		git_vector_sort(v);++	if (v->length >= v->_alloc_size &&+		resize_vector(v) < 0)+		return -1;++	/* If we find the element and have a duplicate handler callback,+	 * invoke it.  If it returns non-zero, then cancel insert, otherwise+	 * proceed with normal insert.+	 */+	if (git__bsearch(v->contents, v->length, element, v->_cmp, &pos) >= 0 &&+		on_dup != NULL &&+		(result = on_dup(&v->contents[pos], element)) < 0)+		return result;++	/* shift elements to the right */+	if (pos < v->length) {+		memmove(v->contents + pos + 1, v->contents + pos,+		        (v->length - pos) * sizeof(void *));+	}++	v->contents[pos] = element;+	v->length++;+	return 0;+}++void git_vector_sort(git_vector *v)+{+	assert(v);++	if (v->sorted || v->_cmp == NULL)+		return;++	git__tsort(v->contents, v->length, v->_cmp);+	v->sorted = 1;+}++int git_vector_bsearch3(+	unsigned int *at_pos,+	git_vector *v,+	git_vector_cmp key_lookup,+	const void *key)+{+	int rval;+	size_t pos;++	assert(v && key && key_lookup);++	/* need comparison function to sort the vector */+	assert(v->_cmp != NULL);++	git_vector_sort(v);++	rval = git__bsearch(v->contents, v->length, key, key_lookup, &pos);++	if (at_pos != NULL)+		*at_pos = (unsigned int)pos;++	return (rval >= 0) ? (int)pos : GIT_ENOTFOUND;+}++int git_vector_search2(+	git_vector *v, git_vector_cmp key_lookup, const void *key)+{+	unsigned int i;++	assert(v && key && key_lookup);++	for (i = 0; i < v->length; ++i) {+		if (key_lookup(key, v->contents[i]) == 0)+			return i;+	}++	return GIT_ENOTFOUND;+}++static int strict_comparison(const void *a, const void *b)+{+	return (a == b) ? 0 : -1;+}++int git_vector_search(git_vector *v, const void *entry)+{+	return git_vector_search2(v, v->_cmp ? v->_cmp : strict_comparison, entry);+}++int git_vector_remove(git_vector *v, unsigned int idx)+{+	unsigned int i;++	assert(v);++	if (idx >= v->length || v->length == 0)+		return GIT_ENOTFOUND;++	for (i = idx; i < v->length - 1; ++i)+		v->contents[i] = v->contents[i + 1];++	v->length--;+	return 0;+}++void git_vector_pop(git_vector *v)+{+	if (v->length > 0)+		v->length--;+}++void git_vector_uniq(git_vector *v)+{+	git_vector_cmp cmp;+	unsigned int i, j;++	if (v->length <= 1)+		return;++	git_vector_sort(v);+	cmp = v->_cmp ? v->_cmp : strict_comparison;++	for (i = 0, j = 1 ; j < v->length; ++j)+		if (!cmp(v->contents[i], v->contents[j]))+			v->contents[i] = v->contents[j];+		else+			v->contents[++i] = v->contents[j];++	v->length -= j - i - 1;+}++void git_vector_clear(git_vector *v)+{+	assert(v);+	v->length = 0;+	v->sorted = 1;+}++void git_vector_swap(git_vector *a, git_vector *b)+{+	git_vector t;++	if (!a || !b || a == b)+		return;++	memcpy(&t, a, sizeof(t));+	memcpy(a, b, sizeof(t));+	memcpy(b, &t, sizeof(t));+}
+ libgit2/src/vector.h view
@@ -0,0 +1,78 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_vector_h__+#define INCLUDE_vector_h__++#include "git2/common.h"++typedef int (*git_vector_cmp)(const void *, const void *);++typedef struct git_vector {+	unsigned int _alloc_size;+	git_vector_cmp _cmp;+	void **contents;+	unsigned int length;+	int sorted;+} git_vector;++#define GIT_VECTOR_INIT {0}++int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp);+void git_vector_free(git_vector *v);+void git_vector_clear(git_vector *v);+void git_vector_swap(git_vector *a, git_vector *b);++void git_vector_sort(git_vector *v);++int git_vector_search(git_vector *v, const void *entry);+int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key);++int git_vector_bsearch3(+	unsigned int *at_pos, git_vector *v, git_vector_cmp cmp, const void *key);++GIT_INLINE(int) git_vector_bsearch(git_vector *v, const void *key)+{+	return git_vector_bsearch3(NULL, v, v->_cmp, key);+}++GIT_INLINE(int) git_vector_bsearch2(+	git_vector *v, git_vector_cmp cmp, const void *key)+{+	return git_vector_bsearch3(NULL, v, cmp, key);+}++GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)+{+	return (position < v->length) ? v->contents[position] : NULL;+}++GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int position)+{+	return (position < v->length) ? v->contents[position] : NULL;+}++#define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)++GIT_INLINE(void *) git_vector_last(git_vector *v)+{+	return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;+}++#define git_vector_foreach(v, iter, elem)	\+	for ((iter) = 0; (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )++#define git_vector_rforeach(v, iter, elem)	\+	for ((iter) = (v)->length; (iter) > 0 && ((elem) = (v)->contents[(iter)-1], 1); (iter)-- )++int git_vector_insert(git_vector *v, void *element);+int git_vector_insert_sorted(git_vector *v, void *element,+	int (*on_dup)(void **old, void *new));+int git_vector_remove(git_vector *v, unsigned int idx);+void git_vector_pop(git_vector *v);+void git_vector_uniq(git_vector *v);++#endif
+ libgit2/src/win32/dir.c view
@@ -0,0 +1,155 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#define GIT__WIN32_NO_WRAP_DIR+#include "dir.h"+#include "utf-conv.h"+#include "git2/windows.h"++static int init_filter(char *filter, size_t n, const char *dir)+{+	size_t len = strlen(dir);++	if (len+3 >= n)+		return 0;++	strcpy(filter, dir);+	if (len && dir[len-1] != '/')+		strcat(filter, "/");+	strcat(filter, "*");++	return 1;+}++git__DIR *git__opendir(const char *dir)+{+	char filter[4096];+	wchar_t* filter_w = NULL;+	git__DIR *new = NULL;++	if (!dir || !init_filter(filter, sizeof(filter), dir))+		return NULL;++	new = git__malloc(sizeof(*new));+	if (!new)+		return NULL;++	new->dir = git__strdup(dir);+	if (!new->dir)+		goto fail;++	filter_w = gitwin_to_utf16(filter);+	if (!filter_w)+		goto fail;++	new->h = FindFirstFileW(filter_w, &new->f);+	git__free(filter_w);++	if (new->h == INVALID_HANDLE_VALUE) {+		giterr_set(GITERR_OS, "Could not open directory '%s'", dir);+		goto fail;+	}++	new->first = 1;+	return new;++fail:+	git__free(new->dir);+	git__free(new);+	return NULL;+}++int git__readdir_ext(+	git__DIR *d,+	struct git__dirent *entry,+	struct git__dirent **result,+	int *is_dir)+{+	if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE)+		return -1;++	*result = NULL;++	if (d->first)+		d->first = 0;+	else if (!FindNextFileW(d->h, &d->f)) {+		if (GetLastError() == ERROR_NO_MORE_FILES)+			return 0;+		giterr_set(GITERR_OS, "Could not read from directory '%s'", d->dir);+		return -1;+	}++	if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))+		return -1;++	entry->d_ino = 0;++	if (WideCharToMultiByte(+		gitwin_get_codepage(), 0, d->f.cFileName, -1,+		entry->d_name, GIT_PATH_MAX, NULL, NULL) == 0)+	{+		giterr_set(GITERR_OS, "Could not convert filename to UTF-8");+		return -1;+	}++	*result = entry;++	if (is_dir != NULL)+		*is_dir = ((d->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);++	return 0;+}++struct git__dirent *git__readdir(git__DIR *d)+{+	struct git__dirent *result;+	if (git__readdir_ext(d, &d->entry, &result, NULL) < 0)+		return NULL;+	return result;+}++void git__rewinddir(git__DIR *d)+{+	char filter[4096];+	wchar_t* filter_w;++	if (!d)+		return;++	if (d->h != INVALID_HANDLE_VALUE) {+		FindClose(d->h);+		d->h = INVALID_HANDLE_VALUE;+		d->first = 0;+	}++	if (!init_filter(filter, sizeof(filter), d->dir) ||+		(filter_w = gitwin_to_utf16(filter)) == NULL)+		return;++	d->h = FindFirstFileW(filter_w, &d->f);+	git__free(filter_w);++	if (d->h == INVALID_HANDLE_VALUE)+		giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir);+	else+		d->first = 1;+}++int git__closedir(git__DIR *d)+{+	if (!d)+		return 0;++	if (d->h != INVALID_HANDLE_VALUE) {+		FindClose(d->h);+		d->h = INVALID_HANDLE_VALUE;+	}+	git__free(d->dir);+	d->dir = NULL;+	git__free(d);+	return 0;+}+
+ libgit2/src/win32/dir.h view
@@ -0,0 +1,42 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_dir_h__+#define INCLUDE_dir_h__++#include "common.h"++struct git__dirent {+	int d_ino;+	char d_name[261];+};++typedef struct {+	HANDLE h;+	WIN32_FIND_DATAW f;+	struct git__dirent entry;+	char *dir;+	int first;+} git__DIR;++extern git__DIR *git__opendir(const char *);+extern struct git__dirent *git__readdir(git__DIR *);+extern int git__readdir_ext(+	git__DIR *, struct git__dirent *, struct git__dirent **, int *);+extern void git__rewinddir(git__DIR *);+extern int git__closedir(git__DIR *);++# ifndef GIT__WIN32_NO_WRAP_DIR+#	define dirent git__dirent+#	define DIR git__DIR+#	define opendir	git__opendir+#	define readdir	git__readdir+#   define readdir_r(d,e,r) git__readdir_ext((d),(e),(r),NULL)+#	define rewinddir git__rewinddir+#	define closedir git__closedir+# endif++#endif /* INCLUDE_dir_h__ */
+ libgit2/src/win32/map.c view
@@ -0,0 +1,115 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "map.h"+#include <errno.h>+++static DWORD get_page_size(void)+{+	static DWORD page_size;+	SYSTEM_INFO sys;++	if (!page_size) {+		GetSystemInfo(&sys);+		page_size = sys.dwAllocationGranularity;+	}++	return page_size;+}++int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)+{+	HANDLE fh = (HANDLE)_get_osfhandle(fd);+	DWORD page_size = get_page_size();+	DWORD fmap_prot = 0;+	DWORD view_prot = 0;+	DWORD off_low = 0;+	DWORD off_hi = 0;+	git_off_t page_start;+	git_off_t page_offset;++	GIT_MMAP_VALIDATE(out, len, prot, flags);++	out->data = NULL;+	out->len = 0;+	out->fmh = NULL;++	if (fh == INVALID_HANDLE_VALUE) {+		errno = EBADF;+		giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");+		return -1;+	}++	if (prot & GIT_PROT_WRITE)+		fmap_prot |= PAGE_READWRITE;+	else if (prot & GIT_PROT_READ)+		fmap_prot |= PAGE_READONLY;++	if (prot & GIT_PROT_WRITE)+		view_prot |= FILE_MAP_WRITE;+	if (prot & GIT_PROT_READ)+		view_prot |= FILE_MAP_READ;++	page_start = (offset / page_size) * page_size;+	page_offset = offset - page_start;++	if (page_offset != 0) { /* offset must be multiple of page size */+		errno = EINVAL;+		giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of page size");+		return -1;+	}++	out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL);+	if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) {+		giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");+		out->fmh = NULL;+		return -1;+	}++	assert(sizeof(git_off_t) == 8);++	off_low = (DWORD)(page_start);+	off_hi = (DWORD)(page_start >> 32);+	out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len);+	if (!out->data) {+		giterr_set(GITERR_OS, "Failed to mmap. No data written");+		CloseHandle(out->fmh);+		out->fmh = NULL;+		return -1;+	}+	out->len = len;++	return 0;+}++int p_munmap(git_map *map)+{+	int error = 0;++	assert(map != NULL);++	if (map->data) {+		if (!UnmapViewOfFile(map->data)) {+			giterr_set(GITERR_OS, "Failed to munmap. Could not unmap view of file");+			error = -1;+		}+		map->data = NULL;+	}++	if (map->fmh) {+		if (!CloseHandle(map->fmh)) {+			giterr_set(GITERR_OS, "Failed to munmap. Could not close handle");+			error = -1;+		}+		map->fmh = NULL;+	}++	return error;+}++
+ libgit2/src/win32/mingw-compat.h view
@@ -0,0 +1,24 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_mingw_compat__+#define INCLUDE_mingw_compat__++#if defined(__MINGW32__)++/* use a 64-bit file offset type */+# define lseek _lseeki64+# define stat _stati64+# define fstat _fstati64++/* stat: file mode type testing macros */+# define _S_IFLNK 0120000+# define S_IFLNK _S_IFLNK+# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)++#endif++#endif /* INCLUDE_mingw_compat__ */
+ libgit2/src/win32/msvc-compat.h view
@@ -0,0 +1,41 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_msvc_compat__+#define INCLUDE_msvc_compat__++#if defined(_MSC_VER)++/* access() mode parameter #defines	*/+#	define F_OK 0 /* existence check */+#	define W_OK 2 /* write mode check */+#	define R_OK 4 /* read mode check */++#	define lseek _lseeki64+#	define stat _stat64+#	define fstat _fstat64++/* stat: file mode type testing macros */+#	define _S_IFLNK 0120000+#	define S_IFLNK _S_IFLNK++#	define S_ISDIR(m)	(((m) & _S_IFMT) == _S_IFDIR)+#	define S_ISREG(m)	(((m) & _S_IFMT) == _S_IFREG)+#	define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)+#	define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)++#	define mode_t unsigned short++/* case-insensitive string comparison */+#	define strcasecmp	_stricmp+#	define strncasecmp _strnicmp++/* MSVC doesn't define ssize_t at all */+typedef SSIZE_T ssize_t;++#endif++#endif /* INCLUDE_msvc_compat__ */
+ libgit2/src/win32/posix.h view
@@ -0,0 +1,55 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#ifndef INCLUDE_posix__w32_h__+#define INCLUDE_posix__w32_h__++#include "common.h"+#include "compat/fnmatch.h"+#include "utf-conv.h"++GIT_INLINE(int) p_link(const char *old, const char *new)+{+	GIT_UNUSED(old);+	GIT_UNUSED(new);+	errno = ENOSYS;+	return -1;+}++GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)+{+	wchar_t* buf = gitwin_to_utf16(path);+	int ret = _wmkdir(buf);++	GIT_UNUSED(mode);++	git__free(buf);+	return ret;+}++extern int p_unlink(const char *path);+extern int p_lstat(const char *file_name, struct stat *buf);+extern int p_readlink(const char *link, char *target, size_t target_len);+extern int p_hide_directory__w32(const char *path);+extern char *p_realpath(const char *orig_path, char *buffer);+extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);+extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4);+extern int p_mkstemp(char *tmp_path);+extern int p_setenv(const char* name, const char* value, int overwrite);+extern int p_stat(const char* path, struct stat* buf);+extern int p_chdir(const char* path);+extern int p_chmod(const char* path, mode_t mode);+extern int p_rmdir(const char* path);+extern int p_access(const char* path, mode_t mode);+extern int p_fsync(int fd);+extern int p_open(const char *path, int flags, ...);+extern int p_creat(const char *path, mode_t mode);+extern int p_getcwd(char *buffer_out, size_t size);+extern int p_rename(const char *from, const char *to);+extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);+extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);++#endif
+ libgit2/src/win32/posix_w32.c view
@@ -0,0 +1,472 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */+#include "../posix.h"+#include "path.h"+#include "utf-conv.h"+#include <errno.h>+#include <io.h>+#include <fcntl.h>+++int p_unlink(const char *path)+{+	int ret = 0;+	wchar_t* buf;++	if ((buf = gitwin_to_utf16(path)) != NULL) {+		_wchmod(buf, 0666);+		ret = _wunlink(buf);+		git__free(buf);+	}++	return ret;+}++int p_fsync(int fd)+{+	HANDLE fh = (HANDLE)_get_osfhandle(fd);++	if (fh == INVALID_HANDLE_VALUE) {+		errno = EBADF;+		return -1;+	}++	if (!FlushFileBuffers(fh)) {+		DWORD code = GetLastError();++		if (code == ERROR_INVALID_HANDLE)+			errno = EINVAL;+		else+			errno = EIO;++		return -1;+	}++	return 0;+}++GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft)+{+	long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;+	winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */+	winTime /= 10000000;		 /* Nano to seconds resolution */+	return (time_t)winTime;+}++static int do_lstat(const char *file_name, struct stat *buf)+{+	WIN32_FILE_ATTRIBUTE_DATA fdata;+	wchar_t* fbuf = gitwin_to_utf16(file_name);+	if (!fbuf)+		return -1;++	if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) {+		int fMode = S_IREAD;++		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)+			fMode |= S_IFDIR;+		else+			fMode |= S_IFREG;++		if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))+			fMode |= S_IWRITE;++		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)+			fMode |= S_IFLNK;++		buf->st_ino = 0;+		buf->st_gid = 0;+		buf->st_uid = 0;+		buf->st_nlink = 1;+		buf->st_mode = (mode_t)fMode;+		buf->st_size = ((git_off_t)fdata.nFileSizeHigh << 32) + fdata.nFileSizeLow;+		buf->st_dev = buf->st_rdev = (_getdrive() - 1);+		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));+		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));+		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));++		git__free(fbuf);+		return 0;+	}++	git__free(fbuf);+	return -1;+}++int p_lstat(const char *file_name, struct stat *buf)+{+	int error;+	size_t namelen;+	char *alt_name;++	if (do_lstat(file_name, buf) == 0)+		return 0;++	/* if file_name ended in a '/', Windows returned ENOENT;+	 * try again without trailing slashes+	 */+	namelen = strlen(file_name);+	if (namelen && file_name[namelen-1] != '/')+		return -1;++	while (namelen && file_name[namelen-1] == '/')+		--namelen;++	if (!namelen)+		return -1;++	alt_name = git__strndup(file_name, namelen);+	if (!alt_name)+		return -1;++	error = do_lstat(alt_name, buf);++	git__free(alt_name);+	return error;+}++int p_readlink(const char *link, char *target, size_t target_len)+{+	typedef DWORD (WINAPI *fpath_func)(HANDLE, LPWSTR, DWORD, DWORD);+	static fpath_func pGetFinalPath = NULL;+	HANDLE hFile;+	DWORD dwRet;+	wchar_t* link_w;+	wchar_t* target_w;+	int error = 0;++	assert(link && target && target_len > 0);++	/*+	 * Try to load the pointer to pGetFinalPath dynamically, because+	 * it is not available in platforms older than Vista+	 */+	if (pGetFinalPath == NULL) {+		HINSTANCE library = LoadLibrary("kernel32");++		if (library != NULL)+			pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW");++		if (pGetFinalPath == NULL) {+			giterr_set(GITERR_OS,+				"'GetFinalPathNameByHandleW' is not available in this platform");+			return -1;+		}+	}++	link_w = gitwin_to_utf16(link);+	GITERR_CHECK_ALLOC(link_w);++	hFile = CreateFileW(link_w,			// file to open+			GENERIC_READ,			// open for reading+			FILE_SHARE_READ,		// share for reading+			NULL,					// default security+			OPEN_EXISTING,			// existing file only+			FILE_FLAG_BACKUP_SEMANTICS, // normal file+			NULL);					// no attr. template++	git__free(link_w);++	if (hFile == INVALID_HANDLE_VALUE) {+		giterr_set(GITERR_OS, "Cannot open '%s' for reading", link);+		return -1;+	}++	target_w = (wchar_t*)git__malloc(target_len * sizeof(wchar_t));+	GITERR_CHECK_ALLOC(target_w);++	dwRet = pGetFinalPath(hFile, target_w, (DWORD)target_len, 0x0);+	if (dwRet == 0 ||+		dwRet >= target_len ||+		!WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target,+			(int)(target_len * sizeof(char)), NULL, NULL))+		error = -1;++	git__free(target_w);+	CloseHandle(hFile);++	if (error)+		return error;++	/* Skip first 4 characters if they are "\\?\" */+	if (dwRet > 4 &&+		target[0] == '\\' && target[1] == '\\' &&+		target[2] == '?' && target[3] == '\\')+	{+		unsigned int offset = 4;+		dwRet -= 4;++		/* \??\UNC\ */+		if (dwRet > 7 &&+			target[4] == 'U' && target[5] == 'N' && target[6] == 'C')+		{+			offset += 2;+			dwRet -= 2;+			target[offset] = '\\';+		}++		memmove(target, target + offset, dwRet);+	}++	target[dwRet] = '\0';++	return dwRet;+}++int p_open(const char *path, int flags, ...)+{+	int fd;+	wchar_t* buf;+	mode_t mode = 0;++	buf = gitwin_to_utf16(path);+	if (!buf)+		return -1;++	if (flags & O_CREAT)+	{+		va_list arg_list;++		va_start(arg_list, flags);+		mode = va_arg(arg_list, mode_t);+		va_end(arg_list);+	}++	fd = _wopen(buf, flags | _O_BINARY, mode);++	git__free(buf);+	return fd;+}++int p_creat(const char *path, mode_t mode)+{+	int fd;+	wchar_t* buf = gitwin_to_utf16(path);+	if (!buf)+		return -1;+	fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);+	git__free(buf);+	return fd;+}++int p_getcwd(char *buffer_out, size_t size)+{+	int ret;+	wchar_t* buf;++	if ((size_t)((int)size) != size)+		return -1;++	buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size);+	GITERR_CHECK_ALLOC(buf);++	_wgetcwd(buf, (int)size);++	ret = WideCharToMultiByte(+		CP_UTF8, 0, buf, -1, buffer_out, (int)size, NULL, NULL);++	git__free(buf);+	return !ret ? -1 : 0;+}++int p_stat(const char* path, struct stat* buf)+{+	return do_lstat(path, buf);+}++int p_chdir(const char* path)+{+	wchar_t* buf = gitwin_to_utf16(path);+	int ret;+	if (!buf)+		return -1;+	ret = _wchdir(buf);+	git__free(buf);+	return ret;+}++int p_chmod(const char* path, mode_t mode)+{+	wchar_t* buf = gitwin_to_utf16(path);+	int ret;+	if (!buf)+		return -1;+	ret = _wchmod(buf, mode);+	git__free(buf);+	return ret;+}++int p_rmdir(const char* path)+{+	wchar_t* buf = gitwin_to_utf16(path);+	int ret;+	if (!buf)+		return -1;+	ret = _wrmdir(buf);+	git__free(buf);+	return ret;+}++int p_hide_directory__w32(const char *path)+{+	int res;+	wchar_t* buf = gitwin_to_utf16(path);+	if (!buf)+		return -1;++	res = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN);+	git__free(buf);++	return (res != 0) ? 0 : -1; /* MSDN states a "non zero" value indicates a success */+}++char *p_realpath(const char *orig_path, char *buffer)+{+	int ret, buffer_sz = 0;+	wchar_t* orig_path_w = gitwin_to_utf16(orig_path);+	wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t));++	if (!orig_path_w || !buffer_w)+		return NULL;++	ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);+	git__free(orig_path_w);++	/* According to MSDN, a return value equals to zero means a failure. */+	if (ret == 0 || ret > GIT_PATH_MAX) {+		buffer = NULL;+		goto done;+	}++	if (buffer == NULL) {+		buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL);++		if (!buffer_sz ||+			!(buffer = (char *)git__malloc(buffer_sz)) ||+			!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL))+		{+			git__free(buffer);+			buffer = NULL;+			goto done;+		}+	} else {+		if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) {+			buffer = NULL;+			goto done;+		}+	}++	if (!git_path_exists(buffer))+	{+		if (buffer_sz > 0)+			git__free(buffer);++		buffer = NULL;+		errno = ENOENT;+	}++done:+	git__free(buffer_w);+	if (buffer)+		git_path_mkposix(buffer);+	return buffer;+}++int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)+{+#ifdef _MSC_VER+	int len;++	if (count == 0 || (len = _vsnprintf(buffer, count, format, argptr)) < 0)+		return _vscprintf(format, argptr);++	return len;+#else /* MinGW */+	return vsnprintf(buffer, count, format, argptr);+#endif+}++int p_snprintf(char *buffer, size_t count, const char *format, ...)+{+	va_list va;+	int r;++	va_start(va, format);+	r = p_vsnprintf(buffer, count, format, va);+	va_end(va);++	return r;+}++extern int p_creat(const char *path, mode_t mode);++int p_mkstemp(char *tmp_path)+{+#if defined(_MSC_VER)+	if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)+		return -1;+#else+	if (_mktemp(tmp_path) == NULL)+		return -1;+#endif++	return p_creat(tmp_path, 0744);+}++int p_setenv(const char* name, const char* value, int overwrite)+{+	if (overwrite != 1)+		return -1;++	return (SetEnvironmentVariableA(name, value) == 0 ? -1 : 0);+}++int p_access(const char* path, mode_t mode)+{+	wchar_t *buf = gitwin_to_utf16(path);+	int ret;+	if (!buf)+		return -1;++	ret = _waccess(buf, mode);+	git__free(buf);++	return ret;+}++int p_rename(const char *from, const char *to)+{+	wchar_t *wfrom = gitwin_to_utf16(from);+	wchar_t *wto = gitwin_to_utf16(to);+	int ret;++	if (!wfrom || !wto)+		return -1;++	ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;++	git__free(wfrom);+	git__free(wto);++	return ret;+}++int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)+{+	if ((size_t)((int)length) != length)+		return -1; /* giterr_set will be done by caller */++	return recv(socket, buffer, (int)length, flags);+}++int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)+{+	if ((size_t)((int)length) != length)+		return -1; /* giterr_set will be done by caller */++	return send(socket, buffer, (int)length, flags);+}
+ libgit2/src/win32/pthread.c view
@@ -0,0 +1,68 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "pthread.h"++int pthread_create(+	pthread_t *GIT_RESTRICT thread,+	const pthread_attr_t *GIT_RESTRICT attr,+	void *(*start_routine)(void*),+	void *GIT_RESTRICT arg)+{+	GIT_UNUSED(attr);+	*thread = (pthread_t) CreateThread(+		NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);+	return *thread ? 0 : -1;+}++int pthread_join(pthread_t thread, void **value_ptr)+{+	int ret;+	ret = WaitForSingleObject(thread, INFINITE);+	if (ret && value_ptr)+		GetExitCodeThread(thread, (void*) value_ptr);+	return -(!!ret);+}++int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex,+						const pthread_mutexattr_t *GIT_RESTRICT mutexattr)+{+	GIT_UNUSED(mutexattr);+	InitializeCriticalSection(mutex);+	return 0;+}++int pthread_mutex_destroy(pthread_mutex_t *mutex)+{+	DeleteCriticalSection(mutex);+	return 0;+}++int pthread_mutex_lock(pthread_mutex_t *mutex)+{+	EnterCriticalSection(mutex);+	return 0;+}++int pthread_mutex_unlock(pthread_mutex_t *mutex)+{+	LeaveCriticalSection(mutex);+	return 0;+}++int pthread_num_processors_np(void)+{+	DWORD_PTR p, s;+	int n = 0;++	if (GetProcessAffinityMask(GetCurrentProcess(), &p, &s))+		for (; p; p >>= 1)+			n += p&1;++	return n ? n : 1;+}+
+ libgit2/src/win32/pthread.h view
@@ -0,0 +1,40 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#ifndef GIT_PTHREAD_H+#define GIT_PTHREAD_H++#include "../common.h"++#if defined (_MSC_VER)+#	define GIT_RESTRICT __restrict+#else+#	define GIT_RESTRICT __restrict__+#endif++typedef int pthread_mutexattr_t;+typedef int pthread_condattr_t;+typedef int pthread_attr_t;+typedef CRITICAL_SECTION pthread_mutex_t;+typedef HANDLE pthread_t;++#define PTHREAD_MUTEX_INITIALIZER {(void*)-1};++int pthread_create(pthread_t *GIT_RESTRICT,+					const pthread_attr_t *GIT_RESTRICT,+					void *(*start_routine)(void*), void *__restrict);++int pthread_join(pthread_t, void **);++int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT, const pthread_mutexattr_t *GIT_RESTRICT);+int pthread_mutex_destroy(pthread_mutex_t *);+int pthread_mutex_lock(pthread_mutex_t *);+int pthread_mutex_unlock(pthread_mutex_t *);++int pthread_num_processors_np(void);++#endif
+ libgit2/src/win32/utf-conv.c view
@@ -0,0 +1,95 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include "common.h"+#include "utf-conv.h"+#include "git2/windows.h"++/*+ * Default codepage value+ */+static int _active_codepage = CP_UTF8;++void gitwin_set_codepage(unsigned int codepage)+{+	_active_codepage = codepage;+}++unsigned int gitwin_get_codepage(void)+{+	return _active_codepage;+}++void gitwin_set_utf8(void)+{+	_active_codepage = CP_UTF8;+}++wchar_t* gitwin_to_utf16(const char* str)+{+	wchar_t* ret;+	size_t cb;++	if (!str)+		return NULL;++	cb = strlen(str) * sizeof(wchar_t);+	if (cb == 0)+		return (wchar_t *)git__calloc(1, sizeof(wchar_t));++	/* Add space for null terminator */+	cb += sizeof(wchar_t);++	ret = (wchar_t *)git__malloc(cb);+	if (!ret)+		return NULL;++	if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, (int)cb) == 0) {+		giterr_set(GITERR_OS, "Could not convert string to UTF-16");+		git__free(ret);+		ret = NULL;+	}++	return ret;+}++int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len)+{+	int result = MultiByteToWideChar(_active_codepage, 0, str, -1, buffer, (int)len);+	if (result == 0)+		giterr_set(GITERR_OS, "Could not convert string to UTF-16");+	return result;+}++char* gitwin_from_utf16(const wchar_t* str)+{+	char* ret;+	size_t cb;++	if (!str)+		return NULL;++	cb = wcslen(str) * sizeof(char);+	if (cb == 0)+		return (char *)git__calloc(1, sizeof(char));++	/* Add space for null terminator */+	cb += sizeof(char);++	ret = (char*)git__malloc(cb);+	if (!ret)+		return NULL;++	if (WideCharToMultiByte(_active_codepage, 0, str, -1, ret, (int)cb, NULL, NULL) == 0) {+		giterr_set(GITERR_OS, "Could not convert string to UTF-8");+		git__free(ret);+		ret = NULL;+	}++	return ret;++}
+ libgit2/src/win32/utf-conv.h view
@@ -0,0 +1,18 @@+/*+ * Copyright (C) 2009-2012 the libgit2 contributors+ *+ * This file is part of libgit2, distributed under the GNU GPL v2 with+ * a Linking Exception. For full terms see the included COPYING file.+ */++#include <wchar.h>++#ifndef INCLUDE_git_utfconv_h__+#define INCLUDE_git_utfconv_h__++wchar_t* gitwin_to_utf16(const char* str);+int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len);+char* gitwin_from_utf16(const wchar_t* str);++#endif+
+ libgit2/src/xdiff/xdiff.h view
@@ -0,0 +1,135 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#if !defined(XDIFF_H)+#define XDIFF_H++#ifdef __cplusplus+extern "C" {+#endif /* #ifdef __cplusplus */+++#define XDF_NEED_MINIMAL (1 << 1)+#define XDF_IGNORE_WHITESPACE (1 << 2)+#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)+#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)+#define XDF_PATIENCE_DIFF (1 << 5)+#define XDF_HISTOGRAM_DIFF (1 << 6)+#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)++#define XDL_PATCH_NORMAL '-'+#define XDL_PATCH_REVERSE '+'+#define XDL_PATCH_MODEMASK ((1 << 8) - 1)+#define XDL_PATCH_IGNOREBSPACE (1 << 8)++#define XDL_EMIT_FUNCNAMES (1 << 0)+#define XDL_EMIT_COMMON (1 << 1)+#define XDL_EMIT_FUNCCONTEXT (1 << 2)++#define XDL_MMB_READONLY (1 << 0)++#define XDL_MMF_ATOMIC (1 << 0)++#define XDL_BDOP_INS 1+#define XDL_BDOP_CPY 2+#define XDL_BDOP_INSB 3++/* merge simplification levels */+#define XDL_MERGE_MINIMAL 0+#define XDL_MERGE_EAGER 1+#define XDL_MERGE_ZEALOUS 2+#define XDL_MERGE_ZEALOUS_ALNUM 3++/* merge favor modes */+#define XDL_MERGE_FAVOR_OURS 1+#define XDL_MERGE_FAVOR_THEIRS 2+#define XDL_MERGE_FAVOR_UNION 3++/* merge output styles */+#define XDL_MERGE_DIFF3 1++typedef struct s_mmfile {+	char *ptr;+	size_t size;+} mmfile_t;++typedef struct s_mmbuffer {+	char *ptr;+	size_t size;+} mmbuffer_t;++typedef struct s_xpparam {+	unsigned long flags;+} xpparam_t;++typedef struct s_xdemitcb {+	void *priv;+	int (*outf)(void *, mmbuffer_t *, int);+} xdemitcb_t;++typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);++typedef struct s_xdemitconf {+	long ctxlen;+	long interhunkctxlen;+	unsigned long flags;+	find_func_t find_func;+	void *find_func_priv;+	void (*emit_func)(void);+} xdemitconf_t;++typedef struct s_bdiffparam {+	long bsize;+} bdiffparam_t;+++#define xdl_malloc(x) malloc(x)+#define xdl_free(ptr) free(ptr)+#define xdl_realloc(ptr,x) realloc(ptr,x)++void *xdl_mmfile_first(mmfile_t *mmf, long *size);+long xdl_mmfile_size(mmfile_t *mmf);++int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,+	     xdemitconf_t const *xecfg, xdemitcb_t *ecb);++typedef struct s_xmparam {+	xpparam_t xpp;+	int marker_size;+	int level;+	int favor;+	int style;+	const char *ancestor;	/* label for orig */+	const char *file1;	/* label for mf1 */+	const char *file2;	/* label for mf2 */+} xmparam_t;++#define DEFAULT_CONFLICT_MARKER_SIZE 7++int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,+		xmparam_t const *xmp, mmbuffer_t *result);++#ifdef __cplusplus+}+#endif /* #ifdef __cplusplus */++#endif /* #if !defined(XDIFF_H) */
+ libgit2/src/xdiff/xdiffi.c view
@@ -0,0 +1,572 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003	Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#include "xinclude.h"++++#define XDL_MAX_COST_MIN 256+#define XDL_HEUR_MIN_COST 256+#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)+#define XDL_SNAKE_CNT 20+#define XDL_K_HEUR 4++++typedef struct s_xdpsplit {+	long i1, i2;+	int min_lo, min_hi;+} xdpsplit_t;+++++static long xdl_split(unsigned long const *ha1, long off1, long lim1,+		      unsigned long const *ha2, long off2, long lim2,+		      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,+		      xdalgoenv_t *xenv);+static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);++++++/*+ * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.+ * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both+ * the forward diagonal starting from (off1, off2) and the backward diagonal+ * starting from (lim1, lim2). If the K values on the same diagonal crosses+ * returns the furthest point of reach. We might end up having to expensive+ * cases using this algorithm is full, so a little bit of heuristic is needed+ * to cut the search and to return a suboptimal point.+ */+static long xdl_split(unsigned long const *ha1, long off1, long lim1,+		      unsigned long const *ha2, long off2, long lim2,+		      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,+		      xdalgoenv_t *xenv) {+	long dmin = off1 - lim2, dmax = lim1 - off2;+	long fmid = off1 - off2, bmid = lim1 - lim2;+	long odd = (fmid - bmid) & 1;+	long fmin = fmid, fmax = fmid;+	long bmin = bmid, bmax = bmid;+	long ec, d, i1, i2, prev1, best, dd, v, k;++	/*+	 * Set initial diagonal values for both forward and backward path.+	 */+	kvdf[fmid] = off1;+	kvdb[bmid] = lim1;++	for (ec = 1;; ec++) {+		int got_snake = 0;++		/*+		 * We need to extent the diagonal "domain" by one. If the next+		 * values exits the box boundaries we need to change it in the+		 * opposite direction because (max - min) must be a power of two.+		 * Also we initialize the external K value to -1 so that we can+		 * avoid extra conditions check inside the core loop.+		 */+		if (fmin > dmin)+			kvdf[--fmin - 1] = -1;+		else+			++fmin;+		if (fmax < dmax)+			kvdf[++fmax + 1] = -1;+		else+			--fmax;++		for (d = fmax; d >= fmin; d -= 2) {+			if (kvdf[d - 1] >= kvdf[d + 1])+				i1 = kvdf[d - 1] + 1;+			else+				i1 = kvdf[d + 1];+			prev1 = i1;+			i2 = i1 - d;+			for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);+			if (i1 - prev1 > xenv->snake_cnt)+				got_snake = 1;+			kvdf[d] = i1;+			if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) {+				spl->i1 = i1;+				spl->i2 = i2;+				spl->min_lo = spl->min_hi = 1;+				return ec;+			}+		}++		/*+		 * We need to extent the diagonal "domain" by one. If the next+		 * values exits the box boundaries we need to change it in the+		 * opposite direction because (max - min) must be a power of two.+		 * Also we initialize the external K value to -1 so that we can+		 * avoid extra conditions check inside the core loop.+		 */+		if (bmin > dmin)+			kvdb[--bmin - 1] = XDL_LINE_MAX;+		else+			++bmin;+		if (bmax < dmax)+			kvdb[++bmax + 1] = XDL_LINE_MAX;+		else+			--bmax;++		for (d = bmax; d >= bmin; d -= 2) {+			if (kvdb[d - 1] < kvdb[d + 1])+				i1 = kvdb[d - 1];+			else+				i1 = kvdb[d + 1] - 1;+			prev1 = i1;+			i2 = i1 - d;+			for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);+			if (prev1 - i1 > xenv->snake_cnt)+				got_snake = 1;+			kvdb[d] = i1;+			if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) {+				spl->i1 = i1;+				spl->i2 = i2;+				spl->min_lo = spl->min_hi = 1;+				return ec;+			}+		}++		if (need_min)+			continue;++		/*+		 * If the edit cost is above the heuristic trigger and if+		 * we got a good snake, we sample current diagonals to see+		 * if some of the, have reached an "interesting" path. Our+		 * measure is a function of the distance from the diagonal+		 * corner (i1 + i2) penalized with the distance from the+		 * mid diagonal itself. If this value is above the current+		 * edit cost times a magic factor (XDL_K_HEUR) we consider+		 * it interesting.+		 */+		if (got_snake && ec > xenv->heur_min) {+			for (best = 0, d = fmax; d >= fmin; d -= 2) {+				dd = d > fmid ? d - fmid: fmid - d;+				i1 = kvdf[d];+				i2 = i1 - d;+				v = (i1 - off1) + (i2 - off2) - dd;++				if (v > XDL_K_HEUR * ec && v > best &&+				    off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&+				    off2 + xenv->snake_cnt <= i2 && i2 < lim2) {+					for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)+						if (k == xenv->snake_cnt) {+							best = v;+							spl->i1 = i1;+							spl->i2 = i2;+							break;+						}+				}+			}+			if (best > 0) {+				spl->min_lo = 1;+				spl->min_hi = 0;+				return ec;+			}++			for (best = 0, d = bmax; d >= bmin; d -= 2) {+				dd = d > bmid ? d - bmid: bmid - d;+				i1 = kvdb[d];+				i2 = i1 - d;+				v = (lim1 - i1) + (lim2 - i2) - dd;++				if (v > XDL_K_HEUR * ec && v > best &&+				    off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&+				    off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {+					for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)+						if (k == xenv->snake_cnt - 1) {+							best = v;+							spl->i1 = i1;+							spl->i2 = i2;+							break;+						}+				}+			}+			if (best > 0) {+				spl->min_lo = 0;+				spl->min_hi = 1;+				return ec;+			}+		}++		/*+		 * Enough is enough. We spent too much time here and now we collect+		 * the furthest reaching path using the (i1 + i2) measure.+		 */+		if (ec >= xenv->mxcost) {+			long fbest, fbest1, bbest, bbest1;++			fbest = fbest1 = -1;+			for (d = fmax; d >= fmin; d -= 2) {+				i1 = XDL_MIN(kvdf[d], lim1);+				i2 = i1 - d;+				if (lim2 < i2)+					i1 = lim2 + d, i2 = lim2;+				if (fbest < i1 + i2) {+					fbest = i1 + i2;+					fbest1 = i1;+				}+			}++			bbest = bbest1 = XDL_LINE_MAX;+			for (d = bmax; d >= bmin; d -= 2) {+				i1 = XDL_MAX(off1, kvdb[d]);+				i2 = i1 - d;+				if (i2 < off2)+					i1 = off2 + d, i2 = off2;+				if (i1 + i2 < bbest) {+					bbest = i1 + i2;+					bbest1 = i1;+				}+			}++			if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) {+				spl->i1 = fbest1;+				spl->i2 = fbest - fbest1;+				spl->min_lo = 1;+				spl->min_hi = 0;+			} else {+				spl->i1 = bbest1;+				spl->i2 = bbest - bbest1;+				spl->min_lo = 0;+				spl->min_hi = 1;+			}+			return ec;+		}+	}+}+++/*+ * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling+ * the box splitting function. Note that the real job (marking changed lines)+ * is done in the two boundary reaching checks.+ */+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,+		 diffdata_t *dd2, long off2, long lim2,+		 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {+	unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;++	/*+	 * Shrink the box by walking through each diagonal snake (SW and NE).+	 */+	for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);+	for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);++	/*+	 * If one dimension is empty, then all records on the other one must+	 * be obviously changed.+	 */+	if (off1 == lim1) {+		char *rchg2 = dd2->rchg;+		long *rindex2 = dd2->rindex;++		for (; off2 < lim2; off2++)+			rchg2[rindex2[off2]] = 1;+	} else if (off2 == lim2) {+		char *rchg1 = dd1->rchg;+		long *rindex1 = dd1->rindex;++		for (; off1 < lim1; off1++)+			rchg1[rindex1[off1]] = 1;+	} else {+		xdpsplit_t spl;+		spl.i1 = spl.i2 = 0;++		/*+		 * Divide ...+		 */+		if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,+			      need_min, &spl, xenv) < 0) {++			return -1;+		}++		/*+		 * ... et Impera.+		 */+		if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,+				 kvdf, kvdb, spl.min_lo, xenv) < 0 ||+		    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,+				 kvdf, kvdb, spl.min_hi, xenv) < 0) {++			return -1;+		}+	}++	return 0;+}+++int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,+		xdfenv_t *xe) {+	long ndiags;+	long *kvd, *kvdf, *kvdb;+	xdalgoenv_t xenv;+	diffdata_t dd1, dd2;++	if (xpp->flags & XDF_PATIENCE_DIFF)+		return xdl_do_patience_diff(mf1, mf2, xpp, xe);++	if (xpp->flags & XDF_HISTOGRAM_DIFF)+		return xdl_do_histogram_diff(mf1, mf2, xpp, xe);++	if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {++		return -1;+	}++	/*+	 * Allocate and setup K vectors to be used by the differential algorithm.+	 * One is to store the forward path and one to store the backward path.+	 */+	ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;+	if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) {++		xdl_free_env(xe);+		return -1;+	}+	kvdf = kvd;+	kvdb = kvdf + ndiags;+	kvdf += xe->xdf2.nreff + 1;+	kvdb += xe->xdf2.nreff + 1;++	xenv.mxcost = xdl_bogosqrt(ndiags);+	if (xenv.mxcost < XDL_MAX_COST_MIN)+		xenv.mxcost = XDL_MAX_COST_MIN;+	xenv.snake_cnt = XDL_SNAKE_CNT;+	xenv.heur_min = XDL_HEUR_MIN_COST;++	dd1.nrec = xe->xdf1.nreff;+	dd1.ha = xe->xdf1.ha;+	dd1.rchg = xe->xdf1.rchg;+	dd1.rindex = xe->xdf1.rindex;+	dd2.nrec = xe->xdf2.nreff;+	dd2.ha = xe->xdf2.ha;+	dd2.rchg = xe->xdf2.rchg;+	dd2.rindex = xe->xdf2.rindex;++	if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,+			 kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) {++		xdl_free(kvd);+		xdl_free_env(xe);+		return -1;+	}++	xdl_free(kvd);++	return 0;+}+++static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) {+	xdchange_t *xch;++	if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t))))+		return NULL;++	xch->next = xscr;+	xch->i1 = i1;+	xch->i2 = i2;+	xch->chg1 = chg1;+	xch->chg2 = chg2;++	return xch;+}+++int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {+	long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;+	char *rchg = xdf->rchg, *rchgo = xdfo->rchg;+	xrecord_t **recs = xdf->recs;++	/*+	 * This is the same of what GNU diff does. Move back and forward+	 * change groups for a consistent and pretty diff output. This also+	 * helps in finding joinable change groups and reduce the diff size.+	 */+	for (ix = ixo = 0;;) {+		/*+		 * Find the first changed line in the to-be-compacted file.+		 * We need to keep track of both indexes, so if we find a+		 * changed lines group on the other file, while scanning the+		 * to-be-compacted file, we need to skip it properly. Note+		 * that loops that are testing for changed lines on rchg* do+		 * not need index bounding since the array is prepared with+		 * a zero at position -1 and N.+		 */+		for (; ix < nrec && !rchg[ix]; ix++)+			while (rchgo[ixo++]);+		if (ix == nrec)+			break;++		/*+		 * Record the start of a changed-group in the to-be-compacted file+		 * and find the end of it, on both to-be-compacted and other file+		 * indexes (ix and ixo).+		 */+		ixs = ix;+		for (ix++; rchg[ix]; ix++);+		for (; rchgo[ixo]; ixo++);++		do {+			grpsiz = ix - ixs;++			/*+			 * If the line before the current change group, is equal to+			 * the last line of the current change group, shift backward+			 * the group.+			 */+			while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&+			       xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {+				rchg[--ixs] = 1;+				rchg[--ix] = 0;++				/*+				 * This change might have joined two change groups,+				 * so we try to take this scenario in account by moving+				 * the start index accordingly (and so the other-file+				 * end-of-group index).+				 */+				for (; rchg[ixs - 1]; ixs--);+				while (rchgo[--ixo]);+			}++			/*+			 * Record the end-of-group position in case we are matched+			 * with a group of changes in the other file (that is, the+			 * change record before the end-of-group index in the other+			 * file is set).+			 */+			ixref = rchgo[ixo - 1] ? ix: nrec;++			/*+			 * If the first line of the current change group, is equal to+			 * the line next of the current change group, shift forward+			 * the group.+			 */+			while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&+			       xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {+				rchg[ixs++] = 0;+				rchg[ix++] = 1;++				/*+				 * This change might have joined two change groups,+				 * so we try to take this scenario in account by moving+				 * the start index accordingly (and so the other-file+				 * end-of-group index). Keep tracking the reference+				 * index in case we are shifting together with a+				 * corresponding group of changes in the other file.+				 */+				for (; rchg[ix]; ix++);+				while (rchgo[++ixo])+					ixref = ix;+			}+		} while (grpsiz != ix - ixs);++		/*+		 * Try to move back the possibly merged group of changes, to match+		 * the recorded postion in the other file.+		 */+		while (ixref < ix) {+			rchg[--ixs] = 1;+			rchg[--ix] = 0;+			while (rchgo[--ixo]);+		}+	}++	return 0;+}+++int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {+	xdchange_t *cscr = NULL, *xch;+	char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;+	long i1, i2, l1, l2;++	/*+	 * Trivial. Collects "groups" of changes and creates an edit script.+	 */+	for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)+		if (rchg1[i1 - 1] || rchg2[i2 - 1]) {+			for (l1 = i1; rchg1[i1 - 1]; i1--);+			for (l2 = i2; rchg2[i2 - 1]; i2--);++			if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {+				xdl_free_script(cscr);+				return -1;+			}+			cscr = xch;+		}++	*xscr = cscr;++	return 0;+}+++void xdl_free_script(xdchange_t *xscr) {+	xdchange_t *xch;++	while ((xch = xscr) != NULL) {+		xscr = xscr->next;+		xdl_free(xch);+	}+}+++int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,+	     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {+	xdchange_t *xscr;+	xdfenv_t xe;+	emit_func_t ef = xecfg->emit_func ?+		(emit_func_t)xecfg->emit_func : xdl_emit_diff;++	if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {++		return -1;+	}+	if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||+	    xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||+	    xdl_build_script(&xe, &xscr) < 0) {++		xdl_free_env(&xe);+		return -1;+	}+	if (xscr) {+		if (ef(&xe, xscr, ecb, xecfg) < 0) {++			xdl_free_script(xscr);+			xdl_free_env(&xe);+			return -1;+		}+		xdl_free_script(xscr);+	}+	xdl_free_env(&xe);++	return 0;+}
+ libgit2/src/xdiff/xdiffi.h view
@@ -0,0 +1,63 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#if !defined(XDIFFI_H)+#define XDIFFI_H+++typedef struct s_diffdata {+	long nrec;+	unsigned long const *ha;+	long *rindex;+	char *rchg;+} diffdata_t;++typedef struct s_xdalgoenv {+	long mxcost;+	long snake_cnt;+	long heur_min;+} xdalgoenv_t;++typedef struct s_xdchange {+	struct s_xdchange *next;+	long i1, i2;+	long chg1, chg2;+} xdchange_t;++++int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,+		 diffdata_t *dd2, long off2, long lim2,+		 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,+		xdfenv_t *xe);+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);+void xdl_free_script(xdchange_t *xscr);+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,+		  xdemitconf_t const *xecfg);+int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,+		xdfenv_t *env);+int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,+		xdfenv_t *env);++#endif /* #if !defined(XDIFFI_H) */
+ libgit2/src/xdiff/xemit.c view
@@ -0,0 +1,253 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003	Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#include "xinclude.h"+++++static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);+++++static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {++	*rec = xdf->recs[ri]->ptr;++	return xdf->recs[ri]->size;+}+++static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {+	long size, psize = (long)strlen(pre);+	char const *rec;++	size = xdl_get_rec(xdf, ri, &rec);+	if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {++		return -1;+	}++	return 0;+}+++/*+ * Starting at the passed change atom, find the latest change atom to be included+ * inside the differential hunk according to the specified configuration.+ */+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {+	xdchange_t *xch, *xchp;+	long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;++	for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)+		if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)+			break;++	return xchp;+}+++static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)+{+	(void)priv;++	if (len > 0 &&+			(isalpha((unsigned char)*rec) || /* identifier? */+			 *rec == '_' ||	/* also identifier? */+			 *rec == '$')) { /* identifiers from VMS and other esoterico */+		if (len > sz)+			len = sz;+		while (0 < len && isspace((unsigned char)rec[len - 1]))+			len--;+		memcpy(buf, rec, len);+		return len;+	}+	return -1;+}++static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,+                           xdemitconf_t const *xecfg) {+	xdfile_t *xdf = &xe->xdf2;+	const char *rchg = xdf->rchg;+	long ix;++	(void)xscr;+	(void)xecfg;++	for (ix = 0; ix < xdf->nrec; ix++) {+		if (rchg[ix])+			continue;+		if (xdl_emit_record(xdf, ix, "", ecb))+			return -1;+	}+	return 0;+}++struct func_line {+	long len;+	char buf[80];+};++static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,+			  struct func_line *func_line, long start, long limit)+{+	find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;+	long l, size, step = (start > limit) ? -1 : 1;+	char *buf, dummy[1];++	buf = func_line ? func_line->buf : dummy;+	size = func_line ? sizeof(func_line->buf) : sizeof(dummy);++	for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {+		const char *rec;+		long reclen = xdl_get_rec(&xe->xdf1, l, &rec);+		long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);+		if (len >= 0) {+			if (func_line)+				func_line->len = len;+			return l;+		}+	}+	return -1;+}++int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,+		  xdemitconf_t const *xecfg) {+	long s1, s2, e1, e2, lctx;+	xdchange_t *xch, *xche;+	long funclineprev = -1;+	struct func_line func_line = { 0 };++	if (xecfg->flags & XDL_EMIT_COMMON)+		return xdl_emit_common(xe, xscr, ecb, xecfg);++	for (xch = xscr; xch; xch = xche->next) {+		xche = xdl_get_hunk(xch, xecfg);++		s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);+		s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);++		if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {+			long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);+			if (fs1 < 0)+				fs1 = 0;+			if (fs1 < s1) {+				s2 -= s1 - fs1;+				s1 = fs1;+			}+		}++ again:+		lctx = xecfg->ctxlen;+		lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));+		lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));++		e1 = xche->i1 + xche->chg1 + lctx;+		e2 = xche->i2 + xche->chg2 + lctx;++		if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {+			long fe1 = get_func_line(xe, xecfg, NULL,+						 xche->i1 + xche->chg1,+						 xe->xdf1.nrec);+			if (fe1 < 0)+				fe1 = xe->xdf1.nrec;+			if (fe1 > e1) {+				e2 += fe1 - e1;+				e1 = fe1;+			}++			/*+			 * Overlap with next change?  Then include it+			 * in the current hunk and start over to find+			 * its new end.+			 */+			if (xche->next) {+				long l = xche->next->i1;+				if (l <= e1 ||+				    get_func_line(xe, xecfg, NULL, l, e1) < 0) {+					xche = xche->next;+					goto again;+				}+			}+		}++		/*+		 * Emit current hunk header.+		 */++		if (xecfg->flags & XDL_EMIT_FUNCNAMES) {+			get_func_line(xe, xecfg, &func_line,+				      s1 - 1, funclineprev);+			funclineprev = s1 - 1;+		}+		if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,+				      func_line.buf, func_line.len, ecb) < 0)+			return -1;++		/*+		 * Emit pre-context.+		 */+		for (; s2 < xch->i2; s2++)+			if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)+				return -1;++		for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {+			/*+			 * Merge previous with current change atom.+			 */+			for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)+				if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)+					return -1;++			/*+			 * Removes lines from the first file.+			 */+			for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)+				if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)+					return -1;++			/*+			 * Adds lines from the second file.+			 */+			for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)+				if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)+					return -1;++			if (xch == xche)+				break;+			s1 = xch->i1 + xch->chg1;+			s2 = xch->i2 + xch->chg2;+		}++		/*+		 * Emit post-context.+		 */+		for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++)+			if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)+				return -1;+	}++	return 0;+}
+ libgit2/src/xdiff/xemit.h view
@@ -0,0 +1,36 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#if !defined(XEMIT_H)+#define XEMIT_H+++typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,+			   xdemitconf_t const *xecfg);++xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,+		  xdemitconf_t const *xecfg);++++#endif /* #if !defined(XEMIT_H) */
+ libgit2/src/xdiff/xhistogram.c view
@@ -0,0 +1,371 @@+/*+ * Copyright (C) 2010, Google Inc.+ * and other copyright owners as documented in JGit's IP log.+ *+ * This program and the accompanying materials are made available+ * under the terms of the Eclipse Distribution License v1.0 which+ * accompanies this distribution, is reproduced below, and is+ * available at http://www.eclipse.org/org/documents/edl-v10.php+ *+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or+ * without modification, are permitted provided that the following+ * conditions are met:+ *+ * - Redistributions of source code must retain the above copyright+ *   notice, this list of conditions and the following disclaimer.+ *+ * - Redistributions in binary form must reproduce the above+ *   copyright notice, this list of conditions and the following+ *   disclaimer in the documentation and/or other materials provided+ *   with the distribution.+ *+ * - Neither the name of the Eclipse Foundation, Inc. nor the+ *   names of its contributors may be used to endorse or promote+ *   products derived from this software without specific prior+ *   written permission.+ *+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include "xinclude.h"+#include "xtypes.h"+#include "xdiff.h"++#define MAX_PTR	UINT_MAX+#define MAX_CNT	UINT_MAX++#define LINE_END(n) (line##n + count##n - 1)+#define LINE_END_PTR(n) (*line##n + *count##n - 1)++struct histindex {+	struct record {+		unsigned int ptr, cnt;+		struct record *next;+	} **records, /* an ocurrence */+	  **line_map; /* map of line to record chain */+	chastore_t rcha;+	unsigned int *next_ptrs;+	unsigned int table_bits,+		     records_size,+		     line_map_size;++	unsigned int max_chain_length,+		     key_shift,+		     ptr_shift;++	unsigned int cnt,+		     has_common;++	xdfenv_t *env;+	xpparam_t const *xpp;+};++struct region {+	unsigned int begin1, end1;+	unsigned int begin2, end2;+};++#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])++#define NEXT_PTR(index, ptr) \+	(index->next_ptrs[(ptr) - index->ptr_shift])++#define CNT(index, ptr) \+	((LINE_MAP(index, ptr))->cnt)++#define REC(env, s, l) \+	(env->xdf##s.recs[l - 1])++static int cmp_recs(xpparam_t const *xpp,+	xrecord_t *r1, xrecord_t *r2)+{+	return r1->ha == r2->ha &&+		xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,+			    xpp->flags);+}++#define CMP_ENV(xpp, env, s1, l1, s2, l2) \+	(cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))++#define CMP(i, s1, l1, s2, l2) \+	(cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))++#define TABLE_HASH(index, side, line) \+	XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)++static int scanA(struct histindex *index, unsigned int line1, unsigned int count1)+{+	unsigned int ptr;+	unsigned int tbl_idx;+	unsigned int chain_len;+	struct record **rec_chain, *rec;++	for (ptr = LINE_END(1); line1 <= ptr; ptr--) {+		tbl_idx = TABLE_HASH(index, 1, ptr);+		rec_chain = index->records + tbl_idx;+		rec = *rec_chain;++		chain_len = 0;+		while (rec) {+			if (CMP(index, 1, rec->ptr, 1, ptr)) {+				/*+				 * ptr is identical to another element. Insert+				 * it onto the front of the existing element+				 * chain.+				 */+				NEXT_PTR(index, ptr) = rec->ptr;+				rec->ptr = ptr;+				/* cap rec->cnt at MAX_CNT */+				rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);+				LINE_MAP(index, ptr) = rec;+				goto continue_scan;+			}++			rec = rec->next;+			chain_len++;+		}++		if (chain_len == index->max_chain_length)+			return -1;++		/*+		 * This is the first time we have ever seen this particular+		 * element in the sequence. Construct a new chain for it.+		 */+		if (!(rec = xdl_cha_alloc(&index->rcha)))+			return -1;+		rec->ptr = ptr;+		rec->cnt = 1;+		rec->next = *rec_chain;+		*rec_chain = rec;+		LINE_MAP(index, ptr) = rec;++continue_scan:+		; /* no op */+	}++	return 0;+}++static int try_lcs(+	struct histindex *index, struct region *lcs, unsigned int b_ptr,+	unsigned int line1, unsigned int count1,+	unsigned int line2, unsigned int count2)+{+	unsigned int b_next = b_ptr + 1;+	struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];+	unsigned int as, ae, bs, be, np, rc;+	int should_break;++	for (; rec; rec = rec->next) {+		if (rec->cnt > index->cnt) {+			if (!index->has_common)+				index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);+			continue;+		}++		as = rec->ptr;+		if (!CMP(index, 1, as, 2, b_ptr))+			continue;++		index->has_common = 1;+		for (;;) {+			should_break = 0;+			np = NEXT_PTR(index, as);+			bs = b_ptr;+			ae = as;+			be = bs;+			rc = rec->cnt;++			while (line1 < as && line2 < bs+				&& CMP(index, 1, as - 1, 2, bs - 1)) {+				as--;+				bs--;+				if (1 < rc)+					rc = XDL_MIN(rc, CNT(index, as));+			}+			while (ae < LINE_END(1) && be < LINE_END(2)+				&& CMP(index, 1, ae + 1, 2, be + 1)) {+				ae++;+				be++;+				if (1 < rc)+					rc = XDL_MIN(rc, CNT(index, ae));+			}++			if (b_next <= be)+				b_next = be + 1;+			if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {+				lcs->begin1 = as;+				lcs->begin2 = bs;+				lcs->end1 = ae;+				lcs->end2 = be;+				index->cnt = rc;+			}++			if (np == 0)+				break;++			while (np <= ae) {+				np = NEXT_PTR(index, np);+				if (np == 0) {+					should_break = 1;+					break;+				}+			}++			if (should_break)+				break;++			as = np;+		}+	}+	return b_next;+}++static int find_lcs(+	struct histindex *index, struct region *lcs,+	unsigned int line1, unsigned int count1,+	unsigned int line2, unsigned int count2)+{+	unsigned int b_ptr;++	if (scanA(index, line1, count1))+		return -1;++	index->cnt = index->max_chain_length + 1;++	for (b_ptr = line2; b_ptr <= LINE_END(2); )+		b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2);++	return index->has_common && index->max_chain_length < index->cnt;+}++static int fall_back_to_classic_diff(struct histindex *index,+		int line1, int count1, int line2, int count2)+{+	xpparam_t xpp;+	xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;++	return xdl_fall_back_diff(index->env, &xpp,+				  line1, count1, line2, count2);+}++static int histogram_diff(+	xpparam_t const *xpp, xdfenv_t *env,+	unsigned int line1, unsigned int count1,+	unsigned int line2, unsigned int count2)+{+	struct histindex index;+	struct region lcs;+	unsigned int sz;+	int result = -1;++	if (count1 <= 0 && count2 <= 0)+		return 0;++	if (LINE_END(1) >= MAX_PTR)+		return -1;++	if (!count1) {+		while(count2--)+			env->xdf2.rchg[line2++ - 1] = 1;+		return 0;+	} else if (!count2) {+		while(count1--)+			env->xdf1.rchg[line1++ - 1] = 1;+		return 0;+	}++	memset(&index, 0, sizeof(index));++	index.env = env;+	index.xpp = xpp;++	index.records = NULL;+	index.line_map = NULL;+	/* in case of early xdl_cha_free() */+	index.rcha.head = NULL;++	index.table_bits = xdl_hashbits(count1);+	sz = index.records_size = 1 << index.table_bits;+	sz *= sizeof(struct record *);+	if (!(index.records = (struct record **) xdl_malloc(sz)))+		goto cleanup;+	memset(index.records, 0, sz);++	sz = index.line_map_size = count1;+	sz *= sizeof(struct record *);+	if (!(index.line_map = (struct record **) xdl_malloc(sz)))+		goto cleanup;+	memset(index.line_map, 0, sz);++	sz = index.line_map_size;+	sz *= sizeof(unsigned int);+	if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))+		goto cleanup;+	memset(index.next_ptrs, 0, sz);++	/* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */+	if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)+		goto cleanup;++	index.ptr_shift = line1;+	index.max_chain_length = 64;++	memset(&lcs, 0, sizeof(lcs));+	if (find_lcs(&index, &lcs, line1, count1, line2, count2))+		result = fall_back_to_classic_diff(&index, line1, count1, line2, count2);+	else {+		if (lcs.begin1 == 0 && lcs.begin2 == 0) {+			while (count1--)+				env->xdf1.rchg[line1++ - 1] = 1;+			while (count2--)+				env->xdf2.rchg[line2++ - 1] = 1;+			result = 0;+		} else {+			result = histogram_diff(xpp, env,+						line1, lcs.begin1 - line1,+						line2, lcs.begin2 - line2);+			if (result)+				goto cleanup;+			result = histogram_diff(xpp, env,+						lcs.end1 + 1, LINE_END(1) - lcs.end1,+						lcs.end2 + 1, LINE_END(2) - lcs.end2);+			if (result)+				goto cleanup;+		}+	}++cleanup:+	xdl_free(index.records);+	xdl_free(index.line_map);+	xdl_free(index.next_ptrs);+	xdl_cha_free(&index.rcha);++	return result;+}++int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,+	xpparam_t const *xpp, xdfenv_t *env)+{+	if (xdl_prepare_env(file1, file2, xpp, env) < 0)+		return -1;++	return histogram_diff(xpp, env,+		env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,+		env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);+}
+ libgit2/src/xdiff/xinclude.h view
@@ -0,0 +1,46 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#if !defined(XINCLUDE_H)+#define XINCLUDE_H++#include <ctype.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <limits.h>++#ifdef _WIN32+#else+#include <unistd.h>+#endif++#include "xmacros.h"+#include "xdiff.h"+#include "xtypes.h"+#include "xutils.h"+#include "xprepare.h"+#include "xdiffi.h"+#include "xemit.h"+++#endif /* #if !defined(XINCLUDE_H) */
+ libgit2/src/xdiff/xmacros.h view
@@ -0,0 +1,54 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#if !defined(XMACROS_H)+#define XMACROS_H+++++#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))+#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))+#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))+#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')+#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))+#define XDL_ADDBITS(v,b)	((v) + ((v) >> (b)))+#define XDL_MASKBITS(b)		((1UL << (b)) - 1)+#define XDL_HASHLONG(v,b)	(XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))+#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)+#define XDL_LE32_PUT(p, v) \+do { \+	unsigned char *__p = (unsigned char *) (p); \+	*__p++ = (unsigned char) (v); \+	*__p++ = (unsigned char) ((v) >> 8); \+	*__p++ = (unsigned char) ((v) >> 16); \+	*__p = (unsigned char) ((v) >> 24); \+} while (0)+#define XDL_LE32_GET(p, v) \+do { \+	unsigned char const *__p = (unsigned char const *) (p); \+	(v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \+		((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \+} while (0)+++#endif /* #if !defined(XMACROS_H) */
+ libgit2/src/xdiff/xmerge.c view
@@ -0,0 +1,619 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#include "xinclude.h"++typedef struct s_xdmerge {+	struct s_xdmerge *next;+	/*+	 * 0 = conflict,+	 * 1 = no conflict, take first,+	 * 2 = no conflict, take second.+	 * 3 = no conflict, take both.+	 */+	int mode;+	/*+	 * These point at the respective postimages.  E.g. <i1,chg1> is+	 * how side #1 wants to change the common ancestor; if there is no+	 * overlap, lines before i1 in the postimage of side #1 appear+	 * in the merge result as a region touched by neither side.+	 */+	long i1, i2;+	long chg1, chg2;+	/*+	 * These point at the preimage; of course there is just one+	 * preimage, that is from the shared common ancestor.+	 */+	long i0;+	long chg0;+} xdmerge_t;++static int xdl_append_merge(xdmerge_t **merge, int mode,+			    long i0, long chg0,+			    long i1, long chg1,+			    long i2, long chg2)+{+	xdmerge_t *m = *merge;+	if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {+		if (mode != m->mode)+			m->mode = 0;+		m->chg0 = i0 + chg0 - m->i0;+		m->chg1 = i1 + chg1 - m->i1;+		m->chg2 = i2 + chg2 - m->i2;+	} else {+		m = xdl_malloc(sizeof(xdmerge_t));+		if (!m)+			return -1;+		m->next = NULL;+		m->mode = mode;+		m->i0 = i0;+		m->chg0 = chg0;+		m->i1 = i1;+		m->chg1 = chg1;+		m->i2 = i2;+		m->chg2 = chg2;+		if (*merge)+			(*merge)->next = m;+		*merge = m;+	}+	return 0;+}++static int xdl_cleanup_merge(xdmerge_t *c)+{+	int count = 0;+	xdmerge_t *next_c;++	/* were there conflicts? */+	for (; c; c = next_c) {+		if (c->mode == 0)+			count++;+		next_c = c->next;+		free(c);+	}+	return count;+}++static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,+		int line_count, long flags)+{+	int i;+	xrecord_t **rec1 = xe1->xdf2.recs + i1;+	xrecord_t **rec2 = xe2->xdf2.recs + i2;++	for (i = 0; i < line_count; i++) {+		int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,+			rec2[i]->ptr, rec2[i]->size, flags);+		if (!result)+			return -1;+	}+	return 0;+}++static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)+{+	xrecord_t **recs;+	int size = 0;++	recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;++	if (count < 1)+		return 0;++	for (i = 0; i < count; size += recs[i++]->size)+		if (dest)+			memcpy(dest + size, recs[i]->ptr, recs[i]->size);+	if (add_nl) {+		i = recs[count - 1]->size;+		if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {+			if (dest)+				dest[size] = '\n';+			size++;+		}+	}+	return size;+}++static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)+{+	return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);+}++static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)+{+	return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);+}++static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,+			      xdfenv_t *xe2, const char *name2,+			      const char *name3,+			      int size, int i, int style,+			      xdmerge_t *m, char *dest, int marker_size)+{+	int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0);+	int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0);+	int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0);++	if (marker_size <= 0)+		marker_size = DEFAULT_CONFLICT_MARKER_SIZE;++	/* Before conflicting part */+	size += xdl_recs_copy(xe1, i, m->i1 - i, 0,+			      dest ? dest + size : NULL);++	if (!dest) {+		size += marker_size + 1 + marker1_size;+	} else {+		memset(dest + size, '<', marker_size);+		size += marker_size;+		if (marker1_size) {+			dest[size] = ' ';+			memcpy(dest + size + 1, name1, marker1_size - 1);+			size += marker1_size;+		}+		dest[size++] = '\n';+	}++	/* Postimage from side #1 */+	size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,+			      dest ? dest + size : NULL);++	if (style == XDL_MERGE_DIFF3) {+		/* Shared preimage */+		if (!dest) {+			size += marker_size + 1 + marker3_size;+		} else {+			memset(dest + size, '|', marker_size);+			size += marker_size;+			if (marker3_size) {+				dest[size] = ' ';+				memcpy(dest + size + 1, name3, marker3_size - 1);+				size += marker3_size;+			}+			dest[size++] = '\n';+		}+		size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,+				      dest ? dest + size : NULL);+	}++	if (!dest) {+		size += marker_size + 1;+	} else {+		memset(dest + size, '=', marker_size);+		size += marker_size;+		dest[size++] = '\n';+	}++	/* Postimage from side #2 */+	size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,+			      dest ? dest + size : NULL);+	if (!dest) {+		size += marker_size + 1 + marker2_size;+	} else {+		memset(dest + size, '>', marker_size);+		size += marker_size;+		if (marker2_size) {+			dest[size] = ' ';+			memcpy(dest + size + 1, name2, marker2_size - 1);+			size += marker2_size;+		}+		dest[size++] = '\n';+	}+	return size;+}++static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,+				 xdfenv_t *xe2, const char *name2,+				 const char *ancestor_name,+				 int favor,+				 xdmerge_t *m, char *dest, int style,+				 int marker_size)+{+	int size, i;++	for (size = i = 0; m; m = m->next) {+		if (favor && !m->mode)+			m->mode = favor;++		if (m->mode == 0)+			size = fill_conflict_hunk(xe1, name1, xe2, name2,+						  ancestor_name,+						  size, i, style, m, dest,+						  marker_size);+		else if (m->mode & 3) {+			/* Before conflicting part */+			size += xdl_recs_copy(xe1, i, m->i1 - i, 0,+					      dest ? dest + size : NULL);+			/* Postimage from side #1 */+			if (m->mode & 1)+				size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,+						      dest ? dest + size : NULL);+			/* Postimage from side #2 */+			if (m->mode & 2)+				size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,+						      dest ? dest + size : NULL);+		} else+			continue;+		i = m->i1 + m->chg1;+	}+	size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0,+			      dest ? dest + size : NULL);+	return size;+}++/*+ * Sometimes, changes are not quite identical, but differ in only a few+ * lines. Try hard to show only these few lines as conflicting.+ */+static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,+		xpparam_t const *xpp)+{+	for (; m; m = m->next) {+		mmfile_t t1, t2;+		xdfenv_t xe;+		xdchange_t *xscr, *x;+		int i1 = m->i1, i2 = m->i2;++		/* let's handle just the conflicts */+		if (m->mode)+			continue;++		/* no sense refining a conflict when one side is empty */+		if (m->chg1 == 0 || m->chg2 == 0)+			continue;++		/*+		 * This probably does not work outside git, since+		 * we have a very simple mmfile structure.+		 */+		t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;+		t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr+			+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;+		t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;+		t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr+			+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;+		if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)+			return -1;+		if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||+		    xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||+		    xdl_build_script(&xe, &xscr) < 0) {+			xdl_free_env(&xe);+			return -1;+		}+		if (!xscr) {+			/* If this happens, the changes are identical. */+			xdl_free_env(&xe);+			m->mode = 4;+			continue;+		}+		x = xscr;+		m->i1 = xscr->i1 + i1;+		m->chg1 = xscr->chg1;+		m->i2 = xscr->i2 + i2;+		m->chg2 = xscr->chg2;+		while (xscr->next) {+			xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t));+			if (!m2) {+				xdl_free_env(&xe);+				xdl_free_script(x);+				return -1;+			}+			xscr = xscr->next;+			m2->next = m->next;+			m->next = m2;+			m = m2;+			m->mode = 0;+			m->i1 = xscr->i1 + i1;+			m->chg1 = xscr->chg1;+			m->i2 = xscr->i2 + i2;+			m->chg2 = xscr->chg2;+		}+		xdl_free_env(&xe);+		xdl_free_script(x);+	}+	return 0;+}++static int line_contains_alnum(const char *ptr, long size)+{+	while (size--)+		if (isalnum((unsigned char)*(ptr++)))+			return 1;+	return 0;+}++static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)+{+	for (; chg; chg--, i++)+		if (line_contains_alnum(xe->xdf2.recs[i]->ptr,+				xe->xdf2.recs[i]->size))+			return 1;+	return 0;+}++/*+ * This function merges m and m->next, marking everything between those hunks+ * as conflicting, too.+ */+static void xdl_merge_two_conflicts(xdmerge_t *m)+{+	xdmerge_t *next_m = m->next;+	m->chg1 = next_m->i1 + next_m->chg1 - m->i1;+	m->chg2 = next_m->i2 + next_m->chg2 - m->i2;+	m->next = next_m->next;+	free(next_m);+}++/*+ * If there are less than 3 non-conflicting lines between conflicts,+ * it appears simpler -- because it takes up less (or as many) lines --+ * if the lines are moved into the conflicts.+ */+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,+				      int simplify_if_no_alnum)+{+	int result = 0;++	if (!m)+		return result;+	for (;;) {+		xdmerge_t *next_m = m->next;+		int begin, end;++		if (!next_m)+			return result;++		begin = m->i1 + m->chg1;+		end = next_m->i1;++		if (m->mode != 0 || next_m->mode != 0 ||+		    (end - begin > 3 &&+		     (!simplify_if_no_alnum ||+		      lines_contain_alnum(xe1, begin, end - begin)))) {+			m = next_m;+		} else {+			result++;+			xdl_merge_two_conflicts(m);+		}+	}+}++/*+ * level == 0: mark all overlapping changes as conflict+ * level == 1: mark overlapping changes as conflict only if not identical+ * level == 2: analyze non-identical changes for minimal conflict set+ * level == 3: analyze non-identical changes for minimal conflict set, but+ *             treat hunks not containing any letter or number as conflicting+ *+ * returns < 0 on error, == 0 for no conflicts, else number of conflicts+ */+static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,+		xdfenv_t *xe2, xdchange_t *xscr2,+		xmparam_t const *xmp, mmbuffer_t *result)+{+	xdmerge_t *changes, *c;+	xpparam_t const *xpp = &xmp->xpp;+	const char *const ancestor_name = xmp->ancestor;+	const char *const name1 = xmp->file1;+	const char *const name2 = xmp->file2;+	int i0, i1, i2, chg0, chg1, chg2;+	int level = xmp->level;+	int style = xmp->style;+	int favor = xmp->favor;++	if (style == XDL_MERGE_DIFF3) {+		/*+		 * "diff3 -m" output does not make sense for anything+		 * more aggressive than XDL_MERGE_EAGER.+		 */+		if (XDL_MERGE_EAGER < level)+			level = XDL_MERGE_EAGER;+	}++	c = changes = NULL;++	while (xscr1 && xscr2) {+		if (!changes)+			changes = c;+		if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {+			i0 = xscr1->i1;+			i1 = xscr1->i2;+			i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;+			chg0 = xscr1->chg1;+			chg1 = xscr1->chg2;+			chg2 = xscr1->chg1;+			if (xdl_append_merge(&c, 1,+					     i0, chg0, i1, chg1, i2, chg2)) {+				xdl_cleanup_merge(changes);+				return -1;+			}+			xscr1 = xscr1->next;+			continue;+		}+		if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {+			i0 = xscr2->i1;+			i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;+			i2 = xscr2->i2;+			chg0 = xscr2->chg1;+			chg1 = xscr2->chg1;+			chg2 = xscr2->chg2;+			if (xdl_append_merge(&c, 2,+					     i0, chg0, i1, chg1, i2, chg2)) {+				xdl_cleanup_merge(changes);+				return -1;+			}+			xscr2 = xscr2->next;+			continue;+		}+		if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||+				xscr1->chg1 != xscr2->chg1 ||+				xscr1->chg2 != xscr2->chg2 ||+				xdl_merge_cmp_lines(xe1, xscr1->i2,+					xe2, xscr2->i2,+					xscr1->chg2, xpp->flags)) {+			/* conflict */+			int off = xscr1->i1 - xscr2->i1;+			int ffo = off + xscr1->chg1 - xscr2->chg1;++			i0 = xscr1->i1;+			i1 = xscr1->i2;+			i2 = xscr2->i2;+			if (off > 0) {+				i0 -= off;+				i1 -= off;+			}+			else+				i2 += off;+			chg0 = xscr1->i1 + xscr1->chg1 - i0;+			chg1 = xscr1->i2 + xscr1->chg2 - i1;+			chg2 = xscr2->i2 + xscr2->chg2 - i2;+			if (ffo < 0) {+				chg0 -= ffo;+				chg1 -= ffo;+			} else+				chg2 += ffo;+			if (xdl_append_merge(&c, 0,+					     i0, chg0, i1, chg1, i2, chg2)) {+				xdl_cleanup_merge(changes);+				return -1;+			}+		}++		i1 = xscr1->i1 + xscr1->chg1;+		i2 = xscr2->i1 + xscr2->chg1;++		if (i1 >= i2)+			xscr2 = xscr2->next;+		if (i2 >= i1)+			xscr1 = xscr1->next;+	}+	while (xscr1) {+		if (!changes)+			changes = c;+		i0 = xscr1->i1;+		i1 = xscr1->i2;+		i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;+		chg0 = xscr1->chg1;+		chg1 = xscr1->chg2;+		chg2 = xscr1->chg1;+		if (xdl_append_merge(&c, 1,+				     i0, chg0, i1, chg1, i2, chg2)) {+			xdl_cleanup_merge(changes);+			return -1;+		}+		xscr1 = xscr1->next;+	}+	while (xscr2) {+		if (!changes)+			changes = c;+		i0 = xscr2->i1;+		i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;+		i2 = xscr2->i2;+		chg0 = xscr2->chg1;+		chg1 = xscr2->chg1;+		chg2 = xscr2->chg2;+		if (xdl_append_merge(&c, 2,+				     i0, chg0, i1, chg1, i2, chg2)) {+			xdl_cleanup_merge(changes);+			return -1;+		}+		xscr2 = xscr2->next;+	}+	if (!changes)+		changes = c;+	/* refine conflicts */+	if (XDL_MERGE_ZEALOUS <= level &&+	    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||+	     xdl_simplify_non_conflicts(xe1, changes,+					XDL_MERGE_ZEALOUS < level) < 0)) {+		xdl_cleanup_merge(changes);+		return -1;+	}+	/* output */+	if (result) {+		int marker_size = xmp->marker_size;+		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,+						 ancestor_name,+						 favor, changes, NULL, style,+						 marker_size);+		result->ptr = xdl_malloc(size);+		if (!result->ptr) {+			xdl_cleanup_merge(changes);+			return -1;+		}+		result->size = size;+		xdl_fill_merge_buffer(xe1, name1, xe2, name2,+				      ancestor_name, favor, changes,+				      result->ptr, style, marker_size);+	}+	return xdl_cleanup_merge(changes);+}++int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,+		xmparam_t const *xmp, mmbuffer_t *result)+{+	xdchange_t *xscr1, *xscr2;+	xdfenv_t xe1, xe2;+	int status;+	xpparam_t const *xpp = &xmp->xpp;++	result->ptr = NULL;+	result->size = 0;++	if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 ||+			xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {+		return -1;+	}+	if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||+	    xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||+	    xdl_build_script(&xe1, &xscr1) < 0) {+		xdl_free_env(&xe1);+		return -1;+	}+	if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||+	    xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||+	    xdl_build_script(&xe2, &xscr2) < 0) {+		xdl_free_env(&xe2);+		return -1;+	}+	status = 0;+	if (!xscr1) {+		result->ptr = xdl_malloc(mf2->size);+		memcpy(result->ptr, mf2->ptr, mf2->size);+		result->size = mf2->size;+	} else if (!xscr2) {+		result->ptr = xdl_malloc(mf1->size);+		memcpy(result->ptr, mf1->ptr, mf1->size);+		result->size = mf1->size;+	} else {+		status = xdl_do_merge(&xe1, xscr1,+				      &xe2, xscr2,+				      xmp, result);+	}+	xdl_free_script(xscr1);+	xdl_free_script(xscr2);++	xdl_free_env(&xe1);+	xdl_free_env(&xe2);++	return status;+}
+ libgit2/src/xdiff/xpatience.c view
@@ -0,0 +1,358 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */+#include "xinclude.h"+#include "xtypes.h"+#include "xdiff.h"++/*+ * The basic idea of patience diff is to find lines that are unique in+ * both files.  These are intuitively the ones that we want to see as+ * common lines.+ *+ * The maximal ordered sequence of such line pairs (where ordered means+ * that the order in the sequence agrees with the order of the lines in+ * both files) naturally defines an initial set of common lines.+ *+ * Now, the algorithm tries to extend the set of common lines by growing+ * the line ranges where the files have identical lines.+ *+ * Between those common lines, the patience diff algorithm is applied+ * recursively, until no unique line pairs can be found; these line ranges+ * are handled by the well-known Myers algorithm.+ */++#define NON_UNIQUE ULONG_MAX++/*+ * This is a hash mapping from line hash to line numbers in the first and+ * second file.+ */+struct hashmap {+	int nr, alloc;+	struct entry {+		unsigned long hash;+		/*+		 * 0 = unused entry, 1 = first line, 2 = second, etc.+		 * line2 is NON_UNIQUE if the line is not unique+		 * in either the first or the second file.+		 */+		unsigned long line1, line2;+		/*+		 * "next" & "previous" are used for the longest common+		 * sequence;+		 * initially, "next" reflects only the order in file1.+		 */+		struct entry *next, *previous;+	} *entries, *first, *last;+	/* were common records found? */+	unsigned long has_matches;+	mmfile_t *file1, *file2;+	xdfenv_t *env;+	xpparam_t const *xpp;+};++/* The argument "pass" is 1 for the first file, 2 for the second. */+static void insert_record(int line, struct hashmap *map, int pass)+{+	xrecord_t **records = pass == 1 ?+		map->env->xdf1.recs : map->env->xdf2.recs;+	xrecord_t *record = records[line - 1], *other;+	/*+	 * After xdl_prepare_env() (or more precisely, due to+	 * xdl_classify_record()), the "ha" member of the records (AKA lines)+	 * is _not_ the hash anymore, but a linearized version of it.  In+	 * other words, the "ha" member is guaranteed to start with 0 and+	 * the second record's ha can only be 0 or 1, etc.+	 *+	 * So we multiply ha by 2 in the hope that the hashing was+	 * "unique enough".+	 */+	int index = (int)((record->ha << 1) % map->alloc);++	while (map->entries[index].line1) {+		other = map->env->xdf1.recs[map->entries[index].line1 - 1];+		if (map->entries[index].hash != record->ha ||+				!xdl_recmatch(record->ptr, record->size,+					other->ptr, other->size,+					map->xpp->flags)) {+			if (++index >= map->alloc)+				index = 0;+			continue;+		}+		if (pass == 2)+			map->has_matches = 1;+		if (pass == 1 || map->entries[index].line2)+			map->entries[index].line2 = NON_UNIQUE;+		else+			map->entries[index].line2 = line;+		return;+	}+	if (pass == 2)+		return;+	map->entries[index].line1 = line;+	map->entries[index].hash = record->ha;+	if (!map->first)+		map->first = map->entries + index;+	if (map->last) {+		map->last->next = map->entries + index;+		map->entries[index].previous = map->last;+	}+	map->last = map->entries + index;+	map->nr++;+}++/*+ * This function has to be called for each recursion into the inter-hunk+ * parts, as previously non-unique lines can become unique when being+ * restricted to a smaller part of the files.+ *+ * It is assumed that env has been prepared using xdl_prepare().+ */+static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,+		xpparam_t const *xpp, xdfenv_t *env,+		struct hashmap *result,+		int line1, int count1, int line2, int count2)+{+	result->file1 = file1;+	result->file2 = file2;+	result->xpp = xpp;+	result->env = env;++	/* We know exactly how large we want the hash map */+	result->alloc = count1 * 2;+	result->entries = (struct entry *)+		xdl_malloc(result->alloc * sizeof(struct entry));+	if (!result->entries)+		return -1;+	memset(result->entries, 0, result->alloc * sizeof(struct entry));++	/* First, fill with entries from the first file */+	while (count1--)+		insert_record(line1++, result, 1);++	/* Then search for matches in the second file */+	while (count2--)+		insert_record(line2++, result, 2);++	return 0;+}++/*+ * Find the longest sequence with a smaller last element (meaning a smaller+ * line2, as we construct the sequence with entries ordered by line1).+ */+static int binary_search(struct entry **sequence, int longest,+		struct entry *entry)+{+	int left = -1, right = longest;++	while (left + 1 < right) {+		int middle = (left + right) / 2;+		/* by construction, no two entries can be equal */+		if (sequence[middle]->line2 > entry->line2)+			right = middle;+		else+			left = middle;+	}+	/* return the index in "sequence", _not_ the sequence length */+	return left;+}++/*+ * The idea is to start with the list of common unique lines sorted by+ * the order in file1.  For each of these pairs, the longest (partial)+ * sequence whose last element's line2 is smaller is determined.+ *+ * For efficiency, the sequences are kept in a list containing exactly one+ * item per sequence length: the sequence with the smallest last+ * element (in terms of line2).+ */+static struct entry *find_longest_common_sequence(struct hashmap *map)+{+	struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));+	int longest = 0, i;+	struct entry *entry;++	for (entry = map->first; entry; entry = entry->next) {+		if (!entry->line2 || entry->line2 == NON_UNIQUE)+			continue;+		i = binary_search(sequence, longest, entry);+		entry->previous = i < 0 ? NULL : sequence[i];+		sequence[++i] = entry;+		if (i == longest)+			longest++;+	}++	/* No common unique lines were found */+	if (!longest) {+		xdl_free(sequence);+		return NULL;+	}++	/* Iterate starting at the last element, adjusting the "next" members */+	entry = sequence[longest - 1];+	entry->next = NULL;+	while (entry->previous) {+		entry->previous->next = entry;+		entry = entry->previous;+	}+	xdl_free(sequence);+	return entry;+}++static int match(struct hashmap *map, int line1, int line2)+{+	xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];+	xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];+	return xdl_recmatch(record1->ptr, record1->size,+		record2->ptr, record2->size, map->xpp->flags);+}++static int patience_diff(mmfile_t *file1, mmfile_t *file2,+		xpparam_t const *xpp, xdfenv_t *env,+		int line1, int count1, int line2, int count2);++static int walk_common_sequence(struct hashmap *map, struct entry *first,+		int line1, int count1, int line2, int count2)+{+	int end1 = line1 + count1, end2 = line2 + count2;+	int next1, next2;++	for (;;) {+		/* Try to grow the line ranges of common lines */+		if (first) {+			next1 = first->line1;+			next2 = first->line2;+			while (next1 > line1 && next2 > line2 &&+					match(map, next1 - 1, next2 - 1)) {+				next1--;+				next2--;+			}+		} else {+			next1 = end1;+			next2 = end2;+		}+		while (line1 < next1 && line2 < next2 &&+				match(map, line1, line2)) {+			line1++;+			line2++;+		}++		/* Recurse */+		if (next1 > line1 || next2 > line2) {+			struct hashmap submap;++			memset(&submap, 0, sizeof(submap));+			if (patience_diff(map->file1, map->file2,+					map->xpp, map->env,+					line1, next1 - line1,+					line2, next2 - line2))+				return -1;+		}++		if (!first)+			return 0;++		while (first->next &&+				first->next->line1 == first->line1 + 1 &&+				first->next->line2 == first->line2 + 1)+			first = first->next;++		line1 = first->line1 + 1;+		line2 = first->line2 + 1;++		first = first->next;+	}+}++static int fall_back_to_classic_diff(struct hashmap *map,+		int line1, int count1, int line2, int count2)+{+	xpparam_t xpp;+	xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;++	return xdl_fall_back_diff(map->env, &xpp,+				  line1, count1, line2, count2);+}++/*+ * Recursively find the longest common sequence of unique lines,+ * and if none was found, ask xdl_do_diff() to do the job.+ *+ * This function assumes that env was prepared with xdl_prepare_env().+ */+static int patience_diff(mmfile_t *file1, mmfile_t *file2,+		xpparam_t const *xpp, xdfenv_t *env,+		int line1, int count1, int line2, int count2)+{+	struct hashmap map;+	struct entry *first;+	int result = 0;++	/* trivial case: one side is empty */+	if (!count1) {+		while(count2--)+			env->xdf2.rchg[line2++ - 1] = 1;+		return 0;+	} else if (!count2) {+		while(count1--)+			env->xdf1.rchg[line1++ - 1] = 1;+		return 0;+	}++	memset(&map, 0, sizeof(map));+	if (fill_hashmap(file1, file2, xpp, env, &map,+			line1, count1, line2, count2))+		return -1;++	/* are there any matching lines at all? */+	if (!map.has_matches) {+		while(count1--)+			env->xdf1.rchg[line1++ - 1] = 1;+		while(count2--)+			env->xdf2.rchg[line2++ - 1] = 1;+		xdl_free(map.entries);+		return 0;+	}++	first = find_longest_common_sequence(&map);+	if (first)+		result = walk_common_sequence(&map, first,+			line1, count1, line2, count2);+	else+		result = fall_back_to_classic_diff(&map,+			line1, count1, line2, count2);++	xdl_free(map.entries);+	return result;+}++int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,+		xpparam_t const *xpp, xdfenv_t *env)+{+	if (xdl_prepare_env(file1, file2, xpp, env) < 0)+		return -1;++	/* environment is cleaned up in xdl_diff() */+	return patience_diff(file1, file2, xpp, env,+			1, env->xdf1.nrec, 1, env->xdf2.nrec);+}
+ libgit2/src/xdiff/xprepare.c view
@@ -0,0 +1,483 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#include "xinclude.h"+++#define XDL_KPDIS_RUN 4+#define XDL_MAX_EQLIMIT 1024+#define XDL_SIMSCAN_WINDOW 100+#define XDL_GUESS_NLINES1 256+#define XDL_GUESS_NLINES2 20+++typedef struct s_xdlclass {+	struct s_xdlclass *next;+	unsigned long ha;+	char const *line;+	long size;+	long idx;+	long len1, len2;+} xdlclass_t;++typedef struct s_xdlclassifier {+	unsigned int hbits;+	long hsize;+	xdlclass_t **rchash;+	chastore_t ncha;+	xdlclass_t **rcrecs;+	long alloc;+	long count;+	long flags;+} xdlclassifier_t;+++++static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);+static void xdl_free_classifier(xdlclassifier_t *cf);+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,+			       unsigned int hbits, xrecord_t *rec);+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,+			   xdlclassifier_t *cf, xdfile_t *xdf);+static void xdl_free_ctx(xdfile_t *xdf);+static int xdl_clean_mmatch(char const *dis, long i, long s, long e);+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);+++++static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {+	cf->flags = flags;++	cf->hbits = xdl_hashbits((unsigned int) size);+	cf->hsize = 1 << cf->hbits;++	if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {++		return -1;+	}+	if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) {++		xdl_cha_free(&cf->ncha);+		return -1;+	}+	memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));++	cf->alloc = size;+	if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) {++		xdl_free(cf->rchash);+		xdl_cha_free(&cf->ncha);+		return -1;+	}++	cf->count = 0;++	return 0;+}+++static void xdl_free_classifier(xdlclassifier_t *cf) {++	xdl_free(cf->rcrecs);+	xdl_free(cf->rchash);+	xdl_cha_free(&cf->ncha);+}+++static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,+			       unsigned int hbits, xrecord_t *rec) {+	long hi;+	char const *line;+	xdlclass_t *rcrec;+	xdlclass_t **rcrecs;++	line = rec->ptr;+	hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);+	for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)+		if (rcrec->ha == rec->ha &&+				xdl_recmatch(rcrec->line, rcrec->size,+					rec->ptr, rec->size, cf->flags))+			break;++	if (!rcrec) {+		if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {++			return -1;+		}+		rcrec->idx = cf->count++;+		if (cf->count > cf->alloc) {+			cf->alloc *= 2;+			if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) {++				return -1;+			}+			cf->rcrecs = rcrecs;+		}+		cf->rcrecs[rcrec->idx] = rcrec;+		rcrec->line = line;+		rcrec->size = rec->size;+		rcrec->ha = rec->ha;+		rcrec->len1 = rcrec->len2 = 0;+		rcrec->next = cf->rchash[hi];+		cf->rchash[hi] = rcrec;+	}++	(pass == 1) ? rcrec->len1++ : rcrec->len2++;++	rec->ha = (unsigned long) rcrec->idx;++	hi = (long) XDL_HASHLONG(rec->ha, hbits);+	rec->next = rhash[hi];+	rhash[hi] = rec;++	return 0;+}+++static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,+			   xdlclassifier_t *cf, xdfile_t *xdf) {+	unsigned int hbits;+	long nrec, hsize, bsize;+	unsigned long hav;+	char const *blk, *cur, *top, *prev;+	xrecord_t *crec;+	xrecord_t **recs, **rrecs;+	xrecord_t **rhash;+	unsigned long *ha;+	char *rchg;+	long *rindex;++	ha = NULL;+	rindex = NULL;+	rchg = NULL;+	rhash = NULL;+	recs = NULL;++	if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)+		goto abort;+	if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))+		goto abort;++	if (xpp->flags & XDF_HISTOGRAM_DIFF)+		hbits = hsize = 0;+	else {+		hbits = xdl_hashbits((unsigned int) narec);+		hsize = 1 << hbits;+		if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))+			goto abort;+		memset(rhash, 0, hsize * sizeof(xrecord_t *));+	}++	nrec = 0;+	if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {+		for (top = blk + bsize; cur < top; ) {+			prev = cur;+			hav = xdl_hash_record(&cur, top, xpp->flags);+			if (nrec >= narec) {+				narec *= 2;+				if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))+					goto abort;+				recs = rrecs;+			}+			if (!(crec = xdl_cha_alloc(&xdf->rcha)))+				goto abort;+			crec->ptr = prev;+			crec->size = (long) (cur - prev);+			crec->ha = hav;+			recs[nrec++] = crec;++			if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&+				xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)+				goto abort;+		}+	}++	if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))+		goto abort;+	memset(rchg, 0, (nrec + 2) * sizeof(char));++	if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))+		goto abort;+	if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))+		goto abort;++	xdf->nrec = nrec;+	xdf->recs = recs;+	xdf->hbits = hbits;+	xdf->rhash = rhash;+	xdf->rchg = rchg + 1;+	xdf->rindex = rindex;+	xdf->nreff = 0;+	xdf->ha = ha;+	xdf->dstart = 0;+	xdf->dend = nrec - 1;++	return 0;++abort:+	xdl_free(ha);+	xdl_free(rindex);+	xdl_free(rchg);+	xdl_free(rhash);+	xdl_free(recs);+	xdl_cha_free(&xdf->rcha);+	return -1;+}+++static void xdl_free_ctx(xdfile_t *xdf) {++	xdl_free(xdf->rhash);+	xdl_free(xdf->rindex);+	xdl_free(xdf->rchg - 1);+	xdl_free(xdf->ha);+	xdl_free(xdf->recs);+	xdl_cha_free(&xdf->rcha);+}+++int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,+		    xdfenv_t *xe) {+	long enl1, enl2, sample;+	xdlclassifier_t cf;++	memset(&cf, 0, sizeof(cf));++	/*+	 * For histogram diff, we can afford a smaller sample size and+	 * thus a poorer estimate of the number of lines, as the hash+	 * table (rhash) won't be filled up/grown. The number of lines+	 * (nrecs) will be updated correctly anyway by+	 * xdl_prepare_ctx().+	 */+	sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;++	enl1 = xdl_guess_lines(mf1, sample) + 1;+	enl2 = xdl_guess_lines(mf2, sample) + 1;++	if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&+		xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {++		return -1;+	}++	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {++		xdl_free_classifier(&cf);+		return -1;+	}+	if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {++		xdl_free_ctx(&xe->xdf1);+		xdl_free_classifier(&cf);+		return -1;+	}++	if (!(xpp->flags & XDF_PATIENCE_DIFF) &&+			!(xpp->flags & XDF_HISTOGRAM_DIFF) &&+			xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {++		xdl_free_ctx(&xe->xdf2);+		xdl_free_ctx(&xe->xdf1);+		return -1;+	}++	if (!(xpp->flags & XDF_HISTOGRAM_DIFF))+		xdl_free_classifier(&cf);++	return 0;+}+++void xdl_free_env(xdfenv_t *xe) {++	xdl_free_ctx(&xe->xdf2);+	xdl_free_ctx(&xe->xdf1);+}+++static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {+	long r, rdis0, rpdis0, rdis1, rpdis1;++	/*+	 * Limits the window the is examined during the similar-lines+	 * scan. The loops below stops when dis[i - r] == 1 (line that+	 * has no match), but there are corner cases where the loop+	 * proceed all the way to the extremities by causing huge+	 * performance penalties in case of big files.+	 */+	if (i - s > XDL_SIMSCAN_WINDOW)+		s = i - XDL_SIMSCAN_WINDOW;+	if (e - i > XDL_SIMSCAN_WINDOW)+		e = i + XDL_SIMSCAN_WINDOW;++	/*+	 * Scans the lines before 'i' to find a run of lines that either+	 * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).+	 * Note that we always call this function with dis[i] > 1, so the+	 * current line (i) is already a multimatch line.+	 */+	for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {+		if (!dis[i - r])+			rdis0++;+		else if (dis[i - r] == 2)+			rpdis0++;+		else+			break;+	}+	/*+	 * If the run before the line 'i' found only multimatch lines, we+	 * return 0 and hence we don't make the current line (i) discarded.+	 * We want to discard multimatch lines only when they appear in the+	 * middle of runs with nomatch lines (dis[j] == 0).+	 */+	if (rdis0 == 0)+		return 0;+	for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {+		if (!dis[i + r])+			rdis1++;+		else if (dis[i + r] == 2)+			rpdis1++;+		else+			break;+	}+	/*+	 * If the run after the line 'i' found only multimatch lines, we+	 * return 0 and hence we don't make the current line (i) discarded.+	 */+	if (rdis1 == 0)+		return 0;+	rdis1 += rdis0;+	rpdis1 += rpdis0;++	return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);+}+++/*+ * Try to reduce the problem complexity, discard records that have no+ * matches on the other file. Also, lines that have multiple matches+ * might be potentially discarded if they happear in a run of discardable.+ */+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {+	long i, nm, nreff, mlim;+	xrecord_t **recs;+	xdlclass_t *rcrec;+	char *dis, *dis1, *dis2;++	if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {++		return -1;+	}+	memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);+	dis1 = dis;+	dis2 = dis1 + xdf1->nrec + 1;++	if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)+		mlim = XDL_MAX_EQLIMIT;+	for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {+		rcrec = cf->rcrecs[(*recs)->ha];+		nm = rcrec ? rcrec->len2 : 0;+		dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;+	}++	if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)+		mlim = XDL_MAX_EQLIMIT;+	for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {+		rcrec = cf->rcrecs[(*recs)->ha];+		nm = rcrec ? rcrec->len1 : 0;+		dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;+	}++	for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];+	     i <= xdf1->dend; i++, recs++) {+		if (dis1[i] == 1 ||+		    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {+			xdf1->rindex[nreff] = i;+			xdf1->ha[nreff] = (*recs)->ha;+			nreff++;+		} else+			xdf1->rchg[i] = 1;+	}+	xdf1->nreff = nreff;++	for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];+	     i <= xdf2->dend; i++, recs++) {+		if (dis2[i] == 1 ||+		    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {+			xdf2->rindex[nreff] = i;+			xdf2->ha[nreff] = (*recs)->ha;+			nreff++;+		} else+			xdf2->rchg[i] = 1;+	}+	xdf2->nreff = nreff;++	xdl_free(dis);++	return 0;+}+++/*+ * Early trim initial and terminal matching records.+ */+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {+	long i, lim;+	xrecord_t **recs1, **recs2;++	recs1 = xdf1->recs;+	recs2 = xdf2->recs;+	for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;+	     i++, recs1++, recs2++)+		if ((*recs1)->ha != (*recs2)->ha)+			break;++	xdf1->dstart = xdf2->dstart = i;++	recs1 = xdf1->recs + xdf1->nrec - 1;+	recs2 = xdf2->recs + xdf2->nrec - 1;+	for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)+		if ((*recs1)->ha != (*recs2)->ha)+			break;++	xdf1->dend = xdf1->nrec - i - 1;+	xdf2->dend = xdf2->nrec - i - 1;++	return 0;+}+++static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {++	if (xdl_trim_ends(xdf1, xdf2) < 0 ||+	    xdl_cleanup_records(cf, xdf1, xdf2) < 0) {++		return -1;+	}++	return 0;+}
+ libgit2/src/xdiff/xprepare.h view
@@ -0,0 +1,34 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#if !defined(XPREPARE_H)+#define XPREPARE_H++++int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,+		    xdfenv_t *xe);+void xdl_free_env(xdfenv_t *xe);++++#endif /* #if !defined(XPREPARE_H) */
+ libgit2/src/xdiff/xtypes.h view
@@ -0,0 +1,67 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#if !defined(XTYPES_H)+#define XTYPES_H++++typedef struct s_chanode {+	struct s_chanode *next;+	long icurr;+} chanode_t;++typedef struct s_chastore {+	chanode_t *head, *tail;+	long isize, nsize;+	chanode_t *ancur;+	chanode_t *sncur;+	long scurr;+} chastore_t;++typedef struct s_xrecord {+	struct s_xrecord *next;+	char const *ptr;+	long size;+	unsigned long ha;+} xrecord_t;++typedef struct s_xdfile {+	chastore_t rcha;+	long nrec;+	unsigned int hbits;+	xrecord_t **rhash;+	long dstart, dend;+	xrecord_t **recs;+	char *rchg;+	long *rindex;+	long nreff;+	unsigned long *ha;+} xdfile_t;++typedef struct s_xdfenv {+	xdfile_t xdf1, xdf2;+} xdfenv_t;++++#endif /* #if !defined(XTYPES_H) */
+ libgit2/src/xdiff/xutils.c view
@@ -0,0 +1,419 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003	Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#include "xinclude.h"+++++long xdl_bogosqrt(long n) {+	long i;++	/*+	 * Classical integer square root approximation using shifts.+	 */+	for (i = 1; n > 0; n >>= 2)+		i <<= 1;++	return i;+}+++int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,+		     xdemitcb_t *ecb) {+	int i = 2;+	mmbuffer_t mb[3];++	mb[0].ptr = (char *) pre;+	mb[0].size = psize;+	mb[1].ptr = (char *) rec;+	mb[1].size = size;+	if (size > 0 && rec[size - 1] != '\n') {+		mb[2].ptr = (char *) "\n\\ No newline at end of file\n";+		mb[2].size = strlen(mb[2].ptr);+		i++;+	}+	if (ecb->outf(ecb->priv, mb, i) < 0) {++		return -1;+	}++	return 0;+}++void *xdl_mmfile_first(mmfile_t *mmf, long *size)+{+	*size = (long)mmf->size;+	return mmf->ptr;+}+++long xdl_mmfile_size(mmfile_t *mmf)+{+	return (long)mmf->size;+}+++int xdl_cha_init(chastore_t *cha, long isize, long icount) {++	cha->head = cha->tail = NULL;+	cha->isize = isize;+	cha->nsize = icount * isize;+	cha->ancur = cha->sncur = NULL;+	cha->scurr = 0;++	return 0;+}+++void xdl_cha_free(chastore_t *cha) {+	chanode_t *cur, *tmp;++	for (cur = cha->head; (tmp = cur) != NULL;) {+		cur = cur->next;+		xdl_free(tmp);+	}+}+++void *xdl_cha_alloc(chastore_t *cha) {+	chanode_t *ancur;+	void *data;++	if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {+		if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {++			return NULL;+		}+		ancur->icurr = 0;+		ancur->next = NULL;+		if (cha->tail)+			cha->tail->next = ancur;+		if (!cha->head)+			cha->head = ancur;+		cha->tail = ancur;+		cha->ancur = ancur;+	}++	data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;+	ancur->icurr += cha->isize;++	return data;+}+++void *xdl_cha_first(chastore_t *cha) {+	chanode_t *sncur;++	if (!(cha->sncur = sncur = cha->head))+		return NULL;++	cha->scurr = 0;++	return (char *) sncur + sizeof(chanode_t) + cha->scurr;+}+++void *xdl_cha_next(chastore_t *cha) {+	chanode_t *sncur;++	if (!(sncur = cha->sncur))+		return NULL;+	cha->scurr += cha->isize;+	if (cha->scurr == sncur->icurr) {+		if (!(sncur = cha->sncur = sncur->next))+			return NULL;+		cha->scurr = 0;+	}++	return (char *) sncur + sizeof(chanode_t) + cha->scurr;+}+++long xdl_guess_lines(mmfile_t *mf, long sample) {+	long nl = 0, size, tsize = 0;+	char const *data, *cur, *top;++	if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {+		for (top = data + size; nl < sample && cur < top; ) {+			nl++;+			if (!(cur = memchr(cur, '\n', top - cur)))+				cur = top;+			else+				cur++;+		}+		tsize += (long) (cur - data);+	}++	if (nl && tsize)+		nl = xdl_mmfile_size(mf) / (tsize / nl);++	return nl + 1;+}++int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)+{+	int i1, i2;++	if (s1 == s2 && !memcmp(l1, l2, s1))+		return 1;+	if (!(flags & XDF_WHITESPACE_FLAGS))+		return 0;++	i1 = 0;+	i2 = 0;++	/*+	 * -w matches everything that matches with -b, and -b in turn+	 * matches everything that matches with --ignore-space-at-eol.+	 *+	 * Each flavor of ignoring needs different logic to skip whitespaces+	 * while we have both sides to compare.+	 */+	if (flags & XDF_IGNORE_WHITESPACE) {+		goto skip_ws;+		while (i1 < s1 && i2 < s2) {+			if (l1[i1++] != l2[i2++])+				return 0;+		skip_ws:+			while (i1 < s1 && XDL_ISSPACE(l1[i1]))+				i1++;+			while (i2 < s2 && XDL_ISSPACE(l2[i2]))+				i2++;+		}+	} else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {+		while (i1 < s1 && i2 < s2) {+			if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {+				/* Skip matching spaces and try again */+				while (i1 < s1 && XDL_ISSPACE(l1[i1]))+					i1++;+				while (i2 < s2 && XDL_ISSPACE(l2[i2]))+					i2++;+				continue;+			}+			if (l1[i1++] != l2[i2++])+				return 0;+		}+	} else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {+		while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])+			; /* keep going */+	}++	/*+	 * After running out of one side, the remaining side must have+	 * nothing but whitespace for the lines to match.  Note that+	 * ignore-whitespace-at-eol case may break out of the loop+	 * while there still are characters remaining on both lines.+	 */+	if (i1 < s1) {+		while (i1 < s1 && XDL_ISSPACE(l1[i1]))+			i1++;+		if (s1 != i1)+			return 0;+	}+	if (i2 < s2) {+		while (i2 < s2 && XDL_ISSPACE(l2[i2]))+			i2++;+		return (s2 == i2);+	}+	return 1;+}++static unsigned long xdl_hash_record_with_whitespace(char const **data,+		char const *top, long flags) {+	unsigned long ha = 5381;+	char const *ptr = *data;++	for (; ptr < top && *ptr != '\n'; ptr++) {+		if (XDL_ISSPACE(*ptr)) {+			const char *ptr2 = ptr;+			int at_eol;+			while (ptr + 1 < top && XDL_ISSPACE(ptr[1])+					&& ptr[1] != '\n')+				ptr++;+			at_eol = (top <= ptr + 1 || ptr[1] == '\n');+			if (flags & XDF_IGNORE_WHITESPACE)+				; /* already handled */+			else if (flags & XDF_IGNORE_WHITESPACE_CHANGE+				 && !at_eol) {+				ha += (ha << 5);+				ha ^= (unsigned long) ' ';+			}+			else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL+				 && !at_eol) {+				while (ptr2 != ptr + 1) {+					ha += (ha << 5);+					ha ^= (unsigned long) *ptr2;+					ptr2++;+				}+			}+			continue;+		}+		ha += (ha << 5);+		ha ^= (unsigned long) *ptr;+	}+	*data = ptr < top ? ptr + 1: ptr;++	return ha;+}+++unsigned long xdl_hash_record(char const **data, char const *top, long flags) {+	unsigned long ha = 5381;+	char const *ptr = *data;++	if (flags & XDF_WHITESPACE_FLAGS)+		return xdl_hash_record_with_whitespace(data, top, flags);++	for (; ptr < top && *ptr != '\n'; ptr++) {+		ha += (ha << 5);+		ha ^= (unsigned long) *ptr;+	}+	*data = ptr < top ? ptr + 1: ptr;++	return ha;+}+++unsigned int xdl_hashbits(unsigned int size) {+	unsigned int val = 1, bits = 0;++	for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);+	return bits ? bits: 1;+}+++int xdl_num_out(char *out, long val) {+	char *ptr, *str = out;+	char buf[32];++	ptr = buf + sizeof(buf) - 1;+	*ptr = '\0';+	if (val < 0) {+		*--ptr = '-';+		val = -val;+	}+	for (; val && ptr > buf; val /= 10)+		*--ptr = "0123456789"[val % 10];+	if (*ptr)+		for (; *ptr; ptr++, str++)+			*str = *ptr;+	else+		*str++ = '0';+	*str = '\0';++	return (int)(str - out);+}+++long xdl_atol(char const *str, char const **next) {+	long val, base;+	char const *top;++	for (top = str; XDL_ISDIGIT(*top); top++);+	if (next)+		*next = top;+	for (val = 0, base = 1, top--; top >= str; top--, base *= 10)+		val += base * (long)(*top - '0');+	return val;+}+++int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,+		      const char *func, long funclen, xdemitcb_t *ecb) {+	int nb = 0;+	mmbuffer_t mb;+	char buf[128];++	memcpy(buf, "@@ -", 4);+	nb += 4;++	nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);++	if (c1 != 1) {+		memcpy(buf + nb, ",", 1);+		nb += 1;++		nb += xdl_num_out(buf + nb, c1);+	}++	memcpy(buf + nb, " +", 2);+	nb += 2;++	nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);++	if (c2 != 1) {+		memcpy(buf + nb, ",", 1);+		nb += 1;++		nb += xdl_num_out(buf + nb, c2);+	}++	memcpy(buf + nb, " @@", 3);+	nb += 3;+	if (func && funclen) {+		buf[nb++] = ' ';+		if (funclen > (long)sizeof(buf) - nb - 1)+			funclen = (long)sizeof(buf) - nb - 1;+		memcpy(buf + nb, func, funclen);+		nb += funclen;+	}+	buf[nb++] = '\n';++	mb.ptr = buf;+	mb.size = nb;+	if (ecb->outf(ecb->priv, &mb, 1) < 0)+		return -1;++	return 0;+}++int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,+		int line1, int count1, int line2, int count2)+{+	/*+	 * This probably does not work outside Git, since+	 * we have a very simple mmfile structure.+	 *+	 * Note: ideally, we would reuse the prepared environment, but+	 * the libxdiff interface does not (yet) allow for diffing only+	 * ranges of lines instead of the whole files.+	 */+	mmfile_t subfile1, subfile2;+	xdfenv_t env;++	subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;+	subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr ++		diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;+	subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;+	subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr ++		diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;+	if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)+		return -1;++	memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);+	memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);++	xdl_free_env(&env);++	return 0;+}
+ libgit2/src/xdiff/xutils.h view
@@ -0,0 +1,49 @@+/*+ *  LibXDiff by Davide Libenzi ( File Differential Library )+ *  Copyright (C) 2003  Davide Libenzi+ *+ *  This library is free software; you can redistribute it and/or+ *  modify it under the terms of the GNU Lesser General Public+ *  License as published by the Free Software Foundation; either+ *  version 2.1 of the License, or (at your option) any later version.+ *+ *  This library is distributed in the hope that it will be useful,+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+ *  Lesser General Public License for more details.+ *+ *  You should have received a copy of the GNU Lesser General Public+ *  License along with this library; if not, write to the Free Software+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+ *+ *  Davide Libenzi <davidel@xmailserver.org>+ *+ */++#if !defined(XUTILS_H)+#define XUTILS_H++++long xdl_bogosqrt(long n);+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,+		     xdemitcb_t *ecb);+int xdl_cha_init(chastore_t *cha, long isize, long icount);+void xdl_cha_free(chastore_t *cha);+void *xdl_cha_alloc(chastore_t *cha);+void *xdl_cha_first(chastore_t *cha);+void *xdl_cha_next(chastore_t *cha);+long xdl_guess_lines(mmfile_t *mf, long sample);+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);+unsigned long xdl_hash_record(char const **data, char const *top, long flags);+unsigned int xdl_hashbits(unsigned int size);+int xdl_num_out(char *out, long val);+long xdl_atol(char const *str, char const **next);+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,+		      const char *func, long funclen, xdemitcb_t *ecb);+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,+		       int line1, int count1, int line2, int count2);++++#endif /* #if !defined(XUTILS_H) */
+ test/Main.hs view
@@ -0,0 +1,24 @@+module Main where++import Bindings.Libgit2+import Control.Monad+import Foreign.C.String+import Foreign.Marshal.Alloc+import Foreign.Storable+import Prelude+import System.Exit+import System.Process (system)++main :: IO ()+main = do+  putStrLn "Creating Git repository..."+  _ <- system "git init smoke.git"++  putStrLn "Accessing directly..."+  alloca $ \ptr -> do+    withCString "smoke.git/.git" $ \str -> do+      r <- c'git_repository_open ptr str+      when (r < 0) $ exitWith (ExitFailure 1)+      peek ptr >>= c'git_repository_free++-- Main.hs ends here
− tests/Main.hs
@@ -1,24 +0,0 @@-module Main where--import Bindings.Libgit2-import Control.Monad-import Foreign.C.String-import Foreign.Marshal.Alloc-import Foreign.Storable-import Prelude-import System.Exit-import System.Process (system)--main :: IO ()-main = do-  putStrLn "Creating Git repository..."-  _ <- system "git init smoke.git"--  putStrLn "Accessing directly..."-  alloca $ \ptr -> do-    withCString "smoke.git/.git" $ \str -> do-      r <- c'git_repository_open ptr str-      when (r < 0) $ exitWith (ExitFailure 1)-      peek ptr >>= c'git_repository_free---- Main.hs ends here