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