/* * aegis - project change supervisor * Copyright (C) 2001 Peter Miller; * All rights reserved. * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * MANIFEST: functions to manipulate contexts */ #include #include #include #include #include #include #include static string_ty *star15; static int starts_with _((string_ty *, const char *, size_t)); static int starts_with(line, prefix, pfxlen) string_ty *line; const char *prefix; size_t pfxlen; { 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 *)); static string_ty * second_word(line) 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 *context_diff_header _((patch_context_ty *)); static patch_ty * context_diff_header(context) patch_context_ty *context; { string_ty *line; int idx; patch_ty *result; string_ty *s; trace(("context_diff_header()\n{\n")); if (!star15) star15 = str_from_c("***************"); 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:", 6)) { s = second_word(line); string_list_append(&result->name, s); str_free(s); idx++; } /* * Look for the optional before line. */ line = patch_context_getline(context, idx); if (!line) goto oops; if (starts_with(line, "***", 3)) { s = second_word(line); string_list_append(&result->name, 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, "---", 3)) { s = second_word(line); string_list_append(&result->name, s); str_free(s); idx++; } /* * If there are no names at all, * this isn't one of our files. */ if (result->name.nstrings == 0) goto oops; /* * Look for a line which contains exactly 15 stars. */ line = patch_context_getline(context, idx); if (!str_equal(line, star15)) goto oops; /* * Discard all of the header lines, except the row of stars * (it's actually part of the first hunk). */ patch_context_discard(context, idx); trace(("return %08lX\n", (long)result)); trace(("}\n")); return result; } static int range _((string_ty *, const char *, int *, int *)); static int range(line, prefix, n1, n2) string_ty *line; const char *prefix; int *n1; int *n2; { const char *cp; int n; trace(("range(line = \"%s\", pfx = \"%s\")\n{\n", line->str_text, prefix)); if (line->str_length < 10) { oops: trace(("return 0;\n}\n")); return 0; } cp = line->str_text; if (0 != memcmp(cp, prefix, 3)) goto oops; cp += 3; if (*cp++ != ' ') goto oops; switch (*cp) { default: goto oops; 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; } *n1 = n; *n2 = n; trace(("n1 = %d\n", n)); if (*cp == ',') { ++cp; switch (*cp) { default: goto oops; 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; } *n2 = n; trace(("n2 = %d\n", n)); if (*n1 > *n2) goto oops; } if (*cp++ != ' ') goto oops; if (0 != memcmp(cp, prefix, 4)) goto oops; cp += 4; if (*cp) goto oops; trace(("return 1;\n}\n")); return 1; } static int line_append _((patch_line_list_ty *, string_ty *)); static int line_append(pllp, line) patch_line_list_ty *pllp; string_ty *line; { patch_line_type type; string_ty *value; if (line->str_length < 2) return 0; if (line->str_text[1] != ' ') return 0; type = patch_line_type_unchanged; switch (line->str_text[0]) { default: return 0; case ' ': type = patch_line_type_unchanged; break; case '!': type = patch_line_type_changed; break; case '+': type = patch_line_type_inserted; break; case '-': type = patch_line_type_deleted; break; } value = str_n_from_c(line->str_text + 2, line->str_length - 2); patch_line_list_append(pllp, type, value); str_free(value); return 1; } static patch_hunk_ty *context_diff_hunk _((patch_context_ty *)); static patch_hunk_ty * context_diff_hunk(context) patch_context_ty *context; { int before1, before2, before_list; int after1, after2, after_list; string_ty *line; int lino; patch_hunk_ty *php; int idx; int n; size_t j; patch_line_ty *plp; trace(("context_diff_hunk()\n{\n")); assert(star15); line = patch_context_getline(context, 0); if (!line || !str_equal(line, star15)) { trace(("no start 15 line\n")); trace(("return 0;\n}\n")); return 0; } idx = 1; php = patch_hunk_new(); /* * Look for a line of the form * *** N **** * or * *** N,N **** */ trace(("mark\n")); line = patch_context_getline(context, idx++); if (!line) { oops: patch_hunk_delete(php); trace(("return 0;\n}\n")); return 0; } trace(("line = \"%s\"\n", line->str_text)); if (!range(line, "****", &before1, &before2)) goto oops; php->before.start_line_number = before1; line = patch_context_getline(context, idx); if (!line) goto oops; trace(("line = \"%s\"\n", line->str_text)); before_list = (line->str_length >= 2 && line->str_text[1] == ' '); if (before_list) { trace(("mark\n")); n = before2 - before1 + 1; /* collect a bunch of lines */ for (lino = before1; lino <= before2; ++lino) { trace(("mark\n")); line = patch_context_getline(context, idx++); if (!line) goto oops; trace(("line = \"%s\"\n", line->str_text)); if (!line_append(&php->before, line)) goto oops; } trace(("mark\n")); } /* * Look for a line of the form * --- N ---- * or * --- N,N ---- */ trace(("mark\n")); line = patch_context_getline(context, idx++); if (!line) goto oops; trace(("line = \"%s\"\n", line->str_text)); if (!range(line, "----", &after1, &after2)) goto oops; trace(("mark\n")); php->after.start_line_number = after1; trace(("mark\n")); line = patch_context_getline(context, idx); if (!line) goto oops; trace(("line = \"%s\"\n", line->str_text)); after_list = (line->str_length >= 2 && line->str_text[1] == ' '); trace(("mark\n")); if (after_list) { trace(("mark\n")); n = after2 - after1 + 1; /* collect a bunch of lines */ for (lino = after1; lino <= after2; ++lino) { trace(("mark\n")); line = patch_context_getline(context, idx++); if (!line) goto oops; trace(("line = \"%s\"\n", line->str_text)); if (!line_append(&php->after, line)) goto oops; } trace(("mark\n")); } /* * 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); /* * Now we have all the lines, fill in the blanks. This happens * when there is only deletes or only inserts in a hunk. */ if (!before_list) { trace(("mark\n")); for (j = 0; j < php->after.length; ++j) { plp = &php->after.item[j]; if (plp->type == patch_line_type_unchanged) { trace(("mark\n")); patch_line_list_append ( &php->before, plp->type, plp->value ); } } trace(("mark\n")); } if (!after_list) { trace(("mark\n")); for (j = 0; j < php->before.length; ++j) { plp = &php->before.item[j]; if (plp->type == patch_line_type_unchanged) { patch_line_list_append ( &php->after, plp->type, plp->value ); } } } /* * In the limiting case, using the diff -C0 flag, inserts * and deletes are off by one. They mean "insert 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++; trace(("mark\n")); trace(("return %08lX\n", (long)php)); trace(("}\n")); return php; } patch_format_ty patch_format_context = { "context diff", context_diff_header, context_diff_hunk, };