1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 #include "jove.h"
9 #include <errno.h>
10 #include "jctype.h"
11 #include "disp.h"
12 #include "fp.h"
13 #include "ask.h"
14 #include "chars.h"
15 #include "fmt.h"
16 #include "insert.h"
17 #include "macros.h"
18 #include "marks.h"
19 #include "move.h"
20 #include "rec.h"
21 
22 #ifdef MAC
23 # include "mac.h"
24 #endif
25 
26 bool
blnkp(buf)27 blnkp(buf)
28 register char	*buf;
29 {
30 	register char	c;
31 
32 	do {
33 		c = *buf++;
34 	} while (jiswhite(c));
35 	return c == '\0';	/* It's NUL if we got to the end of the Line */
36 }
37 
38 bool
within_indent()39 within_indent()
40 {
41 	register int	i;
42 
43 	i = curchar;
44 	for (;;) {
45 		if (--i < 0)
46 			return YES;
47 		if (!jiswhite(linebuf[i]))
48 			return NO;
49 	}
50 }
51 
52 void
DotTo(line,col)53 DotTo(line, col)
54 LinePtr	line;
55 int	col;
56 {
57 	Bufpos	bp;
58 
59 	bp.p_line = line;
60 	bp.p_char = col;
61 	SetDot(&bp);
62 }
63 
64 /* If bp->p_line is != current line, then save current line.  Then set dot
65    to bp->p_line, and if they weren't equal get that line into linebuf.  */
66 
67 void
SetDot(bp)68 SetDot(bp)
69 register Bufpos	*bp;
70 {
71 	register bool	notequal;
72 
73 	if (bp == NULL)
74 		return;
75 
76 	notequal = bp->p_line != curline;
77 	if (notequal)
78 		lsave();
79 	if (bp->p_line != NULL)
80 		curline = bp->p_line;
81 	if (notequal)
82 		getDOT();
83 	curchar = bp->p_char;
84 	if (curchar > length(curline))
85 		curchar = length(curline);
86 }
87 
88 void
ToLast()89 ToLast()
90 {
91 	SetLine(curbuf->b_last);
92 	Eol();
93 }
94 
95 int	MarkThresh = 22;	/* VAR: moves greater than MarkThresh will SetMark (avg screen size) */
96 
97 private int	line_diff;
98 
99 int
inorder(nextp,char1,endp,char2)100 inorder(nextp, char1, endp, char2)
101 register LinePtr	nextp,
102 		endp;
103 int	char1,
104 	char2;
105 {
106 	register LinePtr	prevp = nextp;
107 
108 	line_diff = 0;
109 	if (nextp == endp)
110 		return char1 < char2;
111 
112 	while (nextp && prevp) {
113 		nextp = nextp->l_next;
114 		prevp = prevp->l_prev;
115 		line_diff += 1;
116 		if (nextp == endp)
117 			return YES;
118 		if (prevp == endp)
119 			return NO;
120 	}
121 	while (nextp!=NULL && nextp!=endp) {
122 		nextp = nextp->l_next;
123 		line_diff += 1;
124 	}
125 	while (prevp!=NULL && prevp!=endp) {
126 		prevp = prevp->l_prev;
127 		line_diff += 1;
128 	}
129 	/* nextp == prevp implies both are NULL: the lines are not ordered */
130 	return nextp==prevp? -1 : nextp==endp;
131 }
132 
133 /* Number of lines (forward OR back) from nextp to endp.
134  * Note: if they are not related, returns 0.
135  */
136 int
LineDist(nextp,endp)137 LineDist(nextp, endp)
138 LinePtr	nextp,
139 		endp;
140 {
141 	return inorder(nextp, 0, endp, 0) == -1? 0 : line_diff;
142 }
143 
144 /* Number of lines forward from "from" to "to"; -1 if not found.
145  * Note: if "to" is (LinePtr)NULL, returns number of lines to end + 1
146  */
147 int
LinesTo(from,to)148 LinesTo(from, to)
149 register LinePtr
150 	from,
151 	to;
152 {
153 	int	n = 0;
154 
155 	for (;;) {
156 		if (from == to)
157 			return n;
158 		if (from == NULL)
159 			return -1;
160 		n += 1;
161 		from = from->l_next;
162 	}
163 }
164 
165 void
PushPntp(line)166 PushPntp(line)
167 register LinePtr	line;
168 {
169 	if (LineDist(curline, line) >= MarkThresh)
170 		set_mark();
171 }
172 
173 void
ToFirst()174 ToFirst()
175 {
176 	SetLine(curbuf->b_first);
177 }
178 
179 int
length(line)180 length(line)
181 LinePtr	line;
182 {
183 	return strlen(lcontents(line));
184 }
185 
186 void
to_word(dir)187 to_word(dir)
188 register int	dir;
189 {
190 	if (dir == FORWARD) {
191 		for(;;) {
192 			register ZXchar	c = ZXC(linebuf[curchar]);
193 
194 			if (c == '\0') {
195 				if (curline->l_next == NULL)
196 					break;	/* failure: out of buffer */
197 				SetLine(curline->l_next);
198 			} else if (jisword(c)) {
199 				break;	/* success */
200 			} else {
201 				curchar += 1;
202 			}
203 		}
204 	} else {
205 		for (;;) {
206 			if (bolp()) {
207 				if (curline->l_prev == NULL)
208 					break;	/* failure: out of buffer */
209 				SetLine(curline->l_prev);
210 				Eol();
211 			} else if (jisword(linebuf[curchar - 1])) {
212 				break;	/* success */
213 			} else {
214 				curchar -= 1;
215 			}
216 		}
217 	}
218 }
219 
220 /* Are there any modified buffers?  Allp means include B_PROCESS
221    buffers in the check. */
222 
223 bool
ModBufs(allp)224 ModBufs(allp)
225 bool	allp;
226 {
227 	register Buffer	*b;
228 
229 	for (b = world; b != NULL; b = b->b_next)
230 		if (b->b_type != B_SCRATCH
231 		&& (b->b_type == B_FILE || allp)
232 		&& IsModified(b))
233 			return YES;
234 	return NO;
235 }
236 
237 char *
filename(b)238 filename(b)
239 register Buffer	*b;
240 {
241 	return b->b_fname ? pr_name(b->b_fname, YES) : "[No file]";
242 }
243 
244 int
min(a,b)245 min(a, b)
246 register int	a,
247 		b;
248 {
249 	return (a < b) ? a : b;
250 }
251 
252 int
max(a,b)253 max(a, b)
254 register int	a,
255 		b;
256 {
257 	return (a > b) ? a : b;
258 }
259 
260 char *
lcontents(line)261 lcontents(line)
262 register LinePtr	line;
263 {
264 	if (line == curline)
265 		return linebuf;
266 	else
267 		return lbptr(line);
268 }
269 
270 char *
ltobuf(line,buf)271 ltobuf(line, buf)
272 LinePtr	line;
273 char	*buf;
274 {
275 	if (line == curline) {
276 		if (buf != linebuf)
277 			strcpy(buf, linebuf);
278 		Jr_Len = strlen(linebuf);
279 	} else
280 		get_line(line->l_dline, buf);
281 	return buf;
282 }
283 
284 void
DOTsave(buf)285 DOTsave(buf)
286 Bufpos *buf;
287 {
288 	buf->p_line = curline;
289 	buf->p_char = curchar;
290 }
291 
292 /* Return YES iff we had to rearrange the order. */
293 
294 bool
fixorder(line1,char1,line2,char2)295 fixorder(line1, char1, line2, char2)
296 register LinePtr	*line1,
297 		*line2;
298 register int	*char1,
299 		*char2;
300 {
301 	LinePtr	tline;
302 	int	tchar;
303 
304 	if (inorder(*line1, *char1, *line2, *char2))
305 		return NO;
306 
307 	tline = *line1;
308 	tchar = *char1;
309 	*line1 = *line2;
310 	*char1 = *char2;
311 	*line2 = tline;
312 	*char2 = tchar;
313 
314 	return YES;
315 }
316 
317 bool
inlist(first,what)318 inlist(first, what)
319 LinePtr	first,
320 		what;
321 {
322 	return LinesTo(first, what) != -1;
323 }
324 
325 /* Make curbuf (un)modified and tell the redisplay code to update the modeline
326    if it will need to be changed. */
327 
328 void
modify()329 modify()
330 {
331 	if (!curbuf->b_modified) {
332 		UpdModLine = YES;
333 		curbuf->b_modified = YES;
334 	}
335 	DOLsave = YES;
336 #ifdef RECOVER
337 	if (curbuf->b_type != B_SCRATCH)
338 		ModCount += 1;
339 #endif
340 }
341 
342 void
unmodify()343 unmodify()
344 {
345 	if (curbuf->b_modified) {
346 		UpdModLine = YES;
347 		curbuf->b_modified = NO;
348 	}
349 }
350 
351 /* Set or clear the divergence flag for `buf'.
352    A buffer that contains a file is considered to have diverged
353    if the file in the filesystem appears to have changed since the
354    last time the buffer was loaded from or saved to that file.
355    If the flag has changed, tell the redisplay code to update the
356    modeline. */
357 
358 void
diverge(buf,d)359 diverge(buf, d)
360 Buffer	*buf;
361 bool	d;
362 {
363 	if (buf->b_diverged != d) {
364 		UpdModLine = YES;
365 		buf->b_diverged = d;
366 	}
367 }
368 
369 int
numcomp(s1,s2)370 numcomp(s1, s2)
371 register char	*s1,
372 		*s2;
373 {
374 	register int	count = 0;
375 
376 	while (*s1 != '\0' && *s1++ == *s2++)
377 		count += 1;
378 	return count;
379 }
380 
381 #ifdef FILENAME_CASEINSENSITIVE
382 int
numcompcase(s1,s2)383 numcompcase(s1, s2)
384 register char	*s1,
385 		*s2;
386 {
387 	register int	count = 0;
388 
389 	while (*s1 != '\0' && CharDowncase(*s1++) == CharDowncase(*s2++))
390 		count += 1;
391 	return count;
392 }
393 #endif
394 
395 char *
copystr(str)396 copystr(str)
397 const char	*str;
398 {
399 	char	*val;
400 
401 	if (str == NULL)
402 		return NULL;
403 	val = emalloc((size_t) (strlen(str) + 1));
404 
405 	strcpy(val, str);
406 	return val;
407 }
408 
409 #ifndef byte_copy
410 void
byte_copy(from,to,count)411 byte_copy(from, to, count)
412 UnivConstPtr	*from;
413 UnivPtr		*to;
414 register size_t	count;
415 {
416 	register const char	*p = from;
417 	register char		*q = to;
418 
419 	if (count != 0) {
420 	    do *q++ = *p++; while (--count != 0);
421 	}
422 }
423 #endif
424 
425 void
len_error(flag)426 len_error(flag)
427 int	flag;
428 {
429 	static const char	mesg[] = "[line too long]";
430 
431 	if (flag == JMP_COMPLAIN)
432 		complain(mesg);
433 	else
434 		error(mesg);
435 }
436 
437 /* Insert num copies of character c at offset atchar in buffer buf of size max */
438 
439 void
ins_c(c,buf,atchar,num,max)440 ins_c(c, buf, atchar, num, max)
441 char	c;
442 char	*buf;
443 int	atchar,
444 	num,
445 	max;
446 {
447 	/* hint to reader: all copying and filling is done right to left */
448 	register char	*from, *to;
449 	int taillen;
450 
451 	if (num <= 0)
452 		return;
453 	from = &buf[atchar];
454 	taillen = *from == '\0'?  1 : strlen(from) + 1;	/* include NUL */
455 	if (atchar + taillen + num > max)
456 		len_error(JMP_COMPLAIN);
457 	from += taillen;
458 	to = from + num;
459 	do {
460 		*--to = *--from;
461 	} while (--taillen != 0);
462 	while (to != from)
463 		*--to = c;
464 }
465 
466 bool
TwoBlank()467 TwoBlank()
468 {
469 	register LinePtr	next = curline->l_next;
470 
471 	return (next != NULL
472 		&& *(lcontents(next)) == '\0'
473 		&& next->l_next != NULL
474 		&& *(lcontents(next->l_next)) == '\0');
475 }
476 
477 void
linecopy(onto,atchar,from)478 linecopy(onto, atchar, from)
479 register char	*onto,
480 		*from;
481 int	atchar;
482 {
483 	register char	*endp = &onto[LBSIZE];
484 
485 	onto += atchar;
486 
487 	do {
488 		if (onto >= endp)
489 			len_error(JMP_ERROR);
490 	} while ((*onto++ = *from++) != '\0');
491 }
492 
493 char *
IOerr(err,file)494 IOerr(err, file)
495 char	*err, *file;
496 {
497 	return sprint("Couldn't %s \"%s\".", err, file);
498 }
499 
500 #ifdef UNIX
501 void
dopipe(p)502 dopipe(p)
503 int	*p;
504 {
505 	if (pipe(p) == -1)
506 		complain("[Pipe failed: %s]", strerror(errno));
507 }
508 
509 void
pipeclose(p)510 pipeclose(p)
511 int	*p;
512 {
513 	(void) close(p[0]);
514 	(void) close(p[1]);
515 }
516 #endif /* UNIX */
517 
518 /* NOSTRICT */
519 
520 UnivPtr
emalloc(size)521 emalloc(size)
522 size_t	size;
523 {
524 	register UnivPtr	ptr;
525 
526 	if ((ptr = malloc(size)) == NULL) {
527 		/* Try garbage collecting lines */
528 		GCchunks();
529 		if ((ptr = malloc(size)) == NULL) {
530 			/* Uh ... Oh screw it! */
531 			error("[Out of memory]");
532 			/* NOTREACHED */
533 		}
534 	}
535 	return ptr;
536 }
537 
538 UnivPtr
erealloc(ptr,size)539 erealloc(ptr, size)
540 UnivPtr	ptr;
541 size_t	size;
542 {
543 	if (ptr == NULL) {
544 		ptr = emalloc(size);
545 	} else if ((ptr = realloc(ptr, size)) == NULL) {
546 		/* no second chance for realloc! */
547 		error("[out of memory]");
548 		/* NOTREACHED */
549 	}
550 	return ptr;
551 }
552 
553 /* Return the basename of file F. */
554 
555 char *
basename(f)556 basename(f)
557 register char	*f;
558 {
559 	register char	*cp;
560 
561 #ifdef MSFILESYSTEM
562 	if (f[0] != '\0'  && f[1] == ':')
563 		f += 2;
564 	if ((cp = strrchr(f, '\\')) != NULL)
565 		f = cp + 1;
566 #endif /* MSFILESYSTEM */
567 	if ((cp = strrchr(f, '/')) != NULL)
568 		f = cp + 1;
569 	return f;
570 }
571 
572 void
push_env(savejmp)573 push_env(savejmp)
574 jmp_buf	savejmp;
575 {
576 	byte_copy((UnivPtr) mainjmp, (UnivPtr) savejmp, sizeof (jmp_buf));
577 }
578 
579 void
pop_env(savejmp)580 pop_env(savejmp)
581 jmp_buf	savejmp;
582 {
583 	byte_copy((UnivPtr) savejmp, (UnivPtr) mainjmp, sizeof (jmp_buf));
584 }
585 
586 /* get the time buf, designated by *timep, from FROM to TO. */
587 char *
get_time(timep,buf,from,to)588 get_time(timep, buf, from, to)
589 time_t	*timep;
590 char	*buf;
591 int	from,
592 	to;
593 {
594 	time_t	now;
595 	char	*cp;
596 
597 	if (timep != NULL)
598 		now = *timep;
599 	else
600 		(void) time(&now);
601 	cp = ctime(&now) + from;
602 	if (to == -1)
603 		cp[strlen(cp) - 1] = '\0';		/* Get rid of \n */
604 	else
605 		cp[to - from] = '\0';
606 	if (buf) {
607 		strcpy(buf, cp);
608 		return buf;
609 	} else {
610 		return cp;
611 	}
612 }
613 
614 /* Are s1 and s2 equal, at least for the first n chars, ignoring case? */
615 
616 bool
caseeqn(s1,s2,n)617 caseeqn(s1, s2, n)
618 register const char	*s1,
619 		*s2;
620 register size_t	n;
621 {
622 	if (s1==NULL || s2==NULL)
623 		return NO;
624 	for (;;) {
625 		if (n == 0)
626 			return YES;
627 		n--;
628 		if (!cind_eq(*s1, *s2++))
629 			return NO;
630 		if (*s1++ == '\0')
631 			return YES;
632 	}
633 }
634 
635 void
null_ncpy(to,from,n)636 null_ncpy(to, from, n)
637 char	*to;
638 const char	*from;
639 size_t	n;
640 {
641 	(void) strncpy(to, from, n);
642 	to[n] = '\0';
643 }
644 
645 bool
sindex(pattern,string)646 sindex(pattern, string)
647 register char	*pattern,
648 		*string;
649 {
650 	register size_t	len = strlen(pattern);
651 
652 	if (len == 0)
653 		return YES;
654 	while (*string != '\0') {
655 		if (*pattern == *string && strncmp(pattern, string, len) == 0)
656 			return YES;
657 		string += 1;
658 	}
659 	return NO;
660 }
661 
662 /* Free, then allocate a block.
663  * Like erealloc, except that the previous contents of the block are lost.
664  */
665 
666 UnivPtr
freealloc(obj,size)667 freealloc(obj, size)
668 register UnivPtr	obj;
669 size_t	size;
670 {
671 	if (obj != NULL)
672 		obj = realloc(obj, size);
673 	if (obj == NULL)
674 		obj = emalloc(size);
675 	return obj;
676 }
677 
678 /* order file names (parameter for qsort) */
679 int
fnamecomp(a,b)680 fnamecomp(a, b)
681 UnivConstPtr	a,
682 	b;
683 {
684 	return strcmp(*(const char **)a, *(const char **)b);
685 }
686 
687 #ifdef NO_STRERROR
688 extern int sys_nerr;
689 extern char *sys_errlist[];
690 
691 /*
692  * Unix version of strerror - map error number to descriptive string.
693  * ANSI systems should have this.
694  */
695 char *
strerror(errnum)696 strerror(errnum)
697 int errnum;
698 {
699 	if (errnum > 0 && errnum < sys_nerr)
700 		return(sys_errlist[errnum]);
701 	return sprint("Error number %d", errnum);
702 }
703 #endif /* NO_STRERROR */
704 
705 /* decode a pair of characters representing \x or ^x */
706 
707 ZXchar
DecodePair(first,second)708 DecodePair(first, second)
709 ZXchar	first, second;
710 {
711 	if (second == EOF || second == '\n')
712 		complain("unexpected end of file after %p", first);
713 	if (first == '^') {
714 		if (second == '?') {
715 			second = DEL;
716 		} else {
717 			ZXchar	us = CharUpcase(second);
718 
719 			if (us < '@' || '_' < us)
720 				complain("unknown control character %p", second);
721 			second = CTL(us);
722 		}
723 	}
724 	return second;
725 }
726