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
bounded_strlen(const char * s,size_t maxlen)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
chartorune(Rune * rp,char * s)96 chartorune(Rune *rp, char *s)
97 {
98 *rp = s[0];
99 return 1;
100 }
101
102 static int
priorrune(Rune * rp,char * s)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 *
strnsave(char * s,int n)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
bufgap_open(bufgap_t * bp,const char * f)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
bufgap_close(bufgap_t * bp)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
bufgap_forwards(bufgap_t * bp,uint64_t n,int type)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
bufgap_backwards(bufgap_t * bp,uint64_t n,int type)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
bufgap_seek(bufgap_t * bp,int64_t off,int whence,int type)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 *
bufgap_getstr(bufgap_t * bp)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
bufgap_getbin(bufgap_t * bp,void * dst,size_t len)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
bufgap_tell(bufgap_t * bp,int whence,int type)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
bufgap_size(bufgap_t * bp,int type)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
bufgap_insert(bufgap_t * bp,const char * s,int n)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
bufgap_delete(bufgap_t * bp,uint64_t n)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
bufgap_peek(bufgap_t * bp,int64_t delta)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 *
bufgap_gettext(bufgap_t * bp,int64_t from,int64_t to)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
bufgap_write(bufgap_t * bp,FILE * filep)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
bufgap_dirty(bufgap_t * bp)516 bufgap_dirty(bufgap_t *bp)
517 {
518 return (int)bp->modified;
519 }
520