]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/contrib/csup/diff.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / contrib / csup / diff.c
1 /*-
2  * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <assert.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "diff.h"
36 #include "keyword.h"
37 #include "misc.h"
38 #include "stream.h"
39
40 typedef long lineno_t;
41
42 #define EC_ADD  0
43 #define EC_DEL  1
44
45 /* Editing command and state. */
46 struct editcmd {
47         int cmd;
48         lineno_t where;
49         lineno_t count;
50         lineno_t lasta;
51         lineno_t lastd;
52         lineno_t editline;
53         /* For convenience. */
54         struct keyword *keyword;
55         struct diffinfo *di;
56         struct stream *orig;
57         struct stream *dest;
58 };
59
60 static int      diff_geteditcmd(struct editcmd *, char *);
61 static int      diff_copyln(struct editcmd *, lineno_t);
62 static void     diff_write(struct editcmd *, void *, size_t);
63
64 int
65 diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
66     struct keyword *keyword, struct diffinfo *di)
67 {
68         struct editcmd ec;
69         lineno_t i;
70         char *line;
71         size_t size;
72         int empty, error, noeol;
73
74         memset(&ec, 0, sizeof(ec));
75         empty = 0;
76         noeol = 0;
77         ec.di = di;
78         ec.keyword = keyword;
79         ec.orig = orig;
80         ec.dest = dest;
81         line = stream_getln(rd, NULL);
82         while (line != NULL && strcmp(line, ".") != 0 &&
83             strcmp(line, ".+") != 0) {
84                 /*
85                  * The server sends an empty line and then terminates
86                  * with .+ for forced (and thus empty) commits.
87                  */
88                 if (*line == '\0') {
89                         if (empty)
90                                 return (-1);
91                         empty = 1;
92                         line = stream_getln(rd, NULL);
93                         continue;
94                 }
95                 error = diff_geteditcmd(&ec, line);
96                 if (error)
97                         return (-1);
98
99                 if (ec.cmd == EC_ADD) {
100                         error = diff_copyln(&ec, ec.where);
101                         if (error)
102                                 return (-1);
103                         for (i = 0; i < ec.count; i++) {
104                                 line = stream_getln(rd, &size);
105                                 if (line == NULL)
106                                         return (-1);
107                                 if (line[0] == '.') {
108                                         line++;
109                                         size--;
110                                 }
111                                 diff_write(&ec, line, size);
112                         }
113                 } else {
114                         assert(ec.cmd == EC_DEL);
115                         error = diff_copyln(&ec, ec.where - 1);
116                         if (error)
117                                 return (-1);
118                         for (i = 0; i < ec.count; i++) {
119                                 line = stream_getln(orig, NULL);
120                                 if (line == NULL)
121                                         return (-1);
122                                 ec.editline++;
123                         }
124                 }
125                 line = stream_getln(rd, NULL);
126         }
127         if (line == NULL)
128                 return (-1);
129         /* If we got ".+", there's no ending newline. */
130         if (strcmp(line, ".+") == 0 && !empty)
131                 noeol = 1;
132         ec.where = 0;
133         while ((line = stream_getln(orig, &size)) != NULL)
134                 diff_write(&ec, line, size);
135         stream_flush(dest);
136         if (noeol) {
137                 error = stream_truncate_rel(dest, -1);
138                 if (error) {
139                         warn("stream_truncate_rel");
140                         return (-1);
141                 }
142         }
143         return (0);
144 }
145
146 /* Get an editing command from the diff. */
147 static int
148 diff_geteditcmd(struct editcmd *ec, char *line)
149 {
150         char *end;
151
152         if (line[0] == 'a')
153                 ec->cmd = EC_ADD;
154         else if (line[0] == 'd')
155                 ec->cmd = EC_DEL;
156         else
157                 return (-1);
158         errno = 0;
159         ec->where = strtol(line + 1, &end, 10);
160         if (errno || ec->where < 0 || *end != ' ')
161                 return (-1);
162         line = end + 1;
163         errno = 0;
164         ec->count = strtol(line, &end, 10);
165         if (errno || ec->count <= 0 || *end != '\0')
166                 return (-1);
167         if (ec->cmd == EC_ADD) {
168                 if (ec->where < ec->lasta)
169                         return (-1);
170                 ec->lasta = ec->where + 1;
171         } else {
172                 if (ec->where < ec->lasta || ec->where < ec->lastd)
173                         return (-1);
174                 ec->lasta = ec->where;
175                 ec->lastd = ec->where + ec->count;
176         }
177         return (0);
178 }
179
180 /* Copy lines from the original version of the file up to line "to". */
181 static int
182 diff_copyln(struct editcmd *ec, lineno_t to)
183 {
184         char *line;
185         size_t size;
186
187         while (ec->editline < to) {
188                 line = stream_getln(ec->orig, &size);
189                 if (line == NULL)
190                         return (-1);
191                 ec->editline++;
192                 diff_write(ec, line, size);
193         }
194         return (0);
195 }
196
197 /* Write a new line to the file, expanding RCS keywords appropriately. */
198 static void
199 diff_write(struct editcmd *ec, void *buf, size_t size)
200 {
201         char *line, *newline;
202         size_t newsize;
203         int ret;
204
205         line = buf;
206         ret = keyword_expand(ec->keyword, ec->di, line, size,
207             &newline, &newsize);
208         if (ret) {
209                 stream_write(ec->dest, newline, newsize);
210                 free(newline);
211         } else {
212                 stream_write(ec->dest, buf, size);
213         }
214 }