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