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