#include "cgit.h" #include "ui-ssdiff.h" #include "html.h" #include "ui-shared.h" #include "ui-diff.h" extern int use_ssdiff; static int current_old_line, current_new_line; static int **L = NULL; struct deferred_lines { int line_no; char *line; struct deferred_lines *next; }; static struct deferred_lines *deferred_old, *deferred_old_last; static struct deferred_lines *deferred_new, *deferred_new_last; static void create_or_reset_lcs_table() { int i; if (L != NULL) { memset(*L, 0, sizeof(int) * MAX_SSDIFF_SIZE); return; } // xcalloc will die if we ran out of memory; // not very helpful for debugging L = (int**)xcalloc(MAX_SSDIFF_M, sizeof(int *)); *L = (int*)xcalloc(MAX_SSDIFF_SIZE, sizeof(int)); for (i = 1; i < MAX_SSDIFF_M; i++) { L[i] = *L + i * MAX_SSDIFF_N; } } static char *longest_common_subsequence(char *A, char *B) { int i, j, ri; int m = strlen(A); int n = strlen(B); int tmp1, tmp2; int lcs_length; char *result; // We bail if the lines are too long if (m >= MAX_SSDIFF_M || n >= MAX_SSDIFF_N) return NULL; create_or_reset_lcs_table(); for (i = m; i >= 0; i--) { for (j = n; j >= 0; j--) { if (A[i] == '\0' || B[j] == '\0') { L[i][j] = 0; } else if (A[i] == B[j]) { L[i][j] = 1 + L[i + 1][j + 1]; } else { tmp1 = L[i + 1][j]; tmp2 = L[i][j + 1]; L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2); } } } lcs_length = L[0][0]; result = xmalloc(lcs_length + 2); memset(result, 0, sizeof(*result) * (lcs_length + 2)); ri = 0; i = 0; j = 0; while (i < m && j < n) { if (A[i] == B[j]) { result[ri] = A[i]; ri += 1; i += 1; j += 1; } else if (L[i + 1][j] >= L[i][j + 1]) { i += 1; } else { j += 1; } } return result; } static int line_from_hunk(char *line, char type) { char *buf1, *buf2; int len; buf1 = strchr(line, type); if (buf1 == NULL) return 0; buf1 += 1; buf2 = strchr(buf1, ','); if (buf2 == NULL) return 0; len = buf2 - buf1; buf2 = xmalloc(len + 1); strncpy(buf2, buf1, len); buf2[len] = '\0'; int res = atoi(buf2); free(buf2); return res; } static char *replace_tabs(char *line) { char *prev_buf = line; char *cur_buf; int linelen = strlen(line); int n_tabs = 0; int i; char *result; char *spaces = " "; if (linelen == 0) { result = xmalloc(1); result[0] = '\0'; return result; } for (i = 0; i < linelen; i++) if (line[i] == '\t') n_tabs += 1; result = xmalloc(linelen + n_tabs * 8 + 1); result[0] = '\0'; while (1) { cur_buf = strchr(prev_buf, '\t'); if (!cur_buf) { strcat(result, prev_buf); break; } else { strncat(result, prev_buf, cur_buf - prev_buf); strncat(result, spaces, 8 - (strlen(result) % 8)); } prev_buf = cur_buf + 1; } return result; } static int calc_deferred_lines(struct deferred_lines *start) { struct deferred_lines *item = start; int result = 0; while (item) { result += 1; item = item->next; } return result; } static void deferred_old_add(char *line, int line_no) { struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); item->line = xstrdup(line); item->line_no = line_no; item->next = NULL; if (deferred_old) { deferred_old_last->next = item; deferred_old_last = item; } else { deferred_old = deferred_old_last = item; } } static void deferred_new_add(char *line, int line_no) { struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); item->line = xstrdup(line); item->line_no = line_no; item->next = NULL; if (deferred_new) { deferred_new_last->next = item; deferred_new_last = item; } else { deferred_new = deferred_new_last = item; } } static void print_part_with_lcs(char *class, char *line, char *lcs) { int line_len = strlen(line); int i, j; char c[2] = " "; int same = 1; j = 0; for (i = 0; i < line_len; i++) { c[0] = line[i]; if (same) { if (line[i] == lcs[j]) j += 1; else { same = 0; htmlf("<span class='%s'>", class); } } else if (line[i] == lcs[j]) { same = 1; htmlf("</span>"); j += 1; } html_txt(c); } } static void print_ssdiff_line(char *class, int old_line_no, char *old_line, int new_line_no, char *new_line, int individual_chars) { char *lcs = NULL; if (old_line) old_line = replace_tabs(old_line + 1); if (new_line) new_line = replace_tabs(new_line + 1); if (individual_chars && old_line && new_line) lcs = longest_common_subsequence(old_line, new_line); html("<tr>\n"); if (old_line_no > 0) { struct diff_filespec *old_file = cgit_get_current_old_file(); char *lineno_str = fmt("n%d", old_line_no); char *id_str = fmt("id=%s#%s", is_null_sha1(old_file->sha1)?"HEAD":sha1_to_hex(old_rev_sha1), lineno_str); html("<td class='lineno'><a class='no' href='"); html(cgit_fileurl(ctx.repo->url, "tree", old_file->path, id_str)); htmlf("' id='%s' name='%s'>%s</a>", lineno_str, lineno_str, lineno_str + 1); html("</td>"); htmlf("<td class='%s'>", class); } else if (old_line) htmlf("<td class='lineno'></td><td class='%s'>", class); else htmlf("<td class='lineno'></td><td class='%s_dark'>", class); if (old_line) { if (lcs) print_part_with_lcs("del", old_line, lcs); else html_txt(old_line); } html("</td>\n"); if (new_line_no > 0) { struct diff_filespec *new_file = cgit_get_current_new_file(); char *lineno_str = fmt("n%d", new_line_no); char *id_str = fmt("id=%s#%s", is_null_sha1(new_file->sha1)?"HEAD":sha1_to_hex(new_rev_sha1), lineno_str); html("<td class='lineno'><a class='no' href='"); html(cgit_fileurl(ctx.repo->url, "tree", new_file->path, id_str)); htmlf("' id='%s' name='%s'>%s</a>", lineno_str, line<style>pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .hig