]> gitweb.ps.run Git - ps-cgit/blob - ui-ssdiff.c
Fixed side-by-side diff bugs related to binary diff and more.
[ps-cgit] / ui-ssdiff.c
1 #include "cgit.h"
2 #include "html.h"
3 #include "ui-shared.h"
4
5 extern int use_ssdiff;
6
7 static int current_old_line, current_new_line;
8
9 struct deferred_lines {
10         int line_no;
11         char *line;
12         struct deferred_lines *next;
13 };
14
15 static struct deferred_lines *deferred_old, *deferred_old_last;
16 static struct deferred_lines *deferred_new, *deferred_new_last;
17
18 static int line_from_hunk(char *line, char type)
19 {
20         char *buf1, *buf2;
21         int len;
22
23         buf1 = strchr(line, type);
24         if (buf1 == NULL)
25                 return 0;
26         buf1 += 1;
27         buf2 = strchr(buf1, ',');
28         if (buf2 == NULL)
29                 return 0;
30         len = buf2 - buf1;
31         buf2 = xmalloc(len + 1);
32         strncpy(buf2, buf1, len);
33         buf2[len] = '\0';
34         int res = atoi(buf2);
35         free(buf2);
36         return res;
37 }
38
39 static char *replace_tabs(char *line)
40 {
41         char *prev_buf = line;
42         char *cur_buf;
43         int linelen = strlen(line);
44         int n_tabs = 0;
45         int i;
46         char *result;
47         char *spaces = "        ";
48
49         if (linelen == 0) {
50                 result = xmalloc(1);
51                 result[0] = '\0';
52                 return result;
53         }
54
55         for (i = 0; i < linelen; i++)
56                 if (line[i] == '\t')
57                         n_tabs += 1;
58         result = xmalloc(linelen + n_tabs * 8 + 1);
59         result[0] = '\0';
60
61         while (1) {
62                 cur_buf = strchr(prev_buf, '\t');
63                 if (!cur_buf) {
64                         strcat(result, prev_buf);
65                         break;
66                 } else {
67                         strcat(result, " ");
68                         strncat(result, spaces, 8 - (strlen(result) % 8));
69                         strncat(result, prev_buf, cur_buf - prev_buf);
70                 }
71                 prev_buf = cur_buf + 1;
72         }
73         return result;
74 }
75
76 static void deferred_old_add(char *line, int line_no)
77 {
78         struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
79         item->line = xstrdup(line);
80         item->line_no = line_no;
81         item->next = NULL;
82         if (deferred_old) {
83                 deferred_old_last->next = item;
84                 deferred_old_last = item;
85         } else {
86                 deferred_old = deferred_old_last = item;
87         }
88 }
89
90 static void deferred_new_add(char *line, int line_no)
91 {
92         struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
93         item->line = xstrdup(line);
94         item->line_no = line_no;
95         item->next = NULL;
96         if (deferred_new) {
97                 deferred_new_last->next = item;
98                 deferred_new_last = item;
99         } else {
100                 deferred_new = deferred_new_last = item;
101         }
102 }
103
104 static void print_ssdiff_line(char *class, int old_line_no, char *old_line,
105                               int new_line_no, char *new_line)
106 {
107         html("<tr>");
108         if (old_line_no > 0)
109                 htmlf("<td class='lineno'>%d</td><td class='%s'>",
110                       old_line_no, class);
111         else if (old_line)
112                 htmlf("<td class='lineno'></td><td class='%s'>", class);
113         else
114                 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
115
116         if (old_line) {
117                 old_line = replace_tabs(old_line + 1);
118                 html_txt(old_line);
119                 free(old_line);
120         }
121
122         html("</td>");
123
124         if (new_line_no > 0)
125                 htmlf("<td class='lineno'>%d</td><td class='%s'>",
126                       new_line_no, class);
127         else if (new_line)
128                 htmlf("<td class='lineno'></td><td class='%s'>", class);
129         else
130                 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
131
132         if (new_line) {
133                 new_line = replace_tabs(new_line + 1);
134                 html_txt(new_line);
135                 free(new_line);
136         }
137
138         html("</td></tr>");
139 }
140
141 static void print_deferred_old_lines()
142 {
143         struct deferred_lines *iter_old, *tmp;
144
145         iter_old = deferred_old;
146         while (iter_old) {
147                 print_ssdiff_line("del", iter_old->line_no,
148                                   iter_old->line, -1, NULL);
149                 tmp = iter_old->next;
150                 free(iter_old);
151                 iter_old = tmp;
152         }
153 }
154
155 static void print_deferred_new_lines()
156 {
157         struct deferred_lines *iter_new, *tmp;
158
159         iter_new = deferred_new;
160         while (iter_new) {
161                 print_ssdiff_line("add", -1, NULL, iter_new->line_no,
162                                   iter_new->line);
163                 tmp = iter_new->next;
164                 free(iter_new);
165                 iter_new = tmp;
166         }
167 }
168
169 static void print_deferred_changed_lines()
170 {
171         struct deferred_lines *iter_old, *iter_new, *tmp;
172
173         iter_old = deferred_old;
174         iter_new = deferred_new;
175         while (iter_old || iter_new) {
176                 if (iter_old && iter_new)
177                         print_ssdiff_line("changed", iter_old->line_no,
178                                           iter_old->line,
179                                           iter_new->line_no, iter_new->line);
180                 else if (iter_old)
181                         print_ssdiff_line("changed", iter_old->line_no,
182                                           iter_old->line, -1, NULL);
183                 else if (iter_new)
184                         print_ssdiff_line("changed", -1, NULL,
185                                           iter_new->line_no, iter_new->line);
186
187                 if (iter_old) {
188                         tmp = iter_old->next;
189                         free(iter_old);
190                         iter_old = tmp;
191                 }
192
193                 if (iter_new) {
194                         tmp = iter_new->next;
195                         free(iter_new);
196                         iter_new = tmp;
197                 }
198         }
199 }
200
201 void cgit_ssdiff_print_deferred_lines()
202 {
203         if (!deferred_old && !deferred_new)
204                 return;
205
206         if (deferred_old && !deferred_new)
207                 print_deferred_old_lines();
208         else if (!deferred_old && deferred_new)
209                 print_deferred_new_lines();
210         else
211                 print_deferred_changed_lines();
212
213         deferred_old = deferred_old_last = NULL;
214         deferred_new = deferred_new_last = NULL;
215 }
216
217 /*
218  * print a single line returned from xdiff
219  */
220 void cgit_ssdiff_line_cb(char *line, int len)
221 {
222         char c = line[len - 1];
223
224         line[len - 1] = '\0';
225
226         if (line[0] == '@') {
227                 current_old_line = line_from_hunk(line, '-');
228                 current_new_line = line_from_hunk(line, '+');
229         }
230
231         if (line[0] == ' ') {
232                 if (deferred_old || deferred_new)
233                         cgit_ssdiff_print_deferred_lines();
234                 print_ssdiff_line("ctx", current_old_line, line,
235                                   current_new_line, line);
236                 current_old_line += 1;
237                 current_new_line += 1;
238         } else if (line[0] == '+') {
239                 deferred_new_add(line, current_new_line);
240                 current_new_line += 1;
241         } else if (line[0] == '-') {
242                 deferred_old_add(line, current_old_line);
243                 current_old_line += 1;
244         } else if (line[0] == '@') {
245                 html("<tr><td colspan='4' class='hunk'>");
246                 html_txt(line);
247                 html("</td></tr>");
248         } else {
249                 html("<tr><td colspan='4' class='ctx'>");
250                 html_txt(line);
251                 html("</td></tr>");
252         }
253         line[len - 1] = c;
254 }
255
256 void cgit_ssdiff_header_begin()
257 {
258         current_old_line = -1;
259         current_new_line = -1;
260         html("<tr><td class='space' colspan='4'><div></div></td></tr>");
261         html("<tr><td class='head' colspan='4'>");
262 }
263
264 void cgit_ssdiff_header_end()
265 {
266         html("</td><tr>");
267 }
268
269 void cgit_ssdiff_footer()
270 {
271         if (deferred_old || deferred_new)
272                 cgit_ssdiff_print_deferred_lines();
273         html("<tr><td class='foot' colspan='4'></td></tr>");
274 }