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
85static size_t
86bounded_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
95static int
96chartorune(Rune *rp, char *s)
97{
98	*rp = s[0];
99	return 1;
100}
101
102static int
103priorrune(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 */
113static char *
114strnsave(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 */
128int
129bufgap_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 */
172void
173bufgap_close(bufgap_t *bp)
174{
175	FREE(bp->buf);
176}
177
178/* move forwards `n' chars/bytes in a buffer gap */
179int
180bufgap_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 */
236int
237bufgap_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 */
293int
294bufgap_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 */
361char *
362bufgap_getstr(bufgap_t *bp)
363{
364	return &BEFSUB(bp, bp->bbc);
365}
366
367/* return the binary text in the buffer gap */
368int
369bufgap_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 */
379int64_t
380bufgap_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 */
397int64_t
398bufgap_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 */
406int
407bufgap_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 */
442int
443bufgap_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 */
465int
466bufgap_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 */
483char *
484bufgap_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 */
502int
503bufgap_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 */
515int
516bufgap_dirty(bufgap_t *bp)
517{
518	return (int)bp->modified;
519}
520