1 /* @(#)storage.c	1.54 16/08/05 Copyright 1984-2016 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)storage.c	1.54 16/08/05 Copyright 1984-2016 J. Schilling";
6 #endif
7 /*
8  *	Storage management based on the paged virtual memory
9  *	provided by buffer.c
10  *
11  *	Copyright (c) 1984-2016 J. Schilling
12  */
13 /*
14  * The contents of this file are subject to the terms of the
15  * Common Development and Distribution License, Version 1.0 only
16  * (the "License").  You may not use this file except in compliance
17  * with the License.
18  *
19  * See the file CDDL.Schily.txt in this distribution for details.
20  * A copy of the CDDL is also available via the Internet at
21  * http://www.opensource.org/licenses/cddl1.txt
22  *
23  * When distributing Covered Code, include this CDDL HEADER in each
24  * file and include the License file CDDL.Schily.txt from this distribution.
25  */
26 
27 /*
28  * The basic buffer operations:
29  *
30  *	insertion, deletion, loading/storing from/to a file and extracting
31  *
32  * are handled in this package. Findpos() gives direct buffer access for
33  * a given buffer offset. All operations are based on:
34  *
35  *	dot	- The current corsor position offset in the buffer
36  *
37  *	eof	- The offset that comes directly after the last valid
38  *		  character in the buffer, 'eof' is the number of valid
39  *		  characters in the buffer. The 'eof' position contains a
40  *		  space charcter to be able to access this position.
41  *
42  *	gap	- The offset in the buffer memory where the gap is. It
43  *		  has been prepared for insertions or deletions by
44  *		  splitting the actual buffer at the 'gap' position.
45  *		  The 'gap' position is maintained by the insertion
46  *		  and deletion routines.
47  *
48  * To speed up findpos() on very long files, we maintain an 'anker' that
49  * is at or close before the start or the current screen window. This allows
50  * findpos() to work relative to this 'anker'. The variables that are
51  * used to maintain this 'anker' are 'winlink', 'winpos' and 'winoff'.
52  */
53 
54 /*
55  * Include code to speed up findpos().
56  */
57 /*#define	FASTPOS*/
58 /*
59  * Include code to time the fastpos code.
60  */
61 /*#define	TIMEPOS*/
62 /*
63  * Include code for extended fastpos debugging.
64  */
65 /*#define	XPOSDEBUG*/
66 
67 #include "ved.h"
68 #include "buffer.h"
69 #ifdef	TIMEPOS
70 #	include <schily/time.h>	/* Nur f�r Zeitmessungen */
71 #endif
72 #include <schily/errno.h>
73 
74 #define	wp_gaplink(p)	((headr_t *)((p)->gaplink))
75 #define	wp_winlink(p)	((headr_t *)((p)->winlink))
76 
77 EXPORT	void	insert		__PR((ewin_t *wp, Uchar* str, long size));
78 EXPORT	void	delete		__PR((ewin_t *wp, epos_t size));
79 EXPORT	void	rubout		__PR((ewin_t *wp, epos_t size));
80 EXPORT	BOOL	loadfile	__PR((ewin_t *wp, Uchar* filename, BOOL newdefault));
81 EXPORT	BOOL	savefile	__PR((ewin_t *wp, epos_t begin, epos_t end, FILE * f, char *name));
82 EXPORT	BOOL	backsavefile	__PR((ewin_t *wp, epos_t begin, epos_t end, FILE * f, char *name));
83 EXPORT	void	getfile		__PR((ewin_t *wp, FILE * f, epos_t size, char *name));
84 EXPORT	void	backgetfile	__PR((ewin_t *wp, FILE * f, epos_t size, char *name));
85 LOCAL	void	reversebuffer	__PR((Uchar* from, Uchar* to, long size));
86 EXPORT	BOOL	isdos		__PR((ewin_t *wp));
87 EXPORT	int	extract		__PR((ewin_t *wp, epos_t begin, Uchar *str, int size));
88 EXPORT	int	extr_line	__PR((ewin_t *wp, epos_t begin, char *str, int size));
89 EXPORT	int	retractline	__PR((ewin_t *wp, epos_t begin, char *str, int size));
90 LOCAL	void	movegap		__PR((ewin_t *wp, epos_t pos));
91 EXPORT	void	clearifwpos	__PR((ewin_t *wp, headr_t *this));
92 EXPORT	void	clearwpos	__PR((ewin_t *wp));
93 EXPORT	void	backwpos	__PR((ewin_t *wp));
94 EXPORT	void	findwpos	__PR((ewin_t *wp, epos_t new));
95 EXPORT	void	findpos		__PR((ewin_t *wp, epos_t pos, headr_t ** returnlinkp, int *returnoff));
96 #ifdef	FASTPOS
97 #ifdef	CHECKPOS
98 LOCAL	void	ckfindpos	__PR((ewin_t *wp, epos_t pos, headr_t ** returnlinkp, int *returnoff));
99 #endif
100 LOCAL	void	rfindpos	__PR((ewin_t *wp, epos_t pos, headr_t ** returnlinkp, int *returnoff));
101 #endif
102 
103 /*
104  * Insert 'size' characters at dot.
105  * Then put the dot after the inserted characters.
106  */
107 EXPORT void
insert(wp,str,size)108 insert(wp, str, size)
109 		ewin_t	*wp;
110 	register Uchar	*str;
111 	register long	size;
112 {
113 	register Uchar	*to;
114 	register long 	n = size;
115 	register int	amount;
116 
117 	if (n <= 0)
118 		return;
119 
120 	movegap(wp, wp->dot);
121 	readybuffer(wp, wp_gaplink(wp));	/* Make sure buffer is incore */
122 
123 #ifdef	FASTPOS
124 	if (wp->winlink && wp->dot <= wp->winoff)
125 		clearwpos(wp);
126 #endif
127 
128 	if (wp->markvalid && wp->mark == wp->dot) {
129 		resetmark(wp);
130 		wp->markvalid = 1;
131 	}
132 
133 	while (n > 0) {
134 		/*
135 		 * There is no leading gap after a movegap() call.
136 		 */
137 		amount = min(n, BUFFERSIZE - wp_gaplink(wp)->size);
138 		to = wp_gaplink(wp)->cont + wp_gaplink(wp)->size;
139 		wp_gaplink(wp)->flags |= MODIFIED;
140 		wp_gaplink(wp)->size += amount;
141 		n -= amount;
142 		if (amount > 16) {
143 			movebytes(C str, C to, amount);
144 			str += amount;
145 		} else {
146 			while (amount--)
147 				*to++ = *str++;
148 		}
149 		if (n)
150 			wp->gaplink = addbuffer(wp, wp_gaplink(wp));
151 	}
152 	wp->eof += size;
153 	wp->gap += size;
154 	if (wp->markvalid && wp->mark > wp->dot)
155 		wp->mark += size;
156 	wp->dot += size;
157 }
158 
159 /*
160  * Delete 'size' characters after the dot.
161  * Leave 'dot' at the old position.
162  */
163 EXPORT void
delete(wp,size)164 delete(wp, size)
165 	ewin_t	*wp;
166 	epos_t	size;
167 {
168 	register headr_t *next;
169 	register epos_t	n;
170 	register int	amount;
171 
172 #ifdef	DEBUG
173 	cdbg("delete(%lld)", (Llong)size);
174 #endif
175 	if (size > wp->eof-wp->dot)
176 		size = wp->eof-wp->dot;
177 	n = size;
178 	if (n <= 0)
179 		return;
180 
181 	movegap(wp, wp->dot);		/* No need to have the buffer incore */
182 
183 	while (n > 0) {
184 		if ((next = wp_gaplink(wp)->next) == NULL)	/* Paranoia */
185 			break;
186 #ifdef	FASTPOS
187 		if (next == wp->winlink)
188 			clearwpos(wp);
189 #endif
190 /*		readybuffer(wp, next);*/
191 		amount = min(n, next->size);
192 		n -= amount;
193 		next->size -= amount;
194 		next->cont += amount;
195 		/*
196 		 * We are modifying next->cont and thus must set the MODIFIED
197 		 * flag in the header. This is needed because we swap out from
198 		 * linkp->cont and remove any leading gap when we swap in.
199 		 * If we don't want to set the MODIFIED flag here, we must
200 		 * always swap out the complete buffer and restore the old
201 		 * leading gap and the linkp->cont offset when swapping in
202 		 * again.
203 		 */
204 		next->flags |= MODIFIED;
205 		if (n)
206 			deletebuffer(wp, next);
207 	}
208 	wp->eof -= size;
209 	if (wp->markvalid) {
210 		if (wp->mark >= wp->dot && wp->mark < wp->dot+size) {
211 			setmark(wp, wp->dot);
212 		} else if (wp->mark >= wp->dot+size) {
213 			wp->mark -= size;
214 		}
215 	}
216 #ifdef	DEBUG
217 	cdbg("delete() done");
218 #endif
219 }
220 
221 /*
222  * Delete 'size' characters before the dot.
223  * Then move the dot back 'size' characters.
224  */
225 EXPORT void
rubout(wp,size)226 rubout(wp, size)
227 	ewin_t	*wp;
228 	epos_t	size;
229 {
230 	register epos_t	n;
231 		epos_t	savedot = wp->dot;
232 	register int	amount;
233 
234 	if (size > wp->dot)
235 		size = wp->dot;
236 	n = size;
237 	if (n <= 0)
238 		return;
239 
240 	movegap(wp, wp->dot);		/* No need to have the buffer incore */
241 
242 	while (n > 0) {
243 		if (! wp_gaplink(wp)->next)	/* Paranoia */
244 			break;
245 #ifdef	FASTPOS
246 		if (wp_gaplink(wp) == wp->winlink)
247 			clearwpos(wp);
248 #endif
249 		amount = min(n, wp_gaplink(wp)->size);
250 		n -= amount;
251 		wp_gaplink(wp)->size -= amount;
252 		if (n)
253 			wp->gaplink = deletebuffer(wp, wp_gaplink(wp));
254 /*		readybuffer(wp, wp_gaplink(wp));*/
255 	}
256 	wp->dot -= size;
257 	wp->eof -= size;
258 	wp->gap -= size;
259 	if (wp->markvalid) {
260 		if (wp->mark < savedot && wp->mark >= wp->dot) {
261 			setmark(wp, wp->dot);
262 		} else if (wp->mark >= savedot) {
263 			wp->mark -= size;
264 		}
265 	}
266 }
267 
268 /*
269  * Load the content of a file at dot position.
270  */
271 EXPORT BOOL
loadfile(wp,filename,newdefault)272 loadfile(wp, filename, newdefault)
273 	ewin_t	*wp;
274 	Uchar	*filename;
275 	BOOL	newdefault;	/* if true: this is the currenlty edited file*/
276 {
277 	FILE	*infile;
278 	Uchar	str[BUFSIZ < 8192 ? 8192 : BUFSIZ];
279 	long	size;
280 #if	defined(IS_CYGWIN) || defined(__DJGPP__)
281 	BOOL	retry = FALSE;
282 #endif
283 
284 	if (newdefault) {
285 		wp->eflags &= ~(FREADONLY|FNOLOCK);
286 		if (wp->curfd >= 0)
287 			close(wp->curfd);
288 		wp->curfd = -1;
289 		if (wp->curfp != NULL)
290 			fclose(wp->curfp);
291 		wp->curfp = NULL;
292 		if (!ReadOnly && writable(filename))
293 			lockfile(wp, C filename);
294 	}
295 	if ((infile = fileopen(C filename, "rub")) == (FILE *) NULL) {
296 		if (geterrno() == ENOENT) {
297 			if (newdefault && ReadOnly == 0) {
298 				defaulterr(wp, UC "NEW FILE");
299 			} else {
300 				writeerr(wp, "FILE DOES NOT EXIST");
301 			}
302 		} else {
303 			write_errno(wp, "CAN'T OPEN FILE");
304 		}
305 		return (FALSE);
306 	}
307 	file_raise(infile, FALSE);
308 #if	defined(IS_CYGWIN) || defined(__DJGPP__)
309 again:
310 #endif
311 	while ((size = readsyserr(wp, infile, str, sizeof (str), UC "FILE")) > 0) {
312 		insert(wp, str, size);
313 	}
314 
315 	if (size < 0) {
316 #if	defined(IS_CYGWIN) || defined(__DJGPP__)
317 		/*
318 		 * On Cygwin, we cannot lock correctly.
319 		 * Locks are on a fd base not on a process base.
320 		 * In addition, we may not even read from a diffrerent fd
321 		 * if we wold a write lock on another fd.
322 		 */
323 		if (newdefault && !retry && wp->eof == 0 &&
324 		    wp->curfd >= 0 && geterrno() == EACCES) {
325 			/*
326 			 * If read did not work and we did nothing
327 			 * until now, this is a Cygwin locking problem
328 			 * and we must destroy the locking.
329 			 */
330 			close(wp->curfd);
331 			wp->curfd = -1;
332 			retry = TRUE;
333 			goto again;
334 		}
335 #endif
336 		fclose(infile);
337 		return (FALSE);
338 	}
339 #if	defined(IS_CYGWIN) || defined(__DJGPP__)
340 	if (newdefault && !retry && wp->curfd >= 0) {
341 		write_errno(wp, "LOCK WORKED!");
342 #ifdef	SLEEP_ON_WORKING_CYGWIN_LOCK
343 		sleep(2);
344 #endif
345 	}
346 #ifdef	NONOONO
347 	if (newdefault && wp->curfd < 0) {
348 		if (!ReadOnly && writable(C filename))
349 			lockfile(wp, C filename);
350 	}
351 #endif
352 #endif
353 	if (wp->curfd >= 0) {
354 		/*
355 		 * Due to a conceptional bug of lockf()/fcntl(f, F_SETLK)
356 		 * we will loose the lock if we close any fd associated
357 		 * with the loked file.
358 		 */
359 		wp->curfp = infile;
360 	} else {
361 		fclose(infile);
362 		wp->curfp = NULL;
363 	}
364 	return (TRUE);
365 }
366 
367 /*
368  * Save a part of the current buffer into a file.
369  */
370 EXPORT BOOL
savefile(wp,begin,end,f,name)371 savefile(wp, begin, end, f, name)
372 	ewin_t	*wp;
373 	epos_t	begin;
374 	epos_t	end;
375 	FILE	*f;
376 	char	*name;
377 {
378 	register headr_t *linkp = wp->bhead;
379 		headr_t *passlinkp;
380 	register int	amount;
381 		int	pos = 0;
382 		int	nextsize;
383 		Uchar	*from;
384 
385 #ifdef	JOS
386 	getpid();
387 #else
388 	seterrno(0);		/* Damit write_errno korrekt arbeitet */
389 #endif
390 
391 	if (end >= wp->eof)
392 		end = wp->eof;
393 	if (begin >= end)
394 		return (FALSE);
395 	if (f == (FILE *) NULL)
396 		return (FALSE);
397 
398 	findpos(wp, begin, &passlinkp, &pos);
399 	linkp = passlinkp;
400 	from = linkp->cont + pos;
401 	nextsize = linkp->size - pos;
402 
403 	while (begin < end) {
404 		amount = min(end-begin, nextsize);
405 		if (writesyserr(wp, f, from, amount, UC name) != amount)
406 			return (FALSE);
407 		begin += amount;
408 		if (begin < end) {
409 			linkp = linkp->next;
410 			readybuffer(wp, linkp);
411 			from = linkp->cont;
412 			nextsize = linkp->size;
413 		}
414 	}
415 	return (fflush(f) != EOF);
416 }
417 
418 /*
419  * Save a part of the current buffer into a file, write the buffer backwards.
420  */
421 EXPORT BOOL
backsavefile(wp,begin,end,f,name)422 backsavefile(wp, begin, end, f, name)
423 	ewin_t	*wp;
424 	epos_t	begin;
425 	epos_t	end;
426 	FILE	*f;
427 	char	*name;
428 {
429 	register headr_t *linkp;
430 		headr_t *passlinkp;
431 	register int	amount;
432 		Uchar	buf[BUFFERSIZE];
433 	int pos = 0;
434 	int nextsize;
435 	register int	n;
436 		Uchar	*from;
437 		Uchar	*to;
438 
439 #ifdef	JOS
440 	getpid();
441 #else
442 	seterrno(0);		/* Damit write_errno korrekt arbeitet */
443 #endif
444 
445 	if (end > wp->eof)
446 		end = wp->eof;
447 	if (end == 0)
448 		return (FALSE);
449 	if (begin >= end)
450 		return (FALSE);
451 	if (f == (FILE *) NULL)
452 		return (FALSE);
453 
454 	findpos(wp, end-1, &passlinkp, &pos);
455 	linkp = passlinkp;
456 	from = linkp->cont + pos;
457 	nextsize = pos + 1;
458 
459 	while (end > begin) {
460 		amount = min(end-begin, nextsize);
461 		end -= amount;
462 		n = amount;
463 		to = buf;
464 		while (--n >= 0) {
465 			*to++ = *from--;
466 		}
467 		if (writesyserr(wp, f, buf, amount, UC name) != amount)
468 			return (FALSE);
469 		if (end > begin) {
470 			linkp = linkp->prev;
471 			readybuffer(wp, linkp);
472 			from = linkp->cont + linkp->size - 1;
473 			nextsize = linkp->size;
474 		}
475 	}
476 	return (fflush(f) != EOF);
477 }
478 
479 /*
480  * Get the content of a file and insert it 'curnum' times at dot position.
481  * The dot position remains at the old position.
482  */
483 EXPORT void
getfile(wp,f,size,name)484 getfile(wp, f, size, name)
485 	ewin_t	*wp;
486 	FILE	*f;
487 	epos_t	size;
488 	char	*name;
489 {
490 	Uchar	buf[BUFSIZ];
491 	epos_t 	left;
492 	int	this;
493 	long	result;
494 	register ecnt_t	n = wp->curnum;
495 	epos_t	save = wp->dot;
496 
497 	if (size <= 0)
498 		return;
499 
500 	while (--n >= 0) {
501 		lseek(fdown(f), (off_t)0, SEEK_SET);
502 		for (left = size; left > 0; ) {
503 			if (left > sizeof (buf))
504 				this = sizeof (buf);
505 			else
506 				this = (int)left;
507 			if ((result = readsyserr(wp, f, buf, this, UC name)) < 0)
508 				break;
509 			insert(wp, buf, result);
510 			left -= result;
511 		}
512 	}
513 	dispup(wp, wp->dot, save);
514 	wp->dot = save;
515 	modified(wp);
516 }
517 
518 /*
519  * Get the content of a file and insert it backwards 'curnum' times at
520  * dot position.
521  * Then put the dot after the inserted characters.
522  */
523 EXPORT void
backgetfile(wp,f,size,name)524 backgetfile(wp, f, size, name)
525 	ewin_t	*wp;
526 	FILE	*f;
527 	epos_t 	size;
528 	char	*name;
529 {
530 	Uchar	buf[BUFSIZ];
531 	Uchar	rbuf[BUFSIZ];
532 	epos_t	left;
533 	int	this;
534 	long	result;
535 	register ecnt_t	n = wp->curnum;
536 	epos_t	save = wp->dot;
537 
538 	if (size <= 0)
539 		return;
540 
541 	while (--n >= 0) {
542 		for (left = size; left > 0; ) {
543 			if (left > sizeof (buf))
544 				this = sizeof (buf);
545 			else
546 				this = (int)left;
547 			left -= this;
548 			lseek(fdown(f), (off_t)left, SEEK_SET);
549 			if ((result = readsyserr(wp, f, buf, this, UC name)) < 0)
550 				break;
551 			reversebuffer(buf, rbuf, result);
552 			insert(wp, rbuf, result);
553 		}
554 	}
555 	dispup(wp, wp->dot, save);
556 	modified(wp);
557 }
558 
559 /*
560  * Reverse the content of a buffer while copying it to another location
561  */
562 LOCAL void
reversebuffer(from,to,size)563 reversebuffer(from, to, size)
564 	register Uchar	*from;
565 	register Uchar	*to;
566 		long	size;
567 {
568 	to += (size-1);
569 	while (--size >= 0)
570 		*to-- = *from++;
571 }
572 
573 /*
574  * Look into the first block of the buffer memory and find out whether
575  * it is using DOS newlines.
576  */
577 EXPORT BOOL
isdos(wp)578 isdos(wp)
579 	ewin_t	*wp;
580 {
581 	register headr_t *linkp;
582 		headr_t *passlinkp;
583 		int	pos;
584 		int	linksize;
585 		Uchar	*from;
586 	register Uchar	*cp;
587 		int	dosnlcnt = 0;
588 
589 	if (wp->eof <= (epos_t)0)
590 		return (FALSE);
591 
592 	findpos(wp, (epos_t)0, &passlinkp, &pos);
593 	linkp = passlinkp;
594 	linksize = linkp->size - pos;
595 	cp = from = linkp->cont + pos;
596 
597 	while (--linksize >= 0) {
598 		register Uchar	c;
599 
600 		c = *cp++;
601 		if (c == '\0')			/* Is binary, cannot be DOS */
602 			return (FALSE);
603 		if (c == '\n') {
604 			if ((cp - from) < 2)	/* First char is Newline    */
605 				return (FALSE);
606 
607 			if (cp[-2] == '\r') {
608 				dosnlcnt++;
609 				continue;
610 			}
611 			if (dosnlcnt > 0) {
612 				error(
613 				"Non DOS newline at %d after (line %d).\r\n",
614 				linkp->size - pos - linksize - 1, dosnlcnt+1);
615 			}
616 			return (FALSE);		/* Found '\n' without '\r'  */
617 		}
618 	}
619 	return (dosnlcnt > 0);
620 }
621 
622 /*
623  * Extract a portion of the buffer memory.
624  * Fill in up to 'size' characters and return the amount of characters read.
625  * NOTE: a NULL byte is added to the extracted string so it must be able
626  *	 to hold up to size + 1 characters.
627  */
628 EXPORT int
extract(wp,begin,str,size)629 extract(wp, begin, str, size)
630 	ewin_t	*wp;
631 	epos_t	begin;
632 	Uchar	*str;
633 	int	size;
634 {
635 	register headr_t *linkp;
636 		headr_t *passlinkp;
637 		int	pos;
638 		int	savesize = size;
639 		int	amount;
640 		int	linksize;
641 		Uchar	*out = str;
642 		Uchar	*from;
643 
644 /*	if (begin > wp->eof) {*/
645 	if (begin >= wp->eof) {
646 		*out = 0;
647 		return (0);
648 	}
649 	findpos(wp, begin, &passlinkp, &pos);
650 	linkp = passlinkp;
651 	linksize = linkp->size - pos;
652 	from = linkp->cont + pos;
653 
654 	while (size) {
655 		amount = min(size, linksize);
656 		movebytes(C from, C out, (int)amount);
657 		size -= amount;
658 		out += amount;
659 		if (size) {
660 			linkp = linkp->next;
661 			if (! linkp)
662 				break;
663 			readybuffer(wp, linkp);
664 			linksize = linkp->size;
665 			from = linkp->cont;
666 		}
667 	}
668 	amount = savesize - size;
669 	str[amount] = '\0';
670 	return (amount);
671 }
672 
673 /*
674  * Extract a portion of the buffer memory,
675  * stop after extracting a new-line character.
676  * Fill in up to 'size' characters and return the amount of characters read.
677  * NOTE: a NULL byte is added to the extracted string so it must be able
678  *	 to hold up to size + 1 characters.
679  */
680 EXPORT int
extr_line(wp,begin,str,size)681 extr_line(wp, begin, str, size)
682 	ewin_t	*wp;
683 	epos_t	begin;
684 	char	*str;
685 	int	size;
686 {
687 	register headr_t *linkp;
688 		headr_t *passlinkp;
689 		int	pos;
690 		int	length = 0;
691 		int	amount;
692 		int	linksize;
693 		Uchar	*from;
694 
695 /*	if (begin > wp->eof) {*/
696 	if (begin >= wp->eof) {
697 		*str = 0;
698 		return (0);
699 	}
700 	findpos(wp, begin, &passlinkp, &pos);
701 	linkp = passlinkp;
702 	linksize = linkp->size - pos;
703 	from = linkp->cont + pos;
704 
705 	while (size) {
706 		amount = min(size, linksize);
707 		while (amount-- > 0) {
708 			size--, length++;
709 			if ((*str++ = *from++) == '\n') {
710 				size = 0;
711 				break;
712 			}
713 		}
714 		if (size) {
715 			linkp = linkp->next;
716 			if (! linkp)
717 				break;
718 			readybuffer(wp, linkp);
719 			linksize = linkp->size;
720 			from = linkp->cont;
721 		}
722 	}
723 	*str = '\0';
724 	return (length);
725 }
726 
727 /*
728  * Extract a portion of the buffer memory, after searching backwards for
729  * the previous new-line, stop after extracting a new-line character.
730  * Fill in up to 'size' characters and return the amount of characters read.
731  * The 'begin' character position is not extracted to make it complementary
732  * to 'extr_line' and allow symmetrical behavior in search functions.
733  * The new-line that was found before 'begin' is not included.
734  * NOTE: a NULL byte is added to the extracted string so it must be able
735  *	 to hold up to size + 1 characters.
736  */
737 EXPORT int
retractline(wp,begin,str,size)738 retractline(wp, begin, str, size)
739 	ewin_t	*wp;
740 	epos_t	begin;			/* Start searching backw. from here */
741 	char	*str;
742 	int	size;
743 {
744 	epos_t newpos;
745 
746 	if (begin == 0) {
747 		*str = 0;
748 		return (0);
749 	}
750 	if (begin == 1)
751 		return (extr_line(wp, (epos_t)0, str, min(size, 1)));
752 	newpos = srevchar(wp, begin-2, '\n') + 1;
753 	if (newpos >= wp->eof)
754 		newpos = 0;
755 	if (begin-newpos > size)
756 		newpos = begin-size;
757 	return (extr_line(wp, newpos, str, (int)min(size, begin-newpos)));
758 }
759 
760 /*
761  * Move the gap to a new position.
762  * First remove the current gap with compressbuffer().
763  * Then find the header where the new gap position is in and compress the
764  * buffer in case it has a leading gap. This helps us not to waste too much
765  * space with partially filled buffers.
766  * If the new gap position is at the end of the buffer, we are done.
767  * If not, we must split the buffer at the new gap position.
768  * When we are done, the gap is at the end if the current buffer.
769  *
770  * Note that there is no grant that the new buffer at the gap is always ready
771  * and incore when movegap() returns. This is in special true if the gap is
772  * already at the right position (see first return).
773  */
774 LOCAL void
movegap(wp,pos)775 movegap(wp, pos)
776 	ewin_t	*wp;
777 	epos_t	pos;
778 {
779 #ifdef	DEBUG
780 	cdbg("movegap(%lld) gaplink: %p", (Llong)pos, (void *)wp_gaplink(wp));
781 #endif
782 	if (pos == wp->gap && wp_gaplink(wp) != (headr_t *)0)
783 		return;
784 	if (wp_gaplink(wp))
785 		compressbuffer(wp, wp_gaplink(wp));
786 	findpos(wp, wp->gap = pos, (headr_t **)&wp->gaplink, &wp->gapoff);
787 	if (wp_gaplink(wp)->buf != wp_gaplink(wp)->cont)
788 		compressbuffer(wp, wp_gaplink(wp));
789 	if (wp_gaplink(wp)->size > wp->gapoff)
790 		splitbuffer(wp, wp_gaplink(wp), wp->gapoff);
791 }
792 
793 /*
794  * If we are removing the buffer that is pointed to by winlink, we
795  * must invalidate winlink.
796  */
797 EXPORT void
clearifwpos(wp,this)798 clearifwpos(wp, this)
799 	ewin_t	*wp;
800 	headr_t		*this;
801 {
802 #ifdef	FASTPOS
803 	if (this == wp->winlink)
804 		clearwpos(wp);
805 #endif
806 }
807 
808 #ifdef	FASTPOS
809 
810 /*
811  * Invalidate winlink.
812  *
813  * 'winlink' wird ung�ltig wenn:
814  *	1)	der Header entfernt wird auf den 'winlink' zeigt
815  *	2)	vor 'winoff' eingef�gt oder gel�scht wird
816  */
817 EXPORT void
clearwpos(wp)818 clearwpos(wp)
819 	ewin_t	*wp;
820 {
821 #ifdef	XPOSDEBUG
822 	writeerr(wp, "winlink: %p winpos: %lld winoff: %lld",
823 		wp->winlink, (Llong)wp->winpos, (Llong)wp->winoff);
824 	sleep(1);
825 #endif
826 	/*
827 	 * Das Neuberechnen dauert bei 200000 Headern mit einer Sparcstation-2
828 	 * ca. 0,25 Sekunden. Das entspricht bei der aktuellen Buffersize
829 	 * einer Datei von 1,5 GB.
830 	 * Gibt es einen besseren Weg als loeschen?
831 	 */
832 	wp->winlink = NULL;
833 	wp->winpos = 0L;
834 	wp->winoff = 0L;
835 }
836 
837 /*
838  * Try to move winpos/winlink backwards.
839  */
840 /* ARGSUSED */
841 EXPORT void
backwpos(wp)842 backwpos(wp)
843 	ewin_t	*wp;
844 {
845 	/*
846 	 * Das geht nicht!!! Es ist hier nur als Anregung.
847 	 */
848 #ifdef	used
849 	if (winlink(wp)->prev != NULL) {
850 		wp->winlink = winlink(wp)->prev;
851 		wp->winpos -= winlink(wp)->size;
852 	}
853 	wp->winoff = 0L;
854 #endif
855 }
856 
857 /*
858  * Find current window start position.
859  */
860 EXPORT void
findwpos(wp,new)861 findwpos(wp, new)
862 	ewin_t	*wp;
863 	epos_t	new;
864 {
865 	int	xp;
866 
867 	findpos(wp, new, (headr_t **)&wp->winlink, &xp);
868 	wp->winpos = new;	/* The position of the start of the window   */
869 	wp->winoff = new - xp;	/* The position of the first char in winlink */
870 }
871 
872 #else	/* FASTPOS */
873 
874 /*
875  * Find current window start position (dummy).
876  */
877 EXPORT void
findwpos(new)878 findwpos(new)
879 	epos_t	new;
880 {
881 }
882 
883 #endif	/* FASTPOS */
884 
885 /*
886  * Find link pointer and offset in found buffer for 'pos'.
887  */
888 EXPORT void
findpos(wp,pos,returnlinkp,returnoff)889 findpos(wp, pos, returnlinkp, returnoff)
890 		ewin_t	*wp;
891 	register epos_t	pos;
892 	headr_t		**returnlinkp;	/* To return found link header */
893 	int		*returnoff;	/* To return found link offset */
894 {
895 	register headr_t	*linkp = wp->bhead;
896 #ifdef	FASTPOS
897 	epos_t	SAVE = pos;
898 #ifdef	TIMEPOS
899 	struct timeval t1, t2;
900 
901 	gettimeofday(&t1, 0);
902 #endif	/* TIMEPOS */
903 #endif	/* FASTPOS */
904 
905 /*cdbg("findpos(%d) caller %x", pos, getcaller());*/
906 	if (pos > wp->eof)
907 		pos = wp->eof;
908 
909 #ifdef	FASTPOS
910 	if (wp->eof < wp->winoff)
911 		clearwpos(wp);
912 
913 #ifdef	XPOSDEBUG
914 	if (wp->winlink) {
915 		if (((headr_t *)wp->winlink)->flags & INVALID)
916 			cdbg("winoff: %lld INVALID", (Llong)wp->winoff);
917 		if (((headr_t *)wp->winlink)->next == 0) {
918 			cdbg("winoff: %lld winoff+winlink->size: %lld eof: %lld",
919 				(Llong)wp->winoff,
920 				(Llong)wp->winoff+((headr_t *)wp->winlink)->size,
921 				(Llong)wp->eof);
922 		}
923 	}
924 #endif
925 	if (wp->winlink) {
926 		if (pos >= wp->winoff) {
927 			linkp = wp->winlink;
928 			pos -= wp->winoff;
929 		} else {
930 			rfindpos(wp, pos, returnlinkp, returnoff);
931 #ifdef	CHECKPOS
932 			goto checkpos;
933 #else
934 			return;
935 #endif
936 		}
937 	}
938 #endif	/* FASTPOS */
939 	/*
940 	 * Find header that contains 'pos'
941 	 */
942 	while (linkp->size <= pos) {
943 		pos -= linkp->size;
944 		linkp = linkp->next;
945 		if (linkp == 0 || linkp->flags & INVALID) {		/* Paranoia */
946 			rsttmodes(wp);
947 #ifdef	FASTPOS
948 			error("findpos: winlink: %p winpos: %lld winoff: %lld pos: %lld eof: %lld\n",
949 				(void *)wp->winlink,
950 				(Llong)wp->winpos, (Llong)wp->winoff,
951 				(Llong)SAVE, (Llong)wp->eof);
952 			error("findpos: winlink->next: %p\n", (void *)wp_winlink(wp)->next);
953 			if (linkp)
954 				error("linkp: %p linkp->flags = %X\n", (void *)linkp, linkp->flags);
955 #endif
956 			error("pos: %lld linkp: %p\n", (Llong)pos, (void *)linkp);
957 			error("\nBAD POSITION TO BE FOUND\n");
958 			flushprot(wp);
959 			abort();
960 		}
961 	}
962 	readybuffer(wp, linkp);
963 /*cdbg("findpos() returnoff: %d linkp: %X", pos, linkp);*/
964 	*returnlinkp = linkp;
965 	*returnoff = (int)pos;
966 #ifdef	FASTPOS
967 #ifdef	CHECKPOS
968 checkpos:
969 	;
970 #endif
971 #ifdef	TIMEPOS
972 	gettimeofday(&t2, 0);
973 	t2.tv_sec -= t1.tv_sec;
974 	t2.tv_usec -= t1.tv_usec;
975 	while (t2.tv_usec < 0) {
976 		t2.tv_usec += 1000000;
977 		t2.tv_sec -= 1;
978 	}
979 	if (t2.tv_usec)
980 		cdbg("time: %d.%06d", t2.tv_sec, t2.tv_usec);
981 #endif	/* TIMEPOS */
982 #ifdef	CHECKPOS
983 #ifdef	TIMEPOS
984 	gettimeofday(&t1, 0);
985 #endif
986 	/*
987 	 * Uncomment to force a difference that needs to be found.
988 	 */
989 /*	(*returnoff)++;*/
990 	ckfindpos(wp, SAVE, returnlinkp, returnoff);
991 #ifdef	TIMEPOS
992 	gettimeofday(&t2, 0);
993 	t2.tv_sec -= t1.tv_sec;
994 	t2.tv_usec -= t1.tv_usec;
995 	while (t2.tv_usec < 0) {
996 		t2.tv_usec += 1000000;
997 		t2.tv_sec -= 1;
998 	}
999 	if (t2.tv_usec)
1000 		cdbg("Time: %d.%06d", t2.tv_sec, t2.tv_usec);
1001 #endif	/* TIMEPOS */
1002 #endif	/* CHECKPOS */
1003 #endif	/* FASTPOS */
1004 }
1005 
1006 #ifdef	FASTPOS
1007 #ifdef	CHECKPOS
1008 /*
1009  * Find link pointer and offset in found buffer for 'pos'.
1010  * This is the old version that may be called after the new one and
1011  * checks for diffs.
1012  */
1013 LOCAL void
ckfindpos(wp,pos,returnlinkp,returnoff)1014 ckfindpos(wp, pos, returnlinkp, returnoff)
1015 		ewin_t	*wp;
1016 	register epos_t	pos;
1017 	headr_t		**returnlinkp;	/* To return found link header */
1018 	int		*returnoff;	/* To return found link offset */
1019 {
1020 	register headr_t	*linkp = wp->bhead;
1021 
1022 /*cdbg("findpos(%d) caller %x", pos, getcaller());*/
1023 	if (pos > wp->eof)
1024 		pos = wp->eof;
1025 
1026 	/*
1027 	 * Find header that contains 'pos'
1028 	 */
1029 	while (linkp->size <= pos) {
1030 		pos -= linkp->size;
1031 		linkp = linkp->next;
1032 		if (linkp == 0 || linkp->flags & INVALID) {		/* Paranoia */
1033 			rsttmodes(wp);
1034 			error("pos: %d linkp: %X\n", pos, linkp);
1035 			error("\nBAD POSITION TO BE FOUND\n");
1036 			flushprot(wp);
1037 			abort();
1038 		}
1039 	}
1040 	readybuffer(wp, linkp);
1041 /*cdbg("ckfindpos() returnoff: %d linkp: %X", pos, linkp);*/
1042 	if (*returnlinkp != linkp || *returnoff != pos) {
1043 		cdbg("DIFF: %X o%X %d o%d caller: %X", *returnlinkp, linkp, *returnoff, pos,
1044 			getcaller());
1045 		writeerr(wp, "DIFF: %X o%X %d o%d", *returnlinkp, linkp, *returnoff, pos); sleep(10);
1046 	}
1047 	*returnlinkp = linkp;
1048 	*returnoff = (int)pos;
1049 }
1050 #endif	/* CHECKPOS */
1051 
1052 /*
1053  * Find link pointer and offset in found buffer for 'pos'.
1054  * This is a version that only counts backwards from winlink.
1055  */
1056 LOCAL void
rfindpos(wp,pos,returnlinkp,returnoff)1057 rfindpos(wp, pos, returnlinkp, returnoff)
1058 		ewin_t	*wp;
1059 	register epos_t pos;
1060 	headr_t		**returnlinkp;	/* To return found link header */
1061 	int		*returnoff;	/* To return found link offset */
1062 {
1063 	register headr_t	*linkp;
1064 	register epos_t		linkpos;
1065 epos_t	SAVE = pos;
1066 
1067 #ifdef	TEST_FROM_EOF
1068 	linkp = wp->bhead;
1069 	while (linkp->next)
1070 		linkp = linkp->next;
1071 	linkpos = wp->eof + 1;			/* XXX see buffer.c/initbufs() */
1072 	linkpos -= linkp->size;
1073 #else
1074 	linkp = wp->winlink;
1075 	linkpos = wp->winoff;
1076 #endif
1077 /*	cdbg("last: %X", linkp);*/
1078 
1079 	while (linkpos > pos) {
1080 		linkp = linkp->prev;
1081 		if (linkp == 0 || linkp->flags & INVALID) {		/* Paranoia */
1082 			rsttmodes(wp);
1083 #ifdef	FASTPOS
1084 			error("rfindpos: winlink: %p winpos: %lld winoff: %lld pos: %lld eof: %lld\n",
1085 				(void *)wp->winlink,
1086 				(Llong)wp->winpos, (Llong)wp->winoff,
1087 				(Llong)SAVE, (Llong)wp->eof);
1088 			if (linkp)
1089 				error("linkp: %p linkp->flags = %X\n", (void *)linkp, linkp->flags);
1090 #endif
1091 			error("pos: %lld linkp: %p\n", (Llong)pos, (void *)linkp);
1092 			error("\nBAD POSITION TO BE FOUND\n");
1093 			flushprot(wp);
1094 			abort();
1095 		}
1096 		linkpos -= linkp->size;
1097 	}
1098 	readybuffer(wp, linkp);
1099 
1100 /*	cdbg("pos: %d linkp: %X linksz: %d lpos: %d loff: %d next: %X",*/
1101 /*	pos, linkp, linkp->size, linkpos, pos - linkpos, linkp->next);*/
1102 
1103 	pos -= linkpos;
1104 	*returnlinkp = linkp;
1105 	*returnoff = (int)pos;
1106 }
1107 #endif	/* FASTPOS */
1108