tacme: add log file in acme root directory - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 4a3fb87264f8bc03fc62f00ef335056f30d18023
 (DIR) parent 833216fef8b946895956737d205bcad7031bf06f
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Wed, 30 Apr 2014 12:14:29 -0400
       
       acme: add log file in acme root directory
       
       Reading /mnt/acme/log reports a log of window create,
       put, and delete events, as they happen. It blocks until the
       next event is available.
       
       Example log output:
       
       8 new /Users/rsc/foo.go
       8 put /Users/rsc/foo.go
       8 del /Users/rsc/foo.go
       
       This lets acme-aware programs react to file writes, for example
       compiling code, running a test, or updating an import block.
       
       TBR=r
       R=r
       https://codereview.appspot.com/89560044
       
       Diffstat:
         M man/man4/acme.4                     |      28 ++++++++++++++++++++++++++--
         M src/cmd/acme/acme.c                 |       2 ++
         M src/cmd/acme/dat.h                  |       7 ++++++-
         M src/cmd/acme/exec.c                 |      11 +++++++++--
         M src/cmd/acme/fsys.c                 |       1 +
         A src/cmd/acme/logf.c                 |     197 +++++++++++++++++++++++++++++++
         M src/cmd/acme/look.c                 |      10 ++++++++--
         M src/cmd/acme/mkfile                 |       1 +
         M src/cmd/acme/rows.c                 |       1 +
         M src/cmd/acme/util.c                 |       1 +
         M src/cmd/acme/wind.c                 |       3 ++-
         M src/cmd/acme/xfid.c                 |       8 ++++++++
       
       12 files changed, 262 insertions(+), 8 deletions(-)
       ---
 (DIR) diff --git a/man/man4/acme.4 b/man/man4/acme.4
       t@@ -73,7 +73,7 @@ was run.
        The window is created if necessary, but not until text is actually written.
        .TP
        .B consctl
       -Is an empty unwritable file present only for compatibility; there is no way
       +is an empty unwritable file present only for compatibility; there is no way
        to turn off `echo', for example, under
        .IR acme .
        .TP
       t@@ -95,8 +95,32 @@ file.
        is an empty file, writable without effect, present only for compatibility with
        .BR rio .
        .TP
       +.B log
       +reports a log of window operations since the opening of the
       +.B log
       +file.
       +Each line describes a single operation using three fields separated by single spaces:
       +the decimal window ID, the operation, and the window name.
       +Reading from
       +.B log
       +blocks until there is an operation to report, so reading the file
       +can be used to monitor editor activity and react to changes.
       +The reported operations are
       +.L new
       +(window creation),
       +.L zerox
       +(window creation via zerox),
       +.LR get ,
       +.LR put ,
       +and
       +.LR del
       +(window deletion).
       +The window name can be the empty string; in particular it is empty in
       +.L new
       +log entries corresponding to windows created by external programs.
       +.TP
        .B new
       -A directory analogous to the numbered directories
       +is a directory analogous to the numbered directories
        .RI ( q.v. ).
        Accessing any
        file in
 (DIR) diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c
       t@@ -303,6 +303,7 @@ readfile(Column *c, char *s)
                winresize(w, w->r, FALSE, TRUE);
                textscrdraw(&w->body);
                textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
       +        xfidlog(w, "new");
        }
        
        char *ignotes[] = {
       t@@ -866,6 +867,7 @@ newwindowthread(void *v)
                        recvp(cnewwindow);
                        w = makenewwindow(nil);
                        winsettag(w);
       +                xfidlog(w, "new");
                        sendp(cnewwindow, w);
                }
        }
 (DIR) diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h
       t@@ -8,6 +8,7 @@ enum
                Qeditout,
                Qindex,
                Qlabel,
       +        Qlog,
                Qnew,
        
                QWaddr,
       t@@ -391,6 +392,7 @@ struct Fid
                Mntdir        *mntdir;
                int                nrpart;
                uchar        rpart[UTFmax];
       +        vlong        logoff;        // for putlog
        };
        
        
       t@@ -403,7 +405,6 @@ struct Xfid
                Fid        *f;
                uchar        *buf;
                int        flushed;
       -
        };
        
        void                xfidctl(void *);
       t@@ -418,6 +419,10 @@ void                xfideventwrite(Xfid*, Window*);
        void                xfidindexread(Xfid*);
        void                xfidutfread(Xfid*, Text*, uint, int);
        int                xfidruneread(Xfid*, Text*, uint, uint);
       +void                xfidlogopen(Xfid*);
       +void                xfidlogread(Xfid*);
       +void                xfidlogflush(Xfid*);
       +void                xfidlog(Window*, char*);
        
        struct Reffont
        {
 (DIR) diff --git a/src/cmd/acme/exec.c b/src/cmd/acme/exec.c
       t@@ -347,6 +347,7 @@ void
        newcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
        {
                Column *c;
       +        Window *w;
        
                USED(_0);
                USED(_1);
       t@@ -356,8 +357,11 @@ newcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5)
                USED(_5);
        
                c = rowadd(et->row, nil, -1);
       -        if(c)
       -                winsettag(coladd(c, nil, nil, -1));
       +        if(c) {
       +                w = coladd(c, nil, nil, -1);
       +                winsettag(w);
       +                xfidlog(w, "new");
       +        }
        }
        
        void
       t@@ -562,6 +566,7 @@ zeroxx(Text *et, Text *t, Text *_1, int _2, int _3, Rune *_4, int _5)
                        nw = coladd(t->w->col, nil, t->w, -1);
                        /* ugly: fix locks so w->unlock works */
                        winlock1(nw, t->w->owner);
       +                xfidlog(nw, "zerox");
                }
                if(locked)
                        winunlock(t->w);
       t@@ -627,6 +632,7 @@ get(Text *et, Text *t, Text *argt, int flag1, int _0, Rune *arg, int narg)
                        textsetselect(&u->w->tag, u->w->tag.file->b.nc, u->w->tag.file->b.nc);
                        textscrdraw(u);
                }
       +        xfidlog(w, "get");
        }
        
        void
       t@@ -782,6 +788,7 @@ put(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg)
                }
                namer = bytetorune(name, &nname);
                putfile(f, 0, f->b.nc, namer, nname);
       +        xfidlog(w, "put");
                free(name);
        }
        
 (DIR) diff --git a/src/cmd/acme/fsys.c b/src/cmd/acme/fsys.c
       t@@ -71,6 +71,7 @@ Dirtab dirtab[]=
                { "editout",        QTFILE,        Qeditout,        0200 },
                { "index",                QTFILE,        Qindex,        0400 },
                { "label",                QTFILE,        Qlabel,        0600 },
       +        { "log",                QTFILE,        Qlog,        0400 },
                { "new",                QTDIR,        Qnew,        0500|DMDIR },
                { nil, }
        };
 (DIR) diff --git a/src/cmd/acme/logf.c b/src/cmd/acme/logf.c
       t@@ -0,0 +1,197 @@
       +#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"
       +
       +// State for global log file.
       +typedef struct Log Log;
       +struct Log
       +{
       +        QLock lk;
       +        Rendez r;
       +
       +        vlong start; // msg[0] corresponds to 'start' in the global sequence of events
       +        
       +        // queued events (nev=entries in ev, mev=capacity of p)
       +        char **ev;
       +        int nev;
       +        int mev;
       +
       +        // open acme/put files that need to read events
       +        Fid **f;
       +        int nf;
       +        int mf;
       +        
       +        // active (blocked) reads waiting for events
       +        Xfid **read;
       +        int nread;
       +        int mread;
       +};
       +
       +static Log eventlog;
       +
       +void
       +xfidlogopen(Xfid *x)
       +{
       +        qlock(&eventlog.lk);
       +        if(eventlog.nf >= eventlog.mf) {        
       +                eventlog.mf = eventlog.mf*2;
       +                if(eventlog.mf == 0)
       +                        eventlog.mf = 8;
       +                eventlog.f = erealloc(eventlog.f, eventlog.mf*sizeof eventlog.f[0]);
       +        }
       +        eventlog.f[eventlog.nf++] = x->f;
       +        x->f->logoff = eventlog.start + eventlog.nev;
       +
       +        qunlock(&eventlog.lk);
       +}
       +
       +void
       +xfidlogclose(Xfid *x)
       +{
       +        int i;
       +
       +        qlock(&eventlog.lk);
       +        for(i=0; i<eventlog.nf; i++) {
       +                if(eventlog.f[i] == x->f) {
       +                        eventlog.f[i] = eventlog.f[--eventlog.nf];
       +                        break;
       +                }
       +        }
       +        qunlock(&eventlog.lk);
       +}
       +
       +void
       +xfidlogread(Xfid *x)
       +{
       +        char *p;
       +        int i;
       +        Fcall fc;
       +
       +        qlock(&eventlog.lk);
       +        if(eventlog.nread >= eventlog.mread) {        
       +                eventlog.mread = eventlog.mread*2;
       +                if(eventlog.mread == 0)
       +                        eventlog.mread = 8;
       +                eventlog.read = erealloc(eventlog.read, eventlog.mread*sizeof eventlog.read[0]);
       +        }
       +        eventlog.read[eventlog.nread++] = x;
       +        
       +        if(eventlog.r.l == nil)
       +                eventlog.r.l = &eventlog.lk;
       +        x->flushed = FALSE;
       +        while(x->f->logoff >= eventlog.start+eventlog.nev && !x->flushed)
       +                rsleep(&eventlog.r);
       +                
       +        for(i=0; i<eventlog.nread; i++) {
       +                if(eventlog.read[i] == x) {
       +                        eventlog.read[i] = eventlog.read[--eventlog.nread];
       +                        break;
       +                }
       +        }
       +
       +        if(x->flushed) {
       +                qunlock(&eventlog.lk);
       +                respond(x, &fc, "read cancelled");
       +                return;
       +        }
       +
       +        i = x->f->logoff - eventlog.start;
       +        p = estrdup(eventlog.ev[i]);
       +        x->f->logoff++;
       +        qunlock(&eventlog.lk);
       +
       +        fc.data = p;
       +        fc.count = strlen(p);
       +        respond(x, &fc, nil);
       +        free(p);        
       +}
       +
       +void
       +xfidlogflush(Xfid *x)
       +{
       +        int i;
       +        Xfid *rx;
       +
       +        qlock(&eventlog.lk);
       +        for(i=0; i<eventlog.nread; i++) {
       +                rx = eventlog.read[i];
       +                if(rx->fcall.tag == x->fcall.oldtag)
       +                        rx->flushed = TRUE;
       +        }
       +        qunlock(&eventlog.lk);
       +}
       +
       +/*
       + * add a log entry for op on w.
       + * expected calls:
       + *
       + * op == "new" for each new window
       + *        - caller of coladd or makenewwindow responsible for calling
       + *                xfidlog after setting window name
       + *        - exception: zerox
       + *
       + * op == "zerox" for new window created via zerox
       + *        - called from zeroxx
       + *
       + * op == "get" for Get executed on window
       + *        - called from get
       + *
       + * op == "put" for Put executed on window
       + *        - called from put
       + *
       + * op == "del" for deleted window
       + *        - called from winclose
       + */
       +void
       +xfidlog(Window *w, char *op)
       +{
       +        int i, n;
       +        vlong min;
       +        File *f;
       +        char *name;
       +
       +        qlock(&eventlog.lk);
       +        if(eventlog.nev >= eventlog.mev) {
       +                // Remove and free any entries that all readers have read.
       +                min = eventlog.start + eventlog.nev;
       +                for(i=0; i<eventlog.nf; i++) {
       +                        if(min > eventlog.f[i]->logoff)
       +                                min = eventlog.f[i]->logoff;
       +                }
       +                if(min > eventlog.start) {
       +                        n = min - eventlog.start;
       +                        for(i=0; i<n; i++)
       +                                free(eventlog.ev[i]);
       +                        eventlog.nev -= n;
       +                        eventlog.start += n;
       +                        memmove(eventlog.ev, eventlog.ev+n, eventlog.nev*sizeof eventlog.ev[0]);
       +                }
       +                
       +                // Otherwise grow.
       +                if(eventlog.nev >= eventlog.mev) {
       +                        eventlog.mev = eventlog.mev*2;
       +                        if(eventlog.mev == 0)
       +                                eventlog.mev = 8;
       +                        eventlog.ev = erealloc(eventlog.ev, eventlog.mev*sizeof eventlog.ev[0]);
       +                }
       +        }
       +        f = w->body.file;
       +        name = runetobyte(f->name, f->nname);
       +        if(name == nil)
       +                name = estrdup("");
       +        eventlog.ev[eventlog.nev++] = smprint("%d %s %s\n", w->id, op, name);
       +        free(name);
       +        if(eventlog.r.l == nil)
       +                eventlog.r.l = &eventlog.lk;
       +        rwakeupall(&eventlog.r);
       +        qunlock(&eventlog.lk);
       +}
 (DIR) diff --git a/src/cmd/acme/look.c b/src/cmd/acme/look.c
       t@@ -298,6 +298,7 @@ plumbshow(Plumbmsg *m)
                winsettag(w);
                textscrdraw(&w->body);
                textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
       +        xfidlog(w, "new");
        }
        
        int
       t@@ -770,6 +771,7 @@ openfile(Text *t, Expand *e)
                                w->autoindent = ow->autoindent;
                        }else
                                w->autoindent = globalautoindent;
       +                xfidlog(w, "new");
                }
                if(e->a1 == e->a0)
                        eval = FALSE;
       t@@ -803,6 +805,7 @@ new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg)
                int na, nf;
                Expand e;
                Runestr rs;
       +        Window *w;
        
                getarg(argt, FALSE, TRUE, &a, &na);
                if(a){
       t@@ -814,8 +817,11 @@ new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg)
                for(ndone=0; ; ndone++){
                        a = findbl(arg, narg, &na);
                        if(a == arg){
       -                        if(ndone==0 && et->col!=nil)
       -                                winsettag(coladd(et->col, nil, nil, -1));
       +                        if(ndone==0 && et->col!=nil) {
       +                                w = coladd(et->col, nil, nil, -1);
       +                                winsettag(w);
       +                                xfidlog(w, "new");
       +                        }
                                break;
                        }
                        nf = narg-na;
 (DIR) diff --git a/src/cmd/acme/mkfile b/src/cmd/acme/mkfile
       t@@ -15,6 +15,7 @@ OFILES=\
                exec.$O\
                file.$O\
                fsys.$O\
       +        logf.$O\
                look.$O\
                regx.$O\
                rows.$O\
 (DIR) diff --git a/src/cmd/acme/rows.c b/src/cmd/acme/rows.c
       t@@ -776,6 +776,7 @@ rowload(Row *row, char *file, int initing)
                                q0 = q1 = 0;
                        textshow(&w->body, q0, q1, 1);
                        w->maxlines = min(w->body.fr.nlines, max(w->maxlines, w->body.fr.maxlines));
       +                xfidlog(w, "new");
        Nextline:
                        l = rdline(b, &line);
                }
 (DIR) diff --git a/src/cmd/acme/util.c b/src/cmd/acme/util.c
       t@@ -97,6 +97,7 @@ errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
                        w = coladd(row.col[row.ncol-1], nil, nil, -1);
                        w->filemenu = FALSE;
                        winsetname(w, r, n);
       +                xfidlog(w, "new");
                }
                free(r);
                for(i=nincl; --i>=0; ){
 (DIR) diff --git a/src/cmd/acme/wind.c b/src/cmd/acme/wind.c
       t@@ -320,6 +320,7 @@ winclose(Window *w)
                int i;
        
                if(decref(&w->ref) == 0){
       +                xfidlog(w, "del");
                        windirfree(w);
                        textclose(&w->tag);
                        textclose(&w->body);
       t@@ -644,7 +645,7 @@ Rescue:
        }
        
        int
       -winclean(Window *w, int conservative)        /* as it stands, conservative is always TRUE */
       +winclean(Window *w, int conservative)
        {
                if(w->isscratch || w->isdir)        /* don't whine if it's a guide file, error window, etc. */
                        return TRUE;
 (DIR) diff --git a/src/cmd/acme/xfid.c b/src/cmd/acme/xfid.c
       t@@ -63,6 +63,8 @@ xfidflush(Xfid *x)
                Column *c;
                Xfid *wx;
        
       +        xfidlogflush(x);
       +
                /* search windows for matching tag */
                qlock(&row.lk);
                for(j=0; j<row.ncol; j++){
       t@@ -186,6 +188,9 @@ xfidopen(Xfid *x)
                }
                else{
                        switch(q){
       +                case Qlog:
       +                        xfidlogopen(x);
       +                        break;
                        case Qeditout:
                                if(!canqlock(&editoutlk)){
                                        respond(x, &fc, Einuse);
       t@@ -300,6 +305,9 @@ xfidread(Xfid *x)
                        case Qindex:
                                xfidindexread(x);
                                return;
       +                case Qlog:
       +                        xfidlogread(x);
       +                        return;
                        default:
                                warning(nil, "unknown qid %d\n", q);
                                break;