git-daemon tests: wait until daemon is ready
[gitweb.git] / convert.c
index 0c42bcb524e2f67e50900d6d045c6dfcebb1d14d..12868ed7bda11648704ffe4e5d3415067764a6e2 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -533,7 +533,7 @@ static int ident_to_git(const char *path, const char *src, size_t len,
                dollar = memchr(src, '$', len);
                if (!dollar)
                        break;
-               memcpy(dst, src, dollar + 1 - src);
+               memmove(dst, src, dollar + 1 - src);
                dst += dollar + 1 - src;
                len -= dollar + 1 - src;
                src  = dollar + 1;
@@ -553,7 +553,7 @@ static int ident_to_git(const char *path, const char *src, size_t len,
                        src  = dollar + 1;
                }
        }
-       memcpy(dst, src, len);
+       memmove(dst, src, len);
        strbuf_setlen(buf, dst + len - buf->buf);
        return 1;
 }
@@ -641,7 +641,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
        return 1;
 }
 
-static int git_path_check_crlf(const char *path, struct git_attr_check *check)
+static enum crlf_action git_path_check_crlf(const char *path, struct git_attr_check *check)
 {
        const char *value = check->value;
 
@@ -658,7 +658,7 @@ static int git_path_check_crlf(const char *path, struct git_attr_check *check)
        return CRLF_GUESS;
 }
 
-static int git_path_check_eol(const char *path, struct git_attr_check *check)
+static enum eol git_path_check_eol(const char *path, struct git_attr_check *check)
 {
        const char *value = check->value;
 
@@ -727,7 +727,7 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
                git_config(read_convert_config, NULL);
        }
 
