/* See LICENSE file for copyright and license details. */ #include "common.h" static size_t hunk_head_part(const char *text, size_t len, size_t *first, size_t *count) { size_t off, r; off = libpatch_get_zu__(&text[off], len - off, first); if (!off) return 0; if (off < len && text[off] == ',') { off += 1; off += r = libpatch_get_zu__(&text[off], len - off, count); if (!r) return 0; } else if (*count) { *count = 1; } return off; } int libpatch_parse_unified_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, r, lineno1, lineno2, count1, count2; int in_hunk = 0, 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); in_hunk = 0; } else if (len >= 4 && !strncmp(text, "@@ -", 4)) { off = 4; off += r = hunk_head_part(&text[off], len - off, &lineno1, &count1); if (!r || 2 >= len - off || strncmp(&text[off], " +", 2)) goto garbage; off += 2; off += r = hunk_head_part(&text[off], len - off, &lineno2, &count2); if (!r || 3 > len - off || strncmp(&text[off], " @@", 3)) goto garbage; off += 3; APPEND_OPTSTRING(off, LIBPATCH_PATCH_HUNK); in_hunk = ((count1 ? 1 : 0) | (count2 ? 2 : 0)); has_patch = 1; } else if (!in_hunk && len >= 3 && !strncmp(text, "---", 3)) { APPEND_OPTSTRING(3, LIBPATCH_PATCH_FILE1_LABEL); (*patch)[*patchlen - 1].lf_terminated = 0; } else if (!in_hunk && len >= 3 && !strncmp(text, "+++", 3)) { APPEND_OPTSTRING(3, LIBPATCH_PATCH_FILE2_LABEL); (*patch)[*patchlen - 1].lf_terminated = 0; has_patch = 1; } else if ((in_hunk & 1) && len >= 1 && text[0] == '-') { APPEND_OPTSTRING(1, LIBPATCH_PATCH_FILE1_ONLY); if (!--count1) in_hunk ^= 1; (*patch)[*patchlen - 1].file1_lineno = lineno1++; } else if ((in_hunk & 2) && len >= 1 && text[0] == '+') { APPEND_OPTSTRING(1, LIBPATCH_PATCH_FILE2_ONLY); if (!--count2) in_hunk ^= 2; (*patch)[*patchlen - 1].file2_lineno = lineno2++; } else if (in_hunk == 3 && len >= 1 && text[0] == ' ') { APPEND_OPTSTRING(1, LIBPATCH_PATCH_CONTEXT); if (!--count1) in_hunk ^= 1; if (!--count2) in_hunk ^= 2; (*patch)[*patchlen - 1].file1_lineno = lineno1++; (*patch)[*patchlen - 1].file2_lineno = lineno2++; } 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; }