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
bounded_strlen(const char * s,size_t maxlen)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
chartorune(Rune * rp,char * s)85 chartorune(Rune *rp, char *s)
86 {
87 *rp = s[0];
88 return 1;
89 }
90
91 static int
priorrune(Rune * rp,char * s)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 *
strnsave(char * s,int n)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
bufgap_open(bufgap_t * bp,const char * f)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
bufgap_close(bufgap_t * bp)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
bufgap_forwards(bufgap_t * bp,uint64_t n,int type)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
bufgap_backwards(bufgap_t * bp,uint64_t n,int type)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
bufgap_seek(bufgap_t * bp,int64_t off,int whence,int type)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 *
bufgap_getstr(bufgap_t * bp)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
bufgap_getbin(bufgap_t * bp,void * dst,size_t len)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
bufgap_tell(bufgap_t * bp,int whence,int type)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
bufgap_size(bufgap_t * bp,int type)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
bufgap_insert(bufgap_t * bp,const char * s,int n)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
bufgap_delete(bufgap_t * bp,uint64_t n)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
bufgap_peek(bufgap_t * bp,int64_t delta)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 *
bufgap_gettext(bufgap_t * bp,int64_t from,int64_t to)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
bufgap_write(bufgap_t * bp,FILE * filep)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
bufgap_dirty(bufgap_t * bp)505 bufgap_dirty(bufgap_t *bp)
506 {
507 return (int)bp->modified;
508 }
509