Logo Search packages:      
Sourcecode: aegis version File versions

context.c

/*
 *    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 <ac/string.h>

#include <error.h>
#include <patch.h>
#include <patch/context.h>
#include <patch/file.h>
#include <patch/format/context.h>
#include <trace.h>

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;
    int               before2;
    int               before_list;
    int               after1;
    int               after2;
    int               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)
    {
      after_list = 0;
    }
    else
    {
      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,
};

Generated by  Doxygen 1.6.0   Back to index