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
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
96 chartorune(Rune *rp, char *s)
97 {
98 	*rp = s[0];
99 	return 1;
100 }
101 
102 static int
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 *
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
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
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
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
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
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 *
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
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
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
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
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
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
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 *
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
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
516 bufgap_dirty(bufgap_t *bp)
517 {
518 	return (int)bp->modified;
519 }
520