2003-12-11 17:50:28 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <draw.h>
|
|
|
|
#include <thread.h>
|
|
|
|
#include <cursor.h>
|
|
|
|
#include <mouse.h>
|
|
|
|
#include <keyboard.h>
|
|
|
|
#include <frame.h>
|
|
|
|
#include <fcall.h>
|
|
|
|
#include <plumb.h>
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
|
|
|
|
static Point prevmouse;
|
|
|
|
static Window *mousew;
|
|
|
|
|
2004-03-26 01:59:35 +00:00
|
|
|
Range
|
|
|
|
range(int q0, int q1)
|
|
|
|
{
|
|
|
|
Range r;
|
|
|
|
|
|
|
|
r.q0 = q0;
|
|
|
|
r.q1 = q1;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2004-03-25 23:03:57 +00:00
|
|
|
Runestr
|
|
|
|
runestr(Rune *r, uint n)
|
|
|
|
{
|
|
|
|
Runestr rs;
|
|
|
|
|
|
|
|
rs.r = r;
|
|
|
|
rs.nr = n;
|
|
|
|
return rs;
|
|
|
|
}
|
|
|
|
|
2003-12-11 17:50:28 +00:00
|
|
|
void
|
|
|
|
cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
|
|
|
|
{
|
|
|
|
uchar *q;
|
|
|
|
Rune *s;
|
|
|
|
int j, w;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always guaranteed that n bytes may be interpreted
|
|
|
|
* without worrying about partial runes. This may mean
|
|
|
|
* reading up to UTFmax-1 more bytes than n; the caller
|
|
|
|
* knows this. If n is a firm limit, the caller should
|
|
|
|
* set p[n] = 0.
|
|
|
|
*/
|
|
|
|
q = (uchar*)p;
|
|
|
|
s = r;
|
|
|
|
for(j=0; j<n; j+=w){
|
|
|
|
if(*q < Runeself){
|
|
|
|
w = 1;
|
|
|
|
*s = *q++;
|
|
|
|
}else{
|
|
|
|
w = chartorune(s, (char*)q);
|
|
|
|
q += w;
|
|
|
|
}
|
|
|
|
if(*s)
|
|
|
|
s++;
|
|
|
|
else if(nulls)
|
|
|
|
*nulls = TRUE;
|
|
|
|
}
|
|
|
|
*nb = (char*)q-p;
|
|
|
|
*nr = s-r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
error(char *s)
|
|
|
|
{
|
|
|
|
fprint(2, "acme: %s: %r\n", s);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
Window*
|
|
|
|
errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
|
|
|
|
{
|
|
|
|
Window *w;
|
|
|
|
Rune *r;
|
|
|
|
int i, n;
|
|
|
|
static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 };
|
|
|
|
|
2004-02-29 22:10:26 +00:00
|
|
|
r = runemalloc(ndir+8);
|
|
|
|
if(n = ndir){ /* assign = */
|
2003-12-11 17:50:28 +00:00
|
|
|
runemove(r, dir, ndir);
|
2004-02-29 22:10:26 +00:00
|
|
|
r[n++] = L'/';
|
|
|
|
}
|
2003-12-11 17:50:28 +00:00
|
|
|
runemove(r+n, Lpluserrors, 7);
|
|
|
|
n += 7;
|
|
|
|
w = lookfile(r, n);
|
|
|
|
if(w == nil){
|
|
|
|
if(row.ncol == 0)
|
|
|
|
if(rowadd(&row, nil, -1) == nil)
|
|
|
|
error("can't create column to make error window");
|
|
|
|
w = coladd(row.col[row.ncol-1], nil, nil, -1);
|
|
|
|
w->filemenu = FALSE;
|
|
|
|
winsetname(w, r, n);
|
|
|
|
}
|
|
|
|
free(r);
|
|
|
|
for(i=nincl; --i>=0; ){
|
|
|
|
n = runestrlen(incl[i]);
|
|
|
|
r = runemalloc(n);
|
|
|
|
runemove(r, incl[i], n);
|
|
|
|
winaddincl(w, r, n);
|
|
|
|
}
|
2004-02-29 22:10:26 +00:00
|
|
|
w->autoindent = globalautoindent;
|
2003-12-11 17:50:28 +00:00
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make new window, if necessary; return with it locked */
|
|
|
|
Window*
|
2004-02-29 22:10:26 +00:00
|
|
|
errorwin(Mntdir *md, int owner)
|
2003-12-11 17:50:28 +00:00
|
|
|
{
|
|
|
|
Window *w;
|
|
|
|
|
|
|
|
for(;;){
|
|
|
|
if(md == nil)
|
|
|
|
w = errorwin1(nil, 0, nil, 0);
|
|
|
|
else
|
|
|
|
w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
|
2004-02-29 22:10:26 +00:00
|
|
|
winlock(w, owner);
|
2003-12-11 17:50:28 +00:00
|
|
|
if(w->col != nil)
|
|
|
|
break;
|
|
|
|
/* window was deleted too fast */
|
2004-02-29 22:10:26 +00:00
|
|
|
winunlock(w);
|
2003-12-11 17:50:28 +00:00
|
|
|
}
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
2004-02-29 22:10:26 +00:00
|
|
|
typedef struct Warning Warning;
|
|
|
|
|
|
|
|
struct Warning{
|
|
|
|
Mntdir *md;
|
|
|
|
Buffer buf;
|
|
|
|
Warning *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
static Warning *warnings;
|
|
|
|
|
|
|
|
static
|
|
|
|
void
|
|
|
|
addwarningtext(Mntdir *md, Rune *r, int nr)
|
|
|
|
{
|
|
|
|
Warning *warn;
|
|
|
|
|
|
|
|
for(warn = warnings; warn; warn=warn->next){
|
|
|
|
if(warn->md == md){
|
|
|
|
bufinsert(&warn->buf, warn->buf.nc, r, nr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
warn = emalloc(sizeof(Warning));
|
|
|
|
warn->next = warnings;
|
2004-03-25 23:03:57 +00:00
|
|
|
warn->md = md;
|
|
|
|
if(md)
|
|
|
|
fsysincid(md);
|
2004-02-29 22:10:26 +00:00
|
|
|
warnings = warn;
|
|
|
|
bufinsert(&warn->buf, 0, r, nr);
|
2004-03-25 23:03:57 +00:00
|
|
|
nbsendp(cwarn, 0);
|
2004-02-29 22:10:26 +00:00
|
|
|
}
|
|
|
|
|
2004-03-25 23:03:57 +00:00
|
|
|
/* called while row is locked */
|
2004-02-29 22:10:26 +00:00
|
|
|
void
|
2004-03-25 23:03:57 +00:00
|
|
|
flushwarnings(void)
|
2003-12-11 17:50:28 +00:00
|
|
|
{
|
2004-02-29 22:10:26 +00:00
|
|
|
Warning *warn, *next;
|
2003-12-11 17:50:28 +00:00
|
|
|
Window *w;
|
|
|
|
Text *t;
|
2004-02-29 22:10:26 +00:00
|
|
|
int owner, nr, q0, n;
|
|
|
|
Rune *r;
|
2003-12-11 17:50:28 +00:00
|
|
|
|
|
|
|
if(row.ncol == 0){ /* really early error */
|
|
|
|
rowinit(&row, screen->clipr);
|
|
|
|
rowadd(&row, nil, -1);
|
|
|
|
rowadd(&row, nil, -1);
|
|
|
|
if(row.ncol == 0)
|
2004-02-29 22:10:26 +00:00
|
|
|
error("initializing columns in flushwarnings()");
|
2003-12-11 17:50:28 +00:00
|
|
|
}
|
|
|
|
|
2004-02-29 22:10:26 +00:00
|
|
|
for(warn=warnings; warn; warn=next) {
|
|
|
|
w = errorwin(warn->md, 'E');
|
|
|
|
t = &w->body;
|
|
|
|
owner = w->owner;
|
|
|
|
if(owner == 0)
|
|
|
|
w->owner = 'E';
|
|
|
|
wincommit(w, t);
|
|
|
|
/*
|
|
|
|
* Most commands don't generate much output. For instance,
|
|
|
|
* Edit ,>cat goes through /dev/cons and is already in blocks
|
|
|
|
* because of the i/o system, but a few can. Edit ,p will
|
|
|
|
* put the entire result into a single hunk. So it's worth doing
|
|
|
|
* this in blocks (and putting the text in a buffer in the first
|
|
|
|
* place), to avoid a big memory footprint.
|
|
|
|
*/
|
|
|
|
r = fbufalloc();
|
|
|
|
q0 = t->file->b.nc;
|
|
|
|
for(n = 0; n < warn->buf.nc; n += nr){
|
|
|
|
nr = warn->buf.nc - n;
|
|
|
|
if(nr > RBUFSIZE)
|
|
|
|
nr = RBUFSIZE;
|
|
|
|
bufread(&warn->buf, n, r, nr);
|
|
|
|
textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr);
|
|
|
|
}
|
|
|
|
textshow(t, q0, t->file->b.nc, 1);
|
|
|
|
free(r);
|
|
|
|
winsettag(t->w);
|
|
|
|
textscrdraw(t);
|
|
|
|
w->owner = owner;
|
|
|
|
w->dirty = FALSE;
|
2003-12-11 17:50:28 +00:00
|
|
|
winunlock(w);
|
2004-02-29 22:10:26 +00:00
|
|
|
bufclose(&warn->buf);
|
|
|
|
next = warn->next;
|
2004-03-25 23:03:57 +00:00
|
|
|
if(warn->md)
|
|
|
|
fsysdelid(warn->md);
|
2004-02-29 22:10:26 +00:00
|
|
|
free(warn);
|
|
|
|
}
|
|
|
|
warnings = nil;
|
2003-12-11 17:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
warning(Mntdir *md, char *s, ...)
|
|
|
|
{
|
|
|
|
Rune *r;
|
|
|
|
va_list arg;
|
|
|
|
|
|
|
|
va_start(arg, s);
|
|
|
|
r = runevsmprint(s, arg);
|
|
|
|
va_end(arg);
|
2004-02-29 22:10:26 +00:00
|
|
|
if(r == nil)
|
|
|
|
error("runevsmprint failed");
|
|
|
|
addwarningtext(md, r, runestrlen(r));
|
2003-12-11 17:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
|
|
|
|
{
|
|
|
|
if(n1 != n2)
|
|
|
|
return FALSE;
|
|
|
|
return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint
|
|
|
|
min(uint a, uint b)
|
|
|
|
{
|
|
|
|
if(a < b)
|
|
|
|
return a;
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint
|
|
|
|
max(uint a, uint b)
|
|
|
|
{
|
|
|
|
if(a > b)
|
|
|
|
return a;
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
|
|
|
runetobyte(Rune *r, int n)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
if(r == nil)
|
|
|
|
return nil;
|
|
|
|
s = emalloc(n*UTFmax+1);
|
|
|
|
setmalloctag(s, getcallerpc(&r));
|
|
|
|
snprint(s, n*UTFmax+1, "%.*S", n, r);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
Rune*
|
|
|
|
bytetorune(char *s, int *ip)
|
|
|
|
{
|
|
|
|
Rune *r;
|
|
|
|
int nb, nr;
|
|
|
|
|
|
|
|
nb = strlen(s);
|
|
|
|
r = runemalloc(nb+1);
|
|
|
|
cvttorunes(s, nb, r, &nb, &nr, nil);
|
|
|
|
r[nr] = '\0';
|
|
|
|
*ip = nr;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
isalnum(Rune c)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Hard to get absolutely right. Use what we know about ASCII
|
|
|
|
* and assume anything above the Latin control characters is
|
|
|
|
* potentially an alphanumeric.
|
|
|
|
*/
|
|
|
|
if(c <= ' ')
|
|
|
|
return FALSE;
|
|
|
|
if(0x7F<=c && c<=0xA0)
|
|
|
|
return FALSE;
|
|
|
|
if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rgetc(void *v, uint n)
|
|
|
|
{
|
|
|
|
return ((Rune*)v)[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
tgetc(void *a, uint n)
|
|
|
|
{
|
|
|
|
Text *t;
|
|
|
|
|
|
|
|
t = a;
|
|
|
|
if(n >= t->file->b.nc)
|
|
|
|
return 0;
|
|
|
|
return textreadc(t, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
Rune*
|
|
|
|
skipbl(Rune *r, int n, int *np)
|
|
|
|
{
|
2004-03-05 05:53:11 +00:00
|
|
|
while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
|
2003-12-11 17:50:28 +00:00
|
|
|
--n;
|
|
|
|
r++;
|
|
|
|
}
|
|
|
|
*np = n;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
Rune*
|
|
|
|
findbl(Rune *r, int n, int *np)
|
|
|
|
{
|
2004-03-05 06:03:26 +00:00
|
|
|
while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
|
2003-12-11 17:50:28 +00:00
|
|
|
--n;
|
|
|
|
r++;
|
|
|
|
}
|
|
|
|
*np = n;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
savemouse(Window *w)
|
|
|
|
{
|
|
|
|
prevmouse = mouse->xy;
|
|
|
|
mousew = w;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
restoremouse(Window *w)
|
|
|
|
{
|
|
|
|
if(mousew!=nil && mousew==w)
|
|
|
|
moveto(mousectl, prevmouse);
|
|
|
|
mousew = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
clearmouse()
|
|
|
|
{
|
|
|
|
mousew = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
char*
|
|
|
|
estrdup(char *s)
|
|
|
|
{
|
|
|
|
char *t;
|
|
|
|
|
|
|
|
t = strdup(s);
|
|
|
|
if(t == nil)
|
|
|
|
error("strdup failed");
|
|
|
|
setmalloctag(t, getcallerpc(&s));
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
emalloc(uint n)
|
|
|
|
{
|
|
|
|
void *p;
|
|
|
|
|
|
|
|
p = malloc(n);
|
|
|
|
if(p == nil){
|
|
|
|
fprint(2, "allocating %d from %lux: %r\n", n, getcallerpc(&n));
|
|
|
|
*(int*)0=0;
|
|
|
|
error("malloc failed");
|
|
|
|
}
|
|
|
|
setmalloctag(p, getcallerpc(&n));
|
|
|
|
memset(p, 0, n);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
erealloc(void *p, uint n)
|
|
|
|
{
|
|
|
|
p = realloc(p, n);
|
|
|
|
if(p == nil){
|
|
|
|
fprint(2, "reallocating %d: %r\n", n);
|
|
|
|
error("realloc failed");
|
|
|
|
}
|
|
|
|
setmalloctag(p, getcallerpc(&n));
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Heuristic city.
|
|
|
|
*/
|
|
|
|
Window*
|
|
|
|
makenewwindow(Text *t)
|
|
|
|
{
|
|
|
|
Column *c;
|
|
|
|
Window *w, *bigw, *emptyw;
|
|
|
|
Text *emptyb;
|
|
|
|
int i, y, el;
|
|
|
|
|
|
|
|
if(activecol)
|
|
|
|
c = activecol;
|
|
|
|
else if(seltext && seltext->col)
|
|
|
|
c = seltext->col;
|
|
|
|
else if(t && t->col)
|
|
|
|
c = t->col;
|
|
|
|
else{
|
|
|
|
if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
|
|
|
|
error("can't make column");
|
|
|
|
c = row.col[row.ncol-1];
|
|
|
|
}
|
|
|
|
activecol = c;
|
|
|
|
if(t==nil || t->w==nil || c->nw==0)
|
|
|
|
return coladd(c, nil, nil, -1);
|
|
|
|
|
|
|
|
/* find biggest window and biggest blank spot */
|
|
|
|
emptyw = c->w[0];
|
|
|
|
bigw = emptyw;
|
|
|
|
for(i=1; i<c->nw; i++){
|
|
|
|
w = c->w[i];
|
|
|
|
/* use >= to choose one near bottom of screen */
|
|
|
|
if(w->body.fr.maxlines >= bigw->body.fr.maxlines)
|
|
|
|
bigw = w;
|
|
|
|
if(w->body.fr.maxlines-w->body.fr.nlines >= emptyw->body.fr.maxlines-emptyw->body.fr.nlines)
|
|
|
|
emptyw = w;
|
|
|
|
}
|
|
|
|
emptyb = &emptyw->body;
|
|
|
|
el = emptyb->fr.maxlines-emptyb->fr.nlines;
|
|
|
|
/* if empty space is big, use it */
|
|
|
|
if(el>15 || (el>3 && el>(bigw->body.fr.maxlines-1)/2))
|
|
|
|
y = emptyb->fr.r.min.y+emptyb->fr.nlines*font->height;
|
|
|
|
else{
|
|
|
|
/* if this window is in column and isn't much smaller, split it */
|
|
|
|
if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
|
|
|
|
bigw = t->w;
|
|
|
|
y = (bigw->r.min.y + bigw->r.max.y)/2;
|
|
|
|
}
|
|
|
|
w = coladd(c, nil, nil, y);
|
|
|
|
if(w->body.fr.maxlines < 2)
|
|
|
|
colgrow(w->col, w, 1);
|
|
|
|
return w;
|
|
|
|
}
|