1 //
2 // aegis - project change supervisor
3 // Copyright (C) 2001-2006, 2008, 2012 Peter Miller
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see
17 // <http://www.gnu.org/licenses/>.
18 //
19
20 #include <libaegis/input/file_text.h>
21 #include <libaegis/output/file.h>
22 #include <libaegis/patch.h>
23 #include <common/trace.h>
24
25
26 bool
patch_apply(patch_ty * pp,string_ty * ifn,string_ty * ofn)27 patch_apply(patch_ty *pp, string_ty *ifn, string_ty *ofn)
28 {
29 trace(("patch_apply(pp = %p, ifn = \"%s\", ofn = \"%s\")\n{\n",
30 pp, (ifn ? ifn->str_text : ""), (ofn ? ofn->str_text : "")));
31 bool ok = true;
32 output::pointer ofp = output_file::text_open(ofn);
33 if (!ifn)
34 {
35 //
36 // Write all the lines from all the hunks
37 // to the output file.
38 //
39 // (There will only be one hunk if the patch asks for
40 // a modification to a file which doesn't exist.)
41 //
42 for (size_t k = 0; k < pp->actions.length; ++k)
43 {
44 patch_hunk_ty *php = pp->actions.item[0];
45 patch_line_list_ty *pllp = &php->after;
46 for (size_t m = 0; m < pllp->length; ++m)
47 {
48 ofp->fputs(pllp->item[m].value);
49 ofp->fputc('\n');
50 }
51 }
52 }
53 else
54 {
55 //
56 // Read the file into an array.
57 //
58 string_list_ty buffer;
59 input ifp = input_file_text_open(ifn);
60 for (;;)
61 {
62 nstring s;
63 if (!ifp->one_line(s))
64 break;
65 buffer.push_back(s.get_ref());
66 }
67 ifp.close();
68
69 //
70 // Now work over the hunk list, looking for where they go.
71 //
72 for (size_t j = 0; j < pp->actions.length; ++j)
73 {
74 int found;
75 patch_hunk_ty *php;
76 size_t min_line;
77 int running_offset;
78
79 php = pp->actions.item[j];
80 trace(("looking for %d,%lu\n",
81 php->before.start_line_number,
82 php->before.start_line_number + php->before.length - 1));
83 found = 0;
84 min_line = 0;
85 running_offset = 0;
86 for (size_t k = 1; k <= 2 * buffer.nstrings; ++k)
87 {
88 size_t m;
89 int idx;
90 int offset;
91
92 //
93 // 0, -1, 1, -2, 2, etc...
94 //
95 offset = k >> 1;
96 if (k & 1)
97 offset = -offset;
98 offset += running_offset;
99
100 //
101 // If it doesn't fit in the buffer,
102 // it can't be there.
103 //
104 idx = php->before.start_line_number - 1 + offset;
105 if (idx < (int)min_line)
106 continue;
107 if (idx + php->before.length > buffer.nstrings)
108 continue;
109
110 //
111 // See if the lines match.
112 //
113 for (m = 0; m < php->before.length; ++m)
114 {
115 if
116 (
117 !str_equal
118 (
119 buffer.string[idx + m],
120 php->before.item[m].value
121 )
122 )
123 break;
124 }
125 if (m >= php->before.length)
126 {
127 trace(("found, offset=%d\n", offset));
128 php->before.start_line_number += offset;
129 found = 1;
130 min_line =
131 php->before.start_line_number - 1 + php->before.length;
132 running_offset = offset;
133 break;
134 }
135 }
136
137 //
138 // Remember if we didn't find a place to apply
139 // the patch. We'll return it later.
140 //
141 if (!found)
142 {
143 php->before.start_line_number += running_offset;
144 ok = false;
145 }
146 }
147
148 //
149 // Go over each hunk, emitting lines as we go.
150 //
151 int curline = 1;
152 for (size_t j = 0; j < pp->actions.length; ++j)
153 {
154 //
155 // First, any prelude.
156 //
157 patch_hunk_ty *php = pp->actions.item[j];
158 while
159 (
160 curline < php->before.start_line_number
161 &&
162 curline <= (int)buffer.nstrings
163 )
164 {
165 ofp->fputs(buffer.string[curline - 1]);
166 ofp->fputc('\n');
167 ++curline;
168 }
169 curline += php->before.length;
170
171 //
172 // Toss the "before" and emit the "after".
173 // We checked that it was there already.
174 //
175 for (size_t k = 0; k < php->after.length; ++k)
176 {
177 ofp->fputs(php->after.item[k].value);
178 ofp->fputc('\n');
179 }
180 }
181
182 //
183 // Emit anything left over.
184 //
185 while (curline <= (int)buffer.nstrings)
186 {
187 ofp->fputs(buffer.string[curline - 1]);
188 ofp->fputc('\n');
189 ++curline;
190 }
191 }
192 ofp.reset();
193 trace(("return %d\n", ok));
194 trace(("}\n"));
195 return ok;
196 }
197
198
199 // vim: set ts=8 sw=4 et :
200