Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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;

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);
        remove(acmeerrorfile);
        abort();
}

Window*
errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
{
        Window *w;
        Rune *r;
        int i, n;

        r = runemalloc(ndir+8);
        if(n = ndir){   /* assign = */
                runemove(r, dir, ndir);
                r[n++] = L'/';
        }
        runemove(r+n, L"+Errors", 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);
        }
        w->autoindent = globalautoindent;
        return w;
}

/* make new window, if necessary; return with it locked */
Window*
errorwin(Mntdir *md, int owner)
{
        Window *w;

        for(;;){
                if(md == nil)
                        w = errorwin1(nil, 0, nil, 0);
                else
                        w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
                winlock(w, owner);
                if(w->col != nil)
                        break;
                /* window was deleted too fast */
                winunlock(w);
        }
        return w;
}

/*
 * Incoming window should be locked. 
 * It will be unlocked and returned window
 * will be locked in its place.
 */
Window*
errorwinforwin(Window *w)
{
        int i, n, nincl, owner;
        Rune **incl;
        Runestr dir;
        Text *t;

        t = &w->body;
        dir = dirname(t, nil, 0);
        if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
                free(dir.r);
                dir.r = nil;
                dir.nr = 0;
        }
        incl = nil;
        nincl = w->nincl;
        if(nincl > 0){
                incl = emalloc(nincl*sizeof(Rune*));
                for(i=0; i<nincl; i++){
                        n = runestrlen(w->incl[i]);
                        incl[i] = runemalloc(n+1);
                        runemove(incl[i], w->incl[i], n);
                }
        }
        owner = w->owner;
        winunlock(w);
        for(;;){
                w = errorwin1(dir.r, dir.nr, incl, nincl);
                winlock(w, owner);
                if(w->col != nil)
                        break;
                /* window deleted too fast */
                winunlock(w);
        }
        return w;
}

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;
        warn->md = md;
        if(md)
                fsysincid(md);
        warnings = warn;
        bufinsert(&warn->buf, 0, r, nr);
        nbsendp(cwarn, 0);
}

/* called while row is locked */
void
flushwarnings(void)
{
        Warning *warn, *next;
        Window *w;
        Text *t;
        int owner, nr, q0, n;
        Rune *r;

        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->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->nc, r, nr, TRUE, &nr);
                }
                textshow(t, q0, t->file->nc, 1);
                free(r);
                winsettag(t->w);
                textscrdraw(t);
                w->owner = owner;
                w->dirty = FALSE;
                winunlock(w);
                bufclose(&warn->buf);
                next = warn->next;
                if(warn->md)
                        fsysdelid(warn->md);
                free(warn);
        }
        warnings = nil;
}

void
warning(Mntdir *md, char *s, ...)
{
        Rune *r;
        va_list arg;

        va_start(arg, s);
        r = runevsmprint(s, arg);
        va_end(arg);
        if(r == nil)
                error("runevsmprint failed");
        addwarningtext(md, r, runestrlen(r));
        free(r);
}

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.
         *
         * Treat 0xA0 (non-breaking space) as a special alphanumeric
         * character [sape]
         */
        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->nc)
                return 0;
        return textreadc(t, n);
}

Rune*
skipbl(Rune *r, int n, int *np)
{
        while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
                --n;
                r++;
        }
        *np = n;
        return r;
}

Rune*
findbl(Rune *r, int n, int *np)
{
        while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
                --n;
                r++;
        }
        *np = n;
        return r;
}

void
savemouse(Window *w)
{
        prevmouse = mouse->xy;
        mousew = w;
}

int
restoremouse(Window *w)
{
        int did;

        did = 0;
        if(mousew!=nil && mousew==w){
                moveto(mousectl, prevmouse);
                did = 1;
        }
        mousew = nil;
        return did;
}

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)
                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)
                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.maxlines >= bigw->body.maxlines)
                        bigw = w;
                if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines)
                        emptyw = w;
        }
        emptyb = &emptyw->body;
        el = emptyb->maxlines-emptyb->nlines;
        /* if empty space is big, use it */
        if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2))
                y = emptyb->r.min.y+emptyb->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.maxlines < 2)
                colgrow(w->col, w, 1);
        return w;
}