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