1 /* $NetBSD: bufgap.c,v 1.5 2010/11/29 06:21:40 agc Exp $ */ 2 3 /*- 4 * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Alistair Crooks (agc@NetBSD.org) 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 #include "config.h" 32 33 #ifdef HAVE_SYS_TYPES_H 34 #include <sys/types.h> 35 #endif 36 37 #ifdef HAVE_SYS_STAT_H 38 #include <sys/stat.h> 39 #endif 40 41 #include <ctype.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 45 #ifdef HAVE_UNISTD_H 46 #include <unistd.h> 47 #endif 48 49 #ifdef HAVE_STRING_H 50 #include <string.h> 51 #endif 52 53 #include "bufgap.h" 54 #include "defs.h" 55 56 /* macros to get subscripts in buffer */ 57 #define AFTSUB(bp, n) ((bp)->buf[(int)n]) 58 #define BEFSUB(bp, n) ((bp)->buf[(int)((bp)->size - (n) - 1)]) 59 60 /* initial allocation size */ 61 #ifndef CHUNKSIZE 62 #define CHUNKSIZE 256 63 #endif 64 65 #ifndef KiB 66 #define KiB(x) ((x) * 1024) 67 #endif 68 69 #define BGCHUNKSIZE KiB(4) 70 71 #ifndef __UNCONST 72 #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) 73 #endif 74 75 #ifndef USE_UTF 76 #define USE_UTF 0 77 #endif 78 79 #if !USE_UTF 80 #define Rune char 81 #define utfbytes(x) strlen(x) 82 #define utfrune(a, b) strchr(a, b) 83 #define utfnlen(a, b) bounded_strlen(a, b) 84 85 static size_t 86 bounded_strlen(const char *s, size_t maxlen) 87 { 88 size_t n; 89 90 for (n = 0 ; n < maxlen && s[n] != 0x0 ; n++) { 91 } 92 return n; 93 } 94 95 static int 96 chartorune(Rune *rp, char *s) 97 { 98 *rp = s[0]; 99 return 1; 100 } 101 102 static int 103 priorrune(Rune *rp, char *s) 104 { 105 *rp = s[0]; 106 return 1; 107 } 108 #else 109 #include "ure.h" 110 #endif 111 112 /* save `n' chars of `s' in malloc'd memory */ 113 static char * 114 strnsave(char *s, int n) 115 { 116 char *cp; 117 118 if (n < 0) { 119 n = (int)strlen(s); 120 } 121 NEWARRAY(char, cp, n + 1, "strnsave", return NULL); 122 (void) memcpy(cp, s, (size_t)n); 123 cp[n] = 0x0; 124 return cp; 125 } 126 127 /* open a file in a buffer gap structure */ 128 int 129 bufgap_open(bufgap_t *bp, const char *f) 130 { 131 struct stat s; 132 int64_t cc; 133 FILE *filep; 134 char *cp; 135 136 (void) memset(bp, 0x0, sizeof(*bp)); 137 filep = NULL; 138 if (f != NULL && (filep = fopen(f, "r")) == NULL) { 139 return 0; 140 } 141 if (f == NULL) { 142 bp->size = BGCHUNKSIZE; 143 NEWARRAY(char, bp->buf, bp->size, "f_open", return 0); 144 } else { 145 (void) fstat(fileno(filep), &s); 146 bp->size = (int) ((s.st_size / BGCHUNKSIZE) + 1) * BGCHUNKSIZE; 147 NEWARRAY(char, bp->buf, bp->size, "f_open", return 0); 148 cc = fread(&BEFSUB(bp, s.st_size), sizeof(char), 149 (size_t)s.st_size, filep); 150 (void) fclose(filep); 151 if (cc != s.st_size) { 152 FREE(bp->buf); 153 FREE(bp); 154 return 0; 155 } 156 bp->name = strnsave(__UNCONST(f), (int)utfbytes(__UNCONST(f))); 157 bp->bbc = s.st_size; 158 cp = &BEFSUB(bp, cc); 159 for (;;) { 160 if ((cp = utfrune(cp, '\n')) == NULL) { 161 break; 162 } 163 bp->blc++; 164 cp++; 165 } 166 bp->bcc = utfnlen(&BEFSUB(bp, cc), (size_t)cc); 167 } 168 return 1; 169 } 170 171 /* close a buffer gapped file */ 172 void 173 bufgap_close(bufgap_t *bp) 174 { 175 FREE(bp->buf); 176 } 177 178 /* move forwards `n' chars/bytes in a buffer gap */ 179 int 180 bufgap_forwards(bufgap_t *bp, uint64_t n, int type) 181 { 182 Rune r; 183 int rlen; 184 185 switch(type) { 186 case BGChar: 187 if (bp->bcc >= n) { 188 while (n-- > 0) { 189 rlen = chartorune(&r, &BEFSUB(bp, bp->bbc)); 190 if (rlen == 1) { 191 AFTSUB(bp, bp->abc) = BEFSUB(bp, bp->bbc); 192 } else { 193 (void) memmove(&AFTSUB(bp, bp->abc), 194 &BEFSUB(bp, bp->bbc), 195 (size_t)rlen); 196 } 197 bp->acc++; 198 bp->bcc--; 199 bp->abc += rlen; 200 bp->bbc -= rlen; 201 if (r == '\n') { 202 bp->alc++; 203 bp->blc--; 204 } 205 } 206 return 1; 207 } 208 break; 209 case BGByte: 210 if (bp->bbc >= n) { 211 for ( ; n > 0 ; n -= rlen) { 212 rlen = chartorune(&r, &BEFSUB(bp, bp->bbc)); 213 if (rlen == 1) { 214 AFTSUB(bp, bp->abc) = BEFSUB(bp, bp->bbc); 215 } else { 216 (void) memmove(&AFTSUB(bp, bp->abc), 217 &BEFSUB(bp, bp->bbc), 218 (size_t)rlen); 219 } 220 bp->acc++; 221 bp->bcc--; 222 bp->abc += rlen; 223 bp->bbc -= rlen; 224 if (r == '\n') { 225 bp->alc++; 226 bp->blc--; 227 } 228 } 229 return 1; 230 } 231 } 232 return 0; 233 } 234 235 /* move backwards `n' chars in a buffer gap */ 236 int 237 bufgap_backwards(bufgap_t *bp, uint64_t n, int type) 238 { 239 Rune r; 240 int rlen; 241 242 switch(type) { 243 case BGChar: 244 if (bp->acc >= n) { 245 while (n-- > 0) { 246 rlen = priorrune(&r, &AFTSUB(bp, bp->abc)); 247 bp->bcc++; 248 bp->acc--; 249 bp->bbc += rlen; 250 bp->abc -= rlen; 251 if (rlen == 1) { 252 BEFSUB(bp, bp->bbc) = AFTSUB(bp, bp->abc); 253 } else { 254 (void) memmove(&BEFSUB(bp, bp->bbc), 255 &AFTSUB(bp, bp->abc), 256 (size_t)rlen); 257 } 258 if (r == '\n') { 259 bp->blc++; 260 bp->alc--; 261 } 262 } 263 return 1; 264 } 265 break; 266 case BGByte: 267 if (bp->acc >= n) { 268 for ( ; n > 0 ; n -= rlen) { 269 rlen = priorrune(&r, &AFTSUB(bp, bp->abc)); 270 bp->bcc++; 271 bp->acc--; 272 bp->bbc += rlen; 273 bp->abc -= rlen; 274 if (rlen == 1) { 275 BEFSUB(bp, bp->bbc) = AFTSUB(bp, bp->abc); 276 } else { 277 (void) memmove(&BEFSUB(bp, bp->bbc), 278 &AFTSUB(bp, bp->abc), 279 (size_t)rlen); 280 } 281 if (r == '\n') { 282 bp->blc++; 283 bp->alc--; 284 } 285 } 286 return 1; 287 } 288 } 289 return 0; 290 } 291 292 /* move within a buffer gap */ 293 int 294 bufgap_seek(bufgap_t *bp, int64_t off, int whence, int type) 295 { 296 switch(type) { 297 case BGLine: 298 switch(whence) { 299 case BGFromBOF: 300 if (off < 0 || off > (int64_t)(bp->alc + bp->blc)) { 301 return 0; 302 } 303 if (off < (int64_t)bp->alc) { 304 while (off <= (int64_t)bp->alc && bufgap_backwards(bp, 1, BGChar)) { 305 } 306 if (off > 0) { 307 (void) bufgap_forwards(bp, 1, BGChar); 308 } 309 } else if (off > (int64_t)bp->alc) { 310 while (off > (int64_t)bp->alc && bufgap_forwards(bp, 1, BGChar)) { 311 } 312 } 313 return 1; 314 case BGFromHere: 315 return bufgap_seek(bp, (int64_t)(bp->alc + off), BGFromBOF, BGLine); 316 case BGFromEOF: 317 return bufgap_seek(bp, (int64_t)(bp->alc + bp->blc + off), BGFromBOF, BGLine); 318 } 319 break; 320 case BGChar: 321 switch(whence) { 322 case BGFromBOF: 323 if (off < 0 || off > (int64_t)(bp->acc + bp->bcc)) { 324 return 0; 325 } 326 if (off < (int64_t)bp->acc) { 327 return bufgap_backwards(bp, bp->acc - off, BGChar); 328 } else if (off > (int64_t)bp->acc) { 329 return bufgap_forwards(bp, off - bp->acc, BGChar); 330 } 331 return 1; 332 case BGFromHere: 333 return bufgap_seek(bp, (int64_t)(bp->acc + off), BGFromBOF, BGChar); 334 case BGFromEOF: 335 return bufgap_seek(bp, (int64_t)(bp->acc + bp->bcc + off), BGFromBOF, BGChar); 336 } 337 break; 338 case BGByte: 339 switch(whence) { 340 case BGFromBOF: 341 if (off < 0 || off > (int64_t)(bp->abc + bp->bbc)) { 342 return 0; 343 } 344 if (off < (int64_t)bp->abc) { 345 return bufgap_backwards(bp, bp->abc - off, BGByte); 346 } else if (off > (int64_t)bp->abc) { 347 return bufgap_forwards(bp, off - bp->abc, BGByte); 348 } 349 return 1; 350 case BGFromHere: 351 return bufgap_seek(bp, (int64_t)(bp->abc + off), BGFromBOF, BGByte); 352 case BGFromEOF: 353 return bufgap_seek(bp, (int64_t)(bp->abc + bp->bbc + off), BGFromBOF, BGByte); 354 } 355 break; 356 } 357 return 0; 358 } 359 360 /* return a pointer to the text in the buffer gap */ 361 char * 362 bufgap_getstr(bufgap_t *bp) 363 { 364 return &BEFSUB(bp, bp->bbc); 365 } 366 367 /* return the binary text in the buffer gap */ 368 int 369 bufgap_getbin(bufgap_t *bp, void *dst, size_t len) 370 { 371 int cc; 372 373 cc = (bp->bcc < len) ? (int)bp->bcc : (int)len; 374 (void) memcpy(dst, &BEFSUB(bp, bp->bbc), len); 375 return cc; 376 } 377 378 /* return offset (from beginning/end) in a buffer gap */ 379 int64_t 380 bufgap_tell(bufgap_t *bp, int whence, int type) 381 { 382 switch(whence) { 383 case BGFromBOF: 384 return (type == BGLine) ? bp->alc : 385 (type == BGByte) ? bp->abc : bp->acc; 386 case BGFromEOF: 387 return (type == BGLine) ? bp->blc : 388 (type == BGByte) ? bp->bbc : bp->bcc; 389 default: 390 (void) fprintf(stderr, "weird whence in bufgap_tell\n"); 391 break; 392 } 393 return (int64_t)0; 394 } 395 396 /* return size of buffer gap */ 397 int64_t 398 bufgap_size(bufgap_t *bp, int type) 399 { 400 return (type == BGLine) ? bp->alc + bp->blc : 401 (type == BGChar) ? bp->acc + bp->bcc : 402 bp->abc + bp->bbc; 403 } 404 405 /* insert `n' chars of `s' in a buffer gap */ 406 int 407 bufgap_insert(bufgap_t *bp, const char *s, int n) 408 { 409 int64_t off; 410 Rune r; 411 int rlen; 412 int i; 413 414 if (n < 0) { 415 n = (int)strlen(s); 416 } 417 for (i = 0 ; i < n ; i += rlen) { 418 if (bp->bbc + bp->abc == bp->size) { 419 off = bufgap_tell(bp, BGFromBOF, BGChar); 420 (void) bufgap_seek(bp, 0, BGFromEOF, BGChar); 421 bp->size *= 2; 422 RENEW(char, bp->buf, bp->size, "bufgap_insert", return 0); 423 (void) bufgap_seek(bp, off, BGFromBOF, BGChar); 424 } 425 if ((rlen = chartorune(&r, __UNCONST(s))) == 1) { 426 AFTSUB(bp, bp->abc) = *s; 427 } else { 428 (void) memmove(&AFTSUB(bp, bp->abc), s, (size_t)rlen); 429 } 430 if (r == '\n') { 431 bp->alc++; 432 } 433 bp->modified = 1; 434 bp->abc += rlen; 435 bp->acc++; 436 s += rlen; 437 } 438 return 1; 439 } 440 441 /* delete `n' bytes from the buffer gap */ 442 int 443 bufgap_delete(bufgap_t *bp, uint64_t n) 444 { 445 uint64_t i; 446 Rune r; 447 int rlen; 448 449 if (n <= bp->bbc) { 450 for (i = 0 ; i < n ; i += rlen) { 451 rlen = chartorune(&r, &BEFSUB(bp, bp->bbc)); 452 if (r == '\n') { 453 bp->blc--; 454 } 455 bp->bbc -= rlen; 456 bp->bcc--; 457 bp->modified = 1; 458 } 459 return 1; 460 } 461 return 0; 462 } 463 464 /* look at a character in a buffer gap `delta' UTF chars away */ 465 int 466 bufgap_peek(bufgap_t *bp, int64_t delta) 467 { 468 int ch; 469 470 if (delta != 0) { 471 if (!bufgap_seek(bp, delta, BGFromHere, BGChar)) { 472 return -1; 473 } 474 } 475 ch = BEFSUB(bp, bp->bbc); 476 if (delta != 0) { 477 (void) bufgap_seek(bp, -delta, BGFromHere, BGChar); 478 } 479 return ch; 480 } 481 482 /* return, in malloc'd storage, text from the buffer gap */ 483 char * 484 bufgap_gettext(bufgap_t *bp, int64_t from, int64_t to) 485 { 486 int64_t off; 487 int64_t n; 488 char *text; 489 490 off = bufgap_tell(bp, BGFromBOF, BGChar); 491 NEWARRAY(char, text, (to - from + 1), "bufgap_gettext", return NULL); 492 (void) bufgap_seek(bp, from, BGFromBOF, BGChar); 493 for (n = 0 ; n < to - from ; n++) { 494 text[(int)n] = BEFSUB(bp, bp->bbc - n); 495 } 496 text[(int)n] = 0x0; 497 (void) bufgap_seek(bp, off, BGFromBOF, BGChar); 498 return text; 499 } 500 501 /* return 1 if we wrote the file correctly */ 502 int 503 bufgap_write(bufgap_t *bp, FILE *filep) 504 { 505 if (fwrite(bp->buf, sizeof(char), (size_t)bp->abc, filep) != (size_t)bp->abc) { 506 return 0; 507 } 508 if (fwrite(&BEFSUB(bp, bp->bbc), sizeof(char), (size_t)bp->bbc, filep) != (size_t)bp->bbc) { 509 return 0; 510 } 511 return 1; 512 } 513 514 /* tell if the buffer gap is dirty - has been modified */ 515 int 516 bufgap_dirty(bufgap_t *bp) 517 { 518 return (int)bp->modified; 519 } 520