/* See LICENSE file for copyright and license details. */ #include "common.h" static size_t hunk_head(const char *text, size_t len, size_t *first, size_t *last) { size_t off = 1, 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; } return off; } int libpatch_parse_ed_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, line, last, count; int in_hunk = 0, can_continue = 0, has_patch = 0; size_t *hunks = NULL, *new; size_t nhunks = 0, hunks_size = 0; struct libpatch_patch *patchcopy = NULL; size_t patchcopysize = 0; *patch = NULL; *patchlen = 0; while ((len = libpatch_next_line__(&text, &textlen, len))) { if (can_continue) { can_continue = 0; if (len == 1 && text[0] == 'a') { in_hunk = 1; } else if (len == 1 && text[0] == 'i') { goto garbage; /* TODO */ } else if (len >= 5 && !strncmp(text, "s/", 2) && text[len - 1] == '/') { if (*patchlen < 2 || !IS_LINE((*patch)[*patchlen - 2].type)) goto garbage; /* TODO add support for more weird alternatives */ if (!(len == 5 && !strncmp(text, "s/.//", len)) && !(len == 6 && !strncmp(text, "s/^.//", len)) && !(len == 6 && !strncmp(text, "s/\\.//", len)) && !(len == 7 && !strncmp(text, "s/^\\.//", len))) goto garbage; (*patch)[*patchlen - 2].text_offset += 1; (*patch)[*patchlen - 2].text_length -= 1; APPEND_STRING(len, LIBPATCH_PATCH_SYNTACTICAL); } else { goto else_if; } APPEND_STRING(len, LIBPATCH_PATCH_SYNTACTICAL); } else else_if: if (in_hunk && len == 1 && text[0] == '.') { APPEND_STRING(len, LIBPATCH_PATCH_SYNTACTICAL); in_hunk = 0; can_continue = 1; } else if (in_hunk) { APPEND_STRING(0, LIBPATCH_PATCH_FILE2_ONLY); (*patch)[*patchlen - 1].file1_lineno = line; } else if (len >= 5 && !strncmp(text, "diff ", 5)) { if (has_patch && textend) break; if (libpatch_reverse_hunks__(*patch, *patchlen, hunks, nhunks, &patchcopy, &patchcopysize)) goto fail; nhunks = 0; APPEND_STRING(5, LIBPATCH_PATCH_DIFF_LINE); } else if (len >= 2 && isdigit(text[0])) { off = hunk_head(text, len, &line, &last); if (len - off != 1) goto garbage; if (!line && last) goto garbage; else if (!line) count = 0, line = 1; else if (last < line) goto garbage; else count = last - line + 1; if (text[off] == 'd') { APPEND_NO_TEXT(LIBPATCH_PATCH_HUNK_WITH_DELETION); (*patch)[*patchlen - 1].first_line = line; (*patch)[*patchlen - 1].line_count = count; } else if (text[off] == 'a') { APPEND_NO_TEXT(LIBPATCH_PATCH_HUNK); in_hunk = 1; } else if (text[off] == 'c') { APPEND_NO_TEXT(LIBPATCH_PATCH_HUNK_WITH_DELETION); (*patch)[*patchlen - 1].first_line = line; (*patch)[*patchlen - 1].line_count = count; in_hunk = 1; } else if (text[off] == 'i') { goto garbage; /* TODO */ } else { goto garbage; } if (nhunks == hunks_size) { if (hunks_size > SIZE_MAX / sizeof(*hunks) - 16) goto enomem; new = realloc(hunks, (hunks_size += 16) * sizeof(*hunks)); if (!new) goto fail; hunks = new; } hunks[nhunks++] = *patchlen - 1; has_patch = 1; } else { garbage: APPEND_STRING(0, LIBPATCH_PATCH_GARBAGE); if (len == textlen) (*patch)[*patchlen - 1].lf_terminated = 0; } } if (libpatch_reverse_hunks__(*patch, *patchlen, hunks, nhunks, &patchcopy, &patchcopysize)) goto fail; if (textend) *textend = (size_t)(text - patchtext); free(patchcopy); free(hunks); return 0; enomem: errno = ENOMEM; fail: free(patchcopy); free(hunks); free(*patch); *patch = NULL; *patchlen = 0; return -1; }