xref: /openbsd-src/usr.bin/cvs/buf.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: buf.c,v 1.55 2006/07/08 09:25:44 ray Exp $	*/
2 /*
3  * Copyright (c) 2003 Jean-Francois Brousseau <jfb@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "includes.h"
28 
29 #include "cvs.h"
30 #include "buf.h"
31 #include "log.h"
32 #include "xmalloc.h"
33 #include "worklist.h"
34 
35 #define BUF_INCR	128
36 
37 struct cvs_buf {
38 	u_int	cb_flags;
39 
40 	/* buffer handle and size */
41 	u_char	*cb_buf;
42 	size_t	 cb_size;
43 
44 	/* start and length of valid data in buffer */
45 	u_char	*cb_cur;
46 	size_t	 cb_len;
47 };
48 
49 #define SIZE_LEFT(b)	(b->cb_size - (size_t)(b->cb_cur - b->cb_buf) \
50 			    - b->cb_len)
51 
52 static void	cvs_buf_grow(BUF *, size_t);
53 
54 /*
55  * cvs_buf_alloc()
56  *
57  * Create a new buffer structure and return a pointer to it.  This structure
58  * uses dynamically-allocated memory and must be freed with cvs_buf_free(),
59  * once the buffer is no longer needed.
60  */
61 BUF *
62 cvs_buf_alloc(size_t len, u_int flags)
63 {
64 	BUF *b;
65 
66 	b = xmalloc(sizeof(*b));
67 	/* Postpone creation of zero-sized buffers */
68 	if (len > 0)
69 		b->cb_buf = xcalloc(1, len);
70 	else
71 		b->cb_buf = NULL;
72 
73 	b->cb_flags = flags;
74 	b->cb_size = len;
75 	b->cb_cur = b->cb_buf;
76 	b->cb_len = 0;
77 
78 	return (b);
79 }
80 
81 BUF *
82 cvs_buf_load(const char *path, u_int flags)
83 {
84 	int fd;
85 	BUF *bp;
86 
87 	if ((fd = open(path, O_RDONLY, 0600)) == -1)
88 		fatal("cvs_buf_load: failed to load '%s' : %s", path,
89 		    strerror(errno));
90 
91 	bp = cvs_buf_load_fd(fd, flags);
92 	(void)close(fd);
93 	return (bp);
94 }
95 
96 BUF *
97 cvs_buf_load_fd(int fd, u_int flags)
98 {
99 	ssize_t ret;
100 	size_t len;
101 	u_char *bp;
102 	struct stat st;
103 	BUF *buf;
104 
105 	if (fstat(fd, &st) == -1)
106 		fatal("cvs_buf_load_fd: fstat: %s", strerror(errno));
107 
108 	if (lseek(fd, 0, SEEK_SET) == -1)
109 		fatal("cvs_buf_load_fd: lseek: %s", strerror(errno));
110 
111 	buf = cvs_buf_alloc(st.st_size, flags);
112 	for (bp = buf->cb_cur; ; bp += (size_t)ret) {
113 		len = SIZE_LEFT(buf);
114 		ret = read(fd, bp, len);
115 		if (ret == -1)
116 			fatal("cvs_buf_load: read: %s", strerror(errno));
117 		else if (ret == 0)
118 			break;
119 
120 		buf->cb_len += (size_t)ret;
121 	}
122 
123 	return (buf);
124 }
125 
126 /*
127  * cvs_buf_free()
128  *
129  * Free the buffer <b> and all associated data.
130  */
131 void
132 cvs_buf_free(BUF *b)
133 {
134 	if (b->cb_buf != NULL)
135 		xfree(b->cb_buf);
136 	xfree(b);
137 }
138 
139 /*
140  * cvs_buf_release()
141  *
142  * Free the buffer <b>'s structural information but do not free the contents
143  * of the buffer.  Instead, they are returned and should be freed later using
144  * free().
145  */
146 void *
147 cvs_buf_release(BUF *b)
148 {
149 	u_char *tmp;
150 
151 	tmp = b->cb_buf;
152 	xfree(b);
153 	return (tmp);
154 }
155 
156 /*
157  * cvs_buf_empty()
158  *
159  * Empty the contents of the buffer <b> and reset pointers.
160  */
161 void
162 cvs_buf_empty(BUF *b)
163 {
164 	memset(b->cb_buf, 0, b->cb_size);
165 	b->cb_cur = b->cb_buf;
166 	b->cb_len = 0;
167 }
168 
169 /*
170  * cvs_buf_set()
171  *
172  * Set the contents of the buffer <b> at offset <off> to the first <len>
173  * bytes of data found at <src>.  If the buffer was not created with
174  * BUF_AUTOEXT, as many bytes as possible will be copied in the buffer.
175  */
176 ssize_t
177 cvs_buf_set(BUF *b, const void *src, size_t len, size_t off)
178 {
179 	size_t rlen = 0;
180 
181 	if (b->cb_size < (len + off)) {
182 		if ((b->cb_flags & BUF_AUTOEXT)) {
183 			cvs_buf_grow(b, len + off - b->cb_size);
184 			rlen = len + off;
185 		} else {
186 			rlen = b->cb_size - off;
187 		}
188 	} else {
189 		rlen = len;
190 	}
191 
192 	b->cb_len = rlen;
193 	memcpy((b->cb_buf + off), src, rlen);
194 
195 	if (b->cb_len == 0) {
196 		b->cb_cur = b->cb_buf + off;
197 		b->cb_len = rlen;
198 	}
199 
200 	return (rlen);
201 }
202 
203 /*
204  * cvs_buf_putc()
205  *
206  * Append a single character <c> to the end of the buffer <b>.
207  */
208 void
209 cvs_buf_putc(BUF *b, int c)
210 {
211 	u_char *bp;
212 
213 	bp = b->cb_cur + b->cb_len;
214 	if (bp == (b->cb_buf + b->cb_size)) {
215 		/* extend */
216 		if (b->cb_flags & BUF_AUTOEXT)
217 			cvs_buf_grow(b, (size_t)BUF_INCR);
218 		else
219 			fatal("cvs_buf_putc failed");
220 
221 		/* the buffer might have been moved */
222 		bp = b->cb_cur + b->cb_len;
223 	}
224 	*bp = (u_char)c;
225 	b->cb_len++;
226 }
227 
228 /*
229  * cvs_buf_getc()
230  *
231  * Return u_char at buffer position <pos>.
232  *
233  */
234 u_char
235 cvs_buf_getc(BUF *b, size_t pos)
236 {
237 	return (b->cb_cur[pos]);
238 }
239 
240 /*
241  * cvs_buf_append()
242  *
243  * Append <len> bytes of data pointed to by <data> to the buffer <b>.  If the
244  * buffer is too small to accept all data, it will attempt to append as much
245  * data as possible, or if the BUF_AUTOEXT flag is set for the buffer, it
246  * will get resized to an appropriate size to accept all data.
247  * Returns the number of bytes successfully appended to the buffer.
248  */
249 ssize_t
250 cvs_buf_append(BUF *b, const void *data, size_t len)
251 {
252 	size_t left, rlen;
253 	u_char *bp, *bep;
254 
255 	bp = b->cb_cur + b->cb_len;
256 	bep = b->cb_buf + b->cb_size;
257 	left = bep - bp;
258 	rlen = len;
259 
260 	if (left < len) {
261 		if (b->cb_flags & BUF_AUTOEXT) {
262 			cvs_buf_grow(b, len - left);
263 			bp = b->cb_cur + b->cb_len;
264 		} else
265 			rlen = bep - bp;
266 	}
267 
268 	memcpy(bp, data, rlen);
269 	b->cb_len += rlen;
270 
271 	return (rlen);
272 }
273 
274 /*
275  * cvs_buf_fappend()
276  *
277  */
278 ssize_t
279 cvs_buf_fappend(BUF *b, const char *fmt, ...)
280 {
281 	ssize_t ret;
282 	char *str;
283 	va_list vap;
284 
285 	va_start(vap, fmt);
286 	ret = vasprintf(&str, fmt, vap);
287 	va_end(vap);
288 
289 	if (ret == -1)
290 		fatal("cvs_buf_fappend: failed to format data");
291 
292 	ret = cvs_buf_append(b, str, (size_t)ret);
293 	xfree(str);
294 	return (ret);
295 }
296 
297 /*
298  * cvs_buf_len()
299  *
300  * Returns the size of the buffer that is being used.
301  */
302 size_t
303 cvs_buf_len(BUF *b)
304 {
305 	return (b->cb_len);
306 }
307 
308 /*
309  * cvs_buf_write_fd()
310  *
311  * Write the contents of the buffer <b> to the specified <fd>
312  */
313 int
314 cvs_buf_write_fd(BUF *b, int fd)
315 {
316 	u_char *bp;
317 	size_t len;
318 	ssize_t ret;
319 
320 	len = b->cb_len;
321 	bp = b->cb_cur;
322 
323 	do {
324 		ret = write(fd, bp, len);
325 		if (ret == -1) {
326 			if (errno == EINTR || errno == EAGAIN)
327 				continue;
328 			return (-1);
329 		}
330 
331 		len -= (size_t)ret;
332 		bp += (size_t)ret;
333 	} while (len > 0);
334 
335 	return (0);
336 }
337 
338 /*
339  * cvs_buf_write()
340  *
341  * Write the contents of the buffer <b> to the file whose path is given in
342  * <path>.  If the file does not exist, it is created with mode <mode>.
343  */
344 int
345 cvs_buf_write(BUF *b, const char *path, mode_t mode)
346 {
347 	int fd;
348  open:
349 	if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
350 		if (errno == EACCES && unlink(path) != -1)
351 			goto open;
352 		else
353 			fatal("open: `%s': %s", path, strerror(errno));
354 	}
355 
356 	if (cvs_buf_write_fd(b, fd) == -1) {
357 		(void)unlink(path);
358 		fatal("cvs_buf_write: cvs_buf_write_fd: `%s'", path);
359 	}
360 
361 	if (fchmod(fd, mode) < 0)
362 		cvs_log(LP_ERR, "permissions not set on file %s", path);
363 
364 	(void)close(fd);
365 
366 	return (0);
367 }
368 
369 /*
370  * cvs_buf_write_stmp()
371  *
372  * Write the contents of the buffer <b> to a temporary file whose path is
373  * specified using <template> (see mkstemp.3). NB. This function will modify
374  * <template>, as per mkstemp
375  */
376 void
377 cvs_buf_write_stmp(BUF *b, char *template, struct timeval *tv)
378 {
379 	int fd;
380 
381 	if ((fd = mkstemp(template)) == -1)
382 		fatal("mkstemp: `%s': %s", template, strerror(errno));
383 
384 	if (cvs_buf_write_fd(b, fd) == -1) {
385 		(void)unlink(template);
386 		fatal("cvs_buf_write_stmp: cvs_buf_write_fd: `%s'", template);
387 	}
388 
389 	if (tv != NULL) {
390 		if (futimes(fd, tv) == -1)
391 			fatal("cvs_buf_write_stmp: futimes failed");
392 	}
393 
394 	(void)close(fd);
395 
396 	cvs_worklist_add(template, &temp_files);
397 }
398 
399 /*
400  * cvs_buf_grow()
401  *
402  * Grow the buffer <b> by <len> bytes.  The contents are unchanged by this
403  * operation regardless of the result.
404  */
405 static void
406 cvs_buf_grow(BUF *b, size_t len)
407 {
408 	void *tmp;
409 	size_t diff;
410 
411 	diff = b->cb_cur - b->cb_buf;
412 	tmp = xrealloc(b->cb_buf, 1, b->cb_size + len);
413 	b->cb_buf = tmp;
414 	b->cb_size += len;
415 
416 	/* readjust pointers in case the buffer moved in memory */
417 	b->cb_cur = b->cb_buf + diff;
418 }
419 
420 /*
421  * cvs_buf_copy()
422  *
423  * Copy the first <len> bytes of data in the buffer <b> starting at offset
424  * <off> in the destination buffer <dst>, which can accept up to <len> bytes.
425  * Returns the number of bytes successfully copied, or -1 on failure.
426  */
427 ssize_t
428 cvs_buf_copy(BUF *b, size_t off, void *dst, size_t len)
429 {
430 	size_t rc;
431 
432 	if (off > b->cb_len)
433 		fatal("cvs_buf_copy failed");
434 
435 	rc = MIN(len, (b->cb_len - off));
436 	memcpy(dst, b->cb_buf + off, rc);
437 
438 	return (ssize_t)rc;
439 }
440 
441 /*
442  * cvs_buf_peek()
443  *
444  * Peek at the contents of the buffer <b> at offset <off>.
445  */
446 const void *
447 cvs_buf_peek(BUF *b, size_t off)
448 {
449 	if (off >= b->cb_len)
450 		return (NULL);
451 
452 	return (b->cb_buf + off);
453 }
454 
455 int
456 cvs_buf_differ(BUF *b1, BUF *b2)
457 {
458 	char *c1, *c2;
459 	int l1, l2, len, ret;
460 
461 	l1 = cvs_buf_len(b1);
462 	l2 = cvs_buf_len(b2);
463 	len = MIN(l1, l2);
464 
465 	if (l1 != l2)
466 		return (1);
467 
468 	c1 = cvs_buf_release(b1);
469 	c2 = cvs_buf_release(b2);
470 
471 	ret = memcmp(c1, c2, len);
472 
473 	if (c1 != NULL)
474 		xfree(c1);
475 	if (c2 != NULL)
476 		xfree(c2);
477 
478 	return (ret);
479 }
480