joe-3.1jupp31.tgz (die zweite Klappe…)
[alioth/jupp.git] / b.c
1 /* $MirOS: contrib/code/jupp/b.c,v 1.15 2016/10/08 17:42:12 tg Exp $ */
2 /*
3  *      Editor engine
4  *      Copyright
5  *              (C) 1992 Joseph H. Allen
6  *
7  *      This file is part of JOE (Joe's Own Editor)
8  */
9 #include "config.h"
10 #include "types.h"
11
12 #include <unistd.h>
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #ifdef HAVE_PWD_H
18 #include <pwd.h>
19 #endif
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #ifdef HAVE_TIME_H
24 #include <time.h>
25 #endif
26
27 #include "b.h"
28 #include "blocks.h"
29 #include "main.h"
30 #include "path.h"
31 #include "queue.h"
32 #include "rc.h"
33 #include "scrn.h"
34 #include "uerror.h"
35 #include "undo.h"
36 #include "utils.h"
37 #include "va.h"
38 #include "vfile.h"
39 #include "vs.h"
40 #include "utf8.h"
41 #include "charmap.h"
42 #include "w.h"
43
44 #if !HAVE_DECL_CTIME
45 char *ctime(const time_t *);
46 #endif
47 #if !HAVE_DECL_POPEN
48 FILE *popen(const char *, const char *);
49 int pclose(FILE *);
50 #endif
51
52 unsigned char stdbuf[stdsiz];
53
54 int guesscrlf = 0;
55 int guessindent = 0;
56
57 int error;
58 int force = 0;
59 VFILE *vmem;
60
61 const unsigned char *msgs[] = {
62         US "No error",
63         US "New File",
64         US "Error reading file",
65         US "Error seeking file",
66         US "Error opening file",
67         US "Error writing file",
68         US "File on disk is newer"
69 };
70
71 /* Get size of gap (amount of free space) */
72 #define GGAPSZ(hdr) ((hdr)->ehole - (hdr)->hole)
73
74 /* Get number of characters in gap buffer */
75 #define GSIZE(hdr) (SEGSIZ - GGAPSZ(hdr))
76
77 /* Get char from buffer (with jumping around the gap) */
78 #define GCHAR(p) ((p)->ofst >= (p)->hdr->hole ? (p)->ptr[(p)->ofst + GGAPSZ((p)->hdr)] \
79                                               : (p)->ptr[(p)->ofst])
80
81 /* Set position of gap */
82 static void gstgap(H *hdr, unsigned char *ptr, int ofst)
83 {
84         if (ofst > hdr->hole) {
85                 mmove(ptr + hdr->hole, ptr + hdr->ehole, ofst - hdr->hole);
86                 vchanged(ptr);
87         } else if (ofst < hdr->hole) {
88                 mmove(ptr + hdr->ehole - (hdr->hole - ofst), ptr + ofst, hdr->hole - ofst);
89                 vchanged(ptr);
90         }
91         hdr->ehole = ofst + hdr->ehole - hdr->hole;
92         hdr->hole = ofst;
93 }
94
95 /* Insert a block */
96 static void ginsm(H *hdr, unsigned char *ptr, int ofst, unsigned char *blk, int size)
97 {
98         if (ofst != hdr->hole)
99                 gstgap(hdr, ptr, ofst);
100         mmove(ptr + hdr->hole, blk, size);
101         hdr->hole += size;
102         vchanged(ptr);
103 }
104
105 /* Read block */
106 static void grmem(H *hdr, unsigned char *ptr, int ofst, unsigned char *blk, int size)
107 {
108         if (ofst < hdr->hole)
109                 if (size > hdr->hole - ofst) {
110                         mmove(blk, ptr + ofst, hdr->hole - ofst);
111                         mmove(blk + hdr->hole - ofst, ptr + hdr->ehole, size - (hdr->hole - ofst));
112                 } else
113                         mmove(blk, ptr + ofst, size);
114         else
115                 mmove(blk, ptr + ofst + hdr->ehole - hdr->hole, size);
116 }
117
118
119 static H nhdrs = { {&nhdrs, &nhdrs} };
120 static H ohdrs = { {&ohdrs, &ohdrs} };
121
122 /* Header allocation */
123 static H *halloc(void)
124 {
125         H *h;
126
127         if (qempty(H, link, &ohdrs)) {
128                 h = (H *) alitem(&nhdrs, sizeof(H));
129                 h->seg = my_valloc(vmem, (long) SEGSIZ);
130         } else
131                 h = deque_f(H, link, ohdrs.link.next);
132         h->hole = 0;
133         h->ehole = SEGSIZ;
134         h->nlines = 0;
135         izque(H, link, h);
136         return h;
137 }
138
139 static void hfree(H *h)
140 {
141         enquef(H, link, &ohdrs, h);
142 }
143
144 static void hfreechn(H *h)
145 {
146         splicef(H, link, &ohdrs, h);
147 }
148
149
150 static P frptrs = { {&frptrs, &frptrs} };
151
152 /* Pointer allocation */
153 static P *palloc(void)
154 {
155         return alitem(&frptrs, sizeof(P));
156 }
157
158 static void pfree(P *p)
159 {
160         enquef(P, link, &frptrs, p);
161 }
162
163 /* Doubly linked list of buffers and free buffer structures */
164 static B bufs = { {&bufs, &bufs} };
165 static B frebufs = { {&frebufs, &frebufs} };
166
167 B *bnext(void)
168 {
169         B *b;
170
171         do {
172                 b = bufs.link.prev;
173                 deque(B, link, &bufs);
174                 enqueb(B, link, b, &bufs);
175         } while (b->internal);
176         return b;
177 }
178
179 B *bprev(void)
180 {
181         B *b;
182
183         do {
184                 b = bufs.link.next;
185                 deque(B, link, &bufs);
186                 enquef(B, link, b, &bufs);
187         } while (b->internal);
188         return b;
189 }
190
191 /* Make a buffer out of a chain */
192 static B *bmkchn(H *chn, B *prop, long amnt, long nlines)
193 {
194         B *b = alitem(&frebufs, sizeof(B));
195
196         b->undo = undomk(b);
197         if (prop)
198                 b->o = prop->o;
199         else
200                 b->o = pdefault;
201         mset(b->marks, 0, sizeof(b->marks));
202         b->rdonly = 0;
203         b->orphan = 0;
204         b->oldcur = NULL;
205         b->oldtop = NULL;
206         b->backup = 1;
207         b->internal = 1;
208         b->scratch = 0;
209         b->changed = 0;
210         b->count = 1;
211         b->name = NULL;
212         b->er = -3;
213         b->bof = palloc();
214         izque(P, link, b->bof);
215         b->bof->end = 0;
216         b->bof->b = b;
217         b->bof->owner = NULL;
218         b->bof->hdr = chn;
219         b->bof->ptr = vlock(vmem, b->bof->hdr->seg);
220         b->bof->ofst = 0;
221         b->bof->byte = 0;
222         b->bof->line = 0;
223         b->bof->col = 0;
224         b->bof->xcol = 0;
225         b->bof->valcol = 1;
226         b->eof = pdup(b->bof);
227         b->eof->end = 1;
228         vunlock(b->eof->ptr);
229         b->eof->hdr = chn->link.prev;
230         b->eof->ptr = vlock(vmem, b->eof->hdr->seg);
231         b->eof->ofst = GSIZE(b->eof->hdr);
232         b->eof->byte = amnt;
233         b->eof->line = nlines;
234         b->eof->valcol = 0;
235         b->pid = 0;
236         b->out = -1;
237         enquef(B, link, &bufs, b);
238         pcoalesce(b->bof);
239         pcoalesce(b->eof);
240         return b;
241 }
242
243 /* Create an empty buffer */
244 B *bmk(B *prop)
245 {
246         return bmkchn(halloc(), prop, 0L, 0L);
247 }
248
249
250 extern B *errbuf;
251
252 /* Eliminate a buffer */
253 void brm(B *b)
254 {
255         if (b && !--b->count) {
256                 if (b->changed)
257                         abrerr(b->name);
258                 if (b == errbuf)
259                         errbuf = NULL;
260                 if (b->undo)
261                         undorm(b->undo);
262                 hfreechn(b->eof->hdr);
263                 while (!qempty(P, link, b->bof))
264                         prm(b->bof->link.next);
265                 prm(b->bof);
266                 if (b->name)
267                         joe_free(b->name);
268                 demote(B, link, &frebufs, b);
269         }
270 }
271
272 P *poffline(P *p)
273 {
274         if (p->ptr) {
275                 vunlock(p->ptr);
276                 p->ptr = NULL;
277         }
278         return p;
279 }
280
281 P *ponline(P *p)
282 {
283         if (!p->ptr)
284                 p->ptr = vlock(vmem, p->hdr->seg);
285         return p;
286 }
287
288 B *boffline(B *b)
289 {
290         P *p = b->bof;
291
292         do {
293                 poffline(p);
294         } while ((p = p->link.next) != b->bof);
295         return b;
296 }
297
298 B *bonline(B *b)
299 {
300         P *p = b->bof;
301
302         do {
303                 ponline(p);
304         } while ((p = p->link.next) != b->bof);
305         return b;
306 }
307
308 P *pdup(P *p)
309 {
310         P *n = palloc();
311
312         n->end = 0;
313         n->ptr = NULL;
314         n->owner = NULL;
315         enquef(P, link, p, n);
316         return pset(n, p);
317 }
318
319 P *pdupown(P *p, P **o)
320 {
321         P *n = palloc();
322
323         n->end = 0;
324         n->ptr = NULL;
325         n->owner = o;
326         enquef(P, link, p, n);
327         pset(n, p);
328         if (*o)
329                 prm(*o);
330         *o = n;
331         return n;
332 }
333
334 void prm(P *p)
335 {
336         if (!p)
337                 return;
338         if (p->owner)
339                 *p->owner = NULL;
340         if (p->ptr)
341                 vunlock(p->ptr);
342         pfree(deque_f(P, link, p));
343 }
344
345 P *pset(P *n, P *p)
346 {
347         if (n != p) {
348                 n->b = p->b;
349                 n->ofst = p->ofst;
350                 n->hdr = p->hdr;
351                 if (n->ptr)
352                         vunlock(n->ptr);
353                 if (p->ptr) {
354                         n->ptr = p->ptr;
355                         vupcount(n->ptr);
356                 } else
357                         n->ptr = vlock(vmem, n->hdr->seg);
358                 n->byte = p->byte;
359                 n->line = p->line;
360                 n->col = p->col;
361                 n->valcol = p->valcol;
362         }
363         return n;
364 }
365
366 P *p_goto_bof(P *p)
367 {
368         return pset(p, p->b->bof);
369 }
370
371 P *p_goto_eof(P *p)
372 {
373         return pset(p, p->b->eof);
374 }
375
376 /* is p at the beginning of file? */
377 int pisbof(P *p)
378 {
379         return p->hdr == p->b->bof->hdr && !p->ofst;
380 }
381
382 /* is p at the end of file? */
383 int piseof(P *p)
384 {
385         return p->ofst == GSIZE(p->hdr);
386 }
387
388 /* is p at the end of line? */
389 int piseol(P *p)
390 {
391         int c;
392
393         if (piseof(p))
394                 return 1;
395         c = brc(p);
396         if (c == '\n')
397                 return 1;
398         if (p->b->o.crlf)
399                 if (c == '\r') {
400                         P *q = pdup(p);
401
402                         pfwrd(q, 1L);
403                         if (pgetb(q) == '\n') {
404                                 prm(q);
405                                 return 1;
406                         } else
407                                 prm(q);
408                 }
409         return 0;
410 }
411
412 /* is p at the beginning of line? */
413 int pisbol(P *p)
414 {
415         int c;
416
417         if (pisbof(p))
418                 return 1;
419         c = prgetb(p);
420         pgetb(p);
421         return c == '\n';
422 }
423
424 /* is p at the beginning of word? */
425 int pisbow(P *p)
426 {
427         P *q = pdup(p);
428         int c = brc(p);
429         int d = prgetc(q);
430
431         prm(q);
432         if (joe_isalnum_(p->b->o.charmap,c) && (!joe_isalnum_(p->b->o.charmap,d) || pisbof(p)))
433                 return 1;
434         else
435                 return 0;
436 }
437
438 /* is p at the end of word? */
439 int piseow(P *p)
440 {
441         P *q = pdup(p);
442         int d = brc(q);
443         int c = prgetc(q);
444
445         prm(q);
446         if (joe_isalnum_(p->b->o.charmap,c) && (!joe_isalnum_(p->b->o.charmap,d) || piseof(p)))
447                 return 1;
448         else
449                 return 0;
450 }
451
452 /* is p on the blank line (ie. full of spaces/tabs)? */
453 int pisblank(P *p)
454 {
455         P *q = pdup(p);
456
457         p_goto_bol(q);
458         while (joe_isblank(p->b->o.charmap,brc(q)))
459                 pgetb(q);
460         if (piseol(q)) {
461                 prm(q);
462                 return 1;
463         } else {
464                 prm(q);
465                 return 0;
466         }
467 }
468
469 /* is p at end of line or spaces followed by end of line? */
470 int piseolblank(P *p)
471 {
472         P *q = pdup(p);
473
474         while (joe_isblank(p->b->o.charmap,brc(q)))
475                 pgetb(q);
476         if (piseol(q)) {
477                 prm(q);
478                 return 1;
479         } else {
480                 prm(q);
481                 return 0;
482         }
483 }
484
485 /* return column of first nonblank character */
486 long pisindent(P *p)
487 {
488         P *q = pdup(p);
489         long col;
490
491         p_goto_bol(q);
492         while (joe_isblank(p->b->o.charmap,brc(q)))
493                 pgetc(q);
494         col = q->col;
495         prm(q);
496         return col;
497 }
498
499 /* return true if all characters to left of cursor match c */
500
501 int pispure(P *p,int c)
502 {
503         P *q = pdup(p);
504         p_goto_bol(q);
505         while (q->byte!=p->byte)
506                 if (pgetc(q)!=c) {
507                         prm(q);
508                         return 0;
509                 }
510         prm(q);
511         return 1;
512 }
513
514 int pnext(P *p)
515 {
516         if (p->hdr == p->b->eof->hdr) {
517                 p->ofst = GSIZE(p->hdr);
518                 return 0;
519         }
520         p->hdr = p->hdr->link.next;
521         p->ofst = 0;
522         vunlock(p->ptr);
523         p->ptr = vlock(vmem, p->hdr->seg);
524         return 1;
525 }
526
527 int pprev(P *p)
528 {
529         if (p->hdr == p->b->bof->hdr) {
530                 p->ofst = 0;
531                 return 0;
532         }
533         p->hdr = p->hdr->link.prev;
534         p->ofst = GSIZE(p->hdr);
535         vunlock(p->ptr);
536         p->ptr = vlock(vmem, p->hdr->seg);
537         return 1;
538 }
539
540 /* return current byte and move p to the next byte.  column will be unchanged. */
541 int pgetb(P *p)
542 {
543         unsigned char c;
544
545         if (p->ofst == GSIZE(p->hdr))
546                 return NO_MORE_DATA;
547         c = GCHAR(p);
548         if (++p->ofst == GSIZE(p->hdr))
549                 pnext(p);
550         ++p->byte;
551         if (c == '\n') {
552                 ++(p->line);
553                 p->col = 0;
554                 p->valcol = 1;
555         } else if (p->b->o.crlf && c == '\r') {
556                 if (brc(p) == '\n')
557                         return pgetb(p);
558                 else
559                         p->valcol = 0;
560         } else {
561                 p->valcol = 0;
562         }
563         return c;
564 }
565
566 /* return current character and move p to the next character.  column will be updated if it was valid. */
567 int pgetc(P *p)
568 {
569         if (p->b->o.charmap->type) {
570                 int val, c, n, wid;
571                 /* int m, oc; */
572
573                 val = p->valcol;        /* Remember if column number was valid */
574                 c = pgetb(p);           /* Get first byte */
575                 /* oc = c; */
576
577                 if (c==NO_MORE_DATA)
578                         return c;
579
580                 if ((c&0xE0)==0xC0) { /* Two bytes */
581                         n = 1;
582                         c &= 0x1F;
583                 } else if ((c&0xF0)==0xE0) { /* Three bytes */
584                         n = 2;
585                         c &= 0x0F;
586                 } else if ((c&0xF8)==0xF0) { /* Four bytes */
587                         n = 3;
588                         c &= 0x07;
589                 } else if ((c&0xFC)==0xF8) { /* Five bytes */
590                         n = 4;
591                         c &= 0x03;
592                 } else if ((c&0xFE)==0xFC) { /* Six bytes */
593                         n = 5;
594                         c &= 0x01;
595                 } else if ((c&0x80)==0x00) { /* One byte */
596                         n = 0;
597                 } else { /* 128-191, 254, 255: Not a valid UTF-8 start character */
598                         n = 0;
599                         c = 0x1000FFFE;
600                         /* c -= 384; */
601                 }
602
603                 /* m = n; */
604
605                 if (n) {
606                         int d;
607
608                         do {
609                                 d = brc(p);
610                                 if (d == NO_MORE_DATA || (d&0xC0)!=0x80)
611                                         break;
612                                 pgetb(p);
613                                 c = ((c<<6)|(d&0x3F));
614                         } while (--n);
615                         if (n) { /* FIXME: there was a bad UTF-8 sequence */
616                                 /* How to represent this? */
617                                 /* pbkwd(p,m-n);
618                                 c = oc - 384; */
619                                 c = d == NO_MORE_DATA ? 0x1000FFFF : 0x1000FFFE;
620                                 wid = 1;
621                         } else if (val)
622                                 wid = joe_wcwidth(1,c);
623                 } else {
624                         wid = 1;
625                 }
626
627                 if (val) { /* Update column no. if it was valid to start with */
628                         p->valcol = 1;
629                         if (c=='\t')
630                                 p->col += (p->b->o.tab) - (p->col) % (p->b->o.tab);
631                         else if (c=='\n')
632                                 p->col = 0;
633                         else
634                                 p->col += wid;
635                 }
636
637                 return c;
638         } else {
639                 unsigned char c;
640
641                 if (p->ofst == GSIZE(p->hdr))
642                         return NO_MORE_DATA;
643                 c = GCHAR(p);
644                 if (++p->ofst == GSIZE(p->hdr))
645                         pnext(p);
646                 ++p->byte;
647
648                 if (c == '\n') {
649                         ++(p->line);
650                         p->col = 0;
651                         p->valcol = 1;
652                 } else if (p->b->o.crlf && c == '\r') {
653                         if (brc(p) == '\n')
654                                 return pgetc(p);
655                         else
656                                 ++p->col;
657                 } else {
658                         if (c == '\t')
659                                 p->col += (p->b->o.tab) - (p->col) % (p->b->o.tab);
660                         else
661                                 ++(p->col);
662                 }
663                 return c;
664         }
665 }
666
667 /* move p n characters forward */
668 P *pfwrd(P *p, long n)
669 {
670         if (!n)
671                 return p;
672         p->valcol = 0;
673         do {
674                 if (p->ofst == GSIZE(p->hdr))
675                         do {
676                                 if (!p->ofst) {
677                                         p->byte += GSIZE(p->hdr);
678                                         n -= GSIZE(p->hdr);
679                                         p->line += p->hdr->nlines;
680                                 }
681                                 if (!pnext(p))
682                                         return NULL;
683                         } while (n > GSIZE(p->hdr));
684                 if (GCHAR(p) == '\n')
685                         ++p->line;
686                 ++p->byte;
687                 ++p->ofst;
688         } while (--n);
689         if (p->ofst == GSIZE(p->hdr))
690                 pnext(p);
691         return p;
692 }
693
694 /* move p to the previous byte: does not take into account -crlf mode */
695 static int prgetb1(P *p)
696 {
697         unsigned char c;
698
699         if (!p->ofst)
700                 if (!pprev(p))
701                         return NO_MORE_DATA;
702         --p->ofst;
703         c = GCHAR(p);
704         --p->byte;
705         p->valcol = 0;
706         if (c == '\n')
707                 --p->line;
708         return c;
709 }
710
711 /* move p to the previous byte */
712 int prgetb(P *p)
713 {
714         int c = prgetb1(p);
715
716         if (p->b->o.crlf && c == '\n') {
717                 c = prgetb1(p);
718                 if (c == '\r')
719                         return '\n';
720                 if (c != NO_MORE_DATA)
721                         pgetb(p);
722                 c = '\n';
723         }
724         return c;
725 }
726
727 /* move p to the previous character (try to keep col updated) */
728 int prgetc(P *p)
729 {
730         if (p->b->o.charmap->type) {
731
732                 if (pisbol(p))
733                         return prgetb(p);
734                 else {
735                         P *q = pdup(p);
736                         P *r;
737                         p_goto_bol(q);
738                         r = pdup(q);
739                         while (q->byte<p->byte) {
740                                 pset(r, q);
741                                 pgetc(q);
742                         }
743                         pset(p,r);
744                         prm(r);
745                         prm(q);
746                         return brch(p);
747                 }
748
749 #if 0
750                 int d = 0;
751                 int c;
752                 int n = 0;
753                 int val = p->valcol;
754                 for(;;) {
755                         c = prgetb(p);
756                         if (c == NO_MORE_DATA)
757                                 return NO_MORE_DATA;
758                         else if ((c&0xC0)==0x80) {
759                                 d |= ((c&0x3F)<<n);
760                                 n += 6;
761                         } else if ((c&0x80)==0x00) { /* One char */
762                                 d = c;
763                                 break;
764                         } else if ((c&0xE0)==0xC0) { /* Two chars */
765                                 d |= ((c&0x1F)<<n);
766                                 break;
767                         } else if ((c&0xF0)==0xE0) { /* Three chars */
768                                 d |= ((c&0x0F)<<n);
769                                 break;
770                         } else if ((c&0xF8)==0xF0) { /* Four chars */
771                                 d |= ((c&0x07)<<n);
772                                 break;
773                         } else if ((c&0xFC)==0xF8) { /* Five chars */
774                                 d |= ((c&0x03)<<n);
775                                 break;
776                         } else if ((c&0xFE)==0xFC) { /* Six chars */
777                                 d |= ((c&0x01)<<n);
778                                 break;
779                         } else { /* FIXME: Invalid (0xFE or 0xFF found) */
780                                 break;
781                         }
782                 }
783
784                 if (val && c!='\t' && c!='\n') {
785                         p->valcol = 1;
786                         p->col -= joe_wcwidth(1,d);
787                 }
788                 
789                 return d;
790 #endif
791         }
792         else {
793                 return prgetb(p);
794         }
795 }
796
797 /* move p n characters backwards */
798 P *pbkwd(P *p, long n)
799 {
800         if (!n)
801                 return p;
802         p->valcol = 0;
803         do {
804                 if (!p->ofst)
805                         do {
806                                 if (p->ofst) {
807                                         p->byte -= p->ofst;
808                                         n -= p->ofst;
809                                         p->line -= p->hdr->nlines;
810                                 }
811                                 if (!pprev(p))
812                                         return NULL;
813                         } while (n > GSIZE(p->hdr));
814                 --p->ofst;
815                 --p->byte;
816                 if (GCHAR(p) == '\n')
817                         --p->line;
818         } while (--n);
819         return p;
820 }
821
822 /* move p n characters forwards/backwards according to loc */
823 P *pgoto(P *p, long loc)
824 {
825         if (loc > p->byte)
826                 pfwrd(p, loc - p->byte);
827         else if (loc < p->byte)
828                 pbkwd(p, p->byte - loc);
829         return p;
830 }
831
832 /* make p->col valid */
833 P *pfcol(P *p)
834 {
835         long pos = p->byte;
836
837         p_goto_bol(p);
838         while (p->byte < pos)
839                 pgetc(p);
840         return p;
841 }
842
843 /* move p to the beginning of line */
844 P *p_goto_bol(P *p)
845 {
846         if (pprevl(p))
847                 pgetb(p);
848         p->col = 0;
849         p->valcol = 1;
850         return p;
851 }
852
853 /* move p to the indentation point */
854 P *p_goto_indent(P *p, int c)
855 {
856         int d;
857         p_goto_bol(p);
858         while ((d=brc(p)), d==c || ((c==' ' || c=='\t') && (d==' ' || d=='\t')))
859                 pgetc(p);
860         return p;
861 }
862
863 /* move p to the end of line */
864 P *p_goto_eol(P *p)
865 {
866         if (p->b->o.crlf || p->b->o.charmap->type)
867                 while (!piseol(p))
868                         pgetc(p);
869         else
870                 while (p->ofst != GSIZE(p->hdr)) {
871                         unsigned char c;
872
873                         c = GCHAR(p);
874                         if (c == '\n')
875                                 break;
876                         else {
877                                 ++p->byte;
878                                 ++p->ofst;
879                                 if (c == '\t')
880                                         p->col += p->b->o.tab - p->col % p->b->o.tab;
881                                 else
882                                         ++p->col;
883                                 if (p->ofst == GSIZE(p->hdr))
884                                         pnext(p);
885                         }
886                 }
887         return p;
888 }
889
890 /* move p to the beginning of next line */
891 P *pnextl(P *p)
892 {
893         int c;
894
895         do {
896                 if (p->ofst == GSIZE(p->hdr))
897                         do {
898                                 p->byte += GSIZE(p->hdr) - p->ofst;
899                                 if (!pnext(p))
900                                         return NULL;
901                         } while (!p->hdr->nlines);
902                 c = GCHAR(p);
903                 ++p->byte;
904                 ++p->ofst;
905         } while (c != '\n');
906         ++p->line;
907         p->col = 0;
908         p->valcol = 1;
909         if (p->ofst == GSIZE(p->hdr))
910                 pnext(p);
911         return p;
912 }
913
914 /* move p to the end of previous line */
915 P *pprevl(P *p)
916 {
917         int c;
918
919         p->valcol = 0;
920         do {
921                 if (!p->ofst)
922                         do {
923                                 p->byte -= p->ofst;
924                                 if (!pprev(p))
925                                         return NULL;
926                         } while (!p->hdr->nlines);
927                 --p->ofst;
928                 --p->byte;
929                 c = GCHAR(p);
930         } while (c != '\n');
931         --p->line;
932         if (p->b->o.crlf && c == '\n') {
933                 int k = prgetb1(p);
934
935                 if (k != '\r' && k != NO_MORE_DATA)
936                         pgetb(p);
937         }
938         return p;
939 }
940
941 /* move p to the given 'line' line */
942 P *pline(P *p, long line)
943 {
944         if (line > p->b->eof->line) {
945                 pset(p, p->b->eof);
946                 return p;
947         }
948         if (line < labs(p->line - line))
949                 pset(p, p->b->bof);
950         if (labs(p->b->eof->line - line) < labs(p->line - line))
951                 pset(p, p->b->eof);
952         if (p->line == line) {
953                 p_goto_bol(p);
954                 return p;
955         }
956         while (line > p->line)
957                 pnextl(p);
958         if (line < p->line) {
959                 while (line < p->line)
960                         pprevl(p);
961                 p_goto_bol(p);
962         }
963         return p;
964 }
965
966 /* move p to the given 'goalcol' column */
967 /* lands at exact column or on character which would cause us to go past goalcol */
968 P *pcol(P *p, long goalcol)
969 {
970         p_goto_bol(p);
971         if(p->b->o.charmap->type) {
972                 do {
973                         int c;
974                         int wid;
975
976                         c = brch(p);
977
978                         if (c == NO_MORE_DATA)
979                                 break;
980
981                         if (c == '\n')
982                                 break;
983
984                         if (p->b->o.crlf && c == '\r' && piseol(p))
985                                 break;
986
987                         if (c == '\t')
988                                 wid = p->b->o.tab - p->col % p->b->o.tab;
989                         else
990                                 wid = joe_wcwidth(1,c);
991
992                         if (p->col + wid > goalcol)
993                                 break;
994
995                         pgetc(p);
996                 } while (p->col != goalcol);
997         } else {
998                 do {
999                         unsigned char c;
1000                         int wid;
1001
1002                         if (p->ofst == GSIZE(p->hdr))
1003                                 break;
1004                         c = GCHAR(p);
1005                         if (c == '\n')
1006                                 break;
1007                         if (p->b->o.crlf && c == '\r' && piseol(p))
1008                                 break;
1009                         if (c == '\t')
1010                                 wid = p->b->o.tab - p->col % p->b->o.tab;
1011                         else
1012                                 wid = 1;
1013                         if (p->col + wid > goalcol)
1014                                 break;
1015                         if (++p->ofst == GSIZE(p->hdr))
1016                                 pnext(p);
1017                         ++p->byte;
1018                         p->col += wid;
1019                 } while (p->col != goalcol);
1020         }
1021         return p;
1022 }
1023
1024 /* Move to goal column, then skip backwards to just after first non-whitespace character */
1025 P *pcolwse(P *p, long goalcol)
1026 {
1027         int c;
1028
1029         pcol(p, goalcol);
1030         do {
1031                 c = prgetc(p);
1032         } while (c == ' ' || c == '\t');
1033         if (c != NO_MORE_DATA)
1034                 pgetc(p);
1035         return p;
1036 }
1037
1038 /* Move p to goalcol: stops after first character which equals or exceeds goal col (unlike
1039    pcol() which will stops before character which would exceed goal col) */
1040 P *pcoli(P *p, long goalcol)
1041 {
1042         p_goto_bol(p);
1043         if (p->b->o.charmap->type) {
1044                 while (p->col < goalcol) {
1045                         int c;
1046                         c = brc(p);
1047
1048                         if (c == NO_MORE_DATA)
1049                                 break;
1050
1051                         if (c == '\n')
1052                                 break;
1053
1054                         if (p->b->o.crlf && c=='\r' && piseol(p))
1055                                 break;
1056
1057                         pgetc(p);
1058                 }
1059         } else {
1060                 while (p->col < goalcol) {
1061                         unsigned char c;
1062
1063                         if (p->ofst == GSIZE(p->hdr))
1064                                 break;
1065                         c = GCHAR(p);
1066                         if (c == '\n')
1067                                 break;
1068
1069                         if (p->b->o.crlf && c == '\r' && piseol(p))
1070                                 break;
1071
1072                         if (c == '\t')
1073                                 p->col += p->b->o.tab - p->col % p->b->o.tab;
1074                         else
1075                                 ++p->col;
1076                         if (++p->ofst == GSIZE(p->hdr))
1077                                 pnext(p);
1078                         ++p->byte;
1079                 }
1080         }
1081         return p;
1082 }
1083
1084 /* fill space between curent column and 'to' column with tabs/spaces */
1085 void pfill(P *p, long to, int usetabs)
1086 {
1087         if (usetabs=='\t')
1088                 while (piscol(p) < to)
1089                         if (p->col + p->b->o.tab - p->col % p->b->o.tab <= to) {
1090                                 binsc(p, '\t');
1091                                 pgetc(p);
1092                         } else {
1093                                 binsc(p, ' ');
1094                                 pgetc(p);
1095                         }
1096         else
1097                 while (piscol(p) < to) {
1098                         binsc(p, usetabs);
1099                         pgetc(p);
1100                 }
1101 }
1102
1103 /* delete sequence of whitespaces - backwards */
1104 void pbackws(P *p)
1105 {
1106         int c;
1107         P *q = pdup(p);
1108
1109         do {
1110                 c = prgetc(q);
1111         } while (c == ' ' || c == '\t');
1112         if (c != NO_MORE_DATA)
1113                 pgetc(q);
1114         bdel(q, p);
1115         prm(q);
1116 }
1117
1118 static int frgetc(P *p)
1119 {
1120         if (!p->ofst)
1121                 pprev(p);
1122         --p->ofst;
1123         return GCHAR(p);
1124 }
1125
1126 static void ffwrd(P *p, int n)
1127 {
1128         while (n > GSIZE(p->hdr) - p->ofst) {
1129                 n -= GSIZE(p->hdr) - p->ofst;
1130                 if (!pnext(p))
1131                         return;
1132         }
1133         if ((p->ofst += n) == GSIZE(p->hdr))
1134                 pnext(p);
1135 }
1136
1137 /* forward find pattern 's' in text pointed by 'p' (Boyer-Moore algorithm) */
1138 static P *ffind(P *p, unsigned char *s, int len)
1139 {
1140         long amnt = p->b->eof->byte - p->byte;
1141         int x;
1142         unsigned char table[256], c;
1143
1144         if (len > amnt)
1145                 return NULL;
1146         if (!len)
1147                 return p;
1148         p->valcol = 0;
1149         mset(table, 255, 256);
1150         for (x = 0; x != len - 1; ++x)
1151                 table[s[x]] = x;
1152         ffwrd(p, len);
1153         amnt -= len;
1154         x = len;
1155         do {
1156                 if ((c = frgetc(p)) != s[--x]) {
1157                         if (table[c] == 255) {
1158                                 ffwrd(p, len + 1);
1159                                 amnt -= x + 1;
1160                         } else if (x <= table[c]) {
1161                                 ffwrd(p, len - x + 1);
1162                                 --amnt;
1163                         } else {
1164                                 ffwrd(p, len - table[c]);
1165                                 amnt -= x - table[c];
1166                         }
1167                         if (amnt < 0)
1168                                 return NULL;
1169                         else
1170                                 x = len;
1171                 }
1172         } while (x);
1173         return p;
1174 }
1175
1176 /* forward find (case insensitive) pattern 's' in text pointed by 'p' (Boyer-Moore algorithm) */
1177 static P *fifind(P *p, unsigned char *s, int len)
1178 {
1179         long amnt = p->b->eof->byte - p->byte;
1180         int x;
1181         struct charmap *map = p->b->o.charmap;
1182         unsigned char table[256], c;
1183
1184         if (len > amnt)
1185                 return NULL;
1186         if (!len)
1187                 return p;
1188         p->valcol = 0;
1189         mset(table, 255, 256);
1190         for (x = 0; x != len - 1; ++x)
1191                 table[s[x]] = x;
1192         ffwrd(p, len);
1193         amnt -= len;
1194         x = len;
1195         do {
1196                 if ((c = joe_tolower(map,frgetc(p))) != s[--x]) {
1197                         if (table[c] == 255) {
1198                                 ffwrd(p, len + 1);
1199                                 amnt -= x + 1;
1200                         } else if (x <= table[c]) {
1201                                 ffwrd(p, len - x + 1);
1202                                 --amnt;
1203                         } else {
1204                                 ffwrd(p, len - table[c]);
1205                                 amnt -= x - table[c];
1206                         }
1207                         if (amnt < 0)
1208                                 return NULL;
1209                         else
1210                                 x = len;
1211                 }
1212         } while (x);
1213         return p;
1214 }
1215
1216 /* move cursor p to q's position and set p's col, line, ofst, byte etc. accordingly */
1217 /* same as rgetto() but p is before q */
1218 static P *getto(P *p, P *q)
1219 {
1220         while (p->hdr != q->hdr || p->ofst != q->ofst) {
1221                 if (GCHAR(p) == '\n')
1222                         ++p->line;
1223                 ++p->byte;
1224                 ++p->ofst;
1225                 if (p->ofst == GSIZE(p->hdr))
1226                         pnext(p);
1227                 while (!p->ofst && p->hdr != q->hdr) {
1228                         p->byte += GSIZE(p->hdr);
1229                         p->line += p->hdr->nlines;
1230                         pnext(p);
1231                 }
1232         }
1233         return p;
1234 }
1235
1236 /* find forward substring s in text pointed by p and set p after found substring */
1237 P *pfind(P *p, unsigned char *s, int len)
1238 {
1239         P *q = pdup(p);
1240
1241         if (ffind(q, s, len)) {
1242                 getto(p, q);
1243                 prm(q);
1244                 return p;
1245         } else {
1246                 prm(q);
1247                 return NULL;
1248         }
1249 }
1250
1251 /* same as pfind() but case insensitive */
1252 P *pifind(P *p, unsigned char *s, int len)
1253 {
1254         P *q = pdup(p);
1255
1256         if (fifind(q, s, len)) {
1257                 getto(p, q);
1258                 prm(q);
1259                 return p;
1260         } else {
1261                 prm(q);
1262                 return NULL;
1263         }
1264 }
1265
1266 static void fbkwd(P *p, int n)
1267 {
1268         while (n > p->ofst) {
1269                 n -= p->ofst;
1270                 if (!pprev(p))
1271                         return;
1272         }
1273         if (p->ofst >= n)
1274                 p->ofst -= n;
1275         else
1276                 p->ofst = 0;
1277 }
1278
1279 static int fpgetc(P *p)
1280 {
1281         int c;
1282
1283         if (p->ofst == GSIZE(p->hdr))
1284                 return NO_MORE_DATA;
1285         c = GCHAR(p);
1286         if (++p->ofst == GSIZE(p->hdr))
1287                 pnext(p);
1288         return c;
1289 }
1290
1291 /* backward find pattern 's' in text pointed by 'p' (Boyer-Moore algorithm) */
1292 static P *frfind(P *p, unsigned char *s, int len)
1293 {
1294         long amnt = p->byte;
1295         int x;
1296         unsigned char table[256], c;
1297
1298         if (len > p->b->eof->byte - p->byte) {
1299                 x = len - (p->b->eof->byte - p->byte);
1300                 if (amnt < x)
1301                         return NULL;
1302                 amnt -= x;
1303                 fbkwd(p, x);
1304         }
1305         if (!len)
1306                 return p;
1307         p->valcol = 0;
1308         mset(table, 255, 256);
1309         for (x = len; --x; table[s[x]] = len - x - 1) ;
1310         x = 0;
1311         do {
1312                 if ((c = fpgetc(p)) != s[x++]) {
1313                         if (table[c] == 255) {
1314                                 fbkwd(p, len + 1);
1315                                 amnt -= len - x + 1;
1316                         } else if (len - table[c] <= x) {
1317                                 fbkwd(p, x + 1);
1318                                 --amnt;
1319                         } else {
1320                                 fbkwd(p, len - table[c]);
1321                                 amnt -= len - table[c] - x;
1322                         }
1323                         if (amnt < 0)
1324                                 return NULL;
1325                         else
1326                                 x = 0;
1327                 }
1328         } while (x != len);
1329         fbkwd(p, len);
1330         return p;
1331 }
1332
1333 /* backward find (case insensitive) pattern 's' in text pointed by 'p' (Boyer-Moore algorithm) */
1334 static P *frifind(P *p, unsigned char *s, int len)
1335 {
1336         long amnt = p->byte;
1337         int x;
1338         unsigned char table[256], c;
1339         struct charmap *map = p->b->o.charmap;
1340
1341         if (len > p->b->eof->byte - p->byte) {
1342                 x = len - (p->b->eof->byte - p->byte);
1343                 if (amnt < x)
1344                         return NULL;
1345                 amnt -= x;
1346                 fbkwd(p, x);
1347         }
1348         if (!len)
1349                 return p;
1350         p->valcol = 0;
1351         mset(table, 255, 256);
1352         for (x = len; --x; table[s[x]] = len - x - 1) ;
1353         x = 0;
1354         do {
1355                 if ((c = joe_tolower(map,fpgetc(p))) != s[x++]) {
1356                         if (table[c] == 255) {
1357                                 fbkwd(p, len + 1);
1358                                 amnt -= len - x + 1;
1359                         } else if (len - table[c] <= x) {
1360                                 fbkwd(p, x + 1);
1361                                 --amnt;
1362                         } else {
1363                                 fbkwd(p, len - table[c]);
1364                                 amnt -= len - table[c] - x;
1365                         }
1366                         if (amnt < 0)
1367                                 return NULL;
1368                         else
1369                                 x = 0;
1370                 }
1371         } while (x != len);
1372         fbkwd(p, len);
1373         return p;
1374 }
1375
1376 /* move cursor p to q's position and set p's col, line, ofst, byte etc. accordingly */
1377 /* same as getto() but q is before p */
1378 static P *rgetto(P *p, P *q)
1379 {
1380         while (p->hdr != q->hdr || p->ofst != q->ofst) {
1381                 if (!p->ofst)
1382                         do {
1383                                 if (p->ofst) {
1384                                         p->byte -= p->ofst;
1385                                         p->line -= p->hdr->nlines;
1386                                 }
1387                                 pprev(p);
1388                         } while (p->hdr != q->hdr);
1389                 --p->ofst;
1390                 --p->byte;
1391                 if (GCHAR(p) == '\n')
1392                         --p->line;
1393         }
1394         return p;
1395 }
1396
1397 /* find backward substring s in text pointed by p and set p on the first of found substring */
1398 P *prfind(P *p, unsigned char *s, int len)
1399 {
1400         P *q = pdup(p);
1401
1402         if (frfind(q, s, len)) {
1403                 rgetto(p, q);
1404                 prm(q);
1405                 return p;
1406         } else {
1407                 prm(q);
1408                 return NULL;
1409         }
1410 }
1411
1412 /* same as prfind() but case insensitive */
1413 P *prifind(P *p, unsigned char *s, int len)
1414 {
1415         P *q = pdup(p);
1416
1417         if (frifind(q, s, len)) {
1418                 rgetto(p, q);
1419                 prm(q);
1420                 return p;
1421         } else {
1422                 prm(q);
1423                 return NULL;
1424         }
1425 }
1426
1427 /* copy text between 'from' and 'to' into new buffer */
1428 B *bcpy(P *from, P *to)
1429 {
1430         H anchor, *l;
1431         unsigned char *ptr;
1432         P *q;
1433
1434         if (from->byte >= to->byte)
1435                 return bmk(from->b);
1436
1437         q = pdup(from);
1438         izque(H, link, &anchor);
1439
1440         if (q->hdr == to->hdr) {
1441                 l = halloc();
1442                 ptr = vlock(vmem, l->seg);
1443                 if (q->ofst != q->hdr->hole)
1444                         gstgap(q->hdr, q->ptr, q->ofst);
1445                 l->nlines = mcnt(q->ptr + q->hdr->ehole, '\n', l->hole = to->ofst - q->ofst);
1446                 mmove(ptr, q->ptr + q->hdr->ehole, l->hole);
1447                 vchanged(ptr);
1448                 vunlock(ptr);
1449                 enqueb(H, link, &anchor, l);
1450         } else {
1451                 l = halloc();
1452                 ptr = vlock(vmem, l->seg);
1453                 if (q->ofst != q->hdr->hole)
1454                         gstgap(q->hdr, q->ptr, q->ofst);
1455                 l->nlines = mcnt(q->ptr + q->hdr->ehole, '\n', l->hole = SEGSIZ - q->hdr->ehole);
1456                 mmove(ptr, q->ptr + q->hdr->ehole, l->hole);
1457                 vchanged(ptr);
1458                 vunlock(ptr);
1459                 enqueb(H, link, &anchor, l);
1460                 pnext(q);
1461                 while (q->hdr != to->hdr) {
1462                         l = halloc();
1463                         ptr = vlock(vmem, l->seg);
1464                         l->nlines = q->hdr->nlines;
1465                         mmove(ptr, q->ptr, q->hdr->hole);
1466                         mmove(ptr + q->hdr->hole, q->ptr + q->hdr->ehole, SEGSIZ - q->hdr->ehole);
1467                         l->hole = GSIZE(q->hdr);
1468                         vchanged(ptr);
1469                         vunlock(ptr);
1470                         enqueb(H, link, &anchor, l);
1471                         pnext(q);
1472                 }
1473                 if (to->ofst) {
1474                         l = halloc();
1475                         ptr = vlock(vmem, l->seg);
1476                         if (to->ofst != to->hdr->hole)
1477                                 gstgap(to->hdr, to->ptr, to->ofst);
1478                         l->nlines = mcnt(to->ptr, '\n', to->ofst);
1479                         mmove(ptr, to->ptr, l->hole = to->ofst);
1480                         vchanged(ptr);
1481                         vunlock(ptr);
1482                         enqueb(H, link, &anchor, l);
1483                 }
1484         }
1485
1486         l = anchor.link.next;
1487         deque(H, link, &anchor);
1488         prm(q);
1489
1490         return bmkchn(l, from->b, to->byte - from->byte, to->line - from->line);
1491 }
1492
1493 /* Coalesce small blocks into a single larger one */
1494 void pcoalesce(P *p)
1495 {
1496         if (p->hdr != p->b->eof->hdr && GSIZE(p->hdr) + GSIZE(p->hdr->link.next) <= SEGSIZ - SEGSIZ / 4) {
1497                 H *hdr = p->hdr->link.next;
1498                 unsigned char *ptr = vlock(vmem, hdr->seg);
1499                 int osize = GSIZE(p->hdr);
1500                 int size = GSIZE(hdr);
1501                 P *q;
1502
1503                 gstgap(hdr, ptr, size);
1504                 ginsm(p->hdr, p->ptr, GSIZE(p->hdr), ptr, size);
1505                 p->hdr->nlines += hdr->nlines;
1506                 vunlock(ptr);
1507                 hfree(deque_f(H, link, hdr));
1508                 for (q = p->link.next; q != p; q = q->link.next)
1509                         if (q->hdr == hdr) {
1510                                 q->hdr = p->hdr;
1511                                 if (q->ptr) {
1512                                         vunlock(q->ptr);
1513                                         q->ptr = vlock(vmem, q->hdr->seg);
1514                                 }
1515                                 q->ofst += osize;
1516                         }
1517         }
1518         if (p->hdr != p->b->bof->hdr && GSIZE(p->hdr) + GSIZE(p->hdr->link.prev) <= SEGSIZ - SEGSIZ / 4) {
1519                 H *hdr = p->hdr->link.prev;
1520                 unsigned char *ptr = vlock(vmem, hdr->seg);
1521                 int size = GSIZE(hdr);
1522                 P *q;
1523
1524                 gstgap(hdr, ptr, size);
1525                 ginsm(p->hdr, p->ptr, 0, ptr, size);
1526                 p->hdr->nlines += hdr->nlines;
1527                 vunlock(ptr);
1528                 hfree(deque_f(H, link, hdr));
1529                 p->ofst += size;
1530                 for (q = p->link.next; q != p; q = q->link.next)
1531                         if (q->hdr == hdr) {
1532                                 q->hdr = p->hdr;
1533                                 if (q->ptr)
1534                                         vunlock(q->ptr);
1535                                 q->ptr = vlock(vmem, q->hdr->seg);
1536                         } else if (q->hdr == p->hdr)
1537                                 q->ofst += size;
1538         }
1539 }
1540
1541 /* Delete the text between two pointers from a buffer and return it in a new
1542  * buffer.
1543  *
1544  * This routine calls these functions:
1545  *  gstgap      - to position gaps
1546  *  halloc      - to allocate new header/segment pairs
1547  *  vlock       - virtual memory routines
1548  *  vunlock
1549  *  vchanged
1550  *  vupcount
1551  *  mcnt        - to count NLs
1552  *  snip        - queue routines
1553  *  enqueb
1554  *  splicef
1555  *  scrdel      - to tell screen update to scroll when NLs are deleted
1556  *  bmkchn      - to make a buffer out of a chain
1557  */
1558
1559 /* This is only to be used for bdel() */
1560 static B *bcut(P *from, P *to)
1561 {
1562         H *h,                   /* The deleted text */
1563         *i;
1564         unsigned char *ptr;
1565         P *p;
1566         long nlines;            /* No. EOLs to delete */
1567         long amnt;              /* No. bytes to delete */
1568         int toamnt;             /* Amount to delete from segment in 'to' */
1569         int bofmove = 0;        /* Set if bof got deleted */
1570
1571         if (!(amnt = to->byte - from->byte))
1572                 return NULL;    /* ...nothing to delete */
1573
1574         nlines = to->line - from->line;
1575
1576         if (from->hdr == to->hdr) {     /* Delete is within a single segment */
1577                 /* Move gap to deletion point */
1578                 if (from->ofst != from->hdr->hole)
1579                         gstgap(from->hdr, from->ptr, from->ofst);
1580
1581                 /* Store the deleted text */
1582                 h = halloc();
1583                 ptr = vlock(vmem, h->seg);
1584                 mmove(ptr, from->ptr + from->hdr->ehole, (int) amnt);
1585                 h->hole = amnt;
1586                 h->nlines = nlines;
1587                 vchanged(ptr);
1588                 vunlock(ptr);
1589
1590                 /* Delete */
1591                 from->hdr->ehole += amnt;
1592                 from->hdr->nlines -= nlines;
1593
1594                 toamnt = amnt;
1595         } else {                /* Delete crosses segments */
1596                 H *a;
1597
1598                 if ((toamnt = to->ofst) != 0) {
1599                         /* Delete beginning of to */
1600                         /* Move gap to deletion point */
1601                         /* To could be deleted if it's at the end of the file */
1602                         if (to->ofst != to->hdr->hole)
1603                                 gstgap(to->hdr, to->ptr, to->ofst);
1604
1605                         /* Save deleted text */
1606                         i = halloc();
1607                         ptr = vlock(vmem, i->seg);
1608                         mmove(ptr, to->ptr, to->hdr->hole);
1609                         i->hole = to->hdr->hole;
1610                         i->nlines = mcnt(to->ptr, '\n', to->hdr->hole);
1611                         vchanged(ptr);
1612                         vunlock(ptr);
1613
1614                         /* Delete */
1615                         to->hdr->nlines -= i->nlines;
1616                         to->hdr->hole = 0;
1617                 } else
1618                         i = 0;
1619
1620                 /* Delete end of from */
1621                 if (!from->ofst) {
1622                         /* ... unless from needs to be deleted too */
1623                         a = from->hdr->link.prev;
1624                         h = NULL;
1625                         if (a == from->b->eof->hdr)
1626                                 bofmove = 1;
1627                 } else {
1628                         a = from->hdr;
1629                         /* Move gap to deletion point */
1630                         if (from->ofst != from->hdr->hole)
1631                                 gstgap(from->hdr, from->ptr, from->ofst);
1632
1633                         /* Save deleted text */
1634                         h = halloc();
1635                         ptr = vlock(vmem, h->seg);
1636                         mmove(ptr, from->ptr + from->hdr->ehole, SEGSIZ - from->hdr->ehole);
1637                         h->hole = SEGSIZ - from->hdr->ehole;
1638                         h->nlines = mcnt(ptr, '\n', h->hole);
1639                         vchanged(ptr);
1640                         vunlock(ptr);
1641
1642                         /* Delete */
1643                         from->hdr->nlines -= h->nlines;
1644                         from->hdr->ehole = SEGSIZ;
1645                 }
1646
1647                 /* Make from point to header/segment of to */
1648                 from->hdr = to->hdr;
1649                 vunlock(from->ptr);
1650                 from->ptr = to->ptr;
1651                 vupcount(to->ptr);
1652                 from->ofst = 0;
1653
1654                 /* Delete headers/segments between a and to->hdr (if there are any) */
1655                 if (a->link.next != to->hdr)
1656                         if (!h) {
1657                                 h = snip(H, link, a->link.next, to->hdr->link.prev);
1658                                 if (i)
1659                                         enqueb(H, link, h, i);
1660                         } else {
1661                                 splicef(H, link, h, snip(H, link, a->link.next, to->hdr->link.prev));
1662                                 if (i)
1663                                         enqueb(H, link, h, i);
1664                 } else if (!h)
1665                         h = i;
1666                 else if (i)
1667                         enqueb(H, link, h, i);
1668         }
1669
1670         /* If to is empty, then it must have been at the end of the file.  If
1671            the file did not become empty, delete to */
1672         if (!GSIZE(to->hdr) && from->byte) {
1673                 H *ph = from->hdr->link.prev;
1674
1675                 hfree(deque_f(H, link, from->hdr));
1676                 vunlock(from->ptr);
1677                 from->hdr = ph;
1678                 from->ptr = vlock(vmem, from->hdr->seg);
1679                 from->ofst = GSIZE(ph);
1680                 vunlock(from->b->eof->ptr);
1681                 from->b->eof->ptr = from->ptr;
1682                 vupcount(from->ptr);
1683                 from->b->eof->hdr = from->hdr;
1684                 from->b->eof->ofst = from->ofst;
1685         }
1686
1687         /* The deletion is now done */
1688
1689         /* Scroll if necessary */
1690
1691         if (bofmove)
1692                 pset(from->b->bof, from);
1693         if (nlines && !pisbol(from)) {
1694                 scrdel(from->b, from->line, nlines, 1);
1695                 delerr(from->b->name, from->line, nlines);
1696         } else {
1697                 scrdel(from->b, from->line, nlines, 0);
1698                 delerr(from->b->name, from->line, nlines);
1699         }
1700
1701         /* Fix pointers */
1702
1703         for (p = from->link.next; p != from; p = p->link.next)
1704                 if (p->line == from->line && p->byte > from->byte)
1705                         p->valcol = 0;
1706         for (p = from->link.next; p != from; p = p->link.next) {
1707                 if (p->byte >= from->byte) {
1708                         if (p->byte <= from->byte + amnt) {
1709                                 if (p->ptr) {
1710                                         pset(p, from);
1711                                 } else {
1712                                         poffline(pset(p, from));
1713                                 }
1714                         } else {
1715                                 if (p->hdr == to->hdr)
1716                                         p->ofst -= toamnt;
1717                                 p->byte -= amnt;
1718                                 p->line -= nlines;
1719                         }
1720                 }
1721         }
1722
1723         pcoalesce(from);
1724
1725         /* Make buffer out of deleted text and return it */
1726         return bmkchn(h, from->b, amnt, nlines);
1727 }
1728
1729 void bdel(P *from, P *to)
1730 {
1731         if (to->byte - from->byte) {
1732                 B *b = bcut(from, to);
1733
1734                 if (from->b->undo)
1735                         undodel(from->b->undo, from->byte, b);
1736                 else
1737                         brm(b);
1738                 from->b->changed = 1;
1739         }
1740 }
1741
1742 /* Split a block at p's ofst */
1743 /* p is placed in the new block such that it points to the same text but with
1744  * p->ofst==0
1745  */
1746 static void bsplit(P *p)
1747 {
1748         if (p->ofst) {
1749                 H *hdr;
1750                 unsigned char *ptr;
1751                 P *pp;
1752
1753                 hdr = halloc();
1754                 ptr = vlock(vmem, hdr->seg);
1755
1756                 if (p->ofst != p->hdr->hole)
1757                         gstgap(p->hdr, p->ptr, p->ofst);
1758                 mmove(ptr, p->ptr + p->hdr->ehole, SEGSIZ - p->hdr->ehole);
1759                 hdr->hole = SEGSIZ - p->hdr->ehole;
1760                 hdr->nlines = mcnt(ptr, '\n', hdr->hole);
1761                 p->hdr->nlines -= hdr->nlines;
1762                 vchanged(ptr);
1763                 p->hdr->ehole = SEGSIZ;
1764
1765                 enquef(H, link, p->hdr, hdr);
1766
1767                 vunlock(p->ptr);
1768
1769                 for (pp = p->link.next; pp != p; pp = pp->link.next)
1770                         if (pp->hdr == p->hdr && pp->ofst >= p->ofst) {
1771                                 pp->hdr = hdr;
1772                                 if (pp->ptr) {
1773                                         vunlock(pp->ptr);
1774                                         pp->ptr = ptr;
1775                                         vupcount(ptr);
1776                                 }
1777                                 pp->ofst -= p->ofst;
1778                         }
1779
1780                 p->ptr = ptr;
1781                 p->hdr = hdr;
1782                 p->ofst = 0;
1783         }
1784 }
1785
1786 /* Make a chain out of a block of memory (the block must not be empty) */
1787 static H *bldchn(unsigned char *blk, int size, long *nlines)
1788 {
1789         H anchor, *l;
1790
1791         *nlines = 0;
1792         izque(H, link, &anchor);
1793         do {
1794                 unsigned char *ptr;
1795                 int amnt;
1796
1797                 ptr = vlock(vmem, (l = halloc())->seg);
1798                 if (size > SEGSIZ)
1799                         amnt = SEGSIZ;
1800                 else
1801                         amnt = size;
1802                 mmove(ptr, blk, amnt);
1803                 l->hole = amnt;
1804                 l->ehole = SEGSIZ;
1805                 (*nlines) += (l->nlines = mcnt(ptr, '\n', amnt));
1806                 vchanged(ptr);
1807                 vunlock(ptr);
1808                 enqueb(H, link, &anchor, l);
1809                 blk += amnt;
1810                 size -= amnt;
1811         } while (size);
1812         l = anchor.link.next;
1813         deque(H, link, &anchor);
1814 #ifdef CLANG_SCAN_BUILD
1815         /* this can only be fixed properly by using ({ … }) in queue.h */
1816         ITEM = NULL;
1817         QUEUE = NULL;
1818 #endif
1819         return l;
1820 }
1821
1822 /* Insert a chain into a buffer (this does not update pointers) */
1823 static void inschn(P *p, H *a)
1824 {
1825         if (!p->b->eof->byte) { /* P's buffer is empty: replace the empty segment in p with a */
1826                 hfree(p->hdr);
1827                 p->hdr = a;
1828                 vunlock(p->ptr);
1829                 p->ptr = vlock(vmem, a->seg);
1830                 pset(p->b->bof, p);
1831
1832                 p->b->eof->hdr = a->link.prev;
1833                 vunlock(p->b->eof->ptr);
1834                 p->b->eof->ptr = vlock(vmem, p->b->eof->hdr->seg);
1835                 p->b->eof->ofst = GSIZE(p->b->eof->hdr);
1836         } else if (piseof(p)) { /* We're at the end of the file: append a to the file */
1837                 p->b->eof->hdr = a->link.prev;
1838                 spliceb(H, link, p->b->bof->hdr, a);
1839                 vunlock(p->b->eof->ptr);
1840                 p->b->eof->ptr = vlock(vmem, p->b->eof->hdr->seg);
1841                 p->b->eof->ofst = GSIZE(p->b->eof->hdr);
1842                 p->hdr = a;
1843                 vunlock(p->ptr);
1844                 p->ptr = vlock(vmem, p->hdr->seg);
1845                 p->ofst = 0;
1846         } else if (pisbof(p)) { /* We're at the beginning of the file: insert chain and set bof pointer */
1847                 p->hdr = spliceb_f(H, link, p->hdr, a);
1848                 vunlock(p->ptr);
1849                 p->ptr = vlock(vmem, a->seg);
1850                 pset(p->b->bof, p);
1851         } else {                /* We're in the middle of the file: split and insert */
1852                 bsplit(p);
1853                 p->hdr = spliceb_f(H, link, p->hdr, a);
1854                 vunlock(p->ptr);
1855                 p->ptr = vlock(vmem, a->seg);
1856         }
1857 }
1858
1859 static void fixupins(P *p, long amnt, long nlines, H *hdr, int hdramnt)
1860 {
1861         P *pp;
1862
1863         if (nlines && !pisbol(p))
1864                 scrins(p->b, p->line, nlines, 1);
1865         else
1866                 scrins(p->b, p->line, nlines, 0);
1867         inserr(p->b->name, p->line, nlines, pisbol(p)); /* FIXME: last arg ??? */
1868
1869         for (pp = p->link.next; pp != p; pp = pp->link.next)
1870                 if (pp->line == p->line && (pp->byte > p->byte || (pp->end && pp->byte == p->byte)))
1871                         pp->valcol = 0;
1872         for (pp = p->link.next; pp != p; pp = pp->link.next)
1873                 if (pp->byte == p->byte && !pp->end)
1874                         if (pp->ptr)
1875                                 pset(pp, p);
1876                         else
1877                                 poffline(pset(pp, p));
1878                 else if (pp->byte > p->byte || (pp->end && pp->byte == p->byte)) {
1879                         pp->byte += amnt;
1880                         pp->line += nlines;
1881                         if (pp->hdr == hdr)
1882                                 pp->ofst += hdramnt;
1883                 }
1884         if (p->b->undo)
1885                 undoins(p->b->undo, p, amnt);
1886         p->b->changed = 1;
1887 }
1888
1889 /* Insert a buffer at pointer position (the buffer goes away) */
1890 P *binsb(P *p, B *b)
1891 {
1892         if (b->eof->byte) {
1893                 P *q = pdup(p);
1894
1895                 inschn(q, b->bof->hdr);
1896                 b->eof->hdr = halloc();
1897                 fixupins(q, b->eof->byte, b->eof->line, NULL, 0);
1898                 pcoalesce(q);
1899                 prm(q);
1900         }
1901         brm(b);
1902         return p;
1903 }
1904
1905 /* insert memory block 'blk' at 'p' */
1906 P *binsm(P *p, unsigned char *blk, int amnt)
1907 {
1908         long nlines;
1909         H *h = NULL;
1910         int hdramnt = 0;
1911         P *q;
1912
1913         if (!amnt)
1914                 return p;
1915         q = pdup(p);
1916         if (amnt <= GGAPSZ(q->hdr)) {
1917                 h = q->hdr;
1918                 hdramnt = amnt;
1919                 ginsm(q->hdr, q->ptr, q->ofst, blk, amnt);
1920                 q->hdr->nlines += (nlines = mcnt(blk, '\n', amnt));
1921         } else if (!q->ofst && q->hdr != q->b->bof->hdr && amnt <= GGAPSZ(q->hdr->link.prev)) {
1922                 pprev(q);
1923                 ginsm(q->hdr, q->ptr, q->ofst, blk, amnt);
1924                 q->hdr->nlines += (nlines = mcnt(blk, '\n', amnt));
1925         } else {
1926                 H *a = bldchn(blk, amnt, &nlines);
1927
1928                 inschn(q, a);
1929         }
1930         fixupins(q, (long) amnt, nlines, h, hdramnt);
1931         pcoalesce(q);
1932         prm(q);
1933         return p;
1934 }
1935
1936 /* insert byte 'c' at 'p' */
1937 P *binsbyte(P *p, unsigned char c)
1938 {
1939         if (p->b->o.crlf && c == '\n')
1940                 return binsm(p, US "\r\n", 2);
1941         else
1942                 return binsm(p, &c, 1);
1943 }
1944
1945 /* UTF-8 encode a character and insert it */
1946 P *binsc(P *p, int c)
1947 {
1948         if (c>127 && p->b->o.charmap->type) {
1949                 unsigned char buf[8];
1950                 int len = utf8_encode(buf,c);
1951                 return binsm(p,buf,len);
1952         } else {
1953                 unsigned char ch = c;
1954                 if (p->b->o.crlf && c == '\n')
1955                         return binsm(p, US "\r\n", 2);
1956                 else
1957                         return binsm(p, &ch, 1);
1958         }
1959 }
1960
1961 /* insert zero-terminated string 's' at 'p' */
1962 P *binss(P *p, unsigned char *s)
1963 {
1964         return binsm(p, s, strlen((char *)s));
1965 }
1966
1967 /* Read 'size' bytes from file or stream.  Stops and returns amnt. read
1968  * when requested size has been read or when end of file condition occurs.
1969  * Returns with -2 in error for read error or 0 in error for success.
1970  */
1971 static int bkread(int fi, unsigned char *buff, int size)
1972 {
1973         int a, b;
1974
1975         if (!size) {
1976                 error = 0;
1977                 return 0;
1978         }
1979         for (a = b = 0; (a < size) && ((b = joe_read(fi, buff + a, size - a)) > 0); a += b) ;
1980         if (b < 0)
1981                 error = -2;
1982         else
1983                 error = 0;
1984         return a;
1985 }
1986
1987 /* Read up to 'max' bytes from a file into a buffer */
1988 /* Returns with 0 in error or -2 in error for read error */
1989 B *bread(int fi, long int max)
1990 {
1991         H anchor, *l;
1992         long lines = 0, total = 0;
1993         int amnt;
1994         unsigned char *seg;
1995
1996         izque(H, link, &anchor);
1997         error = 0;
1998         while (seg = vlock(vmem, (l = halloc())->seg), !error && (amnt = bkread(fi, seg, max >= SEGSIZ ? SEGSIZ : (int) max))) {
1999                 total += amnt;
2000                 max -= amnt;
2001                 l->hole = amnt;
2002                 lines += (l->nlines = mcnt(seg, '\n', amnt));
2003                 vchanged(seg);
2004                 vunlock(seg);
2005                 enqueb(H, link, &anchor, l);
2006         }
2007         hfree(l);
2008         vunlock(seg);
2009         if (!total)
2010                 return bmk(NULL);
2011         l = anchor.link.next;
2012         deque(H, link, &anchor);
2013         return bmkchn(l, NULL, total, lines);
2014 }
2015
2016 /* Parse file name.
2017  *
2018  * Removes ',xxx,yyy' from end of name and puts their value into skip and amnt
2019  * Replaces ~user/ with directory of given user unless -DJOE_NOPWNAM
2020  * Replaces ~/ with $HOME
2021  *
2022  * Returns new variable length string.
2023  */
2024 unsigned char *parsens(unsigned char *s, long int *skip, long int *amnt)
2025 {
2026         unsigned char *n = vsncpy(NULL, 0, sz(s));
2027         int x;
2028
2029         *skip = 0;
2030         *amnt = MAXLONG;
2031         for (x = sLEN(n) - 1; x > 0 && ((n[x] >= '0' && n[x] <= '9') || n[x] == 'x' || n[x] == 'X'); --x) ;
2032         if (n[x] == ',') {
2033                 n[x] = 0;
2034                 if (n[x + 1] == 'x' || n[x + 1] == 'X')
2035                         sscanf((char *)(n + x + 2), "%lx", skip);
2036                 else if (n[x + 1] == '0' && (n[x + 2] == 'x' || n[x + 2] == 'X'))
2037                         sscanf((char *)(n + x + 3), "%lx", skip);
2038                 else if (n[x + 1] == '0')
2039                         sscanf((char *)(n + x + 1), "%lo", skip);
2040                 else
2041                         sscanf((char *)(n + x + 1), "%ld", skip);
2042                 for (--x; x > 0 && ((n[x] >= '0' && n[x] <= '9') || n[x] == 'x' || n[x] == 'X'); --x) ;
2043                 if (n[x] == ',') {
2044                         n[x] = 0;
2045                         *amnt = *skip;
2046                         if (n[x + 1] == 'x' || n[x + 1] == 'X')
2047                                 sscanf((char *)(n + x + 2), "%lx", skip);
2048                         else if (n[x + 1] == '0' && (n[x + 2] == 'x' || n[x + 2] == 'X'))
2049                                 sscanf((char *)(n + x + 3), "%lx", skip);
2050                         else if (n[x + 1] == '0')
2051                                 sscanf((char *)(n + x + 1), "%lo", skip);
2052                         else
2053                                 sscanf((char *)(n + x + 1), "%ld", skip);
2054                 }
2055         }
2056 #ifndef __MSDOS__
2057         if (n[0] == '~') {
2058                 for (x = 1; n[x] && n[x] != '/'; ++x) ;
2059                 if (n[x] == '/') {
2060                         if (x == 1) {
2061                                 unsigned char *z;
2062
2063                                 s = (unsigned char *)getenv("HOME");
2064                                 z = vsncpy(NULL, 0, sz(s));
2065                                 z = vsncpy(z, sLEN(z), sz(n + x));
2066                                 vsrm(n);
2067                                 n = z;
2068 #ifndef JOE_NOPWNAM
2069                         } else {
2070                                 struct passwd *passwd;
2071
2072                                 n[x] = 0;
2073                                 passwd = getpwnam((char *)(n + 1));
2074                                 n[x] = '/';
2075                                 if (passwd) {
2076                                         unsigned char *z = vsncpy(NULL, 0,
2077                                                          sz((unsigned char *)(passwd->pw_dir)));
2078
2079                                         z = vsncpy(z, sLEN(z), sz(n + x));
2080                                         vsrm(n);
2081                                         n = z;
2082                                 }
2083 #endif
2084                         }
2085                 }
2086         }
2087 #endif
2088         return n;
2089 }
2090
2091 /* Load file into new buffer and return the new buffer */
2092 /* Returns with error set to 0 for success,
2093  * -1 for new file (file doesn't exist)
2094  * -2 for read error
2095  * -3 for seek error
2096  * -4 for open error
2097  */
2098 B *bload(unsigned char *s)
2099 {
2100         unsigned char buffer[SEGSIZ];
2101         FILE *fi;
2102         B *b = NULL;
2103         long skip, amnt;
2104         unsigned char *n;
2105         int nowrite = 0;
2106         P *p;
2107         int x;
2108         long mod_time = 0;
2109         struct stat sbuf;
2110
2111         if (!s || !s[0]) {
2112                 error = -1;
2113                 b = bmk(NULL);
2114                 setopt(b,US "");
2115                 b->rdonly = b->o.readonly;
2116                 b->er = error;
2117                 return b;
2118         }
2119
2120         n = parsens(s, &skip, &amnt);
2121
2122         /* Open file or stream */
2123 #ifndef __MSDOS__
2124         if (n[0] == '!') {
2125                 nescape(maint->t);
2126                 ttclsn();
2127                 fi = popen((char *)(n + 1), "r");
2128         } else
2129 #endif
2130         if (!strcmp(n, "-"))
2131                 fi = stdin;
2132         else {
2133                 fi = fopen((char *)n, "r+");
2134                 if (!fi)
2135                         nowrite = 1;
2136                 else
2137                         fclose(fi);
2138                 fi = fopen((char *)n, "r");
2139                 if (!fi)
2140                         nowrite = 0;
2141                 if (fi) {
2142                         fstat(fileno(fi),&sbuf);
2143                         mod_time = sbuf.st_mtime;
2144                 }
2145         }
2146 #if HAVE_BACKSLASH_PATHS
2147         joesep(n);
2148 #endif
2149
2150         /* Abort if couldn't open */
2151         if (!fi) {
2152                 if (errno == ENOENT)
2153                         error = -1;
2154                 else
2155                         error = -4;
2156                 b = bmk(NULL);
2157                 setopt(b,n);
2158                 b->rdonly = b->o.readonly;
2159                 goto opnerr;
2160         }
2161
2162         /* Skip data if we need to */
2163         if (skip && lseek(fileno(fi), skip, 0) < 0) {
2164                 int r;
2165
2166                 while (skip > SEGSIZ) {
2167                         r = bkread(fileno(fi), buffer, SEGSIZ);
2168                         if (r != SEGSIZ || error) {
2169                                 error = -3;
2170                                 goto err;
2171                         }
2172                         skip -= SEGSIZ;
2173                 }
2174                 skip -= bkread(fileno(fi), buffer, (int) skip);
2175                 if (skip || error) {
2176                         error = -3;
2177                         goto err;
2178                 }
2179         }
2180
2181         /* Read from stream into new buffer */
2182         b = bread(fileno(fi), amnt);
2183         b->mod_time = mod_time;
2184         setopt(b,n);
2185         b->rdonly = b->o.readonly;
2186
2187         /* Close stream */
2188 err:
2189 #ifndef __MSDOS__
2190         if (s[0] == '!')
2191                 pclose(fi);
2192         else
2193 #endif
2194         if (strcmp(n, "-"))
2195                 fclose(fi);
2196
2197 opnerr:
2198         if (s[0] == '!') {
2199                 ttopnn();
2200                 nreturn(maint->t);
2201         }
2202
2203         /* Set name */
2204         b->name = joesep((unsigned char *)strdup(s));
2205
2206         /* Set flags */
2207         if (error || s[0] == '!' || skip || amnt != MAXLONG) {
2208                 b->backup = 1;
2209                 b->changed = 0;
2210         } else if (!strcmp(n, "-")) {
2211                 b->backup = 1;
2212                 b->changed = 1;
2213         } else {
2214                 b->backup = 0;
2215                 b->changed = 0;
2216         }
2217         if (nowrite)
2218                 b->rdonly = b->o.readonly = 1;
2219
2220         /* If first line has CR-LF, assume MS-DOS file */
2221         if (guesscrlf) {
2222                 p=pdup(b->bof);
2223                 b->o.crlf = 0;
2224                 for(x=0;x!=1024;++x) {
2225                         int c = pgetc(p);
2226                         if(c == '\r') {
2227                                 b->o.crlf = 1;
2228                                 break;
2229                                 }
2230                         if(c == '\n') {
2231                                 b->o.crlf = 0;
2232                                 break;
2233                                 }
2234                         if(c == NO_MORE_DATA)
2235                                 break;
2236                 }
2237                 prm(p);
2238         }
2239
2240         /* Search backwards through file: if first indented line
2241            is indented with a tab, assume indentc is tab */
2242         if (guessindent) {
2243                 p=pdup(b->eof);
2244                 for (x=0; x!=20; ++x) {
2245                         p_goto_bol(p);
2246                         if (pisindent(p)) {
2247                                 if (brc(p)=='\t') {
2248                                         b->o.indentc = '\t';
2249                                         b->o.istep = 1;
2250                                 } else {
2251                                         b->o.indentc = ' ';
2252                                         b->o.istep = 2;
2253                                 }
2254                                 break;
2255                         }
2256                         if (prgetc(p)==NO_MORE_DATA)
2257                                 break;
2258                 }
2259                 prm(p);
2260         }
2261
2262         /* Eliminate parsed name */
2263         vsrm(n);
2264
2265         b->er = error;
2266         return b;
2267 }
2268
2269 /* Find already loaded buffer or load file into new buffer */
2270 B *bfind(unsigned char *s)
2271 {
2272         B *b;
2273
2274         if (!s || !s[0]) {
2275                 error = -1;
2276                 b = bmk(NULL);
2277                 setopt(b,US "");
2278                 b->rdonly = b->o.readonly;
2279                 b->internal = 0;
2280                 b->er = error;
2281                 return b;
2282         }
2283         for (b = bufs.link.next; b != &bufs; b = b->link.next)
2284                 if (b->name && !strcmp(s, b->name)) {
2285                         if (!b->orphan)
2286                                 ++b->count;
2287                         else
2288                                 b->orphan = 0;
2289                         error = 0;
2290                         b->internal = 0;
2291                         return b;
2292                 }
2293         b = bload(s);
2294         b->internal = 0;
2295         return b;
2296 }
2297
2298 /* Find already loaded buffer or load file into new buffer */
2299 B *bfind_scratch(unsigned char *s)
2300 {
2301         B *b;
2302
2303         if (!s || !s[0]) {
2304                 error = -1;
2305                 b = bmk(NULL);
2306                 setopt(b,US "");
2307                 b->rdonly = b->o.readonly;
2308                 b->internal = 0;
2309                 b->er = error;
2310                 return b;
2311         }
2312         for (b = bufs.link.next; b != &bufs; b = b->link.next)
2313                 if (b->name && !strcmp(s, b->name)) {
2314                         if (!b->orphan)
2315                                 ++b->count;
2316                         else
2317                                 b->orphan = 0;
2318                         error = 0;
2319                         b->internal = 0;
2320                         return b;
2321                 }
2322         b = bmk(NULL);
2323         error = -1;
2324         setopt(b,s);
2325         b->internal = 0;
2326         b->rdonly = b->o.readonly;
2327         b->er = error;
2328         b->name = (unsigned char *)strdup((char *)s);
2329         b->scratch = 1;
2330         return b;
2331 }
2332
2333 B *bfind_reload(unsigned char *s)
2334 {
2335         B *b;
2336         b = bload(s);
2337         b->internal = 0;
2338         return b;
2339 }
2340
2341 B *bcheck_loaded(unsigned char *s)
2342 {
2343         B *b;
2344
2345         if (!s || !s[0]) {
2346                 return NULL;
2347         }
2348         for (b = bufs.link.next; b != &bufs; b = b->link.next)
2349                 if (b->name && !strcmp(s, b->name)) {
2350                         return b;
2351                 }
2352
2353         return NULL;
2354 }
2355
2356 unsigned char **getbufs(void)
2357 {
2358         unsigned char **s = vamk(16);
2359         B *b;
2360
2361         for (b = bufs.link.next; b != &bufs; b = b->link.next)
2362                 if (b->name)
2363                         s = vaadd(s, vsncpy(NULL, 0, sz(b->name)));
2364         return s;
2365 }
2366
2367 /* Find an orphaned buffer */
2368 B *borphan(void)
2369 {
2370         B *b;
2371
2372         for (b = bufs.link.next; b != &bufs; b = b->link.next)
2373                 if (b->orphan) {
2374                         b->orphan = 0;
2375                         return b;
2376                 }
2377         return NULL;
2378 }
2379
2380 /* Write 'size' bytes from file beginning at 'p' to open file 'fd'.
2381  * Returns error.
2382  * error is set to -5 for write error or 0 for success.
2383  * Don't attempt to write past the end of the file
2384  */
2385 int bsavefd(P *p, int fd, long int size)
2386 {
2387         P *np = pdup(p);
2388         int amnt;
2389
2390         while (size > (amnt = GSIZE(np->hdr) - np->ofst)) {
2391                 if (np->ofst < np->hdr->hole) {
2392                         if (joe_write(fd, np->ptr + np->ofst, np->hdr->hole - np->ofst) < 0)
2393                                 goto err;
2394                         if (joe_write(fd, np->ptr + np->hdr->ehole, SEGSIZ - np->hdr->ehole) < 0)
2395                                 goto err;
2396                 } else if (joe_write(fd, np->ptr + np->ofst + GGAPSZ(np->hdr), amnt) < 0)
2397                         goto err;
2398                 size -= amnt;
2399                 pnext(np);
2400         }
2401         if (size) {
2402                 if (np->ofst < np->hdr->hole) {
2403                         if (size > np->hdr->hole - np->ofst) {
2404                                 if (joe_write(fd, np->ptr + np->ofst, np->hdr->hole - np->ofst) < 0)
2405                                         goto err;
2406                                 if (joe_write(fd, np->ptr + np->hdr->ehole, (int) size - np->hdr->hole + np->ofst) < 0)
2407                                         goto err;
2408                         } else {
2409                                 if (joe_write(fd, np->ptr + np->ofst, (int) size) < 0)
2410                                         goto err;
2411                         }
2412                 } else {
2413                         if (joe_write(fd, np->ptr + np->ofst + GGAPSZ(np->hdr), (int) size) < 0)
2414                                 goto err;
2415                 }
2416         }
2417         prm(np);
2418         return error = 0;
2419 err:
2420         prm(np);
2421         return error = 5;
2422 }
2423
2424 /* Save 'size' bytes beginning at 'p' in file 's' */
2425
2426 /* If flag is set, update original time of file if it makes
2427  * sense to do so (it's a normal file, we're saving with
2428  * same name as buffer or is about to get this name).
2429  */
2430
2431 int bsave(P *p, unsigned char *s, long int size, int flag)
2432 {
2433         FILE *f;
2434         long skip, amnt;
2435         struct stat sbuf;
2436         int norm = 0;
2437
2438         s = parsens(s, &skip, &amnt);
2439
2440         if (amnt < size)
2441                 size = amnt;
2442
2443 #ifndef __MSDOS__
2444         if (s[0] == '!') {
2445                 nescape(maint->t);
2446                 ttclsn();
2447                 f = popen((char *)(s + 1), "w");
2448         } else
2449 #endif
2450         if (s[0] == '>' && s[1] == '>')
2451                 f = fopen((char *)(s + 2), "a");
2452         else if (!strcmp(s, "-")) {
2453                 nescape(maint->t);
2454                 ttclsn();
2455                 f = stdout;
2456         } else if (skip || amnt != MAXLONG)
2457                 f = fopen((char *)s, "r+");
2458         else {
2459                 f = fopen((char *)s, "w");
2460                 norm = 1;
2461         }
2462 #if HAVE_BACKSLASH_PATHS
2463         joesep(s);
2464 #endif
2465
2466         if (!f) {
2467                 error = -4;
2468                 goto opnerr;
2469         }
2470         fflush(f);
2471
2472         if (skip && lseek(fileno(f), skip, 0) < 0) {
2473                 error = -3;
2474                 goto err;
2475         }
2476
2477         bsavefd(p, fileno(f), size);
2478
2479         if (!error && force && size && !skip && amnt == MAXLONG) {
2480                 P *q = pdup(p);
2481                 unsigned char nl = '\n';
2482
2483                 pfwrd(q, size - 1);
2484                 if (brc(q) != '\n' && joe_write(fileno(f), &nl, 1) < 0)
2485                         error = -5;
2486                 prm(q);
2487         }
2488
2489 err:
2490 #ifndef __MSDOS__
2491         if (s[0] == '!')
2492                 pclose(f);
2493         else
2494 #endif
2495         if (strcmp(s, "-"))
2496                 fclose(f);
2497         else
2498                 fflush(f);
2499
2500         /* Update orignal date of file */
2501         /* If it's not named, it's about to be */
2502         if (!error && norm && flag && (!p->b->name || !strcmp((char *)s,p->b->name))) {
2503                 if (!stat((char *)s,&sbuf))
2504                         p->b->mod_time = sbuf.st_mtime;
2505         }
2506
2507 opnerr:
2508         if (s[0] == '!' || !strcmp(s, "-")) {
2509                 ttopnn();
2510                 nreturn(maint->t);
2511         }
2512         return error;
2513 }
2514
2515 /* Return byte at p */
2516
2517 int brc(P *p)
2518 {
2519         if (p->ofst == GSIZE(p->hdr))
2520                 return NO_MORE_DATA;
2521         return GCHAR(p);
2522 }
2523
2524 /* Return character at p */
2525
2526 int brch(P *p)
2527 {
2528         if (p->b->o.charmap->type) {
2529                 P *q = pdup(p);
2530                 int c = pgetc(q);
2531                 prm(q);
2532                 return c;
2533         } else {
2534                 return brc(p);
2535         }
2536 }
2537
2538 unsigned char *brmem(P *p, unsigned char *blk, int size)
2539 {
2540         unsigned char *bk = blk;
2541         P *np;
2542         int amnt;
2543
2544         np = pdup(p);
2545         while (size > (amnt = GSIZE(np->hdr) - np->ofst)) {
2546                 grmem(np->hdr, np->ptr, np->ofst, bk, amnt);
2547                 bk += amnt;
2548                 size -= amnt;
2549                 pnext(np);
2550         }
2551         if (size)
2552                 grmem(np->hdr, np->ptr, np->ofst, bk, size);
2553         prm(np);
2554         return blk;
2555 }
2556
2557 unsigned char *brs(P *p, int size)
2558 {
2559         unsigned char *s = (unsigned char *) joe_malloc(size + 1);
2560
2561         s[size] = 0;
2562         return brmem(p, s, size);
2563 }
2564
2565 unsigned char *brvs(P *p, int size)
2566 {
2567         unsigned char *s = vstrunc(NULL, size);
2568
2569         return brmem(p, (unsigned char *)s, size);
2570 }
2571
2572 unsigned char *brzs(P *p, unsigned char *buf, int size)
2573 {
2574         P *q=pdup(p);
2575         p_goto_eol(q);
2576
2577         if(q->byte-p->byte<size)
2578                 size = q->byte - p->byte;
2579
2580         prm(q);
2581         brmem(p,buf,size);
2582         buf[size]=0;
2583         return buf;
2584 }
2585
2586 /* Save edit buffers when editor dies */
2587
2588 RETSIGTYPE ttsig(int sig)
2589 {
2590         time_t tim = time(NULL);
2591         B *b;
2592         FILE *f;
2593         int tmpfd;
2594         struct stat sbuf;
2595
2596         if ((tmpfd = open("DEADJOE", O_RDWR | O_EXCL | O_CREAT, 0600)) < 0) {
2597                 if (lstat("DEADJOE", &sbuf) < 0)
2598                         _exit(-1);
2599                 if (!S_ISREG(sbuf.st_mode) || sbuf.st_uid != geteuid())
2600                         _exit(-1);
2601                 /*
2602                    A race condition still exists between the lstat() and the open()
2603                    systemcall, which leads to a possible denial-of-service attack
2604                    by setting the file access mode to 600 for every file the
2605                    user executing joe has permissions to.
2606                    This can't be fixed w/o breacking the behavior of the orig. joe!
2607                  */
2608                 if ((tmpfd = open("DEADJOE", O_RDWR | O_APPEND)) < 0)
2609                         _exit(-1);
2610                 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) < 0)
2611                         _exit(-1);
2612         }
2613         if ((f = fdopen(tmpfd, "a")) == NULL)
2614                 _exit(-1);
2615
2616         fprintf(f, "\n*** Modified files in JOE when it aborted on %s", ctime(&tim));
2617         if (sig)
2618                 fprintf(f, "*** JOE was aborted by signal %d\n", sig);
2619         else
2620                 fprintf(f, "*** JOE was aborted because the terminal closed\n");
2621         fflush(f);
2622         for (b = bufs.link.next; b != &bufs; b = b->link.next)
2623                 if (b->changed) {
2624                         if (b->name)
2625                                 fprintf(f, "\n*** File \'%s\'\n", b->name);
2626                         else
2627                                 fprintf(f, "\n*** File \'(Unnamed)\'\n");
2628                         fflush(f);
2629                         bsavefd(b->bof, fileno(f), b->eof->byte);
2630                 }
2631         if (sig)
2632                 ttclsn();
2633         _exit(1);
2634 }