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