1 /* $NetBSD: bufgap.c,v 1.4 2010/08/15 16:10:56 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 <stdio.h> 42 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 "fastctype.h" 54 #include "bufgap.h" 55 #include "defs.h" 56 57 /* macros to get subscripts in buffer */ 58 #define AFTSUB(bp, n) ((bp)->buf[(int)n]) 59 #define BEFSUB(bp, n) ((bp)->buf[(int)((bp)->size - (n) - 1)]) 60 61 /* initial allocation size */ 62 #ifndef CHUNKSIZE 63 #define CHUNKSIZE 256 64 #endif 65 66 #ifndef KiB 67 #define KiB(x) ((x) * 1024) 68 #endif 69 70 #define BGCHUNKSIZE KiB(4) 71 72 #ifndef __UNCONST 73 #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) 74 #endif 75 76 #ifndef USE_UTF 77 #define USE_UTF 0 78 #endif 79 80 #if !USE_UTF 81 #define Rune char 82 #define utfbytes(x) strlen(x) 83 #define utfrune(a, b) strchr(a, b) 84 #define utfnlen(a, b) bounded_strlen(a, b) 85 86 static size_t 87 bounded_strlen(const char *s, size_t maxlen) 88 { 89 size_t n; 90 91 for (n = 0 ; n < maxlen && s[n] != 0x0 ; n++) { 92 } 93 return n; 94 } 95 96 static int 97 chartorune(Rune *rp, char *s) 98 { 99 *rp = s[0]; 100 return 1; 101 } 102 103 static int 104 priorrune(Rune *rp, char *s) 105 { 106 *rp = s[0]; 107 return 1; 108 } 109 #else 110 #include "ure.h" 111 #endif 112 113 /* save `n' chars of `s' in malloc'd memory */ 114 static char * 115 strnsave(char *s, int n) 116 { 117 char *cp; 118 119 if (n < 0) { 120 n = (int)strlen(s); 121 } 122 NEWARRAY(char, cp, n + 1, "strnsave", return NULL); 123 (void) memcpy(cp, s, (size_t)n); 124 cp[n] = 0x0; 125 return cp; 126 } 127 128 /* open a file in a buffer gap structure */ 129 int 130 bufgap_open(bufgap_t *bp, const char *f) 131 { 132 struct stat s; 133 int64_t cc; 134 FILE *filep; 135 char *cp; 136 137 (void) memset(bp, 0x0, sizeof(*bp)); 138 filep = NULL; 139 if (f != NULL && (filep = fopen(f, "r")) == NULL) { 140 return 0; 141 } 142 if (f == NULL) { 143 bp->size = BGCHUNKSIZE; 144 NEWARRAY(char, bp->buf, bp->size, "f_open", return 0); 145 } else { 146 (void) fstat(fileno(filep), &s); 147 bp->size = (int) ((s.st_size / BGCHUNKSIZE) + 1) * BGCHUNKSIZE; 148 NEWARRAY(char, bp->buf, bp->size, "f_open", return 0); 149 cc = fread(&BEFSUB(bp, s.st_size), sizeof(char), 150 (size_t)s.st_size, filep); 151 (void) fclose(filep); 152 if (cc != s.st_size) { 153 FREE(bp->buf); 154 FREE(bp); 155 return 0; 156 } 157 bp->name = strnsave(__UNCONST(f), (int)utfbytes(__UNCONST(f))); 158 bp->bbc = s.st_size; 159 cp = &BEFSUB(bp, cc); 160 for (;;) { 161 if ((cp = utfrune(cp, '\n')) == NULL) { 162 break; 163 } 164 bp->blc++; 165 cp++; 166 } 167 bp->bcc = utfnlen(&BEFSUB(bp, cc), (size_t)cc); 168 } 169 return 1; 170 } 171 172 /* close a buffer gapped file */ 173 void 174 bufgap_close(bufgap_t *bp) 175 { 176 FREE(bp->buf); 177 } 178 179 /* move forwards `n' chars/bytes in a buffer gap */ 180 int 181 bufgap_forwards(bufgap_t *bp, uint64_t n, int type) 182 { 183 Rune r; 184 int rlen; 185 186 switch(type) { 187 case BGChar: 188 if (bp->bcc >= n) { 189 while (n-- > 0) { 190 rlen = chartorune(&r, &BEFSUB(bp, bp->bbc)); 191 if (rlen == 1) { 192 AFTSUB(bp, bp->abc) = BEFSUB(bp, bp->bbc); 193 } else { 194 (void) memmove(&AFTSUB(bp, bp->abc), 195 &BEFSUB(bp, bp->bbc), 196 (size_t)rlen); 197 } 198 bp->acc++; 199 bp->bcc--; 200 bp->abc += rlen; 201 bp->bbc -= rlen; 202 if (r == '\n') { 203 bp->alc++; 204 bp->blc--; 205 } 206 } 207 return 1; 208 } 209 break; 210 case BGByte: 211 if (bp->bbc >= n) { 212 for ( ; n > 0 ; n -= rlen) { 213 rlen = chartorune(&r, &BEFSUB(bp, bp->bbc)); 214 if (rlen == 1) { 215 AFTSUB(bp, bp->abc) = BEFSUB(bp, bp->bbc); 216 } else { 217 (void) memmove(&AFTSUB(bp, bp->abc), 218 &BEFSUB(bp, bp->bbc), 219 (size_t)rlen); 220 } 221 bp->acc++; 222 bp->bcc--; 223 bp->abc += rlen; 224 bp->bbc -= rlen; 225 if (r == '\n') { 226 bp->alc++; 227 bp->blc--; 228 } 229 } 230 return 1; 231 } 232 } 233 return 0; 234 } 235 236 /* move backwards `n' chars in a buffer gap */ 237 int 238 bufgap_backwards(bufgap_t *bp, uint64_t n, int type) 239 { 240 Rune r; 241 int rlen; 242 243 switch(type) { 244 case BGChar: 245 if (bp->acc >= n) { 246 while (n-- > 0) { 247 rlen = priorrune(&r, &AFTSUB(bp, bp->abc)); 248 bp->bcc++; 249 bp->acc--; 250 bp->bbc += rlen; 251 bp->abc -= rlen; 252 if (rlen == 1) { 253 BEFSUB(bp, bp->bbc) = AFTSUB(bp, bp->abc); 254 } else { 255 (void) memmove(&BEFSUB(bp, bp->bbc), 256 &AFTSUB(bp, bp->abc), 257 (size_t)rlen); 258 } 259 if (r == '\n') { 260 bp->blc++; 261 bp->alc--; 262 } 263 } 264 return 1; 265 } 266 break; 267 case BGByte: 268 if (bp->acc >= n) { 269 for ( ; n > 0 ; n -= rlen) { 270 rlen = priorrune(&r, &AFTSUB(bp, bp->abc)); 271 bp->bcc++; 272 bp->acc--; 273 bp->bbc += rlen; 274 bp->abc -= rlen; 275 if (rlen == 1) { 276 BEFSUB(bp, bp->bbc) = AFTSUB(bp, bp->abc); 277 } else { 278 (void) memmove(&BEFSUB(bp, bp->bbc), 279 &AFTSUB(bp, bp->abc), 280 (size_t)rlen); 281 } 282 if (r == '\n') { 283 bp->blc++; 284 bp->alc--; 285 } 286 } 287 return 1; 288 } 289 } 290 return 0; 291 } 292 293 /* move within a buffer gap */ 294 int 295 bufgap_seek(bufgap_t *bp, int64_t off, int whence, int type) 296 { 297 switch(type) { 298 case BGLine: 299 switch(whence) { 300 case BGFromBOF: 301 if (off < 0 || off > (int64_t)(bp->alc + bp->blc)) { 302 return 0; 303 } 304 if (off < (int64_t)bp->alc) { 305 while (off <= (int64_t)bp->alc && bufgap_backwards(bp, 1, BGChar)) { 306 } 307 if (off > 0) { 308 (void) bufgap_forwards(bp, 1, BGChar); 309 } 310 } else if (off > (int64_t)bp->alc) { 311 while (off > (int64_t)bp->alc && bufgap_forwards(bp, 1, BGChar)) { 312 } 313 } 314 return 1; 315 case BGFromHere: 316 return bufgap_seek(bp, (int64_t)(bp->alc + off), BGFromBOF, BGLine); 317 case BGFromEOF: 318 return bufgap_seek(bp, (int64_t)(bp->alc + bp->blc + off), BGFromBOF, BGLine); 319 } 320 break; 321 case BGChar: 322 switch(whence) { 323 case BGFromBOF: 324 if (off < 0 || off > (int64_t)(bp->acc + bp->bcc)) { 325 return 0; 326 } 327 if (off < (int64_t)bp->acc) { 328 return bufgap_backwards(bp, bp->acc - off, BGChar); 329 } else if (off > (int64_t)bp->acc) { 330 return bufgap_forwards(bp, off - bp->acc, BGChar); 331 } 332 return 1; 333 case BGFromHere: 334 return bufgap_seek(bp, (int64_t)(bp->acc + off), BGFromBOF, BGChar); 335 case BGFromEOF: 336 return bufgap_seek(bp, (int64_t)(bp->acc + bp->bcc + off), BGFromBOF, BGChar); 337 } 338 break; 339 case BGByte: 340 switch(whence) { 341 case BGFromBOF: 342 if (off < 0 || off > (int64_t)(bp->abc + bp->bbc)) { 343 return 0; 344 } 345 if (off < (int64_t)bp->abc) { 346 return bufgap_backwards(bp, bp->abc - off, BGByte); 347 } else if (off > (int64_t)bp->abc) { 348 return bufgap_forwards(bp, off - bp->abc, BGByte); 349 } 350 return 1; 351 case BGFromHere: 352 return bufgap_seek(bp, (int64_t)(bp->abc + off), BGFromBOF, BGByte); 353 case BGFromEOF: 354 return bufgap_seek(bp, (int64_t)(bp->abc + bp->bbc + off), BGFromBOF, BGByte); 355 } 356 break; 357 } 358 return 0; 359 } 360 361 /* return a pointer to the text in the buffer gap */ 362 char * 363 bufgap_getstr(bufgap_t *bp) 364 { 365 return &BEFSUB(bp, bp->bbc); 366 } 367 368 /* return the binary text in the buffer gap */ 369 int 370 bufgap_getbin(bufgap_t *bp, void *dst, size_t len) 371 { 372 int cc; 373 374 cc = (bp->bcc < len) ? (int)bp->bcc : (int)len; 375 (void) memcpy(dst, &BEFSUB(bp, bp->bbc), len); 376 return cc; 377 } 378 379 /* return offset (from beginning/end) in a buffer gap */ 380 int64_t 381 bufgap_tell(bufgap_t *bp, int whence, int type) 382 { 383 switch(whence) { 384 case BGFromBOF: 385 return (type == BGLine) ? bp->alc : 386 (type == BGByte) ? bp->abc : bp->acc; 387 case BGFromEOF: 388 return (type == BGLine) ? bp->blc : 389 (type == BGByte) ? bp->bbc : bp->bcc; 390 default: 391 (void) fprintf(stderr, "weird whence in bufgap_tell\n"); 392 break; 393 } 394 return (int64_t)0; 395 } 396 397 /* return size of buffer gap */ 398 int64_t 399 bufgap_size(bufgap_t *bp, int type) 400 { 401 return (type == BGLine) ? bp->alc + bp->blc : 402 (type == BGChar) ? bp->acc + bp->bcc : 403 bp->abc + bp->bbc; 404 } 405 406 /* insert `n' chars of `s' in a buffer gap */ 407 int 408 bufgap_insert(bufgap_t *bp, const char *s, int n) 409 { 410 int64_t off; 411 Rune r; 412 int rlen; 413 int i; 414 415 if (n < 0) { 416 n = (int)strlen(s); 417 } 418 for (i = 0 ; i < n ; i += rlen) { 419 if (bp->bbc + bp->abc == bp->size) { 420 off = bufgap_tell(bp, BGFromBOF, BGChar); 421 (void) bufgap_seek(bp, 0, BGFromEOF, BGChar); 422 bp->size *= 2; 423 RENEW(char, bp->buf, bp->size, "bufgap_insert", return 0); 424 (void) bufgap_seek(bp, off, BGFromBOF, BGChar); 425 } 426 if ((rlen = chartorune(&r, __UNCONST(s))) == 1) { 427 AFTSUB(bp, bp->abc) = *s; 428 } else { 429 (void) memmove(&AFTSUB(bp, bp->abc), s, (size_t)rlen); 430 } 431 if (r == '\n') { 432 bp->alc++; 433 } 434 bp->modified = 1; 435 bp->abc += rlen; 436 bp->acc++; 437 s += rlen; 438 } 439 return 1; 440 } 441 442 /* delete `n' bytes from the buffer gap */ 443 int 444 bufgap_delete(bufgap_t *bp, uint64_t n) 445 { 446 uint64_t i; 447 Rune r; 448 int rlen; 449 450 if (n <= bp->bbc) { 451 for (i = 0 ; i < n ; i += rlen) { 452 rlen = chartorune(&r, &BEFSUB(bp, bp->bbc)); 453 if (r == '\n') { 454 bp->blc--; 455 } 456 bp->bbc -= rlen; 457 bp->bcc--; 458 bp->modified = 1; 459 } 460 return 1; 461 } 462 return 0; 463 } 464 465 /* look at a character in a buffer gap `delta' UTF chars away */ 466 int 467 bufgap_peek(bufgap_t *bp, int64_t delta) 468 { 469 int ch; 470 471 if (delta != 0) { 472 if (!bufgap_seek(bp, delta, BGFromHere, BGChar)) { 473 return -1; 474 } 475 } 476 ch = BEFSUB(bp, bp->bbc); 477 if (delta != 0) { 478 (void) bufgap_seek(bp, -delta, BGFromHere, BGChar); 479 } 480 return ch; 481 } 482 483 /* return, in malloc'd storage, text from the buffer gap */ 484 char * 485 bufgap_gettext(bufgap_t *bp, int64_t from, int64_t to) 486 { 487 int64_t off; 488 int64_t n; 489 char *text; 490 491 off = bufgap_tell(bp, BGFromBOF, BGChar); 492 NEWARRAY(char, text, (to - from + 1), "bufgap_gettext", return NULL); 493 (void) bufgap_seek(bp, from, BGFromBOF, BGChar); 494 for (n = 0 ; n < to - from ; n++) { 495 text[(int)n] = BEFSUB(bp, bp->bbc - n); 496 } 497 text[(int)n] = 0x0; 498 (void) bufgap_seek(bp, off, BGFromBOF, BGChar); 499 return text; 500 } 501 502 /* return 1 if we wrote the file correctly */ 503 int 504 bufgap_write(bufgap_t *bp, FILE *filep) 505 { 506 if (fwrite(bp->buf, sizeof(char), (size_t)bp->abc, filep) != (size_t)bp->abc) { 507 return 0; 508 } 509 if (fwrite(&BEFSUB(bp, bp->bbc), sizeof(char), (size_t)bp->bbc, filep) != (size_t)bp->bbc) { 510 return 0; 511 } 512 return 1; 513 } 514 515 /* tell if the buffer gap is dirty - has been modified */ 516 int 517 bufgap_dirty(bufgap_t *bp) 518 { 519 return (int)bp->modified; 520 } 521