xref: /openbsd-src/usr.bin/cvs/buf.c (revision d874cce4b1d9fe6b41c9e4f2117a77d8a4a37b92)
1 /*	$OpenBSD: buf.c,v 1.72 2008/06/10 01:00:34 joris 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 #include <sys/time.h>
29 
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "atomicio.h"
37 #include "cvs.h"
38 #include "buf.h"
39 
40 #define BUF_INCR	128
41 
42 struct cvs_buf {
43 	/* buffer handle, buffer size, and data length */
44 	u_char	*cb_buf;
45 	size_t	 cb_size;
46 	size_t	 cb_len;
47 };
48 
49 #define SIZE_LEFT(b)	(b->cb_size - b->cb_len)
50 
51 static void	cvs_buf_grow(BUF *, size_t);
52 
53 /*
54  * cvs_buf_alloc()
55  *
56  * Create a new buffer structure and return a pointer to it.  This structure
57  * uses dynamically-allocated memory and must be freed with cvs_buf_free(),
58  * once the buffer is no longer needed.
59  */
60 BUF *
61 cvs_buf_alloc(size_t len)
62 {
63 	BUF *b;
64 
65 	b = xmalloc(sizeof(*b));
66 	/* Postpone creation of zero-sized buffers */
67 	if (len > 0)
68 		b->cb_buf = xcalloc(1, len);
69 	else
70 		b->cb_buf = NULL;
71 
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)
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);
89 	(void)close(fd);
90 	return (bp);
91 }
92 
93 BUF *
94 cvs_buf_load_fd(int fd)
95 {
96 	struct stat st;
97 	BUF *buf;
98 
99 	if (fstat(fd, &st) == -1)
100 		fatal("cvs_buf_load_fd: fstat: %s", strerror(errno));
101 
102 	if (lseek(fd, 0, SEEK_SET) == -1)
103 		fatal("cvs_buf_load_fd: lseek: %s", strerror(errno));
104 
105 	if (st.st_size > SIZE_MAX)
106 		fatal("cvs_buf_load_fd: file size too big");
107 	buf = cvs_buf_alloc(st.st_size);
108 	if (atomicio(read, fd, buf->cb_buf, buf->cb_size) != buf->cb_size)
109 		fatal("cvs_buf_load_fd: read: %s", strerror(errno));
110 	buf->cb_len = buf->cb_size;
111 
112 	return (buf);
113 }
114 
115 /*
116  * cvs_buf_free()
117  *
118  * Free the buffer <b> and all associated data.
119  */
120 void
121 cvs_buf_free(BUF *b)
122 {
123 	if (b->cb_buf != NULL)
124 		xfree(b->cb_buf);
125 	xfree(b);
126 }
127 
128 /*
129  * cvs_buf_release()
130  *
131  * Free the buffer <b>'s structural information but do not free the contents
132  * of the buffer.  Instead, they are returned and should be freed later using
133  * free().
134  */
135 u_char *
136 cvs_buf_release(BUF *b)
137 {
138 	u_char *tmp;
139 
140 	tmp = b->cb_buf;
141 	xfree(b);
142 	return (tmp);
143 }
144 
145 /*
146  * cvs_buf_empty()
147  *
148  * Empty the contents of the buffer <b> and reset pointers.
149  */
150 void
151 cvs_buf_empty(BUF *b)
152 {
153 	memset(b->cb_buf, 0, b->cb_size);
154 	b->cb_len = 0;
155 }
156 
157 /*
158  * cvs_buf_putc()
159  *
160  * Append a single character <c> to the end of the buffer <b>.
161  */
162 void
163 cvs_buf_putc(BUF *b, int c)
164 {
165 	u_char *bp;
166 
167 	bp = b->cb_buf + b->cb_len;
168 	if (bp == (b->cb_buf + b->cb_size)) {
169 		/* extend */
170 		cvs_buf_grow(b, (size_t)BUF_INCR);
171 
172 		/* the buffer might have been moved */
173 		bp = b->cb_buf + b->cb_len;
174 	}
175 	*bp = (u_char)c;
176 	b->cb_len++;
177 }
178 
179 /*
180  * cvs_buf_puts()
181  *
182  * Wrapper function for constant strings to cvs_buf_append.
183  */
184 void
185 cvs_buf_puts(BUF *b, const char *str)
186 {
187 	cvs_buf_append(b, str, strlen(str));
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 void
212 cvs_buf_append(BUF *b, const void *data, size_t len)
213 {
214 	size_t left;
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 
221 	if (left < len) {
222 		cvs_buf_grow(b, len - left);
223 		bp = b->cb_buf + b->cb_len;
224 	}
225 
226 	memcpy(bp, data, len);
227 	b->cb_len += len;
228 }
229 
230 /*
231  * cvs_buf_len()
232  *
233  * Returns the size of the buffer that is being used.
234  */
235 size_t
236 cvs_buf_len(BUF *b)
237 {
238 	return (b->cb_len);
239 }
240 
241 /*
242  * cvs_buf_write_fd()
243  *
244  * Write the contents of the buffer <b> to the specified <fd>
245  */
246 int
247 cvs_buf_write_fd(BUF *b, int fd)
248 {
249 	if (atomicio(vwrite, fd, b->cb_buf, b->cb_len) != b->cb_len)
250 		return (-1);
251 	return (0);
252 }
253 
254 /*
255  * cvs_buf_write()
256  *
257  * Write the contents of the buffer <b> to the file whose path is given in
258  * <path>.  If the file does not exist, it is created with mode <mode>.
259  */
260 int
261 cvs_buf_write(BUF *b, const char *path, mode_t mode)
262 {
263 	int fd;
264  open:
265 	if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
266 		if (errno == EACCES && unlink(path) != -1)
267 			goto open;
268 		else
269 			fatal("open: `%s': %s", path, strerror(errno));
270 	}
271 
272 	if (cvs_buf_write_fd(b, fd) == -1) {
273 		(void)unlink(path);
274 		fatal("cvs_buf_write: cvs_buf_write_fd: `%s'", path);
275 	}
276 
277 	if (fchmod(fd, mode) < 0)
278 		cvs_log(LP_ERR, "permissions not set on file %s", path);
279 
280 	(void)close(fd);
281 
282 	return (0);
283 }
284 
285 /*
286  * cvs_buf_write_stmp()
287  *
288  * Write the contents of the buffer <b> to a temporary file whose path is
289  * specified using <template> (see mkstemp.3). NB. This function will modify
290  * <template>, as per mkstemp
291  */
292 int
293 cvs_buf_write_stmp(BUF *b, char *template, struct timeval *tv)
294 {
295 	int fd;
296 
297 	if ((fd = mkstemp(template)) == -1)
298 		fatal("mkstemp: `%s': %s", template, strerror(errno));
299 
300 	if (cvs_buf_write_fd(b, fd) == -1) {
301 		(void)unlink(template);
302 		fatal("cvs_buf_write_stmp: cvs_buf_write_fd: `%s'", template);
303 	}
304 
305 	if (tv != NULL) {
306 		if (futimes(fd, tv) == -1)
307 			fatal("cvs_buf_write_stmp: futimes failed");
308 	}
309 
310 	cvs_worklist_add(template, &temp_files);
311 
312 	if (lseek(fd, 0, SEEK_SET) < 0)
313 		fatal("cvs_buf_write_stmp: lseek: %s", strerror(errno));
314 
315 	return (fd);
316 }
317 
318 /*
319  * cvs_buf_grow()
320  *
321  * Grow the buffer <b> by <len> bytes.  The contents are unchanged by this
322  * operation regardless of the result.
323  */
324 static void
325 cvs_buf_grow(BUF *b, size_t len)
326 {
327 	b->cb_buf = xrealloc(b->cb_buf, 1, b->cb_size + len);
328 	b->cb_size += len;
329 }
330 
331 /*
332  * cvs_buf_copy()
333  *
334  * Copy the first <len> bytes of data in the buffer <b> starting at offset
335  * <off> in the destination buffer <dst>, which can accept up to <len> bytes.
336  * Returns the number of bytes successfully copied, or -1 on failure.
337  */
338 ssize_t
339 cvs_buf_copy(BUF *b, size_t off, void *dst, size_t len)
340 {
341 	size_t rc;
342 
343 	if (off > b->cb_len)
344 		fatal("cvs_buf_copy failed");
345 
346 	rc = MIN(len, (b->cb_len - off));
347 	memcpy(dst, b->cb_buf + off, rc);
348 
349 	return (ssize_t)rc;
350 }
351 
352 /*
353  * cvs_buf_peek()
354  *
355  * Peek at the contents of the buffer <b> at offset <off>.
356  */
357 const u_char *
358 cvs_buf_peek(BUF *b, size_t off)
359 {
360 	if (off >= b->cb_len)
361 		return (NULL);
362 
363 	return (b->cb_buf + off);
364 }
365 
366 int
367 cvs_buf_differ(const BUF *b1, const BUF *b2)
368 {
369 	if (b1->cb_len != b2->cb_len)
370 		return (1);
371 
372 	return (memcmp(b1->cb_buf, b2->cb_buf, b1->cb_len));
373 }
374