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