// // aegis - project change supervisor // Copyright (C) 2001-2006, 2008, 2012, 2014 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include #include static int starts_with(string_ty *line, const char *prefix) { size_t pfxlen; pfxlen = strlen(prefix); return ( line->str_length > pfxlen + 1 && 0 == memcmp(line->str_text, prefix, pfxlen) && ( line->str_text[pfxlen] == ' ' || line->str_text[pfxlen] == '\t' ) ); } static string_ty * second_word(string_ty *line) { const char *cp; const char *ep; cp = line->str_text; while (*cp && *cp != ' ' && *cp != '\t') ++cp; while (*cp && (*cp == ' ' || *cp == '\t')) ++cp; ep = cp; while (*ep && *ep != ' ' && *ep != '\t') ++ep; return str_n_from_c(cp, ep - cp); } static patch_ty * uni_diff_header(patch_context_ty *context) { string_ty *line; int idx; patch_ty *result; string_ty *s; trace(("uni_diff_header()\n{\n")); result = patch_new(); // // Look for the optional index line. // line = patch_context_getline(context, 0); if (!line) { oops: patch_delete(result); trace(("return 0\n")); trace(("}\n")); return 0; } idx = 0; if (starts_with(line, "Index:")) { s = second_word(line); result->name.push_back(s); str_free(s); idx++; } // // Look for the optional before line. // line = patch_context_getline(context, idx); if (!line) goto oops; static string_ty *dev_null; if (!dev_null) dev_null = str_from_c("/dev/null"); if (starts_with(line, "---")) { s = second_word(line); if (!str_equal(s, dev_null)) result->name.push_back(s); str_free(s); idx++; } // // Look for the optional after line. // line = patch_context_getline(context, idx); if (!line) goto oops; if (starts_with(line, "+++")) { s = second_word(line); if (!str_equal(s, dev_null)) result->name.push_back(s); str_free(s); idx++; } // // If there are no names at all, // this isn't one of our files. // if (result->name.empty()) goto oops; // // Look for a line which starts with "@@" // line = patch_context_getline(context, idx); if (!starts_with(line, "@@")) goto oops; // // Discard all of the header lines, except @@ line // (it's actually part of the first hunk). // patch_context_discard(context, idx); trace(("return %p\n", result)); trace(("}\n")); return result; } static const char * getnum(const char *cp, int *np) { int n; switch (*cp) { default: return 0; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; } n = 0; for (;;) { n = n * 10 + *cp++ - '0'; switch (*cp) { default: break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; } break; } *np = n; return cp; } static int range(string_ty *line, int *b1, int *b2, int *a1, int *a2) { const char *cp; // // The lines are of the form // @@ -N[,N] +N[,N] @@ // // The first pair of numbers is a start line and a line count // (the count defaults to 1 if not given) of the first file, // the second pair of numbers applies to the second file. // trace(("range(line = \"%s\")\n{\n", line->str_text)); if (line->str_length < 11) { oops: trace(("return 0;\n}\n")); return 0; } cp = line->str_text; if (*cp++ != '@') goto oops; if (*cp++ != '@') goto oops; if (*cp++ != ' ') goto oops; if (*cp++ != '-') goto oops; cp = getnum(cp, b1); if (!cp) goto oops; if (*cp == ',') { ++cp; cp = getnum(cp, b2); if (!cp) goto oops; } else *b2 = 1; if (*cp++ != ' ') goto oops; if (*cp++ != '+') goto oops; cp = getnum(cp, a1); if (!cp) goto oops; if (*cp == ',') { ++cp; cp = getnum(cp, a2); if (!cp) goto oops; } else *a2 = 1; if (*cp++ != ' ') goto oops; if (*cp++ != '@') goto oops; if (*cp++ != '@') goto oops; if (*cp) goto oops; trace(("return 1;\n}\n")); return 1; } static patch_hunk_ty * uni_diff_hunk(patch_context_ty *context) { int before1, num_before; int after1, num_after; string_ty *line; patch_hunk_ty *php; int idx; trace(("uni_diff_hunk()\n{\n")); line = patch_context_getline(context, 0); if (!line) { oops: trace(("return 0;\n}\n")); return 0; } if (!range(line, &before1, &num_before, &after1, &num_after)) goto oops; idx = 1; php = patch_hunk_new(); php->before.start_line_number = before1; php->after.start_line_number = after1; while (num_before > 0 || num_after > 0) { patch_line_type type; string_ty *value; trace(("num_before=%d\n", num_before)); trace(("num_after =%d\n", num_after)); line = patch_context_getline(context, idx++); if (!line) goto oops; trace(("line = \"%s\"\n", line->str_text)); type = patch_line_type_unchanged; switch (line->str_text[0]) { default: goto oops; case ' ': break; case '-': type = patch_line_type_deleted; break; case '+': type = patch_line_type_inserted; break; } value = str_n_from_c(line->str_text + 1, line->str_length - 1); if (type != patch_line_type_inserted) { if (num_before <= 0) goto oops; patch_line_list_append(&php->before, type, value); --num_before; } if (type != patch_line_type_deleted) { if (num_after <= 0) goto oops; patch_line_list_append(&php->after, type, value); --num_after; } str_free(value); trace(("mark\n")); } // // In the limiting case, using the diff -U0 flag, inserts // and deletes are off by one. They mean "append after" and // "delete after", but we need them to mean "insert before" and // "delete before". // if (php->before.start_line_number && php->before.length == 0) php->before.start_line_number++; if (php->after.start_line_number && php->after.length == 0) php->after.start_line_number++; // // We have a viable hunk, take them out of the context because // we won't need to backtrack them any more. // trace(("mark\n")); patch_context_discard(context, idx); trace(("mark\n")); trace(("return %p\n", php)); trace(("}\n")); return php; } patch_format_ty patch_format_uni = { "uni diff", uni_diff_header, uni_diff_hunk, }; // vim: set ts=8 sw=4 et :