1 /* $MirOS: contrib/code/jupp/b.c,v 1.15 2016/10/08 17:42:12 tg Exp $ */
5 * (C) 1992 Joseph H. Allen
7 * This file is part of JOE (Joe's Own Editor)
14 #include <sys/types.h>
45 char *ctime(const time_t *);
48 FILE *popen(const char *, const char *);
52 unsigned char stdbuf[stdsiz];
61 const unsigned char *msgs[] = {
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"
71 /* Get size of gap (amount of free space) */
72 #define GGAPSZ(hdr) ((hdr)->ehole - (hdr)->hole)
74 /* Get number of characters in gap buffer */
75 #define GSIZE(hdr) (SEGSIZ - GGAPSZ(hdr))
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])
81 /* Set position of gap */
82 static void gstgap(H *hdr, unsigned char *ptr, int ofst)
84 if (ofst > hdr->hole) {
85 mmove(ptr + hdr->hole, ptr + hdr->ehole, ofst - hdr->hole);
87 } else if (ofst < hdr->hole) {
88 mmove(ptr + hdr->ehole - (hdr->hole - ofst), ptr + ofst, hdr->hole - ofst);
91 hdr->ehole = ofst + hdr->ehole - hdr->hole;
96 static void ginsm(H *hdr, unsigned char *ptr, int ofst, unsigned char *blk, int size)
98 if (ofst != hdr->hole)
99 gstgap(hdr, ptr, ofst);
100 mmove(ptr + hdr->hole, blk, size);
106 static void grmem(H *hdr, unsigned char *ptr, int ofst, unsigned char *blk, int size)
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));
113 mmove(blk, ptr + ofst, size);
115 mmove(blk, ptr + ofst + hdr->ehole - hdr->hole, size);
119 static H nhdrs = { {&nhdrs, &nhdrs} };
120 static H ohdrs = { {&ohdrs, &ohdrs} };
122 /* Header allocation */
123 static H *halloc(void)
127 if (qempty(H, link, &ohdrs)) {
128 h = (H *) alitem(&nhdrs, sizeof(H));
129 h->seg = my_valloc(vmem, (long) SEGSIZ);
131 h = deque_f(H, link, ohdrs.link.next);
139 static void hfree(H *h)
141 enquef(H, link, &ohdrs, h);
144 static void hfreechn(H *h)
146 splicef(H, link, &ohdrs, h);
150 static P frptrs = { {&frptrs, &frptrs} };
152 /* Pointer allocation */
153 static P *palloc(void)
155 return alitem(&frptrs, sizeof(P));
158 static void pfree(P *p)
160 enquef(P, link, &frptrs, p);
163 /* Doubly linked list of buffers and free buffer structures */
164 static B bufs = { {&bufs, &bufs} };
165 static B frebufs = { {&frebufs, &frebufs} };
173 deque(B, link, &bufs);
174 enqueb(B, link, b, &bufs);
175 } while (b->internal);
185 deque(B, link, &bufs);
186 enquef(B, link, b, &bufs);
187 } while (b->internal);
191 /* Make a buffer out of a chain */
192 static B *bmkchn(H *chn, B *prop, long amnt, long nlines)
194 B *b = alitem(&frebufs, sizeof(B));
201 mset(b->marks, 0, sizeof(b->marks));
214 izque(P, link, b->bof);
217 b->bof->owner = NULL;
219 b->bof->ptr = vlock(vmem, b->bof->hdr->seg);
226 b->eof = pdup(b->bof);
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);
233 b->eof->line = nlines;
237 enquef(B, link, &bufs, b);
243 /* Create an empty buffer */
246 return bmkchn(halloc(), prop, 0L, 0L);
252 /* Eliminate a buffer */
255 if (b && !--b->count) {
262 hfreechn(b->eof->hdr);
263 while (!qempty(P, link, b->bof))
264 prm(b->bof->link.next);
268 demote(B, link, &frebufs, b);
284 p->ptr = vlock(vmem, p->hdr->seg);
294 } while ((p = p->link.next) != b->bof);
304 } while ((p = p->link.next) != b->bof);
315 enquef(P, link, p, n);
319 P *pdupown(P *p, P **o)
326 enquef(P, link, p, n);
342 pfree(deque_f(P, link, p));
357 n->ptr = vlock(vmem, n->hdr->seg);
361 n->valcol = p->valcol;
368 return pset(p, p->b->bof);
373 return pset(p, p->b->eof);
376 /* is p at the beginning of file? */
379 return p->hdr == p->b->bof->hdr && !p->ofst;
382 /* is p at the end of file? */
385 return p->ofst == GSIZE(p->hdr);
388 /* is p at the end of line? */
403 if (pgetb(q) == '\n') {
412 /* is p at the beginning of line? */
424 /* is p at the beginning of word? */
432 if (joe_isalnum_(p->b->o.charmap,c) && (!joe_isalnum_(p->b->o.charmap,d) || pisbof(p)))
438 /* is p at the end of word? */
446 if (joe_isalnum_(p->b->o.charmap,c) && (!joe_isalnum_(p->b->o.charmap,d) || piseof(p)))
452 /* is p on the blank line (ie. full of spaces/tabs)? */
458 while (joe_isblank(p->b->o.charmap,brc(q)))
469 /* is p at end of line or spaces followed by end of line? */
470 int piseolblank(P *p)
474 while (joe_isblank(p->b->o.charmap,brc(q)))
485 /* return column of first nonblank character */
492 while (joe_isblank(p->b->o.charmap,brc(q)))
499 /* return true if all characters to left of cursor match c */
501 int pispure(P *p,int c)
505 while (q->byte!=p->byte)
516 if (p->hdr == p->b->eof->hdr) {
517 p->ofst = GSIZE(p->hdr);
520 p->hdr = p->hdr->link.next;
523 p->ptr = vlock(vmem, p->hdr->seg);
529 if (p->hdr == p->b->bof->hdr) {
533 p->hdr = p->hdr->link.prev;
534 p->ofst = GSIZE(p->hdr);
536 p->ptr = vlock(vmem, p->hdr->seg);
540 /* return current byte and move p to the next byte. column will be unchanged. */
545 if (p->ofst == GSIZE(p->hdr))
548 if (++p->ofst == GSIZE(p->hdr))
555 } else if (p->b->o.crlf && c == '\r') {
566 /* return current character and move p to the next character. column will be updated if it was valid. */
569 if (p->b->o.charmap->type) {
573 val = p->valcol; /* Remember if column number was valid */
574 c = pgetb(p); /* Get first byte */
580 if ((c&0xE0)==0xC0) { /* Two bytes */
583 } else if ((c&0xF0)==0xE0) { /* Three bytes */
586 } else if ((c&0xF8)==0xF0) { /* Four bytes */
589 } else if ((c&0xFC)==0xF8) { /* Five bytes */
592 } else if ((c&0xFE)==0xFC) { /* Six bytes */
595 } else if ((c&0x80)==0x00) { /* One byte */
597 } else { /* 128-191, 254, 255: Not a valid UTF-8 start character */
610 if (d == NO_MORE_DATA || (d&0xC0)!=0x80)
613 c = ((c<<6)|(d&0x3F));
615 if (n) { /* FIXME: there was a bad UTF-8 sequence */
616 /* How to represent this? */
619 c = d == NO_MORE_DATA ? 0x1000FFFF : 0x1000FFFE;
622 wid = joe_wcwidth(1,c);
627 if (val) { /* Update column no. if it was valid to start with */
630 p->col += (p->b->o.tab) - (p->col) % (p->b->o.tab);
641 if (p->ofst == GSIZE(p->hdr))
644 if (++p->ofst == GSIZE(p->hdr))
652 } else if (p->b->o.crlf && c == '\r') {
659 p->col += (p->b->o.tab) - (p->col) % (p->b->o.tab);
667 /* move p n characters forward */
668 P *pfwrd(P *p, long n)
674 if (p->ofst == GSIZE(p->hdr))
677 p->byte += GSIZE(p->hdr);
679 p->line += p->hdr->nlines;
683 } while (n > GSIZE(p->hdr));
684 if (GCHAR(p) == '\n')
689 if (p->ofst == GSIZE(p->hdr))
694 /* move p to the previous byte: does not take into account -crlf mode */
695 static int prgetb1(P *p)
711 /* move p to the previous byte */
716 if (p->b->o.crlf && c == '\n') {
720 if (c != NO_MORE_DATA)
727 /* move p to the previous character (try to keep col updated) */
730 if (p->b->o.charmap->type) {
739 while (q->byte<p->byte) {
756 if (c == NO_MORE_DATA)
758 else if ((c&0xC0)==0x80) {
761 } else if ((c&0x80)==0x00) { /* One char */
764 } else if ((c&0xE0)==0xC0) { /* Two chars */
767 } else if ((c&0xF0)==0xE0) { /* Three chars */
770 } else if ((c&0xF8)==0xF0) { /* Four chars */
773 } else if ((c&0xFC)==0xF8) { /* Five chars */
776 } else if ((c&0xFE)==0xFC) { /* Six chars */
779 } else { /* FIXME: Invalid (0xFE or 0xFF found) */
784 if (val && c!='\t' && c!='\n') {
786 p->col -= joe_wcwidth(1,d);
797 /* move p n characters backwards */
798 P *pbkwd(P *p, long n)
809 p->line -= p->hdr->nlines;
813 } while (n > GSIZE(p->hdr));
816 if (GCHAR(p) == '\n')
822 /* move p n characters forwards/backwards according to loc */
823 P *pgoto(P *p, long loc)
826 pfwrd(p, loc - p->byte);
827 else if (loc < p->byte)
828 pbkwd(p, p->byte - loc);
832 /* make p->col valid */
838 while (p->byte < pos)
843 /* move p to the beginning of line */
853 /* move p to the indentation point */
854 P *p_goto_indent(P *p, int c)
858 while ((d=brc(p)), d==c || ((c==' ' || c=='\t') && (d==' ' || d=='\t')))
863 /* move p to the end of line */
866 if (p->b->o.crlf || p->b->o.charmap->type)
870 while (p->ofst != GSIZE(p->hdr)) {
880 p->col += p->b->o.tab - p->col % p->b->o.tab;
883 if (p->ofst == GSIZE(p->hdr))
890 /* move p to the beginning of next line */
896 if (p->ofst == GSIZE(p->hdr))
898 p->byte += GSIZE(p->hdr) - p->ofst;
901 } while (!p->hdr->nlines);
909 if (p->ofst == GSIZE(p->hdr))
914 /* move p to the end of previous line */
926 } while (!p->hdr->nlines);
932 if (p->b->o.crlf && c == '\n') {
935 if (k != '\r' && k != NO_MORE_DATA)
941 /* move p to the given 'line' line */
942 P *pline(P *p, long line)
944 if (line > p->b->eof->line) {
948 if (line < labs(p->line - line))
950 if (labs(p->b->eof->line - line) < labs(p->line - line))
952 if (p->line == line) {
956 while (line > p->line)
958 if (line < p->line) {
959 while (line < p->line)
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)
971 if(p->b->o.charmap->type) {
978 if (c == NO_MORE_DATA)
984 if (p->b->o.crlf && c == '\r' && piseol(p))
988 wid = p->b->o.tab - p->col % p->b->o.tab;
990 wid = joe_wcwidth(1,c);
992 if (p->col + wid > goalcol)
996 } while (p->col != goalcol);
1002 if (p->ofst == GSIZE(p->hdr))
1007 if (p->b->o.crlf && c == '\r' && piseol(p))
1010 wid = p->b->o.tab - p->col % p->b->o.tab;
1013 if (p->col + wid > goalcol)
1015 if (++p->ofst == GSIZE(p->hdr))
1019 } while (p->col != goalcol);
1024 /* Move to goal column, then skip backwards to just after first non-whitespace character */
1025 P *pcolwse(P *p, long goalcol)
1032 } while (c == ' ' || c == '\t');
1033 if (c != NO_MORE_DATA)
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)
1043 if (p->b->o.charmap->type) {
1044 while (p->col < goalcol) {
1048 if (c == NO_MORE_DATA)
1054 if (p->b->o.crlf && c=='\r' && piseol(p))
1060 while (p->col < goalcol) {
1063 if (p->ofst == GSIZE(p->hdr))
1069 if (p->b->o.crlf && c == '\r' && piseol(p))
1073 p->col += p->b->o.tab - p->col % p->b->o.tab;
1076 if (++p->ofst == GSIZE(p->hdr))
1084 /* fill space between curent column and 'to' column with tabs/spaces */
1085 void pfill(P *p, long to, int usetabs)
1088 while (piscol(p) < to)
1089 if (p->col + p->b->o.tab - p->col % p->b->o.tab <= to) {
1097 while (piscol(p) < to) {
1103 /* delete sequence of whitespaces - backwards */
1111 } while (c == ' ' || c == '\t');
1112 if (c != NO_MORE_DATA)
1118 static int frgetc(P *p)
1126 static void ffwrd(P *p, int n)
1128 while (n > GSIZE(p->hdr) - p->ofst) {
1129 n -= GSIZE(p->hdr) - p->ofst;
1133 if ((p->ofst += n) == GSIZE(p->hdr))
1137 /* forward find pattern 's' in text pointed by 'p' (Boyer-Moore algorithm) */
1138 static P *ffind(P *p, unsigned char *s, int len)
1140 long amnt = p->b->eof->byte - p->byte;
1142 unsigned char table[256], c;
1149 mset(table, 255, 256);
1150 for (x = 0; x != len - 1; ++x)
1156 if ((c = frgetc(p)) != s[--x]) {
1157 if (table[c] == 255) {
1160 } else if (x <= table[c]) {
1161 ffwrd(p, len - x + 1);
1164 ffwrd(p, len - table[c]);
1165 amnt -= x - table[c];
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)
1179 long amnt = p->b->eof->byte - p->byte;
1181 struct charmap *map = p->b->o.charmap;
1182 unsigned char table[256], c;
1189 mset(table, 255, 256);
1190 for (x = 0; x != len - 1; ++x)
1196 if ((c = joe_tolower(map,frgetc(p))) != s[--x]) {
1197 if (table[c] == 255) {
1200 } else if (x <= table[c]) {
1201 ffwrd(p, len - x + 1);
1204 ffwrd(p, len - table[c]);
1205 amnt -= x - table[c];
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)
1220 while (p->hdr != q->hdr || p->ofst != q->ofst) {
1221 if (GCHAR(p) == '\n')
1225 if (p->ofst == GSIZE(p->hdr))
1227 while (!p->ofst && p->hdr != q->hdr) {
1228 p->byte += GSIZE(p->hdr);
1229 p->line += p->hdr->nlines;
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)
1241 if (ffind(q, s, len)) {
1251 /* same as pfind() but case insensitive */
1252 P *pifind(P *p, unsigned char *s, int len)
1256 if (fifind(q, s, len)) {
1266 static void fbkwd(P *p, int n)
1268 while (n > p->ofst) {
1279 static int fpgetc(P *p)
1283 if (p->ofst == GSIZE(p->hdr))
1284 return NO_MORE_DATA;
1286 if (++p->ofst == GSIZE(p->hdr))
1291 /* backward find pattern 's' in text pointed by 'p' (Boyer-Moore algorithm) */
1292 static P *frfind(P *p, unsigned char *s, int len)
1294 long amnt = p->byte;
1296 unsigned char table[256], c;
1298 if (len > p->b->eof->byte - p->byte) {
1299 x = len - (p->b->eof->byte - p->byte);
1308 mset(table, 255, 256);
1309 for (x = len; --x; table[s[x]] = len - x - 1) ;
1312 if ((c = fpgetc(p)) != s[x++]) {
1313 if (table[c] == 255) {
1315 amnt -= len - x + 1;
1316 } else if (len - table[c] <= x) {
1320 fbkwd(p, len - table[c]);
1321 amnt -= len - table[c] - x;
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)
1336 long amnt = p->byte;
1338 unsigned char table[256], c;
1339 struct charmap *map = p->b->o.charmap;
1341 if (len > p->b->eof->byte - p->byte) {
1342 x = len - (p->b->eof->byte - p->byte);
1351 mset(table, 255, 256);
1352 for (x = len; --x; table[s[x]] = len - x - 1) ;
1355 if ((c = joe_tolower(map,fpgetc(p))) != s[x++]) {
1356 if (table[c] == 255) {
1358 amnt -= len - x + 1;
1359 } else if (len - table[c] <= x) {
1363 fbkwd(p, len - table[c]);
1364 amnt -= len - table[c] - x;
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)
1380 while (p->hdr != q->hdr || p->ofst != q->ofst) {
1385 p->line -= p->hdr->nlines;
1388 } while (p->hdr != q->hdr);
1391 if (GCHAR(p) == '\n')
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)
1402 if (frfind(q, s, len)) {
1412 /* same as prfind() but case insensitive */
1413 P *prifind(P *p, unsigned char *s, int len)
1417 if (frifind(q, s, len)) {
1427 /* copy text between 'from' and 'to' into new buffer */
1428 B *bcpy(P *from, P *to)
1434 if (from->byte >= to->byte)
1435 return bmk(from->b);
1438 izque(H, link, &anchor);
1440 if (q->hdr == to->hdr) {
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);
1449 enqueb(H, link, &anchor, l);
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);
1459 enqueb(H, link, &anchor, l);
1461 while (q->hdr != to->hdr) {
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);
1470 enqueb(H, link, &anchor, l);
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);
1482 enqueb(H, link, &anchor, l);
1486 l = anchor.link.next;
1487 deque(H, link, &anchor);
1490 return bmkchn(l, from->b, to->byte - from->byte, to->line - from->line);
1493 /* Coalesce small blocks into a single larger one */
1494 void pcoalesce(P *p)
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);
1503 gstgap(hdr, ptr, size);
1504 ginsm(p->hdr, p->ptr, GSIZE(p->hdr), ptr, size);
1505 p->hdr->nlines += hdr->nlines;
1507 hfree(deque_f(H, link, hdr));
1508 for (q = p->link.next; q != p; q = q->link.next)
1509 if (q->hdr == hdr) {
1513 q->ptr = vlock(vmem, q->hdr->seg);
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);
1524 gstgap(hdr, ptr, size);
1525 ginsm(p->hdr, p->ptr, 0, ptr, size);
1526 p->hdr->nlines += hdr->nlines;
1528 hfree(deque_f(H, link, hdr));
1530 for (q = p->link.next; q != p; q = q->link.next)
1531 if (q->hdr == hdr) {
1535 q->ptr = vlock(vmem, q->hdr->seg);
1536 } else if (q->hdr == p->hdr)
1541 /* Delete the text between two pointers from a buffer and return it in a new
1544 * This routine calls these functions:
1545 * gstgap - to position gaps
1546 * halloc - to allocate new header/segment pairs
1547 * vlock - virtual memory routines
1551 * mcnt - to count NLs
1552 * snip - queue routines
1555 * scrdel - to tell screen update to scroll when NLs are deleted
1556 * bmkchn - to make a buffer out of a chain
1559 /* This is only to be used for bdel() */
1560 static B *bcut(P *from, P *to)
1562 H *h, /* The deleted text */
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 */
1571 if (!(amnt = to->byte - from->byte))
1572 return NULL; /* ...nothing to delete */
1574 nlines = to->line - from->line;
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);
1581 /* Store the deleted text */
1583 ptr = vlock(vmem, h->seg);
1584 mmove(ptr, from->ptr + from->hdr->ehole, (int) amnt);
1591 from->hdr->ehole += amnt;
1592 from->hdr->nlines -= nlines;
1595 } else { /* Delete crosses segments */
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);
1605 /* Save deleted text */
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);
1615 to->hdr->nlines -= i->nlines;
1620 /* Delete end of from */
1622 /* ... unless from needs to be deleted too */
1623 a = from->hdr->link.prev;
1625 if (a == from->b->eof->hdr)
1629 /* Move gap to deletion point */
1630 if (from->ofst != from->hdr->hole)
1631 gstgap(from->hdr, from->ptr, from->ofst);
1633 /* Save deleted text */
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);
1643 from->hdr->nlines -= h->nlines;
1644 from->hdr->ehole = SEGSIZ;
1647 /* Make from point to header/segment of to */
1648 from->hdr = to->hdr;
1650 from->ptr = to->ptr;
1654 /* Delete headers/segments between a and to->hdr (if there are any) */
1655 if (a->link.next != to->hdr)
1657 h = snip(H, link, a->link.next, to->hdr->link.prev);
1659 enqueb(H, link, h, i);
1661 splicef(H, link, h, snip(H, link, a->link.next, to->hdr->link.prev));
1663 enqueb(H, link, h, i);
1667 enqueb(H, link, h, i);
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;
1675 hfree(deque_f(H, link, from->hdr));
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;
1687 /* The deletion is now done */
1689 /* Scroll if necessary */
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);
1697 scrdel(from->b, from->line, nlines, 0);
1698 delerr(from->b->name, from->line, nlines);
1703 for (p = from->link.next; p != from; p = p->link.next)
1704 if (p->line == from->line && p->byte > from->byte)
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) {
1712 poffline(pset(p, from));
1715 if (p->hdr == to->hdr)
1725 /* Make buffer out of deleted text and return it */
1726 return bmkchn(h, from->b, amnt, nlines);
1729 void bdel(P *from, P *to)
1731 if (to->byte - from->byte) {
1732 B *b = bcut(from, to);
1735 undodel(from->b->undo, from->byte, b);
1738 from->b->changed = 1;
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
1746 static void bsplit(P *p)
1754 ptr = vlock(vmem, hdr->seg);
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;
1763 p->hdr->ehole = SEGSIZ;
1765 enquef(H, link, p->hdr, hdr);
1769 for (pp = p->link.next; pp != p; pp = pp->link.next)
1770 if (pp->hdr == p->hdr && pp->ofst >= p->ofst) {
1777 pp->ofst -= p->ofst;
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)
1792 izque(H, link, &anchor);
1797 ptr = vlock(vmem, (l = halloc())->seg);
1802 mmove(ptr, blk, amnt);
1805 (*nlines) += (l->nlines = mcnt(ptr, '\n', amnt));
1808 enqueb(H, link, &anchor, l);
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 */
1822 /* Insert a chain into a buffer (this does not update pointers) */
1823 static void inschn(P *p, H *a)
1825 if (!p->b->eof->byte) { /* P's buffer is empty: replace the empty segment in p with a */
1829 p->ptr = vlock(vmem, a->seg);
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);
1844 p->ptr = vlock(vmem, p->hdr->seg);
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);
1849 p->ptr = vlock(vmem, a->seg);
1851 } else { /* We're in the middle of the file: split and insert */
1853 p->hdr = spliceb_f(H, link, p->hdr, a);
1855 p->ptr = vlock(vmem, a->seg);
1859 static void fixupins(P *p, long amnt, long nlines, H *hdr, int hdramnt)
1863 if (nlines && !pisbol(p))
1864 scrins(p->b, p->line, nlines, 1);
1866 scrins(p->b, p->line, nlines, 0);
1867 inserr(p->b->name, p->line, nlines, pisbol(p)); /* FIXME: last arg ??? */
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)))
1872 for (pp = p->link.next; pp != p; pp = pp->link.next)
1873 if (pp->byte == p->byte && !pp->end)
1877 poffline(pset(pp, p));
1878 else if (pp->byte > p->byte || (pp->end && pp->byte == p->byte)) {
1882 pp->ofst += hdramnt;
1885 undoins(p->b->undo, p, amnt);
1889 /* Insert a buffer at pointer position (the buffer goes away) */
1890 P *binsb(P *p, B *b)
1895 inschn(q, b->bof->hdr);
1896 b->eof->hdr = halloc();
1897 fixupins(q, b->eof->byte, b->eof->line, NULL, 0);
1905 /* insert memory block 'blk' at 'p' */
1906 P *binsm(P *p, unsigned char *blk, int amnt)
1916 if (amnt <= GGAPSZ(q->hdr)) {
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)) {
1923 ginsm(q->hdr, q->ptr, q->ofst, blk, amnt);
1924 q->hdr->nlines += (nlines = mcnt(blk, '\n', amnt));
1926 H *a = bldchn(blk, amnt, &nlines);
1930 fixupins(q, (long) amnt, nlines, h, hdramnt);
1936 /* insert byte 'c' at 'p' */
1937 P *binsbyte(P *p, unsigned char c)
1939 if (p->b->o.crlf && c == '\n')
1940 return binsm(p, US "\r\n", 2);
1942 return binsm(p, &c, 1);
1945 /* UTF-8 encode a character and insert it */
1946 P *binsc(P *p, int c)
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);
1953 unsigned char ch = c;
1954 if (p->b->o.crlf && c == '\n')
1955 return binsm(p, US "\r\n", 2);
1957 return binsm(p, &ch, 1);
1961 /* insert zero-terminated string 's' at 'p' */
1962 P *binss(P *p, unsigned char *s)
1964 return binsm(p, s, strlen((char *)s));
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.
1971 static int bkread(int fi, unsigned char *buff, int size)
1979 for (a = b = 0; (a < size) && ((b = joe_read(fi, buff + a, size - a)) > 0); a += b) ;
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)
1992 long lines = 0, total = 0;
1996 izque(H, link, &anchor);
1998 while (seg = vlock(vmem, (l = halloc())->seg), !error && (amnt = bkread(fi, seg, max >= SEGSIZ ? SEGSIZ : (int) max))) {
2002 lines += (l->nlines = mcnt(seg, '\n', amnt));
2005 enqueb(H, link, &anchor, l);
2011 l = anchor.link.next;
2012 deque(H, link, &anchor);
2013 return bmkchn(l, NULL, total, lines);
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
2022 * Returns new variable length string.
2024 unsigned char *parsens(unsigned char *s, long int *skip, long int *amnt)
2026 unsigned char *n = vsncpy(NULL, 0, sz(s));
2031 for (x = sLEN(n) - 1; x > 0 && ((n[x] >= '0' && n[x] <= '9') || n[x] == 'x' || n[x] == 'X'); --x) ;
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);
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) ;
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);
2053 sscanf((char *)(n + x + 1), "%ld", skip);
2058 for (x = 1; n[x] && n[x] != '/'; ++x) ;
2063 s = (unsigned char *)getenv("HOME");
2064 z = vsncpy(NULL, 0, sz(s));
2065 z = vsncpy(z, sLEN(z), sz(n + x));
2070 struct passwd *passwd;
2073 passwd = getpwnam((char *)(n + 1));
2076 unsigned char *z = vsncpy(NULL, 0,
2077 sz((unsigned char *)(passwd->pw_dir)));
2079 z = vsncpy(z, sLEN(z), sz(n + x));
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)
2098 B *bload(unsigned char *s)
2100 unsigned char buffer[SEGSIZ];
2115 b->rdonly = b->o.readonly;
2120 n = parsens(s, &skip, &amnt);
2122 /* Open file or stream */
2127 fi = popen((char *)(n + 1), "r");
2130 if (!strcmp(n, "-"))
2133 fi = fopen((char *)n, "r+");
2138 fi = fopen((char *)n, "r");
2142 fstat(fileno(fi),&sbuf);
2143 mod_time = sbuf.st_mtime;
2146 #if HAVE_BACKSLASH_PATHS
2150 /* Abort if couldn't open */
2152 if (errno == ENOENT)
2158 b->rdonly = b->o.readonly;
2162 /* Skip data if we need to */
2163 if (skip && lseek(fileno(fi), skip, 0) < 0) {
2166 while (skip > SEGSIZ) {
2167 r = bkread(fileno(fi), buffer, SEGSIZ);
2168 if (r != SEGSIZ || error) {
2174 skip -= bkread(fileno(fi), buffer, (int) skip);
2175 if (skip || error) {
2181 /* Read from stream into new buffer */
2182 b = bread(fileno(fi), amnt);
2183 b->mod_time = mod_time;
2185 b->rdonly = b->o.readonly;
2204 b->name = joesep((unsigned char *)strdup(s));
2207 if (error || s[0] == '!' || skip || amnt != MAXLONG) {
2210 } else if (!strcmp(n, "-")) {
2218 b->rdonly = b->o.readonly = 1;
2220 /* If first line has CR-LF, assume MS-DOS file */
2224 for(x=0;x!=1024;++x) {
2234 if(c == NO_MORE_DATA)
2240 /* Search backwards through file: if first indented line
2241 is indented with a tab, assume indentc is tab */
2244 for (x=0; x!=20; ++x) {
2248 b->o.indentc = '\t';
2256 if (prgetc(p)==NO_MORE_DATA)
2262 /* Eliminate parsed name */
2269 /* Find already loaded buffer or load file into new buffer */
2270 B *bfind(unsigned char *s)
2278 b->rdonly = b->o.readonly;
2283 for (b = bufs.link.next; b != &bufs; b = b->link.next)
2284 if (b->name && !strcmp(s, b->name)) {
2298 /* Find already loaded buffer or load file into new buffer */
2299 B *bfind_scratch(unsigned char *s)
2307 b->rdonly = b->o.readonly;
2312 for (b = bufs.link.next; b != &bufs; b = b->link.next)
2313 if (b->name && !strcmp(s, b->name)) {
2326 b->rdonly = b->o.readonly;
2328 b->name = (unsigned char *)strdup((char *)s);
2333 B *bfind_reload(unsigned char *s)
2341 B *bcheck_loaded(unsigned char *s)
2348 for (b = bufs.link.next; b != &bufs; b = b->link.next)
2349 if (b->name && !strcmp(s, b->name)) {
2356 unsigned char **getbufs(void)
2358 unsigned char **s = vamk(16);
2361 for (b = bufs.link.next; b != &bufs; b = b->link.next)
2363 s = vaadd(s, vsncpy(NULL, 0, sz(b->name)));
2367 /* Find an orphaned buffer */
2372 for (b = bufs.link.next; b != &bufs; b = b->link.next)
2380 /* Write 'size' bytes from file beginning at 'p' to open file 'fd'.
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
2385 int bsavefd(P *p, int fd, long int size)
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)
2394 if (joe_write(fd, np->ptr + np->hdr->ehole, SEGSIZ - np->hdr->ehole) < 0)
2396 } else if (joe_write(fd, np->ptr + np->ofst + GGAPSZ(np->hdr), amnt) < 0)
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)
2406 if (joe_write(fd, np->ptr + np->hdr->ehole, (int) size - np->hdr->hole + np->ofst) < 0)
2409 if (joe_write(fd, np->ptr + np->ofst, (int) size) < 0)
2413 if (joe_write(fd, np->ptr + np->ofst + GGAPSZ(np->hdr), (int) size) < 0)
2424 /* Save 'size' bytes beginning at 'p' in file 's' */
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).
2431 int bsave(P *p, unsigned char *s, long int size, int flag)
2438 s = parsens(s, &skip, &amnt);
2447 f = popen((char *)(s + 1), "w");
2450 if (s[0] == '>' && s[1] == '>')
2451 f = fopen((char *)(s + 2), "a");
2452 else if (!strcmp(s, "-")) {
2456 } else if (skip || amnt != MAXLONG)
2457 f = fopen((char *)s, "r+");
2459 f = fopen((char *)s, "w");
2462 #if HAVE_BACKSLASH_PATHS
2472 if (skip && lseek(fileno(f), skip, 0) < 0) {
2477 bsavefd(p, fileno(f), size);
2479 if (!error && force && size && !skip && amnt == MAXLONG) {
2481 unsigned char nl = '\n';
2484 if (brc(q) != '\n' && joe_write(fileno(f), &nl, 1) < 0)
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;
2508 if (s[0] == '!' || !strcmp(s, "-")) {
2515 /* Return byte at p */
2519 if (p->ofst == GSIZE(p->hdr))
2520 return NO_MORE_DATA;
2524 /* Return character at p */
2528 if (p->b->o.charmap->type) {
2538 unsigned char *brmem(P *p, unsigned char *blk, int size)
2540 unsigned char *bk = blk;
2545 while (size > (amnt = GSIZE(np->hdr) - np->ofst)) {
2546 grmem(np->hdr, np->ptr, np->ofst, bk, amnt);
2552 grmem(np->hdr, np->ptr, np->ofst, bk, size);
2557 unsigned char *brs(P *p, int size)
2559 unsigned char *s = (unsigned char *) joe_malloc(size + 1);
2562 return brmem(p, s, size);
2565 unsigned char *brvs(P *p, int size)
2567 unsigned char *s = vstrunc(NULL, size);
2569 return brmem(p, (unsigned char *)s, size);
2572 unsigned char *brzs(P *p, unsigned char *buf, int size)
2577 if(q->byte-p->byte<size)
2578 size = q->byte - p->byte;
2586 /* Save edit buffers when editor dies */
2588 RETSIGTYPE ttsig(int sig)
2590 time_t tim = time(NULL);
2596 if ((tmpfd = open("DEADJOE", O_RDWR | O_EXCL | O_CREAT, 0600)) < 0) {
2597 if (lstat("DEADJOE", &sbuf) < 0)
2599 if (!S_ISREG(sbuf.st_mode) || sbuf.st_uid != geteuid())
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!
2608 if ((tmpfd = open("DEADJOE", O_RDWR | O_APPEND)) < 0)
2610 if (fchmod(tmpfd, S_IRUSR | S_IWUSR) < 0)
2613 if ((f = fdopen(tmpfd, "a")) == NULL)
2616 fprintf(f, "\n*** Modified files in JOE when it aborted on %s", ctime(&tim));
2618 fprintf(f, "*** JOE was aborted by signal %d\n", sig);
2620 fprintf(f, "*** JOE was aborted because the terminal closed\n");
2622 for (b = bufs.link.next; b != &bufs; b = b->link.next)
2625 fprintf(f, "\n*** File \'%s\'\n", b->name);
2627 fprintf(f, "\n*** File \'(Unnamed)\'\n");
2629 bsavefd(b->bof, fileno(f), b->eof->byte);