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