1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2019 Tobias Kortkamp <tobik@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "config.h"
30
31 #include <sys/param.h>
32 #include <stdbool.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "array.h"
37 #include "color.h"
38 #include "diff.h"
39 #include "diffutil.h"
40 #include "flow.h"
41 #include "mempool.h"
42 #include "str.h"
43
44 struct Hunk {
45 size_t start;
46 size_t end;
47 };
48
49 // Prototypes
50 DECLARE_COMPARE(by_start);
51 static struct Array *get_hunks(struct Mempool *, struct diff *, size_t);
52
DEFINE_COMPARE(by_start,struct Hunk,void)53 DEFINE_COMPARE(by_start, struct Hunk, void)
54 {
55 if (a->start < b->start) {
56 return -1;
57 } else if (a->start > b->start) {
58 return 1;
59 } else {
60 return 0;
61 }
62 }
63
64 struct Array *
get_hunks(struct Mempool * pool,struct diff * p,size_t context)65 get_hunks(struct Mempool *pool, struct diff *p, size_t context)
66 {
67 panic_unless(p->sessz > 0, "shortest common subsequence length is 0");
68 if (context == 0) {
69 struct Array *hunks = mempool_array(pool);
70 struct Hunk *h = mempool_alloc(pool, sizeof(struct Hunk));
71 h->start = 0;
72 h->end = p->sessz - 1;
73 array_append(hunks, h);
74 return hunks;
75 }
76
77 struct Array *edit_ranges = mempool_array(pool);
78 struct Hunk *h = mempool_alloc(pool, sizeof(struct Hunk));
79 bool first = true;
80 bool last_common = true;
81 for (size_t i = 0; i < p->sessz; i++) {
82 switch (p->ses[i].type) {
83 case DIFF_COMMON:
84 if (!last_common) {
85 array_append(edit_ranges, h);
86 h = mempool_alloc(pool, sizeof(struct Hunk));
87 }
88 last_common = true;
89 first = true;
90 break;
91 case DIFF_ADD:
92 case DIFF_DELETE:
93 last_common = false;
94 if (first) {
95 h->start = i;
96 first = false;
97 }
98 h->end = i;
99 break;
100 }
101 }
102 if (!first) {
103 array_append(edit_ranges, h);
104 }
105
106 ARRAY_FOREACH(edit_ranges, struct Hunk *, h) {
107 if (h->start > context) {
108 h->start -= context;
109 }
110 h->end = MIN(h->end + context, p->sessz - 1);
111 }
112 array_sort(edit_ranges, by_start, NULL);
113
114 struct Array *hunks = mempool_array(pool);
115 struct Hunk *last = NULL;
116 ARRAY_FOREACH(edit_ranges, struct Hunk *, h) {
117 unless (last) {
118 last = h;
119 continue;
120 } else {
121 if (h->start <= last->end) {
122 if (h->end > last->end) {
123 last->end = h->end;
124 }
125 } else if (h->start > last->end) {
126 array_append(hunks, last);
127 last = h;
128 }
129 }
130 }
131 if (last) {
132 array_append(hunks, last);
133 }
134
135 panic_unless(array_len(hunks) > 0, "no hunks found");
136 return hunks;
137 }
138
139
140 char *
diff_to_patch(struct diff * p,struct Mempool * extpool,TostringFn tostring,void * tostring_userdata,size_t context,bool color)141 diff_to_patch(struct diff *p, struct Mempool *extpool, TostringFn tostring, void *tostring_userdata, size_t context, bool color)
142 {
143 if (p->sessz == 0) {
144 return NULL;
145 }
146
147 SCOPE_MEMPOOL(pool);
148 const char *color_add = ANSI_COLOR_GREEN;
149 const char *color_context = ANSI_COLOR_CYAN;
150 const char *color_delete = ANSI_COLOR_RED;
151 const char *color_reset = ANSI_COLOR_RESET;
152 if (!color) {
153 color_add = color_context = color_delete = color_reset = "";
154 }
155
156 struct Array *hunks = get_hunks(pool, p, context);
157 struct Array *result = mempool_array(pool);
158 ARRAY_FOREACH(hunks, struct Hunk *, h) {
159 size_t origin_len = 0;
160 size_t target_len = 0;
161 for (size_t i = h->start; i <= h->end; i++) {
162 switch (p->ses[i].type) {
163 case DIFF_ADD:
164 target_len++;
165 break;
166 case DIFF_COMMON:
167 origin_len++;
168 target_len++;
169 break;
170 case DIFF_DELETE:
171 origin_len++;
172 break;
173 }
174 }
175 size_t origin_start = p->ses[h->start].originIdx;
176 if (origin_start == 0) {
177 origin_start = 1;
178 }
179 size_t target_start = p->ses[h->start].targetIdx;
180 char *buf;
181 if (origin_len > 1) {
182 buf = str_printf(pool, "%s@@ -%zu,%zu", color_context, origin_start, origin_len);
183 } else {
184 buf = str_printf(pool, "%s@@ -%zu", color_context, origin_start);
185 }
186 array_append(result, buf);
187 if (target_len > 1) {
188 buf = str_printf(pool, " +%zu,%zu @@%s\n", target_start, target_len, color_reset);
189 } else {
190 buf = str_printf(pool, " +%zu @@%s\n", target_start, color_reset);
191 }
192 array_append(result, buf);
193 for (size_t i = h->start; i <= h->end; i++) {
194 char *line;
195 if (tostring) {
196 line = mempool_take(pool, tostring(*(void **)p->ses[i].e, tostring_userdata));
197 } else {
198 line = *(void **)p->ses[i].e;
199 }
200 switch (p->ses[i].type) {
201 case DIFF_ADD:
202 buf = str_printf(pool, "%s+%s%s\n", color_add, line, color_reset);
203 break;
204 case DIFF_COMMON:
205 buf = str_printf(pool, " %s\n", line);
206 break;
207 case DIFF_DELETE:
208 buf = str_printf(pool, "%s-%s%s\n", color_delete, line, color_reset);
209 break;
210 }
211 array_append(result, buf);
212 }
213 }
214
215 return str_join(extpool, result, "");
216 }
217