Merge branch 'jc/maint-1.6.0-blank-at-eof' (early part) into jc/maint-blank-at-eof
authorJunio C Hamano <gitster@pobox.com>
Tue, 15 Sep 2009 10:38:30 +0000 (03:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 15 Sep 2009 10:38:30 +0000 (03:38 -0700)
* 'jc/maint-1.6.0-blank-at-eof' (early part):
diff.c: shuffling code around

1  2 
diff.c
diff --cc diff.c
index 87a3e8956be3592f42d2bb560dadf72f1fb72576,75489665bae378afd3b6cd81373273d12b23e4c4..e03b16c0066181e3a07faaed9db582bc447c4633
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -174,93 -241,191 +174,218 @@@ static struct diff_tempfile 
        char tmp_path[PATH_MAX];
  } diff_temp[2];
  
 -      struct xdiff_emit_state xm;
+ typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
+ struct emit_callback {
 -static void print_line_count(FILE *file, int count)
 -{
 -      switch (count) {
 -      case 0:
 -              fprintf(file, "0,0");
 -              break;
 -      case 1:
 -              fprintf(file, "1");
 -              break;
 -      default:
 -              fprintf(file, "1,%d", count);
 -              break;
 -      }
 -}
 -
 -static void copy_file_with_prefix(FILE *file,
 -                                int prefix, const char *data, int size,
 -                                const char *set, const char *reset)
 -{
 -      int ch, nl_just_seen = 1;
 -      while (0 < size--) {
 -              ch = *data++;
 -              if (nl_just_seen) {
 -                      fputs(set, file);
 -                      putc(prefix, file);
 -              }
 -              if (ch == '\n') {
 -                      nl_just_seen = 1;
 -                      fputs(reset, file);
 -              } else
 -                      nl_just_seen = 0;
 -              putc(ch, file);
 -      }
 -      if (!nl_just_seen)
 -              fprintf(file, "%s\n\\ No newline at end of file\n", reset);
 -}
 -
+       int color_diff;
+       unsigned ws_rule;
+       int blank_at_eof_in_preimage;
+       int blank_at_eof_in_postimage;
+       int lno_in_preimage;
+       int lno_in_postimage;
+       sane_truncate_fn truncate;
+       const char **label_path;
+       struct diff_words_data *diff_words;
+       int *found_changesp;
+       FILE *file;
+ };
+ static int count_lines(const char *data, int size)
+ {
+       int count, ch, completely_empty = 1, nl_just_seen = 0;
+       count = 0;
+       while (0 < size--) {
+               ch = *data++;
+               if (ch == '\n') {
+                       count++;
+                       nl_just_seen = 1;
+                       completely_empty = 0;
+               }
+               else {
+                       nl_just_seen = 0;
+                       completely_empty = 0;
+               }
+       }
+       if (completely_empty)
+               return 0;
+       if (!nl_just_seen)
+               count++; /* no trailing newline */
+       return count;
+ }
- static int count_lines(const char *data, int size)
- {
-       int count, ch, completely_empty = 1, nl_just_seen = 0;
-       count = 0;
-       while (0 < size--) {
-               ch = *data++;
-               if (ch == '\n') {
-                       count++;
-                       nl_just_seen = 1;
-                       completely_empty = 0;
-               }
-               else {
-                       nl_just_seen = 0;
-                       completely_empty = 0;
-               }
-       }
-       if (completely_empty)
-               return 0;
-       if (!nl_just_seen)
-               count++; /* no trailing newline */
-       return count;
- }
+ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+ {
+       if (!DIFF_FILE_VALID(one)) {
+               mf->ptr = (char *)""; /* does not matter */
+               mf->size = 0;
+               return 0;
+       }
+       else if (diff_populate_filespec(one, 0))
+               return -1;
++
+       mf->ptr = one->data;
+       mf->size = one->size;
+       return 0;
+ }
+ static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
+ {
+       char *ptr = mf->ptr;
+       long size = mf->size;
+       int cnt = 0;
+       if (!size)
+               return cnt;
+       ptr += size - 1; /* pointing at the very end */
+       if (*ptr != '\n')
+               ; /* incomplete line */
+       else
+               ptr--; /* skip the last LF */
+       while (mf->ptr < ptr) {
+               char *prev_eol;
+               for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
+                       if (*prev_eol == '\n')
+                               break;
+               if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
+                       break;
+               cnt++;
+               ptr = prev_eol - 1;
+       }
+       return cnt;
+ }
+ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
+                              struct emit_callback *ecbdata)
+ {
+       int l1, l2, at;
+       unsigned ws_rule = ecbdata->ws_rule;
+       l1 = count_trailing_blank(mf1, ws_rule);
+       l2 = count_trailing_blank(mf2, ws_rule);
+       if (l2 <= l1) {
+               ecbdata->blank_at_eof_in_preimage = 0;
+               ecbdata->blank_at_eof_in_postimage = 0;
+               return;
+       }
+       at = count_lines(mf1->ptr, mf1->size);
+       ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;
+       at = count_lines(mf2->ptr, mf2->size);
+       ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
+ }
+ static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
+ {
+       int has_trailing_newline, has_trailing_carriage_return;
+       has_trailing_newline = (len > 0 && line[len-1] == '\n');
+       if (has_trailing_newline)
+               len--;
+       has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
+       if (has_trailing_carriage_return)
+               len--;
+       fputs(set, file);
+       fwrite(line, len, 1, file);
+       fputs(reset, file);
+       if (has_trailing_carriage_return)
+               fputc('\r', file);
+       if (has_trailing_newline)
+               fputc('\n', file);
+ }
+ static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
+ {
+       if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
+             ecbdata->blank_at_eof_in_preimage &&
+             ecbdata->blank_at_eof_in_postimage &&
+             ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
+             ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
+               return 0;
+       return ws_blank_line(line + 1, len - 1, ecbdata->ws_rule);
+ }
+ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
+ {
+       const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+       const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
+       if (!*ws)
+               emit_line(ecbdata->file, set, reset, line, len);
+       else if (new_blank_line_at_eof(ecbdata, line, len))
+               /* Blank line at EOF - paint '+' as well */
+               emit_line(ecbdata->file, ws, reset, line, len);
+       else {
+               /* Emit just the prefix, then the rest. */
+               emit_line(ecbdata->file, set, reset, line, 1);
+               ws_check_emit(line + 1, len - 1, ecbdata->ws_rule,
+                             ecbdata->file, set, reset, ws);
+       }
+ }
 +static struct diff_tempfile *claim_diff_tempfile(void) {
 +      int i;
 +      for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
 +              if (!diff_temp[i].name)
 +                      return diff_temp + i;
 +      die("BUG: diff is failing to clean up its tempfiles");
 +}
 +
 +static int remove_tempfile_installed;
 +
 +static void remove_tempfile(void)
 +{
 +      int i;
 +      for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
 +              if (diff_temp[i].name == diff_temp[i].tmp_path)
 +                      unlink_or_warn(diff_temp[i].name);
 +              diff_temp[i].name = NULL;
 +      }
 +}
 +
 +static void remove_tempfile_on_signal(int signo)
 +{
 +      remove_tempfile();
 +      sigchain_pop(signo);
 +      raise(signo);
 +}
 +
 +static void print_line_count(FILE *file, int count)
 +{
 +      switch (count) {
 +      case 0:
 +              fprintf(file, "0,0");
 +              break;
 +      case 1:
 +              fprintf(file, "1");
 +              break;
 +      default:
 +              fprintf(file, "1,%d", count);
 +              break;
 +      }
 +}
 +
 +static void copy_file_with_prefix(FILE *file,
 +                                int prefix, const char *data, int size,
 +                                const char *set, const char *reset)
 +{
 +      int ch, nl_just_seen = 1;
 +      while (0 < size--) {
 +              ch = *data++;
 +              if (nl_just_seen) {
 +                      fputs(set, file);
 +                      putc(prefix, file);
 +              }
 +              if (ch == '\n') {
 +                      nl_just_seen = 1;
 +                      fputs(reset, file);
 +              } else
 +                      nl_just_seen = 0;
 +              putc(ch, file);
 +      }
 +      if (!nl_just_seen)
 +              fprintf(file, "%s\n\\ No newline at end of file\n", reset);
 +}
 +
  static void emit_rewrite_diff(const char *name_a,
                              const char *name_b,
                              struct diff_filespec *one,
        print_line_count(o->file, lc_b);
        fprintf(o->file, " @@%s\n", reset);
        if (lc_a)
 -              copy_file_with_prefix(o->file, '-', one->data, one->size, old, reset);
 +              copy_file_with_prefix(o->file, '-', data_one, size_one, old, reset);
        if (lc_b)
 -              copy_file_with_prefix(o->file, '+', two->data, two->size, new, reset);
 +              copy_file_with_prefix(o->file, '+', data_two, size_two, new, reset);
  }
  
