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