diff options
Diffstat (limited to 'libpatch_parse_copied_patch.c')
-rw-r--r-- | libpatch_parse_copied_patch.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/libpatch_parse_copied_patch.c b/libpatch_parse_copied_patch.c new file mode 100644 index 0000000..b6849b8 --- /dev/null +++ b/libpatch_parse_copied_patch.c @@ -0,0 +1,153 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static size_t +hunk_head(const char *text, size_t len, const char *suffix, size_t *first, size_t *last) +{ + size_t off = 4, r; + off += r = libpatch_get_zu__(&text[off], len - off, first); + if (!r) + return 0; + if (off < len && text[off] == ',') { + off += 1; + off += r = libpatch_get_zu__(&text[off], len - off, last); + if (!r) + return 0; + } else { + *last = *first; + } + if (5 < len - off && strncmp(&text[off], suffix, 5)) + return 0; + off += 5; + return off; +} + + +int +libpatch_parse_copied_patch(const char *text, size_t textlen, size_t *textend, + struct libpatch_patch **patch, size_t *patchlen) +{ + const char *patchtext = text; + size_t len = 0, size = 0, off, lineno, last, count; + int need_hunk = 0, in_hunk = 0, type, has_patch = 0; + + *patch = NULL; + *patchlen = 0; + + while ((len = libpatch_next_line__(&text, &textlen, len))) { + if (len >= 5 && !strncmp(text, "diff ", 5)) { + if (has_patch && textend) + break; + APPEND_STRING(5, LIBPATCH_PATCH_DIFF_LINE); + need_hunk = 0; + in_hunk = 0; + + } else if (len >= 15 && !strncmp(text, "***************", 15)) { + APPEND_OPTSTRING(15, LIBPATCH_PATCH_HUNK); + need_hunk = 1; + in_hunk = 0; + has_patch = 1; + + } else if (len >= 3 && !strncmp(text, "***", 3)) { + if (!need_hunk) { + APPEND_OPTSTRING(3, LIBPATCH_PATCH_FILE1_LABEL); + (*patch)[*patchlen - 1].lf_terminated = 0; + } else if (need_hunk == 1 && len > 9 && text[3] == ' ') { + off = hunk_head(text, len, " ****", &lineno, &last); + if (!off) + goto garbage; + APPEND_OPTSTRING(off, LIBPATCH_PATCH_HUNK_FILE1); + if (!lineno && last) + goto garbage; + else if (!lineno) + count = 0; + else if (last < lineno) + goto garbage; + else + count = last - lineno + 1; + need_hunk = 2; + in_hunk = lineno > 0 ? 1 : 0; + } else { + goto garbage; + } + + } else if (len >= 3 && !strncmp(text, "---", 3)) { + if (!need_hunk) { + APPEND_OPTSTRING(3, LIBPATCH_PATCH_FILE2_LABEL); + (*patch)[*patchlen - 1].lf_terminated = 0; + has_patch = 1; + } else if (need_hunk == 2 && len > 9 && text[3] == ' ') { + off = hunk_head(text, len, " ----", &lineno, &last); + if (!off) + goto garbage; + APPEND_OPTSTRING(off, LIBPATCH_PATCH_HUNK_FILE2); + count = 0; + if (!lineno && last) + goto garbage; + else if (!lineno) + count = 0; + else if (last < lineno) + goto garbage; + else + count = last - lineno + 1; + need_hunk = 0; + in_hunk = lineno > 0 ? 2 : 0; + } else { + goto garbage; + } + + } else if (len >= 2 && in_hunk == 1 && !strncmp(text, "- ", 2)) { + type = LIBPATCH_PATCH_FILE1_ONLY; + goto file1_line; + } else if (len >= 2 && in_hunk == 1 && !strncmp(text, "! ", 2)) { + type = LIBPATCH_PATCH_PRECHANGE; + goto file1_line; + } else if (len >= 2 && in_hunk == 1 && !strncmp(text, " ", 2)) { + type = LIBPATCH_PATCH_FILE1_CONTEXT; + file1_line: + APPEND_OPTSTRING(2, type); + if (!--count) + in_hunk = 0; + (*patch)[*patchlen - 1].file1_lineno = lineno++; + + } else if (len >= 2 && in_hunk == 2 && !strncmp(text, "+ ", 2)) { + type = LIBPATCH_PATCH_FILE2_ONLY; + goto file2_line; + } else if (len >= 2 && in_hunk == 2 && !strncmp(text, "! ", 2)) { + type = LIBPATCH_PATCH_POSTCHANGE; + goto file2_line; + } else if (len >= 2 && in_hunk == 2 && !strncmp(text, " ", 2)) { + type = LIBPATCH_PATCH_FILE2_CONTEXT; + file2_line: + APPEND_OPTSTRING(2, type); + if (!--count) + in_hunk = 0; + (*patch)[*patchlen - 1].file2_lineno = lineno++; + + } else if (len >= 1 && !strncmp(text, "\\", 1) && *patchlen) { + if (IS_LINE((*patch)[*patchlen - 1].type) || + !(*patch)[*patchlen - 1].lf_terminated) + goto garbage; + (*patch)[*patchlen - 1].lf_terminated = 0; + APPEND_NO_TEXT(LIBPATCH_PATCH_SYNTACTICAL); + + } else { + garbage: + APPEND_STRING(0, LIBPATCH_PATCH_GARBAGE); + if (len == textlen) + (*patch)[*patchlen - 1].lf_terminated = 0; + } + } + + if (textend) + *textend = (size_t)(text - patchtext); + + return 0; + +fail: + free(*patch); + *patch = NULL; + *patchlen = 0; + return -1; +} |