Home | History | Annotate | Line # | Download | only in lib
      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 (at) 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