/*
* Editor engine
* Copyright
* (C) 1992 Joseph H. Allen
*
* This file is part of JOE (Joe's Own Editor)
*/
#include "types.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifndef S_ISLNK
#ifdef S_IFLNK
#define S_ISLNK(n) (((n) & (S_IFMT)) == (S_IFLNK))
#else
#define S_ISLNK(n) (0)
#endif
#endif
#ifndef JOEWIN
extern int errno;
#endif
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
static int selinux_enabled = -1;
#endif
int guesscrlf = 0;
int guessindent = 0;
int berror;
int force = 0;
VFILE *vmem;
int nodeadjoe = 0;
const char *msgs[] = {
_("No error"),
_("New File"),
_("Error reading file"),
_("Error seeking file"),
_("Error opening file"),
_("Error writing file"),
_("File on disk is newer")
};
/* Get size of gap (amount of free space) */
#define GGAPSZ(hdr) ((hdr)->ehole - (hdr)->hole)
/* Get number of characters in gap buffer */
#define GSIZE(hdr) (SEGSIZ - GGAPSZ(hdr))
/* Get char from buffer (with jumping around the gap) */
#define GCHAR(p) ((p)->ofst >= (p)->hdr->hole ? ((unsigned char *)(p)->ptr)[(p)->ofst + GGAPSZ((p)->hdr)] \
: ((unsigned char *)(p)->ptr)[(p)->ofst])
/* Set position of gap */
static void gstgap(H *hdr, char *ptr, ptrdiff_t ofst)
{
if (ofst > hdr->hole) {
mmove(ptr + hdr->hole, ptr + hdr->ehole, ofst - hdr->hole);
vchanged(ptr);
} else if (ofst < hdr->hole) {
mmove(ptr + hdr->ehole - (hdr->hole - ofst), ptr + ofst, hdr->hole - ofst);
vchanged(ptr);
}
hdr->ehole = ofst + hdr->ehole - hdr->hole;
hdr->hole = ofst;
}
/* Insert a block */
static void ginsm(H *hdr, char *ptr, ptrdiff_t ofst, const char *blk, ptrdiff_t size)
{
if (ofst != hdr->hole)
gstgap(hdr, ptr, ofst);
mmove(ptr + hdr->hole, blk, size);
hdr->hole += size;
vchanged(ptr);
}
/* Read block */
static void grmem(H *hdr, char *ptr, ptrdiff_t ofst, char *blk, ptrdiff_t size)
{
if (ofst < hdr->hole)
if (size > hdr->hole - ofst) {
mmove(blk, ptr + ofst, hdr->hole - ofst);
mmove(blk + hdr->hole - ofst, ptr + hdr->ehole, size - (hdr->hole - ofst));
} else
mmove(blk, ptr + ofst, size);
else
mmove(blk, ptr + ofst + hdr->ehole - hdr->hole, size);
}
static H nhdrs = { {&nhdrs, &nhdrs} };
static H ohdrs = { {&ohdrs, &ohdrs} };
/* Header allocation */
static H *halloc(void)
{
H *h;
if (qempty(H, link, &ohdrs)) {
h = (H *) alitem(&nhdrs, SIZEOF(H));
h->seg = my_valloc(vmem, SEGSIZ);
} else
h = deque_f(H, link, ohdrs.link.next);
h->hole = 0;
h->ehole = SEGSIZ;
h->nlines = 0;
izque(H, link, h);
return h;
}
static void hfree(H *h)
{
enquef(H, link, &ohdrs, h);
}
static void hfreechn(H *h)
{
splicef(H, link, &ohdrs, h);
}
static P frptrs = { {&frptrs, &frptrs} };
/* Pointer allocation */
static P *palloc(void)
{
return (P *)alitem(&frptrs, SIZEOF(P));
}
static void pfree(P *p)
{
enquef(P, link, &frptrs, p);
}
/* Doubly linked list of buffers and free buffer structures */
B bufs = { {&bufs, &bufs} };
static B frebufs = { {&frebufs, &frebufs} };
void set_file_pos_orphaned()
{
B *b;
for (b = bufs.link.next; b != &bufs; b = b->link.next)
if (b->orphan && b->oldcur)
set_file_pos(b->name,b->oldcur->line);
}
/* Find next buffer in list: for multi-file search and replace */
/* This does not bump reference count on found buffer */
B *bafter(B *b)
{
B *first = b;
for (b = b->link.next; b != first && (b->internal || b->scratch || b == &bufs); b = b->link.next);
if (b == first)
return NULL;
else
return b;
}
int udebug_joe(W *w, int k)
{
void debug_stacks(BW*);
char *buf = vsmk(128);
BW *bw;
B *b;
P *p;
WIND_BW(bw, w);
binss(bw->cursor, "Buffers and pointers (the number of pointers per buffer should not grow, except for 20 from markpos):\n\n");
pnextl(bw->cursor);
for (b = bufs.link.next; b != &bufs; b = b->link.next) {
if (b->name)
buf = vsfmt(buf, 0, "Buffer %s\n", b->name);
else
buf = vsfmt(buf, 0, "Buffer 0x%p\n", (void *)b);
binss(bw->cursor, buf);
pnextl(bw->cursor);
for (p = b->bof->link.next; p != b->bof; p = p->link.next) {
buf = vsfmt(buf, 0, " Pointer created by %s\n", p->tracker);
binss(bw->cursor, buf);
pnextl(bw->cursor);
}
}
#ifdef JOEWIN
{
const char *jwQueueMemory();
binss(bw->cursor, jwQueueMemory());
pnextl(bw->cursor);
pnextl(bw->cursor);
pnextl(bw->cursor); /* Three lines of output */
}
#endif
dump_syntax(bw);
return 0;
}
B *bnext(void)
{
B *b;
for (b = bufs.link.prev; b != &bufs; b = b->link.prev)
if (!b->internal)
break;
if (b == &bufs)
return NULL;
deque(B, link, &bufs);
enqueb(B, link, b, &bufs);
return b;
}
B *bprev(void)
{
B *b;
for (b = bufs.link.next; b != &bufs; b = b->link.next)
if (!b->internal)
break;
if (b == &bufs)
return NULL;
deque(B, link, &bufs);
enquef(B, link, b, &bufs);
return b;
}
/* Make a buffer out of a chain */
static B *bmkchn(H *chn, B *prop, off_t amnt, off_t nlines)
{
B *b = (B *)alitem(&frebufs, SIZEOF(B));
b->undo = undomk(b);
if (prop)
b->o = prop->o;
else
b->o = pdefault;
mset((char *)b->marks, 0, SIZEOF(b->marks));
b->rdonly = 0;
b->orphan = 0;
b->oldcur = NULL;
b->err = NULL;
b->oldtop = NULL;
b->current_dir = NULL;
b->shell_flag = 0;
b->backup = 1;
b->internal = 1;
b->scratch = 0;
b->changed = 0;
b->gave_notice = 0;
b->locked = 0;
b->ignored_lock = 0;
b->didfirst = 0;
b->count = 1;
b->name = NULL;
b->er = -3;
b->bof = palloc();
b->mod_time = 0;
b->check_time = time(NULL);
izque(P, link, b->bof);
b->bof->end = 0;
b->bof->b = b;
b->bof->owner = NULL;
b->bof->hdr = chn;
b->bof->ptr = vlock(vmem, b->bof->hdr->seg);
b->bof->ofst = 0;
b->bof->byte = 0;
b->bof->line = 0;
b->bof->col = 0;
b->bof->xcol = 0;
b->bof->valcol = 1;
b->bof->attr = 0;
b->bof->valattr = 1;
b->bof->tracker = "bmkchn";
b->eof = pdup(b->bof, "bmkchn");
b->eof->end = 1;
vunlock(b->eof->ptr);
b->eof->hdr = chn->link.prev;
b->eof->ptr = vlock(vmem, b->eof->hdr->seg);
b->eof->ofst = GSIZE(b->eof->hdr);
b->eof->byte = amnt;
b->eof->line = nlines;
b->eof->valcol = 0;
b->eof->valattr = 0;
b->pid = 0;
b->out = -1;
b->vt = 0;
b->db = 0;
b->parseone = 0;
#ifdef JOEWIN
b->pchanges = 0;
#endif
enquef(B, link, &bufs, b);
pcoalesce(b->bof);
pcoalesce(b->eof);
return b;
}
/* Create an empty buffer */
B *bmk(B *prop)
{
return bmkchn(halloc(), prop, 0L, 0L);
}
/* Eliminate a buffer */
void brm(B *b)
{
#ifdef JOEWIN
notify_deleting_buffer();
#endif
if (b && !--b->count) {
if (b->changed)
abrerr(b->name);
if (b->locked && !b->ignored_lock && plain_file(b))
unlock_it(b->name);
if (b == errbuf)
errbuf = NULL;
if (b->undo)
undorm(b->undo);
if (b->eof) {
hfreechn(b->eof->hdr);
while (!qempty(P, link, b->bof))
prm(b->bof->link.next);
prm(b->bof);
}
if (b->name)
joe_free(b->name);
if (b->db)
rm_all_lattr_db(b->db);
obj_free(b->current_dir);
demote(B, link, &frebufs, b);
}
}
void brmall()
{
while (!qempty(B, link, &bufs))
brm(bufs.link.next);
}
/* Replace contents of b with n. n is destroyed. */
void breplace(B *b, B *n)
{
P *p, *next;
/* Take out many references to b */
abrerr(b->name);
if (b->locked && !b->ignored_lock && plain_file(b)) {
unlock_it(b->name);
b->locked = 0;
}
if (b == errbuf)
errbuf = NULL;
if (b->undo) {
undorm(b->undo);
b->undo = 0;
}
/* Remove all vfile references */
for (p = b->eof->link.next; p != b->eof; p = p->link.next)
if (p->ptr)
vunlock(p->ptr);
if (b->eof->ptr)
vunlock(b->eof->ptr);
/* Delete buffer */
hfreechn(b->eof->hdr);
/* Delete file name */
if (b->name)
joe_free(b->name);
reset_all_lattr_db(b->db);
/* Take new name */
b->name = zdup(n->name);
/* Take bof Pointer */
/* Taking n->bof's vlock */
b->bof->ofst = n->bof->ofst;
b->bof->ptr = n->bof->ptr;
b->bof->hdr = n->bof->hdr;
b->bof->byte = 0;
b->bof->line = 0;
b->bof->col = 0;
b->bof->xcol = 0;
b->bof->valcol = 1;
b->bof->attr = 0;
b->bof->valattr = 1;
b->bof->end = 0;
/* Take eof Pointer */
/* Taking b->eof's vlock */
b->eof->ofst = n->eof->ofst;
b->eof->ptr = n->eof->ptr;
b->eof->hdr = n->eof->hdr;
b->eof->byte = n->eof->byte;
b->eof->line = n->eof->line;
b->eof->col = n->eof->col;
b->eof->xcol = n->eof->xcol;
b->eof->valcol = n->eof->valcol;
b->eof->attr = n->eof->attr;
b->eof->valattr = n->eof->valattr;
b->eof->end = 1;
/* Reset other pointers */
for (p = b->eof->link.next; p != b->eof; p = p->link.next)
if (p != b->bof) {
off_t goal_line = p->line;
off_t goal_col = p->xcol;
p->ptr = 0; /* No need for pset to unlock: we already did it */
if (goal_line > b->eof->line) {
pset(p, b->eof);
p_goto_bol(p);
} else {
pset(p, b->bof);
pline(p, goal_line);
pcol(p, goal_col);
}
}
/* OK, delete pointers from n */
for (p = n->eof->link.next; p != n->eof; p = next) {
next = p->link.next;
if (p != n->bof)
prm(p);
}
/* Delete bof and eof pointers */
/* Don't delete their locks, they were transferred. */
n->bof->ptr = 0;
prm(n->bof);
n->bof = 0;
n->eof->ptr = 0;
prm(n->eof);
n->eof = 0;
b->undo = undomk(b);
b->changed = 0;
b->rdonly = n->rdonly;
b->mod_time = n->mod_time;
/* Delete rest of n */
brm(n);
#ifdef JOEWIN
notify_renamed_buffer(b);
#endif
}
P *poffline(P *p)
{
if (p->ptr) {
vunlock(p->ptr);
p->ptr = NULL;
}
return p;
}
P *ponline(P *p)
{
if (!p->ptr)
p->ptr = vlock(vmem, p->hdr->seg);
return p;
}
B *boffline(B *b)
{
P *p = b->bof;
do {
poffline(p);
} while ((p = p->link.next) != b->bof);
return b;
}
B *bonline(B *b)
{
P *p = b->bof;
do {
ponline(p);
} while ((p = p->link.next) != b->bof);
return b;
}
P *pdup(P *p, const char *tr)
{
P *n = palloc();
n->end = 0;
n->ptr = NULL;
n->owner = NULL;
n->tracker = tr;
enquef(P, link, p, n);
return pset(n, p);
}
P *pdupown(P *p, P **o, const char *tr)
{
P *n = palloc();
n->end = 0;
n->ptr = NULL;
n->owner = o;
n->tracker = tr;
enquef(P, link, p, n);
pset(n, p);
if (*o)
prm(*o);
*o = n;
return n;
}
void prm(P *p)
{
if (!p)
return;
if (p->owner)
*p->owner = NULL;
if (p->ptr)
vunlock(p->ptr);
pfree(deque_f(P, link, p));
}
P *pset(P *n, P *p)
{
if (n != p) {
n->b = p->b;
n->ofst = p->ofst;
n->hdr = p->hdr;
if (n->ptr)
vunlock(n->ptr);
if (p->ptr) {
n->ptr = p->ptr;
vupcount(n->ptr);
} else
n->ptr = vlock(vmem, n->hdr->seg);
n->byte = p->byte;
n->line = p->line;
n->col = p->col;
n->valcol = p->valcol;
n->attr = p->attr;
n->valattr = p->valattr;
}
return n;
}
P *p_goto_bof(P *p)
{
return pset(p, p->b->bof);
}
P *p_goto_eof(P *p)
{
return pset(p, p->b->eof);
}
/* is p at the beginning of file? */
int pisbof(P *p)
{
return p->hdr == p->b->bof->hdr && !p->ofst;
}
/* is p at the end of file? */
int piseof(P *p)
{
return p->ofst == GSIZE(p->hdr);
}
/* is p at the end of line? */
int piseol(P *p)
{
int c;
if (piseof(p))
return 1;
c = brc(p);
if (c == '\n')
return 1;
if (p->b->o.crlf)
if (c == '\r') {
P *q = pdup(p, "piseol");
pfwrd(q, 1L);
if (pgetb(q) == '\n') {
prm(q);
return 1;
} else
prm(q);
}
return 0;
}
/* is p at the beginning of line? */
/* This needs to be fast and should not disturb valcol or valattr. It's used by fixupins(). */
int pisbol(P *p)
{
int c;
if (pisbof(p))
return 1;
if (!p->ofst)
pprev(p);
--p->ofst;
c = GCHAR(p);
if (++p->ofst == GSIZE(p->hdr))
pnext(p);
return c == '\n';
}
/* is p at the beginning of word? */
int pisbow(P *p)
{
P *q = pdup(p, "pisbow");
int c = brch(p);
int d = prgetc(q);
prm(q);
if (joe_isalnum_(p->b->o.charmap,c) && (!joe_isalnum_(p->b->o.charmap,d) || pisbof(p)))
return 1;
else
return 0;
}
/* is p at the end of word? */
int piseow(P *p)
{
P *q = pdup(p, "piseow");
int d = brch(q);
int c = prgetc(q);
prm(q);
if (joe_isalnum_(p->b->o.charmap,c) && (!joe_isalnum_(p->b->o.charmap,d) || piseof(p)))
return 1;
else
return 0;
}
/* is p on the blank line (ie. full of spaces/tabs)? */
int pisblank(P *p)
{
P *q = pdup(p, "pisblank");
p_goto_bol(q);
while (joe_isblank(p->b->o.charmap,brc(q)))
pgetb(q);
if (piseol(q)) {
prm(q);
return 1;
} else {
prm(q);
return 0;
}
}
/* is p at end of line or spaces followed by end of line? */
int piseolblank(P *p)
{
P *q = pdup(p, "piseolblank");
while (joe_isblank(p->b->o.charmap,brc(q)))
pgetb(q);
if (piseol(q)) {
prm(q);
return 1;
} else {
prm(q);
return 0;
}
}
/* return column of first nonblank character */
off_t pisindent(P *p)
{
P *q = pdup(p, "pisindent");
off_t col;
p_goto_bol(q);
while (joe_isblank(p->b->o.charmap,brc(q)))
pgetc(q);
col = q->col;
prm(q);
return col;
}
/* return true if all characters to left of cursor match c */
int pispure(P *p,int c)
{
P *q = pdup(p, "pispure");
if (c < 0)
c += 256;
p_goto_bol(q);
while (q->byte!=p->byte)
if (pgetc(q)!=c) {
prm(q);
return 0;
}
prm(q);
return 1;
}
int pnext(P *p)
{
if (p->hdr == p->b->eof->hdr) {
p->ofst = GSIZE(p->hdr);
return 0;
}
p->hdr = p->hdr->link.next;
p->ofst = 0;
vunlock(p->ptr);
p->ptr = vlock(vmem, p->hdr->seg);
return 1;
}
int pprev(P *p)
{
if (p->hdr == p->b->bof->hdr) {
p->ofst = 0;
return 0;
}
p->hdr = p->hdr->link.prev;
p->ofst = GSIZE(p->hdr);
vunlock(p->ptr);
p->ptr = vlock(vmem, p->hdr->seg);
return 1;
}
/* return current byte and move p to the next byte. column will be unchanged. */
int pgetb(P *p)
{
int c;
if (p->ofst == GSIZE(p->hdr))
return NO_MORE_DATA;
c = GCHAR(p);
if (++p->ofst == GSIZE(p->hdr))
pnext(p);
++p->byte;
if (c == '\n') {
++(p->line);
p->col = 0;
p->valcol = 1;
p->attr = 0;
p->valattr = 1;
} else if (p->b->o.crlf && c == '\r') {
if (brc(p) == '\n')
return pgetb(p);
else {
p->valcol = 0;
p->valattr = 0;
}
} else {
p->valcol = 0;
p->valattr = 0;
}
return c;
}
/* Interned string table for ansi sequences */
HASH *ansi_hash;
struct ansi_entry {
int code;
char *name;
};
struct ansi_entry **ansi_table;
int ansi_siz;
int ansi_len;
int ansi_code(char *s)
{
struct ansi_entry *e;
if (!ansi_hash)
ansi_hash = htmk(128);
e = (struct ansi_entry *)htfind(ansi_hash, s);
if (!e) {
e = (struct ansi_entry *)joe_malloc(SIZEOF(struct ansi_entry));
e->name = zdup(s);
e->code = ansi_len;
htadd(ansi_hash, e->name, e);
if (!ansi_siz)
ansi_table = (struct ansi_entry **)joe_malloc(ansi_siz = 128);
if (ansi_siz == ansi_len)
ansi_table = (struct ansi_entry **)joe_realloc(ansi_table, ansi_siz *= 2);
ansi_table[ansi_len++] = e;
}
return (e->code | ANSI_BIT);
}
char *ansi_string(int code)
{
code &= ~ANSI_BIT;
if (code < 0 || code >= ansi_len)
return 0;
else
return ansi_table[code]->name;
}
#define ANSIMAX 64
/* return current character and move p to the next character. column will be updated if it was valid. */
int pgetc(P *p)
{
if (p->b->o.charmap->type) {
int val;
int valattr;
int c; /* , oc; */
int d;
ptrdiff_t n; /* , m; */
off_t wid;
val = p->valcol; /* Remember if column number was valid */
valattr = p->valattr;
c = pgetb(p); /* Get first byte */
/* oc = c; */
if (c==NO_MORE_DATA)
return c;
if (p->b->o.ansi && c == '\033') { /* Hide ansi */
char buf[ANSIMAX];
int bufx = 0;
buf[bufx++] = TO_CHAR_OK(c);
while ((d = pgetb(p)) != NO_MORE_DATA) {
if (bufx < SIZEOF(buf) - 1)
buf[bufx++] = TO_CHAR_OK(d);
if ((d >= 'A' && d <= 'Z') || (d >= 'a' && d <= 'z') || d == '\n')
break;
}
p->valcol |= val;
p->valattr = 0;
buf[bufx] = 0;
return ansi_code(buf);
}
if ((c & 0x80) == 0x00) { /* One byte */
n = 0;
} else if ((c&0xE0)==0xC0) { /* Two bytes */
n = 1;
c &= 0x1F;
} else if ((c&0xF0)==0xE0) { /* Three bytes */
n = 2;
c &= 0x0F;
} else if ((c&0xF8)==0xF0) { /* Four bytes */
n = 3;
c &= 0x07;
} else if ((c&0xFC)==0xF8) { /* Five bytes */
n = 4;
c &= 0x03;
} else if ((c&0xFE)==0xFC) { /* Six bytes */
n = 5;
c &= 0x01;
} else { /* 128-191, 254, 255: Not a valid UTF-8 start character */
n = 0;
c = 'X';
/* c -= 384; */
}
/* m = n; */
if (n) {
while (n) {
d = brc(p);
if ((d&0xC0)!=0x80)
break;
pgetb(p);
c = ((c<<6)|(d&0x3F));
--n;
}
if (n) { /* FIXME: there was a bad UTF-8 sequence */
/* How to represent this? */
/* pbkwd(p,m-n);
c = oc - 384; */
c = 'X';
wid = 1;
} else if (val)
wid = joe_wcwidth(1,c);
} else {
wid = 1;
}
if (val) { /* Update column no. if it was valid to start with */
p->valcol = 1;
if (c=='\t')
p->col += (p->b->o.tab) - (p->col) % (p->b->o.tab);
else if (c=='\n')
p->col = 0;
else
p->col += wid;
}
p->valattr |= valattr;
return c;
} else {
int c;
if (p->ofst == GSIZE(p->hdr))
return NO_MORE_DATA;
c = GCHAR(p);
if (++p->ofst == GSIZE(p->hdr))
pnext(p);
++p->byte;
if (c == '\n') {
++(p->line);
p->col = 0;
p->valcol = 1;
p->attr = 0;
p->valattr = 1;
} else if (p->b->o.ansi && c == '\033') { /* Hide ansi */
int d;
int v = p->valcol;
char buf[ANSIMAX];
ptrdiff_t bufx = 0;
buf[bufx++] = TO_CHAR_OK(c);
while ((d = pgetb(p)) != NO_MORE_DATA) {
if (bufx < SIZEOF(buf) - 1)
buf[bufx++] = TO_CHAR_OK(d);
if ((d >= 'A' && d <= 'Z') || (d >= 'a' && d <= 'z') || d == '\n')
break;
}
buf[bufx] = 0;
p->valcol |= v;
p->valattr = 0;
return ansi_code(buf);
} else if (p->b->o.crlf && c == '\r') {
if (brc(p) == '\n')
return pgetc(p);
else
++p->col;
} else {
if (c == '\t')
p->col += (p->b->o.tab) - (p->col) % (p->b->o.tab);
else
++(p->col);
}
return c;
}
}
/* move p n bytes forward */
P *pfwrd(P *p, off_t n)
{
if (!n)
return p;
p->valcol = 0;
p->valattr = 0;
do {
if (p->ofst == GSIZE(p->hdr))
do {
if (!p->ofst) {
p->byte += GSIZE(p->hdr);
n -= GSIZE(p->hdr);
p->line += p->hdr->nlines;
}
if (!pnext(p))
return NULL;
} while (n > GSIZE(p->hdr));
if (GCHAR(p) == '\n')
++p->line;
++p->byte;
++p->ofst;
} while (--n);
if (p->ofst == GSIZE(p->hdr))
pnext(p);
return p;
}
/* move p to the previous byte: does not take into account -crlf mode */
static int prgetb1(P *p)
{
int c;
if (!p->ofst)
if (!pprev(p))
return NO_MORE_DATA;
--p->ofst;
c = GCHAR(p);
--p->byte;
p->valcol = 0;
p->valattr = 0;
if (c == '\n')
--p->line;
return c;
}
/* move p to the previous byte */
int prgetb(P *p)
{
int c = prgetb1(p);
if (p->b->o.crlf && c == '\n') {
c = prgetb1(p);
if (c == '\r')
return '\n';
if (c != NO_MORE_DATA)
pgetb(p);
c = '\n';
}
return c;
}
/* move p to the previous character (try to keep col updated) */
int prgetc(P *p)
{
P *q;
int c, left = 6;
off_t startbyte, startcol;
int val = 0;
if (!p->b->o.charmap->type || pisbol(p))
return prgetb(p);
/* Save p for later column calculation */
val = p->valcol;
startbyte = p->byte;
startcol = p->col;
/* Read to start of utf-8 sequence */
do {
c = prgetb(p);
} while (left-- > 0 && (c & 0xC0) == 0x80 && c != NO_MORE_DATA);
if (c == NO_MORE_DATA)
return c;
/* Get full character */
q = pdup(p, "prgetc");
c = pgetc(q);
/* Keep column valid */
if (val && c != '\n' && c != '\t' && q->byte == startbyte) {
p->valcol = 1;
p->col = startcol - joe_wcwidth(1, c);
}
prm(q);
return c;
}
/* move p n bytes backwards */
P *pbkwd(P *p, off_t n)
{
if (!n)
return p;
p->valcol = 0;
p->valattr = 0;
do {
if (!p->ofst)
do {
if (p->ofst) {
p->byte -= p->ofst;
n -= p->ofst;
p->line -= p->hdr->nlines;
}
if (!pprev(p))
return NULL;
} while (n > GSIZE(p->hdr));
--p->ofst;
--p->byte;
if (GCHAR(p) == '\n')
--p->line;
} while (--n);
return p;
}
/* move p n characters forwards/backwards according to loc */
P *pgoto(P *p, off_t loc)
{
if (loc > p->byte)
pfwrd(p, loc - p->byte);
else if (loc < p->byte)
pbkwd(p, p->byte - loc);
return p;
}
/* make p->col valid */
P *pfcol(P *p)
{
off_t pos = p->byte;
p_goto_bol(p);
while (p->byte < pos)
pgetc(p);
return p;
}
/* move p to the beginning of line */
P *p_goto_bol(P *p)
{
if (pprevl(p))
pgetb(p);
p->col = 0;
p->valcol = 1;
p->attr = 0;
p->valattr = 1;
return p;
}
/* move p to the indentation point */
P *p_goto_indent(P *p, int c)
{
int d;
p_goto_bol(p);
while ((d=brc(p)), d==c || ((c==' ' || c=='\t') && (d==' ' || d=='\t')))
pgetc(p);
return p;
}
/* move p to the end of line */
P *p_goto_eol(P *p)
{
if (p->b->o.crlf || p->b->o.charmap->type || p->b->o.ansi)
while (!piseol(p))
pgetc(p);
else
while (p->ofst != GSIZE(p->hdr)) {
int c;
c = GCHAR(p);
if (c == '\n')
break;
else {
++p->byte;
++p->ofst;
if (c == '\t')
p->col += p->b->o.tab - p->col % p->b->o.tab;
else
++p->col;
if (p->ofst == GSIZE(p->hdr))
pnext(p);
}
}
return p;
}
/* move p to the beginning of next line */
P *pnextl(P *p)
{
int c;
do {
if (p->ofst == GSIZE(p->hdr))
do {
p->byte += GSIZE(p->hdr) - p->ofst;
if (!pnext(p))
return NULL;
} while (!p->hdr->nlines);
c = GCHAR(p);
++p->byte;
++p->ofst;
} while (c != '\n');
++p->line;
p->col = 0;
p->valcol = 1;
p->attr = 0;
p->valattr = 1;
if (p->ofst == GSIZE(p->hdr))
pnext(p);
return p;
}
/* move p to the end of previous line */
P *pprevl(P *p)
{
int c;
p->valcol = 0;
p->valattr = 0;
do {
if (!p->ofst)
do {
p->byte -= p->ofst;
if (!pprev(p))
return NULL;
} while (!p->hdr->nlines);
--p->ofst;
--p->byte;
c = GCHAR(p);
} while (c != '\n');
--p->line;
if (p->b->o.crlf && c == '\n') {
int k = prgetb1(p);
if (k != '\r' && k != NO_MORE_DATA)
pgetb(p);
}
return p;
}
/* move p to the given 'line' line */
P *pline(P *p, off_t line)
{
if (line > p->b->eof->line) {
pset(p, p->b->eof);
return p;
}
if (line < oabs(p->line - line)) {
pset(p, p->b->bof);
}
if (oabs(p->b->eof->line - line) < oabs(p->line - line)) {
pset(p, p->b->eof);
}
if (p->line == line) {
p_goto_bol(p);
return p;
}
while (line > p->line)
pnextl(p);
if (line < p->line) {
while (line < p->line)
pprevl(p);
p_goto_bol(p);
}
return p;
}
/* move p to the given 'goalcol' column */
/* lands at exact column or on character which would cause us to go past goalcol */
P *pcol(P *p, off_t goalcol)
{
p_goto_bol(p);
if(p->b->o.charmap->type || p->b->o.ansi) {
do {
int c;
off_t wid;
c = brch(p);
if (c == NO_MORE_DATA)
break;
if (c == '\n')
break;
if (p->b->o.crlf && c == '\r' && piseol(p))
break;
if (c == '\t')
wid = p->b->o.tab - p->col % p->b->o.tab;
else
wid = joe_wcwidth(1,c);
if (p->col + wid > goalcol)
break;
pgetc(p);
} while (p->col != goalcol);
} else {
do {
int c;
off_t wid;
if (p->ofst == GSIZE(p->hdr))
break;
c = GCHAR(p);
if (c == '\n')
break;
if (p->b->o.crlf && c == '\r' && piseol(p))
break;
if (c == '\t')
wid = p->b->o.tab - p->col % p->b->o.tab;
else
wid = 1;
if (p->col + wid > goalcol)
break;
if (++p->ofst == GSIZE(p->hdr))
pnext(p);
++p->byte;
p->col += wid;
} while (p->col != goalcol);
}
return p;
}
/* Move to goal column, then skip backwards to just after first non-whitespace character */
P *pcolwse(P *p, off_t goalcol)
{
int c;
pcol(p, goalcol);
do {
c = prgetc(p);
} while (c == ' ' || c == '\t');
if (c != NO_MORE_DATA)
pgetc(p);
return p;
}
/* Move p to goalcol: stops after first character which equals or exceeds goal col (unlike
pcol() which will stops before character which would exceed goal col) */
P *pcoli(P *p, off_t goalcol)
{
p_goto_bol(p);
if (p->b->o.charmap->type || p->b->o.ansi) {
while (p->col < goalcol) {
int c;
c = brc(p);
if (c == NO_MORE_DATA)
break;
if (c == '\n')
break;
if (p->b->o.crlf && c=='\r' && piseol(p))
break;
pgetc(p);
}
} else {
while (p->col < goalcol) {
int c;
if (p->ofst == GSIZE(p->hdr))
break;
c = GCHAR(p);
if (c == '\n')
break;
if (p->b->o.crlf && c == '\r' && piseol(p))
break;
if (c == '\t')
p->col += p->b->o.tab - p->col % p->b->o.tab;
else
++p->col;
if (++p->ofst == GSIZE(p->hdr))
pnext(p);
++p->byte;
}
}
return p;
}
/* fill space between curent column and 'to' column with tabs/spaces */
void pfill(P *p, off_t to, int usetabs)
{
if (usetabs=='\t')
while (piscol(p) < to)
if (p->col + p->b->o.tab - p->col % p->b->o.tab <= to) {
binsc(p, '\t');
pgetc(p);
} else {
binsc(p, ' ');
pgetc(p);
}
else
while (piscol(p) < to) {
binsc(p, usetabs);
pgetc(p);
}
}
/* delete sequence of whitespaces - backwards */
void pbackws(P *p)
{
int c;
P *q = pdup(p, "pbackws");
do {
c = prgetc(q);
} while (c == ' ' || c == '\t');
if (c != NO_MORE_DATA)
pgetc(q);
bdel(q, p);
prm(q);
}
static int frgetc(P *p)
{
if (!p->ofst)
pprev(p);
--p->ofst;
return GCHAR(p);
}
static void ffwrd(P *p, ptrdiff_t n)
{
while (n > GSIZE(p->hdr) - p->ofst) {
n -= GSIZE(p->hdr) - p->ofst;
if (!pnext(p))
return;
}
if ((p->ofst += n) == GSIZE(p->hdr))
pnext(p);
}
/* forward find pattern 's' in text pointed by 'p' (Boyer-Moore algorithm) */
static P *ffind(P *p, const char *s, ptrdiff_t len)
{
off_t amnt = p->b->eof->byte - p->byte;
ptrdiff_t x;
int c;
ptrdiff_t table[256];
if (len > amnt)
return NULL;
if (!len)
return p;
p->valcol = 0;
p->valattr = 0;
msetD(table, -1, 256);
for (x = 0; x != len - 1; ++x)
table[((const unsigned char *)s)[x]] = x;
ffwrd(p, len);
amnt -= len;
x = len;
do {
if ((c = frgetc(p)) != ((const unsigned char *)s)[--x]) {
if (table[(unsigned char)c] == -1) {
ffwrd(p, len + 1);
amnt -= x + 1;
} else if (x <= table[(unsigned char)c]) {
ffwrd(p, len - x + 1);
--amnt;
} else {
ffwrd(p, len - table[(unsigned char)c]);
amnt -= x - table[(unsigned char)c];
}
if (amnt < 0)
return NULL;
else
x = len;
}
} while (x);
return p;
}
/* Forward find (case insensitive) pattern 's' in text pointed by 'p' (Boyer-Moore algorithm)
Only use this for 8-bit character sets. Do not use for UTF-8. */
static P *fifind(P *p, const char *s, ptrdiff_t len)
{
off_t amnt = p->b->eof->byte - p->byte;
ptrdiff_t x;
struct charmap *map = p->b->o.charmap;
char c;
ptrdiff_t table[256];
if (len > amnt)
return NULL;
if (!len)
return p;
p->valcol = 0;
p->valattr = 0;
msetD(table, -1, 256);
for (x = 0; x != len - 1; ++x)
table[((const unsigned char *)s)[x]] = x;
ffwrd(p, len);
amnt -= len;
x = len;
do {
if ((c = TO_CHAR_OK(joe_tolower(map,frgetc(p)))) != s[--x]) {
if (table[(unsigned char)c] == -1) {
ffwrd(p, len + 1);
amnt -= x + 1;
} else if (x <= table[(unsigned char)c]) {
ffwrd(p, len - x + 1);
--amnt;
} else {
ffwrd(p, len - table[(unsigned char)c]);
amnt -= x - table[(unsigned char)c];
}
if (amnt < 0)
return NULL;
else
x = len;
}
} while (x);
return p;
}
/* move cursor p to q's position and set p's col, line, ofst, byte etc. accordingly */
/* same as rgetto() but p is before q */
static P *getto(P *p, P *q)
{
while (p->hdr != q->hdr || p->ofst != q->ofst) {
if (GCHAR(p) == '\n')
++p->line;
++p->byte;
++p->ofst;
if (p->ofst == GSIZE(p->hdr))
pnext(p);
while (!p->ofst && p->hdr != q->hdr) {
p->byte += GSIZE(p->hdr);
p->line += p->hdr->nlines;
pnext(p);
}
}
return p;
}
/* find forward substring s in text pointed by p and set p after found substring */
P *pfind(P *p, const char *s, ptrdiff_t len)
{
P *q = pdup(p, "pfind");
if (ffind(q, s, len)) {
getto(p, q);
prm(q);
return p;
} else {
prm(q);
return NULL;
}
}
/* same as pfind() but case insensitive */
P *pifind(P *p, const char *s, ptrdiff_t len)
{
P *q = pdup(p, "pifind");
if (fifind(q, s, len)) {
getto(p, q);
prm(q);
return p;
} else {
prm(q);
return NULL;
}
}
static void fbkwd(P *p, ptrdiff_t n)
{
while (n > p->ofst) {
n -= p->ofst;
if (!pprev(p))
return;
}
if (p->ofst >= n)
p->ofst -= n;
else
p->ofst = 0;
}
static int fpgetc(P *p)
{
int c;
if (p->ofst == GSIZE(p->hdr))
return NO_MORE_DATA;
c = GCHAR(p);
if (++p->ofst == GSIZE(p->hdr))
pnext(p);
return c;
}
/* backward find pattern 's' in text pointed by 'p' (Boyer-Moore algorithm) */
static P *frfind(P *p, const char *s, ptrdiff_t len)
{
off_t amnt = p->byte;
ptrdiff_t x;
char c;
ptrdiff_t table[256];
if (len > p->b->eof->byte - p->byte) {
x = len - TO_DIFF_OK(p->b->eof->byte - p->byte);
if (amnt < x)
return NULL;
amnt -= x;
fbkwd(p, x);
}
if (!len)
return p;
p->valcol = 0;
p->valattr = 0;
msetD(table, -1, 256);
for (x = len; --x; table[((const unsigned char *)s)[x]] = len - x - 1) ;
x = 0;
do {
if ((c = TO_CHAR_OK(fpgetc(p))) != s[x++]) {
if (table[(unsigned char)c] == -1) {
fbkwd(p, len + 1);
amnt -= len - x + 1;
} else if (len - table[(unsigned char)(c)] <= x) {
fbkwd(p, x + 1);
--amnt;
} else {
fbkwd(p, len - table[(unsigned char)(c)]);
amnt -= len - table[(unsigned char)(c)] - x;
}
if (amnt < 0)
return NULL;
else
x = 0;
}
} while (x != len);
fbkwd(p, len);
return p;
}
/* backward find (case insensitive) pattern 's' in text pointed by 'p' (Boyer-Moore algorithm)
* Use only for 8-bit character sets. Will not work properly for UTF-8 */
static P *frifind(P *p, const char *s, ptrdiff_t len)
{
off_t amnt = p->byte;
ptrdiff_t x;
char c;
ptrdiff_t table[256];
struct charmap *map = p->b->o.charmap;
if (len > p->b->eof->byte - p->byte) {
x = len - TO_DIFF_OK(p->b->eof->byte - p->byte);
if (amnt < x)
return NULL;
amnt -= x;
fbkwd(p, x);
}
if (!len)
return p;
p->valcol = 0;
p->valattr = 0;
msetD(table, -1, 256);
for (x = len; --x; table[((const unsigned char *)s)[x]] = len - x - 1) ;
x = 0;
do {
if ((c = TO_CHAR_OK(joe_tolower(map,fpgetc(p)))) != s[x++]) {
if (table[(unsigned char)c] == -1) {
fbkwd(p, len + 1);
amnt -= len - x + 1;
} else if (len - table[(unsigned char)c] <= x) {
fbkwd(p, x + 1);
--amnt;
} else {
fbkwd(p, len - table[(unsigned char)c]);
amnt -= len - table[(unsigned char)c] - x;
}
if (amnt < 0)
return NULL;
else
x = 0;
}
} while (x != len);
fbkwd(p, len);
return p;
}
/* move cursor p to q's position and set p's col, line, ofst, byte etc. accordingly */
/* same as getto() but q is before p */
static P *rgetto(P *p, P *q)
{
while (p->hdr != q->hdr || p->ofst != q->ofst) {
if (!p->ofst)
do {
if (p->ofst) {
p->byte -= p->ofst;
p->line -= p->hdr->nlines;
}
pprev(p);
} while (p->hdr != q->hdr);
--p->ofst;
--p->byte;
if (GCHAR(p) == '\n')
--p->line;
}
return p;
}
/* find backward substring s in text pointed by p and set p on the first of found substring */
P *prfind(P *p, const char *s, ptrdiff_t len)
{
P *q = pdup(p, "prfind");
if (frfind(q, s, len)) {
rgetto(p, q);
prm(q);
return p;
} else {
prm(q);
return NULL;
}
}
/* same as prfind() but case insensitive */
P *prifind(P *p, const char *s, ptrdiff_t len)
{
P *q = pdup(p, "prifind");
if (frifind(q, s, len)) {
rgetto(p, q);
prm(q);
return p;
} else {
prm(q);
return NULL;
}
}
/* copy text between 'from' and 'to' into new buffer */
B *bcpy(P *from, P *to)
{
H anchor, *l;
char *ptr;
P *q;
if (from->byte >= to->byte)
return bmk(from->b);
q = pdup(from, "bcpy");
izque(H, link, &anchor);
if (q->hdr == to->hdr) {
l = halloc();
ptr = vlock(vmem, l->seg);
if (q->ofst != q->hdr->hole)
gstgap(q->hdr, q->ptr, q->ofst);
l->nlines = mcnt(q->ptr + q->hdr->ehole, '\n', l->hole = to->ofst - q->ofst);
mmove(ptr, q->ptr + q->hdr->ehole, l->hole);
vchanged(ptr);
vunlock(ptr);
enqueb(H, link, &anchor, l);
} else {
l = halloc();
ptr = vlock(vmem, l->seg);
if (q->ofst != q->hdr->hole)
gstgap(q->hdr, q->ptr, q->ofst);
l->nlines = mcnt(q->ptr + q->hdr->ehole, '\n', l->hole = SEGSIZ - q->hdr->ehole);
mmove(ptr, q->ptr + q->hdr->ehole, l->hole);
vchanged(ptr);
vunlock(ptr);
enqueb(H, link, &anchor, l);
pnext(q);
while (q->hdr != to->hdr) {
l = halloc();
ptr = vlock(vmem, l->seg);
l->nlines = q->hdr->nlines;
mmove(ptr, q->ptr, q->hdr->hole);
mmove(ptr + q->hdr->hole, q->ptr + q->hdr->ehole, SEGSIZ - q->hdr->ehole);
l->hole = GSIZE(q->hdr);
vchanged(ptr);
vunlock(ptr);
enqueb(H, link, &anchor, l);
pnext(q);
}
if (to->ofst) {
l = halloc();
ptr = vlock(vmem, l->seg);
if (to->ofst != to->hdr->hole)
gstgap(to->hdr, to->ptr, to->ofst);
l->nlines = mcnt(to->ptr, '\n', to->ofst);
mmove(ptr, to->ptr, l->hole = to->ofst);
vchanged(ptr);
vunlock(ptr);
enqueb(H, link, &anchor, l);
}
}
l = anchor.link.next;
deque(H, link, &anchor);
prm(q);
return bmkchn(l, from->b, to->byte - from->byte, to->line - from->line);
}
/* Coalesce small blocks into a single larger one */
void pcoalesce(P *p)
{
if (p->hdr != p->b->eof->hdr && GSIZE(p->hdr) + GSIZE(p->hdr->link.next) <= SEGSIZ - SEGSIZ / 4) {
H *hdr = p->hdr->link.next;
char *ptr = vlock(vmem, hdr->seg);
ptrdiff_t osize = GSIZE(p->hdr);
ptrdiff_t size = GSIZE(hdr);
P *q;
gstgap(hdr, ptr, size);
ginsm(p->hdr, p->ptr, GSIZE(p->hdr), ptr, size);
p->hdr->nlines += hdr->nlines;
vunlock(ptr);
hfree(deque_f(H, link, hdr));
for (q = p->link.next; q != p; q = q->link.next)
if (q->hdr == hdr) {
q->hdr = p->hdr;
if (q->ptr) {
vunlock(q->ptr);
q->ptr = vlock(vmem, q->hdr->seg);
}
q->ofst += osize;
}
}
if (p->hdr != p->b->bof->hdr && GSIZE(p->hdr) + GSIZE(p->hdr->link.prev) <= SEGSIZ - SEGSIZ / 4) {
H *hdr = p->hdr->link.prev;
char *ptr = vlock(vmem, hdr->seg);
ptrdiff_t size = GSIZE(hdr);
P *q;
gstgap(hdr, ptr, size);
ginsm(p->hdr, p->ptr, 0, ptr, size);
p->hdr->nlines += hdr->nlines;
vunlock(ptr);
hfree(deque_f(H, link, hdr));
p->ofst += size;
for (q = p->link.next; q != p; q = q->link.next)
if (q->hdr == hdr) {
q->hdr = p->hdr;
if (q->ptr)
vunlock(q->ptr);
q->ptr = vlock(vmem, q->hdr->seg);
} else if (q->hdr == p->hdr)
q->ofst += size;
}
}
/* Delete the text between two pointers from a buffer and return it in a new
* buffer.
*
* This routine calls these functions:
* gstgap - to position gaps
* halloc - to allocate new header/segment pairs
* vlock - virtual memory routines
* vunlock
* vchanged
* vupcount
* mcnt - to count NLs
* snip - queue routines
* enqueb
* splicef
* scrdel - to tell screen update to scroll when NLs are deleted
* bmkchn - to make a buffer out of a chain
*/
/* This is only to be used for bdel() */
static B *bcut(P *from, P *to)
{
H *h, /* The deleted text */
*i;
char *ptr;
P *p;
off_t nlines; /* No. EOLs to delete */
off_t amnt; /* No. bytes to delete */
ptrdiff_t toamnt; /* Amount to delete from segment in 'to' */
int bofmove = 0; /* Set if bof got deleted */
struct lattr_db *db;
if (!(amnt = to->byte - from->byte))
return NULL; /* ...nothing to delete */
nlines = to->line - from->line;
if (from->hdr == to->hdr) { /* Delete is within a single segment */
/* Move gap to deletion point */
if (from->ofst != from->hdr->hole)
gstgap(from->hdr, from->ptr, from->ofst);
/* Store the deleted text */
h = halloc();
ptr = vlock(vmem, h->seg);
mmove(ptr, from->ptr + from->hdr->ehole, (ptrdiff_t) amnt);
h->hole = TO_DIFF_OK(amnt);
h->nlines = TO_DIFF_OK(nlines);
vchanged(ptr);
vunlock(ptr);
/* Delete */
from->hdr->ehole += TO_DIFF_OK(amnt);
from->hdr->nlines -= TO_DIFF_OK(nlines);
toamnt = TO_DIFF_OK(amnt);
} else { /* Delete crosses segments */
H *a;
if ((toamnt = to->ofst) != 0) {
/* Delete beginning of to */
/* Move gap to deletion point */
/* To could be deleted if it's at the end of the file */
if (to->ofst != to->hdr->hole)
gstgap(to->hdr, to->ptr, to->ofst);
/* Save deleted text */
i = halloc();
ptr = vlock(vmem, i->seg);
mmove(ptr, to->ptr, to->hdr->hole);
i->hole = to->hdr->hole;
i->nlines = mcnt(to->ptr, '\n', to->hdr->hole);
vchanged(ptr);
vunlock(ptr);
/* Delete */
to->hdr->nlines -= i->nlines;
to->hdr->hole = 0;
} else
i = 0;
/* Delete end of from */
if (!from->ofst) {
/* ... unless from needs to be deleted too */
a = from->hdr->link.prev;
h = NULL;
if (a == from->b->eof->hdr)
bofmove = 1;
} else {
a = from->hdr;
/* Move gap to deletion point */
if (from->ofst != from->hdr->hole)
gstgap(from->hdr, from->ptr, from->ofst);
/* Save deleted text */
h = halloc();
ptr = vlock(vmem, h->seg);
mmove(ptr, from->ptr + from->hdr->ehole, SEGSIZ - from->hdr->ehole);
h->hole = SEGSIZ - from->hdr->ehole;
h->nlines = mcnt(ptr, '\n', h->hole);
vchanged(ptr);
vunlock(ptr);
/* Delete */
from->hdr->nlines -= h->nlines;
from->hdr->ehole = SEGSIZ;
}
/* Make from point to header/segment of to */
from->hdr = to->hdr;
vunlock(from->ptr);
from->ptr = to->ptr;
vupcount(to->ptr);
from->ofst = 0;
/* Delete headers/segments between a and to->hdr (if there are any) */
if (a->link.next != to->hdr)
if (!h) {
h = snip(H, link, a->link.next, to->hdr->link.prev);
if (i)
enqueb(H, link, h, i);
} else {
splicef(H, link, h, snip(H, link, a->link.next, to->hdr->link.prev));
if (i)
enqueb(H, link, h, i);
} else if (!h)
h = i;
else if (i)
enqueb(H, link, h, i);
}
/* If to is empty, then it must have been at the end of the file. If
the file did not become empty, delete to */
if (!GSIZE(to->hdr) && from->byte) {
H *ph = from->hdr->link.prev;
hfree(deque_f(H, link, from->hdr));
vunlock(from->ptr);
from->hdr = ph;
from->ptr = vlock(vmem, from->hdr->seg);
from->ofst = GSIZE(ph);
vunlock(from->b->eof->ptr);
from->b->eof->ptr = from->ptr;
vupcount(from->ptr);
from->b->eof->hdr = from->hdr;
from->b->eof->ofst = from->ofst;
}
/* The deletion is now done */
/* Scroll if necessary */
if (bofmove)
pset(from->b->bof, from);
for (db = from->b->db; db; db = db->next)
lattr_del(db, from->line, nlines);
if (!pisbol(from)) {
scrdel(from->b, from->line, nlines, 1);
delerr(from->b->name, from->line, nlines);
} else {
scrdel(from->b, from->line, nlines, 0);
delerr(from->b->name, from->line, nlines);
}
/* Fix pointers */
for (p = from->link.next; p != from; p = p->link.next)
if (p->line == from->line && p->byte > from->byte) {
p->valcol = 0;
p->valattr = 0;
}
for (p = from->link.next; p != from; p = p->link.next) {
if (p->byte >= from->byte) {
if (p->byte <= from->byte + amnt) {
if (p->ptr) {
pset(p, from);
} else {
poffline(pset(p, from));
}
} else {
if (p->hdr == to->hdr)
p->ofst -= toamnt;
p->byte -= amnt;
p->line -= nlines;
}
}
}
pcoalesce(from);
/* Make buffer out of deleted text and return it */
return bmkchn(h, from->b, amnt, nlines);
}
void bdel(P *from, P *to)
{
if (to->byte - from->byte) {
B *b = bcut(from, to);
if (from->b->undo)
undodel(from->b->undo, from->byte, b);
else
brm(b);
from->b->changed = 1;
#ifdef JOEWIN
notify_changed_buffer(from->b);
#endif
}
}
/* Split a block at p's ofst */
/* p is placed in the new block such that it points to the same text but with
* p->ofst==0
*/
static void bsplit(P *p)
{
if (p->ofst) {
H *hdr;
char *ptr;
P *pp;
hdr = halloc();
ptr = vlock(vmem, hdr->seg);
if (p->ofst != p->hdr->hole)
gstgap(p->hdr, p->ptr, p->ofst);
mmove(ptr, p->ptr + p->hdr->ehole, SEGSIZ - p->hdr->ehole);
hdr->hole = SEGSIZ - p->hdr->ehole;
hdr->nlines = mcnt(ptr, '\n', hdr->hole);
p->hdr->nlines -= hdr->nlines;
vchanged(ptr);
p->hdr->ehole = SEGSIZ;
enquef(H, link, p->hdr, hdr);
vunlock(p->ptr);
for (pp = p->link.next; pp != p; pp = pp->link.next)
if (pp->hdr == p->hdr && pp->ofst >= p->ofst) {
pp->hdr = hdr;
if (pp->ptr) {
vunlock(pp->ptr);
pp->ptr = ptr;
vupcount(ptr);
}
pp->ofst -= p->ofst;
}
p->ptr = ptr;
p->hdr = hdr;
p->ofst = 0;
}
}
/* Make a chain out of a block of memory (the block must not be empty) */
static H *bldchn(const char *blk, ptrdiff_t size, off_t *nlines)
{
H anchor, *l;
*nlines = 0;
izque(H, link, &anchor);
do {
char *ptr;
ptrdiff_t amnt;
ptr = vlock(vmem, (l = halloc())->seg);
if (size > SEGSIZ)
amnt = SEGSIZ;
else
amnt = size;
mmove(ptr, blk, amnt);
l->hole = amnt;
l->ehole = SEGSIZ;
(*nlines) += (l->nlines = mcnt(ptr, '\n', amnt));
vchanged(ptr);
vunlock(ptr);
enqueb(H, link, &anchor, l);
blk += amnt;
size -= amnt;
} while (size);
l = anchor.link.next;
deque(H, link, &anchor);
return l;
}
/* Insert a chain into a buffer (this does not update pointers) */
static void inschn(P *p, H *a)
{
if (!p->b->eof->byte) { /* P's buffer is empty: replace the empty segment in p with a */
hfree(p->hdr);
p->hdr = a;
vunlock(p->ptr);
p->ptr = vlock(vmem, a->seg);
pset(p->b->bof, p);
p->b->eof->hdr = a->link.prev;
vunlock(p->b->eof->ptr);
p->b->eof->ptr = vlock(vmem, p->b->eof->hdr->seg);
p->b->eof->ofst = GSIZE(p->b->eof->hdr);
} else if (piseof(p)) { /* We're at the end of the file: append a to the file */
p->b->eof->hdr = a->link.prev;
spliceb(H, link, p->b->bof->hdr, a);
vunlock(p->b->eof->ptr);
p->b->eof->ptr = vlock(vmem, p->b->eof->hdr->seg);
p->b->eof->ofst = GSIZE(p->b->eof->hdr);
p->hdr = a;
vunlock(p->ptr);
p->ptr = vlock(vmem, p->hdr->seg);
p->ofst = 0;
} else if (pisbof(p)) { /* We're at the beginning of the file: insert chain and set bof pointer */
p->hdr = spliceb_f(H, link, p->hdr, a);
vunlock(p->ptr);
p->ptr = vlock(vmem, a->seg);
pset(p->b->bof, p);
} else { /* We're in the middle of the file: split and insert */
bsplit(p);
p->hdr = spliceb_f(H, link, p->hdr, a);
vunlock(p->ptr);
p->ptr = vlock(vmem, a->seg);
}
}
static void fixupins(P *p, off_t amnt, off_t nlines, H *hdr, ptrdiff_t hdramnt)
{
P *pp;
struct lattr_db *db;
if (!pisbol(p))
scrins(p->b, p->line, nlines, 1);
else
scrins(p->b, p->line, nlines, 0);
for (db = p->b->db; db; db = db->next)
lattr_ins(db, p->line, nlines);
inserr(p->b->name, p->line, nlines, pisbol(p)); /* FIXME: last arg ??? */
for (pp = p->link.next; pp != p; pp = pp->link.next)
if (pp->line == p->line && (pp->byte > p->byte || (pp->end && pp->byte == p->byte))) {
pp->valcol = 0;
pp->valattr = 0;
}
for (pp = p->link.next; pp != p; pp = pp->link.next)
if (pp->byte == p->byte && !pp->end)
if (pp->ptr)
pset(pp, p);
else
poffline(pset(pp, p));
else if (pp->byte > p->byte || (pp->end && pp->byte == p->byte)) {
pp->byte += amnt;
pp->line += nlines;
if (pp->hdr == hdr)
pp->ofst += hdramnt;
}
if (p->b->undo)
undoins(p->b->undo, p, amnt);
p->b->changed = 1;
#ifdef JOEWIN
notify_changed_buffer(p->b);
#endif
}
/* Insert a buffer at pointer position (the buffer goes away) */
P *binsb(P *p, B *b)
{
if (b->eof->byte) {
P *q = pdup(p, "binsb");
inschn(q, b->bof->hdr);
b->eof->hdr = halloc();
fixupins(q, b->eof->byte, b->eof->line, NULL, 0);
pcoalesce(q);
prm(q);
}
brm(b);
return p;
}
/* insert memory block 'blk' at 'p' */
P *binsm(P *p, const char *blk, ptrdiff_t amnt)
{
ptrdiff_t nlines;
off_t nlines1;
H *h = NULL;
ptrdiff_t hdramnt = 0; /* Only used if h is set */
P *q;
if (!amnt)
return p;
q = pdup(p, "binsm");
if (amnt <= GGAPSZ(q->hdr)) {
h = q->hdr;
hdramnt = amnt;
ginsm(q->hdr, q->ptr, q->ofst, blk, amnt);
q->hdr->nlines += (nlines = mcnt(blk, '\n', amnt));
nlines1 = nlines;
} else if (!q->ofst && q->hdr != q->b->bof->hdr && amnt <= GGAPSZ(q->hdr->link.prev)) {
pprev(q);
ginsm(q->hdr, q->ptr, q->ofst, blk, amnt);
q->hdr->nlines += (nlines = mcnt(blk, '\n', amnt));
nlines1 = nlines;
} else {
H *a = bldchn(blk, amnt, &nlines1);
inschn(q, a);
}
fixupins(q, amnt, nlines1, h, hdramnt);
pcoalesce(q);
prm(q);
return p;
}
/* insert byte 'c' at 'p' */
P *binsbyte(P *p, char c)
{
if (p->b->o.crlf && c == '\n')
return binsm(p, "\r\n", 2);
else
return binsm(p, &c, 1);
}
/* UTF-8 encode a character and insert it */
P *binsc(P *p, int c)
{
if (c >= -128 && c < 0)
c += 256;
if ((c & ANSI_BIT) && p->b->o.ansi) {
char *s = ansi_string(c);
return binsm(p, s, zlen(s));
} else if (c>127 && p->b->o.charmap->type) {
char buf[8];
ptrdiff_t len = utf8_encode(buf,c);
return binsm(p,buf,len);
} else {
char ch = TO_CHAR_OK(c);
if (p->b->o.crlf && c == '\n')
return binsm(p, "\r\n", 2);
else
return binsm(p, &ch, 1);
}
}
/* insert zero-terminated string 's' at 'p' */
P *binss(P *p, const char *s)
{
return binsm(p, s, zlen(s));
}
/* Read 'size' bytes from file or stream. Stops and returns amnt. read
* when requested size has been read or when end of file condition occurs.
* Returns with -2 in error for read error or 0 in error for success.
*/
static ptrdiff_t bkread(int fi, char *buff, ptrdiff_t size)
{
ptrdiff_t a, b;
if (!size) {
berror = 0;
return 0;
}
for (a = b = 0; (a < size) && ((b = joe_read(fi, buff + a, size - a)) > 0); a += b) ;
if (b < 0)
berror = -2;
else
berror = 0;
return a;
}
/* Read up to 'max' bytes from a file into a buffer */
/* Returns with 0 in error or -2 in error for read error */
B *bread(int fi, off_t max)
{
H anchor, *l;
off_t lines = 0, total = 0;
ptrdiff_t amnt;
char *seg;
izque(H, link, &anchor);
berror = 0;
while (seg = vlock(vmem, (l = halloc())->seg), !berror && (amnt = bkread(fi, seg, max >= SEGSIZ ? SEGSIZ : (ptrdiff_t) max))) {
total += amnt;
max -= amnt;
l->hole = amnt;
lines += (l->nlines = mcnt(seg, '\n', amnt));
vchanged(seg);
vunlock(seg);
enqueb(H, link, &anchor, l);
}
hfree(l);
vunlock(seg);
if (!total)
return bmk(NULL);
l = anchor.link.next;
deque(H, link, &anchor);
return bmkchn(l, NULL, total, lines);
}
/* Parse file name.
*
* Removes ',xxx,yyy' from end of name and puts their value into skip and amnt
* Replaces ~user/ with directory of given user
* Replaces ~/ with $HOME
*
* Returns new variable length string.
*/
char *parsens(const char *s, off_t *skip, off_t *amnt)
{
char *n = vsncpy(NULL, 0, sz(s));
ptrdiff_t x;
*skip = 0;
*amnt = MAXLONG;
x = obj_len(n) - 1;
if (x > 0 && n[x] >= '0' && n[x] <= '9') {
for (x = obj_len(n) - 1; x > 0 && ((n[x] >= '0' && n[x] <= '9') || n[x] == 'x' || n[x] == 'X'); --x) ;
if (n[x] == ',' && x && n[x-1] != '\\') {
n[x] = 0;
*skip = ztoo(n + x + 1);
--x;
if (x > 0 && n[x] >= '0' && n[x] <= '9') {
for (; x > 0 && ((n[x] >= '0' && n[x] <= '9') || n[x] == 'x' || n[x] == 'X'); --x) ;
if (n[x] == ',' && x && n[x-1] != '\\') {
n[x] = 0;
*amnt = *skip;
*skip = ztoo(n + x + 1);
}
}
}
}
/* Don't do this here: do it in prompt buffer instead, so we're just like
the shell doing it on the command line. */
/* n = canonical(n); */
return n;
}
/* Canonicalize file name: do ~ expansion */
char *canonical(char *n)
{
ptrdiff_t y = 0;
#ifndef JOEWIN
ptrdiff_t x;
char *s;
for (y = zlen(n); ; --y)
if (y <= 2) {
y = 0;
break;
} else if (n[y-2] == '/' && (n[y-1] == '/' || n[y-1] == '~')) {
y -= 1;
break;
}
if (n[y] == '~') {
for (x = y + 1; n[x] && n[x] != '/'; ++x) ;
if (n[x] == '/') {
if (x == y + 1) {
char *z;
s = getenv("HOME");
z = vsncpy(NULL, 0, sz(s));
z = vsncpy(sv(z), sz(n + x));
n = z;
y = 0;
} else {
struct passwd *passwd;
n[x] = 0;
passwd = getpwnam((n + y + 1));
n[x] = '/';
if (passwd) {
char *z = vsdupz(passwd->pw_dir);
z = vscatz(z, n + x);
n = z;
y = 0;
}
}
}
}
#endif
if (y)
return vsncpy(NULL, 0, n + y, zlen(n + y));
else
return n;
}
static off_t euclid(off_t a, off_t b)
{
if (!a)
return b;
while (b)
if (a > b)
a -= b;
else
b -= a;
return a;
}
/* return column of first nonblank character, but don't count comments */
int found_space;
int found_tab;
static off_t pisindentg(P *p)
{
int i_spc = 0;
int i_tab = 0;
P *q = pdup(p, "pisindentg");
off_t col;
int ch;
p_goto_bol(q);
while (joe_isblank(p->b->o.charmap,ch = brc(q))) {
if (ch == ' ')
i_spc = 1;
else if (ch == '\t')
i_tab = 1;
pgetc(q);
}
col = q->col;
if (ch == '*' || ch == '/' || ch == '-' || ch =='%' || ch == '#' || ch == '\r' || ch == '\n')
col = 0;
if (col) {
found_space |= i_spc;
found_tab |= i_tab;
}
prm(q);
return col;
}
char *dequote(const char *s)
{
static char buf[1024];
#ifdef JOEWIN
/* Backslashes should never mean 'escape' in Windows-land. */
zcpy(buf, s);
return buf;
#else
char *p = buf;
while (*s) {
if (*s =='\\')
++s;
if (*s)
*p++ = *s++;
}
*p = 0;
return buf;
#endif
}
/* Load file into new buffer and return the new buffer */
/* Returns with error set to 0 for success,
* -1 for new file (file doesn't exist)
* -2 for read error
* -3 for seek error
* -4 for open error
*/
B *bload(const char *s)
{
char buffer[SEGSIZ];
FILE *fi = 0;
B *b = 0;
off_t skip, amnt;
char *n;
int nowrite = 0;
P *p;
int x;
time_t mod_time = 0;
struct stat sbuf;
if (!s || !s[0]) {
berror = -1;
b = bmk(NULL);
setopt(b,"");
b->rdonly = b->o.readonly;
b->er = berror;
return b;
}
n = parsens(s, &skip, &amnt);
/* Open file or stream */
#if !defined(__MSDOS__) && !defined(JOEWIN)
if (n[0] == '!') {
nescape(maint->t);
ttclsn();
fi = popen(dequote(n + 1), "r");
} else
#endif
if (!zcmp(n, "-")) {
#ifdef junk
FILE *f;
struct stat y;
fi = stdin;
/* Make sure stdin is not tty */
if (fstat(fileno(fi), &y))
goto no_stat;
if (y.st_mode & S_IFCHR) {
no_stat:
b = bmk(NULL);
goto empty;
}
#endif
/* Now we always just create an empty buffer for "-" */
b = bmk(NULL);
goto empty;
} else {
fi = fopen(dequote(n), "r+");
if (!fi)
nowrite = 1;
else
fclose(fi);
fi = fopen(dequote(n), "r");
if (!fi)
nowrite = 0;
if (fi) {
fstat(fileno(fi),&sbuf);
mod_time = sbuf.st_mtime;
}
}
joesep(n);
/* Abort if couldn't open */
if (!fi) {
if (errno == ENOENT)
berror = -1;
else
berror = -4;
b = bmk(NULL);
setopt(b,n);
b->rdonly = b->o.readonly;
goto opnerr;
}
/* Skip data if we need to */
if (skip && lseek(fileno(fi), skip, 0) < 0) {
ptrdiff_t r;
while (skip > SEGSIZ) {
r = bkread(fileno(fi), buffer, SEGSIZ);
if (r != SEGSIZ || berror) {
berror = -3;
goto err;
}
skip -= SEGSIZ;
}
skip -= bkread(fileno(fi), buffer, (int) skip);
if (skip || berror) {
berror = -3;
goto err;
}
}
/* Read from stream into new buffer */
b = bread(fileno(fi), amnt);
empty:
b->mod_time = mod_time;
setopt(b,n);
b->rdonly = b->o.readonly;
#ifdef JOEWIN
notify_new_buffer(b);
#endif
/* Close stream */
err:
#if !defined(__MSDOS__) && !defined(JOEWIN)
if (s[0] == '!')
pclose(fi);
else
#endif
if (zcmp(n, "-"))
fclose(fi);
opnerr:
if (s[0] == '!') {
ttopnn();
nreturn(maint->t);
}
/* Set name */
b->name = joesep(zdup(s));
/* Set flags */
if (berror || s[0] == '!' || skip || amnt != MAXLONG) {
b->backup = 1;
b->changed = 0;
} else if (!zcmp(n, "-")) {
b->backup = 1;
b->changed = 1;
} else {
b->backup = 0;
b->changed = 0;
}
if (nowrite)
b->rdonly = b->o.readonly = 1;
/* If first line has CR-LF, assume MS-DOS file */
if (guesscrlf) {
p=pdup(b->bof, "bload");
b->o.crlf = 0;
for(x=0;x!=1024;++x) {
int c = pgetc(p);
if(c == '\r') {
b->o.crlf = 1;
break;
}
if(c == '\n') {
b->o.crlf = 0;
break;
}
if(c == NO_MORE_DATA)
break;
}
prm(p);
/* Hex mode should turn off crlf */
if (b->o.hex && b->o.crlf) {
b->o.crlf = 0;
b->o.hex |= HEX_RESTORE_CRLF;
}
}
/* Search backwards through file: if first indented line
is indented with a tab, assume indentc is tab */
if (guessindent) {
int ix, y;
off_t guessed_step = 0;
int hist[20];
off_t i;
off_t hist_val[20];
int nhist = 0;
int old_max;
int max;
int maxi;
found_space = 0;
found_tab = 0;
p=pdup(b->eof, "bload");
/* Create histogram of indentation values */
for (y = 0; y != 50; ++y) {
p_goto_bol(p);
if ((i = pisindentg(p))) {
for (ix = 0; ix != nhist; ++ix)
if (hist_val[ix] == i)
break;
if (ix == nhist && nhist != 20) {
hist[nhist] = 1;
hist_val[nhist] = i;
++nhist;
} else if (ix != nhist) {
++hist[ix];
}
}
if (prgetc(p)==NO_MORE_DATA)
break;
}
/* Find GCM of top 3 most popular indentation values */
old_max = 0;
for (y = 0; y != 3; ++y) {
max = 0;
for (x = 0; x != nhist; ++x)
if (hist[x] > max) {
max = hist[x];
maxi = x;
}
if (max) {
if (!old_max)
old_max = max;
if (guessed_step)
guessed_step = euclid(guessed_step, hist_val[maxi]);
else
guessed_step = hist_val[maxi];
hist[maxi] = 0;
}
}
/* If guessed value is large, scale it down some */
while (!(guessed_step & 1) && guessed_step > 8)
guessed_step >>= 1;
if (found_tab && !found_space) {
b->o.indentc = '\t';
b->o.istep = 1;
} else if (found_space) {
b->o.indentc = ' ';
if (guessed_step)
b->o.istep = guessed_step;
}
prm(p);
}
b->er = berror;
return b;
}
/* Find already loaded buffer or load file into new buffer */
B *bfind(const char *s)
{
B *b;
if (!s || !s[0]) {
berror = -1;
b = bmk(NULL);
setopt(b,"");
b->rdonly = b->o.readonly;
b->internal = 0;
b->er = berror;
return b;
}
for (b = bufs.link.next; b != &bufs; b = b->link.next)
if (b->name && !fullfilecmp(s, b->name)) {
if (!b->orphan)
++b->count; /* Assumes caller is going to put this in a window! */
else
b->orphan = 0;
berror = 0;
b->internal = 0;
return b;
}
b = bload(s); /* Returns count==1 */
b->internal = 0;
return b;
}
/* Find already loaded buffer or load file into new buffer */
B *bfind_scratch(const char *s)
{
B *b;
if (!s || !s[0]) {
berror = -1;
b = bmk(NULL);
setopt(b,"");
b->rdonly = b->o.readonly;
b->internal = 0;
b->er = berror;
return b;
}
for (b = bufs.link.next; b != &bufs; b = b->link.next)
if (b->name && !fullfilecmp(s, b->name)) {
if (!b->orphan)
++b->count;
else
b->orphan = 0;
berror = 0;
b->internal = 0;
return b;
}
b = bmk(NULL);
berror = -1;
setopt(b,s);
b->internal = 0;
b->rdonly = b->o.readonly;
b->er = berror;
b->name = zdup(s);
b->scratch = 1;
#ifdef JOEWIN
notify_renamed_buffer(b);
#endif
return b;
}
B *bfind_reload(const char *s)
{
B *b;
b = bload(s);
b->internal = 0;
return b;
}
B *bcheck_loaded(const char *s)
{
B *b;
if (!s || !s[0]) {
return NULL;
}
for (b = bufs.link.next; b != &bufs; b = b->link.next)
if (b->name && !fullfilecmp(s, b->name)) {
return b;
}
return NULL;
}
char **getbufs(void)
{
char **s = vamk(16);
B *b;
for (b = bufs.link.next; b != &bufs; b = b->link.next)
if (b->name && !b->internal)
s = vaadd(s, vsncpy(NULL, 0, sz(b->name)));
return s;
}
/* Find an orphaned buffer: b->count of returned buffer should be 1. */
B *borphan(void)
{
B *b;
for (b = bufs.link.next; b != &bufs; b = b->link.next)
if (b->orphan && (!b->scratch || b->pid)) {
b->orphan = 0;
return b;
}
return NULL;
}
/* Write 'size' bytes from file beginning at 'p' to open file 'fd'.
* Returns error.
* error is set to -5 for write error or 0 for success.
* Don't attempt to write past the end of the file
*/
int bsavefd(P *p, int fd, off_t size)
{
P *np = pdup(p, "bsavefd");
ptrdiff_t amnt;
while (size > (amnt = GSIZE(np->hdr) - np->ofst)) {
if (np->ofst < np->hdr->hole) {
if (joe_write(fd, np->ptr + np->ofst, np->hdr->hole - np->ofst) < 0)
goto err;
if (joe_write(fd, np->ptr + np->hdr->ehole, SEGSIZ - np->hdr->ehole) < 0)
goto err;
} else if (joe_write(fd, np->ptr + np->ofst + GGAPSZ(np->hdr), amnt) < 0)
goto err;
size -= amnt;
pnext(np);
}
if (size) {
if (np->ofst < np->hdr->hole) {
if (size > np->hdr->hole - np->ofst) {
if (joe_write(fd, np->ptr + np->ofst, np->hdr->hole - np->ofst) < 0)
goto err;
if (joe_write(fd, np->ptr + np->hdr->ehole, (ptrdiff_t) size - np->hdr->hole + np->ofst) < 0)
goto err;
} else {
if (joe_write(fd, np->ptr + np->ofst, (ptrdiff_t) size) < 0)
goto err;
}
} else {
if (joe_write(fd, np->ptr + np->ofst + GGAPSZ(np->hdr), (ptrdiff_t) size) < 0)
goto err;
}
}
prm(np);
return berror = 0;
err:
prm(np);
return berror = -5;
}
/* Save 'size' bytes beginning at 'p' in file 's' */
/* If flag is set, update original time of file if it makes
* sense to do so (it's a normal file, we're saving with
* same name as buffer or is about to get this name).
*
* If flag is set to 2, we update original time even if file
* name changed (i.e., we're renaming the file).
*/
int break_links; /* Set to break hard links on writes */
int break_symlinks; /* Set to break symbolic links and hard links on writes */
int bsave(P *p, const char *as, off_t size, int flag)
{
struct stat sbuf;
const char* filename = as;
int have_stat = 0;
FILE *f;
off_t skip, amnt;
int norm = 0;
char *s = parsens(as, &skip, &amnt);
if (amnt < size)
size = amnt;
#if !defined(__MSDOS__) && !defined(JOEWIN)
if (s[0] == '!') {
nescape(maint->t);
ttclsn();
f = popen(dequote(s + 1), "w");
} else
#endif
if (s[0] == '>' && s[1] == '>') {
filename = dequote(s + 2);
f = fopen(filename, "a");
} else if (!zcmp(s, "-")) {
nescape(maint->t);
ttclsn();
f = stdout;
filename = NULL;
} else if (skip || amnt != MAXLONG) {
filename = dequote(s);
f = fopen(filename, "r+");
} else {
have_stat = !stat(dequote(s), &sbuf);
if (!have_stat)
sbuf.st_mode = 0666;
/* Normal file save */
if (break_links || break_symlinks) {
struct stat lsbuf;
/* Try to copy permissions */
if (!lstat(dequote(s),&lsbuf)) {
int g;
if (!break_symlinks && S_ISLNK(lsbuf.st_mode))
goto nobreak;
#ifdef WITH_SELINUX
security_context_t se;
if (selinux_enabled == -1)
selinux_enabled = (is_selinux_enabled() > 0);
if (selinux_enabled) {
if (getfilecon(dequote(s), &se) < 0) {
berror = -4;
goto opnerr;
}
}
#endif
#ifndef JOEWIN
g = creat(dequote(s), sbuf.st_mode & ~(unsigned)(S_ISUID | S_ISGID));
#else
g = creat(dequote(s), sbuf.st_mode);
#endif
#ifdef WITH_SELINUX
if (selinux_enabled) {
setfilecon(dequote(s), &se);
freecon(se);
}
#endif
close(g);
nobreak:;
} else {
unlink(dequote(s));
}
}
filename = (char *)dequote(s);
f = fopen(filename, "w");
norm = 1;
}
joesep(s);
if (!f) {
berror = -4;
goto opnerr;
}
fflush(f);
if (skip && lseek(fileno(f), skip, 0) < 0) {
berror = -3;
goto err;
}
bsavefd(p, fileno(f), size);
if (!berror && force && size && !skip && amnt == MAXLONG) {
P *q = pdup(p, "bsave");
char nl = '\n';
pfwrd(q, size - 1);
if (brc(q) != '\n' && joe_write(fileno(f), &nl, 1) < 0)
berror = -5;
prm(q);
}
/* Restore setuid bit */
if (!berror && have_stat) {
#ifndef JOEWIN
fchmod(fileno(f), sbuf.st_mode);
#else
chmod(filename, sbuf.st_mode & (S_IREAD | S_IWRITE));
#endif
}
err:
#if !defined(__MSDOS__) && !defined(JOEWIN)
if (s[0] == '!')
pclose(f);
else
#endif
if (zcmp(s, "-"))
fclose(f);
else
fflush(f);
/* Update orignal date of file */
/* If it's not named, it's about to be */
if (!berror && norm && flag && (!p->b->name || flag == 2 || !filecmp(s,p->b->name))) {
if (!stat(dequote(s),&sbuf))
p->b->mod_time = sbuf.st_mtime;
}
opnerr:
if (s[0] == '!' || !zcmp(s,"-")) {
ttopnn();
nreturn(maint->t);
}
return berror;
}
/* Return byte at p: (it's OK to use this on UTF-8 if we are just feeding the result into joe_isblank) */
int brc(P *p)
{
if (p->ofst == GSIZE(p->hdr))
return NO_MORE_DATA;
return GCHAR(p);
}
/* Return character at p */
int brch(P *p)
{
if (p->b->o.charmap->type) {
P *q = pdup(p, "brch");
int c = pgetc(q);
prm(q);
return c;
} else {
return brc(p);
}
}
char *brmem(P *p, char *blk, ptrdiff_t size)
{
char *bk = blk;
P *np;
ptrdiff_t amnt;
np = pdup(p, "brmem");
while (size > (amnt = GSIZE(np->hdr) - np->ofst)) {
grmem(np->hdr, np->ptr, np->ofst, bk, amnt);
bk += amnt;
size -= amnt;
pnext(np);
}
if (size)
grmem(np->hdr, np->ptr, np->ofst, bk, size);
prm(np);
return blk;
}
char *brs(P *p, ptrdiff_t size)
{
char *s = (char *)joe_malloc(size + 1);
s[size] = 0;
return brmem(p, s, size);
}
char *brvs(char *s, P *p, ptrdiff_t size)
{
s = vstrunc(s, size);
return brmem(p, s, size);
}
char *brzs(char *buf, P *p)
{
P *q=pdup(p, "brzs");
size_t size;
p_goto_eol(q);
size = q->byte - p->byte;
prm(q);
buf = vsensure(buf, size);
brmem(p,buf,size);
buf[size]=0;
obj_len(buf) = size;
return buf;
}
/* Save edit buffers when editor dies */
static int ttsig_handled = 0;
RETSIGTYPE ttsig(int sig)
{
FILE *ttsig_f = 0;
time_t tim = time(NULL);
B *b;
int tmpfd;
#ifdef JOEWIN
/* Allow us to inspect */
assert(0);
#endif
/* Do not allow double-fault */
if (ttsig_handled)
_exit(1);
ttsig_handled = 1;
if (nodeadjoe)
goto skipfile;
if ((tmpfd = open("DEADJOE", O_RDWR | O_EXCL | O_CREAT, 0600)) < 0) {
#ifndef JOEWIN
struct stat sbuf;
if (lstat("DEADJOE", &sbuf) < 0)
_exit(1);
if (!S_ISREG(sbuf.st_mode) || sbuf.st_uid != geteuid())
_exit(1);
#endif
/*
A race condition still exists between the lstat() and the open()
systemcall, which leads to a possible denial-of-service attack
by setting the file access mode to 600 for every file the
user executing joe has permissions to.
This can't be fixed w/o breacking the behavior of the orig. joe!
*/
if ((tmpfd = open("DEADJOE", O_RDWR | O_APPEND)) < 0)
_exit(1);
#ifndef JOEWIN
if (fchmod(tmpfd, S_IRUSR | S_IWUSR) < 0)
_exit(1);
#endif
}
if ((ttsig_f = fdopen(tmpfd, "a")) == NULL)
_exit(1);
/* Do not use joe_gettext() here or you might get an infinite loop */
fprintf(ttsig_f, "\n*** These modified files were found in JOE when it aborted on %s", ctime(&tim));
if (sig == -2)
fprintf(ttsig_f, "*** JOE was aborted due to swap file I/O error\n");
else if (sig == -1)
fprintf(ttsig_f, "*** JOE was aborted due to malloc returning NULL\n");
#ifdef JOEWIN
else if (sig < 0)
fprintf(ttsig_f, "*** JOE was aborted by Windows error %X\n", sig);
#endif
else if (sig)
fprintf(ttsig_f, "*** JOE was aborted by UNIX signal %d\n", sig);
else
fprintf(ttsig_f, "*** JOE was aborted because the terminal closed\n");
fflush(ttsig_f);
for (b = bufs.link.next; b != &bufs; b = b->link.next)
if (b->changed) {
if (b->name)
fprintf(ttsig_f, joe_gettext(_("\n*** File \'%s\'\n")), b->name);
else
fputs(joe_gettext(_("\n*** File \'(Unnamed)\'\n")), ttsig_f);
fflush(ttsig_f);
bsavefd(b->bof, fileno(ttsig_f), b->eof->byte);
}
skipfile:
if (sig)
ttclsn();
if (sig == -2) {
fprintf(stderr,"\n*** JOE was aborted due to swap file I/O error\n");
} else if (sig == -1) {
fprintf(stderr,"\n*** JOE was aborted due to malloc returning NULL.");
if (nodeadjoe) {
fprintf(stderr, "\n");
} else {
fprintf(stderr, " Buffers saved in DEADJOE\n");
}
} else if (sig) {
fprintf(stderr,"\n*** JOE was aborted by UNIX signal %d.", sig);
if (nodeadjoe) {
fprintf(stderr, "\n");
} else {
fprintf(stderr, " Buffers saved in DEADJOE\n");
}
}
_exit(1);
}
/* Create lock for a file
Return 0 for success or -1 for failure
*/
int lock_it(const char *qpath,char *bf)
{
#ifdef JOEWIN
return 0;
#else
char *path = dequote(qpath);
char *lock_name=dirprt(path);
char *name=namprt(path);
char *buf;
const char *user = getenv("USER");
const char *host = getenv("HOSTNAME");
if (!user) user="me";
if (!host) host="here";
lock_name=vscat(lock_name,sc(".#"));
lock_name=vscat(lock_name,sv(name));
buf=vsfmt(NULL,0,"%s@%s.%d",user,host,getpid());
/* Fail only if there was an existing lock */
if (!symlink(buf,lock_name) || errno != EEXIST) {
obj_free(lock_name);
return 0;
}
if (bf) {
ssize_t len = readlink(lock_name,bf,255);
if (len<0) len = 0;
bf[len] = 0;
}
obj_free(lock_name);
return -1;
#endif
}
void unlock_it(const char *qpath)
{
#ifndef JOEWIN
char *path = dequote(qpath);
char *lock_name=dirprt(path);
char *name=namprt(path);
lock_name=vscat(lock_name,sc(".#"));
lock_name=vscat(lock_name,sv(name));
unlink(lock_name);
obj_free(lock_name);
#endif
}
/* True if file is regular */
int plain_file(B *b)
{
if (b->name && zcmp(b->name,"-") && b->name[0]!='!' && b->name[0]!='>' &&
!b->scratch)
return 1;
else
return 0;
}
/* True if file changed under us */
int check_mod(B *b)
{
struct stat sbuf;
if (!plain_file(b))
return 0;
if (!stat(b->name,&sbuf)) {
if (sbuf.st_mtime>b->mod_time) {
return 1;
}
}
return 0;
}
/* True if file exists */
int file_exists(const char *path)
{
struct stat sbuf;
if (!path) return 0;
return !stat(path, &sbuf);
}