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