7 static int current_old_line, current_new_line;
9 struct deferred_lines {
12 struct deferred_lines *next;
15 static struct deferred_lines *deferred_old, *deferred_old_last;
16 static struct deferred_lines *deferred_new, *deferred_new_last;
18 static char *longest_common_subsequence(char *A, char *B)
28 for (i = m; i >= 0; i--) {
29 for (j = n; j >= 0; j--) {
30 if (A[i] == '\0' || B[j] == '\0') {
32 } else if (A[i] == B[j]) {
33 L[i][j] = 1 + L[i + 1][j + 1];
37 L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2);
43 result = xmalloc(lcs_length + 2);
44 memset(result, 0, sizeof(*result) * (lcs_length + 2));
49 while (i < m && j < n) {
55 } else if (L[i + 1][j] >= L[i][j + 1]) {
64 static int line_from_hunk(char *line, char type)
69 buf1 = strchr(line, type);
73 buf2 = strchr(buf1, ',');
77 buf2 = xmalloc(len + 1);
78 strncpy(buf2, buf1, len);
85 static char *replace_tabs(char *line)
87 char *prev_buf = line;
89 int linelen = strlen(line);
101 for (i = 0; i < linelen; i++)
104 result = xmalloc(linelen + n_tabs * 8 + 1);
108 cur_buf = strchr(prev_buf, '\t');
110 strcat(result, prev_buf);
114 strncat(result, spaces, 8 - (strlen(result) % 8));
115 strncat(result, prev_buf, cur_buf - prev_buf);
117 prev_buf = cur_buf + 1;
122 static int calc_deferred_lines(struct deferred_lines *start)
124 struct deferred_lines *item = start;
133 static void deferred_old_add(char *line, int line_no)
135 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
136 item->line = xstrdup(line);
137 item->line_no = line_no;
140 deferred_old_last->next = item;
141 deferred_old_last = item;
143 deferred_old = deferred_old_last = item;
147 static void deferred_new_add(char *line, int line_no)
149 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
150 item->line = xstrdup(line);
151 item->line_no = line_no;
154 deferred_new_last->next = item;
155 deferred_new_last = item;
157 deferred_new = deferred_new_last = item;
161 static void print_part_with_lcs(char *class, char *line, char *lcs)
163 int line_len = strlen(line);
169 for (i = 0; i < line_len; i++) {
172 if (line[i] == lcs[j])
176 htmlf("<span class='%s'>", class);
178 } else if (line[i] == lcs[j]) {
187 static void print_ssdiff_line(char *class,
191 char *new_line, int individual_chars)
195 old_line = replace_tabs(old_line + 1);
197 new_line = replace_tabs(new_line + 1);
198 if (individual_chars && old_line && new_line)
199 lcs = longest_common_subsequence(old_line, new_line);
202 htmlf("<td class='lineno'>%d</td><td class='%s'>",
205 htmlf("<td class='lineno'></td><td class='%s'>", class);
207 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
210 print_part_with_lcs("del", old_line, lcs);
217 htmlf("<td class='lineno'>%d</td><td class='%s'>",
220 htmlf("<td class='lineno'></td><td class='%s'>", class);
222 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
225 print_part_with_lcs("add", new_line, lcs);
239 static void print_deferred_old_lines()
241 struct deferred_lines *iter_old, *tmp;
242 iter_old = deferred_old;
244 print_ssdiff_line("del", iter_old->line_no,
245 iter_old->line, -1, NULL, 0);
246 tmp = iter_old->next;
252 static void print_deferred_new_lines()
254 struct deferred_lines *iter_new, *tmp;
255 iter_new = deferred_new;
257 print_ssdiff_line("add", -1, NULL,
258 iter_new->line_no, iter_new->line, 0);
259 tmp = iter_new->next;
265 static void print_deferred_changed_lines()
267 struct deferred_lines *iter_old, *iter_new, *tmp;
268 int n_old_lines = calc_deferred_lines(deferred_old);
269 int n_new_lines = calc_deferred_lines(deferred_new);
270 int individual_chars = (n_old_lines == n_new_lines ? 1 : 0);
272 iter_old = deferred_old;
273 iter_new = deferred_new;
274 while (iter_old || iter_new) {
275 if (iter_old && iter_new)
276 print_ssdiff_line("changed", iter_old->line_no,
278 iter_new->line_no, iter_new->line,
281 print_ssdiff_line("changed", iter_old->line_no,
282 iter_old->line, -1, NULL, 0);
284 print_ssdiff_line("changed", -1, NULL,
285 iter_new->line_no, iter_new->line, 0);
287 tmp = iter_old->next;
293 tmp = iter_new->next;
300 void cgit_ssdiff_print_deferred_lines()
302 if (!deferred_old && !deferred_new)
304 if (deferred_old && !deferred_new)
305 print_deferred_old_lines();
306 else if (!deferred_old && deferred_new)
307 print_deferred_new_lines();
309 print_deferred_changed_lines();
310 deferred_old = deferred_old_last = NULL;
311 deferred_new = deferred_new_last = NULL;
315 * print a single line returned from xdiff
317 void cgit_ssdiff_line_cb(char *line, int len)
319 char c = line[len - 1];
320 line[len - 1] = '\0';
321 if (line[0] == '@') {
322 current_old_line = line_from_hunk(line, '-');
323 current_new_line = line_from_hunk(line, '+');
326 if (line[0] == ' ') {
327 if (deferred_old || deferred_new)
328 cgit_ssdiff_print_deferred_lines();
329 print_ssdiff_line("ctx", current_old_line, line,
330 current_new_line, line, 0);
331 current_old_line += 1;
332 current_new_line += 1;
333 } else if (line[0] == '+') {
334 deferred_new_add(line, current_new_line);
335 current_new_line += 1;
336 } else if (line[0] == '-') {
337 deferred_old_add(line, current_old_line);
338 current_old_line += 1;
339 } else if (line[0] == '@') {
340 html("<tr><td colspan='4' class='hunk'>");
344 html("<tr><td colspan='4' class='ctx'>");
351 void cgit_ssdiff_header_begin()
353 current_old_line = -1;
354 current_new_line = -1;
355 html("<tr><td class='space' colspan='4'><div></div></td></tr>");
356 html("<tr><td class='head' colspan='4'>");
359 void cgit_ssdiff_header_end()
364 void cgit_ssdiff_footer()
366 if (deferred_old || deferred_new)
367 cgit_ssdiff_print_deferred_lines();
368 html("<tr><td class='foot' colspan='4'></td></tr>");