all repos — cgit @ 4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9

a hyperfast web frontend for git written in c

ui-ssdiff.c (view raw)

  1#include "cgit.h"
  2#include "html.h"
  3#include "ui-shared.h"
  4
  5extern int use_ssdiff;
  6
  7static int current_old_line, current_new_line;
  8
  9struct deferred_lines {
 10	int line_no;
 11	char *line;
 12	struct deferred_lines *next;
 13};
 14
 15static struct deferred_lines *deferred_old, *deferred_old_last;
 16static struct deferred_lines *deferred_new, *deferred_new_last;
 17
 18static 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
 39static 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
 76static 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
 90static 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
104static 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
141static 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
155static 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
169static 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
201void 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 */
220void 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
256void 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
264void cgit_ssdiff_header_end()
265{
266	html("</td><tr>");
267}
268
269void 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}