xref: /openbsd-src/usr.bin/cvs/buf.c (revision 94fd4554194a14f126fba33b837cc68a1df42468)
1 /*	$OpenBSD: buf.c,v 1.60 2007/02/22 06:42:09 otto 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 <sys/stat.h>
28 
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include "cvs.h"
35 #include "buf.h"
36 
37 #define BUF_INCR	128
38 
39 struct cvs_buf {
40 	u_int	cb_flags;
41 
42 	/* buffer handle, buffer size, and data length */
43 	u_char	*cb_buf;
44 	size_t	 cb_size;
45 	size_t	 cb_len;
46 };
47 
48 #define SIZE_LEFT(b)	(b->cb_size - b->cb_len)
49 
50 static void	cvs_buf_grow(BUF *, size_t);
51 
52 /*
53  * cvs_buf_alloc()
54  *
55  * Create a new buffer structure and return a pointer to it.  This structure
56  * uses dynamically-allocated memory and must be freed with cvs_buf_free(),
57  * once the buffer is no longer needed.
58  */
59 BUF *
60 cvs_buf_alloc(size_t len, u_int flags)
61 {
62 	BUF *b;
63 
64 	b = xmalloc(sizeof(*b));
65 	/* Postpone creation of zero-sized buffers */
66 	if (len > 0)
67 		b->cb_buf = xcalloc(1, len);
68 	else
69 		b->cb_buf = NULL;
70 
71 	b->cb_flags = flags;
72 	b->cb_size = len;
73 	b->cb_len = 0;
74 
75 	return (b);
76 }
77 
78 BUF *
79 cvs_buf_load(const char *path, u_int flags)
80 {
81 	int fd;
82 	BUF *bp;
83 
84 	if ((fd = open(path, O_RDONLY, 0600)) == -1)
85 		fatal("cvs_buf_load: failed to load '%s' : %s", path,
86 		    strerror(errno));
87 
88 	bp = cvs_buf_load_fd(fd, flags);
89 	(void)close(fd);
90 	return (bp);
91 }
92 
93 BUF *
94 cvs_buf_load_fd(int fd, u_int flags)
95 {
96 	ssize_t ret;
97 	size_t len;
98 	u_char *bp;
99 	struct stat st;
100 	BUF *buf;
101 
102 	if (fstat(fd, &st) == -1)
103 		fatal("cvs_buf_load_fd: fstat: %s", strerror(errno));
104 
105 	if (lseek(fd, 0, SEEK_SET) == -1)
106 		fatal("cvs_buf_load_fd: lseek: %s", strerror(errno));
107 
108 	buf = cvs_buf_alloc(st.st_size, flags);
109 	for (bp = buf->cb_buf; ; bp += (size_t)ret) {
110 		len = SIZE_LEFT(buf);
111 		ret = read(fd, bp, len);
112 		if (ret == -1)
113 			fatal("cvs_buf_load: read: %s", strerror(errno));
114 		else if (ret == 0)
115 			break;
116 
117 		buf->cb_len += (size_t)ret;
118 	}
119 
120 	return (buf);
121 }
122 
123 /*
124  * cvs_buf_free()
125  *
126  * Free the buffer <b> and all associated data.
127  */
128 void
129 cvs_buf_free(BUF *b)
130 {
131 	if (b->cb_buf != NULL)
132 		xfree(b->cb_buf);
133 	xfree(b);
134 }
135 
136 /*
137  * cvs_buf_release()
138  *
139  * Free the buffer <b>'s structural information but do not free the contents
140  * of the buffer.  Instead, they are returned and should be freed later using
141  * free().
142  */
143 u_char *
144 cvs_buf_release(BUF *b)
145 {
146 	u_char *tmp;
147 
148 	tmp = b->cb_buf;
149 	xfree(b);
150 	return (tmp);
151 }
152 
153 /*
154  * cvs_buf_empty()
155  *
156  * Empty the contents of the buffer <b> and reset pointers.
157  */
158 void
159 cvs_buf_empty(BUF *b)
160 {
161 	memset(b->cb_buf, 0, b->cb_size);
162 	b->cb_len = 0;
163 }
164 
165 /*
166  * cvs_buf_putc()
167  *
168  * Append a single character <c> to the end of the buffer <b>.
169  */
170 void
171 cvs_buf_putc(BUF *b, int c)
172 {
173 	u_char *bp;
174 
175 	bp = b->cb_buf + b->cb_len;
176 	if (bp == (b->cb_buf + b->cb_size)) {
177 		/* extend */
178 		if (b->cb_flags & BUF_AUTOEXT)
179 			cvs_buf_grow(b, (size_t)BUF_INCR);
180 		else
181 			fatal("cvs_buf_putc failed");
182 
183 		/* the buffer might have been moved */
184 		bp = b->cb_buf + b->cb_len;
185 	}
186 	*bp = (u_char)c;
187 	b->cb_len++;
188 }
189 
190 /*
191  * cvs_buf_getc()
192  *
193  * Return u_char at buffer position <pos>.
194  *
195  */
196 u_char
197 cvs_buf_getc(BUF *b, size_t pos)
198 {
199 	return (b->cb_buf[pos]);
200 }
201 
202 /*
203  * cvs_buf_append()
204  *
205  * Append <len> bytes of data pointed to by <data> to the buffer <b>.  If the
206  * buffer is too small to accept all data, it will attempt to append as much
207  * data as possible, or if the BUF_AUTOEXT flag is set for the buffer, it
208  * will get resized to an appropriate size to accept all data.
209  * Returns the number of bytes successfully appended to the buffer.
210  */
211 ssize_t
212 cvs_buf_append(BUF *b, const void *data, size_t len)
213 {
214 	size_t left, rlen;
215 	u_char *bp, *bep;
216 
217 	bp = b->cb_buf + b->cb_len;
218 	bep = b->cb_buf + b->cb_size;
219 	left = bep - bp;
220 	rlen = len;
221 
222 	if (left < len) {
223 		if (b->cb_flags & BUF_AUTOEXT) {
224 			cvs_buf_grow(b, len - left);
225 			bp = b->cb_buf + b->cb_len;
226 		} else
227 			rlen = bep - bp;
228 	}
229 
230 	memcpy(bp, data, rlen);
231 	b->cb_len += rlen;
232 
233 	return (rlen);
234 }
235 
236 /*
237  * cvs_buf_fappend()
238  *
239  */
240 ssize_t
241 cvs_buf_fappend(BUF *b, const char *fmt, ...)
242 {
243 	ssize_t ret;
244 	char *str;
245 	va_list vap;
246 
247 	va_start(vap, fmt);
248 	ret = vasprintf(&str, fmt, vap);
249 	va_end(vap);
250 
251 	if (ret == -1)
252 		fatal("cvs_buf_fappend: failed to format data");
253 
254 	ret = cvs_buf_append(b, str, (size_t)ret);
255 	xfree(str);
256 	return (ret);
257 }
258 
259 /*
260  * cvs_buf_len()
261  *
262  * Returns the size of the buffer that is being used.
263  */
264 size_t
265 cvs_buf_len(BUF *b)
266 {
267 	return (b->cb_len);
268 }
269 
270 /*
271  * cvs_buf_write_fd()
272  *
273  * Write the contents of the buffer <b> to the specified <fd>
274  */
275 int
276 cvs_buf_write_fd(BUF *b, int fd)
277 {
278 	u_char *bp;
279 	size_t len;
280 	ssize_t ret;
281 
282 	len = b->cb_len;
283 	bp = b->cb_buf;
284 
285 	do {
286 		ret = write(fd, bp, len);
287 		if (ret == -1) {
288 			if (errno == EINTR || errno == EAGAIN)
289 				continue;
290 			return (-1);
291 		}
292 
293 		len -= (size_t)ret;
294 		bp += (size_t)ret;
295 	} while (len > 0);
296 
297 	return (0);
298 }
299 
300 /*
301  * cvs_buf_write()
302  *
303  * Write the contents of the buffer <b> to the file whose path is given in
304  * <path>.  If the file does not exist, it is created with mode <mode>.
305  */
306 int
307 cvs_buf_write(BUF *b, const char *path, mode_t mode)
308 {
309 	int fd;
310  open:
311 	if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
312 		if (errno == EACCES && unlink(path) != -1)
313 			goto open;
314 		else
315 			fatal("open: `%s': %s", path, strerror(errno));
316 	}
317 
318 	if (cvs_buf_write_fd(b, fd) == -1) {
319 		(void)unlink(path);
320 		fatal("cvs_buf_write: cvs_buf_write_fd: `%s'", path);
321 	}
322 
323 	if (fchmod(fd, mode) < 0)
324 		cvs_log(LP_ERR, "permissions not set on file %s", path);
325 
326 	(void)close(fd);
327 
328 	return (0);
329 }
330 
331 /*
332  * cvs_buf_write_stmp()
333  *
334  * Write the contents of the buffer <b> to a temporary file whose path is
335  * specified using <template> (see mkstemp.3). NB. This function will modify
336  * <template>, as per mkstemp
337  */
338 void
339 cvs_buf_write_stmp(BUF *b, char *template, struct timeval *tv)
340 {
341 	int fd;
342 
343 	if ((fd = mkstemp(template)) == -1)
344 		fatal("mkstemp: `%s': %s", template, strerror(errno));
345 
346 	if (cvs_buf_write_fd(b, fd) == -1) {
347 		(void)unlink(template);
348 		fatal("cvs_buf_write_stmp: cvs_buf_write_fd: `%s'", template);
349 	}
350 
351 	if (tv != NULL) {
352 		if (futimes(fd, tv) == -1)
353 			fatal("cvs_buf_write_stmp: futimes failed");
354 	}
355 
356 	(void)close(fd);
357 
358 	cvs_worklist_add(template, &temp_files);
359 }
360 
361 /*
362  * cvs_buf_grow()
363  *
364  * Grow the buffer <b> by <len> bytes.  The contents are unchanged by this
365  * operation regardless of the result.
366  */
367 static void
368 cvs_buf_grow(BUF *b, size_t len)
369 {
370 	void *tmp;
371 
372 	tmp = xrealloc(b->cb_buf, 1, b->cb_size + len);
373 	b->cb_buf = tmp;
374 	b->cb_size += len;
375 }
376 
377 /*
378  * cvs_buf_copy()
379  *
380  * Copy the first <len> bytes of data in the buffer <b> starting at offset
381  * <off> in the destination buffer <dst>, which can accept up to <len> bytes.
382  * Returns the number of bytes successfully copied, or -1 on failure.
383  */
384 ssize_t
385 cvs_buf_copy(BUF *b, size_t off, void *dst, size_t len)
386 {
387 	size_t rc;
388 
389 	if (off > b->cb_len)
390 		fatal("cvs_buf_copy failed");
391 
392 	rc = MIN(len, (b->cb_len - off));
393 	memcpy(dst, b->cb_buf + off, rc);
394 
395 	return (ssize_t)rc;
396 }
397 
398 /*
399  * cvs_buf_peek()
400  *
401  * Peek at the contents of the buffer <b> at offset <off>.
402  */
403 const u_char *
404 cvs_buf_peek(BUF *b, size_t off)
405 {
406 	if (off >= b->cb_len)
407 		return (NULL);
408 
409 	return (b->cb_buf + off);
410 }
411 
412 int
413 cvs_buf_differ(const BUF *b1, const BUF *b2)
414 {
415 	if (b1->cb_len != b2->cb_len)
416 		return (1);
417 
418 	return (memcmp(b1->cb_buf, b2->cb_buf, b1->cb_len));
419 }
420