- static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
- {
-       if (!DIFF_FILE_VALID(one)) {
-               mf->ptr = (char *)""; /* does not matter */
-               mf->size = 0;
-               return 0;
-       }
-       else if (diff_populate_filespec(one, 0))
-               return -1;
-       mf->ptr = one->data;
-       mf->size = one->size;
-       return 0;
- }
  struct diff_words_buffer {
        mmfile_t text;
        long alloc;
@@@ -497,54 -560,42 +607,38 @@@ static void diff_words_show(struct diff
        xdemitconf_t xecfg;
        xdemitcb_t ecb;
        mmfile_t minus, plus;
 -      int i;
  
 -      memset(&xecfg, 0, sizeof(xecfg));
 -      minus.size = diff_words->minus.text.size;
 -      minus.ptr = xmalloc(minus.size);
 -      memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
 -      for (i = 0; i < minus.size; i++)
 -              if (isspace(minus.ptr[i]))
 -                      minus.ptr[i] = '\n';
 -      diff_words->minus.current = 0;
 -
 -      plus.size = diff_words->plus.text.size;
 -      plus.ptr = xmalloc(plus.size);
 -      memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
 -      for (i = 0; i < plus.size; i++)
 -              if (isspace(plus.ptr[i]))
 -                      plus.ptr[i] = '\n';
 -      diff_words->plus.current = 0;
 +      /* special case: only removal */
 +      if (!diff_words->plus.text.size) {
 +              color_fwrite_lines(diff_words->file,
 +                      diff_get_color(1, DIFF_FILE_OLD),
 +                      diff_words->minus.text.size, diff_words->minus.text.ptr);
 +              diff_words->minus.text.size = 0;
 +              return;
 +      }
  
 -      xpp.flags = XDF_NEED_MINIMAL;
 -      xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
 -      ecb.outf = xdiff_outf;
 -      ecb.priv = diff_words;
 -      diff_words->xm.consume = fn_out_diff_words_aux;
 -      xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
 +      diff_words->current_plus = diff_words->plus.text.ptr;
  
 +      memset(&xpp, 0, sizeof(xpp));
 +      memset(&xecfg, 0, sizeof(xecfg));
 +      diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
 +      diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
 +      xpp.flags = XDF_NEED_MINIMAL;
 +      /* as only the hunk header will be parsed, we need a 0-context */
 +      xecfg.ctxlen = 0;
 +      xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
 +                    &xpp, &xecfg, &ecb);
        free(minus.ptr);
        free(plus.ptr);
 +      if (diff_words->current_plus != diff_words->plus.text.ptr +
 +                      diff_words->plus.text.size)
 +              fwrite(diff_words->current_plus,
 +                      diff_words->plus.text.ptr + diff_words->plus.text.size
 +                      - diff_words->current_plus, 1,
 +                      diff_words->file);
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
 -
 -      if (diff_words->minus.suppressed_newline) {
 -              putc('\n', diff_words->file);
 -              diff_words->minus.suppressed_newline = 0;
 -      }
  }
  
- typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
- struct emit_callback {
-       int color_diff;
-       unsigned ws_rule;
-       int blank_at_eof_in_preimage;
-       int blank_at_eof_in_postimage;
-       int lno_in_preimage;
-       int lno_in_postimage;
-       sane_truncate_fn truncate;
-       const char **label_path;
-       struct diff_words_data *diff_words;
-       int *found_changesp;
-       FILE *file;
- };
  static void free_diff_words_data(struct emit_callback *ecbdata)
  {
        if (ecbdata->diff_words) {
@@@ -1393,108 -1390,125 +1438,63 @@@ static void emit_binary_diff(FILE *file
        emit_binary_diff_body(file, two, one);
  }
  
 -static void setup_diff_attr_check(struct git_attr_check *check)
 +static void diff_filespec_load_driver(struct diff_filespec *one)
  {
 -      static struct git_attr *attr_diff;
 -
 -      if (!attr_diff) {
 -              attr_diff = git_attr("diff", 4);
 -      }
 -      check[0].attr = attr_diff;
 +      if (!one->driver)
 +              one->driver = userdiff_find_by_path(one->path);
 +      if (!one->driver)
 +              one->driver = userdiff_find_by_name("default");
  }
  
 -static void diff_filespec_check_attr(struct diff_filespec *one)
 +int diff_filespec_is_binary(struct diff_filespec *one)
  {
 -      struct git_attr_check attr_diff_check;
 -      int check_from_data = 0;
 -
 -      if (one->checked_attr)
 -              return;
 -
 -      setup_diff_attr_check(&attr_diff_check);
 -      one->is_binary = 0;
 -      one->funcname_pattern_ident = NULL;
 -
 -      if (!git_checkattr(one->path, 1, &attr_diff_check)) {
 -              const char *value;
 -
 -              /* binaryness */
 -              value = attr_diff_check.value;
 -              if (ATTR_TRUE(value))
 -                      ;
 -              else if (ATTR_FALSE(value))
 -                      one->is_binary = 1;
 -              else
 -                      check_from_data = 1;
 -
 -              /* funcname pattern ident */
 -              if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
 -                      ;
 -              else
 -                      one->funcname_pattern_ident = value;
 -      }
 -
 -      if (check_from_data) {
 -              if (!one->data && DIFF_FILE_VALID(one))
 -                      diff_populate_filespec(one, 0);
 -
 -              if (one->data)
 -                      one->is_binary = buffer_is_binary(one->data, one->size);
 +      if (one->is_binary == -1) {
 +              diff_filespec_load_driver(one);
 +              if (one->driver->binary != -1)
 +                      one->is_binary = one->driver->binary;
 +              else {
 +                      if (!one->data && DIFF_FILE_VALID(one))
 +                              diff_populate_filespec(one, 0);
 +                      if (one->data)
 +                              one->is_binary = buffer_is_binary(one->data,
 +                                              one->size);
 +                      if (one->is_binary == -1)
 +                              one->is_binary = 0;
 +              }
        }
 +      return one->is_binary;
  }
  
 -int diff_filespec_is_binary(struct diff_filespec *one)
 +static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
  {
 -      diff_filespec_check_attr(one);
 -      return one->is_binary;
 +      diff_filespec_load_driver(one);
 +      return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
  }
  
 -static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
 -{
 -      struct funcname_pattern_list *pp;
 -
 -      for (pp = funcname_pattern_list; pp; pp = pp->next)
 -              if (!strcmp(ident, pp->e.name))
 -                      return &pp->e;
 -      return NULL;
 -}
 -
 -static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
 -      { "java",
 -        "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
 -        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
 -        REG_EXTENDED },
 -      { "pascal",
 -        "^((procedure|function|constructor|destructor|interface|"
 -              "implementation|initialization|finalization)[ \t]*.*)$"
 -        "|"
 -        "^(.*=[ \t]*(class|record).*)$",
 -        REG_EXTENDED },
 -      { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
 -        REG_EXTENDED },
 -      { "tex",
 -        "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
 -        REG_EXTENDED },
 -      { "ruby", "^[ \t]*((class|module|def)[ \t].*)$",
 -        REG_EXTENDED },
 -};
 -
 -static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
 +static const char *userdiff_word_regex(struct diff_filespec *one)
  {
 -      const char *ident;
 -      const struct funcname_pattern_entry *pe;
 -      int i;
 -
 -      diff_filespec_check_attr(one);
 -      ident = one->funcname_pattern_ident;
 -
 -      if (!ident)
 -              /*
 -               * If the config file has "funcname.default" defined, that
 -               * regexp is used; otherwise NULL is returned and xemit uses
 -               * the built-in default.
 -               */
 -              return funcname_pattern("default");
 -
 -      /* Look up custom "funcname.$ident" regexp from config. */
 -      pe = funcname_pattern(ident);
 -      if (pe)
 -              return pe;
 +      diff_filespec_load_driver(one);
 +      return one->driver->word_regex;
 +}
  
 -      /*
 -       * And define built-in fallback patterns here.  Note that
 -       * these can be overridden by the user's config settings.
 -       */
 -      for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
 -              if (!strcmp(ident, builtin_funcname_pattern[i].name))
 -                      return &builtin_funcname_pattern[i];
 +void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
 +{
 +      if (!options->a_prefix)
 +              options->a_prefix = a;
 +      if (!options->b_prefix)
 +              options->b_prefix = b;
 +}
  
 -      return NULL;
 +static const char *get_textconv(struct diff_filespec *one)
 +{
 +      if (!DIFF_FILE_VALID(one))
 +              return NULL;
 +      if (!S_ISREG(one->mode))
 +              return NULL;
 +      diff_filespec_load_driver(one);
 +      return one->driver->textconv;
  }
  
- static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
- {
-       char *ptr = mf->ptr;
-       long size = mf->size;
-       int cnt = 0;
-       if (!size)
-               return cnt;
-       ptr += size - 1; /* pointing at the very end */
-       if (*ptr != '\n')
-               ; /* incomplete line */
-       else
-               ptr--; /* skip the last LF */
-       while (mf->ptr < ptr) {
-               char *prev_eol;
-               for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
-                       if (*prev_eol == '\n')
-                               break;
-               if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
-                       break;
-               cnt++;
-               ptr = prev_eol - 1;
-       }
-       return cnt;
- }
- static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
-                              struct emit_callback *ecbdata)
- {
-       int l1, l2, at;
-       unsigned ws_rule = ecbdata->ws_rule;
-       l1 = count_trailing_blank(mf1, ws_rule);
-       l2 = count_trailing_blank(mf2, ws_rule);
-       if (l2 <= l1) {
-               ecbdata->blank_at_eof_in_preimage = 0;
-               ecbdata->blank_at_eof_in_postimage = 0;
-               return;
-       }
-       at = count_lines(mf1->ptr, mf1->size);
-       ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;
-       at = count_lines(mf2->ptr, mf2->size);
-       ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
- }
  static void builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_filespec *one,