-       if (!git_checkattr(path, NUM_CONV_ATTRS, ccheck)) {
+       if (!git_check_attr(path, NUM_CONV_ATTRS, ccheck)) {
                ca->crlf_action = git_path_check_crlf(path, ccheck + 4);
                if (ca->crlf_action == CRLF_GUESS)
                        ca->crlf_action = git_path_check_crlf(path, ccheck + 0);
@@ -811,7 +811,7 @@ int renormalize_buffer(const char *path, const char *src, size_t len, struct str
                src = dst->buf;
                len = dst->len;
        }
-       return ret | convert_to_git(path, src, len, dst, 0);
+       return ret | convert_to_git(path, src, len, dst, SAFE_CRLF_FALSE);
 }
 
 /*****************************************************************
@@ -876,43 +876,215 @@ int is_null_stream_filter(struct stream_filter *filter)
 /*
  * LF-to-CRLF filter
  */
+
+struct lf_to_crlf_filter {
+       struct stream_filter filter;
+       unsigned has_held:1;
+       char held;
+};
+
 static int lf_to_crlf_filter_fn(struct stream_filter *filter,
                                const char *input, size_t *isize_p,
                                char *output, size_t *osize_p)
 {
-       size_t count;
+       size_t count, o = 0;
+       struct lf_to_crlf_filter *lf_to_crlf = (struct lf_to_crlf_filter *)filter;
+
+       /*
+        * We may be holding onto the CR to see if it is followed by a
+        * LF, in which case we would need to go to the main loop.
+        * Otherwise, just emit it to the output stream.
+        */
+       if (lf_to_crlf->has_held && (lf_to_crlf->held != '\r' || !input)) {
+               output[o++] = lf_to_crlf->held;
+               lf_to_crlf->has_held = 0;
+       }
+
+       /* We are told to drain */
+       if (!input) {
+               *osize_p -= o;
+               return 0;
+       }
 
-       if (!input)
-               return 0; /* we do not keep any states */
        count = *isize_p;
-       if (count) {
-               size_t i, o;
-               for (i = o = 0; o < *osize_p && i < count; i++) {
+       if (count || lf_to_crlf->has_held) {
+               size_t i;
+               int was_cr = 0;
+
+               if (lf_to_crlf->has_held) {
+                       was_cr = 1;
+                       lf_to_crlf->has_held = 0;
+               }
+
+               for (i = 0; o < *osize_p && i < count; i++) {
                        char ch = input[i];
+
                        if (ch == '\n') {
-                               if (o + 1 < *osize_p)
-                                       output[o++] = '\r';
-                               else
-                                       break;
+                               output[o++] = '\r';
+                       } else if (was_cr) {
+                               /*
+                                * Previous round saw CR and it is not followed
+                                * by a LF; emit the CR before processing the
+                                * current character.
+                                */
+                               output[o++] = '\r';
+                       }
+
+                       /*
+                        * We may have consumed the last output slot,
+                        * in which case we need to break out of this
+                        * loop; hold the current character before
+                        * returning.
+                        */
+                       if (*osize_p <= o) {
+                               lf_to_crlf->has_held = 1;
+                               lf_to_crlf->held = ch;
+                               continue; /* break but increment i */
+                       }
+
+                       if (ch == '\r') {
+                               was_cr = 1;
+                               continue;
                        }
+
+                       was_cr = 0;
                        output[o++] = ch;
                }
 
                *osize_p -= o;
                *isize_p -= i;
+
+               if (!lf_to_crlf->has_held && was_cr) {
+                       lf_to_crlf->has_held = 1;
+                       lf_to_crlf->held = '\r';
+               }
        }
        return 0;
 }
 
+static void lf_to_crlf_free_fn(struct stream_filter *filter)
+{
+       free(filter);
+}
+
 static struct stream_filter_vtbl lf_to_crlf_vtbl = {
        lf_to_crlf_filter_fn,
-       null_free_fn,
+       lf_to_crlf_free_fn,
+};
+
+static struct stream_filter *lf_to_crlf_filter(void)
+{
+       struct lf_to_crlf_filter *lf_to_crlf = xcalloc(1, sizeof(*lf_to_crlf));
+
+       lf_to_crlf->filter.vtbl = &lf_to_crlf_vtbl;
+       return (struct stream_filter *)lf_to_crlf;
+}
+
+/*
+ * Cascade filter
+ */
+#define FILTER_BUFFER 1024
+struct cascade_filter {
+       struct stream_filter filter;
+       struct stream_filter *one;
+       struct stream_filter *two;
+       char buf[FILTER_BUFFER];
+       int end, ptr;
 };
 
-static struct stream_filter lf_to_crlf_filter_singleton = {
-       &lf_to_crlf_vtbl,
+static int cascade_filter_fn(struct stream_filter *filter,
+                            const char *input, size_t *isize_p,
+                            char *output, size_t *osize_p)
+{
+       struct cascade_filter *cas = (struct cascade_filter *) filter;
+       size_t filled = 0;
+       size_t sz = *osize_p;
+       size_t to_feed, remaining;
+
+       /*
+        * input -- (one) --> buf -- (two) --> output
+        */
+       while (filled < sz) {
+               remaining = sz - filled;
+
+               /* do we already have something to feed two with? */
+               if (cas->ptr < cas->end) {
+                       to_feed = cas->end - cas->ptr;
+                       if (stream_filter(cas->two,
+                                         cas->buf + cas->ptr, &to_feed,
+                                         output + filled, &remaining))
+                               return -1;
+                       cas->ptr += (cas->end - cas->ptr) - to_feed;
+                       filled = sz - remaining;
+                       continue;
+               }
+
+               /* feed one from upstream and have it emit into our buffer */
+               to_feed = input ? *isize_p : 0;
+               if (input && !to_feed)
+                       break;
+               remaining = sizeof(cas->buf);
+               if (stream_filter(cas->one,
+                                 input, &to_feed,
+                                 cas->buf, &remaining))
+                       return -1;
+               cas->end = sizeof(cas->buf) - remaining;
+               cas->ptr = 0;
+               if (input) {
+                       size_t fed = *isize_p - to_feed;
+                       *isize_p -= fed;
+                       input += fed;
+               }
+
+               /* do we know that we drained one completely? */
+               if (input || cas->end)
+                       continue;
+
+               /* tell two to drain; we have nothing more to give it */
+               to_feed = 0;
+               remaining = sz - filled;
+               if (stream_filter(cas->two,
+                                 NULL, &to_feed,
+                                 output + filled, &remaining))
+                       return -1;
+               if (remaining == (sz - filled))
+                       break; /* completely drained two */
+               filled = sz - remaining;
+       }
+       *osize_p -= filled;
+       return 0;
+}
+
+static void cascade_free_fn(struct stream_filter *filter)
+{
+       struct cascade_filter *cas = (struct cascade_filter *)filter;
+       free_stream_filter(cas->one);
+       free_stream_filter(cas->two);
+       free(filter);
+}
+
+static struct stream_filter_vtbl cascade_vtbl = {
+       cascade_filter_fn,
+       cascade_free_fn,
 };
 
+static struct stream_filter *cascade_filter(struct stream_filter *one,
+                                           struct stream_filter *two)
+{
+       struct cascade_filter *cascade;
+
+       if (!one || is_null_stream_filter(one))
+               return two;
+       if (!two || is_null_stream_filter(two))
+               return one;
+
+       cascade = xmalloc(sizeof(*cascade));
+       cascade->one = one;
+       cascade->two = two;
+       cascade->end = cascade->ptr = 0;
+       cascade->filter.vtbl = &cascade_vtbl;
+       return (struct stream_filter *)cascade;
+}
 
 /*
  * ident filter
@@ -1083,20 +1255,12 @@ struct stream_filter *get_stream_filter(const char *path, const unsigned char *s
        crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
 
        if ((crlf_action == CRLF_BINARY) || (crlf_action == CRLF_INPUT) ||
-           (crlf_action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE)) {
-               if (filter) {
-                       free_stream_filter(filter);
-                       return NULL;
-               }
-               return &null_filter_singleton;
-       } else if (output_eol(crlf_action) == EOL_CRLF &&
-                  !(crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS)) {
-               if (filter) {
-                       free_stream_filter(filter);
-                       return NULL;
-               }
-               return &lf_to_crlf_filter_singleton;
-       }
+           (crlf_action == CRLF_GUESS && auto_crlf == AUTO_CRLF_FALSE))
+               filter = cascade_filter(filter, &null_filter_singleton);
+
+       else if (output_eol(crlf_action) == EOL_CRLF &&
+                !(crlf_action == CRLF_AUTO || crlf_action == CRLF_GUESS))
+               filter = cascade_filter(filter, lf_to_crlf_filter());
 
        return filter;
 }