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