diff options
Diffstat (limited to 'convert.c')
-rw-r--r-- | convert.c | 512 |
1 files changed, 200 insertions, 312 deletions
@@ -80,24 +80,19 @@ static int is_binary(unsigned long size, struct text_stat *stats) return 0; } -static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep, int action) +static int crlf_to_git(const char *path, const char *src, size_t len, + struct strbuf *buf, int action) { - char *buffer, *dst; - unsigned long size, nsize; struct text_stat stats; + char *dst; - if ((action == CRLF_BINARY) || !auto_crlf) - return NULL; - - size = *sizep; - if (!size) - return NULL; - - gather_stats(src, size, &stats); + if ((action == CRLF_BINARY) || !auto_crlf || !len) + return 0; + gather_stats(src, len, &stats); /* No CR? Nothing to convert, regardless. */ if (!stats.cr) - return NULL; + return 0; if (action == CRLF_GUESS) { /* @@ -106,24 +101,19 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep * stuff? */ if (stats.cr != stats.crlf) - return NULL; + return 0; /* * And add some heuristics for binary vs text, of course... */ - if (is_binary(size, &stats)) - return NULL; + if (is_binary(len, &stats)) + return 0; } - /* - * Ok, allocate a new buffer, fill it in, and return it - * to let the caller know that we switched buffers. - */ - nsize = size - stats.crlf; - buffer = xmalloc(nsize); - *sizep = nsize; - - dst = buffer; + /* only grow if not in place */ + if (strbuf_avail(buf) + buf->len < len) + strbuf_grow(buf, len - buf->len); + dst = buf->buf; if (action == CRLF_GUESS) { /* * If we guessed, we already know we rejected a file with @@ -134,120 +124,112 @@ static char *crlf_to_git(const char *path, const char *src, unsigned long *sizep unsigned char c = *src++; if (c != '\r') *dst++ = c; - } while (--size); + } while (--len); } else { do { unsigned char c = *src++; - if (! (c == '\r' && (1 < size && *src == '\n'))) + if (! (c == '\r' && (1 < len && *src == '\n'))) *dst++ = c; - } while (--size); + } while (--len); } - - return buffer; + strbuf_setlen(buf, dst - buf->buf); + return 1; } -static char *crlf_to_worktree(const char *path, const char *src, unsigned long *sizep, int action) +static int crlf_to_worktree(const char *path, const char *src, size_t len, + struct strbuf *buf, int action) { - char *buffer, *dst; - unsigned long size, nsize; + char *to_free = NULL; struct text_stat stats; - unsigned char last; if ((action == CRLF_BINARY) || (action == CRLF_INPUT) || auto_crlf <= 0) - return NULL; + return 0; - size = *sizep; - if (!size) - return NULL; + if (!len) + return 0; - gather_stats(src, size, &stats); + gather_stats(src, len, &stats); /* No LF? Nothing to convert, regardless. */ if (!stats.lf) - return NULL; + return 0; /* Was it already in CRLF format? */ if (stats.lf == stats.crlf) - return NULL; + return 0; if (action == CRLF_GUESS) { /* If we have any bare CR characters, we're not going to touch it */ if (stats.cr != stats.crlf) - return NULL; + return 0; - if (is_binary(size, &stats)) - return NULL; + if (is_binary(len, &stats)) + return 0; } - /* - * Ok, allocate a new buffer, fill it in, and return it - * to let the caller know that we switched buffers. - */ - nsize = size + stats.lf - stats.crlf; - buffer = xmalloc(nsize); - *sizep = nsize; - last = 0; - - dst = buffer; - do { - unsigned char c = *src++; - if (c == '\n' && last != '\r') - *dst++ = '\r'; - *dst++ = c; - last = c; - } while (--size); - - return buffer; + /* are we "faking" in place editing ? */ + if (src == buf->buf) + to_free = strbuf_detach(buf, NULL); + + strbuf_grow(buf, len + stats.lf - stats.crlf); + for (;;) { + const char *nl = memchr(src, '\n', len); + if (!nl) + break; + if (nl > src && nl[-1] == '\r') { + strbuf_add(buf, src, nl + 1 - src); + } else { + strbuf_add(buf, src, nl - src); + strbuf_addstr(buf, "\r\n"); + } + len -= nl + 1 - src; + src = nl + 1; + } + strbuf_add(buf, src, len); + + free(to_free); + return 1; } -static int filter_buffer(const char *path, const char *src, - unsigned long size, const char *cmd) +struct filter_params { + const char *src; + unsigned long size; + const char *cmd; +}; + +static int filter_buffer(int fd, void *data) { /* * Spawn cmd and feed the buffer contents through its stdin. */ struct child_process child_process; - int pipe_feed[2]; + struct filter_params *params = (struct filter_params *)data; int write_err, status; + const char *argv[] = { "sh", "-c", params->cmd, NULL }; memset(&child_process, 0, sizeof(child_process)); + child_process.argv = argv; + child_process.in = -1; + child_process.out = fd; - if (pipe(pipe_feed) < 0) { - error("cannot create pipe to run external filter %s", cmd); - return 1; - } + if (start_command(&child_process)) + return error("cannot fork to run external filter %s", params->cmd); - child_process.pid = fork(); - if (child_process.pid < 0) { - error("cannot fork to run external filter %s", cmd); - close(pipe_feed[0]); - close(pipe_feed[1]); - return 1; - } - if (!child_process.pid) { - dup2(pipe_feed[0], 0); - close(pipe_feed[0]); - close(pipe_feed[1]); - execlp("sh", "sh", "-c", cmd, NULL); - return 1; - } - close(pipe_feed[0]); - - write_err = (write_in_full(pipe_feed[1], src, size) < 0); - if (close(pipe_feed[1])) + write_err = (write_in_full(child_process.in, params->src, params->size) < 0); + if (close(child_process.in)) write_err = 1; if (write_err) - error("cannot feed the input to external filter %s", cmd); + error("cannot feed the input to external filter %s", params->cmd); status = finish_command(&child_process); if (status) - error("external filter %s failed %d", cmd, -status); + error("external filter %s failed %d", params->cmd, -status); return (write_err || status); } -static char *apply_filter(const char *path, const char *src, - unsigned long *sizep, const char *cmd) +static int apply_filter(const char *path, const char *src, size_t len, + struct strbuf *dst, const char *cmd) { /* * Create a pipeline to have the command filter the buffer's @@ -255,77 +237,44 @@ static char *apply_filter(const char *path, const char *src, * * (child --> cmd) --> us */ - const int SLOP = 4096; - int pipe_feed[2]; - int status; - char *dst; - unsigned long dstsize, dstalloc; - struct child_process child_process; + int ret = 1; + struct strbuf nbuf; + struct async async; + struct filter_params params; if (!cmd) - return NULL; + return 0; - memset(&child_process, 0, sizeof(child_process)); - - if (pipe(pipe_feed) < 0) { - error("cannot create pipe to run external filter %s", cmd); - return NULL; - } + memset(&async, 0, sizeof(async)); + async.proc = filter_buffer; + async.data = ¶ms; + params.src = src; + params.size = len; + params.cmd = cmd; fflush(NULL); - child_process.pid = fork(); - if (child_process.pid < 0) { - error("cannot fork to run external filter %s", cmd); - close(pipe_feed[0]); - close(pipe_feed[1]); - return NULL; - } - if (!child_process.pid) { - dup2(pipe_feed[1], 1); - close(pipe_feed[0]); - close(pipe_feed[1]); - exit(filter_buffer(path, src, *sizep, cmd)); - } - close(pipe_feed[1]); - - dstalloc = *sizep; - dst = xmalloc(dstalloc); - dstsize = 0; + if (start_async(&async)) + return 0; /* error was already reported */ - while (1) { - ssize_t numread = xread(pipe_feed[0], dst + dstsize, - dstalloc - dstsize); - - if (numread <= 0) { - if (!numread) - break; - error("read from external filter %s failed", cmd); - free(dst); - dst = NULL; - break; - } - dstsize += numread; - if (dstalloc <= dstsize + SLOP) { - dstalloc = dstsize + SLOP; - dst = xrealloc(dst, dstalloc); - } + strbuf_init(&nbuf, 0); + if (strbuf_read(&nbuf, async.out, len) < 0) { + error("read from external filter %s failed", cmd); + ret = 0; } - if (close(pipe_feed[0])) { + if (close(async.out)) { error("read from external filter %s failed", cmd); - free(dst); - dst = NULL; + ret = 0; } - - status = finish_command(&child_process); - if (status) { - error("external filter %s failed %d", cmd, -status); - free(dst); - dst = NULL; + if (finish_async(&async)) { + error("external filter %s failed", cmd); + ret = 0; } - if (dst) - *sizep = dstsize; - return dst; + if (ret) { + strbuf_swap(dst, &nbuf); + } + strbuf_release(&nbuf); + return ret; } static struct convert_driver { @@ -353,13 +302,8 @@ static int read_convert_config(const char *var, const char *value) if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) break; if (!drv) { - char *namebuf; drv = xcalloc(1, sizeof(struct convert_driver)); - namebuf = xmalloc(namelen + 1); - memcpy(namebuf, name, namelen); - namebuf[namelen] = 0; - drv->name = namebuf; - drv->next = NULL; + drv->name = xmemdupz(name, namelen); *user_convert_tail = drv; user_convert_tail = &(drv->next); } @@ -449,137 +393,106 @@ static int count_ident(const char *cp, unsigned long size) return cnt; } -static char *ident_to_git(const char *path, const char *src, unsigned long *sizep, int ident) +static int ident_to_git(const char *path, const char *src, size_t len, + struct strbuf *buf, int ident) { - int cnt; - unsigned long size; - char *dst, *buf; + char *dst, *dollar; - if (!ident) - return NULL; - size = *sizep; - cnt = count_ident(src, size); - if (!cnt) - return NULL; - buf = xmalloc(size); - - for (dst = buf; size; size--) { - char ch = *src++; - *dst++ = ch; - if ((ch == '$') && (3 <= size) && - !memcmp("Id:", src, 3)) { - unsigned long rem = size - 3; - const char *cp = src + 3; - do { - ch = *cp++; - if (ch == '$') - break; - rem--; - } while (rem); - if (!rem) - continue; + if (!ident || !count_ident(src, len)) + return 0; + + /* only grow if not in place */ + if (strbuf_avail(buf) + buf->len < len) + strbuf_grow(buf, len - buf->len); + dst = buf->buf; + for (;;) { + dollar = memchr(src, '$', len); + if (!dollar) + break; + memcpy(dst, src, dollar + 1 - src); + dst += dollar + 1 - src; + len -= dollar + 1 - src; + src = dollar + 1; + + if (len > 3 && !memcmp(src, "Id:", 3)) { + dollar = memchr(src + 3, '$', len - 3); + if (!dollar) + break; memcpy(dst, "Id$", 3); dst += 3; - size -= (cp - src); - src = cp; + len -= dollar + 1 - src; + src = dollar + 1; } } - - *sizep = dst - buf; - return buf; + memcpy(dst, src, len); + strbuf_setlen(buf, dst + len - buf->buf); + return 1; } -static char *ident_to_worktree(const char *path, const char *src, unsigned long *sizep, int ident) +static int ident_to_worktree(const char *path, const char *src, size_t len, + struct strbuf *buf, int ident) { - int cnt; - unsigned long size; - char *dst, *buf; unsigned char sha1[20]; + char *to_free = NULL, *dollar; + int cnt; if (!ident) - return NULL; + return 0; - size = *sizep; - cnt = count_ident(src, size); + cnt = count_ident(src, len); if (!cnt) - return NULL; + return 0; - hash_sha1_file(src, size, "blob", sha1); - buf = xmalloc(size + cnt * 43); - - for (dst = buf; size; size--) { - const char *cp; - /* Fetch next source character, move the pointer on */ - char ch = *src++; - /* Copy the current character to the destination */ - *dst++ = ch; - /* If the current character is "$" or there are less than three - * remaining bytes or the two bytes following this one are not - * "Id", then simply read the next character */ - if ((ch != '$') || (size < 3) || memcmp("Id", src, 2)) - continue; - /* - * Here when - * - There are more than 2 bytes remaining - * - The current three bytes are "$Id" - * with - * - ch == "$" - * - src[0] == "I" - */ + /* are we "faking" in place editing ? */ + if (src == buf->buf) + to_free = strbuf_detach(buf, NULL); + hash_sha1_file(src, len, "blob", sha1); - /* - * It's possible that an expanded Id has crept its way into the - * repository, we cope with that by stripping the expansion out - */ - if (src[2] == ':') { - /* Expanded keywords have "$Id:" at the front */ + strbuf_grow(buf, len + cnt * 43); + for (;;) { + /* step 1: run to the next '$' */ + dollar = memchr(src, '$', len); + if (!dollar) + break; + strbuf_add(buf, src, dollar + 1 - src); + len -= dollar + 1 - src; + src = dollar + 1; - /* discard up to but not including the closing $ */ - unsigned long rem = size - 3; - /* Point at first byte after the ":" */ - cp = src + 3; - /* - * Throw away characters until either - * - we reach a "$" - * - we run out of bytes (rem == 0) - */ - do { - ch = *cp; - if (ch == '$') - break; - cp++; - rem--; - } while (rem); - /* If the above finished because it ran out of characters, then - * this is an incomplete keyword, so don't run the expansion */ - if (!rem) - continue; - } else if (src[2] == '$') - cp = src + 2; - else - /* Anything other than "$Id:XXX$" or $Id$ and we skip the - * expansion */ + /* step 2: does it looks like a bit like Id:xxx$ or Id$ ? */ + if (len < 3 || memcmp("Id", src, 2)) continue; - /* cp is now pointing at the last $ of the keyword */ - - memcpy(dst, "Id: ", 4); - dst += 4; - memcpy(dst, sha1_to_hex(sha1), 40); - dst += 40; - *dst++ = ' '; + /* step 3: skip over Id$ or Id:xxxxx$ */ + if (src[2] == '$') { + src += 3; + len -= 3; + } else if (src[2] == ':') { + /* + * It's possible that an expanded Id has crept its way into the + * repository, we cope with that by stripping the expansion out + */ + dollar = memchr(src + 3, '$', len - 3); + if (!dollar) { + /* incomplete keyword, no more '$', so just quit the loop */ + break; + } - /* Adjust for the characters we've discarded */ - size -= (cp - src); - src = cp; + len -= dollar + 1 - src; + src = dollar + 1; + } else { + /* it wasn't a "Id$" or "Id:xxxx$" */ + continue; + } - /* Copy the final "$" */ - *dst++ = *src++; - size--; + /* step 4: substitute */ + strbuf_addstr(buf, "Id: "); + strbuf_add(buf, sha1_to_hex(sha1), 40); + strbuf_addstr(buf, " $"); } + strbuf_add(buf, src, len); - *sizep = dst - buf; - return buf; + free(to_free); + return 1; } static int git_path_check_crlf(const char *path, struct git_attr_check *check) @@ -618,13 +531,12 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check) return !!ATTR_TRUE(value); } -char *convert_to_git(const char *path, const char *src, unsigned long *sizep) +int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst) { struct git_attr_check check[3]; int crlf = CRLF_GUESS; - int ident = 0; + int ident = 0, ret = 0; char *filter = NULL; - char *buf, *buf2; setup_convert_check(check); if (!git_checkattr(path, ARRAY_SIZE(check), check)) { @@ -636,30 +548,25 @@ char *convert_to_git(const char *path, const char *src, unsigned long *sizep) filter = drv->clean; } - buf = apply_filter(path, src, sizep, filter); - - buf2 = crlf_to_git(path, buf ? buf : src, sizep, crlf); - if (buf2) { - free(buf); - buf = buf2; + ret |= apply_filter(path, src, len, dst, filter); + if (ret) { + src = dst->buf; + len = dst->len; } - - buf2 = ident_to_git(path, buf ? buf : src, sizep, ident); - if (buf2) { - free(buf); - buf = buf2; + ret |= crlf_to_git(path, src, len, dst, crlf); + if (ret) { + src = dst->buf; + len = dst->len; } - - return buf; + return ret | ident_to_git(path, src, len, dst, ident); } -char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep) +int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst) { struct git_attr_check check[3]; int crlf = CRLF_GUESS; - int ident = 0; + int ident = 0, ret = 0; char *filter = NULL; - char *buf, *buf2; setup_convert_check(check); if (!git_checkattr(path, ARRAY_SIZE(check), check)) { @@ -671,34 +578,15 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long * filter = drv->smudge; } - buf = ident_to_worktree(path, src, sizep, ident); - - buf2 = crlf_to_worktree(path, buf ? buf : src, sizep, crlf); - if (buf2) { - free(buf); - buf = buf2; + ret |= ident_to_worktree(path, src, len, dst, ident); + if (ret) { + src = dst->buf; + len = dst->len; } - - buf2 = apply_filter(path, buf ? buf : src, sizep, filter); - if (buf2) { - free(buf); - buf = buf2; - } - - return buf; -} - -void *convert_sha1_file(const char *path, const unsigned char *sha1, - unsigned int mode, enum object_type *type, - unsigned long *size) -{ - void *buffer = read_sha1_file(sha1, type, size); - if (S_ISREG(mode) && buffer) { - void *converted = convert_to_working_tree(path, buffer, size); - if (converted) { - free(buffer); - buffer = converted; - } + ret |= crlf_to_worktree(path, src, len, dst, crlf); + if (ret) { + src = dst->buf; + len = dst->len; } - return buffer; + return ret | apply_filter(path, src, len, dst, filter); } |