// // aegis - project change supervisor // Copyright (C) 2002-2006, 2008 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 #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 int parse_command(string_ty *line, long *b1, long *b2, char *c, long *a1, long *a2) { char *s; char *end; // // Get the first range. // s = line->str_text; *b1 = strtol(s, &end, 10); if (s == end) return 0; s = end; if (*s == ',') { ++s; *b2 = strtol(s, &end, 10); if (s == end) return 0; s = end; } else *b2 = *b1; // // Get the command. // if (!strchr("acd", *s)) return 0; *c = *s++; // // Get the second range. // *a1 = strtol(s, &end, 10); if (s == end) return 0; s = end; if (*s == ',') { ++s; *a2 = strtol(s, &end, 10); if (s == end) return 0; s = end; } else *a2 = *a1; if (*c == 'a' && *b1 != *b2) return 0; if (*c == 'd' && *a1 != *a2) return 0; // // Make sure there is no junk on the end of the line. // return !*s; } 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 * diff_header(patch_context_ty *context) { string_ty *line; int idx; patch_ty *result; string_ty *s; long b1, b2, a1, a2; char cmd; trace(("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 a diff -r line, with two file names on it. // This may be the only clue we get as to the file names. // line = patch_context_getline(context, idx); if (!line) goto oops; if (starts_with(line, "diff")) { string_list_ty wl; size_t j; wl.split(line, 0, true); for (j = 1; j < wl.nstrings; ++j) if (wl.string[j]->str_text[0] != '-') break; if (j + 2 == wl.nstrings) { static string_ty *dev_null; if (!dev_null) dev_null = str_from_c("/dev/null"); s = wl.string[j]; if (!str_equal(s, dev_null)) result->name.push_back(s); s = wl.string[j + 1]; if (!str_equal(s, dev_null)) result->name.push_back(s); // // Get next line, we've used this one. // ++idx; line = patch_context_getline(context, idx); if (!line) goto oops; } } // // Unlike "diff -u" or "diff -c", it is possible with this format to // have zero filenames. This happens to aeannotate in particular. // // So you can't say // if (result->name.nstrings == 0) goto oops; // at this point, HOWEVER other code needs at least one filename. // if (result->name.empty()) { string_ty *nsf = str_from_c("(no file name present)"); result->name.push_back(nsf); str_free(nsf); } // // Look for a line which contains one of our commands // if (!parse_command(line, &b1, &b2, &cmd, &a1, &a2)) goto oops; // // Discard all of the header lines, except the command line // (it's actually part of the first hunk). // patch_context_discard(context, idx); trace(("return %08lX\n", (long)result)); trace(("}\n")); return result; } static patch_hunk_ty * diff_hunk(patch_context_ty *context) { long before1; long before2; long after1; long after2; char cmd; string_ty *line; patch_hunk_ty *php; int idx; long j; string_ty *value; trace(("diff_hunk()\n{\n")); line = patch_context_getline(context, 0); if (!line) { oops: trace(("return 0;\n}\n")); return 0; } if (!parse_command(line, &before1, &before2, &cmd, &after1, &after2)) goto oops; idx = 1; php = patch_hunk_new(); php->before.start_line_number = before1; php->after.start_line_number = after1; switch (cmd) { case 'a': php->before.start_line_number++; for (j = after1; j <= after2; ++j) { line = patch_context_getline(context, idx++); if (!line) goto oops; if ( line->str_length < 2 || line->str_text[0] != '>' || line->str_text[1] != ' ' ) goto oops; value = str_n_from_c(line->str_text + 2, line->str_length - 2); patch_line_list_append ( &php->after, patch_line_type_inserted, value ); str_free(value); } break; case 'c': for (j = before1; j <= before2; ++j) { line = patch_context_getline(context, idx++); if (!line) goto oops; if ( line->str_length < 2 || line->str_text[0] != '<' || line->str_text[1] != ' ' ) goto oops; value = str_n_from_c(line->str_text + 2, line->str_length - 2); patch_line_list_append ( &php->before, patch_line_type_deleted, value ); str_free(value); } line = patch_context_getline(context, idx++); if (!line) goto oops; static string_ty *dash3; if (!dash3) dash3 = str_from_c("---"); if (!str_equal(line, dash3)) goto oops; for (j = after1; j <= after2; ++j) { line = patch_context_getline(context, idx++); if (!line) goto oops; if ( line->str_length < 2 || line->str_text[0] != '>' || line->str_text[1] != ' ' ) goto oops; value = str_n_from_c(line->str_text + 2, line->str_length - 2); patch_line_list_append ( &php->after, patch_line_type_inserted, value ); str_free(value); } break; case 'd': php->after.start_line_number++; for (j = before1; j <= before2; ++j) { line = patch_context_getline(context, idx++); if (!line) goto oops; if ( line->str_length < 2 || line->str_text[0] != '<' || line->str_text[1] != ' ' ) goto oops; value = str_n_from_c(line->str_text + 2, line->str_length - 2); patch_line_list_append ( &php->before, patch_line_type_deleted, value ); str_free(value); } break; default: assert(0); break; } // // We have a viable hunk, take them out of the context because // we won't need to backtrack them any more. // patch_context_discard(context, idx); trace(("return %08lX\n", (long)php)); trace(("}\n")); return php; } patch_format_ty patch_format_diff = { "diff", diff_header, diff_hunk, };